@evolu/common 5.4.8 → 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,417 @@
1
+ import { Kysely, SelectQueryBuilder } from "kysely";
2
+ import { pack } from "msgpackr";
3
+ import { mapObject, objectToEntries, ReadonlyRecord } from "../Object.js";
4
+ import { err, ok, Result } from "../Result.js";
5
+ import { SqliteBoolean, SqliteQueryOptions, SqliteValue } from "../Sqlite.js";
6
+ import {
7
+ AnyType,
8
+ brand,
9
+ BrandType,
10
+ createTypeErrorFormatter,
11
+ DateIsoString,
12
+ EvoluType,
13
+ Id,
14
+ InferErrors,
15
+ InferInput,
16
+ InferType,
17
+ MergeObjectTypeErrors,
18
+ nullableToOptional,
19
+ NullableToOptionalProps,
20
+ nullOr,
21
+ object,
22
+ ObjectType,
23
+ omit,
24
+ optional,
25
+ OptionalType,
26
+ record,
27
+ Type,
28
+ TypeError,
29
+ Unknown,
30
+ } from "../Type.js";
31
+ import { Simplify } from "../Types.js";
32
+ import { DbSchema, DbTable } from "./Db.js";
33
+ import { createIndexes, DbIndexesBuilder } from "./Kysely.js";
34
+ import { AppOwner, ShardOwner, SharedOwner } from "./Owner.js";
35
+ import {
36
+ Base64Url256,
37
+ BinaryId,
38
+ maxProtocolMessageRangesSize,
39
+ } from "./Protocol.js";
40
+ import { Query, Row } from "./Query.js";
41
+ import { BinaryTimestamp } from "./Timestamp.js";
42
+
43
+ /**
44
+ * Defines the schema of an Evolu database.
45
+ *
46
+ * - Each top-level key represents a table name.
47
+ * - The value for each table name is a record of column names mapped to their
48
+ * respective data types, defined by {@link Type}.
49
+ * - Each table must include a mandatory `id` column of type {@link Id}.
50
+ * - No table may contain {@link DefaultColumns}.
51
+ *
52
+ * Table schema defines columns that are required for table rows. For not
53
+ * required columns, use {@link nullOr}.
54
+ *
55
+ * ### Example
56
+ *
57
+ * ```ts
58
+ * const TodoId = id("Todo");
59
+ * type TodoId = typeof TodoId.Type;
60
+ *
61
+ * const TodoCategoryId = id("TodoCategory");
62
+ * type TodoCategoryId = typeof TodoCategoryId.Type;
63
+ *
64
+ * const NonEmptyString50 = maxLength(50)(NonEmptyString);
65
+ * type NonEmptyString50 = typeof NonEmptyString50.Type;
66
+ *
67
+ * // Database schema.
68
+ * const Schema = {
69
+ * todo: {
70
+ * id: TodoId,
71
+ * title: NonEmptyString1000,
72
+ * isCompleted: nullable(SqliteBoolean),
73
+ * categoryId: nullable(TodoCategoryId),
74
+ * },
75
+ * todoCategory: {
76
+ * id: TodoCategoryId,
77
+ * name: NonEmptyString50,
78
+ * json: nullable(SomeJson),
79
+ * },
80
+ * };
81
+ * ```
82
+ */
83
+ export type EvoluSchema = ReadonlyRecord<
84
+ string,
85
+ ReadonlyRecord<string, Type<any, any, any, any, any>> & {
86
+ readonly id: Type<any, any, any, any, any>;
87
+ }
88
+ >;
89
+
90
+ export type CreateQuery<S extends EvoluSchema> = <R extends Row>(
91
+ queryCallback: (
92
+ db: Pick<
93
+ Kysely<
94
+ {
95
+ [Table in keyof S]: {
96
+ readonly [Column in keyof S[Table]]: Column extends
97
+ | "id"
98
+ | "createdAt"
99
+ | "updatedAt"
100
+ ? InferType<S[Table][Column]>
101
+ : InferType<S[Table][Column]> | null;
102
+ } & DefaultColumns;
103
+ } & {
104
+ readonly evolu_history: {
105
+ readonly timestamp: BinaryTimestamp;
106
+ readonly table: keyof S;
107
+ readonly row: BinaryId;
108
+ readonly column: string;
109
+ readonly value: SqliteValue;
110
+ };
111
+ }
112
+ >,
113
+ "selectFrom" | "fn" | "with" | "withRecursive"
114
+ >,
115
+ ) => SelectQueryBuilder<any, any, R>,
116
+ options?: SqliteQueryOptions,
117
+ ) => Query<Simplify<R>>;
118
+
119
+ export const DefaultColumns = object({
120
+ createdAt: DateIsoString,
121
+ updatedAt: DateIsoString,
122
+ isDeleted: nullOr(SqliteBoolean),
123
+ });
124
+ export type DefaultColumns = typeof DefaultColumns.Type;
125
+
126
+ const isDefaultColumnName = (value: string): boolean =>
127
+ value === "createdAt" || value === "updatedAt" || value === "isDeleted";
128
+
129
+ /**
130
+ * Valid {@link EvoluSchema}.
131
+ *
132
+ * - Table and column names must be Base64Url strings.
133
+ * - Each table must include an `id` column of type {@link Id}.
134
+ * - Default column names (`createdAt`, `updatedAt`, `isDeleted`) are not allowed.
135
+ */
136
+ export const ValidEvoluSchema = brand(
137
+ "ValidEvoluSchema",
138
+ record(
139
+ Base64Url256,
140
+ object({ id: EvoluType }, record(Base64Url256, Unknown)),
141
+ ),
142
+ (value) => {
143
+ for (const tableName in value) {
144
+ for (const columnName in value[tableName as never]) {
145
+ if (isDefaultColumnName(columnName)) {
146
+ return err<ValidEvoluSchemaError>({
147
+ type: "ValidEvoluSchema",
148
+ value,
149
+ reason: {
150
+ kind: "DefaultColumnError",
151
+ tableName,
152
+ columnName,
153
+ },
154
+ });
155
+ }
156
+ }
157
+ }
158
+
159
+ return ok(value);
160
+ },
161
+ );
162
+
163
+ export type ValidEvoluSchema = typeof ValidEvoluSchema.Type;
164
+
165
+ export interface ValidEvoluSchemaError extends TypeError<"ValidEvoluSchema"> {
166
+ readonly reason: {
167
+ kind: "DefaultColumnError";
168
+ tableName: string;
169
+ columnName: string;
170
+ };
171
+ }
172
+
173
+ /**
174
+ * Asserts that the given value is {@link ValidEvoluSchema}.
175
+ *
176
+ * Throws an error if the value is not a valid Evolu schema.
177
+ */
178
+ export const assertValidEvoluSchema = (value: unknown): ValidEvoluSchema => {
179
+ const validEvoluSchema = ValidEvoluSchema.fromUnknown(value);
180
+ if (!validEvoluSchema.ok) {
181
+ const message = formatValidEvoluSchemaError(validEvoluSchema.error);
182
+ throw new Error(`Invalid Evolu schema: ${message}`);
183
+ }
184
+ return validEvoluSchema.value;
185
+ };
186
+
187
+ const formatValidEvoluSchemaError = (
188
+ error: typeof ValidEvoluSchema.Error | typeof ValidEvoluSchema.ParentError,
189
+ ): string => {
190
+ if (error.type === "Record") {
191
+ if (error.reason.kind === "Key") {
192
+ return `The table "${error.reason.key}" has invalid name. A table name must be Base64Url256 string (A-Z, a-z, 0-9, -, _).`;
193
+ }
194
+
195
+ if (
196
+ error.reason.kind === "Value" &&
197
+ error.reason.error.reason.kind === "Props" &&
198
+ error.reason.error.reason.errors.id?.type === "EvoluType"
199
+ ) {
200
+ return `The table "${error.reason.key}" has invalid ID column. Check examples.`;
201
+ }
202
+
203
+ if (
204
+ error.reason.kind === "Value" &&
205
+ error.reason.error.reason.kind === "IndexKey"
206
+ ) {
207
+ return `The table "${error.reason.key}" has invalid column name "${error.reason.error.reason.key}". A column name must be Base64Url256 string (A-Z, a-z, 0-9, -, _).`;
208
+ }
209
+ }
210
+
211
+ if (error.type === "ValidEvoluSchema") {
212
+ return `The table "${error.reason.tableName}" uses reserved column name "${error.reason.columnName}". Reserved column names are: createdAt, updatedAt, isDeleted.`;
213
+ }
214
+
215
+ return JSON.stringify(error, null, 2);
216
+ };
217
+
218
+ export const validEvoluSchemaToDbSchema = (
219
+ validEvoluSchema: ValidEvoluSchema,
220
+ indexes?: DbIndexesBuilder,
221
+ ): DbSchema => {
222
+ const tables = objectToEntries(validEvoluSchema).map(
223
+ ([tableName, table]): DbTable => ({
224
+ name: tableName,
225
+ columns: objectToEntries(table)
226
+ .filter(([k]) => k !== "id")
227
+ .map(([k]) => k as Base64Url256),
228
+ }),
229
+ );
230
+ return {
231
+ tables,
232
+ indexes: createIndexes(indexes),
233
+ };
234
+ };
235
+
236
+ export type MutationKind = "insert" | "update" | "upsert";
237
+
238
+ export type Mutation<S extends EvoluSchema, Kind extends MutationKind> = <
239
+ TableName extends keyof S,
240
+ >(
241
+ table: TableName,
242
+ props: InferInput<ObjectType<MutationMapping<S[TableName], Kind>>>,
243
+ options?: MutationOptions,
244
+ ) => Result<
245
+ { readonly id: S[TableName]["id"]["Type"] },
246
+ | ValidMutationSizeError
247
+ | MergeObjectTypeErrors<ObjectType<MutationMapping<S[TableName], Kind>>>
248
+ >;
249
+
250
+ export type MutationMapping<
251
+ P extends Record<string, AnyType>,
252
+ M extends MutationKind,
253
+ > = M extends "insert"
254
+ ? InsertableProps<P>
255
+ : M extends "update"
256
+ ? UpdateableProps<P>
257
+ : UpsertableProps<P>;
258
+
259
+ export interface MutationOptions {
260
+ readonly onComplete?: () => void;
261
+ /**
262
+ * Only validate, don't mutate.
263
+ *
264
+ * For example, `onChange` handler can call `insert`/`update`/`upsert` with
265
+ * `onlyValidate: true`.
266
+ */
267
+ readonly onlyValidate?: boolean;
268
+
269
+ /**
270
+ * The owner to use for this mutation. Can be a {@link ShardOwner} for sharding
271
+ * app data or a {@link SharedOwner} for collaborative write access. If
272
+ * omitted, defaults to the app's {@link AppOwner}.
273
+ */
274
+ readonly owner?: ShardOwner | SharedOwner;
275
+ }
276
+
277
+ /**
278
+ * Evolu has to limit the maximum mutation size. Otherwise, sync couldn't use
279
+ * the {@link maxProtocolMessageRangesSize}. The max size is 640KB in bytes,
280
+ * measured via MessagePack. Evolu Protocol DbChange will be smaller thanks to
281
+ * various optimizations.
282
+ */
283
+ export const maxMutationSize = 655360;
284
+
285
+ const validMutationSize = <T extends AnyType>(type: T) =>
286
+ brand("ValidMutationSize", type, (value) =>
287
+ pack(value).byteLength <= maxMutationSize
288
+ ? ok(value)
289
+ : err<ValidMutationSizeError>({ type: "ValidMutationSize", value }),
290
+ );
291
+
292
+ export interface ValidMutationSizeError
293
+ extends TypeError<"ValidMutationSize"> {}
294
+
295
+ export const formatValidMutationSizeError =
296
+ createTypeErrorFormatter<ValidMutationSizeError>(
297
+ (error) =>
298
+ `The mutation size exceeds the maximum limit of ${maxMutationSize} bytes. The provided mutation has a size of ${pack(error.value).byteLength} bytes.`,
299
+ );
300
+
301
+ export type ValidMutationSize<Props extends Record<string, AnyType>> =
302
+ BrandType<
303
+ ObjectType<Props>,
304
+ "ValidMutationSize",
305
+ ValidMutationSizeError,
306
+ InferErrors<ObjectType<Props>>
307
+ >;
308
+
309
+ /**
310
+ * Type Factory to create insertable {@link Type}. It makes nullable Types
311
+ * optional, omits Id, and ensures the {@link maxMutationSize}.
312
+ *
313
+ * ### Example
314
+ *
315
+ * ```ts
316
+ * const InsertableTodo = insertable(Schema.todo);
317
+ * type InsertableTodo = typeof InsertableTodo.Type;
318
+ * const todo = InsertableTodo.from({ title });
319
+ * if (!todo.ok) return; // handle errors
320
+ * ```
321
+ */
322
+ export const insertable = <Props extends Record<string, AnyType>>(
323
+ props: Props,
324
+ ): ValidMutationSize<InsertableProps<Props>> => {
325
+ const optionalNullable = nullableToOptional(props);
326
+ const withoutId = omit(optionalNullable, "id");
327
+ return validMutationSize(withoutId);
328
+ };
329
+
330
+ export type InsertableProps<Props extends Record<string, AnyType>> = Omit<
331
+ NullableToOptionalProps<Props>,
332
+ "id"
333
+ >;
334
+
335
+ export type Insertable<Props extends Record<string, AnyType>> = InferInput<
336
+ ObjectType<InsertableProps<Props>>
337
+ >;
338
+
339
+ /**
340
+ * Type Factory to create updateable {@link Type}. It makes everything except for
341
+ * the `id` column partial (i.e. optional) and ensures the
342
+ * {@link maxMutationSize}.
343
+ *
344
+ * ### Example
345
+ *
346
+ * ```ts
347
+ * const UpdateableTodo = updateable(Schema.todo);
348
+ * type UpdateableTodo = typeof UpdateableTodo.Type;
349
+ *
350
+ * // `id` is required; all other fields are optional.
351
+ * const todoResult = UpdateableTodo.from({
352
+ * id: "123",
353
+ * title: "New Title",
354
+ * });
355
+ * if (!todo.ok) return; // handle errors
356
+ * ```
357
+ */
358
+ export const updateable = <Props extends Record<string, AnyType>>(
359
+ props: Props,
360
+ ): ValidMutationSize<UpdateableProps<Props>> => {
361
+ const propsWithIsDeleted = { ...props, isDeleted: SqliteBoolean };
362
+ const updateableProps = mapObject(propsWithIsDeleted, (value, key) =>
363
+ key === "id" ? value : optional(value),
364
+ ) as UpdateableProps<Props>;
365
+ return validMutationSize(object(updateableProps));
366
+ };
367
+
368
+ export type UpdateableProps<Props extends Record<string, AnyType>> = {
369
+ [K in keyof Props]: K extends "id" ? Props[K] : OptionalType<Props[K]>;
370
+ } & { isDeleted: OptionalType<typeof SqliteBoolean> };
371
+
372
+ export type Updateable<Props extends Record<string, AnyType>> = InferInput<
373
+ ObjectType<UpdateableProps<Props>>
374
+ >;
375
+
376
+ /**
377
+ * Type Factory to create upsertable Type. It makes nullable Types optional and
378
+ * ensures the {@link maxMutationSize}.
379
+ *
380
+ * ### Example
381
+ *
382
+ * ```ts
383
+ * const UpsertableTodo = upsertable(Schema.todo);
384
+ * type UpsertableTodo = typeof UpsertableTodo.Type;
385
+ * const todo = UpsertableTodo.from({ id, title });
386
+ * if (!todo.ok) return; // handle errors
387
+ * ```
388
+ */
389
+ export const upsertable = <Props extends Record<string, AnyType>>(
390
+ props: Props,
391
+ ): ValidMutationSize<UpsertableProps<Props>> =>
392
+ validMutationSize(nullableToOptional(props));
393
+
394
+ export type UpsertableProps<Props extends Record<string, AnyType>> =
395
+ NullableToOptionalProps<Props>;
396
+
397
+ export type Upsertable<Props extends Record<string, AnyType>> = InferInput<
398
+ ObjectType<UpsertableProps<Props>>
399
+ >;
400
+
401
+ export type InferEvoluSchemaError<S extends EvoluSchema> = {
402
+ [Table in keyof S]: InferMutationTypeErrors<S[Table]>;
403
+ }[keyof S];
404
+
405
+ export type InferMutationTypeErrors<T extends Record<string, AnyType>> =
406
+ | InferColumnErrors<T, "insert">
407
+ | InferColumnErrors<T, "update">
408
+ | InferColumnErrors<T, "upsert">;
409
+
410
+ export type InferColumnErrors<
411
+ T extends Record<string, AnyType>,
412
+ M extends MutationKind,
413
+ > = {
414
+ [Column in keyof MutationMapping<T, M>]: InferErrors<
415
+ MutationMapping<T, M>[Column]
416
+ >;
417
+ }[keyof MutationMapping<T, M>];