@evolu/common 6.0.1-preview.9 → 7.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 (184) hide show
  1. package/README.md +2 -2
  2. package/dist/src/Array.d.ts +256 -12
  3. package/dist/src/Array.d.ts.map +1 -1
  4. package/dist/src/Array.js +215 -9
  5. package/dist/src/Assert.d.ts +0 -13
  6. package/dist/src/Assert.d.ts.map +1 -1
  7. package/dist/src/Assert.js +0 -15
  8. package/dist/src/Brand.d.ts +75 -0
  9. package/dist/src/Brand.d.ts.map +1 -0
  10. package/dist/src/Brand.js +1 -0
  11. package/dist/src/Buffer.d.ts +1 -1
  12. package/dist/src/Buffer.d.ts.map +1 -1
  13. package/dist/src/Buffer.js +8 -7
  14. package/dist/src/Cache.d.ts +44 -0
  15. package/dist/src/Cache.d.ts.map +1 -0
  16. package/dist/src/Cache.js +52 -0
  17. package/dist/src/Callbacks.d.ts +45 -12
  18. package/dist/src/Callbacks.d.ts.map +1 -1
  19. package/dist/src/Callbacks.js +14 -7
  20. package/dist/src/Console.d.ts +31 -6
  21. package/dist/src/Console.d.ts.map +1 -1
  22. package/dist/src/Console.js +72 -9
  23. package/dist/src/Crypto.d.ts +56 -42
  24. package/dist/src/Crypto.d.ts.map +1 -1
  25. package/dist/src/Crypto.js +40 -53
  26. package/dist/src/Evolu/Db.d.ts +162 -74
  27. package/dist/src/Evolu/Db.d.ts.map +1 -1
  28. package/dist/src/Evolu/Db.js +284 -702
  29. package/dist/src/Evolu/Diff.d.ts +3 -3
  30. package/dist/src/Evolu/Diff.d.ts.map +1 -1
  31. package/dist/src/Evolu/Diff.js +7 -5
  32. package/dist/src/Evolu/Evolu.d.ts +213 -133
  33. package/dist/src/Evolu/Evolu.d.ts.map +1 -1
  34. package/dist/src/Evolu/Evolu.js +189 -180
  35. package/dist/src/Evolu/Internal.d.ts +0 -2
  36. package/dist/src/Evolu/Internal.d.ts.map +1 -1
  37. package/dist/src/Evolu/Internal.js +0 -2
  38. package/dist/src/Evolu/LocalAuth.d.ts +150 -0
  39. package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
  40. package/dist/src/Evolu/LocalAuth.js +174 -0
  41. package/dist/src/Evolu/Owner.d.ts +273 -120
  42. package/dist/src/Evolu/Owner.d.ts.map +1 -1
  43. package/dist/src/Evolu/Owner.js +130 -104
  44. package/dist/src/Evolu/Platform.d.ts +9 -7
  45. package/dist/src/Evolu/Platform.d.ts.map +1 -1
  46. package/dist/src/Evolu/Protocol.d.ts +268 -240
  47. package/dist/src/Evolu/Protocol.d.ts.map +1 -1
  48. package/dist/src/Evolu/Protocol.js +595 -447
  49. package/dist/src/Evolu/Public.d.ts +6 -8
  50. package/dist/src/Evolu/Public.d.ts.map +1 -1
  51. package/dist/src/Evolu/Public.js +2 -3
  52. package/dist/src/Evolu/PublicKysely.d.ts.map +1 -1
  53. package/dist/src/Evolu/PublicKysely.js +3 -4
  54. package/dist/src/Evolu/Query.d.ts +2 -1
  55. package/dist/src/Evolu/Query.d.ts.map +1 -1
  56. package/dist/src/Evolu/Query.js +1 -1
  57. package/dist/src/Evolu/Relay.d.ts +91 -8
  58. package/dist/src/Evolu/Relay.d.ts.map +1 -1
  59. package/dist/src/Evolu/Relay.js +214 -88
  60. package/dist/src/Evolu/Schema.d.ts +125 -47
  61. package/dist/src/Evolu/Schema.d.ts.map +1 -1
  62. package/dist/src/Evolu/Schema.js +175 -31
  63. package/dist/src/Evolu/Storage.d.ts +249 -27
  64. package/dist/src/Evolu/Storage.d.ts.map +1 -1
  65. package/dist/src/Evolu/Storage.js +198 -92
  66. package/dist/src/Evolu/Sync.d.ts +68 -13
  67. package/dist/src/Evolu/Sync.d.ts.map +1 -1
  68. package/dist/src/Evolu/Sync.js +469 -20
  69. package/dist/src/Evolu/Timestamp.d.ts +83 -30
  70. package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
  71. package/dist/src/Evolu/Timestamp.js +79 -34
  72. package/dist/src/Identicon.d.ts +35 -0
  73. package/dist/src/Identicon.d.ts.map +1 -0
  74. package/dist/src/Identicon.js +143 -0
  75. package/dist/src/Instances.d.ts +34 -0
  76. package/dist/src/Instances.d.ts.map +1 -0
  77. package/dist/src/Instances.js +44 -0
  78. package/dist/src/Number.d.ts +4 -3
  79. package/dist/src/Number.d.ts.map +1 -1
  80. package/dist/src/Number.js +5 -4
  81. package/dist/src/Object.d.ts +10 -4
  82. package/dist/src/Object.d.ts.map +1 -1
  83. package/dist/src/Object.js +9 -3
  84. package/dist/src/Platform.d.ts +20 -0
  85. package/dist/src/Platform.d.ts.map +1 -0
  86. package/dist/src/Platform.js +22 -0
  87. package/dist/src/Random.d.ts +3 -2
  88. package/dist/src/Random.d.ts.map +1 -1
  89. package/dist/src/Relation.d.ts +79 -0
  90. package/dist/src/Relation.d.ts.map +1 -0
  91. package/dist/src/Relation.js +127 -0
  92. package/dist/src/Resources.d.ts +118 -0
  93. package/dist/src/Resources.d.ts.map +1 -0
  94. package/dist/src/Resources.js +197 -0
  95. package/dist/src/Result.d.ts +184 -52
  96. package/dist/src/Result.d.ts.map +1 -1
  97. package/dist/src/Result.js +30 -241
  98. package/dist/src/Skiplist.js +2 -1
  99. package/dist/src/Sqlite.d.ts +89 -5
  100. package/dist/src/Sqlite.d.ts.map +1 -1
  101. package/dist/src/Sqlite.js +136 -9
  102. package/dist/src/Task.d.ts +586 -0
  103. package/dist/src/Task.d.ts.map +1 -0
  104. package/dist/src/Task.js +469 -0
  105. package/dist/src/Time.d.ts +66 -1
  106. package/dist/src/Time.d.ts.map +1 -1
  107. package/dist/src/Time.js +99 -5
  108. package/dist/src/Type.d.ts +676 -343
  109. package/dist/src/Type.d.ts.map +1 -1
  110. package/dist/src/Type.js +718 -467
  111. package/dist/src/Types.d.ts +1 -75
  112. package/dist/src/Types.d.ts.map +1 -1
  113. package/dist/src/WebSocket.d.ts +5 -2
  114. package/dist/src/WebSocket.d.ts.map +1 -1
  115. package/dist/src/WebSocket.js +12 -18
  116. package/dist/src/Worker.d.ts +39 -11
  117. package/dist/src/Worker.d.ts.map +1 -1
  118. package/dist/src/Worker.js +22 -4
  119. package/dist/src/index.d.ts +8 -3
  120. package/dist/src/index.d.ts.map +1 -1
  121. package/dist/src/index.js +8 -3
  122. package/package.json +14 -13
  123. package/src/Array.ts +301 -19
  124. package/src/Assert.ts +0 -21
  125. package/src/Brand.ts +75 -0
  126. package/src/Buffer.ts +7 -7
  127. package/src/Cache.ts +85 -0
  128. package/src/Callbacks.ts +62 -22
  129. package/src/Console.ts +91 -11
  130. package/src/Crypto.ts +94 -90
  131. package/src/Evolu/Db.ts +519 -1026
  132. package/src/Evolu/Diff.ts +7 -5
  133. package/src/Evolu/Evolu.ts +476 -360
  134. package/src/Evolu/Internal.ts +0 -2
  135. package/src/Evolu/LocalAuth.ts +463 -0
  136. package/src/Evolu/Owner.ts +355 -228
  137. package/src/Evolu/Platform.ts +9 -9
  138. package/src/Evolu/Protocol.ts +846 -742
  139. package/src/Evolu/Public.ts +7 -14
  140. package/src/Evolu/PublicKysely.ts +4 -5
  141. package/src/Evolu/Query.ts +3 -2
  142. package/src/Evolu/Relay.ts +406 -103
  143. package/src/Evolu/Schema.ts +323 -91
  144. package/src/Evolu/Storage.ts +559 -137
  145. package/src/Evolu/Sync.ts +819 -36
  146. package/src/Evolu/Timestamp.ts +90 -58
  147. package/src/Identicon.ts +197 -0
  148. package/src/Instances.ts +90 -0
  149. package/src/Number.ts +6 -10
  150. package/src/Object.ts +13 -5
  151. package/src/Platform.ts +26 -0
  152. package/src/Random.ts +3 -2
  153. package/src/Relation.ts +234 -0
  154. package/src/Resources.ts +367 -0
  155. package/src/Result.ts +191 -54
  156. package/src/Skiplist.ts +1 -1
  157. package/src/Sqlite.ts +152 -17
  158. package/src/Task.ts +901 -0
  159. package/src/Time.ts +180 -5
  160. package/src/Type.ts +1135 -730
  161. package/src/Types.ts +1 -77
  162. package/src/WebSocket.ts +27 -25
  163. package/src/Worker.ts +72 -23
  164. package/src/index.ts +8 -3
  165. package/dist/src/Evolu/Config.d.ts +0 -69
  166. package/dist/src/Evolu/Config.d.ts.map +0 -1
  167. package/dist/src/Evolu/Config.js +0 -9
  168. package/dist/src/Evolu/Kysely.d.ts +0 -6
  169. package/dist/src/Evolu/Kysely.d.ts.map +0 -1
  170. package/dist/src/Evolu/Kysely.js +0 -21
  171. package/dist/src/ManyToManyMap.d.ts +0 -26
  172. package/dist/src/ManyToManyMap.d.ts.map +0 -1
  173. package/dist/src/ManyToManyMap.js +0 -92
  174. package/dist/src/NanoId.d.ts +0 -27
  175. package/dist/src/NanoId.d.ts.map +0 -1
  176. package/dist/src/NanoId.js +0 -6
  177. package/dist/src/Promise.d.ts +0 -180
  178. package/dist/src/Promise.d.ts.map +0 -1
  179. package/dist/src/Promise.js +0 -176
  180. package/src/Evolu/Config.ts +0 -83
  181. package/src/Evolu/Kysely.ts +0 -38
  182. package/src/ManyToManyMap.ts +0 -140
  183. package/src/NanoId.ts +0 -39
  184. package/src/Promise.ts +0 -295
@@ -1,242 +1,3 @@
1
- /**
2
- * 🛡️ Type-safe errors
3
- *
4
- * ## Intro
5
- *
6
- * The problem with throwing an exception in JavaScript is that the caught error
7
- * is always of an unknown type. The unknown type is a problem because we can't
8
- * be sure all errors have been handled because the TypeScript compiler can't
9
- * help us.
10
- *
11
- * Some other languages like Rust 🦀 or Haskell 📚 use a type-safe approach to
12
- * error handling, where errors are explicitly represented as part of the return
13
- * type, such as Result or Either, allowing the developer to handle all errors
14
- * safely. ✅
15
- *
16
- * ✨ Evolu uses {@link Result}, and it looks like this:
17
- *
18
- * ```ts
19
- * type Result<T, E> = Ok<T> | Err<E>;
20
- *
21
- * interface Ok<T> {
22
- * readonly ok: true;
23
- * readonly value: T;
24
- * }
25
- *
26
- * interface Err<E> {
27
- * readonly ok: false;
28
- * readonly error: E;
29
- * }
30
- *
31
- * const ok = <T>(value: T): Ok<T> => ({ ok: true, value });
32
- * const err = <E>(error: E): Err<E> => ({ ok: false, error });
33
- * ```
34
- *
35
- * Now let's look at how `Result` can be used for safe JSON parsing:
36
- *
37
- * ```ts
38
- * interface ParseJsonError {
39
- * readonly type: "ParseJsonError";
40
- * readonly message: string;
41
- * }
42
- *
43
- * const parseJson = (value: string): Result<unknown, ParseJsonError> => {
44
- * try {
45
- * return ok(JSON.parse(value));
46
- * } catch (error) {
47
- * return err({ type: "ParseJsonError", message: String(error) });
48
- * }
49
- * };
50
- *
51
- * // Result<unknown, ParseJsonError>
52
- * const json = parseJson('{"key": "value"}');
53
- *
54
- * // Fail fast to handle errors early.
55
- * if (!json.ok) return json; // Err<ParseJsonError>
56
- *
57
- * // Now, we have access to the json.value.
58
- * expectTypeOf(json.value).toBeUnknown();
59
- * ```
60
- *
61
- * Note how we didn't have to use the try/catch, just `if (!json.ok)`, and how
62
- * the error isn't unknown but has a type.
63
- *
64
- * But we had to use `try/catch` in the `parseJson` function. For such a case,
65
- * wrapping unsafe code, Evolu provides the {@link trySync} helper:
66
- *
67
- * ```ts
68
- * const parseJson = (value: string): Result<unknown, ParseJsonError> =>
69
- * trySync(
70
- * () => JSON.parse(value) as unknown,
71
- * (error) => ({ type: "ParseJsonError", message: String(error) }),
72
- * );
73
- * ```
74
- *
75
- * ✨ {@link trySync} helper makes unsafe (can throw) synchronous code safe; for
76
- * unsafe asynchronous code, use {@link tryAsync}.
77
- *
78
- * Let's summarize it:
79
- *
80
- * - For synchronous safe code, use `ok` and `err`.
81
- * - For unsafe code, use `trySync` or `tryAsync`.
82
- * - For asynchronous safe code, use `Promise` with {@link Result}.
83
- *
84
- * Asynchronous safe (because of a Promise using Result) code is
85
- * straightforward:
86
- *
87
- * ```ts
88
- * const fetchUser = async (
89
- * userId: string,
90
- * ): Promise<Result<User, FetchUserError>> => {
91
- * // Simulate an API call
92
- * return new Promise((resolve) => {
93
- * setTimeout(() => {
94
- * if (userId === "1") {
95
- * resolve(ok({ id: "1", name: "Alice" }));
96
- * } else {
97
- * resolve(
98
- * err({ type: "FetchUserError", reason: "user not found" }),
99
- * );
100
- * }
101
- * }, 1000);
102
- * });
103
- * };
104
- * ```
105
- *
106
- * ## Examples
107
- *
108
- * ### Sequential Operations with Short-Circuiting
109
- *
110
- * When performing a sequence of operations where any failure should stop
111
- * further processing, use the `Result` type with early returns.
112
- *
113
- * Here's an example of a database reset operation that drops tables, restores a
114
- * schema, and initializes the database, stopping on the first error:
115
- *
116
- * ```ts
117
- * const resetResult = deps.sqlite.transaction(() => {
118
- * const dropAllTablesResult = dropAllTables(deps);
119
- * if (!dropAllTablesResult.ok) return dropAllTablesResult;
120
- *
121
- * if (message.restore) {
122
- * const dbSchema = getDbSchema(deps)();
123
- * if (!dbSchema.ok) return dbSchema;
124
- *
125
- * const ensureDbSchemaResult = ensureDbSchema(deps)(
126
- * message.restore.dbSchema,
127
- * dbSchema.value,
128
- * );
129
- * if (!ensureDbSchemaResult.ok) return ensureDbSchemaResult;
130
- *
131
- * const initializeDbResult = initializeDb(deps)(
132
- * message.restore.mnemonic,
133
- * );
134
- * if (!initializeDbResult.ok) return initializeDbResult;
135
- * }
136
- * return ok();
137
- * });
138
- *
139
- * if (!resetResult.ok) {
140
- * deps.postMessage({ type: "onError", error: resetResult.error });
141
- * return;
142
- * }
143
- * ```
144
- *
145
- * In this pattern:
146
- *
147
- * - Each operation returns a `Result` (e.g., `Result<void, E>` or `Result<T,
148
- * E>`).
149
- * - After each operation, check `if (!result.ok)` and return the `Err` to
150
- * short-circuit.
151
- * - If all operations succeed, return `ok()` (or another value if needed).
152
- * - Outside the transaction, handle the final `Result` to report success or
153
- * failure.
154
- *
155
- * This approach ensures type-safe error handling, avoids nested try/catch
156
- * blocks, and clearly communicates the control flow.
157
- *
158
- * ### A function with two different errors:
159
- *
160
- * ```ts
161
- * const example = (value: string): Result<number, FooError | BarError> => {
162
- * const foo = getFoo(value);
163
- * if (!foo.ok) return foo;
164
- *
165
- * const bar = barize(foo.value);
166
- * if (!bar.ok) return bar;
167
- *
168
- * return ok(barToNumber(bar.value));
169
- * };
170
- * ```
171
- *
172
- * ## FAQ
173
- *
174
- * ### What if my function doesn't return a value on success?
175
- *
176
- * If your function performs an operation but doesn't need to return a value on
177
- * success, you can use `Result<void, E>`. Using `Result<void, E>` is clearer
178
- * than using `Result<true, E>` or `Result<null, E>` because it communicates
179
- * that the function doesn't produce a value but can produce errors.
180
- *
181
- * ### How do I short-circuit processing of an array on the first error?
182
- *
183
- * If you want to stop processing as soon as an error occurs (short-circuit),
184
- * you should produce and check each `Result` inside a loop:
185
- *
186
- * ```ts
187
- * for (const query of [
188
- * sql`drop table evolu_owner;`,
189
- * sql`drop table evolu_message;`,
190
- * ]) {
191
- * const result = deps.sqlite.exec(query);
192
- * if (!result.ok) return result;
193
- * }
194
- * // All queries succeeded
195
- * ```
196
- *
197
- * ### How do I handle an array of operations and short-circuit on the first error?
198
- *
199
- * If you have an array of operations (not results), you should make them
200
- * _lazy_—that is, represent each operation as a function (see `LazyValue` in
201
- * `Function.ts`). This way, you only execute each operation as needed, and can
202
- * stop on the first error:
203
- *
204
- * ```ts
205
- * import type { LazyValue } from "./Function";
206
- *
207
- * const operations: LazyValue<Result<void, MyError>>[] = [
208
- * () => doSomething(),
209
- * () => doSomethingElse(),
210
- * ];
211
- *
212
- * for (const op of operations) {
213
- * const result = op();
214
- * if (!result.ok) return result;
215
- * }
216
- * // All operations succeeded
217
- * ```
218
- *
219
- * If you already have an array of `Result`s, the processing has already
220
- * happened, so you can't short-circuit. In that case, you can check for the
221
- * first error:
222
- *
223
- * ```ts
224
- * const firstError = results.find((r) => !r.ok);
225
- * if (firstError) return firstError;
226
- * // All results are Ok
227
- * ```
228
- *
229
- * ### Why doesn't Evolu provide "handy helpers"?
230
- *
231
- * Evolu intentionally favors imperative patterns (like the `for...of` loop
232
- * above) over monadic helpers. Imperative code is generally more readable,
233
- * easier to debug, and more familiar to most JavaScript and TypeScript
234
- * developers. While monads and functional helpers can be powerful, they often
235
- * obscure control flow and make debugging harder. Evolu's approach keeps error
236
- * handling explicit and straightforward.
237
- *
238
- * @module
239
- */
240
1
  export function ok(value = undefined) {
241
2
  return { ok: true, value: value };
242
3
  }
@@ -269,15 +30,40 @@ export const err = (error) => ({ ok: false, error });
269
30
  * const config = getOrThrow(loadConfig());
270
31
  * // Safe to use config here
271
32
  * ```
33
+ *
34
+ * Throws: `Error` with the original error attached as `cause`.
272
35
  */
273
36
  export const getOrThrow = (result) => {
274
37
  if (result.ok) {
275
38
  return result.value;
276
39
  }
277
40
  else {
278
- throw new Error(`Result error: ${JSON.stringify(result.error)}`);
41
+ throw new Error("getOrThrow", { cause: result.error });
279
42
  }
280
43
  };
44
+ /**
45
+ * Extracts the value from a {@link Result} if it is an `Ok`, or returns `null`
46
+ * if it is an `Err`.
47
+ *
48
+ * **Intended usage:**
49
+ *
50
+ * - When you need to convert a `Result` to a nullable value for APIs that expect
51
+ * `T | null`.
52
+ * - When the error is not important and you just want the value or nothing.
53
+ *
54
+ * ### Example
55
+ *
56
+ * ```ts
57
+ * const parseResult = parseJson('{"key": "value"}');
58
+ * const value = getOrNull(parseResult);
59
+ * // value is unknown | null
60
+ *
61
+ * if (value != null) {
62
+ * console.log("Parsed value:", value);
63
+ * }
64
+ * ```
65
+ */
66
+ export const getOrNull = (result) => result.ok ? result.value : null;
281
67
  /**
282
68
  * Wraps synchronous functions that may throw exceptions, returning a
283
69
  * {@link Result}.
@@ -298,7 +84,10 @@ export const getOrThrow = (result) => {
298
84
  * const parseJson = (value: string): Result<unknown, ParseJsonError> =>
299
85
  * trySync(
300
86
  * () => JSON.parse(value) as unknown,
301
- * (error) => ({ type: "ParseJsonError", message: String(error) }),
87
+ * (error): ParseJsonError => ({
88
+ * type: "ParseJsonError",
89
+ * message: String(error),
90
+ * }),
302
91
  * );
303
92
  * ```
304
93
  */
@@ -1,3 +1,4 @@
1
+ import { PositiveInt } from "./Type.js";
1
2
  // TODO: Use in Storage.
2
3
  export const createSkiplistLevel = (deps) => (config = {}) => {
3
4
  const { probability = 0.5, maxLevel = 32 } = config;
@@ -7,7 +8,7 @@ export const createSkiplistLevel = (deps) => (config = {}) => {
7
8
  while (deps.random.next() <= probability && level < maxLevel) {
8
9
  level += 1;
9
10
  }
10
- return level;
11
+ return PositiveInt.orThrow(level);
11
12
  },
12
13
  };
13
14
  };
@@ -1,8 +1,10 @@
1
+ import { Brand } from "./Brand.js";
1
2
  import { ConsoleDep } from "./Console.js";
3
+ import { EncryptionKey } from "./Crypto.js";
2
4
  import { TransferableError } from "./Error.js";
3
5
  import { Result } from "./Result.js";
4
6
  import { SimpleName } from "./Type.js";
5
- import { Brand, Predicate } from "./Types.js";
7
+ import { Predicate } from "./Types.js";
6
8
  /**
7
9
  * SQLite driver interface. This is the minimal interface that platform-specific
8
10
  * drivers must implement.
@@ -17,6 +19,7 @@ export interface CreateSqliteDriverDep {
17
19
  }
18
20
  export interface SqliteDriverOptions {
19
21
  memory?: boolean;
22
+ encryptionKey?: EncryptionKey | undefined;
20
23
  }
21
24
  /**
22
25
  * Cross-platform SQLite abstraction.
@@ -108,13 +111,54 @@ export interface RawSql {
108
111
  sql: string;
109
112
  }
110
113
  export type SqlTemplateParam = SqliteValue | SqlIdentifier | RawSql;
111
- /** TODO: Docs. */
114
+ /**
115
+ * Creates a safe SQL query using a tagged template literal.
116
+ *
117
+ * Parameters are automatically escaped and bound as SQLite values. Use
118
+ * `sql.identifier` for column/table names and `sql.raw` for unescaped SQL.
119
+ *
120
+ * ### Example
121
+ *
122
+ * ```ts
123
+ * const id = 42;
124
+ * const name = "Alice";
125
+ *
126
+ * const result = sqlite.exec(sql`
127
+ * select *
128
+ * from users
129
+ * where id = ${id} and name = ${name};
130
+ * `);
131
+ *
132
+ * // For identifiers
133
+ * const tableName = "users";
134
+ * sqlite.exec(sql`
135
+ * create table ${sql.identifier(tableName)} (
136
+ * "id" text primary key,
137
+ * "name" text not null
138
+ * );
139
+ * `);
140
+ *
141
+ * // For raw SQL (use with caution)
142
+ * const orderBy = "created_at desc";
143
+ * sqlite.exec(sql`select * from users order by ${sql.raw(orderBy)};`);
144
+ * ```
145
+ *
146
+ * ### TIP
147
+ *
148
+ * Use `prettier-plugin-sql-cst` for SQL formatting. Like Prettier for
149
+ * JavaScript, this plugin formats SQL expressions differently depending on
150
+ * their length.
151
+ */
112
152
  export declare const sql: {
113
153
  (strings: TemplateStringsArray, ...parameters: Array<SqlTemplateParam>): SqliteQuery;
114
154
  identifier(identifier: string): SqlIdentifier;
115
155
  raw(raw: string): RawSql;
116
156
  prepared(strings: TemplateStringsArray, ...parameters: Array<SqlTemplateParam>): SqliteQuery;
117
157
  };
158
+ /**
159
+ * Checks if a SQL string contains mutation keywords (insert, update, delete,
160
+ * etc.). Results are cached for performance.
161
+ */
118
162
  export declare const isSqlMutation: Predicate<string>;
119
163
  export interface SqliteQueryPlanRow {
120
164
  id: number;
@@ -124,11 +168,51 @@ export interface SqliteQueryPlanRow {
124
168
  export declare const explainSqliteQueryPlan: (deps: SqliteDep) => (query: SqliteQuery) => Result<void, SqliteError>;
125
169
  /**
126
170
  * SQLite represents boolean values using `0` (false) and `1` (true) instead of
127
- * a dedicated boolean type. This transform Type ensures that values conform to
128
- * SQLite's boolean representation.
171
+ * a dedicated boolean type.
129
172
  *
130
173
  * See: https://www.sqlite.org/quirks.html#no_separate_boolean_datatype
174
+ *
175
+ * ### Tips
176
+ *
177
+ * - Use {@link sqliteTrue} and {@link sqliteFalse} constants for better
178
+ * readability.
179
+ * - Use {@link booleanToSqliteBoolean} and {@link sqliteBooleanToBoolean} for
180
+ * converting between JavaScript booleans and SQLite boolean values.
131
181
  */
132
- export declare const SqliteBoolean: import("./Type.js").TransformType<import("./Type.js").Type<"Boolean", boolean, boolean, import("./Type.js").BooleanError, boolean, import("./Type.js").BooleanError>, import("./Type.js").UnionType<[import("./Type.js").LiteralType<0>, import("./Type.js").LiteralType<1>]>, never>;
182
+ export declare const SqliteBoolean: import("./Type.js").UnionType<[import("./Type.js").LiteralType<0>, import("./Type.js").LiteralType<1>]>;
133
183
  export type SqliteBoolean = typeof SqliteBoolean.Type;
184
+ /**
185
+ * Represents the {@link SqliteBoolean} value for `true`.
186
+ *
187
+ * See {@link SqliteBoolean}.
188
+ */
189
+ export declare const sqliteTrue = 1;
190
+ /**
191
+ * Represents the {@link SqliteBoolean} value for `false`.
192
+ *
193
+ * See {@link SqliteBoolean}.
194
+ */
195
+ export declare const sqliteFalse = 0;
196
+ /**
197
+ * Converts a JavaScript boolean to a {@link SqliteBoolean}.
198
+ *
199
+ * ### Example
200
+ *
201
+ * ```ts
202
+ * const isActive = true;
203
+ * const sqlValue = booleanToSqliteBoolean(isActive); // Returns 1
204
+ * ```
205
+ */
206
+ export declare const booleanToSqliteBoolean: (value: boolean) => SqliteBoolean;
207
+ /**
208
+ * Converts a {@link SqliteBoolean} to a JavaScript boolean.
209
+ *
210
+ * ### Example
211
+ *
212
+ * ```ts
213
+ * const sqlValue: SqliteBoolean = 1;
214
+ * const bool = sqliteBooleanToBoolean(sqlValue); // Returns true
215
+ * ```
216
+ */
217
+ export declare const sqliteBooleanToBoolean: (value: SqliteBoolean) => boolean;
134
218
  //# sourceMappingURL=Sqlite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sqlite.d.ts","sourceRoot":"","sources":["../../src/Sqlite.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Sqlite.d.ts","sourceRoot":"","sources":["../../src/Sqlite.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
+ import { createLruCache } from "./Cache.js";
1
2
  import { createTransferableError } from "./Error.js";
2
3
  import { err, ok, tryAsync, trySync } from "./Result.js";
3
- import { Boolean, Null, Number, String, transform, Uint8Array, union, } from "./Type.js";
4
+ import { Null, Number, PositiveInt, String, Uint8Array, union, } from "./Type.js";
4
5
  /**
5
6
  * A value that can be stored in Sqlite.
6
7
  *
@@ -21,9 +22,9 @@ export const createSqlite = (deps) => async (name, options) => tryAsync(async ()
21
22
  }, createSqliteError);
22
23
  const sqlite = {
23
24
  exec: (query) => trySync(() => {
24
- deps.console?.log("[sql]", query);
25
+ deps.console?.log("[sql]", { query });
25
26
  const result = maybeLogSqliteQueryExecutionTime(query, () => driver.exec(query, isSqlMutation(query.sql)));
26
- deps.console?.log("[sql]", result);
27
+ deps.console?.log("[sql]", { result });
27
28
  return result;
28
29
  }, (error) => ({
29
30
  type: "SqliteError",
@@ -121,7 +122,44 @@ export const createPreparedStatementsCache = (factory, disposeFn) => {
121
122
  },
122
123
  };
123
124
  };
124
- /** TODO: Docs. */
125
+ /**
126
+ * Creates a safe SQL query using a tagged template literal.
127
+ *
128
+ * Parameters are automatically escaped and bound as SQLite values. Use
129
+ * `sql.identifier` for column/table names and `sql.raw` for unescaped SQL.
130
+ *
131
+ * ### Example
132
+ *
133
+ * ```ts
134
+ * const id = 42;
135
+ * const name = "Alice";
136
+ *
137
+ * const result = sqlite.exec(sql`
138
+ * select *
139
+ * from users
140
+ * where id = ${id} and name = ${name};
141
+ * `);
142
+ *
143
+ * // For identifiers
144
+ * const tableName = "users";
145
+ * sqlite.exec(sql`
146
+ * create table ${sql.identifier(tableName)} (
147
+ * "id" text primary key,
148
+ * "name" text not null
149
+ * );
150
+ * `);
151
+ *
152
+ * // For raw SQL (use with caution)
153
+ * const orderBy = "created_at desc";
154
+ * sqlite.exec(sql`select * from users order by ${sql.raw(orderBy)};`);
155
+ * ```
156
+ *
157
+ * ### TIP
158
+ *
159
+ * Use `prettier-plugin-sql-cst` for SQL formatting. Like Prettier for
160
+ * JavaScript, this plugin formats SQL expressions differently depending on
161
+ * their length.
162
+ */
125
163
  export const sql = (strings, ...parameters) => {
126
164
  let sql = "";
127
165
  const values = [];
@@ -158,6 +196,25 @@ sql.prepared = (strings, ...parameters) => {
158
196
  const query = sql(strings, ...parameters);
159
197
  return { ...query, options: { prepare: true } };
160
198
  };
199
+ /**
200
+ * Checks if a SQL string contains mutation keywords (insert, update, delete,
201
+ * etc.). Results are cached for performance.
202
+ */
203
+ export const isSqlMutation = (sql) => {
204
+ /**
205
+ * Without cache, "insert 1_000_000" Storage test dropped from 57742
206
+ * inserts/sec to 34k. Regex we used was fast, but CodeQL flagged it as a
207
+ * potential ReDoS vulnerability, so manual comment removal was the only
208
+ * option. LRU cache restores performance.
209
+ */
210
+ const cached = isSqlMutationCache.get(sql);
211
+ if (cached !== undefined)
212
+ return cached;
213
+ const result = isSqlMutationRegEx.test(removeSqlComments(sql));
214
+ isSqlMutationCache.set(sql, result);
215
+ return result;
216
+ };
217
+ const isSqlMutationCache = createLruCache(PositiveInt.orThrow(10_000));
161
218
  const isSqlMutationRegEx = new RegExp(`\\b(${[
162
219
  "alter",
163
220
  "create",
@@ -172,7 +229,37 @@ const isSqlMutationRegEx = new RegExp(`\\b(${[
172
229
  "pragma",
173
230
  "vacuum",
174
231
  ].join("|")})\\b`, "i");
175
- export const isSqlMutation = (sql) => isSqlMutationRegEx.test(sql.replace(/--.*$/gm, ""));
232
+ /**
233
+ * Removes SQL line comments (--) from a SQL string without using regex to avoid
234
+ * ReDoS vulnerabilities.
235
+ */
236
+ const removeSqlComments = (sql) => {
237
+ // Fast path: if there are no comments, return the original string
238
+ if (!sql.includes("--"))
239
+ return sql;
240
+ let result = "";
241
+ let i = 0;
242
+ while (i < sql.length) {
243
+ // Check for comment start
244
+ if (i < sql.length - 1 && sql[i] === "-" && sql[i + 1] === "-") {
245
+ // Skip until end of line or end of string
246
+ i += 2;
247
+ while (i < sql.length && sql[i] !== "\n") {
248
+ i++;
249
+ }
250
+ // Keep the newline if present
251
+ if (i < sql.length && sql[i] === "\n") {
252
+ result += "\n";
253
+ i++;
254
+ }
255
+ }
256
+ else {
257
+ result += sql[i];
258
+ i++;
259
+ }
260
+ }
261
+ return result;
262
+ };
176
263
  export const explainSqliteQueryPlan = (deps) => (query) => {
177
264
  const result = deps.sqlite.exec({
178
265
  ...query,
@@ -181,7 +268,7 @@ export const explainSqliteQueryPlan = (deps) => (query) => {
181
268
  if (!result.ok)
182
269
  return result;
183
270
  // eslint-disable-next-line no-console
184
- console.log("ExplainQueryPlan", query);
271
+ console.log("[explainSqliteQueryPlan]", query);
185
272
  // eslint-disable-next-line no-console
186
273
  console.log(drawSqliteQueryPlan(result.value.rows));
187
274
  return ok();
@@ -203,9 +290,49 @@ const drawSqliteQueryPlan = (rows) => rows
203
290
  .join("\n");
204
291
  /**
205
292
  * SQLite represents boolean values using `0` (false) and `1` (true) instead of
206
- * a dedicated boolean type. This transform Type ensures that values conform to
207
- * SQLite's boolean representation.
293
+ * a dedicated boolean type.
208
294
  *
209
295
  * See: https://www.sqlite.org/quirks.html#no_separate_boolean_datatype
296
+ *
297
+ * ### Tips
298
+ *
299
+ * - Use {@link sqliteTrue} and {@link sqliteFalse} constants for better
300
+ * readability.
301
+ * - Use {@link booleanToSqliteBoolean} and {@link sqliteBooleanToBoolean} for
302
+ * converting between JavaScript booleans and SQLite boolean values.
303
+ */
304
+ export const SqliteBoolean = union(0, 1);
305
+ /**
306
+ * Represents the {@link SqliteBoolean} value for `true`.
307
+ *
308
+ * See {@link SqliteBoolean}.
309
+ */
310
+ export const sqliteTrue = 1;
311
+ /**
312
+ * Represents the {@link SqliteBoolean} value for `false`.
313
+ *
314
+ * See {@link SqliteBoolean}.
315
+ */
316
+ export const sqliteFalse = 0;
317
+ /**
318
+ * Converts a JavaScript boolean to a {@link SqliteBoolean}.
319
+ *
320
+ * ### Example
321
+ *
322
+ * ```ts
323
+ * const isActive = true;
324
+ * const sqlValue = booleanToSqliteBoolean(isActive); // Returns 1
325
+ * ```
326
+ */
327
+ export const booleanToSqliteBoolean = (value) => value ? sqliteTrue : sqliteFalse;
328
+ /**
329
+ * Converts a {@link SqliteBoolean} to a JavaScript boolean.
330
+ *
331
+ * ### Example
332
+ *
333
+ * ```ts
334
+ * const sqlValue: SqliteBoolean = 1;
335
+ * const bool = sqliteBooleanToBoolean(sqlValue); // Returns true
336
+ * ```
210
337
  */
211
- export const SqliteBoolean = transform(Boolean, union(0, 1), (value) => ok(value ? 1 : 0), (value) => value === 1);
338
+ export const sqliteBooleanToBoolean = (value) => value === sqliteTrue;