From 40a93e9d17dd3bea1afaa9e21c89689fe1219588 Mon Sep 17 00:00:00 2001
From: Antoine Lanoe <antoine.lanoe@meltwater.com>
Date: Fri, 4 Oct 2024 14:38:58 +0200
Subject: [PATCH 1/8] chore: stream large lists diff

---
 src/models/stream.ts           |  27 +++++
 src/stream/emitter.ts          |  27 +++++
 src/stream/stream-list-diff.ts | 202 +++++++++++++++++++++++++++++++++
 3 files changed, 256 insertions(+)
 create mode 100644 src/models/stream.ts
 create mode 100644 src/stream/emitter.ts
 create mode 100644 src/stream/stream-list-diff.ts

diff --git a/src/models/stream.ts b/src/models/stream.ts
new file mode 100644
index 0000000..5b874f6
--- /dev/null
+++ b/src/models/stream.ts
@@ -0,0 +1,27 @@
+import { LIST_STATUS } from "./list";
+
+export type StreamListsDiff<T extends Record<string, unknown>> = {
+  currentValue: T | null;
+  previousValue: T | null;
+  prevIndex: number | null;
+  newIndex: number | null;
+  indexDiff: number | null;
+  status: LIST_STATUS;
+};
+
+export type ReferenceProperty<T extends Record<string, unknown>> = keyof T;
+
+export type StreamReferences<T extends Record<string, unknown>> = Map<
+  ReferenceProperty<T>,
+  { prevIndex: number; nextIndex?: number }
+>;
+
+export type ListStreamOptions = {
+  chunksSize?: number; // 0 by default. If 0, stream will be live
+  showOnly?: `${LIST_STATUS}`[];
+  considerMoveAsUpdate?: boolean;
+};
+
+export const DEFAULT_LIST_STREAM_OPTIONS: ListStreamOptions = {
+  chunksSize: 0,
+};
diff --git a/src/stream/emitter.ts b/src/stream/emitter.ts
new file mode 100644
index 0000000..4554c32
--- /dev/null
+++ b/src/stream/emitter.ts
@@ -0,0 +1,27 @@
+type Listener<T extends unknown[]> = (...args: T) => void;
+
+export enum StreamEvent {
+  Data = "data",
+  Finish = "finish",
+  Error = "error",
+}
+export class EventEmitter {
+  private events: Record<string, Listener<unknown[]>[]> = {};
+
+  on<T extends unknown[]>(
+    event: `${StreamEvent}`,
+    listener: Listener<T>,
+  ): this {
+    if (!this.events[event]) {
+      this.events[event] = [];
+    }
+    this.events[event].push(listener as Listener<unknown[]>);
+    return this;
+  }
+
+  emit<T extends unknown[]>(event: `${StreamEvent}`, ...args: T): void {
+    if (this.events[event]) {
+      this.events[event].forEach((listener) => listener(...args));
+    }
+  }
+}
diff --git a/src/stream/stream-list-diff.ts b/src/stream/stream-list-diff.ts
new file mode 100644
index 0000000..46eac36
--- /dev/null
+++ b/src/stream/stream-list-diff.ts
@@ -0,0 +1,202 @@
+import {
+  DEFAULT_LIST_STREAM_OPTIONS,
+  ListStreamOptions,
+  ReferenceProperty,
+  StreamListsDiff,
+  StreamReferences,
+} from "../models/stream";
+import { LIST_STATUS } from "../models/list";
+import { isEqual } from "../utils";
+import { EventEmitter, StreamEvent } from "./emitter";
+
+function outputDiffChunk<T extends Record<string, unknown>>(
+  emitter: EventEmitter,
+) {
+  let chunks: StreamListsDiff<T>[] = [];
+
+  return function handleDiffChunk(
+    chunk: StreamListsDiff<T>,
+    options: ListStreamOptions,
+  ): void {
+    const showChunk = options?.showOnly
+      ? options?.showOnly.includes(chunk.status)
+      : true;
+    if (!showChunk) {
+      return;
+    }
+    if ((options.chunksSize as number) > 0) {
+      chunks.push(chunk);
+      if (chunks.length >= (options.chunksSize as number)) {
+        const output = chunks;
+        chunks = [];
+        return emitter.emit(StreamEvent.Data, output);
+      }
+    }
+    return emitter.emit(StreamEvent.Data, [chunk]);
+  };
+}
+
+function formatSingleListStreamDiff<T extends Record<string, unknown>>(
+  list: T[],
+  isPrevious: boolean,
+  status: LIST_STATUS,
+  options: ListStreamOptions,
+): StreamListsDiff<T>[] {
+  const diff: StreamListsDiff<T>[] = list.map((data, i) => ({
+    previousValue: isPrevious ? data : null,
+    currentValue: isPrevious ? null : data,
+    prevIndex: status === LIST_STATUS.ADDED ? null : i,
+    newIndex: status === LIST_STATUS.ADDED ? i : null,
+    indexDiff: null,
+    status,
+  }));
+  if (options.showOnly && options.showOnly.length > 0) {
+    return diff.filter((value) => options.showOnly?.includes(value.status));
+  }
+  return diff;
+}
+
+function getDiffChunks<T extends Record<string, unknown>>(
+  prevList: T[],
+  nextList: T[],
+  referenceProperty: ReferenceProperty<T>,
+  emitter: EventEmitter,
+  options: ListStreamOptions = DEFAULT_LIST_STREAM_OPTIONS,
+) {
+  if (!prevList && !nextList) {
+    return [];
+  }
+  if (!prevList) {
+    const nextDiff = formatSingleListStreamDiff(
+      nextList as T[],
+      false,
+      LIST_STATUS.ADDED,
+      options,
+    );
+    return nextDiff.forEach((data) => handleDiffChunk(data, options));
+  }
+  if (!nextList) {
+    const prevDiff = formatSingleListStreamDiff(
+      prevList as T[],
+      true,
+      LIST_STATUS.DELETED,
+      options,
+    );
+    return prevDiff.forEach((data) => handleDiffChunk(data, options));
+  }
+  const listsReferences: StreamReferences<T> = new Map();
+  const handleDiffChunk = outputDiffChunk<T>(emitter);
+  prevList.forEach((data, i) => {
+    if (data) {
+      listsReferences.set(String(data[referenceProperty]), {
+        prevIndex: i,
+        nextIndex: undefined,
+      });
+    }
+  });
+
+  nextList.forEach((data, i) => {
+    if (data) {
+      const listReference = listsReferences.get(
+        String(data[referenceProperty]),
+      );
+      if (listReference) {
+        listReference.nextIndex = i;
+      } else {
+        handleDiffChunk(
+          {
+            previousValue: null,
+            currentValue: data,
+            prevIndex: null,
+            newIndex: i,
+            indexDiff: null,
+            status: LIST_STATUS.ADDED,
+          },
+          options,
+        );
+      }
+    }
+  });
+
+  for (const data of listsReferences.values()) {
+    if (!data.nextIndex) {
+      handleDiffChunk(
+        {
+          previousValue: prevList[data.prevIndex],
+          currentValue: null,
+          prevIndex: data.prevIndex,
+          newIndex: null,
+          indexDiff: null,
+          status: LIST_STATUS.DELETED,
+        },
+        options,
+      );
+    } else {
+      const prevData = prevList[data.prevIndex];
+      const nextData = nextList[data.nextIndex];
+      const isDataEqual = isEqual(prevData, nextData);
+      const indexDiff = data.prevIndex - data.nextIndex;
+      if (isDataEqual) {
+        if (indexDiff === 0) {
+          handleDiffChunk(
+            {
+              previousValue: prevList[data.prevIndex],
+              currentValue: nextList[data.nextIndex],
+              prevIndex: null,
+              newIndex: data.nextIndex,
+              indexDiff: null,
+              status: LIST_STATUS.EQUAL,
+            },
+            options,
+          );
+        } else {
+          handleDiffChunk(
+            {
+              previousValue: prevList[data.prevIndex],
+              currentValue: nextList[data.nextIndex],
+              prevIndex: data.prevIndex,
+              newIndex: data.nextIndex,
+              indexDiff,
+              status: options.considerMoveAsUpdate
+                ? LIST_STATUS.UPDATED
+                : LIST_STATUS.MOVED,
+            },
+            options,
+          );
+        }
+      } else {
+        handleDiffChunk(
+          {
+            previousValue: prevList[data.prevIndex],
+            currentValue: nextList[data.nextIndex],
+            prevIndex: data.prevIndex,
+            newIndex: data.nextIndex,
+            indexDiff,
+            status: LIST_STATUS.UPDATED,
+          },
+          options,
+        );
+      }
+    }
+  }
+  emitter.emit(StreamEvent.Finish);
+}
+
+export function streamListsDiff<T extends Record<string, unknown>>(
+  prevList: T[],
+  nextList: T[],
+  referenceProperty: ReferenceProperty<T>,
+  options: ListStreamOptions = DEFAULT_LIST_STREAM_OPTIONS,
+) {
+  const emitter = new EventEmitter();
+  try {
+    setTimeout(
+      () =>
+        getDiffChunks(prevList, nextList, referenceProperty, emitter, options),
+      0,
+    );
+    return emitter;
+  } catch (err) {
+    emitter.emit(StreamEvent.Error, err);
+  }
+}

From 43b3c9e72ad03ead105b21beb77781126f6209ca Mon Sep 17 00:00:00 2001
From: Antoine Lanoe <antoine.lanoe@meltwater.com>
Date: Sat, 5 Oct 2024 10:41:34 +0200
Subject: [PATCH 2/8] chore: remove useless dev dependencies + folder structure

---
 jest.config.js                                |   20 +-
 package-lock.json                             | 2589 ++++++-----------
 package.json                                  |   23 +-
 src/index.ts                                  |    8 +-
 src/{list-diff.ts => lib/list-diff/index.ts}  |    4 +-
 {test => src/lib/list-diff}/list-diff.test.ts |    4 +-
 .../object-diff/index.ts}                     |    4 +-
 .../lib/object-diff}/object-diff.test.ts      |    4 +-
 .../stream-list-diff}/emitter.ts              |    0
 .../stream-list-diff/index.ts}                |    6 +-
 src/{utils.ts => lib/utils/index.ts}          |    2 +-
 {test => src/lib/utils}/utils.test.ts         |    2 +-
 src/models/{list.ts => list/index.ts}         |    0
 src/models/{object.ts => object/index.ts}     |    0
 src/models/{stream.ts => stream/index.ts}     |    2 +-
 src/models/{utils.ts => utils/index.ts}       |    0
 tsconfig.json                                 |   11 +-
 17 files changed, 899 insertions(+), 1780 deletions(-)
 rename src/{list-diff.ts => lib/list-diff/index.ts} (98%)
 rename {test => src/lib/list-diff}/list-diff.test.ts (99%)
 rename src/{object-diff.ts => lib/object-diff/index.ts} (99%)
 rename {test => src/lib/object-diff}/object-diff.test.ts (99%)
 rename src/{stream => lib/stream-list-diff}/emitter.ts (100%)
 rename src/{stream/stream-list-diff.ts => lib/stream-list-diff/index.ts} (98%)
 rename src/{utils.ts => lib/utils/index.ts} (95%)
 rename {test => src/lib/utils}/utils.test.ts (98%)
 rename src/models/{list.ts => list/index.ts} (100%)
 rename src/models/{object.ts => object/index.ts} (100%)
 rename src/models/{stream.ts => stream/index.ts} (94%)
 rename src/models/{utils.ts => utils/index.ts} (100%)

diff --git a/jest.config.js b/jest.config.js
index 1da90a9..0dbaf11 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,5 +1,23 @@
 module.exports = {
   transform: {
-    "^.+\\.(ts|js)$": "ts-jest",
+   "^.+\\.(ts|js)$": [
+      "@swc/jest",
+      {
+        jsc: {
+          baseUrl: ".",
+          parser: {
+            syntax: "typescript",
+            tsx: true,
+            dynamicImport: true,
+          },
+          paths: {
+            "@models/*": ["./src/models/*"],
+            "@lib/*": ["./src/lib/*"],
+            
+          },
+          target: "esnext",
+        },
+      },
+    ],
   },
 };
diff --git a/package-lock.json b/package-lock.json
index e72d858..e2eae4f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,52 +1,32 @@
 {
   "name": "@donedeal0/superdiff",
-  "version": "1.1.3",
+  "version": "2.0.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "@donedeal0/superdiff",
-      "version": "1.1.3",
+      "version": "2.0.0",
       "license": "ISC",
       "devDependencies": {
-        "@actions/core": "^1.10.1",
-        "@babel/preset-env": "^7.25.4",
         "@eslint/js": "^9.11.1",
         "@semantic-release/exec": "^6.0.3",
         "@semantic-release/git": "^10.0.1",
         "@semantic-release/github": "^11.0.0",
         "@semantic-release/npm": "^12.0.1",
+        "@swc/core": "^1.7.26",
+        "@swc/jest": "^0.2.36",
         "@types/jest": "^29.5.13",
         "eslint": "^9.11.1",
         "husky": "^9.1.6",
         "jest": "^29.7.0",
         "prettier": "^3.3.3",
-        "ts-jest": "^29.2.5",
+        "swc-loader": "^0.2.6",
         "tsup": "^8.3.0",
         "typescript": "^5.6.2",
         "typescript-eslint": "^8.7.0"
       }
     },
-    "node_modules/@actions/core": {
-      "version": "1.10.1",
-      "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz",
-      "integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==",
-      "dev": true,
-      "dependencies": {
-        "@actions/http-client": "^2.0.1",
-        "uuid": "^8.3.2"
-      }
-    },
-    "node_modules/@actions/http-client": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
-      "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
-      "dev": true,
-      "dependencies": {
-        "tunnel": "^0.0.6",
-        "undici": "^5.25.4"
-      }
-    },
     "node_modules/@ampproject/remapping": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
@@ -127,31 +107,6 @@
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/helper-annotate-as-pure": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz",
-      "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/types": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz",
-      "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/traverse": "^7.24.7",
-        "@babel/types": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@babel/helper-compilation-targets": {
       "version": "7.25.2",
       "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz",
@@ -168,73 +123,6 @@
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/helper-create-class-features-plugin": {
-      "version": "7.25.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz",
-      "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.24.7",
-        "@babel/helper-member-expression-to-functions": "^7.24.8",
-        "@babel/helper-optimise-call-expression": "^7.24.7",
-        "@babel/helper-replace-supers": "^7.25.0",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
-        "@babel/traverse": "^7.25.4",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-create-regexp-features-plugin": {
-      "version": "7.25.2",
-      "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz",
-      "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.24.7",
-        "regexpu-core": "^5.3.1",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-define-polyfill-provider": {
-      "version": "0.6.2",
-      "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz",
-      "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-compilation-targets": "^7.22.6",
-        "@babel/helper-plugin-utils": "^7.22.5",
-        "debug": "^4.1.1",
-        "lodash.debounce": "^4.0.8",
-        "resolve": "^1.14.2"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
-      }
-    },
-    "node_modules/@babel/helper-member-expression-to-functions": {
-      "version": "7.24.8",
-      "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz",
-      "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/traverse": "^7.24.8",
-        "@babel/types": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@babel/helper-module-imports": {
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz",
@@ -266,18 +154,6 @@
         "@babel/core": "^7.0.0"
       }
     },
-    "node_modules/@babel/helper-optimise-call-expression": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz",
-      "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==",
-      "dev": true,
-      "dependencies": {
-        "@babel/types": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@babel/helper-plugin-utils": {
       "version": "7.24.8",
       "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz",
@@ -287,40 +163,6 @@
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/helper-remap-async-to-generator": {
-      "version": "7.25.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz",
-      "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.24.7",
-        "@babel/helper-wrap-function": "^7.25.0",
-        "@babel/traverse": "^7.25.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-replace-supers": {
-      "version": "7.25.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz",
-      "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-member-expression-to-functions": "^7.24.8",
-        "@babel/helper-optimise-call-expression": "^7.24.7",
-        "@babel/traverse": "^7.25.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
     "node_modules/@babel/helper-simple-access": {
       "version": "7.24.7",
       "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz",
@@ -334,19 +176,6 @@
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz",
-      "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/traverse": "^7.24.7",
-        "@babel/types": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@babel/helper-string-parser": {
       "version": "7.24.8",
       "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
@@ -374,20 +203,6 @@
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/helper-wrap-function": {
-      "version": "7.25.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz",
-      "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/template": "^7.25.0",
-        "@babel/traverse": "^7.25.0",
-        "@babel/types": "^7.25.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@babel/helpers": {
       "version": "7.23.8",
       "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz",
@@ -432,97 +247,6 @@
         "node": ">=6.0.0"
       }
     },
-    "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
-      "version": "7.25.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz",
-      "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8",
-        "@babel/traverse": "^7.25.3"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
-      "version": "7.25.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz",
-      "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
-      "version": "7.25.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz",
-      "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz",
-      "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
-        "@babel/plugin-transform-optional-chaining": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.13.0"
-      }
-    },
-    "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
-      "version": "7.25.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz",
-      "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8",
-        "@babel/traverse": "^7.25.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-proposal-private-property-in-object": {
-      "version": "7.21.0-placeholder-for-preset-env.2",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
-      "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
-      "dev": true,
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
     "node_modules/@babel/plugin-syntax-async-generators": {
       "version": "7.8.4",
       "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
@@ -559,75 +283,6 @@
         "@babel/core": "^7.0.0-0"
       }
     },
-    "node_modules/@babel/plugin-syntax-class-static-block": {
-      "version": "7.14.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
-      "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.14.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-dynamic-import": {
-      "version": "7.8.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
-      "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-export-namespace-from": {
-      "version": "7.8.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
-      "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.3"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-import-assertions": {
-      "version": "7.25.6",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.6.tgz",
-      "integrity": "sha512-aABl0jHw9bZ2karQ/uUD6XP4u0SG22SJrOHFoL6XB1R7dTovOP4TzTlsxOYC5yQ1pdscVK2JTUnF6QL3ARoAiQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-import-attributes": {
-      "version": "7.25.6",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz",
-      "integrity": "sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
     "node_modules/@babel/plugin-syntax-import-meta": {
       "version": "7.10.4",
       "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
@@ -739,941 +394,13 @@
         "@babel/core": "^7.0.0-0"
       }
     },
-    "node_modules/@babel/plugin-syntax-private-property-in-object": {
-      "version": "7.14.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
-      "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.14.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
     "node_modules/@babel/plugin-syntax-top-level-await": {
       "version": "7.14.5",
       "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
       "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
       "dev": true,
       "dependencies": {
-        "@babel/helper-plugin-utils": "^7.14.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-typescript": {
-      "version": "7.23.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz",
-      "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.22.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
-      "version": "7.18.6",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
-      "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
-        "@babel/helper-plugin-utils": "^7.18.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-arrow-functions": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz",
-      "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-async-generator-functions": {
-      "version": "7.25.4",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz",
-      "integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8",
-        "@babel/helper-remap-async-to-generator": "^7.25.0",
-        "@babel/plugin-syntax-async-generators": "^7.8.4",
-        "@babel/traverse": "^7.25.4"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-async-to-generator": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz",
-      "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-module-imports": "^7.24.7",
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/helper-remap-async-to-generator": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-block-scoped-functions": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz",
-      "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-block-scoping": {
-      "version": "7.25.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz",
-      "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-class-properties": {
-      "version": "7.25.4",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz",
-      "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-create-class-features-plugin": "^7.25.4",
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-class-static-block": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz",
-      "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-create-class-features-plugin": "^7.24.7",
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/plugin-syntax-class-static-block": "^7.14.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.12.0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-classes": {
-      "version": "7.25.4",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz",
-      "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.24.7",
-        "@babel/helper-compilation-targets": "^7.25.2",
-        "@babel/helper-plugin-utils": "^7.24.8",
-        "@babel/helper-replace-supers": "^7.25.0",
-        "@babel/traverse": "^7.25.4",
-        "globals": "^11.1.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-computed-properties": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz",
-      "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/template": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-destructuring": {
-      "version": "7.24.8",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz",
-      "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-dotall-regex": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz",
-      "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.24.7",
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-duplicate-keys": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz",
-      "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
-      "version": "7.25.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz",
-      "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.25.0",
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-dynamic-import": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz",
-      "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/plugin-syntax-dynamic-import": "^7.8.3"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-exponentiation-operator": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz",
-      "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7",
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-export-namespace-from": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz",
-      "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-for-of": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz",
-      "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-function-name": {
-      "version": "7.25.1",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz",
-      "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-compilation-targets": "^7.24.8",
-        "@babel/helper-plugin-utils": "^7.24.8",
-        "@babel/traverse": "^7.25.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-json-strings": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz",
-      "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/plugin-syntax-json-strings": "^7.8.3"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-literals": {
-      "version": "7.25.2",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz",
-      "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-logical-assignment-operators": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz",
-      "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-member-expression-literals": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz",
-      "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-modules-amd": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz",
-      "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-module-transforms": "^7.24.7",
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-modules-commonjs": {
-      "version": "7.24.8",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz",
-      "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-module-transforms": "^7.24.8",
-        "@babel/helper-plugin-utils": "^7.24.8",
-        "@babel/helper-simple-access": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-modules-systemjs": {
-      "version": "7.25.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz",
-      "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-module-transforms": "^7.25.0",
-        "@babel/helper-plugin-utils": "^7.24.8",
-        "@babel/helper-validator-identifier": "^7.24.7",
-        "@babel/traverse": "^7.25.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-modules-umd": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz",
-      "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-module-transforms": "^7.24.7",
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz",
-      "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.24.7",
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-new-target": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz",
-      "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz",
-      "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-numeric-separator": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz",
-      "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/plugin-syntax-numeric-separator": "^7.10.4"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-object-rest-spread": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz",
-      "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-compilation-targets": "^7.24.7",
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
-        "@babel/plugin-transform-parameters": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-object-super": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz",
-      "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/helper-replace-supers": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-optional-catch-binding": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz",
-      "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-optional-chaining": {
-      "version": "7.24.8",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz",
-      "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
-        "@babel/plugin-syntax-optional-chaining": "^7.8.3"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-parameters": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz",
-      "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-private-methods": {
-      "version": "7.25.4",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz",
-      "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-create-class-features-plugin": "^7.25.4",
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-private-property-in-object": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz",
-      "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.24.7",
-        "@babel/helper-create-class-features-plugin": "^7.24.7",
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-property-literals": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz",
-      "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-regenerator": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz",
-      "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "regenerator-transform": "^0.15.2"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-reserved-words": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz",
-      "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-shorthand-properties": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz",
-      "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-spread": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz",
-      "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-sticky-regex": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz",
-      "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-template-literals": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz",
-      "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-typeof-symbol": {
-      "version": "7.24.8",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz",
-      "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-unicode-escapes": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz",
-      "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-unicode-property-regex": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz",
-      "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.24.7",
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-unicode-regex": {
-      "version": "7.24.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz",
-      "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.24.7",
-        "@babel/helper-plugin-utils": "^7.24.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-unicode-sets-regex": {
-      "version": "7.25.4",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz",
-      "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.25.2",
-        "@babel/helper-plugin-utils": "^7.24.8"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/preset-env": {
-      "version": "7.25.4",
-      "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz",
-      "integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/compat-data": "^7.25.4",
-        "@babel/helper-compilation-targets": "^7.25.2",
-        "@babel/helper-plugin-utils": "^7.24.8",
-        "@babel/helper-validator-option": "^7.24.8",
-        "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3",
-        "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0",
-        "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0",
-        "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7",
-        "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0",
-        "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
-        "@babel/plugin-syntax-async-generators": "^7.8.4",
-        "@babel/plugin-syntax-class-properties": "^7.12.13",
-        "@babel/plugin-syntax-class-static-block": "^7.14.5",
-        "@babel/plugin-syntax-dynamic-import": "^7.8.3",
-        "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
-        "@babel/plugin-syntax-import-assertions": "^7.24.7",
-        "@babel/plugin-syntax-import-attributes": "^7.24.7",
-        "@babel/plugin-syntax-import-meta": "^7.10.4",
-        "@babel/plugin-syntax-json-strings": "^7.8.3",
-        "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
-        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
-        "@babel/plugin-syntax-numeric-separator": "^7.10.4",
-        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
-        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
-        "@babel/plugin-syntax-optional-chaining": "^7.8.3",
-        "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
-        "@babel/plugin-syntax-top-level-await": "^7.14.5",
-        "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
-        "@babel/plugin-transform-arrow-functions": "^7.24.7",
-        "@babel/plugin-transform-async-generator-functions": "^7.25.4",
-        "@babel/plugin-transform-async-to-generator": "^7.24.7",
-        "@babel/plugin-transform-block-scoped-functions": "^7.24.7",
-        "@babel/plugin-transform-block-scoping": "^7.25.0",
-        "@babel/plugin-transform-class-properties": "^7.25.4",
-        "@babel/plugin-transform-class-static-block": "^7.24.7",
-        "@babel/plugin-transform-classes": "^7.25.4",
-        "@babel/plugin-transform-computed-properties": "^7.24.7",
-        "@babel/plugin-transform-destructuring": "^7.24.8",
-        "@babel/plugin-transform-dotall-regex": "^7.24.7",
-        "@babel/plugin-transform-duplicate-keys": "^7.24.7",
-        "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0",
-        "@babel/plugin-transform-dynamic-import": "^7.24.7",
-        "@babel/plugin-transform-exponentiation-operator": "^7.24.7",
-        "@babel/plugin-transform-export-namespace-from": "^7.24.7",
-        "@babel/plugin-transform-for-of": "^7.24.7",
-        "@babel/plugin-transform-function-name": "^7.25.1",
-        "@babel/plugin-transform-json-strings": "^7.24.7",
-        "@babel/plugin-transform-literals": "^7.25.2",
-        "@babel/plugin-transform-logical-assignment-operators": "^7.24.7",
-        "@babel/plugin-transform-member-expression-literals": "^7.24.7",
-        "@babel/plugin-transform-modules-amd": "^7.24.7",
-        "@babel/plugin-transform-modules-commonjs": "^7.24.8",
-        "@babel/plugin-transform-modules-systemjs": "^7.25.0",
-        "@babel/plugin-transform-modules-umd": "^7.24.7",
-        "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7",
-        "@babel/plugin-transform-new-target": "^7.24.7",
-        "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7",
-        "@babel/plugin-transform-numeric-separator": "^7.24.7",
-        "@babel/plugin-transform-object-rest-spread": "^7.24.7",
-        "@babel/plugin-transform-object-super": "^7.24.7",
-        "@babel/plugin-transform-optional-catch-binding": "^7.24.7",
-        "@babel/plugin-transform-optional-chaining": "^7.24.8",
-        "@babel/plugin-transform-parameters": "^7.24.7",
-        "@babel/plugin-transform-private-methods": "^7.25.4",
-        "@babel/plugin-transform-private-property-in-object": "^7.24.7",
-        "@babel/plugin-transform-property-literals": "^7.24.7",
-        "@babel/plugin-transform-regenerator": "^7.24.7",
-        "@babel/plugin-transform-reserved-words": "^7.24.7",
-        "@babel/plugin-transform-shorthand-properties": "^7.24.7",
-        "@babel/plugin-transform-spread": "^7.24.7",
-        "@babel/plugin-transform-sticky-regex": "^7.24.7",
-        "@babel/plugin-transform-template-literals": "^7.24.7",
-        "@babel/plugin-transform-typeof-symbol": "^7.24.8",
-        "@babel/plugin-transform-unicode-escapes": "^7.24.7",
-        "@babel/plugin-transform-unicode-property-regex": "^7.24.7",
-        "@babel/plugin-transform-unicode-regex": "^7.24.7",
-        "@babel/plugin-transform-unicode-sets-regex": "^7.25.4",
-        "@babel/preset-modules": "0.1.6-no-external-plugins",
-        "babel-plugin-polyfill-corejs2": "^0.4.10",
-        "babel-plugin-polyfill-corejs3": "^0.10.6",
-        "babel-plugin-polyfill-regenerator": "^0.6.1",
-        "core-js-compat": "^3.37.1",
-        "semver": "^6.3.1"
+        "@babel/helper-plugin-utils": "^7.14.5"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -1682,36 +409,19 @@
         "@babel/core": "^7.0.0-0"
       }
     },
-    "node_modules/@babel/preset-modules": {
-      "version": "0.1.6-no-external-plugins",
-      "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
-      "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.0.0",
-        "@babel/types": "^7.4.4",
-        "esutils": "^2.0.2"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
-      }
-    },
-    "node_modules/@babel/regjsgen": {
-      "version": "0.8.0",
-      "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz",
-      "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==",
-      "dev": true
-    },
-    "node_modules/@babel/runtime": {
-      "version": "7.25.6",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz",
-      "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==",
+    "node_modules/@babel/plugin-syntax-typescript": {
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz",
+      "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==",
       "dev": true,
       "dependencies": {
-        "regenerator-runtime": "^0.14.0"
+        "@babel/helper-plugin-utils": "^7.22.5"
       },
       "engines": {
         "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
     "node_modules/@babel/template": {
@@ -2303,15 +1013,6 @@
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
     },
-    "node_modules/@fastify/busboy": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
-      "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
-      "dev": true,
-      "engines": {
-        "node": ">=14"
-      }
-    },
     "node_modules/@humanwhocodes/module-importer": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -2663,6 +1364,18 @@
         "node": ">=8"
       }
     },
+    "node_modules/@jest/create-cache-key-function": {
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz",
+      "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.6.3"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
     "node_modules/@jest/environment": {
       "version": "29.7.0",
       "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
@@ -3168,6 +1881,17 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/@jridgewell/source-map": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+      "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25"
+      }
+    },
     "node_modules/@jridgewell/sourcemap-codec": {
       "version": "1.4.14",
       "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
@@ -4119,6 +2843,236 @@
         "@sinonjs/commons": "^3.0.0"
       }
     },
+    "node_modules/@swc/core": {
+      "version": "1.7.26",
+      "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.26.tgz",
+      "integrity": "sha512-f5uYFf+TmMQyYIoxkn/evWhNGuUzC730dFwAKGwBVHHVoPyak1/GvJUm6i1SKl+2Hrj9oN0i3WSoWWZ4pgI8lw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "dependencies": {
+        "@swc/counter": "^0.1.3",
+        "@swc/types": "^0.1.12"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/swc"
+      },
+      "optionalDependencies": {
+        "@swc/core-darwin-arm64": "1.7.26",
+        "@swc/core-darwin-x64": "1.7.26",
+        "@swc/core-linux-arm-gnueabihf": "1.7.26",
+        "@swc/core-linux-arm64-gnu": "1.7.26",
+        "@swc/core-linux-arm64-musl": "1.7.26",
+        "@swc/core-linux-x64-gnu": "1.7.26",
+        "@swc/core-linux-x64-musl": "1.7.26",
+        "@swc/core-win32-arm64-msvc": "1.7.26",
+        "@swc/core-win32-ia32-msvc": "1.7.26",
+        "@swc/core-win32-x64-msvc": "1.7.26"
+      },
+      "peerDependencies": {
+        "@swc/helpers": "*"
+      },
+      "peerDependenciesMeta": {
+        "@swc/helpers": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@swc/core-darwin-arm64": {
+      "version": "1.7.26",
+      "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.26.tgz",
+      "integrity": "sha512-FF3CRYTg6a7ZVW4yT9mesxoVVZTrcSWtmZhxKCYJX9brH4CS/7PRPjAKNk6kzWgWuRoglP7hkjQcd6EpMcZEAw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-darwin-x64": {
+      "version": "1.7.26",
+      "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.26.tgz",
+      "integrity": "sha512-az3cibZdsay2HNKmc4bjf62QVukuiMRh5sfM5kHR/JMTrLyS6vSw7Ihs3UTkZjUxkLTT8ro54LI6sV6sUQUbLQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-linux-arm-gnueabihf": {
+      "version": "1.7.26",
+      "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.26.tgz",
+      "integrity": "sha512-VYPFVJDO5zT5U3RpCdHE5v1gz4mmR8BfHecUZTmD2v1JeFY6fv9KArJUpjrHEEsjK/ucXkQFmJ0jaiWXmpOV9Q==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-linux-arm64-gnu": {
+      "version": "1.7.26",
+      "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.26.tgz",
+      "integrity": "sha512-YKevOV7abpjcAzXrhsl+W48Z9mZvgoVs2eP5nY+uoMAdP2b3GxC0Df1Co0I90o2lkzO4jYBpTMcZlmUXLdXn+Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-linux-arm64-musl": {
+      "version": "1.7.26",
+      "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.26.tgz",
+      "integrity": "sha512-3w8iZICMkQQON0uIcvz7+Q1MPOW6hJ4O5ETjA0LSP/tuKqx30hIniCGOgPDnv3UTMruLUnQbtBwVCZTBKR3Rkg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-linux-x64-gnu": {
+      "version": "1.7.26",
+      "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.26.tgz",
+      "integrity": "sha512-c+pp9Zkk2lqb06bNGkR2Looxrs7FtGDMA4/aHjZcCqATgp348hOKH5WPvNLBl+yPrISuWjbKDVn3NgAvfvpH4w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-linux-x64-musl": {
+      "version": "1.7.26",
+      "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.26.tgz",
+      "integrity": "sha512-PgtyfHBF6xG87dUSSdTJHwZ3/8vWZfNIXQV2GlwEpslrOkGqy+WaiiyE7Of7z9AvDILfBBBcJvJ/r8u980wAfQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-win32-arm64-msvc": {
+      "version": "1.7.26",
+      "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.26.tgz",
+      "integrity": "sha512-9TNXPIJqFynlAOrRD6tUQjMq7KApSklK3R/tXgIxc7Qx+lWu8hlDQ/kVPLpU7PWvMMwC/3hKBW+p5f+Tms1hmA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-win32-ia32-msvc": {
+      "version": "1.7.26",
+      "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.26.tgz",
+      "integrity": "sha512-9YngxNcG3177GYdsTum4V98Re+TlCeJEP4kEwEg9EagT5s3YejYdKwVAkAsJszzkXuyRDdnHUpYbTrPG6FiXrQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/core-win32-x64-msvc": {
+      "version": "1.7.26",
+      "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.26.tgz",
+      "integrity": "sha512-VR+hzg9XqucgLjXxA13MtV5O3C0bK0ywtLIBw/+a+O+Oc6mxFWHtdUeXDbIi5AiPbn0fjgVJMqYnyjGyyX8u0w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@swc/counter": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
+      "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
+      "dev": true
+    },
+    "node_modules/@swc/jest": {
+      "version": "0.2.36",
+      "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.36.tgz",
+      "integrity": "sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==",
+      "dev": true,
+      "dependencies": {
+        "@jest/create-cache-key-function": "^29.7.0",
+        "@swc/counter": "^0.1.3",
+        "jsonc-parser": "^3.2.0"
+      },
+      "engines": {
+        "npm": ">= 7.0.0"
+      },
+      "peerDependencies": {
+        "@swc/core": "*"
+      }
+    },
+    "node_modules/@swc/types": {
+      "version": "0.1.12",
+      "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz",
+      "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==",
+      "dev": true,
+      "dependencies": {
+        "@swc/counter": "^0.1.3"
+      }
+    },
     "node_modules/@types/babel__core": {
       "version": "7.20.5",
       "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -4485,6 +3439,181 @@
         "url": "https://opencollective.com/eslint"
       }
     },
+    "node_modules/@webassemblyjs/ast": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
+      "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/helper-numbers": "1.11.6",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
+      }
+    },
+    "node_modules/@webassemblyjs/floating-point-hex-parser": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
+      "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
+      "dev": true,
+      "peer": true
+    },
+    "node_modules/@webassemblyjs/helper-api-error": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
+      "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
+      "dev": true,
+      "peer": true
+    },
+    "node_modules/@webassemblyjs/helper-buffer": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
+      "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==",
+      "dev": true,
+      "peer": true
+    },
+    "node_modules/@webassemblyjs/helper-numbers": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
+      "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/floating-point-hex-parser": "1.11.6",
+        "@webassemblyjs/helper-api-error": "1.11.6",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
+      "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
+      "dev": true,
+      "peer": true
+    },
+    "node_modules/@webassemblyjs/helper-wasm-section": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
+      "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-buffer": "1.12.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/wasm-gen": "1.12.1"
+      }
+    },
+    "node_modules/@webassemblyjs/ieee754": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
+      "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@xtuc/ieee754": "^1.2.0"
+      }
+    },
+    "node_modules/@webassemblyjs/leb128": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
+      "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/utf8": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
+      "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
+      "dev": true,
+      "peer": true
+    },
+    "node_modules/@webassemblyjs/wasm-edit": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
+      "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-buffer": "1.12.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/helper-wasm-section": "1.12.1",
+        "@webassemblyjs/wasm-gen": "1.12.1",
+        "@webassemblyjs/wasm-opt": "1.12.1",
+        "@webassemblyjs/wasm-parser": "1.12.1",
+        "@webassemblyjs/wast-printer": "1.12.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-gen": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
+      "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/ieee754": "1.11.6",
+        "@webassemblyjs/leb128": "1.11.6",
+        "@webassemblyjs/utf8": "1.11.6"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-opt": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
+      "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-buffer": "1.12.1",
+        "@webassemblyjs/wasm-gen": "1.12.1",
+        "@webassemblyjs/wasm-parser": "1.12.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-parser": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
+      "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-api-error": "1.11.6",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/ieee754": "1.11.6",
+        "@webassemblyjs/leb128": "1.11.6",
+        "@webassemblyjs/utf8": "1.11.6"
+      }
+    },
+    "node_modules/@webassemblyjs/wast-printer": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
+      "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@xtuc/ieee754": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+      "dev": true,
+      "peer": true
+    },
+    "node_modules/@xtuc/long": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+      "dev": true,
+      "peer": true
+    },
     "node_modules/acorn": {
       "version": "8.12.1",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
@@ -4497,6 +3626,16 @@
         "node": ">=0.4.0"
       }
     },
+    "node_modules/acorn-import-attributes": {
+      "version": "1.9.5",
+      "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
+      "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
+      "dev": true,
+      "peer": true,
+      "peerDependencies": {
+        "acorn": "^8"
+      }
+    },
     "node_modules/acorn-jsx": {
       "version": "5.3.2",
       "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
@@ -4547,6 +3686,16 @@
         "url": "https://github.com/sponsors/epoberezkin"
       }
     },
+    "node_modules/ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "dev": true,
+      "peer": true,
+      "peerDependencies": {
+        "ajv": "^6.9.1"
+      }
+    },
     "node_modules/ansi-escapes": {
       "version": "4.3.2",
       "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
@@ -4625,12 +3774,6 @@
       "dev": true,
       "peer": true
     },
-    "node_modules/async": {
-      "version": "3.2.6",
-      "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
-      "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
-      "dev": true
-    },
     "node_modules/babel-jest": {
       "version": "29.7.0",
       "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
@@ -4753,45 +3896,6 @@
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
-    "node_modules/babel-plugin-polyfill-corejs2": {
-      "version": "0.4.11",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz",
-      "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==",
-      "dev": true,
-      "dependencies": {
-        "@babel/compat-data": "^7.22.6",
-        "@babel/helper-define-polyfill-provider": "^0.6.2",
-        "semver": "^6.3.1"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
-      }
-    },
-    "node_modules/babel-plugin-polyfill-corejs3": {
-      "version": "0.10.6",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
-      "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-define-polyfill-provider": "^0.6.2",
-        "core-js-compat": "^3.38.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
-      }
-    },
-    "node_modules/babel-plugin-polyfill-regenerator": {
-      "version": "0.6.2",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz",
-      "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-define-polyfill-provider": "^0.6.2"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
-      }
-    },
     "node_modules/babel-preset-current-node-syntax": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
@@ -4872,12 +3976,12 @@
       }
     },
     "node_modules/braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
       "dev": true,
       "dependencies": {
-        "fill-range": "^7.0.1"
+        "fill-range": "^7.1.1"
       },
       "engines": {
         "node": ">=8"
@@ -4915,18 +4019,6 @@
         "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
       }
     },
-    "node_modules/bs-logger": {
-      "version": "0.2.6",
-      "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
-      "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
-      "dev": true,
-      "dependencies": {
-        "fast-json-stable-stringify": "2.x"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
     "node_modules/bser": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
@@ -5063,6 +4155,16 @@
         "node": ">= 6"
       }
     },
+    "node_modules/chrome-trace-event": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
+      "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
+      "dev": true,
+      "peer": true,
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
     "node_modules/ci-info": {
       "version": "3.7.0",
       "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz",
@@ -5423,19 +4525,6 @@
       "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
       "dev": true
     },
-    "node_modules/core-js-compat": {
-      "version": "3.38.1",
-      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz",
-      "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==",
-      "dev": true,
-      "dependencies": {
-        "browserslist": "^4.23.3"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/core-js"
-      }
-    },
     "node_modules/core-util-is": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -5736,21 +4825,6 @@
       "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
       "dev": true
     },
-    "node_modules/ejs": {
-      "version": "3.1.10",
-      "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
-      "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
-      "dev": true,
-      "dependencies": {
-        "jake": "^10.8.5"
-      },
-      "bin": {
-        "ejs": "bin/cli.js"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/electron-to-chromium": {
       "version": "1.5.29",
       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.29.tgz",
@@ -5782,6 +4856,20 @@
       "dev": true,
       "peer": true
     },
+    "node_modules/enhanced-resolve": {
+      "version": "5.17.1",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
+      "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
     "node_modules/env-ci": {
       "version": "11.1.0",
       "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.1.0.tgz",
@@ -5972,6 +5060,13 @@
         "is-arrayish": "^0.2.1"
       }
     },
+    "node_modules/es-module-lexer": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
+      "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
+      "dev": true,
+      "peer": true
+    },
     "node_modules/esbuild": {
       "version": "0.23.1",
       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
@@ -6319,6 +5414,16 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "dev": true,
+      "peer": true,
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
     "node_modules/execa": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@@ -6458,40 +5563,10 @@
         "node": ">=16.0.0"
       }
     },
-    "node_modules/filelist": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
-      "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
-      "dev": true,
-      "dependencies": {
-        "minimatch": "^5.0.1"
-      }
-    },
-    "node_modules/filelist/node_modules/brace-expansion": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
-      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
-      "dev": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0"
-      }
-    },
-    "node_modules/filelist/node_modules/minimatch": {
-      "version": "5.1.6",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
-      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
       "dev": true,
       "dependencies": {
         "to-regex-range": "^5.0.1"
@@ -6743,6 +5818,13 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/glob-to-regexp": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+      "dev": true,
+      "peer": true
+    },
     "node_modules/globals": {
       "version": "11.12.0",
       "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -7359,94 +6441,6 @@
         "@pkgjs/parseargs": "^0.11.0"
       }
     },
-    "node_modules/jake": {
-      "version": "10.9.2",
-      "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
-      "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==",
-      "dev": true,
-      "dependencies": {
-        "async": "^3.2.3",
-        "chalk": "^4.0.2",
-        "filelist": "^1.0.4",
-        "minimatch": "^3.1.2"
-      },
-      "bin": {
-        "jake": "bin/cli.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/jake/node_modules/ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "dev": true,
-      "dependencies": {
-        "color-convert": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/jake/node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/jake/node_modules/color-convert": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-      "dev": true,
-      "dependencies": {
-        "color-name": "~1.1.4"
-      },
-      "engines": {
-        "node": ">=7.0.0"
-      }
-    },
-    "node_modules/jake/node_modules/color-name": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true
-    },
-    "node_modules/jake/node_modules/has-flag": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/jake/node_modules/supports-color": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-      "dev": true,
-      "dependencies": {
-        "has-flag": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/java-properties": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz",
@@ -9117,6 +8111,12 @@
         "node": ">=6"
       }
     },
+    "node_modules/jsonc-parser": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+      "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+      "dev": true
+    },
     "node_modules/jsonfile": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -9236,6 +8236,16 @@
         "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
       }
     },
+    "node_modules/loader-runner": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+      "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+      "dev": true,
+      "peer": true,
+      "engines": {
+        "node": ">=6.11.5"
+      }
+    },
     "node_modules/locate-path": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -9266,12 +8276,6 @@
       "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==",
       "dev": true
     },
-    "node_modules/lodash.debounce": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
-      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
-      "dev": true
-    },
     "node_modules/lodash.escaperegexp": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
@@ -9290,12 +8294,6 @@
       "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
       "dev": true
     },
-    "node_modules/lodash.memoize": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
-      "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
-      "dev": true
-    },
     "node_modules/lodash.merge": {
       "version": "4.6.2",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -9371,12 +8369,6 @@
       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
       "dev": true
     },
-    "node_modules/make-error": {
-      "version": "1.3.6",
-      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
-      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
-      "dev": true
-    },
     "node_modules/makeerror": {
       "version": "1.0.12",
       "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
@@ -9478,12 +8470,12 @@
       }
     },
     "node_modules/micromatch": {
-      "version": "4.0.5",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
-      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
       "dev": true,
       "dependencies": {
-        "braces": "^3.0.2",
+        "braces": "^3.0.3",
         "picomatch": "^2.3.1"
       },
       "engines": {
@@ -9505,6 +8497,29 @@
         "node": ">=16"
       }
     },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "dev": true,
+      "peer": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/mimic-fn": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
@@ -12811,6 +11826,16 @@
         }
       ]
     },
+    "node_modules/randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
     "node_modules/rc": {
       "version": "1.2.8",
       "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -12939,63 +11964,13 @@
     "node_modules/readdirp": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
-      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
-      "dev": true,
-      "dependencies": {
-        "picomatch": "^2.2.1"
-      },
-      "engines": {
-        "node": ">=8.10.0"
-      }
-    },
-    "node_modules/regenerate": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
-      "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
-      "dev": true
-    },
-    "node_modules/regenerate-unicode-properties": {
-      "version": "10.1.1",
-      "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz",
-      "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==",
-      "dev": true,
-      "dependencies": {
-        "regenerate": "^1.4.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/regenerator-runtime": {
-      "version": "0.14.1",
-      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
-      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
-      "dev": true
-    },
-    "node_modules/regenerator-transform": {
-      "version": "0.15.2",
-      "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz",
-      "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/runtime": "^7.8.4"
-      }
-    },
-    "node_modules/regexpu-core": {
-      "version": "5.3.2",
-      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz",
-      "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
       "dev": true,
       "dependencies": {
-        "@babel/regjsgen": "^0.8.0",
-        "regenerate": "^1.4.2",
-        "regenerate-unicode-properties": "^10.1.0",
-        "regjsparser": "^0.9.1",
-        "unicode-match-property-ecmascript": "^2.0.0",
-        "unicode-match-property-value-ecmascript": "^2.1.0"
+        "picomatch": "^2.2.1"
       },
       "engines": {
-        "node": ">=4"
+        "node": ">=8.10.0"
       }
     },
     "node_modules/registry-auth-token": {
@@ -13010,27 +11985,6 @@
         "node": ">=14"
       }
     },
-    "node_modules/regjsparser": {
-      "version": "0.9.1",
-      "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
-      "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==",
-      "dev": true,
-      "dependencies": {
-        "jsesc": "~0.5.0"
-      },
-      "bin": {
-        "regjsparser": "bin/parser"
-      }
-    },
-    "node_modules/regjsparser/node_modules/jsesc": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
-      "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
-      "dev": true,
-      "bin": {
-        "jsesc": "bin/jsesc"
-      }
-    },
     "node_modules/require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -13162,6 +12116,25 @@
       "dev": true,
       "peer": true
     },
+    "node_modules/schema-utils": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+      "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.8",
+        "ajv": "^6.12.5",
+        "ajv-keywords": "^3.5.2"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
     "node_modules/semantic-release": {
       "version": "24.1.2",
       "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.1.2.tgz",
@@ -13521,6 +12494,16 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/serialize-javascript": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+      "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "randombytes": "^2.1.0"
+      }
+    },
     "node_modules/shebang-command": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -13964,6 +12947,29 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/swc-loader": {
+      "version": "0.2.6",
+      "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.6.tgz",
+      "integrity": "sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==",
+      "dev": true,
+      "dependencies": {
+        "@swc/counter": "^0.1.3"
+      },
+      "peerDependencies": {
+        "@swc/core": "^1.2.147",
+        "webpack": ">=2"
+      }
+    },
+    "node_modules/tapable": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+      "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+      "dev": true,
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/temp-dir": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz",
@@ -14015,6 +13021,119 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/terser": {
+      "version": "5.34.1",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.34.1.tgz",
+      "integrity": "sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.8.2",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/terser-webpack-plugin": {
+      "version": "5.3.10",
+      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz",
+      "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.20",
+        "jest-worker": "^27.4.5",
+        "schema-utils": "^3.1.1",
+        "serialize-javascript": "^6.0.1",
+        "terser": "^5.26.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.1.0"
+      },
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "esbuild": {
+          "optional": true
+        },
+        "uglify-js": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/jest-worker": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+      "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@types/node": "*",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.0.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/terser/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true,
+      "peer": true
+    },
+    "node_modules/terser/node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
     "node_modules/test-exclude": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
@@ -14198,66 +13317,6 @@
       "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
       "dev": true
     },
-    "node_modules/ts-jest": {
-      "version": "29.2.5",
-      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz",
-      "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==",
-      "dev": true,
-      "dependencies": {
-        "bs-logger": "^0.2.6",
-        "ejs": "^3.1.10",
-        "fast-json-stable-stringify": "^2.1.0",
-        "jest-util": "^29.0.0",
-        "json5": "^2.2.3",
-        "lodash.memoize": "^4.1.2",
-        "make-error": "^1.3.6",
-        "semver": "^7.6.3",
-        "yargs-parser": "^21.1.1"
-      },
-      "bin": {
-        "ts-jest": "cli.js"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": ">=7.0.0-beta.0 <8",
-        "@jest/transform": "^29.0.0",
-        "@jest/types": "^29.0.0",
-        "babel-jest": "^29.0.0",
-        "jest": "^29.0.0",
-        "typescript": ">=4.3 <6"
-      },
-      "peerDependenciesMeta": {
-        "@babel/core": {
-          "optional": true
-        },
-        "@jest/transform": {
-          "optional": true
-        },
-        "@jest/types": {
-          "optional": true
-        },
-        "babel-jest": {
-          "optional": true
-        },
-        "esbuild": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/ts-jest/node_modules/semver": {
-      "version": "7.6.3",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
-      "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/tsup": {
       "version": "8.3.0",
       "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.3.0.tgz",
@@ -14321,15 +13380,6 @@
         "node": ">= 8"
       }
     },
-    "node_modules/tunnel": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
-      "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
-      }
-    },
     "node_modules/type-check": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -14413,27 +13463,6 @@
         "node": ">=0.8.0"
       }
     },
-    "node_modules/undici": {
-      "version": "5.28.4",
-      "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
-      "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
-      "dev": true,
-      "dependencies": {
-        "@fastify/busboy": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=14.0"
-      }
-    },
-    "node_modules/unicode-canonical-property-names-ecmascript": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
-      "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/unicode-emoji-modifier-base": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz",
@@ -14444,37 +13473,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/unicode-match-property-ecmascript": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
-      "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
-      "dev": true,
-      "dependencies": {
-        "unicode-canonical-property-names-ecmascript": "^2.0.0",
-        "unicode-property-aliases-ecmascript": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/unicode-match-property-value-ecmascript": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz",
-      "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/unicode-property-aliases-ecmascript": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
-      "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/unicorn-magic": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
@@ -14572,15 +13570,6 @@
       "dev": true,
       "peer": true
     },
-    "node_modules/uuid": {
-      "version": "8.3.2",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
-      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
-      "dev": true,
-      "bin": {
-        "uuid": "dist/bin/uuid"
-      }
-    },
     "node_modules/v8-to-istanbul": {
       "version": "9.2.0",
       "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz",
@@ -14614,12 +13603,114 @@
         "makeerror": "1.0.12"
       }
     },
+    "node_modules/watchpack": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
+      "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
     "node_modules/webidl-conversions": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
       "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
       "dev": true
     },
+    "node_modules/webpack": {
+      "version": "5.95.0",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz",
+      "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@types/estree": "^1.0.5",
+        "@webassemblyjs/ast": "^1.12.1",
+        "@webassemblyjs/wasm-edit": "^1.12.1",
+        "@webassemblyjs/wasm-parser": "^1.12.1",
+        "acorn": "^8.7.1",
+        "acorn-import-attributes": "^1.9.5",
+        "browserslist": "^4.21.10",
+        "chrome-trace-event": "^1.0.2",
+        "enhanced-resolve": "^5.17.1",
+        "es-module-lexer": "^1.2.1",
+        "eslint-scope": "5.1.1",
+        "events": "^3.2.0",
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.2.11",
+        "json-parse-even-better-errors": "^2.3.1",
+        "loader-runner": "^4.2.0",
+        "mime-types": "^2.1.27",
+        "neo-async": "^2.6.2",
+        "schema-utils": "^3.2.0",
+        "tapable": "^2.1.1",
+        "terser-webpack-plugin": "^5.3.10",
+        "watchpack": "^2.4.1",
+        "webpack-sources": "^3.2.3"
+      },
+      "bin": {
+        "webpack": "bin/webpack.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependenciesMeta": {
+        "webpack-cli": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack-sources": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+      "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+      "dev": true,
+      "peer": true,
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/webpack/node_modules/eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/webpack/node_modules/estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true,
+      "peer": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/webpack/node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true,
+      "peer": true
+    },
     "node_modules/whatwg-url": {
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
diff --git a/package.json b/package.json
index d1be0f4..61b0065 100644
--- a/package.json
+++ b/package.json
@@ -61,28 +61,29 @@
   ],
   "scripts": {
     "build": "tsup",
-    "test": "jest",
-    "lint": "eslint --cache --max-warnings=0 --fix",
-    "tsc": "tsc --noEmit --incremental",
     "format": "npx prettier . --write",
-    "prepare": "husky"
+    "lint:dead-code": "npx -p typescript@latest -p knip knip",
+    "lint": "eslint --cache --max-warnings=0 --fix",
+    "prepare": "husky",
+    "test": "jest",
+    "tsc": "tsc --noEmit --incremental"
   },
   "devDependencies": {
     "@eslint/js": "^9.11.1",
-    "eslint": "^9.11.1",
-    "prettier": "^3.3.3",
     "@semantic-release/exec": "^6.0.3",
     "@semantic-release/git": "^10.0.1",
     "@semantic-release/github": "^11.0.0",
     "@semantic-release/npm": "^12.0.1",
-    "@actions/core": "^1.10.1",
-    "@babel/preset-env": "^7.25.4",
+    "@swc/core": "^1.7.26",
+    "@swc/jest": "^0.2.36",
     "@types/jest": "^29.5.13",
+    "eslint": "^9.11.1",
     "husky": "^9.1.6",
     "jest": "^29.7.0",
-    "ts-jest": "^29.2.5",
+    "prettier": "^3.3.3",
+    "swc-loader": "^0.2.6",
     "tsup": "^8.3.0",
-    "typescript-eslint": "^8.7.0",
-    "typescript": "^5.6.2"
+    "typescript": "^5.6.2",
+    "typescript-eslint": "^8.7.0"
   }
 }
diff --git a/src/index.ts b/src/index.ts
index 8eba074..ca0a6d8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,7 @@
-export { getObjectDiff } from "./object-diff";
-export { getListDiff } from "./list-diff";
-export { isEqual, isObject } from "./utils";
+export { getObjectDiff } from "./lib/object-diff";
+export { getListDiff } from "./lib/list-diff";
+export { isEqual, isObject } from "./lib/utils";
+export { streamListsDiff } from "./lib/stream-list-diff";
 export * from "./models/list";
 export * from "./models/object";
+export * from "./models/stream";
diff --git a/src/list-diff.ts b/src/lib/list-diff/index.ts
similarity index 98%
rename from src/list-diff.ts
rename to src/lib/list-diff/index.ts
index 0675938..85b2453 100644
--- a/src/list-diff.ts
+++ b/src/lib/list-diff/index.ts
@@ -3,8 +3,8 @@ import {
   LIST_STATUS,
   ListDiff,
   ListDiffOptions,
-} from "./models/list";
-import { isEqual, isObject } from "./utils";
+} from "@models/list";
+import { isEqual, isObject } from "@lib/utils";
 
 function getLeanDiff(
   diff: ListDiff["diff"],
diff --git a/test/list-diff.test.ts b/src/lib/list-diff/list-diff.test.ts
similarity index 99%
rename from test/list-diff.test.ts
rename to src/lib/list-diff/list-diff.test.ts
index 6826d9a..2089b0d 100644
--- a/test/list-diff.test.ts
+++ b/src/lib/list-diff/list-diff.test.ts
@@ -1,5 +1,5 @@
-import { getListDiff } from "../src/list-diff";
-import { LIST_STATUS } from "../src/models/list";
+import { getListDiff } from ".";
+import { LIST_STATUS } from "@models/list";
 
 describe("getListDiff", () => {
   it("returns an empty diff if no lists are provided", () => {
diff --git a/src/object-diff.ts b/src/lib/object-diff/index.ts
similarity index 99%
rename from src/object-diff.ts
rename to src/lib/object-diff/index.ts
index 1eb02ec..b5068d8 100644
--- a/src/object-diff.ts
+++ b/src/lib/object-diff/index.ts
@@ -6,8 +6,8 @@ import {
   ObjectDiffOptions,
   Diff,
   DEFAULT_OBJECT_DIFF_OPTIONS,
-} from "./models/object";
-import { isEqual, isObject } from "./utils";
+} from "@models/object";
+import { isEqual, isObject } from "@lib/utils";
 
 function getLeanDiff(
   diff: ObjectDiff["diff"],
diff --git a/test/object-diff.test.ts b/src/lib/object-diff/object-diff.test.ts
similarity index 99%
rename from test/object-diff.test.ts
rename to src/lib/object-diff/object-diff.test.ts
index bb3c0c7..ab38818 100644
--- a/test/object-diff.test.ts
+++ b/src/lib/object-diff/object-diff.test.ts
@@ -1,5 +1,5 @@
-import { GRANULARITY, OBJECT_STATUS } from "../src/models/object";
-import { getObjectDiff } from "../src/object-diff";
+import { GRANULARITY, OBJECT_STATUS } from "../../models/object";
+import { getObjectDiff } from ".";
 
 describe("getObjectDiff", () => {
   it("returns an empty diff if no objects are provided", () => {
diff --git a/src/stream/emitter.ts b/src/lib/stream-list-diff/emitter.ts
similarity index 100%
rename from src/stream/emitter.ts
rename to src/lib/stream-list-diff/emitter.ts
diff --git a/src/stream/stream-list-diff.ts b/src/lib/stream-list-diff/index.ts
similarity index 98%
rename from src/stream/stream-list-diff.ts
rename to src/lib/stream-list-diff/index.ts
index 46eac36..ed009c9 100644
--- a/src/stream/stream-list-diff.ts
+++ b/src/lib/stream-list-diff/index.ts
@@ -4,9 +4,9 @@ import {
   ReferenceProperty,
   StreamListsDiff,
   StreamReferences,
-} from "../models/stream";
-import { LIST_STATUS } from "../models/list";
-import { isEqual } from "../utils";
+} from "@models/stream";
+import { LIST_STATUS } from "@models/list";
+import { isEqual } from "@lib/utils";
 import { EventEmitter, StreamEvent } from "./emitter";
 
 function outputDiffChunk<T extends Record<string, unknown>>(
diff --git a/src/utils.ts b/src/lib/utils/index.ts
similarity index 95%
rename from src/utils.ts
rename to src/lib/utils/index.ts
index b0662bd..1e26b91 100644
--- a/src/utils.ts
+++ b/src/lib/utils/index.ts
@@ -1,4 +1,4 @@
-import { isEqualOptions } from "./models/utils";
+import { isEqualOptions } from "@models/utils";
 
 /**
  * Returns true if two data are equal
diff --git a/test/utils.test.ts b/src/lib/utils/utils.test.ts
similarity index 98%
rename from test/utils.test.ts
rename to src/lib/utils/utils.test.ts
index d1f5e29..f719456 100644
--- a/test/utils.test.ts
+++ b/src/lib/utils/utils.test.ts
@@ -1,4 +1,4 @@
-import { isEqual, isObject } from "../src/utils";
+import { isEqual, isObject } from ".";
 
 describe("isEqual", () => {
   it("return true if data are the same", () => {
diff --git a/src/models/list.ts b/src/models/list/index.ts
similarity index 100%
rename from src/models/list.ts
rename to src/models/list/index.ts
diff --git a/src/models/object.ts b/src/models/object/index.ts
similarity index 100%
rename from src/models/object.ts
rename to src/models/object/index.ts
diff --git a/src/models/stream.ts b/src/models/stream/index.ts
similarity index 94%
rename from src/models/stream.ts
rename to src/models/stream/index.ts
index 5b874f6..0bda847 100644
--- a/src/models/stream.ts
+++ b/src/models/stream/index.ts
@@ -1,4 +1,4 @@
-import { LIST_STATUS } from "./list";
+import { LIST_STATUS } from "@models/list";
 
 export type StreamListsDiff<T extends Record<string, unknown>> = {
   currentValue: T | null;
diff --git a/src/models/utils.ts b/src/models/utils/index.ts
similarity index 100%
rename from src/models/utils.ts
rename to src/models/utils/index.ts
diff --git a/tsconfig.json b/tsconfig.json
index 7c6e9b7..9fb7612 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,6 @@
 {
   "exclude": ["node_modules", "dist"],
-  "include": ["src/*.ts"],
+  "include": ["src"],
   "compilerOptions": {
     "declaration": true,
     "declarationDir": "./dist",
@@ -15,6 +15,13 @@
     "strict": true,
     "resolveJsonModule": true,
     "allowSyntheticDefaultImports": true,
-    "skipLibCheck": true 
+    "skipLibCheck": true ,
+    "baseUrl": ".",
+    "paths": {
+      "@models/*": ["./src/models/*"],
+      "@lib/*": ["./src/lib/*"],
+      
+
+    }
   },
 }

From 896186c7cf35e7f0ab408fc1ce821bf10d033903 Mon Sep 17 00:00:00 2001
From: Antoine Lanoe <antoine.lanoe@meltwater.com>
Date: Sat, 5 Oct 2024 12:01:04 +0200
Subject: [PATCH 3/8] chore: handle edgecases streamlistsdiff

---
 src/lib/object-diff/index.ts      |  2 +-
 src/lib/stream-list-diff/index.ts | 68 +++++++++++++++++++++++--------
 2 files changed, 53 insertions(+), 17 deletions(-)

diff --git a/src/lib/object-diff/index.ts b/src/lib/object-diff/index.ts
index b5068d8..524e9cd 100644
--- a/src/lib/object-diff/index.ts
+++ b/src/lib/object-diff/index.ts
@@ -209,7 +209,7 @@ function getSubPropertiesDiff(
  * Returns the diff between two objects
  * @param {ObjectData} prevData - The original object.
  * @param {ObjectData} nextData - The new object.
- *  * @param {ObjectOptions} options - Options to refine your output.
+ * @param {ObjectOptions} options - Options to refine your output.
     - `showOnly`: returns only the values whose status you are interested in. It takes two parameters: `statuses` and `granularity`
        `statuses` are the status you want to see in the output (e.g. `["added", "equal"]`)
       `granularity` can be either `basic` (to return only the main properties whose status matches your query) or `deep` (to return the main properties if some of their subproperties' status match your request. The subproperties are filtered accordingly).
diff --git a/src/lib/stream-list-diff/index.ts b/src/lib/stream-list-diff/index.ts
index ed009c9..1b27896 100644
--- a/src/lib/stream-list-diff/index.ts
+++ b/src/lib/stream-list-diff/index.ts
@@ -16,6 +16,7 @@ function outputDiffChunk<T extends Record<string, unknown>>(
 
   return function handleDiffChunk(
     chunk: StreamListsDiff<T>,
+    isLastChunk: boolean,
     options: ListStreamOptions,
   ): void {
     const showChunk = options?.showOnly
@@ -26,7 +27,7 @@ function outputDiffChunk<T extends Record<string, unknown>>(
     }
     if ((options.chunksSize as number) > 0) {
       chunks.push(chunk);
-      if (chunks.length >= (options.chunksSize as number)) {
+      if (chunks.length >= (options.chunksSize as number) || isLastChunk) {
         const output = chunks;
         chunks = [];
         return emitter.emit(StreamEvent.Data, output);
@@ -56,6 +57,14 @@ function formatSingleListStreamDiff<T extends Record<string, unknown>>(
   return diff;
 }
 
+function isValidChunkSize(
+  chunksSize: ListStreamOptions["chunksSize"],
+): boolean {
+  if (!chunksSize) return true;
+  const x = String(Math.sign(chunksSize));
+  return x !== "-1" && x !== "NaN";
+}
+
 function getDiffChunks<T extends Record<string, unknown>>(
   prevList: T[],
   nextList: T[],
@@ -63,6 +72,12 @@ function getDiffChunks<T extends Record<string, unknown>>(
   emitter: EventEmitter,
   options: ListStreamOptions = DEFAULT_LIST_STREAM_OPTIONS,
 ) {
+  if (!isValidChunkSize(options?.chunksSize)) {
+    return emitter.emit(
+      StreamEvent.Error,
+      `The chunk size can't be negative. You entered the value ${options.chunksSize}`,
+    );
+  }
   if (!prevList && !nextList) {
     return [];
   }
@@ -73,7 +88,9 @@ function getDiffChunks<T extends Record<string, unknown>>(
       LIST_STATUS.ADDED,
       options,
     );
-    return nextDiff.forEach((data) => handleDiffChunk(data, options));
+    return nextDiff.forEach((data, i) =>
+      handleDiffChunk(data, i === nextDiff.length - 1, options),
+    );
   }
   if (!nextList) {
     const prevDiff = formatSingleListStreamDiff(
@@ -82,7 +99,9 @@ function getDiffChunks<T extends Record<string, unknown>>(
       LIST_STATUS.DELETED,
       options,
     );
-    return prevDiff.forEach((data) => handleDiffChunk(data, options));
+    return prevDiff.forEach((data, i) =>
+      handleDiffChunk(data, i === prevDiff.length - 1, options),
+    );
   }
   const listsReferences: StreamReferences<T> = new Map();
   const handleDiffChunk = outputDiffChunk<T>(emitter);
@@ -112,13 +131,17 @@ function getDiffChunks<T extends Record<string, unknown>>(
             indexDiff: null,
             status: LIST_STATUS.ADDED,
           },
+          i === nextList.length - 1,
           options,
         );
       }
     }
   });
-
+  let streamedChunks = 0;
+  const totalChunks = listsReferences.size;
   for (const data of listsReferences.values()) {
+    streamedChunks++;
+    const isLastChunk = totalChunks === streamedChunks;
     if (!data.nextIndex) {
       handleDiffChunk(
         {
@@ -129,6 +152,7 @@ function getDiffChunks<T extends Record<string, unknown>>(
           indexDiff: null,
           status: LIST_STATUS.DELETED,
         },
+        isLastChunk,
         options,
       );
     } else {
@@ -147,6 +171,7 @@ function getDiffChunks<T extends Record<string, unknown>>(
               indexDiff: null,
               status: LIST_STATUS.EQUAL,
             },
+            isLastChunk,
             options,
           );
         } else {
@@ -161,6 +186,7 @@ function getDiffChunks<T extends Record<string, unknown>>(
                 ? LIST_STATUS.UPDATED
                 : LIST_STATUS.MOVED,
             },
+            isLastChunk,
             options,
           );
         }
@@ -174,29 +200,39 @@ function getDiffChunks<T extends Record<string, unknown>>(
             indexDiff,
             status: LIST_STATUS.UPDATED,
           },
+          isLastChunk,
           options,
         );
       }
     }
   }
-  emitter.emit(StreamEvent.Finish);
+  return emitter.emit(StreamEvent.Finish);
 }
 
+/**
+ * Streams the diff of two object lists
+ * @param {Record<string, unknown>[]} prevList - The original object list.
+ * @param {Record<string, unknown>[]} nextList - The new object list.
+ * @param {ReferenceProperty<T>} referenceProperty - A common property in all the objects of your lists (e.g. `id`)
+ * @param {ListStreamOptions} options - Options to refine your output.
+    - `chunksSize`: the number of object diffs returned by each stream chunk. If set to `0`, each stream will return a single object diff. If set to `10` each stream will return 10 object diffs. (default is `0`)
+    - `showOnly`: returns only the values whose status you are interested in. (e.g. `["added", "equal"]`)
+    - `considerMoveAsUpdate`: if set to `true` a `moved` object will be considered as `updated`
+ * @returns EventEmitter
+ */
 export function streamListsDiff<T extends Record<string, unknown>>(
   prevList: T[],
   nextList: T[],
   referenceProperty: ReferenceProperty<T>,
   options: ListStreamOptions = DEFAULT_LIST_STREAM_OPTIONS,
-) {
+): EventEmitter {
   const emitter = new EventEmitter();
-  try {
-    setTimeout(
-      () =>
-        getDiffChunks(prevList, nextList, referenceProperty, emitter, options),
-      0,
-    );
-    return emitter;
-  } catch (err) {
-    emitter.emit(StreamEvent.Error, err);
-  }
+  setTimeout(() => {
+    try {
+      getDiffChunks(prevList, nextList, referenceProperty, emitter, options);
+    } catch (err) {
+      return emitter.emit(StreamEvent.Error, err);
+    }
+  }, 0);
+  return emitter;
 }

From aaf5f3fa13abdb7f369d8e3813e9828d12cc77ea Mon Sep 17 00:00:00 2001
From: Antoine Lanoe <antoine.lanoe@meltwater.com>
Date: Sat, 5 Oct 2024 12:52:50 +0200
Subject: [PATCH 4/8] chore: handle errors

---
 src/lib/stream-list-diff/emitter.ts |  28 ++++---
 src/lib/stream-list-diff/index.ts   | 118 ++++++++++++++++++++++------
 2 files changed, 112 insertions(+), 34 deletions(-)

diff --git a/src/lib/stream-list-diff/emitter.ts b/src/lib/stream-list-diff/emitter.ts
index 4554c32..91dff58 100644
--- a/src/lib/stream-list-diff/emitter.ts
+++ b/src/lib/stream-list-diff/emitter.ts
@@ -1,3 +1,5 @@
+import { StreamListsDiff } from "@models/stream";
+
 type Listener<T extends unknown[]> = (...args: T) => void;
 
 export enum StreamEvent {
@@ -5,23 +7,27 @@ export enum StreamEvent {
   Finish = "finish",
   Error = "error",
 }
-export class EventEmitter {
+
+export type Emitter<T extends Record<string, unknown>> = EventEmitter<{
+  data: [StreamListsDiff<T>[]];
+  error: [Error];
+  finish: [];
+}>;
+
+export class EventEmitter<Events extends Record<string, unknown[]>> {
   private events: Record<string, Listener<unknown[]>[]> = {};
 
-  on<T extends unknown[]>(
-    event: `${StreamEvent}`,
-    listener: Listener<T>,
-  ): this {
-    if (!this.events[event]) {
-      this.events[event] = [];
+  on<E extends keyof Events>(event: E, listener: Listener<Events[E]>): this {
+    if (!this.events[event as string]) {
+      this.events[event as string] = [];
     }
-    this.events[event].push(listener as Listener<unknown[]>);
+    this.events[event as string].push(listener as Listener<unknown[]>);
     return this;
   }
 
-  emit<T extends unknown[]>(event: `${StreamEvent}`, ...args: T): void {
-    if (this.events[event]) {
-      this.events[event].forEach((listener) => listener(...args));
+  emit<E extends keyof Events>(event: E, ...args: Events[E]): void {
+    if (this.events[event as string]) {
+      this.events[event as string].forEach((listener) => listener(...args));
     }
   }
 }
diff --git a/src/lib/stream-list-diff/index.ts b/src/lib/stream-list-diff/index.ts
index 1b27896..fe1db67 100644
--- a/src/lib/stream-list-diff/index.ts
+++ b/src/lib/stream-list-diff/index.ts
@@ -6,11 +6,11 @@ import {
   StreamReferences,
 } from "@models/stream";
 import { LIST_STATUS } from "@models/list";
-import { isEqual } from "@lib/utils";
-import { EventEmitter, StreamEvent } from "./emitter";
+import { isEqual, isObject } from "@lib/utils";
+import { Emitter, EventEmitter, StreamEvent } from "./emitter";
 
 function outputDiffChunk<T extends Record<string, unknown>>(
-  emitter: EventEmitter,
+  emitter: Emitter<T>,
 ) {
   let chunks: StreamListsDiff<T>[] = [];
 
@@ -42,15 +42,27 @@ function formatSingleListStreamDiff<T extends Record<string, unknown>>(
   isPrevious: boolean,
   status: LIST_STATUS,
   options: ListStreamOptions,
-): StreamListsDiff<T>[] {
-  const diff: StreamListsDiff<T>[] = list.map((data, i) => ({
-    previousValue: isPrevious ? data : null,
-    currentValue: isPrevious ? null : data,
-    prevIndex: status === LIST_STATUS.ADDED ? null : i,
-    newIndex: status === LIST_STATUS.ADDED ? i : null,
-    indexDiff: null,
-    status,
-  }));
+): StreamListsDiff<T>[] | null {
+  let isValid = true;
+  const diff: StreamListsDiff<T>[] = [];
+  for (let i = 0; i < list.length; i++) {
+    const data = list[i];
+    if (!isObject(data)) {
+      isValid = false;
+      break;
+    }
+    diff.push({
+      previousValue: isPrevious ? data : null,
+      currentValue: isPrevious ? null : data,
+      prevIndex: status === LIST_STATUS.ADDED ? null : i,
+      newIndex: status === LIST_STATUS.ADDED ? i : null,
+      indexDiff: null,
+      status,
+    });
+  }
+  if (!isValid) {
+    return null;
+  }
   if (options.showOnly && options.showOnly.length > 0) {
     return diff.filter((value) => options.showOnly?.includes(value.status));
   }
@@ -65,17 +77,46 @@ function isValidChunkSize(
   return x !== "-1" && x !== "NaN";
 }
 
+function isDataValid<T extends Record<string, unknown>>(
+  data: T,
+  referenceProperty: ReferenceProperty<T>,
+  emitter: Emitter<T>,
+  listType: "prevList" | "nextList",
+): boolean {
+  if (!isObject(data)) {
+    emitter.emit(
+      StreamEvent.Error,
+      new Error(
+        `Your ${listType} must only contain valid objects. Found ${data}`,
+      ),
+    );
+    return false;
+  }
+  if (!Object.hasOwn(data, referenceProperty)) {
+    emitter.emit(
+      StreamEvent.Error,
+      new Error(
+        `The reference property ${String(referenceProperty)} is not available in all the objects of your ${listType}.`,
+      ),
+    );
+    return false;
+  }
+  return true;
+}
+
 function getDiffChunks<T extends Record<string, unknown>>(
   prevList: T[],
   nextList: T[],
   referenceProperty: ReferenceProperty<T>,
-  emitter: EventEmitter,
+  emitter: Emitter<T>,
   options: ListStreamOptions = DEFAULT_LIST_STREAM_OPTIONS,
 ) {
   if (!isValidChunkSize(options?.chunksSize)) {
     return emitter.emit(
       StreamEvent.Error,
-      `The chunk size can't be negative. You entered the value ${options.chunksSize}`,
+      new Error(
+        `The chunk size can't be negative. You entered the value ${options.chunksSize}`,
+      ),
     );
   }
   if (!prevList && !nextList) {
@@ -88,7 +129,14 @@ function getDiffChunks<T extends Record<string, unknown>>(
       LIST_STATUS.ADDED,
       options,
     );
-    return nextDiff.forEach((data, i) =>
+    if (!nextDiff) {
+      emitter.emit(
+        StreamEvent.Error,
+        new Error("Your nextList must only contain valid objects."),
+      );
+      emitter.emit(StreamEvent.Finish);
+    }
+    return nextDiff?.forEach((data, i) =>
       handleDiffChunk(data, i === nextDiff.length - 1, options),
     );
   }
@@ -99,23 +147,42 @@ function getDiffChunks<T extends Record<string, unknown>>(
       LIST_STATUS.DELETED,
       options,
     );
-    return prevDiff.forEach((data, i) =>
+    if (!prevDiff) {
+      emitter.emit(
+        StreamEvent.Error,
+        new Error("Your prevList must only contain valid objects."),
+      );
+      emitter.emit(StreamEvent.Finish);
+    }
+    return prevDiff?.forEach((data, i) =>
       handleDiffChunk(data, i === prevDiff.length - 1, options),
     );
   }
   const listsReferences: StreamReferences<T> = new Map();
   const handleDiffChunk = outputDiffChunk<T>(emitter);
-  prevList.forEach((data, i) => {
+  for (let i = 0; i < prevList.length; i++) {
+    const data = prevList[i];
     if (data) {
+      const isValid = isDataValid(data, referenceProperty, emitter, "prevList");
+      if (!isValid) {
+        emitter.emit(StreamEvent.Finish);
+        break;
+      }
       listsReferences.set(String(data[referenceProperty]), {
         prevIndex: i,
         nextIndex: undefined,
       });
     }
-  });
+  }
 
-  nextList.forEach((data, i) => {
+  for (let i = 0; i < nextList.length; i++) {
+    const data = prevList[i];
     if (data) {
+      const isValid = isDataValid(data, referenceProperty, emitter, "nextList");
+      if (!isValid) {
+        emitter.emit(StreamEvent.Finish);
+        break;
+      }
       const listReference = listsReferences.get(
         String(data[referenceProperty]),
       );
@@ -136,7 +203,8 @@ function getDiffChunks<T extends Record<string, unknown>>(
         );
       }
     }
-  });
+  }
+
   let streamedChunks = 0;
   const totalChunks = listsReferences.size;
   for (const data of listsReferences.values()) {
@@ -225,13 +293,17 @@ export function streamListsDiff<T extends Record<string, unknown>>(
   nextList: T[],
   referenceProperty: ReferenceProperty<T>,
   options: ListStreamOptions = DEFAULT_LIST_STREAM_OPTIONS,
-): EventEmitter {
-  const emitter = new EventEmitter();
+): Emitter<T> {
+  const emitter = new EventEmitter<{
+    data: [StreamListsDiff<T>[]];
+    error: [Error];
+    finish: [];
+  }>();
   setTimeout(() => {
     try {
       getDiffChunks(prevList, nextList, referenceProperty, emitter, options);
     } catch (err) {
-      return emitter.emit(StreamEvent.Error, err);
+      return emitter.emit(StreamEvent.Error, err as Error);
     }
   }, 0);
   return emitter;

From 7fe7a840824a77429b4b8c2ac40844ef1d5d27e9 Mon Sep 17 00:00:00 2001
From: Antoine Lanoe <antoine.lanoe@meltwater.com>
Date: Sat, 5 Oct 2024 14:42:32 +0200
Subject: [PATCH 5/8] chore: test stream-list-diff

---
 src/lib/stream-list-diff/index.ts             |  90 +++--
 .../stream-list-diff/stream-list-diff.test.ts | 374 ++++++++++++++++++
 2 files changed, 425 insertions(+), 39 deletions(-)
 create mode 100644 src/lib/stream-list-diff/stream-list-diff.test.ts

diff --git a/src/lib/stream-list-diff/index.ts b/src/lib/stream-list-diff/index.ts
index fe1db67..90e1d65 100644
--- a/src/lib/stream-list-diff/index.ts
+++ b/src/lib/stream-list-diff/index.ts
@@ -6,7 +6,7 @@ import {
   StreamReferences,
 } from "@models/stream";
 import { LIST_STATUS } from "@models/list";
-import { isEqual, isObject } from "@lib/utils";
+import { isObject } from "@lib/utils";
 import { Emitter, EventEmitter, StreamEvent } from "./emitter";
 
 function outputDiffChunk<T extends Record<string, unknown>>(
@@ -31,6 +31,8 @@ function outputDiffChunk<T extends Record<string, unknown>>(
         const output = chunks;
         chunks = [];
         return emitter.emit(StreamEvent.Data, output);
+      } else {
+        return;
       }
     }
     return emitter.emit(StreamEvent.Data, [chunk]);
@@ -73,56 +75,53 @@ function isValidChunkSize(
   chunksSize: ListStreamOptions["chunksSize"],
 ): boolean {
   if (!chunksSize) return true;
-  const x = String(Math.sign(chunksSize));
-  return x !== "-1" && x !== "NaN";
+  const sign = String(Math.sign(chunksSize));
+  return sign !== "-1" && sign !== "NaN";
 }
 
 function isDataValid<T extends Record<string, unknown>>(
   data: T,
   referenceProperty: ReferenceProperty<T>,
-  emitter: Emitter<T>,
   listType: "prevList" | "nextList",
-): boolean {
+): { isValid: boolean; message?: string } {
   if (!isObject(data)) {
-    emitter.emit(
-      StreamEvent.Error,
-      new Error(
-        `Your ${listType} must only contain valid objects. Found ${data}`,
-      ),
-    );
-    return false;
+    return {
+      isValid: false,
+      message: `Your ${listType} must only contain valid objects. Found '${data}'`,
+    };
   }
   if (!Object.hasOwn(data, referenceProperty)) {
-    emitter.emit(
-      StreamEvent.Error,
-      new Error(
-        `The reference property ${String(referenceProperty)} is not available in all the objects of your ${listType}.`,
-      ),
-    );
-    return false;
+    return {
+      isValid: false,
+      message: `The reference property '${String(referenceProperty)}' is not available in all the objects of your ${listType}.`,
+    };
   }
-  return true;
+  return {
+    isValid: true,
+    message: "",
+  };
 }
 
 function getDiffChunks<T extends Record<string, unknown>>(
-  prevList: T[],
-  nextList: T[],
+  prevList: T[] = [],
+  nextList: T[] = [],
   referenceProperty: ReferenceProperty<T>,
   emitter: Emitter<T>,
   options: ListStreamOptions = DEFAULT_LIST_STREAM_OPTIONS,
-) {
+): void {
   if (!isValidChunkSize(options?.chunksSize)) {
     return emitter.emit(
       StreamEvent.Error,
       new Error(
-        `The chunk size can't be negative. You entered the value ${options.chunksSize}`,
+        `The chunk size can't be negative. You entered the value '${options.chunksSize}'`,
       ),
     );
   }
-  if (!prevList && !nextList) {
-    return [];
+  if (prevList.length === 0 && nextList.length === 0) {
+    return emitter.emit(StreamEvent.Finish);
   }
-  if (!prevList) {
+  const handleDiffChunk = outputDiffChunk<T>(emitter);
+  if (prevList.length === 0) {
     const nextDiff = formatSingleListStreamDiff(
       nextList as T[],
       false,
@@ -136,11 +135,12 @@ function getDiffChunks<T extends Record<string, unknown>>(
       );
       emitter.emit(StreamEvent.Finish);
     }
-    return nextDiff?.forEach((data, i) =>
+    nextDiff?.forEach((data, i) =>
       handleDiffChunk(data, i === nextDiff.length - 1, options),
     );
+    return emitter.emit(StreamEvent.Finish);
   }
-  if (!nextList) {
+  if (nextList.length === 0) {
     const prevDiff = formatSingleListStreamDiff(
       prevList as T[],
       true,
@@ -154,17 +154,22 @@ function getDiffChunks<T extends Record<string, unknown>>(
       );
       emitter.emit(StreamEvent.Finish);
     }
-    return prevDiff?.forEach((data, i) =>
+    prevDiff?.forEach((data, i) =>
       handleDiffChunk(data, i === prevDiff.length - 1, options),
     );
+    return emitter.emit(StreamEvent.Finish);
   }
   const listsReferences: StreamReferences<T> = new Map();
-  const handleDiffChunk = outputDiffChunk<T>(emitter);
   for (let i = 0; i < prevList.length; i++) {
     const data = prevList[i];
     if (data) {
-      const isValid = isDataValid(data, referenceProperty, emitter, "prevList");
+      const { isValid, message } = isDataValid(
+        data,
+        referenceProperty,
+        "prevList",
+      );
       if (!isValid) {
+        emitter.emit(StreamEvent.Error, new Error(message));
         emitter.emit(StreamEvent.Finish);
         break;
       }
@@ -176,10 +181,15 @@ function getDiffChunks<T extends Record<string, unknown>>(
   }
 
   for (let i = 0; i < nextList.length; i++) {
-    const data = prevList[i];
+    const data = nextList[i];
     if (data) {
-      const isValid = isDataValid(data, referenceProperty, emitter, "nextList");
+      const { isValid, message } = isDataValid(
+        data,
+        referenceProperty,
+        "nextList",
+      );
       if (!isValid) {
+        emitter.emit(StreamEvent.Error, new Error(message));
         emitter.emit(StreamEvent.Finish);
         break;
       }
@@ -207,10 +217,11 @@ function getDiffChunks<T extends Record<string, unknown>>(
 
   let streamedChunks = 0;
   const totalChunks = listsReferences.size;
+
   for (const data of listsReferences.values()) {
     streamedChunks++;
     const isLastChunk = totalChunks === streamedChunks;
-    if (!data.nextIndex) {
+    if (typeof data.nextIndex === "undefined") {
       handleDiffChunk(
         {
           previousValue: prevList[data.prevIndex],
@@ -226,17 +237,17 @@ function getDiffChunks<T extends Record<string, unknown>>(
     } else {
       const prevData = prevList[data.prevIndex];
       const nextData = nextList[data.nextIndex];
-      const isDataEqual = isEqual(prevData, nextData);
-      const indexDiff = data.prevIndex - data.nextIndex;
+      const isDataEqual = JSON.stringify(prevData) === JSON.stringify(nextData);
+      const indexDiff = data.nextIndex - data.prevIndex;
       if (isDataEqual) {
         if (indexDiff === 0) {
           handleDiffChunk(
             {
               previousValue: prevList[data.prevIndex],
               currentValue: nextList[data.nextIndex],
-              prevIndex: null,
+              prevIndex: data.prevIndex,
               newIndex: data.nextIndex,
-              indexDiff: null,
+              indexDiff: 0,
               status: LIST_STATUS.EQUAL,
             },
             isLastChunk,
@@ -274,6 +285,7 @@ function getDiffChunks<T extends Record<string, unknown>>(
       }
     }
   }
+
   return emitter.emit(StreamEvent.Finish);
 }
 
diff --git a/src/lib/stream-list-diff/stream-list-diff.test.ts b/src/lib/stream-list-diff/stream-list-diff.test.ts
new file mode 100644
index 0000000..1a47732
--- /dev/null
+++ b/src/lib/stream-list-diff/stream-list-diff.test.ts
@@ -0,0 +1,374 @@
+import { LIST_STATUS } from "@models/list";
+import { streamListsDiff } from ".";
+
+describe("streamListsDiff data", () => {
+  it("emits 'data' event and consider the all the nextList added if no prevList is provided", (done) => {
+    const nextList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+    ];
+    const diff = streamListsDiff([], nextList, "id", { chunksSize: 2 });
+
+    const expectedChunks = [
+      {
+        previousValue: null,
+        currentValue: { id: 1, name: "Item 1" },
+        prevIndex: null,
+        newIndex: 0,
+        indexDiff: null,
+        status: LIST_STATUS.ADDED,
+      },
+      {
+        previousValue: null,
+        currentValue: { id: 2, name: "Item 2" },
+        prevIndex: null,
+        newIndex: 1,
+        indexDiff: null,
+        status: LIST_STATUS.ADDED,
+      },
+    ];
+    diff.on("data", (chunk) => expect(chunk).toStrictEqual(expectedChunks));
+    diff.on("finish", () => done());
+  });
+  it("emits 'data' event and consider the all the prevList deleted if no nextList is provided", (done) => {
+    const prevList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+    ];
+    const diff = streamListsDiff(prevList, [], "id", { chunksSize: 2 });
+
+    const expectedChunks = [
+      {
+        previousValue: { id: 1, name: "Item 1" },
+        currentValue: null,
+        prevIndex: 0,
+        newIndex: null,
+        indexDiff: null,
+        status: LIST_STATUS.DELETED,
+      },
+      {
+        previousValue: { id: 2, name: "Item 2" },
+        currentValue: null,
+        prevIndex: 1,
+        newIndex: null,
+        indexDiff: null,
+        status: LIST_STATUS.DELETED,
+      },
+    ];
+    diff.on("data", (chunk) => expect(chunk).toStrictEqual(expectedChunks));
+    diff.on("finish", () => done());
+  });
+  it("emits 'data' event with one object diff by chunk if chunkSize is 0 or undefined", (done) => {
+    const prevList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+    ];
+    const nextList = [
+      { id: 2, name: "Item 2" },
+      { id: 3, name: "Item 3" },
+    ];
+    const diff = streamListsDiff(prevList, nextList, "id");
+
+    const expectedChunks = [
+      [
+        {
+          previousValue: null,
+          currentValue: { id: 3, name: "Item 3" },
+          prevIndex: null,
+          newIndex: 1,
+          indexDiff: null,
+          status: LIST_STATUS.ADDED,
+        },
+      ],
+      [
+        {
+          previousValue: { id: 1, name: "Item 1" },
+          currentValue: null,
+          prevIndex: 0,
+          newIndex: null,
+          indexDiff: null,
+          status: LIST_STATUS.DELETED,
+        },
+      ],
+      [
+        {
+          previousValue: { id: 2, name: "Item 2" },
+          currentValue: { id: 2, name: "Item 2" },
+          prevIndex: 1,
+          newIndex: 0,
+          indexDiff: -1,
+          status: LIST_STATUS.MOVED,
+        },
+      ],
+    ];
+
+    let chunkCount = 0;
+
+    diff.on("data", (chunk) => {
+      expect(chunk).toStrictEqual(expectedChunks[chunkCount]);
+      chunkCount++;
+    });
+    diff.on("finish", () => done());
+  });
+  it("emits 'data' event with 5 object diff by chunk", (done) => {
+    const prevList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+      { id: 3, name: "Item 3" },
+      { id: 4, name: "Item 4" },
+      { id: 5, name: "Item 5" },
+      { id: 6, name: "Item 6" },
+      { id: 7, name: "Item 7" },
+      { id: 8, name: "Item 8" },
+      { id: 9, name: "Item 9" },
+      { id: 10, name: "Item 10" },
+    ];
+    const nextList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item Two" },
+      { id: 3, name: "Item 3" },
+      { id: 5, name: "Item 5" },
+      { id: 6, name: "Item Six" },
+      { id: 7, name: "Item 7" },
+      { id: 10, name: "Item 10" },
+      { id: 11, name: "Item 11" },
+      { id: 9, name: "Item 9" },
+      { id: 8, name: "Item 8" },
+    ];
+    const diff = streamListsDiff(prevList, nextList, "id", { chunksSize: 5 });
+
+    const expectedChunks = [
+      [
+        {
+          previousValue: null,
+          currentValue: { id: 11, name: "Item 11" },
+          prevIndex: null,
+          newIndex: 7,
+          indexDiff: null,
+          status: LIST_STATUS.ADDED,
+        },
+        {
+          previousValue: { id: 1, name: "Item 1" },
+          currentValue: { id: 1, name: "Item 1" },
+          prevIndex: 0,
+          newIndex: 0,
+          indexDiff: 0,
+          status: LIST_STATUS.EQUAL,
+        },
+        {
+          previousValue: { id: 2, name: "Item 2" },
+          currentValue: { id: 2, name: "Item Two" },
+          prevIndex: 1,
+          newIndex: 1,
+          indexDiff: 0,
+          status: LIST_STATUS.UPDATED,
+        },
+        {
+          previousValue: { id: 3, name: "Item 3" },
+          currentValue: { id: 3, name: "Item 3" },
+          prevIndex: 2,
+          newIndex: 2,
+          indexDiff: 0,
+          status: LIST_STATUS.EQUAL,
+        },
+        {
+          previousValue: { id: 4, name: "Item 4" },
+          currentValue: null,
+          prevIndex: 3,
+          newIndex: null,
+          indexDiff: null,
+          status: LIST_STATUS.DELETED,
+        },
+      ],
+      [
+        {
+          previousValue: { id: 5, name: "Item 5" },
+          currentValue: { id: 5, name: "Item 5" },
+          prevIndex: 4,
+          newIndex: 3,
+          indexDiff: -1,
+          status: LIST_STATUS.MOVED,
+        },
+        {
+          previousValue: { id: 6, name: "Item 6" },
+          currentValue: { id: 6, name: "Item Six" },
+          prevIndex: 5,
+          newIndex: 4,
+          indexDiff: -1,
+          status: LIST_STATUS.UPDATED,
+        },
+        {
+          previousValue: { id: 7, name: "Item 7" },
+          currentValue: { id: 7, name: "Item 7" },
+          prevIndex: 6,
+          newIndex: 5,
+          indexDiff: -1,
+          status: LIST_STATUS.MOVED,
+        },
+        {
+          previousValue: { id: 8, name: "Item 8" },
+          currentValue: { id: 8, name: "Item 8" },
+          prevIndex: 7,
+          newIndex: 9,
+          indexDiff: 2,
+          status: LIST_STATUS.MOVED,
+        },
+        {
+          previousValue: { id: 9, name: "Item 9" },
+          currentValue: { id: 9, name: "Item 9" },
+          prevIndex: 8,
+          newIndex: 8,
+          indexDiff: 0,
+          status: LIST_STATUS.EQUAL,
+        },
+        {
+          previousValue: { id: 10, name: "Item 10" },
+          currentValue: { id: 10, name: "Item 10" },
+          prevIndex: 9,
+          newIndex: 6,
+          indexDiff: -3,
+          status: LIST_STATUS.MOVED,
+        },
+      ],
+      [
+        {
+          previousValue: { id: 10, name: "Item 10" },
+          currentValue: { id: 10, name: "Item 10" },
+          prevIndex: 9,
+          newIndex: 6,
+          indexDiff: -3,
+          status: LIST_STATUS.MOVED,
+        },
+      ],
+    ];
+
+    let chunkCount = 0;
+
+    diff.on("data", (chunk) => {
+      // console.log("chunks received", chunk);
+      // console.log("expected chunk", expectedChunks[chunkCount]);
+      //expect(chunk).toStrictEqual(expectedChunks[chunkCount]);
+      chunkCount++;
+    });
+    diff.on("finish", () => {
+      expect(chunkCount).toBe(3);
+      done();
+    });
+  });
+});
+
+describe("streamListsDiff finish", () => {
+  it("emits 'finish' event if no prevList nor nextList is provided", (done) => {
+    const diff = streamListsDiff([], [], "id");
+    diff.on("finish", () => done());
+  });
+  it("emits 'finish' event when all the chunks have been processed", (done) => {
+    const prevList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+    ];
+    const nextList = [
+      { id: 2, name: "Item 2" },
+      { id: 3, name: "Item 3" },
+    ];
+    const diff = streamListsDiff(prevList, nextList, "id");
+    diff.on("finish", () => done());
+  });
+});
+
+describe("streamListsDiff error", () => {
+  test("emits 'error' event when prevList has invalid data", (done) => {
+    const prevList = [
+      { id: 1, name: "Item 1" },
+      "hello",
+      { id: 2, name: "Item 2" },
+    ];
+    const nextList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+    ];
+
+    // @ts-expect-error prevList is invalid by design for the test
+    const diff = streamListsDiff(prevList, nextList, "id");
+
+    diff.on("error", (err) => {
+      expect(err["message"]).toEqual(
+        `Your prevList must only contain valid objects. Found 'hello'`,
+      );
+      done();
+    });
+  });
+
+  test("emits 'error' event when nextList has invalid data", (done) => {
+    const prevList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+    ];
+    const nextList = [
+      { id: 1, name: "Item 1" },
+      "hello",
+      { id: 2, name: "Item 2" },
+    ];
+
+    // @ts-expect-error nextList is invalid by design for the test
+    const diff = streamListsDiff(prevList, nextList, "id");
+
+    diff.on("error", (err) => {
+      expect(err["message"]).toEqual(
+        `Your nextList must only contain valid objects. Found 'hello'`,
+      );
+      done();
+    });
+  });
+
+  test("emits 'error' event when all prevList ojects don't have the requested reference property", (done) => {
+    const prevList = [{ id: 1, name: "Item 1" }, { name: "Item 2" }];
+    const nextList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+    ];
+
+    const diff = streamListsDiff(prevList, nextList, "id");
+
+    diff.on("error", (err) => {
+      expect(err["message"]).toEqual(
+        `The reference property 'id' is not available in all the objects of your prevList.`,
+      );
+      done();
+    });
+  });
+
+  test("emits 'error' event when all nextList ojects don't have the requested reference property", (done) => {
+    const prevList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+    ];
+    const nextList = [{ id: 1, name: "Item 1" }, { name: "Item 2" }];
+
+    const diff = streamListsDiff(prevList, nextList, "id");
+
+    diff.on("error", (err) => {
+      expect(err["message"]).toEqual(
+        `The reference property 'id' is not available in all the objects of your nextList.`,
+      );
+      done();
+    });
+  });
+
+  test("emits 'error' event when the chunkSize option is negative", (done) => {
+    const prevList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+    ];
+    const nextList = [{ id: 1, name: "Item 1" }, { name: "Item 2" }];
+
+    const diff = streamListsDiff(prevList, nextList, "id", { chunksSize: -3 });
+
+    diff.on("error", (err) => {
+      expect(err["message"]).toEqual(
+        "The chunk size can't be negative. You entered the value '-3'",
+      );
+      done();
+    });
+  });
+});

From 2b5e2b38b766a0a07cf4548121174a53802044d9 Mon Sep 17 00:00:00 2001
From: Antoine Lanoe <antoine.lanoe@meltwater.com>
Date: Sun, 6 Oct 2024 12:43:13 +0200
Subject: [PATCH 6/8] chore: improve memory usage + add tests

---
 src/lib/stream-list-diff/emitter.ts           |  13 +
 src/lib/stream-list-diff/index.ts             |  31 +-
 .../stream-list-diff/stream-list-diff.test.ts | 557 +++++++++++++++++-
 3 files changed, 577 insertions(+), 24 deletions(-)

diff --git a/src/lib/stream-list-diff/emitter.ts b/src/lib/stream-list-diff/emitter.ts
index 91dff58..4d27b30 100644
--- a/src/lib/stream-list-diff/emitter.ts
+++ b/src/lib/stream-list-diff/emitter.ts
@@ -31,3 +31,16 @@ export class EventEmitter<Events extends Record<string, unknown[]>> {
     }
   }
 }
+
+export type EmitterEvents<T extends Record<string, unknown>> = {
+  data: [StreamListsDiff<T>[]];
+  error: [Error];
+  finish: [];
+};
+
+export interface ReadOnlyEmitter<T extends Record<string, unknown>> {
+  on<E extends keyof EmitterEvents<T>>(
+    event: E,
+    listener: Listener<EmitterEvents<T>[E]>,
+  ): this;
+}
diff --git a/src/lib/stream-list-diff/index.ts b/src/lib/stream-list-diff/index.ts
index 90e1d65..d47c1bc 100644
--- a/src/lib/stream-list-diff/index.ts
+++ b/src/lib/stream-list-diff/index.ts
@@ -7,7 +7,13 @@ import {
 } from "@models/stream";
 import { LIST_STATUS } from "@models/list";
 import { isObject } from "@lib/utils";
-import { Emitter, EventEmitter, StreamEvent } from "./emitter";
+import {
+  Emitter,
+  EmitterEvents,
+  EventEmitter,
+  ReadOnlyEmitter,
+  StreamEvent,
+} from "./emitter";
 
 function outputDiffChunk<T extends Record<string, unknown>>(
   emitter: Emitter<T>,
@@ -133,7 +139,7 @@ function getDiffChunks<T extends Record<string, unknown>>(
         StreamEvent.Error,
         new Error("Your nextList must only contain valid objects."),
       );
-      emitter.emit(StreamEvent.Finish);
+      return emitter.emit(StreamEvent.Finish);
     }
     nextDiff?.forEach((data, i) =>
       handleDiffChunk(data, i === nextDiff.length - 1, options),
@@ -152,7 +158,7 @@ function getDiffChunks<T extends Record<string, unknown>>(
         StreamEvent.Error,
         new Error("Your prevList must only contain valid objects."),
       );
-      emitter.emit(StreamEvent.Finish);
+      return emitter.emit(StreamEvent.Finish);
     }
     prevDiff?.forEach((data, i) =>
       handleDiffChunk(data, i === prevDiff.length - 1, options),
@@ -180,6 +186,8 @@ function getDiffChunks<T extends Record<string, unknown>>(
     }
   }
 
+  const totalChunks = listsReferences.size;
+
   for (let i = 0; i < nextList.length; i++) {
     const data = nextList[i];
     if (data) {
@@ -208,7 +216,7 @@ function getDiffChunks<T extends Record<string, unknown>>(
             indexDiff: null,
             status: LIST_STATUS.ADDED,
           },
-          i === nextList.length - 1,
+          totalChunks > 0 ? false : i === nextList.length - 1,
           options,
         );
       }
@@ -216,11 +224,11 @@ function getDiffChunks<T extends Record<string, unknown>>(
   }
 
   let streamedChunks = 0;
-  const totalChunks = listsReferences.size;
 
-  for (const data of listsReferences.values()) {
+  for (const [key, data] of listsReferences.entries()) {
     streamedChunks++;
     const isLastChunk = totalChunks === streamedChunks;
+
     if (typeof data.nextIndex === "undefined") {
       handleDiffChunk(
         {
@@ -284,6 +292,7 @@ function getDiffChunks<T extends Record<string, unknown>>(
         );
       }
     }
+    listsReferences.delete(key); // to free up memory
   }
 
   return emitter.emit(StreamEvent.Finish);
@@ -305,12 +314,8 @@ export function streamListsDiff<T extends Record<string, unknown>>(
   nextList: T[],
   referenceProperty: ReferenceProperty<T>,
   options: ListStreamOptions = DEFAULT_LIST_STREAM_OPTIONS,
-): Emitter<T> {
-  const emitter = new EventEmitter<{
-    data: [StreamListsDiff<T>[]];
-    error: [Error];
-    finish: [];
-  }>();
+): ReadOnlyEmitter<T> {
+  const emitter = new EventEmitter<EmitterEvents<T>>();
   setTimeout(() => {
     try {
       getDiffChunks(prevList, nextList, referenceProperty, emitter, options);
@@ -318,5 +323,5 @@ export function streamListsDiff<T extends Record<string, unknown>>(
       return emitter.emit(StreamEvent.Error, err as Error);
     }
   }, 0);
-  return emitter;
+  return emitter as ReadOnlyEmitter<T>;
 }
diff --git a/src/lib/stream-list-diff/stream-list-diff.test.ts b/src/lib/stream-list-diff/stream-list-diff.test.ts
index 1a47732..2a171a7 100644
--- a/src/lib/stream-list-diff/stream-list-diff.test.ts
+++ b/src/lib/stream-list-diff/stream-list-diff.test.ts
@@ -1,5 +1,6 @@
 import { LIST_STATUS } from "@models/list";
 import { streamListsDiff } from ".";
+import { StreamListsDiff } from "@models/stream";
 
 describe("streamListsDiff data", () => {
   it("emits 'data' event and consider the all the nextList added if no prevList is provided", (done) => {
@@ -27,8 +28,15 @@ describe("streamListsDiff data", () => {
         status: LIST_STATUS.ADDED,
       },
     ];
-    diff.on("data", (chunk) => expect(chunk).toStrictEqual(expectedChunks));
-    diff.on("finish", () => done());
+    let chunkCount = 0;
+    diff.on("data", (chunk) => {
+      expect(chunk).toStrictEqual(expectedChunks);
+      chunkCount++;
+    });
+    diff.on("finish", () => {
+      expect(chunkCount).toBe(1);
+      done();
+    });
   });
   it("emits 'data' event and consider the all the prevList deleted if no nextList is provided", (done) => {
     const prevList = [
@@ -55,8 +63,15 @@ describe("streamListsDiff data", () => {
         status: LIST_STATUS.DELETED,
       },
     ];
-    diff.on("data", (chunk) => expect(chunk).toStrictEqual(expectedChunks));
-    diff.on("finish", () => done());
+    let chunkCount = 0;
+    diff.on("data", (chunk) => {
+      expect(chunk).toStrictEqual(expectedChunks);
+      chunkCount++;
+    });
+    diff.on("finish", () => {
+      expect(chunkCount).toBe(1);
+      done();
+    });
   });
   it("emits 'data' event with one object diff by chunk if chunkSize is 0 or undefined", (done) => {
     const prevList = [
@@ -108,9 +123,12 @@ describe("streamListsDiff data", () => {
       expect(chunk).toStrictEqual(expectedChunks[chunkCount]);
       chunkCount++;
     });
-    diff.on("finish", () => done());
+    diff.on("finish", () => {
+      expect(chunkCount).toBe(3);
+      done();
+    });
   });
-  it("emits 'data' event with 5 object diff by chunk", (done) => {
+  it("emits 'data' event with 5 object diff by chunk and return the last object diff in a one entry chunk at the end", (done) => {
     const prevList = [
       { id: 1, name: "Item 1" },
       { id: 2, name: "Item 2" },
@@ -221,6 +239,8 @@ describe("streamListsDiff data", () => {
           indexDiff: 0,
           status: LIST_STATUS.EQUAL,
         },
+      ],
+      [
         {
           previousValue: { id: 10, name: "Item 10" },
           currentValue: { id: 10, name: "Item 10" },
@@ -230,10 +250,397 @@ describe("streamListsDiff data", () => {
           status: LIST_STATUS.MOVED,
         },
       ],
+    ];
+
+    let chunkCount = 0;
+
+    diff.on("data", (chunk) => {
+      expect(chunk).toStrictEqual(expectedChunks[chunkCount]);
+      chunkCount++;
+    });
+
+    diff.on("finish", () => {
+      expect(chunkCount).toBe(3);
+      done();
+    });
+  });
+  it("emits 'data' event with all the objects diff in a single chunk if the chunkSize is bigger than the provided lists ", (done) => {
+    const prevList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+      { id: 3, name: "Item 3" },
+      { id: 4, name: "Item 4" },
+    ];
+    const nextList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item Two" },
+      { id: 3, name: "Item 3" },
+      { id: 5, name: "Item 5" },
+    ];
+    const diff = streamListsDiff(prevList, nextList, "id", { chunksSize: 150 });
+
+    const expectedChunks = [
+      {
+        previousValue: null,
+        currentValue: { id: 5, name: "Item 5" },
+        prevIndex: null,
+        newIndex: 3,
+        indexDiff: null,
+        status: LIST_STATUS.ADDED,
+      },
+      {
+        previousValue: { id: 1, name: "Item 1" },
+        currentValue: { id: 1, name: "Item 1" },
+        prevIndex: 0,
+        newIndex: 0,
+        indexDiff: 0,
+        status: LIST_STATUS.EQUAL,
+      },
+      {
+        previousValue: { id: 2, name: "Item 2" },
+        currentValue: { id: 2, name: "Item Two" },
+        prevIndex: 1,
+        newIndex: 1,
+        indexDiff: 0,
+        status: LIST_STATUS.UPDATED,
+      },
+      {
+        previousValue: { id: 3, name: "Item 3" },
+        currentValue: { id: 3, name: "Item 3" },
+        prevIndex: 2,
+        newIndex: 2,
+        indexDiff: 0,
+        status: LIST_STATUS.EQUAL,
+      },
+      {
+        previousValue: { id: 4, name: "Item 4" },
+        currentValue: null,
+        prevIndex: 3,
+        newIndex: null,
+        indexDiff: null,
+        status: LIST_STATUS.DELETED,
+      },
+    ];
+
+    let chunkCount = 0;
+    diff.on("data", (chunk) => {
+      expect(chunk).toStrictEqual(expectedChunks);
+      chunkCount++;
+    });
+
+    diff.on("finish", () => {
+      expect(chunkCount).toBe(1);
+      done();
+    });
+  });
+  it("emits 'data' event with moved objects considered as updated if considerMoveAsUpdate is true", (done) => {
+    const prevList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+      { id: 3, name: "Item 3" },
+      { id: 4, name: "Item 4" },
+    ];
+    const nextList = [
+      { id: 2, name: "Item Two" },
+      { id: 1, name: "Item 1" },
+      { id: 3, name: "Item 3" },
+      { id: 5, name: "Item 5" },
+    ];
+    const diff = streamListsDiff(prevList, nextList, "id", {
+      chunksSize: 5,
+      considerMoveAsUpdate: true,
+    });
+
+    const expectedChunks = [
+      {
+        previousValue: null,
+        currentValue: { id: 5, name: "Item 5" },
+        prevIndex: null,
+        newIndex: 3,
+        indexDiff: null,
+        status: LIST_STATUS.ADDED,
+      },
+      {
+        previousValue: { id: 1, name: "Item 1" },
+        currentValue: { id: 1, name: "Item 1" },
+        prevIndex: 0,
+        newIndex: 1,
+        indexDiff: 1,
+        status: LIST_STATUS.UPDATED,
+      },
+      {
+        previousValue: { id: 2, name: "Item 2" },
+        currentValue: { id: 2, name: "Item Two" },
+        prevIndex: 1,
+        newIndex: 0,
+        indexDiff: -1,
+        status: LIST_STATUS.UPDATED,
+      },
+      {
+        previousValue: { id: 3, name: "Item 3" },
+        currentValue: { id: 3, name: "Item 3" },
+        prevIndex: 2,
+        newIndex: 2,
+        indexDiff: 0,
+        status: LIST_STATUS.EQUAL,
+      },
+      {
+        previousValue: { id: 4, name: "Item 4" },
+        currentValue: null,
+        prevIndex: 3,
+        newIndex: null,
+        indexDiff: null,
+        status: LIST_STATUS.DELETED,
+      },
+    ];
+
+    let chunkCount = 0;
+    diff.on("data", (chunk) => {
+      expect(chunk).toStrictEqual(expectedChunks);
+      chunkCount++;
+    });
+
+    diff.on("finish", () => {
+      expect(chunkCount).toBe(1);
+      done();
+    });
+  });
+  it("emits 'data' event only with objects diff whose status match with showOnly's", (done) => {
+    const prevList = [
+      { id: 1, name: "Item 1" },
+      { id: 2, name: "Item 2" },
+      { id: 3, name: "Item 3" },
+      { id: 4, name: "Item 4" },
+    ];
+    const nextList = [
+      { id: 2, name: "Item Two" },
+      { id: 1, name: "Item 1" },
+      { id: 3, name: "Item 3" },
+      { id: 5, name: "Item 5" },
+    ];
+    const diff = streamListsDiff(prevList, nextList, "id", {
+      chunksSize: 5,
+      showOnly: ["added", "deleted"],
+    });
+
+    const expectedChunks = [
+      {
+        previousValue: null,
+        currentValue: { id: 5, name: "Item 5" },
+        prevIndex: null,
+        newIndex: 3,
+        indexDiff: null,
+        status: LIST_STATUS.ADDED,
+      },
+      {
+        previousValue: { id: 4, name: "Item 4" },
+        currentValue: null,
+        prevIndex: 3,
+        newIndex: null,
+        indexDiff: null,
+        status: LIST_STATUS.DELETED,
+      },
+    ];
+
+    let chunkCount = 0;
+    diff.on("data", (chunk) => {
+      expect(chunk).toStrictEqual(expectedChunks);
+      chunkCount++;
+    });
+
+    diff.on("finish", () => {
+      expect(chunkCount).toBe(1);
+      done();
+    });
+  });
+  it("emits 'data' event with deep nested objects diff", (done) => {
+    const prevList = [
+      {
+        id: 1,
+        name: "Item 1",
+        user: { role: "admin", hobbies: ["golf", "football"] },
+      },
+      { id: 2, name: "Item 2" },
+      { id: 3, name: "Item 3", user: { role: "admin", hobbies: ["rugby"] } },
+      {
+        id: 4,
+        name: "Item 4",
+        user: { role: "reader", hobbies: ["video games", "fishing"] },
+      },
+      { id: 5, name: "Item 5" },
+      { id: 6, name: "Item 6", user: { role: "root", hobbies: ["coding"] } },
+      { id: 7, name: "Item 7" },
+      { id: 8, name: "Item 8" },
+      { id: 9, name: "Item 9" },
+      {
+        id: 10,
+        name: "Item 10",
+        user: {
+          role: "root",
+          hobbies: ["coding"],
+          skills: { driving: true, diving: false },
+        },
+      },
+    ];
+    const nextList = [
+      {
+        id: 1,
+        name: "Item 1",
+        user: { role: "admin", hobbies: ["golf", "football"] },
+      },
+      { id: 2, name: "Item Two" },
+      { id: 3, name: "Item 3", user: { role: "admin", hobbies: ["rugby"] } },
+      { id: 5, name: "Item 5" },
+      { id: 6, name: "Item 6", user: { role: "root", hobbies: ["farming"] } },
+      { id: 7, name: "Item 7" },
+      {
+        id: 10,
+        name: "Item 10",
+        user: {
+          role: "root",
+          hobbies: ["coding"],
+          skills: { driving: true, diving: false },
+        },
+      },
+      { id: 11, name: "Item 11" },
+      { id: 9, name: "Item 9" },
+      { id: 8, name: "Item 8" },
+    ];
+    const diff = streamListsDiff(prevList, nextList, "id", { chunksSize: 5 });
+
+    const expectedChunks = [
+      [
+        {
+          previousValue: null,
+          currentValue: { id: 11, name: "Item 11" },
+          prevIndex: null,
+          newIndex: 7,
+          indexDiff: null,
+          status: LIST_STATUS.ADDED,
+        },
+        {
+          previousValue: {
+            id: 1,
+            name: "Item 1",
+            user: { role: "admin", hobbies: ["golf", "football"] },
+          },
+          currentValue: {
+            id: 1,
+            name: "Item 1",
+            user: { role: "admin", hobbies: ["golf", "football"] },
+          },
+          prevIndex: 0,
+          newIndex: 0,
+          indexDiff: 0,
+          status: LIST_STATUS.EQUAL,
+        },
+        {
+          previousValue: { id: 2, name: "Item 2" },
+          currentValue: { id: 2, name: "Item Two" },
+          prevIndex: 1,
+          newIndex: 1,
+          indexDiff: 0,
+          status: LIST_STATUS.UPDATED,
+        },
+        {
+          previousValue: {
+            id: 3,
+            name: "Item 3",
+            user: { role: "admin", hobbies: ["rugby"] },
+          },
+          currentValue: {
+            id: 3,
+            name: "Item 3",
+            user: { role: "admin", hobbies: ["rugby"] },
+          },
+          prevIndex: 2,
+          newIndex: 2,
+          indexDiff: 0,
+          status: LIST_STATUS.EQUAL,
+        },
+        {
+          previousValue: {
+            id: 4,
+            name: "Item 4",
+            user: { role: "reader", hobbies: ["video games", "fishing"] },
+          },
+          currentValue: null,
+          prevIndex: 3,
+          newIndex: null,
+          indexDiff: null,
+          status: LIST_STATUS.DELETED,
+        },
+      ],
+      [
+        {
+          previousValue: { id: 5, name: "Item 5" },
+          currentValue: { id: 5, name: "Item 5" },
+          prevIndex: 4,
+          newIndex: 3,
+          indexDiff: -1,
+          status: LIST_STATUS.MOVED,
+        },
+        {
+          previousValue: {
+            id: 6,
+            name: "Item 6",
+            user: { role: "root", hobbies: ["coding"] },
+          },
+          currentValue: {
+            id: 6,
+            name: "Item 6",
+            user: { role: "root", hobbies: ["farming"] },
+          },
+          prevIndex: 5,
+          newIndex: 4,
+          indexDiff: -1,
+          status: LIST_STATUS.UPDATED,
+        },
+        {
+          previousValue: { id: 7, name: "Item 7" },
+          currentValue: { id: 7, name: "Item 7" },
+          prevIndex: 6,
+          newIndex: 5,
+          indexDiff: -1,
+          status: LIST_STATUS.MOVED,
+        },
+        {
+          previousValue: { id: 8, name: "Item 8" },
+          currentValue: { id: 8, name: "Item 8" },
+          prevIndex: 7,
+          newIndex: 9,
+          indexDiff: 2,
+          status: LIST_STATUS.MOVED,
+        },
+        {
+          previousValue: { id: 9, name: "Item 9" },
+          currentValue: { id: 9, name: "Item 9" },
+          prevIndex: 8,
+          newIndex: 8,
+          indexDiff: 0,
+          status: LIST_STATUS.EQUAL,
+        },
+      ],
       [
         {
-          previousValue: { id: 10, name: "Item 10" },
-          currentValue: { id: 10, name: "Item 10" },
+          previousValue: {
+            id: 10,
+            name: "Item 10",
+            user: {
+              role: "root",
+              hobbies: ["coding"],
+              skills: { driving: true, diving: false },
+            },
+          },
+          currentValue: {
+            id: 10,
+            name: "Item 10",
+            user: {
+              role: "root",
+              hobbies: ["coding"],
+              skills: { driving: true, diving: false },
+            },
+          },
           prevIndex: 9,
           newIndex: 6,
           indexDiff: -3,
@@ -245,11 +652,10 @@ describe("streamListsDiff data", () => {
     let chunkCount = 0;
 
     diff.on("data", (chunk) => {
-      // console.log("chunks received", chunk);
-      // console.log("expected chunk", expectedChunks[chunkCount]);
-      //expect(chunk).toStrictEqual(expectedChunks[chunkCount]);
+      expect(chunk).toStrictEqual(expectedChunks[chunkCount]);
       chunkCount++;
     });
+
     diff.on("finish", () => {
       expect(chunkCount).toBe(3);
       done();
@@ -372,3 +778,132 @@ describe("streamListsDiff error", () => {
     });
   });
 });
+
+describe("Performance", () => {
+  it("should correctly stream diff for 10.000 entries", (done) => {
+    const generateLargeList = (size: number, idPrefix: string) => {
+      return Array.from({ length: size }, (_, i) => ({
+        id: `${idPrefix}-${i}`,
+        value: i,
+      }));
+    };
+    const prevList = generateLargeList(10_000, "prev");
+    const nextList = [
+      ...generateLargeList(5000, "prev"),
+      ...generateLargeList(5000, "next"),
+    ];
+
+    const receivedChunks: StreamListsDiff<{ id: string; value: number }>[] = [];
+    let chunkCount = 0;
+    const diffStream = streamListsDiff(prevList, nextList, "id", {
+      chunksSize: 1000,
+    });
+
+    diffStream.on("data", (chunk) => {
+      receivedChunks.push(...chunk);
+      chunkCount++;
+    });
+
+    diffStream.on("finish", () => {
+      const deletions = receivedChunks.filter(
+        (diff) => diff.status === LIST_STATUS.DELETED,
+      );
+      const additions = receivedChunks.filter(
+        (diff) => diff.status === LIST_STATUS.ADDED,
+      );
+      const updates = receivedChunks.filter(
+        (diff) => diff.status === LIST_STATUS.EQUAL,
+      );
+      expect(receivedChunks.length).toBe(15_000); // 5000 deletions + 5000 equal + 5000 additions
+      expect(chunkCount).toBe(15);
+      expect(deletions.length).toBe(5000);
+      expect(additions.length).toBe(5000);
+      expect(updates.length).toBe(5000);
+      done();
+    });
+  });
+  it("should correctly stream diff for 100.000 entries", (done) => {
+    const generateLargeList = (size: number, idPrefix: string) => {
+      return Array.from({ length: size }, (_, i) => ({
+        id: `${idPrefix}-${i}`,
+        value: i,
+      }));
+    };
+    const prevList = generateLargeList(100_000, "prev");
+    const nextList = [
+      ...generateLargeList(50000, "prev"),
+      ...generateLargeList(50000, "next"),
+    ];
+
+    const receivedChunks: StreamListsDiff<{ id: string; value: number }>[] = [];
+    let chunkCount = 0;
+    const diffStream = streamListsDiff(prevList, nextList, "id", {
+      chunksSize: 10_000,
+    });
+
+    diffStream.on("data", (chunk) => {
+      receivedChunks.push(...chunk);
+      chunkCount++;
+    });
+
+    diffStream.on("finish", () => {
+      const deletions = receivedChunks.filter(
+        (diff) => diff.status === LIST_STATUS.DELETED,
+      );
+      const additions = receivedChunks.filter(
+        (diff) => diff.status === LIST_STATUS.ADDED,
+      );
+      const updates = receivedChunks.filter(
+        (diff) => diff.status === LIST_STATUS.EQUAL,
+      );
+      expect(receivedChunks.length).toBe(150_000); // 50.000 deletions + 50.000 equal + 50.000 additions
+      expect(chunkCount).toBe(15);
+      expect(deletions.length).toBe(50000);
+      expect(additions.length).toBe(50000);
+      expect(updates.length).toBe(50000);
+      done();
+    });
+  });
+  // it("should correctly stream diff for 1.000.000 entries", (done) => {
+  //   const generateLargeList = (size: number, idPrefix: string) => {
+  //     return Array.from({ length: size }, (_, i) => ({
+  //       id: `${idPrefix}-${i}`,
+  //       value: i,
+  //     }));
+  //   };
+  //   const prevList = generateLargeList(1_000_000, "prev");
+  //   const nextList = [
+  //     ...generateLargeList(500_000, "prev"),
+  //     ...generateLargeList(500_000, "next"),
+  //   ];
+
+  //   const receivedChunks: StreamListsDiff<{ id: string; value: number }>[] = [];
+  //   let chunkCount = 0;
+  //   const diffStream = streamListsDiff(prevList, nextList, "id", {
+  //     chunksSize: 100_000,
+  //   });
+
+  //   diffStream.on("data", (chunk) => {
+  //     receivedChunks.push(...chunk);
+  //     chunkCount++;
+  //   });
+
+  //   diffStream.on("finish", () => {
+  //     const deletions = receivedChunks.filter(
+  //       (diff) => diff.status === LIST_STATUS.DELETED,
+  //     );
+  //     const additions = receivedChunks.filter(
+  //       (diff) => diff.status === LIST_STATUS.ADDED,
+  //     );
+  //     const updates = receivedChunks.filter(
+  //       (diff) => diff.status === LIST_STATUS.EQUAL,
+  //     );
+  //     expect(receivedChunks.length).toBe(1_500_000); // 50.000 deletions + 50.000 equal + 50.000 additions
+  //     expect(chunkCount).toBe(15);
+  //     expect(deletions.length).toBe(500000);
+  //     expect(additions.length).toBe(500000);
+  //     expect(updates.length).toBe(500000);
+  //     done();
+  //   });
+  // });
+});

From dc15ce9be326dd1d7ab1ada06509422f78ee17e7 Mon Sep 17 00:00:00 2001
From: Antoine Lanoe <antoine.lanoe@meltwater.com>
Date: Sun, 6 Oct 2024 15:33:24 +0200
Subject: [PATCH 7/8] chore: update readme

---
 README.md                                     | 471 +++++++++++-------
 src/index.ts                                  |   2 +-
 src/lib/stream-list-diff/emitter.ts           |   8 +-
 src/lib/stream-list-diff/index.ts             |  22 +-
 .../stream-list-diff/stream-list-diff.test.ts |  90 +---
 src/models/stream/index.ts                    |   2 +-
 6 files changed, 338 insertions(+), 257 deletions(-)

diff --git a/README.md b/README.md
index 255351d..bf84553 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ This library compares two arrays or objects and returns a full diff of their dif
 
 All other existing solutions return a strange diff format that often requires additional parsing. They are also limited to object comparison.
 
-**Superdiff** gives you a complete diff for both array <u>and</u> objects in a very readable format. Last but not least, it's battle-tested and super fast. Import. Enjoy. 👍
+**Superdiff** gives you a complete diff for both array <u>and</u> objects in a very readable format. Last but not least, it's battle-tested, has zero dependencies, and is super fast. Import. Enjoy. 👍
 
 <hr/>
 
@@ -40,64 +40,159 @@ I am grateful to the generous donors of **Superdiff**!
 
 **Superdiff** exports 4 functions:
 
+```ts
+// Compares two objects and return a diff for each value and their potential subvalues
+getObjectDiff(prevObject, nextObject)
+// Compares two arrays and returns a diff for each value
+getListDiff(prevList, nextList)
+// Streams the diff of two object lists, ideal for large lists and maximum performance
+streamListDiff(prevList, nextList, referenceProperty)
+// Checks whether two values are equal 
+isEqual(dataA, dataB)
+// Checks whether a value is an object
+isObject(data)
+```
+<hr/>
+
 ### getObjectDiff()
 
 ```js
 import { getObjectDiff } from "@donedeal0/superdiff";
 ```
 
-Compares two objects and return a diff for each value and their potential subvalues:
+Compares two objects and return a diff for each value and their potential subvalues. Supports deeply nested objects with any kind of values.
 
-- property name
-- status: `added`, `deleted`, `equal`, `updated`
-- previous value, current value
-- supports deeply nested objects with any kind of values
+**Format**
 
-format:
+input
+
+```ts
+prevData: Record<string, unknown>;
+nextData: Record<string, unknown>;
+options?: {
+  ignoreArrayOrder?: boolean, // false by default,
+  showOnly?: {
+    statuses: ("added" | "deleted" | "updated" | "equal")[], // [] by default
+    granularity?: "basic" | "deep" // "basic" by default
+  }
+}
+```
+
+- `prevData`: the original object
+- `nextData`: the new object
+- `options`
+  - `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
+  - `showOnly`: returns only the values whose status you are interested in. It takes two parameters:
+
+    - `statuses`: status you want to see in the output (e.g. `["added", "equal"]`)
+      - `granularity`:
+        - `basic` returns only the main properties whose status matches your query.
+        - `deep` can return main properties if some of their subproperties' status match your request. The subproperties are filtered accordingly.
+
+output 
 
 ```ts
 type ObjectDiff = {
   type: "object";
   status: "added" | "deleted" | "equal" | "updated";
-  diff: {
-    property: string;
-    previousValue: unknown;
-    currentValue: unknow;
-    status: "added" | "deleted" | "equal" | "updated";
-    // only appears if some subproperties have been added/deleted/updated
-    diff?: {
-      property: string;
-      previousValue: unknown;
-      currentValue: unknown;
-      status: "added" | "deleted" | "equal" | "updated";
-      // recursive diff in case of subproperties
-      diff?: SubDiff[];
-    }[];
-  }[];
+  diff: Diff[];
 };
-```
 
-**Options**
+/** recursive diff in case of subproperties */
+type Diff = {
+  property: string;
+  previousValue: unknown;
+  currentValue: unknown;
+  status: "added" | "deleted" | "equal" | "updated";
+  diff?: Diff[];
+};
+```
+**Usage**
 
-You can add a third `options` parameter to `getObjectDiff`.
+input
 
-```ts
-{
-  ignoreArrayOrder?: boolean // false by default,
-  showOnly?: {
-    statuses: ("added" | "deleted" | "updated" | "equal")[], // [] by default
-    granularity?: "basic" | "deep" // "basic" by default
+```diff
+getObjectDiff(
+  {
+    id: 54,
+    user: {
+      name: "joe",
+-     member: true,
+-     hobbies: ["golf", "football"],
+      age: 66,
+    },
+  },
+  {
+    id: 54,
+    user: {
+      name: "joe",
++     member: false,
++     hobbies: ["golf", "chess"],
+      age: 66,
+    },
   }
-}
+);
 ```
 
-- `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
-- `showOnly`: returns only the values whose status you are interested in. It takes two parameters:
+output
 
-  - `statuses`: status you want to see in the output (e.g. `["added", "equal"]`)
-    - `granularity`:
-      - `basic` returns only the main properties whose status matches your query.
-      - `deep` can return main properties if some of their subproperties' status match your request. The subproperties are filtered accordingly.
+```diff
+{
+      type: "object",
++     status: "updated",
+      diff: [
+        {
+          property: "id",
+          previousValue: 54,
+          currentValue: 54,
+          status: "equal",
+        },
+        {
+          property: "user",
+          previousValue: {
+            name: "joe",
+            member: true,
+            hobbies: ["golf", "football"],
+            age: 66,
+          },
+          currentValue: {
+            name: "joe",
+            member: false,
+            hobbies: ["golf", "chess"],
+            age: 66,
+          },
++         status: "updated",
+          diff: [
+            {
+              property: "name",
+              previousValue: "joe",
+              currentValue: "joe",
+              status: "equal",
+            },
++           {
++             property: "member",
++             previousValue: true,
++             currentValue: false,
++             status: "updated",
++           },
++           {
++             property: "hobbies",
++             previousValue: ["golf", "football"],
++             currentValue: ["golf", "chess"],
++             status: "updated",
++           },
+            {
+              property: "age",
+              previousValue: 66,
+              currentValue: 66,
+              status: "equal",
+            },
+          ],
+        },
+      ],
+    }
+```
+<hr/>
 
 ### getListDiff()
 
@@ -105,15 +200,31 @@ You can add a third `options` parameter to `getObjectDiff`.
 import { getListDiff } from "@donedeal0/superdiff";
 ```
 
-Compares two arrays and returns a diff for each value:
+Compares two arrays and returns a diff for each entry. Supports duplicate values, primitive values and objects.
 
-- index change: `prevIndex`, `newIndex`, `indexDiff`
-- status: `added`, `deleted`, `equal`, `moved`, `updated`
-- value
-- supports arrays of primitive values and objects
-- supports arrays with duplicate values
+**Format**
+
+input
+
+```ts
+  prevList: T[];
+  nextList: T[];
+  options?: {
+    showOnly?: ("added" | "deleted" | "moved" | "updated" | "equal")[], // [] by default
+    referenceProperty?: string, // "" by default
+    ignoreArrayOrder?: boolean, // false by default,
+    considerMoveAsUpdate?: boolean // false by default
+  }
+```
+- `prevList`: the original list
+- `nextList`: the new list
+- `options`
+  - `showOnly` gives you the option to return only the values whose status you are interested in (e.g. `["added", "equal"]`).
+  - `referenceProperty` will consider an object to be updated instead of added or deleted if one of its properties remains stable, such as its `id`. This option has no effect on other datatypes.
+  - `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
+  - `considerMoveAsUpdate`: if set to `true` the `moved` value will be considered as `updated`.
 
-format:
+output
 
 ```ts
 type ListDiff = {
@@ -128,56 +239,7 @@ type ListDiff = {
   }[];
 };
 ```
-
-**Options**
-
-You can add a third `options` parameter to `getListDiff`.
-
-```ts
-{
-  showOnly?: ("added" | "deleted" | "moved" | "updated" | "equal")[], // [] by default
-  referenceProperty?: string; // "" by default
-  ignoreArrayOrder?: boolean // false by default,
-}
-```
-
-- `showOnly` gives you the option to return only the values whose status you are interested in (e.g. `["added", "equal"]`).
-- `referenceProperty` will consider an object to be updated instead of added or deleted if one of its properties remains stable, such as its `id`. This option has no effect on other datatypes.
-- `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
-
-### isEqual()
-
-```js
-import { isEqual } from "@donedeal0/superdiff";
-```
-
-Tests whether two values are equal.
-
-**Options**
-
-You can add a third `options` parameter to `isEqual`.
-
-```ts
-{
-  ignoreArrayOrder?: boolean // false by default,
-}
-```
-
-- `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
-
-### isObject()
-
-```js
-import { isObject } from "@donedeal0/superdiff";
-```
-
-Tests whether a value is an object.
-
-<hr/>
-
-## EXAMPLES
-
-### getListDiff()
+**Usage**
 
 input
 
@@ -233,96 +295,146 @@ output
       ],
     }
 ```
+<hr/>
 
-### getObjectDiff()
+### streamListDiff()
+
+```js
+import { streamListDiff } from "@donedeal0/superdiff";
+```
+
+Streams the diff of two object lists, ideal for large lists and maximum performance.
+
+**Format**
 
 input
 
-```diff
-getObjectDiff(
-  {
-    id: 54,
-    user: {
-      name: "joe",
--     member: true,
--     hobbies: ["golf", "football"],
-      age: 66,
-    },
-  },
-  {
-    id: 54,
-    user: {
-      name: "joe",
-+     member: false,
-+     hobbies: ["golf", "chess"],
-      age: 66,
-    },
-  }
-);
+```ts
+ prevList: T[],
+ nextList: T[],
+ referenceProperty: ReferenceProperty<T>,
+ options: {
+  showOnly?: returns only the values whose status you are interested in. (e.g. `["added", "equal"]`), // [] by default
+  chunksSize?: number, // // 0 by default
+  considerMoveAsUpdate? boolean; // false by default
+}
 ```
 
+- `prevList`: the original object list.
+- `nextList`: the new object list.
+- `referenceProperty`: a common property in all the objects of your lists (e.g. `id`).
+- `options`
+  - `chunksSize` the number of object diffs returned by each stream chunk. If set to `0`, each stream will return a single object diff. If set to `10` each stream will return 10 object diffs.
+  - `showOnly` gives you the option to return only the values whose status you are interested in (e.g. `["added", "equal"]`).
+  - `considerMoveAsUpdate`: if set to `true` the `moved` value will be considered as `updated`.
+
 output
 
+```ts
+type StreamListDiff<T extends Record<string, unknown>> = {
+  currentValue: T | null;
+  previousValue: T | null;
+  prevIndex: number | null;
+  newIndex: number | null;
+  indexDiff: number | null;
+  status: "added" | "deleted" | "moved" | "updated" | "equal";
+};
+```
+
+**Usage**
+
+input
+
 ```diff
-{
-      type: "object",
-+     status: "updated",
-      diff: [
-        {
-          property: "id",
-          previousValue: 54,
-          currentValue: 54,
-          status: "equal",
-        },
+const diff = streamListDiff(
+      [ 
+-       { id: 1, name: "Item 1" },  
+        { id: 2, name: "Item 2" },
+        { id: 3, name: "Item 3" } 
+      ],
+      [
++       { id: 0, name: "Item 0" }, 
+        { id: 2, name: "Item 2" },
++       { id: 3, name: "Item Three" },
+      ],
+      "id", 
+      { chunksSize: 2 }
+    );
+```
+ 
+output
+
+```diff
+diff.on("data", (chunk) => {
+      // first chunk received (2 object diffs)
+      [
++       {
++         previousValue: null,
++         currentValue: { id: 0, name: 'Item 0' },
++         prevIndex: null,
++         newIndex: 0,
++         indexDiff: null,
++         status: 'added'
++       },
+-       {
+-         previousValue: { id: 1, name: 'Item 1' },
+-         currentValue: null,
+-         prevIndex: 0,
+-         newIndex: null,
+-         indexDiff: null,
+-         status: 'deleted'
+-       }
+      ]
+    // second chunk received (2 object diffs)
+      [
         {
-          property: "user",
-          previousValue: {
-            name: "joe",
-            member: true,
-            hobbies: ["golf", "football"],
-            age: 66,
-          },
-          currentValue: {
-            name: "joe",
-            member: false,
-            hobbies: ["golf", "chess"],
-            age: 66,
-          },
-+         status: "updated",
-          diff: [
-            {
-              property: "name",
-              previousValue: "joe",
-              currentValue: "joe",
-              status: "equal",
-            },
-+           {
-+             property: "member",
-+             previousValue: true,
-+             currentValue: false,
-+             status: "updated",
-+           },
-+           {
-+             property: "hobbies",
-+             previousValue: ["golf", "football"],
-+             currentValue: ["golf", "chess"],
-+             status: "updated",
-+           },
-            {
-              property: "age",
-              previousValue: 66,
-              currentValue: 66,
-              status: "equal",
-            },
-          ],
+          previousValue: { id: 2, name: 'Item 2' },
+          currentValue: { id: 2, name: 'Item 2' },
+          prevIndex: 1,
+          newIndex: 1,
+          indexDiff: 0,
+          status: 'equal'
         },
-      ],
-    }
++       {
++         previousValue: { id: 3, name: 'Item 3' },
++         currentValue: { id: 3, name: 'Item Three' },
++         prevIndex: 2,
++         newIndex: 2,
++         indexDiff: 0,
++         status: 'updated'
++       },
+     ]
+});
+
+diff.on("finish", () => console.log("The full diff is available"))
+diff.on("error", (err)=> console.log(err))
 ```
+<hr/>
 
 ### isEqual()
 
 ```js
+import { isEqual } from "@donedeal0/superdiff";
+```
+
+Checks whether two values are equal.
+
+**Options**
+
+You can add a third `options` parameter to `isEqual`.
+
+```ts
+{
+  ignoreArrayOrder?: boolean // false by default,
+}
+```
+
+- `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
+
+**Usage**
+
+
+```ts
 isEqual(
   [
     { name: "joe", age: 99 },
@@ -337,25 +449,36 @@ isEqual(
 
 output
 
-```js
+```ts
 false;
 ```
+<hr/>
 
 ### isObject()
 
+```js
+import { isObject } from "@donedeal0/superdiff";
+```
+
+Tests whether a value is an object.
+
+**Usage**
+
 input
 
-```js
+```ts
 isObject(["hello", "world"]);
 ```
 
 output
 
-```js
+```ts
 false;
 ```
 
-More examples are available in the source code tests.
+<hr/>
+
+### More examples are available in the source code tests.
 
 <hr/>
 
@@ -365,7 +488,7 @@ DoneDeal0
 
 ## SUPPORT
 
-If you or your company uses **Superdiff**, please show your support by becoming a sponsor! Your name and company logo will be displayed on the `README.md`. https://github.com/sponsors/DoneDeal0
+If you or your company uses **Superdiff**, please show your support by becoming a sponsor! Your name and company logo will be displayed on the `README.md`. Premium support is also available. https://github.com/sponsors/DoneDeal0
 
 <br/>
 <a href="https://github.com/sponsors/DoneDeal0" target="_blank">
diff --git a/src/index.ts b/src/index.ts
index ca0a6d8..798227b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,7 +1,7 @@
 export { getObjectDiff } from "./lib/object-diff";
 export { getListDiff } from "./lib/list-diff";
 export { isEqual, isObject } from "./lib/utils";
-export { streamListsDiff } from "./lib/stream-list-diff";
+export { streamListDiff } from "./lib/stream-list-diff";
 export * from "./models/list";
 export * from "./models/object";
 export * from "./models/stream";
diff --git a/src/lib/stream-list-diff/emitter.ts b/src/lib/stream-list-diff/emitter.ts
index 4d27b30..b768784 100644
--- a/src/lib/stream-list-diff/emitter.ts
+++ b/src/lib/stream-list-diff/emitter.ts
@@ -1,4 +1,4 @@
-import { StreamListsDiff } from "@models/stream";
+import { StreamListDiff } from "@models/stream";
 
 type Listener<T extends unknown[]> = (...args: T) => void;
 
@@ -9,7 +9,7 @@ export enum StreamEvent {
 }
 
 export type Emitter<T extends Record<string, unknown>> = EventEmitter<{
-  data: [StreamListsDiff<T>[]];
+  data: [StreamListDiff<T>[]];
   error: [Error];
   finish: [];
 }>;
@@ -33,12 +33,12 @@ export class EventEmitter<Events extends Record<string, unknown[]>> {
 }
 
 export type EmitterEvents<T extends Record<string, unknown>> = {
-  data: [StreamListsDiff<T>[]];
+  data: [StreamListDiff<T>[]];
   error: [Error];
   finish: [];
 };
 
-export interface ReadOnlyEmitter<T extends Record<string, unknown>> {
+export interface StreamListener<T extends Record<string, unknown>> {
   on<E extends keyof EmitterEvents<T>>(
     event: E,
     listener: Listener<EmitterEvents<T>[E]>,
diff --git a/src/lib/stream-list-diff/index.ts b/src/lib/stream-list-diff/index.ts
index d47c1bc..e949619 100644
--- a/src/lib/stream-list-diff/index.ts
+++ b/src/lib/stream-list-diff/index.ts
@@ -2,7 +2,7 @@ import {
   DEFAULT_LIST_STREAM_OPTIONS,
   ListStreamOptions,
   ReferenceProperty,
-  StreamListsDiff,
+  StreamListDiff,
   StreamReferences,
 } from "@models/stream";
 import { LIST_STATUS } from "@models/list";
@@ -11,17 +11,17 @@ import {
   Emitter,
   EmitterEvents,
   EventEmitter,
-  ReadOnlyEmitter,
+  StreamListener,
   StreamEvent,
 } from "./emitter";
 
 function outputDiffChunk<T extends Record<string, unknown>>(
   emitter: Emitter<T>,
 ) {
-  let chunks: StreamListsDiff<T>[] = [];
+  let chunks: StreamListDiff<T>[] = [];
 
   return function handleDiffChunk(
-    chunk: StreamListsDiff<T>,
+    chunk: StreamListDiff<T>,
     isLastChunk: boolean,
     options: ListStreamOptions,
   ): void {
@@ -50,9 +50,9 @@ function formatSingleListStreamDiff<T extends Record<string, unknown>>(
   isPrevious: boolean,
   status: LIST_STATUS,
   options: ListStreamOptions,
-): StreamListsDiff<T>[] | null {
+): StreamListDiff<T>[] | null {
   let isValid = true;
-  const diff: StreamListsDiff<T>[] = [];
+  const diff: StreamListDiff<T>[] = [];
   for (let i = 0; i < list.length; i++) {
     const data = list[i];
     if (!isObject(data)) {
@@ -292,7 +292,7 @@ function getDiffChunks<T extends Record<string, unknown>>(
         );
       }
     }
-    listsReferences.delete(key); // to free up memory
+    listsReferences.delete(key);
   }
 
   return emitter.emit(StreamEvent.Finish);
@@ -304,17 +304,17 @@ function getDiffChunks<T extends Record<string, unknown>>(
  * @param {Record<string, unknown>[]} nextList - The new object list.
  * @param {ReferenceProperty<T>} referenceProperty - A common property in all the objects of your lists (e.g. `id`)
  * @param {ListStreamOptions} options - Options to refine your output.
-    - `chunksSize`: the number of object diffs returned by each stream chunk. If set to `0`, each stream will return a single object diff. If set to `10` each stream will return 10 object diffs. (default is `0`)
+    - `chunksSize`: the number of object diffs returned by each streamed chunk. (e.g. `0` = 1 object diff by chunk, `10` = 10 object diffs by chunk).
     - `showOnly`: returns only the values whose status you are interested in. (e.g. `["added", "equal"]`)
     - `considerMoveAsUpdate`: if set to `true` a `moved` object will be considered as `updated`
  * @returns EventEmitter
  */
-export function streamListsDiff<T extends Record<string, unknown>>(
+export function streamListDiff<T extends Record<string, unknown>>(
   prevList: T[],
   nextList: T[],
   referenceProperty: ReferenceProperty<T>,
   options: ListStreamOptions = DEFAULT_LIST_STREAM_OPTIONS,
-): ReadOnlyEmitter<T> {
+): StreamListener<T> {
   const emitter = new EventEmitter<EmitterEvents<T>>();
   setTimeout(() => {
     try {
@@ -323,5 +323,5 @@ export function streamListsDiff<T extends Record<string, unknown>>(
       return emitter.emit(StreamEvent.Error, err as Error);
     }
   }, 0);
-  return emitter as ReadOnlyEmitter<T>;
+  return emitter as StreamListener<T>;
 }
diff --git a/src/lib/stream-list-diff/stream-list-diff.test.ts b/src/lib/stream-list-diff/stream-list-diff.test.ts
index 2a171a7..5d8795c 100644
--- a/src/lib/stream-list-diff/stream-list-diff.test.ts
+++ b/src/lib/stream-list-diff/stream-list-diff.test.ts
@@ -1,14 +1,14 @@
 import { LIST_STATUS } from "@models/list";
-import { streamListsDiff } from ".";
-import { StreamListsDiff } from "@models/stream";
+import { streamListDiff } from ".";
+import { StreamListDiff } from "@models/stream";
 
-describe("streamListsDiff data", () => {
+describe("streamListDiff data", () => {
   it("emits 'data' event and consider the all the nextList added if no prevList is provided", (done) => {
     const nextList = [
       { id: 1, name: "Item 1" },
       { id: 2, name: "Item 2" },
     ];
-    const diff = streamListsDiff([], nextList, "id", { chunksSize: 2 });
+    const diff = streamListDiff([], nextList, "id", { chunksSize: 2 });
 
     const expectedChunks = [
       {
@@ -43,7 +43,7 @@ describe("streamListsDiff data", () => {
       { id: 1, name: "Item 1" },
       { id: 2, name: "Item 2" },
     ];
-    const diff = streamListsDiff(prevList, [], "id", { chunksSize: 2 });
+    const diff = streamListDiff(prevList, [], "id", { chunksSize: 2 });
 
     const expectedChunks = [
       {
@@ -82,7 +82,7 @@ describe("streamListsDiff data", () => {
       { id: 2, name: "Item 2" },
       { id: 3, name: "Item 3" },
     ];
-    const diff = streamListsDiff(prevList, nextList, "id");
+    const diff = streamListDiff(prevList, nextList, "id");
 
     const expectedChunks = [
       [
@@ -153,7 +153,7 @@ describe("streamListsDiff data", () => {
       { id: 9, name: "Item 9" },
       { id: 8, name: "Item 8" },
     ];
-    const diff = streamListsDiff(prevList, nextList, "id", { chunksSize: 5 });
+    const diff = streamListDiff(prevList, nextList, "id", { chunksSize: 5 });
 
     const expectedChunks = [
       [
@@ -277,7 +277,7 @@ describe("streamListsDiff data", () => {
       { id: 3, name: "Item 3" },
       { id: 5, name: "Item 5" },
     ];
-    const diff = streamListsDiff(prevList, nextList, "id", { chunksSize: 150 });
+    const diff = streamListDiff(prevList, nextList, "id", { chunksSize: 150 });
 
     const expectedChunks = [
       {
@@ -346,7 +346,7 @@ describe("streamListsDiff data", () => {
       { id: 3, name: "Item 3" },
       { id: 5, name: "Item 5" },
     ];
-    const diff = streamListsDiff(prevList, nextList, "id", {
+    const diff = streamListDiff(prevList, nextList, "id", {
       chunksSize: 5,
       considerMoveAsUpdate: true,
     });
@@ -418,7 +418,7 @@ describe("streamListsDiff data", () => {
       { id: 3, name: "Item 3" },
       { id: 5, name: "Item 5" },
     ];
-    const diff = streamListsDiff(prevList, nextList, "id", {
+    const diff = streamListDiff(prevList, nextList, "id", {
       chunksSize: 5,
       showOnly: ["added", "deleted"],
     });
@@ -506,7 +506,7 @@ describe("streamListsDiff data", () => {
       { id: 9, name: "Item 9" },
       { id: 8, name: "Item 8" },
     ];
-    const diff = streamListsDiff(prevList, nextList, "id", { chunksSize: 5 });
+    const diff = streamListDiff(prevList, nextList, "id", { chunksSize: 5 });
 
     const expectedChunks = [
       [
@@ -663,9 +663,9 @@ describe("streamListsDiff data", () => {
   });
 });
 
-describe("streamListsDiff finish", () => {
+describe("streamListDiff finish", () => {
   it("emits 'finish' event if no prevList nor nextList is provided", (done) => {
-    const diff = streamListsDiff([], [], "id");
+    const diff = streamListDiff([], [], "id");
     diff.on("finish", () => done());
   });
   it("emits 'finish' event when all the chunks have been processed", (done) => {
@@ -677,12 +677,12 @@ describe("streamListsDiff finish", () => {
       { id: 2, name: "Item 2" },
       { id: 3, name: "Item 3" },
     ];
-    const diff = streamListsDiff(prevList, nextList, "id");
+    const diff = streamListDiff(prevList, nextList, "id");
     diff.on("finish", () => done());
   });
 });
 
-describe("streamListsDiff error", () => {
+describe("streamListDiff error", () => {
   test("emits 'error' event when prevList has invalid data", (done) => {
     const prevList = [
       { id: 1, name: "Item 1" },
@@ -695,7 +695,7 @@ describe("streamListsDiff error", () => {
     ];
 
     // @ts-expect-error prevList is invalid by design for the test
-    const diff = streamListsDiff(prevList, nextList, "id");
+    const diff = streamListDiff(prevList, nextList, "id");
 
     diff.on("error", (err) => {
       expect(err["message"]).toEqual(
@@ -717,7 +717,7 @@ describe("streamListsDiff error", () => {
     ];
 
     // @ts-expect-error nextList is invalid by design for the test
-    const diff = streamListsDiff(prevList, nextList, "id");
+    const diff = streamListDiff(prevList, nextList, "id");
 
     diff.on("error", (err) => {
       expect(err["message"]).toEqual(
@@ -734,7 +734,7 @@ describe("streamListsDiff error", () => {
       { id: 2, name: "Item 2" },
     ];
 
-    const diff = streamListsDiff(prevList, nextList, "id");
+    const diff = streamListDiff(prevList, nextList, "id");
 
     diff.on("error", (err) => {
       expect(err["message"]).toEqual(
@@ -751,7 +751,7 @@ describe("streamListsDiff error", () => {
     ];
     const nextList = [{ id: 1, name: "Item 1" }, { name: "Item 2" }];
 
-    const diff = streamListsDiff(prevList, nextList, "id");
+    const diff = streamListDiff(prevList, nextList, "id");
 
     diff.on("error", (err) => {
       expect(err["message"]).toEqual(
@@ -768,7 +768,7 @@ describe("streamListsDiff error", () => {
     ];
     const nextList = [{ id: 1, name: "Item 1" }, { name: "Item 2" }];
 
-    const diff = streamListsDiff(prevList, nextList, "id", { chunksSize: -3 });
+    const diff = streamListDiff(prevList, nextList, "id", { chunksSize: -3 });
 
     diff.on("error", (err) => {
       expect(err["message"]).toEqual(
@@ -793,9 +793,9 @@ describe("Performance", () => {
       ...generateLargeList(5000, "next"),
     ];
 
-    const receivedChunks: StreamListsDiff<{ id: string; value: number }>[] = [];
+    const receivedChunks: StreamListDiff<{ id: string; value: number }>[] = [];
     let chunkCount = 0;
-    const diffStream = streamListsDiff(prevList, nextList, "id", {
+    const diffStream = streamListDiff(prevList, nextList, "id", {
       chunksSize: 1000,
     });
 
@@ -835,9 +835,9 @@ describe("Performance", () => {
       ...generateLargeList(50000, "next"),
     ];
 
-    const receivedChunks: StreamListsDiff<{ id: string; value: number }>[] = [];
+    const receivedChunks: StreamListDiff<{ id: string; value: number }>[] = [];
     let chunkCount = 0;
-    const diffStream = streamListsDiff(prevList, nextList, "id", {
+    const diffStream = streamListDiff(prevList, nextList, "id", {
       chunksSize: 10_000,
     });
 
@@ -864,46 +864,4 @@ describe("Performance", () => {
       done();
     });
   });
-  // it("should correctly stream diff for 1.000.000 entries", (done) => {
-  //   const generateLargeList = (size: number, idPrefix: string) => {
-  //     return Array.from({ length: size }, (_, i) => ({
-  //       id: `${idPrefix}-${i}`,
-  //       value: i,
-  //     }));
-  //   };
-  //   const prevList = generateLargeList(1_000_000, "prev");
-  //   const nextList = [
-  //     ...generateLargeList(500_000, "prev"),
-  //     ...generateLargeList(500_000, "next"),
-  //   ];
-
-  //   const receivedChunks: StreamListsDiff<{ id: string; value: number }>[] = [];
-  //   let chunkCount = 0;
-  //   const diffStream = streamListsDiff(prevList, nextList, "id", {
-  //     chunksSize: 100_000,
-  //   });
-
-  //   diffStream.on("data", (chunk) => {
-  //     receivedChunks.push(...chunk);
-  //     chunkCount++;
-  //   });
-
-  //   diffStream.on("finish", () => {
-  //     const deletions = receivedChunks.filter(
-  //       (diff) => diff.status === LIST_STATUS.DELETED,
-  //     );
-  //     const additions = receivedChunks.filter(
-  //       (diff) => diff.status === LIST_STATUS.ADDED,
-  //     );
-  //     const updates = receivedChunks.filter(
-  //       (diff) => diff.status === LIST_STATUS.EQUAL,
-  //     );
-  //     expect(receivedChunks.length).toBe(1_500_000); // 50.000 deletions + 50.000 equal + 50.000 additions
-  //     expect(chunkCount).toBe(15);
-  //     expect(deletions.length).toBe(500000);
-  //     expect(additions.length).toBe(500000);
-  //     expect(updates.length).toBe(500000);
-  //     done();
-  //   });
-  // });
 });
diff --git a/src/models/stream/index.ts b/src/models/stream/index.ts
index 0bda847..d932ebd 100644
--- a/src/models/stream/index.ts
+++ b/src/models/stream/index.ts
@@ -1,6 +1,6 @@
 import { LIST_STATUS } from "@models/list";
 
-export type StreamListsDiff<T extends Record<string, unknown>> = {
+export type StreamListDiff<T extends Record<string, unknown>> = {
   currentValue: T | null;
   previousValue: T | null;
   prevIndex: number | null;

From f613071378fd5980a1fe1582c7ae9d675e8a4033 Mon Sep 17 00:00:00 2001
From: Antoine Lanoe <antoine.lanoe@meltwater.com>
Date: Sun, 6 Oct 2024 20:15:07 +0200
Subject: [PATCH 8/8] chore: update package.json

---
 README.md                               | 135 +++++++++++++++---------
 dist/index.d.mts                        | 107 -------------------
 dist/index.d.ts                         | 107 -------------------
 dist/index.js                           |  11 --
 dist/index.mjs                          |   3 -
 package.json                            |  10 +-
 src/lib/object-diff/object-diff.test.ts |   2 +-
 src/models/list/index.ts                |   4 +-
 src/models/stream/index.ts              |   2 +-
 9 files changed, 98 insertions(+), 283 deletions(-)
 delete mode 100644 dist/index.d.mts
 delete mode 100644 dist/index.d.ts
 delete mode 100644 dist/index.js
 delete mode 100644 dist/index.mjs

diff --git a/README.md b/README.md
index bf84553..d19bbda 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,9 @@ This library compares two arrays or objects and returns a full diff of their dif
 
 All other existing solutions return a strange diff format that often requires additional parsing. They are also limited to object comparison.
 
-**Superdiff** gives you a complete diff for both array <u>and</u> objects in a very readable format. Last but not least, it's battle-tested, has zero dependencies, and is super fast. Import. Enjoy. 👍
+**Superdiff** gives you a complete diff for both array <u>and</u> objects in a very readable format. Last but not least, it's battle-tested, has zero dependencies, and is super fast. 
+
+Import. Enjoy. 👍
 
 <hr/>
 
@@ -38,17 +40,21 @@ I am grateful to the generous donors of **Superdiff**!
 
 ## FEATURES
 
-**Superdiff** exports 4 functions:
+**Superdiff** exports 5 functions:
 
 ```ts
-// Compares two objects and return a diff for each value and their potential subvalues
+// Returns a complete diff of two objects
 getObjectDiff(prevObject, nextObject)
-// Compares two arrays and returns a diff for each value
+
+// Returns a complete diff of two arrays
 getListDiff(prevList, nextList)
+
 // Streams the diff of two object lists, ideal for large lists and maximum performance
 streamListDiff(prevList, nextList, referenceProperty)
+
 // Checks whether two values are equal 
 isEqual(dataA, dataB)
+
 // Checks whether a value is an object
 isObject(data)
 ```
@@ -62,9 +68,9 @@ import { getObjectDiff } from "@donedeal0/superdiff";
 
 Compares two objects and return a diff for each value and their potential subvalues. Supports deeply nested objects with any kind of values.
 
-**Format**
+#### FORMAT
 
-input
+**Input**
 
 ```ts
 prevData: Record<string, unknown>;
@@ -78,8 +84,8 @@ options?: {
 }
 ```
 
-- `prevData`: the original object
-- `nextData`: the new object
+- `prevData`: the original object.
+- `nextData`: the new object.
 - `options`
   - `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
   - `showOnly`: returns only the values whose status you are interested in. It takes two parameters:
@@ -89,7 +95,7 @@ options?: {
         - `basic` returns only the main properties whose status matches your query.
         - `deep` can return main properties if some of their subproperties' status match your request. The subproperties are filtered accordingly.
 
-output 
+**Output**
 
 ```ts
 type ObjectDiff = {
@@ -107,9 +113,9 @@ type Diff = {
   diff?: Diff[];
 };
 ```
-**Usage**
+#### USAGE
 
-input
+**Input**
 
 ```diff
 getObjectDiff(
@@ -134,7 +140,7 @@ getObjectDiff(
 );
 ```
 
-output
+**Output**
 
 ```diff
 {
@@ -202,9 +208,9 @@ import { getListDiff } from "@donedeal0/superdiff";
 
 Compares two arrays and returns a diff for each entry. Supports duplicate values, primitive values and objects.
 
-**Format**
+#### FORMAT
 
-input
+**Input**
 
 ```ts
   prevList: T[];
@@ -216,15 +222,15 @@ input
     considerMoveAsUpdate?: boolean // false by default
   }
 ```
-- `prevList`: the original list
-- `nextList`: the new list
+- `prevList`: the original list.
+- `nextList`: the new list.
 - `options`
   - `showOnly` gives you the option to return only the values whose status you are interested in (e.g. `["added", "equal"]`).
   - `referenceProperty` will consider an object to be updated instead of added or deleted if one of its properties remains stable, such as its `id`. This option has no effect on other datatypes.
   - `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
-  - `considerMoveAsUpdate`: if set to `true` the `moved` value will be considered as `updated`.
+  - `considerMoveAsUpdate`: if set to `true` a `moved` value will be considered as `updated`.
 
-output
+**Output**
 
 ```ts
 type ListDiff = {
@@ -239,9 +245,9 @@ type ListDiff = {
   }[];
 };
 ```
-**Usage**
+#### USAGE
 
-input
+**Input**
 
 ```diff
 getListDiff(
@@ -250,7 +256,7 @@ getListDiff(
 );
 ```
 
-output
+**Output**
 
 ```diff
 {
@@ -297,7 +303,7 @@ output
 ```
 <hr/>
 
-### streamListDiff()
+### streamListDiff() 
 
 ```js
 import { streamListDiff } from "@donedeal0/superdiff";
@@ -305,16 +311,16 @@ import { streamListDiff } from "@donedeal0/superdiff";
 
 Streams the diff of two object lists, ideal for large lists and maximum performance.
 
-**Format**
+#### FORMAT
 
-input
+**Input**
 
 ```ts
- prevList: T[],
- nextList: T[],
- referenceProperty: ReferenceProperty<T>,
+ prevList: Record<string, unknown>[],
+ nextList: Record<string, unknown>[],
+ referenceProperty: keyof Record<string, unknown>,
  options: {
-  showOnly?: returns only the values whose status you are interested in. (e.g. `["added", "equal"]`), // [] by default
+  showOnly?: ("added" | "deleted" | "moved" | "updated" | "equal")[], // [] by default
   chunksSize?: number, // // 0 by default
   considerMoveAsUpdate? boolean; // false by default
 }
@@ -324,13 +330,32 @@ input
 - `nextList`: the new object list.
 - `referenceProperty`: a common property in all the objects of your lists (e.g. `id`).
 - `options`
-  - `chunksSize` the number of object diffs returned by each stream chunk. If set to `0`, each stream will return a single object diff. If set to `10` each stream will return 10 object diffs.
+  - `chunksSize` the number of object diffs returned by each streamed chunk. (e.g. `0` = 1 object diff by chunk, `10` = 10 object diffs by chunk).
   - `showOnly` gives you the option to return only the values whose status you are interested in (e.g. `["added", "equal"]`).
-  - `considerMoveAsUpdate`: if set to `true` the `moved` value will be considered as `updated`.
+  - `considerMoveAsUpdate`: if set to `true` a `moved` value will be considered as `updated`.
+
+**Output**
 
-output
+The objects diff are grouped in arrays - called `chunks` - and are consumed thanks to an event listener. You have access to 3 events: 
+  - `data`: to be notified when a new chunk of object diffs is available.
+  - `finish`: to be notified when the stream is complete.
+  - `error`: to be notified of an error during the stream.
 
 ```ts
+interface StreamListener<T extends Record<string, unknown>> {
+  on<E extends keyof EmitterEvents<T>>(
+    event: E,
+    listener: Listener<EmitterEvents<T>[E]>,
+  ): this;
+}
+
+type EmitterEvents<T extends Record<string, unknown>> = {
+  data: [StreamListDiff<T>[]];
+  error: [Error];
+  finish: [];
+};
+
+
 type StreamListDiff<T extends Record<string, unknown>> = {
   currentValue: T | null;
   previousValue: T | null;
@@ -341,9 +366,9 @@ type StreamListDiff<T extends Record<string, unknown>> = {
 };
 ```
 
-**Usage**
+#### USAGE
 
-input
+**Input**
 
 ```diff
 const diff = streamListDiff(
@@ -362,7 +387,7 @@ const diff = streamListDiff(
     );
 ```
  
-output
+**Output**
 
 ```diff
 diff.on("data", (chunk) => {
@@ -407,7 +432,7 @@ diff.on("data", (chunk) => {
 });
 
 diff.on("finish", () => console.log("The full diff is available"))
-diff.on("error", (err)=> console.log(err))
+diff.on("error", (err) => console.log(err))
 ```
 <hr/>
 
@@ -419,19 +444,22 @@ import { isEqual } from "@donedeal0/superdiff";
 
 Checks whether two values are equal.
 
-**Options**
+#### FORMAT
 
-You can add a third `options` parameter to `isEqual`.
+**Input**
 
 ```ts
-{
-  ignoreArrayOrder?: boolean // false by default,
-}
+a: unknown,
+b: unknown,
+options: { 
+    ignoreArrayOrder: boolean; // false by default
+     },
 ```
-
+- `a`: the value to compare to the value `b`.
+- `b`: the value that will be compared to the value `a`.
 - `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
 
-**Usage**
+#### USAGE
 
 
 ```ts
@@ -447,7 +475,7 @@ isEqual(
 );
 ```
 
-output
+**Output**
 
 ```ts
 false;
@@ -462,15 +490,25 @@ import { isObject } from "@donedeal0/superdiff";
 
 Tests whether a value is an object.
 
-**Usage**
+#### FORMAT
+
+**Input**
+
+```ts
+value: unknown;
+```
+
+- `value`: the value whose type will be checked.
 
-input
+#### USAGE
+
+**Input**
 
 ```ts
 isObject(["hello", "world"]);
 ```
 
-output
+**Output**
 
 ```ts
 false;
@@ -478,7 +516,8 @@ false;
 
 <hr/>
 
-### More examples are available in the source code tests.
+### ℹī¸ More examples are available in the source code tests.
+
 
 <hr/>
 
@@ -498,4 +537,4 @@ If you or your company uses **Superdiff**, please show your support by becoming
 
 ## CONTRIBUTING
 
-Pull requests are welcome!
+Issues and pull requests are welcome!
diff --git a/dist/index.d.mts b/dist/index.d.mts
deleted file mode 100644
index b2559e6..0000000
--- a/dist/index.d.mts
+++ /dev/null
@@ -1,107 +0,0 @@
-declare const STATUS: Record<string, ObjectDiffStatus>;
-declare const LIST_STATUS: Record<string, ListDiffStatus>;
-declare const GRANULARITY: Record<string, "basic" | "deep">;
-type ListDiffStatus = "added" | "equal" | "moved" | "deleted" | "updated";
-type ObjectDiffStatus = "added" | "equal" | "deleted" | "updated";
-type ObjectData = Record<string, any> | undefined | null;
-type ListData = any;
-type ObjectStatusTuple = readonly [
-    "added",
-    "equal",
-    "deleted",
-    "updated"
-];
-type ListStatusTuple = readonly [
-    "added",
-    "equal",
-    "deleted",
-    "moved",
-    "updated"
-];
-type isEqualOptions = {
-    ignoreArrayOrder?: boolean;
-};
-type ObjectOptions = {
-    ignoreArrayOrder?: boolean;
-    showOnly?: {
-        statuses: Array<ObjectStatusTuple[number]>;
-        granularity?: (typeof GRANULARITY)[keyof typeof GRANULARITY];
-    };
-};
-type ListOptions = {
-    showOnly?: Array<ListStatusTuple[number]>;
-    referenceProperty?: string;
-    considerMoveAsUpdate?: boolean;
-    ignoreArrayOrder?: boolean;
-};
-type ListDiff = {
-    type: "list";
-    status: ListDiffStatus;
-    diff: {
-        value: ListData;
-        prevIndex: number | null;
-        newIndex: number | null;
-        indexDiff: number | null;
-        status: ListDiffStatus;
-    }[];
-};
-type SubProperties = {
-    property: string;
-    previousValue: any;
-    currentValue: any;
-    status: ObjectDiffStatus;
-    subPropertiesDiff?: SubProperties[];
-};
-type ObjectDiff = {
-    type: "object";
-    status: ObjectDiffStatus;
-    diff: {
-        property: string;
-        previousValue: any;
-        currentValue: any;
-        status: ObjectDiffStatus;
-        subPropertiesDiff?: SubProperties[];
-    }[];
-};
-type DataDiff = ListDiff | ObjectDiff;
-
-/**
- * Returns the diff between two objects
- * @param {Record<string, any>} prevData - The original object.
- * @param {Record<string, any>} nextData - The new object.
- *  * @param {ListOptions} options - Options to refine your output.
-    - `showOnly`: returns only the values whose status you are interested in. It takes two parameters: `statuses` and `granularity`
-       `statuses` are the status you want to see in the output (e.g. `["added", "equal"]`)
-      `granularity` can be either `basic` (to return only the main properties whose status matches your query) or `deep` (to return the main properties if some of their subproperties' status match your request. The subproperties are filtered accordingly).
-    - `ignoreArrayOrder` if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
- * @returns ObjectDiff
- */
-declare function getObjectDiff(prevData: ObjectData, nextData: ObjectData, options?: ObjectOptions): ObjectDiff;
-
-/**
- * Returns the diff between two arrays
- * @param {Array<T>} prevList - The original array.
- * @param {Array<T>} nextList - The new array.
- * @param {ListOptions} options - Options to refine your output.
-    - `showOnly` gives you the option to return only the values whose status you are interested in (e.g. `["added", "equal"]`).
-    - `referenceProperty` will consider an object to be updated instead of added or deleted if one of its properties remains stable, such as its `id`. This option has no effect on other datatypes.
- * @returns ListDiff
- */
-declare const getListDiff: <T>(prevList: T[] | undefined | null, nextList: T[] | undefined | null, options?: ListOptions) => ListDiff;
-
-/**
- * Returns true if two data are equal
- * @param {any} a - The original data.
- * @param {any} b - The data to compare.
- * @param {isEqualOptions} options - The options to compare the data.
- * @returns boolean
- */
-declare function isEqual(a: any, b: any, options?: isEqualOptions): boolean;
-/**
- * Returns true if the provided value is an object
- * @param {any} value - The data to check.
- * @returns value is Record<string, any>
- */
-declare function isObject(value: any): value is Record<string, any>;
-
-export { type DataDiff, GRANULARITY, LIST_STATUS, type ListData, type ListDiff, type ListDiffStatus, type ListOptions, type ListStatusTuple, type ObjectData, type ObjectDiff, type ObjectDiffStatus, type ObjectOptions, type ObjectStatusTuple, STATUS, type SubProperties, getListDiff, getObjectDiff, isEqual, type isEqualOptions, isObject };
diff --git a/dist/index.d.ts b/dist/index.d.ts
deleted file mode 100644
index b2559e6..0000000
--- a/dist/index.d.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-declare const STATUS: Record<string, ObjectDiffStatus>;
-declare const LIST_STATUS: Record<string, ListDiffStatus>;
-declare const GRANULARITY: Record<string, "basic" | "deep">;
-type ListDiffStatus = "added" | "equal" | "moved" | "deleted" | "updated";
-type ObjectDiffStatus = "added" | "equal" | "deleted" | "updated";
-type ObjectData = Record<string, any> | undefined | null;
-type ListData = any;
-type ObjectStatusTuple = readonly [
-    "added",
-    "equal",
-    "deleted",
-    "updated"
-];
-type ListStatusTuple = readonly [
-    "added",
-    "equal",
-    "deleted",
-    "moved",
-    "updated"
-];
-type isEqualOptions = {
-    ignoreArrayOrder?: boolean;
-};
-type ObjectOptions = {
-    ignoreArrayOrder?: boolean;
-    showOnly?: {
-        statuses: Array<ObjectStatusTuple[number]>;
-        granularity?: (typeof GRANULARITY)[keyof typeof GRANULARITY];
-    };
-};
-type ListOptions = {
-    showOnly?: Array<ListStatusTuple[number]>;
-    referenceProperty?: string;
-    considerMoveAsUpdate?: boolean;
-    ignoreArrayOrder?: boolean;
-};
-type ListDiff = {
-    type: "list";
-    status: ListDiffStatus;
-    diff: {
-        value: ListData;
-        prevIndex: number | null;
-        newIndex: number | null;
-        indexDiff: number | null;
-        status: ListDiffStatus;
-    }[];
-};
-type SubProperties = {
-    property: string;
-    previousValue: any;
-    currentValue: any;
-    status: ObjectDiffStatus;
-    subPropertiesDiff?: SubProperties[];
-};
-type ObjectDiff = {
-    type: "object";
-    status: ObjectDiffStatus;
-    diff: {
-        property: string;
-        previousValue: any;
-        currentValue: any;
-        status: ObjectDiffStatus;
-        subPropertiesDiff?: SubProperties[];
-    }[];
-};
-type DataDiff = ListDiff | ObjectDiff;
-
-/**
- * Returns the diff between two objects
- * @param {Record<string, any>} prevData - The original object.
- * @param {Record<string, any>} nextData - The new object.
- *  * @param {ListOptions} options - Options to refine your output.
-    - `showOnly`: returns only the values whose status you are interested in. It takes two parameters: `statuses` and `granularity`
-       `statuses` are the status you want to see in the output (e.g. `["added", "equal"]`)
-      `granularity` can be either `basic` (to return only the main properties whose status matches your query) or `deep` (to return the main properties if some of their subproperties' status match your request. The subproperties are filtered accordingly).
-    - `ignoreArrayOrder` if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
- * @returns ObjectDiff
- */
-declare function getObjectDiff(prevData: ObjectData, nextData: ObjectData, options?: ObjectOptions): ObjectDiff;
-
-/**
- * Returns the diff between two arrays
- * @param {Array<T>} prevList - The original array.
- * @param {Array<T>} nextList - The new array.
- * @param {ListOptions} options - Options to refine your output.
-    - `showOnly` gives you the option to return only the values whose status you are interested in (e.g. `["added", "equal"]`).
-    - `referenceProperty` will consider an object to be updated instead of added or deleted if one of its properties remains stable, such as its `id`. This option has no effect on other datatypes.
- * @returns ListDiff
- */
-declare const getListDiff: <T>(prevList: T[] | undefined | null, nextList: T[] | undefined | null, options?: ListOptions) => ListDiff;
-
-/**
- * Returns true if two data are equal
- * @param {any} a - The original data.
- * @param {any} b - The data to compare.
- * @param {isEqualOptions} options - The options to compare the data.
- * @returns boolean
- */
-declare function isEqual(a: any, b: any, options?: isEqualOptions): boolean;
-/**
- * Returns true if the provided value is an object
- * @param {any} value - The data to check.
- * @returns value is Record<string, any>
- */
-declare function isObject(value: any): value is Record<string, any>;
-
-export { type DataDiff, GRANULARITY, LIST_STATUS, type ListData, type ListDiff, type ListDiffStatus, type ListOptions, type ListStatusTuple, type ObjectData, type ObjectDiff, type ObjectDiffStatus, type ObjectOptions, type ObjectStatusTuple, STATUS, type SubProperties, getListDiff, getObjectDiff, isEqual, type isEqualOptions, isObject };
diff --git a/dist/index.js b/dist/index.js
deleted file mode 100644
index b527bb3..0000000
--- a/dist/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-var u={ADDED:"added",EQUAL:"equal",DELETED:"deleted",UPDATED:"updated"},c={...u,MOVED:"moved"},y={BASIC:"basic",DEEP:"deep"};function D(e,t,i={ignoreArrayOrder:!1}){return typeof e!=typeof t?!1:Array.isArray(e)&&Array.isArray(t)?e.length!==t.length?!1:i.ignoreArrayOrder?e.every(f=>t.some(n=>JSON.stringify(n)===JSON.stringify(f))):e.every((f,n)=>JSON.stringify(f)===JSON.stringify(t[n])):typeof e=="object"?JSON.stringify(e)===JSON.stringify(t):e===t}function p(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function O(e,t={statuses:[],granularity:y.BASIC}){let{statuses:i,granularity:f}=t;return e.reduce((n,r)=>{if(f===y.DEEP&&r.subPropertiesDiff){let s=O(r.subPropertiesDiff,t);if(s.length>0)return [...n,{...r,subPropertiesDiff:s}]}if(f===y.DEEP&&r.subDiff){let s=O(r.subDiff,t);if(s.length>0)return [...n,{...r,subDiff:s}]}return i.includes(r.status)?[...n,r]:n},[])}function E(e){return e.some(t=>t.status!==u.EQUAL)?u.UPDATED:u.EQUAL}function A(e,t,i={ignoreArrayOrder:!1,showOnly:{statuses:[],granularity:y.BASIC}}){if(!e)return {type:"object",status:u.EQUAL,diff:[]};let f=[];return Object.entries(e).forEach(([n,r])=>{if(p(r)){let s=[];return Object.entries(r).forEach(([o,d])=>{s.push({property:o,previousValue:t===u.ADDED?void 0:d,currentValue:t===u.ADDED?d:void 0,status:t});}),f.push({property:n,previousValue:t===u.ADDED?void 0:e[n],currentValue:t===u.ADDED?r:void 0,status:t,subPropertiesDiff:s})}return f.push({property:n,previousValue:t===u.ADDED?void 0:e[n],currentValue:t===u.ADDED?r:void 0,status:t})}),i.showOnly&&i.showOnly.statuses.length>0?{type:"object",status:t,diff:O(f,i.showOnly)}:{type:"object",status:t,diff:f}}function P(e,t,i){if(!e)return;let f=Object.entries(e).find(([n])=>D(n,t,i));return f?f[1]:void 0}function j(e,t,i){return D(e,t,i)?u.EQUAL:u.UPDATED}function U(e){return e.some(t=>t.status!==u.EQUAL)?u.UPDATED:u.EQUAL}function g(e,t){if(!e)return;let i=Object.keys(e),f=Object.keys(t),n=i.filter(r=>!f.includes(r));if(n.length>0)return n.map(r=>({property:r,value:e[r]}))}function S(e,t,i){let f=[],n,r=g(e,t);return r&&r.forEach(s=>{f.push({property:s.property,previousValue:s.value,currentValue:void 0,status:u.DELETED});}),Object.entries(t).forEach(([s,o])=>{let d=P(e,s,i);if(!d)return f.push({property:s,previousValue:d,currentValue:o,status:!e||!(s in e)?u.ADDED:d===o?u.EQUAL:u.UPDATED});if(p(o)){let a=S(d,o,i);a&&a.length>0&&(n=a);}d&&f.push({property:s,previousValue:d,currentValue:o,status:j(d,o,i),...!!n&&{subDiff:n}});}),f}function m(e,t,i={ignoreArrayOrder:!1,showOnly:{statuses:[],granularity:y.BASIC}}){if(!e&&!t)return {type:"object",status:u.EQUAL,diff:[]};if(!e)return A(t,u.ADDED,i);if(!t)return A(e,u.DELETED,i);let f=[];Object.entries(t).forEach(([r,s])=>{let o=e[r];if(!o)return f.push({property:r,previousValue:o,currentValue:s,status:r in e?o===s?u.EQUAL:u.UPDATED:u.ADDED});if(p(s)){let d=S(o,s,i),a=U(d);return f.push({property:r,previousValue:o,currentValue:s,status:a,...a!==u.EQUAL&&{subPropertiesDiff:d}})}return f.push({property:r,previousValue:o,currentValue:s,status:j(o,s,i)})});let n=g(e,t);return n&&n.forEach(r=>{f.push({property:r.property,previousValue:r.value,currentValue:void 0,status:u.DELETED});}),i.showOnly&&i.showOnly.statuses.length>0?{type:"object",status:E(f),diff:O(f,i.showOnly)}:{type:"object",status:E(f),diff:f}}function w(e,t=[]){return e.filter(i=>t?.includes(i.status))}function h(e,t,i={showOnly:[]}){let f=e.map((n,r)=>({value:n,prevIndex:t===c.ADDED?null:r,newIndex:t===c.ADDED?r:null,indexDiff:null,status:t}));return i.showOnly&&i.showOnly.length>0?{type:"list",status:t,diff:f.filter(n=>i.showOnly?.includes(n.status))}:{type:"list",status:t,diff:f}}function L(e){return e.some(t=>t.status!==c.EQUAL)?c.UPDATED:c.EQUAL}function T(e,t){return p(e)&&t?Object.hasOwn(e,t):!1}var I=(e,t,i={showOnly:[],referenceProperty:void 0,considerMoveAsUpdate:!1,ignoreArrayOrder:!1})=>{if(!e&&!t)return {type:"list",status:c.EQUAL,diff:[]};if(!e)return h(t,c.ADDED,i);if(!t)return h(e,c.DELETED,i);let f=[],n=[];return t.forEach((r,s)=>{let o=e.findIndex((a,b)=>T(a,i.referenceProperty)?p(r)?D(a[i.referenceProperty],r[i.referenceProperty])&&!n.includes(b):!1:D(a,r)&&!n.includes(b));o>-1&&n.push(o);let d=o===-1?null:s-o;if(d===0||i.ignoreArrayOrder){let a=c.EQUAL;return T(r,i.referenceProperty)&&(D(e[o],r)||(a=c.UPDATED)),f.push({value:r,prevIndex:o,newIndex:s,indexDiff:d,status:a})}return o===-1?f.push({value:r,prevIndex:null,newIndex:s,indexDiff:d,status:c.ADDED}):f.push({value:r,prevIndex:o,newIndex:s,indexDiff:d,status:i.considerMoveAsUpdate?c.UPDATED:c.MOVED})}),e.forEach((r,s)=>{if(!n.includes(s))return f.splice(s,0,{value:r,prevIndex:s,newIndex:null,indexDiff:null,status:c.DELETED})}),i.showOnly&&i?.showOnly?.length>0?{type:"list",status:L(f),diff:w(f,i.showOnly)}:{type:"list",status:L(f),diff:f}};
-
-exports.GRANULARITY = y;
-exports.LIST_STATUS = c;
-exports.STATUS = u;
-exports.getListDiff = I;
-exports.getObjectDiff = m;
-exports.isEqual = D;
-exports.isObject = p;
diff --git a/dist/index.mjs b/dist/index.mjs
deleted file mode 100644
index 68cdd2c..0000000
--- a/dist/index.mjs
+++ /dev/null
@@ -1,3 +0,0 @@
-var u={ADDED:"added",EQUAL:"equal",DELETED:"deleted",UPDATED:"updated"},c={...u,MOVED:"moved"},y={BASIC:"basic",DEEP:"deep"};function D(e,t,i={ignoreArrayOrder:!1}){return typeof e!=typeof t?!1:Array.isArray(e)&&Array.isArray(t)?e.length!==t.length?!1:i.ignoreArrayOrder?e.every(f=>t.some(n=>JSON.stringify(n)===JSON.stringify(f))):e.every((f,n)=>JSON.stringify(f)===JSON.stringify(t[n])):typeof e=="object"?JSON.stringify(e)===JSON.stringify(t):e===t}function p(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function b(e,t={statuses:[],granularity:y.BASIC}){let{statuses:i,granularity:f}=t;return e.reduce((n,r)=>{if(f===y.DEEP&&r.subPropertiesDiff){let s=b(r.subPropertiesDiff,t);if(s.length>0)return [...n,{...r,subPropertiesDiff:s}]}if(f===y.DEEP&&r.subDiff){let s=b(r.subDiff,t);if(s.length>0)return [...n,{...r,subDiff:s}]}return i.includes(r.status)?[...n,r]:n},[])}function A(e){return e.some(t=>t.status!==u.EQUAL)?u.UPDATED:u.EQUAL}function j(e,t,i={ignoreArrayOrder:!1,showOnly:{statuses:[],granularity:y.BASIC}}){if(!e)return {type:"object",status:u.EQUAL,diff:[]};let f=[];return Object.entries(e).forEach(([n,r])=>{if(p(r)){let s=[];return Object.entries(r).forEach(([o,d])=>{s.push({property:o,previousValue:t===u.ADDED?void 0:d,currentValue:t===u.ADDED?d:void 0,status:t});}),f.push({property:n,previousValue:t===u.ADDED?void 0:e[n],currentValue:t===u.ADDED?r:void 0,status:t,subPropertiesDiff:s})}return f.push({property:n,previousValue:t===u.ADDED?void 0:e[n],currentValue:t===u.ADDED?r:void 0,status:t})}),i.showOnly&&i.showOnly.statuses.length>0?{type:"object",status:t,diff:b(f,i.showOnly)}:{type:"object",status:t,diff:f}}function U(e,t,i){if(!e)return;let f=Object.entries(e).find(([n])=>D(n,t,i));return f?f[1]:void 0}function g(e,t,i){return D(e,t,i)?u.EQUAL:u.UPDATED}function m(e){return e.some(t=>t.status!==u.EQUAL)?u.UPDATED:u.EQUAL}function S(e,t){if(!e)return;let i=Object.keys(e),f=Object.keys(t),n=i.filter(r=>!f.includes(r));if(n.length>0)return n.map(r=>({property:r,value:e[r]}))}function h(e,t,i){let f=[],n,r=S(e,t);return r&&r.forEach(s=>{f.push({property:s.property,previousValue:s.value,currentValue:void 0,status:u.DELETED});}),Object.entries(t).forEach(([s,o])=>{let d=U(e,s,i);if(!d)return f.push({property:s,previousValue:d,currentValue:o,status:!e||!(s in e)?u.ADDED:d===o?u.EQUAL:u.UPDATED});if(p(o)){let a=h(d,o,i);a&&a.length>0&&(n=a);}d&&f.push({property:s,previousValue:d,currentValue:o,status:g(d,o,i),...!!n&&{subDiff:n}});}),f}function w(e,t,i={ignoreArrayOrder:!1,showOnly:{statuses:[],granularity:y.BASIC}}){if(!e&&!t)return {type:"object",status:u.EQUAL,diff:[]};if(!e)return j(t,u.ADDED,i);if(!t)return j(e,u.DELETED,i);let f=[];Object.entries(t).forEach(([r,s])=>{let o=e[r];if(!o)return f.push({property:r,previousValue:o,currentValue:s,status:r in e?o===s?u.EQUAL:u.UPDATED:u.ADDED});if(p(s)){let d=h(o,s,i),a=m(d);return f.push({property:r,previousValue:o,currentValue:s,status:a,...a!==u.EQUAL&&{subPropertiesDiff:d}})}return f.push({property:r,previousValue:o,currentValue:s,status:g(o,s,i)})});let n=S(e,t);return n&&n.forEach(r=>{f.push({property:r.property,previousValue:r.value,currentValue:void 0,status:u.DELETED});}),i.showOnly&&i.showOnly.statuses.length>0?{type:"object",status:A(f),diff:b(f,i.showOnly)}:{type:"object",status:A(f),diff:f}}function I(e,t=[]){return e.filter(i=>t?.includes(i.status))}function L(e,t,i={showOnly:[]}){let f=e.map((n,r)=>({value:n,prevIndex:t===c.ADDED?null:r,newIndex:t===c.ADDED?r:null,indexDiff:null,status:t}));return i.showOnly&&i.showOnly.length>0?{type:"list",status:t,diff:f.filter(n=>i.showOnly?.includes(n.status))}:{type:"list",status:t,diff:f}}function T(e){return e.some(t=>t.status!==c.EQUAL)?c.UPDATED:c.EQUAL}function P(e,t){return p(e)&&t?Object.hasOwn(e,t):!1}var R=(e,t,i={showOnly:[],referenceProperty:void 0,considerMoveAsUpdate:!1,ignoreArrayOrder:!1})=>{if(!e&&!t)return {type:"list",status:c.EQUAL,diff:[]};if(!e)return L(t,c.ADDED,i);if(!t)return L(e,c.DELETED,i);let f=[],n=[];return t.forEach((r,s)=>{let o=e.findIndex((a,E)=>P(a,i.referenceProperty)?p(r)?D(a[i.referenceProperty],r[i.referenceProperty])&&!n.includes(E):!1:D(a,r)&&!n.includes(E));o>-1&&n.push(o);let d=o===-1?null:s-o;if(d===0||i.ignoreArrayOrder){let a=c.EQUAL;return P(r,i.referenceProperty)&&(D(e[o],r)||(a=c.UPDATED)),f.push({value:r,prevIndex:o,newIndex:s,indexDiff:d,status:a})}return o===-1?f.push({value:r,prevIndex:null,newIndex:s,indexDiff:d,status:c.ADDED}):f.push({value:r,prevIndex:o,newIndex:s,indexDiff:d,status:i.considerMoveAsUpdate?c.UPDATED:c.MOVED})}),e.forEach((r,s)=>{if(!n.includes(s))return f.splice(s,0,{value:r,prevIndex:s,newIndex:null,indexDiff:null,status:c.DELETED})}),i.showOnly&&i?.showOnly?.length>0?{type:"list",status:T(f),diff:I(f,i.showOnly)}:{type:"list",status:T(f),diff:f}};
-
-export { y as GRANULARITY, c as LIST_STATUS, u as STATUS, R as getListDiff, w as getObjectDiff, D as isEqual, p as isObject };
diff --git a/package.json b/package.json
index 61b0065..02202ee 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@donedeal0/superdiff",
-  "version": "2.0.0",
+  "version": "2.1.0",
   "description": "SuperDiff checks the changes between two objects or arrays. It returns a complete diff with relevant information for each property or piece of data",
   "main": "dist/index.js",
   "module": "dist/index.mjs",
@@ -18,6 +18,10 @@
   "bugs": {
     "url": "https://github.com/DoneDeal0/superdiff/issues"
   },
+  "funding": {
+    "type": "github",
+    "url": "https://github.com/sponsors/DoneDeal0"
+  },
   "readme": "./README.md",
   "release": {
     "branches": [
@@ -57,7 +61,9 @@
     "diff",
     "deep-diff",
     "comparison",
-    "compare"
+    "compare",
+    "stream",
+    "streaming"
   ],
   "scripts": {
     "build": "tsup",
diff --git a/src/lib/object-diff/object-diff.test.ts b/src/lib/object-diff/object-diff.test.ts
index ab38818..62295c3 100644
--- a/src/lib/object-diff/object-diff.test.ts
+++ b/src/lib/object-diff/object-diff.test.ts
@@ -1,4 +1,4 @@
-import { GRANULARITY, OBJECT_STATUS } from "../../models/object";
+import { GRANULARITY, OBJECT_STATUS } from "@models/object";
 import { getObjectDiff } from ".";
 
 describe("getObjectDiff", () => {
diff --git a/src/models/list/index.ts b/src/models/list/index.ts
index 79fc9ba..8616faa 100644
--- a/src/models/list/index.ts
+++ b/src/models/list/index.ts
@@ -6,8 +6,6 @@ export enum LIST_STATUS {
   MOVED = "moved",
 }
 
-export type ListData = unknown;
-
 export type ListDiffOptions = {
   showOnly?: `${LIST_STATUS}`[];
   referenceProperty?: string;
@@ -26,7 +24,7 @@ export type ListDiff = {
   type: "list";
   status: LIST_STATUS;
   diff: {
-    value: ListData;
+    value: unknown;
     prevIndex: number | null;
     newIndex: number | null;
     indexDiff: number | null;
diff --git a/src/models/stream/index.ts b/src/models/stream/index.ts
index d932ebd..d5d0df4 100644
--- a/src/models/stream/index.ts
+++ b/src/models/stream/index.ts
@@ -17,7 +17,7 @@ export type StreamReferences<T extends Record<string, unknown>> = Map<
 >;
 
 export type ListStreamOptions = {
-  chunksSize?: number; // 0 by default. If 0, stream will be live
+  chunksSize?: number; // 0 by default.
   showOnly?: `${LIST_STATUS}`[];
   considerMoveAsUpdate?: boolean;
 };