venerate 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # treetrunks
2
+
3
+ <a aria-label="NPM version" href="https://www.npmjs.com/package/treetrunks">
4
+ <img alt="NPM Version" src="https://img.shields.io/npm/v/treetrunks?style=for-the-badge">
5
+ </a>
6
+ <a aria-label="Dependencies 0" href="https://www.npmjs.com/package/treetrunks">
7
+ <img alt="Dependencies 0" src=" https://img.shields.io/badge/dependencies-0-0?style=for-the-badge">
8
+ </a>
9
+ <a href="https://bundlephobia.com/result?p=treetrunks">
10
+ <img alt="Bundlephobia" src="https://img.shields.io/bundlephobia/minzip/treetrunks?style=for-the-badge">
11
+ </a>
12
+ <a aria-label="Coverage" href="https://recoverage.cloud/">
13
+ <img alt="Coverage" src="https://img.shields.io/endpoint?url=https%3A%2F%2Frecoverage.cloud%2Fshields%2FS1ikz1yFmk93qbAI7lLnu%2Ftreetrunks">
14
+ </a>
15
+
16
+ ```sh
17
+ npm i treetrunks
18
+ ```
19
+
20
+ treetrunks is a convenient way to define type-safe routers.
21
+
22
+ ## overview
23
+
24
+ a tree structure affords many possible routes through the tree
25
+
26
+ ```typescript
27
+ import type { Tree, TreePath } from "treetrunks";
28
+ import { optional, required } from "treetrunks";
29
+
30
+ const greetingTree = required({
31
+ hello: optional({
32
+ world: null,
33
+ $name: optional({
34
+ good: required({
35
+ morning: null,
36
+ }),
37
+ }),
38
+ }),
39
+ }) satisfies Tree;
40
+
41
+ const validPaths: TreePath<typeof greetingTree>[] = [
42
+ [`hello`],
43
+ [`hello`, `world`],
44
+ [`hello`, `jeremybanka`],
45
+ [`hello`, `treetrunks`, `good`, `morning`],
46
+ ];
47
+ ```
48
+
49
+ the `optional` and `required` functions help determine what routes are valid.
50
+
51
+ note that,
52
+
53
+ - `"hello"` is required
54
+ - `"world"`, or any `$name` is optional
55
+ - `"good morning"` is optional
@@ -0,0 +1,48 @@
1
+ //#region src/venerate.d.ts
2
+ /**
3
+ * Cross-realm-safe check for anything that extends Set.
4
+ */
5
+ declare function isSet(value: unknown): value is Set<any>;
6
+ /**
7
+ * Materializes an unknown iterable as a Set, without providing for mutation.
8
+ * Does not copy the input if it is already a Set.
9
+ */
10
+ declare const toSet: <T>(it: Iterable<T>) => ReadonlySet<T>;
11
+ /**
12
+ * Lazily yield the union of A, B, C, ... preserving first-seen order.
13
+ * Only a small "seen" Set is maintained; inputs are streamed.
14
+ */
15
+ declare function union<T>(...iterables: Iterable<T>[]): Iterable<T>;
16
+ /**
17
+ * Lazily yield A \ (B ∪ C ∪ ...), streaming A and pre-materializing removals.
18
+ */
19
+ declare function difference<T>(a: Iterable<T>, ...rest: Iterable<T>[]): Iterable<T>;
20
+ /**
21
+ * Lazily yield A ∩ B ∩ C ∩ ...
22
+ * Streams only the FIRST iterable; others are materialized as Sets for O(1) lookups.
23
+ * If no others are provided, yields a shallow copy of A.
24
+ */
25
+ declare function intersection<T>(a: Iterable<T>, ...rest: Iterable<T>[]): Iterable<T>;
26
+ /**
27
+ * Lazily yield A △ B (pairwise symmetric difference).
28
+ * Streams A and B once; materializes the opposite side for membership checks.
29
+ * - First yields items unique to A (preserving A's order)
30
+ * - Then yields items unique to B (preserving B's order)
31
+ */
32
+ declare function symmetricDifference<T>(a: Iterable<T>, b: Iterable<T>): Iterable<T>;
33
+ /**
34
+ * Variadic symmetric difference via left fold of the pairwise version.
35
+ * Still lazy in the sense that we only materialize small per-step Sets;
36
+ * each fold pass consumes its input generator.
37
+ */
38
+ declare function symmetricDifferenceVariadic<T>(first: Iterable<T>, ...rest: Iterable<T>[]): Iterable<T>;
39
+ /**
40
+ * Predicates (not lazy outputs, but efficient w/ early exits)
41
+ */
42
+ declare function isSubset<T>(a: Iterable<T>, b: Iterable<T>): boolean;
43
+ declare function isSuperset<T>(a: Iterable<T>, b: Iterable<T>): boolean;
44
+ declare function isDisjoint<T>(a: Iterable<T>, b: Iterable<T>): boolean;
45
+ declare function equals<T>(a: Iterable<T>, b: Iterable<T>): boolean;
46
+ //#endregion
47
+ export { difference, equals, intersection, isDisjoint, isSet, isSubset, isSuperset, symmetricDifference, symmetricDifferenceVariadic, toSet, union };
48
+ //# sourceMappingURL=venerate.d.ts.map
@@ -0,0 +1,109 @@
1
+ //#region src/venerate.ts
2
+ /**
3
+ * Cross-realm-safe check for anything that extends Set.
4
+ */
5
+ function isSet(value) {
6
+ if (value == null) return false;
7
+ try {
8
+ Set.prototype.has.call(value, Symbol.for(`@@brandCheck`));
9
+ return true;
10
+ } catch {
11
+ return false;
12
+ }
13
+ }
14
+ /**
15
+ * Materializes an unknown iterable as a Set, without providing for mutation.
16
+ * Does not copy the input if it is already a Set.
17
+ */
18
+ const toSet = (it) => isSet(it) ? it : new Set(it);
19
+ /**
20
+ * Lazily yield the union of A, B, C, ... preserving first-seen order.
21
+ * Only a small "seen" Set is maintained; inputs are streamed.
22
+ */
23
+ function* union(...iterables) {
24
+ const seen = /* @__PURE__ */ new Set();
25
+ for (const it of iterables) for (const v of it) if (!seen.has(v)) {
26
+ seen.add(v);
27
+ yield v;
28
+ }
29
+ }
30
+ /**
31
+ * Lazily yield A \ (B ∪ C ∪ ...), streaming A and pre-materializing removals.
32
+ */
33
+ function* difference(a, ...rest) {
34
+ if (rest.length === 0) {
35
+ yield* a;
36
+ return;
37
+ }
38
+ const remove = /* @__PURE__ */ new Set();
39
+ for (const it of rest) for (const v of it) remove.add(v);
40
+ for (const v of a) if (!remove.has(v)) yield v;
41
+ }
42
+ /**
43
+ * Lazily yield A ∩ B ∩ C ∩ ...
44
+ * Streams only the FIRST iterable; others are materialized as Sets for O(1) lookups.
45
+ * If no others are provided, yields a shallow copy of A.
46
+ */
47
+ function* intersection(a, ...rest) {
48
+ if (rest.length === 0) {
49
+ yield* a;
50
+ return;
51
+ }
52
+ const sets = rest.map(toSet);
53
+ for (const v of a) if (sets.every((s) => s.has(v))) yield v;
54
+ }
55
+ /**
56
+ * Lazily yield A △ B (pairwise symmetric difference).
57
+ * Streams A and B once; materializes the opposite side for membership checks.
58
+ * - First yields items unique to A (preserving A's order)
59
+ * - Then yields items unique to B (preserving B's order)
60
+ */
61
+ function* symmetricDifference(a, b) {
62
+ const A = toSet(a);
63
+ const B = toSet(b);
64
+ for (const v of A) if (!B.has(v)) yield v;
65
+ for (const v of B) if (!A.has(v)) yield v;
66
+ }
67
+ /**
68
+ * Variadic symmetric difference via left fold of the pairwise version.
69
+ * Still lazy in the sense that we only materialize small per-step Sets;
70
+ * each fold pass consumes its input generator.
71
+ */
72
+ function* symmetricDifferenceVariadic(first, ...rest) {
73
+ let acc = first;
74
+ for (const it of rest) acc = symmetricDifference(acc, it);
75
+ yield* acc;
76
+ }
77
+ /**
78
+ * Predicates (not lazy outputs, but efficient w/ early exits)
79
+ */
80
+ function isSubset(a, b) {
81
+ const A = toSet(a);
82
+ const B = toSet(b);
83
+ if (A.size > B.size) return false;
84
+ for (const v of A) if (!B.has(v)) return false;
85
+ return true;
86
+ }
87
+ function isSuperset(a, b) {
88
+ return isSubset(b, a);
89
+ }
90
+ function isDisjoint(a, b) {
91
+ const A = toSet(a);
92
+ const B = toSet(b);
93
+ const aIsSmall = A.size <= B.size;
94
+ const small = aIsSmall ? A : B;
95
+ const big = aIsSmall ? B : A;
96
+ for (const v of small) if (big.has(v)) return false;
97
+ return true;
98
+ }
99
+ function equals(a, b) {
100
+ const A = toSet(a);
101
+ const B = toSet(b);
102
+ if (A.size !== B.size) return false;
103
+ for (const v of A) if (!B.has(v)) return false;
104
+ return true;
105
+ }
106
+
107
+ //#endregion
108
+ export { difference, equals, intersection, isDisjoint, isSet, isSubset, isSuperset, symmetricDifference, symmetricDifferenceVariadic, toSet, union };
109
+ //# sourceMappingURL=venerate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"venerate.js","names":["acc: Iterable<T>"],"sources":["../src/venerate.ts"],"sourcesContent":["/**\n * Cross-realm-safe check for anything that extends Set.\n */\nexport function isSet(value: unknown): value is Set<any> {\n\tif (value == null) return false\n\ttry {\n\t\t// brand check: throws TypeError unless `value` has [[SetData]]\n\t\tSet.prototype.has.call(value, Symbol.for(`@@brandCheck`))\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Materializes an unknown iterable as a Set, without providing for mutation.\n * Does not copy the input if it is already a Set.\n */\nexport const toSet = <T>(it: Iterable<T>): ReadonlySet<T> =>\n\tisSet(it) ? it : new Set(it)\n\n/**\n * Lazily yield the union of A, B, C, ... preserving first-seen order.\n * Only a small \"seen\" Set is maintained; inputs are streamed.\n */\nexport function* union<T>(...iterables: Iterable<T>[]): Iterable<T> {\n\tconst seen = new Set<T>()\n\tfor (const it of iterables) {\n\t\tfor (const v of it) {\n\t\t\tif (!seen.has(v)) {\n\t\t\t\tseen.add(v)\n\t\t\t\tyield v\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Lazily yield A \\ (B ∪ C ∪ ...), streaming A and pre-materializing removals.\n */\nexport function* difference<T>(\n\ta: Iterable<T>,\n\t...rest: Iterable<T>[]\n): Iterable<T> {\n\tif (rest.length === 0) {\n\t\tyield* a\n\t\treturn\n\t}\n\tconst remove = new Set<T>()\n\tfor (const it of rest) for (const v of it) remove.add(v)\n\tfor (const v of a) if (!remove.has(v)) yield v\n}\n\n/**\n * Lazily yield A ∩ B ∩ C ∩ ...\n * Streams only the FIRST iterable; others are materialized as Sets for O(1) lookups.\n * If no others are provided, yields a shallow copy of A.\n */\nexport function* intersection<T>(\n\ta: Iterable<T>,\n\t...rest: Iterable<T>[]\n): Iterable<T> {\n\tif (rest.length === 0) {\n\t\tyield* a\n\t\treturn\n\t}\n\tconst sets = rest.map(toSet)\n\tfor (const v of a) {\n\t\tif (sets.every((s) => s.has(v))) yield v\n\t}\n}\n\n/**\n * Lazily yield A △ B (pairwise symmetric difference).\n * Streams A and B once; materializes the opposite side for membership checks.\n * - First yields items unique to A (preserving A's order)\n * - Then yields items unique to B (preserving B's order)\n */\nexport function* symmetricDifference<T>(\n\ta: Iterable<T>,\n\tb: Iterable<T>,\n): Iterable<T> {\n\tconst A = toSet(a)\n\tconst B = toSet(b)\n\n\t// Items unique to A\n\tfor (const v of A) if (!B.has(v)) yield v\n\t// Items unique to B\n\tfor (const v of B) if (!A.has(v)) yield v\n}\n\n/**\n * Variadic symmetric difference via left fold of the pairwise version.\n * Still lazy in the sense that we only materialize small per-step Sets;\n * each fold pass consumes its input generator.\n */\nexport function* symmetricDifferenceVariadic<T>(\n\tfirst: Iterable<T>,\n\t...rest: Iterable<T>[]\n): Iterable<T> {\n\tlet acc: Iterable<T> = first\n\tfor (const it of rest) acc = symmetricDifference(acc, it)\n\tyield* acc\n}\n\n/**\n * Predicates (not lazy outputs, but efficient w/ early exits)\n */\nexport function isSubset<T>(a: Iterable<T>, b: Iterable<T>): boolean {\n\tconst A = toSet(a)\n\tconst B = toSet(b)\n\tif (A.size > B.size) return false\n\tfor (const v of A) if (!B.has(v)) return false\n\treturn true\n}\n\nexport function isSuperset<T>(a: Iterable<T>, b: Iterable<T>): boolean {\n\treturn isSubset(b, a)\n}\n\nexport function isDisjoint<T>(a: Iterable<T>, b: Iterable<T>): boolean {\n\tconst A = toSet(a)\n\tconst B = toSet(b)\n\tconst aIsSmall = A.size <= B.size\n\tconst small = aIsSmall ? A : B\n\tconst big = aIsSmall ? B : A\n\tfor (const v of small) if (big.has(v)) return false\n\treturn true\n}\n\nexport function equals<T>(a: Iterable<T>, b: Iterable<T>): boolean {\n\tconst A = toSet(a)\n\tconst B = toSet(b)\n\tif (A.size !== B.size) return false\n\tfor (const v of A) if (!B.has(v)) return false\n\treturn true\n}\n"],"mappings":";;;;AAGA,SAAgB,MAAM,OAAmC;AACxD,KAAI,SAAS,KAAM,QAAO;AAC1B,KAAI;AAEH,MAAI,UAAU,IAAI,KAAK,OAAO,OAAO,IAAI;AACzC,SAAO;CACP,QAAO;AACP,SAAO;CACP;AACD;;;;;AAMD,MAAa,SAAY,OACxB,MAAM,MAAM,KAAK,IAAI,IAAI;;;;;AAM1B,UAAiB,MAAS,GAAG,WAAuC;CACnE,MAAM,uBAAO,IAAI;AACjB,MAAK,MAAM,MAAM,UAChB,MAAK,MAAM,KAAK,GACf,KAAI,CAAC,KAAK,IAAI,IAAI;AACjB,OAAK,IAAI;AACT,QAAM;CACN;AAGH;;;;AAKD,UAAiB,WAChB,GACA,GAAG,MACW;AACd,KAAI,KAAK,WAAW,GAAG;AACtB,SAAO;AACP;CACA;CACD,MAAM,yBAAS,IAAI;AACnB,MAAK,MAAM,MAAM,KAAM,MAAK,MAAM,KAAK,GAAI,QAAO,IAAI;AACtD,MAAK,MAAM,KAAK,EAAG,KAAI,CAAC,OAAO,IAAI,GAAI,OAAM;AAC7C;;;;;;AAOD,UAAiB,aAChB,GACA,GAAG,MACW;AACd,KAAI,KAAK,WAAW,GAAG;AACtB,SAAO;AACP;CACA;CACD,MAAM,OAAO,KAAK,IAAI;AACtB,MAAK,MAAM,KAAK,EACf,KAAI,KAAK,OAAO,MAAM,EAAE,IAAI,IAAK,OAAM;AAExC;;;;;;;AAQD,UAAiB,oBAChB,GACA,GACc;CACd,MAAM,IAAI,MAAM;CAChB,MAAM,IAAI,MAAM;AAGhB,MAAK,MAAM,KAAK,EAAG,KAAI,CAAC,EAAE,IAAI,GAAI,OAAM;AAExC,MAAK,MAAM,KAAK,EAAG,KAAI,CAAC,EAAE,IAAI,GAAI,OAAM;AACxC;;;;;;AAOD,UAAiB,4BAChB,OACA,GAAG,MACW;CACd,IAAIA,MAAmB;AACvB,MAAK,MAAM,MAAM,KAAM,OAAM,oBAAoB,KAAK;AACtD,QAAO;AACP;;;;AAKD,SAAgB,SAAY,GAAgB,GAAyB;CACpE,MAAM,IAAI,MAAM;CAChB,MAAM,IAAI,MAAM;AAChB,KAAI,EAAE,OAAO,EAAE,KAAM,QAAO;AAC5B,MAAK,MAAM,KAAK,EAAG,KAAI,CAAC,EAAE,IAAI,GAAI,QAAO;AACzC,QAAO;AACP;AAED,SAAgB,WAAc,GAAgB,GAAyB;AACtE,QAAO,SAAS,GAAG;AACnB;AAED,SAAgB,WAAc,GAAgB,GAAyB;CACtE,MAAM,IAAI,MAAM;CAChB,MAAM,IAAI,MAAM;CAChB,MAAM,WAAW,EAAE,QAAQ,EAAE;CAC7B,MAAM,QAAQ,WAAW,IAAI;CAC7B,MAAM,MAAM,WAAW,IAAI;AAC3B,MAAK,MAAM,KAAK,MAAO,KAAI,IAAI,IAAI,GAAI,QAAO;AAC9C,QAAO;AACP;AAED,SAAgB,OAAU,GAAgB,GAAyB;CAClE,MAAM,IAAI,MAAM;CAChB,MAAM,IAAI,MAAM;AAChB,KAAI,EAAE,SAAS,EAAE,KAAM,QAAO;AAC9B,MAAK,MAAM,KAAK,EAAG,KAAI,CAAC,EAAE,IAAI,GAAI,QAAO;AACzC,QAAO;AACP"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "venerate",
3
+ "version": "0.0.0",
4
+ "license": "MIT",
5
+ "author": {
6
+ "name": "Jeremy Banka",
7
+ "email": "hello@jeremybanka.com"
8
+ },
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/jeremybanka/wayforge.git",
15
+ "directory": "packages/venerate"
16
+ },
17
+ "type": "module",
18
+ "files": [
19
+ "dist",
20
+ "src"
21
+ ],
22
+ "main": "dist/venerate.js",
23
+ "scripts": {
24
+ "build": "tsdown",
25
+ "lint:biome": "biome check -- .",
26
+ "lint:eslint": "eslint -- .",
27
+ "lint:types": "tsgo --noEmit",
28
+ "watch:types": "tsgo --watch --noEmit",
29
+ "lint": "concurrently \"bun:lint:*\"",
30
+ "test": "vitest",
31
+ "test:coverage": "vitest run --coverage",
32
+ "test:once": "vitest run",
33
+ "postversion": "biome format --write package.json"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "24.3.0",
37
+ "@vitest/coverage-v8": "3.2.4",
38
+ "concurrently": "9.2.0",
39
+ "eslint": "9.33.0",
40
+ "recoverage": "0.1.11",
41
+ "tsdown": "0.14.1",
42
+ "vite": "7.1.3",
43
+ "vitest": "3.2.4"
44
+ }
45
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Cross-realm-safe check for anything that extends Set.
3
+ */
4
+ export function isSet(value: unknown): value is Set<any> {
5
+ if (value == null) return false
6
+ try {
7
+ // brand check: throws TypeError unless `value` has [[SetData]]
8
+ Set.prototype.has.call(value, Symbol.for(`@@brandCheck`))
9
+ return true
10
+ } catch {
11
+ return false
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Materializes an unknown iterable as a Set, without providing for mutation.
17
+ * Does not copy the input if it is already a Set.
18
+ */
19
+ export const toSet = <T>(it: Iterable<T>): ReadonlySet<T> =>
20
+ isSet(it) ? it : new Set(it)
21
+
22
+ /**
23
+ * Lazily yield the union of A, B, C, ... preserving first-seen order.
24
+ * Only a small "seen" Set is maintained; inputs are streamed.
25
+ */
26
+ export function* union<T>(...iterables: Iterable<T>[]): Iterable<T> {
27
+ const seen = new Set<T>()
28
+ for (const it of iterables) {
29
+ for (const v of it) {
30
+ if (!seen.has(v)) {
31
+ seen.add(v)
32
+ yield v
33
+ }
34
+ }
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Lazily yield A \ (B ∪ C ∪ ...), streaming A and pre-materializing removals.
40
+ */
41
+ export function* difference<T>(
42
+ a: Iterable<T>,
43
+ ...rest: Iterable<T>[]
44
+ ): Iterable<T> {
45
+ if (rest.length === 0) {
46
+ yield* a
47
+ return
48
+ }
49
+ const remove = new Set<T>()
50
+ for (const it of rest) for (const v of it) remove.add(v)
51
+ for (const v of a) if (!remove.has(v)) yield v
52
+ }
53
+
54
+ /**
55
+ * Lazily yield A ∩ B ∩ C ∩ ...
56
+ * Streams only the FIRST iterable; others are materialized as Sets for O(1) lookups.
57
+ * If no others are provided, yields a shallow copy of A.
58
+ */
59
+ export function* intersection<T>(
60
+ a: Iterable<T>,
61
+ ...rest: Iterable<T>[]
62
+ ): Iterable<T> {
63
+ if (rest.length === 0) {
64
+ yield* a
65
+ return
66
+ }
67
+ const sets = rest.map(toSet)
68
+ for (const v of a) {
69
+ if (sets.every((s) => s.has(v))) yield v
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Lazily yield A △ B (pairwise symmetric difference).
75
+ * Streams A and B once; materializes the opposite side for membership checks.
76
+ * - First yields items unique to A (preserving A's order)
77
+ * - Then yields items unique to B (preserving B's order)
78
+ */
79
+ export function* symmetricDifference<T>(
80
+ a: Iterable<T>,
81
+ b: Iterable<T>,
82
+ ): Iterable<T> {
83
+ const A = toSet(a)
84
+ const B = toSet(b)
85
+
86
+ // Items unique to A
87
+ for (const v of A) if (!B.has(v)) yield v
88
+ // Items unique to B
89
+ for (const v of B) if (!A.has(v)) yield v
90
+ }
91
+
92
+ /**
93
+ * Variadic symmetric difference via left fold of the pairwise version.
94
+ * Still lazy in the sense that we only materialize small per-step Sets;
95
+ * each fold pass consumes its input generator.
96
+ */
97
+ export function* symmetricDifferenceVariadic<T>(
98
+ first: Iterable<T>,
99
+ ...rest: Iterable<T>[]
100
+ ): Iterable<T> {
101
+ let acc: Iterable<T> = first
102
+ for (const it of rest) acc = symmetricDifference(acc, it)
103
+ yield* acc
104
+ }
105
+
106
+ /**
107
+ * Predicates (not lazy outputs, but efficient w/ early exits)
108
+ */
109
+ export function isSubset<T>(a: Iterable<T>, b: Iterable<T>): boolean {
110
+ const A = toSet(a)
111
+ const B = toSet(b)
112
+ if (A.size > B.size) return false
113
+ for (const v of A) if (!B.has(v)) return false
114
+ return true
115
+ }
116
+
117
+ export function isSuperset<T>(a: Iterable<T>, b: Iterable<T>): boolean {
118
+ return isSubset(b, a)
119
+ }
120
+
121
+ export function isDisjoint<T>(a: Iterable<T>, b: Iterable<T>): boolean {
122
+ const A = toSet(a)
123
+ const B = toSet(b)
124
+ const aIsSmall = A.size <= B.size
125
+ const small = aIsSmall ? A : B
126
+ const big = aIsSmall ? B : A
127
+ for (const v of small) if (big.has(v)) return false
128
+ return true
129
+ }
130
+
131
+ export function equals<T>(a: Iterable<T>, b: Iterable<T>): boolean {
132
+ const A = toSet(a)
133
+ const B = toSet(b)
134
+ if (A.size !== B.size) return false
135
+ for (const v of A) if (!B.has(v)) return false
136
+ return true
137
+ }