@evolu/common 8.0.0-next.5 → 8.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.
Files changed (236) hide show
  1. package/dist/src/Array.d.ts +279 -173
  2. package/dist/src/Array.d.ts.map +1 -1
  3. package/dist/src/Array.js +105 -101
  4. package/dist/src/Assert.d.ts +39 -44
  5. package/dist/src/Assert.d.ts.map +1 -1
  6. package/dist/src/Assert.js +38 -31
  7. package/dist/src/BigInt.d.ts +6 -3
  8. package/dist/src/BigInt.d.ts.map +1 -1
  9. package/dist/src/BigInt.js +5 -2
  10. package/dist/src/Brand.d.ts +27 -40
  11. package/dist/src/Brand.d.ts.map +1 -1
  12. package/dist/src/Buffer.d.ts +27 -11
  13. package/dist/src/Buffer.d.ts.map +1 -1
  14. package/dist/src/Cache.d.ts +9 -4
  15. package/dist/src/Cache.d.ts.map +1 -1
  16. package/dist/src/Cache.js +8 -3
  17. package/dist/src/Callbacks.d.ts +30 -19
  18. package/dist/src/Callbacks.d.ts.map +1 -1
  19. package/dist/src/Console.d.ts +145 -66
  20. package/dist/src/Console.d.ts.map +1 -1
  21. package/dist/src/Console.js +91 -43
  22. package/dist/src/Crypto.d.ts +85 -42
  23. package/dist/src/Crypto.d.ts.map +1 -1
  24. package/dist/src/Crypto.js +44 -20
  25. package/dist/src/Eq.d.ts +87 -21
  26. package/dist/src/Eq.d.ts.map +1 -1
  27. package/dist/src/Eq.js +67 -19
  28. package/dist/src/Error.d.ts +3 -3
  29. package/dist/src/Error.d.ts.map +1 -1
  30. package/dist/src/Function.d.ts +119 -58
  31. package/dist/src/Function.d.ts.map +1 -1
  32. package/dist/src/Function.js +86 -43
  33. package/dist/src/Http.d.ts +394 -0
  34. package/dist/src/Http.d.ts.map +1 -0
  35. package/dist/src/Http.js +102 -0
  36. package/dist/src/Identicon.d.ts +16 -7
  37. package/dist/src/Identicon.d.ts.map +1 -1
  38. package/dist/src/Identicon.js +14 -5
  39. package/dist/src/LeakDetector.d.ts +88 -0
  40. package/dist/src/LeakDetector.d.ts.map +1 -0
  41. package/dist/src/LeakDetector.js +86 -0
  42. package/dist/src/LockManager.d.ts +18 -9
  43. package/dist/src/LockManager.d.ts.map +1 -1
  44. package/dist/src/LockManager.js +7 -8
  45. package/dist/src/Lookup.d.ts +43 -13
  46. package/dist/src/Lookup.d.ts.map +1 -1
  47. package/dist/src/Lookup.js +28 -8
  48. package/dist/src/Microtask.d.ts +1 -1
  49. package/dist/src/Microtask.d.ts.map +1 -1
  50. package/dist/src/Microtask.js +3 -7
  51. package/dist/src/Number.d.ts +49 -13
  52. package/dist/src/Number.d.ts.map +1 -1
  53. package/dist/src/Number.js +34 -5
  54. package/dist/src/Object.d.ts +160 -40
  55. package/dist/src/Object.d.ts.map +1 -1
  56. package/dist/src/Object.js +92 -46
  57. package/dist/src/Option.d.ts +10 -7
  58. package/dist/src/Option.d.ts.map +1 -1
  59. package/dist/src/Option.js +9 -6
  60. package/dist/src/Order.d.ts +21 -21
  61. package/dist/src/Order.d.ts.map +1 -1
  62. package/dist/src/Order.js +18 -21
  63. package/dist/src/Platform.d.ts +31 -0
  64. package/dist/src/Platform.d.ts.map +1 -1
  65. package/dist/src/Platform.js +98 -0
  66. package/dist/src/Random.d.ts +32 -17
  67. package/dist/src/Random.d.ts.map +1 -1
  68. package/dist/src/Random.js +12 -8
  69. package/dist/src/Redacted.d.ts +43 -29
  70. package/dist/src/Redacted.d.ts.map +1 -1
  71. package/dist/src/Redacted.js +14 -3
  72. package/dist/src/Ref.d.ts +11 -3
  73. package/dist/src/Ref.d.ts.map +1 -1
  74. package/dist/src/Ref.js +2 -2
  75. package/dist/src/RefCount.d.ts +2 -6
  76. package/dist/src/RefCount.d.ts.map +1 -1
  77. package/dist/src/RefCount.js +33 -90
  78. package/dist/src/Relation.d.ts +70 -5
  79. package/dist/src/Relation.d.ts.map +1 -1
  80. package/dist/src/Relation.js +77 -2
  81. package/dist/src/Resource.d.ts +590 -155
  82. package/dist/src/Resource.d.ts.map +1 -1
  83. package/dist/src/Resource.js +592 -343
  84. package/dist/src/Result.d.ts +838 -191
  85. package/dist/src/Result.d.ts.map +1 -1
  86. package/dist/src/Result.js +163 -102
  87. package/dist/src/Schedule.d.ts +684 -231
  88. package/dist/src/Schedule.d.ts.map +1 -1
  89. package/dist/src/Schedule.js +766 -295
  90. package/dist/src/Set.d.ts +89 -40
  91. package/dist/src/Set.d.ts.map +1 -1
  92. package/dist/src/Set.js +50 -29
  93. package/dist/src/Sqlite.d.ts +29 -72
  94. package/dist/src/Sqlite.d.ts.map +1 -1
  95. package/dist/src/Sqlite.js +39 -45
  96. package/dist/src/StackTrace.d.ts +40 -0
  97. package/dist/src/StackTrace.d.ts.map +1 -0
  98. package/dist/src/StackTrace.js +140 -0
  99. package/dist/src/Store.d.ts +5 -4
  100. package/dist/src/Store.d.ts.map +1 -1
  101. package/dist/src/Store.js +5 -4
  102. package/dist/src/String.d.ts +7 -1
  103. package/dist/src/String.d.ts.map +1 -1
  104. package/dist/src/String.js +20 -7
  105. package/dist/src/Task.d.ts +3428 -1629
  106. package/dist/src/Task.d.ts.map +1 -1
  107. package/dist/src/Task.js +1876 -1279
  108. package/dist/src/Test.d.ts +17 -124
  109. package/dist/src/Test.d.ts.map +1 -1
  110. package/dist/src/Test.js +17 -68
  111. package/dist/src/Time.d.ts +108 -49
  112. package/dist/src/Time.d.ts.map +1 -1
  113. package/dist/src/Time.js +172 -73
  114. package/dist/src/Type.d.ts +3542 -1926
  115. package/dist/src/Type.d.ts.map +1 -1
  116. package/dist/src/Type.js +3070 -1784
  117. package/dist/src/Types.d.ts +200 -100
  118. package/dist/src/Types.d.ts.map +1 -1
  119. package/dist/src/Types.js +39 -8
  120. package/dist/src/WebSocket.d.ts +48 -23
  121. package/dist/src/WebSocket.d.ts.map +1 -1
  122. package/dist/src/WebSocket.js +15 -6
  123. package/dist/src/Worker.d.ts +94 -36
  124. package/dist/src/Worker.d.ts.map +1 -1
  125. package/dist/src/Worker.js +58 -24
  126. package/dist/src/index.d.ts +55 -52
  127. package/dist/src/index.d.ts.map +1 -1
  128. package/dist/src/index.js +3 -1
  129. package/dist/src/intl/cs.d.ts +94 -0
  130. package/dist/src/intl/cs.d.ts.map +1 -0
  131. package/dist/src/intl/cs.js +220 -0
  132. package/dist/src/intl/index.d.ts +7 -0
  133. package/dist/src/intl/index.d.ts.map +1 -0
  134. package/dist/src/intl/index.js +6 -0
  135. package/dist/src/local-first/Db.d.ts +12 -8
  136. package/dist/src/local-first/Db.d.ts.map +1 -1
  137. package/dist/src/local-first/Db.js +107 -130
  138. package/dist/src/local-first/Error.d.ts +4 -4
  139. package/dist/src/local-first/Evolu.d.ts +338 -92
  140. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  141. package/dist/src/local-first/Evolu.js +47 -55
  142. package/dist/src/local-first/LocalAuth.d.ts +4 -4
  143. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  144. package/dist/src/local-first/Owner.d.ts +61 -24
  145. package/dist/src/local-first/Owner.d.ts.map +1 -1
  146. package/dist/src/local-first/Owner.js +45 -8
  147. package/dist/src/local-first/Protocol.d.ts +44 -31
  148. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  149. package/dist/src/local-first/Protocol.js +36 -13
  150. package/dist/src/local-first/Query.d.ts +147 -62
  151. package/dist/src/local-first/Query.d.ts.map +1 -1
  152. package/dist/src/local-first/Query.js +112 -58
  153. package/dist/src/local-first/Relay.d.ts +44 -14
  154. package/dist/src/local-first/Relay.d.ts.map +1 -1
  155. package/dist/src/local-first/Schema.d.ts +92 -41
  156. package/dist/src/local-first/Schema.d.ts.map +1 -1
  157. package/dist/src/local-first/Schema.js +25 -8
  158. package/dist/src/local-first/Shared.d.ts +23 -21
  159. package/dist/src/local-first/Shared.d.ts.map +1 -1
  160. package/dist/src/local-first/Shared.js +85 -42
  161. package/dist/src/local-first/Storage.d.ts +53 -32
  162. package/dist/src/local-first/Storage.d.ts.map +1 -1
  163. package/dist/src/local-first/Storage.js +194 -234
  164. package/dist/src/local-first/Timestamp.d.ts +21 -21
  165. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  166. package/dist/src/local-first/Timestamp.js +2 -2
  167. package/dist/src/local-first/index.d.ts +11 -11
  168. package/package.json +14 -13
  169. package/src/Array.ts +306 -205
  170. package/src/Assert.ts +48 -68
  171. package/src/BigInt.ts +6 -3
  172. package/src/Brand.ts +27 -40
  173. package/src/Buffer.ts +28 -12
  174. package/src/Cache.ts +9 -4
  175. package/src/Callbacks.ts +30 -19
  176. package/src/Console.ts +156 -84
  177. package/src/Crypto.ts +83 -41
  178. package/src/Eq.ts +95 -22
  179. package/src/Error.ts +5 -2
  180. package/src/Function.ts +167 -59
  181. package/src/Http.ts +549 -0
  182. package/src/Identicon.ts +16 -7
  183. package/src/LeakDetector.ts +182 -0
  184. package/src/LockManager.ts +43 -39
  185. package/src/Lookup.ts +62 -28
  186. package/src/Microtask.ts +19 -23
  187. package/src/Number.ts +64 -12
  188. package/src/Object.ts +173 -49
  189. package/src/Option.ts +10 -7
  190. package/src/Order.ts +23 -21
  191. package/src/Platform.ts +136 -0
  192. package/src/Polyfills.ts +6 -12
  193. package/src/Random.ts +32 -19
  194. package/src/Redacted.ts +44 -30
  195. package/src/Ref.ts +14 -6
  196. package/src/RefCount.ts +38 -80
  197. package/src/Relation.ts +202 -10
  198. package/src/Resource.ts +1199 -591
  199. package/src/Result.ts +930 -271
  200. package/src/Schedule.ts +859 -343
  201. package/src/Set.ts +89 -40
  202. package/src/Sqlite.ts +142 -137
  203. package/src/StackTrace.ts +230 -0
  204. package/src/Store.ts +8 -7
  205. package/src/String.ts +22 -5
  206. package/src/Task.ts +5109 -2845
  207. package/src/Test.ts +20 -167
  208. package/src/Time.ts +293 -90
  209. package/src/Type.ts +10034 -4117
  210. package/src/Types.ts +227 -101
  211. package/src/WebSocket.ts +64 -36
  212. package/src/Worker.ts +180 -72
  213. package/src/index.ts +55 -52
  214. package/src/intl/cs.ts +388 -0
  215. package/src/intl/index.ts +7 -0
  216. package/src/local-first/Db.ts +161 -190
  217. package/src/local-first/Error.ts +4 -4
  218. package/src/local-first/Evolu.ts +495 -250
  219. package/src/local-first/LocalAuth.ts +6 -12
  220. package/src/local-first/Owner.ts +58 -21
  221. package/src/local-first/Protocol.ts +49 -31
  222. package/src/local-first/Query.ts +146 -61
  223. package/src/local-first/Relay.ts +51 -21
  224. package/src/local-first/Schema.ts +109 -51
  225. package/src/local-first/Shared.ts +253 -214
  226. package/src/local-first/Storage.ts +266 -264
  227. package/src/local-first/Timestamp.ts +25 -18
  228. package/src/local-first/index.ts +11 -11
  229. package/dist/src/Skiplist.d.ts +0 -28
  230. package/dist/src/Skiplist.d.ts.map +0 -1
  231. package/dist/src/Skiplist.js +0 -64
  232. package/dist/src/Tracer.d.ts +0 -48
  233. package/dist/src/Tracer.d.ts.map +0 -1
  234. package/dist/src/Tracer.js +0 -6
  235. package/src/Skiplist.ts +0 -108
  236. package/src/Tracer.ts +0 -54
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Seeded random number generation.
2
+ * Random number generation.
3
3
  *
4
4
  * @module
5
5
  */
6
6
  import { Random as RandomLib } from "random";
7
- import type { Brand } from "./Brand.js";
7
+ import type { Brand } from "./Brand.ts";
8
8
  /**
9
9
  * A random floating point number in [0, 1).
10
10
  *
@@ -19,41 +19,56 @@ export type RandomNumber = number & Brand<"RandomNumber">;
19
19
  * ### Example
20
20
  *
21
21
  * ```ts
22
- * // For apps
22
+ * import {
23
+ * createRandom,
24
+ * testCreateRandom,
25
+ * type RandomNumber,
26
+ * } from "@evolu/common";
27
+ *
28
+ * // For apps, use the Math.random-backed implementation.
23
29
  * const random = createRandom();
24
- * random.next();
30
+ * const value = random.next();
31
+ * expectTypeOf(value).toEqualTypeOf<RandomNumber>();
32
+ * expect(value).toBeGreaterThanOrEqual(0);
33
+ * expect(value).toBeLessThan(1);
25
34
  *
26
- * // For tests
27
- * const random = createRandomWithSeed("test");
28
- * random.next();
35
+ * // For tests, use a seed.
36
+ * const firstTestRandom = testCreateRandom("test");
37
+ * const secondTestRandom = testCreateRandom("test");
38
+ * expect(firstTestRandom.next()).toBe(secondTestRandom.next());
29
39
  * ```
30
40
  */
31
41
  export interface Random {
32
42
  /** Returns a floating point number in [0, 1). Just like Math.random(). */
33
- next: () => RandomNumber;
43
+ readonly next: () => RandomNumber;
34
44
  }
35
45
  export interface RandomDep {
36
- random: Random;
46
+ readonly random: Random;
37
47
  }
38
48
  /** Creates a {@link Random} using Math.random(). */
39
49
  export declare const createRandom: () => Random;
40
- /** Creates a seeded {@link Random} for deterministic tests. Default seed "evolu". */
41
- export declare const testCreateRandom: (seed?: string) => Random;
42
50
  /**
43
- * Creates {@link Random} using {@link RandomLibDep} with a seed which is useful
44
- * for tests.
51
+ * Creates a seeded {@link Random} for deterministic tests.
52
+ *
53
+ * Default seed "evolu".
45
54
  */
46
- export declare const createRandomWithSeed: (seed: string) => Random;
55
+ export declare const testCreateRandom: (seed?: string) => Random;
47
56
  /**
48
- * A random number generator using the NPM `random` package dependency.
57
+ * Seedable random number generator supporting many common distributions,
58
+ * provided by the NPM `random` package.
49
59
  *
50
60
  * https://github.com/transitive-bullshit/random
51
61
  */
52
62
  export interface RandomLibDep {
53
63
  readonly randomLib: RandomLib;
54
64
  }
55
- /** Creates a `RandomLib` using the NPM `random` package. */
65
+ /** Creates a random number generator from the NPM `random` package. */
56
66
  export declare const createRandomLib: () => RandomLib;
57
- /** Creates a seeded `RandomLib` for deterministic tests. Default seed "evolu". */
67
+ /**
68
+ * Creates a seeded random number generator from the NPM `random` package for
69
+ * deterministic tests.
70
+ *
71
+ * Default seed "evolu".
72
+ */
58
73
  export declare const testCreateRandomLib: (seed?: string) => RandomLib;
59
74
  //# sourceMappingURL=Random.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Random.d.ts","sourceRoot":"","sources":["../../src/Random.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,MAAM;IACrB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,YAAY,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,oDAAoD;AACpD,eAAO,MAAM,YAAY,QAAO,MAE9B,CAAC;AAEH,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,GAAI,aAAc,KAAG,MACtB,CAAC;AAE7B;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,MAKnD,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC/B;AAED,4DAA4D;AAC5D,eAAO,MAAM,eAAe,QAAO,SAA4B,CAAC;AAEhE,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,GAAI,aAAc,KAAG,SAChC,CAAC"}
1
+ {"version":3,"file":"Random.d.ts","sourceRoot":"","sources":["../../src/Random.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,MAAM;IACrB,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,YAAY,CAAC;CACnC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,oDAAoD;AACpD,eAAO,MAAM,YAAY,QAAO,MAE9B,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,qBAAqB,MAKjD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC/B;AAED,uEAAuE;AACvE,eAAO,MAAM,eAAe,QAAO,SAA4B,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,qBAAqB,SAChC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Seeded random number generation.
2
+ * Random number generation.
3
3
  *
4
4
  * @module
5
5
  */
@@ -8,19 +8,23 @@ import { Random as RandomLib } from "random";
8
8
  export const createRandom = () => ({
9
9
  next: () => Math.random(),
10
10
  });
11
- /** Creates a seeded {@link Random} for deterministic tests. Default seed "evolu". */
12
- export const testCreateRandom = (seed = "evolu") => createRandomWithSeed(seed);
13
11
  /**
14
- * Creates {@link Random} using {@link RandomLibDep} with a seed which is useful
15
- * for tests.
12
+ * Creates a seeded {@link Random} for deterministic tests.
13
+ *
14
+ * Default seed "evolu".
16
15
  */
17
- export const createRandomWithSeed = (seed) => {
16
+ export const testCreateRandom = (seed = "evolu") => {
18
17
  const random = new RandomLib(seed);
19
18
  return {
20
19
  next: () => random.next(),
21
20
  };
22
21
  };
23
- /** Creates a `RandomLib` using the NPM `random` package. */
22
+ /** Creates a random number generator from the NPM `random` package. */
24
23
  export const createRandomLib = () => new RandomLib();
25
- /** Creates a seeded `RandomLib` for deterministic tests. Default seed "evolu". */
24
+ /**
25
+ * Creates a seeded random number generator from the NPM `random` package for
26
+ * deterministic tests.
27
+ *
28
+ * Default seed "evolu".
29
+ */
26
30
  export const testCreateRandomLib = (seed = "evolu") => new RandomLib(seed);
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import type { Brand } from "./Brand.js";
7
- import type { Eq } from "./Eq.js";
6
+ import type { Brand } from "./Brand.ts";
7
+ import type { Eq } from "./Eq.ts";
8
8
  /**
9
9
  * A wrapper type that prevents sensitive values from being accidentally exposed
10
10
  * through logging, serialization, or inspection.
@@ -26,35 +26,38 @@ import type { Eq } from "./Eq.js";
26
26
  * ### Example
27
27
  *
28
28
  * ```ts
29
- * // Define branded types for your secrets
29
+ * import {
30
+ * createRedacted,
31
+ * revealRedacted,
32
+ * type Brand,
33
+ * type Redacted,
34
+ * } from "@evolu/common";
35
+ *
30
36
  * type ApiKey = string & Brand<"ApiKey">;
31
37
  * type DbPassword = string & Brand<"DbPassword">;
32
- *
33
- * // Wrap them with Redacted for safe passing
34
38
  * type RedactedApiKey = Redacted<ApiKey>;
35
- * type RedactedDbPassword = Redacted<DbPassword>;
36
39
  *
37
- * // Create a redacted secret
40
+ * // Apply brands only after validation or at another trusted boundary.
38
41
  * const apiKey: ApiKey = "secret-123" as ApiKey;
39
- * const redactedKey: RedactedApiKey = createRedacted(apiKey);
40
- *
41
- * console.log(redactedKey); // <redacted>
42
- * console.log(revealRedacted(redactedKey)); // secret-123
43
- *
44
- * // Type safety: RedactedApiKey ≠ RedactedDbPassword
45
- * const fetchUser = (key: RedactedApiKey) => {
46
- * const value: ApiKey = revealRedacted(key);
47
- * // use value...
48
- * };
49
- *
50
- * fetchUser(redactedKey); // ok
51
- * // fetchUser(createRedacted("x" as DbPassword)); // type error
52
- *
53
- * // Automatic cleanup with `using`
54
- * {
55
- * using secret = createRedacted("sensitive" as ApiKey);
56
- * // ... use secret ...
57
- * } // automatically wiped from memory
42
+ * using redactedKey: RedactedApiKey = createRedacted(apiKey);
43
+ * const fetchUser = (key: RedactedApiKey): ApiKey => revealRedacted(key);
44
+ *
45
+ * expect(redactedKey.toString()).toBe("<redacted>");
46
+ * expect(JSON.stringify({ apiKey: redactedKey })).toBe(
47
+ * '{"apiKey":"<redacted>"}',
48
+ * );
49
+ * expect(fetchUser(redactedKey)).toBe(apiKey);
50
+ *
51
+ * using password = createRedacted("password" as DbPassword);
52
+ * // @ts-expect-error Redacted secrets retain their distinct branded types.
53
+ * fetchUser(password);
54
+ *
55
+ * const disposedKey = (() => {
56
+ * using key = createRedacted(apiKey);
57
+ * return key;
58
+ * })();
59
+ * // Leaving the `using` scope removes the value from memory.
60
+ * expect(() => revealRedacted(disposedKey)).toThrow();
58
61
  * ```
59
62
  */
60
63
  export interface Redacted<A> extends Brand<"Redacted">, Disposable {
@@ -80,12 +83,23 @@ export declare const isRedacted: (value: unknown) => value is Redacted<unknown>;
80
83
  * ### Example
81
84
  *
82
85
  * ```ts
86
+ * import {
87
+ * createEqRedacted,
88
+ * createRedacted,
89
+ * eqString,
90
+ * type Brand,
91
+ * } from "@evolu/common";
92
+ *
83
93
  * type ApiKey = string & Brand<"ApiKey">;
84
94
  * const eqRedactedApiKey = createEqRedacted<ApiKey>(eqString);
85
95
  *
86
- * const a = createRedacted("x" as ApiKey);
87
- * const b = createRedacted("x" as ApiKey);
88
- * eqRedactedApiKey(a, b); // true
96
+ * // Apply brands only after validation or at another trusted boundary.
97
+ * using a = createRedacted("x" as ApiKey);
98
+ * using b = createRedacted("x" as ApiKey);
99
+ * using c = createRedacted("y" as ApiKey);
100
+ *
101
+ * expect(eqRedactedApiKey(a, b)).toBe(true);
102
+ * expect(eqRedactedApiKey(a, c)).toBe(false);
89
103
  * ```
90
104
  */
91
105
  export declare const createEqRedacted: <A>(eq: Eq<A>) => Eq<Redacted<A>>;
@@ -1 +1 @@
1
- {"version":3,"file":"Redacted.d.ts","sourceRoot":"","sources":["../../src/Redacted.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,CAAE,SAAQ,KAAK,CAAC,UAAU,CAAC,EAAE,UAAU;IAChE,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;CAClB;AAED,gEAAgE;AAChE,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,QAAQ,CAAC,CAAC,CAItD,CAAC;AAaF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,UAAU,QAAQ,CAAC,CAAC,CAAC,KAAG,CAGzD,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,QAAQ,CAAC,OAAO,CAG7B,CAAC;AAEzC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,GAC1B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,KAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAEY,CAAC"}
1
+ {"version":3,"file":"Redacted.d.ts","sourceRoot":"","sources":["../../src/Redacted.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,CAAE,SAAQ,KAAK,CAAC,UAAU,CAAC,EAAE,UAAU;IAChE,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;CAClB;AAED,gEAAgE;AAChE,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,CAAC,KAAG,QAAQ,CAAC,CAAC,CAItD,CAAC;AAaF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,YAAY,QAAQ,CAAC,CAAC,CAAC,KAAG,CAGzD,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,UAAU,UAAW,OAAO,KAAG,KAAK,IAAI,QAAQ,CAAC,OAAO,CAG7B,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,gBAAgB,GAC1B,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAEY,CAAC"}
@@ -42,12 +42,23 @@ export const isRedacted = (value) => typeof value === "object" &&
42
42
  * ### Example
43
43
  *
44
44
  * ```ts
45
+ * import {
46
+ * createEqRedacted,
47
+ * createRedacted,
48
+ * eqString,
49
+ * type Brand,
50
+ * } from "@evolu/common";
51
+ *
45
52
  * type ApiKey = string & Brand<"ApiKey">;
46
53
  * const eqRedactedApiKey = createEqRedacted<ApiKey>(eqString);
47
54
  *
48
- * const a = createRedacted("x" as ApiKey);
49
- * const b = createRedacted("x" as ApiKey);
50
- * eqRedactedApiKey(a, b); // true
55
+ * // Apply brands only after validation or at another trusted boundary.
56
+ * using a = createRedacted("x" as ApiKey);
57
+ * using b = createRedacted("x" as ApiKey);
58
+ * using c = createRedacted("y" as ApiKey);
59
+ *
60
+ * expect(eqRedactedApiKey(a, b)).toBe(true);
61
+ * expect(eqRedactedApiKey(a, c)).toBe(false);
51
62
  * ```
52
63
  */
53
64
  export const createEqRedacted = (eq) => (x, y) => eq(revealRedacted(x), revealRedacted(y));
package/dist/src/Ref.d.ts CHANGED
@@ -18,18 +18,26 @@
18
18
  * ### Example
19
19
  *
20
20
  * ```ts
21
+ * import { createRef } from "@evolu/common";
22
+ *
21
23
  * const count = createRef(0);
22
24
  * count.set(1);
23
25
  * count.update((n) => n + 1);
24
- * console.log(count.get()); // 2
26
+ * expect(count.get()).toBe(2);
25
27
  * ```
26
28
  *
27
- * ### Example of using Ref as a dependency
29
+ * ### As a dependency
28
30
  *
29
31
  * ```ts
32
+ * import { createRef, type Ref } from "@evolu/common";
33
+ *
30
34
  * interface CounterRefDep {
31
35
  * readonly counterRef: Ref<number>;
32
36
  * }
37
+ *
38
+ * const deps: CounterRefDep = { counterRef: createRef(0) };
39
+ * deps.counterRef.update((count) => count + 1);
40
+ * expect(deps.counterRef.get()).toBe(1);
33
41
  * ```
34
42
  */
35
43
  export interface Ref<T> {
@@ -48,7 +56,7 @@ export interface Ref<T> {
48
56
  /** Updates the current value and returns it. */
49
57
  readonly updateAndGet: (updater: (current: T) => T) => T;
50
58
  /** Modifies the current value and returns a computed result. */
51
- readonly modify: <R>(updater: (current: T) => readonly [result: R, nextValue: T]) => R;
59
+ readonly modify: <R>(modifier: (current: T) => readonly [result: R, nextValue: T]) => R;
52
60
  }
53
61
  /** Creates a {@link Ref} with the given initial immutable value. */
54
62
  export declare const createRef: <T>(initialValue: T) => Ref<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"Ref.d.ts","sourceRoot":"","sources":["../../src/Ref.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,WAAW,GAAG,CAAC,CAAC;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAEtB,8BAA8B;IAC9B,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAEjC,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;IAEpC,6CAA6C;IAC7C,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;IAEpC,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;IAEtD,gEAAgE;IAChE,QAAQ,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEzD,gDAAgD;IAChD,QAAQ,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEzD,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,EACjB,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,KACxD,CAAC,CAAC;CACR;AAED,oEAAoE;AACpE,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,cAAc,CAAC,KAAG,GAAG,CAAC,CAAC,CA0CnD,CAAC"}
1
+ {"version":3,"file":"Ref.d.ts","sourceRoot":"","sources":["../../src/Ref.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,WAAW,GAAG,CAAC,CAAC;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAEtB,8BAA8B;IAC9B,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAEjC,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;IAEpC,6CAA6C;IAC7C,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;IAEpC,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;IAEtD,gEAAgE;IAChE,QAAQ,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEzD,gDAAgD;IAChD,QAAQ,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEzD,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,EACjB,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,KACzD,CAAC,CAAC;CACR;AAED,oEAAoE;AACpE,eAAO,MAAM,SAAS,GAAI,CAAC,gBAAgB,CAAC,KAAG,GAAG,CAAC,CAAC,CA0CnD,CAAC"}
package/dist/src/Ref.js CHANGED
@@ -32,8 +32,8 @@ export const createRef = (initialValue) => {
32
32
  currentValue = updater(currentValue);
33
33
  return currentValue;
34
34
  },
35
- modify: (updater) => {
36
- const [result, nextValue] = updater(currentValue);
35
+ modify: (modifier) => {
36
+ const [result, nextValue] = modifier(currentValue);
37
37
  currentValue = nextValue;
38
38
  return result;
39
39
  },
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { type LookupOption } from "./Lookup.js";
7
- import { NonNegativeInt, PositiveInt } from "./Type.js";
6
+ import { type LookupOption } from "./Lookup.ts";
7
+ import { NonNegativeInt, PositiveInt } from "./Type.ts";
8
8
  /**
9
9
  * Reference count for one retained value.
10
10
  *
@@ -21,8 +21,6 @@ export interface RefCount extends Disposable {
21
21
  readonly decrement: () => NonNegativeInt;
22
22
  /** Returns the current count. */
23
23
  readonly getCount: () => NonNegativeInt;
24
- /** Disposes and invalidates the helper. Further method calls throw. */
25
- readonly [Symbol.dispose]: () => void;
26
24
  }
27
25
  /** Creates {@link RefCount}. */
28
26
  export declare const createRefCount: () => RefCount;
@@ -50,8 +48,6 @@ export interface RefCountByKey<TKey> extends Disposable {
50
48
  readonly has: (key: TKey) => boolean;
51
49
  /** Returns all currently tracked keys. */
52
50
  readonly keys: () => ReadonlySet<TKey>;
53
- /** Disposes and invalidates the helper. Further method calls throw. */
54
- readonly [Symbol.dispose]: () => void;
55
51
  }
56
52
  /** Options for {@link createRefCountByKey}. */
57
53
  export interface CreateRefCountByKeyOptions<TKey, L = TKey> extends LookupOption<TKey, L> {
@@ -1 +1 @@
1
- {"version":3,"file":"RefCount.d.ts","sourceRoot":"","sources":["../../src/RefCount.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAgC,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,WAAW,EAAsB,MAAM,WAAW,CAAC;AAE5E;;;;GAIG;AACH,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,MAAM,WAAW,CAAC;IAEtC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,cAAc,CAAC;IAEzC,iCAAiC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,cAAc,CAAC;IAExC,uEAAuE;IACvE,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACvC;AAED,gCAAgC;AAChC,eAAO,MAAM,cAAc,QAAO,QA4BjC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa,CAAC,IAAI,CAAE,SAAQ,UAAU;IACrD,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,WAAW,CAAC;IAE/C;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,cAAc,CAAC;IAElD,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,cAAc,CAAC;IAEjD,2EAA2E;IAC3E,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC;IAErC,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;IAEvC,uEAAuE;IACvE,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACvC;AAED,+CAA+C;AAC/C,MAAM,WAAW,0BAA0B,CACzC,IAAI,EACJ,CAAC,GAAG,IAAI,CACR,SAAQ,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;CAAG;AAElC,qCAAqC;AACrC,wBAAgB,mBAAmB,CAAC,IAAI,GAAG,OAAO,KAAK,aAAa,CAAC,IAAI,CAAC,CAAC;AAC3E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,CAAC,EACzC,OAAO,EAAE,0BAA0B,CAAC,IAAI,EAAE,CAAC,CAAC,GAC3C,aAAa,CAAC,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"RefCount.d.ts","sourceRoot":"","sources":["../../src/RefCount.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAgC,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,WAAW,EAAsB,MAAM,WAAW,CAAC;AAE5E;;;;GAIG;AACH,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,MAAM,WAAW,CAAC;IAEtC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,cAAc,CAAC;IAEzC,iCAAiC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,cAAc,CAAC;CACzC;AAED,gCAAgC;AAChC,eAAO,MAAM,cAAc,QAAO,QAkBjC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa,CAAC,IAAI,CAAE,SAAQ,UAAU;IACrD,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,WAAW,CAAC;IAE/C;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,cAAc,CAAC;IAElD,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,cAAc,CAAC;IAEjD,2EAA2E;IAC3E,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC;IAErC,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,+CAA+C;AAC/C,MAAM,WAAW,0BAA0B,CACzC,IAAI,EACJ,CAAC,GAAG,IAAI,CACR,SAAQ,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;CAAG;AAElC,qCAAqC;AACrC,wBAAgB,mBAAmB,CAAC,IAAI,GAAG,OAAO,KAAK,aAAa,CAAC,IAAI,CAAC,CAAC;AAC3E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,CAAC,EACzC,OAAO,EAAE,0BAA0B,CAAC,IAAI,EAAE,CAAC,CAAC,GAC3C,aAAa,CAAC,IAAI,CAAC,CAAC"}
@@ -55,87 +55,40 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
55
55
  var e = new Error(message);
56
56
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
57
57
  });
58
- import { assert, assertNotDisposed } from "./Assert.js";
59
- import { identity } from "./Function.js";
58
+ import { assert } from "./Assert.js";
59
+ import { disposable, identity } from "./Function.js";
60
60
  import { createLookupMap } from "./Lookup.js";
61
61
  import { NonNegativeInt, PositiveInt, zeroNonNegativeInt } from "./Type.js";
62
62
  /** Creates {@link RefCount}. */
63
63
  export const createRefCount = () => {
64
- const env_1 = { stack: [], error: void 0, hasError: false };
65
- try {
66
- // We use DisposableStack only because of assertNotDisposed.
67
- const disposer = __addDisposableResource(env_1, new DisposableStack(), false);
68
- let count = zeroNonNegativeInt;
69
- const disposables = disposer.move();
70
- return {
71
- increment: () => {
72
- assertNotDisposed(disposables);
73
- const nextCount = PositiveInt.orThrow(count + 1);
74
- count = nextCount;
75
- return nextCount;
76
- },
77
- decrement: () => {
78
- assertNotDisposed(disposables);
79
- assert(count > 0, "RefCount must not be decremented below zero.");
80
- count = NonNegativeInt.orThrow(count - 1);
81
- return count;
82
- },
83
- getCount: () => {
84
- assertNotDisposed(disposables);
85
- return count;
86
- },
87
- [Symbol.dispose]: () => disposables.dispose(),
88
- };
89
- }
90
- catch (e_1) {
91
- env_1.error = e_1;
92
- env_1.hasError = true;
93
- }
94
- finally {
95
- __disposeResources(env_1);
96
- }
64
+ let count = zeroNonNegativeInt;
65
+ return disposable({
66
+ increment: () => {
67
+ const nextCount = PositiveInt.orThrow(count + 1);
68
+ count = nextCount;
69
+ return nextCount;
70
+ },
71
+ decrement: () => {
72
+ assert(count > 0, "RefCount must not be decremented below zero.");
73
+ count = NonNegativeInt.orThrow(count - 1);
74
+ return count;
75
+ },
76
+ getCount: () => count,
77
+ });
97
78
  };
98
79
  export function createRefCountByKey({ lookup = identity, } = {}) {
99
- const env_2 = { stack: [], error: void 0, hasError: false };
80
+ const env_1 = { stack: [], error: void 0, hasError: false };
100
81
  try {
101
- // We use DisposableStack only because of assertNotDisposed.
102
- const disposer = __addDisposableResource(env_2, new DisposableStack(), false);
103
- const refCountByKey = disposer.adopt(createLookupMap({ lookup }), (refCountByKey) => {
104
- const env_3 = { stack: [], error: void 0, hasError: false };
105
- try {
106
- const disposer = __addDisposableResource(env_3, new DisposableStack(), false);
107
- disposer.defer(() => {
108
- refCountByKey.clear();
109
- });
110
- for (const refCount of refCountByKey.values()) {
111
- disposer.use(refCount);
112
- }
113
- }
114
- catch (e_3) {
115
- env_3.error = e_3;
116
- env_3.hasError = true;
117
- }
118
- finally {
119
- __disposeResources(env_3);
120
- }
82
+ const disposer = __addDisposableResource(env_1, new DisposableStack(), false);
83
+ const refCountByKey = createLookupMap({ lookup });
84
+ disposer.defer(() => {
85
+ refCountByKey.clear();
121
86
  });
122
- const disposables = disposer.move();
123
- const getRefCount = (key) => {
124
- let refCount = refCountByKey.get(key);
125
- if (!refCount) {
126
- refCount = createRefCount();
127
- refCountByKey.set(key, refCount);
128
- }
129
- return refCount;
130
- };
131
- return {
132
- increment: (key) => {
133
- assertNotDisposed(disposables);
134
- return getRefCount(key).increment();
135
- },
87
+ return disposable({
88
+ increment: (key) => refCountByKey.getOrInsertComputed(key, createRefCount).increment(),
136
89
  decrement: (key) => {
137
- assertNotDisposed(disposables);
138
- const refCount = getRefCount(key);
90
+ const refCount = refCountByKey.get(key);
91
+ assert(refCount, "RefCount must not be decremented for an untracked key.");
139
92
  const nextCount = refCount.decrement();
140
93
  if (nextCount === 0) {
141
94
  refCountByKey.delete(key);
@@ -143,26 +96,16 @@ export function createRefCountByKey({ lookup = identity, } = {}) {
143
96
  }
144
97
  return nextCount;
145
98
  },
146
- getCount: (key) => {
147
- assertNotDisposed(disposables);
148
- return refCountByKey.get(key)?.getCount() ?? zeroNonNegativeInt;
149
- },
150
- has: (key) => {
151
- assertNotDisposed(disposables);
152
- return refCountByKey.has(key);
153
- },
154
- keys: () => {
155
- assertNotDisposed(disposables);
156
- return new Set(refCountByKey.keys());
157
- },
158
- [Symbol.dispose]: () => disposables.dispose(),
159
- };
99
+ getCount: (key) => refCountByKey.get(key)?.getCount() ?? zeroNonNegativeInt,
100
+ has: (key) => refCountByKey.has(key),
101
+ keys: () => new Set(refCountByKey.keys()),
102
+ }, disposer);
160
103
  }
161
- catch (e_2) {
162
- env_2.error = e_2;
163
- env_2.hasError = true;
104
+ catch (e_1) {
105
+ env_1.error = e_1;
106
+ env_1.hasError = true;
164
107
  }
165
108
  finally {
166
- __disposeResources(env_2);
109
+ __disposeResources(env_1);
167
110
  }
168
111
  }
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { type Lookup } from "./Lookup.js";
6
+ import { type Lookup } from "./Lookup.ts";
7
+ import { NonNegativeInt, PositiveInt } from "./Type.ts";
7
8
  /**
8
9
  * Bidirectional relation between two types.
9
10
  *
@@ -34,20 +35,33 @@ import { type Lookup } from "./Lookup.js";
34
35
  * The input parameter types of `lookupA` and `lookupB` determine which values
35
36
  * the returned relation accepts on each side.
36
37
  *
37
- * ### Example
38
+ * ### Reference identity
38
39
  *
39
40
  * Use the default identity semantics.
40
41
  *
41
42
  * ```ts
42
- * const relation = createRelation<WebSocket, string>();
43
+ * import { createRelation } from "@evolu/common";
44
+ *
45
+ * interface Socket {
46
+ * readonly id: string;
47
+ * }
48
+ *
49
+ * const relation = createRelation<Socket, string>();
50
+ * const socket = { id: "socket-1" };
51
+ *
43
52
  * relation.add(socket, "owner-1");
53
+ *
54
+ * expect(relation.has(socket, "owner-1")).toBe(true);
55
+ * expect(relation.has({ id: "socket-1" }, "owner-1")).toBe(false);
44
56
  * ```
45
57
  *
46
- * ### Example
58
+ * ### Lookup-derived equality
47
59
  *
48
60
  * Use lookup-derived equality.
49
61
  *
50
62
  * ```ts
63
+ * import { createRelation } from "@evolu/common";
64
+ *
51
65
  * interface Person {
52
66
  * readonly id: string;
53
67
  * readonly name: string;
@@ -59,7 +73,10 @@ import { type Lookup } from "./Lookup.js";
59
73
  * });
60
74
  *
61
75
  * relation.add({ id: "1", name: "Ada" }, { id: "admins" });
62
- * relation.has({ id: "1", name: "Grace" }, { id: "admins" }); // true
76
+ *
77
+ * expect(relation.has({ id: "1", name: "Grace" }, { id: "admins" })).toBe(
78
+ * true,
79
+ * );
63
80
  * ```
64
81
  *
65
82
  * @see {@link createRelation}
@@ -131,4 +148,52 @@ export interface CreateRelationOptions<A, B, LA = A, LB = B> {
131
148
  /** Creates a {@link Relation}. */
132
149
  export declare function createRelation<A, B>(): Relation<A, B>;
133
150
  export declare function createRelation<A, B, LA, LB>(options: CreateRelationOptions<A, B, LA, LB>): Relation<A, B>;
151
+ /**
152
+ * Bidirectional relation with a positive retain count for every active pair.
153
+ *
154
+ * Logical equality and canonical representatives follow the same lookup rules
155
+ * as {@link Relation}. The first active representative on each side remains
156
+ * canonical until that value has no active pairs. Increment and decrement
157
+ * return those representatives so callers can reliably handle pair transitions
158
+ * without repeating lookup or canonicalization logic.
159
+ *
160
+ * Unlike {@link Relation}'s live iterators, directional and entry reads return
161
+ * snapshots so callers can mutate this relation while iterating. Their order
162
+ * follows first insertion of the current canonical representatives and pairs.
163
+ */
164
+ export interface RefCountedRelation<A, B> {
165
+ /** Increments a pair count and returns its canonical values and new count. */
166
+ readonly increment: (a: A, b: B) => {
167
+ readonly a: A;
168
+ readonly b: B;
169
+ readonly count: PositiveInt;
170
+ };
171
+ /**
172
+ * Decrements a pair count and returns its canonical values and new count.
173
+ * Decrementing a missing pair is a programmer error checked with
174
+ * {@link assert}.
175
+ */
176
+ readonly decrement: (a: A, b: B) => {
177
+ readonly a: A;
178
+ readonly b: B;
179
+ readonly count: NonNegativeInt;
180
+ };
181
+ /** Returns the pair count, or zero when the pair is absent. */
182
+ readonly getCount: (a: A, b: B) => NonNegativeInt;
183
+ /** Returns a snapshot of canonical A values related to `b`. */
184
+ readonly getAs: (b: B) => ReadonlyArray<A>;
185
+ /** Returns a snapshot of canonical B values related to `a`. */
186
+ readonly getBs: (a: A) => ReadonlyArray<B>;
187
+ /** Returns whether an A value has at least one active pair. */
188
+ readonly hasA: (a: A) => boolean;
189
+ /** Returns whether a B value has at least one active pair. */
190
+ readonly hasB: (b: B) => boolean;
191
+ /** Returns a snapshot of every canonical pair and its positive count. */
192
+ readonly getEntries: () => ReadonlyArray<readonly [a: A, b: B, count: PositiveInt]>;
193
+ /** Removes all pair counts and both directional indexes. */
194
+ readonly clear: () => void;
195
+ }
196
+ /** Creates a {@link RefCountedRelation}. */
197
+ export declare function createRefCountedRelation<A, B>(): RefCountedRelation<A, B>;
198
+ export declare function createRefCountedRelation<A, B, LA, LB>(options: CreateRelationOptions<A, B, LA, LB>): RefCountedRelation<A, B>;
134
199
  //# sourceMappingURL=Relation.d.ts.map
@@ -1 +1 @@
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
+ {"version":3,"file":"Relation.d.ts","sourceRoot":"","sources":["../../src/Relation.ts"],"names":[],"mappings":"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"}