@evolu/common 8.0.0-next.4 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/dist/src/Array.d.ts +279 -173
  2. package/dist/src/Array.d.ts.map +1 -1
  3. package/dist/src/Array.js +105 -101
  4. package/dist/src/Assert.d.ts +44 -44
  5. package/dist/src/Assert.d.ts.map +1 -1
  6. package/dist/src/Assert.js +44 -32
  7. package/dist/src/BigInt.d.ts +6 -3
  8. package/dist/src/BigInt.d.ts.map +1 -1
  9. package/dist/src/BigInt.js +5 -2
  10. package/dist/src/Brand.d.ts +27 -40
  11. package/dist/src/Brand.d.ts.map +1 -1
  12. package/dist/src/Buffer.d.ts +27 -11
  13. package/dist/src/Buffer.d.ts.map +1 -1
  14. package/dist/src/Cache.d.ts +9 -4
  15. package/dist/src/Cache.d.ts.map +1 -1
  16. package/dist/src/Cache.js +8 -3
  17. package/dist/src/Callbacks.d.ts +30 -19
  18. package/dist/src/Callbacks.d.ts.map +1 -1
  19. package/dist/src/Console.d.ts +145 -66
  20. package/dist/src/Console.d.ts.map +1 -1
  21. package/dist/src/Console.js +91 -43
  22. package/dist/src/Crypto.d.ts +85 -42
  23. package/dist/src/Crypto.d.ts.map +1 -1
  24. package/dist/src/Crypto.js +44 -20
  25. package/dist/src/Eq.d.ts +87 -21
  26. package/dist/src/Eq.d.ts.map +1 -1
  27. package/dist/src/Eq.js +67 -19
  28. package/dist/src/Error.d.ts +5 -3
  29. package/dist/src/Error.d.ts.map +1 -1
  30. package/dist/src/Error.js +16 -0
  31. package/dist/src/Function.d.ts +119 -58
  32. package/dist/src/Function.d.ts.map +1 -1
  33. package/dist/src/Function.js +86 -43
  34. package/dist/src/Http.d.ts +394 -0
  35. package/dist/src/Http.d.ts.map +1 -0
  36. package/dist/src/Http.js +102 -0
  37. package/dist/src/Identicon.d.ts +16 -7
  38. package/dist/src/Identicon.d.ts.map +1 -1
  39. package/dist/src/Identicon.js +14 -5
  40. package/dist/src/LeakDetector.d.ts +88 -0
  41. package/dist/src/LeakDetector.d.ts.map +1 -0
  42. package/dist/src/LeakDetector.js +86 -0
  43. package/dist/src/LockManager.d.ts +18 -9
  44. package/dist/src/LockManager.d.ts.map +1 -1
  45. package/dist/src/LockManager.js +7 -8
  46. package/dist/src/Lookup.d.ts +43 -13
  47. package/dist/src/Lookup.d.ts.map +1 -1
  48. package/dist/src/Lookup.js +28 -8
  49. package/dist/src/Microtask.d.ts +1 -1
  50. package/dist/src/Microtask.d.ts.map +1 -1
  51. package/dist/src/Microtask.js +3 -7
  52. package/dist/src/Number.d.ts +49 -13
  53. package/dist/src/Number.d.ts.map +1 -1
  54. package/dist/src/Number.js +34 -5
  55. package/dist/src/Object.d.ts +160 -40
  56. package/dist/src/Object.d.ts.map +1 -1
  57. package/dist/src/Object.js +92 -46
  58. package/dist/src/Option.d.ts +10 -7
  59. package/dist/src/Option.d.ts.map +1 -1
  60. package/dist/src/Option.js +9 -6
  61. package/dist/src/Order.d.ts +21 -21
  62. package/dist/src/Order.d.ts.map +1 -1
  63. package/dist/src/Order.js +18 -21
  64. package/dist/src/Platform.d.ts +31 -0
  65. package/dist/src/Platform.d.ts.map +1 -1
  66. package/dist/src/Platform.js +98 -0
  67. package/dist/src/Random.d.ts +32 -17
  68. package/dist/src/Random.d.ts.map +1 -1
  69. package/dist/src/Random.js +12 -8
  70. package/dist/src/Redacted.d.ts +43 -29
  71. package/dist/src/Redacted.d.ts.map +1 -1
  72. package/dist/src/Redacted.js +14 -3
  73. package/dist/src/Ref.d.ts +11 -3
  74. package/dist/src/Ref.d.ts.map +1 -1
  75. package/dist/src/Ref.js +2 -2
  76. package/dist/src/RefCount.d.ts +2 -6
  77. package/dist/src/RefCount.d.ts.map +1 -1
  78. package/dist/src/RefCount.js +33 -90
  79. package/dist/src/Relation.d.ts +70 -5
  80. package/dist/src/Relation.d.ts.map +1 -1
  81. package/dist/src/Relation.js +77 -2
  82. package/dist/src/Resource.d.ts +590 -155
  83. package/dist/src/Resource.d.ts.map +1 -1
  84. package/dist/src/Resource.js +592 -343
  85. package/dist/src/Result.d.ts +838 -191
  86. package/dist/src/Result.d.ts.map +1 -1
  87. package/dist/src/Result.js +163 -102
  88. package/dist/src/Schedule.d.ts +684 -231
  89. package/dist/src/Schedule.d.ts.map +1 -1
  90. package/dist/src/Schedule.js +766 -295
  91. package/dist/src/Set.d.ts +89 -40
  92. package/dist/src/Set.d.ts.map +1 -1
  93. package/dist/src/Set.js +50 -29
  94. package/dist/src/Sqlite.d.ts +29 -72
  95. package/dist/src/Sqlite.d.ts.map +1 -1
  96. package/dist/src/Sqlite.js +41 -46
  97. package/dist/src/StackTrace.d.ts +40 -0
  98. package/dist/src/StackTrace.d.ts.map +1 -0
  99. package/dist/src/StackTrace.js +140 -0
  100. package/dist/src/Store.d.ts +5 -4
  101. package/dist/src/Store.d.ts.map +1 -1
  102. package/dist/src/Store.js +5 -4
  103. package/dist/src/String.d.ts +7 -1
  104. package/dist/src/String.d.ts.map +1 -1
  105. package/dist/src/String.js +20 -7
  106. package/dist/src/Task.d.ts +3572 -1770
  107. package/dist/src/Task.d.ts.map +1 -1
  108. package/dist/src/Task.js +1879 -1274
  109. package/dist/src/Test.d.ts +17 -125
  110. package/dist/src/Test.d.ts.map +1 -1
  111. package/dist/src/Test.js +17 -68
  112. package/dist/src/Time.d.ts +108 -49
  113. package/dist/src/Time.d.ts.map +1 -1
  114. package/dist/src/Time.js +172 -73
  115. package/dist/src/Type.d.ts +3542 -1926
  116. package/dist/src/Type.d.ts.map +1 -1
  117. package/dist/src/Type.js +3070 -1784
  118. package/dist/src/Types.d.ts +200 -108
  119. package/dist/src/Types.d.ts.map +1 -1
  120. package/dist/src/Types.js +39 -8
  121. package/dist/src/WebSocket.d.ts +48 -23
  122. package/dist/src/WebSocket.d.ts.map +1 -1
  123. package/dist/src/WebSocket.js +15 -6
  124. package/dist/src/Worker.d.ts +94 -36
  125. package/dist/src/Worker.d.ts.map +1 -1
  126. package/dist/src/Worker.js +58 -24
  127. package/dist/src/index.d.ts +55 -52
  128. package/dist/src/index.d.ts.map +1 -1
  129. package/dist/src/index.js +3 -1
  130. package/dist/src/intl/cs.d.ts +94 -0
  131. package/dist/src/intl/cs.d.ts.map +1 -0
  132. package/dist/src/intl/cs.js +220 -0
  133. package/dist/src/intl/index.d.ts +7 -0
  134. package/dist/src/intl/index.d.ts.map +1 -0
  135. package/dist/src/intl/index.js +6 -0
  136. package/dist/src/local-first/Db.d.ts +12 -8
  137. package/dist/src/local-first/Db.d.ts.map +1 -1
  138. package/dist/src/local-first/Db.js +109 -132
  139. package/dist/src/local-first/Error.d.ts +4 -4
  140. package/dist/src/local-first/Evolu.d.ts +338 -92
  141. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  142. package/dist/src/local-first/Evolu.js +47 -55
  143. package/dist/src/local-first/LocalAuth.d.ts +4 -4
  144. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  145. package/dist/src/local-first/Owner.d.ts +61 -24
  146. package/dist/src/local-first/Owner.d.ts.map +1 -1
  147. package/dist/src/local-first/Owner.js +45 -8
  148. package/dist/src/local-first/Protocol.d.ts +44 -31
  149. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  150. package/dist/src/local-first/Protocol.js +36 -13
  151. package/dist/src/local-first/Query.d.ts +147 -62
  152. package/dist/src/local-first/Query.d.ts.map +1 -1
  153. package/dist/src/local-first/Query.js +112 -58
  154. package/dist/src/local-first/Relay.d.ts +44 -14
  155. package/dist/src/local-first/Relay.d.ts.map +1 -1
  156. package/dist/src/local-first/Schema.d.ts +92 -41
  157. package/dist/src/local-first/Schema.d.ts.map +1 -1
  158. package/dist/src/local-first/Schema.js +25 -8
  159. package/dist/src/local-first/Shared.d.ts +23 -21
  160. package/dist/src/local-first/Shared.d.ts.map +1 -1
  161. package/dist/src/local-first/Shared.js +100 -57
  162. package/dist/src/local-first/Storage.d.ts +53 -32
  163. package/dist/src/local-first/Storage.d.ts.map +1 -1
  164. package/dist/src/local-first/Storage.js +194 -234
  165. package/dist/src/local-first/Timestamp.d.ts +21 -21
  166. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  167. package/dist/src/local-first/Timestamp.js +2 -2
  168. package/dist/src/local-first/index.d.ts +11 -11
  169. package/package.json +14 -13
  170. package/src/Array.ts +306 -205
  171. package/src/Assert.ts +54 -69
  172. package/src/BigInt.ts +6 -3
  173. package/src/Brand.ts +27 -40
  174. package/src/Buffer.ts +28 -12
  175. package/src/Cache.ts +9 -4
  176. package/src/Callbacks.ts +30 -19
  177. package/src/Console.ts +156 -84
  178. package/src/Crypto.ts +83 -41
  179. package/src/Eq.ts +95 -22
  180. package/src/Error.ts +8 -4
  181. package/src/Function.ts +167 -59
  182. package/src/Http.ts +549 -0
  183. package/src/Identicon.ts +16 -7
  184. package/src/LeakDetector.ts +182 -0
  185. package/src/LockManager.ts +43 -39
  186. package/src/Lookup.ts +62 -28
  187. package/src/Microtask.ts +19 -23
  188. package/src/Number.ts +64 -12
  189. package/src/Object.ts +173 -49
  190. package/src/Option.ts +10 -7
  191. package/src/Order.ts +23 -21
  192. package/src/Platform.ts +136 -0
  193. package/src/Polyfills.ts +6 -12
  194. package/src/Random.ts +32 -19
  195. package/src/Redacted.ts +44 -30
  196. package/src/Ref.ts +14 -6
  197. package/src/RefCount.ts +38 -80
  198. package/src/Relation.ts +202 -10
  199. package/src/Resource.ts +1199 -591
  200. package/src/Result.ts +930 -271
  201. package/src/Schedule.ts +859 -343
  202. package/src/Set.ts +89 -40
  203. package/src/Sqlite.ts +144 -138
  204. package/src/StackTrace.ts +230 -0
  205. package/src/Store.ts +8 -7
  206. package/src/String.ts +22 -5
  207. package/src/Task.ts +5101 -2832
  208. package/src/Test.ts +20 -168
  209. package/src/Time.ts +293 -90
  210. package/src/Type.ts +10034 -4117
  211. package/src/Types.ts +227 -110
  212. package/src/WebSocket.ts +64 -36
  213. package/src/Worker.ts +180 -72
  214. package/src/index.ts +55 -52
  215. package/src/intl/cs.ts +388 -0
  216. package/src/intl/index.ts +7 -0
  217. package/src/local-first/Db.ts +152 -179
  218. package/src/local-first/Error.ts +4 -4
  219. package/src/local-first/Evolu.ts +495 -250
  220. package/src/local-first/LocalAuth.ts +6 -12
  221. package/src/local-first/Owner.ts +58 -21
  222. package/src/local-first/Protocol.ts +49 -31
  223. package/src/local-first/Query.ts +146 -61
  224. package/src/local-first/Relay.ts +51 -21
  225. package/src/local-first/Schema.ts +109 -51
  226. package/src/local-first/Shared.ts +266 -226
  227. package/src/local-first/Storage.ts +266 -264
  228. package/src/local-first/Timestamp.ts +25 -18
  229. package/src/local-first/index.ts +11 -11
  230. package/dist/src/Skiplist.d.ts +0 -28
  231. package/dist/src/Skiplist.d.ts.map +0 -1
  232. package/dist/src/Skiplist.js +0 -64
  233. package/dist/src/Tracer.d.ts +0 -48
  234. package/dist/src/Tracer.d.ts.map +0 -1
  235. package/dist/src/Tracer.js +0 -6
  236. package/src/Skiplist.ts +0 -108
  237. package/src/Tracer.ts +0 -54
@@ -21,15 +21,14 @@ import {
21
21
  TableNode,
22
22
  ValueNode,
23
23
  } from "kysely";
24
- import type { Brand } from "../Brand.js";
25
- import { createRandomBytes } from "../Crypto.js";
26
- import type { ReadonlyRecord } from "../Object.js";
27
- import { createRecord, isPlainObject } from "../Object.js";
28
- import type { SqliteQueryString } from "../Sqlite.js";
29
- import { eqSqliteValue, SqliteValue } from "../Sqlite.js";
30
- import { createId, String } from "../Type.js";
31
- import type { Simplify } from "../Types.js";
32
- import type { EvoluSchema } from "./Schema.js";
24
+ import type { Brand } from "../Brand.ts";
25
+ import { createRandomBytes } from "../Crypto.ts";
26
+ import type { ReadonlyRecord } from "../Object.ts";
27
+ import { createMutableRecord, isPlainObject } from "../Object.ts";
28
+ import type { SqliteQueryString } from "../Sqlite.ts";
29
+ import { eqSqliteValue, SqliteValue } from "../Sqlite.ts";
30
+ import { createId, String } from "../Type.ts";
31
+ import type { EvoluSchema } from "./Schema.ts";
33
32
 
34
33
  export { sql as kyselySql } from "kysely";
35
34
  export type { NotNull as KyselyNotNull } from "kysely";
@@ -40,9 +39,27 @@ export type { NotNull as KyselyNotNull } from "kysely";
40
39
  * ### Example
41
40
  *
42
41
  * ```ts
42
+ * import {
43
+ * createQueryBuilder,
44
+ * id,
45
+ * NonEmptyTrimmedString100,
46
+ * type Query,
47
+ * } from "@evolu/common";
48
+ *
49
+ * const TodoId = id("Todo");
50
+ * type TodoId = typeof TodoId.Output;
51
+ * const Schema = {
52
+ * todo: { id: TodoId, title: NonEmptyTrimmedString100 },
53
+ * };
43
54
  * const createQuery = createQueryBuilder(Schema);
44
55
  * const allTodos = createQuery((db) => db.selectFrom("todo").selectAll());
56
+ *
45
57
  * type AllTodosRow = typeof allTodos.Row;
58
+ * expectTypeOf<typeof allTodos>().toExtend<Query<typeof Schema>>();
59
+ * expectTypeOf<AllTodosRow["id"]>().toEqualTypeOf<TodoId>();
60
+ * expectTypeOf<
61
+ * AllTodosRow["title"]
62
+ * >().toEqualTypeOf<NonEmptyTrimmedString100 | null>();
46
63
  * ```
47
64
  */
48
65
  export type Query<
@@ -59,11 +76,25 @@ export type Query<
59
76
  * ### Example
60
77
  *
61
78
  * ```ts
79
+ * import {
80
+ * createQueryBuilder,
81
+ * id,
82
+ * type InferRow,
83
+ * NonEmptyTrimmedString100,
84
+ * } from "@evolu/common";
85
+ *
86
+ * const Schema = {
87
+ * todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
88
+ * };
62
89
  * const createQuery = createQueryBuilder(Schema);
63
90
  * const allTodos = createQuery((db) =>
64
91
  * db.selectFrom("todo").selectAll(),
65
92
  * );
93
+ *
66
94
  * type AllTodosRow = typeof allTodos.Row;
95
+ * expectTypeOf<AllTodosRow>().toEqualTypeOf<
96
+ * InferRow<typeof allTodos>
97
+ * >();
67
98
  * ```
68
99
  */
69
100
  Row: R;
@@ -91,25 +122,42 @@ export interface Row {
91
122
  * ### Example
92
123
  *
93
124
  * ```ts
94
- * import { evoluJsonArrayFrom } from "@evolu/common";
125
+ * import {
126
+ * createQueryBuilder,
127
+ * evoluJsonArrayFrom,
128
+ * id,
129
+ * NonEmptyTrimmedString100,
130
+ * } from "@evolu/common";
95
131
  *
96
- * const result = await db
97
- * .selectFrom("person")
98
- * .select((eb) => [
99
- * "id",
100
- * evoluJsonArrayFrom(
101
- * eb
102
- * .selectFrom("pet")
103
- * .select(["pet.id as pet_id", "pet.name"])
104
- * .whereRef("pet.owner_id", "=", "person.id")
105
- * .orderBy("pet.name"),
106
- * ).as("pets"),
107
- * ])
108
- * .execute();
132
+ * const PersonId = id("Person");
133
+ * const Schema = {
134
+ * person: { id: PersonId },
135
+ * pet: {
136
+ * id: id("Pet"),
137
+ * name: NonEmptyTrimmedString100,
138
+ * personId: PersonId,
139
+ * },
140
+ * };
141
+ * const createQuery = createQueryBuilder(Schema);
142
+ * const people = createQuery((db) =>
143
+ * db
144
+ * .selectFrom("person")
145
+ * .select(["person.id"])
146
+ * .select((eb) => [
147
+ * evoluJsonArrayFrom(
148
+ * eb
149
+ * .selectFrom("pet")
150
+ * .select(["pet.id as petId", "pet.name"])
151
+ * .whereRef("pet.personId", "=", "person.id")
152
+ * .orderBy("pet.name"),
153
+ * ).as("pets"),
154
+ * ]),
155
+ * );
109
156
  *
110
- * result[0]?.id;
111
- * result[0]?.pets[0].pet_id;
112
- * result[0]?.pets[0].name;
157
+ * expectTypeOf<(typeof people.Row.pets)[number]>().toEqualTypeOf<{
158
+ * petId: typeof Schema.pet.id.Output;
159
+ * name: NonEmptyTrimmedString100 | null;
160
+ * }>();
113
161
  * ```
114
162
  */
115
163
  export const evoluJsonArrayFrom = <O>(
@@ -133,25 +181,44 @@ export const evoluJsonArrayFrom = <O>(
133
181
  * ### Example
134
182
  *
135
183
  * ```ts
136
- * import { evoluJsonObjectFrom } from "@evolu/common";
184
+ * import {
185
+ * createQueryBuilder,
186
+ * evoluJsonObjectFrom,
187
+ * id,
188
+ * NonEmptyTrimmedString100,
189
+ * SqliteBoolean,
190
+ * } from "@evolu/common";
137
191
  *
138
- * const result = await db
139
- * .selectFrom("person")
140
- * .select((eb) => [
141
- * "id",
142
- * evoluJsonObjectFrom(
143
- * eb
144
- * .selectFrom("pet")
145
- * .select(["pet.id as pet_id", "pet.name"])
146
- * .whereRef("pet.owner_id", "=", "person.id")
147
- * .where("pet.is_favorite", "=", true),
148
- * ).as("favorite_pet"),
149
- * ])
150
- * .execute();
192
+ * const PersonId = id("Person");
193
+ * const Schema = {
194
+ * person: { id: PersonId },
195
+ * pet: {
196
+ * id: id("Pet"),
197
+ * name: NonEmptyTrimmedString100,
198
+ * personId: PersonId,
199
+ * isFavorite: SqliteBoolean,
200
+ * },
201
+ * };
202
+ * const createQuery = createQueryBuilder(Schema);
203
+ * const people = createQuery((db) =>
204
+ * db
205
+ * .selectFrom("person")
206
+ * .select(["person.id"])
207
+ * .select((eb) => [
208
+ * evoluJsonObjectFrom(
209
+ * eb
210
+ * .selectFrom("pet")
211
+ * .select(["pet.id as petId", "pet.name"])
212
+ * .whereRef("pet.personId", "=", "person.id")
213
+ * .where("pet.isFavorite", "=", 1),
214
+ * ).as("favoritePet"),
215
+ * ]),
216
+ * );
151
217
  *
152
- * result[0]?.id;
153
- * result[0]?.favorite_pet?.pet_id;
154
- * result[0]?.favorite_pet?.name;
218
+ * expectTypeOf<typeof people.Row.favoritePet>().toEqualTypeOf<{
219
+ * petId: typeof Schema.pet.id.Output;
220
+ * name: NonEmptyTrimmedString100 | null;
221
+ * } | null>();
155
222
  * ```
156
223
  */
157
224
  export const evoluJsonObjectFrom = <O>(
@@ -173,24 +240,42 @@ export const evoluJsonObjectFrom = <O>(
173
240
  * ### Example
174
241
  *
175
242
  * ```ts
176
- * import { evoluJsonBuildObject, kyselySql } from "@evolu/common";
243
+ * import {
244
+ * createQueryBuilder,
245
+ * evoluJsonBuildObject,
246
+ * id,
247
+ * kyselySql,
248
+ * NonEmptyTrimmedString100,
249
+ * } from "@evolu/common";
177
250
  *
178
- * const result = await db
179
- * .selectFrom("person")
180
- * .select((eb) => [
181
- * "id",
182
- * evoluJsonBuildObject({
183
- * first: eb.ref("first_name"),
184
- * last: eb.ref("last_name"),
185
- * full: kyselySql<string>`first_name || ' ' || last_name`,
186
- * }).as("name"),
187
- * ])
188
- * .execute();
251
+ * const Schema = {
252
+ * person: {
253
+ * id: id("Person"),
254
+ * firstName: NonEmptyTrimmedString100,
255
+ * lastName: NonEmptyTrimmedString100,
256
+ * },
257
+ * };
258
+ * const createQuery = createQueryBuilder(Schema);
259
+ * const people = createQuery((db) =>
260
+ * db
261
+ * .selectFrom("person")
262
+ * .select("person.id")
263
+ * .select((eb) => [
264
+ * evoluJsonBuildObject({
265
+ * first: eb.ref("firstName"),
266
+ * last: eb.ref("lastName"),
267
+ * full: kyselySql<string>`${eb.ref("firstName")} || ' ' || ${eb.ref(
268
+ * "lastName",
269
+ * )}`,
270
+ * }).as("name"),
271
+ * ]),
272
+ * );
189
273
  *
190
- * result[0]?.id;
191
- * result[0]?.name.first;
192
- * result[0]?.name.last;
193
- * result[0]?.name.full;
274
+ * expectTypeOf<typeof people.Row.name>().toEqualTypeOf<{
275
+ * first: NonEmptyTrimmedString100 | null;
276
+ * last: NonEmptyTrimmedString100 | null;
277
+ * full: string;
278
+ * }>();
194
279
  * ```
195
280
  */
196
281
  export const evoluJsonBuildObject = <
@@ -232,7 +317,7 @@ export const getJsonObjectArgs = (
232
317
 
233
318
  /** Rows returned by a query. */
234
319
  export type QueryRows<R extends Row = Row> = ReadonlyArray<
235
- Readonly<Simplify<R>>
320
+ Readonly<KyselySimplify<R>>
236
321
  >;
237
322
 
238
323
  export type Queries<
@@ -396,7 +481,7 @@ const parse = (obj: unknown): unknown => {
396
481
  const parseObject = (
397
482
  obj: ReadonlyRecord<string, unknown>,
398
483
  ): ReadonlyRecord<string, unknown> => {
399
- const result = createRecord();
484
+ const result = createMutableRecord();
400
485
  for (const key in obj) {
401
486
  result[key] = parse(obj[key]);
402
487
  }
@@ -10,35 +10,35 @@ import {
10
10
  firstInArray,
11
11
  isNonEmptyArray,
12
12
  mapArray,
13
- } from "../Array.js";
14
- import { assert } from "../Assert.js";
15
- import type { TimingSafeEqualDep } from "../Crypto.js";
16
- import { err, ok } from "../Result.js";
17
- import type { SqliteDep } from "../Sqlite.js";
18
- import { sql } from "../Sqlite.js";
19
- import { createMutexByKey } from "../Task.js";
20
- import { Name, PositiveInt, uint8ArrayToBase64Url } from "../Type.js";
21
- import { isPromiseLike, type Awaitable } from "../Types.js";
13
+ } from "../Array.ts";
14
+ import { assert } from "../Assert.ts";
15
+ import type { TimingSafeEqualDep } from "../Crypto.ts";
16
+ import { err, ok } from "../Result.ts";
17
+ import type { SqliteDep } from "../Sqlite.ts";
18
+ import { sql } from "../Sqlite.ts";
19
+ import { createMutexByKey } from "../Task.ts";
20
+ import { Name, PositiveInt, uint8ArrayToBase64Url } from "../Type.ts";
21
+ import { isPromiseLike, type Awaitable } from "../Types.ts";
22
22
  import {
23
23
  OwnerId,
24
24
  ownerIdBytesToOwnerId,
25
25
  // OwnerTransport,
26
26
  OwnerWriteKey,
27
- } from "./Owner.js";
27
+ } from "./Owner.ts";
28
28
  import type {
29
29
  EncryptedDbChange,
30
30
  SqliteStorageDeps,
31
31
  Storage,
32
32
  StorageConfig,
33
33
  StorageQuotaError,
34
- } from "./Storage.js";
34
+ } from "./Storage.ts";
35
35
  import {
36
36
  createBaseSqliteStorage,
37
37
  getTimestampInsertStrategy,
38
38
  readOwnerUsageOrDefault,
39
39
  updateOwnerUsage,
40
- } from "./Storage.js";
41
- import { timestampToTimestampBytes } from "./Timestamp.js";
40
+ } from "./Storage.ts";
41
+ import { timestampToTimestampBytes } from "./Timestamp.ts";
42
42
 
43
43
  export interface RelayConfig extends StorageConfig {
44
44
  /**
@@ -77,20 +77,50 @@ export interface RelayConfig extends StorageConfig {
77
77
  * ### Example
78
78
  *
79
79
  * ```ts
80
- * // Client
80
+ * import {
81
+ * AppName,
82
+ * createAppOwner,
83
+ * createEvolu,
84
+ * createOwnerWebSocketTransport,
85
+ * createOwnerSecret,
86
+ * createRandomBytes,
87
+ * id,
88
+ * type AnyTask,
89
+ * } from "@evolu/common";
90
+ * import type { RelayConfig } from "@evolu/common/local-first";
91
+ *
92
+ * // Create once, persist the mnemonic securely, and restore it on later runs.
93
+ * const appOwner = createAppOwner(
94
+ * createOwnerSecret({ randomBytes: createRandomBytes() }),
95
+ * );
96
+ * // Client: include the OwnerId so the relay can authenticate the connection.
81
97
  * const transport = createOwnerWebSocketTransport({
82
98
  * url: "wss://relay.evolu.dev",
83
- * ownerId: owner.id,
99
+ * ownerId: appOwner.id,
84
100
  * });
85
101
  *
86
- * const evolu = createEvolu(deps)(Schema, {
87
- * transports: [transport],
88
- * });
102
+ * const createTodoEvolu = createEvolu(
103
+ * { todo: { id: id("Todo") } },
104
+ * {
105
+ * appName: AppName.orThrow("AuthenticatedRelayExample"),
106
+ * appOwner,
107
+ * transports: [transport],
108
+ * },
109
+ * );
110
+ * expectTypeOf(createTodoEvolu).toExtend<AnyTask>();
89
111
  *
112
+ * // Relay: accept owners allowed by the app's access policy.
113
+ * type IsOwnerAllowed = NonNullable<RelayConfig["isOwnerAllowed"]>;
114
+ * const allowedOwnerIds = new Set([appOwner.id]);
115
+ * const isOwnerAllowed: IsOwnerAllowed = (ownerId, { signal }) =>
116
+ * !signal.aborted && allowedOwnerIds.has(ownerId);
90
117
  *
91
- * // Relay
92
- * isOwnerAllowed: (ownerId, { signal: _signal }) =>
93
- * Promise.resolve(ownerId === "6jy_2F4RT5qqeLgJ14_dnQ"),
118
+ * expect(
119
+ * isOwnerAllowed(appOwner.id, {
120
+ * signal: new AbortController().signal,
121
+ * }),
122
+ * ).toBe(true);
123
+ * expect(transport.url).toContain(`ownerId=${appOwner.id}`);
94
124
  * ```
95
125
  */
96
126
  readonly isOwnerAllowed?: (
@@ -5,7 +5,9 @@
5
5
  */
6
6
 
7
7
  import * as Kysely from "kysely";
8
- import { getProperty, mapObject, type ReadonlyRecord } from "../Object.js";
8
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
9
+ import { assertNonNullable } from "../Assert.ts";
10
+ import { getOwnProp, mapObject, type ReadonlyRecord } from "../Object.ts";
9
11
  import {
10
12
  eqSqliteIndex,
11
13
  getSqliteSchema,
@@ -19,29 +21,31 @@ import {
19
21
  type SqliteQueryOptions,
20
22
  type SqliteSchema,
21
23
  SqliteValue,
22
- } from "../Sqlite.js";
23
- import type { InferType } from "../Type.js";
24
+ } from "../Sqlite.ts";
24
25
  import {
25
26
  DateIso,
26
27
  type Id,
27
28
  IdBytes,
29
+ type InferType,
30
+ Null,
28
31
  nullOr,
29
32
  object,
30
- type StandardSchemaV1,
31
- } from "../Type.js";
32
- import type { Simplify } from "../Types.js";
33
- import type { AppOwner } from "./Owner.js";
34
- import { OwnerId } from "./Owner.js";
33
+ type ObjectType,
34
+ type UnionType,
35
+ } from "../Type.ts";
36
+ import type { CompileTimeError, Simplify } from "../Types.ts";
37
+ import type { AppOwner } from "./Owner.ts";
38
+ import { OwnerId } from "./Owner.ts";
35
39
  import type {
36
40
  evoluJsonArrayFrom,
37
41
  evoluJsonObjectFrom,
38
42
  Query,
39
43
  Row,
40
- } from "./Query.js";
41
- import type { CrdtMessage, DbChange } from "./Storage.js";
42
- import { TimestampBytes } from "./Timestamp.js";
44
+ } from "./Query.ts";
45
+ import type { CrdtMessage, DbChange } from "./Storage.ts";
46
+ import { TimestampBytes } from "./Timestamp.ts";
43
47
 
44
- /** Any {@link StandardSchemaV1}. */
48
+ /** Any Standard Schema V1 declaration. */
45
49
  export type AnyStandardSchemaV1 = StandardSchemaV1<any, any>;
46
50
 
47
51
  /**
@@ -56,26 +60,36 @@ export type AnyStandardSchemaV1 = StandardSchemaV1<any, any>;
56
60
  * ### Example
57
61
  *
58
62
  * ```ts
59
- * // With Evolu Type
63
+ * import * as z from "zod";
64
+ * import {
65
+ * id,
66
+ * NonEmptyTrimmedString100,
67
+ * nullOr,
68
+ * SqliteBoolean,
69
+ * } from "@evolu/common";
70
+ *
71
+ * // Evolu Type
60
72
  * const TodoId = id("Todo");
61
- * type TodoId = typeof TodoId.Type;
73
+ * type TodoId = typeof TodoId.Output;
62
74
  *
63
75
  * const Schema = {
64
76
  * todo: {
65
77
  * id: TodoId,
66
- * title: NonEmptyString100,
78
+ * title: NonEmptyTrimmedString100,
67
79
  * isCompleted: nullOr(SqliteBoolean),
68
80
  * },
69
81
  * };
82
+ * expectOk(Schema.todo.title.fromUnknown("Write docs"), "Write docs");
70
83
  *
71
- * // With Zod (or any Standard Schema library)
72
- * const Schema = {
84
+ * // Zod, or another Standard Schema library
85
+ * const ZodSchema = {
73
86
  * todo: {
74
- * id: TodoId, // Evolu id() for branded IDs
87
+ * id: TodoId,
75
88
  * title: z.string().min(1).max(100),
76
89
  * isCompleted: z.union([z.literal(0), z.literal(1)]).nullable(),
77
90
  * },
78
91
  * };
92
+ * expect(ZodSchema.todo.title.safeParse("Write docs").success).toBe(true);
79
93
  * ```
80
94
  */
81
95
  export type EvoluSchema = ReadonlyRecord<
@@ -150,7 +164,7 @@ export type CreateQuery<S extends EvoluSchema> = <R extends Row>(
150
164
  >,
151
165
  ) => Kysely.SelectQueryBuilder<any, any, R>,
152
166
  options?: SqliteQueryOptions,
153
- ) => Query<S, Simplify<R>>;
167
+ ) => Query<S, Kysely.Simplify<R>>;
154
168
 
155
169
  /**
156
170
  * System columns that are implicitly defined by Evolu.
@@ -161,7 +175,12 @@ export type CreateQuery<S extends EvoluSchema> = <R extends Row>(
161
175
  * mark rows as deleted.
162
176
  * - `ownerId`: Represents ownership and logically partitions the database.
163
177
  */
164
- export const SystemColumns = /*#__PURE__*/ object({
178
+ export const SystemColumns: ObjectType<{
179
+ readonly createdAt: typeof DateIso;
180
+ readonly updatedAt: typeof DateIso;
181
+ readonly isDeleted: UnionType<readonly [typeof SqliteBoolean, typeof Null]>;
182
+ readonly ownerId: typeof OwnerId;
183
+ }> = /*#__PURE__*/ object({
165
184
  createdAt: DateIso,
166
185
  updatedAt: DateIso,
167
186
  isDeleted: /*#__PURE__*/ nullOr(SqliteBoolean),
@@ -218,24 +237,50 @@ export interface MutationOptions {
218
237
  * ### Example
219
238
  *
220
239
  * ```ts
221
- * // Partition your own data by project (derived from your AppOwner)
240
+ * import {
241
+ * createAppOwner,
242
+ * createOwnerSecret,
243
+ * createRandomBytes,
244
+ * createSharedOwner,
245
+ * deriveShardOwner,
246
+ * id,
247
+ * NonEmptyTrimmedString100,
248
+ * type Evolu,
249
+ * } from "@evolu/common";
250
+ *
251
+ * const Schema = {
252
+ * task: { id: id("Task"), title: NonEmptyTrimmedString100 },
253
+ * comment: { id: id("Comment"), text: NonEmptyTrimmedString100 },
254
+ * };
255
+ * const randomBytes = createRandomBytes();
256
+ * // Create once, persist the mnemonic securely, and restore it on later runs.
257
+ * const appOwner = createAppOwner(createOwnerSecret({ randomBytes }));
222
258
  * const projectOwner = deriveShardOwner(appOwner, [
223
259
  * "project",
224
- * projectId,
260
+ * "project-1",
225
261
  * ]);
226
- * evolu.insert(
227
- * "task",
228
- * { title: "Task 1" },
229
- * { ownerId: projectOwner.id },
262
+ * const sharedOwner = createSharedOwner(
263
+ * createOwnerSecret({ randomBytes }),
230
264
  * );
231
265
  *
232
- * // Collaborative data (independent owner shared with others)
233
- * const sharedOwner = createSharedOwner(sharedSecret);
234
- * evolu.insert(
235
- * "comment",
236
- * { text: "Hello" },
237
- * { ownerId: sharedOwner.id },
238
- * );
266
+ * const insertOwnedRows = (evolu: Evolu<typeof Schema>): void => {
267
+ * // Partition app data by project.
268
+ * evolu.insert(
269
+ * "task",
270
+ * { title: NonEmptyTrimmedString100.orThrow("Task 1") },
271
+ * { ownerId: projectOwner.id },
272
+ * );
273
+ *
274
+ * // Put collaborative data under a shared owner.
275
+ * evolu.insert(
276
+ * "comment",
277
+ * { text: NonEmptyTrimmedString100.orThrow("Hello") },
278
+ * { ownerId: sharedOwner.id },
279
+ * );
280
+ * };
281
+ *
282
+ * expect(projectOwner.type).toBe("ShardOwner");
283
+ * expect(sharedOwner.type).toBe("SharedOwner");
239
284
  * ```
240
285
  */
241
286
  readonly ownerId?: OwnerId;
@@ -316,10 +361,7 @@ export type ValidateNoSystemColumns<S extends EvoluSchema> =
316
361
  ? keyof S[TableName] extends infer ColumnName
317
362
  ? ColumnName extends keyof S[TableName]
318
363
  ? ColumnName extends
319
- | "createdAt"
320
- | "updatedAt"
321
- | "isDeleted"
322
- | "ownerId"
364
+ "createdAt" | "updatedAt" | "isDeleted" | "ownerId"
323
365
  ? SchemaValidationError<`Table "${TableName & string}" uses system column name "${ColumnName & string}". System columns (createdAt, updatedAt, isDeleted, ownerId) are added automatically.`>
324
366
  : never
325
367
  : never
@@ -343,8 +385,10 @@ export type ValidateColumnTypes<S extends EvoluSchema> =
343
385
  : never;
344
386
 
345
387
  /** Schema validation error that shows clear, readable messages */
346
- export type SchemaValidationError<Message extends string> =
347
- `❌ Schema Error: ${Message}`;
388
+ export type SchemaValidationError<Message extends string> = CompileTimeError<
389
+ "Schema",
390
+ Message
391
+ >;
348
392
 
349
393
  /** Makes columns whose output type includes `null` optional. */
350
394
  export type NullableColumnsToOptional<T extends TableSchema> = {
@@ -382,12 +426,10 @@ export const evoluSchemaToSqliteSchema = <S extends EvoluSchema>(
382
426
  );
383
427
 
384
428
  const indexes = indexesConfig
385
- ? indexesConfig(createIndex).map(
386
- (index): SqliteIndex => ({
387
- name: index.toOperationNode().name.name,
388
- sql: index.compile().sql,
389
- }),
390
- )
429
+ ? indexesConfig(createIndex).map((index): SqliteIndex => ({
430
+ name: index.toOperationNode().name.name,
431
+ sql: index.compile().sql,
432
+ }))
391
433
  : [];
392
434
 
393
435
  return { tables, indexes };
@@ -405,21 +447,36 @@ export const evoluSchemaToSqliteSchema = <S extends EvoluSchema>(
405
447
  * ### Example
406
448
  *
407
449
  * ```ts
450
+ * import {
451
+ * createQueryBuilder,
452
+ * id,
453
+ * NonEmptyTrimmedString100,
454
+ * nullOr,
455
+ * SqliteBoolean,
456
+ * } from "@evolu/common";
457
+ *
458
+ * const TodoId = id("Todo");
459
+ * type TodoId = typeof TodoId.Output;
408
460
  * const Schema = {
409
461
  * todo: {
410
- * id: id("Todo"),
411
- * title: NonEmptyString100,
462
+ * id: TodoId,
463
+ * title: NonEmptyTrimmedString100,
412
464
  * isCompleted: nullOr(SqliteBoolean),
413
465
  * },
414
466
  * };
415
467
  *
416
- * // Create a typed query builder (once per schema)
468
+ * // Create one typed builder per schema and reuse it for every query.
417
469
  * const createQuery = createQueryBuilder(Schema);
418
- *
419
- * // Use it for all queries
420
470
  * const todosQuery = createQuery((db) =>
421
471
  * db.selectFrom("todo").select(["id", "title", "isCompleted"]),
422
472
  * );
473
+ *
474
+ * expectTypeOf<typeof todosQuery.Row>().toEqualTypeOf<{
475
+ * id: TodoId;
476
+ * title: NonEmptyTrimmedString100 | null;
477
+ * isCompleted: SqliteBoolean | null;
478
+ * }>();
479
+ * expect(todosQuery).toBeTypeOf("string");
423
480
  * ```
424
481
  */
425
482
  export const createQueryBuilder = <S extends EvoluSchema>(
@@ -449,7 +506,8 @@ export const ensureSqliteSchema =
449
506
  currentSchema ??= getEvoluSqliteSchema(deps)();
450
507
 
451
508
  for (const [tableName, newColumns] of Object.entries(newSchema.tables)) {
452
- const currentColumns = getProperty(currentSchema.tables, tableName);
509
+ assertNonNullable(newColumns);
510
+ const currentColumns = getOwnProp(currentSchema.tables, tableName);
453
511
  if (!currentColumns) {
454
512
  queries.push(createAppTable(tableName, newColumns));
455
513
  } else {