@evolu/common 7.4.0 → 8.0.0-next.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 +4 -61
- package/dist/src/Array.d.ts +343 -102
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +181 -85
- package/dist/src/Assert.d.ts +57 -11
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +47 -11
- package/dist/src/BigInt.d.ts +6 -1
- package/dist/src/BigInt.d.ts.map +1 -1
- package/dist/src/BigInt.js +5 -0
- package/dist/src/Brand.d.ts +40 -12
- package/dist/src/Brand.d.ts.map +1 -1
- package/dist/src/Brand.js +5 -0
- package/dist/src/Buffer.d.ts +6 -1
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Buffer.js +7 -2
- package/dist/src/Cache.d.ts +10 -2
- package/dist/src/Cache.d.ts.map +1 -1
- package/dist/src/Cache.js +6 -0
- package/dist/src/Callbacks.d.ts +10 -7
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Callbacks.js +23 -13
- package/dist/src/Console.d.ts +361 -69
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +217 -117
- package/dist/src/Crypto.d.ts +70 -21
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +72 -27
- package/dist/src/Eq.d.ts +22 -2
- package/dist/src/Eq.d.ts.map +1 -1
- package/dist/src/Eq.js +21 -1
- package/dist/src/Error.d.ts +23 -12
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +27 -11
- package/dist/src/Function.d.ts +128 -56
- package/dist/src/Function.d.ts.map +1 -1
- package/dist/src/Function.js +118 -14
- package/dist/src/Identicon.d.ts +5 -0
- package/dist/src/Identicon.d.ts.map +1 -1
- package/dist/src/Identicon.js +6 -1
- package/dist/src/Lookup.d.ts +160 -0
- package/dist/src/Lookup.d.ts.map +1 -0
- package/dist/src/Lookup.js +192 -0
- package/dist/src/Microtask.d.ts +21 -0
- package/dist/src/Microtask.d.ts.map +1 -0
- package/dist/src/Microtask.js +37 -0
- package/dist/src/Number.d.ts +18 -4
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +23 -1
- package/dist/src/Object.d.ts +110 -5
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +108 -5
- package/dist/src/Option.d.ts +58 -0
- package/dist/src/Option.d.ts.map +1 -0
- package/dist/src/Option.js +43 -0
- package/dist/src/Order.d.ts +5 -0
- package/dist/src/Order.d.ts.map +1 -1
- package/dist/src/Order.js +8 -3
- package/dist/src/Platform.d.ts +36 -2
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +10 -6
- package/dist/src/Polyfills.d.ts +27 -0
- package/dist/src/Polyfills.d.ts.map +1 -0
- package/dist/src/Polyfills.js +299 -0
- package/dist/src/Random.d.ts +18 -7
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +9 -7
- package/dist/src/Redacted.d.ts +7 -4
- package/dist/src/Redacted.d.ts.map +1 -1
- package/dist/src/Redacted.js +5 -0
- package/dist/src/Ref.d.ts +31 -16
- package/dist/src/Ref.d.ts.map +1 -1
- package/dist/src/Ref.js +35 -7
- package/dist/src/RefCount.d.ts +62 -0
- package/dist/src/RefCount.d.ts.map +1 -0
- package/dist/src/RefCount.js +83 -0
- package/dist/src/Relation.d.ts +84 -29
- package/dist/src/Relation.d.ts.map +1 -1
- package/dist/src/Relation.js +83 -105
- package/dist/src/Resource.d.ts +263 -0
- package/dist/src/Resource.d.ts.map +1 -0
- package/dist/src/Resource.js +389 -0
- package/dist/src/Result.d.ts +390 -374
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +142 -70
- package/dist/src/Schedule.d.ts +953 -0
- package/dist/src/Schedule.d.ts.map +1 -0
- package/dist/src/Schedule.js +1199 -0
- package/dist/src/Set.d.ts +181 -0
- package/dist/src/Set.d.ts.map +1 -0
- package/dist/src/Set.js +137 -0
- package/dist/src/Skiplist.d.ts +6 -1
- package/dist/src/Skiplist.d.ts.map +1 -1
- package/dist/src/Skiplist.js +5 -0
- package/dist/src/Sqlite.d.ts +160 -60
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +290 -190
- package/dist/src/Store.d.ts +36 -28
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +55 -15
- package/dist/src/String.d.ts +5 -0
- package/dist/src/String.d.ts.map +1 -1
- package/dist/src/String.js +5 -0
- package/dist/src/Task.d.ts +2263 -423
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +1488 -348
- package/dist/src/Test.d.ts +122 -0
- package/dist/src/Test.d.ts.map +1 -0
- package/dist/src/Test.js +66 -0
- package/dist/src/Time.d.ts +201 -48
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +190 -86
- package/dist/src/Tracer.d.ts +48 -0
- package/dist/src/Tracer.d.ts.map +1 -0
- package/dist/src/Tracer.js +6 -0
- package/dist/src/Type.d.ts +582 -209
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +362 -240
- package/dist/src/Types.d.ts +160 -6
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/Types.js +22 -2
- package/dist/src/WebSocket.d.ts +113 -58
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +210 -118
- package/dist/src/Worker.d.ts +245 -60
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +220 -64
- package/dist/src/index.d.ts +25 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +19 -3
- package/dist/src/local-first/Db.d.ts +24 -213
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +622 -293
- package/dist/src/local-first/Error.d.ts +12 -0
- package/dist/src/local-first/Error.d.ts.map +1 -0
- package/dist/src/local-first/Error.js +6 -0
- package/dist/src/local-first/Evolu.d.ts +330 -285
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +472 -402
- package/dist/src/local-first/LocalAuth.d.ts +10 -10
- package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
- package/dist/src/local-first/LocalAuth.js +6 -1
- package/dist/src/local-first/Owner.d.ts +70 -27
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +27 -14
- package/dist/src/local-first/Protocol.d.ts +58 -50
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +253 -155
- package/dist/src/local-first/Query.d.ts +133 -43
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Query.js +139 -90
- package/dist/src/local-first/Relay.d.ts +18 -38
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +59 -171
- package/dist/src/local-first/Schema.d.ts +139 -144
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +78 -163
- package/dist/src/local-first/Shared.d.ts +125 -0
- package/dist/src/local-first/Shared.d.ts.map +1 -0
- package/dist/src/local-first/Shared.js +569 -0
- package/dist/src/local-first/Storage.d.ts +50 -53
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +121 -173
- package/dist/src/local-first/Timestamp.d.ts +28 -35
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +24 -25
- package/dist/src/local-first/index.d.ts +2 -15
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +2 -15
- package/package.json +36 -26
- package/src/Array.ts +467 -106
- package/src/Assert.ts +87 -12
- package/src/BigInt.ts +7 -1
- package/src/Brand.ts +41 -12
- package/src/Buffer.ts +10 -4
- package/src/Cache.ts +11 -2
- package/src/Callbacks.ts +33 -23
- package/src/Console.ts +567 -178
- package/src/Crypto.ts +111 -62
- package/src/Eq.ts +30 -3
- package/src/Error.ts +50 -34
- package/src/Function.ts +140 -68
- package/src/Identicon.ts +7 -1
- package/src/Lookup.ts +415 -0
- package/src/Microtask.ts +58 -0
- package/src/Number.ts +40 -7
- package/src/Object.ts +155 -16
- package/src/Option.ts +74 -0
- package/src/Order.ts +11 -3
- package/src/Platform.ts +43 -7
- package/src/Polyfills.ts +465 -0
- package/src/Random.ts +25 -11
- package/src/Redacted.ts +8 -4
- package/src/Ref.ts +72 -21
- package/src/RefCount.ts +170 -0
- package/src/Relation.ts +188 -136
- package/src/Resource.ts +864 -0
- package/src/Result.ts +548 -381
- package/src/Schedule.ts +1471 -0
- package/src/Set.ts +247 -0
- package/src/Skiplist.ts +7 -1
- package/src/Sqlite.ts +409 -304
- package/src/Store.ts +94 -48
- package/src/String.ts +6 -0
- package/src/Task.ts +3685 -740
- package/src/Test.ts +162 -0
- package/src/Time.ts +348 -145
- package/src/Tracer.ts +54 -0
- package/src/Type.ts +1064 -630
- package/src/Types.ts +202 -7
- package/src/WebSocket.ts +311 -178
- package/src/Worker.ts +527 -147
- package/src/index.ts +74 -3
- package/src/local-first/Db.ts +871 -597
- package/src/local-first/Error.ts +17 -0
- package/src/local-first/Evolu.ts +884 -823
- package/src/local-first/LocalAuth.ts +10 -10
- package/src/local-first/Owner.ts +94 -42
- package/src/local-first/Protocol.ts +440 -303
- package/src/local-first/Query.ts +243 -194
- package/src/local-first/Relay.ts +91 -252
- package/src/local-first/Schema.ts +284 -394
- package/src/local-first/Shared.ts +932 -0
- package/src/local-first/Storage.ts +209 -282
- package/src/local-first/Timestamp.ts +38 -45
- package/src/local-first/index.ts +2 -16
- package/LICENSE +0 -21
- package/dist/src/Instances.d.ts +0 -34
- package/dist/src/Instances.d.ts.map +0 -1
- package/dist/src/Instances.js +0 -44
- package/dist/src/Resources.d.ts +0 -118
- package/dist/src/Resources.d.ts.map +0 -1
- package/dist/src/Resources.js +0 -197
- package/dist/src/local-first/Platform.d.ts +0 -25
- package/dist/src/local-first/Platform.d.ts.map +0 -1
- package/dist/src/local-first/Platform.js +0 -1
- package/dist/src/local-first/Public.d.ts +0 -22
- package/dist/src/local-first/Public.d.ts.map +0 -1
- package/dist/src/local-first/Public.js +0 -15
- package/dist/src/local-first/PublicKysely.d.ts +0 -148
- package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
- package/dist/src/local-first/PublicKysely.js +0 -184
- package/dist/src/local-first/Sync.d.ts +0 -112
- package/dist/src/local-first/Sync.d.ts.map +0 -1
- package/dist/src/local-first/Sync.js +0 -529
- package/src/Instances.ts +0 -90
- package/src/Resources.ts +0 -367
- package/src/local-first/Platform.ts +0 -27
- package/src/local-first/Public.ts +0 -42
- package/src/local-first/PublicKysely.ts +0 -239
- package/src/local-first/Sync.ts +0 -960
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference counting helpers.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { assert, assertNotDisposed } from "./Assert.js";
|
|
7
|
+
import { identity } from "./Function.js";
|
|
8
|
+
import { createLookupMap } from "./Lookup.js";
|
|
9
|
+
import { NonNegativeInt, PositiveInt, zeroNonNegativeInt } from "./Type.js";
|
|
10
|
+
/** Creates {@link RefCount}. */
|
|
11
|
+
export const createRefCount = () => {
|
|
12
|
+
let count = zeroNonNegativeInt;
|
|
13
|
+
const stack = new DisposableStack();
|
|
14
|
+
stack.defer(() => {
|
|
15
|
+
count = zeroNonNegativeInt;
|
|
16
|
+
});
|
|
17
|
+
const moved = stack.move();
|
|
18
|
+
return {
|
|
19
|
+
increment: () => {
|
|
20
|
+
assertNotDisposed(moved);
|
|
21
|
+
const nextCount = PositiveInt.orThrow(count + 1);
|
|
22
|
+
count = nextCount;
|
|
23
|
+
return nextCount;
|
|
24
|
+
},
|
|
25
|
+
decrement: () => {
|
|
26
|
+
assertNotDisposed(moved);
|
|
27
|
+
assert(count > 0, "RefCount must not be decremented below zero.");
|
|
28
|
+
count = NonNegativeInt.orThrow(count - 1);
|
|
29
|
+
return count;
|
|
30
|
+
},
|
|
31
|
+
getCount: () => {
|
|
32
|
+
assertNotDisposed(moved);
|
|
33
|
+
return count;
|
|
34
|
+
},
|
|
35
|
+
[Symbol.dispose]: () => moved.dispose(),
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
export function createRefCountByKey({ lookup = identity, } = {}) {
|
|
39
|
+
const stack = new DisposableStack();
|
|
40
|
+
const refCountByKey = stack.adopt(createLookupMap({ lookup }), (refCountByKey) => {
|
|
41
|
+
for (const refCount of refCountByKey.values())
|
|
42
|
+
refCount[Symbol.dispose]();
|
|
43
|
+
refCountByKey.clear();
|
|
44
|
+
});
|
|
45
|
+
const moved = stack.move();
|
|
46
|
+
const getRefCount = (key) => {
|
|
47
|
+
let refCount = refCountByKey.get(key);
|
|
48
|
+
if (!refCount) {
|
|
49
|
+
refCount = createRefCount();
|
|
50
|
+
refCountByKey.set(key, refCount);
|
|
51
|
+
}
|
|
52
|
+
return refCount;
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
increment: (key) => {
|
|
56
|
+
assertNotDisposed(moved);
|
|
57
|
+
return getRefCount(key).increment();
|
|
58
|
+
},
|
|
59
|
+
decrement: (key) => {
|
|
60
|
+
assertNotDisposed(moved);
|
|
61
|
+
const refCount = getRefCount(key);
|
|
62
|
+
const nextCount = refCount.decrement();
|
|
63
|
+
if (nextCount === 0) {
|
|
64
|
+
refCount[Symbol.dispose]();
|
|
65
|
+
refCountByKey.delete(key);
|
|
66
|
+
}
|
|
67
|
+
return nextCount;
|
|
68
|
+
},
|
|
69
|
+
getCount: (key) => {
|
|
70
|
+
assertNotDisposed(moved);
|
|
71
|
+
return refCountByKey.get(key)?.getCount() ?? zeroNonNegativeInt;
|
|
72
|
+
},
|
|
73
|
+
has: (key) => {
|
|
74
|
+
assertNotDisposed(moved);
|
|
75
|
+
return refCountByKey.has(key);
|
|
76
|
+
},
|
|
77
|
+
keys: () => {
|
|
78
|
+
assertNotDisposed(moved);
|
|
79
|
+
return new Set(refCountByKey.keys());
|
|
80
|
+
},
|
|
81
|
+
[Symbol.dispose]: () => moved.dispose(),
|
|
82
|
+
};
|
|
83
|
+
}
|
package/dist/src/Relation.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bidirectional
|
|
2
|
+
* Bidirectional relations with O(1) lookup in both directions.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { type Lookup } from "./Lookup.js";
|
|
7
|
+
/**
|
|
8
|
+
* Bidirectional relation between two types.
|
|
5
9
|
*
|
|
6
10
|
* - Provides O(1) (amortized) lookup in both directions (A → B and B → A) without
|
|
7
11
|
* maintaining two maps manually and risking them diverging.
|
|
@@ -9,26 +13,56 @@
|
|
|
9
13
|
* item, user ↔ role, entity ↔ subscription where both directions are
|
|
10
14
|
* frequently queried.
|
|
11
15
|
* - Supports fast membership tests via `has`, `hasA`, and `hasB`.
|
|
12
|
-
* -
|
|
13
|
-
*
|
|
16
|
+
* - Exposes directional iterators for imperative `for...of` traversal.
|
|
17
|
+
* - The relation itself is iterable, allowing it to be treated as a set of pairs
|
|
18
|
+
* when needed.
|
|
14
19
|
*
|
|
15
20
|
* Complexity:
|
|
16
21
|
*
|
|
17
|
-
* - `add` / `remove` / `has*` /
|
|
18
|
-
* operations (O(1) expected).
|
|
19
|
-
* - `
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
+
* - `add` / `remove` / `has*` / `*Count*` each perform a constant number of
|
|
23
|
+
* Map/Set operations (O(1) expected).
|
|
24
|
+
* - `iterateA` and `iterateB` create iterators in O(1); consuming them is O(d)
|
|
25
|
+
* where d is the number of associated elements.
|
|
26
|
+
* - `removeByA` and `removeByB` are O(d) where d is the number of associated
|
|
27
|
+
* elements, because every associated pair must be touched once.
|
|
28
|
+
*
|
|
29
|
+
* By default, {@link createRelation} uses reference identity for both sides,
|
|
30
|
+
* matching native `Map` and `Set`. Callers may instead provide
|
|
31
|
+
* {@link Lookup lookup} functions so logical equality is based on a derived
|
|
32
|
+
* stable key.
|
|
33
|
+
*
|
|
34
|
+
* The input parameter types of `lookupA` and `lookupB` determine which values
|
|
35
|
+
* the returned relation accepts on each side.
|
|
36
|
+
*
|
|
37
|
+
* ### Example
|
|
38
|
+
*
|
|
39
|
+
* Use the default identity semantics.
|
|
22
40
|
*
|
|
23
|
-
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* const relation = createRelation<WebSocket, string>();
|
|
43
|
+
* relation.add(socket, "owner-1");
|
|
44
|
+
* ```
|
|
24
45
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
46
|
+
* ### Example
|
|
47
|
+
*
|
|
48
|
+
* Use lookup-derived equality.
|
|
49
|
+
*
|
|
50
|
+
* ```ts
|
|
51
|
+
* interface Person {
|
|
52
|
+
* readonly id: string;
|
|
53
|
+
* readonly name: string;
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* const relation = createRelation({
|
|
57
|
+
* lookupA: (person: Person) => person.id,
|
|
58
|
+
* lookupB: (group: { readonly id: string }) => group.id,
|
|
59
|
+
* });
|
|
60
|
+
*
|
|
61
|
+
* relation.add({ id: "1", name: "Ada" }, { id: "admins" });
|
|
62
|
+
* relation.has({ id: "1", name: "Grace" }, { id: "admins" }); // true
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @see {@link createRelation}
|
|
32
66
|
*/
|
|
33
67
|
export interface Relation<A, B> {
|
|
34
68
|
/**
|
|
@@ -41,15 +75,28 @@ export interface Relation<A, B> {
|
|
|
41
75
|
* and was removed, false if it was not present.
|
|
42
76
|
*/
|
|
43
77
|
readonly remove: (a: A, b: B) => boolean;
|
|
44
|
-
/**
|
|
45
|
-
readonly
|
|
46
|
-
/**
|
|
47
|
-
readonly
|
|
78
|
+
/** Removes all pairs containing the given A element. */
|
|
79
|
+
readonly removeByA: (a: A) => boolean;
|
|
80
|
+
/** Removes all pairs containing the given B element. */
|
|
81
|
+
readonly removeByB: (b: B) => boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Iterates over all A elements related to a B element.
|
|
84
|
+
*
|
|
85
|
+
* Returns a live iterator over the current relation state rather than a
|
|
86
|
+
* snapshot copy.
|
|
87
|
+
*
|
|
88
|
+
* Returns an empty iterator when the B element has no related values.
|
|
89
|
+
*/
|
|
90
|
+
readonly iterateA: (b: B) => IterableIterator<A>;
|
|
48
91
|
/**
|
|
49
|
-
* Iterates over
|
|
50
|
-
*
|
|
92
|
+
* Iterates over all B elements related to an A element.
|
|
93
|
+
*
|
|
94
|
+
* Returns a live iterator over the current relation state rather than a
|
|
95
|
+
* snapshot copy.
|
|
96
|
+
*
|
|
97
|
+
* Returns an empty iterator when the A element has no related values.
|
|
51
98
|
*/
|
|
52
|
-
readonly
|
|
99
|
+
readonly iterateB: (a: A) => IterableIterator<B>;
|
|
53
100
|
/**
|
|
54
101
|
* Iterator over all pairs enabling for..of and spread. Yields readonly [a, b]
|
|
55
102
|
* tuples.
|
|
@@ -61,19 +108,27 @@ export interface Relation<A, B> {
|
|
|
61
108
|
readonly hasA: (a: A) => boolean;
|
|
62
109
|
/** Checks if a B element exists in the relation. */
|
|
63
110
|
readonly hasB: (b: B) => boolean;
|
|
64
|
-
/** Deletes all pairs containing the given A element. */
|
|
65
|
-
readonly deleteA: (a: A) => boolean;
|
|
66
|
-
/** Deletes all pairs containing the given B element. */
|
|
67
|
-
readonly deleteB: (b: B) => boolean;
|
|
68
111
|
/** Clears all pairs from the relation. */
|
|
69
112
|
readonly clear: () => void;
|
|
70
113
|
/** Number of distinct A elements currently present. */
|
|
71
114
|
readonly aCount: () => number;
|
|
72
115
|
/** Number of distinct B elements currently present. */
|
|
73
116
|
readonly bCount: () => number;
|
|
117
|
+
/** Number of B elements related to the given A element. */
|
|
118
|
+
readonly bCountForA: (a: A) => number;
|
|
119
|
+
/** Number of A elements related to the given B element. */
|
|
120
|
+
readonly aCountForB: (b: B) => number;
|
|
74
121
|
/** Number of pairs currently stored in the relation. */
|
|
75
122
|
readonly size: () => number;
|
|
76
123
|
}
|
|
124
|
+
/** Options for {@link createRelation}. */
|
|
125
|
+
export interface CreateRelationOptions<A, B, LA = A, LB = B> {
|
|
126
|
+
/** Derives logical identity for A values. Defaults to {@link identity}. */
|
|
127
|
+
readonly lookupA?: Lookup<A, LA>;
|
|
128
|
+
/** Derives logical identity for B values. Defaults to {@link identity}. */
|
|
129
|
+
readonly lookupB?: Lookup<B, LB>;
|
|
130
|
+
}
|
|
77
131
|
/** Creates a {@link Relation}. */
|
|
78
|
-
export declare
|
|
132
|
+
export declare function createRelation<A, B>(): Relation<A, B>;
|
|
133
|
+
export declare function createRelation<A, B, LA, LB>(options: CreateRelationOptions<A, B, LA, LB>): Relation<A, B>;
|
|
79
134
|
//# sourceMappingURL=Relation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Relation.d.ts","sourceRoot":"","sources":["../../src/Relation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Relation.d.ts","sourceRoot":"","sources":["../../src/Relation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAGL,KAAK,MAAM,EAEZ,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,EAAE,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAEtC;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAEzC,wDAAwD;IACxD,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAEtC,wDAAwD;IACxD,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAEtC;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAEjD;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAEjD;;;OAGG;IACH,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,gBAAgB,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAEpE,wDAAwD;IACxD,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAEtC,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAEjC,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAEjC,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAE3B,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC;IAE9B,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC;IAE9B,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;IAEtC,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;IAEtC,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,0CAA0C;AAC1C,MAAM,WAAW,qBAAqB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC;IACzD,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEjC,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;CAClC;AAED,kCAAkC;AAClC,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,KAAK,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACvD,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EACzC,OAAO,EAAE,qBAAqB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,GAC3C,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC"}
|
package/dist/src/Relation.js
CHANGED
|
@@ -1,127 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bidirectional relations with O(1) lookup in both directions.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { emptyArray } from "./Array.js";
|
|
1
7
|
import { assert } from "./Assert.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
8
|
+
import { identity } from "./Function.js";
|
|
9
|
+
import { createLookupMap, createLookupSet, } from "./Lookup.js";
|
|
10
|
+
export function createRelation({ lookupA = identity, lookupB = identity, } = {}) {
|
|
11
|
+
const bByA = createLookupMap({ lookup: lookupA });
|
|
12
|
+
const aByB = createLookupMap({ lookup: lookupB });
|
|
6
13
|
let sizeInternal = 0;
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
const removePair = (a, b) => {
|
|
15
|
+
const relatedB = bByA.get(a);
|
|
16
|
+
// This should only fail if a leaked view was mutated via an unsafe cast.
|
|
17
|
+
assertRelationMappingConsistency(relatedB);
|
|
18
|
+
assertRelationMappingConsistency(relatedB.has(b));
|
|
19
|
+
relatedB.delete(b);
|
|
20
|
+
if (relatedB.size === 0) {
|
|
21
|
+
bByA.delete(a);
|
|
22
|
+
}
|
|
23
|
+
const relatedA = aByB.get(b);
|
|
24
|
+
// This should only fail if a leaked view was mutated via an unsafe cast.
|
|
25
|
+
assertRelationMappingConsistency(relatedA);
|
|
26
|
+
assertRelationMappingConsistency(relatedA.has(a));
|
|
27
|
+
relatedA.delete(a);
|
|
28
|
+
if (relatedA.size === 0) {
|
|
29
|
+
aByB.delete(b);
|
|
30
|
+
}
|
|
31
|
+
sizeInternal--;
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
add: (a, b) => {
|
|
35
|
+
const canonicalA = bByA.getKey(a) ?? a;
|
|
36
|
+
const canonicalB = aByB.getKey(b) ?? b;
|
|
37
|
+
let relatedB = bByA.get(canonicalA);
|
|
38
|
+
if (relatedB?.has(canonicalB))
|
|
11
39
|
return false;
|
|
12
|
-
if (!
|
|
13
|
-
|
|
14
|
-
|
|
40
|
+
if (!relatedB) {
|
|
41
|
+
relatedB = createLookupSet({ lookup: lookupB });
|
|
42
|
+
bByA.set(canonicalA, relatedB);
|
|
15
43
|
}
|
|
16
|
-
|
|
17
|
-
let
|
|
18
|
-
if (!
|
|
19
|
-
|
|
20
|
-
|
|
44
|
+
relatedB.add(canonicalB);
|
|
45
|
+
let relatedA = aByB.get(canonicalB);
|
|
46
|
+
if (!relatedA) {
|
|
47
|
+
relatedA = createLookupSet({ lookup: lookupA });
|
|
48
|
+
aByB.set(canonicalB, relatedA);
|
|
21
49
|
}
|
|
22
|
-
|
|
50
|
+
relatedA.add(canonicalA);
|
|
23
51
|
sizeInternal++;
|
|
24
52
|
return true;
|
|
25
53
|
},
|
|
26
|
-
remove(a, b) {
|
|
27
|
-
|
|
28
|
-
if (!bSet?.has(b))
|
|
54
|
+
remove: (a, b) => {
|
|
55
|
+
if (!bByA.get(a)?.has(b))
|
|
29
56
|
return false;
|
|
30
|
-
|
|
31
|
-
if (bSet.size === 0) {
|
|
32
|
-
aToB.delete(a);
|
|
33
|
-
}
|
|
34
|
-
const aSet = bToA.get(b);
|
|
35
|
-
assert(aSet, "Relation mapping inconsistency");
|
|
36
|
-
aSet.delete(a);
|
|
37
|
-
if (aSet.size === 0) {
|
|
38
|
-
bToA.delete(b);
|
|
39
|
-
}
|
|
40
|
-
sizeInternal--;
|
|
57
|
+
removePair(a, b);
|
|
41
58
|
return true;
|
|
42
59
|
},
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
getA(b) {
|
|
47
|
-
return bToA.get(b);
|
|
48
|
-
},
|
|
49
|
-
forEach(callback) {
|
|
50
|
-
for (const [a, bSet] of aToB) {
|
|
51
|
-
for (const b of bSet)
|
|
52
|
-
callback(a, b);
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
[Symbol.iterator]() {
|
|
56
|
-
const iterator = function* () {
|
|
57
|
-
for (const [a, bSet] of aToB) {
|
|
58
|
-
for (const b of bSet) {
|
|
59
|
-
yield [a, b];
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
return iterator();
|
|
64
|
-
},
|
|
65
|
-
has(a, b) {
|
|
66
|
-
const bSet = aToB.get(a);
|
|
67
|
-
return bSet?.has(b) ?? false;
|
|
68
|
-
},
|
|
69
|
-
hasA(a) {
|
|
70
|
-
return aToB.has(a);
|
|
71
|
-
},
|
|
72
|
-
hasB(b) {
|
|
73
|
-
return bToA.has(b);
|
|
74
|
-
},
|
|
75
|
-
deleteA(a) {
|
|
76
|
-
const bSet = aToB.get(a);
|
|
77
|
-
if (!bSet)
|
|
60
|
+
removeByA: (a) => {
|
|
61
|
+
const relatedB = bByA.get(a);
|
|
62
|
+
if (!relatedB)
|
|
78
63
|
return false;
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
const aSet = bToA.get(b);
|
|
82
|
-
if (aSet) {
|
|
83
|
-
aSet.delete(a);
|
|
84
|
-
if (aSet.size === 0) {
|
|
85
|
-
bToA.delete(b);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
aToB.delete(a);
|
|
90
|
-
sizeInternal -= removed;
|
|
64
|
+
for (const b of [...relatedB.keys()])
|
|
65
|
+
removePair(a, b);
|
|
91
66
|
return true;
|
|
92
67
|
},
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
if (!
|
|
68
|
+
removeByB: (b) => {
|
|
69
|
+
const relatedA = aByB.get(b);
|
|
70
|
+
if (!relatedA)
|
|
96
71
|
return false;
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
const bSet = aToB.get(a);
|
|
100
|
-
if (bSet) {
|
|
101
|
-
bSet.delete(b);
|
|
102
|
-
if (bSet.size === 0) {
|
|
103
|
-
aToB.delete(a);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
bToA.delete(b);
|
|
108
|
-
sizeInternal -= removed;
|
|
72
|
+
for (const a of [...relatedA.keys()])
|
|
73
|
+
removePair(a, b);
|
|
109
74
|
return true;
|
|
110
75
|
},
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
76
|
+
iterateA: (b) => aByB.get(b)?.keys() ?? emptyArray.values(),
|
|
77
|
+
iterateB: (a) => bByA.get(a)?.keys() ?? emptyArray.values(),
|
|
78
|
+
*[Symbol.iterator]() {
|
|
79
|
+
for (const [a, relatedB] of bByA) {
|
|
80
|
+
for (const b of relatedB.keys()) {
|
|
81
|
+
yield [a, b];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
118
84
|
},
|
|
119
|
-
|
|
120
|
-
|
|
85
|
+
has: (a, b) => {
|
|
86
|
+
const relatedB = bByA.get(a);
|
|
87
|
+
return relatedB?.has(b) ?? false;
|
|
121
88
|
},
|
|
122
|
-
|
|
123
|
-
|
|
89
|
+
hasA: (a) => bByA.has(a),
|
|
90
|
+
hasB: (b) => aByB.has(b),
|
|
91
|
+
clear: () => {
|
|
92
|
+
bByA.clear();
|
|
93
|
+
aByB.clear();
|
|
94
|
+
sizeInternal = 0;
|
|
124
95
|
},
|
|
96
|
+
aCount: () => bByA.size,
|
|
97
|
+
bCount: () => aByB.size,
|
|
98
|
+
bCountForA: (a) => bByA.get(a)?.size ?? 0,
|
|
99
|
+
aCountForB: (b) => aByB.get(b)?.size ?? 0,
|
|
100
|
+
size: () => sizeInternal,
|
|
125
101
|
};
|
|
126
|
-
|
|
102
|
+
}
|
|
103
|
+
const assertRelationMappingConsistency = (condition) => {
|
|
104
|
+
assert(condition, "Relation mapping inconsistency");
|
|
127
105
|
};
|