@korajs/core 1.0.0-beta.0 → 1.0.0-beta.2
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 +2 -2
- package/dist/bindings/index.d.ts +2 -2
- package/dist/{build-scope-map-DOf4JLTh.d.ts → build-scope-map-BvdTzmm4.d.ts} +1 -1
- package/dist/{build-scope-map-BIeawJzC.d.cts → build-scope-map-K25AyQXb.d.cts} +1 -1
- package/dist/{events-BynBOsO3.d.cts → events-DhblJN4r.d.cts} +144 -2
- package/dist/{events-BynBOsO3.d.ts → events-DhblJN4r.d.ts} +144 -2
- package/dist/index.cjs +566 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +174 -7
- package/dist/index.d.ts +174 -7
- package/dist/index.js +553 -22
- package/dist/index.js.map +1 -1
- package/dist/internal.d.cts +2 -2
- package/dist/internal.d.ts +2 -2
- package/dist/{operation-D5WOZYvy.d.ts → operation-CC7O3-wf.d.ts} +1 -1
- package/dist/{operation-BpZlYSpe.d.cts → operation-D-pemjcf.d.cts} +1 -1
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,7 @@ __export(index_exports, {
|
|
|
38
38
|
MergeConflictError: () => MergeConflictError,
|
|
39
39
|
MigrationBuilder: () => MigrationBuilder,
|
|
40
40
|
MigrationRollbackError: () => MigrationRollbackError,
|
|
41
|
+
MissingSecretKeyError: () => MissingSecretKeyError,
|
|
41
42
|
OperationError: () => OperationError,
|
|
42
43
|
RemoteClockDriftError: () => RemoteClockDriftError,
|
|
43
44
|
RollbackBuilder: () => RollbackBuilder,
|
|
@@ -54,16 +55,19 @@ __export(index_exports, {
|
|
|
54
55
|
collectSchemaScopeFields: () => collectSchemaScopeFields,
|
|
55
56
|
collectSchemaScopeValueKeys: () => collectSchemaScopeValueKeys,
|
|
56
57
|
computeDelta: () => computeDelta,
|
|
58
|
+
createBlobRef: () => createBlobRef,
|
|
57
59
|
createOperation: () => createOperation,
|
|
58
60
|
createReversibleMigration: () => createReversibleMigration,
|
|
59
61
|
createVersionVector: () => createVersionVector,
|
|
60
62
|
decodeBytesFromOpData: () => decodeBytesFromOpData,
|
|
63
|
+
decryptSecret: () => decryptSecret,
|
|
61
64
|
defaultApplyFailureReason: () => defaultApplyFailureReason,
|
|
62
65
|
defaultSequenceFormat: () => defaultSequenceFormat,
|
|
63
66
|
defineSchema: () => defineSchema,
|
|
64
67
|
deserializeVector: () => deserializeVector,
|
|
65
68
|
dominates: () => dominates,
|
|
66
69
|
encodeBytesForOpData: () => encodeBytesForOpData,
|
|
70
|
+
encryptSecret: () => encryptSecret,
|
|
67
71
|
extractScopeValuesFromClaims: () => extractScopeValuesFromClaims,
|
|
68
72
|
extractTimestamp: () => extractTimestamp,
|
|
69
73
|
formatSequenceValue: () => formatSequenceValue,
|
|
@@ -76,8 +80,11 @@ __export(index_exports, {
|
|
|
76
80
|
getKoraErrorFix: () => getKoraErrorFix,
|
|
77
81
|
getTransitionMap: () => getTransitionMap,
|
|
78
82
|
hasSchemaSyncRules: () => hasSchemaSyncRules,
|
|
83
|
+
hashBlob: () => hashBlob,
|
|
84
|
+
hashSecret: () => hashSecret,
|
|
79
85
|
isApplyFailure: () => isApplyFailure,
|
|
80
86
|
isAtomicOp: () => isAtomicOp,
|
|
87
|
+
isBlobRef: () => isBlobRef,
|
|
81
88
|
isCollectionSyncScoped: () => isCollectionSyncScoped,
|
|
82
89
|
isKoraBytesValue: () => isKoraBytesValue,
|
|
83
90
|
isLegacyNumericByteObject: () => isLegacyNumericByteObject,
|
|
@@ -87,15 +94,20 @@ __export(index_exports, {
|
|
|
87
94
|
migrate: () => migrate,
|
|
88
95
|
migrationStepsToSQL: () => migrationStepsToSQL,
|
|
89
96
|
op: () => op,
|
|
97
|
+
quoteIdent: () => quoteIdent,
|
|
90
98
|
resolveAtomicOp: () => resolveAtomicOp,
|
|
99
|
+
revealSecret: () => revealSecret,
|
|
91
100
|
rollbackStepsToSQL: () => rollbackStepsToSQL,
|
|
92
101
|
serializeVector: () => serializeVector,
|
|
93
102
|
t: () => t,
|
|
94
103
|
toAtomicOp: () => toAtomicOp,
|
|
104
|
+
transformSecretFieldsForWrite: () => transformSecretFieldsForWrite,
|
|
95
105
|
validateRecord: () => validateRecord,
|
|
96
106
|
validateTransition: () => validateTransition,
|
|
97
107
|
vectorsEqual: () => vectorsEqual,
|
|
98
|
-
verifyOperationIntegrity: () => verifyOperationIntegrity
|
|
108
|
+
verifyOperationIntegrity: () => verifyOperationIntegrity,
|
|
109
|
+
verifySecret: () => verifySecret,
|
|
110
|
+
verifySecretValue: () => verifySecretValue
|
|
99
111
|
});
|
|
100
112
|
module.exports = __toCommonJS(index_exports);
|
|
101
113
|
|
|
@@ -534,6 +546,226 @@ function formatUUID(bytes) {
|
|
|
534
546
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
535
547
|
}
|
|
536
548
|
|
|
549
|
+
// src/blob/blob-ref.ts
|
|
550
|
+
async function hashBlob(bytes) {
|
|
551
|
+
const view = new Uint8Array(bytes.byteLength);
|
|
552
|
+
view.set(bytes);
|
|
553
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", view);
|
|
554
|
+
const out = new Uint8Array(digest);
|
|
555
|
+
return Array.from(out, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
556
|
+
}
|
|
557
|
+
async function createBlobRef(bytes, metadata = {}) {
|
|
558
|
+
const hash = await hashBlob(bytes);
|
|
559
|
+
const ref = { hash, size: bytes.byteLength };
|
|
560
|
+
if (metadata.mimeType !== void 0) {
|
|
561
|
+
ref.mimeType = metadata.mimeType;
|
|
562
|
+
}
|
|
563
|
+
if (metadata.filename !== void 0) {
|
|
564
|
+
ref.filename = metadata.filename;
|
|
565
|
+
}
|
|
566
|
+
return ref;
|
|
567
|
+
}
|
|
568
|
+
function isBlobRef(value) {
|
|
569
|
+
if (typeof value !== "object" || value === null) {
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
const ref = value;
|
|
573
|
+
if (typeof ref.hash !== "string" || !/^[0-9a-f]{64}$/.test(ref.hash)) {
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
576
|
+
if (typeof ref.size !== "number" || !Number.isInteger(ref.size) || ref.size < 0) {
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
if (ref.mimeType !== void 0 && typeof ref.mimeType !== "string") {
|
|
580
|
+
return false;
|
|
581
|
+
}
|
|
582
|
+
if (ref.filename !== void 0 && typeof ref.filename !== "string") {
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
if (ref.manifestHash !== void 0 && (typeof ref.manifestHash !== "string" || !/^[0-9a-f]{64}$/.test(ref.manifestHash))) {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
return true;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// src/secret/secret-crypto.ts
|
|
592
|
+
var PBKDF2_ITERATIONS = 21e4;
|
|
593
|
+
var SALT_BYTES = 16;
|
|
594
|
+
var IV_BYTES = 12;
|
|
595
|
+
var HASH_BYTES = 32;
|
|
596
|
+
function toHex(bytes) {
|
|
597
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
598
|
+
}
|
|
599
|
+
function fromHex(hex) {
|
|
600
|
+
if (hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) {
|
|
601
|
+
throw new Error("Invalid hex string");
|
|
602
|
+
}
|
|
603
|
+
const out = new Uint8Array(hex.length / 2);
|
|
604
|
+
for (let i = 0; i < out.length; i++) {
|
|
605
|
+
out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
606
|
+
}
|
|
607
|
+
return out;
|
|
608
|
+
}
|
|
609
|
+
function randomBytes(n) {
|
|
610
|
+
const out = new Uint8Array(n);
|
|
611
|
+
globalThis.crypto.getRandomValues(out);
|
|
612
|
+
return out;
|
|
613
|
+
}
|
|
614
|
+
function timingSafeEqual(a, b) {
|
|
615
|
+
if (a.length !== b.length) {
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
let diff = 0;
|
|
619
|
+
for (let i = 0; i < a.length; i++) {
|
|
620
|
+
diff |= a[i] ^ b[i];
|
|
621
|
+
}
|
|
622
|
+
return diff === 0;
|
|
623
|
+
}
|
|
624
|
+
async function deriveAesKey(secretKey, salt) {
|
|
625
|
+
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
626
|
+
"raw",
|
|
627
|
+
new TextEncoder().encode(secretKey),
|
|
628
|
+
"PBKDF2",
|
|
629
|
+
false,
|
|
630
|
+
["deriveKey"]
|
|
631
|
+
);
|
|
632
|
+
return globalThis.crypto.subtle.deriveKey(
|
|
633
|
+
{ name: "PBKDF2", salt, iterations: PBKDF2_ITERATIONS, hash: "SHA-256" },
|
|
634
|
+
baseKey,
|
|
635
|
+
{ name: "AES-GCM", length: 256 },
|
|
636
|
+
false,
|
|
637
|
+
["encrypt", "decrypt"]
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
async function encryptSecret(plaintext, secretKey) {
|
|
641
|
+
const salt = randomBytes(SALT_BYTES);
|
|
642
|
+
const iv = randomBytes(IV_BYTES);
|
|
643
|
+
const key = await deriveAesKey(secretKey, salt);
|
|
644
|
+
const ciphertext = await globalThis.crypto.subtle.encrypt(
|
|
645
|
+
{ name: "AES-GCM", iv },
|
|
646
|
+
key,
|
|
647
|
+
new TextEncoder().encode(plaintext)
|
|
648
|
+
);
|
|
649
|
+
return `v1.${toHex(salt)}.${toHex(iv)}.${toHex(new Uint8Array(ciphertext))}`;
|
|
650
|
+
}
|
|
651
|
+
async function decryptSecret(encrypted, secretKey) {
|
|
652
|
+
const parts = encrypted.split(".");
|
|
653
|
+
if (parts.length !== 4 || parts[0] !== "v1") {
|
|
654
|
+
throw new Error("Invalid encrypted secret format");
|
|
655
|
+
}
|
|
656
|
+
const salt = fromHex(parts[1]);
|
|
657
|
+
const iv = fromHex(parts[2]);
|
|
658
|
+
const ciphertext = fromHex(parts[3]);
|
|
659
|
+
const key = await deriveAesKey(secretKey, salt);
|
|
660
|
+
const plaintext = await globalThis.crypto.subtle.decrypt(
|
|
661
|
+
{ name: "AES-GCM", iv },
|
|
662
|
+
key,
|
|
663
|
+
ciphertext
|
|
664
|
+
);
|
|
665
|
+
return new TextDecoder().decode(plaintext);
|
|
666
|
+
}
|
|
667
|
+
async function hashSecret(plaintext) {
|
|
668
|
+
const salt = randomBytes(SALT_BYTES);
|
|
669
|
+
const hash = await pbkdf2(plaintext, salt);
|
|
670
|
+
return `v1.${toHex(salt)}.${toHex(hash)}`;
|
|
671
|
+
}
|
|
672
|
+
async function verifySecret(plaintext, stored) {
|
|
673
|
+
const parts = stored.split(".");
|
|
674
|
+
if (parts.length !== 3 || parts[0] !== "v1") {
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
let salt;
|
|
678
|
+
let expected;
|
|
679
|
+
try {
|
|
680
|
+
salt = fromHex(parts[1]);
|
|
681
|
+
expected = fromHex(parts[2]);
|
|
682
|
+
} catch {
|
|
683
|
+
return false;
|
|
684
|
+
}
|
|
685
|
+
const actual = await pbkdf2(plaintext, salt);
|
|
686
|
+
return timingSafeEqual(actual, expected);
|
|
687
|
+
}
|
|
688
|
+
async function pbkdf2(plaintext, salt) {
|
|
689
|
+
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
690
|
+
"raw",
|
|
691
|
+
new TextEncoder().encode(plaintext),
|
|
692
|
+
"PBKDF2",
|
|
693
|
+
false,
|
|
694
|
+
["deriveBits"]
|
|
695
|
+
);
|
|
696
|
+
const bits = await globalThis.crypto.subtle.deriveBits(
|
|
697
|
+
{ name: "PBKDF2", salt, iterations: PBKDF2_ITERATIONS, hash: "SHA-256" },
|
|
698
|
+
baseKey,
|
|
699
|
+
HASH_BYTES * 8
|
|
700
|
+
);
|
|
701
|
+
return new Uint8Array(bits);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// src/secret/secret-transform.ts
|
|
705
|
+
var MissingSecretKeyError = class extends Error {
|
|
706
|
+
constructor(field) {
|
|
707
|
+
super(
|
|
708
|
+
`Secret field "${field}" is encrypted but no encryption key is configured. Provide sync.encryption.key (or a key provider) to createApp.`
|
|
709
|
+
);
|
|
710
|
+
this.field = field;
|
|
711
|
+
this.name = "MissingSecretKeyError";
|
|
712
|
+
}
|
|
713
|
+
field;
|
|
714
|
+
};
|
|
715
|
+
async function resolveKey(provider) {
|
|
716
|
+
if (!provider) {
|
|
717
|
+
return void 0;
|
|
718
|
+
}
|
|
719
|
+
return provider();
|
|
720
|
+
}
|
|
721
|
+
async function transformSecretFieldsForWrite(data, collectionDef, keyProvider) {
|
|
722
|
+
const hasSecret = Object.values(collectionDef.fields).some((f) => f.kind === "secret");
|
|
723
|
+
if (!hasSecret) {
|
|
724
|
+
return data;
|
|
725
|
+
}
|
|
726
|
+
const result = { ...data };
|
|
727
|
+
let key;
|
|
728
|
+
let keyResolved = false;
|
|
729
|
+
for (const [field, value] of Object.entries(data)) {
|
|
730
|
+
const descriptor = collectionDef.fields[field];
|
|
731
|
+
if (descriptor?.kind !== "secret") {
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
if (typeof value !== "string") {
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
const mode = descriptor.secretMode ?? "encrypted";
|
|
738
|
+
if (mode === "hashed") {
|
|
739
|
+
result[field] = await hashSecret(value);
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (!keyResolved) {
|
|
743
|
+
key = await resolveKey(keyProvider);
|
|
744
|
+
keyResolved = true;
|
|
745
|
+
}
|
|
746
|
+
if (key === void 0) {
|
|
747
|
+
throw new MissingSecretKeyError(field);
|
|
748
|
+
}
|
|
749
|
+
result[field] = await encryptSecret(value, key);
|
|
750
|
+
}
|
|
751
|
+
return result;
|
|
752
|
+
}
|
|
753
|
+
async function revealSecret(storedValue, mode, keyProvider, field = "secret") {
|
|
754
|
+
if (mode === "hashed") {
|
|
755
|
+
throw new Error(
|
|
756
|
+
`Secret field "${field}" is hashed (one-way) and cannot be revealed. Use verifySecretValue to check a candidate.`
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
const key = await resolveKey(keyProvider);
|
|
760
|
+
if (key === void 0) {
|
|
761
|
+
throw new MissingSecretKeyError(field);
|
|
762
|
+
}
|
|
763
|
+
return decryptSecret(storedValue, key);
|
|
764
|
+
}
|
|
765
|
+
async function verifySecretValue(candidate, storedValue) {
|
|
766
|
+
return verifySecret(candidate, storedValue);
|
|
767
|
+
}
|
|
768
|
+
|
|
537
769
|
// src/operations/apply-result.ts
|
|
538
770
|
var APPLY_RESULTS = ["applied", "duplicate", "skipped", "rejected", "deferred"];
|
|
539
771
|
var APPLY_FAILURE_CODES = {
|
|
@@ -1079,7 +1311,7 @@ function validateStateMachineDefinition(collectionName, sm, fields) {
|
|
|
1079
1311
|
}
|
|
1080
1312
|
|
|
1081
1313
|
// src/schema/define.ts
|
|
1082
|
-
var COLLECTION_NAME_RE = /^[
|
|
1314
|
+
var COLLECTION_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
1083
1315
|
var FIELD_NAME_RE = /^[a-z][a-zA-Z0-9_]*$/;
|
|
1084
1316
|
var RESERVED_FIELDS = /* @__PURE__ */ new Set(["id", "_created_at", "_updated_at", "_version", "_deleted"]);
|
|
1085
1317
|
function defineSchema(input) {
|
|
@@ -1144,7 +1376,7 @@ function validateVersion(version) {
|
|
|
1144
1376
|
function validateCollectionName(name) {
|
|
1145
1377
|
if (!COLLECTION_NAME_RE.test(name)) {
|
|
1146
1378
|
throw new SchemaValidationError(
|
|
1147
|
-
`Collection name "${name}" is invalid. Must
|
|
1379
|
+
`Collection name "${name}" is invalid. Must start with a letter and contain only letters, numbers, and underscores.`,
|
|
1148
1380
|
{ collection: name }
|
|
1149
1381
|
);
|
|
1150
1382
|
}
|
|
@@ -1336,6 +1568,11 @@ function validateRelation(name, relation, collections) {
|
|
|
1336
1568
|
}
|
|
1337
1569
|
}
|
|
1338
1570
|
|
|
1571
|
+
// src/schema/quote-ident.ts
|
|
1572
|
+
function quoteIdent(name) {
|
|
1573
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1339
1576
|
// src/schema/sql-gen.ts
|
|
1340
1577
|
function generateSQL(collectionName, collection, relations) {
|
|
1341
1578
|
const statements = [];
|
|
@@ -1347,7 +1584,7 @@ function generateSQL(collectionName, collection, relations) {
|
|
|
1347
1584
|
if (relations) {
|
|
1348
1585
|
for (const rel of Object.values(relations)) {
|
|
1349
1586
|
if (rel.from === collectionName && rel.field === fieldName) {
|
|
1350
|
-
colDef += ` REFERENCES ${rel.to}(id)`;
|
|
1587
|
+
colDef += ` REFERENCES ${quoteIdent(rel.to)}(id)`;
|
|
1351
1588
|
fkFields.push(fieldName);
|
|
1352
1589
|
break;
|
|
1353
1590
|
}
|
|
@@ -1360,36 +1597,40 @@ function generateSQL(collectionName, collection, relations) {
|
|
|
1360
1597
|
columns.push("_version TEXT NOT NULL DEFAULT ''");
|
|
1361
1598
|
columns.push("_field_versions TEXT NOT NULL DEFAULT '{}'");
|
|
1362
1599
|
columns.push("_deleted INTEGER NOT NULL DEFAULT 0");
|
|
1363
|
-
statements.push(
|
|
1600
|
+
statements.push(
|
|
1601
|
+
`CREATE TABLE IF NOT EXISTS ${quoteIdent(collectionName)} (
|
|
1364
1602
|
${columns.join(",\n ")}
|
|
1365
|
-
)`
|
|
1603
|
+
)`
|
|
1604
|
+
);
|
|
1366
1605
|
for (const [fieldName, descriptor] of Object.entries(collection.fields)) {
|
|
1367
1606
|
const colDef = columnDefinition(fieldName, descriptor);
|
|
1368
|
-
statements.push(
|
|
1369
|
-
|
|
1607
|
+
statements.push(
|
|
1608
|
+
`--kora:safe-alter
|
|
1609
|
+
ALTER TABLE ${quoteIdent(collectionName)} ADD COLUMN ${colDef}`
|
|
1610
|
+
);
|
|
1370
1611
|
}
|
|
1371
1612
|
statements.push(
|
|
1372
1613
|
`--kora:safe-alter
|
|
1373
|
-
ALTER TABLE ${collectionName} ADD COLUMN _version TEXT NOT NULL DEFAULT ''`
|
|
1614
|
+
ALTER TABLE ${quoteIdent(collectionName)} ADD COLUMN _version TEXT NOT NULL DEFAULT ''`
|
|
1374
1615
|
);
|
|
1375
1616
|
statements.push(
|
|
1376
1617
|
`--kora:safe-alter
|
|
1377
|
-
ALTER TABLE ${collectionName} ADD COLUMN _field_versions TEXT NOT NULL DEFAULT '{}'`
|
|
1618
|
+
ALTER TABLE ${quoteIdent(collectionName)} ADD COLUMN _field_versions TEXT NOT NULL DEFAULT '{}'`
|
|
1378
1619
|
);
|
|
1379
1620
|
for (const indexField of collection.indexes) {
|
|
1380
1621
|
statements.push(
|
|
1381
|
-
`CREATE INDEX IF NOT EXISTS idx_${collectionName}_${indexField} ON ${collectionName} (${indexField})`
|
|
1622
|
+
`CREATE INDEX IF NOT EXISTS idx_${collectionName}_${indexField} ON ${quoteIdent(collectionName)} (${quoteIdent(indexField)})`
|
|
1382
1623
|
);
|
|
1383
1624
|
}
|
|
1384
1625
|
for (const fkField of fkFields) {
|
|
1385
1626
|
if (!indexedFields.has(fkField)) {
|
|
1386
1627
|
statements.push(
|
|
1387
|
-
`CREATE INDEX IF NOT EXISTS idx_${collectionName}_${fkField} ON ${collectionName} (${fkField})`
|
|
1628
|
+
`CREATE INDEX IF NOT EXISTS idx_${collectionName}_${fkField} ON ${quoteIdent(collectionName)} (${quoteIdent(fkField)})`
|
|
1388
1629
|
);
|
|
1389
1630
|
}
|
|
1390
1631
|
}
|
|
1391
1632
|
statements.push(
|
|
1392
|
-
`CREATE TABLE IF NOT EXISTS _kora_ops_${collectionName} (
|
|
1633
|
+
`CREATE TABLE IF NOT EXISTS ${quoteIdent(`_kora_ops_${collectionName}`)} (
|
|
1393
1634
|
id TEXT PRIMARY KEY NOT NULL,
|
|
1394
1635
|
node_id TEXT NOT NULL,
|
|
1395
1636
|
type TEXT NOT NULL,
|
|
@@ -1403,7 +1644,7 @@ ALTER TABLE ${collectionName} ADD COLUMN _field_versions TEXT NOT NULL DEFAULT '
|
|
|
1403
1644
|
)`
|
|
1404
1645
|
);
|
|
1405
1646
|
statements.push(
|
|
1406
|
-
`CREATE INDEX IF NOT EXISTS idx_kora_ops_${collectionName}_record_id ON _kora_ops_${collectionName} (record_id)`
|
|
1647
|
+
`CREATE INDEX IF NOT EXISTS idx_kora_ops_${collectionName}_record_id ON ${quoteIdent(`_kora_ops_${collectionName}`)} (record_id)`
|
|
1407
1648
|
);
|
|
1408
1649
|
return statements;
|
|
1409
1650
|
}
|
|
@@ -1421,6 +1662,9 @@ function generateFullDDL(schema) {
|
|
|
1421
1662
|
statements.push(
|
|
1422
1663
|
"CREATE TABLE IF NOT EXISTS _kora_sync_queue (\n id TEXT PRIMARY KEY NOT NULL,\n payload TEXT NOT NULL\n)"
|
|
1423
1664
|
);
|
|
1665
|
+
statements.push(
|
|
1666
|
+
"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)"
|
|
1667
|
+
);
|
|
1424
1668
|
statements.push(
|
|
1425
1669
|
"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)"
|
|
1426
1670
|
);
|
|
@@ -1437,7 +1681,7 @@ function generateFullDDL(schema) {
|
|
|
1437
1681
|
}
|
|
1438
1682
|
function columnDefinition(fieldName, descriptor) {
|
|
1439
1683
|
const sqlType = mapFieldType(descriptor);
|
|
1440
|
-
const parts = [fieldName, sqlType];
|
|
1684
|
+
const parts = [quoteIdent(fieldName), sqlType];
|
|
1441
1685
|
if (descriptor.required && descriptor.defaultValue === void 0 && !descriptor.auto) {
|
|
1442
1686
|
parts.push("NOT NULL");
|
|
1443
1687
|
}
|
|
@@ -1446,7 +1690,7 @@ function columnDefinition(fieldName, descriptor) {
|
|
|
1446
1690
|
}
|
|
1447
1691
|
if (descriptor.kind === "enum" && descriptor.enumValues) {
|
|
1448
1692
|
const values = descriptor.enumValues.map((v) => `'${v}'`).join(", ");
|
|
1449
|
-
parts.push(`CHECK (${fieldName} IN (${values}))`);
|
|
1693
|
+
parts.push(`CHECK (${quoteIdent(fieldName)} IN (${values}))`);
|
|
1450
1694
|
}
|
|
1451
1695
|
return parts.join(" ");
|
|
1452
1696
|
}
|
|
@@ -1465,6 +1709,17 @@ function mapFieldType(descriptor) {
|
|
|
1465
1709
|
case "array":
|
|
1466
1710
|
return "TEXT";
|
|
1467
1711
|
// JSON-serialized
|
|
1712
|
+
case "object":
|
|
1713
|
+
return "TEXT";
|
|
1714
|
+
// JSON-serialized structured object
|
|
1715
|
+
case "json":
|
|
1716
|
+
return "TEXT";
|
|
1717
|
+
// JSON-serialized dynamic-key value
|
|
1718
|
+
case "blob":
|
|
1719
|
+
return "TEXT";
|
|
1720
|
+
// JSON-serialized content-addressed BlobRef (bytes stored out of band)
|
|
1721
|
+
case "secret":
|
|
1722
|
+
return "TEXT";
|
|
1468
1723
|
case "richtext":
|
|
1469
1724
|
return "BLOB";
|
|
1470
1725
|
}
|
|
@@ -1698,6 +1953,160 @@ var ArrayFieldBuilder = class _ArrayFieldBuilder extends FieldBuilder {
|
|
|
1698
1953
|
};
|
|
1699
1954
|
}
|
|
1700
1955
|
};
|
|
1956
|
+
var ObjectFieldBuilder = class _ObjectFieldBuilder extends FieldBuilder {
|
|
1957
|
+
_fields;
|
|
1958
|
+
constructor(fields, required = true, defaultValue = void 0, auto = false, mergeStrategy = null) {
|
|
1959
|
+
super("object", required, defaultValue, auto, mergeStrategy);
|
|
1960
|
+
this._fields = fields;
|
|
1961
|
+
}
|
|
1962
|
+
optional() {
|
|
1963
|
+
return new _ObjectFieldBuilder(
|
|
1964
|
+
this._fields,
|
|
1965
|
+
false,
|
|
1966
|
+
this._defaultValue,
|
|
1967
|
+
this._auto,
|
|
1968
|
+
this._mergeStrategy
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
default(value) {
|
|
1972
|
+
return new _ObjectFieldBuilder(this._fields, false, value, this._auto, this._mergeStrategy);
|
|
1973
|
+
}
|
|
1974
|
+
auto() {
|
|
1975
|
+
return new _ObjectFieldBuilder(this._fields, false, void 0, true, this._mergeStrategy);
|
|
1976
|
+
}
|
|
1977
|
+
merge(strategy) {
|
|
1978
|
+
return new _ObjectFieldBuilder(
|
|
1979
|
+
this._fields,
|
|
1980
|
+
this._required,
|
|
1981
|
+
this._defaultValue,
|
|
1982
|
+
this._auto,
|
|
1983
|
+
strategy
|
|
1984
|
+
);
|
|
1985
|
+
}
|
|
1986
|
+
_build() {
|
|
1987
|
+
const nestedFields = {};
|
|
1988
|
+
for (const [key, builder] of Object.entries(this._fields)) {
|
|
1989
|
+
nestedFields[key] = builder._build();
|
|
1990
|
+
}
|
|
1991
|
+
return {
|
|
1992
|
+
kind: "object",
|
|
1993
|
+
required: this._required,
|
|
1994
|
+
defaultValue: this._defaultValue,
|
|
1995
|
+
auto: this._auto,
|
|
1996
|
+
enumValues: null,
|
|
1997
|
+
itemKind: null,
|
|
1998
|
+
mergeStrategy: this._mergeStrategy,
|
|
1999
|
+
transitions: null,
|
|
2000
|
+
nestedFields
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
};
|
|
2004
|
+
var JsonFieldBuilder = class _JsonFieldBuilder extends FieldBuilder {
|
|
2005
|
+
optional() {
|
|
2006
|
+
return new _JsonFieldBuilder(
|
|
2007
|
+
"json",
|
|
2008
|
+
false,
|
|
2009
|
+
this._defaultValue,
|
|
2010
|
+
this._auto,
|
|
2011
|
+
this._mergeStrategy
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
2014
|
+
default(value) {
|
|
2015
|
+
return new _JsonFieldBuilder(
|
|
2016
|
+
"json",
|
|
2017
|
+
false,
|
|
2018
|
+
value,
|
|
2019
|
+
this._auto,
|
|
2020
|
+
this._mergeStrategy
|
|
2021
|
+
);
|
|
2022
|
+
}
|
|
2023
|
+
auto() {
|
|
2024
|
+
return new _JsonFieldBuilder("json", false, void 0, true, this._mergeStrategy);
|
|
2025
|
+
}
|
|
2026
|
+
merge(strategy) {
|
|
2027
|
+
return new _JsonFieldBuilder(
|
|
2028
|
+
"json",
|
|
2029
|
+
this._required,
|
|
2030
|
+
this._defaultValue,
|
|
2031
|
+
this._auto,
|
|
2032
|
+
strategy
|
|
2033
|
+
);
|
|
2034
|
+
}
|
|
2035
|
+
_build() {
|
|
2036
|
+
return {
|
|
2037
|
+
kind: "json",
|
|
2038
|
+
required: this._required,
|
|
2039
|
+
defaultValue: this._defaultValue,
|
|
2040
|
+
auto: this._auto,
|
|
2041
|
+
enumValues: null,
|
|
2042
|
+
itemKind: null,
|
|
2043
|
+
mergeStrategy: this._mergeStrategy,
|
|
2044
|
+
transitions: null,
|
|
2045
|
+
nestedFields: null
|
|
2046
|
+
};
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
var SecretFieldBuilder = class _SecretFieldBuilder extends FieldBuilder {
|
|
2050
|
+
_secretMode;
|
|
2051
|
+
constructor(secretMode = "encrypted", required = true, defaultValue = void 0, auto = false, mergeStrategy = null) {
|
|
2052
|
+
super("secret", required, defaultValue, auto, mergeStrategy);
|
|
2053
|
+
this._secretMode = secretMode;
|
|
2054
|
+
}
|
|
2055
|
+
/** Store this secret as a one-way salted hash (passwords: verify, never read back). */
|
|
2056
|
+
hashed() {
|
|
2057
|
+
return new _SecretFieldBuilder(
|
|
2058
|
+
"hashed",
|
|
2059
|
+
this._required,
|
|
2060
|
+
this._defaultValue,
|
|
2061
|
+
this._auto,
|
|
2062
|
+
this._mergeStrategy
|
|
2063
|
+
);
|
|
2064
|
+
}
|
|
2065
|
+
/** Store this secret as reversible ciphertext (tokens/keys: decrypt to use). */
|
|
2066
|
+
encrypted() {
|
|
2067
|
+
return new _SecretFieldBuilder(
|
|
2068
|
+
"encrypted",
|
|
2069
|
+
this._required,
|
|
2070
|
+
this._defaultValue,
|
|
2071
|
+
this._auto,
|
|
2072
|
+
this._mergeStrategy
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
optional() {
|
|
2076
|
+
return new _SecretFieldBuilder(
|
|
2077
|
+
this._secretMode,
|
|
2078
|
+
false,
|
|
2079
|
+
this._defaultValue,
|
|
2080
|
+
this._auto,
|
|
2081
|
+
this._mergeStrategy
|
|
2082
|
+
);
|
|
2083
|
+
}
|
|
2084
|
+
auto() {
|
|
2085
|
+
return new _SecretFieldBuilder(this._secretMode, false, void 0, true, this._mergeStrategy);
|
|
2086
|
+
}
|
|
2087
|
+
merge(strategy) {
|
|
2088
|
+
return new _SecretFieldBuilder(
|
|
2089
|
+
this._secretMode,
|
|
2090
|
+
this._required,
|
|
2091
|
+
this._defaultValue,
|
|
2092
|
+
this._auto,
|
|
2093
|
+
strategy
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
_build() {
|
|
2097
|
+
return {
|
|
2098
|
+
kind: "secret",
|
|
2099
|
+
required: this._required,
|
|
2100
|
+
defaultValue: this._defaultValue,
|
|
2101
|
+
auto: this._auto,
|
|
2102
|
+
enumValues: null,
|
|
2103
|
+
itemKind: null,
|
|
2104
|
+
mergeStrategy: this._mergeStrategy,
|
|
2105
|
+
transitions: null,
|
|
2106
|
+
secretMode: this._secretMode
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
};
|
|
1701
2110
|
var t = {
|
|
1702
2111
|
string() {
|
|
1703
2112
|
return new FieldBuilder("string", true, void 0, false);
|
|
@@ -1719,6 +2128,18 @@ var t = {
|
|
|
1719
2128
|
},
|
|
1720
2129
|
array(itemBuilder) {
|
|
1721
2130
|
return new ArrayFieldBuilder(itemBuilder, true, void 0, false);
|
|
2131
|
+
},
|
|
2132
|
+
object(fields) {
|
|
2133
|
+
return new ObjectFieldBuilder(fields, true, void 0, false);
|
|
2134
|
+
},
|
|
2135
|
+
json() {
|
|
2136
|
+
return new JsonFieldBuilder("json", true, void 0, false);
|
|
2137
|
+
},
|
|
2138
|
+
blob() {
|
|
2139
|
+
return new FieldBuilder("blob", true, void 0, false);
|
|
2140
|
+
},
|
|
2141
|
+
secret() {
|
|
2142
|
+
return new SecretFieldBuilder("encrypted", true, void 0, false);
|
|
1722
2143
|
}
|
|
1723
2144
|
};
|
|
1724
2145
|
|
|
@@ -1883,7 +2304,98 @@ function validateFieldValue(collection, fieldName, descriptor, value) {
|
|
|
1883
2304
|
}
|
|
1884
2305
|
break;
|
|
1885
2306
|
}
|
|
2307
|
+
case "object": {
|
|
2308
|
+
if (!isPlainObject(value)) {
|
|
2309
|
+
throw new SchemaValidationError(
|
|
2310
|
+
`Field "${fieldName}" in collection "${collection}" must be a plain object, got ${describeType(value)}`,
|
|
2311
|
+
{
|
|
2312
|
+
collection,
|
|
2313
|
+
field: fieldName,
|
|
2314
|
+
expectedType: "object",
|
|
2315
|
+
receivedType: describeType(value)
|
|
2316
|
+
}
|
|
2317
|
+
);
|
|
2318
|
+
}
|
|
2319
|
+
if (descriptor.nestedFields) {
|
|
2320
|
+
for (const [nestedName, nestedDescriptor] of Object.entries(descriptor.nestedFields)) {
|
|
2321
|
+
const nestedValue = value[nestedName];
|
|
2322
|
+
if (nestedValue !== void 0 && nestedValue !== null) {
|
|
2323
|
+
validateFieldValue(
|
|
2324
|
+
collection,
|
|
2325
|
+
`${fieldName}.${nestedName}`,
|
|
2326
|
+
nestedDescriptor,
|
|
2327
|
+
nestedValue
|
|
2328
|
+
);
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
break;
|
|
2333
|
+
}
|
|
2334
|
+
case "json": {
|
|
2335
|
+
if (!isJsonSerializable(value)) {
|
|
2336
|
+
throw new SchemaValidationError(
|
|
2337
|
+
`Field "${fieldName}" in collection "${collection}" must be JSON-serializable, got ${describeType(value)}`,
|
|
2338
|
+
{ collection, field: fieldName, expectedType: "json", receivedType: describeType(value) }
|
|
2339
|
+
);
|
|
2340
|
+
}
|
|
2341
|
+
break;
|
|
2342
|
+
}
|
|
2343
|
+
case "blob": {
|
|
2344
|
+
if (!isBlobRef(value)) {
|
|
2345
|
+
throw new SchemaValidationError(
|
|
2346
|
+
`Field "${fieldName}" in collection "${collection}" must be a BlobRef (from the blob store), got ${describeType(value)}`,
|
|
2347
|
+
{ collection, field: fieldName, expectedType: "blob", receivedType: describeType(value) }
|
|
2348
|
+
);
|
|
2349
|
+
}
|
|
2350
|
+
break;
|
|
2351
|
+
}
|
|
2352
|
+
case "secret": {
|
|
2353
|
+
if (typeof value !== "string") {
|
|
2354
|
+
throw new SchemaValidationError(
|
|
2355
|
+
`Field "${fieldName}" in collection "${collection}" must be a string, got ${describeType(value)}`,
|
|
2356
|
+
{
|
|
2357
|
+
collection,
|
|
2358
|
+
field: fieldName,
|
|
2359
|
+
expectedType: "secret",
|
|
2360
|
+
receivedType: describeType(value)
|
|
2361
|
+
}
|
|
2362
|
+
);
|
|
2363
|
+
}
|
|
2364
|
+
break;
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
function isPlainObject(value) {
|
|
2369
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2370
|
+
return false;
|
|
1886
2371
|
}
|
|
2372
|
+
const proto = Object.getPrototypeOf(value);
|
|
2373
|
+
return proto === null || proto === Object.prototype;
|
|
2374
|
+
}
|
|
2375
|
+
function isJsonSerializable(value) {
|
|
2376
|
+
if (value === null) {
|
|
2377
|
+
return true;
|
|
2378
|
+
}
|
|
2379
|
+
const type = typeof value;
|
|
2380
|
+
if (type === "string" || type === "number" || type === "boolean") {
|
|
2381
|
+
return Number.isFinite(value) || type !== "number";
|
|
2382
|
+
}
|
|
2383
|
+
if (Array.isArray(value)) {
|
|
2384
|
+
return value.every(isJsonSerializable);
|
|
2385
|
+
}
|
|
2386
|
+
if (type === "object") {
|
|
2387
|
+
return Object.values(value).every(isJsonSerializable);
|
|
2388
|
+
}
|
|
2389
|
+
return false;
|
|
2390
|
+
}
|
|
2391
|
+
function describeType(value) {
|
|
2392
|
+
if (value === null) {
|
|
2393
|
+
return "null";
|
|
2394
|
+
}
|
|
2395
|
+
if (Array.isArray(value)) {
|
|
2396
|
+
return "array";
|
|
2397
|
+
}
|
|
2398
|
+
return typeof value;
|
|
1887
2399
|
}
|
|
1888
2400
|
function jsTypeForKind(kind) {
|
|
1889
2401
|
switch (kind) {
|
|
@@ -2433,14 +2945,18 @@ function migrationStepsToSQL(steps) {
|
|
|
2433
2945
|
statements.push(addFieldSQL(step.collection, step.field, step.descriptor));
|
|
2434
2946
|
break;
|
|
2435
2947
|
case "removeField":
|
|
2436
|
-
statements.push(
|
|
2948
|
+
statements.push(
|
|
2949
|
+
`ALTER TABLE ${quoteIdent(step.collection)} DROP COLUMN ${quoteIdent(step.field)}`
|
|
2950
|
+
);
|
|
2437
2951
|
break;
|
|
2438
2952
|
case "renameField":
|
|
2439
|
-
statements.push(
|
|
2953
|
+
statements.push(
|
|
2954
|
+
`ALTER TABLE ${quoteIdent(step.collection)} RENAME COLUMN ${quoteIdent(step.from)} TO ${quoteIdent(step.to)}`
|
|
2955
|
+
);
|
|
2440
2956
|
break;
|
|
2441
2957
|
case "addIndex":
|
|
2442
2958
|
statements.push(
|
|
2443
|
-
`CREATE INDEX IF NOT EXISTS idx_${step.collection}_${step.field} ON ${step.collection} (${step.field})`
|
|
2959
|
+
`CREATE INDEX IF NOT EXISTS idx_${step.collection}_${step.field} ON ${quoteIdent(step.collection)} (${quoteIdent(step.field)})`
|
|
2444
2960
|
);
|
|
2445
2961
|
break;
|
|
2446
2962
|
case "removeIndex":
|
|
@@ -2458,13 +2974,13 @@ function rollbackStepsToSQL(migration) {
|
|
|
2458
2974
|
}
|
|
2459
2975
|
function addFieldSQL(collection, field, descriptor) {
|
|
2460
2976
|
const sqlType = mapFieldType2(descriptor);
|
|
2461
|
-
const parts = [`ALTER TABLE ${collection} ADD COLUMN ${field}`, sqlType];
|
|
2977
|
+
const parts = [`ALTER TABLE ${quoteIdent(collection)} ADD COLUMN ${quoteIdent(field)}`, sqlType];
|
|
2462
2978
|
if (descriptor.defaultValue !== void 0) {
|
|
2463
2979
|
parts.push(`DEFAULT ${sqlDefault2(descriptor.defaultValue)}`);
|
|
2464
2980
|
}
|
|
2465
2981
|
if (descriptor.kind === "enum" && descriptor.enumValues) {
|
|
2466
2982
|
const values = descriptor.enumValues.map((v) => `'${v}'`).join(", ");
|
|
2467
|
-
parts.push(`CHECK (${field} IN (${values}))`);
|
|
2983
|
+
parts.push(`CHECK (${quoteIdent(field)} IN (${values}))`);
|
|
2468
2984
|
}
|
|
2469
2985
|
return parts.join(" ");
|
|
2470
2986
|
}
|
|
@@ -2482,6 +2998,14 @@ function mapFieldType2(descriptor) {
|
|
|
2482
2998
|
return "INTEGER";
|
|
2483
2999
|
case "array":
|
|
2484
3000
|
return "TEXT";
|
|
3001
|
+
case "object":
|
|
3002
|
+
return "TEXT";
|
|
3003
|
+
case "json":
|
|
3004
|
+
return "TEXT";
|
|
3005
|
+
case "blob":
|
|
3006
|
+
return "TEXT";
|
|
3007
|
+
case "secret":
|
|
3008
|
+
return "TEXT";
|
|
2485
3009
|
case "richtext":
|
|
2486
3010
|
return "BLOB";
|
|
2487
3011
|
}
|
|
@@ -2500,7 +3024,14 @@ var SCALAR_TYPE_MAP = {
|
|
|
2500
3024
|
number: "double",
|
|
2501
3025
|
boolean: "bool",
|
|
2502
3026
|
timestamp: "int64",
|
|
2503
|
-
richtext: "bytes"
|
|
3027
|
+
richtext: "bytes",
|
|
3028
|
+
// object/json values travel as JSON-serialized strings on the proto wire.
|
|
3029
|
+
object: "string",
|
|
3030
|
+
json: "string",
|
|
3031
|
+
// A blob field carries a JSON-serialized content-addressed reference, not bytes.
|
|
3032
|
+
blob: "string",
|
|
3033
|
+
// A secret field's stored value is a JSON string (ciphertext or hash).
|
|
3034
|
+
secret: "string"
|
|
2504
3035
|
};
|
|
2505
3036
|
var ARRAY_ITEM_TYPE_MAP = {
|
|
2506
3037
|
string: "string",
|
|
@@ -2744,6 +3275,7 @@ function generateProtoDefinitions(schema) {
|
|
|
2744
3275
|
MergeConflictError,
|
|
2745
3276
|
MigrationBuilder,
|
|
2746
3277
|
MigrationRollbackError,
|
|
3278
|
+
MissingSecretKeyError,
|
|
2747
3279
|
OperationError,
|
|
2748
3280
|
RemoteClockDriftError,
|
|
2749
3281
|
RollbackBuilder,
|
|
@@ -2760,16 +3292,19 @@ function generateProtoDefinitions(schema) {
|
|
|
2760
3292
|
collectSchemaScopeFields,
|
|
2761
3293
|
collectSchemaScopeValueKeys,
|
|
2762
3294
|
computeDelta,
|
|
3295
|
+
createBlobRef,
|
|
2763
3296
|
createOperation,
|
|
2764
3297
|
createReversibleMigration,
|
|
2765
3298
|
createVersionVector,
|
|
2766
3299
|
decodeBytesFromOpData,
|
|
3300
|
+
decryptSecret,
|
|
2767
3301
|
defaultApplyFailureReason,
|
|
2768
3302
|
defaultSequenceFormat,
|
|
2769
3303
|
defineSchema,
|
|
2770
3304
|
deserializeVector,
|
|
2771
3305
|
dominates,
|
|
2772
3306
|
encodeBytesForOpData,
|
|
3307
|
+
encryptSecret,
|
|
2773
3308
|
extractScopeValuesFromClaims,
|
|
2774
3309
|
extractTimestamp,
|
|
2775
3310
|
formatSequenceValue,
|
|
@@ -2782,8 +3317,11 @@ function generateProtoDefinitions(schema) {
|
|
|
2782
3317
|
getKoraErrorFix,
|
|
2783
3318
|
getTransitionMap,
|
|
2784
3319
|
hasSchemaSyncRules,
|
|
3320
|
+
hashBlob,
|
|
3321
|
+
hashSecret,
|
|
2785
3322
|
isApplyFailure,
|
|
2786
3323
|
isAtomicOp,
|
|
3324
|
+
isBlobRef,
|
|
2787
3325
|
isCollectionSyncScoped,
|
|
2788
3326
|
isKoraBytesValue,
|
|
2789
3327
|
isLegacyNumericByteObject,
|
|
@@ -2793,14 +3331,19 @@ function generateProtoDefinitions(schema) {
|
|
|
2793
3331
|
migrate,
|
|
2794
3332
|
migrationStepsToSQL,
|
|
2795
3333
|
op,
|
|
3334
|
+
quoteIdent,
|
|
2796
3335
|
resolveAtomicOp,
|
|
3336
|
+
revealSecret,
|
|
2797
3337
|
rollbackStepsToSQL,
|
|
2798
3338
|
serializeVector,
|
|
2799
3339
|
t,
|
|
2800
3340
|
toAtomicOp,
|
|
3341
|
+
transformSecretFieldsForWrite,
|
|
2801
3342
|
validateRecord,
|
|
2802
3343
|
validateTransition,
|
|
2803
3344
|
vectorsEqual,
|
|
2804
|
-
verifyOperationIntegrity
|
|
3345
|
+
verifyOperationIntegrity,
|
|
3346
|
+
verifySecret,
|
|
3347
|
+
verifySecretValue
|
|
2805
3348
|
});
|
|
2806
3349
|
//# sourceMappingURL=index.cjs.map
|