@korajs/core 1.0.0-beta.0 → 1.0.0-beta.10
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.
- package/dist/bindings/index.d.cts +6 -3
- package/dist/bindings/index.d.ts +6 -3
- package/dist/{build-scope-map-BIeawJzC.d.cts → build-scope-map-B8dpCt6P.d.cts} +1 -1
- package/dist/{build-scope-map-DOf4JLTh.d.ts → build-scope-map-CDpIGOsf.d.ts} +1 -1
- package/dist/{events-BynBOsO3.d.cts → events-3z0Tema3.d.cts} +169 -3
- package/dist/{events-BynBOsO3.d.ts → events-3z0Tema3.d.ts} +169 -3
- package/dist/index.cjs +648 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +222 -7
- package/dist/index.d.ts +222 -7
- package/dist/index.js +633 -29
- package/dist/index.js.map +1 -1
- package/dist/internal.d.cts +2 -2
- package/dist/internal.d.ts +2 -2
- package/dist/{operation-BpZlYSpe.d.cts → operation-BvBgl-4c.d.cts} +1 -1
- package/dist/{operation-D5WOZYvy.d.ts → operation-ClhEbhoV.d.ts} +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -109,6 +109,226 @@ function formatUUID(bytes) {
|
|
|
109
109
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
// src/blob/blob-ref.ts
|
|
113
|
+
async function hashBlob(bytes) {
|
|
114
|
+
const view = new Uint8Array(bytes.byteLength);
|
|
115
|
+
view.set(bytes);
|
|
116
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", view);
|
|
117
|
+
const out = new Uint8Array(digest);
|
|
118
|
+
return Array.from(out, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
119
|
+
}
|
|
120
|
+
async function createBlobRef(bytes, metadata = {}) {
|
|
121
|
+
const hash = await hashBlob(bytes);
|
|
122
|
+
const ref = { hash, size: bytes.byteLength };
|
|
123
|
+
if (metadata.mimeType !== void 0) {
|
|
124
|
+
ref.mimeType = metadata.mimeType;
|
|
125
|
+
}
|
|
126
|
+
if (metadata.filename !== void 0) {
|
|
127
|
+
ref.filename = metadata.filename;
|
|
128
|
+
}
|
|
129
|
+
return ref;
|
|
130
|
+
}
|
|
131
|
+
function isBlobRef(value) {
|
|
132
|
+
if (typeof value !== "object" || value === null) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
const ref = value;
|
|
136
|
+
if (typeof ref.hash !== "string" || !/^[0-9a-f]{64}$/.test(ref.hash)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
if (typeof ref.size !== "number" || !Number.isInteger(ref.size) || ref.size < 0) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
if (ref.mimeType !== void 0 && typeof ref.mimeType !== "string") {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
if (ref.filename !== void 0 && typeof ref.filename !== "string") {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if (ref.manifestHash !== void 0 && (typeof ref.manifestHash !== "string" || !/^[0-9a-f]{64}$/.test(ref.manifestHash))) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/secret/secret-crypto.ts
|
|
155
|
+
var PBKDF2_ITERATIONS = 21e4;
|
|
156
|
+
var SALT_BYTES = 16;
|
|
157
|
+
var IV_BYTES = 12;
|
|
158
|
+
var HASH_BYTES = 32;
|
|
159
|
+
function toHex(bytes) {
|
|
160
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
161
|
+
}
|
|
162
|
+
function fromHex(hex) {
|
|
163
|
+
if (hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) {
|
|
164
|
+
throw new Error("Invalid hex string");
|
|
165
|
+
}
|
|
166
|
+
const out = new Uint8Array(hex.length / 2);
|
|
167
|
+
for (let i = 0; i < out.length; i++) {
|
|
168
|
+
out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
169
|
+
}
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
function randomBytes(n) {
|
|
173
|
+
const out = new Uint8Array(n);
|
|
174
|
+
globalThis.crypto.getRandomValues(out);
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
function timingSafeEqual(a, b) {
|
|
178
|
+
if (a.length !== b.length) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
let diff = 0;
|
|
182
|
+
for (let i = 0; i < a.length; i++) {
|
|
183
|
+
diff |= a[i] ^ b[i];
|
|
184
|
+
}
|
|
185
|
+
return diff === 0;
|
|
186
|
+
}
|
|
187
|
+
async function deriveAesKey(secretKey, salt) {
|
|
188
|
+
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
189
|
+
"raw",
|
|
190
|
+
new TextEncoder().encode(secretKey),
|
|
191
|
+
"PBKDF2",
|
|
192
|
+
false,
|
|
193
|
+
["deriveKey"]
|
|
194
|
+
);
|
|
195
|
+
return globalThis.crypto.subtle.deriveKey(
|
|
196
|
+
{ name: "PBKDF2", salt, iterations: PBKDF2_ITERATIONS, hash: "SHA-256" },
|
|
197
|
+
baseKey,
|
|
198
|
+
{ name: "AES-GCM", length: 256 },
|
|
199
|
+
false,
|
|
200
|
+
["encrypt", "decrypt"]
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
async function encryptSecret(plaintext, secretKey) {
|
|
204
|
+
const salt = randomBytes(SALT_BYTES);
|
|
205
|
+
const iv = randomBytes(IV_BYTES);
|
|
206
|
+
const key = await deriveAesKey(secretKey, salt);
|
|
207
|
+
const ciphertext = await globalThis.crypto.subtle.encrypt(
|
|
208
|
+
{ name: "AES-GCM", iv },
|
|
209
|
+
key,
|
|
210
|
+
new TextEncoder().encode(plaintext)
|
|
211
|
+
);
|
|
212
|
+
return `v1.${toHex(salt)}.${toHex(iv)}.${toHex(new Uint8Array(ciphertext))}`;
|
|
213
|
+
}
|
|
214
|
+
async function decryptSecret(encrypted, secretKey) {
|
|
215
|
+
const parts = encrypted.split(".");
|
|
216
|
+
if (parts.length !== 4 || parts[0] !== "v1") {
|
|
217
|
+
throw new Error("Invalid encrypted secret format");
|
|
218
|
+
}
|
|
219
|
+
const salt = fromHex(parts[1]);
|
|
220
|
+
const iv = fromHex(parts[2]);
|
|
221
|
+
const ciphertext = fromHex(parts[3]);
|
|
222
|
+
const key = await deriveAesKey(secretKey, salt);
|
|
223
|
+
const plaintext = await globalThis.crypto.subtle.decrypt(
|
|
224
|
+
{ name: "AES-GCM", iv },
|
|
225
|
+
key,
|
|
226
|
+
ciphertext
|
|
227
|
+
);
|
|
228
|
+
return new TextDecoder().decode(plaintext);
|
|
229
|
+
}
|
|
230
|
+
async function hashSecret(plaintext) {
|
|
231
|
+
const salt = randomBytes(SALT_BYTES);
|
|
232
|
+
const hash = await pbkdf2(plaintext, salt);
|
|
233
|
+
return `v1.${toHex(salt)}.${toHex(hash)}`;
|
|
234
|
+
}
|
|
235
|
+
async function verifySecret(plaintext, stored) {
|
|
236
|
+
const parts = stored.split(".");
|
|
237
|
+
if (parts.length !== 3 || parts[0] !== "v1") {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
let salt;
|
|
241
|
+
let expected;
|
|
242
|
+
try {
|
|
243
|
+
salt = fromHex(parts[1]);
|
|
244
|
+
expected = fromHex(parts[2]);
|
|
245
|
+
} catch {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
const actual = await pbkdf2(plaintext, salt);
|
|
249
|
+
return timingSafeEqual(actual, expected);
|
|
250
|
+
}
|
|
251
|
+
async function pbkdf2(plaintext, salt) {
|
|
252
|
+
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
253
|
+
"raw",
|
|
254
|
+
new TextEncoder().encode(plaintext),
|
|
255
|
+
"PBKDF2",
|
|
256
|
+
false,
|
|
257
|
+
["deriveBits"]
|
|
258
|
+
);
|
|
259
|
+
const bits = await globalThis.crypto.subtle.deriveBits(
|
|
260
|
+
{ name: "PBKDF2", salt, iterations: PBKDF2_ITERATIONS, hash: "SHA-256" },
|
|
261
|
+
baseKey,
|
|
262
|
+
HASH_BYTES * 8
|
|
263
|
+
);
|
|
264
|
+
return new Uint8Array(bits);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/secret/secret-transform.ts
|
|
268
|
+
var MissingSecretKeyError = class extends Error {
|
|
269
|
+
constructor(field) {
|
|
270
|
+
super(
|
|
271
|
+
`Secret field "${field}" is encrypted but no encryption key is configured. Provide sync.encryption.key (or a key provider) to createApp.`
|
|
272
|
+
);
|
|
273
|
+
this.field = field;
|
|
274
|
+
this.name = "MissingSecretKeyError";
|
|
275
|
+
}
|
|
276
|
+
field;
|
|
277
|
+
};
|
|
278
|
+
async function resolveKey(provider) {
|
|
279
|
+
if (!provider) {
|
|
280
|
+
return void 0;
|
|
281
|
+
}
|
|
282
|
+
return provider();
|
|
283
|
+
}
|
|
284
|
+
async function transformSecretFieldsForWrite(data, collectionDef, keyProvider) {
|
|
285
|
+
const hasSecret = Object.values(collectionDef.fields).some((f) => f.kind === "secret");
|
|
286
|
+
if (!hasSecret) {
|
|
287
|
+
return data;
|
|
288
|
+
}
|
|
289
|
+
const result = { ...data };
|
|
290
|
+
let key;
|
|
291
|
+
let keyResolved = false;
|
|
292
|
+
for (const [field, value] of Object.entries(data)) {
|
|
293
|
+
const descriptor = collectionDef.fields[field];
|
|
294
|
+
if (descriptor?.kind !== "secret") {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
if (typeof value !== "string") {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const mode = descriptor.secretMode ?? "encrypted";
|
|
301
|
+
if (mode === "hashed") {
|
|
302
|
+
result[field] = await hashSecret(value);
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (!keyResolved) {
|
|
306
|
+
key = await resolveKey(keyProvider);
|
|
307
|
+
keyResolved = true;
|
|
308
|
+
}
|
|
309
|
+
if (key === void 0) {
|
|
310
|
+
throw new MissingSecretKeyError(field);
|
|
311
|
+
}
|
|
312
|
+
result[field] = await encryptSecret(value, key);
|
|
313
|
+
}
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
async function revealSecret(storedValue, mode, keyProvider, field = "secret") {
|
|
317
|
+
if (mode === "hashed") {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`Secret field "${field}" is hashed (one-way) and cannot be revealed. Use verifySecretValue to check a candidate.`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const key = await resolveKey(keyProvider);
|
|
323
|
+
if (key === void 0) {
|
|
324
|
+
throw new MissingSecretKeyError(field);
|
|
325
|
+
}
|
|
326
|
+
return decryptSecret(storedValue, key);
|
|
327
|
+
}
|
|
328
|
+
async function verifySecretValue(candidate, storedValue) {
|
|
329
|
+
return verifySecret(candidate, storedValue);
|
|
330
|
+
}
|
|
331
|
+
|
|
112
332
|
// src/operations/apply-result.ts
|
|
113
333
|
var APPLY_RESULTS = ["applied", "duplicate", "skipped", "rejected", "deferred"];
|
|
114
334
|
var APPLY_FAILURE_CODES = {
|
|
@@ -145,32 +365,36 @@ var KORA_ATOMIC_OP = /* @__PURE__ */ Symbol.for("kora.atomic_op");
|
|
|
145
365
|
function isAtomicOp(value) {
|
|
146
366
|
return typeof value === "object" && value !== null && KORA_ATOMIC_OP in value && value[KORA_ATOMIC_OP] === true;
|
|
147
367
|
}
|
|
148
|
-
function
|
|
149
|
-
switch (
|
|
368
|
+
function applyAtomicOp(currentValue, atomicOp) {
|
|
369
|
+
switch (atomicOp.type) {
|
|
150
370
|
case "increment": {
|
|
151
371
|
const current = typeof currentValue === "number" ? currentValue : 0;
|
|
152
|
-
const operand =
|
|
372
|
+
const operand = atomicOp.value;
|
|
153
373
|
return current + operand;
|
|
154
374
|
}
|
|
155
375
|
case "max": {
|
|
156
376
|
const current = typeof currentValue === "number" ? currentValue : Number.NEGATIVE_INFINITY;
|
|
157
|
-
return Math.max(current,
|
|
377
|
+
return Math.max(current, atomicOp.value);
|
|
158
378
|
}
|
|
159
379
|
case "min": {
|
|
160
380
|
const current = typeof currentValue === "number" ? currentValue : Number.POSITIVE_INFINITY;
|
|
161
|
-
return Math.min(current,
|
|
381
|
+
return Math.min(current, atomicOp.value);
|
|
162
382
|
}
|
|
163
383
|
case "append": {
|
|
164
384
|
const current = Array.isArray(currentValue) ? [...currentValue] : [];
|
|
165
|
-
current.push(
|
|
385
|
+
current.push(atomicOp.value);
|
|
166
386
|
return current;
|
|
167
387
|
}
|
|
168
388
|
case "remove": {
|
|
169
389
|
if (!Array.isArray(currentValue)) return [];
|
|
170
|
-
|
|
390
|
+
const target = JSON.stringify(atomicOp.value);
|
|
391
|
+
return currentValue.filter((item) => JSON.stringify(item) !== target);
|
|
171
392
|
}
|
|
172
393
|
}
|
|
173
394
|
}
|
|
395
|
+
function resolveAtomicOp(currentValue, sentinel) {
|
|
396
|
+
return applyAtomicOp(currentValue, { type: sentinel.type, value: sentinel.value });
|
|
397
|
+
}
|
|
174
398
|
function createSentinel(type, value) {
|
|
175
399
|
return Object.freeze({
|
|
176
400
|
[KORA_ATOMIC_OP]: true,
|
|
@@ -238,6 +462,46 @@ function toAtomicOp(sentinel) {
|
|
|
238
462
|
return { type: sentinel.type, value: sentinel.value };
|
|
239
463
|
}
|
|
240
464
|
|
|
465
|
+
// src/operations/replay-record.ts
|
|
466
|
+
function replayOperationsForRecord(ops) {
|
|
467
|
+
let record = null;
|
|
468
|
+
let deleted = false;
|
|
469
|
+
const lastWriterKind = {};
|
|
470
|
+
for (const op2 of ops) {
|
|
471
|
+
switch (op2.type) {
|
|
472
|
+
case "insert":
|
|
473
|
+
if (op2.data) {
|
|
474
|
+
record = { ...op2.data };
|
|
475
|
+
deleted = false;
|
|
476
|
+
for (const field of Object.keys(op2.data)) {
|
|
477
|
+
lastWriterKind[field] = "lww";
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
case "update":
|
|
482
|
+
if (op2.data) {
|
|
483
|
+
const next = { ...record ?? {} };
|
|
484
|
+
for (const [field, value] of Object.entries(op2.data)) {
|
|
485
|
+
const atomicOp = op2.atomicOps?.[field];
|
|
486
|
+
if (atomicOp && lastWriterKind[field] === `atomic:${atomicOp.type}`) {
|
|
487
|
+
next[field] = applyAtomicOp(next[field], atomicOp);
|
|
488
|
+
} else {
|
|
489
|
+
next[field] = value;
|
|
490
|
+
}
|
|
491
|
+
lastWriterKind[field] = atomicOp ? `atomic:${atomicOp.type}` : "lww";
|
|
492
|
+
}
|
|
493
|
+
record = next;
|
|
494
|
+
deleted = false;
|
|
495
|
+
}
|
|
496
|
+
break;
|
|
497
|
+
case "delete":
|
|
498
|
+
deleted = true;
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return deleted ? null : record;
|
|
503
|
+
}
|
|
504
|
+
|
|
241
505
|
// src/operations/op-data-binary.ts
|
|
242
506
|
function isKoraBytesValue(value) {
|
|
243
507
|
if (typeof value !== "object" || value === null) {
|
|
@@ -488,7 +752,7 @@ function validateStateMachineDefinition(collectionName, sm, fields) {
|
|
|
488
752
|
}
|
|
489
753
|
|
|
490
754
|
// src/schema/define.ts
|
|
491
|
-
var COLLECTION_NAME_RE = /^[
|
|
755
|
+
var COLLECTION_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
492
756
|
var FIELD_NAME_RE = /^[a-z][a-zA-Z0-9_]*$/;
|
|
493
757
|
var RESERVED_FIELDS = /* @__PURE__ */ new Set(["id", "_created_at", "_updated_at", "_version", "_deleted"]);
|
|
494
758
|
function defineSchema(input) {
|
|
@@ -553,7 +817,7 @@ function validateVersion(version) {
|
|
|
553
817
|
function validateCollectionName(name) {
|
|
554
818
|
if (!COLLECTION_NAME_RE.test(name)) {
|
|
555
819
|
throw new SchemaValidationError(
|
|
556
|
-
`Collection name "${name}" is invalid. Must
|
|
820
|
+
`Collection name "${name}" is invalid. Must start with a letter and contain only letters, numbers, and underscores.`,
|
|
557
821
|
{ collection: name }
|
|
558
822
|
);
|
|
559
823
|
}
|
|
@@ -745,6 +1009,11 @@ function validateRelation(name, relation, collections) {
|
|
|
745
1009
|
}
|
|
746
1010
|
}
|
|
747
1011
|
|
|
1012
|
+
// src/schema/quote-ident.ts
|
|
1013
|
+
function quoteIdent(name) {
|
|
1014
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
748
1017
|
// src/schema/sql-gen.ts
|
|
749
1018
|
function generateSQL(collectionName, collection, relations) {
|
|
750
1019
|
const statements = [];
|
|
@@ -756,7 +1025,7 @@ function generateSQL(collectionName, collection, relations) {
|
|
|
756
1025
|
if (relations) {
|
|
757
1026
|
for (const rel of Object.values(relations)) {
|
|
758
1027
|
if (rel.from === collectionName && rel.field === fieldName) {
|
|
759
|
-
colDef += ` REFERENCES ${rel.to}(id)`;
|
|
1028
|
+
colDef += ` REFERENCES ${quoteIdent(rel.to)}(id)`;
|
|
760
1029
|
fkFields.push(fieldName);
|
|
761
1030
|
break;
|
|
762
1031
|
}
|
|
@@ -769,36 +1038,40 @@ function generateSQL(collectionName, collection, relations) {
|
|
|
769
1038
|
columns.push("_version TEXT NOT NULL DEFAULT ''");
|
|
770
1039
|
columns.push("_field_versions TEXT NOT NULL DEFAULT '{}'");
|
|
771
1040
|
columns.push("_deleted INTEGER NOT NULL DEFAULT 0");
|
|
772
|
-
statements.push(
|
|
1041
|
+
statements.push(
|
|
1042
|
+
`CREATE TABLE IF NOT EXISTS ${quoteIdent(collectionName)} (
|
|
773
1043
|
${columns.join(",\n ")}
|
|
774
|
-
)`
|
|
1044
|
+
)`
|
|
1045
|
+
);
|
|
775
1046
|
for (const [fieldName, descriptor] of Object.entries(collection.fields)) {
|
|
776
1047
|
const colDef = columnDefinition(fieldName, descriptor);
|
|
777
|
-
statements.push(
|
|
778
|
-
|
|
1048
|
+
statements.push(
|
|
1049
|
+
`--kora:safe-alter
|
|
1050
|
+
ALTER TABLE ${quoteIdent(collectionName)} ADD COLUMN ${colDef}`
|
|
1051
|
+
);
|
|
779
1052
|
}
|
|
780
1053
|
statements.push(
|
|
781
1054
|
`--kora:safe-alter
|
|
782
|
-
ALTER TABLE ${collectionName} ADD COLUMN _version TEXT NOT NULL DEFAULT ''`
|
|
1055
|
+
ALTER TABLE ${quoteIdent(collectionName)} ADD COLUMN _version TEXT NOT NULL DEFAULT ''`
|
|
783
1056
|
);
|
|
784
1057
|
statements.push(
|
|
785
1058
|
`--kora:safe-alter
|
|
786
|
-
ALTER TABLE ${collectionName} ADD COLUMN _field_versions TEXT NOT NULL DEFAULT '{}'`
|
|
1059
|
+
ALTER TABLE ${quoteIdent(collectionName)} ADD COLUMN _field_versions TEXT NOT NULL DEFAULT '{}'`
|
|
787
1060
|
);
|
|
788
1061
|
for (const indexField of collection.indexes) {
|
|
789
1062
|
statements.push(
|
|
790
|
-
`CREATE INDEX IF NOT EXISTS idx_${collectionName}_${indexField} ON ${collectionName} (${indexField})`
|
|
1063
|
+
`CREATE INDEX IF NOT EXISTS idx_${collectionName}_${indexField} ON ${quoteIdent(collectionName)} (${quoteIdent(indexField)})`
|
|
791
1064
|
);
|
|
792
1065
|
}
|
|
793
1066
|
for (const fkField of fkFields) {
|
|
794
1067
|
if (!indexedFields.has(fkField)) {
|
|
795
1068
|
statements.push(
|
|
796
|
-
`CREATE INDEX IF NOT EXISTS idx_${collectionName}_${fkField} ON ${collectionName} (${fkField})`
|
|
1069
|
+
`CREATE INDEX IF NOT EXISTS idx_${collectionName}_${fkField} ON ${quoteIdent(collectionName)} (${quoteIdent(fkField)})`
|
|
797
1070
|
);
|
|
798
1071
|
}
|
|
799
1072
|
}
|
|
800
1073
|
statements.push(
|
|
801
|
-
`CREATE TABLE IF NOT EXISTS _kora_ops_${collectionName} (
|
|
1074
|
+
`CREATE TABLE IF NOT EXISTS ${quoteIdent(`_kora_ops_${collectionName}`)} (
|
|
802
1075
|
id TEXT PRIMARY KEY NOT NULL,
|
|
803
1076
|
node_id TEXT NOT NULL,
|
|
804
1077
|
type TEXT NOT NULL,
|
|
@@ -812,7 +1085,7 @@ ALTER TABLE ${collectionName} ADD COLUMN _field_versions TEXT NOT NULL DEFAULT '
|
|
|
812
1085
|
)`
|
|
813
1086
|
);
|
|
814
1087
|
statements.push(
|
|
815
|
-
`CREATE INDEX IF NOT EXISTS idx_kora_ops_${collectionName}_record_id ON _kora_ops_${collectionName} (record_id)`
|
|
1088
|
+
`CREATE INDEX IF NOT EXISTS idx_kora_ops_${collectionName}_record_id ON ${quoteIdent(`_kora_ops_${collectionName}`)} (record_id)`
|
|
816
1089
|
);
|
|
817
1090
|
return statements;
|
|
818
1091
|
}
|
|
@@ -830,6 +1103,9 @@ function generateFullDDL(schema) {
|
|
|
830
1103
|
statements.push(
|
|
831
1104
|
"CREATE TABLE IF NOT EXISTS _kora_sync_queue (\n id TEXT PRIMARY KEY NOT NULL,\n payload TEXT NOT NULL\n)"
|
|
832
1105
|
);
|
|
1106
|
+
statements.push(
|
|
1107
|
+
"CREATE TABLE IF NOT EXISTS _kora_sync_rejected (\n operation_id TEXT PRIMARY KEY NOT NULL,\n collection TEXT NOT NULL,\n record_id TEXT NOT NULL,\n code TEXT NOT NULL,\n message TEXT NOT NULL,\n retriable INTEGER NOT NULL,\n rejected_at INTEGER NOT NULL\n)"
|
|
1108
|
+
);
|
|
833
1109
|
statements.push(
|
|
834
1110
|
"CREATE TABLE IF NOT EXISTS _kora_audit_traces (\n id TEXT PRIMARY KEY NOT NULL,\n recorded_at INTEGER NOT NULL,\n event_type TEXT NOT NULL,\n collection TEXT NOT NULL,\n record_id TEXT NOT NULL,\n field TEXT NOT NULL,\n strategy TEXT NOT NULL,\n tier INTEGER NOT NULL,\n constraint_name TEXT,\n trace_json TEXT NOT NULL\n)"
|
|
835
1111
|
);
|
|
@@ -846,7 +1122,7 @@ function generateFullDDL(schema) {
|
|
|
846
1122
|
}
|
|
847
1123
|
function columnDefinition(fieldName, descriptor) {
|
|
848
1124
|
const sqlType = mapFieldType(descriptor);
|
|
849
|
-
const parts = [fieldName, sqlType];
|
|
1125
|
+
const parts = [quoteIdent(fieldName), sqlType];
|
|
850
1126
|
if (descriptor.required && descriptor.defaultValue === void 0 && !descriptor.auto) {
|
|
851
1127
|
parts.push("NOT NULL");
|
|
852
1128
|
}
|
|
@@ -855,7 +1131,7 @@ function columnDefinition(fieldName, descriptor) {
|
|
|
855
1131
|
}
|
|
856
1132
|
if (descriptor.kind === "enum" && descriptor.enumValues) {
|
|
857
1133
|
const values = descriptor.enumValues.map((v) => `'${v}'`).join(", ");
|
|
858
|
-
parts.push(`CHECK (${fieldName} IN (${values}))`);
|
|
1134
|
+
parts.push(`CHECK (${quoteIdent(fieldName)} IN (${values}))`);
|
|
859
1135
|
}
|
|
860
1136
|
return parts.join(" ");
|
|
861
1137
|
}
|
|
@@ -874,6 +1150,17 @@ function mapFieldType(descriptor) {
|
|
|
874
1150
|
case "array":
|
|
875
1151
|
return "TEXT";
|
|
876
1152
|
// JSON-serialized
|
|
1153
|
+
case "object":
|
|
1154
|
+
return "TEXT";
|
|
1155
|
+
// JSON-serialized structured object
|
|
1156
|
+
case "json":
|
|
1157
|
+
return "TEXT";
|
|
1158
|
+
// JSON-serialized dynamic-key value
|
|
1159
|
+
case "blob":
|
|
1160
|
+
return "TEXT";
|
|
1161
|
+
// JSON-serialized content-addressed BlobRef (bytes stored out of band)
|
|
1162
|
+
case "secret":
|
|
1163
|
+
return "TEXT";
|
|
877
1164
|
case "richtext":
|
|
878
1165
|
return "BLOB";
|
|
879
1166
|
}
|
|
@@ -1107,6 +1394,160 @@ var ArrayFieldBuilder = class _ArrayFieldBuilder extends FieldBuilder {
|
|
|
1107
1394
|
};
|
|
1108
1395
|
}
|
|
1109
1396
|
};
|
|
1397
|
+
var ObjectFieldBuilder = class _ObjectFieldBuilder extends FieldBuilder {
|
|
1398
|
+
_fields;
|
|
1399
|
+
constructor(fields, required = true, defaultValue = void 0, auto = false, mergeStrategy = null) {
|
|
1400
|
+
super("object", required, defaultValue, auto, mergeStrategy);
|
|
1401
|
+
this._fields = fields;
|
|
1402
|
+
}
|
|
1403
|
+
optional() {
|
|
1404
|
+
return new _ObjectFieldBuilder(
|
|
1405
|
+
this._fields,
|
|
1406
|
+
false,
|
|
1407
|
+
this._defaultValue,
|
|
1408
|
+
this._auto,
|
|
1409
|
+
this._mergeStrategy
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
default(value) {
|
|
1413
|
+
return new _ObjectFieldBuilder(this._fields, false, value, this._auto, this._mergeStrategy);
|
|
1414
|
+
}
|
|
1415
|
+
auto() {
|
|
1416
|
+
return new _ObjectFieldBuilder(this._fields, false, void 0, true, this._mergeStrategy);
|
|
1417
|
+
}
|
|
1418
|
+
merge(strategy) {
|
|
1419
|
+
return new _ObjectFieldBuilder(
|
|
1420
|
+
this._fields,
|
|
1421
|
+
this._required,
|
|
1422
|
+
this._defaultValue,
|
|
1423
|
+
this._auto,
|
|
1424
|
+
strategy
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
_build() {
|
|
1428
|
+
const nestedFields = {};
|
|
1429
|
+
for (const [key, builder] of Object.entries(this._fields)) {
|
|
1430
|
+
nestedFields[key] = builder._build();
|
|
1431
|
+
}
|
|
1432
|
+
return {
|
|
1433
|
+
kind: "object",
|
|
1434
|
+
required: this._required,
|
|
1435
|
+
defaultValue: this._defaultValue,
|
|
1436
|
+
auto: this._auto,
|
|
1437
|
+
enumValues: null,
|
|
1438
|
+
itemKind: null,
|
|
1439
|
+
mergeStrategy: this._mergeStrategy,
|
|
1440
|
+
transitions: null,
|
|
1441
|
+
nestedFields
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
};
|
|
1445
|
+
var JsonFieldBuilder = class _JsonFieldBuilder extends FieldBuilder {
|
|
1446
|
+
optional() {
|
|
1447
|
+
return new _JsonFieldBuilder(
|
|
1448
|
+
"json",
|
|
1449
|
+
false,
|
|
1450
|
+
this._defaultValue,
|
|
1451
|
+
this._auto,
|
|
1452
|
+
this._mergeStrategy
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1455
|
+
default(value) {
|
|
1456
|
+
return new _JsonFieldBuilder(
|
|
1457
|
+
"json",
|
|
1458
|
+
false,
|
|
1459
|
+
value,
|
|
1460
|
+
this._auto,
|
|
1461
|
+
this._mergeStrategy
|
|
1462
|
+
);
|
|
1463
|
+
}
|
|
1464
|
+
auto() {
|
|
1465
|
+
return new _JsonFieldBuilder("json", false, void 0, true, this._mergeStrategy);
|
|
1466
|
+
}
|
|
1467
|
+
merge(strategy) {
|
|
1468
|
+
return new _JsonFieldBuilder(
|
|
1469
|
+
"json",
|
|
1470
|
+
this._required,
|
|
1471
|
+
this._defaultValue,
|
|
1472
|
+
this._auto,
|
|
1473
|
+
strategy
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1476
|
+
_build() {
|
|
1477
|
+
return {
|
|
1478
|
+
kind: "json",
|
|
1479
|
+
required: this._required,
|
|
1480
|
+
defaultValue: this._defaultValue,
|
|
1481
|
+
auto: this._auto,
|
|
1482
|
+
enumValues: null,
|
|
1483
|
+
itemKind: null,
|
|
1484
|
+
mergeStrategy: this._mergeStrategy,
|
|
1485
|
+
transitions: null,
|
|
1486
|
+
nestedFields: null
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
var SecretFieldBuilder = class _SecretFieldBuilder extends FieldBuilder {
|
|
1491
|
+
_secretMode;
|
|
1492
|
+
constructor(secretMode = "encrypted", required = true, defaultValue = void 0, auto = false, mergeStrategy = null) {
|
|
1493
|
+
super("secret", required, defaultValue, auto, mergeStrategy);
|
|
1494
|
+
this._secretMode = secretMode;
|
|
1495
|
+
}
|
|
1496
|
+
/** Store this secret as a one-way salted hash (passwords: verify, never read back). */
|
|
1497
|
+
hashed() {
|
|
1498
|
+
return new _SecretFieldBuilder(
|
|
1499
|
+
"hashed",
|
|
1500
|
+
this._required,
|
|
1501
|
+
this._defaultValue,
|
|
1502
|
+
this._auto,
|
|
1503
|
+
this._mergeStrategy
|
|
1504
|
+
);
|
|
1505
|
+
}
|
|
1506
|
+
/** Store this secret as reversible ciphertext (tokens/keys: decrypt to use). */
|
|
1507
|
+
encrypted() {
|
|
1508
|
+
return new _SecretFieldBuilder(
|
|
1509
|
+
"encrypted",
|
|
1510
|
+
this._required,
|
|
1511
|
+
this._defaultValue,
|
|
1512
|
+
this._auto,
|
|
1513
|
+
this._mergeStrategy
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
optional() {
|
|
1517
|
+
return new _SecretFieldBuilder(
|
|
1518
|
+
this._secretMode,
|
|
1519
|
+
false,
|
|
1520
|
+
this._defaultValue,
|
|
1521
|
+
this._auto,
|
|
1522
|
+
this._mergeStrategy
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
auto() {
|
|
1526
|
+
return new _SecretFieldBuilder(this._secretMode, false, void 0, true, this._mergeStrategy);
|
|
1527
|
+
}
|
|
1528
|
+
merge(strategy) {
|
|
1529
|
+
return new _SecretFieldBuilder(
|
|
1530
|
+
this._secretMode,
|
|
1531
|
+
this._required,
|
|
1532
|
+
this._defaultValue,
|
|
1533
|
+
this._auto,
|
|
1534
|
+
strategy
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
_build() {
|
|
1538
|
+
return {
|
|
1539
|
+
kind: "secret",
|
|
1540
|
+
required: this._required,
|
|
1541
|
+
defaultValue: this._defaultValue,
|
|
1542
|
+
auto: this._auto,
|
|
1543
|
+
enumValues: null,
|
|
1544
|
+
itemKind: null,
|
|
1545
|
+
mergeStrategy: this._mergeStrategy,
|
|
1546
|
+
transitions: null,
|
|
1547
|
+
secretMode: this._secretMode
|
|
1548
|
+
};
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1110
1551
|
var t = {
|
|
1111
1552
|
string() {
|
|
1112
1553
|
return new FieldBuilder("string", true, void 0, false);
|
|
@@ -1128,6 +1569,18 @@ var t = {
|
|
|
1128
1569
|
},
|
|
1129
1570
|
array(itemBuilder) {
|
|
1130
1571
|
return new ArrayFieldBuilder(itemBuilder, true, void 0, false);
|
|
1572
|
+
},
|
|
1573
|
+
object(fields) {
|
|
1574
|
+
return new ObjectFieldBuilder(fields, true, void 0, false);
|
|
1575
|
+
},
|
|
1576
|
+
json() {
|
|
1577
|
+
return new JsonFieldBuilder("json", true, void 0, false);
|
|
1578
|
+
},
|
|
1579
|
+
blob() {
|
|
1580
|
+
return new FieldBuilder("blob", true, void 0, false);
|
|
1581
|
+
},
|
|
1582
|
+
secret() {
|
|
1583
|
+
return new SecretFieldBuilder("encrypted", true, void 0, false);
|
|
1131
1584
|
}
|
|
1132
1585
|
};
|
|
1133
1586
|
|
|
@@ -1292,8 +1745,126 @@ function validateFieldValue(collection, fieldName, descriptor, value) {
|
|
|
1292
1745
|
}
|
|
1293
1746
|
break;
|
|
1294
1747
|
}
|
|
1748
|
+
case "object": {
|
|
1749
|
+
if (!isPlainObject(value)) {
|
|
1750
|
+
throw new SchemaValidationError(
|
|
1751
|
+
`Field "${fieldName}" in collection "${collection}" must be a plain object, got ${describeType(value)}`,
|
|
1752
|
+
{
|
|
1753
|
+
collection,
|
|
1754
|
+
field: fieldName,
|
|
1755
|
+
expectedType: "object",
|
|
1756
|
+
receivedType: describeType(value)
|
|
1757
|
+
}
|
|
1758
|
+
);
|
|
1759
|
+
}
|
|
1760
|
+
if (descriptor.nestedFields) {
|
|
1761
|
+
for (const [nestedName, nestedDescriptor] of Object.entries(descriptor.nestedFields)) {
|
|
1762
|
+
const nestedValue = value[nestedName];
|
|
1763
|
+
if (nestedValue !== void 0 && nestedValue !== null) {
|
|
1764
|
+
validateFieldValue(
|
|
1765
|
+
collection,
|
|
1766
|
+
`${fieldName}.${nestedName}`,
|
|
1767
|
+
nestedDescriptor,
|
|
1768
|
+
nestedValue
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
break;
|
|
1774
|
+
}
|
|
1775
|
+
case "json": {
|
|
1776
|
+
const nonJson = findNonJsonSerializable(value, fieldName);
|
|
1777
|
+
if (nonJson) {
|
|
1778
|
+
throw new SchemaValidationError(
|
|
1779
|
+
`Field "${fieldName}" in collection "${collection}" must be JSON-serializable; found ${nonJson.receivedType} at "${nonJson.path}"`,
|
|
1780
|
+
{
|
|
1781
|
+
collection,
|
|
1782
|
+
field: fieldName,
|
|
1783
|
+
expectedType: "json",
|
|
1784
|
+
receivedType: nonJson.receivedType,
|
|
1785
|
+
path: nonJson.path
|
|
1786
|
+
}
|
|
1787
|
+
);
|
|
1788
|
+
}
|
|
1789
|
+
break;
|
|
1790
|
+
}
|
|
1791
|
+
case "blob": {
|
|
1792
|
+
if (!isBlobRef(value)) {
|
|
1793
|
+
throw new SchemaValidationError(
|
|
1794
|
+
`Field "${fieldName}" in collection "${collection}" must be a BlobRef (from the blob store), got ${describeType(value)}`,
|
|
1795
|
+
{ collection, field: fieldName, expectedType: "blob", receivedType: describeType(value) }
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1798
|
+
break;
|
|
1799
|
+
}
|
|
1800
|
+
case "secret": {
|
|
1801
|
+
if (typeof value !== "string") {
|
|
1802
|
+
throw new SchemaValidationError(
|
|
1803
|
+
`Field "${fieldName}" in collection "${collection}" must be a string, got ${describeType(value)}`,
|
|
1804
|
+
{
|
|
1805
|
+
collection,
|
|
1806
|
+
field: fieldName,
|
|
1807
|
+
expectedType: "secret",
|
|
1808
|
+
receivedType: describeType(value)
|
|
1809
|
+
}
|
|
1810
|
+
);
|
|
1811
|
+
}
|
|
1812
|
+
break;
|
|
1813
|
+
}
|
|
1295
1814
|
}
|
|
1296
1815
|
}
|
|
1816
|
+
function isPlainObject(value) {
|
|
1817
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1818
|
+
return false;
|
|
1819
|
+
}
|
|
1820
|
+
const proto = Object.getPrototypeOf(value);
|
|
1821
|
+
return proto === null || proto === Object.prototype;
|
|
1822
|
+
}
|
|
1823
|
+
function findNonJsonSerializable(value, path, seen = /* @__PURE__ */ new WeakSet()) {
|
|
1824
|
+
if (value === null) {
|
|
1825
|
+
return null;
|
|
1826
|
+
}
|
|
1827
|
+
const type = typeof value;
|
|
1828
|
+
if (type === "string" || type === "number" || type === "boolean") {
|
|
1829
|
+
if (type === "number" && !Number.isFinite(value)) {
|
|
1830
|
+
return { path, receivedType: describeType(value) };
|
|
1831
|
+
}
|
|
1832
|
+
return null;
|
|
1833
|
+
}
|
|
1834
|
+
if (type === "undefined" || type === "function" || type === "symbol" || type === "bigint") {
|
|
1835
|
+
return { path, receivedType: describeType(value) };
|
|
1836
|
+
}
|
|
1837
|
+
if (Array.isArray(value)) {
|
|
1838
|
+
for (let index = 0; index < value.length; index++) {
|
|
1839
|
+
const invalid = findNonJsonSerializable(value[index], `${path}[${index}]`, seen);
|
|
1840
|
+
if (invalid) return invalid;
|
|
1841
|
+
}
|
|
1842
|
+
return null;
|
|
1843
|
+
}
|
|
1844
|
+
if (type === "object") {
|
|
1845
|
+
const objectValue = value;
|
|
1846
|
+
if (seen.has(objectValue)) {
|
|
1847
|
+
return { path, receivedType: "circular object" };
|
|
1848
|
+
}
|
|
1849
|
+
seen.add(objectValue);
|
|
1850
|
+
for (const [key, nestedValue] of Object.entries(objectValue)) {
|
|
1851
|
+
const invalid = findNonJsonSerializable(nestedValue, `${path}.${key}`, seen);
|
|
1852
|
+
if (invalid) return invalid;
|
|
1853
|
+
}
|
|
1854
|
+
seen.delete(objectValue);
|
|
1855
|
+
return null;
|
|
1856
|
+
}
|
|
1857
|
+
return { path, receivedType: describeType(value) };
|
|
1858
|
+
}
|
|
1859
|
+
function describeType(value) {
|
|
1860
|
+
if (value === null) {
|
|
1861
|
+
return "null";
|
|
1862
|
+
}
|
|
1863
|
+
if (Array.isArray(value)) {
|
|
1864
|
+
return "array";
|
|
1865
|
+
}
|
|
1866
|
+
return typeof value;
|
|
1867
|
+
}
|
|
1297
1868
|
function jsTypeForKind(kind) {
|
|
1298
1869
|
switch (kind) {
|
|
1299
1870
|
case "string":
|
|
@@ -1724,14 +2295,18 @@ function migrationStepsToSQL(steps) {
|
|
|
1724
2295
|
statements.push(addFieldSQL(step.collection, step.field, step.descriptor));
|
|
1725
2296
|
break;
|
|
1726
2297
|
case "removeField":
|
|
1727
|
-
statements.push(
|
|
2298
|
+
statements.push(
|
|
2299
|
+
`ALTER TABLE ${quoteIdent(step.collection)} DROP COLUMN ${quoteIdent(step.field)}`
|
|
2300
|
+
);
|
|
1728
2301
|
break;
|
|
1729
2302
|
case "renameField":
|
|
1730
|
-
statements.push(
|
|
2303
|
+
statements.push(
|
|
2304
|
+
`ALTER TABLE ${quoteIdent(step.collection)} RENAME COLUMN ${quoteIdent(step.from)} TO ${quoteIdent(step.to)}`
|
|
2305
|
+
);
|
|
1731
2306
|
break;
|
|
1732
2307
|
case "addIndex":
|
|
1733
2308
|
statements.push(
|
|
1734
|
-
`CREATE INDEX IF NOT EXISTS idx_${step.collection}_${step.field} ON ${step.collection} (${step.field})`
|
|
2309
|
+
`CREATE INDEX IF NOT EXISTS idx_${step.collection}_${step.field} ON ${quoteIdent(step.collection)} (${quoteIdent(step.field)})`
|
|
1735
2310
|
);
|
|
1736
2311
|
break;
|
|
1737
2312
|
case "removeIndex":
|
|
@@ -1749,13 +2324,13 @@ function rollbackStepsToSQL(migration) {
|
|
|
1749
2324
|
}
|
|
1750
2325
|
function addFieldSQL(collection, field, descriptor) {
|
|
1751
2326
|
const sqlType = mapFieldType2(descriptor);
|
|
1752
|
-
const parts = [`ALTER TABLE ${collection} ADD COLUMN ${field}`, sqlType];
|
|
2327
|
+
const parts = [`ALTER TABLE ${quoteIdent(collection)} ADD COLUMN ${quoteIdent(field)}`, sqlType];
|
|
1753
2328
|
if (descriptor.defaultValue !== void 0) {
|
|
1754
2329
|
parts.push(`DEFAULT ${sqlDefault2(descriptor.defaultValue)}`);
|
|
1755
2330
|
}
|
|
1756
2331
|
if (descriptor.kind === "enum" && descriptor.enumValues) {
|
|
1757
2332
|
const values = descriptor.enumValues.map((v) => `'${v}'`).join(", ");
|
|
1758
|
-
parts.push(`CHECK (${field} IN (${values}))`);
|
|
2333
|
+
parts.push(`CHECK (${quoteIdent(field)} IN (${values}))`);
|
|
1759
2334
|
}
|
|
1760
2335
|
return parts.join(" ");
|
|
1761
2336
|
}
|
|
@@ -1773,6 +2348,14 @@ function mapFieldType2(descriptor) {
|
|
|
1773
2348
|
return "INTEGER";
|
|
1774
2349
|
case "array":
|
|
1775
2350
|
return "TEXT";
|
|
2351
|
+
case "object":
|
|
2352
|
+
return "TEXT";
|
|
2353
|
+
case "json":
|
|
2354
|
+
return "TEXT";
|
|
2355
|
+
case "blob":
|
|
2356
|
+
return "TEXT";
|
|
2357
|
+
case "secret":
|
|
2358
|
+
return "TEXT";
|
|
1776
2359
|
case "richtext":
|
|
1777
2360
|
return "BLOB";
|
|
1778
2361
|
}
|
|
@@ -1791,7 +2374,14 @@ var SCALAR_TYPE_MAP = {
|
|
|
1791
2374
|
number: "double",
|
|
1792
2375
|
boolean: "bool",
|
|
1793
2376
|
timestamp: "int64",
|
|
1794
|
-
richtext: "bytes"
|
|
2377
|
+
richtext: "bytes",
|
|
2378
|
+
// object/json values travel as JSON-serialized strings on the proto wire.
|
|
2379
|
+
object: "string",
|
|
2380
|
+
json: "string",
|
|
2381
|
+
// A blob field carries a JSON-serialized content-addressed reference, not bytes.
|
|
2382
|
+
blob: "string",
|
|
2383
|
+
// A secret field's stored value is a JSON string (ciphertext or hash).
|
|
2384
|
+
secret: "string"
|
|
1795
2385
|
};
|
|
1796
2386
|
var ARRAY_ITEM_TYPE_MAP = {
|
|
1797
2387
|
string: "string",
|
|
@@ -2034,6 +2624,7 @@ export {
|
|
|
2034
2624
|
MergeConflictError,
|
|
2035
2625
|
MigrationBuilder,
|
|
2036
2626
|
MigrationRollbackError,
|
|
2627
|
+
MissingSecretKeyError,
|
|
2037
2628
|
OperationError,
|
|
2038
2629
|
RemoteClockDriftError,
|
|
2039
2630
|
RollbackBuilder,
|
|
@@ -2041,6 +2632,7 @@ export {
|
|
|
2041
2632
|
StorageError,
|
|
2042
2633
|
SyncError,
|
|
2043
2634
|
advanceVector,
|
|
2635
|
+
applyAtomicOp,
|
|
2044
2636
|
applyOperationTransforms,
|
|
2045
2637
|
base64ToBytes,
|
|
2046
2638
|
buildScopeMap,
|
|
@@ -2050,16 +2642,19 @@ export {
|
|
|
2050
2642
|
collectSchemaScopeFields,
|
|
2051
2643
|
collectSchemaScopeValueKeys,
|
|
2052
2644
|
computeDelta,
|
|
2645
|
+
createBlobRef,
|
|
2053
2646
|
createOperation,
|
|
2054
2647
|
createReversibleMigration,
|
|
2055
2648
|
createVersionVector,
|
|
2056
2649
|
decodeBytesFromOpData,
|
|
2650
|
+
decryptSecret,
|
|
2057
2651
|
defaultApplyFailureReason,
|
|
2058
2652
|
defaultSequenceFormat,
|
|
2059
2653
|
defineSchema,
|
|
2060
2654
|
deserializeVector,
|
|
2061
2655
|
dominates,
|
|
2062
2656
|
encodeBytesForOpData,
|
|
2657
|
+
encryptSecret,
|
|
2063
2658
|
extractScopeValuesFromClaims,
|
|
2064
2659
|
extractTimestamp,
|
|
2065
2660
|
formatSequenceValue,
|
|
@@ -2072,8 +2667,11 @@ export {
|
|
|
2072
2667
|
getKoraErrorFix,
|
|
2073
2668
|
getTransitionMap,
|
|
2074
2669
|
hasSchemaSyncRules,
|
|
2670
|
+
hashBlob,
|
|
2671
|
+
hashSecret,
|
|
2075
2672
|
isApplyFailure,
|
|
2076
2673
|
isAtomicOp,
|
|
2674
|
+
isBlobRef,
|
|
2077
2675
|
isCollectionSyncScoped,
|
|
2078
2676
|
isKoraBytesValue,
|
|
2079
2677
|
isLegacyNumericByteObject,
|
|
@@ -2083,14 +2681,20 @@ export {
|
|
|
2083
2681
|
migrate,
|
|
2084
2682
|
migrationStepsToSQL,
|
|
2085
2683
|
op,
|
|
2684
|
+
quoteIdent,
|
|
2685
|
+
replayOperationsForRecord,
|
|
2086
2686
|
resolveAtomicOp,
|
|
2687
|
+
revealSecret,
|
|
2087
2688
|
rollbackStepsToSQL,
|
|
2088
2689
|
serializeVector,
|
|
2089
2690
|
t,
|
|
2090
2691
|
toAtomicOp,
|
|
2692
|
+
transformSecretFieldsForWrite,
|
|
2091
2693
|
validateRecord,
|
|
2092
2694
|
validateTransition,
|
|
2093
2695
|
vectorsEqual,
|
|
2094
|
-
verifyOperationIntegrity
|
|
2696
|
+
verifyOperationIntegrity,
|
|
2697
|
+
verifySecret,
|
|
2698
|
+
verifySecretValue
|
|
2095
2699
|
};
|
|
2096
2700
|
//# sourceMappingURL=index.js.map
|