@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/kernel/enclave/classify/verify.ts"],"sourcesContent":["/**\n * The enclave verify oracle (spec §3). Verdict-only egress: no path\n * distinguishes \"record missing\" / \"no digest\" / \"AAD-tamper\" / \"mismatch\"\n * beyond { ok: false } — existence oracles are oracles too. Every path that\n * cannot run a real comparison runs ONE dummy PBKDF2 + ONE dummy tag-compare\n * first (C4), so wall-clock cannot enumerate records/fields/answers. The\n * text door (verifyTextField) has no real PBKDF2 of its own, so it pays the\n * pad UNCONDITIONALLY — every outcome, including a real compare, costs the\n * same one PBKDF2 unit (no inverted exists-vs-absent oracle).\n * Elevated records (_tier > 0, with-audit tier moves) are non-comparable\n * through every door and pad-false exactly like missing records (#691) —\n * the tier-aware read surface is getAtTier, never verify.\n * Throws are reserved for caller/config bugs (ClassifiedVerifyError /\n * ClassifiedConfigError) and are exempt from the pad by design (R6 note).\n * @module\n */\nimport { generateSalt, base64ToBuffer, type EnclaveKey } from '../crypto.js'\nimport { pbkdf2VerifyDigest, VDIG_ITERATIONS } from './digest.js'\nimport { normalizeForVerify, type VerifyNormalizeMode } from './normalize.js'\nimport { blindedEqual } from './compare.js'\nimport { evaluateKofN } from './kofn.js'\nimport { openVdigPayload, type VdigPayload } from './vdig.js'\nimport { dualReadSealedSlot } from '../record-keys/sealed-slot.js'\nimport { ClassifiedConfigError, ClassifiedVerifyError } from '../../errors.js'\nimport type { EncryptedEnvelope, VdigFieldPolicy, ClassifiedVerdict } from '../../types.js'\n\nexport interface VerifyEngineCtx {\n readonly collection: string\n getEnvelope(id: string): Promise<EncryptedEnvelope | null>\n resolveCek(env: EncryptedEnvelope): Promise<EnclaveKey | undefined>\n getDEK(): Promise<EnclaveKey>\n readonly now: () => number\n}\n\n/**\n * C4 pad: one full-cost dummy digest + one dummy compare; result discarded.\n * Pad at `iter` when the caller holds an AUTHENTICATED payload.iter, so pad\n * cost tracks the real compare cost across future iteration bumps; the\n * fully-absent paths (no payload to read an iter from) use VDIG_ITERATIONS.\n */\nasync function padOnce(iter: number = VDIG_ITERATIONS): Promise<void> {\n const dummy = await pbkdf2VerifyDigest('noydb-classify-c4-pad', generateSalt(), iter)\n await blindedEqual(dummy, dummy)\n}\n\nasync function padFalse(): Promise<ClassifiedVerdict> {\n await padOnce()\n return { ok: false }\n}\n\n/** R6 verify-side transition evidence — config bug, fail-loud, pad-exempt. */\nfunction refuseSealedResidue(collection: string, field: string): never {\n throw new ClassifiedConfigError(collection,\n `field \"${field}\" carries a recoverable _sealed slot but no _vdig — storage-form ` +\n `transition detected (R6); refused fail-loud, never an ok:false masquerading as wrong-password`)\n}\n\nexport async function verifyDigestField(\n ctx: VerifyEngineCtx,\n id: string,\n field: string,\n candidate: string,\n policy: VdigFieldPolicy,\n): Promise<ClassifiedVerdict> {\n const env = await ctx.getEnvelope(id)\n // #691: an elevated envelope (_tier > 0) is non-comparable through every\n // tier-0 door — its key material is tier-DEK-wrapped and must never be\n // resolved here (the elevating session's warm cekCache would otherwise\n // leak past the tier audit). Exactly the missing-record pad path, checked\n // BEFORE the R6 residue probe so tier-0 callers learn nothing.\n if (env === null || (env._tier ?? 0) > 0) return padFalse()\n const blob = env._vdig?.[field]\n if (blob === undefined) {\n if (env._sealed?.[field] !== undefined) refuseSealedResidue(ctx.collection, field)\n return padFalse()\n }\n const cek = await ctx.resolveCek(env)\n if (cek === undefined) return padFalse()\n\n let payload: VdigPayload\n try {\n payload = await openVdigPayload(blob, cek, ctx.collection, id, field)\n } catch {\n return padFalse() // AAD/tamper (C1) → padded false; no tamper oracle to the caller\n }\n\n const normalized = normalizeForVerify(policy.normalize, candidate)\n const digest = await pbkdf2VerifyDigest(normalized, base64ToBuffer(payload.cur.salt), payload.iter)\n const ok = await blindedEqual(digest, base64ToBuffer(payload.cur.hash))\n if (!ok) return { ok: false } // I1: bare false — mustRotate never computed here\n\n if (policy.rotateDays !== undefined\n && ctx.now() > Date.parse(payload.cur.at) + policy.rotateDays * 86_400_000) {\n // Disclosing write-age-vs-policy to a SUCCESSFUL verifier is intended (audit F6).\n return { ok: true, mustRotate: true }\n }\n return { ok: true }\n}\n\nexport async function verifyTextField(\n ctx: VerifyEngineCtx,\n id: string,\n field: string,\n candidate: string,\n normalize: VerifyNormalizeMode,\n): Promise<ClassifiedVerdict> {\n // C4, single code path: (a) attempt the unseal where a slot exists, then\n // for EVERY outcome — present-correct, present-wrong, missing record,\n // missing slot, tampered — (b) run the one constant-cost pad, (c) run one\n // blindedEqual, (d) return the boolean. Unlike the digest door there is no\n // real PBKDF2 here (unseal is sub-ms AES-GCM), so skipping the pad on the\n // real compare would make exists-but-wrong ~100x FASTER than absent — an\n // inverted existence oracle. No branch below skips the dominant cost.\n const env = await ctx.getEnvelope(id)\n const blob = env?._sealed?.[field]\n let stored: string | undefined\n if (env !== null && (env._tier ?? 0) === 0 && blob !== undefined) { // #691: elevated ≡ missing\n const cek = await ctx.resolveCek(env)\n const dek = await ctx.getDEK()\n try {\n // Plaintext exists microseconds inside this function; only the boolean leaves.\n stored = String(JSON.parse(await dualReadSealedSlot(blob, field, ctx.collection, cek, dek)))\n } catch {\n stored = undefined // AAD/tamper (C1) → same padded compare tail as absent\n }\n }\n await padOnce()\n const a = new TextEncoder().encode(normalizeForVerify(normalize, candidate))\n const b = stored !== undefined\n ? new TextEncoder().encode(normalizeForVerify(normalize, stored))\n : a // no-compare dummy comparand — the verdict is forced false below\n const eq = await blindedEqual(a, b)\n return { ok: stored !== undefined && eq }\n}\n\nexport async function matchGroupFields(\n ctx: VerifyEngineCtx,\n id: string,\n answers: Record<string, string>,\n members: ReadonlyArray<{ readonly field: string; readonly policy: VdigFieldPolicy }>,\n opts: { readonly min: number },\n): Promise<{ readonly passed: boolean }> {\n // I2 step 1 — validate EVERYTHING up front, before any PBKDF2, throwing\n // uniformly at ~0 elapsed (no member-position leak via timing/throw type).\n if (!Number.isInteger(opts.min) || opts.min < 1 || opts.min > members.length) {\n throw new ClassifiedVerifyError(ctx.collection, '*',\n `matchGroup min ${opts.min} out of range 1..${members.length}`)\n }\n const normalized = new Map<string, string>()\n for (const m of members) {\n const answer = answers[m.field] // non-member answer keys: silently ignored\n if (answer === undefined) continue\n if (typeof answer !== 'string') {\n throw new ClassifiedVerifyError(ctx.collection, m.field, 'candidate must be a string')\n }\n normalized.set(m.field, normalizeForVerify(m.policy.normalize, answer))\n }\n\n const fetched = await ctx.getEnvelope(id)\n // #691: elevated ≡ missing — nulling the view here also skips the R6\n // residue probe (a tier-0 caller must not learn an elevated record's\n // storage form) and forces the cek-undefined pad path for every member.\n const env = fetched === null || (fetched._tier ?? 0) > 0 ? null : fetched\n if (env !== null) {\n for (const m of members) { // R6 evidence — uniform, before any PBKDF2\n if (env._sealed?.[m.field] !== undefined && env._vdig?.[m.field] === undefined) {\n refuseSealedResidue(ctx.collection, m.field)\n }\n }\n }\n const cek = env !== null ? await ctx.resolveCek(env) : undefined\n\n // I2 step 2+3 — iterate RESOLVED GROUP MEMBERS (denominator = |members|),\n // evaluate EVERY member (collect, never break), pad every no-compare slot.\n const results: boolean[] = []\n for (const m of members) {\n const candidate = normalized.get(m.field)\n const blob = env?._vdig?.[m.field]\n if (env === null || cek === undefined || blob === undefined) {\n await padOnce()\n results.push(false)\n continue\n }\n try {\n const payload = await openVdigPayload(blob, cek, ctx.collection, id, m.field)\n if (candidate === undefined) {\n // Slot present, answer absent: the payload's authenticated iter is in\n // hand — pad at IT (not the constant) so a future iteration bump\n // cannot desync this pad's cost from the real compare's cost.\n await padOnce(payload.iter)\n results.push(false)\n continue\n }\n const digest = await pbkdf2VerifyDigest(candidate, base64ToBuffer(payload.cur.salt), payload.iter)\n results.push(await blindedEqual(digest, base64ToBuffer(payload.cur.hash)))\n } catch {\n await padOnce()\n results.push(false)\n }\n }\n // Per-member results never appear in any return, error, or audit payload.\n return { passed: evaluateKofN(results, opts.min) }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/kernel/enclave/classify/verify.ts"],"sourcesContent":["/**\n * The enclave verify oracle (spec §3). Verdict-only egress: no path\n * distinguishes \"record missing\" / \"no digest\" / \"AAD-tamper\" / \"mismatch\"\n * beyond { ok: false } — existence oracles are oracles too. Every path that\n * cannot run a real comparison runs ONE dummy PBKDF2 + ONE dummy tag-compare\n * first (C4), so wall-clock cannot enumerate records/fields/answers. The\n * text door (verifyTextField) has no real PBKDF2 of its own, so it pays the\n * pad UNCONDITIONALLY — every outcome, including a real compare, costs the\n * same one PBKDF2 unit (no inverted exists-vs-absent oracle).\n * Elevated records (_tier > 0, with-audit tier moves) are non-comparable\n * through every door and pad-false exactly like missing records (#691) —\n * the tier-aware read surface is getAtTier, never verify.\n * Throws are reserved for caller/config bugs (ClassifiedVerifyError /\n * ClassifiedConfigError) and are exempt from the pad by design (R6 note).\n * @module\n */\nimport { generateSalt, base64ToBuffer, type EnclaveKey } from '../crypto.js'\nimport { pbkdf2VerifyDigest, VDIG_ITERATIONS } from './digest.js'\nimport { normalizeForVerify, type VerifyNormalizeMode } from './normalize.js'\nimport { blindedEqual } from './compare.js'\nimport { evaluateKofN } from './kofn.js'\nimport { openVdigPayload, type VdigPayload } from './vdig.js'\nimport { dualReadSealedSlot } from '../record-keys/sealed-slot.js'\nimport { ClassifiedConfigError, ClassifiedVerifyError } from '../../errors.js'\nimport type { EncryptedEnvelope, VdigFieldPolicy, ClassifiedVerdict } from '../../types.js'\n\nexport interface VerifyEngineCtx {\n readonly collection: string\n getEnvelope(id: string): Promise<EncryptedEnvelope | null>\n resolveCek(env: EncryptedEnvelope): Promise<EnclaveKey | undefined>\n getDEK(): Promise<EnclaveKey>\n readonly now: () => number\n}\n\n/**\n * C4 pad: one full-cost dummy digest + one dummy compare; result discarded.\n * Pad at `iter` when the caller holds an AUTHENTICATED payload.iter, so pad\n * cost tracks the real compare cost across future iteration bumps; the\n * fully-absent paths (no payload to read an iter from) use VDIG_ITERATIONS.\n */\nasync function padOnce(iter: number = VDIG_ITERATIONS): Promise<void> {\n const dummy = await pbkdf2VerifyDigest('noydb-classify-c4-pad', generateSalt(), iter)\n await blindedEqual(dummy, dummy)\n}\n\nasync function padFalse(): Promise<ClassifiedVerdict> {\n await padOnce()\n return { ok: false }\n}\n\n/** R6 verify-side transition evidence — config bug, fail-loud, pad-exempt. */\nfunction refuseSealedResidue(collection: string, field: string): never {\n throw new ClassifiedConfigError(collection,\n `field \"${field}\" carries a recoverable _sealed slot but no _vdig — storage-form ` +\n `transition detected (R6); refused fail-loud, never an ok:false masquerading as wrong-password`)\n}\n\nexport async function verifyDigestField(\n ctx: VerifyEngineCtx,\n id: string,\n field: string,\n candidate: string,\n policy: VdigFieldPolicy,\n): Promise<ClassifiedVerdict> {\n const env = await ctx.getEnvelope(id)\n // #691: an elevated envelope (_tier > 0) is non-comparable through every\n // tier-0 door — its key material is tier-DEK-wrapped and must never be\n // resolved here (the elevating session's warm cekCache would otherwise\n // leak past the tier audit). Exactly the missing-record pad path, checked\n // BEFORE the R6 residue probe so tier-0 callers learn nothing.\n if (env === null || (env._tier ?? 0) > 0) return padFalse()\n const blob = env._vdig?.[field]\n if (blob === undefined) {\n if (env._sealed?.[field] !== undefined) refuseSealedResidue(ctx.collection, field)\n return padFalse()\n }\n const cek = await ctx.resolveCek(env)\n if (cek === undefined) return padFalse()\n\n let payload: VdigPayload\n try {\n payload = await openVdigPayload(blob, cek, ctx.collection, id, field)\n } catch {\n return padFalse() // AAD/tamper (C1) → padded false; no tamper oracle to the caller\n }\n\n const normalized = normalizeForVerify(policy.normalize, candidate)\n const digest = await pbkdf2VerifyDigest(normalized, base64ToBuffer(payload.cur.salt), payload.iter)\n const ok = await blindedEqual(digest, base64ToBuffer(payload.cur.hash))\n if (!ok) return { ok: false } // I1: bare false — mustRotate never computed here\n\n if (policy.rotateDays !== undefined\n && ctx.now() > Date.parse(payload.cur.at) + policy.rotateDays * 86_400_000) {\n // Disclosing write-age-vs-policy to a SUCCESSFUL verifier is intended (audit F6).\n return { ok: true, mustRotate: true }\n }\n return { ok: true }\n}\n\nexport async function verifyTextField(\n ctx: VerifyEngineCtx,\n id: string,\n field: string,\n candidate: string,\n normalize: VerifyNormalizeMode,\n): Promise<ClassifiedVerdict> {\n // C4, single code path: (a) attempt the unseal where a slot exists, then\n // for EVERY outcome — present-correct, present-wrong, missing record,\n // missing slot, tampered — (b) run the one constant-cost pad, (c) run one\n // blindedEqual, (d) return the boolean. Unlike the digest door there is no\n // real PBKDF2 here (unseal is sub-ms AES-GCM), so skipping the pad on the\n // real compare would make exists-but-wrong ~100x FASTER than absent — an\n // inverted existence oracle. No branch below skips the dominant cost.\n const env = await ctx.getEnvelope(id)\n const blob = env?._sealed?.[field]\n let stored: string | undefined\n if (env !== null && (env._tier ?? 0) === 0 && blob !== undefined) { // #691: elevated ≡ missing\n const cek = await ctx.resolveCek(env)\n const dek = await ctx.getDEK()\n try {\n // Plaintext exists microseconds inside this function; only the boolean leaves.\n stored = String(JSON.parse(await dualReadSealedSlot(blob, field, ctx.collection, cek, dek)))\n } catch {\n stored = undefined // AAD/tamper (C1) → same padded compare tail as absent\n }\n }\n await padOnce()\n const a = new TextEncoder().encode(normalizeForVerify(normalize, candidate))\n const b = stored !== undefined\n ? new TextEncoder().encode(normalizeForVerify(normalize, stored))\n : a // no-compare dummy comparand — the verdict is forced false below\n const eq = await blindedEqual(a, b)\n return { ok: stored !== undefined && eq }\n}\n\nexport async function matchGroupFields(\n ctx: VerifyEngineCtx,\n id: string,\n answers: Record<string, string>,\n members: ReadonlyArray<{ readonly field: string; readonly policy: VdigFieldPolicy }>,\n opts: { readonly min: number },\n): Promise<{ readonly passed: boolean }> {\n // I2 step 1 — validate EVERYTHING up front, before any PBKDF2, throwing\n // uniformly at ~0 elapsed (no member-position leak via timing/throw type).\n if (!Number.isInteger(opts.min) || opts.min < 1 || opts.min > members.length) {\n throw new ClassifiedVerifyError(ctx.collection, '*',\n `matchGroup min ${opts.min} out of range 1..${members.length}`)\n }\n const normalized = new Map<string, string>()\n for (const m of members) {\n const answer = answers[m.field] // non-member answer keys: silently ignored\n if (answer === undefined) continue\n if (typeof answer !== 'string') {\n throw new ClassifiedVerifyError(ctx.collection, m.field, 'candidate must be a string')\n }\n normalized.set(m.field, normalizeForVerify(m.policy.normalize, answer))\n }\n\n const fetched = await ctx.getEnvelope(id)\n // #691: elevated ≡ missing — nulling the view here also skips the R6\n // residue probe (a tier-0 caller must not learn an elevated record's\n // storage form) and forces the cek-undefined pad path for every member.\n const env = fetched === null || (fetched._tier ?? 0) > 0 ? null : fetched\n if (env !== null) {\n for (const m of members) { // R6 evidence — uniform, before any PBKDF2\n if (env._sealed?.[m.field] !== undefined && env._vdig?.[m.field] === undefined) {\n refuseSealedResidue(ctx.collection, m.field)\n }\n }\n }\n const cek = env !== null ? await ctx.resolveCek(env) : undefined\n\n // I2 step 2+3 — iterate RESOLVED GROUP MEMBERS (denominator = |members|),\n // evaluate EVERY member (collect, never break), pad every no-compare slot.\n const results: boolean[] = []\n for (const m of members) {\n const candidate = normalized.get(m.field)\n const blob = env?._vdig?.[m.field]\n if (env === null || cek === undefined || blob === undefined) {\n await padOnce()\n results.push(false)\n continue\n }\n try {\n const payload = await openVdigPayload(blob, cek, ctx.collection, id, m.field)\n if (candidate === undefined) {\n // Slot present, answer absent: the payload's authenticated iter is in\n // hand — pad at IT (not the constant) so a future iteration bump\n // cannot desync this pad's cost from the real compare's cost.\n await padOnce(payload.iter)\n results.push(false)\n continue\n }\n const digest = await pbkdf2VerifyDigest(candidate, base64ToBuffer(payload.cur.salt), payload.iter)\n results.push(await blindedEqual(digest, base64ToBuffer(payload.cur.hash)))\n } catch {\n await padOnce()\n results.push(false)\n }\n }\n // Per-member results never appear in any return, error, or audit payload.\n return { passed: evaluateKofN(results, opts.min) }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dumpVaultSchema
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FUJSIP4K.js";
|
|
4
4
|
import "./chunk-RXLYKVTF.js";
|
|
5
5
|
import "./chunk-VYYM3MEN.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-REDOOY2Q.js";
|
|
7
7
|
import "./chunk-Y2NBBT6K.js";
|
|
8
8
|
import "./chunk-WWNYSV7S.js";
|
|
9
9
|
import "./chunk-CIGAQBJR.js";
|
|
@@ -15,8 +15,7 @@ import "./chunk-LP7BEXCT.js";
|
|
|
15
15
|
import "./chunk-SL3JAAKN.js";
|
|
16
16
|
import "./chunk-N4CHCXDL.js";
|
|
17
17
|
import "./chunk-7O3Y4PPD.js";
|
|
18
|
-
import "./chunk-PZ5AY32C.js";
|
|
19
18
|
export {
|
|
20
19
|
dumpVaultSchema
|
|
21
20
|
};
|
|
22
|
-
//# sourceMappingURL=walk-
|
|
21
|
+
//# sourceMappingURL=walk-ERAJ5RHO.js.map
|
|
@@ -36,7 +36,12 @@ export declare class DerivationRegistry {
|
|
|
36
36
|
* own strategies rather than taken as a parameter — a field this
|
|
37
37
|
* collection's own derivations write via `denorm` is never a typo.
|
|
38
38
|
*/
|
|
39
|
-
validateFieldsFor(collectionName: string, schema: unknown, configKeys: ReadonlyArray<string
|
|
39
|
+
validateFieldsFor(collectionName: string, schema: unknown, configKeys: ReadonlyArray<string>, viaSources?: {
|
|
40
|
+
readonly computed?: Readonly<Record<string, unknown>> | undefined;
|
|
41
|
+
readonly viaFields?: Readonly<Record<string, unknown>> | undefined;
|
|
42
|
+
} | undefined): void;
|
|
43
|
+
/** #1266 — one message shape for both match sides; see {@link matchTargetFieldNames}. */
|
|
44
|
+
private _virtualMatchMessage;
|
|
40
45
|
/**
|
|
41
46
|
* All registered strategies as a flat, deduplicated array.
|
|
42
47
|
* Each strategy is indexed once per source (not once per output key),
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* @module
|
|
12
12
|
*/
|
|
13
13
|
import type { FieldMeta } from './field-meta.js';
|
|
14
|
-
import { FieldMetaUnknownFieldError } from './field-meta.js';
|
|
14
|
+
import { FieldMetaUnknownFieldError, schemaFieldKeys } from './field-meta.js';
|
|
15
|
+
export { schemaFieldKeys };
|
|
15
16
|
import type { CollectionMeta } from './meta.js';
|
|
16
17
|
import type { ViaDescriptor, ViaPosture } from '../../kernel/via/index.js';
|
|
17
18
|
import type { DictKeyDescriptor, StaticDictDescriptor } from '../../via/i18n/dictionary.js';
|
|
@@ -186,38 +187,6 @@ export interface ZodFieldSlot {
|
|
|
186
187
|
*
|
|
187
188
|
* No static zod import — all zod access is lazy via derivePersistedSchema.
|
|
188
189
|
*/
|
|
189
|
-
/**
|
|
190
|
-
* Field KEYS from a configured validator, synchronously — the complement to
|
|
191
|
-
* {@link deriveZodFields}, which derives field TYPES and must be async.
|
|
192
|
-
*
|
|
193
|
-
* The distinction is the whole point (#1253). `describe()`'s sync path passed
|
|
194
|
-
* `zodFields: undefined`, so `fieldMeta` key-validation could never run there
|
|
195
|
-
* and a typo'd key became a PHANTOM FIELD carrying its declared `sensitivity`
|
|
196
|
-
* while the real field went undescribed — an inventory wrong in both
|
|
197
|
-
* directions, silently, on the surface `sensitivity` exists to serve.
|
|
198
|
-
*
|
|
199
|
-
* That was read as unavoidable ("the sync path cannot know the schema's
|
|
200
|
-
* fields"), which is true of types and false of keys: a Zod object exposes
|
|
201
|
-
* `.shape` directly, on both v3 and v4, with no JSON-Schema derivation and no
|
|
202
|
-
* `zod-to-json-schema` peer. The latter matters — on Zod 3 that peer is
|
|
203
|
-
* required for the async path, so the sync path is the only one some
|
|
204
|
-
* consumers can reach.
|
|
205
|
-
*
|
|
206
|
-
* A duck-typed probe, not a Zod dependency: an unrecognised validator returns
|
|
207
|
-
* `undefined` and the caller stays silent rather than guessing, so a validator
|
|
208
|
-
* hub cannot read never produces a false "unknown field" error.
|
|
209
|
-
*
|
|
210
|
-
* ZodEffects (reported by the pilot on #1249's guard, and it equally affected
|
|
211
|
-
* #1253's): on Zod 3 an OBJECT-LEVEL `.refine()` wraps the object in a
|
|
212
|
-
* ZodEffects whose `.shape` is undefined — the object sits at `_def.schema` —
|
|
213
|
-
* so "has a schema" and "hub can enumerate its fields" silently diverged for
|
|
214
|
-
* exactly the schemas most worth guarding (validated ones). Unwrapped here,
|
|
215
|
-
* but ONLY through refinement effects: a `.transform()`/`.preprocess()`
|
|
216
|
-
* changes the OUTPUT shape, so its inner keys would be a lie — those stay
|
|
217
|
-
* `undefined` (silent), deliberately. Zod 4 keeps `.shape` through `.refine()`
|
|
218
|
-
* and never enters the loop. Bounded depth, so a cyclic duck cannot hang us.
|
|
219
|
-
*/
|
|
220
|
-
export declare function schemaFieldKeys(schema: unknown): readonly string[] | undefined;
|
|
221
190
|
export declare function deriveZodFields(schema: unknown): Promise<Record<string, ZodFieldSlot>>;
|
|
222
191
|
/**
|
|
223
192
|
* Resolve the persisted-schema `fieldIds` map for `describeAsync()`'s
|
|
@@ -59,3 +59,89 @@ export interface ResolvedMeta extends Partial<FieldMeta> {
|
|
|
59
59
|
export declare function humanizeFieldKey(key: string): string;
|
|
60
60
|
/** Merge one field's metadata: channel > zodMeta > inferred; label always present. */
|
|
61
61
|
export declare function resolveFieldMeta(key: string, inputs: MergeInputs): ResolvedMeta;
|
|
62
|
+
/**
|
|
63
|
+
* Field KEYS from a configured validator, synchronously — the complement to
|
|
64
|
+
* {@link deriveZodFields}, which derives field TYPES and must be async.
|
|
65
|
+
*
|
|
66
|
+
* The distinction is the whole point (#1253). `describe()`'s sync path passed
|
|
67
|
+
* `zodFields: undefined`, so `fieldMeta` key-validation could never run there
|
|
68
|
+
* and a typo'd key became a PHANTOM FIELD carrying its declared `sensitivity`
|
|
69
|
+
* while the real field went undescribed — an inventory wrong in both
|
|
70
|
+
* directions, silently, on the surface `sensitivity` exists to serve.
|
|
71
|
+
*
|
|
72
|
+
* That was read as unavoidable ("the sync path cannot know the schema's
|
|
73
|
+
* fields"), which is true of types and false of keys: a Zod object exposes
|
|
74
|
+
* `.shape` directly, on both v3 and v4, with no JSON-Schema derivation and no
|
|
75
|
+
* `zod-to-json-schema` peer. The latter matters — on Zod 3 that peer is
|
|
76
|
+
* required for the async path, so the sync path is the only one some
|
|
77
|
+
* consumers can reach.
|
|
78
|
+
*
|
|
79
|
+
* A duck-typed probe, not a Zod dependency: an unrecognised validator returns
|
|
80
|
+
* `undefined` and the caller stays silent rather than guessing, so a validator
|
|
81
|
+
* hub cannot read never produces a false "unknown field" error.
|
|
82
|
+
*
|
|
83
|
+
* Wrappers (reported by the pilot on #1249's guard, and it equally affected
|
|
84
|
+
* #1253's): a wrapped object's `.shape` is undefined, so "has a schema" and
|
|
85
|
+
* "hub can enumerate its fields" silently diverged for exactly the schemas
|
|
86
|
+
* most worth guarding — validated ones.
|
|
87
|
+
*
|
|
88
|
+
* ONE RULE UNWRAPS ALL OF THEM: FOLLOW THE OUTPUT SIDE. These keys describe
|
|
89
|
+
* the PARSED record, so the only question a wrapper raises is whether it
|
|
90
|
+
* changes the parsed shape.
|
|
91
|
+
*
|
|
92
|
+
* Zod 3 `ZodEffects` — `.refine()`/`.superRefine()` (`effect.type` is
|
|
93
|
+
* `'refinement'`) and `z.preprocess()` (`'preprocess'`) both parse WITH
|
|
94
|
+
* the inner schema, so the output is the inner object: follow
|
|
95
|
+
* `_def.schema`. `.transform()` REPLACES the output, so the inner keys
|
|
96
|
+
* would be a lie: stay `undefined`, deliberately.
|
|
97
|
+
* Zod 4 `ZodPipe` — `z.preprocess()` is `pipe(transform -> object)` and
|
|
98
|
+
* `.transform()` is `pipe(object -> transform)`. Following `_def.out`
|
|
99
|
+
* resolves both correctly with no effect-kind test: preprocess reaches the
|
|
100
|
+
* object, transform reaches a `ZodTransform` that has no shape and no
|
|
101
|
+
* inner schema, so the loop returns `undefined` on its own.
|
|
102
|
+
*
|
|
103
|
+
* `preprocess` was the pilot's second finding (#1262): the first fix followed
|
|
104
|
+
* refinements only, which left four of their registered collections unguarded
|
|
105
|
+
* — including the one carrying their only `fieldMeta` PII declaration, i.e.
|
|
106
|
+
* precisely the collection #1253 was written for. It is one string away from
|
|
107
|
+
* `transform` and means the opposite thing, so a test pins the two apart.
|
|
108
|
+
*
|
|
109
|
+
* A duck-typed probe throughout: an unrecognised validator returns `undefined`
|
|
110
|
+
* and the caller stays silent rather than guessing. Bounded depth, so a cyclic
|
|
111
|
+
* duck cannot hang us.
|
|
112
|
+
*/
|
|
113
|
+
export declare function schemaFieldKeys(schema: unknown): readonly string[] | undefined;
|
|
114
|
+
/**
|
|
115
|
+
* Registration-time `fieldMeta` key check (#1253 follow-up, the "refuse as early
|
|
116
|
+
* as the information allows" pass).
|
|
117
|
+
*
|
|
118
|
+
* The same validation `buildDescription` runs, hoisted to collection
|
|
119
|
+
* construction so a typo is refused when it is DECLARED rather than when
|
|
120
|
+
* someone happens to call `describe()`. A collection nobody describes was
|
|
121
|
+
* previously never checked at all — and `fieldMeta` is what carries
|
|
122
|
+
* `sensitivity`, so the un-checked case was the one with a data-classification
|
|
123
|
+
* inventory hanging off it.
|
|
124
|
+
*
|
|
125
|
+
* Deliberately NOT a replacement for the check inside `buildDescription`. There
|
|
126
|
+
* are three tiers of knowability, not two:
|
|
127
|
+
*
|
|
128
|
+
* always — a name declared in the config itself (see the virtual-field
|
|
129
|
+
* refusal in the derivation registry: "declared and never
|
|
130
|
+
* stored" needs no schema at all)
|
|
131
|
+
* at reg. — a validator whose `.shape` is readable synchronously: THIS
|
|
132
|
+
* at describe — a validator whose fields only exist after async JSON-Schema
|
|
133
|
+
* derivation (Zod 3 + `zod-to-json-schema`)
|
|
134
|
+
*
|
|
135
|
+
* The third tier is why `buildDescription` keeps its own call. Hoisting alone
|
|
136
|
+
* would move the check earlier for some collections and REMOVE it for others.
|
|
137
|
+
*
|
|
138
|
+
* Silent when the field set cannot be enumerated, exactly as the describe-time
|
|
139
|
+
* check is: a TS-generic collection names real fields that appear in no runtime
|
|
140
|
+
* config, and rejecting those teaches people to stop declaring `fieldMeta`.
|
|
141
|
+
*/
|
|
142
|
+
export declare function validateFieldMetaAtRegistration(input: {
|
|
143
|
+
readonly collection: string;
|
|
144
|
+
readonly schema?: unknown;
|
|
145
|
+
readonly fieldMeta?: Record<string, unknown> | undefined;
|
|
146
|
+
readonly configKeys: readonly string[];
|
|
147
|
+
}): void;
|
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
freezeSnapshotOnly,
|
|
5
5
|
randomId,
|
|
6
6
|
withdrawAccessibleData
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-TGPST6FK.js";
|
|
8
|
+
import "./chunk-EKZ6VZYF.js";
|
|
9
|
+
import "./chunk-NLOLF6LD.js";
|
|
10
10
|
import "./chunk-MLO75SFJ.js";
|
|
11
11
|
import "./chunk-CIGAQBJR.js";
|
|
12
12
|
import "./chunk-4RUFYGWG.js";
|
|
@@ -17,7 +17,6 @@ import "./chunk-LP7BEXCT.js";
|
|
|
17
17
|
import "./chunk-SL3JAAKN.js";
|
|
18
18
|
import "./chunk-N4CHCXDL.js";
|
|
19
19
|
import "./chunk-7O3Y4PPD.js";
|
|
20
|
-
import "./chunk-PZ5AY32C.js";
|
|
21
20
|
export {
|
|
22
21
|
FROZEN_SNAPSHOTS_COLLECTION,
|
|
23
22
|
freezeAndDeleteClosure,
|
|
@@ -25,4 +24,4 @@ export {
|
|
|
25
24
|
randomId,
|
|
26
25
|
withdrawAccessibleData
|
|
27
26
|
};
|
|
28
|
-
//# sourceMappingURL=withdraw-accessible-
|
|
27
|
+
//# sourceMappingURL=withdraw-accessible-42D32Z7G.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noy-db/hub",
|
|
3
|
-
"version": "0.7.0-pre.
|
|
3
|
+
"version": "0.7.0-pre.14",
|
|
4
4
|
"description": "Zero-knowledge, offline-first, encrypted document store — core library with AES-256-GCM, PBKDF2, multi-user keyring, and sync engine",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vLannaAi <vicio@lanna.ai>",
|
|
@@ -233,19 +233,23 @@
|
|
|
233
233
|
"node": ">=22.0.0"
|
|
234
234
|
},
|
|
235
235
|
"dependencies": {
|
|
236
|
-
"@noy-db/attestation": "0.7.0-pre.
|
|
236
|
+
"@noy-db/attestation": "0.7.0-pre.14"
|
|
237
237
|
},
|
|
238
238
|
"devDependencies": {
|
|
239
239
|
"@types/node": "^22.0.0",
|
|
240
240
|
"esbuild": "^0.25.0",
|
|
241
241
|
"zod": "^4.0.0",
|
|
242
242
|
"zod-to-json-schema": "^3.25.2",
|
|
243
|
-
"@noy-db/on-shamir": "0.7.0-pre.
|
|
243
|
+
"@noy-db/on-shamir": "0.7.0-pre.14"
|
|
244
244
|
},
|
|
245
245
|
"peerDependencies": {
|
|
246
|
+
"zod": "^4.0.0",
|
|
246
247
|
"zod-to-json-schema": "^3.25.0"
|
|
247
248
|
},
|
|
248
249
|
"peerDependenciesMeta": {
|
|
250
|
+
"zod": {
|
|
251
|
+
"optional": true
|
|
252
|
+
},
|
|
249
253
|
"zod-to-json-schema": {
|
|
250
254
|
"optional": true
|
|
251
255
|
}
|