@noy-db/hub 0.2.0-pre.9 → 0.3.0-pre.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/README.md +129 -3
- package/dist/adapter/index.d.ts +5 -0
- package/dist/adapter/index.js +15 -0
- package/dist/aggregate/index.d.ts +6 -2
- package/dist/aggregate/index.js +24 -16
- package/dist/aggregate/index.js.map +1 -1
- package/dist/api-RW3KP7WU.js +14 -0
- package/dist/as/index.d.ts +7 -0
- package/dist/as/index.js +24 -0
- package/dist/at/index.d.ts +5 -0
- package/dist/at/index.js +1 -0
- package/dist/attestation/index.d.ts +15 -47
- package/dist/attestation/index.js +54 -10
- package/dist/attestation/index.js.map +1 -1
- package/dist/backup-XV3IOEGB.js +217 -0
- package/dist/backup-XV3IOEGB.js.map +1 -0
- package/dist/blobs/index.d.ts +6 -8
- package/dist/blobs/index.js +19 -12
- package/dist/blobs/index.js.map +1 -1
- package/dist/bundle/index.d.ts +16 -70
- package/dist/bundle/index.js +39 -476
- package/dist/bundle/index.js.map +1 -1
- package/dist/{ulid-CJ8RzRrm.d.ts → bundle-CH-H06dp.d.ts} +31 -86
- package/dist/by/index.d.ts +1 -0
- package/dist/by/index.js +11 -0
- package/dist/cargo/index.d.ts +9 -0
- package/dist/cargo/index.js +89 -0
- package/dist/chunk-26LGBE4T.js +42 -0
- package/dist/chunk-26LGBE4T.js.map +1 -0
- package/dist/chunk-2P2HZEXU.js +233 -0
- package/dist/chunk-2P2HZEXU.js.map +1 -0
- package/dist/{chunk-K3DK3NU5.js → chunk-3YFXJ3ZP.js} +20 -5
- package/dist/chunk-3YFXJ3ZP.js.map +1 -0
- package/dist/chunk-4Q6HSHHL.js +90 -0
- package/dist/chunk-4Q6HSHHL.js.map +1 -0
- package/dist/chunk-5LUY7UTV.js +380 -0
- package/dist/chunk-5LUY7UTV.js.map +1 -0
- package/dist/chunk-5N6CZTCY.js +367 -0
- package/dist/chunk-5N6CZTCY.js.map +1 -0
- package/dist/chunk-5O3AOPDT.js +992 -0
- package/dist/chunk-5O3AOPDT.js.map +1 -0
- package/dist/chunk-5R3ERCDH.js +239 -0
- package/dist/chunk-5R3ERCDH.js.map +1 -0
- package/dist/{chunk-6CME4UEK.js → chunk-5R5JW2JT.js} +7000 -4827
- package/dist/chunk-5R5JW2JT.js.map +1 -0
- package/dist/chunk-5TDJ56JE.js +32 -0
- package/dist/chunk-5TDJ56JE.js.map +1 -0
- package/dist/{chunk-T7JEBOGN.js → chunk-62QYRORB.js} +18 -11
- package/dist/chunk-62QYRORB.js.map +1 -0
- package/dist/{chunk-J66GRPNH.js → chunk-6S7T6WC4.js} +2 -2
- package/dist/chunk-6S7T6WC4.js.map +1 -0
- package/dist/chunk-76722EBH.js +451 -0
- package/dist/chunk-76722EBH.js.map +1 -0
- package/dist/{chunk-Z6FNBOTC.js → chunk-AIWULCUO.js} +13 -17
- package/dist/chunk-AIWULCUO.js.map +1 -0
- package/dist/{chunk-O3VZPBZG.js → chunk-AQTBSL7R.js} +47 -11
- package/dist/chunk-AQTBSL7R.js.map +1 -0
- package/dist/chunk-AURFOK3D.js +35 -0
- package/dist/chunk-AURFOK3D.js.map +1 -0
- package/dist/{chunk-53XBRIRT.js → chunk-AXRXJTE2.js} +9 -9
- package/dist/chunk-AXRXJTE2.js.map +1 -0
- package/dist/chunk-AZAOEIKW.js +155 -0
- package/dist/chunk-AZAOEIKW.js.map +1 -0
- package/dist/{chunk-HNRHLRDC.js → chunk-BG3SPSR4.js} +3 -3
- package/dist/chunk-BG3SPSR4.js.map +1 -0
- package/dist/chunk-BGIZAFY7.js +1 -0
- package/dist/chunk-CHFZDSE4.js +1 -0
- package/dist/{chunk-DJHHA6XH.js → chunk-CMGR4ZEW.js} +50 -20
- package/dist/chunk-CMGR4ZEW.js.map +1 -0
- package/dist/chunk-CWPPNPOH.js +23 -0
- package/dist/chunk-CWPPNPOH.js.map +1 -0
- package/dist/{chunk-QODLLGQ7.js → chunk-DFEMTNPJ.js} +371 -38
- package/dist/chunk-DFEMTNPJ.js.map +1 -0
- package/dist/{chunk-ZYWZWG6G.js → chunk-DGDI2D4M.js} +10 -10
- package/dist/chunk-DGDI2D4M.js.map +1 -0
- package/dist/{chunk-SYMWGEET.js → chunk-EIZUR2XW.js} +3 -3
- package/dist/chunk-EIZUR2XW.js.map +1 -0
- package/dist/{chunk-BVRYKATC.js → chunk-FISN7WE4.js} +36 -33
- package/dist/chunk-FISN7WE4.js.map +1 -0
- package/dist/chunk-GHVIKOHT.js +1 -0
- package/dist/chunk-GYGWYIOZ.js +200 -0
- package/dist/chunk-GYGWYIOZ.js.map +1 -0
- package/dist/chunk-HCIPRAGS.js +45 -0
- package/dist/chunk-HCIPRAGS.js.map +1 -0
- package/dist/{chunk-PX35GA7L.js → chunk-I2NOIW44.js} +10 -10
- package/dist/chunk-I2NOIW44.js.map +1 -0
- package/dist/{chunk-OIF6LZUR.js → chunk-I3TIKTJO.js} +3 -3
- package/dist/chunk-I3TIKTJO.js.map +1 -0
- package/dist/chunk-I7FD46FF.js +9 -0
- package/dist/chunk-I7FD46FF.js.map +1 -0
- package/dist/chunk-IAGBDSCS.js +40 -0
- package/dist/chunk-IAGBDSCS.js.map +1 -0
- package/dist/{chunk-JWRVQKRZ.js → chunk-IELYAOGG.js} +6 -18
- package/dist/chunk-IELYAOGG.js.map +1 -0
- package/dist/chunk-IGUYVGGI.js +205 -0
- package/dist/chunk-IGUYVGGI.js.map +1 -0
- package/dist/{chunk-U26HQ6KJ.js → chunk-IO6GRPT6.js} +4 -4
- package/dist/chunk-IO6GRPT6.js.map +1 -0
- package/dist/chunk-IPB7FTQX.js +273 -0
- package/dist/chunk-IPB7FTQX.js.map +1 -0
- package/dist/chunk-ITNUCOXM.js +148 -0
- package/dist/chunk-ITNUCOXM.js.map +1 -0
- package/dist/{chunk-WPLVTJ5D.js → chunk-IUEN57TV.js} +10 -10
- package/dist/chunk-IUEN57TV.js.map +1 -0
- package/dist/{chunk-DL3HWOQ5.js → chunk-JNUWZ6D3.js} +9 -9
- package/dist/chunk-JNUWZ6D3.js.map +1 -0
- package/dist/chunk-K2WCO3NX.js +1 -0
- package/dist/chunk-KVOXU4T5.js +30 -0
- package/dist/chunk-KVOXU4T5.js.map +1 -0
- package/dist/chunk-KXGNJJXB.js +135 -0
- package/dist/chunk-KXGNJJXB.js.map +1 -0
- package/dist/chunk-LB7FD65R.js +163 -0
- package/dist/chunk-LB7FD65R.js.map +1 -0
- package/dist/{chunk-22DWZL57.js → chunk-LFLXAY2W.js} +43 -218
- package/dist/chunk-LFLXAY2W.js.map +1 -0
- package/dist/chunk-LMTH2RM6.js +129 -0
- package/dist/chunk-LMTH2RM6.js.map +1 -0
- package/dist/{aggregate/index.cjs → chunk-LV7M27WM.js} +390 -219
- package/dist/chunk-LV7M27WM.js.map +1 -0
- package/dist/{chunk-PE2FR4J3.js → chunk-LXQT4WMP.js} +16 -18
- package/dist/chunk-LXQT4WMP.js.map +1 -0
- package/dist/chunk-M2YKN37S.js +524 -0
- package/dist/chunk-M2YKN37S.js.map +1 -0
- package/dist/chunk-M6OST55S.js +14 -0
- package/dist/chunk-M6OST55S.js.map +1 -0
- package/dist/{chunk-F3GDRNUT.js → chunk-MD3Y6J3L.js} +35 -69
- package/dist/chunk-MD3Y6J3L.js.map +1 -0
- package/dist/{chunk-XJFLDA7A.js → chunk-MTMJVJ5W.js} +8 -7
- package/dist/chunk-MTMJVJ5W.js.map +1 -0
- package/dist/{chunk-DFMLEQZB.js → chunk-NF5JWERG.js} +5 -10
- package/dist/chunk-NF5JWERG.js.map +1 -0
- package/dist/{chunk-DO7C2TOG.js → chunk-PKHL44ER.js} +3 -3
- package/dist/{chunk-DO7C2TOG.js.map → chunk-PKHL44ER.js.map} +1 -1
- package/dist/chunk-PSMV73A5.js +117 -0
- package/dist/chunk-PSMV73A5.js.map +1 -0
- package/dist/chunk-PY4KJPYU.js +9 -0
- package/dist/chunk-PY4KJPYU.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/{chunk-DE6GKS6G.js → chunk-Q7SS3IME.js} +50 -9
- package/dist/chunk-Q7SS3IME.js.map +1 -0
- package/dist/chunk-QHJW5EXU.js +54 -0
- package/dist/chunk-QHJW5EXU.js.map +1 -0
- package/dist/{chunk-NONAAENK.js → chunk-RUEKROOS.js} +11 -4
- package/dist/chunk-RUEKROOS.js.map +1 -0
- package/dist/chunk-RUIMKQTA.js +23 -0
- package/dist/chunk-RUIMKQTA.js.map +1 -0
- package/dist/{chunk-TFBP23BX.js → chunk-SKF73JOP.js} +66 -7
- package/dist/chunk-SKF73JOP.js.map +1 -0
- package/dist/chunk-SM3AVUHC.js +42 -0
- package/dist/chunk-SM3AVUHC.js.map +1 -0
- package/dist/{chunk-ML236QKC.js → chunk-SMXWYP24.js} +5 -5
- package/dist/chunk-SMXWYP24.js.map +1 -0
- package/dist/chunk-SRB2XEWB.js +148 -0
- package/dist/chunk-SRB2XEWB.js.map +1 -0
- package/dist/chunk-SVLR4POP.js +64 -0
- package/dist/chunk-SVLR4POP.js.map +1 -0
- package/dist/chunk-TA66UMI4.js +123 -0
- package/dist/chunk-TA66UMI4.js.map +1 -0
- package/dist/chunk-TEILUWOI.js +664 -0
- package/dist/chunk-TEILUWOI.js.map +1 -0
- package/dist/chunk-TJYQIGAP.js +82 -0
- package/dist/chunk-TJYQIGAP.js.map +1 -0
- package/dist/{chunk-H2X3ISXG.js → chunk-UAG5KWVA.js} +7 -7
- package/dist/chunk-UAG5KWVA.js.map +1 -0
- package/dist/chunk-UDRIWL7A.js +382 -0
- package/dist/chunk-UDRIWL7A.js.map +1 -0
- package/dist/{chunk-2GLDA55J.js → chunk-UIU2THRG.js} +498 -133
- package/dist/chunk-UIU2THRG.js.map +1 -0
- package/dist/{chunk-3YPCK6JX.js → chunk-UPJNJGGA.js} +165 -423
- package/dist/chunk-UPJNJGGA.js.map +1 -0
- package/dist/chunk-UV5MFVO3.js +21 -0
- package/dist/chunk-UV5MFVO3.js.map +1 -0
- package/dist/{chunk-2WUSG3IT.js → chunk-V7RWQRG7.js} +5 -5
- package/dist/chunk-V7RWQRG7.js.map +1 -0
- package/dist/{chunk-VTKGMEPP.js → chunk-VCTFO5CO.js} +13 -3
- package/dist/chunk-VCTFO5CO.js.map +1 -0
- package/dist/{chunk-5T22KDPN.js → chunk-VFQGRQIH.js} +8 -8
- package/dist/chunk-VFQGRQIH.js.map +1 -0
- package/dist/{chunk-2QR2PQTT.js → chunk-VQNJ7UZL.js} +5 -3
- package/dist/chunk-VQNJ7UZL.js.map +1 -0
- package/dist/{chunk-DONMZAD2.js → chunk-WWGT2MDV.js} +43 -165
- package/dist/chunk-WWGT2MDV.js.map +1 -0
- package/dist/{chunk-EMIGCR7X.js → chunk-WXSYDNBT.js} +2 -2
- package/dist/chunk-WXSYDNBT.js.map +1 -0
- package/dist/chunk-WYSO2NIH.js +30 -0
- package/dist/chunk-WYSO2NIH.js.map +1 -0
- package/dist/chunk-XXYIOFUB.js +164 -0
- package/dist/chunk-XXYIOFUB.js.map +1 -0
- package/dist/{chunk-3BFJOWSU.js → chunk-Y22T3KQE.js} +35 -7
- package/dist/chunk-Y22T3KQE.js.map +1 -0
- package/dist/{chunk-A3JMGXPG.js → chunk-YMUGBZN2.js} +2 -2
- package/dist/chunk-YMUGBZN2.js.map +1 -0
- package/dist/{chunk-I5FOWO4J.js → chunk-ZCGAHGUS.js} +52 -73
- package/dist/chunk-ZCGAHGUS.js.map +1 -0
- package/dist/{chunk-FZU343FL.js → chunk-ZJADGNYF.js} +2 -2
- package/dist/chunk-ZJADGNYF.js.map +1 -0
- package/dist/{chunk-B6PB7JLN.js → chunk-ZYUC53LT.js} +427 -6
- package/dist/chunk-ZYUC53LT.js.map +1 -0
- package/dist/collection-facade-GQAOGJP2.js +33 -0
- package/dist/consent/index.d.ts +5 -7
- package/dist/consent/index.js +7 -5
- package/dist/consent/index.js.map +1 -1
- package/dist/crdt/index.d.ts +6 -2
- package/dist/crdt/index.js +3 -2
- package/dist/crdt/index.js.map +1 -1
- package/dist/decrypt-partition-IHtxEnTi.d.ts +21 -0
- package/dist/delegation-UL5HKLER.js +19 -0
- package/dist/derivations/index.d.ts +7 -9
- package/dist/derivations/index.js +11 -6
- package/dist/describe/index.d.ts +1 -0
- package/dist/describe/index.js +1 -0
- package/dist/describe-aBkJR2sd.d.ts +131 -0
- package/dist/{dev-unlock-B4kDxep_.d.ts → dev-unlock-C9Ro3v_O.d.ts} +1 -1
- package/dist/discriminant-BN9REW3o.d.ts +60 -0
- package/dist/enclave-UL5LW66V.js +88 -0
- package/dist/executor-2FWQRLMG.js +15 -0
- package/dist/executor-QZ7BXICW.js +9 -0
- package/dist/executor-Z5ZLJVTV.js +9 -0
- package/dist/export-accessible-KRV5W4GH.js +17 -0
- package/dist/extract-partition-GLKBOXFQ.js +30 -0
- package/dist/{fanout-sidecar-OKPMMPLG.js → fanout-sidecar-GF3DJ6KR.js} +34 -14
- package/dist/fanout-sidecar-GF3DJ6KR.js.map +1 -0
- package/dist/forget/index.d.ts +36 -0
- package/dist/forget/index.js +19 -0
- package/dist/forget/index.js.map +1 -0
- package/dist/guards/index.d.ts +13 -9
- package/dist/guards/index.js +12 -5
- package/dist/{hash-DdpVypUp.d.cts → hash-Cp5uwiox.d.ts} +5 -1
- package/dist/history/index.d.ts +6 -8
- package/dist/history/index.js +19 -12
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.d.ts +5 -7
- package/dist/i18n/index.js +104 -15
- package/dist/i18n/index.js.map +1 -1
- package/dist/in/index.d.ts +5 -0
- package/dist/in/index.js +1 -0
- package/dist/in/index.js.map +1 -0
- package/dist/index-B9QdHytu.d.ts +123 -0
- package/dist/index-BF9_96A5.d.ts +123 -0
- package/dist/{types-Df28QFKb.d.ts → index-BzUo1B6n.d.ts} +16270 -8358
- package/dist/index.d.ts +1088 -450
- package/dist/index.js +1165 -293
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.d.ts +6 -3
- package/dist/indexing/index.js +6 -5
- package/dist/indexing/index.js.map +1 -1
- package/dist/issue-Y6UVIR43.js +13 -0
- package/dist/issue-Y6UVIR43.js.map +1 -0
- package/dist/kernel/index.d.ts +32 -0
- package/dist/kernel/index.js +53 -0
- package/dist/kernel/index.js.map +1 -0
- package/dist/{ledger-TMRZYNVJ.js → ledger-RYI35CDS.js} +12 -9
- package/dist/ledger-RYI35CDS.js.map +1 -0
- package/dist/liberate-CRPZEV7O.js +18 -0
- package/dist/liberate-CRPZEV7O.js.map +1 -0
- package/dist/materialized-views/index.d.ts +6 -19
- package/dist/materialized-views/index.js +16 -12
- package/dist/mime-magic-CGhw37_E.d.ts +103 -0
- package/dist/noydb-367AM4HT.js +50 -0
- package/dist/noydb-367AM4HT.js.map +1 -0
- package/dist/on/index.d.ts +5 -0
- package/dist/on/index.js +11 -0
- package/dist/on/index.js.map +1 -0
- package/dist/overlay-views/index.d.ts +27 -11
- package/dist/overlay-views/index.js +7 -6
- package/dist/periods/index.d.ts +5 -7
- package/dist/periods/index.js +13 -9
- package/dist/pod/index.d.ts +63 -0
- package/dist/pod/index.js +61 -0
- package/dist/pod/index.js.map +1 -0
- package/dist/policy-IXK4FVXP.js +41 -0
- package/dist/policy-IXK4FVXP.js.map +1 -0
- package/dist/portability/index.d.ts +20 -0
- package/dist/portability/index.js +43 -0
- package/dist/portability/index.js.map +1 -0
- package/dist/{public-envelope-BW6OXORV.js → public-envelope-JHDQCPSX.js} +6 -5
- package/dist/public-envelope-JHDQCPSX.js.map +1 -0
- package/dist/query/index.d.ts +5 -3
- package/dist/query/index.js +21 -13
- package/dist/read-only-facade-5ADRJVTM.js +8 -0
- package/dist/read-only-facade-5ADRJVTM.js.map +1 -0
- package/dist/registry-45RJNWGU.js +9 -0
- package/dist/registry-45RJNWGU.js.map +1 -0
- package/dist/registry-5GRV5VXI.js +13 -0
- package/dist/registry-5GRV5VXI.js.map +1 -0
- package/dist/registry-VW6P6A3J.js +8 -0
- package/dist/registry-VW6P6A3J.js.map +1 -0
- package/dist/registry-WEUCHBO4.js +11 -0
- package/dist/registry-WEUCHBO4.js.map +1 -0
- package/dist/request-withdrawal-GBPH2FDY.js +25 -0
- package/dist/request-withdrawal-GBPH2FDY.js.map +1 -0
- package/dist/revoke-TUFRGI6S.js +18 -0
- package/dist/revoke-TUFRGI6S.js.map +1 -0
- package/dist/sealed-record/index.d.ts +69 -0
- package/dist/sealed-record/index.js +65 -0
- package/dist/sealed-record/index.js.map +1 -0
- package/dist/session/index.d.ts +6 -8
- package/dist/session/index.js +7 -5
- package/dist/session/index.js.map +1 -1
- package/dist/shadow/index.d.ts +5 -7
- package/dist/shadow/index.js +4 -3
- package/dist/shadow/index.js.map +1 -1
- package/dist/{signer-72QAUSVW.js → signer-PNKTBVF7.js} +6 -5
- package/dist/signer-PNKTBVF7.js.map +1 -0
- package/dist/snapshots/index.d.ts +6 -8
- package/dist/snapshots/index.js +13 -11
- package/dist/snapshots/index.js.map +1 -1
- package/dist/{stale-6ZDBTQT7.js → stale-3JWHNDIY.js} +3 -2
- package/dist/stale-3JWHNDIY.js.map +1 -0
- package/dist/store-coordination-provider-3I7XWZZK.js +142 -0
- package/dist/store-coordination-provider-3I7XWZZK.js.map +1 -0
- package/dist/subject-index-O75wKshW.d.ts +362 -0
- package/dist/sync/index.d.ts +4 -6
- package/dist/sync/index.js +7 -6
- package/dist/sync/index.js.map +1 -1
- package/dist/team/index.d.ts +5 -7
- package/dist/team/index.js +20 -16
- package/dist/tiers/index.d.ts +5 -0
- package/dist/tiers/index.js +33 -0
- package/dist/tiers/index.js.map +1 -0
- package/dist/to/index.d.ts +5 -0
- package/dist/to/index.js +17 -0
- package/dist/to/index.js.map +1 -0
- package/dist/transition-guard-C7ol6oPw.d.ts +165 -0
- package/dist/tx/index.d.ts +23 -9
- package/dist/tx/index.js +13 -8
- package/dist/tx/index.js.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +1 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ulid-DRH25k3y.d.ts +66 -0
- package/dist/ulid-PTAIMDG4.js +10 -0
- package/dist/ulid-PTAIMDG4.js.map +1 -0
- package/dist/util/index.d.ts +2 -0
- package/dist/util/index.js +7 -2
- package/dist/util/index.js.map +1 -1
- package/dist/vault-diff-B5fYNBL7.d.ts +147 -0
- package/dist/with/index.d.ts +38 -0
- package/dist/with/index.js +25 -0
- package/dist/with/index.js.map +1 -0
- package/dist/{with-materialized-view-BLkQxoQN.d.ts → with-materialized-view-6p0Fk8bL.d.ts} +1 -1
- package/dist/{with-overlayed-view-CRsSEwHR.d.ts → with-overlayed-view-BJ6mXGMd.d.ts} +2 -3
- package/dist/with-rollup-BvQo3ROo.d.ts +47 -0
- package/dist/withdraw-accessible-FFS46EOD.js +22 -0
- package/dist/withdraw-accessible-FFS46EOD.js.map +1 -0
- package/dist/zod-HAJM7LMS.js +14763 -0
- package/dist/zod-HAJM7LMS.js.map +1 -0
- package/package.json +121 -201
- package/dist/aggregate/index.cjs.map +0 -1
- package/dist/aggregate/index.d.cts +0 -38
- package/dist/attestation/index.cjs +0 -305
- package/dist/attestation/index.cjs.map +0 -1
- package/dist/attestation/index.d.cts +0 -52
- package/dist/blobs/index.cjs +0 -1480
- package/dist/blobs/index.cjs.map +0 -1
- package/dist/blobs/index.d.cts +0 -46
- package/dist/bundle/index.cjs +0 -19264
- package/dist/bundle/index.cjs.map +0 -1
- package/dist/bundle/index.d.cts +0 -176
- package/dist/chunk-22DWZL57.js.map +0 -1
- package/dist/chunk-2GLDA55J.js.map +0 -1
- package/dist/chunk-2QR2PQTT.js.map +0 -1
- package/dist/chunk-2WUSG3IT.js.map +0 -1
- package/dist/chunk-3BFJOWSU.js.map +0 -1
- package/dist/chunk-3YPCK6JX.js.map +0 -1
- package/dist/chunk-53XBRIRT.js.map +0 -1
- package/dist/chunk-5T22KDPN.js.map +0 -1
- package/dist/chunk-5XHKQ56N.js +0 -340
- package/dist/chunk-5XHKQ56N.js.map +0 -1
- package/dist/chunk-6CME4UEK.js.map +0 -1
- package/dist/chunk-6STK5TQP.js +0 -139
- package/dist/chunk-6STK5TQP.js.map +0 -1
- package/dist/chunk-A3JMGXPG.js.map +0 -1
- package/dist/chunk-B6PB7JLN.js.map +0 -1
- package/dist/chunk-BVRYKATC.js.map +0 -1
- package/dist/chunk-DE6GKS6G.js.map +0 -1
- package/dist/chunk-DFMLEQZB.js.map +0 -1
- package/dist/chunk-DJHHA6XH.js.map +0 -1
- package/dist/chunk-DL3HWOQ5.js.map +0 -1
- package/dist/chunk-DONMZAD2.js.map +0 -1
- package/dist/chunk-EMIGCR7X.js.map +0 -1
- package/dist/chunk-F3GDRNUT.js.map +0 -1
- package/dist/chunk-FZU343FL.js.map +0 -1
- package/dist/chunk-H2X3ISXG.js.map +0 -1
- package/dist/chunk-HNRHLRDC.js.map +0 -1
- package/dist/chunk-I5FOWO4J.js.map +0 -1
- package/dist/chunk-J66GRPNH.js.map +0 -1
- package/dist/chunk-JWRVQKRZ.js.map +0 -1
- package/dist/chunk-K3DK3NU5.js.map +0 -1
- package/dist/chunk-ML236QKC.js.map +0 -1
- package/dist/chunk-NONAAENK.js.map +0 -1
- package/dist/chunk-O3VZPBZG.js.map +0 -1
- package/dist/chunk-OIF6LZUR.js.map +0 -1
- package/dist/chunk-OQ6PIGHA.js +0 -19
- package/dist/chunk-OQ6PIGHA.js.map +0 -1
- package/dist/chunk-PE2FR4J3.js.map +0 -1
- package/dist/chunk-PP6W64Y3.js +0 -275
- package/dist/chunk-PP6W64Y3.js.map +0 -1
- package/dist/chunk-PX35GA7L.js.map +0 -1
- package/dist/chunk-QEILDE6R.js +0 -793
- package/dist/chunk-QEILDE6R.js.map +0 -1
- package/dist/chunk-QODLLGQ7.js.map +0 -1
- package/dist/chunk-RAZ4OVLL.js +0 -51
- package/dist/chunk-RAZ4OVLL.js.map +0 -1
- package/dist/chunk-SYMWGEET.js.map +0 -1
- package/dist/chunk-T7JEBOGN.js.map +0 -1
- package/dist/chunk-TFBP23BX.js.map +0 -1
- package/dist/chunk-TV3YZ35S.js +0 -90
- package/dist/chunk-TV3YZ35S.js.map +0 -1
- package/dist/chunk-U26HQ6KJ.js.map +0 -1
- package/dist/chunk-UF3BUNQZ.js +0 -1
- package/dist/chunk-UMLVJTYV.js +0 -20
- package/dist/chunk-UMLVJTYV.js.map +0 -1
- package/dist/chunk-VTKGMEPP.js.map +0 -1
- package/dist/chunk-W6EQLGMB.js +0 -100
- package/dist/chunk-W6EQLGMB.js.map +0 -1
- package/dist/chunk-WIRRPTFH.js +0 -17
- package/dist/chunk-WIRRPTFH.js.map +0 -1
- package/dist/chunk-WPLVTJ5D.js.map +0 -1
- package/dist/chunk-XJFLDA7A.js.map +0 -1
- package/dist/chunk-Z6FNBOTC.js.map +0 -1
- package/dist/chunk-ZYWZWG6G.js.map +0 -1
- package/dist/consent/index.cjs +0 -204
- package/dist/consent/index.cjs.map +0 -1
- package/dist/consent/index.d.cts +0 -25
- package/dist/crdt/index.cjs +0 -152
- package/dist/crdt/index.cjs.map +0 -1
- package/dist/crdt/index.d.cts +0 -30
- package/dist/crypto-HTZ4FJOP.js +0 -44
- package/dist/delegation-RI54P6I5.js +0 -17
- package/dist/derivations/index.cjs +0 -351
- package/dist/derivations/index.cjs.map +0 -1
- package/dist/derivations/index.d.cts +0 -72
- package/dist/dev-unlock-BIoEFbwm.d.cts +0 -263
- package/dist/executor-5PNY7LGW.js +0 -8
- package/dist/executor-B4QIYGZX.js +0 -8
- package/dist/executor-BWXXDQ7K.js +0 -11
- package/dist/fanout-sidecar-OKPMMPLG.js.map +0 -1
- package/dist/guards/index.cjs +0 -322
- package/dist/guards/index.cjs.map +0 -1
- package/dist/guards/index.d.cts +0 -31
- package/dist/hash-HJqD14vS.d.ts +0 -63
- package/dist/history/index.cjs +0 -1222
- package/dist/history/index.cjs.map +0 -1
- package/dist/history/index.d.cts +0 -63
- package/dist/i18n/index.cjs +0 -1100
- package/dist/i18n/index.cjs.map +0 -1
- package/dist/i18n/index.d.cts +0 -39
- package/dist/index-4fBVt8j9.d.cts +0 -2387
- package/dist/index-D8I_pyJD.d.ts +0 -2387
- package/dist/index.cjs +0 -24487
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -776
- package/dist/indexing/index.cjs +0 -742
- package/dist/indexing/index.cjs.map +0 -1
- package/dist/indexing/index.d.cts +0 -36
- package/dist/issue-VGDPP4B5.js +0 -12
- package/dist/lazy-builder-7tIpFyWN.d.ts +0 -304
- package/dist/lazy-builder-wY4pMCEe.d.cts +0 -304
- package/dist/materialized-views/index.cjs +0 -854
- package/dist/materialized-views/index.cjs.map +0 -1
- package/dist/materialized-views/index.d.cts +0 -186
- package/dist/mime-magic-CBBSOkjm.d.cts +0 -50
- package/dist/mime-magic-CBBSOkjm.d.ts +0 -50
- package/dist/noydb-G725ZSZG.js +0 -34
- package/dist/overlay-views/index.cjs +0 -359
- package/dist/overlay-views/index.cjs.map +0 -1
- package/dist/overlay-views/index.d.cts +0 -82
- package/dist/periods/index.cjs +0 -1041
- package/dist/periods/index.cjs.map +0 -1
- package/dist/periods/index.d.cts +0 -22
- package/dist/predicate-BSAGEyu5.d.cts +0 -209
- package/dist/predicate-BSAGEyu5.d.ts +0 -209
- package/dist/query/index.cjs +0 -2375
- package/dist/query/index.cjs.map +0 -1
- package/dist/query/index.d.cts +0 -3
- package/dist/read-only-facade-ITU6L7BL.js +0 -7
- package/dist/registry-3QP3YKQS.js +0 -8
- package/dist/registry-DKEXOJVO.js +0 -7
- package/dist/registry-OJIOSBV6.js +0 -10
- package/dist/registry-USRVT6YF.js +0 -8
- package/dist/revoke-JCC7N56X.js +0 -17
- package/dist/session/index.cjs +0 -495
- package/dist/session/index.cjs.map +0 -1
- package/dist/session/index.d.cts +0 -46
- package/dist/shadow/index.cjs +0 -133
- package/dist/shadow/index.cjs.map +0 -1
- package/dist/shadow/index.d.cts +0 -17
- package/dist/snapshots/index.cjs +0 -937
- package/dist/snapshots/index.cjs.map +0 -1
- package/dist/snapshots/index.d.cts +0 -28
- package/dist/store/index.cjs +0 -1083
- package/dist/store/index.cjs.map +0 -1
- package/dist/store/index.d.cts +0 -492
- package/dist/store/index.d.ts +0 -492
- package/dist/store/index.js +0 -37
- package/dist/strategy-BSxFXGzb.d.cts +0 -110
- package/dist/strategy-BSxFXGzb.d.ts +0 -110
- package/dist/strategy-DSTrsZ8t.d.cts +0 -601
- package/dist/strategy-DSTrsZ8t.d.ts +0 -601
- package/dist/sync/index.cjs +0 -1062
- package/dist/sync/index.cjs.map +0 -1
- package/dist/sync/index.d.cts +0 -43
- package/dist/team/index.cjs +0 -2798
- package/dist/team/index.cjs.map +0 -1
- package/dist/team/index.d.cts +0 -118
- package/dist/tx/index.cjs +0 -544
- package/dist/tx/index.cjs.map +0 -1
- package/dist/tx/index.d.cts +0 -21
- package/dist/types-DyXYr8rE.d.cts +0 -12818
- package/dist/ulid-COREQ2RQ.js +0 -9
- package/dist/ulid-Drr7ykr6.d.cts +0 -603
- package/dist/util/index.cjs +0 -230
- package/dist/util/index.cjs.map +0 -1
- package/dist/util/index.d.cts +0 -77
- package/dist/with-derivation-DFnFByiQ.d.ts +0 -13
- package/dist/with-derivation-DU3Sjazm.d.cts +0 -13
- package/dist/with-guard-ByyxmEe7.d.cts +0 -18
- package/dist/with-guard-qube6BMI.d.ts +0 -18
- package/dist/with-materialized-view-BmEToIR1.d.cts +0 -27
- package/dist/with-overlayed-view-BMsQQbWm.d.cts +0 -13
- /package/dist/{store → adapter}/index.js.map +0 -0
- /package/dist/{chunk-UF3BUNQZ.js.map → api-RW3KP7WU.js.map} +0 -0
- /package/dist/{crypto-HTZ4FJOP.js.map → as/index.js.map} +0 -0
- /package/dist/{delegation-RI54P6I5.js.map → at/index.js.map} +0 -0
- /package/dist/{executor-5PNY7LGW.js.map → by/index.js.map} +0 -0
- /package/dist/{executor-B4QIYGZX.js.map → cargo/index.js.map} +0 -0
- /package/dist/{executor-BWXXDQ7K.js.map → chunk-BGIZAFY7.js.map} +0 -0
- /package/dist/{issue-VGDPP4B5.js.map → chunk-CHFZDSE4.js.map} +0 -0
- /package/dist/{ledger-TMRZYNVJ.js.map → chunk-GHVIKOHT.js.map} +0 -0
- /package/dist/{noydb-G725ZSZG.js.map → chunk-K2WCO3NX.js.map} +0 -0
- /package/dist/{public-envelope-BW6OXORV.js.map → chunk-PZ5AY32C.js.map} +0 -0
- /package/dist/{read-only-facade-ITU6L7BL.js.map → collection-facade-GQAOGJP2.js.map} +0 -0
- /package/dist/{registry-3QP3YKQS.js.map → delegation-UL5HKLER.js.map} +0 -0
- /package/dist/{registry-DKEXOJVO.js.map → describe/index.js.map} +0 -0
- /package/dist/{registry-OJIOSBV6.js.map → enclave-UL5LW66V.js.map} +0 -0
- /package/dist/{registry-USRVT6YF.js.map → executor-2FWQRLMG.js.map} +0 -0
- /package/dist/{revoke-JCC7N56X.js.map → executor-QZ7BXICW.js.map} +0 -0
- /package/dist/{signer-72QAUSVW.js.map → executor-Z5ZLJVTV.js.map} +0 -0
- /package/dist/{stale-6ZDBTQT7.js.map → export-accessible-KRV5W4GH.js.map} +0 -0
- /package/dist/{ulid-COREQ2RQ.js.map → extract-partition-GLKBOXFQ.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-party/team/presence.ts","../src/with-party/team/sync.ts","../src/with-party/team/sync-transaction.ts"],"sourcesContent":["/**\n * Presence handle — real-time awareness of who is viewing/editing a collection.\n * encrypted ephemeral channel keyed by collection DEK via HKDF.\n *\n * The presence key is derived from the collection DEK so:\n * - The adapter never learns user identities from presence payloads.\n * - Presence rotates automatically when the DEK rotates (revoked users\n * can no longer participate after a DEK rotation).\n *\n * Two transport strategies:\n * 1. **Pub/sub** (real-time): used when the adapter implements\n * `presencePublish` and `presenceSubscribe`.\n * 2. **Storage-poll** (fallback): presence records are written to a\n * reserved `_presence_<collection>` collection on the sync adapter\n * (if available) or local adapter, and polled periodically.\n */\n\nimport type { NoydbStore, PresencePeer } from '../../kernel/types.js'\nimport { encrypt, decrypt, generateIV, bufferToBase64, derivePresenceKey, type EnclaveKey } from '../../kernel/enclave/index.js'\n\n/** Options for constructing a PresenceHandle. @internal */\nexport interface PresenceHandleOpts {\n /** Local adapter for storage-poll fallback. */\n adapter: NoydbStore\n /** Remote (sync) adapter — preferred for broadcasting presence if available. */\n syncAdapter?: NoydbStore\n /** Vault name — used as part of the channel and storage key. */\n vault: string\n /** Collection name — used as HKDF `info` and channel suffix. */\n collectionName: string\n /** Calling user's ID, embedded unencrypted in storage records. */\n userId: string\n /** Whether encryption is active. When false, presence payloads are stored as JSON. */\n encrypted: boolean\n /** Callback that resolves the collection DEK (used to derive the presence key). */\n getDEK: (collectionName: string) => Promise<EnclaveKey>\n /** How long (ms) before a peer's presence is considered stale. Default: 30_000. */\n staleMs?: number\n /** Poll interval (ms) for the storage-poll fallback. Default: 5_000. */\n pollIntervalMs?: number\n}\n\n/**\n * Internal storage envelope for the storage-poll fallback.\n * Written to `_presence_<collection>` as `{ userId, lastSeen, iv, data }`.\n */\ninterface StoragePresenceRecord {\n userId: string\n lastSeen: string\n iv: string // base64 AES-GCM IV (empty when not encrypted)\n data: string // base64 ciphertext or JSON string when not encrypted\n}\n\n/** Presence handle for a single collection. */\nexport class PresenceHandle<P> {\n private readonly adapter: NoydbStore\n private readonly syncAdapter: NoydbStore | undefined\n private readonly vault: string\n private readonly collectionName: string\n private readonly userId: string\n private readonly encrypted: boolean\n private readonly getDEK: (collectionName: string) => Promise<EnclaveKey>\n private readonly staleMs: number\n private readonly pollIntervalMs: number\n private readonly channel: string\n private readonly storageCollection: string\n\n private presenceKey: EnclaveKey | null = null\n private subscribers: Array<(peers: PresencePeer<P>[]) => void> = []\n private unsubscribePubSub: (() => void) | null = null\n private pollTimer: ReturnType<typeof setInterval> | null = null\n private stopped = false\n\n constructor(opts: PresenceHandleOpts) {\n this.adapter = opts.adapter\n this.syncAdapter = opts.syncAdapter\n this.vault = opts.vault\n this.collectionName = opts.collectionName\n this.userId = opts.userId\n this.encrypted = opts.encrypted\n this.getDEK = opts.getDEK\n this.staleMs = opts.staleMs ?? 30_000\n this.pollIntervalMs = opts.pollIntervalMs ?? 5_000\n // Channel used by pub/sub adapters — vault-scoped so two collections\n // in the same vault don't bleed into each other's presence channels.\n this.channel = `${opts.vault}:${opts.collectionName}:presence`\n // Reserved collection name for the storage-poll fallback.\n this.storageCollection = `_presence_${opts.collectionName}`\n }\n\n /**\n * Announce yourself (or update your cursor/status).\n * Encrypts `payload` with the presence key and publishes it.\n */\n async update(payload: P): Promise<void> {\n if (this.stopped) return\n\n const key = await this.getPresenceKey()\n const now = new Date().toISOString()\n const plaintext = JSON.stringify({ userId: this.userId, lastSeen: now, payload })\n let encryptedPayload: string\n\n if (this.encrypted && key) {\n const iv = generateIV()\n const ivB64 = bufferToBase64(iv)\n const { data } = await encrypt(plaintext, key)\n encryptedPayload = JSON.stringify({ iv: ivB64, data })\n } else {\n encryptedPayload = plaintext\n }\n\n // Pub/sub path — publish to any adapter that supports it\n const pubAdapter = this.getPubSubAdapter()\n if (pubAdapter?.presencePublish) {\n await pubAdapter.presencePublish(this.channel, encryptedPayload)\n }\n\n // Storage-poll path — write a record to the storage adapter\n await this.writeStorageRecord(payload, now)\n }\n\n /**\n * Subscribe to presence updates. The callback receives a filtered, decrypted\n * list of all currently-active peers (excluding yourself, excluding stale).\n *\n * Returns an unsubscribe function. Also call `stop()` to release all resources.\n */\n subscribe(cb: (peers: PresencePeer<P>[]) => void): () => void {\n if (this.stopped) return () => {}\n\n this.subscribers.push(cb)\n\n // Start pub/sub listener on first subscriber\n if (this.subscribers.length === 1) {\n this.startListening()\n }\n\n return () => {\n this.subscribers = this.subscribers.filter(s => s !== cb)\n if (this.subscribers.length === 0) this.stopListening()\n }\n }\n\n /** Stop all listening and clear resources. */\n stop(): void {\n this.stopped = true\n this.stopListening()\n this.subscribers = []\n }\n\n // ─── Private ────────────────────────────────────────────────────────\n\n private async getPresenceKey(): Promise<EnclaveKey | null> {\n if (!this.encrypted) return null\n if (!this.presenceKey) {\n try {\n const dek = await this.getDEK(this.collectionName)\n this.presenceKey = await derivePresenceKey(dek, this.collectionName)\n } catch {\n // no-op — presence degrades gracefully if crypto fails\n }\n }\n return this.presenceKey\n }\n\n private getPubSubAdapter(): NoydbStore | undefined {\n // Prefer the sync adapter (it broadcasts to other devices)\n if (this.syncAdapter?.presencePublish) return this.syncAdapter\n if (this.adapter.presencePublish) return this.adapter\n return undefined\n }\n\n private startListening(): void {\n const pubAdapter = this.getPubSubAdapter()\n\n if (pubAdapter?.presenceSubscribe) {\n // Real-time pub/sub path\n this.unsubscribePubSub = pubAdapter.presenceSubscribe(\n this.channel,\n (encryptedPayload) => { void this.handlePubSubMessage(encryptedPayload) },\n )\n } else {\n // Storage-poll fallback\n this.pollTimer = setInterval(\n () => { void this.pollStoragePresence() },\n this.pollIntervalMs,\n )\n // Kick off an immediate poll\n void this.pollStoragePresence()\n }\n }\n\n private stopListening(): void {\n if (this.unsubscribePubSub) {\n this.unsubscribePubSub()\n this.unsubscribePubSub = null\n }\n if (this.pollTimer) {\n clearInterval(this.pollTimer)\n this.pollTimer = null\n }\n }\n\n private async handlePubSubMessage(encryptedPayload: string): Promise<void> {\n try {\n const peer = await this.decryptPresencePayload(encryptedPayload)\n if (!peer || peer.userId === this.userId) return\n\n const cutoff = new Date(Date.now() - this.staleMs).toISOString()\n if (peer.lastSeen < cutoff) return\n\n // Deliver only this new peer to subscribers; a full snapshot poll follows\n // on next interval. For pub/sub, we could maintain a map of active peers,\n // but for simplicity: emit a snapshot read from storage.\n await this.pollStoragePresence()\n } catch {\n // Decrypt failure — stale key or tampered payload, ignore\n }\n }\n\n private async decryptPresencePayload(\n encryptedPayload: string,\n ): Promise<{ userId: string; lastSeen: string; payload: P } | null> {\n const key = await this.getPresenceKey()\n\n if (!this.encrypted || !key) {\n return JSON.parse(encryptedPayload) as { userId: string; lastSeen: string; payload: P }\n }\n\n const { iv: ivB64, data } = JSON.parse(encryptedPayload) as { iv: string; data: string }\n const plaintext = await decrypt(ivB64, data, key)\n return JSON.parse(plaintext) as { userId: string; lastSeen: string; payload: P }\n }\n\n private async writeStorageRecord(payload: P, now: string): Promise<void> {\n const key = await this.getPresenceKey()\n const plaintext = JSON.stringify(payload)\n let iv = ''\n let data: string\n\n if (this.encrypted && key) {\n const ivBytes = generateIV()\n iv = bufferToBase64(ivBytes)\n const result = await encrypt(plaintext, key)\n data = result.data\n } else {\n data = plaintext\n }\n\n const record: StoragePresenceRecord = { userId: this.userId, lastSeen: now, iv, data }\n const json = JSON.stringify(record)\n\n // Use the sync adapter if available (so other devices can read it);\n // fall back to local adapter.\n const storeAdapter = this.syncAdapter ?? this.adapter\n const envelope = {\n _noydb: 1 as const,\n _v: 1,\n _ts: now,\n _iv: '',\n _data: json,\n }\n try {\n await storeAdapter.put(\n this.vault,\n this.storageCollection,\n this.userId,\n envelope,\n )\n } catch {\n // Presence write failure is non-fatal — the user is still present locally\n }\n }\n\n private async pollStoragePresence(): Promise<void> {\n if (this.stopped || this.subscribers.length === 0) return\n\n try {\n const storeAdapter = this.syncAdapter ?? this.adapter\n const ids = await storeAdapter.list(this.vault, this.storageCollection)\n const cutoff = new Date(Date.now() - this.staleMs).toISOString()\n const peers: PresencePeer<P>[] = []\n\n for (const id of ids) {\n if (id === this.userId) continue // skip ourselves\n const envelope = await storeAdapter.get(this.vault, this.storageCollection, id)\n if (!envelope) continue\n\n const record = JSON.parse(envelope._data) as StoragePresenceRecord\n if (record.lastSeen < cutoff) continue\n\n let peerPayload: P\n if (this.encrypted && this.presenceKey && record.iv) {\n const plaintext = await decrypt(record.iv, record.data, this.presenceKey)\n peerPayload = JSON.parse(plaintext) as P\n } else {\n peerPayload = JSON.parse(record.data) as P\n }\n\n peers.push({ userId: record.userId, payload: peerPayload, lastSeen: record.lastSeen })\n }\n\n for (const cb of this.subscribers) {\n cb(peers)\n }\n } catch {\n // Poll failure is non-fatal\n }\n }\n}\n","import type {\n NoydbStore,\n DirtyEntry,\n Conflict,\n ConflictStrategy,\n CollectionConflictResolver,\n PushOptions,\n PullOptions,\n PushResult,\n PullResult,\n SyncStatus,\n EncryptedEnvelope,\n SyncMetadata,\n SyncTargetRole,\n} from '../../kernel/types.js'\nimport { NOYDB_SYNC_VERSION } from '../../kernel/types.js'\nimport { ConflictError } from '../../kernel/errors.js'\nimport type { NoydbEventEmitter } from '../../kernel/events.js'\nimport type { SyncPolicy } from '../../kernel/sync-policy.js'\nimport { SyncScheduler } from '../../kernel/sync-policy.js'\n\n/** Sync engine: dirty tracking, push, pull, conflict resolution, scheduling. */\nexport class SyncEngine {\n private readonly local: NoydbStore\n private readonly remote: NoydbStore\n private readonly strategy: ConflictStrategy\n private readonly emitter: NoydbEventEmitter\n private readonly vault: string\n readonly role: SyncTargetRole\n readonly label: string | undefined\n\n private dirty: DirtyEntry[] = []\n private lastPush: string | null = null\n private lastPull: string | null = null\n private loaded = false\n private autoSyncInterval: ReturnType<typeof setInterval> | null = null\n private isOnline = true\n\n /** Sync scheduler. Manages push/pull timing. */\n readonly scheduler: SyncScheduler | null\n\n /** Per-collection conflict resolvers registered by Collection instances. */\n private readonly conflictResolvers = new Map<string, CollectionConflictResolver>()\n\n constructor(opts: {\n local: NoydbStore\n remote: NoydbStore\n vault: string\n strategy: ConflictStrategy\n emitter: NoydbEventEmitter\n syncPolicy?: SyncPolicy\n role?: SyncTargetRole\n label?: string\n }) {\n this.local = opts.local\n this.remote = opts.remote\n this.vault = opts.vault\n this.strategy = opts.strategy\n this.emitter = opts.emitter\n this.role = opts.role ?? 'sync-peer'\n this.label = opts.label\n\n // Create scheduler if a policy is provided\n const policy = opts.syncPolicy\n if (policy && policy.push.mode !== 'manual') {\n this.scheduler = new SyncScheduler(policy, {\n push: () => this.push().then(() => {}),\n pull: () => this.pull().then(() => {}),\n getDirtyCount: () => this.dirty.length,\n })\n } else {\n this.scheduler = null\n }\n }\n\n /** Start the sync scheduler. Called after vault is fully opened. */\n startScheduler(): void {\n this.scheduler?.start()\n }\n\n /** Stop the sync scheduler. Called on close. */\n stopScheduler(): void {\n this.scheduler?.stop()\n }\n\n /**\n * Register a per-collection conflict resolver.\n * Called by Collection when `conflictPolicy` is set.\n */\n registerConflictResolver(collection: string, resolver: CollectionConflictResolver): void {\n this.conflictResolvers.set(collection, resolver)\n }\n\n /** Record a local change for later push. */\n async trackChange(collection: string, id: string, action: 'put' | 'delete', version: number): Promise<void> {\n await this.ensureLoaded()\n\n // Deduplicate: if same collection+id already in dirty, update it\n const idx = this.dirty.findIndex(d => d.collection === collection && d.id === id)\n const entry: DirtyEntry = {\n vault: this.vault,\n collection,\n id,\n action,\n version,\n timestamp: new Date().toISOString(),\n }\n\n if (idx >= 0) {\n this.dirty[idx] = entry\n } else {\n this.dirty.push(entry)\n }\n\n await this.persistMeta()\n\n // Notify scheduler of the write (triggers on-change or debounce)\n this.scheduler?.notifyChange()\n }\n\n /** Push dirty records to remote adapter. Accepts optional `PushOptions` for partial sync. */\n async push(options?: PushOptions): Promise<PushResult> {\n await this.ensureLoaded()\n\n let pushed = 0\n const conflicts: Conflict[] = []\n const errors: Error[] = []\n const completed: number[] = []\n\n for (let i = 0; i < this.dirty.length; i++) {\n const entry = this.dirty[i]!\n\n // Partial sync: skip collections not in the filter\n if (options?.collections && !options.collections.includes(entry.collection)) {\n continue\n }\n\n try {\n if (entry.action === 'delete') {\n await this.remote.delete(this.vault, entry.collection, entry.id)\n completed.push(i)\n pushed++\n } else {\n const envelope = await this.local.get(this.vault, entry.collection, entry.id)\n if (!envelope) {\n // Record was deleted locally after being marked dirty\n completed.push(i)\n continue\n }\n\n try {\n await this.remote.put(\n this.vault,\n entry.collection,\n entry.id,\n envelope,\n entry.version - 1,\n )\n completed.push(i)\n pushed++\n } catch (err) {\n if (err instanceof ConflictError) {\n const remoteEnvelope = await this.remote.get(this.vault, entry.collection, entry.id)\n if (remoteEnvelope) {\n const { handled, conflict } = await this.handleConflict(\n entry.collection,\n entry.id,\n envelope,\n remoteEnvelope,\n 'push',\n )\n conflicts.push(conflict)\n if (handled === 'local') {\n await this.remote.put(this.vault, entry.collection, entry.id, conflict.local)\n completed.push(i)\n pushed++\n } else if (handled === 'remote') {\n await this.local.put(this.vault, entry.collection, entry.id, conflict.remote)\n completed.push(i)\n } else if (handled === 'merged' && conflict.local !== envelope) {\n // Merged envelope is stored in conflict.local (the winner)\n const merged = conflict.local\n await this.remote.put(this.vault, entry.collection, entry.id, merged)\n await this.local.put(this.vault, entry.collection, entry.id, merged)\n completed.push(i)\n pushed++\n }\n // handled === 'deferred': leave in dirty log\n }\n } else {\n throw err\n }\n }\n }\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)))\n }\n }\n\n // Remove completed entries from dirty log (reverse order to preserve indices)\n for (const i of completed.sort((a, b) => b - a)) {\n this.dirty.splice(i, 1)\n }\n\n this.lastPush = new Date().toISOString()\n await this.persistMeta()\n\n const result: PushResult = { pushed, conflicts, errors }\n this.emitter.emit('sync:push', result)\n return result\n }\n\n /** Pull remote records to local adapter. Accepts optional `PullOptions` for partial sync. */\n async pull(options?: PullOptions): Promise<PullResult> {\n await this.ensureLoaded()\n\n let pulled = 0\n const conflicts: Conflict[] = []\n const errors: Error[] = []\n\n try {\n const remoteSnapshot = await this.remote.loadAll(this.vault)\n\n for (const [collName, records] of Object.entries(remoteSnapshot)) {\n // Partial sync: skip collections not in the filter\n if (options?.collections && !options.collections.includes(collName)) {\n continue\n }\n\n for (const [id, remoteEnvelope] of Object.entries(records)) {\n // Partial sync: modifiedSince filter\n if (options?.modifiedSince && remoteEnvelope._ts <= options.modifiedSince) {\n continue\n }\n\n try {\n const localEnvelope = await this.local.get(this.vault, collName, id)\n\n if (!localEnvelope) {\n // New record from remote\n await this.local.put(this.vault, collName, id, remoteEnvelope)\n pulled++\n } else if (remoteEnvelope._v > localEnvelope._v) {\n // Remote is newer — check if we have a dirty entry for this\n const isDirty = this.dirty.some(d => d.collection === collName && d.id === id)\n if (isDirty) {\n // Both changed — conflict\n const { handled, conflict } = await this.handleConflict(\n collName,\n id,\n localEnvelope,\n remoteEnvelope,\n 'pull',\n )\n conflicts.push(conflict)\n if (handled === 'remote') {\n await this.local.put(this.vault, collName, id, conflict.remote)\n this.dirty = this.dirty.filter(d => !(d.collection === collName && d.id === id))\n pulled++\n } else if (handled === 'merged' && conflict.local !== localEnvelope) {\n const merged = conflict.local\n await this.local.put(this.vault, collName, id, merged)\n this.dirty = this.dirty.filter(d => !(d.collection === collName && d.id === id))\n pulled++\n }\n // 'local' or 'deferred': push handles it\n } else {\n // Remote is newer, no local changes — update\n await this.local.put(this.vault, collName, id, remoteEnvelope)\n pulled++\n }\n }\n // Same version or local is newer — skip (push will handle)\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)))\n }\n }\n }\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)))\n }\n\n this.lastPull = new Date().toISOString()\n await this.persistMeta()\n\n const result: PullResult = { pulled, conflicts, errors }\n this.emitter.emit('sync:pull', result)\n return result\n }\n\n /** Bidirectional sync: pull then push. */\n async sync(options?: { push?: PushOptions; pull?: PullOptions }): Promise<{ pull: PullResult; push: PushResult }> {\n const pullResult = await this.pull(options?.pull)\n const pushResult = await this.push(options?.push)\n return { pull: pullResult, push: pushResult }\n }\n\n /**\n * Push a specific subset of dirty entries (for sync transactions, ).\n * Entries are matched by collection+id from the dirty log; matched entries\n * are removed from the dirty log on success.\n */\n async pushFiltered(predicate: (entry: DirtyEntry) => boolean): Promise<PushResult> {\n await this.ensureLoaded()\n\n let pushed = 0\n const conflicts: Conflict[] = []\n const errors: Error[] = []\n const completed: number[] = []\n\n for (let i = 0; i < this.dirty.length; i++) {\n const entry = this.dirty[i]!\n if (!predicate(entry)) continue\n\n try {\n if (entry.action === 'delete') {\n await this.remote.delete(this.vault, entry.collection, entry.id)\n completed.push(i)\n pushed++\n } else {\n const envelope = await this.local.get(this.vault, entry.collection, entry.id)\n if (!envelope) {\n completed.push(i)\n continue\n }\n\n try {\n await this.remote.put(\n this.vault,\n entry.collection,\n entry.id,\n envelope,\n entry.version - 1,\n )\n completed.push(i)\n pushed++\n } catch (err) {\n if (err instanceof ConflictError) {\n const remoteEnvelope = await this.remote.get(this.vault, entry.collection, entry.id)\n if (remoteEnvelope) {\n const { handled, conflict } = await this.handleConflict(\n entry.collection,\n entry.id,\n envelope,\n remoteEnvelope,\n 'push',\n )\n conflicts.push(conflict)\n if (handled === 'local') {\n await this.remote.put(this.vault, entry.collection, entry.id, conflict.local)\n completed.push(i)\n pushed++\n } else if (handled === 'remote') {\n await this.local.put(this.vault, entry.collection, entry.id, conflict.remote)\n completed.push(i)\n } else if (handled === 'merged' && conflict.local !== envelope) {\n const merged = conflict.local\n await this.remote.put(this.vault, entry.collection, entry.id, merged)\n await this.local.put(this.vault, entry.collection, entry.id, merged)\n completed.push(i)\n pushed++\n }\n }\n } else {\n throw err\n }\n }\n }\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)))\n }\n }\n\n for (const i of completed.sort((a, b) => b - a)) {\n this.dirty.splice(i, 1)\n }\n\n this.lastPush = new Date().toISOString()\n await this.persistMeta()\n\n const result: PushResult = { pushed, conflicts, errors }\n this.emitter.emit('sync:push', result)\n return result\n }\n\n /** Get current sync status. */\n status(): SyncStatus {\n return {\n dirty: this.dirty.length,\n lastPush: this.lastPush,\n lastPull: this.lastPull,\n online: this.isOnline,\n }\n }\n\n // ─── Auto-Sync ───────────────────────────────────────────────────\n\n /** Start auto-sync: listen for online/offline events, optional periodic sync. */\n startAutoSync(intervalMs?: number): void {\n // Online/offline detection\n if (typeof globalThis.addEventListener === 'function') {\n globalThis.addEventListener('online', this.handleOnline)\n globalThis.addEventListener('offline', this.handleOffline)\n }\n\n // Periodic sync\n if (intervalMs && intervalMs > 0) {\n this.autoSyncInterval = setInterval(() => {\n if (this.isOnline) {\n void this.sync()\n }\n }, intervalMs)\n }\n }\n\n /** Stop auto-sync and scheduler. */\n stopAutoSync(): void {\n this.stopScheduler()\n if (typeof globalThis.removeEventListener === 'function') {\n globalThis.removeEventListener('online', this.handleOnline)\n globalThis.removeEventListener('offline', this.handleOffline)\n }\n if (this.autoSyncInterval) {\n clearInterval(this.autoSyncInterval)\n this.autoSyncInterval = null\n }\n }\n\n private handleOnline = (): void => {\n this.isOnline = true\n this.emitter.emit('sync:online', undefined as never)\n void this.sync()\n }\n\n private handleOffline = (): void => {\n this.isOnline = false\n this.emitter.emit('sync:offline', undefined as never)\n }\n\n /**\n * Resolve a conflict, checking per-collection resolvers first,\n * then falling back to the db-level `ConflictStrategy`.\n *\n * Returns the resolved `Conflict` object (possibly with `resolve` set for\n * manual mode) and a `handled` discriminant:\n * - `'local'` — keep the local envelope; push it to remote.\n * - `'remote'` — keep the remote envelope; update local.\n * - `'merged'` — a custom merge fn produced a new envelope stored as `conflict.local`.\n * - `'deferred'` — manual mode, resolve was not called synchronously.\n */\n private async handleConflict(\n collection: string,\n id: string,\n local: EncryptedEnvelope,\n remote: EncryptedEnvelope,\n _phase: 'push' | 'pull',\n ): Promise<{ handled: 'local' | 'remote' | 'merged' | 'deferred'; conflict: Conflict }> {\n const resolver = this.conflictResolvers.get(collection)\n\n if (resolver) {\n // Per-collection resolver is responsible for emitting sync:conflict\n // (manual policy emits with a resolve callback; LWW/FWW/custom are silent).\n const winner = await resolver(id, local, remote)\n const base: Conflict = {\n vault: this.vault,\n collection,\n id,\n local,\n remote,\n localVersion: local._v,\n remoteVersion: remote._v,\n }\n if (winner === null) return { handled: 'deferred', conflict: base }\n if (winner === local) return { handled: 'local', conflict: base }\n if (winner === remote) return { handled: 'remote', conflict: base }\n // Custom merge fn produced a new envelope — store as conflict.local for the caller\n return {\n handled: 'merged',\n conflict: { ...base, local: winner, localVersion: winner._v },\n }\n }\n\n // Fall back to db-level strategy — emit once\n const baseConflict: Conflict = {\n vault: this.vault,\n collection,\n id,\n local,\n remote,\n localVersion: local._v,\n remoteVersion: remote._v,\n }\n this.emitter.emit('sync:conflict', baseConflict)\n const side = this.legacyResolve(baseConflict)\n return { handled: side, conflict: baseConflict }\n }\n\n /** DB-level ConflictStrategy resolution (legacy, kept for backward compat). */\n private legacyResolve(conflict: Conflict): 'local' | 'remote' {\n if (typeof this.strategy === 'function') {\n return this.strategy(conflict)\n }\n switch (this.strategy) {\n case 'local-wins': return 'local'\n case 'remote-wins': return 'remote'\n case 'version':\n default:\n return conflict.localVersion >= conflict.remoteVersion ? 'local' : 'remote'\n }\n }\n\n // ─── Persistence ─────────────────────────────────────────────────\n\n private async ensureLoaded(): Promise<void> {\n if (this.loaded) return\n\n const envelope = await this.local.get(this.vault, '_sync', 'meta')\n if (envelope) {\n const meta = JSON.parse(envelope._data) as SyncMetadata\n this.dirty = [...meta.dirty]\n this.lastPush = meta.last_push\n this.lastPull = meta.last_pull\n }\n\n this.loaded = true\n }\n\n private async persistMeta(): Promise<void> {\n const meta: SyncMetadata = {\n _noydb_sync: NOYDB_SYNC_VERSION,\n last_push: this.lastPush,\n last_pull: this.lastPull,\n dirty: this.dirty,\n }\n\n const envelope: EncryptedEnvelope = {\n _noydb: 1,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: JSON.stringify(meta),\n }\n\n await this.local.put(this.vault, '_sync', 'meta', envelope)\n }\n}\n","import type { SyncTransactionResult } from '../../kernel/types.js'\nimport type { SyncEngine } from './sync.js'\nimport type { Vault } from '../../kernel/vault.js'\n\ninterface TxOp {\n readonly type: 'put' | 'delete'\n readonly collection: string\n readonly id: string\n readonly record?: unknown\n}\n\n/**\n * Sync transaction.\n *\n * Stages local writes and then pushes only those records to remote in a\n * single batch. If any record conflicts during the push, the result\n * carries `status: 'conflict'` — no automatic rollback is performed;\n * the caller handles conflict resolution.\n *\n * Obtain via `db.transaction(compartmentName)`.\n */\nexport class SyncTransaction {\n private readonly comp: Vault\n private readonly engine: SyncEngine\n private readonly ops: TxOp[] = []\n\n /** @internal — constructed by `Noydb.transaction()` */\n constructor(comp: Vault, engine: SyncEngine) {\n this.comp = comp\n this.engine = engine\n }\n\n /** Stage a record write. Does not write to any adapter until `commit()`. */\n put(collection: string, id: string, record: unknown): this {\n this.ops.push({ type: 'put', collection, id, record })\n return this\n }\n\n /** Stage a record delete. Does not write to any adapter until `commit()`. */\n delete(collection: string, id: string): this {\n this.ops.push({ type: 'delete', collection, id })\n return this\n }\n\n /**\n * Commit the transaction.\n *\n * Phase 1 — writes all staged operations to the local adapter via the\n * collection layer (encryption + dirty-log tracking).\n *\n * Phase 2 — pushes only the records that were written in this\n * transaction to the remote adapter. Existing dirty entries from\n * outside this transaction are not affected.\n *\n * If any record conflicts during the push, `status` is `'conflict'`\n * and `conflicts` lists the affected records. No automatic rollback is\n * performed.\n */\n async commit(): Promise<SyncTransactionResult> {\n // Phase 1: write all staged ops to local via collection layer\n for (const op of this.ops) {\n if (op.type === 'put') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await (this.comp.collection<any>(op.collection)).put(op.id, op.record as any)\n } else {\n await this.comp.collection(op.collection).delete(op.id)\n }\n }\n\n // Phase 2: push only the records from this transaction\n const opSet = new Set<string>()\n for (const op of this.ops) {\n opSet.add(`${op.collection}::${op.id}`)\n }\n\n const pushResult = await this.engine.pushFiltered(\n (entry) => opSet.has(`${entry.collection}::${entry.id}`),\n )\n\n return {\n status: pushResult.conflicts.length > 0 ? 'conflict' : 'committed',\n pushed: pushResult.pushed,\n conflicts: pushResult.conflicts,\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
encrypt,
|
|
3
|
+
openEnvelopeJson
|
|
4
|
+
} from "./chunk-5O3AOPDT.js";
|
|
4
5
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "./chunk-PP6W64Y3.js";
|
|
6
|
+
NOYDB_FORMAT_VERSION
|
|
7
|
+
} from "./chunk-5TDJ56JE.js";
|
|
8
8
|
import {
|
|
9
9
|
ConflictError
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ZYUC53LT.js";
|
|
11
11
|
|
|
12
|
-
// src/attestation/signer.ts
|
|
12
|
+
// src/with-audit/attestation/signer.ts
|
|
13
13
|
import { generateDocSigningKeyPair } from "@noy-db/attestation";
|
|
14
14
|
var ATTESTATIONS_COLLECTION = "_attestations";
|
|
15
15
|
var SIGNER_RECORD_ID = "_signer";
|
|
@@ -18,7 +18,7 @@ async function loadSigner(store, vault, getDEK) {
|
|
|
18
18
|
const existing = await store.get(vault, ATTESTATIONS_COLLECTION, SIGNER_RECORD_ID);
|
|
19
19
|
if (!existing) return null;
|
|
20
20
|
const dek = await getDEK(ATTESTATIONS_COLLECTION);
|
|
21
|
-
const json = await
|
|
21
|
+
const json = await openEnvelopeJson(existing, dek);
|
|
22
22
|
return JSON.parse(json);
|
|
23
23
|
}
|
|
24
24
|
async function loadOrCreateSigner(store, vault, getDEK) {
|
|
@@ -54,4 +54,4 @@ export {
|
|
|
54
54
|
loadSigner,
|
|
55
55
|
loadOrCreateSigner
|
|
56
56
|
};
|
|
57
|
-
//# sourceMappingURL=chunk-
|
|
57
|
+
//# sourceMappingURL=chunk-JNUWZ6D3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-audit/attestation/signer.ts"],"sourcesContent":["import type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\nimport { encrypt, openEnvelopeJson, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { ConflictError } from '../../kernel/errors.js'\nimport { generateDocSigningKeyPair } from '@noy-db/attestation'\n\nexport const ATTESTATIONS_COLLECTION = '_attestations'\nexport const SIGNER_RECORD_ID = '_signer'\nexport const REVOKED_RECORD_ID = '_revoked'\n\nexport interface DocSigner {\n readonly keyId: string\n readonly publicKeyB64: string\n readonly privateKeyPkcs8B64: string\n}\n\n/**\n * Pure read: return the firm's persisted document-signing keypair, or `null`\n * if none has been minted yet. Never writes — callers that must NOT mint\n * (e.g. an ungated public-key getter) use this instead of `loadOrCreateSigner`.\n *\n * Stored as an encrypted record `_attestations/_signer` under the\n * `_attestations` collection DEK (resolved via `getDEK`, which is\n * AES-KW-wrapped under the owner KEK + persisted by the keyring). The KEK\n * itself is AES-KW-only and cannot AES-GCM-encrypt these bytes — hence\n * storage under a normal collection DEK.\n */\nexport async function loadSigner(\n store: NoydbStore,\n vault: string,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n): Promise<DocSigner | null> {\n const existing = await store.get(vault, ATTESTATIONS_COLLECTION, SIGNER_RECORD_ID)\n if (!existing) return null\n const dek = await getDEK(ATTESTATIONS_COLLECTION)\n const json = await openEnvelopeJson(existing, dek)\n return JSON.parse(json) as DocSigner\n}\n\n/**\n * Lazily mint (or load) the firm's Ed25519 document-signing keypair.\n *\n * On a concurrent first-mint, two callers can both read `null` and both mint\n * distinct keypairs. The `put(…, expectedVersion: 0)` (\"must not already\n * exist\") lets exactly one win; the loser catches `ConflictError`, re-reads,\n * and returns the winner's signer — converging on a single keypair rather than\n * clobbering it or surfacing a raw conflict. (All real stores treat a missing\n * record + `ev: 0` as a no-conflict write, so the catch fires on lost-race only.)\n */\nexport async function loadOrCreateSigner(\n store: NoydbStore,\n vault: string,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n): Promise<DocSigner> {\n const existing = await loadSigner(store, vault, getDEK)\n if (existing) return existing\n\n const dek = await getDEK(ATTESTATIONS_COLLECTION)\n const signer = await generateDocSigningKeyPair()\n const { iv, data } = await encrypt(JSON.stringify(signer), dek)\n const env: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: new Date().toISOString(), _iv: iv, _data: data,\n }\n try {\n await store.put(vault, ATTESTATIONS_COLLECTION, SIGNER_RECORD_ID, env, 0)\n return signer\n } catch (e) {\n if (!(e instanceof ConflictError)) throw e\n // Lost the race — another writer minted first. Adopt the winner.\n const winner = await loadSigner(store, vault, getDEK)\n if (!winner) {\n throw new ConflictError(0, 'loadOrCreateSigner: signer mint lost a concurrent race but the winning record could not be re-read.')\n }\n return winner\n }\n}\n"],"mappings":";;;;;;;;;;;;AAIA,SAAS,iCAAiC;AAEnC,IAAM,0BAA0B;AAChC,IAAM,mBAAmB;AACzB,IAAM,oBAAoB;AAmBjC,eAAsB,WACpB,OACA,OACA,QAC2B;AAC3B,QAAM,WAAW,MAAM,MAAM,IAAI,OAAO,yBAAyB,gBAAgB;AACjF,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,MAAM,MAAM,OAAO,uBAAuB;AAChD,QAAM,OAAO,MAAM,iBAAiB,UAAU,GAAG;AACjD,SAAO,KAAK,MAAM,IAAI;AACxB;AAYA,eAAsB,mBACpB,OACA,OACA,QACoB;AACpB,QAAM,WAAW,MAAM,WAAW,OAAO,OAAO,MAAM;AACtD,MAAI,SAAU,QAAO;AAErB,QAAM,MAAM,MAAM,OAAO,uBAAuB;AAChD,QAAM,SAAS,MAAM,0BAA0B;AAC/C,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,KAAK,UAAU,MAAM,GAAG,GAAG;AAC9D,QAAM,MAAyB;AAAA,IAC7B,QAAQ;AAAA,IAAsB,IAAI;AAAA,IAAG,MAAK,oBAAI,KAAK,GAAE,YAAY;AAAA,IAAG,KAAK;AAAA,IAAI,OAAO;AAAA,EACtF;AACA,MAAI;AACF,UAAM,MAAM,IAAI,OAAO,yBAAyB,kBAAkB,KAAK,CAAC;AACxE,WAAO;AAAA,EACT,SAAS,GAAG;AACV,QAAI,EAAE,aAAa,eAAgB,OAAM;AAEzC,UAAM,SAAS,MAAM,WAAW,OAAO,OAAO,MAAM;AACpD,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,cAAc,GAAG,qGAAqG;AAAA,IAClI;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=chunk-K2WCO3NX.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PortabilityNotEnabledError
|
|
3
|
+
} from "./chunk-ZYUC53LT.js";
|
|
4
|
+
|
|
5
|
+
// src/with-audit/portability/strategy.ts
|
|
6
|
+
var NO_PORTABILITY = {
|
|
7
|
+
async exportAccessibleData() {
|
|
8
|
+
throw new PortabilityNotEnabledError();
|
|
9
|
+
},
|
|
10
|
+
async withdrawAccessibleData() {
|
|
11
|
+
throw new PortabilityNotEnabledError();
|
|
12
|
+
},
|
|
13
|
+
async requestWithdrawal() {
|
|
14
|
+
throw new PortabilityNotEnabledError();
|
|
15
|
+
},
|
|
16
|
+
async listWithdrawalRequests() {
|
|
17
|
+
throw new PortabilityNotEnabledError();
|
|
18
|
+
},
|
|
19
|
+
async approveWithdrawal() {
|
|
20
|
+
throw new PortabilityNotEnabledError();
|
|
21
|
+
},
|
|
22
|
+
async rejectWithdrawal() {
|
|
23
|
+
throw new PortabilityNotEnabledError();
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
NO_PORTABILITY
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=chunk-KVOXU4T5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-audit/portability/strategy.ts"],"sourcesContent":["/**\n * Portability capability strategy — the six on-demand data-sovereignty methods\n * the vault's `UserApi` routes through (`exportAccessibleData` /\n * `withdrawAccessibleData` / `requestWithdrawal` / `listWithdrawalRequests` /\n * `approveWithdrawal` / `rejectWithdrawal`). The active engine\n * ({@link withPortability}) dynamically imports the export/withdraw/request\n * cores (keeping them reachable only via opt-in); {@link NO_PORTABILITY} throws.\n * The Vault always injects the closures the UserApi calls; they delegate here,\n * so an un-opted-in caller hits `NO_PORTABILITY`'s throw.\n * @internal\n */\nimport type { Vault } from '../../kernel/vault.js'\nimport type { ExportAccessibleOptions } from './export-accessible.js'\nimport type { WithdrawAccessibleOptions, WithdrawResult } from './withdraw-accessible.js'\nimport type {\n RequestWithdrawalOptions,\n RequestWithdrawalResult,\n WithdrawalRequest,\n WithdrawalRequestStatus,\n ApproveWithdrawalOptions,\n RejectWithdrawalOptions,\n} from './request-withdrawal.js'\nimport { PortabilityNotEnabledError } from '../../kernel/errors.js'\n\nexport interface PortabilityStrategy {\n exportAccessibleData(vault: Vault, opts: ExportAccessibleOptions): Promise<Uint8Array>\n withdrawAccessibleData(vault: Vault, opts: WithdrawAccessibleOptions): Promise<WithdrawResult>\n requestWithdrawal(vault: Vault, opts: RequestWithdrawalOptions): Promise<RequestWithdrawalResult>\n listWithdrawalRequests(vault: Vault, opts: { status?: WithdrawalRequestStatus }): Promise<WithdrawalRequest[]>\n approveWithdrawal(vault: Vault, requestId: string, opts: ApproveWithdrawalOptions): Promise<WithdrawResult>\n rejectWithdrawal(vault: Vault, requestId: string, opts: RejectWithdrawalOptions): Promise<WithdrawalRequest>\n}\n\n/**\n * No-op stub — the floor default. Every capability method throws\n * {@link PortabilityNotEnabledError}; opt in with\n * `portabilityStrategy: withPortability()` in createNoydb. @internal\n */\nexport const NO_PORTABILITY: PortabilityStrategy = {\n async exportAccessibleData() { throw new PortabilityNotEnabledError() },\n async withdrawAccessibleData() { throw new PortabilityNotEnabledError() },\n async requestWithdrawal() { throw new PortabilityNotEnabledError() },\n async listWithdrawalRequests() { throw new PortabilityNotEnabledError() },\n async approveWithdrawal() { throw new PortabilityNotEnabledError() },\n async rejectWithdrawal() { throw new PortabilityNotEnabledError() },\n}\n"],"mappings":";;;;;AAsCO,IAAM,iBAAsC;AAAA,EACjD,MAAM,uBAAuB;AAAE,UAAM,IAAI,2BAA2B;AAAA,EAAE;AAAA,EACtE,MAAM,yBAAyB;AAAE,UAAM,IAAI,2BAA2B;AAAA,EAAE;AAAA,EACxE,MAAM,oBAAoB;AAAE,UAAM,IAAI,2BAA2B;AAAA,EAAE;AAAA,EACnE,MAAM,yBAAyB;AAAE,UAAM,IAAI,2BAA2B;AAAA,EAAE;AAAA,EACxE,MAAM,oBAAoB;AAAE,UAAM,IAAI,2BAA2B;AAAA,EAAE;AAAA,EACnE,MAAM,mBAAmB;AAAE,UAAM,IAAI,2BAA2B;AAAA,EAAE;AACpE;","names":[]}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {
|
|
2
|
+
freezeSnapshotOnly
|
|
3
|
+
} from "./chunk-PSMV73A5.js";
|
|
4
|
+
import {
|
|
5
|
+
resolveManagedSecret
|
|
6
|
+
} from "./chunk-ZCGAHGUS.js";
|
|
7
|
+
import {
|
|
8
|
+
createOwnerKeyring
|
|
9
|
+
} from "./chunk-WWGT2MDV.js";
|
|
10
|
+
import {
|
|
11
|
+
wrapKey
|
|
12
|
+
} from "./chunk-5O3AOPDT.js";
|
|
13
|
+
import {
|
|
14
|
+
NOYDB_FORMAT_VERSION
|
|
15
|
+
} from "./chunk-5TDJ56JE.js";
|
|
16
|
+
import {
|
|
17
|
+
PermissionDeniedError
|
|
18
|
+
} from "./chunk-ZYUC53LT.js";
|
|
19
|
+
|
|
20
|
+
// src/with-party/team/deed.ts
|
|
21
|
+
var DEED_RECORD_ID = "deed";
|
|
22
|
+
async function createDeedOwner(store, vault, ownerUserId, sealing) {
|
|
23
|
+
const passphrase = await resolveManagedSecret(store, vault, sealing);
|
|
24
|
+
const keyring = await createOwnerKeyring(store, vault, ownerUserId, passphrase);
|
|
25
|
+
await saveDeedMarker(store, vault, {
|
|
26
|
+
ownerUserId,
|
|
27
|
+
sealedUnder: sealing.id,
|
|
28
|
+
latent: true,
|
|
29
|
+
issuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
30
|
+
});
|
|
31
|
+
return keyring;
|
|
32
|
+
}
|
|
33
|
+
async function loadDeedMarker(store, vault) {
|
|
34
|
+
const envelope = await store.get(vault, "_meta", DEED_RECORD_ID);
|
|
35
|
+
if (!envelope) return null;
|
|
36
|
+
let payload;
|
|
37
|
+
try {
|
|
38
|
+
payload = JSON.parse(envelope._data);
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (typeof payload !== "object" || payload === null) return null;
|
|
43
|
+
const r = payload;
|
|
44
|
+
if (r._noydb_deed !== 1) return null;
|
|
45
|
+
if (typeof r.ownerUserId !== "string" || typeof r.sealedUnder !== "string" || r.latent !== true || typeof r.issuedAt !== "string") {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const marker = {
|
|
49
|
+
ownerUserId: r.ownerUserId,
|
|
50
|
+
sealedUnder: r.sealedUnder,
|
|
51
|
+
latent: true,
|
|
52
|
+
issuedAt: r.issuedAt,
|
|
53
|
+
...typeof r.liberatedAt === "string" ? { liberatedAt: r.liberatedAt } : {}
|
|
54
|
+
};
|
|
55
|
+
return marker;
|
|
56
|
+
}
|
|
57
|
+
async function isDeedVault(store, vault) {
|
|
58
|
+
return await loadDeedMarker(store, vault) !== null;
|
|
59
|
+
}
|
|
60
|
+
async function saveDeedMarker(store, vault, marker) {
|
|
61
|
+
const persisted = { _noydb_deed: 1, ...marker };
|
|
62
|
+
const prior = await store.get(vault, "_meta", DEED_RECORD_ID);
|
|
63
|
+
const env = {
|
|
64
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
65
|
+
_v: (prior?._v ?? 0) + 1,
|
|
66
|
+
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
67
|
+
// AES-GCM bypassed — the marker is plaintext audit metadata.
|
|
68
|
+
_iv: "",
|
|
69
|
+
_data: JSON.stringify(persisted)
|
|
70
|
+
};
|
|
71
|
+
await store.put(vault, "_meta", DEED_RECORD_ID, env);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/with-party/custody/liberate.ts
|
|
75
|
+
async function liberateVault(vault, opts) {
|
|
76
|
+
await vault.noydb.checkGate(vault.name, "liberate-vault", opts.factors);
|
|
77
|
+
const { name: vaultName, adapter, keyring } = vault._introspectState();
|
|
78
|
+
if (keyring.role !== "custodian") {
|
|
79
|
+
throw new PermissionDeniedError(
|
|
80
|
+
"liberation is claimed only by the custodian (the de-facto authority holding the DEKs)"
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const existing = await adapter.get(vaultName, "_keyring", opts.newOwnerId);
|
|
84
|
+
if (existing) {
|
|
85
|
+
throw new PermissionDeniedError(
|
|
86
|
+
`liberateVault: newOwnerId "${opts.newOwnerId}" already exists as a principal; choose a fresh id (liberation mints a distinct owner, it never overwrites an existing keyring)`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
const collections = await listOperationalCollections(vault);
|
|
90
|
+
const snapshot = await freezeSnapshotOnly(vault, collections, { actorUserId: keyring.userId });
|
|
91
|
+
const newOwner = await createOwnerKeyring(adapter, vaultName, opts.newOwnerId, opts.newOwnerPassphrase);
|
|
92
|
+
if (!newOwner.kek) {
|
|
93
|
+
throw new PermissionDeniedError(
|
|
94
|
+
`new owner keyring for "${opts.newOwnerId}" has no KEK to re-wrap the incumbent DEKs under`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const env = await adapter.get(vaultName, "_keyring", opts.newOwnerId);
|
|
98
|
+
if (!env) {
|
|
99
|
+
throw new PermissionDeniedError(`new owner keyring for "${opts.newOwnerId}" did not persist`);
|
|
100
|
+
}
|
|
101
|
+
const keyringFile = JSON.parse(env._data);
|
|
102
|
+
const mergedDeks = { ...keyringFile.deks };
|
|
103
|
+
for (const [collection, dek] of keyring.deks) {
|
|
104
|
+
mergedDeks[collection] = await wrapKey(dek, newOwner.kek);
|
|
105
|
+
}
|
|
106
|
+
const mergedFile = { ...keyringFile, deks: mergedDeks };
|
|
107
|
+
await adapter.put(vaultName, "_keyring", opts.newOwnerId, { ...env, _data: JSON.stringify(mergedFile) });
|
|
108
|
+
await vault._getLedgerOrNull()?.append({
|
|
109
|
+
op: "lifecycle",
|
|
110
|
+
collection: "",
|
|
111
|
+
id: "",
|
|
112
|
+
version: 0,
|
|
113
|
+
actor: opts.newOwnerId,
|
|
114
|
+
payloadHash: "",
|
|
115
|
+
reason: `liberation-claimed:${opts.newOwnerId}:${opts.legalBasis}`
|
|
116
|
+
});
|
|
117
|
+
const marker = await loadDeedMarker(adapter, vaultName);
|
|
118
|
+
if (marker) {
|
|
119
|
+
await saveDeedMarker(adapter, vaultName, { ...marker, liberatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
120
|
+
}
|
|
121
|
+
return { snapshot };
|
|
122
|
+
}
|
|
123
|
+
async function listOperationalCollections(vault) {
|
|
124
|
+
const { keyring } = vault._introspectState();
|
|
125
|
+
return [...keyring.deks.keys()].filter((c) => !c.startsWith("_"));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export {
|
|
129
|
+
DEED_RECORD_ID,
|
|
130
|
+
createDeedOwner,
|
|
131
|
+
loadDeedMarker,
|
|
132
|
+
isDeedVault,
|
|
133
|
+
liberateVault
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=chunk-KXGNJJXB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-party/team/deed.ts","../src/with-party/custody/liberate.ts"],"sourcesContent":["/**\n * FR-6 — Deed: sealed / hidden-owner provisioning.\n *\n * A **Deed** is a vault owned by a *latent* principal who never\n * authenticates. The owner credential (the random passphrase that\n * derives `KEK_owner`) is minted machine-side and sealed under a\n * **non-firm** {@link SealingKeyProvider} — that sealing boundary is\n * the cryptographic *inalienability anchor*: whoever holds the\n * provider (the client / their KMS) is the only party that can ever\n * re-derive `KEK_owner`. A firm-side custodian operates the vault\n * fully but can never reach the owner key — there is deliberately NO\n * firm-controlled fallback.\n *\n * Provisioning reuses the managed-mode seam:\n * - {@link resolveManagedSecret} mints + seals + persists the random\n * passphrase at `_meta/sealed-passphrase` (and unseals it on every\n * subsequent open through the same provider).\n * - {@link createOwnerKeyring} derives `KEK_owner` from that\n * passphrase and writes the `_keyring/<ownerUserId>` file.\n *\n * On top of that, a Deed writes a `_meta/deed` **marker**. The marker\n * is PLAINTEXT metadata — it records WHO the latent owner is and WHICH\n * sealing boundary protects them, so it must be readable without\n * unlocking the vault (a custodian or auditor inspecting the vault must\n * be able to see \"this is a Deed vault, owner = X, sealed under Y\"\n * without any key). The marker itself is therefore never sealed; only\n * the owner *credential* is.\n *\n * @module\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from './keyring.js'\nimport { createOwnerKeyring } from './keyring.js'\nimport type { SealingKeyProvider } from './managed-passphrase.js'\nimport { resolveManagedSecret } from './managed-passphrase.js'\n\n/** Reserved id for the Deed marker under `_meta`. */\nexport const DEED_RECORD_ID = 'deed' as const\n\n/**\n * Plaintext metadata recording a vault's latent-owner Deed. Persisted\n * at `_meta/deed`. Readable without unlocking the vault by design — it\n * carries no secret material, only the identity of the latent owner\n * and the sealing boundary that anchors inalienability.\n */\nexport interface DeedMarker {\n /** The latent owner principal — the user id of the `_keyring/<id>` owner file. */\n readonly ownerUserId: string\n /**\n * The `SealingKeyProvider.id` the owner credential is sealed under.\n * The inalienability anchor: only the holder of this (non-firm)\n * provider can re-derive `KEK_owner`. Audit metadata — never secret.\n */\n readonly sealedUnder: string\n /** Always `true` for a Deed — the owner never authenticates interactively. */\n readonly latent: true\n /** ISO-8601 timestamp the Deed was issued. */\n readonly issuedAt: string\n /**\n * ISO-8601 timestamp the vault was liberated (a new owner claimed it\n * via the audited Liberate ceremony), if any. Absent until liberation.\n */\n readonly liberatedAt?: string\n}\n\n/**\n * Wire-format payload stored inside the `_meta/deed` envelope. Carries\n * a magic marker for forensics + the {@link DeedMarker} fields. JSON,\n * AES-GCM bypassed (the marker is plaintext metadata by design).\n */\ninterface DeedEnvelopePayload extends DeedMarker {\n readonly _noydb_deed: 1\n}\n\n/**\n * Provision a Deed: a latent owner whose credential is sealed under\n * `sealing` (a NON-firm provider). Mints + seals the owner passphrase\n * via {@link resolveManagedSecret}, derives the owner keyring via\n * {@link createOwnerKeyring}, then writes the plaintext `_meta/deed`\n * marker. Returns the unlocked owner keyring.\n *\n * The returned keyring is the only in-memory window onto `KEK_owner`\n * for this call; the persistent re-entry point is the sealing provider\n * (re-run {@link resolveManagedSecret} with the same provider to\n * re-resolve the passphrase, then {@link createOwnerKeyring}/load to\n * unlock — no interactive passphrase is ever required).\n *\n * There is deliberately no firm-controlled fallback: the marker's\n * `sealedUnder` is the single cryptographic anchor of ownership.\n */\nexport async function createDeedOwner(\n store: NoydbStore,\n vault: string,\n ownerUserId: string,\n sealing: SealingKeyProvider,\n): Promise<UnlockedKeyring> {\n // 1. Mint + seal + persist the random owner passphrase under the\n // non-firm provider (writes _meta/sealed-passphrase).\n const passphrase = await resolveManagedSecret(store, vault, sealing)\n\n // 2. Derive KEK_owner from the (never-human-seen) passphrase and\n // write the owner keyring file.\n const keyring = await createOwnerKeyring(store, vault, ownerUserId, passphrase)\n\n // 3. Write the plaintext Deed marker. NOT sealed — it is audit\n // metadata that must be readable without unlocking.\n await saveDeedMarker(store, vault, {\n ownerUserId,\n sealedUnder: sealing.id,\n latent: true,\n issuedAt: new Date().toISOString(),\n })\n\n return keyring\n}\n\n/**\n * Read the {@link DeedMarker} from `_meta/deed`, or `null` if the vault\n * has no Deed marker (i.e. it is not a Deed vault). Plaintext read — no\n * unlocking required.\n */\nexport async function loadDeedMarker(\n store: NoydbStore,\n vault: string,\n): Promise<DeedMarker | null> {\n const envelope = await store.get(vault, '_meta', DEED_RECORD_ID)\n if (!envelope) return null\n let payload: unknown\n try {\n payload = JSON.parse(envelope._data)\n } catch {\n return null\n }\n if (typeof payload !== 'object' || payload === null) return null\n const r = payload as Record<string, unknown>\n if (r._noydb_deed !== 1) return null\n if (\n typeof r.ownerUserId !== 'string'\n || typeof r.sealedUnder !== 'string'\n || r.latent !== true\n || typeof r.issuedAt !== 'string'\n ) {\n return null\n }\n const marker: DeedMarker = {\n ownerUserId: r.ownerUserId,\n sealedUnder: r.sealedUnder,\n latent: true,\n issuedAt: r.issuedAt,\n ...(typeof r.liberatedAt === 'string' ? { liberatedAt: r.liberatedAt } : {}),\n }\n return marker\n}\n\n/** Whether `vault` carries a Deed marker (a sealed/latent owner). */\nexport async function isDeedVault(\n store: NoydbStore,\n vault: string,\n): Promise<boolean> {\n return (await loadDeedMarker(store, vault)) !== null\n}\n\n/**\n * Persist a {@link DeedMarker} at `_meta/deed`. Mirrors\n * `saveSealedPassphrase`'s envelope shape: a standard\n * {@link EncryptedEnvelope} with AES-GCM bypassed (`_iv: ''`), the\n * payload stored as plaintext JSON in `_data`. The marker is metadata,\n * not a secret — sealing it would defeat its purpose (auditors /\n * custodians must read it without a key).\n *\n * @internal — used by `createDeedOwner` and (later) the Liberate\n * ceremony to stamp `liberatedAt`.\n */\nexport async function saveDeedMarker(\n store: NoydbStore,\n vault: string,\n marker: DeedMarker,\n): Promise<void> {\n const persisted: DeedEnvelopePayload = { _noydb_deed: 1, ...marker }\n const prior = await store.get(vault, '_meta', DEED_RECORD_ID)\n const env: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: (prior?._v ?? 0) + 1,\n _ts: new Date().toISOString(),\n // AES-GCM bypassed — the marker is plaintext audit metadata.\n _iv: '',\n _data: JSON.stringify(persisted),\n }\n await store.put(vault, '_meta', DEED_RECORD_ID, env)\n}\n","/**\n * `liberateVault` — the audited claim of ownership over a\n * sealed-owner (Deed) vault. The inverse of withdrawal.\n *\n * A **Deed** vault's owner credential is sealed under a non-firm provider, so\n * the firm-side **custodian** (which holds every collection DEK and operates\n * the vault fully) can never reach `KEK_owner`. Liberation is the ONLY route\n * by which a custodian assumes ownership, and it is deliberately a manual,\n * audited ceremony:\n *\n * 1. gate `'liberate-vault'` (fail-closed)\n * 2. caller MUST be the `custodian` (the de-facto authority holding the DEKs)\n * 3. freeze a PRE-liberation EVIDENCE snapshot (hash-pinned in the ledger) —\n * but PRESERVE the live data for the new owner (see the freeze decision\n * below)\n * 4. mint a NEW owner keyring re-wrapping the incumbent DEKs under the new\n * owner's KEK\n * 5. lifecycle ledger `liberation-claimed:<newOwnerId>:<legalBasis>`\n * 6. stamp the `_meta/deed` marker with `liberatedAt`\n *\n * ## Security: the inalienability floor\n *\n * Liberation **mints a new owner from the custodian's DEKs** — it does NOT\n * unseal the original sealed owner. The old sealed-owner credential is left\n * untouched and ORPHANED (its `_keyring/<id>` file remains, its KEK is still\n * sealed under the non-firm provider), never impersonated. The new owner is a\n * DISTINCT principal under a fresh KEK derived from `newOwnerPassphrase`. This\n * preserves the inalienability floor: the act of claiming ownership is itself\n * auditable and produces a different principal, rather than silently assuming\n * the latent owner's identity.\n *\n * ## Freeze decision: snapshot-only, not freeze-and-delete\n *\n * `freezeAndDeleteClosure` (withdraw-accessible.ts) writes a hash-pinned\n * snapshot and THEN delete-closures the live records — correct for a\n * destructive withdrawal, WRONG for liberation. Liberation transfers\n * operational continuity; it must leave the live data intact for the new\n * owner. We therefore call the snapshot-only core `freezeSnapshotOnly`\n * (factored out of that module; the freeze-AND-delete withdrawal path is\n * unchanged) to pin the evidence snapshot while preserving the records.\n *\n * @module\n */\n\nimport type { Vault } from '../../kernel/vault.js'\nimport type { FactorProofBundle, KeyringFile } from '../../kernel/types.js'\nimport { PermissionDeniedError } from '../../kernel/errors.js'\nimport { wrapKey } from '../../kernel/enclave/index.js'\nimport { createOwnerKeyring } from '../team/keyring.js'\nimport type { FrozenSnapshotRef } from '../../with-audit/portability/withdraw-accessible.js'\nimport { freezeSnapshotOnly } from '../../with-audit/portability/withdraw-accessible.js'\nimport { loadDeedMarker, saveDeedMarker } from '../team/deed.js'\n\nexport interface LiberateOptions {\n /** The id of the new owner principal the custodian mints by claiming ownership. */\n readonly newOwnerId: string\n /** The passphrase that derives the new owner's KEK (the DEKs are re-wrapped under it). */\n readonly newOwnerPassphrase: string\n /** Legal/contractual basis recorded in the audit (e.g. 'contractual-handover'). */\n readonly legalBasis: string\n readonly factors?: FactorProofBundle\n}\n\nexport interface LiberateResult {\n /** The hash-pinned pre-liberation evidence snapshot. */\n readonly snapshot: FrozenSnapshotRef\n}\n\n/**\n * Audited claim of ownership over a sealed-owner vault by its custodian. See\n * the module doc for the full ceremony + security rationale.\n */\nexport async function liberateVault(\n vault: Vault,\n opts: LiberateOptions,\n): Promise<LiberateResult> {\n // 1. gate — fail-closed `liberate-vault`.\n await vault.noydb.checkGate(vault.name, 'liberate-vault', opts.factors)\n\n // 2. caller must be the custodian (the de-facto authority holding the DEKs).\n const { name: vaultName, adapter, keyring } = vault._introspectState()\n if (keyring.role !== 'custodian') {\n throw new PermissionDeniedError(\n 'liberation is claimed only by the custodian (the de-facto authority holding the DEKs)',\n )\n }\n\n // Refuse to clobber an existing principal: the new owner must be a FRESH id.\n // (Checked before any side effect so a colliding id never leaves a half-run\n // ceremony — no orphan snapshot, no partial keyring overwrite.)\n const existing = await adapter.get(vaultName, '_keyring', opts.newOwnerId)\n if (existing) {\n throw new PermissionDeniedError(\n `liberateVault: newOwnerId \"${opts.newOwnerId}\" already exists as a principal; choose a fresh id (liberation mints a distinct owner, it never overwrites an existing keyring)`,\n )\n }\n\n // 3. Pre-liberation EVIDENCE snapshot of every operational collection —\n // snapshot-only (live data preserved for the new owner; liberation\n // transfers operational continuity, it does not erase).\n const collections = await listOperationalCollections(vault)\n const snapshot = await freezeSnapshotOnly(vault, collections, { actorUserId: keyring.userId })\n\n // 4. Mint a NEW owner keyring, re-wrapping the incumbent DEKs under the new\n // owner's fresh KEK. The old sealed owner is NOT unsealed — a DISTINCT\n // principal is minted and the original owner credential is left orphaned\n // (inalienability floor). Mirrors adopt-partition.ts's DEK re-wrap.\n const newOwner = await createOwnerKeyring(adapter, vaultName, opts.newOwnerId, opts.newOwnerPassphrase)\n if (!newOwner.kek) {\n throw new PermissionDeniedError(\n `new owner keyring for \"${opts.newOwnerId}\" has no KEK to re-wrap the incumbent DEKs under`,\n )\n }\n const env = await adapter.get(vaultName, '_keyring', opts.newOwnerId)\n if (!env) {\n throw new PermissionDeniedError(`new owner keyring for \"${opts.newOwnerId}\" did not persist`)\n }\n const keyringFile = JSON.parse(env._data) as KeyringFile\n const mergedDeks: Record<string, string> = { ...keyringFile.deks }\n for (const [collection, dek] of keyring.deks) {\n mergedDeks[collection] = await wrapKey(dek, newOwner.kek)\n }\n const mergedFile: KeyringFile = { ...keyringFile, deks: mergedDeks }\n await adapter.put(vaultName, '_keyring', opts.newOwnerId, { ...env, _data: JSON.stringify(mergedFile) })\n\n // 5. Lifecycle ledger audit (no-op if the history strategy is absent).\n await vault._getLedgerOrNull()?.append({\n op: 'lifecycle', collection: '', id: '', version: 0, actor: opts.newOwnerId, payloadHash: '',\n reason: `liberation-claimed:${opts.newOwnerId}:${opts.legalBasis}`,\n })\n\n // 6. Stamp the Deed marker with `liberatedAt` (if a marker exists).\n const marker = await loadDeedMarker(adapter, vaultName)\n if (marker) {\n await saveDeedMarker(adapter, vaultName, { ...marker, liberatedAt: new Date().toISOString() })\n }\n\n return { snapshot }\n}\n\n/**\n * Enumerate the vault's operational (user-facing) collections — those carried\n * by the caller's keyring DEK map, minus the reserved internal collections\n * (`_*`). These are the collections frozen into the evidence snapshot.\n */\nasync function listOperationalCollections(vault: Vault): Promise<readonly string[]> {\n const { keyring } = vault._introspectState()\n return [...keyring.deks.keys()].filter((c) => !c.startsWith('_'))\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {
|
|
2
|
+
diff
|
|
3
|
+
} from "./chunk-SVLR4POP.js";
|
|
4
|
+
|
|
5
|
+
// src/with-cargo/vault-diff.ts
|
|
6
|
+
async function diffVault(vault, candidate, options = {}) {
|
|
7
|
+
const idKey = options.idKey ?? "id";
|
|
8
|
+
const filter = options.collections ? new Set(options.collections) : null;
|
|
9
|
+
const compareFn = options.compareFn ?? ((a, b) => diff(a, b).length === 0);
|
|
10
|
+
const live = /* @__PURE__ */ new Map();
|
|
11
|
+
for await (const chunk of vault.exportStream({ granularity: "collection" })) {
|
|
12
|
+
if (filter && !filter.has(chunk.collection)) continue;
|
|
13
|
+
const collection = live.get(chunk.collection) ?? /* @__PURE__ */ new Map();
|
|
14
|
+
for (const record of chunk.records) {
|
|
15
|
+
const id = readIdField(record, idKey);
|
|
16
|
+
if (!id) continue;
|
|
17
|
+
collection.set(id, record);
|
|
18
|
+
}
|
|
19
|
+
live.set(chunk.collection, collection);
|
|
20
|
+
}
|
|
21
|
+
const cand = await normaliseCandidate(candidate, idKey, filter);
|
|
22
|
+
const added = [];
|
|
23
|
+
const modified = [];
|
|
24
|
+
const deleted = [];
|
|
25
|
+
const unchanged = options.includeUnchanged ? [] : void 0;
|
|
26
|
+
const collectionNames = /* @__PURE__ */ new Set([...live.keys(), ...cand.keys()]);
|
|
27
|
+
for (const collection of [...collectionNames].sort()) {
|
|
28
|
+
const liveColl = live.get(collection) ?? /* @__PURE__ */ new Map();
|
|
29
|
+
const candColl = cand.get(collection) ?? /* @__PURE__ */ new Map();
|
|
30
|
+
const allIds = /* @__PURE__ */ new Set([...liveColl.keys(), ...candColl.keys()]);
|
|
31
|
+
for (const id of [...allIds].sort()) {
|
|
32
|
+
const before = liveColl.get(id);
|
|
33
|
+
const after = candColl.get(id);
|
|
34
|
+
if (before === void 0 && after !== void 0) {
|
|
35
|
+
added.push({ collection, id, record: after });
|
|
36
|
+
} else if (before !== void 0 && after === void 0) {
|
|
37
|
+
const metadata = options.includeMetadata ? await vault.collection(collection).getMetadata(id) ?? void 0 : void 0;
|
|
38
|
+
deleted.push({ collection, id, record: before, ...metadata !== void 0 ? { metadata } : {} });
|
|
39
|
+
} else if (before !== void 0 && after !== void 0) {
|
|
40
|
+
if (compareFn(before, after)) {
|
|
41
|
+
unchanged?.push({ collection, id, record: after });
|
|
42
|
+
} else {
|
|
43
|
+
const metadata = options.includeMetadata ? await vault.collection(collection).getMetadata(id) ?? void 0 : void 0;
|
|
44
|
+
const fieldDiffs = diff(before, after);
|
|
45
|
+
const fieldsChanged = uniqueTopLevelKeys(fieldDiffs);
|
|
46
|
+
modified.push({
|
|
47
|
+
collection,
|
|
48
|
+
id,
|
|
49
|
+
record: after,
|
|
50
|
+
before,
|
|
51
|
+
fieldsChanged,
|
|
52
|
+
fieldDiffs,
|
|
53
|
+
...metadata !== void 0 ? { metadata } : {}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const summary = {
|
|
60
|
+
add: added.length,
|
|
61
|
+
modify: modified.length,
|
|
62
|
+
delete: deleted.length,
|
|
63
|
+
total: added.length + modified.length + deleted.length
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
added,
|
|
67
|
+
modified,
|
|
68
|
+
deleted,
|
|
69
|
+
unchanged,
|
|
70
|
+
summary,
|
|
71
|
+
format(opts) {
|
|
72
|
+
return formatDiff(opts?.detail ?? "full", { added, modified, deleted, summary });
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async function normaliseCandidate(candidate, idKey, filter) {
|
|
77
|
+
const out = /* @__PURE__ */ new Map();
|
|
78
|
+
if (typeof candidate === "object" && candidate !== null && "exportStream" in candidate && typeof candidate.exportStream === "function") {
|
|
79
|
+
for await (const chunk of candidate.exportStream({ granularity: "collection" })) {
|
|
80
|
+
if (filter && !filter.has(chunk.collection)) continue;
|
|
81
|
+
const collection = out.get(chunk.collection) ?? /* @__PURE__ */ new Map();
|
|
82
|
+
for (const record of chunk.records) {
|
|
83
|
+
const id = readIdField(record, idKey);
|
|
84
|
+
if (!id) continue;
|
|
85
|
+
collection.set(id, record);
|
|
86
|
+
}
|
|
87
|
+
out.set(chunk.collection, collection);
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
if (typeof candidate === "string") {
|
|
92
|
+
let parsed;
|
|
93
|
+
try {
|
|
94
|
+
parsed = JSON.parse(candidate);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`diffVault: candidate string is not valid JSON (${err.message})`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return collectionsFromObject(parsed, idKey, filter);
|
|
101
|
+
}
|
|
102
|
+
return collectionsFromObject(candidate, idKey, filter);
|
|
103
|
+
}
|
|
104
|
+
function collectionsFromObject(raw, idKey, filter) {
|
|
105
|
+
const out = /* @__PURE__ */ new Map();
|
|
106
|
+
if (raw === null || typeof raw !== "object") {
|
|
107
|
+
throw new Error("diffVault: candidate must be a Vault, an object, or a JSON string");
|
|
108
|
+
}
|
|
109
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
110
|
+
if (key.startsWith("_")) continue;
|
|
111
|
+
if (filter && !filter.has(key)) continue;
|
|
112
|
+
if (!Array.isArray(value)) continue;
|
|
113
|
+
const collection = /* @__PURE__ */ new Map();
|
|
114
|
+
for (const record of value) {
|
|
115
|
+
if (record === null || typeof record !== "object") continue;
|
|
116
|
+
const id = readIdField(record, idKey);
|
|
117
|
+
if (!id) continue;
|
|
118
|
+
collection.set(id, record);
|
|
119
|
+
}
|
|
120
|
+
out.set(key, collection);
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
function uniqueTopLevelKeys(diffs) {
|
|
125
|
+
const keys = /* @__PURE__ */ new Set();
|
|
126
|
+
for (const d of diffs) {
|
|
127
|
+
const m = /^[^.[]+/.exec(d.path);
|
|
128
|
+
if (m) keys.add(m[0]);
|
|
129
|
+
}
|
|
130
|
+
return [...keys];
|
|
131
|
+
}
|
|
132
|
+
function readIdField(record, idKey) {
|
|
133
|
+
if (record === null || typeof record !== "object") return "";
|
|
134
|
+
const v = record[idKey];
|
|
135
|
+
if (typeof v === "string") return v;
|
|
136
|
+
if (typeof v === "number" && Number.isFinite(v)) return String(v);
|
|
137
|
+
return "";
|
|
138
|
+
}
|
|
139
|
+
function formatDiff(detail, b) {
|
|
140
|
+
const head = `${b.summary.add} added \xB7 ${b.summary.modify} modified \xB7 ${b.summary.delete} deleted`;
|
|
141
|
+
if (detail === "count") return head;
|
|
142
|
+
if (b.summary.total === 0) return head + "\n(no changes)";
|
|
143
|
+
if (detail === "one-line") return head;
|
|
144
|
+
const rows = [head, ""];
|
|
145
|
+
for (const e of b.added) rows.push(`${e.collection}/${e.id} added`);
|
|
146
|
+
for (const e of b.modified) {
|
|
147
|
+
const fields = e.fieldDiffs.map((f) => `${f.path}: ${shortJSON(f.from)} \u2192 ${shortJSON(f.to)}`).join(", ");
|
|
148
|
+
rows.push(`${e.collection}/${e.id} modified ${fields}`);
|
|
149
|
+
}
|
|
150
|
+
for (const e of b.deleted) rows.push(`${e.collection}/${e.id} deleted`);
|
|
151
|
+
return rows.join("\n");
|
|
152
|
+
}
|
|
153
|
+
function shortJSON(value) {
|
|
154
|
+
if (value === void 0) return "undefined";
|
|
155
|
+
const s = JSON.stringify(value);
|
|
156
|
+
if (typeof s !== "string") return "<unrepresentable>";
|
|
157
|
+
return s.length > 60 ? s.slice(0, 57) + "..." : s;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export {
|
|
161
|
+
diffVault
|
|
162
|
+
};
|
|
163
|
+
//# sourceMappingURL=chunk-LB7FD65R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-cargo/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 '../kernel/vault.js'\nimport { diff as fieldDiff, type DiffEntry as FieldDiffEntry } from '../with-commit/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 * Unencrypted envelope metadata for the RECEIVER-side record.\n * Populated only when `diffVault` is called with `{includeMetadata: true}`.\n * Default: `undefined` (zero extra reads, no behavior change).\n *\n * Populated for `modified` (the CURRENT receiver state, before the candidate\n * is applied) and `deleted` (the receiver envelope of the record being\n * removed). `added` entries have no receiver envelope at diff time, so their\n * `metadata` is always absent.\n */\n readonly metadata?: {\n readonly version: number\n readonly timestamp: string\n readonly by?: string\n readonly source?: string\n readonly sourceTs?: string\n }\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 * When `true`, each entry in `added`, `modified`, and `deleted` will carry a\n * `metadata` field with the RECEIVER-side envelope metadata\n * (`version`, `timestamp`, `by?`, `source?`, `sourceTs?`).\n *\n * Off by default — no extra adapter reads, no behavior change for existing callers.\n */\n readonly includeMetadata?: 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 * Ungated shared import/merge infrastructure: the `as-*` exporters call this\n * internally from their `fromString`/import merge logic, so it is always\n * available (not behind `withCargo()`). Only `extractPartition` is a gated\n * cargo capability.\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: record only in candidate — no receiver envelope\n added.push({ collection, id, record: after })\n } else if (before !== undefined && after === undefined) {\n // deleted: record only in receiver — attach metadata if requested\n const metadata = options.includeMetadata\n ? (await vault.collection(collection).getMetadata(id)) ?? undefined\n : undefined\n deleted.push({ collection, id, record: before, ...(metadata !== undefined ? { metadata } : {}) })\n } else if (before !== undefined && after !== undefined) {\n if (compareFn(before, after)) {\n unchanged?.push({ collection, id, record: after })\n } else {\n // modified: record in both — attach receiver-side metadata if requested\n const metadata = options.includeMetadata\n ? (await vault.collection(collection).getMetadata(id)) ?? undefined\n : undefined\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 ...(metadata !== undefined ? { metadata } : {}),\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":[]}
|