Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export default [
{ settings: { react: { version: "detect" } } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
{ rules: { "preserve-caught-error": "off" } },
];
2 changes: 0 additions & 2 deletions jest.setup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { TextEncoder, TextDecoder } from "util";

//@ts-expect-error - the TextEncoder is valid
global.TextEncoder = TextEncoder;
//@ts-expect-error - the TextDecoder is valid
global.TextDecoder = TextDecoder;
7,075 changes: 4,134 additions & 2,941 deletions package-lock.json

Large diffs are not rendered by default.

101 changes: 51 additions & 50 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@donedeal0/superdiff",
"version": "4.2.2",
"version": "4.2.3",
"type": "module",
"description": "Superdiff provides a rich and readable diff for arrays, objects, texts and coordinates. It supports stream and file inputs for handling large datasets efficiently, is battle-tested, has zero dependencies, and offers a top-tier performance.",
"main": "dist/index.js",
Expand Down Expand Up @@ -45,42 +45,42 @@
]
]
},
"keywords": [
"array-comparison",
"array-diff",
"chunks",
"code-diff",
"compare",
"comparison-tool",
"coordinates",
"data-diff",
"deep-comparison",
"deep-diff",
"deep-object-diff",
"diff",
"file-diff",
"geo-diff",
"geo-distance",
"haversine",
"isequal",
"json-diff",
"json",
"lat-long",
"lcs",
"list-diff",
"object-comparison",
"object-diff",
"object-difference",
"object",
"stream-diff",
"streaming-diff",
"streaming",
"string-diff",
"text-diff",
"textdiff",
"vincenty",
"word-diff"
],
"keywords": [
"array-comparison",
"array-diff",
"chunks",
"code-diff",
"compare",
"comparison-tool",
"coordinates",
"data-diff",
"deep-comparison",
"deep-diff",
"deep-object-diff",
"diff",
"file-diff",
"geo-diff",
"geo-distance",
"haversine",
"isequal",
"json-diff",
"json",
"lat-long",
"lcs",
"list-diff",
"object-comparison",
"object-diff",
"object-difference",
"object",
"stream-diff",
"streaming-diff",
"streaming",
"string-diff",
"text-diff",
"textdiff",
"vincenty",
"word-diff"
],
"scripts": {
"benchmark": "tsx benchmark/index.ts",
"build": "tsup",
Expand All @@ -93,32 +93,33 @@
"tsc": "tsc --noEmit --incremental"
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"@eslint/js": "^10.0.1",
"@semantic-release/exec": "^7.1.0",
"@semantic-release/git": "^10.0.1",
"@swc/core": "^1.15.8",
"@swc/core": "^1.15.40",
"@swc/jest": "^0.2.39",
"@types/arr-diff": "^4.0.3",
"@types/deep-diff": "^1.0.5",
"@types/jest": "^29.5.14",
"@types/jest": "^30.0.0",
"@types/node": "^25.9.1",
"arr-diff": "^4.0.0",
"blob-polyfill": "^9.0.20240710",
"deep-diff": "^1.0.2",
"deep-object-diff": "^1.1.9",
"diff": "^8.0.2",
"eslint": "^9.21.0",
"eslint": "^10.4.0",
"husky": "^9.1.7",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jsdom": "^26.0.0",
"jest": "^30.4.2",
"jest-environment-jsdom": "^30.4.1",
"jsdom": "^29.1.1",
"microdiff": "^1.5.0",
"prettier": "^3.5.2",
"swc-loader": "^0.2.6",
"prettier": "^3.8.3",
"swc-loader": "^0.2.7",
"ts-node": "^10.9.2",
"tsup": "^8.5.1",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.52.0",
"web-streams-polyfill": "^4.2.0"
"tsx": "^4.22.3",
"typescript": "^6.0.3",
"typescript-eslint": "^8.60.0",
"web-streams-polyfill": "^4.3.0"
}
}
18 changes: 9 additions & 9 deletions src/lib/list-diff/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ function getDiffStatus(statusMap: Set<ListStatus>): ListStatus {
return ListStatus.UPDATED;
}

function getLeanDiff(
diff: ListDiff["diff"],
function getLeanDiff<T>(
diff: ListDiff<T>["diff"],
showOnly: ListDiffOptions["showOnly"] = [],
): ListDiff["diff"] {
): ListDiff<T>["diff"] {
const set = new Set(showOnly);
const leanDiff: ListDiff["diff"] = [];
const leanDiff: ListDiff<T>["diff"] = [];
for (let i = 0; i < diff.length; i++) {
if (set.has(diff[i].status)) {
leanDiff.push(diff[i]);
Expand All @@ -37,8 +37,8 @@ function formatSingleListDiff<T>(
listData: T[],
status: ListStatus,
options: ListDiffOptions,
): ListDiff {
const diff: ListDiff["diff"] = new Array(listData.length);
): ListDiff<T> {
const diff: ListDiff<T>["diff"] = new Array(listData.length);
const isAdded = status === ListStatus.ADDED;
for (let i = 0; i < listData.length; i++) {
diff[i] = {
Expand Down Expand Up @@ -88,8 +88,8 @@ function getNextStatus(
export const getListDiff = <T>(
prevList: T[] | undefined | null,
nextList: T[] | undefined | null,
options: ListDiffOptions = DEFAULT_LIST_DIFF_OPTIONS,
): ListDiff => {
options: ListDiffOptions<T> = DEFAULT_LIST_DIFF_OPTIONS,
): ListDiff<T> => {
if (!prevList && !nextList) {
return { type: "list", status: ListStatus.EQUAL, diff: [] };
}
Expand All @@ -100,7 +100,7 @@ export const getListDiff = <T>(
return formatSingleListDiff(prevList as T[], ListStatus.DELETED, options);
}

const diff: ListDiff["diff"] = [];
const diff: ListDiff<T>["diff"] = [];
const previousMap = new Map<string, ListData<T>>();
const statusMap = new Set<ListStatus>();
const { referenceKey, ignoreArrayOrder } = options;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/stream-list-diff/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async function getDiffChunks<T extends Record<string, unknown>>(
nextDataBuffer.delete(ref);
const isDataEqual =
JSON.stringify(chunk) === JSON.stringify(relatedChunk.data);
const isEqual = (relatedChunk.index as number) - currentPrevIndex === 0;
const isEqual = relatedChunk.index - currentPrevIndex === 0;
if (isDataEqual) {
handleDiffChunk(
{
Expand Down Expand Up @@ -105,7 +105,7 @@ async function getDiffChunks<T extends Record<string, unknown>>(
prevDataBuffer.delete(ref);
const isDataEqual =
JSON.stringify(chunk) === JSON.stringify(relatedChunk.data);
const isEqual = currentNextIndex - (relatedChunk.index as number) === 0;
const isEqual = currentNextIndex - relatedChunk.index === 0;
if (isDataEqual) {
handleDiffChunk(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
* @jest-environment jsdom
*/
import "blob-polyfill";
import { ReadableStream } from "web-streams-polyfill";
import { ReadableStream as ReadableStreamPolyfill } from "web-streams-polyfill";
import prevListFile from "@mocks/prevList.json";
import nextListFile from "@mocks/nextList.json";
import { ListStatus } from "@models/list";
import { StreamListDiff } from "@models/stream";
import { streamListDiff } from ".";

// @ts-expect-error - the ReadableStream polyfill is necessary to test ReadableStream in a Node environment.
global.ReadableStream = ReadableStream;
global.ReadableStream = ReadableStreamPolyfill;

describe("data emission", () => {
it("emits 'data' event and consider the all the nextList added if no prevList is provided", (done) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
* @jest-environment jsdom
*/
import "blob-polyfill";
import "jsdom";
import { ReadableStream } from "web-streams-polyfill";
import { ReadableStream as ReadableStreamPolyfill } from "web-streams-polyfill";
import prevListFile from "@mocks/prevList.json";
import nextListFile from "@mocks/nextList.json";
import { ListStatus } from "@models/list";
Expand Down Expand Up @@ -55,7 +54,7 @@ class Worker {
// @ts-expect-error - a Worker polyfill is necessary to test a Web Worker in a Node environment.
global.Worker = Worker;
// @ts-expect-error - the ReadableStream polyfill is necessary to test ReadableStream in a Node environment.
global.ReadableStream = ReadableStream;
global.ReadableStream = ReadableStreamPolyfill;

describe("data emission", () => {
it("emits 'data' event and consider the all the nextList added if no prevList is provided", (done) => {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/stream-list-diff/server/worker/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export async function generateWorker<T extends Record<string, unknown>>(
worker.terminate();
}
});
worker.on(WorkerEvent.Error, (err) =>
worker.on(WorkerEvent.Error, (err: Error) =>
emitter.emit(StreamEvent.Error, new Error(err.message)),
);
} catch (err) {
Expand Down
1 change: 0 additions & 1 deletion src/lib/stream-list-diff/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export function outputDiffChunk<T extends Record<string, unknown>>(
emitter: IEmitter<T>,
) {
let chunks: StreamListDiff<T>[] = [];

function handleDiffChunk(
chunk: StreamListDiff<T>,
options: ListStreamOptions,
Expand Down
8 changes: 3 additions & 5 deletions src/models/emitter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ export type EmitterEvents<T extends Record<string, unknown>> = {
finish: [];
};

export type IEmitter<T extends Record<string, unknown>> = EventEmitter<{
data: [StreamListDiff<T>[]];
error: [Error];
finish: [];
}>;
export type IEmitter<T extends Record<string, unknown>> = EventEmitter<
EmitterEvents<T>
>;

export class EventEmitter<Events extends Record<string, unknown[]>> {
private events: Record<string, Listener<unknown[]>[]> = {};
Expand Down
81 changes: 44 additions & 37 deletions src/models/geo/index.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,61 @@


export type GeoUnit = "centimeter" | "foot" | "inch" | "kilometer" | "meter" | "mile" | "mile-scandinavian" | "millimeter" | "yard"
export type GeoUnit =
| "centimeter"
| "foot"
| "inch"
| "kilometer"
| "meter"
| "mile"
| "mile-scandinavian"
| "millimeter"
| "yard";

export type GeoDiffOptions = {
unit?: GeoUnit;
maxDecimals?: number;
accuracy?: "normal" | "high"
locale?: Intl.Locale | string;
unit?: GeoUnit;
maxDecimals?: number;
accuracy?: "normal" | "high";
locale?: Intl.Locale | string;
};

export const DEFAULT_GEODIFF_OPTIONS: Required<GeoDiffOptions> = {
unit: "kilometer",
maxDecimals: 2,
accuracy: "normal",
locale: "en-US"
}
unit: "kilometer",
maxDecimals: 2,
accuracy: "normal",
locale: "en-US",
};

export enum GeoStatus {
ADDED = "added",
DELETED = "deleted",
ERROR = "error",
EQUAL = "equal",
UPDATED = "updated",
ADDED = "added",
DELETED = "deleted",
ERROR = "error",
EQUAL = "equal",
UPDATED = "updated",
}

type Longitude = number;
type Latitude = number;

export type GeoCoordinates = [Longitude, Latitude]
export type GeoCoordinates = [Longitude, Latitude];
export enum GeoDirection {
East = "east",
North = "north",
South = "south",
West = "west",
NorthEast = "north-east",
NorthWest = "north-west",
SouthEast = "south-east",
SouthWest = "south-west",
Stationary = "stationary"
East = "east",
North = "north",
South = "south",
West = "west",
NorthEast = "north-east",
NorthWest = "north-west",
SouthEast = "south-east",
SouthWest = "south-west",
Stationary = "stationary",
}

export type GeoDiff = {
type: "geo";
status: GeoStatus;
diff: {
coordinates: GeoCoordinates | null;
previousCoordinates: GeoCoordinates | null;
distance: number;
unit: GeoUnit,
label: string,
direction: GeoDirection;
};
type: "geo";
status: `${GeoStatus}`;
diff: {
coordinates: GeoCoordinates | null;
previousCoordinates: GeoCoordinates | null;
distance: number;
unit: GeoUnit;
label: string;
direction: GeoDirection;
};
};
Loading
Loading