@korajs/store 0.3.2 → 0.5.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 (73) hide show
  1. package/dist/adapters/better-sqlite3.cjs +1 -1
  2. package/dist/adapters/better-sqlite3.cjs.map +1 -1
  3. package/dist/adapters/better-sqlite3.d.cts +1 -1
  4. package/dist/adapters/better-sqlite3.d.ts +1 -1
  5. package/dist/adapters/better-sqlite3.js +1 -1
  6. package/dist/adapters/better-sqlite3.js.map +1 -1
  7. package/dist/adapters/indexeddb.cjs +413 -18
  8. package/dist/adapters/indexeddb.cjs.map +1 -1
  9. package/dist/adapters/indexeddb.d.cts +23 -5
  10. package/dist/adapters/indexeddb.d.ts +23 -5
  11. package/dist/adapters/indexeddb.js +166 -19
  12. package/dist/adapters/indexeddb.js.map +1 -1
  13. package/dist/adapters/sqlite-wasm-shared-host.cjs +68 -0
  14. package/dist/adapters/sqlite-wasm-shared-host.cjs.map +1 -0
  15. package/dist/adapters/sqlite-wasm-shared-host.d.cts +2 -0
  16. package/dist/adapters/sqlite-wasm-shared-host.d.ts +2 -0
  17. package/dist/adapters/sqlite-wasm-shared-host.js +66 -0
  18. package/dist/adapters/sqlite-wasm-shared-host.js.map +1 -0
  19. package/dist/adapters/sqlite-wasm-worker.cjs +34 -5
  20. package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
  21. package/dist/adapters/sqlite-wasm-worker.js +34 -5
  22. package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
  23. package/dist/adapters/sqlite-wasm.cjs +249 -2
  24. package/dist/adapters/sqlite-wasm.cjs.map +1 -1
  25. package/dist/adapters/sqlite-wasm.d.cts +15 -3
  26. package/dist/adapters/sqlite-wasm.d.ts +15 -3
  27. package/dist/adapters/sqlite-wasm.js +2 -2
  28. package/dist/audit-trace-store-AM4EWQNZ.js +15 -0
  29. package/dist/backup-JN3LB4YT.js +14 -0
  30. package/dist/backup-JN3LB4YT.js.map +1 -0
  31. package/dist/chunk-56LL4XP6.js +205 -0
  32. package/dist/chunk-56LL4XP6.js.map +1 -0
  33. package/dist/chunk-6AHYGQQT.js +425 -0
  34. package/dist/chunk-6AHYGQQT.js.map +1 -0
  35. package/dist/chunk-7BPETY5Y.js +570 -0
  36. package/dist/chunk-7BPETY5Y.js.map +1 -0
  37. package/dist/chunk-AGN3CIPB.js +188 -0
  38. package/dist/chunk-AGN3CIPB.js.map +1 -0
  39. package/dist/chunk-EZSTD2XC.js +380 -0
  40. package/dist/chunk-EZSTD2XC.js.map +1 -0
  41. package/dist/chunk-MYSJN3DF.js +117 -0
  42. package/dist/chunk-MYSJN3DF.js.map +1 -0
  43. package/dist/{chunk-DXKLAQ6P.js → chunk-XO4LMPAH.js} +1 -1
  44. package/dist/chunk-XO4LMPAH.js.map +1 -0
  45. package/dist/export-audit-5GV6VRDZ.js +15 -0
  46. package/dist/export-audit-5GV6VRDZ.js.map +1 -0
  47. package/dist/index.cjs +3351 -510
  48. package/dist/index.cjs.map +1 -1
  49. package/dist/index.d.cts +822 -50
  50. package/dist/index.d.ts +822 -50
  51. package/dist/index.js +1976 -558
  52. package/dist/index.js.map +1 -1
  53. package/dist/internal.cjs +789 -0
  54. package/dist/internal.cjs.map +1 -0
  55. package/dist/internal.d.cts +154 -0
  56. package/dist/internal.d.ts +154 -0
  57. package/dist/internal.js +48 -0
  58. package/dist/internal.js.map +1 -0
  59. package/dist/{sqlite-wasm-channel-46AOWNPM.js → sqlite-wasm-channel-HDDW6UBH.js} +2 -2
  60. package/dist/sqlite-wasm-channel-HDDW6UBH.js.map +1 -0
  61. package/dist/{sqlite-wasm-channel-Lakjuk2E.d.cts → sqlite-wasm-channel-I9y_jObg.d.cts} +1 -0
  62. package/dist/{sqlite-wasm-channel-Lakjuk2E.d.ts → sqlite-wasm-channel-I9y_jObg.d.ts} +1 -0
  63. package/dist/types-BMyHmwvn.d.ts +163 -0
  64. package/dist/types-CTr00g_d.d.cts +163 -0
  65. package/dist/types-DepnRDk_.d.cts +212 -0
  66. package/dist/types-DepnRDk_.d.ts +212 -0
  67. package/package.json +23 -2
  68. package/dist/chunk-DXKLAQ6P.js.map +0 -1
  69. package/dist/chunk-ZP5AXQ3Z.js +0 -179
  70. package/dist/chunk-ZP5AXQ3Z.js.map +0 -1
  71. package/dist/types-DF-KDSK1.d.cts +0 -106
  72. package/dist/types-DF-KDSK1.d.ts +0 -106
  73. /package/dist/{sqlite-wasm-channel-46AOWNPM.js.map → audit-trace-store-AM4EWQNZ.js.map} +0 -0
@@ -0,0 +1,789 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/internal.ts
31
+ var internal_exports = {};
32
+ __export(internal_exports, {
33
+ buildCountQuery: () => buildCountQuery,
34
+ buildInsertQuery: () => buildInsertQuery,
35
+ buildLwwSoftDeleteQuery: () => buildLwwSoftDeleteQuery,
36
+ buildLwwUpdateQuery: () => buildLwwUpdateQuery,
37
+ buildSelectQuery: () => buildSelectQuery,
38
+ buildSoftDeleteQuery: () => buildSoftDeleteQuery,
39
+ buildUpdateQuery: () => buildUpdateQuery,
40
+ buildWhereClause: () => buildWhereClause,
41
+ deserializeOperation: () => deserializeOperation,
42
+ deserializeOperationWithCollection: () => deserializeOperationWithCollection,
43
+ deserializeRecord: () => deserializeRecord,
44
+ executeDelete: () => executeDelete,
45
+ executeInsert: () => executeInsert,
46
+ executeUpdate: () => executeUpdate,
47
+ isIncomingNewerThanRow: () => isIncomingNewerThanRow,
48
+ resolveCausalDeps: () => resolveCausalDeps,
49
+ rowVersionFromRecord: () => rowVersionFromRecord,
50
+ serializeOperation: () => serializeOperation,
51
+ serializeRecord: () => serializeRecord,
52
+ serializeRowVersion: () => serializeRowVersion
53
+ });
54
+ module.exports = __toCommonJS(internal_exports);
55
+
56
+ // src/serialization/serializer.ts
57
+ var import_core = require("@korajs/core");
58
+
59
+ // src/serialization/richtext-serializer.ts
60
+ var Y = __toESM(require("yjs"), 1);
61
+ var TEXT_KEY = "content";
62
+ function encodeRichtext(value) {
63
+ if (value === null || value === void 0) {
64
+ return null;
65
+ }
66
+ if (typeof value === "string") {
67
+ const doc = new Y.Doc();
68
+ doc.getText(TEXT_KEY).insert(0, value);
69
+ return Y.encodeStateAsUpdate(doc);
70
+ }
71
+ if (value instanceof Uint8Array) {
72
+ return value;
73
+ }
74
+ if (value instanceof ArrayBuffer) {
75
+ return new Uint8Array(value);
76
+ }
77
+ throw new Error("Richtext value must be a string, Uint8Array, ArrayBuffer, null, or undefined.");
78
+ }
79
+ function decodeRichtext(value) {
80
+ if (value === null || value === void 0) {
81
+ return null;
82
+ }
83
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
84
+ return new Uint8Array(value);
85
+ }
86
+ if (value instanceof Uint8Array) {
87
+ return value;
88
+ }
89
+ if (value instanceof ArrayBuffer) {
90
+ return new Uint8Array(value);
91
+ }
92
+ throw new Error(
93
+ "Richtext storage value must be Uint8Array, ArrayBuffer, Buffer, null, or undefined."
94
+ );
95
+ }
96
+
97
+ // src/serialization/serializer.ts
98
+ function serializeRecord(data, fields) {
99
+ const result = {};
100
+ for (const [key, value] of Object.entries(data)) {
101
+ const descriptor = fields[key];
102
+ if (!descriptor) {
103
+ result[key] = value;
104
+ continue;
105
+ }
106
+ result[key] = serializeValue(value, descriptor);
107
+ }
108
+ return result;
109
+ }
110
+ function deserializeRecord(row, fields) {
111
+ const result = {
112
+ id: row.id,
113
+ createdAt: row._created_at,
114
+ updatedAt: row._updated_at
115
+ };
116
+ for (const [key, descriptor] of Object.entries(fields)) {
117
+ const rawValue = row[key];
118
+ if (rawValue === void 0 || rawValue === null) {
119
+ result[key] = rawValue ?? null;
120
+ continue;
121
+ }
122
+ result[key] = deserializeValue(rawValue, descriptor);
123
+ }
124
+ return result;
125
+ }
126
+ var ATOMIC_OPS_KEY = "__kora_atomic_ops__";
127
+ var TX_ID_KEY = "__kora_tx_id__";
128
+ var MUTATION_NAME_KEY = "__kora_mutation__";
129
+ function serializeOperation(op) {
130
+ const hasMetadata = op.transactionId !== void 0 || op.mutationName !== void 0;
131
+ let dataPayload = null;
132
+ if (op.data) {
133
+ dataPayload = { ...op.data };
134
+ if (op.atomicOps !== void 0 && Object.keys(op.atomicOps).length > 0) {
135
+ dataPayload[ATOMIC_OPS_KEY] = op.atomicOps;
136
+ }
137
+ if (op.transactionId !== void 0) {
138
+ dataPayload[TX_ID_KEY] = op.transactionId;
139
+ }
140
+ if (op.mutationName !== void 0) {
141
+ dataPayload[MUTATION_NAME_KEY] = op.mutationName;
142
+ }
143
+ } else if (hasMetadata) {
144
+ dataPayload = {};
145
+ if (op.transactionId !== void 0) {
146
+ dataPayload[TX_ID_KEY] = op.transactionId;
147
+ }
148
+ if (op.mutationName !== void 0) {
149
+ dataPayload[MUTATION_NAME_KEY] = op.mutationName;
150
+ }
151
+ }
152
+ return {
153
+ id: op.id,
154
+ node_id: op.nodeId,
155
+ type: op.type,
156
+ record_id: op.recordId,
157
+ data: dataPayload ? JSON.stringify(dataPayload) : null,
158
+ previous_data: op.previousData ? JSON.stringify(op.previousData) : null,
159
+ timestamp: import_core.HybridLogicalClock.serialize(op.timestamp),
160
+ sequence_number: op.sequenceNumber,
161
+ causal_deps: JSON.stringify(op.causalDeps),
162
+ schema_version: op.schemaVersion
163
+ };
164
+ }
165
+ function deserializeOperation(row) {
166
+ let data = null;
167
+ let atomicOps;
168
+ let transactionId;
169
+ let mutationName;
170
+ if (row.data) {
171
+ const parsed = JSON.parse(row.data);
172
+ if (ATOMIC_OPS_KEY in parsed) {
173
+ atomicOps = parsed[ATOMIC_OPS_KEY];
174
+ }
175
+ if (TX_ID_KEY in parsed) {
176
+ transactionId = parsed[TX_ID_KEY];
177
+ }
178
+ if (MUTATION_NAME_KEY in parsed) {
179
+ mutationName = parsed[MUTATION_NAME_KEY];
180
+ }
181
+ const { [ATOMIC_OPS_KEY]: _a, [TX_ID_KEY]: _t, [MUTATION_NAME_KEY]: _m, ...rest } = parsed;
182
+ data = Object.keys(rest).length > 0 ? rest : null;
183
+ }
184
+ return {
185
+ id: row.id,
186
+ nodeId: row.node_id,
187
+ type: row.type,
188
+ collection: "",
189
+ // Collection name is derived from the table name by the caller
190
+ recordId: row.record_id,
191
+ data,
192
+ previousData: row.previous_data ? JSON.parse(row.previous_data) : null,
193
+ timestamp: import_core.HybridLogicalClock.deserialize(row.timestamp),
194
+ sequenceNumber: row.sequence_number,
195
+ causalDeps: JSON.parse(row.causal_deps),
196
+ schemaVersion: row.schema_version,
197
+ ...atomicOps !== void 0 ? { atomicOps } : {},
198
+ ...transactionId !== void 0 ? { transactionId } : {},
199
+ ...mutationName !== void 0 ? { mutationName } : {}
200
+ };
201
+ }
202
+ function deserializeOperationWithCollection(row, collection) {
203
+ const op = deserializeOperation(row);
204
+ return { ...op, collection };
205
+ }
206
+ function serializeValue(value, descriptor) {
207
+ if (value === null || value === void 0) {
208
+ return null;
209
+ }
210
+ switch (descriptor.kind) {
211
+ case "boolean":
212
+ return value ? 1 : 0;
213
+ case "array":
214
+ return JSON.stringify(value);
215
+ case "richtext":
216
+ return encodeRichtext(value);
217
+ default:
218
+ return value;
219
+ }
220
+ }
221
+ function deserializeValue(value, descriptor) {
222
+ switch (descriptor.kind) {
223
+ case "boolean":
224
+ return value === 1 || value === true;
225
+ case "array":
226
+ if (typeof value === "string") {
227
+ return JSON.parse(value);
228
+ }
229
+ return value;
230
+ case "richtext":
231
+ return decodeRichtext(value);
232
+ default:
233
+ return value;
234
+ }
235
+ }
236
+
237
+ // src/errors.ts
238
+ var import_core2 = require("@korajs/core");
239
+ var QueryError = class extends import_core2.KoraError {
240
+ constructor(message, context) {
241
+ super(message, "QUERY_ERROR", context);
242
+ this.name = "QueryError";
243
+ }
244
+ };
245
+ var RecordNotFoundError = class extends import_core2.KoraError {
246
+ constructor(collection, recordId) {
247
+ super(`Record "${recordId}" not found in collection "${collection}"`, "RECORD_NOT_FOUND", {
248
+ collection,
249
+ recordId
250
+ });
251
+ this.name = "RecordNotFoundError";
252
+ }
253
+ };
254
+
255
+ // src/lww/row-version.ts
256
+ var import_core3 = require("@korajs/core");
257
+ var EMPTY_ROW_VERSION = "";
258
+ function serializeRowVersion(timestamp) {
259
+ return import_core3.HybridLogicalClock.serialize(timestamp);
260
+ }
261
+ function rowVersionFromRecord(row) {
262
+ const version = row._version;
263
+ if (typeof version === "string" && version.length > 0) {
264
+ return import_core3.HybridLogicalClock.deserialize(version);
265
+ }
266
+ return { wallTime: row._updated_at, logical: 0, nodeId: "" };
267
+ }
268
+ function isIncomingNewerThanRow(incoming, row) {
269
+ return import_core3.HybridLogicalClock.compare(incoming, rowVersionFromRecord(row)) > 0;
270
+ }
271
+ function lwwVersionWhereClause(remoteVersion) {
272
+ return {
273
+ sql: "(_version = ? OR _version < ?)",
274
+ params: [EMPTY_ROW_VERSION, remoteVersion]
275
+ };
276
+ }
277
+
278
+ // src/query/sql-builder.ts
279
+ function buildSelectQuery(descriptor, fields) {
280
+ const params = [];
281
+ const parts = [`SELECT * FROM ${descriptor.collection}`];
282
+ const whereClause = buildWhereClauseParts(descriptor.where, fields, params);
283
+ const deletedFilter = "_deleted = 0";
284
+ if (whereClause) {
285
+ parts.push(`WHERE ${deletedFilter} AND ${whereClause}`);
286
+ } else {
287
+ parts.push(`WHERE ${deletedFilter}`);
288
+ }
289
+ if (descriptor.orderBy.length > 0) {
290
+ const orderParts = descriptor.orderBy.map((o) => {
291
+ validateFieldName(o.field, fields);
292
+ return `${o.field} ${o.direction.toUpperCase()}`;
293
+ });
294
+ parts.push(`ORDER BY ${orderParts.join(", ")}`);
295
+ }
296
+ if (descriptor.limit !== void 0) {
297
+ parts.push(`LIMIT ${descriptor.limit}`);
298
+ }
299
+ if (descriptor.offset !== void 0) {
300
+ parts.push(`OFFSET ${descriptor.offset}`);
301
+ }
302
+ return { sql: parts.join(" "), params };
303
+ }
304
+ function buildCountQuery(descriptor, fields) {
305
+ const params = [];
306
+ const parts = [`SELECT COUNT(*) as count FROM ${descriptor.collection}`];
307
+ const whereClause = buildWhereClauseParts(descriptor.where, fields, params);
308
+ const deletedFilter = "_deleted = 0";
309
+ if (whereClause) {
310
+ parts.push(`WHERE ${deletedFilter} AND ${whereClause}`);
311
+ } else {
312
+ parts.push(`WHERE ${deletedFilter}`);
313
+ }
314
+ return { sql: parts.join(" "), params };
315
+ }
316
+ function buildInsertQuery(collection, record) {
317
+ const columns = Object.keys(record);
318
+ const placeholders = columns.map(() => "?");
319
+ const params = Object.values(record);
320
+ const sql = `INSERT INTO ${collection} (${columns.join(", ")}) VALUES (${placeholders.join(", ")})`;
321
+ return { sql, params };
322
+ }
323
+ function buildUpdateQuery(collection, id, changes) {
324
+ const setClauses = Object.keys(changes).map((col) => `${col} = ?`);
325
+ const params = [...Object.values(changes), id];
326
+ const sql = `UPDATE ${collection} SET ${setClauses.join(", ")} WHERE id = ?`;
327
+ return { sql, params };
328
+ }
329
+ function buildSoftDeleteQuery(collection, id, updatedAt, version) {
330
+ if (version !== void 0) {
331
+ return {
332
+ sql: `UPDATE ${collection} SET _deleted = 1, _updated_at = ?, _version = ? WHERE id = ?`,
333
+ params: [updatedAt, version, id]
334
+ };
335
+ }
336
+ return {
337
+ sql: `UPDATE ${collection} SET _deleted = 1, _updated_at = ? WHERE id = ?`,
338
+ params: [updatedAt, id]
339
+ };
340
+ }
341
+ function buildLwwUpdateQuery(collection, id, changes, remoteVersion) {
342
+ const setClauses = Object.keys(changes).map((col) => `${col} = ?`);
343
+ const lww = lwwVersionWhereClause(remoteVersion);
344
+ const sql = `UPDATE ${collection} SET ${setClauses.join(", ")} WHERE id = ? AND ${lww.sql}`;
345
+ const params = [...Object.values(changes), id, ...lww.params];
346
+ return { sql, params };
347
+ }
348
+ function buildLwwSoftDeleteQuery(collection, id, updatedAt, version) {
349
+ const lww = lwwVersionWhereClause(version);
350
+ return {
351
+ sql: `UPDATE ${collection} SET _deleted = 1, _updated_at = ?, _version = ? WHERE id = ? AND ${lww.sql}`,
352
+ params: [updatedAt, version, id, ...lww.params]
353
+ };
354
+ }
355
+ function buildWhereClause(where, fields) {
356
+ const params = [];
357
+ const result = buildWhereClauseParts(where, fields, params);
358
+ if (!result) return null;
359
+ return { sql: result, params };
360
+ }
361
+ var VALID_OPERATORS = /* @__PURE__ */ new Set(["$eq", "$ne", "$gt", "$gte", "$lt", "$lte", "$in"]);
362
+ function buildWhereClauseParts(where, fields, params) {
363
+ const conditions = [];
364
+ for (const [fieldName, value] of Object.entries(where)) {
365
+ validateFieldName(fieldName, fields);
366
+ const descriptor = fields[fieldName];
367
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
368
+ const ops = value;
369
+ for (const [op, opValue] of Object.entries(ops)) {
370
+ if (!VALID_OPERATORS.has(op)) {
371
+ throw new QueryError(`Unknown operator "${op}" on field "${fieldName}"`, {
372
+ field: fieldName,
373
+ operator: op,
374
+ validOperators: [...VALID_OPERATORS]
375
+ });
376
+ }
377
+ conditions.push(buildOperatorCondition(fieldName, op, opValue, descriptor, params));
378
+ }
379
+ } else {
380
+ conditions.push(buildOperatorCondition(fieldName, "$eq", value, descriptor, params));
381
+ }
382
+ }
383
+ if (conditions.length === 0) return null;
384
+ return conditions.join(" AND ");
385
+ }
386
+ function buildOperatorCondition(fieldName, operator, value, descriptor, params) {
387
+ const sqlValue = descriptor?.kind === "boolean" && typeof value === "boolean" ? value ? 1 : 0 : value;
388
+ switch (operator) {
389
+ case "$eq":
390
+ if (sqlValue === null) {
391
+ return `${fieldName} IS NULL`;
392
+ }
393
+ params.push(sqlValue);
394
+ return `${fieldName} = ?`;
395
+ case "$ne":
396
+ if (sqlValue === null) {
397
+ return `${fieldName} IS NOT NULL`;
398
+ }
399
+ params.push(sqlValue);
400
+ return `${fieldName} != ?`;
401
+ case "$gt":
402
+ params.push(sqlValue);
403
+ return `${fieldName} > ?`;
404
+ case "$gte":
405
+ params.push(sqlValue);
406
+ return `${fieldName} >= ?`;
407
+ case "$lt":
408
+ params.push(sqlValue);
409
+ return `${fieldName} < ?`;
410
+ case "$lte":
411
+ params.push(sqlValue);
412
+ return `${fieldName} <= ?`;
413
+ case "$in": {
414
+ if (!Array.isArray(sqlValue)) {
415
+ throw new QueryError(`$in operator requires an array value for field "${fieldName}"`, {
416
+ field: fieldName,
417
+ received: typeof sqlValue
418
+ });
419
+ }
420
+ const placeholders = sqlValue.map(() => "?");
421
+ for (const item of sqlValue) {
422
+ params.push(
423
+ descriptor?.kind === "boolean" && typeof item === "boolean" ? item ? 1 : 0 : item
424
+ );
425
+ }
426
+ return `${fieldName} IN (${placeholders.join(", ")})`;
427
+ }
428
+ default:
429
+ throw new QueryError(`Unknown operator "${operator}"`, { operator });
430
+ }
431
+ }
432
+ function validateFieldName(fieldName, fields) {
433
+ const allowedFields = /* @__PURE__ */ new Set([
434
+ ...Object.keys(fields),
435
+ "id",
436
+ "createdAt",
437
+ "updatedAt",
438
+ "_created_at",
439
+ "_updated_at"
440
+ ]);
441
+ if (!allowedFields.has(fieldName)) {
442
+ throw new QueryError(
443
+ `Unknown field "${fieldName}" in query. Available fields: ${[...allowedFields].join(", ")}`,
444
+ { field: fieldName }
445
+ );
446
+ }
447
+ }
448
+
449
+ // src/mutations/execute-insert.ts
450
+ var import_core4 = require("@korajs/core");
451
+
452
+ // src/store/sequence-allocator.ts
453
+ async function allocateNextSequenceInTransaction(tx, nodeId) {
454
+ const rows = await tx.query(
455
+ `INSERT INTO _kora_version_vector (node_id, sequence_number) VALUES (?, 1)
456
+ ON CONFLICT(node_id) DO UPDATE SET sequence_number = sequence_number + 1
457
+ RETURNING sequence_number`,
458
+ [nodeId]
459
+ );
460
+ const seq = rows[0]?.sequence_number;
461
+ if (seq === void 0) {
462
+ throw new Error(`Failed to allocate sequence number for node "${nodeId}" in transaction`);
463
+ }
464
+ return seq;
465
+ }
466
+
467
+ // src/mutations/resolve-causal-deps.ts
468
+ function resolveCausalDeps(ctx) {
469
+ const merged = [];
470
+ for (const id of ctx.extraCausalDeps ?? []) {
471
+ if (!merged.includes(id)) {
472
+ merged.push(id);
473
+ }
474
+ }
475
+ for (const id of ctx.causalTracker?.nextCausalDeps(ctx.collection, ctx.inTransaction) ?? []) {
476
+ if (!merged.includes(id)) {
477
+ merged.push(id);
478
+ }
479
+ }
480
+ return merged;
481
+ }
482
+
483
+ // src/mutations/execute-insert.ts
484
+ async function executeInsert(ctx, data) {
485
+ const validated = (0, import_core4.validateRecord)(ctx.collection, ctx.definition, data, "insert");
486
+ const recordId = (0, import_core4.generateUUIDv7)();
487
+ for (const [fieldName, descriptor] of Object.entries(ctx.definition.fields)) {
488
+ if (descriptor.auto && descriptor.kind === "timestamp") {
489
+ validated[fieldName] = Date.now();
490
+ }
491
+ }
492
+ const causalDeps = resolveCausalDeps(ctx);
493
+ let operation;
494
+ let record;
495
+ await ctx.adapter.transaction(async (tx) => {
496
+ const sequenceNumber = await allocateNextSequenceInTransaction(tx, ctx.nodeId);
497
+ operation = await (0, import_core4.createOperation)(
498
+ {
499
+ nodeId: ctx.nodeId,
500
+ type: "insert",
501
+ collection: ctx.collection,
502
+ recordId,
503
+ data: { ...validated },
504
+ previousData: null,
505
+ sequenceNumber,
506
+ causalDeps,
507
+ schemaVersion: ctx.schema.version
508
+ },
509
+ ctx.clock
510
+ );
511
+ ctx.causalTracker?.afterOperation(ctx.collection, operation.id, ctx.inTransaction);
512
+ const serializedData = serializeRecord(validated, ctx.definition.fields);
513
+ const version = serializeRowVersion(operation.timestamp);
514
+ record = {
515
+ id: recordId,
516
+ ...serializedData,
517
+ _created_at: operation.timestamp.wallTime,
518
+ _updated_at: operation.timestamp.wallTime,
519
+ _version: version
520
+ };
521
+ const builtInsert = buildInsertQuery(ctx.collection, record);
522
+ const opInsert = buildInsertQuery(
523
+ `_kora_ops_${ctx.collection}`,
524
+ serializeOperation(operation)
525
+ );
526
+ await tx.execute(builtInsert.sql, builtInsert.params);
527
+ await tx.execute(opInsert.sql, opInsert.params);
528
+ });
529
+ ctx.onMutation(ctx.collection, operation);
530
+ return {
531
+ id: recordId,
532
+ ...validated,
533
+ createdAt: operation.timestamp.wallTime,
534
+ updatedAt: operation.timestamp.wallTime
535
+ };
536
+ }
537
+
538
+ // src/mutations/execute-update.ts
539
+ var import_core6 = require("@korajs/core");
540
+
541
+ // src/state-machine/state-validator.ts
542
+ var import_core5 = require("@korajs/core");
543
+ var InvalidStateTransitionError = class extends import_core5.KoraError {
544
+ constructor(collection, recordId, field, fromState, toState, allowedStates) {
545
+ super(
546
+ `Invalid state transition in collection "${collection}": cannot transition field "${field}" from "${fromState}" to "${toState}". Allowed transitions from "${fromState}": ${allowedStates.length > 0 ? allowedStates.join(", ") : "(none -- terminal state)"}`,
547
+ "INVALID_STATE_TRANSITION",
548
+ { collection, recordId, field, fromState, toState, allowedStates }
549
+ );
550
+ this.collection = collection;
551
+ this.recordId = recordId;
552
+ this.field = field;
553
+ this.fromState = fromState;
554
+ this.toState = toState;
555
+ this.allowedStates = allowedStates;
556
+ this.name = "InvalidStateTransitionError";
557
+ }
558
+ collection;
559
+ recordId;
560
+ field;
561
+ fromState;
562
+ toState;
563
+ allowedStates;
564
+ };
565
+ function validateStateTransition(collectionName, recordId, stateMachine, currentState, newState) {
566
+ if (currentState === null) {
567
+ return { valid: true, allowedStates: [] };
568
+ }
569
+ if (currentState === newState) {
570
+ return { valid: true, allowedStates: stateMachine.transitions[currentState] ?? [] };
571
+ }
572
+ const constraint = {
573
+ field: stateMachine.field,
574
+ collection: collectionName,
575
+ transitions: stateMachine.transitions
576
+ };
577
+ const transitionResult = (0, import_core5.validateTransition)(constraint, currentState, newState);
578
+ if (transitionResult.valid) {
579
+ return { valid: true, allowedStates: transitionResult.allowedTargets };
580
+ }
581
+ const allowedStates = transitionResult.allowedTargets;
582
+ if (stateMachine.onInvalidTransition === "reject") {
583
+ throw new InvalidStateTransitionError(
584
+ collectionName,
585
+ recordId,
586
+ stateMachine.field,
587
+ currentState,
588
+ newState,
589
+ allowedStates
590
+ );
591
+ }
592
+ return { valid: false, allowedStates };
593
+ }
594
+ function validateUpdateStateMachine(collectionName, recordId, collectionDef, currentRecord, updateData) {
595
+ const stateMachine = collectionDef.stateMachine;
596
+ if (stateMachine === void 0) {
597
+ return updateData;
598
+ }
599
+ const stateField = stateMachine.field;
600
+ if (!(stateField in updateData)) {
601
+ return updateData;
602
+ }
603
+ const currentState = currentRecord[stateField];
604
+ const newState = updateData[stateField];
605
+ if (typeof currentState !== "string" || typeof newState !== "string") {
606
+ return updateData;
607
+ }
608
+ const result = validateStateTransition(
609
+ collectionName,
610
+ recordId,
611
+ stateMachine,
612
+ currentState,
613
+ newState
614
+ );
615
+ if (result.valid) {
616
+ return updateData;
617
+ }
618
+ const filtered = { ...updateData };
619
+ delete filtered[stateField];
620
+ return filtered;
621
+ }
622
+
623
+ // src/mutations/execute-update.ts
624
+ async function executeUpdate(ctx, id, data) {
625
+ const currentRows = await ctx.adapter.query(
626
+ `SELECT * FROM ${ctx.collection} WHERE id = ? AND _deleted = 0`,
627
+ [id]
628
+ );
629
+ const currentRow = currentRows[0];
630
+ if (!currentRow) {
631
+ throw new RecordNotFoundError(ctx.collection, id);
632
+ }
633
+ let validated = (0, import_core6.validateRecord)(ctx.collection, ctx.definition, data, "update");
634
+ const currentRecord = deserializeRecord(currentRow, ctx.definition.fields);
635
+ validated = validateUpdateStateMachine(
636
+ ctx.collection,
637
+ id,
638
+ ctx.definition,
639
+ currentRecord,
640
+ validated
641
+ );
642
+ if (Object.keys(validated).length === 0) {
643
+ return { ...currentRecord };
644
+ }
645
+ const previousData = {};
646
+ const resolvedData = {};
647
+ const atomicOps = {};
648
+ for (const key of Object.keys(validated)) {
649
+ const value = validated[key];
650
+ previousData[key] = currentRecord[key];
651
+ if ((0, import_core6.isAtomicOp)(value)) {
652
+ resolvedData[key] = (0, import_core6.resolveAtomicOp)(currentRecord[key], value);
653
+ atomicOps[key] = (0, import_core6.toAtomicOp)(value);
654
+ } else {
655
+ resolvedData[key] = value;
656
+ }
657
+ }
658
+ const hasAtomicOps = Object.keys(atomicOps).length > 0;
659
+ const causalDeps = resolveCausalDeps(ctx);
660
+ let operation;
661
+ await ctx.adapter.transaction(async (tx) => {
662
+ const sequenceNumber = await allocateNextSequenceInTransaction(tx, ctx.nodeId);
663
+ operation = await (0, import_core6.createOperation)(
664
+ {
665
+ nodeId: ctx.nodeId,
666
+ type: "update",
667
+ collection: ctx.collection,
668
+ recordId: id,
669
+ data: { ...resolvedData },
670
+ previousData,
671
+ sequenceNumber,
672
+ causalDeps,
673
+ schemaVersion: ctx.schema.version,
674
+ ...hasAtomicOps ? { atomicOps } : {}
675
+ },
676
+ ctx.clock
677
+ );
678
+ ctx.causalTracker?.afterOperation(ctx.collection, operation.id, ctx.inTransaction);
679
+ const serializedChanges = serializeRecord(resolvedData, ctx.definition.fields);
680
+ const version = serializeRowVersion(operation.timestamp);
681
+ const updateQuery = buildUpdateQuery(ctx.collection, id, {
682
+ ...serializedChanges,
683
+ _updated_at: operation.timestamp.wallTime,
684
+ _version: version
685
+ });
686
+ const opInsert = buildInsertQuery(
687
+ `_kora_ops_${ctx.collection}`,
688
+ serializeOperation(operation)
689
+ );
690
+ await tx.execute(updateQuery.sql, updateQuery.params);
691
+ await tx.execute(opInsert.sql, opInsert.params);
692
+ });
693
+ ctx.onMutation(ctx.collection, operation);
694
+ const rows = await ctx.adapter.query(
695
+ `SELECT * FROM ${ctx.collection} WHERE id = ? AND _deleted = 0`,
696
+ [id]
697
+ );
698
+ const row = rows[0];
699
+ if (!row) {
700
+ throw new RecordNotFoundError(ctx.collection, id);
701
+ }
702
+ return deserializeRecord(row, ctx.definition.fields);
703
+ }
704
+
705
+ // src/mutations/execute-delete.ts
706
+ var import_core7 = require("@korajs/core");
707
+ async function executeDelete(ctx, id, options) {
708
+ const currentRows = await ctx.adapter.query(
709
+ `SELECT * FROM ${ctx.collection} WHERE id = ? AND _deleted = 0`,
710
+ [id]
711
+ );
712
+ if (!currentRows[0]) {
713
+ throw new RecordNotFoundError(ctx.collection, id);
714
+ }
715
+ const causalDeps = resolveCausalDeps(ctx);
716
+ let operation = options?.operation;
717
+ const cascadedOps = [];
718
+ await ctx.adapter.transaction(async (tx) => {
719
+ if (!operation) {
720
+ const sequenceNumber = await allocateNextSequenceInTransaction(tx, ctx.nodeId);
721
+ operation = await (0, import_core7.createOperation)(
722
+ {
723
+ nodeId: ctx.nodeId,
724
+ type: "delete",
725
+ collection: ctx.collection,
726
+ recordId: id,
727
+ data: null,
728
+ previousData: null,
729
+ sequenceNumber,
730
+ causalDeps,
731
+ schemaVersion: ctx.schema.version
732
+ },
733
+ ctx.clock
734
+ );
735
+ ctx.causalTracker?.afterOperation(ctx.collection, operation.id, ctx.inTransaction);
736
+ }
737
+ if (ctx.relationEnforcer && !options?.skipReferentialEnforcement) {
738
+ const enforcementResult = await ctx.relationEnforcer.enforceDelete(ctx.collection, id, tx, [
739
+ operation.id
740
+ ]);
741
+ cascadedOps.push(...enforcementResult.operations);
742
+ }
743
+ const version = serializeRowVersion(operation.timestamp);
744
+ const deleteQuery = buildSoftDeleteQuery(
745
+ ctx.collection,
746
+ id,
747
+ operation.timestamp.wallTime,
748
+ version
749
+ );
750
+ const opInsert = buildInsertQuery(
751
+ `_kora_ops_${ctx.collection}`,
752
+ serializeOperation(operation)
753
+ );
754
+ await tx.execute(deleteQuery.sql, deleteQuery.params);
755
+ await tx.execute(opInsert.sql, opInsert.params);
756
+ });
757
+ if (!operation) {
758
+ throw new Error("Delete operation was not created");
759
+ }
760
+ ctx.onMutation(ctx.collection, operation);
761
+ for (const cascadedOp of cascadedOps) {
762
+ ctx.onMutation(cascadedOp.collection, cascadedOp);
763
+ }
764
+ return [operation, ...cascadedOps];
765
+ }
766
+ // Annotate the CommonJS export names for ESM import in node:
767
+ 0 && (module.exports = {
768
+ buildCountQuery,
769
+ buildInsertQuery,
770
+ buildLwwSoftDeleteQuery,
771
+ buildLwwUpdateQuery,
772
+ buildSelectQuery,
773
+ buildSoftDeleteQuery,
774
+ buildUpdateQuery,
775
+ buildWhereClause,
776
+ deserializeOperation,
777
+ deserializeOperationWithCollection,
778
+ deserializeRecord,
779
+ executeDelete,
780
+ executeInsert,
781
+ executeUpdate,
782
+ isIncomingNewerThanRow,
783
+ resolveCausalDeps,
784
+ rowVersionFromRecord,
785
+ serializeOperation,
786
+ serializeRecord,
787
+ serializeRowVersion
788
+ });
789
+ //# sourceMappingURL=internal.cjs.map