initial update

This commit is contained in:
jackbeeby
2025-05-15 13:32:55 +10:00
commit 7b07a49fbe
4412 changed files with 909535 additions and 0 deletions

218
node_modules/@wry/equality/src/index.ts generated vendored Normal file
View File

@@ -0,0 +1,218 @@
const { toString, hasOwnProperty } = Object.prototype;
const fnToStr = Function.prototype.toString;
const previousComparisons = new Map<object, Set<object>>();
/**
* Performs a deep equality check on two JavaScript values, tolerating cycles.
*/
export function equal(a: any, b: any): boolean {
try {
return check(a, b);
} finally {
previousComparisons.clear();
}
}
// Allow default imports as well.
export default equal;
function check(a: any, b: any): boolean {
// If the two values are strictly equal, our job is easy.
if (a === b) {
return true;
}
// Object.prototype.toString returns a representation of the runtime type of
// the given value that is considerably more precise than typeof.
const aTag = toString.call(a);
const bTag = toString.call(b);
// If the runtime types of a and b are different, they could maybe be equal
// under some interpretation of equality, but for simplicity and performance
// we just return false instead.
if (aTag !== bTag) {
return false;
}
switch (aTag) {
case '[object Array]':
// Arrays are a lot like other objects, but we can cheaply compare their
// lengths as a short-cut before comparing their elements.
if (a.length !== b.length) return false;
// Fall through to object case...
case '[object Object]': {
if (previouslyCompared(a, b)) return true;
const aKeys = definedKeys(a);
const bKeys = definedKeys(b);
// If `a` and `b` have a different number of enumerable keys, they
// must be different.
const keyCount = aKeys.length;
if (keyCount !== bKeys.length) return false;
// Now make sure they have the same keys.
for (let k = 0; k < keyCount; ++k) {
if (!hasOwnProperty.call(b, aKeys[k])) {
return false;
}
}
// Finally, check deep equality of all child properties.
for (let k = 0; k < keyCount; ++k) {
const key = aKeys[k];
if (!check(a[key], b[key])) {
return false;
}
}
return true;
}
case '[object Error]':
return a.name === b.name && a.message === b.message;
case '[object Number]':
// Handle NaN, which is !== itself.
if (a !== a) return b !== b;
// Fall through to shared +a === +b case...
case '[object Boolean]':
case '[object Date]':
return +a === +b;
case '[object RegExp]':
case '[object String]':
return a == `${b}`;
case '[object Map]':
case '[object Set]': {
if (a.size !== b.size) return false;
if (previouslyCompared(a, b)) return true;
const aIterator = a.entries();
const isMap = aTag === '[object Map]';
while (true) {
const info = aIterator.next();
if (info.done) break;
// If a instanceof Set, aValue === aKey.
const [aKey, aValue] = info.value;
// So this works the same way for both Set and Map.
if (!b.has(aKey)) {
return false;
}
// However, we care about deep equality of values only when dealing
// with Map structures.
if (isMap && !check(aValue, b.get(aKey))) {
return false;
}
}
return true;
}
case '[object Uint16Array]':
case '[object Uint8Array]': // Buffer, in Node.js.
case '[object Uint32Array]':
case '[object Int32Array]':
case '[object Int8Array]':
case '[object Int16Array]':
case '[object ArrayBuffer]':
// DataView doesn't need these conversions, but the equality check is
// otherwise the same.
a = new Uint8Array(a);
b = new Uint8Array(b);
// Fall through...
case '[object DataView]': {
let len = a.byteLength;
if (len === b.byteLength) {
while (len-- && a[len] === b[len]) {
// Keep looping as long as the bytes are equal.
}
}
return len === -1;
}
case '[object AsyncFunction]':
case '[object GeneratorFunction]':
case '[object AsyncGeneratorFunction]':
case '[object Function]': {
const aCode = fnToStr.call(a);
if (aCode !== fnToStr.call(b)) {
return false;
}
// We consider non-native functions equal if they have the same code
// (native functions require === because their code is censored).
// Note that this behavior is not entirely sound, since !== function
// objects with the same code can behave differently depending on
// their closure scope. However, any function can behave differently
// depending on the values of its input arguments (including this)
// and its calling context (including its closure scope), even
// though the function object is === to itself; and it is entirely
// possible for functions that are not === to behave exactly the
// same under all conceivable circumstances. Because none of these
// factors are statically decidable in JavaScript, JS function
// equality is not well-defined. This ambiguity allows us to
// consider the best possible heuristic among various imperfect
// options, and equating non-native functions that have the same
// code has enormous practical benefits, such as when comparing
// functions that are repeatedly passed as fresh function
// expressions within objects that are otherwise deeply equal. Since
// any function created from the same syntactic expression (in the
// same code location) will always stringify to the same code
// according to fnToStr.call, we can reasonably expect these
// repeatedly passed function expressions to have the same code, and
// thus behave "the same" (with all the caveats mentioned above),
// even though the runtime function objects are !== to one another.
return !endsWith(aCode, nativeCodeSuffix);
}
}
// Otherwise the values are not equal.
return false;
}
function definedKeys<TObject extends object>(obj: TObject) {
// Remember that the second argument to Array.prototype.filter will be
// used as `this` within the callback function.
return Object.keys(obj).filter(isDefinedKey, obj);
}
function isDefinedKey<TObject extends object>(
this: TObject,
key: keyof TObject,
) {
return this[key] !== void 0;
}
const nativeCodeSuffix = "{ [native code] }";
function endsWith(full: string, suffix: string) {
const fromIndex = full.length - suffix.length;
return fromIndex >= 0 &&
full.indexOf(suffix, fromIndex) === fromIndex;
}
function previouslyCompared(a: object, b: object): boolean {
// Though cyclic references can make an object graph appear infinite from the
// perspective of a depth-first traversal, the graph still contains a finite
// number of distinct object references. We use the previousComparisons cache
// to avoid comparing the same pair of object references more than once, which
// guarantees termination (even if we end up comparing every object in one
// graph to every object in the other graph, which is extremely unlikely),
// while still allowing weird isomorphic structures (like rings with different
// lengths) a chance to pass the equality test.
let bSet = previousComparisons.get(a);
if (bSet) {
// Return true here because we can be sure false will be returned somewhere
// else if the objects are not equivalent.
if (bSet.has(b)) return true;
} else {
previousComparisons.set(a, bSet = new Set);
}
bSet.add(b);
return false;
}

411
node_modules/@wry/equality/src/tests/main.ts generated vendored Normal file
View File

@@ -0,0 +1,411 @@
import * as assert from "assert";
import defaultEqual, { equal } from "../index.js";
function toStr(value: any) {
try {
return JSON.stringify(value);
} catch {
return String(value);
}
}
function assertEqual(a: any, b: any) {
assert.strictEqual(equal(a, b), true, `unexpectedly not equal(${toStr(a)}}, ${toStr(b)})`);
assert.strictEqual(equal(b, a), true, `unexpectedly not equal(${toStr(b)}, ${toStr(a)})`);
}
function assertNotEqual(a: any, b: any) {
assert.strictEqual(equal(a, b), false, `unexpectedly equal(${toStr(a)}, ${toStr(b)})`);
assert.strictEqual(equal(b, a), false, `unexpectedly equal(${toStr(b)}, ${toStr(a)})`);
}
describe("equality", function () {
it("should work with named and default imports", function () {
assert.strictEqual(defaultEqual, equal);
});
it("should work for primitive types", function () {
assertEqual(2 + 2, 4);
assertNotEqual(2 + 2, 5);
assertEqual("oyez", "oyez");
assertNotEqual("oyez", "onoz");
assertEqual(null, null);
assertEqual(void 0, void 1);
assertEqual(NaN, NaN);
assertNotEqual(void 0, null);
assertNotEqual(void 0, false);
assertNotEqual(false, null);
assertNotEqual(0, null);
assertNotEqual(0, false);
assertNotEqual(0, void 0);
assertEqual(123, new Number(123));
assertEqual(true, new Boolean(true));
assertEqual(false, new Boolean(false));
assertEqual("oyez", new String("oyez"));
});
it("should work for arrays", function () {
assertEqual([1, 2, 3], [1, 2, 3]);
assertEqual([1, [2], 3], [1, [2], 3]);
const a: any[] = [1];
a.push(a, 2);
const b: any[] = [1];
b.push(b, 2);
assertEqual(a, b);
assertEqual(
[1, /*hole*/, 3],
[1, /*hole*/, 3],
);
assertEqual(
[1, /*hole*/, 3],
[1, void 0, 3],
);
// Not equal because the arrays are a different length.
assertNotEqual(
[1, 2, /*hole*/,],
[1, 2],
);
});
it("should work for objects", function () {
assertEqual({
a: 1,
b: 2,
}, {
b: 2,
a: 1,
});
assertNotEqual({
a: 1,
b: 2,
c: 3,
}, {
b: 2,
a: 1,
});
const a: any = {};
a.self = a;
const b: any = {};
b.self = b;
assertEqual(a, b);
b.foo = 42;
assertNotEqual(a, b);
});
it("should consider undefined and missing object properties equivalent", function () {
assertEqual({
a: 1,
b: void 0,
c: 3,
}, {
a: 1,
c: 3,
});
assertEqual({
a: void 0,
b: void 0,
c: void 0,
}, {});
});
it("should work for Error objects", function () {
assertEqual(new Error("oyez"), new Error("oyez"));
assertNotEqual(new Error("oyez"), new Error("onoz"));
});
it("should work for Date objects", function () {
const now = new Date;
const alsoNow = new Date(+now);
assert.notStrictEqual(now, alsoNow);
assertEqual(now, alsoNow);
const later = new Date(+now + 10);
assertNotEqual(now, later);
});
it("should work for RegExp objects", function () {
assert.notStrictEqual(/xy/, /xy/);
assertEqual(/xy/img, /xy/mgi);
assertNotEqual(/xy/img, /x.y/img);
});
it("should work for Set objects", function () {
assertEqual(
new Set().add(1).add(2).add(3).add(2),
new Set().add(3).add(1).add(2).add(1),
);
const obj = {};
assertEqual(
new Set().add(1).add(obj).add(3).add(2),
new Set().add(3).add(obj).add(2).add(1),
);
assertNotEqual(
new Set(),
new Set().add(void 0),
);
});
it("should work for Map objects", function () {
assertEqual(
new Map().set(1, 2).set(2, 3),
new Map().set(2, 3).set(1, 2),
);
assertEqual(
new Map().set(1, 2).set(2, 3).set(1, 0),
new Map().set(2, 3).set(1, 2).set(1, 0),
);
assertNotEqual(
new Map().set(1, 2).set(2, 3).set(1, 0),
new Map().set(2, 3).set(1, 2).set(3, 4),
);
assertEqual(
new Map().set(1, new Set().add(2)),
new Map().set(1, new Set().add(2)),
);
assertNotEqual(
new Map().set(1, new Set().add(2)),
new Map().set(1, new Set().add(2).add(3)),
);
const a = new Map;
a.set(a, a);
const b = new Map;
b.set(a, b);
assertEqual(a, b);
a.set(1, 2);
b.set(1, 2);
assertEqual(a, b);
a.set(3, 4);
assertNotEqual(a, b);
});
it("should tolerate cycles", function () {
const a: any[] = [];
a.push(a);
const b: any[] = [];
b.push(b);
assertEqual(a, b);
assertEqual([a], b);
assertEqual(a, [b]);
assertEqual([a], [b]);
a.push(1);
b.push(1);
assertEqual(a, b);
assertEqual([a, 1], b);
assertEqual(a, [b, 1]);
const ring1 = { self: { self: { self: {} as any }}};
ring1.self.self.self.self = ring1;
const ring2 = { self: { self: {} as any }};
ring2.self.self.self = ring2;
assertEqual(ring1, ring2);
ring1.self.self.self.self = ring1.self;
assertEqual(ring1, ring2);
});
it("should not care about repeated references", function () {
const r = { foo: 42 };
assertEqual(
[r, r, r],
JSON.parse(JSON.stringify([r, r, r])),
);
});
it("should equate non-native functions with the same code", function () {
const fn = () => 1234;
assertEqual(fn, fn);
assertEqual(fn, () => 1234);
// These functions are behaviorally the same, but there's no way to
// decide that question statically.
assertNotEqual(
(a: number) => a + 1,
(b: number) => b + 1,
);
assertEqual(
{ before: 123, fn() { return 4 }, after: 321 },
{ after: 321, before: 123, fn() { return 4 } },
);
assertEqual(Object.assign, Object.assign);
// Since these slice methods are native functions, they happen to have
// exactly the same (censored) code, but we can test their equality by
// reference, since we can generally assume native functions are pure.
assertNotEqual(String.prototype.slice, Array.prototype.slice);
assertEqual(
Function.prototype.toString.call(String.prototype.slice),
Function.prototype.toString.call(Array.prototype.slice),
);
});
it("should equate async functions with the same code", function () {
const fn = async () => 1234;
assertEqual(fn, fn);
assertEqual(fn, async () => 1234);
// These functions are behaviorally the same, but there's no way to
// decide that question statically.
assertNotEqual(
async (a: number) => a + 1,
async (b: number) => b + 1,
);
assertEqual(
{ before: 123, async fn() { return 4 }, after: 321 },
{ after: 321, before: 123, async fn() { return 4 } },
);
});
it("should equate generator functions with the same code", function () {
const fn = function *(): Generator<number> { return yield 1234 };
assertEqual(fn, fn);
assertEqual(fn, function *(): Generator<number> { return yield 1234 });
// These functions are behaviorally the same, but there's no way to
// decide that question statically.
assertNotEqual(
function *(a: number): Generator<number> { return yield a + 1 },
function *(b: number): Generator<number> { return yield b + 1 },
);
assertEqual(
{ before: 123, *fn() { return 4 }, after: 321 },
{ after: 321, before: 123, *fn() { return 4 } },
);
});
it("should equate async generator functions with the same code", function () {
const fn = async function *(): AsyncGenerator<number> { return await (yield 1234) };
assertEqual(fn, fn);
assertEqual(fn, async function *(): AsyncGenerator<number> { return await (yield 1234) });
// These functions are behaviorally the same, but there's no way to
// decide that question statically.
assertNotEqual(
async function *(a: number): AsyncGenerator<number> { return yield a + 1 },
async function *(b: number): AsyncGenerator<number> { return yield b + 1 },
);
assertEqual(
{ before: 123, async *fn() { return 4 }, after: 321 },
{ after: 321, before: 123, async *fn() { return 4 } },
);
});
it('should work for Array Buffers And Typed Arrays', function () {
const hello = new Int8Array([1, 2, 3, 4, 5]);
const world = new Int8Array([1, 2, 3, 4, 5]);
const small = new Int8Array([1, 2, 3, 4])
assertEqual(new DataView(new ArrayBuffer(4)), new DataView(new ArrayBuffer(4)))
assertEqual(new Int16Array([42]), new Int16Array([42]));
assertEqual(new Int32Array(new ArrayBuffer(4)), new Int32Array(new ArrayBuffer(4)))
assertEqual(new ArrayBuffer(2), new ArrayBuffer(2))
assertNotEqual(new Int16Array([1, 2, 3]), new Int16Array([1, 2]));
assertNotEqual(new Int16Array([1, 2, 3]), new Uint16Array([1, 2, 3]))
assertNotEqual(new Int16Array([1, 2, 3]), new Int8Array([1, 2, 3]))
assertNotEqual(new Int32Array(8), new Uint32Array(8));
assertNotEqual(new Int32Array(new ArrayBuffer(8)), new Int32Array(Array.from({ length: 8 })));
assertNotEqual(new ArrayBuffer(1), new ArrayBuffer(2));
assertEqual(hello, world);
assertEqual(hello.buffer, world.buffer);
assertEqual(new DataView(hello.buffer), new DataView(world.buffer));
assertNotEqual(small, world)
assertNotEqual(small.buffer, world.buffer);
assertNotEqual(new DataView(small.buffer), new DataView(world.buffer));
});
it('should work with a kitchen sink', function () {
const foo = {
foo: 'value1',
bar: new Set([1, 2, 3]),
baz: /foo/i,
bat: {
hello: new Map([ ['hello', 'world'] ]),
world: {
aaa: new Map([
[{ foo: /bar/ }, 'sub sub value1'],
]),
bbb: [1, 2, { prop2:1, prop:2 }, 4, 5]
}
},
quz: new Set([{ a:1 , b:2 }]),
qut: new Date(2016, 2, 10),
qar: new Uint8Array([1, 2, 3, 4, 5]),
}
const bar = {
quz: new Set([{ a:1 , b:2 }]),
baz: /foo/i,
foo: 'value1',
bar: new Set([1, 2, 3]),
qar: new Uint8Array([1, 2, 3, 4, 5]),
qut: new Date('2016/03/10'),
bat: {
world: {
aaa: new Map([
[{ foo: /bar/ }, 'sub sub value1'],
]),
bbb: [1, 2, { prop2:1, prop:2 }, 4, 5]
},
hello: new Map([ ['hello', 'world'] ])
}
};
assertNotEqual(foo, bar)
});
describe("performance", function () {
const limit = 1e6;
this.timeout(20000);
function check(a: any, bEqual: any, bNotEqual: any) {
for (let i = 0; i < limit; ++i) {
assert.strictEqual(equal(a, bEqual), true);
assert.strictEqual(equal(bEqual, a), true);
assert.strictEqual(equal(a, bNotEqual), false);
assert.strictEqual(equal(bNotEqual, a), false);
}
}
it("should be fast for arrays", function () {
const a = [1, 2, 3];
check(a, a.slice(0), [1, 2, 4]);
});
it("should be fast for objects", function () {
const a = { a: 1, b: 2, c: 3 };
check(a, { ...a }, { a: 1, b: 3 });
});
it("should be fast for strings", function () {
check('foo', new String('foo'), 'bar');
});
it("should be fast for functions", function () {
check(() => 123, () => 123, () => 321);
});
});
});