@korajs/core 1.0.0-beta.0 → 1.0.0-beta.1
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-BIeawJzC.d.cts → build-scope-map-DtTr9C7A.d.cts} +1 -1
- package/dist/{build-scope-map-DOf4JLTh.d.ts → build-scope-map-DvOHbYjn.d.ts} +1 -1
- package/dist/{events-BynBOsO3.d.cts → events-CqDkr1LN.d.cts} +101 -2
- package/dist/{events-BynBOsO3.d.ts → events-CqDkr1LN.d.ts} +101 -2
- package/dist/index.cjs +528 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +141 -6
- package/dist/index.d.ts +141 -6
- package/dist/index.js +516 -2
- 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-BJtu18Fi.d.ts} +1 -1
- package/dist/{operation-BpZlYSpe.d.cts → operation-CFoY-ZmB.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,
|
|
@@ -88,14 +95,18 @@ __export(index_exports, {
|
|
|
88
95
|
migrationStepsToSQL: () => migrationStepsToSQL,
|
|
89
96
|
op: () => op,
|
|
90
97
|
resolveAtomicOp: () => resolveAtomicOp,
|
|
98
|
+
revealSecret: () => revealSecret,
|
|
91
99
|
rollbackStepsToSQL: () => rollbackStepsToSQL,
|
|
92
100
|
serializeVector: () => serializeVector,
|
|
93
101
|
t: () => t,
|
|
94
102
|
toAtomicOp: () => toAtomicOp,
|
|
103
|
+
transformSecretFieldsForWrite: () => transformSecretFieldsForWrite,
|
|
95
104
|
validateRecord: () => validateRecord,
|
|
96
105
|
validateTransition: () => validateTransition,
|
|
97
106
|
vectorsEqual: () => vectorsEqual,
|
|
98
|
-
verifyOperationIntegrity: () => verifyOperationIntegrity
|
|
107
|
+
verifyOperationIntegrity: () => verifyOperationIntegrity,
|
|
108
|
+
verifySecret: () => verifySecret,
|
|
109
|
+
verifySecretValue: () => verifySecretValue
|
|
99
110
|
});
|
|
100
111
|
module.exports = __toCommonJS(index_exports);
|
|
101
112
|
|
|
@@ -534,6 +545,226 @@ function formatUUID(bytes) {
|
|
|
534
545
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
535
546
|
}
|
|
536
547
|
|
|
548
|
+
// src/blob/blob-ref.ts
|
|
549
|
+
async function hashBlob(bytes) {
|
|
550
|
+
const view = new Uint8Array(bytes.byteLength);
|
|
551
|
+
view.set(bytes);
|
|
552
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", view);
|
|
553
|
+
const out = new Uint8Array(digest);
|
|
554
|
+
return Array.from(out, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
555
|
+
}
|
|
556
|
+
async function createBlobRef(bytes, metadata = {}) {
|
|
557
|
+
const hash = await hashBlob(bytes);
|
|
558
|
+
const ref = { hash, size: bytes.byteLength };
|
|
559
|
+
if (metadata.mimeType !== void 0) {
|
|
560
|
+
ref.mimeType = metadata.mimeType;
|
|
561
|
+
}
|
|
562
|
+
if (metadata.filename !== void 0) {
|
|
563
|
+
ref.filename = metadata.filename;
|
|
564
|
+
}
|
|
565
|
+
return ref;
|
|
566
|
+
}
|
|
567
|
+
function isBlobRef(value) {
|
|
568
|
+
if (typeof value !== "object" || value === null) {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
const ref = value;
|
|
572
|
+
if (typeof ref.hash !== "string" || !/^[0-9a-f]{64}$/.test(ref.hash)) {
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
if (typeof ref.size !== "number" || !Number.isInteger(ref.size) || ref.size < 0) {
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
if (ref.mimeType !== void 0 && typeof ref.mimeType !== "string") {
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
if (ref.filename !== void 0 && typeof ref.filename !== "string") {
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
if (ref.manifestHash !== void 0 && (typeof ref.manifestHash !== "string" || !/^[0-9a-f]{64}$/.test(ref.manifestHash))) {
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// src/secret/secret-crypto.ts
|
|
591
|
+
var PBKDF2_ITERATIONS = 21e4;
|
|
592
|
+
var SALT_BYTES = 16;
|
|
593
|
+
var IV_BYTES = 12;
|
|
594
|
+
var HASH_BYTES = 32;
|
|
595
|
+
function toHex(bytes) {
|
|
596
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
597
|
+
}
|
|
598
|
+
function fromHex(hex) {
|
|
599
|
+
if (hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) {
|
|
600
|
+
throw new Error("Invalid hex string");
|
|
601
|
+
}
|
|
602
|
+
const out = new Uint8Array(hex.length / 2);
|
|
603
|
+
for (let i = 0; i < out.length; i++) {
|
|
604
|
+
out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
605
|
+
}
|
|
606
|
+
return out;
|
|
607
|
+
}
|
|
608
|
+
function randomBytes(n) {
|
|
609
|
+
const out = new Uint8Array(n);
|
|
610
|
+
globalThis.crypto.getRandomValues(out);
|
|
611
|
+
return out;
|
|
612
|
+
}
|
|
613
|
+
function timingSafeEqual(a, b) {
|
|
614
|
+
if (a.length !== b.length) {
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
let diff = 0;
|
|
618
|
+
for (let i = 0; i < a.length; i++) {
|
|
619
|
+
diff |= a[i] ^ b[i];
|
|
620
|
+
}
|
|
621
|
+
return diff === 0;
|
|
622
|
+
}
|
|
623
|
+
async function deriveAesKey(secretKey, salt) {
|
|
624
|
+
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
625
|
+
"raw",
|
|
626
|
+
new TextEncoder().encode(secretKey),
|
|
627
|
+
"PBKDF2",
|
|
628
|
+
false,
|
|
629
|
+
["deriveKey"]
|
|
630
|
+
);
|
|
631
|
+
return globalThis.crypto.subtle.deriveKey(
|
|
632
|
+
{ name: "PBKDF2", salt, iterations: PBKDF2_ITERATIONS, hash: "SHA-256" },
|
|
633
|
+
baseKey,
|
|
634
|
+
{ name: "AES-GCM", length: 256 },
|
|
635
|
+
false,
|
|
636
|
+
["encrypt", "decrypt"]
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
async function encryptSecret(plaintext, secretKey) {
|
|
640
|
+
const salt = randomBytes(SALT_BYTES);
|
|
641
|
+
const iv = randomBytes(IV_BYTES);
|
|
642
|
+
const key = await deriveAesKey(secretKey, salt);
|
|
643
|
+
const ciphertext = await globalThis.crypto.subtle.encrypt(
|
|
644
|
+
{ name: "AES-GCM", iv },
|
|
645
|
+
key,
|
|
646
|
+
new TextEncoder().encode(plaintext)
|
|
647
|
+
);
|
|
648
|
+
return `v1.${toHex(salt)}.${toHex(iv)}.${toHex(new Uint8Array(ciphertext))}`;
|
|
649
|
+
}
|
|
650
|
+
async function decryptSecret(encrypted, secretKey) {
|
|
651
|
+
const parts = encrypted.split(".");
|
|
652
|
+
if (parts.length !== 4 || parts[0] !== "v1") {
|
|
653
|
+
throw new Error("Invalid encrypted secret format");
|
|
654
|
+
}
|
|
655
|
+
const salt = fromHex(parts[1]);
|
|
656
|
+
const iv = fromHex(parts[2]);
|
|
657
|
+
const ciphertext = fromHex(parts[3]);
|
|
658
|
+
const key = await deriveAesKey(secretKey, salt);
|
|
659
|
+
const plaintext = await globalThis.crypto.subtle.decrypt(
|
|
660
|
+
{ name: "AES-GCM", iv },
|
|
661
|
+
key,
|
|
662
|
+
ciphertext
|
|
663
|
+
);
|
|
664
|
+
return new TextDecoder().decode(plaintext);
|
|
665
|
+
}
|
|
666
|
+
async function hashSecret(plaintext) {
|
|
667
|
+
const salt = randomBytes(SALT_BYTES);
|
|
668
|
+
const hash = await pbkdf2(plaintext, salt);
|
|
669
|
+
return `v1.${toHex(salt)}.${toHex(hash)}`;
|
|
670
|
+
}
|
|
671
|
+
async function verifySecret(plaintext, stored) {
|
|
672
|
+
const parts = stored.split(".");
|
|
673
|
+
if (parts.length !== 3 || parts[0] !== "v1") {
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
let salt;
|
|
677
|
+
let expected;
|
|
678
|
+
try {
|
|
679
|
+
salt = fromHex(parts[1]);
|
|
680
|
+
expected = fromHex(parts[2]);
|
|
681
|
+
} catch {
|
|
682
|
+
return false;
|
|
683
|
+
}
|
|
684
|
+
const actual = await pbkdf2(plaintext, salt);
|
|
685
|
+
return timingSafeEqual(actual, expected);
|
|
686
|
+
}
|
|
687
|
+
async function pbkdf2(plaintext, salt) {
|
|
688
|
+
const baseKey = await globalThis.crypto.subtle.importKey(
|
|
689
|
+
"raw",
|
|
690
|
+
new TextEncoder().encode(plaintext),
|
|
691
|
+
"PBKDF2",
|
|
692
|
+
false,
|
|
693
|
+
["deriveBits"]
|
|
694
|
+
);
|
|
695
|
+
const bits = await globalThis.crypto.subtle.deriveBits(
|
|
696
|
+
{ name: "PBKDF2", salt, iterations: PBKDF2_ITERATIONS, hash: "SHA-256" },
|
|
697
|
+
baseKey,
|
|
698
|
+
HASH_BYTES * 8
|
|
699
|
+
);
|
|
700
|
+
return new Uint8Array(bits);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// src/secret/secret-transform.ts
|
|
704
|
+
var MissingSecretKeyError = class extends Error {
|
|
705
|
+
constructor(field) {
|
|
706
|
+
super(
|
|
707
|
+
`Secret field "${field}" is encrypted but no encryption key is configured. Provide sync.encryption.key (or a key provider) to createApp.`
|
|
708
|
+
);
|
|
709
|
+
this.field = field;
|
|
710
|
+
this.name = "MissingSecretKeyError";
|
|
711
|
+
}
|
|
712
|
+
field;
|
|
713
|
+
};
|
|
714
|
+
async function resolveKey(provider) {
|
|
715
|
+
if (!provider) {
|
|
716
|
+
return void 0;
|
|
717
|
+
}
|
|
718
|
+
return provider();
|
|
719
|
+
}
|
|
720
|
+
async function transformSecretFieldsForWrite(data, collectionDef, keyProvider) {
|
|
721
|
+
const hasSecret = Object.values(collectionDef.fields).some((f) => f.kind === "secret");
|
|
722
|
+
if (!hasSecret) {
|
|
723
|
+
return data;
|
|
724
|
+
}
|
|
725
|
+
const result = { ...data };
|
|
726
|
+
let key;
|
|
727
|
+
let keyResolved = false;
|
|
728
|
+
for (const [field, value] of Object.entries(data)) {
|
|
729
|
+
const descriptor = collectionDef.fields[field];
|
|
730
|
+
if (descriptor?.kind !== "secret") {
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
733
|
+
if (typeof value !== "string") {
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
const mode = descriptor.secretMode ?? "encrypted";
|
|
737
|
+
if (mode === "hashed") {
|
|
738
|
+
result[field] = await hashSecret(value);
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
if (!keyResolved) {
|
|
742
|
+
key = await resolveKey(keyProvider);
|
|
743
|
+
keyResolved = true;
|
|
744
|
+
}
|
|
745
|
+
if (key === void 0) {
|
|
746
|
+
throw new MissingSecretKeyError(field);
|
|
747
|
+
}
|
|
748
|
+
result[field] = await encryptSecret(value, key);
|
|
749
|
+
}
|
|
750
|
+
return result;
|
|
751
|
+
}
|
|
752
|
+
async function revealSecret(storedValue, mode, keyProvider, field = "secret") {
|
|
753
|
+
if (mode === "hashed") {
|
|
754
|
+
throw new Error(
|
|
755
|
+
`Secret field "${field}" is hashed (one-way) and cannot be revealed. Use verifySecretValue to check a candidate.`
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
const key = await resolveKey(keyProvider);
|
|
759
|
+
if (key === void 0) {
|
|
760
|
+
throw new MissingSecretKeyError(field);
|
|
761
|
+
}
|
|
762
|
+
return decryptSecret(storedValue, key);
|
|
763
|
+
}
|
|
764
|
+
async function verifySecretValue(candidate, storedValue) {
|
|
765
|
+
return verifySecret(candidate, storedValue);
|
|
766
|
+
}
|
|
767
|
+
|
|
537
768
|
// src/operations/apply-result.ts
|
|
538
769
|
var APPLY_RESULTS = ["applied", "duplicate", "skipped", "rejected", "deferred"];
|
|
539
770
|
var APPLY_FAILURE_CODES = {
|
|
@@ -1465,6 +1696,17 @@ function mapFieldType(descriptor) {
|
|
|
1465
1696
|
case "array":
|
|
1466
1697
|
return "TEXT";
|
|
1467
1698
|
// JSON-serialized
|
|
1699
|
+
case "object":
|
|
1700
|
+
return "TEXT";
|
|
1701
|
+
// JSON-serialized structured object
|
|
1702
|
+
case "json":
|
|
1703
|
+
return "TEXT";
|
|
1704
|
+
// JSON-serialized dynamic-key value
|
|
1705
|
+
case "blob":
|
|
1706
|
+
return "TEXT";
|
|
1707
|
+
// JSON-serialized content-addressed BlobRef (bytes stored out of band)
|
|
1708
|
+
case "secret":
|
|
1709
|
+
return "TEXT";
|
|
1468
1710
|
case "richtext":
|
|
1469
1711
|
return "BLOB";
|
|
1470
1712
|
}
|
|
@@ -1698,6 +1940,160 @@ var ArrayFieldBuilder = class _ArrayFieldBuilder extends FieldBuilder {
|
|
|
1698
1940
|
};
|
|
1699
1941
|
}
|
|
1700
1942
|
};
|
|
1943
|
+
var ObjectFieldBuilder = class _ObjectFieldBuilder extends FieldBuilder {
|
|
1944
|
+
_fields;
|
|
1945
|
+
constructor(fields, required = true, defaultValue = void 0, auto = false, mergeStrategy = null) {
|
|
1946
|
+
super("object", required, defaultValue, auto, mergeStrategy);
|
|
1947
|
+
this._fields = fields;
|
|
1948
|
+
}
|
|
1949
|
+
optional() {
|
|
1950
|
+
return new _ObjectFieldBuilder(
|
|
1951
|
+
this._fields,
|
|
1952
|
+
false,
|
|
1953
|
+
this._defaultValue,
|
|
1954
|
+
this._auto,
|
|
1955
|
+
this._mergeStrategy
|
|
1956
|
+
);
|
|
1957
|
+
}
|
|
1958
|
+
default(value) {
|
|
1959
|
+
return new _ObjectFieldBuilder(this._fields, false, value, this._auto, this._mergeStrategy);
|
|
1960
|
+
}
|
|
1961
|
+
auto() {
|
|
1962
|
+
return new _ObjectFieldBuilder(this._fields, false, void 0, true, this._mergeStrategy);
|
|
1963
|
+
}
|
|
1964
|
+
merge(strategy) {
|
|
1965
|
+
return new _ObjectFieldBuilder(
|
|
1966
|
+
this._fields,
|
|
1967
|
+
this._required,
|
|
1968
|
+
this._defaultValue,
|
|
1969
|
+
this._auto,
|
|
1970
|
+
strategy
|
|
1971
|
+
);
|
|
1972
|
+
}
|
|
1973
|
+
_build() {
|
|
1974
|
+
const nestedFields = {};
|
|
1975
|
+
for (const [key, builder] of Object.entries(this._fields)) {
|
|
1976
|
+
nestedFields[key] = builder._build();
|
|
1977
|
+
}
|
|
1978
|
+
return {
|
|
1979
|
+
kind: "object",
|
|
1980
|
+
required: this._required,
|
|
1981
|
+
defaultValue: this._defaultValue,
|
|
1982
|
+
auto: this._auto,
|
|
1983
|
+
enumValues: null,
|
|
1984
|
+
itemKind: null,
|
|
1985
|
+
mergeStrategy: this._mergeStrategy,
|
|
1986
|
+
transitions: null,
|
|
1987
|
+
nestedFields
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
};
|
|
1991
|
+
var JsonFieldBuilder = class _JsonFieldBuilder extends FieldBuilder {
|
|
1992
|
+
optional() {
|
|
1993
|
+
return new _JsonFieldBuilder(
|
|
1994
|
+
"json",
|
|
1995
|
+
false,
|
|
1996
|
+
this._defaultValue,
|
|
1997
|
+
this._auto,
|
|
1998
|
+
this._mergeStrategy
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
2001
|
+
default(value) {
|
|
2002
|
+
return new _JsonFieldBuilder(
|
|
2003
|
+
"json",
|
|
2004
|
+
false,
|
|
2005
|
+
value,
|
|
2006
|
+
this._auto,
|
|
2007
|
+
this._mergeStrategy
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
auto() {
|
|
2011
|
+
return new _JsonFieldBuilder("json", false, void 0, true, this._mergeStrategy);
|
|
2012
|
+
}
|
|
2013
|
+
merge(strategy) {
|
|
2014
|
+
return new _JsonFieldBuilder(
|
|
2015
|
+
"json",
|
|
2016
|
+
this._required,
|
|
2017
|
+
this._defaultValue,
|
|
2018
|
+
this._auto,
|
|
2019
|
+
strategy
|
|
2020
|
+
);
|
|
2021
|
+
}
|
|
2022
|
+
_build() {
|
|
2023
|
+
return {
|
|
2024
|
+
kind: "json",
|
|
2025
|
+
required: this._required,
|
|
2026
|
+
defaultValue: this._defaultValue,
|
|
2027
|
+
auto: this._auto,
|
|
2028
|
+
enumValues: null,
|
|
2029
|
+
itemKind: null,
|
|
2030
|
+
mergeStrategy: this._mergeStrategy,
|
|
2031
|
+
transitions: null,
|
|
2032
|
+
nestedFields: null
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
};
|
|
2036
|
+
var SecretFieldBuilder = class _SecretFieldBuilder extends FieldBuilder {
|
|
2037
|
+
_secretMode;
|
|
2038
|
+
constructor(secretMode = "encrypted", required = true, defaultValue = void 0, auto = false, mergeStrategy = null) {
|
|
2039
|
+
super("secret", required, defaultValue, auto, mergeStrategy);
|
|
2040
|
+
this._secretMode = secretMode;
|
|
2041
|
+
}
|
|
2042
|
+
/** Store this secret as a one-way salted hash (passwords: verify, never read back). */
|
|
2043
|
+
hashed() {
|
|
2044
|
+
return new _SecretFieldBuilder(
|
|
2045
|
+
"hashed",
|
|
2046
|
+
this._required,
|
|
2047
|
+
this._defaultValue,
|
|
2048
|
+
this._auto,
|
|
2049
|
+
this._mergeStrategy
|
|
2050
|
+
);
|
|
2051
|
+
}
|
|
2052
|
+
/** Store this secret as reversible ciphertext (tokens/keys: decrypt to use). */
|
|
2053
|
+
encrypted() {
|
|
2054
|
+
return new _SecretFieldBuilder(
|
|
2055
|
+
"encrypted",
|
|
2056
|
+
this._required,
|
|
2057
|
+
this._defaultValue,
|
|
2058
|
+
this._auto,
|
|
2059
|
+
this._mergeStrategy
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
2062
|
+
optional() {
|
|
2063
|
+
return new _SecretFieldBuilder(
|
|
2064
|
+
this._secretMode,
|
|
2065
|
+
false,
|
|
2066
|
+
this._defaultValue,
|
|
2067
|
+
this._auto,
|
|
2068
|
+
this._mergeStrategy
|
|
2069
|
+
);
|
|
2070
|
+
}
|
|
2071
|
+
auto() {
|
|
2072
|
+
return new _SecretFieldBuilder(this._secretMode, false, void 0, true, this._mergeStrategy);
|
|
2073
|
+
}
|
|
2074
|
+
merge(strategy) {
|
|
2075
|
+
return new _SecretFieldBuilder(
|
|
2076
|
+
this._secretMode,
|
|
2077
|
+
this._required,
|
|
2078
|
+
this._defaultValue,
|
|
2079
|
+
this._auto,
|
|
2080
|
+
strategy
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
2083
|
+
_build() {
|
|
2084
|
+
return {
|
|
2085
|
+
kind: "secret",
|
|
2086
|
+
required: this._required,
|
|
2087
|
+
defaultValue: this._defaultValue,
|
|
2088
|
+
auto: this._auto,
|
|
2089
|
+
enumValues: null,
|
|
2090
|
+
itemKind: null,
|
|
2091
|
+
mergeStrategy: this._mergeStrategy,
|
|
2092
|
+
transitions: null,
|
|
2093
|
+
secretMode: this._secretMode
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
};
|
|
1701
2097
|
var t = {
|
|
1702
2098
|
string() {
|
|
1703
2099
|
return new FieldBuilder("string", true, void 0, false);
|
|
@@ -1719,6 +2115,18 @@ var t = {
|
|
|
1719
2115
|
},
|
|
1720
2116
|
array(itemBuilder) {
|
|
1721
2117
|
return new ArrayFieldBuilder(itemBuilder, true, void 0, false);
|
|
2118
|
+
},
|
|
2119
|
+
object(fields) {
|
|
2120
|
+
return new ObjectFieldBuilder(fields, true, void 0, false);
|
|
2121
|
+
},
|
|
2122
|
+
json() {
|
|
2123
|
+
return new JsonFieldBuilder("json", true, void 0, false);
|
|
2124
|
+
},
|
|
2125
|
+
blob() {
|
|
2126
|
+
return new FieldBuilder("blob", true, void 0, false);
|
|
2127
|
+
},
|
|
2128
|
+
secret() {
|
|
2129
|
+
return new SecretFieldBuilder("encrypted", true, void 0, false);
|
|
1722
2130
|
}
|
|
1723
2131
|
};
|
|
1724
2132
|
|
|
@@ -1883,8 +2291,99 @@ function validateFieldValue(collection, fieldName, descriptor, value) {
|
|
|
1883
2291
|
}
|
|
1884
2292
|
break;
|
|
1885
2293
|
}
|
|
2294
|
+
case "object": {
|
|
2295
|
+
if (!isPlainObject(value)) {
|
|
2296
|
+
throw new SchemaValidationError(
|
|
2297
|
+
`Field "${fieldName}" in collection "${collection}" must be a plain object, got ${describeType(value)}`,
|
|
2298
|
+
{
|
|
2299
|
+
collection,
|
|
2300
|
+
field: fieldName,
|
|
2301
|
+
expectedType: "object",
|
|
2302
|
+
receivedType: describeType(value)
|
|
2303
|
+
}
|
|
2304
|
+
);
|
|
2305
|
+
}
|
|
2306
|
+
if (descriptor.nestedFields) {
|
|
2307
|
+
for (const [nestedName, nestedDescriptor] of Object.entries(descriptor.nestedFields)) {
|
|
2308
|
+
const nestedValue = value[nestedName];
|
|
2309
|
+
if (nestedValue !== void 0 && nestedValue !== null) {
|
|
2310
|
+
validateFieldValue(
|
|
2311
|
+
collection,
|
|
2312
|
+
`${fieldName}.${nestedName}`,
|
|
2313
|
+
nestedDescriptor,
|
|
2314
|
+
nestedValue
|
|
2315
|
+
);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
break;
|
|
2320
|
+
}
|
|
2321
|
+
case "json": {
|
|
2322
|
+
if (!isJsonSerializable(value)) {
|
|
2323
|
+
throw new SchemaValidationError(
|
|
2324
|
+
`Field "${fieldName}" in collection "${collection}" must be JSON-serializable, got ${describeType(value)}`,
|
|
2325
|
+
{ collection, field: fieldName, expectedType: "json", receivedType: describeType(value) }
|
|
2326
|
+
);
|
|
2327
|
+
}
|
|
2328
|
+
break;
|
|
2329
|
+
}
|
|
2330
|
+
case "blob": {
|
|
2331
|
+
if (!isBlobRef(value)) {
|
|
2332
|
+
throw new SchemaValidationError(
|
|
2333
|
+
`Field "${fieldName}" in collection "${collection}" must be a BlobRef (from the blob store), got ${describeType(value)}`,
|
|
2334
|
+
{ collection, field: fieldName, expectedType: "blob", receivedType: describeType(value) }
|
|
2335
|
+
);
|
|
2336
|
+
}
|
|
2337
|
+
break;
|
|
2338
|
+
}
|
|
2339
|
+
case "secret": {
|
|
2340
|
+
if (typeof value !== "string") {
|
|
2341
|
+
throw new SchemaValidationError(
|
|
2342
|
+
`Field "${fieldName}" in collection "${collection}" must be a string, got ${describeType(value)}`,
|
|
2343
|
+
{
|
|
2344
|
+
collection,
|
|
2345
|
+
field: fieldName,
|
|
2346
|
+
expectedType: "secret",
|
|
2347
|
+
receivedType: describeType(value)
|
|
2348
|
+
}
|
|
2349
|
+
);
|
|
2350
|
+
}
|
|
2351
|
+
break;
|
|
2352
|
+
}
|
|
1886
2353
|
}
|
|
1887
2354
|
}
|
|
2355
|
+
function isPlainObject(value) {
|
|
2356
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2357
|
+
return false;
|
|
2358
|
+
}
|
|
2359
|
+
const proto = Object.getPrototypeOf(value);
|
|
2360
|
+
return proto === null || proto === Object.prototype;
|
|
2361
|
+
}
|
|
2362
|
+
function isJsonSerializable(value) {
|
|
2363
|
+
if (value === null) {
|
|
2364
|
+
return true;
|
|
2365
|
+
}
|
|
2366
|
+
const type = typeof value;
|
|
2367
|
+
if (type === "string" || type === "number" || type === "boolean") {
|
|
2368
|
+
return Number.isFinite(value) || type !== "number";
|
|
2369
|
+
}
|
|
2370
|
+
if (Array.isArray(value)) {
|
|
2371
|
+
return value.every(isJsonSerializable);
|
|
2372
|
+
}
|
|
2373
|
+
if (type === "object") {
|
|
2374
|
+
return Object.values(value).every(isJsonSerializable);
|
|
2375
|
+
}
|
|
2376
|
+
return false;
|
|
2377
|
+
}
|
|
2378
|
+
function describeType(value) {
|
|
2379
|
+
if (value === null) {
|
|
2380
|
+
return "null";
|
|
2381
|
+
}
|
|
2382
|
+
if (Array.isArray(value)) {
|
|
2383
|
+
return "array";
|
|
2384
|
+
}
|
|
2385
|
+
return typeof value;
|
|
2386
|
+
}
|
|
1888
2387
|
function jsTypeForKind(kind) {
|
|
1889
2388
|
switch (kind) {
|
|
1890
2389
|
case "string":
|
|
@@ -2482,6 +2981,14 @@ function mapFieldType2(descriptor) {
|
|
|
2482
2981
|
return "INTEGER";
|
|
2483
2982
|
case "array":
|
|
2484
2983
|
return "TEXT";
|
|
2984
|
+
case "object":
|
|
2985
|
+
return "TEXT";
|
|
2986
|
+
case "json":
|
|
2987
|
+
return "TEXT";
|
|
2988
|
+
case "blob":
|
|
2989
|
+
return "TEXT";
|
|
2990
|
+
case "secret":
|
|
2991
|
+
return "TEXT";
|
|
2485
2992
|
case "richtext":
|
|
2486
2993
|
return "BLOB";
|
|
2487
2994
|
}
|
|
@@ -2500,7 +3007,14 @@ var SCALAR_TYPE_MAP = {
|
|
|
2500
3007
|
number: "double",
|
|
2501
3008
|
boolean: "bool",
|
|
2502
3009
|
timestamp: "int64",
|
|
2503
|
-
richtext: "bytes"
|
|
3010
|
+
richtext: "bytes",
|
|
3011
|
+
// object/json values travel as JSON-serialized strings on the proto wire.
|
|
3012
|
+
object: "string",
|
|
3013
|
+
json: "string",
|
|
3014
|
+
// A blob field carries a JSON-serialized content-addressed reference, not bytes.
|
|
3015
|
+
blob: "string",
|
|
3016
|
+
// A secret field's stored value is a JSON string (ciphertext or hash).
|
|
3017
|
+
secret: "string"
|
|
2504
3018
|
};
|
|
2505
3019
|
var ARRAY_ITEM_TYPE_MAP = {
|
|
2506
3020
|
string: "string",
|
|
@@ -2744,6 +3258,7 @@ function generateProtoDefinitions(schema) {
|
|
|
2744
3258
|
MergeConflictError,
|
|
2745
3259
|
MigrationBuilder,
|
|
2746
3260
|
MigrationRollbackError,
|
|
3261
|
+
MissingSecretKeyError,
|
|
2747
3262
|
OperationError,
|
|
2748
3263
|
RemoteClockDriftError,
|
|
2749
3264
|
RollbackBuilder,
|
|
@@ -2760,16 +3275,19 @@ function generateProtoDefinitions(schema) {
|
|
|
2760
3275
|
collectSchemaScopeFields,
|
|
2761
3276
|
collectSchemaScopeValueKeys,
|
|
2762
3277
|
computeDelta,
|
|
3278
|
+
createBlobRef,
|
|
2763
3279
|
createOperation,
|
|
2764
3280
|
createReversibleMigration,
|
|
2765
3281
|
createVersionVector,
|
|
2766
3282
|
decodeBytesFromOpData,
|
|
3283
|
+
decryptSecret,
|
|
2767
3284
|
defaultApplyFailureReason,
|
|
2768
3285
|
defaultSequenceFormat,
|
|
2769
3286
|
defineSchema,
|
|
2770
3287
|
deserializeVector,
|
|
2771
3288
|
dominates,
|
|
2772
3289
|
encodeBytesForOpData,
|
|
3290
|
+
encryptSecret,
|
|
2773
3291
|
extractScopeValuesFromClaims,
|
|
2774
3292
|
extractTimestamp,
|
|
2775
3293
|
formatSequenceValue,
|
|
@@ -2782,8 +3300,11 @@ function generateProtoDefinitions(schema) {
|
|
|
2782
3300
|
getKoraErrorFix,
|
|
2783
3301
|
getTransitionMap,
|
|
2784
3302
|
hasSchemaSyncRules,
|
|
3303
|
+
hashBlob,
|
|
3304
|
+
hashSecret,
|
|
2785
3305
|
isApplyFailure,
|
|
2786
3306
|
isAtomicOp,
|
|
3307
|
+
isBlobRef,
|
|
2787
3308
|
isCollectionSyncScoped,
|
|
2788
3309
|
isKoraBytesValue,
|
|
2789
3310
|
isLegacyNumericByteObject,
|
|
@@ -2794,13 +3315,17 @@ function generateProtoDefinitions(schema) {
|
|
|
2794
3315
|
migrationStepsToSQL,
|
|
2795
3316
|
op,
|
|
2796
3317
|
resolveAtomicOp,
|
|
3318
|
+
revealSecret,
|
|
2797
3319
|
rollbackStepsToSQL,
|
|
2798
3320
|
serializeVector,
|
|
2799
3321
|
t,
|
|
2800
3322
|
toAtomicOp,
|
|
3323
|
+
transformSecretFieldsForWrite,
|
|
2801
3324
|
validateRecord,
|
|
2802
3325
|
validateTransition,
|
|
2803
3326
|
vectorsEqual,
|
|
2804
|
-
verifyOperationIntegrity
|
|
3327
|
+
verifyOperationIntegrity,
|
|
3328
|
+
verifySecret,
|
|
3329
|
+
verifySecretValue
|
|
2805
3330
|
});
|
|
2806
3331
|
//# sourceMappingURL=index.cjs.map
|