Skip to content

Commit 5ec6440

Browse files
committed
feat: handle stream and file inputs
1 parent 82780cc commit 5ec6440

File tree

14 files changed

+2805
-524
lines changed

14 files changed

+2805
-524
lines changed

README.md

Lines changed: 136 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This library compares two arrays or objects and returns a full diff of their dif
1818

1919
Most existing solutions return a confusing diff format that often requires extra parsing. They are also limited to object comparison.
2020

21-
**Superdiff** provides a complete and readable diff for both arrays **and** objects. Plus, it's battle-tested, has zero dependencies, and is super fast.
21+
**Superdiff** provides a complete and readable diff for both arrays **and** objects. Plus, it supports stream and file inputs for handling large datasets efficiently, is battle-tested, has zero dependencies, and is super fast.
2222

2323
Import. Enjoy. 👍
2424

@@ -40,7 +40,7 @@ I am grateful to the generous donors of **Superdiff**!
4040

4141
## FEATURES
4242

43-
**Superdiff** exports 5 functions:
43+
**Superdiff** exports 6 functions:
4444

4545
```ts
4646
// Returns a complete diff of two objects
@@ -52,6 +52,9 @@ getListDiff(prevList, nextList)
5252
// Streams the diff of two object lists, ideal for large lists and maximum performance
5353
streamListDiff(prevList, nextList, referenceProperty)
5454

55+
// Similar to streamListDiff, but for browser use
56+
streamListDiffClient(prevList, nextList, referenceProperty)
57+
5558
// Checks whether two values are equal
5659
isEqual(dataA, dataB)
5760

@@ -306,7 +309,10 @@ getListDiff(
306309
### streamListDiff()
307310

308311
```js
312+
// If you are in a server environment
309313
import { streamListDiff } from "@donedeal0/superdiff";
314+
// If you are in a browser environment
315+
import { streamListDiffClient } from "@donedeal0/superdiff";
310316
```
311317

312318
Streams the diff of two object lists, ideal for large lists and maximum performance.
@@ -315,14 +321,34 @@ Streams the diff of two object lists, ideal for large lists and maximum performa
315321

316322
**Input**
317323

324+
#### streamListDiff (server)
325+
326+
> In a server environment, `Readable` refers to Node.js streams, and `FilePath` refers to the path of a file (e.g., `./list.json`). Examples are provided in the #usage section below.
327+
318328
```ts
319-
prevList: Record<string, unknown>[],
320-
nextList: Record<string, unknown>[],
329+
// streamListDiff
330+
prevList: Readable | FilePath | Record<string, unknown>[],
331+
nextList: Readable | FilePath | Record<string, unknown>[],
321332
referenceProperty: keyof Record<string, unknown>,
322333
options: {
323334
showOnly?: ("added" | "deleted" | "moved" | "updated" | "equal")[], // [] by default
324-
chunksSize?: number, // // 0 by default
325-
considerMoveAsUpdate? boolean; // false by default
335+
chunksSize?: number, // 0 by default
336+
considerMoveAsUpdate?: boolean; // false by default
337+
}
338+
```
339+
340+
#### streamListDiffClient (browser)
341+
342+
> In a browser environment, `ReadableStream` refers to the browser's streaming API, and `File` refers to an uploaded or local file. Examples are provided in the #usage section below.
343+
344+
```ts
345+
prevList: ReadableStream<Record<string, unknown>> | File | Record<string, unknown>[],
346+
nextList: ReadableStream<Record<string, unknown>> | File | Record<string, unknown>[],
347+
referenceProperty: keyof Record<string, unknown>,
348+
options: {
349+
showOnly?: ("added" | "deleted" | "moved" | "updated" | "equal")[], // [] by default
350+
chunksSize?: number, // 0 by default
351+
considerMoveAsUpdate?: boolean; // false by default
326352
}
327353
```
328354

@@ -370,6 +396,40 @@ type StreamListDiff<T extends Record<string, unknown>> = {
370396

371397
**Input**
372398

399+
You can send streams, file paths, or arrays as input:
400+
401+
> If you are in a server environment
402+
403+
```ts
404+
// for a simple array
405+
const stream = [{ id: 1, name: "hello" }]
406+
// for a large array
407+
const stream = Readable.from(list, { objectMode: true });
408+
// for a local file
409+
const stream = path.resolve(__dirname, "./list.json");
410+
411+
```
412+
413+
> If you are in a browser environment
414+
415+
```ts
416+
// for a simple array
417+
const stream = [{ id: 1, name: "hello" }]
418+
// for a large array
419+
const stream = new ReadableStream({
420+
start(controller) {
421+
list.forEach((value) => controller.enqueue(value));
422+
controller.close();
423+
},
424+
});
425+
// for a local file
426+
const stream = new File([JSON.stringify(file)], "file.json", { type: "application/json" });
427+
// for a file input
428+
const stream = e.target.files[0];
429+
430+
```
431+
> Example
432+
373433
```diff
374434
const diff = streamListDiff(
375435
[
@@ -431,9 +491,78 @@ diff.on("data", (chunk) => {
431491
]
432492
});
433493

434-
diff.on("finish", () => console.log("The full diff is available"))
494+
diff.on("finish", () => console.log("Your data has been processed. The full diff is available."))
435495
diff.on("error", (err) => console.log(err))
436496
```
497+
498+
**Using `fetch`**
499+
500+
A common use case would be to do a live diff against a stream, in order to avoid loading the entire dataset into memory. Here are two examples, for browser and server use:
501+
502+
Browser:
503+
```ts
504+
import { streamListDiffClient } from "@donedeal0/superdiff";
505+
506+
async function streamDiffFromAPI() {
507+
try {
508+
const response = await fetch("https://example.com/api/streaming-data");
509+
const reader = response.body.getReader();
510+
511+
const stream = new ReadableStream({
512+
async start(controller) {
513+
let result;
514+
while (!(result = await reader.read()).done) {
515+
controller.enqueue(result.value); // Push the next chunk into the stream
516+
}
517+
controller.close(); // Close the stream when done
518+
},
519+
});
520+
521+
const prevStream = [{ id: 1, name: "Joe" }, { id: 2, name: "Jane" }] // Some previous list or stream
522+
523+
const diff = streamListDiffClient(prevStream, stream, 'id', { chunksSize: 5 });
524+
diff.on("data", (diffChunk) => console.log(diffChunk));
525+
diff.on("finish", () => console.log("Stream diff complete"));
526+
} catch (err) {
527+
console.error(err);
528+
}
529+
}
530+
```
531+
532+
Server:
533+
534+
```ts
535+
import fetch from "node-fetch";
536+
import { Readable } from "stream";
537+
import { streamListDiff } from "@donedeal0/superdiff";
538+
539+
async function streamDiffFromAPI() {
540+
try {
541+
const response = await fetch("https://example.com/api/streaming-data");
542+
const reader = response.body.getReader();
543+
544+
const stream = new Readable({
545+
async read() {
546+
let result;
547+
while (!(result = await reader.read()).done) {
548+
this.push(result.value); // Push the next chunk into the stream
549+
}
550+
this.push(null); // Close the stream when done
551+
},
552+
});
553+
554+
const prevList = [{ id: 1, name: "Joe" }, { id: 2, name: "Jane" }]; // Some previous list or stream
555+
const prevListStream = Readable.from(prevList, { objectMode: true })
556+
const diff = streamListDiff(prevListStream, stream, 'id', { chunksSize: 5 });
557+
558+
diff.on("data", (diffChunk) => console.log(diffChunk));
559+
diff.on("finish", () => console.log("Stream diff complete"));
560+
} catch (err) {
561+
console.error(err);
562+
}
563+
}
564+
```
565+
437566
<hr/>
438567

439568
### isEqual()

0 commit comments

Comments
 (0)