@evolu/common 5.4.7 → 6.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 (242) hide show
  1. package/README.md +30 -34
  2. package/dist/src/Array.d.ts +17 -0
  3. package/dist/src/Array.d.ts.map +1 -0
  4. package/dist/src/Array.js +12 -0
  5. package/dist/src/Assert.d.ts +68 -0
  6. package/dist/src/Assert.d.ts.map +1 -0
  7. package/dist/src/Assert.js +77 -0
  8. package/dist/src/BigInt.d.ts +20 -0
  9. package/dist/src/BigInt.d.ts.map +1 -0
  10. package/dist/src/BigInt.js +18 -0
  11. package/dist/src/Buffer.d.ts +92 -0
  12. package/dist/src/Buffer.d.ts.map +1 -0
  13. package/dist/src/Buffer.js +62 -0
  14. package/dist/src/Callbacks.d.ts +20 -0
  15. package/dist/src/Callbacks.d.ts.map +1 -0
  16. package/dist/src/Callbacks.js +18 -0
  17. package/dist/src/Console.d.ts +78 -0
  18. package/dist/src/Console.d.ts.map +1 -0
  19. package/dist/src/Console.js +103 -0
  20. package/dist/src/Crypto.d.ts +72 -39
  21. package/dist/src/Crypto.d.ts.map +1 -1
  22. package/dist/src/Crypto.js +89 -54
  23. package/dist/src/Eq.d.ts +97 -0
  24. package/dist/src/Eq.d.ts.map +1 -0
  25. package/dist/src/Eq.js +167 -0
  26. package/dist/src/Error.d.ts +14 -10
  27. package/dist/src/Error.d.ts.map +1 -1
  28. package/dist/src/Error.js +43 -11
  29. package/dist/src/Evolu/Config.d.ts +69 -0
  30. package/dist/src/Evolu/Config.d.ts.map +1 -0
  31. package/dist/src/Evolu/Config.js +9 -0
  32. package/dist/src/Evolu/Db.d.ts +126 -0
  33. package/dist/src/Evolu/Db.d.ts.map +1 -0
  34. package/dist/src/Evolu/Db.js +774 -0
  35. package/dist/src/Evolu/Diff.d.ts +43 -0
  36. package/dist/src/Evolu/Diff.d.ts.map +1 -0
  37. package/dist/src/Evolu/Diff.js +95 -0
  38. package/dist/src/Evolu/Evolu.d.ts +334 -0
  39. package/dist/src/Evolu/Evolu.d.ts.map +1 -0
  40. package/dist/src/Evolu/Evolu.js +434 -0
  41. package/dist/src/Evolu/Internal.d.ts +26 -0
  42. package/dist/src/Evolu/Internal.d.ts.map +1 -0
  43. package/dist/src/Evolu/Internal.js +25 -0
  44. package/dist/src/Evolu/Kysely.d.ts +6 -0
  45. package/dist/src/Evolu/Kysely.d.ts.map +1 -0
  46. package/dist/src/Evolu/Kysely.js +21 -0
  47. package/dist/src/Evolu/Owner.d.ts +155 -0
  48. package/dist/src/Evolu/Owner.d.ts.map +1 -0
  49. package/dist/src/Evolu/Owner.js +126 -0
  50. package/dist/src/Evolu/Platform.d.ts +23 -0
  51. package/dist/src/Evolu/Platform.d.ts.map +1 -0
  52. package/dist/src/Evolu/Platform.js +1 -0
  53. package/dist/src/Evolu/Protocol.d.ts +401 -0
  54. package/dist/src/Evolu/Protocol.d.ts.map +1 -0
  55. package/dist/src/Evolu/Protocol.js +1151 -0
  56. package/dist/src/Evolu/Public.d.ts +18 -0
  57. package/dist/src/Evolu/Public.d.ts.map +1 -0
  58. package/dist/src/Evolu/Public.js +11 -0
  59. package/dist/src/Evolu/PublicKysely.d.ts +148 -0
  60. package/dist/src/Evolu/PublicKysely.d.ts.map +1 -0
  61. package/dist/src/Evolu/PublicKysely.js +185 -0
  62. package/dist/src/Evolu/Query.d.ts +63 -0
  63. package/dist/src/Evolu/Query.d.ts.map +1 -0
  64. package/dist/src/Evolu/Query.js +61 -0
  65. package/dist/src/Evolu/Relay.d.ts +13 -0
  66. package/dist/src/Evolu/Relay.d.ts.map +1 -0
  67. package/dist/src/Evolu/Relay.js +109 -0
  68. package/dist/src/Evolu/Schema.d.ts +201 -0
  69. package/dist/src/Evolu/Schema.d.ts.map +1 -0
  70. package/dist/src/Evolu/Schema.js +150 -0
  71. package/dist/src/Evolu/Storage.d.ts +49 -0
  72. package/dist/src/Evolu/Storage.d.ts.map +1 -0
  73. package/dist/src/Evolu/Storage.js +1111 -0
  74. package/dist/src/Evolu/Sync.d.ts +59 -0
  75. package/dist/src/Evolu/Sync.d.ts.map +1 -0
  76. package/dist/src/Evolu/Sync.js +29 -0
  77. package/dist/src/Evolu/Timestamp.d.ts +106 -0
  78. package/dist/src/Evolu/Timestamp.d.ts.map +1 -0
  79. package/dist/src/Evolu/Timestamp.js +179 -0
  80. package/dist/src/Function.d.ts +54 -0
  81. package/dist/src/Function.d.ts.map +1 -0
  82. package/dist/src/Function.js +38 -0
  83. package/dist/src/ManyToManyMap.d.ts +26 -0
  84. package/dist/src/ManyToManyMap.d.ts.map +1 -0
  85. package/dist/src/ManyToManyMap.js +92 -0
  86. package/dist/src/NanoId.d.ts +27 -0
  87. package/dist/src/NanoId.d.ts.map +1 -0
  88. package/dist/src/NanoId.js +6 -0
  89. package/dist/src/Number.d.ts +42 -0
  90. package/dist/src/Number.d.ts.map +1 -0
  91. package/dist/src/Number.js +55 -0
  92. package/dist/src/Object.d.ts +35 -0
  93. package/dist/src/Object.d.ts.map +1 -0
  94. package/dist/src/Object.js +36 -0
  95. package/dist/src/Order.d.ts +90 -0
  96. package/dist/src/Order.d.ts.map +1 -0
  97. package/dist/src/Order.js +85 -0
  98. package/dist/src/Promise.d.ts +180 -0
  99. package/dist/src/Promise.d.ts.map +1 -0
  100. package/dist/src/Promise.js +176 -0
  101. package/dist/src/Random.d.ts +52 -0
  102. package/dist/src/Random.d.ts.map +1 -0
  103. package/dist/src/Random.js +29 -0
  104. package/dist/src/Ref.d.ts +40 -0
  105. package/dist/src/Ref.d.ts.map +1 -0
  106. package/dist/src/Ref.js +13 -0
  107. package/dist/src/Result.d.ts +421 -0
  108. package/dist/src/Result.d.ts.map +1 -0
  109. package/dist/src/Result.js +357 -0
  110. package/dist/src/Skiplist.d.ts +23 -0
  111. package/dist/src/Skiplist.d.ts.map +1 -0
  112. package/dist/src/Skiplist.js +58 -0
  113. package/dist/src/Sqlite.d.ts +116 -52
  114. package/dist/src/Sqlite.d.ts.map +1 -1
  115. package/dist/src/Sqlite.js +183 -67
  116. package/dist/src/Store.d.ts +45 -8
  117. package/dist/src/Store.d.ts.map +1 -1
  118. package/dist/src/Store.js +33 -17
  119. package/dist/src/String.d.ts +2 -0
  120. package/dist/src/String.d.ts.map +1 -0
  121. package/dist/src/String.js +14 -0
  122. package/dist/src/Time.d.ts +20 -0
  123. package/dist/src/Time.d.ts.map +1 -0
  124. package/dist/src/Time.js +25 -0
  125. package/dist/src/Type.d.ts +1937 -0
  126. package/dist/src/Type.d.ts.map +1 -0
  127. package/dist/src/Type.js +2002 -0
  128. package/dist/src/Types.d.ts +188 -0
  129. package/dist/src/Types.d.ts.map +1 -0
  130. package/dist/src/Types.js +6 -0
  131. package/dist/src/WebSocket.d.ts +112 -0
  132. package/dist/src/WebSocket.d.ts.map +1 -0
  133. package/dist/src/WebSocket.js +139 -0
  134. package/dist/src/Worker.d.ts +44 -0
  135. package/dist/src/Worker.d.ts.map +1 -0
  136. package/dist/src/Worker.js +66 -0
  137. package/dist/src/index.d.ts +24 -11
  138. package/dist/src/index.d.ts.map +1 -1
  139. package/dist/src/index.js +24 -11
  140. package/package.json +29 -38
  141. package/src/Array.ts +39 -0
  142. package/src/Assert.ts +116 -0
  143. package/src/BigInt.ts +29 -0
  144. package/src/Buffer.ts +175 -0
  145. package/src/Callbacks.ts +43 -0
  146. package/src/Console.ts +159 -0
  147. package/src/Crypto.ts +169 -115
  148. package/src/Eq.ts +204 -0
  149. package/src/Error.ts +57 -20
  150. package/src/Evolu/Config.ts +83 -0
  151. package/src/Evolu/Db.ts +1275 -0
  152. package/src/Evolu/Diff.ts +142 -0
  153. package/src/Evolu/Evolu.ts +947 -0
  154. package/src/Evolu/Internal.ts +26 -0
  155. package/src/Evolu/Kysely.ts +38 -0
  156. package/src/Evolu/Owner.ts +296 -0
  157. package/src/Evolu/Platform.ts +27 -0
  158. package/src/Evolu/Protocol.ts +1857 -0
  159. package/src/Evolu/Public.ts +43 -0
  160. package/src/Evolu/PublicKysely.ts +240 -0
  161. package/src/Evolu/Query.ts +167 -0
  162. package/src/Evolu/Relay.ts +142 -0
  163. package/src/Evolu/Schema.ts +417 -0
  164. package/src/Evolu/Storage.ts +1281 -0
  165. package/src/Evolu/Sync.ts +105 -0
  166. package/src/Evolu/Timestamp.ts +311 -0
  167. package/src/Function.ts +58 -0
  168. package/src/ManyToManyMap.ts +140 -0
  169. package/src/NanoId.ts +39 -0
  170. package/src/Number.ts +90 -0
  171. package/src/Object.ts +64 -0
  172. package/src/Order.ts +113 -0
  173. package/src/Promise.ts +295 -0
  174. package/src/Random.ts +68 -0
  175. package/src/Ref.ts +63 -0
  176. package/src/Result.ts +453 -0
  177. package/src/Skiplist.ts +102 -0
  178. package/src/Sqlite.ts +366 -153
  179. package/src/Store.ts +79 -36
  180. package/src/String.ts +10 -0
  181. package/src/Time.ts +36 -0
  182. package/src/Type.ts +3978 -0
  183. package/src/Types.ts +209 -0
  184. package/src/WebSocket.ts +273 -0
  185. package/src/Worker.ts +129 -0
  186. package/src/index.ts +24 -11
  187. package/dist/src/Config.d.ts +0 -56
  188. package/dist/src/Config.d.ts.map +0 -1
  189. package/dist/src/Config.js +0 -39
  190. package/dist/src/Crdt.d.ts +0 -89
  191. package/dist/src/Crdt.d.ts.map +0 -1
  192. package/dist/src/Crdt.js +0 -181
  193. package/dist/src/Db.d.ts +0 -107
  194. package/dist/src/Db.d.ts.map +0 -1
  195. package/dist/src/Db.js +0 -443
  196. package/dist/src/Diff.d.ts +0 -27
  197. package/dist/src/Diff.d.ts.map +0 -1
  198. package/dist/src/Diff.js +0 -84
  199. package/dist/src/Evolu.d.ts +0 -426
  200. package/dist/src/Evolu.d.ts.map +0 -1
  201. package/dist/src/Evolu.js +0 -333
  202. package/dist/src/Model.d.ts +0 -141
  203. package/dist/src/Model.d.ts.map +0 -1
  204. package/dist/src/Model.js +0 -125
  205. package/dist/src/Murmurhash.d.ts +0 -2
  206. package/dist/src/Murmurhash.d.ts.map +0 -1
  207. package/dist/src/Murmurhash.js +0 -60
  208. package/dist/src/Owner.d.ts +0 -33
  209. package/dist/src/Owner.d.ts.map +0 -1
  210. package/dist/src/Owner.js +0 -26
  211. package/dist/src/Platform.d.ts +0 -37
  212. package/dist/src/Platform.d.ts.map +0 -1
  213. package/dist/src/Platform.js +0 -11
  214. package/dist/src/Protobuf.d.ts +0 -81
  215. package/dist/src/Protobuf.d.ts.map +0 -1
  216. package/dist/src/Protobuf.js +0 -92
  217. package/dist/src/Public.d.ts +0 -13
  218. package/dist/src/Public.d.ts.map +0 -1
  219. package/dist/src/Public.js +0 -6
  220. package/dist/src/Socket.d.ts +0 -8
  221. package/dist/src/Socket.d.ts.map +0 -1
  222. package/dist/src/Socket.js +0 -51
  223. package/dist/src/Sql.d.ts +0 -12
  224. package/dist/src/Sql.d.ts.map +0 -1
  225. package/dist/src/Sql.js +0 -30
  226. package/dist/src/Sync.d.ts +0 -70
  227. package/dist/src/Sync.d.ts.map +0 -1
  228. package/dist/src/Sync.js +0 -127
  229. package/src/Config.ts +0 -119
  230. package/src/Crdt.ts +0 -361
  231. package/src/Db.ts +0 -955
  232. package/src/Diff.ts +0 -114
  233. package/src/Evolu.ts +0 -1016
  234. package/src/Model.ts +0 -233
  235. package/src/Murmurhash.ts +0 -70
  236. package/src/Owner.ts +0 -69
  237. package/src/Platform.ts +0 -47
  238. package/src/Protobuf.ts +0 -155
  239. package/src/Public.ts +0 -12
  240. package/src/Socket.ts +0 -83
  241. package/src/Sql.ts +0 -41
  242. package/src/Sync.ts +0 -315
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@evolu/common",
3
- "version": "5.4.7",
4
- "description": "Local-first platform designed for privacy, ease of use, and no vendor lock-in to sync and backup people's lifetime data",
3
+ "version": "6.0.0",
4
+ "description": "TypeScript library and local-first framework",
5
5
  "keywords": [
6
6
  "evolu",
7
7
  "local-first",
@@ -22,19 +22,17 @@
22
22
  ".": {
23
23
  "types": "./dist/src/index.d.ts",
24
24
  "import": "./dist/src/index.js",
25
- "browser": "./dist/src/index.js"
25
+ "browser": "./dist/src/index.js",
26
+ "react-native": "./dist/src/index.js"
26
27
  },
27
- "./public": {
28
- "import": "./dist/src/Public.js"
28
+ "./evolu": {
29
+ "import": "./dist/src/Evolu/Internal.js"
29
30
  }
30
31
  },
31
- "react-native": {
32
- "./index.js": "./dist/src/index.js"
33
- },
34
32
  "typesVersions": {
35
33
  "*": {
36
- "public": [
37
- "./dist/src/Public.d.ts"
34
+ "evolu": [
35
+ "./dist/src/Evolu/Internal.d.ts"
38
36
  ]
39
37
  }
40
38
  },
@@ -44,45 +42,38 @@
44
42
  "README.md"
45
43
  ],
46
44
  "dependencies": {
47
- "@noble/ciphers": "^1.0.0",
48
- "@noble/hashes": "^1.4.0",
49
- "@protobuf-ts/runtime": "^2.9.4",
50
- "@scure/bip39": "^1.3.0",
51
- "kysely": "^0.27.3",
52
- "nanoid": "^5.0.7"
45
+ "@noble/ciphers": "^1.3.0",
46
+ "@noble/hashes": "^1.8.0",
47
+ "@scure/bip39": "^1.6.0",
48
+ "kysely": "^0.28.2",
49
+ "msgpackr": "^1.11.4",
50
+ "nanoid": "^3.3.11",
51
+ "random": "^5.4.0"
53
52
  },
54
53
  "devDependencies": {
55
- "@effect/platform": "^0.69.0",
56
- "@effect/schema": "^0.75.0",
57
- "@protobuf-ts/plugin": "^2.9.4",
58
- "@protobuf-ts/protoc": "^2.9.4",
59
- "array-shuffle": "^3.0.0",
60
- "effect": "3.10.4",
61
- "eslint": "^8.57.0",
62
- "typescript": "^5.4.5",
63
- "vitest": "^2.0.0",
64
- "@evolu/tsconfig": "0.0.2",
65
- "eslint-config-evolu": "1.0.0"
66
- },
67
- "peerDependencies": {
68
- "@effect/platform": "^0.69.0",
69
- "@effect/schema": "^0.75.0",
70
- "effect": "3.10.4"
54
+ "@bokuweb/zstd-wasm": "0.0.27",
55
+ "@types/better-sqlite3": "^7.6.13",
56
+ "@types/ws": "^8.18.1",
57
+ "better-sqlite3": "^11.10.0",
58
+ "shx": "^0.3.4",
59
+ "typescript": "^5.8.3",
60
+ "vitest": "^3.2.2",
61
+ "ws": "^8.18.2",
62
+ "@evolu/tsconfig": "0.0.2"
71
63
  },
72
64
  "publishConfig": {
73
65
  "access": "public"
74
66
  },
75
67
  "engines": {
76
- "node": ">=20.0.0"
68
+ "node": ">=22.0.0"
77
69
  },
78
70
  "sideEffects": [],
79
71
  "scripts": {
80
72
  "dev": "tsc --watch",
81
- "build": "rm -rf dist ./README.md && tsc && cp ../../README.md ./",
82
- "lint": "eslint src --ext .ts,.tsx",
83
- "test": "vitest run",
84
- "clean": "rm -rf .turbo node_modules dist",
73
+ "build": "shx rm -rf dist ./README.md && tsc && shx cp ../../README.md ./",
74
+ "test": "vitest --disableConsoleIntercept",
75
+ "clean": "shx rm -rf .turbo node_modules dist",
85
76
  "format": "prettier --write \"src/*.{ts,tsx,md}\"",
86
- "protobuf": "pnpm protoc --ts_out ./src --proto_path protobuf protobuf/Protobuf.proto --ts_opt eslint_disable --ts_opt optimize_code_size && pnpm format"
77
+ "bench": "vitest bench"
87
78
  }
88
79
  }
package/src/Array.ts ADDED
@@ -0,0 +1,39 @@
1
+ /** An array with at least one element. */
2
+ export type NonEmptyArray<T> = [T, ...Array<T>];
3
+
4
+ /** Checks if an array is non-empty. */
5
+ export const isNonEmptyArray = <T>(
6
+ array: Array<T>,
7
+ ): array is NonEmptyArray<T> => array.length > 0;
8
+
9
+ /** A readonly array with at least one element. */
10
+ export type NonEmptyReadonlyArray<T> = readonly [T, ...ReadonlyArray<T>];
11
+
12
+ /** Checks if an array is non-empty. */
13
+ export const isNonEmptyReadonlyArray = <T>(
14
+ array: ReadonlyArray<T>,
15
+ ): array is NonEmptyReadonlyArray<T> => array.length > 0;
16
+
17
+ /** Appends an item to an array returning a non-empty array. */
18
+ export const appendToArray = <T>(
19
+ item: T,
20
+ array: ReadonlyArray<T>,
21
+ ): NonEmptyReadonlyArray<T> =>
22
+ [...array, item] as unknown as NonEmptyReadonlyArray<T>;
23
+
24
+ /** Prepends an item to an array returning a non-empty array. */
25
+ export const prependToArray = <T>(
26
+ item: T,
27
+ array: ReadonlyArray<T>,
28
+ ): NonEmptyReadonlyArray<T> =>
29
+ [item, ...array] as unknown as NonEmptyReadonlyArray<T>;
30
+
31
+ /** Maps a non-empty array using a mapper function. */
32
+ export const mapNonEmptyArray = <T, U>(
33
+ array: NonEmptyReadonlyArray<T>,
34
+ mapper: (item: T, index: number) => U,
35
+ ): NonEmptyReadonlyArray<U> =>
36
+ array.map(mapper) as unknown as NonEmptyReadonlyArray<U>;
37
+
38
+ /** Shifts an item from a non-empty array, guaranteed to return T. */
39
+ export const shiftArray = <T>(array: NonEmptyArray<T>): T => array.shift() as T;
package/src/Assert.ts ADDED
@@ -0,0 +1,116 @@
1
+ /**
2
+ * 🚨
3
+ *
4
+ * This module provides assertion utilities to prevent invalid states from
5
+ * propagating through the system by halting execution when a condition fails,
6
+ * improving reliability and debuggability.
7
+ *
8
+ * **Warning**: Do not use this instead of {@link Type}. Assertions are intended
9
+ * for conditions that are logically guaranteed but not statically known by
10
+ * TypeScript, or for catching and signaling developer mistakes eagerly (e.g.,
11
+ * invalid configuration).
12
+ *
13
+ * @module
14
+ */
15
+ import type { Type } from "./Type.js";
16
+
17
+ /**
18
+ * Ensures a condition is true, throwing an error with the provided message if
19
+ * not.
20
+ *
21
+ * Prevents invalid states from propagating through the system by halting
22
+ * execution when a condition fails, improving reliability and debuggability.
23
+ *
24
+ * **Warning**: Do not use this instead of {@link Type}. Assertions are intended
25
+ * for conditions that are logically guaranteed but not statically known by
26
+ * TypeScript, or for catching and signaling developer mistakes eagerly (e.g.,
27
+ * invalid configuration).
28
+ *
29
+ * ### Example
30
+ *
31
+ * ```ts
32
+ * assert(true, "true is not true"); // no-op
33
+ * assert(false, "true is not true"); // throws Error
34
+ *
35
+ * const size = fooSize - buffer.getLength();
36
+ * // Ensure required type
37
+ * assert(PositiveInt.is(size), "size is not positive int");
38
+ * ```
39
+ */
40
+ export const assert: (
41
+ condition: unknown,
42
+ message: string,
43
+ ) => asserts condition = (condition, message) => {
44
+ if (!condition) {
45
+ throw new Error(message);
46
+ }
47
+ };
48
+
49
+ /**
50
+ * Asserts that an array is non-empty.
51
+ *
52
+ * Ensures the provided array has at least one element, helping TypeScript infer
53
+ * the array as non-empty when this is logically guaranteed but not statically
54
+ * known.
55
+ *
56
+ * ### Example
57
+ *
58
+ * ```ts
59
+ * assertNonEmptyArray([1, 2, 3]); // no-op
60
+ * assertNonEmptyArray([]); // throws Error
61
+ * ```
62
+ */
63
+ export const assertNonEmptyArray: <T>(
64
+ arr: Array<T>,
65
+ message?: string,
66
+ ) => asserts arr is [T, ...Array<T>] = (
67
+ arr,
68
+ message = "Expected a non-empty array.",
69
+ ) => {
70
+ assert(arr.length > 0, message);
71
+ };
72
+
73
+ /**
74
+ * Asserts that a readonly array is non-empty.
75
+ *
76
+ * Ensures the provided readonly array has at least one element, helping
77
+ * TypeScript infer non-emptiness when this is logically guaranteed but not
78
+ * statically known.
79
+ *
80
+ * ### Example
81
+ *
82
+ * ```ts
83
+ * assertNonEmptyReadonlyArray([1, 2, 3]); // no-op
84
+ * assertNonEmptyReadonlyArray([]); // throws Error
85
+ * ```
86
+ */
87
+ export const assertNonEmptyReadonlyArray: <T>(
88
+ arr: ReadonlyArray<T>,
89
+ message?: string,
90
+ ) => asserts arr is readonly [T, ...Array<T>] = (
91
+ arr,
92
+ message = "Expected a non-empty readonly array.",
93
+ ) => {
94
+ assert(arr.length > 0, message);
95
+ };
96
+
97
+ /**
98
+ * Asserts no error reaches a `.catch` block, throwing a developer error if it
99
+ * does. Used in Promise chains where errors indicate bugs to be fixed.
100
+ *
101
+ * ### Example
102
+ *
103
+ * ```ts
104
+ * Promise.reject("test").catch((e) =>
105
+ * assertNoErrorInCatch("WebSocket retry", e),
106
+ * );
107
+ * ```
108
+ */
109
+ export function assertNoErrorInCatch(context: string, error: unknown): never {
110
+ throw Object.assign(
111
+ new Error(
112
+ `Error in ${context}: an unexpected error reached a catch block and requires a fix`,
113
+ ),
114
+ { cause: error },
115
+ );
116
+ }
package/src/BigInt.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { Predicate } from "./Types.js";
2
+
3
+ /** Increments a bigint by 1. */
4
+ export const incrementBigInt = (n: bigint): bigint => n + 1n;
5
+
6
+ /** Decrements a bigint by 1. */
7
+ export const decrementBigInt = (n: bigint): bigint => n - 1n;
8
+
9
+ /** Clamps a bigint within a given range. */
10
+ export const clampBigInt =
11
+ (min: bigint, max: bigint) =>
12
+ (n: bigint): bigint =>
13
+ n < min ? min : n > max ? max : n;
14
+
15
+ /**
16
+ * Creates a predicate that checks if a BigInt is within a range, inclusive.
17
+ *
18
+ * ### Example
19
+ *
20
+ * ```ts
21
+ * const isBetween10And20 = isBetweenBigInt(10n, 20n);
22
+ * console.log(isBetween10And20(15n)); // true
23
+ * console.log(isBetween10And20(25n)); // false
24
+ * ```
25
+ */
26
+ export const isBetweenBigInt =
27
+ (min: bigint, max: bigint): Predicate<bigint> =>
28
+ (value) =>
29
+ value >= min && value <= max;
package/src/Buffer.ts ADDED
@@ -0,0 +1,175 @@
1
+ import { Result } from "./Result.js";
2
+ import { NonNegativeInt } from "./Type.js";
3
+ export {
4
+ bytesToHex,
5
+ bytesToUtf8,
6
+ concatBytes,
7
+ hexToBytes,
8
+ utf8ToBytes,
9
+ } from "@noble/ciphers/utils";
10
+
11
+ /**
12
+ * Custom error for {@link Buffer}-related failures like premature end of data.
13
+ * Provides better stack traces for debugging binary protocol issues.
14
+ */
15
+ export class BufferError extends Error {
16
+ constructor(message: string) {
17
+ super(message);
18
+ this.name = this.constructor.name;
19
+
20
+ Error.captureStackTrace(this, this.constructor);
21
+ }
22
+ }
23
+
24
+ /**
25
+ * A Buffer is a dynamic, resizable container for binary data, optimized for
26
+ * scenarios where the final size is unknown. It grows exponentially (doubling
27
+ * its capacity) to minimize memory reallocations and uses `subarray` for
28
+ * efficient, copy-free data access in methods like `unwrap` and `shift`.
29
+ *
30
+ * ### Recommended Usage
31
+ *
32
+ * Create as few Buffers as possible—typically one main Buffer for the final
33
+ * output. Temporary Buffers are allowed when necessary (e.g., for
34
+ * variable-length headers), but avoid excessive allocations. Buffers can be
35
+ * reused within functions by leveraging `reset` to clear contents while
36
+ * preserving capacity, or `truncate` to adjust the length to a specific size,
37
+ * reducing the need for new allocations. Pass Buffers to `encode*` functions to
38
+ * append serialized data and use `decode*` functions to extract data. Both
39
+ * `shift` and `shiftN` throw an {@link BufferError} with message "Buffer parse
40
+ * ended prematurely" on failure, as do higher-level `decode*` functions,
41
+ * providing stack traces for debugging instead of using {@link Result}. This
42
+ * avoids allocation overhead in success cases and leverages exceptions'
43
+ * diagnostic benefits.
44
+ *
45
+ * ### Example
46
+ *
47
+ * ```ts
48
+ * const buffer = createBuffer();
49
+ * encodeNonNegativeInt(buffer, someInt);
50
+ * encodeId(buffer, someId);
51
+ * const result = buffer.unwrap(); // Final serialized data
52
+ *
53
+ * // Decoding example (throws on error)
54
+ * try {
55
+ * const num = decodeNonNegativeInt(buffer);
56
+ * const id = decodeId(buffer);
57
+ * } catch (e) {
58
+ * console.error(e.stack); // Stack trace for debugging
59
+ * }
60
+ * ```
61
+ *
62
+ * For more on exponential growth, see:
63
+ * https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially
64
+ */
65
+ export interface Buffer {
66
+ /** Returns the total allocated size of the buffer. */
67
+ getCapacity: () => NonNegativeInt;
68
+
69
+ /** Returns the current number of bytes stored in the buffer. */
70
+ getLength: () => NonNegativeInt;
71
+
72
+ /**
73
+ * Appends binary data to the buffer, resizing if necessary. Throws if
74
+ * `arg.length` is not a non-negative integer.
75
+ */
76
+ extend: (arg: Uint8Array | ArrayLike<number>) => void;
77
+
78
+ /**
79
+ * Removes and returns the first byte. Throws an `Error` with message "Buffer
80
+ * parse ended prematurely" if the buffer is empty.
81
+ */
82
+ shift: () => NonNegativeInt;
83
+
84
+ /**
85
+ * Removes and returns the first `n` bytes. Throws an `Error` with message
86
+ * "Buffer parse ended prematurely" if fewer than `n` bytes remain.
87
+ */
88
+ shiftN: (n: NonNegativeInt) => Uint8Array;
89
+
90
+ /**
91
+ * Truncates the buffer to the specified length, discarding data from the end.
92
+ * Throws if the new length is greater than the current length.
93
+ */
94
+ truncate: (length: NonNegativeInt) => void;
95
+
96
+ /**
97
+ * Resets the buffer to its initial empty state, preserving its capacity.
98
+ *
99
+ * This allows efficient buffer reuse without reallocating memory. Use this
100
+ * when you want to clear the buffer and write new data, avoiding unnecessary
101
+ * allocations.
102
+ */
103
+ reset: () => void;
104
+
105
+ /**
106
+ * Returns a view of the buffer’s current data. Do not modify this array, as
107
+ * it directly alters the buffer’s internal state, potentially breaking
108
+ * subsequent operations.
109
+ */
110
+ unwrap: () => Uint8Array;
111
+ }
112
+
113
+ export const createBuffer = (
114
+ arrayLike?: Uint8Array | ArrayLike<number>,
115
+ ): Buffer => {
116
+ let value = arrayLike
117
+ ? new globalThis.Uint8Array(arrayLike)
118
+ : new globalThis.Uint8Array(512);
119
+ let length = (arrayLike ? arrayLike.length : 0) as NonNegativeInt;
120
+
121
+ const buffer: Buffer = {
122
+ getCapacity: () => value.length as NonNegativeInt,
123
+
124
+ getLength: () => length,
125
+
126
+ extend: (arg) => {
127
+ const targetSize = length + arg.length;
128
+ if (value.length < targetSize) {
129
+ const oldValue = value;
130
+ const newCapacity = Math.max(value.length * 2, targetSize);
131
+ value = new globalThis.Uint8Array(newCapacity);
132
+ value.set(oldValue);
133
+ }
134
+ value.set(arg, length);
135
+ length = (length + arg.length) as NonNegativeInt;
136
+ },
137
+
138
+ shift: () => {
139
+ if (length === 0) {
140
+ throw new BufferError("Buffer parse ended prematurely");
141
+ }
142
+ const first = value[0];
143
+ value = value.subarray(1);
144
+ length--;
145
+ return first as NonNegativeInt;
146
+ },
147
+
148
+ shiftN: (n) => {
149
+ if (length < n) {
150
+ throw new BufferError("Buffer parse ended prematurely");
151
+ }
152
+ const subarray = value.subarray(0, n);
153
+ value = value.subarray(n);
154
+ length = (length - n) as NonNegativeInt;
155
+ return subarray;
156
+ },
157
+
158
+ truncate: (newLength) => {
159
+ if (newLength > length) {
160
+ throw new BufferError(
161
+ "Cannot truncate to a length greater than current",
162
+ );
163
+ }
164
+ length = newLength;
165
+ },
166
+
167
+ reset: () => {
168
+ length = 0 as NonNegativeInt;
169
+ },
170
+
171
+ unwrap: () => value.subarray(0, length),
172
+ };
173
+
174
+ return buffer;
175
+ };
@@ -0,0 +1,43 @@
1
+ import { NanoIdLibDep } from "./NanoId.js";
2
+ import { Brand } from "./Types.js";
3
+
4
+ /**
5
+ * Manages one-time callback functions.
6
+ *
7
+ * Allows registering callbacks with a unique ID and executing them once with an
8
+ * optional argument. Executed callbacks are automatically removed.
9
+ *
10
+ * This is useful for managing event-driven or asynchronous workflows where
11
+ * callbacks need to be invoked only once.
12
+ */
13
+ export interface Callbacks {
14
+ /** Registers a callback function and returns a unique ID. */
15
+ readonly register: (callback: (arg?: unknown) => void) => CallbackId;
16
+
17
+ /** Executes and removes a callback associated with the given ID. */
18
+ readonly execute: (id: CallbackId, arg?: unknown) => void;
19
+ }
20
+
21
+ export type CallbackId = string & Brand<"CallbackId">;
22
+
23
+ export const createCallbacks = (deps: NanoIdLibDep): Callbacks => {
24
+ const callbackMap = new Map<CallbackId, (arg?: unknown) => void>();
25
+
26
+ const callbacks: Callbacks = {
27
+ register: (callback) => {
28
+ const id = deps.nanoIdLib.nanoid() as CallbackId;
29
+ callbackMap.set(id, callback);
30
+ return id;
31
+ },
32
+
33
+ execute: (id, arg) => {
34
+ const callback = callbackMap.get(id);
35
+ if (callback) {
36
+ callbackMap.delete(id);
37
+ callback(arg);
38
+ }
39
+ },
40
+ };
41
+
42
+ return callbacks;
43
+ };
package/src/Console.ts ADDED
@@ -0,0 +1,159 @@
1
+ /**
2
+ * 📝 Cross-platform console
3
+ *
4
+ * Console abstraction for Chrome 123+, Firefox 125+, Safari 18.1+, Node.js
5
+ * 22.x+, and React Native 0.75+. Includes methods guaranteed to be available in
6
+ * these environments and expected to remain compatible in future versions.
7
+ * Output formatting may vary (e.g., interactive UI in browsers vs. text in
8
+ * Node.js/React Native), but functionality is consistent across platforms.
9
+ *
10
+ * **Convention**: Use a tag (e.g., `[db]`) as the first argument for log
11
+ * filtering.
12
+ *
13
+ * ### Example
14
+ *
15
+ * ```ts
16
+ * deps.console.log("[db]", "send data message", protocolMessage);
17
+ * ```
18
+ *
19
+ * **Tip**: In browser dev tools, you can filter logs by tag (e.g., `[db]`) to
20
+ * quickly find relevant messages.
21
+ *
22
+ * **Warning**: If you encounter platform-specific issues or missing methods,
23
+ * please contribute a PR with details about the environment and behavior.
24
+ *
25
+ * @module
26
+ */
27
+
28
+ /**
29
+ * Cross-platform Console interface for Chrome 123+, Firefox 125+, Safari 18.1+,
30
+ * Node.js 22.x+, and React Native 0.75+
31
+ */
32
+ export interface Console {
33
+ /** Controls whether console methods produce output (default: true) */
34
+ enabled?: boolean;
35
+
36
+ /** Outputs a message to the console */
37
+ log: (...args: Array<any>) => void;
38
+
39
+ /** Outputs an informational message (often same as log) */
40
+ info: (...args: Array<any>) => void;
41
+
42
+ /** Outputs a warning message */
43
+ warn: (...args: Array<any>) => void;
44
+
45
+ /** Outputs an error message */
46
+ error: (...args: Array<any>) => void;
47
+
48
+ /** Outputs a debug message */
49
+ debug: (...args: Array<any>) => void;
50
+
51
+ /** Starts a timer with an optional label */
52
+ time: (label?: string) => void;
53
+
54
+ /** Ends a timer and logs the elapsed time */
55
+ timeEnd: (label?: string) => void;
56
+
57
+ /** Displays an object's properties in a detailed format */
58
+ dir: (object: any, options?: any) => void;
59
+
60
+ /** Displays tabular data as a table */
61
+ table: (tabularData: any, properties?: Array<string>) => void;
62
+
63
+ /** Logs the number of times this has been called with the given label */
64
+ count: (label?: string) => void;
65
+
66
+ /** Resets the counter for the given label */
67
+ countReset: (label?: string) => void;
68
+
69
+ /** Writes a message if the value is falsy, otherwise does nothing */
70
+ assert: (value: any, message?: string, ...optionalParams: Array<any>) => void;
71
+
72
+ /** Logs the elapsed time for a timer without ending it */
73
+ timeLog: (label?: string, ...data: Array<any>) => void;
74
+
75
+ /** Prints a stack trace with an optional message */
76
+ trace: (message?: any, ...optionalParams: Array<any>) => void;
77
+ }
78
+
79
+ /** Dependency interface for injecting a Console instance. */
80
+ export interface ConsoleDep {
81
+ readonly console: Console;
82
+ }
83
+
84
+ export interface ConsoleConfig {
85
+ /**
86
+ * Enable or disable console logging (default: false). When true, logs are
87
+ * output to the {@link Console}; when false, logging is disabled for all
88
+ * methods except `error`, which always outputs to ensure critical issues are
89
+ * not missed.
90
+ */
91
+ readonly enableLogging?: boolean;
92
+ }
93
+
94
+ /** Creates a console instance using the global console. */
95
+ export const createConsole = (config: ConsoleConfig = {}): Console => {
96
+ const instance: Console = {
97
+ enabled: config.enableLogging ?? false,
98
+
99
+ log: (...args) => {
100
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
101
+ if (instance.enabled) console.log(...args);
102
+ },
103
+ info: (...args) => {
104
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
105
+ if (instance.enabled) console.info(...args);
106
+ },
107
+ warn: (...args) => {
108
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
109
+ if (instance.enabled) console.warn(...args);
110
+ },
111
+ error: (...args) => {
112
+ // Always log errors, even if disabled
113
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
114
+ console.error(...args);
115
+ },
116
+ debug: (...args) => {
117
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
118
+ if (instance.enabled) console.debug(...args);
119
+ },
120
+ time: (label) => {
121
+ // eslint-disable-next-line no-console
122
+ if (instance.enabled) console.time(label);
123
+ },
124
+ timeEnd: (label) => {
125
+ // eslint-disable-next-line no-console
126
+ if (instance.enabled) console.timeEnd(label);
127
+ },
128
+ dir: (object, options) => {
129
+ // eslint-disable-next-line no-console
130
+ if (instance.enabled) console.dir(object, options);
131
+ },
132
+ table: (tabularData, properties) => {
133
+ // eslint-disable-next-line no-console
134
+ if (instance.enabled) console.table(tabularData, properties);
135
+ },
136
+ count: (label) => {
137
+ // eslint-disable-next-line no-console
138
+ if (instance.enabled) console.count(label);
139
+ },
140
+ countReset: (label) => {
141
+ // eslint-disable-next-line no-console
142
+ if (instance.enabled) console.countReset(label);
143
+ },
144
+ assert: (value, message, ...optionalParams) => {
145
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
146
+ if (instance.enabled) console.assert(value, message, ...optionalParams);
147
+ },
148
+ timeLog: (label, ...data) => {
149
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
150
+ if (instance.enabled) console.timeLog(label, ...data);
151
+ },
152
+ trace: (message, ...optionalParams) => {
153
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
154
+ if (instance.enabled) console.trace(message, ...optionalParams);
155
+ },
156
+ };
157
+
158
+ return instance;
159
+ };