@noy-db/hub 0.2.0-pre.2 → 0.2.0-pre.3
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/aggregate/index.cjs.map +1 -1
- package/dist/aggregate/index.js +2 -2
- package/dist/attestation/index.cjs.map +1 -1
- package/dist/attestation/index.d.cts +2 -2
- package/dist/attestation/index.d.ts +2 -2
- package/dist/attestation/index.js +6 -6
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +3 -3
- package/dist/blobs/index.d.ts +3 -3
- package/dist/blobs/index.js +5 -5
- package/dist/bundle/index.cjs +1245 -6
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +4 -4
- package/dist/bundle/index.d.ts +4 -4
- package/dist/bundle/index.js +10 -10
- package/dist/{chunk-EUYOGYGV.js → chunk-2EYC3WDT.js} +6 -6
- package/dist/{chunk-MUWOSVEP.js → chunk-2XLVPKXG.js} +2 -2
- package/dist/{chunk-NWZ3I6R6.js → chunk-4OQWR46B.js} +5 -5
- package/dist/{chunk-J4KLMEUL.js → chunk-4UBOTYP5.js} +2 -2
- package/dist/{chunk-UND4XIB6.js → chunk-4X2S7PBF.js} +3 -3
- package/dist/{chunk-VE6YVP32.js → chunk-5YHWBPOT.js} +2 -2
- package/dist/{chunk-7BUTTVMR.js → chunk-6S3LLAQ5.js} +2 -2
- package/dist/{chunk-7Z23ZFLV.js → chunk-74JEQFMT.js} +5 -5
- package/dist/{chunk-AHPFONIL.js → chunk-75QDHSE4.js} +5 -5
- package/dist/{chunk-3XHOCQK4.js → chunk-A6SWRXUQ.js} +2 -2
- package/dist/{chunk-PLI5TV7N.js → chunk-BFI3RS42.js} +2 -2
- package/dist/{chunk-CXSCDO5T.js → chunk-EMEX37ZN.js} +2 -2
- package/dist/{chunk-PEULZC6M.js → chunk-EPK6A3WJ.js} +8 -1
- package/dist/chunk-EPK6A3WJ.js.map +1 -0
- package/dist/{chunk-JYQTXEIO.js → chunk-FBMXWVGP.js} +5 -5
- package/dist/{chunk-QXQRKXCU.js → chunk-FCDO7UAO.js} +2 -2
- package/dist/{chunk-243PNUA6.js → chunk-FS7A4XNF.js} +3 -3
- package/dist/{chunk-YS3POABP.js → chunk-FXQYZNOW.js} +1 -1
- package/dist/chunk-FXQYZNOW.js.map +1 -0
- package/dist/{chunk-Y2RKOPNC.js → chunk-G7PAZ3TD.js} +4 -4
- package/dist/{chunk-QPEXPHJR.js → chunk-GAUBWHAF.js} +4 -4
- package/dist/{chunk-GIV6DWBG.js → chunk-GD3BGKAR.js} +2 -2
- package/dist/{chunk-TBKOGSYR.js → chunk-GDTCGIPX.js} +2 -2
- package/dist/{chunk-3Z2TPHC4.js → chunk-GVXBHCZ2.js} +8 -3
- package/dist/chunk-GVXBHCZ2.js.map +1 -0
- package/dist/{chunk-OVZDFEOR.js → chunk-HGZ7DC5H.js} +2 -2
- package/dist/{chunk-VPSUZLOJ.js → chunk-IS5HWQO7.js} +4 -4
- package/dist/{chunk-VPSUZLOJ.js.map → chunk-IS5HWQO7.js.map} +1 -1
- package/dist/{chunk-VK5EER6C.js → chunk-K5PVGKE4.js} +2 -2
- package/dist/{chunk-LRAZDV5X.js → chunk-KMI2NBBF.js} +6 -6
- package/dist/{chunk-VRBCTEKQ.js → chunk-KYKMKLJ6.js} +2 -2
- package/dist/{chunk-PFSNOPBQ.js → chunk-LOL725S4.js} +3 -3
- package/dist/{chunk-3Y53S2SA.js → chunk-LS3JLEIB.js} +4 -4
- package/dist/{chunk-XG3PTSCD.js → chunk-NCO2JGKK.js} +1 -1
- package/dist/chunk-NCO2JGKK.js.map +1 -0
- package/dist/{chunk-YTXSFG3C.js → chunk-NGSPBLLE.js} +2 -2
- package/dist/{chunk-FAQVNJD4.js → chunk-NSLTPGEN.js} +2 -2
- package/dist/{chunk-VCGTOS2A.js → chunk-P6256WTJ.js} +3 -3
- package/dist/{chunk-7Q5PLD5C.js → chunk-QAU5HM6Q.js} +3 -3
- package/dist/{chunk-HXJXPZRE.js → chunk-SAVQ6E2O.js} +2 -2
- package/dist/{chunk-E535SAN4.js → chunk-T6HQMVML.js} +1177 -51
- package/dist/chunk-T6HQMVML.js.map +1 -0
- package/dist/{chunk-Q6W2CMEJ.js → chunk-TLFUDXVV.js} +4 -4
- package/dist/{chunk-2PAQNPE3.js → chunk-UOF74WQY.js} +2 -2
- package/dist/{chunk-3QAKZ37R.js → chunk-UVPGJXVO.js} +5 -5
- package/dist/{chunk-7BRE6EUA.js → chunk-WRLHNG6H.js} +2 -2
- package/dist/{chunk-W3XXT26A.js → chunk-YDLAFP36.js} +43 -1
- package/dist/chunk-YDLAFP36.js.map +1 -0
- package/dist/{chunk-G6FRSBKK.js → chunk-YK72A4IT.js} +4 -4
- package/dist/{chunk-3S4BJX25.js → chunk-YL2DR3HY.js} +2 -2
- package/dist/{chunk-RTZVQAJ7.js → chunk-ZC2AAE6J.js} +2 -2
- package/dist/{chunk-4HIL6AHQ.js → chunk-ZUMGGHRB.js} +4 -4
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +3 -3
- package/dist/consent/index.d.ts +3 -3
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-5ZDIY3NG.js → crypto-H2Y3DDFW.js} +3 -3
- package/dist/{delegation-QYXZW25W.js → delegation-QSC7G5QC.js} +5 -5
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +4 -4
- package/dist/derivations/index.d.ts +4 -4
- package/dist/derivations/index.js +4 -4
- package/dist/{dev-unlock-utkybTKb.d.ts → dev-unlock-Cf2B7Kih.d.ts} +1 -1
- package/dist/{dev-unlock-DQCNDfFp.d.cts → dev-unlock-De3mjQWv.d.cts} +1 -1
- package/dist/executor-BZKFZVRC.js +8 -0
- package/dist/executor-GFZFDQXV.js +8 -0
- package/dist/executor-KT2IOZVP.js +11 -0
- package/dist/{fanout-sidecar-VJ52RIEY.js → fanout-sidecar-NRBWSLRK.js} +2 -2
- package/dist/guards/index.cjs +7 -0
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +4 -4
- package/dist/guards/index.d.ts +4 -4
- package/dist/guards/index.js +4 -4
- package/dist/{hash-DcoYWfJ_.d.ts → hash-gVn_uKhp.d.ts} +1 -1
- package/dist/{hash-jDowCrK2.d.cts → hash-vBCB0-Ps.d.cts} +1 -1
- package/dist/history/index.cjs +1 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +4 -4
- package/dist/history/index.d.ts +4 -4
- package/dist/history/index.js +6 -6
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +3 -3
- package/dist/i18n/index.d.ts +3 -3
- package/dist/i18n/index.js +7 -7
- package/dist/{index-BCKdioeh.d.ts → index-BF1B2HB9.d.ts} +25 -1
- package/dist/{index-BMjrzNZr.d.cts → index-DVkvrgpm.d.cts} +25 -1
- package/dist/index.cjs +1273 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -12
- package/dist/index.d.ts +33 -12
- package/dist/index.js +109 -42
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.js +2 -2
- package/dist/issue-BAJ7ZB4S.js +12 -0
- package/dist/{ledger-3IU5GMXA.js → ledger-WOEJUYTP.js} +6 -6
- package/dist/materialized-views/index.cjs.map +1 -1
- package/dist/materialized-views/index.d.cts +5 -5
- package/dist/materialized-views/index.d.ts +5 -5
- package/dist/materialized-views/index.js +6 -6
- package/dist/noydb-XNQSKXGO.js +34 -0
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +4 -4
- package/dist/overlay-views/index.d.ts +4 -4
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +3 -3
- package/dist/periods/index.d.ts +3 -3
- package/dist/periods/index.js +6 -6
- package/dist/{public-envelope-U3CMEOMV.js → public-envelope-OHQ5UZFM.js} +4 -4
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +3 -3
- package/dist/registry-2IEARCGT.js +7 -0
- package/dist/{registry-3ALP62P6.js → registry-CDHASH73.js} +3 -3
- package/dist/registry-EMGLZGR6.js +8 -0
- package/dist/registry-NQALYR77.js +8 -0
- package/dist/{revoke-KY2GB4KP.js → revoke-7JOVLZFD.js} +6 -6
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +4 -4
- package/dist/session/index.d.ts +4 -4
- package/dist/session/index.js +3 -3
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +3 -3
- package/dist/shadow/index.d.ts +3 -3
- package/dist/shadow/index.js +2 -2
- package/dist/{signer-GRI5TZKH.js → signer-M4K5HBLD.js} +5 -5
- package/dist/{stale-OTOF3FH7.js → stale-PAGCS4K5.js} +2 -2
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +3 -3
- package/dist/store/index.d.ts +3 -3
- package/dist/store/index.js +2 -2
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +2 -2
- package/dist/sync/index.d.ts +2 -2
- package/dist/sync/index.js +4 -4
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +3 -3
- package/dist/team/index.d.ts +3 -3
- package/dist/team/index.js +8 -8
- package/dist/tx/index.cjs +81 -1
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +4 -4
- package/dist/tx/index.d.ts +4 -4
- package/dist/tx/index.js +56 -3
- package/dist/tx/index.js.map +1 -1
- package/dist/{types-DJG8HG6F.d.cts → types-CSLcfytP.d.cts} +528 -5
- package/dist/{types-BoFFiskX.d.ts → types-D9eB0Rvh.d.ts} +528 -5
- package/dist/{ulid-C7ms9oli.d.cts → ulid-CG2YvAbg.d.cts} +1 -1
- package/dist/{ulid-BmBgooGm.d.ts → ulid-CiM2OAeM.d.ts} +1 -1
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/{with-derivation-BKXXa8Vt.d.ts → with-derivation-Bzpj6UTv.d.ts} +1 -1
- package/dist/{with-derivation-BjQ7q4NE.d.cts → with-derivation-DWajFh4K.d.cts} +1 -1
- package/dist/{with-guard-DQme5DKE.d.cts → with-guard-DF_Ul3DT.d.cts} +1 -1
- package/dist/{with-guard-C25yNjzd.d.ts → with-guard-DR7U-l4v.d.ts} +1 -1
- package/dist/{with-materialized-view-BbEPFIIJ.d.cts → with-materialized-view-_piodoIz.d.cts} +1 -1
- package/dist/{with-materialized-view-CqnRwI2S.d.ts → with-materialized-view-qtoJ3xKJ.d.ts} +1 -1
- package/dist/{with-overlayed-view-Ct1fSJt-.d.ts → with-overlayed-view-DFaRfgMr.d.ts} +1 -1
- package/dist/{with-overlayed-view-bwlmmFjx.d.cts → with-overlayed-view-DwzCKxn2.d.cts} +1 -1
- package/package.json +3 -3
- package/dist/chunk-3Z2TPHC4.js.map +0 -1
- package/dist/chunk-E535SAN4.js.map +0 -1
- package/dist/chunk-PEULZC6M.js.map +0 -1
- package/dist/chunk-W3XXT26A.js.map +0 -1
- package/dist/chunk-XG3PTSCD.js.map +0 -1
- package/dist/chunk-YS3POABP.js.map +0 -1
- package/dist/executor-AS2IDHKZ.js +0 -11
- package/dist/executor-HLXFXNFM.js +0 -8
- package/dist/executor-HN6YBHZ5.js +0 -8
- package/dist/issue-ORP37MVW.js +0 -12
- package/dist/noydb-5H3C24GG.js +0 -34
- package/dist/registry-7HE6VJGC.js +0 -8
- package/dist/registry-PSIPG2QR.js +0 -8
- package/dist/registry-RFGGMVNJ.js +0 -7
- /package/dist/{chunk-EUYOGYGV.js.map → chunk-2EYC3WDT.js.map} +0 -0
- /package/dist/{chunk-MUWOSVEP.js.map → chunk-2XLVPKXG.js.map} +0 -0
- /package/dist/{chunk-NWZ3I6R6.js.map → chunk-4OQWR46B.js.map} +0 -0
- /package/dist/{chunk-J4KLMEUL.js.map → chunk-4UBOTYP5.js.map} +0 -0
- /package/dist/{chunk-UND4XIB6.js.map → chunk-4X2S7PBF.js.map} +0 -0
- /package/dist/{chunk-VE6YVP32.js.map → chunk-5YHWBPOT.js.map} +0 -0
- /package/dist/{chunk-7BUTTVMR.js.map → chunk-6S3LLAQ5.js.map} +0 -0
- /package/dist/{chunk-7Z23ZFLV.js.map → chunk-74JEQFMT.js.map} +0 -0
- /package/dist/{chunk-AHPFONIL.js.map → chunk-75QDHSE4.js.map} +0 -0
- /package/dist/{chunk-3XHOCQK4.js.map → chunk-A6SWRXUQ.js.map} +0 -0
- /package/dist/{chunk-PLI5TV7N.js.map → chunk-BFI3RS42.js.map} +0 -0
- /package/dist/{chunk-CXSCDO5T.js.map → chunk-EMEX37ZN.js.map} +0 -0
- /package/dist/{chunk-JYQTXEIO.js.map → chunk-FBMXWVGP.js.map} +0 -0
- /package/dist/{chunk-QXQRKXCU.js.map → chunk-FCDO7UAO.js.map} +0 -0
- /package/dist/{chunk-243PNUA6.js.map → chunk-FS7A4XNF.js.map} +0 -0
- /package/dist/{chunk-Y2RKOPNC.js.map → chunk-G7PAZ3TD.js.map} +0 -0
- /package/dist/{chunk-QPEXPHJR.js.map → chunk-GAUBWHAF.js.map} +0 -0
- /package/dist/{chunk-GIV6DWBG.js.map → chunk-GD3BGKAR.js.map} +0 -0
- /package/dist/{chunk-TBKOGSYR.js.map → chunk-GDTCGIPX.js.map} +0 -0
- /package/dist/{chunk-OVZDFEOR.js.map → chunk-HGZ7DC5H.js.map} +0 -0
- /package/dist/{chunk-VK5EER6C.js.map → chunk-K5PVGKE4.js.map} +0 -0
- /package/dist/{chunk-LRAZDV5X.js.map → chunk-KMI2NBBF.js.map} +0 -0
- /package/dist/{chunk-VRBCTEKQ.js.map → chunk-KYKMKLJ6.js.map} +0 -0
- /package/dist/{chunk-PFSNOPBQ.js.map → chunk-LOL725S4.js.map} +0 -0
- /package/dist/{chunk-3Y53S2SA.js.map → chunk-LS3JLEIB.js.map} +0 -0
- /package/dist/{chunk-YTXSFG3C.js.map → chunk-NGSPBLLE.js.map} +0 -0
- /package/dist/{chunk-FAQVNJD4.js.map → chunk-NSLTPGEN.js.map} +0 -0
- /package/dist/{chunk-VCGTOS2A.js.map → chunk-P6256WTJ.js.map} +0 -0
- /package/dist/{chunk-7Q5PLD5C.js.map → chunk-QAU5HM6Q.js.map} +0 -0
- /package/dist/{chunk-HXJXPZRE.js.map → chunk-SAVQ6E2O.js.map} +0 -0
- /package/dist/{chunk-Q6W2CMEJ.js.map → chunk-TLFUDXVV.js.map} +0 -0
- /package/dist/{chunk-2PAQNPE3.js.map → chunk-UOF74WQY.js.map} +0 -0
- /package/dist/{chunk-3QAKZ37R.js.map → chunk-UVPGJXVO.js.map} +0 -0
- /package/dist/{chunk-7BRE6EUA.js.map → chunk-WRLHNG6H.js.map} +0 -0
- /package/dist/{chunk-G6FRSBKK.js.map → chunk-YK72A4IT.js.map} +0 -0
- /package/dist/{chunk-3S4BJX25.js.map → chunk-YL2DR3HY.js.map} +0 -0
- /package/dist/{chunk-RTZVQAJ7.js.map → chunk-ZC2AAE6J.js.map} +0 -0
- /package/dist/{chunk-4HIL6AHQ.js.map → chunk-ZUMGGHRB.js.map} +0 -0
- /package/dist/{crypto-5ZDIY3NG.js.map → crypto-H2Y3DDFW.js.map} +0 -0
- /package/dist/{delegation-QYXZW25W.js.map → delegation-QSC7G5QC.js.map} +0 -0
- /package/dist/{executor-AS2IDHKZ.js.map → executor-BZKFZVRC.js.map} +0 -0
- /package/dist/{executor-HLXFXNFM.js.map → executor-GFZFDQXV.js.map} +0 -0
- /package/dist/{executor-HN6YBHZ5.js.map → executor-KT2IOZVP.js.map} +0 -0
- /package/dist/{fanout-sidecar-VJ52RIEY.js.map → fanout-sidecar-NRBWSLRK.js.map} +0 -0
- /package/dist/{issue-ORP37MVW.js.map → issue-BAJ7ZB4S.js.map} +0 -0
- /package/dist/{ledger-3IU5GMXA.js.map → ledger-WOEJUYTP.js.map} +0 -0
- /package/dist/{noydb-5H3C24GG.js.map → noydb-XNQSKXGO.js.map} +0 -0
- /package/dist/{public-envelope-U3CMEOMV.js.map → public-envelope-OHQ5UZFM.js.map} +0 -0
- /package/dist/{registry-3ALP62P6.js.map → registry-2IEARCGT.js.map} +0 -0
- /package/dist/{registry-7HE6VJGC.js.map → registry-CDHASH73.js.map} +0 -0
- /package/dist/{registry-PSIPG2QR.js.map → registry-EMGLZGR6.js.map} +0 -0
- /package/dist/{registry-RFGGMVNJ.js.map → registry-NQALYR77.js.map} +0 -0
- /package/dist/{revoke-KY2GB4KP.js.map → revoke-7JOVLZFD.js.map} +0 -0
- /package/dist/{signer-GRI5TZKH.js.map → signer-M4K5HBLD.js.map} +0 -0
- /package/dist/{stale-OTOF3FH7.js.map → stale-PAGCS4K5.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
issueDelegation,
|
|
4
4
|
loadActiveDelegations,
|
|
5
5
|
revokeDelegation
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YK72A4IT.js";
|
|
7
7
|
import {
|
|
8
8
|
Collection,
|
|
9
9
|
DEFAULT_FRESHNESS_MS,
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
saveVaultPolicy,
|
|
40
40
|
validateSchemaInput,
|
|
41
41
|
validateSchemaOutput
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-T6HQMVML.js";
|
|
43
43
|
import {
|
|
44
44
|
DEFAULT_PUBLIC_ENVELOPE_SCHEMA,
|
|
45
45
|
PUBLIC_ENVELOPE_FIELDS,
|
|
@@ -50,24 +50,24 @@ import {
|
|
|
50
50
|
TxContext,
|
|
51
51
|
TxVault,
|
|
52
52
|
runTransaction
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-GVXBHCZ2.js";
|
|
54
54
|
import {
|
|
55
55
|
withDerivation
|
|
56
|
-
} from "./chunk-
|
|
57
|
-
import "./chunk-
|
|
58
|
-
import "./chunk-
|
|
56
|
+
} from "./chunk-EMEX37ZN.js";
|
|
57
|
+
import "./chunk-GD3BGKAR.js";
|
|
58
|
+
import "./chunk-HGZ7DC5H.js";
|
|
59
59
|
import {
|
|
60
60
|
withMaterializedView
|
|
61
|
-
} from "./chunk-
|
|
62
|
-
import "./chunk-
|
|
63
|
-
import "./chunk-
|
|
64
|
-
import "./chunk-
|
|
61
|
+
} from "./chunk-ZC2AAE6J.js";
|
|
62
|
+
import "./chunk-G7PAZ3TD.js";
|
|
63
|
+
import "./chunk-GDTCGIPX.js";
|
|
64
|
+
import "./chunk-BFI3RS42.js";
|
|
65
65
|
import {
|
|
66
66
|
withOverlayedView
|
|
67
|
-
} from "./chunk-
|
|
68
|
-
import "./chunk-
|
|
69
|
-
import "./chunk-
|
|
70
|
-
import "./chunk-
|
|
67
|
+
} from "./chunk-YL2DR3HY.js";
|
|
68
|
+
import "./chunk-NGSPBLLE.js";
|
|
69
|
+
import "./chunk-NSLTPGEN.js";
|
|
70
|
+
import "./chunk-WRLHNG6H.js";
|
|
71
71
|
import {
|
|
72
72
|
mergeCrdtStates,
|
|
73
73
|
resolveCrdtSnapshot
|
|
@@ -82,7 +82,7 @@ import {
|
|
|
82
82
|
readNoydbBundlePublicEnvelope,
|
|
83
83
|
resetBrotliSupportCache,
|
|
84
84
|
writeNoydbBundle
|
|
85
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-P6256WTJ.js";
|
|
86
86
|
import {
|
|
87
87
|
MemoryRecipientSealer,
|
|
88
88
|
MemorySealingKeyProvider,
|
|
@@ -93,7 +93,7 @@ import {
|
|
|
93
93
|
parseSealedEnvelope,
|
|
94
94
|
savePersistedSchema,
|
|
95
95
|
saveSealedPassphrase
|
|
96
|
-
} from "./chunk-
|
|
96
|
+
} from "./chunk-4X2S7PBF.js";
|
|
97
97
|
import {
|
|
98
98
|
PUBLIC_ENVELOPE_RECORD_ID,
|
|
99
99
|
isPublicEnvelope,
|
|
@@ -101,31 +101,31 @@ import {
|
|
|
101
101
|
readPublicEnvelope,
|
|
102
102
|
savePublicEnvelope,
|
|
103
103
|
validatePublicEnvelopeInput
|
|
104
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-FS7A4XNF.js";
|
|
105
105
|
import {
|
|
106
106
|
CONSENT_AUDIT_COLLECTION
|
|
107
|
-
} from "./chunk-
|
|
107
|
+
} from "./chunk-K5PVGKE4.js";
|
|
108
108
|
import {
|
|
109
109
|
PERIODS_COLLECTION
|
|
110
|
-
} from "./chunk-
|
|
110
|
+
} from "./chunk-GAUBWHAF.js";
|
|
111
111
|
import "./chunk-UF3BUNQZ.js";
|
|
112
112
|
import {
|
|
113
113
|
withGuard
|
|
114
|
-
} from "./chunk-
|
|
115
|
-
import "./chunk-
|
|
114
|
+
} from "./chunk-5YHWBPOT.js";
|
|
115
|
+
import "./chunk-EPK6A3WJ.js";
|
|
116
116
|
import "./chunk-UMLVJTYV.js";
|
|
117
|
-
import "./chunk-
|
|
117
|
+
import "./chunk-SAVQ6E2O.js";
|
|
118
118
|
import {
|
|
119
119
|
CollectionFrame,
|
|
120
120
|
VaultFrame
|
|
121
|
-
} from "./chunk-
|
|
121
|
+
} from "./chunk-FCDO7UAO.js";
|
|
122
122
|
import {
|
|
123
123
|
applyI18nLocale,
|
|
124
124
|
i18nText,
|
|
125
125
|
isI18nTextDescriptor,
|
|
126
126
|
resolveI18nText,
|
|
127
127
|
validateI18nTextValue
|
|
128
|
-
} from "./chunk-
|
|
128
|
+
} from "./chunk-A6SWRXUQ.js";
|
|
129
129
|
import {
|
|
130
130
|
DICT_COLLECTION_PREFIX,
|
|
131
131
|
DictionaryHandle,
|
|
@@ -133,7 +133,7 @@ import {
|
|
|
133
133
|
dictKey,
|
|
134
134
|
isDictCollectionName,
|
|
135
135
|
isDictKeyDescriptor
|
|
136
|
-
} from "./chunk-
|
|
136
|
+
} from "./chunk-KMI2NBBF.js";
|
|
137
137
|
import {
|
|
138
138
|
createBundleStore,
|
|
139
139
|
routeStore,
|
|
@@ -145,7 +145,7 @@ import {
|
|
|
145
145
|
withRetry,
|
|
146
146
|
wrapBundleStore,
|
|
147
147
|
wrapStore
|
|
148
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-2XLVPKXG.js";
|
|
149
149
|
import {
|
|
150
150
|
SYNC_CREDENTIALS_COLLECTION,
|
|
151
151
|
credentialStatus,
|
|
@@ -153,7 +153,7 @@ import {
|
|
|
153
153
|
getCredential,
|
|
154
154
|
listCredentials,
|
|
155
155
|
putCredential
|
|
156
|
-
} from "./chunk-
|
|
156
|
+
} from "./chunk-4OQWR46B.js";
|
|
157
157
|
import {
|
|
158
158
|
MAGIC_LINK_CONTENT_INFO_PREFIX,
|
|
159
159
|
MAGIC_LINK_GRANTS_COLLECTION,
|
|
@@ -188,17 +188,17 @@ import {
|
|
|
188
188
|
unwrapDeksFromShamirEntry,
|
|
189
189
|
unwrapMagicLinkGrant,
|
|
190
190
|
writeMagicLinkGrant
|
|
191
|
-
} from "./chunk-
|
|
191
|
+
} from "./chunk-2EYC3WDT.js";
|
|
192
192
|
import {
|
|
193
193
|
assertTierAccess,
|
|
194
194
|
dekKey,
|
|
195
195
|
effectiveClearance
|
|
196
|
-
} from "./chunk-
|
|
196
|
+
} from "./chunk-6S3LLAQ5.js";
|
|
197
197
|
import {
|
|
198
198
|
PresenceHandle,
|
|
199
199
|
SyncEngine,
|
|
200
200
|
SyncTransaction
|
|
201
|
-
} from "./chunk-
|
|
201
|
+
} from "./chunk-LS3JLEIB.js";
|
|
202
202
|
import {
|
|
203
203
|
DIRECTORY_RECORD_ID,
|
|
204
204
|
USER_ENVELOPE_COLLECTION,
|
|
@@ -226,7 +226,7 @@ import {
|
|
|
226
226
|
saveUserEnvelope,
|
|
227
227
|
validatePassphrase,
|
|
228
228
|
visibilityRecordId
|
|
229
|
-
} from "./chunk-
|
|
229
|
+
} from "./chunk-TLFUDXVV.js";
|
|
230
230
|
import {
|
|
231
231
|
BUNDLE_STORE_POLICY,
|
|
232
232
|
INDEXED_STORE_POLICY,
|
|
@@ -246,7 +246,7 @@ import {
|
|
|
246
246
|
revokeAllSessions,
|
|
247
247
|
revokeSession,
|
|
248
248
|
validateSessionPolicy
|
|
249
|
-
} from "./chunk-
|
|
249
|
+
} from "./chunk-QAU5HM6Q.js";
|
|
250
250
|
import {
|
|
251
251
|
generateULID,
|
|
252
252
|
isULID
|
|
@@ -256,14 +256,14 @@ import {
|
|
|
256
256
|
VaultInstant,
|
|
257
257
|
diff,
|
|
258
258
|
formatDiff
|
|
259
|
-
} from "./chunk-
|
|
259
|
+
} from "./chunk-IS5HWQO7.js";
|
|
260
260
|
import {
|
|
261
261
|
LEDGER_COLLECTION,
|
|
262
262
|
LEDGER_DELTAS_COLLECTION,
|
|
263
263
|
LedgerStore,
|
|
264
264
|
applyPatch,
|
|
265
265
|
computePatch
|
|
266
|
-
} from "./chunk-
|
|
266
|
+
} from "./chunk-74JEQFMT.js";
|
|
267
267
|
import {
|
|
268
268
|
canonicalJson,
|
|
269
269
|
envelopePayloadHash,
|
|
@@ -271,7 +271,7 @@ import {
|
|
|
271
271
|
paddedIndex,
|
|
272
272
|
parseIndex,
|
|
273
273
|
sha256Hex
|
|
274
|
-
} from "./chunk-
|
|
274
|
+
} from "./chunk-NCO2JGKK.js";
|
|
275
275
|
import {
|
|
276
276
|
DEFAULT_JOIN_MAX_ROWS,
|
|
277
277
|
Query,
|
|
@@ -280,7 +280,7 @@ import {
|
|
|
280
280
|
buildLiveQuery,
|
|
281
281
|
executePlan,
|
|
282
282
|
resetJoinWarnings
|
|
283
|
-
} from "./chunk-
|
|
283
|
+
} from "./chunk-4UBOTYP5.js";
|
|
284
284
|
import {
|
|
285
285
|
CollectionIndexes
|
|
286
286
|
} from "./chunk-YMYK7US4.js";
|
|
@@ -300,7 +300,7 @@ import {
|
|
|
300
300
|
GroupedQueryN,
|
|
301
301
|
groupAndReduce,
|
|
302
302
|
reduceRecords
|
|
303
|
-
} from "./chunk-
|
|
303
|
+
} from "./chunk-KYKMKLJ6.js";
|
|
304
304
|
import {
|
|
305
305
|
evaluateClause,
|
|
306
306
|
evaluateFieldClause,
|
|
@@ -317,15 +317,15 @@ import {
|
|
|
317
317
|
detectMagic,
|
|
318
318
|
detectMimeType,
|
|
319
319
|
isPreCompressed
|
|
320
|
-
} from "./chunk-
|
|
321
|
-
import "./chunk-
|
|
320
|
+
} from "./chunk-FBMXWVGP.js";
|
|
321
|
+
import "./chunk-LOL725S4.js";
|
|
322
322
|
import {
|
|
323
323
|
NOYDB_BACKUP_VERSION,
|
|
324
324
|
NOYDB_FORMAT_VERSION,
|
|
325
325
|
NOYDB_KEYRING_VERSION,
|
|
326
326
|
NOYDB_SYNC_VERSION,
|
|
327
327
|
createStore
|
|
328
|
-
} from "./chunk-
|
|
328
|
+
} from "./chunk-FXQYZNOW.js";
|
|
329
329
|
import {
|
|
330
330
|
base64ToBuffer,
|
|
331
331
|
bufferToBase64,
|
|
@@ -334,7 +334,7 @@ import {
|
|
|
334
334
|
derivePresenceKey,
|
|
335
335
|
encryptBytes,
|
|
336
336
|
encryptDeterministic
|
|
337
|
-
} from "./chunk-
|
|
337
|
+
} from "./chunk-UOF74WQY.js";
|
|
338
338
|
import {
|
|
339
339
|
AlreadyElevatedError,
|
|
340
340
|
AmendmentForbiddenError,
|
|
@@ -375,9 +375,11 @@ import {
|
|
|
375
375
|
MaterializedViewCycleError,
|
|
376
376
|
MaterializedViewSourceUnknownError,
|
|
377
377
|
MaterializedViewTooLargeError,
|
|
378
|
+
MigrationRequiredError,
|
|
378
379
|
MissingTranslationError,
|
|
379
380
|
NetworkError,
|
|
380
381
|
NoAccessError,
|
|
382
|
+
NonAdditiveSchemaChangeError,
|
|
381
383
|
NotFoundError,
|
|
382
384
|
NoydbError,
|
|
383
385
|
OverlayBaseIsVirtualError,
|
|
@@ -388,11 +390,15 @@ import {
|
|
|
388
390
|
PeriodClosedError,
|
|
389
391
|
PermissionDeniedError,
|
|
390
392
|
PrivilegeEscalationError,
|
|
393
|
+
QuiesceTimeoutError,
|
|
391
394
|
ReadOnlyAtInstantError,
|
|
392
395
|
ReadOnlyError,
|
|
393
396
|
ReadOnlyFrameError,
|
|
394
397
|
RecordLockedError,
|
|
395
398
|
ReservedCollectionNameError,
|
|
399
|
+
SchemaFenceError,
|
|
400
|
+
SchemaLockedError,
|
|
401
|
+
SchemaUpdateError,
|
|
396
402
|
SchemaValidationError,
|
|
397
403
|
SessionExpiredError,
|
|
398
404
|
SessionNotFoundError,
|
|
@@ -403,7 +409,58 @@ import {
|
|
|
403
409
|
TierNotGrantedError,
|
|
404
410
|
TranslatorNotConfiguredError,
|
|
405
411
|
ValidationError
|
|
406
|
-
} from "./chunk-
|
|
412
|
+
} from "./chunk-YDLAFP36.js";
|
|
413
|
+
|
|
414
|
+
// src/schema-update/strategies.ts
|
|
415
|
+
function blindUpdate() {
|
|
416
|
+
return { name: "blindUpdate", onSchemaDelta: () => ({ action: "allow" }) };
|
|
417
|
+
}
|
|
418
|
+
function additiveOnly() {
|
|
419
|
+
return {
|
|
420
|
+
name: "additiveOnly",
|
|
421
|
+
onSchemaDelta(delta) {
|
|
422
|
+
if (delta.kind === "non-additive") {
|
|
423
|
+
return {
|
|
424
|
+
action: "reject",
|
|
425
|
+
error: new NonAdditiveSchemaChangeError(
|
|
426
|
+
`Non-additive schema change to "${delta.collection}" (added: [${delta.added.join(", ")}], removed: [${delta.removed.join(", ")}], changed: [${delta.changed.map((c) => c.field).join(", ")}]). Register a coordinatedCutover() strategy to migrate, or revert the change.`
|
|
427
|
+
)
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
return { action: "allow" };
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
function lockSchema(opts) {
|
|
435
|
+
const fields = opts?.fields;
|
|
436
|
+
return {
|
|
437
|
+
name: "lockSchema",
|
|
438
|
+
onSchemaDelta(delta) {
|
|
439
|
+
if (delta.kind === "none") return { action: "allow" };
|
|
440
|
+
const touched = fields ? [...delta.added, ...delta.removed, ...delta.changed.map((c) => c.field)].filter((f) => fields.includes(f)) : ["<any>"];
|
|
441
|
+
if (touched.length === 0) return { action: "allow" };
|
|
442
|
+
return {
|
|
443
|
+
action: "reject",
|
|
444
|
+
error: new SchemaLockedError(
|
|
445
|
+
`Schema for "${delta.collection}" is locked` + (fields ? ` on fields [${fields.join(", ")}] (touched: [${touched.join(", ")}])` : "") + `; the change was refused.`
|
|
446
|
+
)
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/schema-update/cutover.ts
|
|
453
|
+
function coordinatedCutover(opts) {
|
|
454
|
+
return {
|
|
455
|
+
name: "coordinatedCutover",
|
|
456
|
+
onSchemaDelta(delta) {
|
|
457
|
+
if (delta.kind === "non-additive") {
|
|
458
|
+
return { action: "cutover", transform: opts.transform };
|
|
459
|
+
}
|
|
460
|
+
return { action: "allow" };
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
}
|
|
407
464
|
|
|
408
465
|
// src/vault-diff.ts
|
|
409
466
|
async function diffVault(vault, candidate, options = {}) {
|
|
@@ -636,6 +693,7 @@ export {
|
|
|
636
693
|
MaterializedViewTooLargeError,
|
|
637
694
|
MemoryRecipientSealer,
|
|
638
695
|
MemorySealingKeyProvider,
|
|
696
|
+
MigrationRequiredError,
|
|
639
697
|
MissingTranslationError,
|
|
640
698
|
NOYDB_BACKUP_VERSION,
|
|
641
699
|
NOYDB_BUNDLE_FORMAT_VERSION,
|
|
@@ -646,6 +704,7 @@ export {
|
|
|
646
704
|
NOYDB_SYNC_VERSION,
|
|
647
705
|
NetworkError,
|
|
648
706
|
NoAccessError,
|
|
707
|
+
NonAdditiveSchemaChangeError,
|
|
649
708
|
NotFoundError,
|
|
650
709
|
Noydb,
|
|
651
710
|
NoydbError,
|
|
@@ -667,6 +726,7 @@ export {
|
|
|
667
726
|
PrivilegeEscalationError,
|
|
668
727
|
Query,
|
|
669
728
|
QuickUnlockStore,
|
|
729
|
+
QuiesceTimeoutError,
|
|
670
730
|
ReadOnlyAtInstantError,
|
|
671
731
|
ReadOnlyError,
|
|
672
732
|
ReadOnlyFrameError,
|
|
@@ -682,6 +742,9 @@ export {
|
|
|
682
742
|
STRICT_POLICY,
|
|
683
743
|
SYNC_CREDENTIALS_COLLECTION,
|
|
684
744
|
ScanBuilder,
|
|
745
|
+
SchemaFenceError,
|
|
746
|
+
SchemaLockedError,
|
|
747
|
+
SchemaUpdateError,
|
|
685
748
|
SchemaValidationError,
|
|
686
749
|
SessionExpiredError,
|
|
687
750
|
SessionNotFoundError,
|
|
@@ -708,6 +771,7 @@ export {
|
|
|
708
771
|
VaultInstant,
|
|
709
772
|
WeakPassphraseError,
|
|
710
773
|
activeSessionCount,
|
|
774
|
+
additiveOnly,
|
|
711
775
|
applyI18nLocale,
|
|
712
776
|
applyJoins,
|
|
713
777
|
applyPatch,
|
|
@@ -715,6 +779,7 @@ export {
|
|
|
715
779
|
assertTierAccess,
|
|
716
780
|
avg,
|
|
717
781
|
base64ToBuffer,
|
|
782
|
+
blindUpdate,
|
|
718
783
|
bufferToBase64,
|
|
719
784
|
buildLiveQuery,
|
|
720
785
|
buildRecipientKeyringFile,
|
|
@@ -723,6 +788,7 @@ export {
|
|
|
723
788
|
checkGate,
|
|
724
789
|
clearDevUnlock,
|
|
725
790
|
computePatch,
|
|
791
|
+
coordinatedCutover,
|
|
726
792
|
count,
|
|
727
793
|
createBundleStore,
|
|
728
794
|
createEnforcer,
|
|
@@ -801,6 +867,7 @@ export {
|
|
|
801
867
|
loadShamirRecoveryEntries,
|
|
802
868
|
loadUserEnvelope,
|
|
803
869
|
loadVaultPolicy,
|
|
870
|
+
lockSchema,
|
|
804
871
|
magicLinkGrantRecordId,
|
|
805
872
|
max,
|
|
806
873
|
mergeCrdtStates,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vault-diff.ts"],"sourcesContent":["/**\n * Vault-level diff orchestrator.\n *\n * Compares a live `Vault`'s plaintext state against a candidate state\n * (another vault, a plain `{ collection: records[] }` map, or a vault\n * dump JSON) and returns a structured `VaultDiff` plan listing the\n * records that would be added, modified, or deleted to bring the live\n * vault into the candidate's shape.\n *\n * Builds on two existing record-level helpers:\n *\n * 1. `diff(a, b)` from `./history/diff.ts` — emits dot-pathed\n * `DiffEntry[]` with `type: 'added' | 'removed' | 'changed'` for\n * each changed field of two records. Used here for the\n * `fieldDiffs` of every `modified` entry, and (with empty result)\n * as the default deep-equal check.\n *\n * 2. `Vault.exportStream()` from `./vault.ts` — the canonical\n * decrypt-and-stream-records iterator. Used to walk both sides\n * when the candidate is itself a `Vault`. ACL-scoped: collections\n * the caller can't read silently drop out, the same way every\n * other plaintext-emitting export pipeline filters them.\n *\n * The new orchestration is the **vault-level** enumeration: bucket\n * each record id into added (only in candidate), deleted (only in\n * vault), or modified (in both with field changes); leave the\n * field-level granularity to the existing `diff()`.\n *\n * Use cases:\n *\n * - Import preview (`@noy-db/as-*` `fromString` returns a plan\n * whose body is a `VaultDiff`).\n * - Backup verification (\"does this `.noydb` bundle from yesterday\n * match the current vault?\").\n * - Two-vault reconciliation (\"what's different between Office A\n * and Office B before we sync?\").\n * - Test assertions (golden-file testing with one-liner\n * `expect(plan.summary).toEqual(...)`).\n *\n * @module\n */\n\nimport type { Vault } from './vault.js'\nimport { diff as fieldDiff, type DiffEntry as FieldDiffEntry } from './history/diff.js'\n\n// ─── Public types ──────────────────────────────────────────────────────\n\n/** Per-record entry shape — added and deleted records carry only the record value. */\nexport interface VaultDiffEntry<T = unknown> {\n readonly collection: string\n readonly id: string\n readonly record: T\n}\n\n/** Modified records carry both halves of the diff plus the field-level breakdown. */\nexport interface VaultDiffModifiedEntry<T = unknown> extends VaultDiffEntry<T> {\n /** The record as it stands in the live vault. */\n readonly before: T\n /** Top-level keys whose values differ between `before` and `record`. */\n readonly fieldsChanged: readonly string[]\n /**\n * Field-level diff entries from `diff(before, record)`. Reuses the\n * existing per-record diff helper so consumers can render git-style\n * `path: from → to` rows without re-walking the records.\n */\n readonly fieldDiffs: readonly FieldDiffEntry[]\n}\n\nexport interface VaultDiff<T = unknown> {\n readonly added: readonly VaultDiffEntry<T>[]\n readonly modified: readonly VaultDiffModifiedEntry<T>[]\n readonly deleted: readonly VaultDiffEntry<T>[]\n /** Only populated when `options.includeUnchanged: true`. */\n readonly unchanged: readonly VaultDiffEntry<T>[] | undefined\n readonly summary: {\n readonly add: number\n readonly modify: number\n readonly delete: number\n readonly total: number\n }\n /**\n * Format the diff as a human-readable string.\n *\n * - `'count'` — one line, just the numbers (`12 added · 3 modified · 0 deleted`)\n * - `'one-line'` — count plus a single overview line\n * - `'full'` — count + one row per added/modified/deleted record (default)\n */\n format(opts?: { detail?: 'count' | 'one-line' | 'full' }): string\n}\n\nexport interface DiffOptions {\n /** Restrict the diff to a subset of collections. */\n readonly collections?: readonly string[]\n /** Field on each record that carries its id. Defaults to `'id'`. */\n readonly idKey?: string\n /** Override the default deep-equal check for \"modified vs unchanged\". */\n readonly compareFn?: (a: unknown, b: unknown) => boolean\n /** If true, include unchanged records in the diff (off by default to save memory). */\n readonly includeUnchanged?: boolean\n}\n\n/**\n * Candidate state to diff the vault against:\n *\n * - A `Vault` instance — both sides are walked via `exportStream()`.\n * - A `Record<collection, records[]>` map — same shape `as-json.toObject()`\n * produces. Useful for diffing parsed file content against the live vault.\n * - A `VaultDump` (output of `vault.dump()`) — a JSON string carrying the\n * full vault state. Parsed and reduced to the map shape above.\n */\nexport type DiffCandidate<T = unknown> =\n | Vault\n | Record<string, readonly T[]>\n | string\n\n// ─── Implementation ────────────────────────────────────────────────────\n\n/**\n * Compute the diff between a live vault and a candidate state.\n *\n * Returns a fully buffered `VaultDiff` — no streaming. Memory cost is\n * O(n + m) in the row count of vault + candidate. For documented\n * 1K-50K-record vaults this is fine; a streaming variant lands as a\n * follow-up if a > 100K-record consumer arrives.\n */\nexport async function diffVault<T = unknown>(\n vault: Vault,\n candidate: DiffCandidate<T>,\n options: DiffOptions = {},\n): Promise<VaultDiff<T>> {\n const idKey = options.idKey ?? 'id'\n const filter = options.collections ? new Set(options.collections) : null\n const compareFn =\n options.compareFn ?? ((a: unknown, b: unknown) => fieldDiff(a, b).length === 0)\n\n // Side A — walk the live vault via exportStream(). Each chunk arrives\n // already decrypted and ACL-scoped, so collections the caller can't\n // read silently drop out. exportStream's records are typed `unknown[]`\n // — diffVault is the type-erasure boundary; the caller asserts the\n // record shape via the function's `<T>` generic.\n const live = new Map<string, Map<string, T>>()\n for await (const chunk of vault.exportStream({ granularity: 'collection' })) {\n if (filter && !filter.has(chunk.collection)) continue\n const collection = live.get(chunk.collection) ?? new Map<string, T>()\n for (const record of chunk.records) {\n const id = readIdField(record, idKey)\n if (!id) continue\n collection.set(id, record as T)\n }\n live.set(chunk.collection, collection)\n }\n\n // Side B — normalise the candidate into the same shape.\n const cand = await normaliseCandidate<T>(candidate, idKey, filter)\n\n // Walk every (collection, id) on either side and bucket.\n const added: VaultDiffEntry<T>[] = []\n const modified: VaultDiffModifiedEntry<T>[] = []\n const deleted: VaultDiffEntry<T>[] = []\n const unchanged: VaultDiffEntry<T>[] | undefined = options.includeUnchanged ? [] : undefined\n\n const collectionNames = new Set([...live.keys(), ...cand.keys()])\n for (const collection of [...collectionNames].sort()) {\n const liveColl = live.get(collection) ?? new Map<string, T>()\n const candColl = cand.get(collection) ?? new Map<string, T>()\n const allIds = new Set([...liveColl.keys(), ...candColl.keys()])\n\n for (const id of [...allIds].sort()) {\n const before = liveColl.get(id)\n const after = candColl.get(id)\n\n if (before === undefined && after !== undefined) {\n added.push({ collection, id, record: after })\n } else if (before !== undefined && after === undefined) {\n deleted.push({ collection, id, record: before })\n } else if (before !== undefined && after !== undefined) {\n if (compareFn(before, after)) {\n unchanged?.push({ collection, id, record: after })\n } else {\n const fieldDiffs = fieldDiff(before, after)\n const fieldsChanged = uniqueTopLevelKeys(fieldDiffs)\n modified.push({\n collection,\n id,\n record: after,\n before,\n fieldsChanged,\n fieldDiffs,\n })\n }\n }\n }\n }\n\n const summary = {\n add: added.length,\n modify: modified.length,\n delete: deleted.length,\n total: added.length + modified.length + deleted.length,\n }\n\n return {\n added,\n modified,\n deleted,\n unchanged,\n summary,\n format(opts) {\n return formatDiff(opts?.detail ?? 'full', { added, modified, deleted, summary })\n },\n }\n}\n\n// ─── Internals ─────────────────────────────────────────────────────────\n\nasync function normaliseCandidate<T>(\n candidate: DiffCandidate<T>,\n idKey: string,\n filter: Set<string> | null,\n): Promise<Map<string, Map<string, T>>> {\n const out = new Map<string, Map<string, T>>()\n\n // Vault instance — duck-type via the exportStream method (matches\n // vault.ts's structural shape without forcing a runtime instanceof check\n // that would import the class and risk circular deps).\n if (\n typeof candidate === 'object' &&\n candidate !== null &&\n 'exportStream' in candidate &&\n typeof (candidate as Vault).exportStream === 'function'\n ) {\n for await (const chunk of (candidate as Vault).exportStream({ granularity: 'collection' })) {\n if (filter && !filter.has(chunk.collection)) continue\n const collection = out.get(chunk.collection) ?? new Map<string, T>()\n for (const record of chunk.records) {\n const id = readIdField(record, idKey)\n if (!id) continue\n collection.set(id, record as T)\n }\n out.set(chunk.collection, collection)\n }\n return out\n }\n\n // String — assume a vault.dump() JSON string. Parse and reduce to the map shape.\n if (typeof candidate === 'string') {\n let parsed: unknown\n try {\n parsed = JSON.parse(candidate)\n } catch (err) {\n throw new Error(\n `diffVault: candidate string is not valid JSON (${(err as Error).message})`,\n )\n }\n return collectionsFromObject<T>(parsed, idKey, filter)\n }\n\n // Plain object — `Record<collection, records[]>` (same shape as-json.toObject() returns).\n return collectionsFromObject<T>(candidate, idKey, filter)\n}\n\nfunction collectionsFromObject<T>(\n raw: unknown,\n idKey: string,\n filter: Set<string> | null,\n): Map<string, Map<string, T>> {\n const out = new Map<string, Map<string, T>>()\n if (raw === null || typeof raw !== 'object') {\n throw new Error('diffVault: candidate must be a Vault, an object, or a JSON string')\n }\n // A vault dump JSON has a top-level shape like { _compartment, _keyring, <coll>: <records[]> }.\n // We accept both: keys starting with `_` are skipped (they're metadata), the rest are collections.\n for (const [key, value] of Object.entries(raw)) {\n if (key.startsWith('_')) continue\n if (filter && !filter.has(key)) continue\n if (!Array.isArray(value)) continue\n const collection = new Map<string, T>()\n for (const record of value as readonly T[]) {\n if (record === null || typeof record !== 'object') continue\n const id = readIdField(record, idKey)\n if (!id) continue\n collection.set(id, record)\n }\n out.set(key, collection)\n }\n return out\n}\n\nfunction uniqueTopLevelKeys(diffs: readonly FieldDiffEntry[]): readonly string[] {\n const keys = new Set<string>()\n for (const d of diffs) {\n // path is dot-separated; the top-level key is everything before the\n // first `.` or `[`. (`a.b.c` → `a`, `tags[0]` → `tags`, `(root)` → `(root)`).\n const m = /^[^.[]+/.exec(d.path)\n if (m) keys.add(m[0])\n }\n return [...keys]\n}\n\n/**\n * Pull the id field off a record without going through `String(obj)`,\n * which would emit `[object Object]` for nested objects and silently\n * collapse rows that share the same parent. Only string and number ids\n * are accepted; anything else returns the empty string and the record\n * is skipped at the call site.\n */\nfunction readIdField(record: unknown, idKey: string): string {\n if (record === null || typeof record !== 'object') return ''\n const v = (record as Record<string, unknown>)[idKey]\n if (typeof v === 'string') return v\n if (typeof v === 'number' && Number.isFinite(v)) return String(v)\n return ''\n}\n\ninterface FormatBuckets<T> {\n readonly added: readonly VaultDiffEntry<T>[]\n readonly modified: readonly VaultDiffModifiedEntry<T>[]\n readonly deleted: readonly VaultDiffEntry<T>[]\n readonly summary: VaultDiff<T>['summary']\n}\n\nfunction formatDiff<T>(\n detail: 'count' | 'one-line' | 'full',\n b: FormatBuckets<T>,\n): string {\n const head = `${b.summary.add} added · ${b.summary.modify} modified · ${b.summary.delete} deleted`\n if (detail === 'count') return head\n if (b.summary.total === 0) return head + '\\n(no changes)'\n if (detail === 'one-line') return head\n\n const rows: string[] = [head, '']\n for (const e of b.added) rows.push(`${e.collection}/${e.id}\\tadded`)\n for (const e of b.modified) {\n const fields = e.fieldDiffs\n .map((f) => `${f.path}: ${shortJSON(f.from)} → ${shortJSON(f.to)}`)\n .join(', ')\n rows.push(`${e.collection}/${e.id}\\tmodified\\t${fields}`)\n }\n for (const e of b.deleted) rows.push(`${e.collection}/${e.id}\\tdeleted`)\n return rows.join('\\n')\n}\n\nfunction shortJSON(value: unknown): string {\n if (value === undefined) return 'undefined'\n const s = JSON.stringify(value)\n // JSON.stringify returns string for any JSON value except `undefined`\n // (handled above), `function`, and `symbol`. Fall back to a static\n // tag for those — never let an arbitrary object hit the default\n // stringifier (which the lint rule explicitly bans).\n if (typeof s !== 'string') return '<unrepresentable>'\n return s.length > 60 ? s.slice(0, 57) + '...' : s\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6HA,eAAsB,UACpB,OACA,WACA,UAAuB,CAAC,GACD;AACvB,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,SAAS,QAAQ,cAAc,IAAI,IAAI,QAAQ,WAAW,IAAI;AACpE,QAAM,YACJ,QAAQ,cAAc,CAAC,GAAY,MAAe,KAAU,GAAG,CAAC,EAAE,WAAW;AAO/E,QAAM,OAAO,oBAAI,IAA4B;AAC7C,mBAAiB,SAAS,MAAM,aAAa,EAAE,aAAa,aAAa,CAAC,GAAG;AAC3E,QAAI,UAAU,CAAC,OAAO,IAAI,MAAM,UAAU,EAAG;AAC7C,UAAM,aAAa,KAAK,IAAI,MAAM,UAAU,KAAK,oBAAI,IAAe;AACpE,eAAW,UAAU,MAAM,SAAS;AAClC,YAAM,KAAK,YAAY,QAAQ,KAAK;AACpC,UAAI,CAAC,GAAI;AACT,iBAAW,IAAI,IAAI,MAAW;AAAA,IAChC;AACA,SAAK,IAAI,MAAM,YAAY,UAAU;AAAA,EACvC;AAGA,QAAM,OAAO,MAAM,mBAAsB,WAAW,OAAO,MAAM;AAGjE,QAAM,QAA6B,CAAC;AACpC,QAAM,WAAwC,CAAC;AAC/C,QAAM,UAA+B,CAAC;AACtC,QAAM,YAA6C,QAAQ,mBAAmB,CAAC,IAAI;AAEnF,QAAM,kBAAkB,oBAAI,IAAI,CAAC,GAAG,KAAK,KAAK,GAAG,GAAG,KAAK,KAAK,CAAC,CAAC;AAChE,aAAW,cAAc,CAAC,GAAG,eAAe,EAAE,KAAK,GAAG;AACpD,UAAM,WAAW,KAAK,IAAI,UAAU,KAAK,oBAAI,IAAe;AAC5D,UAAM,WAAW,KAAK,IAAI,UAAU,KAAK,oBAAI,IAAe;AAC5D,UAAM,SAAS,oBAAI,IAAI,CAAC,GAAG,SAAS,KAAK,GAAG,GAAG,SAAS,KAAK,CAAC,CAAC;AAE/D,eAAW,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,GAAG;AACnC,YAAM,SAAS,SAAS,IAAI,EAAE;AAC9B,YAAM,QAAQ,SAAS,IAAI,EAAE;AAE7B,UAAI,WAAW,UAAa,UAAU,QAAW;AAC/C,cAAM,KAAK,EAAE,YAAY,IAAI,QAAQ,MAAM,CAAC;AAAA,MAC9C,WAAW,WAAW,UAAa,UAAU,QAAW;AACtD,gBAAQ,KAAK,EAAE,YAAY,IAAI,QAAQ,OAAO,CAAC;AAAA,MACjD,WAAW,WAAW,UAAa,UAAU,QAAW;AACtD,YAAI,UAAU,QAAQ,KAAK,GAAG;AAC5B,qBAAW,KAAK,EAAE,YAAY,IAAI,QAAQ,MAAM,CAAC;AAAA,QACnD,OAAO;AACL,gBAAM,aAAa,KAAU,QAAQ,KAAK;AAC1C,gBAAM,gBAAgB,mBAAmB,UAAU;AACnD,mBAAS,KAAK;AAAA,YACZ;AAAA,YACA;AAAA,YACA,QAAQ;AAAA,YACR;AAAA,YACA;AAAA,YACA;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU;AAAA,IACd,KAAK,MAAM;AAAA,IACX,QAAQ,SAAS;AAAA,IACjB,QAAQ,QAAQ;AAAA,IAChB,OAAO,MAAM,SAAS,SAAS,SAAS,QAAQ;AAAA,EAClD;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,MAAM;AACX,aAAOA,YAAW,MAAM,UAAU,QAAQ,EAAE,OAAO,UAAU,SAAS,QAAQ,CAAC;AAAA,IACjF;AAAA,EACF;AACF;AAIA,eAAe,mBACb,WACA,OACA,QACsC;AACtC,QAAM,MAAM,oBAAI,IAA4B;AAK5C,MACE,OAAO,cAAc,YACrB,cAAc,QACd,kBAAkB,aAClB,OAAQ,UAAoB,iBAAiB,YAC7C;AACA,qBAAiB,SAAU,UAAoB,aAAa,EAAE,aAAa,aAAa,CAAC,GAAG;AAC1F,UAAI,UAAU,CAAC,OAAO,IAAI,MAAM,UAAU,EAAG;AAC7C,YAAM,aAAa,IAAI,IAAI,MAAM,UAAU,KAAK,oBAAI,IAAe;AACnE,iBAAW,UAAU,MAAM,SAAS;AAClC,cAAM,KAAK,YAAY,QAAQ,KAAK;AACpC,YAAI,CAAC,GAAI;AACT,mBAAW,IAAI,IAAI,MAAW;AAAA,MAChC;AACA,UAAI,IAAI,MAAM,YAAY,UAAU;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,cAAc,UAAU;AACjC,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,SAAS;AAAA,IAC/B,SAAS,KAAK;AACZ,YAAM,IAAI;AAAA,QACR,kDAAmD,IAAc,OAAO;AAAA,MAC1E;AAAA,IACF;AACA,WAAO,sBAAyB,QAAQ,OAAO,MAAM;AAAA,EACvD;AAGA,SAAO,sBAAyB,WAAW,OAAO,MAAM;AAC1D;AAEA,SAAS,sBACP,KACA,OACA,QAC6B;AAC7B,QAAM,MAAM,oBAAI,IAA4B;AAC5C,MAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU;AAC3C,UAAM,IAAI,MAAM,mEAAmE;AAAA,EACrF;AAGA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,QAAI,IAAI,WAAW,GAAG,EAAG;AACzB,QAAI,UAAU,CAAC,OAAO,IAAI,GAAG,EAAG;AAChC,QAAI,CAAC,MAAM,QAAQ,KAAK,EAAG;AAC3B,UAAM,aAAa,oBAAI,IAAe;AACtC,eAAW,UAAU,OAAuB;AAC1C,UAAI,WAAW,QAAQ,OAAO,WAAW,SAAU;AACnD,YAAM,KAAK,YAAY,QAAQ,KAAK;AACpC,UAAI,CAAC,GAAI;AACT,iBAAW,IAAI,IAAI,MAAM;AAAA,IAC3B;AACA,QAAI,IAAI,KAAK,UAAU;AAAA,EACzB;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,OAAqD;AAC/E,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,KAAK,OAAO;AAGrB,UAAM,IAAI,UAAU,KAAK,EAAE,IAAI;AAC/B,QAAI,EAAG,MAAK,IAAI,EAAE,CAAC,CAAC;AAAA,EACtB;AACA,SAAO,CAAC,GAAG,IAAI;AACjB;AASA,SAAS,YAAY,QAAiB,OAAuB;AAC3D,MAAI,WAAW,QAAQ,OAAO,WAAW,SAAU,QAAO;AAC1D,QAAM,IAAK,OAAmC,KAAK;AACnD,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,EAAG,QAAO,OAAO,CAAC;AAChE,SAAO;AACT;AASA,SAASA,YACP,QACA,GACQ;AACR,QAAM,OAAO,GAAG,EAAE,QAAQ,GAAG,eAAY,EAAE,QAAQ,MAAM,kBAAe,EAAE,QAAQ,MAAM;AACxF,MAAI,WAAW,QAAS,QAAO;AAC/B,MAAI,EAAE,QAAQ,UAAU,EAAG,QAAO,OAAO;AACzC,MAAI,WAAW,WAAY,QAAO;AAElC,QAAM,OAAiB,CAAC,MAAM,EAAE;AAChC,aAAW,KAAK,EAAE,MAAO,MAAK,KAAK,GAAG,EAAE,UAAU,IAAI,EAAE,EAAE,QAAS;AACnE,aAAW,KAAK,EAAE,UAAU;AAC1B,UAAM,SAAS,EAAE,WACd,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,KAAK,UAAU,EAAE,IAAI,CAAC,WAAM,UAAU,EAAE,EAAE,CAAC,EAAE,EACjE,KAAK,IAAI;AACZ,SAAK,KAAK,GAAG,EAAE,UAAU,IAAI,EAAE,EAAE,aAAe,MAAM,EAAE;AAAA,EAC1D;AACA,aAAW,KAAK,EAAE,QAAS,MAAK,KAAK,GAAG,EAAE,UAAU,IAAI,EAAE,EAAE,UAAW;AACvE,SAAO,KAAK,KAAK,IAAI;AACvB;AAEA,SAAS,UAAU,OAAwB;AACzC,MAAI,UAAU,OAAW,QAAO;AAChC,QAAM,IAAI,KAAK,UAAU,KAAK;AAK9B,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,SAAO,EAAE,SAAS,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI,QAAQ;AAClD;","names":["formatDiff"]}
|
|
1
|
+
{"version":3,"sources":["../src/schema-update/strategies.ts","../src/schema-update/cutover.ts","../src/vault-diff.ts"],"sourcesContent":["/** Bundled light update strategies (#245). */\nimport { NonAdditiveSchemaChangeError, SchemaLockedError } from '../errors.js'\nimport type { SchemaUpdateStrategy, SchemaDelta } from './types.js'\n\n/** Allow any schema change. Explicit blind / back-compat. */\nexport function blindUpdate(): SchemaUpdateStrategy {\n return { name: 'blindUpdate', onSchemaDelta: () => ({ action: 'allow' }) }\n}\n\n/** Allow additive changes; reject non-additive ones. The safety backstop. */\nexport function additiveOnly(): SchemaUpdateStrategy {\n return {\n name: 'additiveOnly',\n onSchemaDelta(delta: SchemaDelta) {\n if (delta.kind === 'non-additive') {\n return {\n action: 'reject' as const,\n error: new NonAdditiveSchemaChangeError(\n `Non-additive schema change to \"${delta.collection}\" ` +\n `(added: [${delta.added.join(', ')}], removed: [${delta.removed.join(', ')}], ` +\n `changed: [${delta.changed.map(c => c.field).join(', ')}]). ` +\n `Register a coordinatedCutover() strategy to migrate, or revert the change.`,\n ),\n }\n }\n return { action: 'allow' as const }\n },\n }\n}\n\n/**\n * Reject schema changes. With `fields`, reject only when one of those\n * fields is added/removed/changed; otherwise reject any non-`none` delta.\n */\nexport function lockSchema(opts?: { readonly fields?: readonly string[] }): SchemaUpdateStrategy {\n const fields = opts?.fields\n return {\n name: 'lockSchema',\n onSchemaDelta(delta: SchemaDelta) {\n if (delta.kind === 'none') return { action: 'allow' as const }\n const touched = fields\n ? [...delta.added, ...delta.removed, ...delta.changed.map(c => c.field)].filter(f => fields.includes(f))\n : ['<any>']\n if (touched.length === 0) return { action: 'allow' as const }\n return {\n action: 'reject' as const,\n error: new SchemaLockedError(\n `Schema for \"${delta.collection}\" is locked` +\n (fields ? ` on fields [${fields.join(', ')}] (touched: [${touched.join(', ')}])` : '') +\n `; the change was refused.`,\n ),\n }\n },\n }\n}\n","/** The coordinatedCutover update strategy (#232, single-step — no from/to). */\nimport type { SchemaUpdateStrategy, SchemaDelta, TransformFn } from './types.js'\n\nexport function coordinatedCutover(opts: { readonly transform: TransformFn }): SchemaUpdateStrategy {\n return {\n name: 'coordinatedCutover',\n onSchemaDelta(delta: SchemaDelta) {\n if (delta.kind === 'non-additive') {\n return { action: 'cutover' as const, transform: opts.transform }\n }\n return { action: 'allow' as const }\n },\n }\n}\n","/**\n * Vault-level diff orchestrator.\n *\n * Compares a live `Vault`'s plaintext state against a candidate state\n * (another vault, a plain `{ collection: records[] }` map, or a vault\n * dump JSON) and returns a structured `VaultDiff` plan listing the\n * records that would be added, modified, or deleted to bring the live\n * vault into the candidate's shape.\n *\n * Builds on two existing record-level helpers:\n *\n * 1. `diff(a, b)` from `./history/diff.ts` — emits dot-pathed\n * `DiffEntry[]` with `type: 'added' | 'removed' | 'changed'` for\n * each changed field of two records. Used here for the\n * `fieldDiffs` of every `modified` entry, and (with empty result)\n * as the default deep-equal check.\n *\n * 2. `Vault.exportStream()` from `./vault.ts` — the canonical\n * decrypt-and-stream-records iterator. Used to walk both sides\n * when the candidate is itself a `Vault`. ACL-scoped: collections\n * the caller can't read silently drop out, the same way every\n * other plaintext-emitting export pipeline filters them.\n *\n * The new orchestration is the **vault-level** enumeration: bucket\n * each record id into added (only in candidate), deleted (only in\n * vault), or modified (in both with field changes); leave the\n * field-level granularity to the existing `diff()`.\n *\n * Use cases:\n *\n * - Import preview (`@noy-db/as-*` `fromString` returns a plan\n * whose body is a `VaultDiff`).\n * - Backup verification (\"does this `.noydb` bundle from yesterday\n * match the current vault?\").\n * - Two-vault reconciliation (\"what's different between Office A\n * and Office B before we sync?\").\n * - Test assertions (golden-file testing with one-liner\n * `expect(plan.summary).toEqual(...)`).\n *\n * @module\n */\n\nimport type { Vault } from './vault.js'\nimport { diff as fieldDiff, type DiffEntry as FieldDiffEntry } from './history/diff.js'\n\n// ─── Public types ──────────────────────────────────────────────────────\n\n/** Per-record entry shape — added and deleted records carry only the record value. */\nexport interface VaultDiffEntry<T = unknown> {\n readonly collection: string\n readonly id: string\n readonly record: T\n}\n\n/** Modified records carry both halves of the diff plus the field-level breakdown. */\nexport interface VaultDiffModifiedEntry<T = unknown> extends VaultDiffEntry<T> {\n /** The record as it stands in the live vault. */\n readonly before: T\n /** Top-level keys whose values differ between `before` and `record`. */\n readonly fieldsChanged: readonly string[]\n /**\n * Field-level diff entries from `diff(before, record)`. Reuses the\n * existing per-record diff helper so consumers can render git-style\n * `path: from → to` rows without re-walking the records.\n */\n readonly fieldDiffs: readonly FieldDiffEntry[]\n}\n\nexport interface VaultDiff<T = unknown> {\n readonly added: readonly VaultDiffEntry<T>[]\n readonly modified: readonly VaultDiffModifiedEntry<T>[]\n readonly deleted: readonly VaultDiffEntry<T>[]\n /** Only populated when `options.includeUnchanged: true`. */\n readonly unchanged: readonly VaultDiffEntry<T>[] | undefined\n readonly summary: {\n readonly add: number\n readonly modify: number\n readonly delete: number\n readonly total: number\n }\n /**\n * Format the diff as a human-readable string.\n *\n * - `'count'` — one line, just the numbers (`12 added · 3 modified · 0 deleted`)\n * - `'one-line'` — count plus a single overview line\n * - `'full'` — count + one row per added/modified/deleted record (default)\n */\n format(opts?: { detail?: 'count' | 'one-line' | 'full' }): string\n}\n\nexport interface DiffOptions {\n /** Restrict the diff to a subset of collections. */\n readonly collections?: readonly string[]\n /** Field on each record that carries its id. Defaults to `'id'`. */\n readonly idKey?: string\n /** Override the default deep-equal check for \"modified vs unchanged\". */\n readonly compareFn?: (a: unknown, b: unknown) => boolean\n /** If true, include unchanged records in the diff (off by default to save memory). */\n readonly includeUnchanged?: boolean\n}\n\n/**\n * Candidate state to diff the vault against:\n *\n * - A `Vault` instance — both sides are walked via `exportStream()`.\n * - A `Record<collection, records[]>` map — same shape `as-json.toObject()`\n * produces. Useful for diffing parsed file content against the live vault.\n * - A `VaultDump` (output of `vault.dump()`) — a JSON string carrying the\n * full vault state. Parsed and reduced to the map shape above.\n */\nexport type DiffCandidate<T = unknown> =\n | Vault\n | Record<string, readonly T[]>\n | string\n\n// ─── Implementation ────────────────────────────────────────────────────\n\n/**\n * Compute the diff between a live vault and a candidate state.\n *\n * Returns a fully buffered `VaultDiff` — no streaming. Memory cost is\n * O(n + m) in the row count of vault + candidate. For documented\n * 1K-50K-record vaults this is fine; a streaming variant lands as a\n * follow-up if a > 100K-record consumer arrives.\n */\nexport async function diffVault<T = unknown>(\n vault: Vault,\n candidate: DiffCandidate<T>,\n options: DiffOptions = {},\n): Promise<VaultDiff<T>> {\n const idKey = options.idKey ?? 'id'\n const filter = options.collections ? new Set(options.collections) : null\n const compareFn =\n options.compareFn ?? ((a: unknown, b: unknown) => fieldDiff(a, b).length === 0)\n\n // Side A — walk the live vault via exportStream(). Each chunk arrives\n // already decrypted and ACL-scoped, so collections the caller can't\n // read silently drop out. exportStream's records are typed `unknown[]`\n // — diffVault is the type-erasure boundary; the caller asserts the\n // record shape via the function's `<T>` generic.\n const live = new Map<string, Map<string, T>>()\n for await (const chunk of vault.exportStream({ granularity: 'collection' })) {\n if (filter && !filter.has(chunk.collection)) continue\n const collection = live.get(chunk.collection) ?? new Map<string, T>()\n for (const record of chunk.records) {\n const id = readIdField(record, idKey)\n if (!id) continue\n collection.set(id, record as T)\n }\n live.set(chunk.collection, collection)\n }\n\n // Side B — normalise the candidate into the same shape.\n const cand = await normaliseCandidate<T>(candidate, idKey, filter)\n\n // Walk every (collection, id) on either side and bucket.\n const added: VaultDiffEntry<T>[] = []\n const modified: VaultDiffModifiedEntry<T>[] = []\n const deleted: VaultDiffEntry<T>[] = []\n const unchanged: VaultDiffEntry<T>[] | undefined = options.includeUnchanged ? [] : undefined\n\n const collectionNames = new Set([...live.keys(), ...cand.keys()])\n for (const collection of [...collectionNames].sort()) {\n const liveColl = live.get(collection) ?? new Map<string, T>()\n const candColl = cand.get(collection) ?? new Map<string, T>()\n const allIds = new Set([...liveColl.keys(), ...candColl.keys()])\n\n for (const id of [...allIds].sort()) {\n const before = liveColl.get(id)\n const after = candColl.get(id)\n\n if (before === undefined && after !== undefined) {\n added.push({ collection, id, record: after })\n } else if (before !== undefined && after === undefined) {\n deleted.push({ collection, id, record: before })\n } else if (before !== undefined && after !== undefined) {\n if (compareFn(before, after)) {\n unchanged?.push({ collection, id, record: after })\n } else {\n const fieldDiffs = fieldDiff(before, after)\n const fieldsChanged = uniqueTopLevelKeys(fieldDiffs)\n modified.push({\n collection,\n id,\n record: after,\n before,\n fieldsChanged,\n fieldDiffs,\n })\n }\n }\n }\n }\n\n const summary = {\n add: added.length,\n modify: modified.length,\n delete: deleted.length,\n total: added.length + modified.length + deleted.length,\n }\n\n return {\n added,\n modified,\n deleted,\n unchanged,\n summary,\n format(opts) {\n return formatDiff(opts?.detail ?? 'full', { added, modified, deleted, summary })\n },\n }\n}\n\n// ─── Internals ─────────────────────────────────────────────────────────\n\nasync function normaliseCandidate<T>(\n candidate: DiffCandidate<T>,\n idKey: string,\n filter: Set<string> | null,\n): Promise<Map<string, Map<string, T>>> {\n const out = new Map<string, Map<string, T>>()\n\n // Vault instance — duck-type via the exportStream method (matches\n // vault.ts's structural shape without forcing a runtime instanceof check\n // that would import the class and risk circular deps).\n if (\n typeof candidate === 'object' &&\n candidate !== null &&\n 'exportStream' in candidate &&\n typeof (candidate as Vault).exportStream === 'function'\n ) {\n for await (const chunk of (candidate as Vault).exportStream({ granularity: 'collection' })) {\n if (filter && !filter.has(chunk.collection)) continue\n const collection = out.get(chunk.collection) ?? new Map<string, T>()\n for (const record of chunk.records) {\n const id = readIdField(record, idKey)\n if (!id) continue\n collection.set(id, record as T)\n }\n out.set(chunk.collection, collection)\n }\n return out\n }\n\n // String — assume a vault.dump() JSON string. Parse and reduce to the map shape.\n if (typeof candidate === 'string') {\n let parsed: unknown\n try {\n parsed = JSON.parse(candidate)\n } catch (err) {\n throw new Error(\n `diffVault: candidate string is not valid JSON (${(err as Error).message})`,\n )\n }\n return collectionsFromObject<T>(parsed, idKey, filter)\n }\n\n // Plain object — `Record<collection, records[]>` (same shape as-json.toObject() returns).\n return collectionsFromObject<T>(candidate, idKey, filter)\n}\n\nfunction collectionsFromObject<T>(\n raw: unknown,\n idKey: string,\n filter: Set<string> | null,\n): Map<string, Map<string, T>> {\n const out = new Map<string, Map<string, T>>()\n if (raw === null || typeof raw !== 'object') {\n throw new Error('diffVault: candidate must be a Vault, an object, or a JSON string')\n }\n // A vault dump JSON has a top-level shape like { _compartment, _keyring, <coll>: <records[]> }.\n // We accept both: keys starting with `_` are skipped (they're metadata), the rest are collections.\n for (const [key, value] of Object.entries(raw)) {\n if (key.startsWith('_')) continue\n if (filter && !filter.has(key)) continue\n if (!Array.isArray(value)) continue\n const collection = new Map<string, T>()\n for (const record of value as readonly T[]) {\n if (record === null || typeof record !== 'object') continue\n const id = readIdField(record, idKey)\n if (!id) continue\n collection.set(id, record)\n }\n out.set(key, collection)\n }\n return out\n}\n\nfunction uniqueTopLevelKeys(diffs: readonly FieldDiffEntry[]): readonly string[] {\n const keys = new Set<string>()\n for (const d of diffs) {\n // path is dot-separated; the top-level key is everything before the\n // first `.` or `[`. (`a.b.c` → `a`, `tags[0]` → `tags`, `(root)` → `(root)`).\n const m = /^[^.[]+/.exec(d.path)\n if (m) keys.add(m[0])\n }\n return [...keys]\n}\n\n/**\n * Pull the id field off a record without going through `String(obj)`,\n * which would emit `[object Object]` for nested objects and silently\n * collapse rows that share the same parent. Only string and number ids\n * are accepted; anything else returns the empty string and the record\n * is skipped at the call site.\n */\nfunction readIdField(record: unknown, idKey: string): string {\n if (record === null || typeof record !== 'object') return ''\n const v = (record as Record<string, unknown>)[idKey]\n if (typeof v === 'string') return v\n if (typeof v === 'number' && Number.isFinite(v)) return String(v)\n return ''\n}\n\ninterface FormatBuckets<T> {\n readonly added: readonly VaultDiffEntry<T>[]\n readonly modified: readonly VaultDiffModifiedEntry<T>[]\n readonly deleted: readonly VaultDiffEntry<T>[]\n readonly summary: VaultDiff<T>['summary']\n}\n\nfunction formatDiff<T>(\n detail: 'count' | 'one-line' | 'full',\n b: FormatBuckets<T>,\n): string {\n const head = `${b.summary.add} added · ${b.summary.modify} modified · ${b.summary.delete} deleted`\n if (detail === 'count') return head\n if (b.summary.total === 0) return head + '\\n(no changes)'\n if (detail === 'one-line') return head\n\n const rows: string[] = [head, '']\n for (const e of b.added) rows.push(`${e.collection}/${e.id}\\tadded`)\n for (const e of b.modified) {\n const fields = e.fieldDiffs\n .map((f) => `${f.path}: ${shortJSON(f.from)} → ${shortJSON(f.to)}`)\n .join(', ')\n rows.push(`${e.collection}/${e.id}\\tmodified\\t${fields}`)\n }\n for (const e of b.deleted) rows.push(`${e.collection}/${e.id}\\tdeleted`)\n return rows.join('\\n')\n}\n\nfunction shortJSON(value: unknown): string {\n if (value === undefined) return 'undefined'\n const s = JSON.stringify(value)\n // JSON.stringify returns string for any JSON value except `undefined`\n // (handled above), `function`, and `symbol`. Fall back to a static\n // tag for those — never let an arbitrary object hit the default\n // stringifier (which the lint rule explicitly bans).\n if (typeof s !== 'string') return '<unrepresentable>'\n return s.length > 60 ? s.slice(0, 57) + '...' : s\n}\n"],"mappings":"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","names":["formatDiff"]}
|