@noy-db/hub 0.7.0-pre.12 → 0.7.0-pre.14
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/CHANGELOG.md +210 -0
- package/README.md +4 -3
- package/dist/{api-UE2XQKDN.js → api-JPZT6BDD.js} +1 -2
- package/dist/as/index.js +3 -4
- package/dist/as/index.js.map +1 -1
- package/dist/at/index.js +0 -1
- package/dist/attestation/index.js +6 -7
- package/dist/attestation/index.js.map +1 -1
- package/dist/{backup-R6A4VZRX.js → backup-AKWIMYWU.js} +1 -2
- package/dist/{backup-R6A4VZRX.js.map → backup-AKWIMYWU.js.map} +1 -1
- package/dist/blobs/index.js +0 -1
- package/dist/blobs/index.js.map +1 -1
- package/dist/broker/index.js +3 -4
- package/dist/broker/index.js.map +1 -1
- package/dist/by/index.js +0 -1
- package/dist/cargo/index.js +10 -11
- package/dist/{chunk-5FUULQ6F.js → chunk-2HVILGVR.js} +6 -6
- package/dist/{chunk-DJ3LNNZQ.js → chunk-3LK2XZCW.js} +2 -2
- package/dist/chunk-4SZMZAYN.js +1652 -0
- package/dist/chunk-4SZMZAYN.js.map +1 -0
- package/dist/chunk-66TQOPFM.js +37 -0
- package/dist/chunk-66TQOPFM.js.map +1 -0
- package/dist/{chunk-PQKG5AHH.js → chunk-6X6SEMEE.js} +4 -2
- package/dist/{chunk-PQKG5AHH.js.map → chunk-6X6SEMEE.js.map} +1 -1
- package/dist/{chunk-E3EBMC4G.js → chunk-AMODH7AU.js} +2 -2
- package/dist/{chunk-B2ZSGYSF.js → chunk-B2KCRDJM.js} +3 -3
- package/dist/{chunk-TO3EQ7HF.js → chunk-BLN54W3L.js} +2 -2
- package/dist/{chunk-WQF3QABE.js → chunk-BOBFL5TW.js} +9 -79
- package/dist/chunk-BOBFL5TW.js.map +1 -0
- package/dist/{chunk-XMF3GKUR.js → chunk-DRB6IHPS.js} +90 -910
- package/dist/chunk-DRB6IHPS.js.map +1 -0
- package/dist/{chunk-H7XCIM3Z.js → chunk-EKVNVJVV.js} +26 -16
- package/dist/chunk-EKVNVJVV.js.map +1 -0
- package/dist/{chunk-ZR2YMODS.js → chunk-EKZ6VZYF.js} +2 -2
- package/dist/{chunk-RTYJRFQO.js → chunk-EWPRPP3M.js} +1 -1
- package/dist/{chunk-JJR2JIN4.js → chunk-FFBYRWDJ.js} +2 -2
- package/dist/{chunk-VGICZPVE.js → chunk-FUJSIP4K.js} +2 -2
- package/dist/chunk-MGOXJIBV.js +176 -0
- package/dist/chunk-MGOXJIBV.js.map +1 -0
- package/dist/{chunk-PKJ43CMD.js → chunk-NLOLF6LD.js} +2 -2
- package/dist/{chunk-7NTBVFGD.js → chunk-NOSRK3ZZ.js} +5 -5
- package/dist/{chunk-HN4FNA4X.js → chunk-OMB2ZKIT.js} +8 -8
- package/dist/{chunk-Y4MJQ2UG.js → chunk-QQ2YMQZU.js} +2 -2
- package/dist/{chunk-URHRIAV6.js → chunk-REDOOY2Q.js} +2 -2
- package/dist/chunk-REDOOY2Q.js.map +1 -0
- package/dist/{chunk-YCXICMOY.js → chunk-RJ7NN2UR.js} +2 -2
- package/dist/chunk-RTBJ5IOO.js +87 -0
- package/dist/chunk-RTBJ5IOO.js.map +1 -0
- package/dist/{chunk-KL7AS3HV.js → chunk-SMH6SRX7.js} +6 -3
- package/dist/chunk-SMH6SRX7.js.map +1 -0
- package/dist/{chunk-QHW35ZON.js → chunk-TGPST6FK.js} +2 -2
- package/dist/{chunk-THNJ3IKU.js → chunk-ULUMAZ5S.js} +2 -2
- package/dist/{chunk-E5OHKSBD.js → chunk-V2PNPF6J.js} +3 -3
- package/dist/{chunk-NO272T7Q.js → chunk-V4PXLYWM.js} +2 -31
- package/dist/chunk-V4PXLYWM.js.map +1 -0
- package/dist/{chunk-BERJ4AWW.js → chunk-VGXZT72I.js} +2 -2
- package/dist/{chunk-2Z47NMFK.js → chunk-WZW37ZHL.js} +2 -2
- package/dist/{chunk-SWVNZQVR.js → chunk-YPXNAP72.js} +2 -169
- package/dist/chunk-YPXNAP72.js.map +1 -0
- package/dist/classified/index.js +1 -2
- package/dist/{classified-marker-67HBIJMZ.js → classified-marker-OO65UYXN.js} +3 -5
- package/dist/{classified-marker-67HBIJMZ.js.map → classified-marker-OO65UYXN.js.map} +1 -1
- package/dist/{collection-facade-MGGHQ3HU.js → collection-facade-VOWW732S.js} +4 -4
- package/dist/{computed-LSEZYRJS.js → computed-VPOKQK4N.js} +1 -2
- package/dist/consent/index.js +0 -1
- package/dist/consent/index.js.map +1 -1
- package/dist/cover/index.js +0 -1
- package/dist/crdt/index.js +0 -1
- package/dist/crdt/index.js.map +1 -1
- package/dist/custody/index.js +1 -2
- package/dist/{dead-filter-M3NZUWBZ.js → dead-filter-OMMVBAKZ.js} +2 -3
- package/dist/{dead-filter-M3NZUWBZ.js.map → dead-filter-OMMVBAKZ.js.map} +1 -1
- package/dist/debug/index.js +0 -1
- package/dist/debug/index.js.map +1 -1
- package/dist/{delegation-OLJUWTVA.js → delegation-VRYMQRCF.js} +1 -2
- package/dist/{delegation-OLJUWTVA.js.map → delegation-VRYMQRCF.js.map} +1 -1
- package/dist/derivations/index.js +7 -4
- package/dist/{derive-LY47HG7Q.js → derive-HHBRFS2U.js} +2 -3
- package/dist/directory/index.js +0 -1
- package/dist/{dispatch-XEBHUGBV.js → dispatch-DC3LQJ3S.js} +14 -10
- package/dist/dispatch-DC3LQJ3S.js.map +1 -0
- package/dist/{dispatch-44J4TL72.js → dispatch-GGFBWQU7.js} +5 -7
- package/dist/{dispatch-44J4TL72.js.map → dispatch-GGFBWQU7.js.map} +1 -1
- package/dist/{enclave-4YBQK77G.js → enclave-VM2YQONI.js} +1 -2
- package/dist/{executor-TOLPBHOC.js → executor-5NIMLSKZ.js} +1 -2
- package/dist/{executor-KSPGNVDO.js → executor-5XJNV5H5.js} +4 -4
- package/dist/{executor-MZJXBWNM.js → executor-AQVXV5GZ.js} +1 -2
- package/dist/{export-accessible-LGV6IAFX.js → export-accessible-2LOBM67T.js} +3 -4
- package/dist/{extract-partition-RYJZIKQZ.js → extract-partition-AZGSEMIF.js} +3 -4
- package/dist/{fanout-sidecar-TSFPXY5C.js → fanout-sidecar-HJIVUPTD.js} +1 -2
- package/dist/{fanout-sidecar-TSFPXY5C.js.map → fanout-sidecar-HJIVUPTD.js.map} +1 -1
- package/dist/{fence-watcher-ZVSMLKQD.js → fence-watcher-U2PW6JU5.js} +1 -3
- package/dist/{fence-watcher-ZVSMLKQD.js.map → fence-watcher-U2PW6JU5.js.map} +1 -1
- package/dist/{find-LC7P63MX.js → find-2ABFCZX4.js} +1 -2
- package/dist/forget/index.js +0 -1
- package/dist/forget/index.js.map +1 -1
- package/dist/guards/index.js +0 -1
- package/dist/history/index.js +0 -1
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.js +0 -1
- package/dist/i18n/index.js.map +1 -1
- package/dist/index.js +43 -40
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +0 -1
- package/dist/indexing/index.js.map +1 -1
- package/dist/introspection/index.js +2 -3
- package/dist/{issue-IOXM3RSJ.js → issue-RH4H733Z.js} +1 -2
- package/dist/{json-schema-I22JCYCG.js → json-schema-V3M5MUCM.js} +1 -3
- package/dist/{json-schema-I22JCYCG.js.map → json-schema-V3M5MUCM.js.map} +1 -1
- package/dist/kernel/collection-config.d.ts +34 -0
- package/dist/lazy/index.js +3 -3
- package/dist/{ledger-HXHIOD36.js → ledger-454SU3VE.js} +1 -2
- package/dist/{liberate-4APIV7FR.js → liberate-AAZSP2BT.js} +5 -6
- package/dist/{link-set-6RAHEKZN.js → link-set-67CAMUAL.js} +1 -2
- package/dist/{managed-secret-P3JQ7ZG4.js → managed-secret-NHRYG7CD.js} +1 -2
- package/dist/materialized-views/index.js +5 -5
- package/dist/{migrate-cek-25KNTS5U.js → migrate-cek-5ENH64AA.js} +1 -2
- package/dist/{migrate-cek-25KNTS5U.js.map → migrate-cek-5ENH64AA.js.map} +1 -1
- package/dist/money/index.js +0 -1
- package/dist/{noydb-JJXPICIC.js → noydb-Y53CQHZK.js} +17 -15
- package/dist/overlay-views/index.js +0 -1
- package/dist/periods/index.js +1 -2
- package/dist/pod/index.js +18 -16
- package/dist/{pod-handle-6II7KFBR.js → pod-handle-S7AMCJIF.js} +2 -3
- package/dist/{pod-handle-6II7KFBR.js.map → pod-handle-S7AMCJIF.js.map} +1 -1
- package/dist/policy/index.js +0 -1
- package/dist/portability/index.js +6 -7
- package/dist/portability/index.js.map +1 -1
- package/dist/{post-register-BPJ36A6D.js → post-register-HIYY4Y5S.js} +4 -5
- package/dist/{post-register-BPJ36A6D.js.map → post-register-HIYY4Y5S.js.map} +1 -1
- package/dist/{purge-scope-FGNNLWHD.js → purge-scope-4HEFPDOW.js} +1 -3
- package/dist/{purge-scope-FGNNLWHD.js.map → purge-scope-4HEFPDOW.js.map} +1 -1
- package/dist/query/index.js +0 -1
- package/dist/{read-only-facade-CHCKSEPD.js → read-only-facade-BIGO37U3.js} +1 -2
- package/dist/reduce/index.js +0 -1
- package/dist/reduce/index.js.map +1 -1
- package/dist/{register-AEOOGKNO.js → register-7OJWAWT5.js} +3 -4
- package/dist/{registry-SKSOUXNF.js → registry-23WF3TGH.js} +1 -2
- package/dist/{registry-SHMGHEK4.js → registry-4AYVUNXK.js} +1 -2
- package/dist/{registry-I6FP4677.js → registry-5MVJGPXW.js} +1 -2
- package/dist/{registry-BZQIVNNO.js → registry-HDINK5QW.js} +8 -5
- package/dist/{request-withdrawal-6HJT4ODF.js → request-withdrawal-P5EQATZ7.js} +5 -6
- package/dist/{reveal-DDQBDWPM.js → reveal-IMGQ2EE5.js} +1 -2
- package/dist/{reveal-DDQBDWPM.js.map → reveal-IMGQ2EE5.js.map} +1 -1
- package/dist/{revoke-U7U3FMXU.js → revoke-POHP4GPB.js} +1 -2
- package/dist/satellites/index.js +0 -1
- package/dist/schema-update/index.js +0 -1
- package/dist/sealed-record/index.js +3 -4
- package/dist/sealed-record/index.js.map +1 -1
- package/dist/search/index.js +1 -2
- package/dist/{seed-3RXT4VGN.js → seed-UYR6YG3V.js} +1 -2
- package/dist/{seed-3RXT4VGN.js.map → seed-UYR6YG3V.js.map} +1 -1
- package/dist/sequence/index.js +0 -1
- package/dist/session/index.js +0 -1
- package/dist/session/index.js.map +1 -1
- package/dist/shadow/index.js +0 -1
- package/dist/shadow/index.js.map +1 -1
- package/dist/share-link/index.js +0 -1
- package/dist/share-link/index.js.map +1 -1
- package/dist/{signer-7NK7TMWU.js → signer-5K3WDHOC.js} +1 -2
- package/dist/snapshots/index.js +1 -2
- package/dist/snapshots/index.js.map +1 -1
- package/dist/{stale-KSPG6LFX.js → stale-AU74OPUW.js} +2 -3
- package/dist/{storage-WP4M7IFO.js → storage-AVJA5BTW.js} +1 -2
- package/dist/store/index.js +0 -1
- package/dist/{store-coordination-provider-O2VUEMJH.js → store-coordination-provider-RX2CGWXV.js} +1 -2
- package/dist/{store-coordination-provider-O2VUEMJH.js.map → store-coordination-provider-RX2CGWXV.js.map} +1 -1
- package/dist/sync/index.js +0 -1
- package/dist/sync/index.js.map +1 -1
- package/dist/team/index.js +0 -1
- package/dist/tiers/index.js +1 -2
- package/dist/to/index.js +0 -1
- package/dist/to/index.js.map +1 -1
- package/dist/transactions/index.js +2 -3
- package/dist/transactions/index.js.map +1 -1
- package/dist/{ulid-VQW6ERQN.js → ulid-4MKXG325.js} +1 -2
- package/dist/util/index.js +0 -1
- package/dist/util/index.js.map +1 -1
- package/dist/{vault-diff-FE5J35NJ.js → vault-diff-C6WUJFNP.js} +1 -2
- package/dist/vault-head/index.js +0 -1
- package/dist/vault-head/index.js.map +1 -1
- package/dist/{verify-JBXCAGDL.js → verify-KOV2G5D6.js} +1 -2
- package/dist/{verify-JBXCAGDL.js.map → verify-KOV2G5D6.js.map} +1 -1
- package/dist/{walk-P3E6QSNC.js → walk-ERAJ5RHO.js} +3 -4
- package/dist/with-formula/derivations/registry.d.ts +6 -1
- package/dist/with-shape/introspection/describe.d.ts +2 -33
- package/dist/with-shape/introspection/field-meta.d.ts +86 -0
- package/dist/{withdraw-accessible-6Q7NE4EY.js → withdraw-accessible-42D32Z7G.js} +4 -5
- package/package.json +7 -3
- package/dist/chunk-7BE7ZDVQ.js +0 -722
- package/dist/chunk-7BE7ZDVQ.js.map +0 -1
- package/dist/chunk-H7XCIM3Z.js.map +0 -1
- package/dist/chunk-KL7AS3HV.js.map +0 -1
- package/dist/chunk-NO272T7Q.js.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -10
- package/dist/chunk-SWVNZQVR.js.map +0 -1
- package/dist/chunk-URHRIAV6.js.map +0 -1
- package/dist/chunk-WQF3QABE.js.map +0 -1
- package/dist/chunk-XMF3GKUR.js.map +0 -1
- package/dist/dispatch-XEBHUGBV.js.map +0 -1
- package/dist/withdraw-accessible-6Q7NE4EY.js.map +0 -1
- package/dist/zod-HAJM7LMS.js +0 -14763
- package/dist/zod-HAJM7LMS.js.map +0 -1
- /package/dist/{api-UE2XQKDN.js.map → api-JPZT6BDD.js.map} +0 -0
- /package/dist/{chunk-5FUULQ6F.js.map → chunk-2HVILGVR.js.map} +0 -0
- /package/dist/{chunk-DJ3LNNZQ.js.map → chunk-3LK2XZCW.js.map} +0 -0
- /package/dist/{chunk-E3EBMC4G.js.map → chunk-AMODH7AU.js.map} +0 -0
- /package/dist/{chunk-B2ZSGYSF.js.map → chunk-B2KCRDJM.js.map} +0 -0
- /package/dist/{chunk-TO3EQ7HF.js.map → chunk-BLN54W3L.js.map} +0 -0
- /package/dist/{chunk-ZR2YMODS.js.map → chunk-EKZ6VZYF.js.map} +0 -0
- /package/dist/{chunk-RTYJRFQO.js.map → chunk-EWPRPP3M.js.map} +0 -0
- /package/dist/{chunk-JJR2JIN4.js.map → chunk-FFBYRWDJ.js.map} +0 -0
- /package/dist/{chunk-VGICZPVE.js.map → chunk-FUJSIP4K.js.map} +0 -0
- /package/dist/{chunk-PKJ43CMD.js.map → chunk-NLOLF6LD.js.map} +0 -0
- /package/dist/{chunk-7NTBVFGD.js.map → chunk-NOSRK3ZZ.js.map} +0 -0
- /package/dist/{chunk-HN4FNA4X.js.map → chunk-OMB2ZKIT.js.map} +0 -0
- /package/dist/{chunk-Y4MJQ2UG.js.map → chunk-QQ2YMQZU.js.map} +0 -0
- /package/dist/{chunk-YCXICMOY.js.map → chunk-RJ7NN2UR.js.map} +0 -0
- /package/dist/{chunk-QHW35ZON.js.map → chunk-TGPST6FK.js.map} +0 -0
- /package/dist/{chunk-THNJ3IKU.js.map → chunk-ULUMAZ5S.js.map} +0 -0
- /package/dist/{chunk-E5OHKSBD.js.map → chunk-V2PNPF6J.js.map} +0 -0
- /package/dist/{chunk-BERJ4AWW.js.map → chunk-VGXZT72I.js.map} +0 -0
- /package/dist/{chunk-2Z47NMFK.js.map → chunk-WZW37ZHL.js.map} +0 -0
- /package/dist/{chunk-PZ5AY32C.js.map → collection-facade-VOWW732S.js.map} +0 -0
- /package/dist/{collection-facade-MGGHQ3HU.js.map → computed-VPOKQK4N.js.map} +0 -0
- /package/dist/{computed-LSEZYRJS.js.map → derive-HHBRFS2U.js.map} +0 -0
- /package/dist/{derive-LY47HG7Q.js.map → enclave-VM2YQONI.js.map} +0 -0
- /package/dist/{enclave-4YBQK77G.js.map → executor-5NIMLSKZ.js.map} +0 -0
- /package/dist/{executor-KSPGNVDO.js.map → executor-5XJNV5H5.js.map} +0 -0
- /package/dist/{executor-MZJXBWNM.js.map → executor-AQVXV5GZ.js.map} +0 -0
- /package/dist/{executor-TOLPBHOC.js.map → export-accessible-2LOBM67T.js.map} +0 -0
- /package/dist/{export-accessible-LGV6IAFX.js.map → extract-partition-AZGSEMIF.js.map} +0 -0
- /package/dist/{extract-partition-RYJZIKQZ.js.map → find-2ABFCZX4.js.map} +0 -0
- /package/dist/{find-LC7P63MX.js.map → issue-RH4H733Z.js.map} +0 -0
- /package/dist/{issue-IOXM3RSJ.js.map → ledger-454SU3VE.js.map} +0 -0
- /package/dist/{ledger-HXHIOD36.js.map → liberate-AAZSP2BT.js.map} +0 -0
- /package/dist/{liberate-4APIV7FR.js.map → link-set-67CAMUAL.js.map} +0 -0
- /package/dist/{link-set-6RAHEKZN.js.map → managed-secret-NHRYG7CD.js.map} +0 -0
- /package/dist/{managed-secret-P3JQ7ZG4.js.map → noydb-Y53CQHZK.js.map} +0 -0
- /package/dist/{noydb-JJXPICIC.js.map → read-only-facade-BIGO37U3.js.map} +0 -0
- /package/dist/{read-only-facade-CHCKSEPD.js.map → register-7OJWAWT5.js.map} +0 -0
- /package/dist/{register-AEOOGKNO.js.map → registry-23WF3TGH.js.map} +0 -0
- /package/dist/{registry-BZQIVNNO.js.map → registry-4AYVUNXK.js.map} +0 -0
- /package/dist/{registry-I6FP4677.js.map → registry-5MVJGPXW.js.map} +0 -0
- /package/dist/{registry-SHMGHEK4.js.map → registry-HDINK5QW.js.map} +0 -0
- /package/dist/{registry-SKSOUXNF.js.map → request-withdrawal-P5EQATZ7.js.map} +0 -0
- /package/dist/{request-withdrawal-6HJT4ODF.js.map → revoke-POHP4GPB.js.map} +0 -0
- /package/dist/{revoke-U7U3FMXU.js.map → signer-5K3WDHOC.js.map} +0 -0
- /package/dist/{signer-7NK7TMWU.js.map → stale-AU74OPUW.js.map} +0 -0
- /package/dist/{stale-KSPG6LFX.js.map → storage-AVJA5BTW.js.map} +0 -0
- /package/dist/{storage-WP4M7IFO.js.map → ulid-4MKXG325.js.map} +0 -0
- /package/dist/{ulid-VQW6ERQN.js.map → vault-diff-C6WUJFNP.js.map} +0 -0
- /package/dist/{vault-diff-FE5J35NJ.js.map → walk-ERAJ5RHO.js.map} +0 -0
- /package/dist/{walk-P3E6QSNC.js.map → withdraw-accessible-42D32Z7G.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/via/lookup/registry.ts","../src/via/lookup/snapshot.ts","../src/port/with/lookup-strategy.ts"],"sourcesContent":["/**\n * Pure dict-registry helpers — extracted from `kernel/vault.ts`'s dict\n * bodies (#650 Task 1 — via-lookup extraction, phase D).\n *\n * These take the vault-resident registry Maps (and a couple of vault-bound\n * callbacks) as ARGUMENTS instead of closing over `this` — no `Vault`\n * import here, so this module stays a plain, testable function library.\n * `kernel/vault.ts` keeps the registry Maps themselves (they're populated\n * by `vault.collection()` and read by the backup path) and calls these\n * helpers through the `port/with/lookup-strategy.ts` seam.\n */\n\nimport { getAtPath } from '../../kernel/paths.js'\nimport { UnknownDictCodeError, ValidationError } from '../../kernel/errors.js'\nimport type { JoinableSource } from '../../kernel/query/index.js'\nimport type { FieldRef, ViaGraph } from '../../kernel/via/graph.js'\nimport type { StaticDictDescriptor } from '../../port/with/i18n-strategy.js'\nimport { dictCollectionName, type LookupHandle } from './handle.js'\nimport type { LookupDescriptor, OnDelete } from './descriptor.js'\n\n/**\n * Validate staticDict codes on a `put()`. For each `staticDict()` field,\n * every stored code must be a declared key of the descriptor's table, else\n * `UnknownDictCodeError`. Opt out per descriptor with `{ validateCodes:\n * false }`. Supports scalar, dotted, and `[].`-wildcard field paths via\n * `getAtPath` (same path support as i18n validation).\n *\n * `staticFields` is the collection's `field → StaticDictDescriptor` map\n * (`Vault#staticDescriptorByField.get(collectionName)`) — `undefined`/empty\n * is a no-op.\n */\nexport function enforceStaticDictOnPut(\n staticFields: Record<string, StaticDictDescriptor> | undefined,\n record: unknown,\n): void {\n if (!staticFields || Object.keys(staticFields).length === 0) return\n if (!record || typeof record !== 'object') return\n\n const obj = record as Record<string, unknown>\n for (const [field, desc] of Object.entries(staticFields)) {\n if (desc.validateCodes === false) continue\n const known = new Set<string>(desc.keys)\n const values = getAtPath(obj, field)\n for (const value of values) {\n if (value === undefined || value === null) continue\n const codes = Array.isArray(value) ? value : [value]\n for (const code of codes) {\n if (typeof code !== 'string') continue\n if (!known.has(code)) {\n throw new UnknownDictCodeError(desc.name, field, code)\n }\n }\n }\n }\n}\n\n/**\n * Build a `JoinableSource` for a dictKey field, for use in dict joins.\n * Returns a source whose snapshot contains `{ key, labels, ...labels }`\n * records — one per dictionary entry — keyed by the stable key.\n *\n * staticDict: a code-table-backed source — snapshot() materialises the\n * in-memory table into rows, mirroring `LookupHandle.snapshotEntries()`.\n * Carries `displayLocale` so a locale-less `{ by: 'label' }` query has a\n * default locale to resolve at.\n *\n * Plain dictKey: the snapshot is built synchronously from the\n * `LookupHandle`'s write-through cache, which is populated on every\n * `put()`, `rename()`, `delete()`, and `list()` call. For pre-existing\n * data not yet touched this session, call `await vault.dictionary(name).list()`\n * first to warm the cache.\n *\n * Returns `null` when `field` is not a dictKey in `leftCollection`.\n */\nexport function resolveDictSource(\n leftCollection: string,\n field: string,\n staticDescriptorByField: ReadonlyMap<string, Record<string, StaticDictDescriptor>>,\n dictKeyFieldRegistry: ReadonlyMap<string, Record<string, string>>,\n getDictionaryHandle: (name: string) => LookupHandle,\n): JoinableSource | null {\n const staticFields = staticDescriptorByField.get(leftCollection)\n if (staticFields && field in staticFields) {\n const desc = staticFields[field]!\n const rows: readonly Record<string, unknown>[] = Object.entries(desc.table).map(\n ([key, labels]) => ({ key, labels, ...(labels as Record<string, string>) }),\n )\n const source: JoinableSource = {\n snapshot(): readonly unknown[] {\n return rows\n },\n lookupById(id: string): unknown {\n return rows.find((e) => e['key'] === id)\n },\n }\n if (desc.displayLocale !== undefined) {\n ;(source as { displayLocale?: string }).displayLocale = desc.displayLocale\n }\n return source\n }\n\n const dictFields = dictKeyFieldRegistry.get(leftCollection)\n if (!dictFields || !(field in dictFields)) return null\n const dictName = dictFields[field]\n if (!dictName) return null\n const handle = getDictionaryHandle(dictName)\n return {\n snapshot(): readonly unknown[] {\n return handle.snapshotEntries()\n },\n lookupById(id: string): unknown {\n const entries = handle.snapshotEntries()\n return entries.find((e) => e['key'] === id)\n },\n }\n}\n\n/** The minimal collection surface `updateReferencingRecords` needs. */\nexport interface DictReferencingCollection {\n list(): Promise<Record<string, unknown>[]>\n put(id: string, record: Record<string, unknown>): Promise<unknown>\n}\n\n/**\n * Find and rewrite records in every registered collection whose\n * dictKeyField points at `name`, replacing `oldKey` with `newKey`. Used by\n * `LookupHandle.rename()` (the only sanctioned mass-mutation path for\n * dictKey fields) via the vault's `findAndUpdateReferences` callback.\n *\n * `registry` is `Vault#dictKeyFieldRegistry` (collection name → field name\n * → dictionary name); `getCollection` is the vault's collection accessor —\n * rewrites go through the public `coll.put()` choke point, same as before.\n */\nexport async function updateReferencingRecords(\n registry: ReadonlyMap<string, Record<string, string>>,\n getCollection: (collectionName: string) => DictReferencingCollection,\n name: string,\n oldKey: string,\n newKey: string,\n): Promise<void> {\n for (const [collectionName, dictFields] of registry) {\n // Find fields that point at this dictionary\n const fields = Object.entries(dictFields)\n .filter(([, dn]) => dn === name)\n .map(([field]) => field)\n if (fields.length === 0) continue\n\n const coll = getCollection(collectionName)\n const records = await coll.list()\n for (const record of records) {\n let changed = false\n const updated = { ...record }\n for (const field of fields) {\n if (updated[field] === oldKey) {\n updated[field] = newKey\n changed = true\n }\n }\n if (changed) {\n const id = record['id'] as string | undefined\n if (id !== undefined) {\n await coll.put(id, updated)\n }\n }\n }\n }\n}\n\n/**\n * Resolve a label from an in-memory `{ locale -> label }` map, walking the\n * same fallback chain semantics as `LookupHandle.resolveLabel` (#650 Task 2\n * — moved here from `kernel/vault.ts` so the SAME chain serves both the\n * i18n binding's `dictLabelResolver` and the lookup binding's\n * `lookupLabelResolver`, which #650 Task 2 wires to the identical closure).\n */\nexport function resolveLabelFromMap(\n labels: Readonly<Record<string, string>>,\n locale: string,\n fallback?: string | readonly string[],\n): string | undefined {\n if (labels[locale] !== undefined) return labels[locale]\n const chain = Array.isArray(fallback)\n ? (fallback as readonly string[])\n : fallback\n ? [fallback as string]\n : []\n for (const fb of chain) {\n if (fb === 'any') {\n const any = Object.values(labels)[0]\n if (any !== undefined) return any\n } else if (labels[fb] !== undefined) {\n return labels[fb]\n }\n }\n return undefined\n}\n\n/**\n * Project a native `lookup(dimension, { backing:'static', table, … })`\n * descriptor into the legacy `StaticDictDescriptor` shape — the\n * alias-equivalence compat seam (#650 Task 2) that lets a native\n * static-tier lookup field reuse the SAME vault registries\n * (`staticByName`/`staticDescriptorByField`) — and therefore the same\n * `dictLabelResolver`/`resolveDictSource` machinery — as its `staticDict()`\n * alias. `vocabulary:'closed'` maps to `validateCodes:true` (closed = only\n * declared codes are legal); `'open'` maps to `validateCodes:false`.\n * Returns `undefined` for non-static or table-less (bare `enumOf`)\n * descriptors — those have nothing to register.\n */\nexport function lookupToStaticDictCompat(desc: LookupDescriptor): StaticDictDescriptor | undefined {\n if (desc.backing !== 'static' || desc.table === undefined) return undefined\n return {\n _noydbStaticDict: true,\n _viaBrand: 'i18n',\n name: desc.dimension,\n table: desc.table,\n keys: desc.keys ?? Object.keys(desc.table),\n ...(desc.displayLocale !== undefined ? { displayLocale: desc.displayLocale } : {}),\n ...(desc.onMissing !== undefined ? { onMissing: desc.onMissing } : {}),\n ...(desc.substitute !== undefined ? { substitute: desc.substitute } : {}),\n validateCodes: desc.vocabulary === 'closed',\n }\n}\n\n/** The vault-registry entries a collection's `lookupFields` contribute — the alias-equivalence bridge (#650 Task 2). */\nexport interface LookupDictCompat {\n /** Reserved-tier fields: field name -> dimension (dictionary) name — merges into `dictKeyFieldRegistry`. */\n readonly dictFieldMap: Record<string, string>\n /** Static-tier (table-bearing) fields, projected — merges into `staticDescriptorByField`/`staticByName`. */\n readonly staticEntries: ReadonlyArray<readonly [string, StaticDictDescriptor]>\n}\n\n/**\n * Bridge a collection's `lookupFields` into the SAME shape the legacy dict\n * registries expect, so `resolveDictSource`/`dictLabelResolver` (and\n * therefore `.join()`/`orderBy({by:'label'})`) serve a native `dict()`/\n * `lookup(static)` field identically to its `dictKey()`/`staticDict()`\n * alias — the reserved-vs-first-class-backing \"matrix\" tier is NOT bridged\n * here (no vault registry backs it; Task 5/6 build its own graph edge /\n * snapshot seam).\n */\nexport function collectLookupDictCompat(\n lookupFields: Record<string, LookupDescriptor> | undefined,\n): LookupDictCompat {\n const dictFieldMap: Record<string, string> = {}\n const staticEntries: Array<readonly [string, StaticDictDescriptor]> = []\n for (const [field, desc] of Object.entries(lookupFields ?? {})) {\n if (desc.backing === 'reserved') {\n dictFieldMap[field] = desc.dimension\n } else {\n const compat = lookupToStaticDictCompat(desc)\n if (compat) staticEntries.push([field, compat])\n }\n }\n return { dictFieldMap, staticEntries }\n}\n\n/**\n * Reserved-dict deps of a set of named collections (#653) — the `_dict_*`\n * collections partial sync must auto-include alongside a `collections`\n * filter, mirroring `SyncEngine`'s existing satellite-pair expansion.\n * One hop is provably complete: `vault.collection()` refuses `_dict_*`\n * names, so a dict can never itself declare lookup fields — no\n * dict-of-dict chain to recurse through.\n */\nexport function reservedDictDepsOf(\n names: readonly string[],\n dictKeyFieldRegistry: ReadonlyMap<string, Record<string, string>>,\n): readonly string[] {\n const out = new Set<string>()\n for (const name of names) {\n const fieldMap = dictKeyFieldRegistry.get(name)\n if (!fieldMap) continue\n for (const dictName of Object.values(fieldMap)) out.add(dictCollectionName(dictName))\n }\n return [...out]\n}\n\n/**\n * A lookup dimension's sync membership/altKey table, materialized from its\n * backing rows (#650 Task 3). `keys` is every canonical key present;\n * `altIndex` maps an altKey candidate VALUE to its owning canonical key.\n */\nexport interface MaterializedBacking {\n /** Canonical key values present in the materialized rows. */\n readonly keys: ReadonlySet<string>\n /** altKey candidate value -> canonical key. */\n readonly altIndex: ReadonlyMap<string, string>\n}\n\n/**\n * Build a lookup dimension's altKey index from its backing rows, enforcing\n * declare/warm-time uniqueness across `key ∪ altKeys` values (#650 Task 3 —\n * the CHE/SWZ drift class: two different rows must never claim the same\n * candidate key). `rows` is keyed by canonical key (`row[descriptor.key]`\n * for the matrix tier; the dimension's own key for static/reserved).\n * An altKey candidate VALUE may be a string or number — both normalize via\n * `coerceLookupKey` (#651 Task 3); non-scalar/absent values are skipped.\n * Pure — no I/O. Throws `ValidationError` on collision.\n */\nexport function materializeBackingTable(\n descriptor: LookupDescriptor,\n rows: ReadonlyMap<string, Record<string, unknown>>,\n): MaterializedBacking {\n const keys = new Set<string>(rows.keys())\n // Every value that has claimed ownership so far (canonical keys seed it) —\n // the union `key ∪ altKeys` uniqueness set the spec requires.\n const owner = new Map<string, string>()\n for (const key of keys) owner.set(key, key)\n\n const altIndex = new Map<string, string>()\n const altFields = descriptor.altKeys ?? []\n for (const [canonicalKey, row] of rows) {\n for (const altField of altFields) {\n const value = coerceLookupKey(row[altField])\n if (value === undefined || value === '') continue\n const existingOwner = owner.get(value)\n if (existingOwner !== undefined && existingOwner !== canonicalKey) {\n throw new ValidationError(\n `lookup \"${descriptor.dimension}\": altKey field \"${altField}\" value \"${value}\" is claimed by ` +\n `both \"${existingOwner}\" and \"${canonicalKey}\" — key/altKey values must be unique across the dimension.`,\n )\n }\n owner.set(value, canonicalKey)\n altIndex.set(value, canonicalKey)\n }\n }\n return { keys, altIndex }\n}\n\n/**\n * Closed-vocabulary membership test for one candidate key (#650 Task 3).\n * Static tier: sync, against the in-config key set (declared `keys`, or the\n * table's own keys when table-bearing). Reserved tier: sync — the declared\n * `keys` union the reserved handle's live write-through snapshot (closes\n * #649 for the native `dict()` spelling: the declared-keys promise the old\n * `dictKey()` doc comment made falsely). Matrix (collection) tier: sync —\n * delegates to `buildLookupAltIndex` so membership is checked against the\n * SAME `row[descriptor.key]` keying the altKey index uses (review fix,\n * Important 1: an earlier PUT-id `.get(key)` scan disagreed with the\n * altIndex whenever `descriptor.key !== 'id'`, wrongly rejecting valid\n * non-id candidates and wrongly accepting an unrelated row's PUT-id).\n */\nexport function checkLookupMembership(\n descriptor: LookupDescriptor,\n key: string,\n getDictionary: (dimension: string) => LookupHandle,\n getCollection: (dimension: string) => { querySourceForJoin(): JoinableSource },\n): boolean {\n if (descriptor.backing === 'static') {\n const known = descriptor.keys ?? (descriptor.table ? Object.keys(descriptor.table) : [])\n return known.includes(key)\n }\n if (descriptor.backing === 'reserved') {\n if ((descriptor.keys ?? []).includes(key)) return true\n return getDictionary(descriptor.dimension).snapshotEntries().some((e) => e['key'] === key)\n }\n return buildLookupAltIndex(descriptor, getDictionary, getCollection).keys.has(key)\n}\n\n/**\n * The ONE guarded key coercion (#651 Task 3, dm12) — string/number values\n * coerce to their canonical `String()` form; everything else (`null`,\n * `undefined`, objects, …) coerces to `undefined`. Every consumer that turns\n * a raw record field into a lookup key routes through this, closing the\n * bare-`String()` `\"undefined\"`/`\"null\"`-key poisoning class (seam map\n * finding 6): a row whose key field is genuinely absent must never mint the\n * literal candidate string `\"undefined\"`.\n */\nexport function coerceLookupKey(raw: unknown): string | undefined {\n return typeof raw === 'string' || typeof raw === 'number' ? String(raw) : undefined\n}\n\n/**\n * Resolve a backing row's canonical key VALUE — `coerceLookupKey(row[descriptor.key])`\n * (#651 Task 3). The matrix tier's `row[descriptor.key]`, never the row's own PUT-id\n * when the two differ (`descriptor.key !== 'id'`).\n */\nexport function resolveBackingRowKey(\n descriptor: LookupDescriptor,\n row: Record<string, unknown>,\n): string | undefined {\n return coerceLookupKey(row[descriptor.key])\n}\n\n/**\n * The referencing-side match predicate — does `rec[field]` (coerced) equal an\n * already-coerced `compareKey` (#651 Task 3)? Shared by every site that scans a\n * referencing collection for rows pointing at a given backing key\n * (`with-shape/links/vault-facade.ts`'s ref-delete propagation, `kernel/via/dispatch.ts`'s\n * forget-fanout twin).\n */\nexport function matchesReferencingValue(\n rec: Record<string, unknown>,\n field: string,\n compareKey: string,\n): boolean {\n return coerceLookupKey(rec[field]) === compareKey\n}\n\n/**\n * Materialize a lookup dimension's altKey index from whatever backing data\n * is synchronously available (#650 Task 3 — the `ingest` source). Static:\n * the in-config table. Reserved: the reserved handle's live write-through\n * cache (the same warm-via-put()/list() cache `resolveDictSource` already\n * relies on). Matrix (collection): the backing collection's own in-memory\n * eager cache via `querySourceForJoin()` — already public, already the\n * mechanism `.join()` uses (`with-shape/links/vault-facade.ts`'s\n * `resolveSource`); no new I/O, no fire-and-forget warm step. Like that\n * existing join precedent, a dimension collection this vault session has\n * not yet opened/populated sees an empty snapshot (no altKey normalization\n * until it has rows) — open/populate it first for normalization to apply.\n * A matrix row whose `descriptor.key` field coerces to `undefined` (missing/\n * non-scalar) is SKIPPED — never enters the index under a poisoned\n * `\"undefined\"` key (#651 Task 3, dm12).\n */\nexport function buildLookupAltIndex(\n descriptor: LookupDescriptor,\n getDictionary: (dimension: string) => LookupHandle,\n getCollection: (dimension: string) => { querySourceForJoin(): JoinableSource },\n): MaterializedBacking {\n if (descriptor.backing === 'static') {\n return materializeBackingTable(descriptor, new Map(Object.entries(descriptor.table ?? {})))\n }\n if (descriptor.backing === 'reserved') {\n const entries = getDictionary(descriptor.dimension).snapshotEntries()\n return materializeBackingTable(descriptor, new Map(entries.map((e) => [String(e['key']), e])))\n }\n const rows = getCollection(descriptor.dimension).querySourceForJoin().snapshot()\n const keyed = new Map<string, Record<string, unknown>>()\n for (const r of rows) {\n const row = r as Record<string, unknown>\n const key = resolveBackingRowKey(descriptor, row)\n if (key !== undefined) keyed.set(key, row)\n }\n return materializeBackingTable(descriptor, keyed)\n}\n\n/** One cross-collection `'ref'` graph edge a lookup field declares (#650 Task 5). Module-private —\n * only `registerLookupRefEdges` below (the exported entry point) consumes it. */\ninterface LookupRefEdge {\n readonly referencing: FieldRef\n readonly sources: readonly FieldRef[]\n readonly onDelete: OnDelete\n /** The backing dimension's canonical-key FIELD NAME on its own row (`desc.key` — matrix tier\n * only varies this; reserved/static tiers are always `'id'`). A referencing field always\n * stores THIS field's value, never the backing row's PUT-id when the two differ. */\n readonly keyField: string\n}\n\n/**\n * Compute the cross-collection `'ref'` edges a collection's `lookupFields` declare (#650 Task 5,\n * spec §4) — one edge per non-static-backing field: target = the referencing field; sources =\n * the backing dimension's whole-collection node (`field:'*'`, the wildcard key\n * `ViaGraph.referencingEdgesOf` does its O(1) reverse lookup against) PLUS, when the descriptor\n * names a presentation field (`present.label`), that SPECIFIC field too. `foldPosture`'s\n * `DEFAULT_POSTURE` is the fold's identity element, so adding the wildcard alongside a real field\n * source changes nothing when that field is plain — but folds in a classified/money posture when\n * it isn't (taint composition, spec §4: \"a lookup edge whose source names a classified field\n * contributes that field's posture\"). Static tier (`backing:'static'`) has no backing collection/\n * dimension rows to reference-check — excluded. Pure; consumed only by `registerLookupRefEdges` below.\n */\nfunction collectLookupRefEdges(\n collectionName: string,\n lookupFields: Record<string, LookupDescriptor> | undefined,\n): readonly LookupRefEdge[] {\n const edges: LookupRefEdge[] = []\n for (const [field, desc] of Object.entries(lookupFields ?? {})) {\n if (desc.backing === 'static') continue\n const backing = desc.backing === 'reserved' ? dictCollectionName(desc.dimension) : desc.dimension\n edges.push({\n referencing: { collection: collectionName, field },\n sources: [\n { collection: backing, field: '*' },\n ...(desc.present?.label !== undefined ? [{ collection: backing, field: desc.present.label }] : []),\n ],\n onDelete: desc.onDelete,\n keyField: desc.key,\n })\n }\n return edges\n}\n\n/** Thin wrapper — `collectLookupRefEdges` + one `graph.registerDerived` call per edge. Lets\n * `vault.collection()` (kernel-surface-budgeted, #650 Task 5) register a collection's lookup-ref\n * edges with a single line, keeping the ceiling-guarded call site a thin call (route logic here). */\nexport function registerLookupRefEdges(\n graph: ViaGraph,\n collectionName: string,\n lookupFields: Record<string, LookupDescriptor> | undefined,\n): void {\n for (const e of collectLookupRefEdges(collectionName, lookupFields)) {\n graph.registerDerived(e.referencing, e.sources, 'ref', 'record', e.onDelete, e.keyField)\n }\n}\n\n/**\n * `LookupViaConfig.snapshotFor`'s vault-built row source (#650 Task 6, spec\n * §5; matrix-tier coverage added #650 Task 7, spec §6 — Task 6 deferred it,\n * see `task-6-report.md`'s Concerns #1; the reviewer's Task-7 dispatch\n * refuted the \"needs a new vault-resident registry\" premise: the descriptor\n * is already in hand at both call sites, so `snapshotFor` just needs to\n * ACCEPT it). Takes the full `descriptor` (not a bare dimension name) so it\n * can route the matrix tier's `key` field, which — unlike reserved tier's\n * hardcoded `'id'` — varies per collection. Routes on `descriptor.backing`:\n *\n * - **reserved**: rows come straight from the SAME `LookupHandle.\n * snapshotEntries()` write-through cache `dictLabelResolver`/\n * `resolveDictSource` already read (no second copy), keyed by each\n * entry's own canonical `key` (always `'id'` by construction for this\n * tier — `dict()`'s factory hardcodes it).\n * - **collection** (matrix): rows come from `getCollection(dimension).\n * querySourceForJoin().snapshot()` — the SAME sync, already-live cache\n * `buildLookupAltIndex`'s matrix branch (above, this file) and `.join()`\n * itself already read — re-keyed via `resolveBackingRowKey(descriptor, row)`,\n * NOT the row's own PUT-id (which may differ when `key !== 'id'`; the exact\n * distinction the #650 Task 3 review fix already applies to\n * `checkLookupMembership`'s matrix branch). A row whose `descriptor.key`\n * field coerces to `undefined` is skipped (#651 Task 3, dm12).\n * - **static**: never routed here — `descriptor.table` is read directly by\n * the caller (`binding.ts`'s `compareLookupOrder`/`resolveLookupOrderLabel`,\n * `snapshot.ts`'s `presentLookupForJoin`); no vault call needed.\n *\n * `isReservedDimension` is the vault's `reservedLookupCollections`\n * membership test.\n */\nexport function buildLookupSnapshotRows(\n descriptor: LookupDescriptor,\n isReservedDimension: (dimension: string) => boolean,\n getDictionary: (dimension: string) => LookupHandle,\n getCollection: (dimension: string) => { querySourceForJoin(): JoinableSource },\n): ReadonlyMap<string, Record<string, unknown>> | undefined {\n const dimension = descriptor.dimension\n if (isReservedDimension(dimension)) {\n const rows = new Map<string, Record<string, unknown>>()\n for (const entry of getDictionary(dimension).snapshotEntries()) {\n const key = entry['key']\n if (typeof key === 'string') rows.set(key, entry)\n }\n return rows\n }\n if (descriptor.backing === 'collection') {\n const rawRows = getCollection(dimension).querySourceForJoin().snapshot()\n const rows = new Map<string, Record<string, unknown>>()\n for (const r of rawRows) {\n const row = r as Record<string, unknown>\n const key = resolveBackingRowKey(descriptor, row)\n if (key !== undefined) rows.set(key, row)\n }\n return rows\n }\n return undefined\n}\n","/**\n * The sync lookup snapshot + join/locale seam (#650 Task 6, spec §5 — \"the\n * snapshot+locale seam\"). Retires the #626 kernel→via grandfather:\n * `kernel/query/join.ts` no longer imports `via/i18n/core.js` directly\n * — it calls the `presentForJoin` hook this file's `buildPresentForJoin`\n * builds instead (seam map Part 2 item 4, the #626 reviewer-spec'd shape:\n * a sync `presentI18nForJoin`-class hook on `JoinableSource`).\n *\n * `LookupSnapshot` is the sync materialized `key -> row` view over ONE\n * lookup dimension's ALREADY-LIVE backing data (the `active.ts`\n * `_syncCache`/`snapshotEntries` write-through-cache pattern — never a\n * second copy of the data: `LookupViaConfig.snapshotFor`'s vault-built\n * closure reads the SAME `LookupHandle._syncCache` /\n * first-class-collection cache every other lookup consumer\n * (`dictLabelResolver`, `resolveDictSource`, `getAltIndex`) already reads).\n * Serves (reserved AND matrix tier since #650 Task 7 — `registry.ts`'s\n * `buildLookupSnapshotRows` routes both; static tier is read straight off\n * `descriptor.table` by every consumer below, never through this cache):\n * - join dressing (`presentForJoin`, consumed by `kernel/query/join.ts`\n * via `JoinableSource.presentForJoin`)\n * - dimension sort (`compareForOrder`, consumed by\n * `via/lookup/binding.ts`'s `NoydbVia.compareForOrder` closure)\n * - per-call-locale order-label resolution (`resolveOrderLabel`, #650\n * Task 7 — the `orderBy(..., {by:'label'})` channel `compareForOrder`\n * structurally can't serve, no locale param; consumed by\n * `kernel/query/builder.ts`'s `buildOrderLabelMaps`)\n * - membership: reserved/static-tier membership (#650 Task 3,\n * `checkLookupMembership`) already reads the identical sync caches\n * directly — not re-plumbed through this file; see\n * `.superpowers/sdd/task-6-report.md`'s \"bridge disposition\" section.\n *\n * Sync end-to-end (#553) — every function here is a pure, synchronous\n * transform over already-materialized rows; no store read, no Promise.\n */\nimport type { LookupDescriptor } from './descriptor.js'\nimport { presentI18nForJoin, type I18nTextDescriptor } from '../i18n/core.js'\n\n/** A lookup dimension's sync materialized view — see file header. */\nexport interface LookupSnapshot {\n /** The full backing row for `key`, or `undefined` when `key` isn't (yet) present in the snapshot. */\n row(key: string): Record<string, unknown> | undefined\n /** The dimension's declared presentation label for `key` at `locale` — mirrors `binding.ts`'s `fetchLookupLabel` (matrix-row branch), generalized to any tier's already-materialized rows. */\n label(key: string, locale: string, fallback?: unknown): string | undefined\n /**\n * Exact ordering for two canonical keys against `descriptor.sortBy`\n * (falls back to `present.label`, then to the raw keys) at `locale`.\n * Never throws — degrades to comparing the raw keys when no sortable\n * value resolves for either side.\n */\n compareKeys(a: string, b: string, locale: string): number\n}\n\n/** Read one row field, resolving a `by`-keyed (locale-map) value when `by` is declared. */\nfunction readRowField(\n row: Record<string, unknown> | undefined,\n field: string | undefined,\n by: string | undefined,\n locale: string,\n fallback?: unknown,\n): string | undefined {\n if (!row || field === undefined) return undefined\n const raw = row[field]\n if (by === undefined) return typeof raw === 'string' ? raw : undefined\n if (!raw || typeof raw !== 'object') return undefined\n const map = raw as Record<string, unknown>\n const val = map[locale]\n if (typeof val === 'string') return val\n if (typeof fallback === 'string' && typeof map[fallback] === 'string') return map[fallback]\n return undefined\n}\n\n/**\n * Build a sync `LookupSnapshot` over one dimension's already-materialized\n * rows (canonical-key -> row, the SAME keying `materializeBackingTable`\n * (`registry.ts`, #650 Task 3) uses). Pure — never reads a store.\n */\nexport function buildLookupSnapshot(\n dimension: string,\n rows: ReadonlyMap<string, Record<string, unknown>>,\n descriptor: LookupDescriptor,\n): LookupSnapshot {\n void dimension // identity only — kept for parity with materializeBackingTable/buildLookupAltIndex's signature and future diagnostics\n return {\n row: (key) => rows.get(key),\n label: (key, locale, fallback) =>\n readRowField(rows.get(key), descriptor.present?.label, descriptor.present?.by, locale, fallback),\n compareKeys: (a, b, locale) => {\n const sortField = descriptor.sortBy ?? descriptor.present?.label\n const av = readRowField(rows.get(a), sortField, descriptor.present?.by, locale) ?? a\n const bv = readRowField(rows.get(b), sortField, descriptor.present?.by, locale) ?? b\n return av < bv ? -1 : av > bv ? 1 : 0\n },\n }\n}\n\n/**\n * The lookup-label HALF of `presentForJoin` (#626 retirement, spec §5;\n * matrix-tier coverage added #650 Task 7) — resolves `<field>Label` for\n * every declared lookup field with a `present` dressing dimension, sync,\n * from `getSnapshotRows` (the vault-built `LookupViaConfig.snapshotFor`\n * closure — now descriptor-routed, see `registry.ts`'s\n * `buildLookupSnapshotRows`; static tier reads its own in-config `table`\n * directly, no vault call — never `undefined` for a declared static table).\n * Mirrors `binding.ts`'s `runLookupPresent` (the async `present()` hook)\n * minus the array/`[].`-wildcard handling that hook needs for full-record\n * reads — join dressing only ever sees the joined RIGHT-side record's\n * scalar fields, so that complexity doesn't apply here.\n */\nfunction presentLookupForJoin(\n record: Record<string, unknown>,\n locale: string,\n lookupFields: Record<string, LookupDescriptor>,\n getSnapshotRows: (descriptor: LookupDescriptor) => ReadonlyMap<string, Record<string, unknown>> | undefined,\n): Record<string, unknown> {\n let result = record\n for (const [field, desc] of Object.entries(lookupFields)) {\n if (desc.present === undefined) continue\n const raw = record[field]\n if (typeof raw !== 'string') continue\n const rows = desc.backing === 'static'\n ? (desc.table ? new Map(Object.entries(desc.table)) : undefined)\n : getSnapshotRows(desc)\n if (!rows) continue\n const label = buildLookupSnapshot(desc.dimension, rows, desc).label(raw, locale)\n if (label === undefined) continue\n if (result === record) result = { ...record }\n result[`${field}Label`] = label\n }\n return result\n}\n\n/**\n * Build the combined sync `presentForJoin(record, locale)` hook a\n * `Collection` attaches to the `JoinableSource` it exposes\n * (`querySourceForJoin()`) — the i18n-text half (`presentI18nForJoin`, the\n * exact `applyI18nLocale(..., 'join')` partial application\n * `kernel/query/join.ts` used to call directly, #626) composed with the\n * lookup-label half above. `undefined` when the collection declares\n * neither — `JoinableSource.presentForJoin` then stays unset, matching\n * today's `i18nFields`-absent behavior exactly (#626 parity lock).\n */\nexport function buildPresentForJoin(\n i18nFields: Record<string, I18nTextDescriptor> | undefined,\n lookupFields: Record<string, LookupDescriptor> | undefined,\n getSnapshotRows: ((descriptor: LookupDescriptor) => ReadonlyMap<string, Record<string, unknown>> | undefined) | undefined,\n): ((record: unknown, locale: string) => unknown) | undefined {\n const hasI18n = i18nFields !== undefined && Object.keys(i18nFields).length > 0\n const hasLookup = lookupFields !== undefined && Object.keys(lookupFields).length > 0\n if (!hasI18n && !hasLookup) return undefined\n const resolveRows = getSnapshotRows ?? (() => undefined)\n return (record, locale) => {\n if (record === null || typeof record !== 'object') return record\n let result = record as Record<string, unknown>\n if (hasI18n) result = presentI18nForJoin(result, i18nFields, locale)\n if (hasLookup) result = presentLookupForJoin(result, locale, lookupFields, resolveRows)\n return result\n }\n}\n","/**\n * Lookup strategy seam (#650 Task 1 — via-lookup extraction, phase D of the\n * Via port; precedent: `port/with/i18n-strategy.ts`). Lives on the `/with`\n * port (the one seam the kernel spine may import statically) so `Vault` can\n * reach the dict-registry pure helpers and the `LookupHandle`/`NO_LOOKUP`\n * types without a spine→`via/` static import (Check 14 via-layering bans\n * that; `port/with/` is always allowed, Check 9's sanctioned exception).\n *\n * `kernel/vault.ts` imports ONLY this module for lookup — never\n * `via/lookup/*` directly.\n *\n * This task (#650 Task 1) is a pure move: `Vault.dictionary()` still\n * constructs its handle through `i18nStrategy.buildDictionaryHandle`\n * (`port/with/i18n-strategy.ts`), which now internally delegates to\n * `withLookup().buildLookupHandle` (same handle, new home). `LookupStrategy`\n * / `NO_LOOKUP` / `isLookupCollectionName` here are the seam later phase-D\n * tasks bind `vault.collection()`'s lookup fields onto — unused by Task 1's\n * wiring, but their exact shapes are frozen now so later tasks don't\n * re-litigate them.\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport type { LedgerStore } from '../../with-commit/history/ledger/store.js'\nimport type { UnlockedKeyring } from '../../with-party/team/keyring.js'\nimport type { NoydbEventEmitter } from '../../kernel/events.js'\nimport type { ViaCryptoCtx } from '../../kernel/via/index.js'\nimport type { LookupHandle, DictionaryOptions } from '../../via/lookup/handle.js'\nimport {\n enforceStaticDictOnPut,\n resolveDictSource,\n updateReferencingRecords,\n resolveLabelFromMap,\n collectLookupDictCompat,\n lookupToStaticDictCompat,\n reservedDictDepsOf,\n materializeBackingTable,\n checkLookupMembership,\n buildLookupAltIndex,\n registerLookupRefEdges,\n buildLookupSnapshotRows,\n coerceLookupKey,\n resolveBackingRowKey,\n matchesReferencingValue,\n type DictReferencingCollection,\n type LookupDictCompat,\n type MaterializedBacking,\n} from '../../via/lookup/registry.js'\nimport { buildPresentForJoin } from '../../via/lookup/snapshot.js'\nimport type { LookupDescriptor } from '../../via/lookup/descriptor.js'\n\n/**\n * Backing options for `LookupStrategy.buildLookupHandle` — same shape as\n * `DictionaryOptions` (kept as a distinct alias since the \"lookup\" name is\n * the forward-looking one; `DictionaryOptions` stays the dict-tier name).\n */\nexport type LookupBackingOptions = DictionaryOptions\n\n/**\n * Options accepted by `LookupStrategy.buildLookupHandle`. Mirrors the\n * `LookupHandle` constructor verbatim, plus the two choke-point\n * participation hooks (`onDirty`/`onRecordMutated`) #647 (Task 4) wires —\n * both `undefined` in this task (pure move, no new call sites).\n */\nexport interface BuildLookupHandleOptions<Keys extends string = string> {\n readonly adapter: NoydbStore\n readonly compartmentName: string\n readonly dimensionName: string\n readonly keyring: UnlockedKeyring\n /**\n * The `reservedEnvelopes('_dict_')` capability (#629 Task 4) — the\n * handle's sanctioned crypto door onto its `_dict_<name>` collection.\n * Bound by the Vault to its own `getDEK`, the same per-collection-name\n * DEK resolver every other collection uses.\n */\n readonly reservedEnvelopes: ReturnType<ViaCryptoCtx['reservedEnvelopes']>\n readonly encrypted: boolean\n readonly ledger: LedgerStore | undefined\n readonly options: LookupBackingOptions\n readonly findAndUpdateReferences:\n | ((dimension: string, oldKey: string, newKey: string) => Promise<void>)\n | undefined\n readonly emitter: NoydbEventEmitter\n /**\n * #647 fix wave 1 — mints a version-ordered delete-marker envelope. Bound by the Vault to the\n * real `kernel/enclave` `buildDeleteMarker` function — `LookupHandle` (`via/lookup/**`)\n * may not import `kernel/enclave/` itself (Check 11/15), so this capability is injected the\n * same way `reservedEnvelopes` above is.\n */\n readonly buildDeleteMarker: (identity: { collection: string; id: string }, version: number, actor: string) => EncryptedEnvelope\n /** #650 Task 4 (#647) — choke-point participation hooks. */\n readonly onDirty?: ((collection: string, id: string, action: 'put' | 'delete', version: number) => Promise<void>) | undefined\n readonly onRecordMutated?: ((collection: string, id: string, action: 'put' | 'delete', version: number) => Promise<void>) | undefined\n /**\n * #650 Task 5 (#648) — the real reference check `LookupHandle.delete()`'s strict branch calls:\n * restrict throws `DictKeyInUseError` naming the referencing collection, cascade/nullify apply\n * their propagation. Bound by the Vault (never a `Collection`/keyring/DEK reach-around); a\n * no-op when the dimension has no declared lookup-referencing edges (today's dangling behavior\n * for undeclared refs is unaffected). Return value is discarded by the handle — callers who\n * need cascade/nullify counts (forget) go through `VaultLinks` directly.\n */\n readonly checkReferencesOnDelete?: ((key: string) => Promise<unknown>) | undefined\n /**\n * Used by the active strategy to satisfy the generic-key parameter on the\n * returned handle. The NO_LOOKUP stub never reads it. Mirrors\n * `BuildDictionaryHandleOptions._keyMarker` (`port/with/i18n-strategy.ts`).\n */\n // marker generic — runtime sees no value\n _keyMarker?: Keys\n}\n\nexport interface LookupStrategy {\n /**\n * Construct a typed `LookupHandle` for the named dimension. Throws under\n * `NO_LOOKUP`.\n */\n buildLookupHandle<Keys extends string = string>(\n opts: BuildLookupHandleOptions<Keys>,\n ): LookupHandle<Keys>\n}\n\nfunction notEnabled(op: string): Error {\n return new Error(\n `${op}: the NO_LOOKUP stub was reached, which should be unreachable today — there is no ` +\n '`lookupStrategy` `createNoydb({ ... })` option yet to select this stub over the real one; ' +\n '`vault.dictionary()` always resolves through `withLookup()` (see `via/i18n/active.ts`\\'s ' +\n 'delegation). This is forward scaffolding for a later task (mirrors `NO_I18N`\\'s shape) — if you ' +\n 'hit this, it indicates a bug, not a missing opt-in.',\n )\n}\n\n/**\n * No-lookup stub. Mirrors `NO_I18N.buildDictionaryHandle`'s shape, but unlike `NO_I18N` (wired as\n * `vault.ts`'s `opts.i18nStrategy ?? NO_I18N` default), nothing selects this stub today — there is\n * no `lookupStrategy` option on `createNoydb()`; `vault.dictionary()`'s only construction path\n * (`Vault.i18nStrategy.buildDictionaryHandle` → `withLookup().buildLookupHandle`) never reaches\n * here. Kept as forward scaffolding for a later task that wires an independent lookup opt-in.\n */\nexport const NO_LOOKUP: LookupStrategy = {\n buildLookupHandle() {\n throw notEnabled('vault.dictionary()')\n },\n}\n\n/** `_dict_*` (legacy dict tier) and `_lookup_*` (the phase-D reserved backing). */\nexport const LOOKUP_COLLECTION_PREFIXES = ['_dict_', '_lookup_'] as const\n\n/** Return true when a collection name is a reserved lookup-backing collection. */\nexport function isLookupCollectionName(name: string): boolean {\n return LOOKUP_COLLECTION_PREFIXES.some((prefix) => name.startsWith(prefix))\n}\n\n// Re-exported pure registry helpers (#650 Task 1) — `kernel/vault.ts`'s\n// thin delegators call these directly; small always-on functions, not\n// behind the tree-shake seam (same bundling class as `isLookupCollectionName`\n// above, just too large to duplicate inline like that one is).\nexport { enforceStaticDictOnPut, resolveDictSource, updateReferencingRecords }\nexport type { DictReferencingCollection }\n\n// #650 Task 4 (#647) — the reserved-collection naming helper `vault.ts`'s sync-registry\n// bookkeeping needs (mapping a declared dimension name to its `_dict_<name>` collection name).\nexport { dictCollectionName } from '../../via/lookup/handle.js'\n\n// #650 Task 2 — the alias-equivalence bridge (`resolveLabelFromMap` +\n// `collectLookupDictCompat`/`lookupToStaticDictCompat`) + the runtime\n// brand/shape predicates `via/compose.ts` needs to route `'lookup'`-branded\n// descriptors, mirroring `isI18nTextDescriptor`/`isDictKeyDescriptor` below.\nexport { resolveLabelFromMap, collectLookupDictCompat, lookupToStaticDictCompat }\nexport type { LookupDictCompat }\n\n// #653 — partial-sync reserved-dict expansion: maps a set of named collections to the\n// one-hop `_dict_*` deps their lookup fields reference, off `dictKeyFieldRegistry`.\nexport { reservedDictDepsOf }\n\n// #650 Task 3 — altKeys ingest normalization + open/closed vocabulary\n// governance: the declare/warm-time altIndex builder + the membership test\n// vault.ts's `membership`/`getAltIndex` closures delegate to.\nexport { materializeBackingTable, checkLookupMembership, buildLookupAltIndex }\nexport type { MaterializedBacking }\n\n// #650 Task 5 — registers a collection's lookup-fields' cross-collection 'ref' graph edges.\nexport { registerLookupRefEdges }\n\n// #650 Task 6 — the sync snapshot+locale seam (#626 retirement, spec §5):\n// `snapshotFor`'s vault-built row source + the combined presentForJoin\n// builder `kernel/collection-config.ts` calls to build the\n// `JoinableSource.presentForJoin` hook `kernel/query/join.ts` consumes.\nexport { buildLookupSnapshotRows, buildPresentForJoin }\nexport type { LookupSnapshot } from '../../via/lookup/snapshot.js'\n\n// #651 Task 3 — the ONE descriptor-keyed key-resolution core (guarded coercion +\n// backing-row-key resolve + referencing-value match) — every consumer (vault.ts,\n// with-shape/links/vault-facade.ts, kernel/via/dispatch.ts) routes through here,\n// never a bare `String()`, ending the dm12 dialect drift.\nexport { coerceLookupKey, resolveBackingRowKey, matchesReferencingValue }\n\n/** Runtime predicate for detecting a `LookupDescriptor` (any of the three tiers). */\nexport function isLookupDescriptor(x: unknown): x is LookupDescriptor {\n return (\n typeof x === 'object' &&\n x !== null &&\n (x as { _viaBrand?: unknown })._viaBrand === 'lookup'\n )\n}\n\n/** Runtime predicate for the bare enum tier (`backing:'static'`, no in-code `table` — no label source). */\nexport function isEnumDescriptor(x: unknown): x is LookupDescriptor {\n return isLookupDescriptor(x) && x.backing === 'static' && x.table === undefined\n}\n\n/**\n * Type-only re-exports — the kernel spine imports these descriptor/handle\n * types through the port instead of reaching into `via/lookup/*` or\n * `via/i18n/*` directly. `isolatedModules: true` erases these at\n * build time — no runtime coupling.\n */\nexport type { LookupHandle, DictEntry, DictionaryOptions } from '../../via/lookup/handle.js'\nexport type { LookupDescriptor, Vocabulary, LookupBacking, OnDelete } from '../../via/lookup/descriptor.js'\nexport type { LookupViaConfig } from '../../via/lookup/binding.js'\nexport type { DictKeyDescriptor, StaticDictDescriptor } from '../../via/i18n/dictionary.js'\n"],"mappings":"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","names":[]}
|
package/dist/classified/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
classified,
|
|
3
3
|
luhnCheck,
|
|
4
4
|
withClassified
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-2HVILGVR.js";
|
|
6
6
|
import {
|
|
7
7
|
ClassifiedNeverStoredError,
|
|
8
8
|
ClassifiedValidationError,
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
ClassifiedRotationError,
|
|
21
21
|
ClassifiedVerifyError
|
|
22
22
|
} from "../chunk-7O3Y4PPD.js";
|
|
23
|
-
import "../chunk-PZ5AY32C.js";
|
|
24
23
|
export {
|
|
25
24
|
ClassifiedConfigError,
|
|
26
25
|
ClassifiedNeverStoredError,
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import "./chunk-PZ5AY32C.js";
|
|
2
|
-
|
|
3
1
|
// src/via/classified/config-drift.ts
|
|
4
2
|
function markerForFields(vdigFields) {
|
|
5
3
|
if (vdigFields === null || vdigFields.size === 0) return null;
|
|
@@ -13,11 +11,11 @@ function markerForFields(vdigFields) {
|
|
|
13
11
|
async function persistClassifiedMarkerForFields(store, vault, collectionName, vdigFields, dek) {
|
|
14
12
|
const marker = markerForFields(vdigFields);
|
|
15
13
|
if (marker === null) return;
|
|
16
|
-
const { persistClassifiedMarker } = await import("./register-
|
|
14
|
+
const { persistClassifiedMarker } = await import("./register-7OJWAWT5.js");
|
|
17
15
|
await persistClassifiedMarker({ store, vault, collectionName, dek, marker });
|
|
18
16
|
}
|
|
19
17
|
async function readClassifiedMarkerDigestOnly(store, vault, collectionName, dek) {
|
|
20
|
-
const { loadPersistedSchema } = await import("./storage-
|
|
18
|
+
const { loadPersistedSchema } = await import("./storage-AVJA5BTW.js");
|
|
21
19
|
const persisted = await loadPersistedSchema(store, vault, collectionName, dek);
|
|
22
20
|
return persisted?.classified?.digestOnly ?? [];
|
|
23
21
|
}
|
|
@@ -25,4 +23,4 @@ export {
|
|
|
25
23
|
persistClassifiedMarkerForFields,
|
|
26
24
|
readClassifiedMarkerDigestOnly
|
|
27
25
|
};
|
|
28
|
-
//# sourceMappingURL=classified-marker-
|
|
26
|
+
//# sourceMappingURL=classified-marker-OO65UYXN.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/via/classified/config-drift.ts"],"sourcesContent":["/**\n * C-A / R10 config-drift marker I/O — the store side of the guard, kept OUT of\n * the kernel `Collection` so the always-on kernel surface stays lean. The\n * kernel holds only the per-handle memoization; these helpers do the actual\n * persisted-schema read/write (via the persisted-schemas storage seam, lazily\n * imported so the classified spine tree-shakes).\n *\n * Marker semantics: a collection declaring classified digest-only fields writes\n * an `x-classified` marker into its `_schemas/<collection>` record on the first\n * classified write. A handle later opened WITHOUT `classifiedFields` (a naive\n * handle) reads it back and refuses to write — see RecordCodec.encryptRecord's\n * R10 guard.\n *\n * `dek` is typed as the bare `CryptoKey` global rather than `EnclaveKey`\n * (`kernel/enclave/crypto.ts`'s alias for the same type) — this file lives\n * under `via/classified/`, which the `via-enclave-isolation`\n * architecture guard (#629) forbids from importing `kernel/enclave/` at\n * all, even type-only.\n *\n * @module\n */\nimport type { NoydbStore, VdigFieldPolicy, ClassifiedMarker } from '../../kernel/types.js'\n\n/**\n * Build the marker for a handle's digest-only field map, or null when it\n * declares none. `epoch` (#597) is stamped fresh on every call, but only\n * ever lands in storage on a genuine first-write or a real reconfiguration —\n * `persistClassifiedMarker`'s equality check (register.ts) ignores epoch, so\n * an identical re-declare's freshly-stamped value is discarded as a no-op\n * and the already-persisted epoch is left untouched (stable per lifetime).\n */\nfunction markerForFields(vdigFields: ReadonlyMap<string, VdigFieldPolicy> | null): ClassifiedMarker | null {\n if (vdigFields === null || vdigFields.size === 0) return null\n const digestOnly = [...vdigFields.keys()]\n return {\n digestOnly, equatable: digestOnly.filter((f) => vdigFields.get(f)?.equatable === true),\n epoch: new Date().toISOString(),\n }\n}\n\n/**\n * Persist the classified marker for a handle's digest-only fields. No-op when\n * the handle declares none. Idempotent (delegates to `persistClassifiedMarker`).\n */\nexport async function persistClassifiedMarkerForFields(\n store: NoydbStore,\n vault: string,\n collectionName: string,\n vdigFields: ReadonlyMap<string, VdigFieldPolicy> | null,\n dek: CryptoKey,\n): Promise<void> {\n const marker = markerForFields(vdigFields)\n if (marker === null) return\n const { persistClassifiedMarker } = await import('../../with-shape/persisted-schemas/register.js')\n await persistClassifiedMarker({ store, vault, collectionName, dek, marker })\n}\n\n/**\n * Read a collection's persisted classified marker's declared digest-only field\n * set (empty when the collection carries no marker). Cross-session drift signal:\n * the R10 superset guard refuses a handle whose declared classified set does not\n * cover every field in this set.\n */\nexport async function readClassifiedMarkerDigestOnly(\n store: NoydbStore,\n vault: string,\n collectionName: string,\n dek: CryptoKey,\n): Promise<readonly string[]> {\n const { loadPersistedSchema } = await import('../../with-shape/persisted-schemas/storage.js')\n const persisted = await loadPersistedSchema(store, vault, collectionName, dek)\n return persisted?.classified?.digestOnly ?? []\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/via/classified/config-drift.ts"],"sourcesContent":["/**\n * C-A / R10 config-drift marker I/O — the store side of the guard, kept OUT of\n * the kernel `Collection` so the always-on kernel surface stays lean. The\n * kernel holds only the per-handle memoization; these helpers do the actual\n * persisted-schema read/write (via the persisted-schemas storage seam, lazily\n * imported so the classified spine tree-shakes).\n *\n * Marker semantics: a collection declaring classified digest-only fields writes\n * an `x-classified` marker into its `_schemas/<collection>` record on the first\n * classified write. A handle later opened WITHOUT `classifiedFields` (a naive\n * handle) reads it back and refuses to write — see RecordCodec.encryptRecord's\n * R10 guard.\n *\n * `dek` is typed as the bare `CryptoKey` global rather than `EnclaveKey`\n * (`kernel/enclave/crypto.ts`'s alias for the same type) — this file lives\n * under `via/classified/`, which the `via-enclave-isolation`\n * architecture guard (#629) forbids from importing `kernel/enclave/` at\n * all, even type-only.\n *\n * @module\n */\nimport type { NoydbStore, VdigFieldPolicy, ClassifiedMarker } from '../../kernel/types.js'\n\n/**\n * Build the marker for a handle's digest-only field map, or null when it\n * declares none. `epoch` (#597) is stamped fresh on every call, but only\n * ever lands in storage on a genuine first-write or a real reconfiguration —\n * `persistClassifiedMarker`'s equality check (register.ts) ignores epoch, so\n * an identical re-declare's freshly-stamped value is discarded as a no-op\n * and the already-persisted epoch is left untouched (stable per lifetime).\n */\nfunction markerForFields(vdigFields: ReadonlyMap<string, VdigFieldPolicy> | null): ClassifiedMarker | null {\n if (vdigFields === null || vdigFields.size === 0) return null\n const digestOnly = [...vdigFields.keys()]\n return {\n digestOnly, equatable: digestOnly.filter((f) => vdigFields.get(f)?.equatable === true),\n epoch: new Date().toISOString(),\n }\n}\n\n/**\n * Persist the classified marker for a handle's digest-only fields. No-op when\n * the handle declares none. Idempotent (delegates to `persistClassifiedMarker`).\n */\nexport async function persistClassifiedMarkerForFields(\n store: NoydbStore,\n vault: string,\n collectionName: string,\n vdigFields: ReadonlyMap<string, VdigFieldPolicy> | null,\n dek: CryptoKey,\n): Promise<void> {\n const marker = markerForFields(vdigFields)\n if (marker === null) return\n const { persistClassifiedMarker } = await import('../../with-shape/persisted-schemas/register.js')\n await persistClassifiedMarker({ store, vault, collectionName, dek, marker })\n}\n\n/**\n * Read a collection's persisted classified marker's declared digest-only field\n * set (empty when the collection carries no marker). Cross-session drift signal:\n * the R10 superset guard refuses a handle whose declared classified set does not\n * cover every field in this set.\n */\nexport async function readClassifiedMarkerDigestOnly(\n store: NoydbStore,\n vault: string,\n collectionName: string,\n dek: CryptoKey,\n): Promise<readonly string[]> {\n const { loadPersistedSchema } = await import('../../with-shape/persisted-schemas/storage.js')\n const persisted = await loadPersistedSchema(store, vault, collectionName, dek)\n return persisted?.classified?.digestOnly ?? []\n}\n"],"mappings":";AA+BA,SAAS,gBAAgB,YAAkF;AACzG,MAAI,eAAe,QAAQ,WAAW,SAAS,EAAG,QAAO;AACzD,QAAM,aAAa,CAAC,GAAG,WAAW,KAAK,CAAC;AACxC,SAAO;AAAA,IACL;AAAA,IAAY,WAAW,WAAW,OAAO,CAAC,MAAM,WAAW,IAAI,CAAC,GAAG,cAAc,IAAI;AAAA,IACrF,QAAO,oBAAI,KAAK,GAAE,YAAY;AAAA,EAChC;AACF;AAMA,eAAsB,iCACpB,OACA,OACA,gBACA,YACA,KACe;AACf,QAAM,SAAS,gBAAgB,UAAU;AACzC,MAAI,WAAW,KAAM;AACrB,QAAM,EAAE,wBAAwB,IAAI,MAAM,OAAO,wBAAgD;AACjG,QAAM,wBAAwB,EAAE,OAAO,OAAO,gBAAgB,KAAK,OAAO,CAAC;AAC7E;AAQA,eAAsB,+BACpB,OACA,OACA,gBACA,KAC4B;AAC5B,QAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,uBAA+C;AAC5F,QAAM,YAAY,MAAM,oBAAoB,OAAO,OAAO,gBAAgB,GAAG;AAC7E,SAAO,WAAW,YAAY,cAAc,CAAC;AAC/C;","names":[]}
|
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
similarTo,
|
|
11
11
|
syncTierSearch,
|
|
12
12
|
warmIndex
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-BOBFL5TW.js";
|
|
14
|
+
import "./chunk-RTBJ5IOO.js";
|
|
15
|
+
import "./chunk-OMB2ZKIT.js";
|
|
15
16
|
import "./chunk-AURFOK3D.js";
|
|
16
17
|
import "./chunk-MVMXZYXT.js";
|
|
17
18
|
import "./chunk-RDZZHYMV.js";
|
|
@@ -28,7 +29,6 @@ import "./chunk-LP7BEXCT.js";
|
|
|
28
29
|
import "./chunk-SL3JAAKN.js";
|
|
29
30
|
import "./chunk-N4CHCXDL.js";
|
|
30
31
|
import "./chunk-7O3Y4PPD.js";
|
|
31
|
-
import "./chunk-PZ5AY32C.js";
|
|
32
32
|
export {
|
|
33
33
|
buildPersistedIndexCallbacks,
|
|
34
34
|
buildRetrievalDocs,
|
|
@@ -42,4 +42,4 @@ export {
|
|
|
42
42
|
syncTierSearch,
|
|
43
43
|
warmIndex
|
|
44
44
|
};
|
|
45
|
-
//# sourceMappingURL=collection-facade-
|
|
45
|
+
//# sourceMappingURL=collection-facade-VOWW732S.js.map
|
|
@@ -3,9 +3,8 @@ import {
|
|
|
3
3
|
evalComputedFields
|
|
4
4
|
} from "./chunk-LWBCUYZW.js";
|
|
5
5
|
import "./chunk-7O3Y4PPD.js";
|
|
6
|
-
import "./chunk-PZ5AY32C.js";
|
|
7
6
|
export {
|
|
8
7
|
ComputedFieldError,
|
|
9
8
|
evalComputedFields
|
|
10
9
|
};
|
|
11
|
-
//# sourceMappingURL=computed-
|
|
10
|
+
//# sourceMappingURL=computed-VPOKQK4N.js.map
|
package/dist/consent/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/with-audit/consent/active.ts"],"sourcesContent":["/**\n * Active consent strategy. Calling `withConsent()` returns a\n * `ConsentStrategy` that delegates to the real\n * `writeConsentEntry` / `loadConsentEntries` functions. Only\n * reachable through the `@noy-db/hub/consent` subpath.\n */\n\nimport { writeConsentEntry, loadConsentEntries } from './consent.js'\nimport type { ConsentStrategy } from './strategy.js'\n\n/**\n * Build the default consent strategy. Pass into\n * `createNoydb({ consentStrategy: withConsent() })` to enable\n * per-operation audit writes into the reserved `_consent_audit`\n * collection.\n */\nexport function withConsent(): ConsentStrategy {\n return {\n write: writeConsentEntry,\n read: loadConsentEntries,\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/with-audit/consent/active.ts"],"sourcesContent":["/**\n * Active consent strategy. Calling `withConsent()` returns a\n * `ConsentStrategy` that delegates to the real\n * `writeConsentEntry` / `loadConsentEntries` functions. Only\n * reachable through the `@noy-db/hub/consent` subpath.\n */\n\nimport { writeConsentEntry, loadConsentEntries } from './consent.js'\nimport type { ConsentStrategy } from './strategy.js'\n\n/**\n * Build the default consent strategy. Pass into\n * `createNoydb({ consentStrategy: withConsent() })` to enable\n * per-operation audit writes into the reserved `_consent_audit`\n * collection.\n */\nexport function withConsent(): ConsentStrategy {\n return {\n write: writeConsentEntry,\n read: loadConsentEntries,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgBO,SAAS,cAA+B;AAC7C,SAAO;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AACF;","names":[]}
|
package/dist/cover/index.js
CHANGED
package/dist/crdt/index.js
CHANGED
package/dist/crdt/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/with-commit/crdt/active.ts"],"sourcesContent":["/**\n * Active CRDT strategy factory. Calling `withCrdt()` returns a\n * `CrdtStrategy` whose methods delegate to the real LWW-Map / RGA /\n * merge / snapshot helpers in `./crdt.ts`. Only reachable through\n * the `@noy-db/hub/crdt` subpath.\n */\n\nimport {\n buildLwwMapState,\n buildRgaState,\n mergeCrdtStates,\n resolveCrdtSnapshot,\n} from './crdt.js'\nimport type { CrdtStrategy } from './strategy.js'\n\n/**\n * Build the default CRDT strategy. Pass into\n * `createNoydb({ crdtStrategy: withCrdt() })` to enable collections\n * declared with `crdt: 'lww-map' | 'rga' | 'yjs'`.\n *\n * @example\n * ```ts\n * import { createNoydb } from '@noy-db/hub'\n * import { withCrdt } from '@noy-db/hub/crdt'\n *\n * const db = await createNoydb({\n * store, user, secret,\n * crdtStrategy: withCrdt(),\n * })\n * const notes = vault.collection('notes', { crdt: 'lww-map' })\n * ```\n */\nexport function withCrdt(): CrdtStrategy {\n return {\n buildLwwMapState,\n buildRgaState,\n mergeCrdtStates,\n resolveCrdtSnapshot,\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/with-commit/crdt/active.ts"],"sourcesContent":["/**\n * Active CRDT strategy factory. Calling `withCrdt()` returns a\n * `CrdtStrategy` whose methods delegate to the real LWW-Map / RGA /\n * merge / snapshot helpers in `./crdt.ts`. Only reachable through\n * the `@noy-db/hub/crdt` subpath.\n */\n\nimport {\n buildLwwMapState,\n buildRgaState,\n mergeCrdtStates,\n resolveCrdtSnapshot,\n} from './crdt.js'\nimport type { CrdtStrategy } from './strategy.js'\n\n/**\n * Build the default CRDT strategy. Pass into\n * `createNoydb({ crdtStrategy: withCrdt() })` to enable collections\n * declared with `crdt: 'lww-map' | 'rga' | 'yjs'`.\n *\n * @example\n * ```ts\n * import { createNoydb } from '@noy-db/hub'\n * import { withCrdt } from '@noy-db/hub/crdt'\n *\n * const db = await createNoydb({\n * store, user, secret,\n * crdtStrategy: withCrdt(),\n * })\n * const notes = vault.collection('notes', { crdt: 'lww-map' })\n * ```\n */\nexport function withCrdt(): CrdtStrategy {\n return {\n buildLwwMapState,\n buildRgaState,\n mergeCrdtStates,\n resolveCrdtSnapshot,\n }\n}\n"],"mappings":";;;;;;;;;;;AAgCO,SAAS,WAAyB;AACvC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
package/dist/custody/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
CustodyApi,
|
|
3
3
|
NO_CUSTODY,
|
|
4
4
|
withCustody
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-QQ2YMQZU.js";
|
|
6
6
|
import "../chunk-VNR2SNVJ.js";
|
|
7
7
|
import "../chunk-YQWBEJCS.js";
|
|
8
8
|
import "../chunk-JMGDHKQF.js";
|
|
@@ -17,7 +17,6 @@ import "../chunk-N4CHCXDL.js";
|
|
|
17
17
|
import {
|
|
18
18
|
CustodyNotEnabledError
|
|
19
19
|
} from "../chunk-7O3Y4PPD.js";
|
|
20
|
-
import "../chunk-PZ5AY32C.js";
|
|
21
20
|
export {
|
|
22
21
|
CustodyApi,
|
|
23
22
|
CustodyNotEnabledError,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
liveBaseIdSet
|
|
3
3
|
} from "./chunk-GYGLAVCX.js";
|
|
4
|
-
import "./chunk-PZ5AY32C.js";
|
|
5
4
|
|
|
6
5
|
// src/with-shape/satellites/dead-filter.ts
|
|
7
6
|
async function liveBaseIdSetsForBundle(store, vaultName, collectionNames, getDEK) {
|
|
8
|
-
const { loadPersistedSchema } = await import("./storage-
|
|
7
|
+
const { loadPersistedSchema } = await import("./storage-AVJA5BTW.js");
|
|
9
8
|
const out = /* @__PURE__ */ new Map();
|
|
10
9
|
for (const name of collectionNames) {
|
|
11
10
|
if (name.startsWith("_")) continue;
|
|
@@ -25,4 +24,4 @@ async function liveBaseIdSetsForBundle(store, vaultName, collectionNames, getDEK
|
|
|
25
24
|
export {
|
|
26
25
|
liveBaseIdSetsForBundle
|
|
27
26
|
};
|
|
28
|
-
//# sourceMappingURL=dead-filter-
|
|
27
|
+
//# sourceMappingURL=dead-filter-OMMVBAKZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/with-shape/satellites/dead-filter.ts"],"sourcesContent":["import type { NoydbStore } from '../../kernel/types.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\nimport { liveBaseIdSet } from './existence.js'\n\n/**\n * Build a live-base id set for every satellite collection detectable from\n * its persisted `_schemas/<name>` pairing marker (#591 Task 10).\n *\n * The bundle-export path (`with-pod/bundle.ts`) has no reachable\n * `SatelliteRegistry` — that registry is private to `Vault` and adding an\n * accessor would grow a kernel file past its line-ceiling ratchet. The\n * marker persisted by `ensureSatelliteMarker` (`_schemas/<satellite>`,\n * decrypted with the satellite collection's own DEK — see\n * `with-shape/persisted-schemas/storage.ts`) is the fallback pairing-info\n * source that's always reachable from just a raw store + a per-collection\n * DEK accessor, so export uses that instead of the in-memory registry.\n *\n * Returns a `Map` from satellite collection name to its base's live id set\n * (existence.ts rule 1: absent or tombstoned base ⇒ not live). Only\n * satellite collections are present as keys — callers should treat a\n * missing key as \"not a satellite, don't filter.\"\n */\nexport async function liveBaseIdSetsForBundle(\n store: NoydbStore,\n vaultName: string,\n collectionNames: readonly string[],\n getDEK: (collectionName: string) => Promise<EnclaveKey>,\n): Promise<Map<string, Set<string>>> {\n const { loadPersistedSchema } = await import('../persisted-schemas/storage.js')\n const out = new Map<string, Set<string>>()\n for (const name of collectionNames) {\n if (name.startsWith('_')) continue\n // SILENT-DEGRADE surface (deliberate asymmetry): on ANY marker-read\n // failure the collection is treated as non-satellite and exported\n // UNFILTERED. That covers, precisely:\n // (a) `getDEK(name)` throwing for any reason (caught below);\n // (b) the persisted `_schemas/<name>` record failing to decrypt or\n // parse — corruption, wrong DEK, transient store error —\n // `loadPersistedSchema` catches internally and returns\n // `undefined` (storage.ts), which reads here as \"no marker\";\n // (c) the marker not yet persisted — `declareSatellite`'s\n // `postRegister` is fire-and-forget, so an export racing a\n // fresh declaration can miss it.\n // Leak-on-error is chosen over exclude-on-error because this is a\n // BACKUP path: exporting stale dead-satellite ciphertext is\n // recoverable; silently dropping live records from a backup is the\n // strictly worse failure. Pinned by the \"leak-on-error posture\" tests\n // in satellites-bundle-filter.test.ts.\n let dek: EnclaveKey\n try {\n dek = await getDEK(name)\n } catch {\n continue // (a) no DEK reachable — can't read the marker, export unfiltered\n }\n const persisted = await loadPersistedSchema(store, vaultName, name, dek)\n const marker = persisted?.satellite\n if (!marker) continue // (b)/(c) — undecryptable/absent/markerless schema record, export unfiltered\n out.set(name, await liveBaseIdSet(store, vaultName, marker.base))\n }\n return out\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/with-shape/satellites/dead-filter.ts"],"sourcesContent":["import type { NoydbStore } from '../../kernel/types.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\nimport { liveBaseIdSet } from './existence.js'\n\n/**\n * Build a live-base id set for every satellite collection detectable from\n * its persisted `_schemas/<name>` pairing marker (#591 Task 10).\n *\n * The bundle-export path (`with-pod/bundle.ts`) has no reachable\n * `SatelliteRegistry` — that registry is private to `Vault` and adding an\n * accessor would grow a kernel file past its line-ceiling ratchet. The\n * marker persisted by `ensureSatelliteMarker` (`_schemas/<satellite>`,\n * decrypted with the satellite collection's own DEK — see\n * `with-shape/persisted-schemas/storage.ts`) is the fallback pairing-info\n * source that's always reachable from just a raw store + a per-collection\n * DEK accessor, so export uses that instead of the in-memory registry.\n *\n * Returns a `Map` from satellite collection name to its base's live id set\n * (existence.ts rule 1: absent or tombstoned base ⇒ not live). Only\n * satellite collections are present as keys — callers should treat a\n * missing key as \"not a satellite, don't filter.\"\n */\nexport async function liveBaseIdSetsForBundle(\n store: NoydbStore,\n vaultName: string,\n collectionNames: readonly string[],\n getDEK: (collectionName: string) => Promise<EnclaveKey>,\n): Promise<Map<string, Set<string>>> {\n const { loadPersistedSchema } = await import('../persisted-schemas/storage.js')\n const out = new Map<string, Set<string>>()\n for (const name of collectionNames) {\n if (name.startsWith('_')) continue\n // SILENT-DEGRADE surface (deliberate asymmetry): on ANY marker-read\n // failure the collection is treated as non-satellite and exported\n // UNFILTERED. That covers, precisely:\n // (a) `getDEK(name)` throwing for any reason (caught below);\n // (b) the persisted `_schemas/<name>` record failing to decrypt or\n // parse — corruption, wrong DEK, transient store error —\n // `loadPersistedSchema` catches internally and returns\n // `undefined` (storage.ts), which reads here as \"no marker\";\n // (c) the marker not yet persisted — `declareSatellite`'s\n // `postRegister` is fire-and-forget, so an export racing a\n // fresh declaration can miss it.\n // Leak-on-error is chosen over exclude-on-error because this is a\n // BACKUP path: exporting stale dead-satellite ciphertext is\n // recoverable; silently dropping live records from a backup is the\n // strictly worse failure. Pinned by the \"leak-on-error posture\" tests\n // in satellites-bundle-filter.test.ts.\n let dek: EnclaveKey\n try {\n dek = await getDEK(name)\n } catch {\n continue // (a) no DEK reachable — can't read the marker, export unfiltered\n }\n const persisted = await loadPersistedSchema(store, vaultName, name, dek)\n const marker = persisted?.satellite\n if (!marker) continue // (b)/(c) — undecryptable/absent/markerless schema record, export unfiltered\n out.set(name, await liveBaseIdSet(store, vaultName, marker.base))\n }\n return out\n}\n"],"mappings":";;;;;AAsBA,eAAsB,wBACpB,OACA,WACA,iBACA,QACmC;AACnC,QAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,uBAAiC;AAC9E,QAAM,MAAM,oBAAI,IAAyB;AACzC,aAAW,QAAQ,iBAAiB;AAClC,QAAI,KAAK,WAAW,GAAG,EAAG;AAiB1B,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,OAAO,IAAI;AAAA,IACzB,QAAQ;AACN;AAAA,IACF;AACA,UAAM,YAAY,MAAM,oBAAoB,OAAO,WAAW,MAAM,GAAG;AACvE,UAAM,SAAS,WAAW;AAC1B,QAAI,CAAC,OAAQ;AACb,QAAI,IAAI,MAAM,MAAM,cAAc,OAAO,WAAW,OAAO,IAAI,CAAC;AAAA,EAClE;AACA,SAAO;AACT;","names":[]}
|
package/dist/debug/index.js
CHANGED
package/dist/debug/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/kernel/debug.ts"],"sourcesContent":["/**\n * Helpers for reading records out of a *plaintext* store (`encrypt: false`)\n * with native tooling — the programmatic core of a `noydb cat`-style unwrap.\n * See the plaintext/debug-store-mode design.\n */\nimport type { EncryptedEnvelope } from './types.js'\n\n/**\n * The option's two failure modes, re-exported here so a consumer can `catch`\n * them by identity (#914). Both are thrown from the kernel — `createNoydb`\n * raises `DebugPlaintextError` when `debugPlaintext` is combined with\n * encryption, and the record codec raises `DebugReservedFieldError` for a\n * field colliding with the `_`-prefixed metadata. This entry is the only one\n * that publishes them.\n */\nexport { DebugPlaintextError, DebugReservedFieldError } from './errors.js'\n\n/** Re-exported so `readPlaintextRecord`'s parameter is nameable from this entry. */\nexport type { EncryptedEnvelope } from './types.js'\n\n/**\n * Extract the record from a plaintext stored envelope, handling both layouts:\n *\n * - **classic plaintext** (`encrypt: false`): the record is JSON in `_data`.\n * - **debugPlaintext**: the record's fields are inlined beside the\n * `_`-prefixed metadata (marked by `_debug`).\n *\n * Returns `null` for an empty/absent body. Throws if handed an **encrypted**\n * envelope (non-empty `_iv`) — there is no key here; decrypt through the vault\n * instead. Intended for record envelopes, not blob chunks.\n *\n * @example\n * ```ts\n * // node script over a to-file store, no vault needed:\n * const env = JSON.parse(readFileSync('data/acme/invoices/inv-1.json', 'utf8'))\n * console.log(readPlaintextRecord(env)) // → { id: 'inv-1', total: '120.00', … }\n * ```\n */\nexport function readPlaintextRecord<T = Record<string, unknown>>(\n envelope: EncryptedEnvelope,\n): T | null {\n if (envelope._iv !== '') {\n throw new Error(\n 'readPlaintextRecord: envelope is encrypted (non-empty _iv) — decrypt via the vault, not this helper',\n )\n }\n if (envelope._debug !== undefined) {\n const record: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(envelope)) {\n if (!key.startsWith('_')) record[key] = value\n }\n return record as T\n }\n if (!envelope._data) return null\n return JSON.parse(envelope._data) as T\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/kernel/debug.ts"],"sourcesContent":["/**\n * Helpers for reading records out of a *plaintext* store (`encrypt: false`)\n * with native tooling — the programmatic core of a `noydb cat`-style unwrap.\n * See the plaintext/debug-store-mode design.\n */\nimport type { EncryptedEnvelope } from './types.js'\n\n/**\n * The option's two failure modes, re-exported here so a consumer can `catch`\n * them by identity (#914). Both are thrown from the kernel — `createNoydb`\n * raises `DebugPlaintextError` when `debugPlaintext` is combined with\n * encryption, and the record codec raises `DebugReservedFieldError` for a\n * field colliding with the `_`-prefixed metadata. This entry is the only one\n * that publishes them.\n */\nexport { DebugPlaintextError, DebugReservedFieldError } from './errors.js'\n\n/** Re-exported so `readPlaintextRecord`'s parameter is nameable from this entry. */\nexport type { EncryptedEnvelope } from './types.js'\n\n/**\n * Extract the record from a plaintext stored envelope, handling both layouts:\n *\n * - **classic plaintext** (`encrypt: false`): the record is JSON in `_data`.\n * - **debugPlaintext**: the record's fields are inlined beside the\n * `_`-prefixed metadata (marked by `_debug`).\n *\n * Returns `null` for an empty/absent body. Throws if handed an **encrypted**\n * envelope (non-empty `_iv`) — there is no key here; decrypt through the vault\n * instead. Intended for record envelopes, not blob chunks.\n *\n * @example\n * ```ts\n * // node script over a to-file store, no vault needed:\n * const env = JSON.parse(readFileSync('data/acme/invoices/inv-1.json', 'utf8'))\n * console.log(readPlaintextRecord(env)) // → { id: 'inv-1', total: '120.00', … }\n * ```\n */\nexport function readPlaintextRecord<T = Record<string, unknown>>(\n envelope: EncryptedEnvelope,\n): T | null {\n if (envelope._iv !== '') {\n throw new Error(\n 'readPlaintextRecord: envelope is encrypted (non-empty _iv) — decrypt via the vault, not this helper',\n )\n }\n if (envelope._debug !== undefined) {\n const record: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(envelope)) {\n if (!key.startsWith('_')) record[key] = value\n }\n return record as T\n }\n if (!envelope._data) return null\n return JSON.parse(envelope._data) as T\n}\n"],"mappings":";;;;;;AAsCO,SAAS,oBACd,UACU;AACV,MAAI,SAAS,QAAQ,IAAI;AACvB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,SAAS,WAAW,QAAW;AACjC,UAAM,SAAkC,CAAC;AACzC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACnD,UAAI,CAAC,IAAI,WAAW,GAAG,EAAG,QAAO,GAAG,IAAI;AAAA,IAC1C;AACA,WAAO;AAAA,EACT;AACA,MAAI,CAAC,SAAS,MAAO,QAAO;AAC5B,SAAO,KAAK,MAAM,SAAS,KAAK;AAClC;","names":[]}
|
|
@@ -25,7 +25,6 @@ import "./chunk-N4CHCXDL.js";
|
|
|
25
25
|
import {
|
|
26
26
|
DelegationTargetMissingError
|
|
27
27
|
} from "./chunk-7O3Y4PPD.js";
|
|
28
|
-
import "./chunk-PZ5AY32C.js";
|
|
29
28
|
|
|
30
29
|
// src/with-party/team/delegation.ts
|
|
31
30
|
var DELEGATIONS_COLLECTION = "_delegations";
|
|
@@ -103,4 +102,4 @@ export {
|
|
|
103
102
|
loadActiveDelegations,
|
|
104
103
|
revokeDelegation
|
|
105
104
|
};
|
|
106
|
-
//# sourceMappingURL=delegation-
|
|
105
|
+
//# sourceMappingURL=delegation-VRYMQRCF.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/with-party/team/delegation.ts"],"sourcesContent":["/**\n * Time-boxed cross-tier delegation tokens.\n *\n * A higher-tier user can issue a delegation that grants another user\n * temporary access to records at a specified tier. The delegation is\n * persisted as an encrypted envelope in the reserved `_delegations`\n * collection. The target user's runtime scans this collection on every\n * open and, while `until` is still in the future, merges the\n * unwrapped tier DEKs into their in-memory DEK map.\n *\n * ## Token shape\n *\n * ```\n * {\n * id, // ULID, also the _delegations record id\n * toUser, // grantee user id\n * fromUser, // grantor user id (owner/admin/higher-tier principal)\n * tier, // tier being delegated\n * collection, // collection name OR null for \"every collection\"\n * record, // optional specific record id\n * until, // ISO timestamp — token expires at this instant\n * wrappedDek, // base64 AES-KW-wrapped tier DEK, wrapped under target KEK\n * createdAt, // ISO timestamp\n * }\n * ```\n *\n * The ciphertext is stored as a normal noy-db envelope — the\n * `_delegations` collection has its own DEK shared across all vault\n * users, so an operator can enumerate active delegations for audit\n * without being able to *use* them (the `wrappedDek` inside is still\n * keyed to the target user's KEK).\n *\n * ## Revocation\n *\n * Delete the `_delegations/<id>` envelope. The target user's runtime\n * reloads the delegation list at each open and at periodic intervals\n * (tracked by the caller — this module is pure logic).\n *\n * @module\n */\n\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from './keyring.js'\nimport { buildRecordAad, buildRecordEnvelope, encrypt, openEnvelopeJson, wrapKey, unwrapKey, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { dekKey } from './tiers.js'\nimport { DelegationTargetMissingError } from '../../kernel/errors.js'\nimport { generateULID } from '../../with-pod/ulid.js'\n\nexport const DELEGATIONS_COLLECTION = '_delegations'\n\n/**\n * Durable payload of a delegation token. Encrypted under the vault's\n * `_delegations` DEK; the `wrappedDek` inside is additionally wrapped\n * under the target user's KEK.\n */\nexport interface DelegationToken {\n readonly id: string\n readonly toUser: string\n readonly fromUser: string\n readonly tier: number\n /** Collection name or `null` for all collections. */\n readonly collection: string | null\n /** Optional specific record id scope. */\n readonly record?: string\n readonly until: string\n readonly wrappedDek: string\n readonly createdAt: string\n}\n\nexport interface IssueDelegationOptions {\n readonly toUser: string\n readonly tier: number\n readonly collection?: string\n readonly record?: string\n readonly until: Date | string\n}\n\n/**\n * Build and persist a delegation token. The caller must hold a tier-N\n * DEK and must have already located the target user's keyring file\n * (so the `wrappedDek` can be re-wrapped against their KEK).\n */\nexport async function issueDelegation(\n store: NoydbStore,\n vault: string,\n grantor: UnlockedKeyring,\n targetKek: EnclaveKey | null,\n delegationsDek: EnclaveKey,\n opts: IssueDelegationOptions,\n): Promise<DelegationToken> {\n if (!targetKek) {\n throw new DelegationTargetMissingError(opts.toUser)\n }\n const tier = opts.tier\n const collectionName = opts.collection ?? null\n const dekLookupCollection = collectionName ?? ''\n // Tier DEK to delegate — fetched from the grantor's own keyring.\n const sourceDek = collectionName\n ? grantor.deks.get(dekKey(collectionName, tier))\n : undefined\n if (!sourceDek) {\n throw new DelegationTargetMissingError(\n `grantor cannot find tier ${tier} DEK for ${dekLookupCollection || '(any)'}`,\n )\n }\n const wrappedDek = await wrapKey(sourceDek, targetKek)\n\n const until = typeof opts.until === 'string' ? opts.until : opts.until.toISOString()\n const token: DelegationToken = {\n id: generateULID(),\n toUser: opts.toUser,\n fromUser: grantor.userId,\n tier,\n collection: collectionName,\n ...(opts.record && { record: opts.record }),\n until,\n wrappedDek,\n createdAt: new Date().toISOString(),\n }\n\n const plaintext = JSON.stringify(token)\n const identity = { collection: DELEGATIONS_COLLECTION, id: token.id, by: grantor.userId, version: 1 }\n const { iv, data } = await encrypt(plaintext, delegationsDek, buildRecordAad(identity))\n const envelope = buildRecordEnvelope(\n identity,\n { ts: token.createdAt, iv, data},\n )\n await store.put(vault, DELEGATIONS_COLLECTION, token.id, envelope)\n return token\n}\n\n/**\n * Enumerate every live (non-expired) delegation addressed to `toUser`\n * and merge the unwrapped tier DEKs into their keyring. Returns the\n * list of merged delegations so the caller can register per-access\n * audit context.\n */\nexport async function loadActiveDelegations(\n store: NoydbStore,\n vault: string,\n user: UnlockedKeyring,\n delegationsDek: EnclaveKey,\n now: Date = new Date(),\n): Promise<DelegationToken[]> {\n const ids = await store.list(vault, DELEGATIONS_COLLECTION)\n const merged: DelegationToken[] = []\n const nowIso = now.toISOString()\n for (const id of ids) {\n const env = await store.get(vault, DELEGATIONS_COLLECTION, id)\n if (!env) continue\n let token: DelegationToken\n try {\n const plaintext = await openEnvelopeJson({ collection: DELEGATIONS_COLLECTION, id }, env, delegationsDek)\n token = JSON.parse(plaintext) as DelegationToken\n } catch {\n continue\n }\n if (token.toUser !== user.userId) continue\n if (token.until <= nowIso) continue\n\n // A user without a KEK in memory (tier-3 PIN resume, wrap-DEKs\n // tier-2 unlock, session restore) cannot unwrap delegation tokens\n // — those were wrapped under the user's KEK at issue time. Skip\n // this token; the consumer reaches it again at tier-1 unlock.\n if (!user.kek) continue\n let dek: EnclaveKey\n try {\n dek = await unwrapKey(token.wrappedDek, user.kek)\n } catch {\n continue\n }\n const k = token.collection\n ? dekKey(token.collection, token.tier)\n : `__any#${token.tier}`\n user.deks.set(k, dek)\n merged.push(token)\n }\n return merged\n}\n\n/**\n * Revoke a delegation by id — the caller resolves the envelope and\n * issues a `delete`. Provided as a stable helper so the naming is\n * symmetric to `issueDelegation`.\n */\nexport async function revokeDelegation(\n store: NoydbStore,\n vault: string,\n id: string,\n): Promise<void> {\n await store.delete(vault, DELEGATIONS_COLLECTION, id)\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/with-party/team/delegation.ts"],"sourcesContent":["/**\n * Time-boxed cross-tier delegation tokens.\n *\n * A higher-tier user can issue a delegation that grants another user\n * temporary access to records at a specified tier. The delegation is\n * persisted as an encrypted envelope in the reserved `_delegations`\n * collection. The target user's runtime scans this collection on every\n * open and, while `until` is still in the future, merges the\n * unwrapped tier DEKs into their in-memory DEK map.\n *\n * ## Token shape\n *\n * ```\n * {\n * id, // ULID, also the _delegations record id\n * toUser, // grantee user id\n * fromUser, // grantor user id (owner/admin/higher-tier principal)\n * tier, // tier being delegated\n * collection, // collection name OR null for \"every collection\"\n * record, // optional specific record id\n * until, // ISO timestamp — token expires at this instant\n * wrappedDek, // base64 AES-KW-wrapped tier DEK, wrapped under target KEK\n * createdAt, // ISO timestamp\n * }\n * ```\n *\n * The ciphertext is stored as a normal noy-db envelope — the\n * `_delegations` collection has its own DEK shared across all vault\n * users, so an operator can enumerate active delegations for audit\n * without being able to *use* them (the `wrappedDek` inside is still\n * keyed to the target user's KEK).\n *\n * ## Revocation\n *\n * Delete the `_delegations/<id>` envelope. The target user's runtime\n * reloads the delegation list at each open and at periodic intervals\n * (tracked by the caller — this module is pure logic).\n *\n * @module\n */\n\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from './keyring.js'\nimport { buildRecordAad, buildRecordEnvelope, encrypt, openEnvelopeJson, wrapKey, unwrapKey, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { dekKey } from './tiers.js'\nimport { DelegationTargetMissingError } from '../../kernel/errors.js'\nimport { generateULID } from '../../with-pod/ulid.js'\n\nexport const DELEGATIONS_COLLECTION = '_delegations'\n\n/**\n * Durable payload of a delegation token. Encrypted under the vault's\n * `_delegations` DEK; the `wrappedDek` inside is additionally wrapped\n * under the target user's KEK.\n */\nexport interface DelegationToken {\n readonly id: string\n readonly toUser: string\n readonly fromUser: string\n readonly tier: number\n /** Collection name or `null` for all collections. */\n readonly collection: string | null\n /** Optional specific record id scope. */\n readonly record?: string\n readonly until: string\n readonly wrappedDek: string\n readonly createdAt: string\n}\n\nexport interface IssueDelegationOptions {\n readonly toUser: string\n readonly tier: number\n readonly collection?: string\n readonly record?: string\n readonly until: Date | string\n}\n\n/**\n * Build and persist a delegation token. The caller must hold a tier-N\n * DEK and must have already located the target user's keyring file\n * (so the `wrappedDek` can be re-wrapped against their KEK).\n */\nexport async function issueDelegation(\n store: NoydbStore,\n vault: string,\n grantor: UnlockedKeyring,\n targetKek: EnclaveKey | null,\n delegationsDek: EnclaveKey,\n opts: IssueDelegationOptions,\n): Promise<DelegationToken> {\n if (!targetKek) {\n throw new DelegationTargetMissingError(opts.toUser)\n }\n const tier = opts.tier\n const collectionName = opts.collection ?? null\n const dekLookupCollection = collectionName ?? ''\n // Tier DEK to delegate — fetched from the grantor's own keyring.\n const sourceDek = collectionName\n ? grantor.deks.get(dekKey(collectionName, tier))\n : undefined\n if (!sourceDek) {\n throw new DelegationTargetMissingError(\n `grantor cannot find tier ${tier} DEK for ${dekLookupCollection || '(any)'}`,\n )\n }\n const wrappedDek = await wrapKey(sourceDek, targetKek)\n\n const until = typeof opts.until === 'string' ? opts.until : opts.until.toISOString()\n const token: DelegationToken = {\n id: generateULID(),\n toUser: opts.toUser,\n fromUser: grantor.userId,\n tier,\n collection: collectionName,\n ...(opts.record && { record: opts.record }),\n until,\n wrappedDek,\n createdAt: new Date().toISOString(),\n }\n\n const plaintext = JSON.stringify(token)\n const identity = { collection: DELEGATIONS_COLLECTION, id: token.id, by: grantor.userId, version: 1 }\n const { iv, data } = await encrypt(plaintext, delegationsDek, buildRecordAad(identity))\n const envelope = buildRecordEnvelope(\n identity,\n { ts: token.createdAt, iv, data},\n )\n await store.put(vault, DELEGATIONS_COLLECTION, token.id, envelope)\n return token\n}\n\n/**\n * Enumerate every live (non-expired) delegation addressed to `toUser`\n * and merge the unwrapped tier DEKs into their keyring. Returns the\n * list of merged delegations so the caller can register per-access\n * audit context.\n */\nexport async function loadActiveDelegations(\n store: NoydbStore,\n vault: string,\n user: UnlockedKeyring,\n delegationsDek: EnclaveKey,\n now: Date = new Date(),\n): Promise<DelegationToken[]> {\n const ids = await store.list(vault, DELEGATIONS_COLLECTION)\n const merged: DelegationToken[] = []\n const nowIso = now.toISOString()\n for (const id of ids) {\n const env = await store.get(vault, DELEGATIONS_COLLECTION, id)\n if (!env) continue\n let token: DelegationToken\n try {\n const plaintext = await openEnvelopeJson({ collection: DELEGATIONS_COLLECTION, id }, env, delegationsDek)\n token = JSON.parse(plaintext) as DelegationToken\n } catch {\n continue\n }\n if (token.toUser !== user.userId) continue\n if (token.until <= nowIso) continue\n\n // A user without a KEK in memory (tier-3 PIN resume, wrap-DEKs\n // tier-2 unlock, session restore) cannot unwrap delegation tokens\n // — those were wrapped under the user's KEK at issue time. Skip\n // this token; the consumer reaches it again at tier-1 unlock.\n if (!user.kek) continue\n let dek: EnclaveKey\n try {\n dek = await unwrapKey(token.wrappedDek, user.kek)\n } catch {\n continue\n }\n const k = token.collection\n ? dekKey(token.collection, token.tier)\n : `__any#${token.tier}`\n user.deks.set(k, dek)\n merged.push(token)\n }\n return merged\n}\n\n/**\n * Revoke a delegation by id — the caller resolves the envelope and\n * issues a `delete`. Provided as a stable helper so the naming is\n * symmetric to `issueDelegation`.\n */\nexport async function revokeDelegation(\n store: NoydbStore,\n vault: string,\n id: string,\n): Promise<void> {\n await store.delete(vault, DELEGATIONS_COLLECTION, id)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDO,IAAM,yBAAyB;AAkCtC,eAAsB,gBACpB,OACA,OACA,SACA,WACA,gBACA,MAC0B;AAC1B,MAAI,CAAC,WAAW;AACd,UAAM,IAAI,6BAA6B,KAAK,MAAM;AAAA,EACpD;AACA,QAAM,OAAO,KAAK;AAClB,QAAM,iBAAiB,KAAK,cAAc;AAC1C,QAAM,sBAAsB,kBAAkB;AAE9C,QAAM,YAAY,iBACd,QAAQ,KAAK,IAAI,OAAO,gBAAgB,IAAI,CAAC,IAC7C;AACJ,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR,4BAA4B,IAAI,YAAY,uBAAuB,OAAO;AAAA,IAC5E;AAAA,EACF;AACA,QAAM,aAAa,MAAM,QAAQ,WAAW,SAAS;AAErD,QAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAK,MAAM,YAAY;AACnF,QAAM,QAAyB;AAAA,IAC7B,IAAI,aAAa;AAAA,IACjB,QAAQ,KAAK;AAAA,IACb,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,YAAY;AAAA,IACZ,GAAI,KAAK,UAAU,EAAE,QAAQ,KAAK,OAAO;AAAA,IACzC;AAAA,IACA;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AAEA,QAAM,YAAY,KAAK,UAAU,KAAK;AACtC,QAAM,WAAW,EAAE,YAAY,wBAAwB,IAAI,MAAM,IAAI,IAAI,QAAQ,QAAQ,SAAS,EAAE;AACpG,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,WAAW,gBAAgB,eAAe,QAAQ,CAAC;AACtF,QAAM,WAAW;AAAA,IACf;AAAA,IACA,EAAE,IAAI,MAAM,WAAW,IAAI,KAAI;AAAA,EACjC;AACA,QAAM,MAAM,IAAI,OAAO,wBAAwB,MAAM,IAAI,QAAQ;AACjE,SAAO;AACT;AAQA,eAAsB,sBACpB,OACA,OACA,MACA,gBACA,MAAY,oBAAI,KAAK,GACO;AAC5B,QAAM,MAAM,MAAM,MAAM,KAAK,OAAO,sBAAsB;AAC1D,QAAM,SAA4B,CAAC;AACnC,QAAM,SAAS,IAAI,YAAY;AAC/B,aAAW,MAAM,KAAK;AACpB,UAAM,MAAM,MAAM,MAAM,IAAI,OAAO,wBAAwB,EAAE;AAC7D,QAAI,CAAC,IAAK;AACV,QAAI;AACJ,QAAI;AACF,YAAM,YAAY,MAAM,iBAAiB,EAAE,YAAY,wBAAwB,GAAG,GAAG,KAAK,cAAc;AACxG,cAAQ,KAAK,MAAM,SAAS;AAAA,IAC9B,QAAQ;AACN;AAAA,IACF;AACA,QAAI,MAAM,WAAW,KAAK,OAAQ;AAClC,QAAI,MAAM,SAAS,OAAQ;AAM3B,QAAI,CAAC,KAAK,IAAK;AACf,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,UAAU,MAAM,YAAY,KAAK,GAAG;AAAA,IAClD,QAAQ;AACN;AAAA,IACF;AACA,UAAM,IAAI,MAAM,aACZ,OAAO,MAAM,YAAY,MAAM,IAAI,IACnC,SAAS,MAAM,IAAI;AACvB,SAAK,KAAK,IAAI,GAAG,GAAG;AACpB,WAAO,KAAK,KAAK;AAAA,EACnB;AACA,SAAO;AACT;AAOA,eAAsB,iBACpB,OACA,OACA,IACe;AACf,QAAM,MAAM,OAAO,OAAO,wBAAwB,EAAE;AACtD;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DerivationRegistry
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-EKVNVJVV.js";
|
|
4
4
|
import "../chunk-TPHAGLUH.js";
|
|
5
5
|
import {
|
|
6
6
|
DerivationExecutor
|
|
@@ -9,12 +9,16 @@ import {
|
|
|
9
9
|
withDerivation,
|
|
10
10
|
withRollup
|
|
11
11
|
} from "../chunk-BUE2D7PX.js";
|
|
12
|
-
import "../chunk-
|
|
12
|
+
import "../chunk-4SZMZAYN.js";
|
|
13
13
|
import "../chunk-TFMM5DBB.js";
|
|
14
|
+
import "../chunk-RTBJ5IOO.js";
|
|
15
|
+
import "../chunk-KCUIMITG.js";
|
|
14
16
|
import "../chunk-VYYM3MEN.js";
|
|
15
|
-
import "../chunk-
|
|
17
|
+
import "../chunk-REDOOY2Q.js";
|
|
16
18
|
import "../chunk-Y2NBBT6K.js";
|
|
17
19
|
import "../chunk-WWNYSV7S.js";
|
|
20
|
+
import "../chunk-YPXNAP72.js";
|
|
21
|
+
import "../chunk-V4PXLYWM.js";
|
|
18
22
|
import "../chunk-MVMXZYXT.js";
|
|
19
23
|
import "../chunk-RDZZHYMV.js";
|
|
20
24
|
import "../chunk-TRGTKX5E.js";
|
|
@@ -35,7 +39,6 @@ import {
|
|
|
35
39
|
DerivationOutputShapeError,
|
|
36
40
|
DerivationOutputUnknownError
|
|
37
41
|
} from "../chunk-7O3Y4PPD.js";
|
|
38
|
-
import "../chunk-PZ5AY32C.js";
|
|
39
42
|
export {
|
|
40
43
|
DerivationCapExceededError,
|
|
41
44
|
DerivationCycleError,
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
derivePersistedSchema,
|
|
3
3
|
isZod4Schema,
|
|
4
4
|
isZodSchema
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-REDOOY2Q.js";
|
|
6
6
|
import "./chunk-Y2NBBT6K.js";
|
|
7
7
|
import "./chunk-CIGAQBJR.js";
|
|
8
8
|
import "./chunk-4RUFYGWG.js";
|
|
@@ -13,10 +13,9 @@ import "./chunk-LP7BEXCT.js";
|
|
|
13
13
|
import "./chunk-SL3JAAKN.js";
|
|
14
14
|
import "./chunk-N4CHCXDL.js";
|
|
15
15
|
import "./chunk-7O3Y4PPD.js";
|
|
16
|
-
import "./chunk-PZ5AY32C.js";
|
|
17
16
|
export {
|
|
18
17
|
derivePersistedSchema,
|
|
19
18
|
isZod4Schema,
|
|
20
19
|
isZodSchema
|
|
21
20
|
};
|
|
22
|
-
//# sourceMappingURL=derive-
|
|
21
|
+
//# sourceMappingURL=derive-HHBRFS2U.js.map
|
package/dist/directory/index.js
CHANGED
|
@@ -4,12 +4,13 @@ import {
|
|
|
4
4
|
} from "./chunk-TPHAGLUH.js";
|
|
5
5
|
import {
|
|
6
6
|
markStale
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-SMH6SRX7.js";
|
|
8
8
|
import "./chunk-TFMM5DBB.js";
|
|
9
9
|
import {
|
|
10
10
|
putDerivedOutput,
|
|
11
11
|
selfWriteFieldEqual
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-MGOXJIBV.js";
|
|
13
|
+
import "./chunk-YPXNAP72.js";
|
|
13
14
|
import "./chunk-5D2ALSM5.js";
|
|
14
15
|
import "./chunk-RDZZHYMV.js";
|
|
15
16
|
import "./chunk-TRGTKX5E.js";
|
|
@@ -26,14 +27,13 @@ import "./chunk-N4CHCXDL.js";
|
|
|
26
27
|
import {
|
|
27
28
|
DerivationCapExceededError
|
|
28
29
|
} from "./chunk-7O3Y4PPD.js";
|
|
29
|
-
import "./chunk-PZ5AY32C.js";
|
|
30
30
|
|
|
31
31
|
// src/with-formula/derivations/dispatch.ts
|
|
32
32
|
async function dispatchArrayDerivationsOnDelete(ctx, id, eraseRecordShapeToo = false) {
|
|
33
33
|
const { derivationSource, collectionName, adapter, vault, getDEK, storeCiphertext } = ctx;
|
|
34
34
|
const strategies = derivationSource.registry().strategiesForSource(collectionName);
|
|
35
35
|
if (strategies.length === 0) return 0;
|
|
36
|
-
const { loadFanoutSidecar, deleteFanoutSidecar } = await import("./fanout-sidecar-
|
|
36
|
+
const { loadFanoutSidecar, deleteFanoutSidecar } = await import("./fanout-sidecar-HJIVUPTD.js");
|
|
37
37
|
const txCtx = derivationSource.getActiveTxContext();
|
|
38
38
|
let erased = 0;
|
|
39
39
|
for (const { spec } of strategies) {
|
|
@@ -68,14 +68,18 @@ async function dispatchDerivations(ctx, id, record, version, wave, prior) {
|
|
|
68
68
|
const mode = typeof spec.lifecycle === "string" ? spec.lifecycle : spec.lifecycle.mode;
|
|
69
69
|
if (spec.rollup) {
|
|
70
70
|
if (mode !== "eager") continue;
|
|
71
|
+
const asParentId = (v) => typeof v === "string" || typeof v === "number" ? String(v) : null;
|
|
71
72
|
let parentId;
|
|
73
|
+
let oldParentId = null;
|
|
72
74
|
if (collectionName === spec.rollup.from) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
parentId = asParentId(incoming[spec.rollup.key]);
|
|
76
|
+
const priorKey = canonicalPrior != null ? asParentId(canonicalPrior[spec.rollup.key]) : null;
|
|
77
|
+
if (priorKey !== null && priorKey !== parentId) oldParentId = priorKey;
|
|
75
78
|
} else {
|
|
76
79
|
parentId = id;
|
|
77
80
|
}
|
|
78
81
|
if (parentId !== null) await recomputeRollup(spec, parentId, { collection: collectionName, id }, wave);
|
|
82
|
+
if (oldParentId !== null) await recomputeRollup(spec, oldParentId, { collection: collectionName, id }, wave);
|
|
79
83
|
continue;
|
|
80
84
|
}
|
|
81
85
|
const isSource = spec.source === collectionName;
|
|
@@ -126,7 +130,7 @@ async function dispatchDerivations(ctx, id, record, version, wave, prior) {
|
|
|
126
130
|
}
|
|
127
131
|
if (executorClass === null) {
|
|
128
132
|
;
|
|
129
|
-
({ DerivationExecutor: executorClass } = await import("./executor-
|
|
133
|
+
({ DerivationExecutor: executorClass } = await import("./executor-5NIMLSKZ.js"));
|
|
130
134
|
}
|
|
131
135
|
for (const run of runs) await runOne(ctx, spec, strategyHash, executorClass, run);
|
|
132
136
|
}
|
|
@@ -150,7 +154,7 @@ async function runOne(ctx, spec, strategyHash, executorClass, run) {
|
|
|
150
154
|
const outputCollection = derivationSource.getCollection(outSpec.collection);
|
|
151
155
|
const txCtx = derivationSource.getActiveTxContext();
|
|
152
156
|
if (out.kind === "array") {
|
|
153
|
-
const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-
|
|
157
|
+
const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-HJIVUPTD.js");
|
|
154
158
|
const prior = await loadFanoutSidecar(adapter, vault, spec.source, run.runId, key, getDEK, storeCiphertext);
|
|
155
159
|
const prevKeys = new Set(prior?.keys ?? []);
|
|
156
160
|
const newKeysList = out.entries.map((e) => e.key);
|
|
@@ -234,7 +238,7 @@ async function dispatchTriggerDerivationsOnDelete(ctx, id, deleted) {
|
|
|
234
238
|
for (const sid of matched) await markStale(registry, spec, sid);
|
|
235
239
|
continue;
|
|
236
240
|
}
|
|
237
|
-
if (executorClass === null) ({ DerivationExecutor: executorClass } = await import("./executor-
|
|
241
|
+
if (executorClass === null) ({ DerivationExecutor: executorClass } = await import("./executor-5NIMLSKZ.js"));
|
|
238
242
|
for (const sid of matched) {
|
|
239
243
|
const raw = await srcColl._getStoredRecord(sid);
|
|
240
244
|
if (raw === null) continue;
|
|
@@ -247,4 +251,4 @@ export {
|
|
|
247
251
|
dispatchDerivations,
|
|
248
252
|
dispatchTriggerDerivationsOnDelete
|
|
249
253
|
};
|
|
250
|
-
//# sourceMappingURL=dispatch-
|
|
254
|
+
//# sourceMappingURL=dispatch-DC3LQJ3S.js.map
|