@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
@@ -0,0 +1,103 @@
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
+ /** Creates a console instance using the global console. */
28
+ export const createConsole = (config = {}) => {
29
+ const instance = {
30
+ enabled: config.enableLogging ?? false,
31
+ log: (...args) => {
32
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
33
+ if (instance.enabled)
34
+ console.log(...args);
35
+ },
36
+ info: (...args) => {
37
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
38
+ if (instance.enabled)
39
+ console.info(...args);
40
+ },
41
+ warn: (...args) => {
42
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
43
+ if (instance.enabled)
44
+ console.warn(...args);
45
+ },
46
+ error: (...args) => {
47
+ // Always log errors, even if disabled
48
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
49
+ console.error(...args);
50
+ },
51
+ debug: (...args) => {
52
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
53
+ if (instance.enabled)
54
+ console.debug(...args);
55
+ },
56
+ time: (label) => {
57
+ // eslint-disable-next-line no-console
58
+ if (instance.enabled)
59
+ console.time(label);
60
+ },
61
+ timeEnd: (label) => {
62
+ // eslint-disable-next-line no-console
63
+ if (instance.enabled)
64
+ console.timeEnd(label);
65
+ },
66
+ dir: (object, options) => {
67
+ // eslint-disable-next-line no-console
68
+ if (instance.enabled)
69
+ console.dir(object, options);
70
+ },
71
+ table: (tabularData, properties) => {
72
+ // eslint-disable-next-line no-console
73
+ if (instance.enabled)
74
+ console.table(tabularData, properties);
75
+ },
76
+ count: (label) => {
77
+ // eslint-disable-next-line no-console
78
+ if (instance.enabled)
79
+ console.count(label);
80
+ },
81
+ countReset: (label) => {
82
+ // eslint-disable-next-line no-console
83
+ if (instance.enabled)
84
+ console.countReset(label);
85
+ },
86
+ assert: (value, message, ...optionalParams) => {
87
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
88
+ if (instance.enabled)
89
+ console.assert(value, message, ...optionalParams);
90
+ },
91
+ timeLog: (label, ...data) => {
92
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
93
+ if (instance.enabled)
94
+ console.timeLog(label, ...data);
95
+ },
96
+ trace: (message, ...optionalParams) => {
97
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
98
+ if (instance.enabled)
99
+ console.trace(message, ...optionalParams);
100
+ },
101
+ };
102
+ return instance;
103
+ };
@@ -1,49 +1,82 @@
1
- import * as S from "@effect/schema/Schema";
2
- import * as Brand from "effect/Brand";
3
- import * as Context from "effect/Context";
4
- import * as Effect from "effect/Effect";
5
- import * as Layer from "effect/Layer";
6
- import { Id } from "./Model.js";
7
1
  /**
8
- * Mnemonic is a password generated by Evolu in BIP39 format.
2
+ * 🔒
9
3
  *
10
- * A mnemonic, also known as a "seed phrase," is a set of 12 words in a specific
11
- * order chosen from a predefined list. The purpose of the BIP39 mnemonic is to
12
- * provide a human-readable way of storing a private key.
4
+ * @module
13
5
  */
14
- export type Mnemonic = string & Brand.Brand<"Mnemonic">;
6
+ import { Result } from "./Result.js";
7
+ import { Id, Mnemonic, NonNegativeInt } from "./Type.js";
8
+ import { Brand } from "./Types.js";
9
+ /** `Uint8Array` created by {@link createRandomBytes}. */
10
+ export type RandomBytes = Uint8Array & Brand<"RandomBytes">;
11
+ export type CreateRandomBytes = (bytesLength?: number) => RandomBytes;
12
+ export interface CreateRandomBytesDep {
13
+ readonly createRandomBytes: CreateRandomBytes;
14
+ }
15
+ /** Cryptographically secure PRNG. Uses internal OS-level crypto.getRandomValues. */
16
+ export declare const createRandomBytes: CreateRandomBytes;
17
+ export type CreateMnemonic = () => Mnemonic;
18
+ export interface CreateMnemonicDep {
19
+ readonly createMnemonic: CreateMnemonic;
20
+ }
21
+ export declare const createEnglishMnemonic: CreateMnemonic;
22
+ export type MnemonicSeed = Uint8Array & Brand<"MnemonicSeed">;
23
+ export declare const mnemonicToMnemonicSeed: (mnemonic: Mnemonic) => MnemonicSeed;
15
24
  /**
16
- * Mnemonic is a password generated by Evolu in BIP39 format.
25
+ * SLIP21.
17
26
  *
18
- * A mnemonic, also known as a "seed phrase," is a set of 12 words in a specific
19
- * order chosen from a predefined list. The purpose of the BIP39 mnemonic is to
20
- * provide a human-readable way of storing a private key.
27
+ * https://github.com/satoshilabs/slips/blob/master/slip-0021.md
21
28
  */
22
- export declare const createMnemonic: () => Mnemonic;
23
- /** Parse a string to {@link Mnemonic}. */
24
- export declare const parseMnemonic: (mnemonic: string) => Effect.Effect<Mnemonic, InvalidMnemonicError>;
25
- export interface InvalidMnemonicError {
26
- readonly _tag: "InvalidMnemonicError";
29
+ export declare const createSlip21: (seed: MnemonicSeed, path: ReadonlyArray<string>) => Uint8Array;
30
+ /**
31
+ * Creates a 21-character Base64URL ID (also known as nanoid) from a SLIP-21
32
+ * derived key.
33
+ *
34
+ * Reduces the 256-bit SLIP-21 output to 126 bits (21 chars × 6 bits) for a
35
+ * compact, human-readable, and shareable identifier suitable for UI display or
36
+ * URL use. While this lowers entropy, 126 bits remains cryptographically secure
37
+ * for uniqueness and unpredictability in most applications (comparable to
38
+ * UUIDv4's 122 bits).
39
+ *
40
+ * See https://github.com/satoshilabs/slips/blob/master/slip-0021.md
41
+ */
42
+ export declare const createSlip21Id: (seed: MnemonicSeed, path: ReadonlyArray<string>) => Id;
43
+ /** The encryption key for {@link SymmetricCrypto}. */
44
+ export declare const EncryptionKey: import("./Type.js").BrandType<import("./Type.js").BrandType<import("./Type.js").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("./Type.js").Uint8ArrayError, Uint8Array<ArrayBufferLike>, import("./Type.js").Uint8ArrayError>, "Length32", import("./Type.js").LengthError<32>, import("./Type.js").Uint8ArrayError>, "EncryptionKey", import("./Type.js").BrandWithoutRefineError<"EncryptionKey", import("./Type.js").Uint8ArrayError | import("./Type.js").LengthError<32>>, never>;
45
+ export type EncryptionKey = typeof EncryptionKey.Type;
46
+ export declare const createEncryptionKey: (seed: MnemonicSeed) => EncryptionKey;
47
+ /** Symmetric cryptography. */
48
+ export interface SymmetricCrypto {
49
+ readonly nonceLength: NonNegativeInt;
50
+ readonly encrypt: (plaintext: Uint8Array, encryptionKey: EncryptionKey) => {
51
+ readonly nonce: Uint8Array;
52
+ readonly ciphertext: Uint8Array;
53
+ };
54
+ readonly decrypt: (ciphertext: Uint8Array, encryptionKey: EncryptionKey, nonce: Uint8Array) => Result<Uint8Array, SymmetricCryptoDecryptError>;
27
55
  }
28
- export declare const mnemonicToSeed: (mnemonic: Mnemonic) => Uint8Array;
29
- declare const NanoIdGenerator_base: Context.TagClass<NanoIdGenerator, "NanoIdGenerator", {
30
- readonly nanoid: Effect.Effect<NanoId>;
31
- readonly nodeId: Effect.Effect<NodeId>;
32
- readonly rowId: Effect.Effect<Id>;
33
- }>;
34
- export declare class NanoIdGenerator extends NanoIdGenerator_base {
56
+ export interface SymmetricCryptoDep {
57
+ readonly symmetricCrypto: SymmetricCrypto;
35
58
  }
36
- export declare const createNanoIdGeneratorLive: (customAlphabet: (alphabet: string, defaultSize?: number) => (size?: number) => string, nanoid: (size?: number) => string) => Layer.Layer<NanoIdGenerator, never, never>;
37
- export type NanoId = string & Brand.Brand<"NanoId">;
38
- export declare const NodeId: S.brand<S.filter<S.Schema<string, string, never>>, "NodeId">;
39
- export type NodeId = typeof NodeId.Type;
40
- export declare const slip21Derive: (seed: Uint8Array, path: ReadonlyArray<string>) => Effect.Effect<Uint8Array>;
41
- declare const SecretBox_base: Context.TagClass<SecretBox, "SecretBox", {
42
- readonly seal: (key: Uint8Array, plaintext: Uint8Array) => Effect.Effect<Uint8Array>;
43
- readonly open: (key: Uint8Array, ciphertext: Uint8Array) => Effect.Effect<Uint8Array>;
44
- }>;
45
- export declare class SecretBox extends SecretBox_base {
46
- static Live: Layer.Layer<SecretBox, never, never>;
59
+ export interface SymmetricCryptoDecryptError {
60
+ readonly type: "SymmetricCryptoDecryptError";
61
+ readonly error: unknown;
47
62
  }
48
- export {};
63
+ /**
64
+ * XChaCha20-Poly1305 encryption
65
+ *
66
+ * https://github.com/paulmillr/noble-ciphers?tab=readme-ov-file#which-cipher-should-i-pick
67
+ */
68
+ export declare const createSymmetricCrypto: (deps: CreateRandomBytesDep) => SymmetricCrypto;
69
+ /**
70
+ * Returns the PADMÉ padded length for a given input length.
71
+ *
72
+ * PADMÉ limits information leakage about the length of the plain-text for a
73
+ * wide range of encrypted data sizes. See the PURBs paper for details:
74
+ * https://bford.info/pub/sec/purb.pdf
75
+ */
76
+ export declare const padmePaddedLength: (length: NonNegativeInt) => NonNegativeInt;
77
+ /**
78
+ * Returns the PADMÉ padding length for a given input length. Uses
79
+ * {@link padmePaddedLength}.
80
+ */
81
+ export declare const padmePaddingLength: (length: NonNegativeInt) => NonNegativeInt;
49
82
  //# sourceMappingURL=Crypto.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Crypto.d.ts","sourceRoot":"","sources":["../../src/Crypto.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,uBAAuB,CAAC;AAQ3C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,QAAO,QACiB,CAAC;AAEpD,0CAA0C;AAC1C,eAAO,MAAM,aAAa,aACd,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,oBAAoB,CAO9C,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;CACvC;AAED,eAAO,MAAM,cAAc,aAAc,QAAQ,KAAG,UAChB,CAAC;;qBAKhB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;qBACrB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;oBACtB,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;;AALrC,qBAAa,eAAgB,SAAQ,oBAOlC;CAAG;AAEN,eAAO,MAAM,yBAAyB,mBACpB,CACd,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,KACjB,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,MAAM,UACtB,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,MAAM,KAChC,KAAK,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,EAAE,KAAK,CAU3C,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AAEpD,eAAO,MAAM,MAAM,8DAGlB,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAC;AAIxC,eAAO,MAAM,YAAY,SACjB,UAAU,QACV,aAAa,CAAC,MAAM,CAAC,KAC1B,MAAM,CAAC,MAAM,CAAC,UAAU,CAWvB,CAAC;;mBAKc,CACb,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,UAAU,KAClB,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;mBAEf,CACb,GAAG,EAAE,UAAU,EACf,UAAU,EAAE,UAAU,KACnB,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;;AAXlC,qBAAa,SAAU,SAAQ,cAa5B;IACD,MAAM,CAAC,IAAI,uCAgBT;CACH"}
1
+ {"version":3,"file":"Crypto.d.ts","sourceRoot":"","sources":["../../src/Crypto.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,EAAc,MAAM,EAAW,MAAM,aAAa,CAAC;AAC1D,OAAO,EAEL,EAAE,EAEF,QAAQ,EACR,cAAc,EAEf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,yDAAyD;AACzD,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;AAE5D,MAAM,MAAM,iBAAiB,GAAG,CAAC,WAAW,CAAC,EAAE,MAAM,KAAK,WAAW,CAAC;AAEtE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;CAC/C;AAED,oFAAoF;AACpF,eAAO,MAAM,iBAAiB,EAAE,iBACS,CAAC;AAE1C,MAAM,MAAM,cAAc,GAAG,MAAM,QAAQ,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;CACzC;AAED,eAAO,MAAM,qBAAqB,EAAE,cACe,CAAC;AAEpD,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC;AAE9D,eAAO,MAAM,sBAAsB,GAAI,UAAU,QAAQ,KAAG,YACR,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,YAAY,EAClB,MAAM,aAAa,CAAC,MAAM,CAAC,KAC1B,UAUF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,YAAY,EAClB,MAAM,aAAa,CAAC,MAAM,CAAC,KAC1B,EAUF,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,aAAa,8fAAiD,CAAC;AAC5E,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAC;AAEtD,eAAO,MAAM,mBAAmB,GAAI,MAAM,YAAY,KAAG,aACS,CAAC;AAEnE,8BAA8B;AAC9B,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IAErC,QAAQ,CAAC,OAAO,EAAE,CAChB,SAAS,EAAE,UAAU,EACrB,aAAa,EAAE,aAAa,KACzB;QACH,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;QAC3B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;KACjC,CAAC;IAEF,QAAQ,CAAC,OAAO,EAAE,CAChB,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,UAAU,KACd,MAAM,CAAC,UAAU,EAAE,2BAA2B,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC3C;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,6BAA6B,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAChC,MAAM,oBAAoB,KACzB,eAyBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,cAAc,KAAG,cAO1D,CAAC;AAEF;;;GAGG;AAEH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,cAAc,KAAG,cAE3D,CAAC"}
@@ -1,67 +1,102 @@
1
- import * as S from "@effect/schema/Schema";
2
- import { secretbox } from "@noble/ciphers/salsa";
3
- import { concatBytes } from "@noble/ciphers/utils";
1
+ /**
2
+ * 🔒
3
+ *
4
+ * @module
5
+ */
6
+ import { xchacha20poly1305 } from "@noble/ciphers/chacha";
4
7
  import { hmac } from "@noble/hashes/hmac";
5
- import { sha512 } from "@noble/hashes/sha512";
8
+ import { sha512 } from "@noble/hashes/sha2";
6
9
  import { randomBytes } from "@noble/hashes/utils";
7
10
  import * as bip39 from "@scure/bip39";
8
11
  import { wordlist } from "@scure/bip39/wordlists/english";
9
- import * as Context from "effect/Context";
10
- import * as Effect from "effect/Effect";
11
- import * as Layer from "effect/Layer";
12
+ import { urlAlphabet } from "nanoid";
13
+ import { getOrThrow, trySync } from "./Result.js";
14
+ import { brand, length, NonNegativeInt, Uint8Array, } from "./Type.js";
15
+ /** Cryptographically secure PRNG. Uses internal OS-level crypto.getRandomValues. */
16
+ export const createRandomBytes = (bytesLength = 32) => randomBytes(bytesLength);
17
+ export const createEnglishMnemonic = () => bip39.generateMnemonic(wordlist, 128);
18
+ export const mnemonicToMnemonicSeed = (mnemonic) => bip39.mnemonicToSeedSync(mnemonic);
12
19
  /**
13
- * Mnemonic is a password generated by Evolu in BIP39 format.
20
+ * SLIP21.
14
21
  *
15
- * A mnemonic, also known as a "seed phrase," is a set of 12 words in a specific
16
- * order chosen from a predefined list. The purpose of the BIP39 mnemonic is to
17
- * provide a human-readable way of storing a private key.
22
+ * https://github.com/satoshilabs/slips/blob/master/slip-0021.md
18
23
  */
19
- export const createMnemonic = () => bip39.generateMnemonic(wordlist, 128);
20
- /** Parse a string to {@link Mnemonic}. */
21
- export const parseMnemonic = (mnemonic) => {
22
- const mnemonicTrimmed = mnemonic.trim();
23
- return bip39.validateMnemonic(mnemonicTrimmed, wordlist)
24
- ? Effect.succeed(mnemonicTrimmed)
25
- : Effect.fail({
26
- _tag: "InvalidMnemonicError",
27
- });
28
- };
29
- export const mnemonicToSeed = (mnemonic) => bip39.mnemonicToSeedSync(mnemonic);
30
- export class NanoIdGenerator extends Context.Tag("NanoIdGenerator")() {
31
- }
32
- export const createNanoIdGeneratorLive = (customAlphabet, nanoid) => {
33
- const nanoidForNodeId = customAlphabet("0123456789abcdef", 16);
34
- return Layer.succeed(NanoIdGenerator, NanoIdGenerator.of({
35
- nanoid: Effect.sync(() => nanoid()),
36
- nodeId: Effect.sync(() => nanoidForNodeId()),
37
- rowId: Effect.sync(() => nanoid()),
38
- }));
39
- };
40
- export const NodeId = S.String.pipe(S.pattern(/^[\w-]{16}$/), S.brand("NodeId"));
41
- // SLIP-21 implementation
42
- // https://github.com/satoshilabs/slips/blob/master/slip-0021.md
43
- export const slip21Derive = (seed, path) => Effect.sync(() => {
24
+ export const createSlip21 = (seed, path) => {
44
25
  let m = hmac(sha512, "Symmetric key seed", seed);
45
- for (let i = 0; i < path.length; i++) {
46
- const p = new TextEncoder().encode(path[i]);
47
- const e = new Uint8Array(p.byteLength + 1);
26
+ for (const component of path) {
27
+ const p = new TextEncoder().encode(component);
28
+ const e = new globalThis.Uint8Array(p.byteLength + 1);
48
29
  e[0] = 0;
49
30
  e.set(p, 1);
50
31
  m = hmac(sha512, m.slice(0, 32), e);
51
32
  }
52
33
  return m.slice(32, 64);
53
- });
54
- export class SecretBox extends Context.Tag("SecretBox")() {
55
- }
56
- SecretBox.Live = Layer.succeed(SecretBox, SecretBox.of({
57
- seal: (key, plaintext) => Effect.sync(() => {
58
- const nonce = randomBytes(24);
59
- const ciphertext = secretbox(key, nonce).seal(plaintext);
60
- return concatBytes(nonce, ciphertext);
61
- }),
62
- open: (key, ciphertext) => Effect.sync(() => {
63
- const nonce = ciphertext.subarray(0, 24);
64
- const ciphertextWithoutNonce = ciphertext.subarray(24);
65
- return secretbox(key, nonce).open(ciphertextWithoutNonce);
66
- }),
67
- }));
34
+ };
35
+ /**
36
+ * Creates a 21-character Base64URL ID (also known as nanoid) from a SLIP-21
37
+ * derived key.
38
+ *
39
+ * Reduces the 256-bit SLIP-21 output to 126 bits (21 chars × 6 bits) for a
40
+ * compact, human-readable, and shareable identifier suitable for UI display or
41
+ * URL use. While this lowers entropy, 126 bits remains cryptographically secure
42
+ * for uniqueness and unpredictability in most applications (comparable to
43
+ * UUIDv4's 122 bits).
44
+ *
45
+ * See https://github.com/satoshilabs/slips/blob/master/slip-0021.md
46
+ */
47
+ export const createSlip21Id = (seed, path) => {
48
+ const slip21 = createSlip21(seed, path);
49
+ let id = "";
50
+ // Convert the key to the Id/NanoId/Base64Url format.
51
+ for (let i = 0; i < 21; i++) {
52
+ id = (id + urlAlphabet[slip21[i] & 63]);
53
+ }
54
+ return id;
55
+ };
56
+ /** The encryption key for {@link SymmetricCrypto}. */
57
+ export const EncryptionKey = brand("EncryptionKey", length(32)(Uint8Array));
58
+ export const createEncryptionKey = (seed) => createSlip21(seed, ["Evolu", "Encryption Key"]);
59
+ /**
60
+ * XChaCha20-Poly1305 encryption
61
+ *
62
+ * https://github.com/paulmillr/noble-ciphers?tab=readme-ov-file#which-cipher-should-i-pick
63
+ */
64
+ export const createSymmetricCrypto = (deps) => {
65
+ const nonceLength = getOrThrow(NonNegativeInt.from(24));
66
+ const symmetricCrypto = {
67
+ nonceLength,
68
+ encrypt: (plaintext, encryptionKey) => {
69
+ const nonce = deps.createRandomBytes(nonceLength);
70
+ const ciphertext = xchacha20poly1305(encryptionKey, nonce).encrypt(plaintext);
71
+ return { nonce, ciphertext };
72
+ },
73
+ decrypt: (ciphertext, encryptionKey, nonce) => trySync(() => xchacha20poly1305(encryptionKey, nonce).decrypt(ciphertext), (error) => ({
74
+ type: "SymmetricCryptoDecryptError",
75
+ error,
76
+ })),
77
+ };
78
+ return symmetricCrypto;
79
+ };
80
+ /**
81
+ * Returns the PADMÉ padded length for a given input length.
82
+ *
83
+ * PADMÉ limits information leakage about the length of the plain-text for a
84
+ * wide range of encrypted data sizes. See the PURBs paper for details:
85
+ * https://bford.info/pub/sec/purb.pdf
86
+ */
87
+ export const padmePaddedLength = (length) => {
88
+ if (length <= 0)
89
+ return 0;
90
+ const e = 31 - Math.clz32(length >>> 0);
91
+ const s = 32 - Math.clz32(e >>> 0);
92
+ const z = Math.max(0, e - s);
93
+ const mask = (1 << z) - 1;
94
+ return ((length + mask) & ~mask);
95
+ };
96
+ /**
97
+ * Returns the PADMÉ padding length for a given input length. Uses
98
+ * {@link padmePaddedLength}.
99
+ */
100
+ export const padmePaddingLength = (length) => {
101
+ return (padmePaddedLength(length) - length);
102
+ };
@@ -0,0 +1,97 @@
1
+ import { JsonValue, JsonValueInput } from "./Type.js";
2
+ import { Order } from "./Order.js";
3
+ /**
4
+ * Compares two values of the same type `A` for equality.
5
+ *
6
+ * Equality functions start with an 'eq' prefix, e.g., `eqString`.
7
+ *
8
+ * TODO: Explain, examples (composition etc.)
9
+ */
10
+ export type Eq<in A> = (x: A, y: A) => boolean;
11
+ export declare const eqStrict: <A>(x: A, y: A) => boolean;
12
+ export declare const eqString: Eq<string>;
13
+ export declare const eqNumber: Eq<number>;
14
+ export declare const eqBigInt: Eq<bigint>;
15
+ export declare const eqBoolean: Eq<boolean>;
16
+ export declare const eqUndefined: Eq<undefined>;
17
+ export declare const eqNull: Eq<null>;
18
+ /** Derives an {@link Eq} from an {@link Order}. */
19
+ export declare const eqFromOrder: <A>(order: Order<A>) => Eq<A>;
20
+ /**
21
+ * Creates an equivalence function for array-like structures based on an
22
+ * equivalence for their elements.
23
+ *
24
+ * ### Example
25
+ *
26
+ * ```ts
27
+ * const eqArrayNumber = createEqArrayLike(eqNumber);
28
+ * eqArrayNumber([1, 2, 3], [1, 2, 3]); // true (works with regular arrays)
29
+ * eqArrayNumber(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3])); // true (works with Uint8Array)
30
+ * eqArrayNumber([1, 2, 3], [1, 2, 4]); // false
31
+ * ```
32
+ */
33
+ export declare const createEqArrayLike: <A>(item: Eq<A>) => Eq<ArrayLike<A>>;
34
+ /**
35
+ * Compares two array-like structures of numbers for equality.
36
+ *
37
+ * ### Example
38
+ *
39
+ * ```ts
40
+ * eqArrayNumber([1, 2, 3], [1, 2, 3]); // true (works with regular arrays)
41
+ * eqArrayNumber(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3])); // true (works with Uint8Array)
42
+ * eqArrayNumber([1, 2, 3], [1, 2, 4]); // false
43
+ * ```
44
+ */
45
+ export declare const eqArrayNumber: Eq<ArrayLike<number>>;
46
+ /**
47
+ * Creates an equivalence function for objects based on an equivalence for their
48
+ * fields.
49
+ *
50
+ * ### Example
51
+ *
52
+ * ```ts
53
+ * const eqObjectNumber = createEqObject({ a: eqNumber });
54
+ * eqObjectNumber({ a: 1 }, { a: 1 }); // true
55
+ * eqObjectNumber({ a: 1 }, { a: 2 }); // false
56
+ * ```
57
+ */
58
+ export declare const createEqObject: <A>(eqs: { [K in keyof A]: Eq<A[K]>; }) => Eq<{ readonly [K in keyof A]: A[K]; }>;
59
+ /**
60
+ * Deeply compares two {@link JsonValue} values for equality.
61
+ *
62
+ * - Uses an iterative approach with a stack to handle large or deeply nested
63
+ * objects without risking stack overflow.
64
+ * - Handles circular references with a WeakMap to prevent infinite loops.
65
+ * - Unlike JSON.stringify, this function directly compares values, avoiding
66
+ * serialization overhead and leveraging short-circuit evaluation for faster
67
+ * failure on mismatched structures.
68
+ *
69
+ * ### Example
70
+ *
71
+ * ```ts
72
+ * const obj1: Json = { name: "Alice", hobbies: ["reading", "hiking"] };
73
+ * const obj2: Json = { name: "Alice", hobbies: ["reading", "hiking"] };
74
+ * console.log(eqJson(obj1, obj2)); // true
75
+ * ```
76
+ */
77
+ export declare const eqJsonValue: (a: JsonValue, b: JsonValue) => boolean;
78
+ /**
79
+ * Deeply compares two {@link JsonValueInput} values for equality.
80
+ *
81
+ * - Uses an iterative approach with a stack to handle large or deeply nested
82
+ * objects without risking stack overflow.
83
+ * - Handles circular references with a WeakMap to prevent infinite loops.
84
+ * - Unlike JSON.stringify, this function directly compares values, avoiding
85
+ * serialization overhead and leveraging short-circuit evaluation for faster
86
+ * failure on mismatched structures.
87
+ *
88
+ * ### Example
89
+ *
90
+ * ```ts
91
+ * const obj1: Json = { name: "Alice", hobbies: ["reading", "hiking"] };
92
+ * const obj2: Json = { name: "Alice", hobbies: ["reading", "hiking"] };
93
+ * console.log(eqJson(obj1, obj2)); // true
94
+ * ```
95
+ */
96
+ export declare const eqJsonValueInput: (a: JsonValueInput, b: JsonValueInput) => boolean;
97
+ //# sourceMappingURL=Eq.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Eq.d.ts","sourceRoot":"","sources":["../../src/Eq.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,SAAS,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC7E,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC;;;;;;GAMG;AACH,MAAM,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;AAE/C,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,KAAG,OAAkB,CAAC;AAE5D,eAAO,MAAM,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAY,CAAC;AAC7C,eAAO,MAAM,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAY,CAAC;AAC7C,eAAO,MAAM,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAY,CAAC;AAC7C,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,OAAO,CAAY,CAAC;AAC/C,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,SAAS,CAAY,CAAC;AACnD,eAAO,MAAM,MAAM,EAAE,EAAE,CAAC,IAAI,CAAY,CAAC;AAEzC,mDAAmD;AACnD,eAAO,MAAM,WAAW,GACrB,CAAC,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAG,EAAE,CAAC,CAAC,CAEP,CAAC;AAEtB;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,GAC3B,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,KAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAUhC,CAAC;AAEJ;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,uBAA8B,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,GACxB,CAAC,EAAE,KAAK,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAE,KAAG,EAAE,CAAC,EACzC,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAC9B,CASA,CAAC;AAEJ;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,WAAW,GAAI,GAAG,SAAS,EAAE,GAAG,SAAS,KAAG,OAsExD,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,gBAAgB,GAC3B,GAAG,cAAc,EACjB,GAAG,cAAc,KAChB,OAAsD,CAAC"}
package/dist/src/Eq.js ADDED
@@ -0,0 +1,167 @@
1
+ export const eqStrict = (x, y) => x === y;
2
+ export const eqString = eqStrict;
3
+ export const eqNumber = eqStrict;
4
+ export const eqBigInt = eqStrict;
5
+ export const eqBoolean = eqStrict;
6
+ export const eqUndefined = eqStrict;
7
+ export const eqNull = eqStrict;
8
+ /** Derives an {@link Eq} from an {@link Order}. */
9
+ export const eqFromOrder = (order) => (x, y) => order(x, y) === 0;
10
+ /**
11
+ * Creates an equivalence function for array-like structures based on an
12
+ * equivalence for their elements.
13
+ *
14
+ * ### Example
15
+ *
16
+ * ```ts
17
+ * const eqArrayNumber = createEqArrayLike(eqNumber);
18
+ * eqArrayNumber([1, 2, 3], [1, 2, 3]); // true (works with regular arrays)
19
+ * eqArrayNumber(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3])); // true (works with Uint8Array)
20
+ * eqArrayNumber([1, 2, 3], [1, 2, 4]); // false
21
+ * ```
22
+ */
23
+ export const createEqArrayLike = (item) => (x, y) => {
24
+ if (x === y)
25
+ return true;
26
+ if (x.length !== y.length)
27
+ return false;
28
+ for (let i = 0; i < x.length; i++) {
29
+ if (!item(x[i], y[i]))
30
+ return false;
31
+ }
32
+ return true;
33
+ };
34
+ /**
35
+ * Compares two array-like structures of numbers for equality.
36
+ *
37
+ * ### Example
38
+ *
39
+ * ```ts
40
+ * eqArrayNumber([1, 2, 3], [1, 2, 3]); // true (works with regular arrays)
41
+ * eqArrayNumber(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3])); // true (works with Uint8Array)
42
+ * eqArrayNumber([1, 2, 3], [1, 2, 4]); // false
43
+ * ```
44
+ */
45
+ export const eqArrayNumber = createEqArrayLike(eqNumber);
46
+ /**
47
+ * Creates an equivalence function for objects based on an equivalence for their
48
+ * fields.
49
+ *
50
+ * ### Example
51
+ *
52
+ * ```ts
53
+ * const eqObjectNumber = createEqObject({ a: eqNumber });
54
+ * eqObjectNumber({ a: 1 }, { a: 1 }); // true
55
+ * eqObjectNumber({ a: 1 }, { a: 2 }); // false
56
+ * ```
57
+ */
58
+ export const createEqObject = (eqs) => (x, y) => {
59
+ if (x === y)
60
+ return true;
61
+ for (const key in eqs) {
62
+ if (!eqs[key](x[key], y[key])) {
63
+ return false;
64
+ }
65
+ }
66
+ return true;
67
+ };
68
+ /**
69
+ * Deeply compares two {@link JsonValue} values for equality.
70
+ *
71
+ * - Uses an iterative approach with a stack to handle large or deeply nested
72
+ * objects without risking stack overflow.
73
+ * - Handles circular references with a WeakMap to prevent infinite loops.
74
+ * - Unlike JSON.stringify, this function directly compares values, avoiding
75
+ * serialization overhead and leveraging short-circuit evaluation for faster
76
+ * failure on mismatched structures.
77
+ *
78
+ * ### Example
79
+ *
80
+ * ```ts
81
+ * const obj1: Json = { name: "Alice", hobbies: ["reading", "hiking"] };
82
+ * const obj2: Json = { name: "Alice", hobbies: ["reading", "hiking"] };
83
+ * console.log(eqJson(obj1, obj2)); // true
84
+ * ```
85
+ */
86
+ export const eqJsonValue = (a, b) => {
87
+ const stack = [[a, b]];
88
+ const seen = new WeakMap();
89
+ while (stack.length > 0) {
90
+ const [x, y] = stack.pop();
91
+ if (x === y)
92
+ continue;
93
+ const typeX = typeof x;
94
+ const typeY = typeof y;
95
+ if (typeX !== typeY || x === null || y === null)
96
+ return false;
97
+ if (typeX === "number" && isNaN(x) && isNaN(y)) {
98
+ continue;
99
+ }
100
+ if (typeX === "object") {
101
+ const isArrayX = Array.isArray(x);
102
+ const isArrayY = Array.isArray(y);
103
+ if (isArrayX !== isArrayY)
104
+ return false;
105
+ const xObj = x;
106
+ const yObj = y;
107
+ if (seen.has(xObj)) {
108
+ const ySet = seen.get(xObj);
109
+ if (ySet.has(yObj)) {
110
+ continue;
111
+ }
112
+ ySet.add(yObj);
113
+ }
114
+ else {
115
+ const ySet = new WeakSet();
116
+ ySet.add(yObj);
117
+ seen.set(xObj, ySet);
118
+ }
119
+ if (isArrayX && isArrayY) {
120
+ const xArr = x;
121
+ const yArr = y;
122
+ if (xArr.length !== yArr.length)
123
+ return false;
124
+ for (let i = 0; i < xArr.length; i++) {
125
+ stack.push([xArr[i], yArr[i]]);
126
+ }
127
+ }
128
+ else {
129
+ const xObjTyped = x;
130
+ const yObjTyped = y;
131
+ const xKeys = Object.keys(xObjTyped);
132
+ const yKeys = Object.keys(yObjTyped);
133
+ if (xKeys.length !== yKeys.length)
134
+ return false;
135
+ const yKeySet = new Set(yKeys);
136
+ for (const key of xKeys) {
137
+ if (!yKeySet.has(key))
138
+ return false;
139
+ stack.push([xObjTyped[key], yObjTyped[key]]);
140
+ }
141
+ }
142
+ }
143
+ else {
144
+ return false;
145
+ }
146
+ }
147
+ return true;
148
+ };
149
+ /**
150
+ * Deeply compares two {@link JsonValueInput} values for equality.
151
+ *
152
+ * - Uses an iterative approach with a stack to handle large or deeply nested
153
+ * objects without risking stack overflow.
154
+ * - Handles circular references with a WeakMap to prevent infinite loops.
155
+ * - Unlike JSON.stringify, this function directly compares values, avoiding
156
+ * serialization overhead and leveraging short-circuit evaluation for faster
157
+ * failure on mismatched structures.
158
+ *
159
+ * ### Example
160
+ *
161
+ * ```ts
162
+ * const obj1: Json = { name: "Alice", hobbies: ["reading", "hiking"] };
163
+ * const obj2: Json = { name: "Alice", hobbies: ["reading", "hiking"] };
164
+ * console.log(eqJson(obj1, obj2)); // true
165
+ * ```
166
+ */
167
+ export const eqJsonValueInput = (a, b) => eqJsonValue(a, b);