@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.
Files changed (251) hide show
  1. package/README.md +4 -61
  2. package/dist/src/Array.d.ts +343 -102
  3. package/dist/src/Array.d.ts.map +1 -1
  4. package/dist/src/Array.js +181 -85
  5. package/dist/src/Assert.d.ts +57 -11
  6. package/dist/src/Assert.d.ts.map +1 -1
  7. package/dist/src/Assert.js +47 -11
  8. package/dist/src/BigInt.d.ts +6 -1
  9. package/dist/src/BigInt.d.ts.map +1 -1
  10. package/dist/src/BigInt.js +5 -0
  11. package/dist/src/Brand.d.ts +40 -12
  12. package/dist/src/Brand.d.ts.map +1 -1
  13. package/dist/src/Brand.js +5 -0
  14. package/dist/src/Buffer.d.ts +6 -1
  15. package/dist/src/Buffer.d.ts.map +1 -1
  16. package/dist/src/Buffer.js +7 -2
  17. package/dist/src/Cache.d.ts +10 -2
  18. package/dist/src/Cache.d.ts.map +1 -1
  19. package/dist/src/Cache.js +6 -0
  20. package/dist/src/Callbacks.d.ts +10 -7
  21. package/dist/src/Callbacks.d.ts.map +1 -1
  22. package/dist/src/Callbacks.js +23 -13
  23. package/dist/src/Console.d.ts +361 -69
  24. package/dist/src/Console.d.ts.map +1 -1
  25. package/dist/src/Console.js +217 -117
  26. package/dist/src/Crypto.d.ts +70 -21
  27. package/dist/src/Crypto.d.ts.map +1 -1
  28. package/dist/src/Crypto.js +72 -27
  29. package/dist/src/Eq.d.ts +22 -2
  30. package/dist/src/Eq.d.ts.map +1 -1
  31. package/dist/src/Eq.js +21 -1
  32. package/dist/src/Error.d.ts +23 -12
  33. package/dist/src/Error.d.ts.map +1 -1
  34. package/dist/src/Error.js +27 -11
  35. package/dist/src/Function.d.ts +128 -56
  36. package/dist/src/Function.d.ts.map +1 -1
  37. package/dist/src/Function.js +118 -14
  38. package/dist/src/Identicon.d.ts +5 -0
  39. package/dist/src/Identicon.d.ts.map +1 -1
  40. package/dist/src/Identicon.js +6 -1
  41. package/dist/src/Lookup.d.ts +160 -0
  42. package/dist/src/Lookup.d.ts.map +1 -0
  43. package/dist/src/Lookup.js +192 -0
  44. package/dist/src/Microtask.d.ts +21 -0
  45. package/dist/src/Microtask.d.ts.map +1 -0
  46. package/dist/src/Microtask.js +37 -0
  47. package/dist/src/Number.d.ts +18 -4
  48. package/dist/src/Number.d.ts.map +1 -1
  49. package/dist/src/Number.js +23 -1
  50. package/dist/src/Object.d.ts +110 -5
  51. package/dist/src/Object.d.ts.map +1 -1
  52. package/dist/src/Object.js +108 -5
  53. package/dist/src/Option.d.ts +58 -0
  54. package/dist/src/Option.d.ts.map +1 -0
  55. package/dist/src/Option.js +43 -0
  56. package/dist/src/Order.d.ts +5 -0
  57. package/dist/src/Order.d.ts.map +1 -1
  58. package/dist/src/Order.js +8 -3
  59. package/dist/src/Platform.d.ts +36 -2
  60. package/dist/src/Platform.d.ts.map +1 -1
  61. package/dist/src/Platform.js +10 -6
  62. package/dist/src/Polyfills.d.ts +27 -0
  63. package/dist/src/Polyfills.d.ts.map +1 -0
  64. package/dist/src/Polyfills.js +299 -0
  65. package/dist/src/Random.d.ts +18 -7
  66. package/dist/src/Random.d.ts.map +1 -1
  67. package/dist/src/Random.js +9 -7
  68. package/dist/src/Redacted.d.ts +7 -4
  69. package/dist/src/Redacted.d.ts.map +1 -1
  70. package/dist/src/Redacted.js +5 -0
  71. package/dist/src/Ref.d.ts +31 -16
  72. package/dist/src/Ref.d.ts.map +1 -1
  73. package/dist/src/Ref.js +35 -7
  74. package/dist/src/RefCount.d.ts +62 -0
  75. package/dist/src/RefCount.d.ts.map +1 -0
  76. package/dist/src/RefCount.js +83 -0
  77. package/dist/src/Relation.d.ts +84 -29
  78. package/dist/src/Relation.d.ts.map +1 -1
  79. package/dist/src/Relation.js +83 -105
  80. package/dist/src/Resource.d.ts +263 -0
  81. package/dist/src/Resource.d.ts.map +1 -0
  82. package/dist/src/Resource.js +389 -0
  83. package/dist/src/Result.d.ts +390 -374
  84. package/dist/src/Result.d.ts.map +1 -1
  85. package/dist/src/Result.js +142 -70
  86. package/dist/src/Schedule.d.ts +953 -0
  87. package/dist/src/Schedule.d.ts.map +1 -0
  88. package/dist/src/Schedule.js +1199 -0
  89. package/dist/src/Set.d.ts +181 -0
  90. package/dist/src/Set.d.ts.map +1 -0
  91. package/dist/src/Set.js +137 -0
  92. package/dist/src/Skiplist.d.ts +6 -1
  93. package/dist/src/Skiplist.d.ts.map +1 -1
  94. package/dist/src/Skiplist.js +5 -0
  95. package/dist/src/Sqlite.d.ts +160 -60
  96. package/dist/src/Sqlite.d.ts.map +1 -1
  97. package/dist/src/Sqlite.js +290 -190
  98. package/dist/src/Store.d.ts +36 -28
  99. package/dist/src/Store.d.ts.map +1 -1
  100. package/dist/src/Store.js +55 -15
  101. package/dist/src/String.d.ts +5 -0
  102. package/dist/src/String.d.ts.map +1 -1
  103. package/dist/src/String.js +5 -0
  104. package/dist/src/Task.d.ts +2263 -423
  105. package/dist/src/Task.d.ts.map +1 -1
  106. package/dist/src/Task.js +1488 -348
  107. package/dist/src/Test.d.ts +122 -0
  108. package/dist/src/Test.d.ts.map +1 -0
  109. package/dist/src/Test.js +66 -0
  110. package/dist/src/Time.d.ts +201 -48
  111. package/dist/src/Time.d.ts.map +1 -1
  112. package/dist/src/Time.js +190 -86
  113. package/dist/src/Tracer.d.ts +48 -0
  114. package/dist/src/Tracer.d.ts.map +1 -0
  115. package/dist/src/Tracer.js +6 -0
  116. package/dist/src/Type.d.ts +582 -209
  117. package/dist/src/Type.d.ts.map +1 -1
  118. package/dist/src/Type.js +362 -240
  119. package/dist/src/Types.d.ts +160 -6
  120. package/dist/src/Types.d.ts.map +1 -1
  121. package/dist/src/Types.js +22 -2
  122. package/dist/src/WebSocket.d.ts +113 -58
  123. package/dist/src/WebSocket.d.ts.map +1 -1
  124. package/dist/src/WebSocket.js +210 -118
  125. package/dist/src/Worker.d.ts +245 -60
  126. package/dist/src/Worker.d.ts.map +1 -1
  127. package/dist/src/Worker.js +220 -64
  128. package/dist/src/index.d.ts +25 -3
  129. package/dist/src/index.d.ts.map +1 -1
  130. package/dist/src/index.js +19 -3
  131. package/dist/src/local-first/Db.d.ts +24 -213
  132. package/dist/src/local-first/Db.d.ts.map +1 -1
  133. package/dist/src/local-first/Db.js +622 -293
  134. package/dist/src/local-first/Error.d.ts +12 -0
  135. package/dist/src/local-first/Error.d.ts.map +1 -0
  136. package/dist/src/local-first/Error.js +6 -0
  137. package/dist/src/local-first/Evolu.d.ts +330 -285
  138. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  139. package/dist/src/local-first/Evolu.js +472 -402
  140. package/dist/src/local-first/LocalAuth.d.ts +10 -10
  141. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  142. package/dist/src/local-first/LocalAuth.js +6 -1
  143. package/dist/src/local-first/Owner.d.ts +70 -27
  144. package/dist/src/local-first/Owner.d.ts.map +1 -1
  145. package/dist/src/local-first/Owner.js +27 -14
  146. package/dist/src/local-first/Protocol.d.ts +58 -50
  147. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  148. package/dist/src/local-first/Protocol.js +253 -155
  149. package/dist/src/local-first/Query.d.ts +133 -43
  150. package/dist/src/local-first/Query.d.ts.map +1 -1
  151. package/dist/src/local-first/Query.js +139 -90
  152. package/dist/src/local-first/Relay.d.ts +18 -38
  153. package/dist/src/local-first/Relay.d.ts.map +1 -1
  154. package/dist/src/local-first/Relay.js +59 -171
  155. package/dist/src/local-first/Schema.d.ts +139 -144
  156. package/dist/src/local-first/Schema.d.ts.map +1 -1
  157. package/dist/src/local-first/Schema.js +78 -163
  158. package/dist/src/local-first/Shared.d.ts +125 -0
  159. package/dist/src/local-first/Shared.d.ts.map +1 -0
  160. package/dist/src/local-first/Shared.js +569 -0
  161. package/dist/src/local-first/Storage.d.ts +50 -53
  162. package/dist/src/local-first/Storage.d.ts.map +1 -1
  163. package/dist/src/local-first/Storage.js +121 -173
  164. package/dist/src/local-first/Timestamp.d.ts +28 -35
  165. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  166. package/dist/src/local-first/Timestamp.js +24 -25
  167. package/dist/src/local-first/index.d.ts +2 -15
  168. package/dist/src/local-first/index.d.ts.map +1 -1
  169. package/dist/src/local-first/index.js +2 -15
  170. package/package.json +36 -26
  171. package/src/Array.ts +467 -106
  172. package/src/Assert.ts +87 -12
  173. package/src/BigInt.ts +7 -1
  174. package/src/Brand.ts +41 -12
  175. package/src/Buffer.ts +10 -4
  176. package/src/Cache.ts +11 -2
  177. package/src/Callbacks.ts +33 -23
  178. package/src/Console.ts +567 -178
  179. package/src/Crypto.ts +111 -62
  180. package/src/Eq.ts +30 -3
  181. package/src/Error.ts +50 -34
  182. package/src/Function.ts +140 -68
  183. package/src/Identicon.ts +7 -1
  184. package/src/Lookup.ts +415 -0
  185. package/src/Microtask.ts +58 -0
  186. package/src/Number.ts +40 -7
  187. package/src/Object.ts +155 -16
  188. package/src/Option.ts +74 -0
  189. package/src/Order.ts +11 -3
  190. package/src/Platform.ts +43 -7
  191. package/src/Polyfills.ts +465 -0
  192. package/src/Random.ts +25 -11
  193. package/src/Redacted.ts +8 -4
  194. package/src/Ref.ts +72 -21
  195. package/src/RefCount.ts +170 -0
  196. package/src/Relation.ts +188 -136
  197. package/src/Resource.ts +864 -0
  198. package/src/Result.ts +548 -381
  199. package/src/Schedule.ts +1471 -0
  200. package/src/Set.ts +247 -0
  201. package/src/Skiplist.ts +7 -1
  202. package/src/Sqlite.ts +409 -304
  203. package/src/Store.ts +94 -48
  204. package/src/String.ts +6 -0
  205. package/src/Task.ts +3685 -740
  206. package/src/Test.ts +162 -0
  207. package/src/Time.ts +348 -145
  208. package/src/Tracer.ts +54 -0
  209. package/src/Type.ts +1064 -630
  210. package/src/Types.ts +202 -7
  211. package/src/WebSocket.ts +311 -178
  212. package/src/Worker.ts +527 -147
  213. package/src/index.ts +74 -3
  214. package/src/local-first/Db.ts +871 -597
  215. package/src/local-first/Error.ts +17 -0
  216. package/src/local-first/Evolu.ts +884 -823
  217. package/src/local-first/LocalAuth.ts +10 -10
  218. package/src/local-first/Owner.ts +94 -42
  219. package/src/local-first/Protocol.ts +440 -303
  220. package/src/local-first/Query.ts +243 -194
  221. package/src/local-first/Relay.ts +91 -252
  222. package/src/local-first/Schema.ts +284 -394
  223. package/src/local-first/Shared.ts +932 -0
  224. package/src/local-first/Storage.ts +209 -282
  225. package/src/local-first/Timestamp.ts +38 -45
  226. package/src/local-first/index.ts +2 -16
  227. package/LICENSE +0 -21
  228. package/dist/src/Instances.d.ts +0 -34
  229. package/dist/src/Instances.d.ts.map +0 -1
  230. package/dist/src/Instances.js +0 -44
  231. package/dist/src/Resources.d.ts +0 -118
  232. package/dist/src/Resources.d.ts.map +0 -1
  233. package/dist/src/Resources.js +0 -197
  234. package/dist/src/local-first/Platform.d.ts +0 -25
  235. package/dist/src/local-first/Platform.d.ts.map +0 -1
  236. package/dist/src/local-first/Platform.js +0 -1
  237. package/dist/src/local-first/Public.d.ts +0 -22
  238. package/dist/src/local-first/Public.d.ts.map +0 -1
  239. package/dist/src/local-first/Public.js +0 -15
  240. package/dist/src/local-first/PublicKysely.d.ts +0 -148
  241. package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
  242. package/dist/src/local-first/PublicKysely.js +0 -184
  243. package/dist/src/local-first/Sync.d.ts +0 -112
  244. package/dist/src/local-first/Sync.d.ts.map +0 -1
  245. package/dist/src/local-first/Sync.js +0 -529
  246. package/src/Instances.ts +0 -90
  247. package/src/Resources.ts +0 -367
  248. package/src/local-first/Platform.ts +0 -27
  249. package/src/local-first/Public.ts +0 -42
  250. package/src/local-first/PublicKysely.ts +0 -239
  251. 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
+ }
@@ -1,7 +1,11 @@
1
1
  /**
2
- * Bidirectional relation between two types.
2
+ * Bidirectional relations with O(1) lookup in both directions.
3
3
  *
4
- * Why useful:
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
- * - Iteration helpers (`forEach`, iterator) allow treating the structure as a set
13
- * of pairs when needed.
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*` / `get*` each perform a constant number of Map/Set
18
- * operations (O(1) expected).
19
- * - `deleteA` and `deleteB` are O(d) where d is the number of associated elements
20
- * (the degree). This is optimal because every associated pair must be touched
21
- * once.
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
- * Object identity:
41
+ * ```ts
42
+ * const relation = createRelation<WebSocket, string>();
43
+ * relation.add(socket, "owner-1");
44
+ * ```
24
45
  *
25
- * - Elements are compared by reference (standard Map / Set semantics). Structural
26
- * hashing of objects in JavaScript is non-trivial, can be expensive, and
27
- * collision-prone if done naively. Prefer using stable primitive identifiers
28
- * (ids, strings) instead of attempting to hash full object structures.
29
- * - If structural equivalence is truly required, wrap objects in an adapter that
30
- * supplies a canonical hash/id and stores/retrieves the original objects
31
- * separately. This is a rare need; avoid unless you have clear requirements.
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
- /** Gets all B elements related to an A element. */
45
- readonly getB: (a: A) => ReadonlySet<B> | undefined;
46
- /** Gets all A elements related to a B element. */
47
- readonly getA: (b: B) => ReadonlySet<A> | undefined;
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 each pair in the relation (in insertion order of A elements,
50
- * then B elements per A).
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 forEach: (callback: (a: A, b: B) => void) => void;
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 const createRelation: <A, B>() => Relation<A, B>;
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":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;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,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAEpD,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAEpD;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC;IAE3D;;;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,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAEpC,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;IAEpC,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,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,kCAAkC;AAClC,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,OAAK,QAAQ,CAAC,CAAC,EAAE,CAAC,CA2IpD,CAAC"}
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"}
@@ -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
- /** Creates a {@link Relation}. */
3
- export const createRelation = () => {
4
- const aToB = new Map();
5
- const bToA = new Map();
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 relation = {
8
- add(a, b) {
9
- let bSet = aToB.get(a);
10
- if (bSet?.has(b))
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 (!bSet) {
13
- bSet = new Set();
14
- aToB.set(a, bSet);
40
+ if (!relatedB) {
41
+ relatedB = createLookupSet({ lookup: lookupB });
42
+ bByA.set(canonicalA, relatedB);
15
43
  }
16
- bSet.add(b);
17
- let aSet = bToA.get(b);
18
- if (!aSet) {
19
- aSet = new Set();
20
- bToA.set(b, aSet);
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
- aSet.add(a);
50
+ relatedA.add(canonicalA);
23
51
  sizeInternal++;
24
52
  return true;
25
53
  },
26
- remove(a, b) {
27
- const bSet = aToB.get(a);
28
- if (!bSet?.has(b))
54
+ remove: (a, b) => {
55
+ if (!bByA.get(a)?.has(b))
29
56
  return false;
30
- bSet.delete(b);
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
- getB(a) {
44
- return aToB.get(a);
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 removed = bSet.size;
80
- for (const b of bSet) {
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
- deleteB(b) {
94
- const aSet = bToA.get(b);
95
- if (!aSet)
68
+ removeByB: (b) => {
69
+ const relatedA = aByB.get(b);
70
+ if (!relatedA)
96
71
  return false;
97
- const removed = aSet.size;
98
- for (const a of aSet) {
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
- clear() {
112
- aToB.clear();
113
- bToA.clear();
114
- sizeInternal = 0;
115
- },
116
- aCount() {
117
- return aToB.size;
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
- bCount() {
120
- return bToA.size;
85
+ has: (a, b) => {
86
+ const relatedB = bByA.get(a);
87
+ return relatedB?.has(b) ?? false;
121
88
  },
122
- size() {
123
- return sizeInternal;
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
- return relation;
102
+ }
103
+ const assertRelationMappingConsistency = (condition) => {
104
+ assert(condition, "Relation mapping inconsistency");
127
105
  };