@noy-db/hub 0.3.0-pre.11 → 0.3.0-pre.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aggregate/index.js +4 -4
- package/dist/api-ZGS6LCOF.js +20 -0
- package/dist/as/index.js +11 -11
- package/dist/attestation/index.js +18 -18
- package/dist/{backup-XA5STIYQ.js → backup-BJ57IKAO.js} +28 -11
- package/dist/backup-BJ57IKAO.js.map +1 -0
- package/dist/blobs/index.js +12 -10
- package/dist/blobs/index.js.map +1 -1
- package/dist/broker/index.js +12 -12
- package/dist/bundle/index.js +25 -22
- package/dist/bundle/index.js.map +1 -1
- package/dist/by/index.js +2 -2
- package/dist/cargo/index.js +22 -22
- package/dist/{chunk-WGV4CUZT.js → chunk-2NVOWNI3.js} +4 -4
- package/dist/{chunk-WGV4CUZT.js.map → chunk-2NVOWNI3.js.map} +1 -1
- package/dist/{chunk-OFJ5GPNA.js → chunk-3B5VVCNY.js} +2 -2
- package/dist/{chunk-KPGX66PM.js → chunk-3LQ2YKO4.js} +2 -2
- package/dist/{chunk-D26JB3LZ.js → chunk-3QCRBHCY.js} +2 -2
- package/dist/{chunk-H6CZSDWK.js → chunk-454JXE57.js} +3 -3
- package/dist/{chunk-H6CZSDWK.js.map → chunk-454JXE57.js.map} +1 -1
- package/dist/{chunk-DGH65E7Q.js → chunk-4CTHCN4J.js} +24 -10
- package/dist/chunk-4CTHCN4J.js.map +1 -0
- package/dist/{chunk-4N5QFSNQ.js → chunk-4JBYV75E.js} +5 -5
- package/dist/{chunk-UTLEAJXC.js → chunk-4NXKCLLQ.js} +2 -2
- package/dist/{chunk-ARSAMO24.js → chunk-4VNQOLML.js} +2 -2
- package/dist/{chunk-X6CMGUAG.js → chunk-5M5DVGV7.js} +2 -2
- package/dist/{chunk-DJ3VKYFA.js → chunk-5O3XF4DV.js} +4 -4
- package/dist/chunk-5O3XF4DV.js.map +1 -0
- package/dist/{chunk-XHV25KBM.js → chunk-5YOLBEQ6.js} +2 -2
- package/dist/{chunk-CB25ONKC.js → chunk-6CRIV7DE.js} +2 -2
- package/dist/{chunk-BE7N3AXA.js → chunk-6JTIAGHD.js} +3 -3
- package/dist/{chunk-JOQ53DMI.js → chunk-6QTNZJBS.js} +3 -3
- package/dist/{chunk-BZO4WCJI.js → chunk-6T2SCQRU.js} +4 -4
- package/dist/{chunk-FQQF23MC.js → chunk-7DSTRQIJ.js} +2 -2
- package/dist/{chunk-IP2QBAGS.js → chunk-AUSTCW2X.js} +10 -10
- package/dist/{chunk-A2Y6YFVN.js → chunk-B3HJYUOF.js} +2 -2
- package/dist/{chunk-SRVQHVNX.js → chunk-BDVFPG6Y.js} +5 -5
- package/dist/{chunk-FH542DDY.js → chunk-BIOUI4HL.js} +50 -1
- package/dist/chunk-BIOUI4HL.js.map +1 -0
- package/dist/{chunk-YXOUQ2H7.js → chunk-CAF6WOK7.js} +2 -2
- package/dist/{chunk-H6HM6CVR.js → chunk-CDUQTA3Z.js} +114 -12
- package/dist/chunk-CDUQTA3Z.js.map +1 -0
- package/dist/{chunk-HUS3J5WM.js → chunk-CIE2BASH.js} +2 -2
- package/dist/{chunk-DIRRS7WP.js → chunk-CRI4OUV4.js} +2 -2
- package/dist/{chunk-FXK4X4BL.js → chunk-DLG74CNB.js} +4 -4
- package/dist/{chunk-WTUNZACT.js → chunk-E5ZQT5KX.js} +2 -2
- package/dist/{chunk-DMFNPHCX.js → chunk-EQO2UVP4.js} +6 -6
- package/dist/{chunk-MCEKIAO4.js → chunk-ERJ7FU55.js} +3 -3
- package/dist/{chunk-6QILBDZU.js → chunk-EVVGJDRQ.js} +3 -3
- package/dist/{chunk-7VSGW5KJ.js → chunk-EVX47VBM.js} +2 -2
- package/dist/{chunk-D3EFFFOL.js → chunk-EXK7QRWF.js} +5 -5
- package/dist/{chunk-5JB73NMC.js → chunk-EYZ6IQ6X.js} +5 -5
- package/dist/{chunk-BCYQA5BK.js → chunk-F64IRYG3.js} +2 -2
- package/dist/{chunk-K62IUKOD.js → chunk-FHS2HQSH.js} +5 -5
- package/dist/{chunk-2JMMY7HZ.js → chunk-FM3G3NOY.js} +27 -9
- package/dist/chunk-FM3G3NOY.js.map +1 -0
- package/dist/{chunk-67TNU77I.js → chunk-FUG4SFN3.js} +2 -2
- package/dist/{chunk-ZP5PL65C.js → chunk-GKEWXIV5.js} +2 -2
- package/dist/{chunk-RKRGDAS7.js → chunk-GTGPO5KW.js} +5 -5
- package/dist/{chunk-MSPNM2OM.js → chunk-HB3RFQSC.js} +3 -3
- package/dist/{chunk-W5D62AQD.js → chunk-HDMEGAQE.js} +20 -7
- package/dist/chunk-HDMEGAQE.js.map +1 -0
- package/dist/chunk-HDQIIUPH.js +135 -0
- package/dist/chunk-HDQIIUPH.js.map +1 -0
- package/dist/{chunk-7QIXUSDQ.js → chunk-HTJQYHZP.js} +15 -12
- package/dist/chunk-HTJQYHZP.js.map +1 -0
- package/dist/{chunk-OOLO4LQG.js → chunk-I6GALP6E.js} +6 -6
- package/dist/{chunk-57V5S7PW.js → chunk-IL6POWCW.js} +6 -6
- package/dist/{chunk-7NS43EXZ.js → chunk-IU6SNYX6.js} +5 -5
- package/dist/{chunk-5ZOSG3RJ.js → chunk-J2ZX2XUV.js} +5 -5
- package/dist/{chunk-L2SNBIV7.js → chunk-JIF24JPM.js} +57 -15
- package/dist/chunk-JIF24JPM.js.map +1 -0
- package/dist/{chunk-SYMS4546.js → chunk-JKJOGPZN.js} +2 -2
- package/dist/{chunk-CKZW6P5V.js → chunk-JNAI2HN2.js} +1 -1
- package/dist/chunk-JNAI2HN2.js.map +1 -0
- package/dist/{chunk-AWB5JZW6.js → chunk-JZUV47FZ.js} +2 -2
- package/dist/{chunk-ODJWBC5I.js → chunk-K5IMOGHB.js} +2 -2
- package/dist/{chunk-3G4KMVUN.js → chunk-KSKBRKGJ.js} +3 -3
- package/dist/{chunk-HFJIGWJC.js → chunk-LASZKVMO.js} +3 -3
- package/dist/{chunk-IGJHJYM6.js → chunk-LXEXBTKL.js} +4 -4
- package/dist/{chunk-3CNQ7FQ3.js → chunk-M4CV6BRT.js} +119 -25
- package/dist/chunk-M4CV6BRT.js.map +1 -0
- package/dist/{chunk-H44ZE4IE.js → chunk-MEFJPTZ4.js} +33 -17
- package/dist/chunk-MEFJPTZ4.js.map +1 -0
- package/dist/{chunk-PR5LC5YX.js → chunk-MV43JSJF.js} +4 -4
- package/dist/{chunk-ELNBMFOS.js → chunk-NJ4EOKMB.js} +4 -4
- package/dist/{chunk-PXWVF7MC.js → chunk-NR5LT5RY.js} +7 -7
- package/dist/{chunk-QYPKGVT4.js → chunk-NWSGI5C7.js} +3 -3
- package/dist/{chunk-P5RY4ZWD.js → chunk-OSDB5WW3.js} +92 -15
- package/dist/chunk-OSDB5WW3.js.map +1 -0
- package/dist/chunk-PN2XMATY.js +37 -0
- package/dist/chunk-PN2XMATY.js.map +1 -0
- package/dist/{chunk-25NDNMQ7.js → chunk-Q5VNKNE5.js} +3 -3
- package/dist/{chunk-CIKY6I4W.js → chunk-QLWMEXCT.js} +2 -2
- package/dist/{chunk-VA4ZWLFY.js → chunk-QRB7NAOJ.js} +2 -2
- package/dist/{chunk-3MSQLQNZ.js → chunk-R4ROFEYC.js} +2 -2
- package/dist/{chunk-BEIRE5K4.js → chunk-RCVJZMFE.js} +4 -4
- package/dist/{chunk-VR24ILCR.js → chunk-RIX4V2TB.js} +3 -3
- package/dist/{chunk-Q3XSMOZM.js → chunk-RYCTDFUX.js} +2 -2
- package/dist/{chunk-7E67FJ33.js → chunk-RZ5SVOYR.js} +2 -2
- package/dist/{chunk-IDIV5NVH.js → chunk-SLFHQTTW.js} +5 -5
- package/dist/chunk-SNZ6URZZ.js +2782 -0
- package/dist/chunk-SNZ6URZZ.js.map +1 -0
- package/dist/{chunk-C3YH34IL.js → chunk-T2FFWNPK.js} +3 -3
- package/dist/{chunk-O2DYPAZY.js → chunk-UNFPPVNI.js} +2 -2
- package/dist/{chunk-MELR46EF.js → chunk-VFUPHI2Z.js} +63 -10
- package/dist/chunk-VFUPHI2Z.js.map +1 -0
- package/dist/{chunk-GA2QHD6P.js → chunk-VLFCJYTL.js} +9 -9
- package/dist/{chunk-YSXEFGND.js → chunk-VN3DBQAU.js} +2 -2
- package/dist/{chunk-C7NTK5I3.js → chunk-VZRODIM3.js} +4 -4
- package/dist/{chunk-QLLKLFE2.js → chunk-W22IKJE2.js} +2 -2
- package/dist/{chunk-5XIPOCQC.js → chunk-X3YYQWOQ.js} +3 -3
- package/dist/{chunk-A2QF4JUE.js → chunk-X7QBD67R.js} +625 -293
- package/dist/chunk-X7QBD67R.js.map +1 -0
- package/dist/{chunk-QQJXBZCO.js → chunk-XDYREZWE.js} +82 -10
- package/dist/chunk-XDYREZWE.js.map +1 -0
- package/dist/{chunk-OEAATWH5.js → chunk-XWZVFISC.js} +40 -6
- package/dist/{chunk-OEAATWH5.js.map → chunk-XWZVFISC.js.map} +1 -1
- package/dist/{chunk-XFXUTFWQ.js → chunk-YFXZEAYK.js} +6 -6
- package/dist/{chunk-7ITPJFGC.js → chunk-ZAJAVEXY.js} +2 -2
- package/dist/{chunk-4R4Q64W5.js → chunk-ZICD77GM.js} +2 -2
- package/dist/{chunk-45ZZLTFH.js → chunk-ZJ2AK4V7.js} +2 -2
- package/dist/{chunk-UFILRICG.js → chunk-ZUODRVHY.js} +2 -2
- package/dist/classified/index.js +5 -5
- package/dist/{classified-marker-LQWWQNIJ.js → classified-marker-CZKA6ITE.js} +8 -4
- package/dist/classified-marker-CZKA6ITE.js.map +1 -0
- package/dist/collection-facade-TLWIPI76.js +44 -0
- package/dist/{computed-W6MVAMWZ.js → computed-ZIQTYTDI.js} +3 -3
- package/dist/consent/index.js +9 -9
- package/dist/{dead-filter-4KXZEB3X.js → dead-filter-ESOBZZP5.js} +2 -2
- package/dist/delegation-W7ZV2322.js +25 -0
- package/dist/derivations/index.js +12 -12
- package/dist/derive-H2BJTLUI.js +21 -0
- package/dist/{enclave-VXAQD4XJ.js → enclave-AP3I5W7O.js} +15 -9
- package/dist/executor-KGWTNEQD.js +9 -0
- package/dist/executor-OVEXR6RT.js +9 -0
- package/dist/executor-UNEH5ZYH.js +25 -0
- package/dist/export-accessible-PCMXSUZS.js +23 -0
- package/dist/extract-partition-SWHFSYJO.js +38 -0
- package/dist/{fanout-sidecar-CKYFH33E.js → fanout-sidecar-A765THMX.js} +9 -9
- package/dist/find-N6345CNT.js +11 -0
- package/dist/forget/index.js +9 -9
- package/dist/guards/index.js +3 -3
- package/dist/history/index.js +14 -11
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.js +14 -14
- package/dist/index.d.ts +5 -1
- package/dist/index.js +108 -89
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +5 -5
- package/dist/indexing/index.js.map +1 -1
- package/dist/issue-5OKB6I2R.js +19 -0
- package/dist/kernel/best-effort-revert.d.ts +52 -0
- package/dist/kernel/collection-config.d.ts +28 -0
- package/dist/kernel/collection.d.ts +49 -32
- package/dist/kernel/enclave/classify/verify.d.ts +3 -0
- package/dist/kernel/enclave/index.d.ts +2 -1
- package/dist/kernel/enclave/record-keys/deterministic.d.ts +1 -0
- package/dist/kernel/enclave/record-keys/lifecycle.d.ts +50 -0
- package/dist/kernel/enclave/record-keys/record-codec.d.ts +27 -0
- package/dist/kernel/errors.d.ts +75 -1
- package/dist/kernel/lazy-count.d.ts +13 -0
- package/dist/kernel/noydb.d.ts +8 -0
- package/dist/kernel/tier-visibility.d.ts +54 -0
- package/dist/kernel/types.d.ts +92 -0
- package/dist/kernel/vault.d.ts +23 -6
- package/dist/kernel/via/dispatch.d.ts +10 -0
- package/dist/kernel/via/graph.d.ts +2 -1
- package/dist/{ledger-FU6GH5EH.js → ledger-GFBKVPV4.js} +11 -11
- package/dist/legacy/bundle.d.ts +1 -1
- package/dist/liberate-3VDOT4KO.js +24 -0
- package/dist/{link-set-MNADSH5M.js → link-set-Z7Q2ASFX.js} +11 -11
- package/dist/materialized-views/index.js +18 -18
- package/dist/noydb-ZCJOYBDI.js +68 -0
- package/dist/on/index.js +9 -9
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +12 -12
- package/dist/pod/index.js +11 -11
- package/dist/{policy-JTFOPPUU.js → policy-Z3RUAIY2.js} +5 -5
- package/dist/port/with/blob-strategy.d.ts +17 -0
- package/dist/port/with/lookup-strategy.d.ts +2 -1
- package/dist/portability/index.js +8 -8
- package/dist/{post-register-HAWO3O7O.js → post-register-JTH76BLY.js} +12 -7
- package/dist/post-register-JTH76BLY.js.map +1 -0
- package/dist/{public-envelope-NJVB7LPA.js → public-envelope-B4KNWVUA.js} +4 -4
- package/dist/query/index.js +5 -5
- package/dist/register-VAHONCID.js +23 -0
- package/dist/registry-7MLVD3VO.js +19 -0
- package/dist/registry-EPKPD5SM.js +18 -0
- package/dist/registry-P5T7PS2U.js +9 -0
- package/dist/request-withdrawal-TI3A5WPS.js +31 -0
- package/dist/{reveal-BOY7XYFH.js → reveal-DIBHLUZX.js} +7 -7
- package/dist/reveal-DIBHLUZX.js.map +1 -0
- package/dist/revoke-FDRD6GRW.js +24 -0
- package/dist/satellites/index.js +1 -1
- package/dist/sealed-record/index.js +12 -12
- package/dist/{seed-4RRXT42N.js → seed-Q6M2IZFL.js} +11 -11
- package/dist/session/index.js +9 -9
- package/dist/shadow/index.js +2 -2
- package/dist/signer-MUWEIUI6.js +25 -0
- package/dist/snapshots/index.js +10 -10
- package/dist/stale-OSQ7J3AA.js +29 -0
- package/dist/storage-MVFCGEQE.js +23 -0
- package/dist/{store-coordination-provider-HPYVFJCV.js → store-coordination-provider-64OEZFGS.js} +3 -3
- package/dist/sync/index.js +9 -9
- package/dist/team/index.js +15 -15
- package/dist/tiers/index.js +14 -10
- package/dist/to/index.js +1 -1
- package/dist/tx/index.js +3 -3
- package/dist/util/index.js +1 -1
- package/dist/{verify-66RHOW7N.js → verify-ZASTRNPR.js} +9 -8
- package/dist/verify-ZASTRNPR.js.map +1 -0
- package/dist/via/lookup/registry.d.ts +9 -0
- package/dist/via/money/where.d.ts +27 -6
- package/dist/{walk-2RWEOCAS.js → walk-SVNDIZ62.js} +11 -11
- package/dist/with/index.js +11 -11
- package/dist/with-audit/forget/strategy.d.ts +22 -0
- package/dist/with-audit/tiers/index.d.ts +275 -3
- package/dist/with-audit/tiers/strategy.d.ts +5 -4
- package/dist/with-cargo/describe-extraction.d.ts +9 -1
- package/dist/with-cargo/extract-partition.d.ts +9 -1
- package/dist/with-cargo/walk-closure.d.ts +32 -3
- package/dist/with-commit/history/history.d.ts +46 -0
- package/dist/with-commit/history/ledger/store.d.ts +21 -0
- package/dist/with-commit/history/strategy.d.ts +10 -0
- package/dist/with-commit/history/time-machine.d.ts +23 -0
- package/dist/with-commit/tx/elevated-handle.d.ts +2 -1
- package/dist/with-commit/tx/transaction.d.ts +5 -0
- package/dist/with-formula/materialized-views/executor.d.ts +9 -0
- package/dist/with-formula/materialized-views/stale.d.ts +52 -1
- package/dist/with-lookup/embeddings/index.d.ts +1 -0
- package/dist/with-lookup/embeddings/vec-id.d.ts +47 -0
- package/dist/with-lookup/indexing/collection-facade.d.ts +38 -1
- package/dist/with-lookup/indexing/lazy-builder.d.ts +35 -2
- package/dist/with-lookup/indexing/persisted-indexes.d.ts +44 -1
- package/dist/with-lookup/search/collection-facade.d.ts +40 -0
- package/dist/with-lookup/search/persisted-index-store.d.ts +82 -2
- package/dist/with-lookup/search/strategy.d.ts +4 -0
- package/dist/with-party/team/sync.d.ts +9 -0
- package/dist/with-shape/blobs/blob-intent.d.ts +153 -0
- package/dist/with-shape/blobs/blob-set.d.ts +949 -5
- package/dist/with-shape/satellites/fanout.d.ts +5 -4
- package/dist/with-shape/satellites/migrate-cek.d.ts +26 -0
- package/dist/with-shape/satellites/types.d.ts +11 -0
- package/dist/withdraw-accessible-WZIEVWSR.js +28 -0
- package/package.json +3 -3
- package/dist/api-YGMINAET.js +0 -20
- package/dist/backup-XA5STIYQ.js.map +0 -1
- package/dist/chunk-2JMMY7HZ.js.map +0 -1
- package/dist/chunk-3CNQ7FQ3.js.map +0 -1
- package/dist/chunk-7QIXUSDQ.js.map +0 -1
- package/dist/chunk-A2QF4JUE.js.map +0 -1
- package/dist/chunk-AOVDSIL3.js +0 -1221
- package/dist/chunk-AOVDSIL3.js.map +0 -1
- package/dist/chunk-CKZW6P5V.js.map +0 -1
- package/dist/chunk-DGH65E7Q.js.map +0 -1
- package/dist/chunk-DJ3VKYFA.js.map +0 -1
- package/dist/chunk-FH542DDY.js.map +0 -1
- package/dist/chunk-H44ZE4IE.js.map +0 -1
- package/dist/chunk-H6HM6CVR.js.map +0 -1
- package/dist/chunk-L2SNBIV7.js.map +0 -1
- package/dist/chunk-MELR46EF.js.map +0 -1
- package/dist/chunk-P5RY4ZWD.js.map +0 -1
- package/dist/chunk-QQJXBZCO.js.map +0 -1
- package/dist/chunk-TPH5EO6L.js +0 -54
- package/dist/chunk-TPH5EO6L.js.map +0 -1
- package/dist/chunk-W5D62AQD.js.map +0 -1
- package/dist/classified-marker-LQWWQNIJ.js.map +0 -1
- package/dist/collection-facade-YE2O4J4O.js +0 -39
- package/dist/delegation-OPUU3R2E.js +0 -25
- package/dist/derive-BGUHU663.js +0 -21
- package/dist/executor-GTVSUUBX.js +0 -25
- package/dist/executor-GZWMTOXB.js +0 -9
- package/dist/executor-XXGS4S3K.js +0 -9
- package/dist/export-accessible-BMULIOYB.js +0 -23
- package/dist/extract-partition-JUQTWDXR.js +0 -36
- package/dist/find-XRGCNGNN.js +0 -11
- package/dist/issue-UB6AA5M6.js +0 -19
- package/dist/liberate-YUN7W5QG.js +0 -24
- package/dist/noydb-6JLBZU4U.js +0 -67
- package/dist/post-register-HAWO3O7O.js.map +0 -1
- package/dist/register-VCKOBWKT.js +0 -23
- package/dist/registry-72UKJ2L4.js +0 -9
- package/dist/registry-BZFLJNEH.js +0 -18
- package/dist/registry-YJEYSYI5.js +0 -19
- package/dist/request-withdrawal-6MJRMFHF.js +0 -31
- package/dist/reveal-BOY7XYFH.js.map +0 -1
- package/dist/revoke-4KUNSVJC.js +0 -24
- package/dist/signer-DDDXP5JD.js +0 -25
- package/dist/stale-NXPT56JA.js +0 -14
- package/dist/storage-IR6EEAV2.js +0 -23
- package/dist/verify-66RHOW7N.js.map +0 -1
- package/dist/withdraw-accessible-CLMZEHQT.js +0 -28
- /package/dist/{api-YGMINAET.js.map → api-ZGS6LCOF.js.map} +0 -0
- /package/dist/{chunk-OFJ5GPNA.js.map → chunk-3B5VVCNY.js.map} +0 -0
- /package/dist/{chunk-KPGX66PM.js.map → chunk-3LQ2YKO4.js.map} +0 -0
- /package/dist/{chunk-D26JB3LZ.js.map → chunk-3QCRBHCY.js.map} +0 -0
- /package/dist/{chunk-4N5QFSNQ.js.map → chunk-4JBYV75E.js.map} +0 -0
- /package/dist/{chunk-UTLEAJXC.js.map → chunk-4NXKCLLQ.js.map} +0 -0
- /package/dist/{chunk-ARSAMO24.js.map → chunk-4VNQOLML.js.map} +0 -0
- /package/dist/{chunk-X6CMGUAG.js.map → chunk-5M5DVGV7.js.map} +0 -0
- /package/dist/{chunk-XHV25KBM.js.map → chunk-5YOLBEQ6.js.map} +0 -0
- /package/dist/{chunk-CB25ONKC.js.map → chunk-6CRIV7DE.js.map} +0 -0
- /package/dist/{chunk-BE7N3AXA.js.map → chunk-6JTIAGHD.js.map} +0 -0
- /package/dist/{chunk-JOQ53DMI.js.map → chunk-6QTNZJBS.js.map} +0 -0
- /package/dist/{chunk-BZO4WCJI.js.map → chunk-6T2SCQRU.js.map} +0 -0
- /package/dist/{chunk-FQQF23MC.js.map → chunk-7DSTRQIJ.js.map} +0 -0
- /package/dist/{chunk-IP2QBAGS.js.map → chunk-AUSTCW2X.js.map} +0 -0
- /package/dist/{chunk-A2Y6YFVN.js.map → chunk-B3HJYUOF.js.map} +0 -0
- /package/dist/{chunk-SRVQHVNX.js.map → chunk-BDVFPG6Y.js.map} +0 -0
- /package/dist/{chunk-YXOUQ2H7.js.map → chunk-CAF6WOK7.js.map} +0 -0
- /package/dist/{chunk-HUS3J5WM.js.map → chunk-CIE2BASH.js.map} +0 -0
- /package/dist/{chunk-DIRRS7WP.js.map → chunk-CRI4OUV4.js.map} +0 -0
- /package/dist/{chunk-FXK4X4BL.js.map → chunk-DLG74CNB.js.map} +0 -0
- /package/dist/{chunk-WTUNZACT.js.map → chunk-E5ZQT5KX.js.map} +0 -0
- /package/dist/{chunk-DMFNPHCX.js.map → chunk-EQO2UVP4.js.map} +0 -0
- /package/dist/{chunk-MCEKIAO4.js.map → chunk-ERJ7FU55.js.map} +0 -0
- /package/dist/{chunk-6QILBDZU.js.map → chunk-EVVGJDRQ.js.map} +0 -0
- /package/dist/{chunk-7VSGW5KJ.js.map → chunk-EVX47VBM.js.map} +0 -0
- /package/dist/{chunk-D3EFFFOL.js.map → chunk-EXK7QRWF.js.map} +0 -0
- /package/dist/{chunk-5JB73NMC.js.map → chunk-EYZ6IQ6X.js.map} +0 -0
- /package/dist/{chunk-BCYQA5BK.js.map → chunk-F64IRYG3.js.map} +0 -0
- /package/dist/{chunk-K62IUKOD.js.map → chunk-FHS2HQSH.js.map} +0 -0
- /package/dist/{chunk-67TNU77I.js.map → chunk-FUG4SFN3.js.map} +0 -0
- /package/dist/{chunk-ZP5PL65C.js.map → chunk-GKEWXIV5.js.map} +0 -0
- /package/dist/{chunk-RKRGDAS7.js.map → chunk-GTGPO5KW.js.map} +0 -0
- /package/dist/{chunk-MSPNM2OM.js.map → chunk-HB3RFQSC.js.map} +0 -0
- /package/dist/{chunk-OOLO4LQG.js.map → chunk-I6GALP6E.js.map} +0 -0
- /package/dist/{chunk-57V5S7PW.js.map → chunk-IL6POWCW.js.map} +0 -0
- /package/dist/{chunk-7NS43EXZ.js.map → chunk-IU6SNYX6.js.map} +0 -0
- /package/dist/{chunk-5ZOSG3RJ.js.map → chunk-J2ZX2XUV.js.map} +0 -0
- /package/dist/{chunk-SYMS4546.js.map → chunk-JKJOGPZN.js.map} +0 -0
- /package/dist/{chunk-AWB5JZW6.js.map → chunk-JZUV47FZ.js.map} +0 -0
- /package/dist/{chunk-ODJWBC5I.js.map → chunk-K5IMOGHB.js.map} +0 -0
- /package/dist/{chunk-3G4KMVUN.js.map → chunk-KSKBRKGJ.js.map} +0 -0
- /package/dist/{chunk-HFJIGWJC.js.map → chunk-LASZKVMO.js.map} +0 -0
- /package/dist/{chunk-IGJHJYM6.js.map → chunk-LXEXBTKL.js.map} +0 -0
- /package/dist/{chunk-PR5LC5YX.js.map → chunk-MV43JSJF.js.map} +0 -0
- /package/dist/{chunk-ELNBMFOS.js.map → chunk-NJ4EOKMB.js.map} +0 -0
- /package/dist/{chunk-PXWVF7MC.js.map → chunk-NR5LT5RY.js.map} +0 -0
- /package/dist/{chunk-QYPKGVT4.js.map → chunk-NWSGI5C7.js.map} +0 -0
- /package/dist/{chunk-25NDNMQ7.js.map → chunk-Q5VNKNE5.js.map} +0 -0
- /package/dist/{chunk-CIKY6I4W.js.map → chunk-QLWMEXCT.js.map} +0 -0
- /package/dist/{chunk-VA4ZWLFY.js.map → chunk-QRB7NAOJ.js.map} +0 -0
- /package/dist/{chunk-3MSQLQNZ.js.map → chunk-R4ROFEYC.js.map} +0 -0
- /package/dist/{chunk-BEIRE5K4.js.map → chunk-RCVJZMFE.js.map} +0 -0
- /package/dist/{chunk-VR24ILCR.js.map → chunk-RIX4V2TB.js.map} +0 -0
- /package/dist/{chunk-Q3XSMOZM.js.map → chunk-RYCTDFUX.js.map} +0 -0
- /package/dist/{chunk-7E67FJ33.js.map → chunk-RZ5SVOYR.js.map} +0 -0
- /package/dist/{chunk-IDIV5NVH.js.map → chunk-SLFHQTTW.js.map} +0 -0
- /package/dist/{chunk-C3YH34IL.js.map → chunk-T2FFWNPK.js.map} +0 -0
- /package/dist/{chunk-O2DYPAZY.js.map → chunk-UNFPPVNI.js.map} +0 -0
- /package/dist/{chunk-GA2QHD6P.js.map → chunk-VLFCJYTL.js.map} +0 -0
- /package/dist/{chunk-YSXEFGND.js.map → chunk-VN3DBQAU.js.map} +0 -0
- /package/dist/{chunk-C7NTK5I3.js.map → chunk-VZRODIM3.js.map} +0 -0
- /package/dist/{chunk-QLLKLFE2.js.map → chunk-W22IKJE2.js.map} +0 -0
- /package/dist/{chunk-5XIPOCQC.js.map → chunk-X3YYQWOQ.js.map} +0 -0
- /package/dist/{chunk-XFXUTFWQ.js.map → chunk-YFXZEAYK.js.map} +0 -0
- /package/dist/{chunk-7ITPJFGC.js.map → chunk-ZAJAVEXY.js.map} +0 -0
- /package/dist/{chunk-4R4Q64W5.js.map → chunk-ZICD77GM.js.map} +0 -0
- /package/dist/{chunk-45ZZLTFH.js.map → chunk-ZJ2AK4V7.js.map} +0 -0
- /package/dist/{chunk-UFILRICG.js.map → chunk-ZUODRVHY.js.map} +0 -0
- /package/dist/{collection-facade-YE2O4J4O.js.map → collection-facade-TLWIPI76.js.map} +0 -0
- /package/dist/{computed-W6MVAMWZ.js.map → computed-ZIQTYTDI.js.map} +0 -0
- /package/dist/{dead-filter-4KXZEB3X.js.map → dead-filter-ESOBZZP5.js.map} +0 -0
- /package/dist/{delegation-OPUU3R2E.js.map → delegation-W7ZV2322.js.map} +0 -0
- /package/dist/{derive-BGUHU663.js.map → derive-H2BJTLUI.js.map} +0 -0
- /package/dist/{enclave-VXAQD4XJ.js.map → enclave-AP3I5W7O.js.map} +0 -0
- /package/dist/{executor-GTVSUUBX.js.map → executor-KGWTNEQD.js.map} +0 -0
- /package/dist/{executor-GZWMTOXB.js.map → executor-OVEXR6RT.js.map} +0 -0
- /package/dist/{executor-XXGS4S3K.js.map → executor-UNEH5ZYH.js.map} +0 -0
- /package/dist/{export-accessible-BMULIOYB.js.map → export-accessible-PCMXSUZS.js.map} +0 -0
- /package/dist/{extract-partition-JUQTWDXR.js.map → extract-partition-SWHFSYJO.js.map} +0 -0
- /package/dist/{fanout-sidecar-CKYFH33E.js.map → fanout-sidecar-A765THMX.js.map} +0 -0
- /package/dist/{find-XRGCNGNN.js.map → find-N6345CNT.js.map} +0 -0
- /package/dist/{issue-UB6AA5M6.js.map → issue-5OKB6I2R.js.map} +0 -0
- /package/dist/{ledger-FU6GH5EH.js.map → ledger-GFBKVPV4.js.map} +0 -0
- /package/dist/{liberate-YUN7W5QG.js.map → liberate-3VDOT4KO.js.map} +0 -0
- /package/dist/{link-set-MNADSH5M.js.map → link-set-Z7Q2ASFX.js.map} +0 -0
- /package/dist/{noydb-6JLBZU4U.js.map → noydb-ZCJOYBDI.js.map} +0 -0
- /package/dist/{policy-JTFOPPUU.js.map → policy-Z3RUAIY2.js.map} +0 -0
- /package/dist/{public-envelope-NJVB7LPA.js.map → public-envelope-B4KNWVUA.js.map} +0 -0
- /package/dist/{register-VCKOBWKT.js.map → register-VAHONCID.js.map} +0 -0
- /package/dist/{registry-72UKJ2L4.js.map → registry-7MLVD3VO.js.map} +0 -0
- /package/dist/{registry-BZFLJNEH.js.map → registry-EPKPD5SM.js.map} +0 -0
- /package/dist/{registry-YJEYSYI5.js.map → registry-P5T7PS2U.js.map} +0 -0
- /package/dist/{request-withdrawal-6MJRMFHF.js.map → request-withdrawal-TI3A5WPS.js.map} +0 -0
- /package/dist/{revoke-4KUNSVJC.js.map → revoke-FDRD6GRW.js.map} +0 -0
- /package/dist/{seed-4RRXT42N.js.map → seed-Q6M2IZFL.js.map} +0 -0
- /package/dist/{signer-DDDXP5JD.js.map → signer-MUWEIUI6.js.map} +0 -0
- /package/dist/{stale-NXPT56JA.js.map → stale-OSQ7J3AA.js.map} +0 -0
- /package/dist/{storage-IR6EEAV2.js.map → storage-MVFCGEQE.js.map} +0 -0
- /package/dist/{store-coordination-provider-HPYVFJCV.js.map → store-coordination-provider-64OEZFGS.js.map} +0 -0
- /package/dist/{walk-2RWEOCAS.js.map → walk-SVNDIZ62.js.map} +0 -0
- /package/dist/{withdraw-accessible-CLMZEHQT.js.map → withdraw-accessible-WZIEVWSR.js.map} +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Collection } from '../../kernel/collection.js';
|
|
2
2
|
import type { TxContext } from '../../with-commit/tx/transaction.js';
|
|
3
3
|
import type { PutDerivedOutputCtx } from '../../kernel/via/dispatch.js';
|
|
4
|
-
import type { MaterializedViewRegistry } from './registry.js';
|
|
4
|
+
import type { MaterializedViewRegistry, RegisteredMV } from './registry.js';
|
|
5
5
|
import type { MVQueryContext } from './types.js';
|
|
6
|
+
import type { NoydbStore } from '../../kernel/types.js';
|
|
6
7
|
/**
|
|
7
8
|
* Accessor shape passed in from the owning Vault. Provides the
|
|
8
9
|
* registry (used as a stable WeakMap key + to look up MVs by output
|
|
@@ -30,6 +31,48 @@ export declare function markMVStale(registry: MaterializedViewRegistry, mvName:
|
|
|
30
31
|
* @internal
|
|
31
32
|
*/
|
|
32
33
|
export declare function isMVStale(registry: MaterializedViewRegistry, mvName: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Reserved collection holding CONTENT-FREE lazy-stale markers (record id =
|
|
36
|
+
* MV name, plaintext `_iv: ''` envelope, no payload) — mirrors the
|
|
37
|
+
* `_meta`/schema-fence marker envelope shape. Written only from the
|
|
38
|
+
* delete/forget/elevate dispatcher (#736); ordinary source writes stay
|
|
39
|
+
* in-memory-only (the cheap path). Read via the adapter directly, never
|
|
40
|
+
* through `vault.collection()` — same reserved-collection convention as
|
|
41
|
+
* `_subject_index`/`_meta`.
|
|
42
|
+
*/
|
|
43
|
+
export declare const MV_STALE_COLLECTION = "_mv_stale";
|
|
44
|
+
/**
|
|
45
|
+
* Invalidate an MV from the delete/forget/elevate dispatcher (#736) — the ordinary
|
|
46
|
+
* source-write path (`dispatchMaterializedViews`) never calls this. Deletes EVERY
|
|
47
|
+
* persisted row in `reg.outputCollection` via `_internalDelete` (the at-rest law: a
|
|
48
|
+
* stale mark alone leaves the elevated/forgotten source's plaintext sitting in the
|
|
49
|
+
* output row) using the adapter directly so the read-path's `resolveStaleMVOnRead`
|
|
50
|
+
* isn't triggered mid-invalidation. `mode: 'lazy'` also persists the stale mark so a
|
|
51
|
+
* cold session recomputes on next read; `'manual'` gets the purge only — the MV
|
|
52
|
+
* serves empty until an explicit `vault.refreshView()` (erasure wins). Deletion is
|
|
53
|
+
* stamp-scoped (`_materializedFrom.mvName === reg.spec.name` — see the loop below),
|
|
54
|
+
* so another registered MV sharing this output collection keeps its own rows intact:
|
|
55
|
+
* no cross-MV re-marking is needed here, only `reg` itself is marked stale.
|
|
56
|
+
*
|
|
57
|
+
* Returns the count of rows actually `_internalDelete`d (#761 item 1) — folded by the
|
|
58
|
+
* caller into `dispatchMaterializedViewsOnDelete`'s `deleted` so `ForgetResult.derivedRecordsErased`
|
|
59
|
+
* counts lazy/manual purges too, not just the eager executor's tombstone leg — plus
|
|
60
|
+
* `residueUndecodable`, the bare ids whose ownership stamp could NOT be decoded (#776): a
|
|
61
|
+
* candidate row that fails to decode under the collection's default DEK (e.g. elevated above
|
|
62
|
+
* tier 0 on a tiered output collection) has UNKNOWN ownership — it might be this MV's own
|
|
63
|
+
* output, or (same-collection shape) a plain user record — so it is never erased, but it must
|
|
64
|
+
* be SURFACED rather than silently skipped (the #724 posture), since it may still hold the
|
|
65
|
+
* forgotten/pre-elevation contribution, decryptable by tier-holders — and `residueDeclined`
|
|
66
|
+
* (#785), the bare ids that DID decode and stamp-match but whose `_internalDelete` declined
|
|
67
|
+
* (ownership CONFIRMED, erasure declined).
|
|
68
|
+
*
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
export declare function invalidateMVAtRest(accessor: MVStaleAccessor, reg: RegisteredMV, mode: 'lazy' | 'manual'): Promise<{
|
|
72
|
+
deleted: number;
|
|
73
|
+
residueUndecodable: string[];
|
|
74
|
+
residueDeclined: string[];
|
|
75
|
+
}>;
|
|
33
76
|
/**
|
|
34
77
|
* Called from `Collection.get` (and any reader that materializes the
|
|
35
78
|
* MV's output collection). If any MV producing `outputCollection` is
|
|
@@ -60,3 +103,11 @@ export declare function resolveStaleMVOnRead(accessor: MVStaleAccessor, outputCo
|
|
|
60
103
|
* @internal
|
|
61
104
|
*/
|
|
62
105
|
export declare function clearMVStale(registry: MaterializedViewRegistry, mvName: string): void;
|
|
106
|
+
/**
|
|
107
|
+
* `clearMVStale` plus the persisted marker (#736) — `Vault.refreshView()`'s
|
|
108
|
+
* completion path. A lingering persisted marker after a manual refresh would
|
|
109
|
+
* make the NEXT cold session recompute redundantly.
|
|
110
|
+
*
|
|
111
|
+
* @internal
|
|
112
|
+
*/
|
|
113
|
+
export declare function clearMVStaleFully(adapter: NoydbStore, vault: string, registry: MaterializedViewRegistry, mvName: string): Promise<void>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection-namespaced `_vec` side-car ids (#726).
|
|
3
|
+
*
|
|
4
|
+
* `_vec` embedding side-cars used to be keyed by the bare record id,
|
|
5
|
+
* vault-wide — two collections sharing a record id shared one `_vec` row.
|
|
6
|
+
* This was verified to be a clobber/crash bug, NOT a confidentiality leak:
|
|
7
|
+
* every collection has its own DEK, and AES-GCM auth-tag verification means
|
|
8
|
+
* decrypting a foreign collection's `_vec` row under the wrong DEK throws
|
|
9
|
+
* `TamperedError` rather than returning wrong plaintext — no disclosure
|
|
10
|
+
* mechanism exists. The real bug was id collision: a same-id write/forget in
|
|
11
|
+
* one collection could clobber or delete the other collection's row, and a
|
|
12
|
+
* collection whose `similarTo()` / cold semantic `retrieve()` encountered a
|
|
13
|
+
* foreign same-id row crashed with an uncaught `TamperedError` (a DoS). The
|
|
14
|
+
* store-collection bucket stays the literal `'_vec'`; the id becomes
|
|
15
|
+
* composite: `${collection}/${recordId}`. Mirrors `encodeIdxId`
|
|
16
|
+
* (`../indexing/persisted-indexes.js`).
|
|
17
|
+
*
|
|
18
|
+
* Decode is deliberately NOT split-on-`/`: the collection is always known
|
|
19
|
+
* at the decode site (`ctx.name`), so `decodeVecId` strips that known
|
|
20
|
+
* prefix — correct even when `recordId` itself contains `/`.
|
|
21
|
+
*
|
|
22
|
+
* Precondition: `collection` itself must not contain `/` — otherwise
|
|
23
|
+
* `${collection}/${recordId}` is ambiguous (e.g. collection `'a'` would
|
|
24
|
+
* misattribute collection `'a/b'`'s rows via prefix match in `isVecIdFor`).
|
|
25
|
+
* Collection names are NOT currently restricted to a `/`-free charset by
|
|
26
|
+
* `vault.collection()`, so `encodeVecId` enforces the precondition itself,
|
|
27
|
+
* failing loud at the write boundary rather than silently misencoding.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Encode the `_vec` side-car id for a (collection, recordId) pair.
|
|
31
|
+
* Format: `<collection>/<recordId>` — no escaping; `recordId` may contain `/`.
|
|
32
|
+
*
|
|
33
|
+
* Throws if `collection` contains `/`: that would make the composite id
|
|
34
|
+
* ambiguous against `isVecIdFor`'s prefix match (collection `'a'` would
|
|
35
|
+
* appear to own collection `'a/b'`'s rows), reintroducing the clobber/crash
|
|
36
|
+
* risk the namespacing fix exists to close.
|
|
37
|
+
*/
|
|
38
|
+
export declare function encodeVecId(collection: string, recordId: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Decode a `_vec` id back to the bare record id, given the KNOWN owning
|
|
41
|
+
* collection — strips the `<collection>/` prefix rather than splitting on
|
|
42
|
+
* `/`, so a `recordId` containing `/` round-trips correctly. Returns `null`
|
|
43
|
+
* if `vecId` does not belong to `collection`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function decodeVecId(collection: string, vecId: string): string | null;
|
|
46
|
+
/** Predicate form of {@link decodeVecId} — used to filter `list()` results. */
|
|
47
|
+
export declare function isVecIdFor(collection: string, vecId: string): boolean;
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
* Internal service — not exported as a `@noy-db/hub/*` subpath.
|
|
22
22
|
*/
|
|
23
|
-
import type { NoydbStore } from '../../kernel/types.js';
|
|
23
|
+
import type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js';
|
|
24
24
|
import type { RecordCodec } from '../../kernel/enclave/index.js';
|
|
25
25
|
import type { NoydbEventEmitter } from '../../kernel/events.js';
|
|
26
26
|
import type { CollectionIndexes } from './eager-indexes.js';
|
|
@@ -118,3 +118,40 @@ export declare function purgePersistedIndexes<T>(ctx: IndexingContext<T>, id: st
|
|
|
118
118
|
purged: number;
|
|
119
119
|
residue: string[];
|
|
120
120
|
}>;
|
|
121
|
+
/**
|
|
122
|
+
* Sync this collection's indexes after a tier move (#709). Persisted
|
|
123
|
+
* `_idx/<field>/<recordId>` side-cars are always encrypted under the
|
|
124
|
+
* tier-0 DEK regardless of the record's own tier, and the eager in-memory
|
|
125
|
+
* `CollectionIndexes` mirror holds plaintext bucket values too — so both
|
|
126
|
+
* leak an elevated record's indexed field values unless swept the same way
|
|
127
|
+
* `purgePersistedIndexes` already sweeps them for `forget()` ("forget()
|
|
128
|
+
* crypto-shreds the body but keeps the collection DEK, under which these
|
|
129
|
+
* side-cars are encrypted — so without this they leave the indexed field
|
|
130
|
+
* VALUES readable", `purgePersistedIndexes` above).
|
|
131
|
+
*
|
|
132
|
+
* `record === null` — the record just left tier 0: purge its persisted
|
|
133
|
+
* side-cars (content-free, no decrypt needed) and drop its eager-mirror
|
|
134
|
+
* entry, read from `ctx.cache` (the caller's pre-write value — the caller
|
|
135
|
+
* MUST invoke this before evicting/overwriting that cache entry, so it
|
|
136
|
+
* still reflects the value being replaced).
|
|
137
|
+
*
|
|
138
|
+
* `record` given — the record is tier-0 again: (re)build its entries from
|
|
139
|
+
* that record, using the SAME pre-write `ctx.cache` read as the previous
|
|
140
|
+
* value so a stale bucket (e.g. a same-tier `putAtTier` value change) is
|
|
141
|
+
* cleaned up rather than left as a false-positive hit. `version` stamps
|
|
142
|
+
* the rebuilt side-car's own envelope version.
|
|
143
|
+
*
|
|
144
|
+
* `priorEnvelope` (#720) — the RAW envelope the caller read BEFORE its own
|
|
145
|
+
* overwrite landed (`putAtTier`'s `existing` / `demote`'s pre-move
|
|
146
|
+
* `envelope`), for the `ctx.cache`-miss lazy case: by this call's time the
|
|
147
|
+
* adapter already holds the NEW envelope, so a `ctx.adapter.get()` here
|
|
148
|
+
* would only ever re-observe the write just made — useless for recovering
|
|
149
|
+
* what a same-tier `putAtTier` may have dropped. Optional: the null-record
|
|
150
|
+
* branches (elevate / demote-to-intermediate / putAtTier(tier>0)) never
|
|
151
|
+
* need it — their only prior-dependent step, the eager `indexes.remove`
|
|
152
|
+
* below, already has what it needs from `ctx.cache` (eager mode is
|
|
153
|
+
* unaffected by the raw-envelope overwrite; lazy mode has no eager mirror).
|
|
154
|
+
*
|
|
155
|
+
* No-ops fast when the collection has neither index kind declared.
|
|
156
|
+
*/
|
|
157
|
+
export declare function syncTierIndexes<T>(ctx: IndexingContext<T>, id: string, record: T | null, version: number, priorEnvelope?: EncryptedEnvelope): Promise<void>;
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import type { FieldClause, Operator } from '../../kernel/query/predicate.js';
|
|
26
26
|
import type { PersistedCollectionIndex } from './persisted-indexes.js';
|
|
27
27
|
import type { QueryField } from '../../kernel/types.js';
|
|
28
|
+
import type { ViaPipeline } from '../../kernel/via/pipeline.js';
|
|
28
29
|
export interface LazyOrderBy {
|
|
29
30
|
readonly field: string;
|
|
30
31
|
readonly direction: 'asc' | 'desc';
|
|
@@ -36,10 +37,42 @@ export interface LazyOrderBy {
|
|
|
36
37
|
export interface LazyQuerySource<T> {
|
|
37
38
|
readonly collectionName: string;
|
|
38
39
|
readonly persistedIndexes: PersistedCollectionIndex;
|
|
40
|
+
/**
|
|
41
|
+
* Per-field bucket-key canonicalizer (#677 — lazy twin of the eager
|
|
42
|
+
* `candidateRecords()` probe-resolution in `kernel/query/builder.ts`).
|
|
43
|
+
* Consulted in `resolveCandidateIds()` for `==`/`in` clause values
|
|
44
|
+
* BEFORE calling `lookupEqual`/`lookupIn`, so a mixed-era stored value
|
|
45
|
+
* (money) probes the SAME canonical bucket the write path now
|
|
46
|
+
* populates. A narrow closure — not the whole `ViaPipeline` — keeps
|
|
47
|
+
* the with-lookup/kernel seam thin. `undefined` (no via, or no binding
|
|
48
|
+
* covers the field) is the common case: falls back to the raw value,
|
|
49
|
+
* unchanged from today.
|
|
50
|
+
*/
|
|
51
|
+
canonicalizeIndexKey?(field: string, value: unknown): string | undefined;
|
|
39
52
|
/** Ensure `_idx/<field>/*` side-cars have been bulk-loaded into the mirror. */
|
|
40
53
|
ensurePersistedIndexesLoaded(): Promise<void>;
|
|
41
|
-
/**
|
|
42
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Decrypt one record by id in RAW (pre-`present()`, stored-form) shape,
|
|
56
|
+
* or return null if it's gone (#684 — the raw-fetch seam). Used by the
|
|
57
|
+
* post-filter (`toArray()`) so a `clause.via.evaluate` (e.g. money) sees
|
|
58
|
+
* the same operand/actual-value space as eager's `filterRecords` — the
|
|
59
|
+
* DECODED form (e.g. money's canonical decimal) is only materialized
|
|
60
|
+
* for survivors, via {@link decodeRecord}.
|
|
61
|
+
*/
|
|
62
|
+
getRawRecord(id: string): Promise<unknown>;
|
|
63
|
+
/**
|
|
64
|
+
* Apply this collection's locale/virtual-field decode (`present()`) to a
|
|
65
|
+
* RAW record already known to match — the output-side twin of
|
|
66
|
+
* {@link getRawRecord}. Mirrors eager's `Query.decodeVia`.
|
|
67
|
+
*/
|
|
68
|
+
decodeRecord(record: unknown): Promise<T>;
|
|
69
|
+
/**
|
|
70
|
+
* This collection's compiled Via pipeline (money, i18n, …), read lazily
|
|
71
|
+
* (a method, not a captured value) so a late `_setVia` (#666) is honored
|
|
72
|
+
* — same closure discipline as {@link canonicalizeIndexKey}. `undefined`
|
|
73
|
+
* when the collection has no Via-covered fields.
|
|
74
|
+
*/
|
|
75
|
+
via(): ViaPipeline | undefined;
|
|
43
76
|
}
|
|
44
77
|
interface LazyPlan {
|
|
45
78
|
readonly clauses: readonly FieldClause[];
|
|
@@ -97,6 +97,23 @@ export declare function compositeKey(fields: readonly string[]): string;
|
|
|
97
97
|
export declare class PersistedCollectionIndex {
|
|
98
98
|
private readonly indexes;
|
|
99
99
|
private readonly defs;
|
|
100
|
+
/**
|
|
101
|
+
* Per-field bucket-key canonicalizer (#677 — lazy twin of #672's
|
|
102
|
+
* `CollectionIndexes.setCanonicalizer`), registered ONCE via
|
|
103
|
+
* {@link setCanonicalizer} when the collection wires indexing up
|
|
104
|
+
* (money-aware via `ViaPipeline.canonicalizeIndexKey`). Consulted by
|
|
105
|
+
* EVERY bucket-mutation site — `ingest`/`upsert`/`remove` — so bucket
|
|
106
|
+
* membership is symmetric: a legacy non-canonical stored value (e.g.
|
|
107
|
+
* `'0100'`) and a canonical one (`'100'`) land in the SAME bucket,
|
|
108
|
+
* mirroring `eager-indexes.ts`'s `addToIndex`/`removeFromIndex`.
|
|
109
|
+
*/
|
|
110
|
+
private canonicalize?;
|
|
111
|
+
/**
|
|
112
|
+
* Register the bucket-key canonicalizer. Called once, where the
|
|
113
|
+
* collection constructs its indexing state — safe to call again (the
|
|
114
|
+
* closure is simply replaced) but there is normally only one caller.
|
|
115
|
+
*/
|
|
116
|
+
setCanonicalizer(fn: (field: string, value: unknown) => string | undefined): void;
|
|
100
117
|
/**
|
|
101
118
|
* Declare a single-field index. Subsequent `upsert` / `ingest` calls
|
|
102
119
|
* populate the in-memory mirror; calls before `declare` are no-ops
|
|
@@ -156,12 +173,21 @@ export declare class PersistedCollectionIndex {
|
|
|
156
173
|
* back to scan or throws `IndexRequiredError`). Returns a shared empty
|
|
157
174
|
* set if the field is declared but no record matches — that set MUST
|
|
158
175
|
* NOT be mutated by the caller.
|
|
176
|
+
*
|
|
177
|
+
* `value` is bucketed via the plain {@link stringifyKey} — it is NOT
|
|
178
|
+
* canonicalized here. Callers on a Via-covered field (money) canonicalize
|
|
179
|
+
* `value` themselves BEFORE calling (see `lazy-builder.ts`'s
|
|
180
|
+
* `resolveCandidateIds()`, #677) — the SAME caller-canonicalizes split
|
|
181
|
+
* `eager-indexes.ts`'s `lookupEqual`/`lookupIn` use (see `kernel/query/
|
|
182
|
+
* builder.ts`'s `candidateRecords()`), so probing here twice never
|
|
183
|
+
* double-canonicalizes.
|
|
159
184
|
*/
|
|
160
185
|
lookupEqual(field: string, value: unknown): ReadonlySet<string> | null;
|
|
161
186
|
/**
|
|
162
187
|
* Set lookup — return the union of record ids whose `field` matches any
|
|
163
188
|
* of `values`. Returns `null` if the field is not declared. Returns a
|
|
164
|
-
* fresh (non-shared) Set — safe for the caller to mutate.
|
|
189
|
+
* fresh (non-shared) Set — safe for the caller to mutate. Same
|
|
190
|
+
* caller-canonicalizes contract as {@link lookupEqual} (#677).
|
|
165
191
|
*/
|
|
166
192
|
lookupIn(field: string, values: readonly unknown[]): ReadonlySet<string> | null;
|
|
167
193
|
/**
|
|
@@ -174,6 +200,23 @@ export declare class PersistedCollectionIndex {
|
|
|
174
200
|
* Supported ops: `'<'`, `'<='`, `'>'`, `'>='`, `'between'`. For
|
|
175
201
|
* `'between'`, `value` is `[lo, hi]` and both bounds are inclusive
|
|
176
202
|
* (matches the eager-mode operator contract in `predicate.ts`).
|
|
203
|
+
*
|
|
204
|
+
* NOT canonicalized (#677): this method compares `value` against the
|
|
205
|
+
* ORIGINAL TYPED `state.values`, not a stringified bucket key, so a
|
|
206
|
+
* `canonicalizeIndexKey`-shaped (`string | undefined`) result would not
|
|
207
|
+
* even type-check as a bound here. This does NOT mirror eager mode the
|
|
208
|
+
* way `lookupEqual`/`lookupIn` do: `moneyIndexProbe` (`via/money/
|
|
209
|
+
* where.ts`) never emits a range probe, so EAGER mode's `candidateRecords`
|
|
210
|
+
* never calls an index for a range clause and always scans.
|
|
211
|
+
*
|
|
212
|
+
* LAZY mode's `resolveCandidateIds()` (`lazy-builder.ts`) no longer
|
|
213
|
+
* dispatches a Via-covered (e.g. money) range clause through THIS method
|
|
214
|
+
* at all (#684) — it enumerates the field's indexed ids via `orderedBy()`
|
|
215
|
+
* instead and leaves the raw/typed comparison here for the (now
|
|
216
|
+
* Via-aware) post-filter to redo correctly in scaled-int space. A
|
|
217
|
+
* NON-via range clause still dispatches through this method exactly as
|
|
218
|
+
* before — the raw/typed comparison here is correct and sufficient for
|
|
219
|
+
* fields with no decode-transforming Via binding.
|
|
177
220
|
*/
|
|
178
221
|
lookupRange(field: string, op: '<' | '<=' | '>' | '>=' | 'between', value: unknown): ReadonlySet<string> | null;
|
|
179
222
|
/**
|
|
@@ -121,3 +121,43 @@ export declare function similarTo<T>(ctx: SearchContext<T>, vector: Float32Array
|
|
|
121
121
|
* (`Collection.put`) only invokes this when `embeddings` is declared.
|
|
122
122
|
*/
|
|
123
123
|
export declare function embedOnWrite<T>(ctx: SearchContext<T>, id: string, record: T, version: number): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Force-re-derive every eligible tier-0 record's `_vec` sidecar once (#788).
|
|
126
|
+
* Opt-in bulk repair for the #726 re-namespace: legacy bare-id rows are
|
|
127
|
+
* unreachable to `buildVectorLoad` (it only recognises `<collection>/<id>`
|
|
128
|
+
* keys) and otherwise self-heal only when a record is next `put()` — this
|
|
129
|
+
* lets an adopter recover recall immediately instead of waiting on writes.
|
|
130
|
+
*
|
|
131
|
+
* **Skips elevated records** (`liveRecordIsElevated`) rather than refusing
|
|
132
|
+
* the whole walk — the OPPOSITE of `_applyCutoverTransform`'s
|
|
133
|
+
* `assertCutoverTierSafe` refuse-whole-batch precedent. An elevated record is
|
|
134
|
+
* SUPPOSED to have no `_vec` (`syncTierSearch` purges it on elevate);
|
|
135
|
+
* re-embedding one here would write searchable plaintext-derived data above
|
|
136
|
+
* tier 0. Load-bearing: never write a `_vec` row for an elevated record.
|
|
137
|
+
*
|
|
138
|
+
* Tombstones, delete markers, and a raw `get` racing a delete between `list`
|
|
139
|
+
* and `get` all decrypt to `null` (`decryptRecord` already folds
|
|
140
|
+
* tombstone/delete-marker into that null) and are skipped the same way.
|
|
141
|
+
* Unconditional re-derive, no already-migrated check — safe to re-run after
|
|
142
|
+
* a partial failure (each id is independently idempotent).
|
|
143
|
+
*/
|
|
144
|
+
export declare function rebuildEmbeddings<T>(ctx: SearchContext<T>): Promise<{
|
|
145
|
+
rebuilt: number;
|
|
146
|
+
skipped: number;
|
|
147
|
+
}>;
|
|
148
|
+
/**
|
|
149
|
+
* Sync the collection's SEARCH artifacts after a tier move (#721). Both the
|
|
150
|
+
* lexical `_ftindex` blob and the `_vec/<id>` embedding are encrypted under
|
|
151
|
+
* the tier-0 DEK and hold the record's derived plaintext (full field text /
|
|
152
|
+
* a text-invertible vector), so leaving them means elevation never hid what
|
|
153
|
+
* the record was searchable by — the `forget()` precedent, unapplied to
|
|
154
|
+
* elevate. `null` → the record left tier 0: purge its `_vec` sidecar (mirrors
|
|
155
|
+
* `Collection._purgeVector`), and invalidate the `_ftindex` blob (mirrors
|
|
156
|
+
* `Collection._purgeSearchIndex`: deletes the persisted blob when persisted,
|
|
157
|
+
* else drops the in-memory index) so the next `retrieve()` rebuilds from the
|
|
158
|
+
* elevated-free `ctx.cache`. A record → it is tier-0 again: re-embed it via
|
|
159
|
+
* {@link embedOnWrite}, then invalidate `_ftindex` so the rebuild includes it
|
|
160
|
+
* again. No-op fast when the collection has neither a lexical index nor a
|
|
161
|
+
* vector set.
|
|
162
|
+
*/
|
|
163
|
+
export declare function syncTierSearch<T>(ctx: SearchContext<T>, id: string, record: T | null, version?: number): Promise<void>;
|
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
* live (L1 behavior); persists an opaque snapshot via a debounced flush, and
|
|
5
5
|
* validates a loaded blob against a {count,maxVersion} fingerprint so a stale
|
|
6
6
|
* blob is never used — only rebuilt.
|
|
7
|
+
*
|
|
8
|
+
* #725: a debounced save (encrypt + adapter.put) racing a purge (removePersisted,
|
|
9
|
+
* called by elevate's tier purge and forget's erasure) is guarded by an epoch counter
|
|
10
|
+
* — see the `epoch` field doc for the mechanism and why it also closes the issue's
|
|
11
|
+
* variant (b) for free. Residual gap, structurally unavoidable without a durable
|
|
12
|
+
* write-ahead log: a process CRASH strictly between a stale save's `adapter.put`
|
|
13
|
+
* landing and its compensating `remove()` running (or between the compensating
|
|
14
|
+
* remove landing and `pendingCompensation` being persisted somewhere durable — it is
|
|
15
|
+
* in-memory only) leaves the resurrected blob at rest until the next natural
|
|
16
|
+
* rebuild-persist. This is on par with the gate-before-put optimization's own crash
|
|
17
|
+
* residual (a crash between `isStale()` returning false and the put landing) — both
|
|
18
|
+
* are eliminated only by making the purge and the save's commit point transactional
|
|
19
|
+
* with the store, which is out of scope for an in-memory debounce layer over an
|
|
20
|
+
* arbitrary opaque-blob backend.
|
|
7
21
|
*/
|
|
8
22
|
import { InvertedIndex, type IndexDoc } from './inverted-index.js';
|
|
9
23
|
import type { IndexStore } from './index-store.js';
|
|
@@ -16,7 +30,14 @@ export interface PersistedIndexCallbacks {
|
|
|
16
30
|
json: string;
|
|
17
31
|
fingerprint: Fingerprint;
|
|
18
32
|
} | null>;
|
|
19
|
-
|
|
33
|
+
/** `isStale()` reflects whether a purge (removePersisted) has landed since this
|
|
34
|
+
* save's epoch was captured — check it AFTER any async encrypt step and BEFORE
|
|
35
|
+
* the actual adapter.put, skipping the write when true (#725 review: shrinks the
|
|
36
|
+
* at-rest window for purge-beats-encrypt orderings to zero). Belt-and-suspenders:
|
|
37
|
+
* PersistedIndexStore's own post-save epoch check (persist(), below) is the
|
|
38
|
+
* backstop that catches every other ordering, including a purge landing during
|
|
39
|
+
* the put itself. */
|
|
40
|
+
save(json: string, fp: Fingerprint, isStale: () => boolean): Promise<void>;
|
|
20
41
|
remove(): Promise<void>;
|
|
21
42
|
currentFingerprint(): Fingerprint;
|
|
22
43
|
debounceMs?: number;
|
|
@@ -27,15 +48,74 @@ export declare class PersistedIndexStore implements IndexStore {
|
|
|
27
48
|
private timer;
|
|
28
49
|
private lastBuild;
|
|
29
50
|
private readonly debounceMs;
|
|
51
|
+
/**
|
|
52
|
+
* Bumped by every purge (removePersisted). A save() captured under a since-stale
|
|
53
|
+
* epoch undoes its own effect once it settles, however it interleaves with the
|
|
54
|
+
* purge's own delete — a delete can never be overtaken by a save that was
|
|
55
|
+
* scheduled before it (#725).
|
|
56
|
+
*
|
|
57
|
+
* Subsumes the issue's variant (b) (a stale, pre-purge in-memory index getting
|
|
58
|
+
* paired with a live, post-purge fingerprint and TRUSTED by a cold load, defeating
|
|
59
|
+
* the fingerprint backstop) for free — no separate handling needed: removePersisted()
|
|
60
|
+
* always resets `index` to undefined in the SAME synchronous step that bumps the
|
|
61
|
+
* epoch (below), and the only two paths that can repopulate `index` (ensureBuilt's
|
|
62
|
+
* cold rebuild, rebuildAndPersist's rebuild) do so strictly from `lastBuild()`, which
|
|
63
|
+
* the collection wires to read its LIVE cache at call time — not a frozen snapshot.
|
|
64
|
+
* Cache eviction (the collection dropping the purged/forgotten record) always
|
|
65
|
+
* precedes removePersisted() in both the elevate and forget call paths (#721's
|
|
66
|
+
* syncCache eviction / forget's tombstone write both land before syncSearch /
|
|
67
|
+
* _purgeSearchIndex runs). So any rebuild that executes strictly after
|
|
68
|
+
* removePersisted() — the only way `index` becomes defined again post-purge —
|
|
69
|
+
* necessarily observes the post-purge cache, and any rebuild that predates
|
|
70
|
+
* removePersisted() has its `index` wiped by it. There is no ordering in which a
|
|
71
|
+
* stale (pre-purge) index survives to be skip-rebuilt and persisted under a
|
|
72
|
+
* matching live fingerprint. Regression-pinned in
|
|
73
|
+
* `search-persisted-index-store.test.ts` ("variant (b) is subsumed...").
|
|
74
|
+
*/
|
|
75
|
+
private epoch;
|
|
76
|
+
/**
|
|
77
|
+
* Set when a compensating/purging remove() (`compensate()` undoing a stale
|
|
78
|
+
* save, or `removePersisted()`'s own purge, #764) fails. Unlike the routine
|
|
79
|
+
* best-effort swallow on the debounced flush path (markDirty's timer
|
|
80
|
+
* .catch(), below — safe there because the fingerprint backstop forces a
|
|
81
|
+
* rebuild on the next cold load), a failed removal leaves a resurrected or
|
|
82
|
+
* un-purged blob at rest whose fingerprint can still match the live cache —
|
|
83
|
+
* a cold load would TRUST it, not rebuild past it. So this is never silently
|
|
84
|
+
* dropped: every subsequent store entrypoint (ensureBuilt, rebuildAndPersist,
|
|
85
|
+
* removePersisted) retries the remove first and refuses to proceed past it
|
|
86
|
+
* until it succeeds (#725 review), and the raw adapter error is wrapped in
|
|
87
|
+
* {@link PersistedIndexCompensationError} (#764) so a caller can catch a
|
|
88
|
+
* stuck removal deliberately instead of an indistinguishable adapter error.
|
|
89
|
+
* A crash between a landed put and its compensating remove (before this
|
|
90
|
+
* flag or a retry can run) is a residual gap — see the class doc.
|
|
91
|
+
*/
|
|
92
|
+
private pendingCompensation;
|
|
30
93
|
constructor(cb: PersistedIndexCallbacks);
|
|
31
94
|
get built(): boolean;
|
|
32
95
|
ensureBuilt(build: () => ReadonlyArray<IndexDoc>): Promise<InvertedIndex>;
|
|
33
96
|
markDirty(): void;
|
|
34
97
|
/** Force an immediate persist (cancels any pending debounce). */
|
|
35
98
|
flush(): Promise<void>;
|
|
36
|
-
/** Delete the persisted blob and drop the in-memory index (forget/erasure
|
|
99
|
+
/** Delete the persisted blob and drop the in-memory index (forget/erasure/tier
|
|
100
|
+
* move). A failed purge is wrapped in {@link PersistedIndexCompensationError}
|
|
101
|
+
* (#764) and left sticky (`pendingCompensation`) so the next store entrypoint
|
|
102
|
+
* retries it — never silently dropped, same posture #725 established for a
|
|
103
|
+
* stale save's own compensating remove. */
|
|
37
104
|
removePersisted(): Promise<void>;
|
|
38
105
|
/** Rebuild using the last known build thunk, then persist. */
|
|
39
106
|
private rebuildAndPersist;
|
|
107
|
+
/** Persist the current index, then undo it if a purge (removePersisted) landed
|
|
108
|
+
* while the save was in flight — a delete can never be overtaken by a save
|
|
109
|
+
* that was scheduled before it (#725). A failed undo does not silently vanish:
|
|
110
|
+
* see `pendingCompensation`. */
|
|
40
111
|
private persist;
|
|
112
|
+
/** Undo a stale save. Failure is sticky, not swallowed: see `pendingCompensation`.
|
|
113
|
+
* Wraps the raw adapter error in {@link PersistedIndexCompensationError} (#764) so
|
|
114
|
+
* a caller (e.g. the tier ops' `syncTierSearch`) can catch a stuck compensation
|
|
115
|
+
* deliberately instead of treating it as an indistinguishable adapter failure. */
|
|
116
|
+
private compensate;
|
|
117
|
+
/** Retries a sticky compensation before any store entrypoint proceeds. Failure
|
|
118
|
+
* re-wraps the raw adapter error the same way `compensate()` does (#764) — the
|
|
119
|
+
* flag stays set (untouched here) so the NEXT entrypoint retries again. */
|
|
120
|
+
private retryPendingCompensation;
|
|
41
121
|
}
|
|
@@ -28,6 +28,10 @@ export interface SearchStrategy {
|
|
|
28
28
|
warmIndex<T>(ctx: SearchContext<T>): Promise<void>;
|
|
29
29
|
flushIndex<T>(ctx: SearchContext<T>): Promise<void>;
|
|
30
30
|
embedOnWrite<T>(ctx: SearchContext<T>, id: string, record: T, version: number): Promise<void>;
|
|
31
|
+
rebuildEmbeddings<T>(ctx: SearchContext<T>): Promise<{
|
|
32
|
+
rebuilt: number;
|
|
33
|
+
skipped: number;
|
|
34
|
+
}>;
|
|
31
35
|
}
|
|
32
36
|
/**
|
|
33
37
|
* No-op stub — the floor default. Every capability method (and the embedding
|
|
@@ -57,6 +57,15 @@ export declare class SyncEngine {
|
|
|
57
57
|
private reservedLookup?;
|
|
58
58
|
/** Wire the reserved-lookup source (#650 Task 4). */
|
|
59
59
|
setReservedLookupSource(source: ReservedLookupSource): void;
|
|
60
|
+
/** #653: expands a (pair-expanded) collection-name filter to include the reserved `_dict_*`
|
|
61
|
+
* collections those names depend on via their lookup fields — mirrors `pairExpander` so a
|
|
62
|
+
* partial `push`/`pull({collections:[...]})` never drops the dictionary a named collection's
|
|
63
|
+
* labels/membership rely on. Wired unconditionally from the vault's `dictKeyFieldRegistry` via
|
|
64
|
+
* `setReservedDictExpander` (returns `[]` when the vault has no dict-backed collections); only
|
|
65
|
+
* `undefined` on a standalone engine that was never vault-attached. */
|
|
66
|
+
private reservedDictExpander?;
|
|
67
|
+
/** Wire the reserved-dict expansion function (#653). Same injection pattern as `setPairExpander`. */
|
|
68
|
+
setReservedDictExpander(expander: (names: readonly string[]) => readonly string[]): void;
|
|
60
69
|
constructor(opts: {
|
|
61
70
|
local: NoydbStore;
|
|
62
71
|
remote: NoydbStore;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `_blob_intent` — the blob durability journal marker (#753, spec §7).
|
|
3
|
+
*
|
|
4
|
+
* One row per in-flight multi-step blob operation (`shred` | `rehome`),
|
|
5
|
+
* written BEFORE the first destructive/movement step and deleted as the
|
|
6
|
+
* operation's LAST step. An absent marker means "no operation in flight"
|
|
7
|
+
* (the normal state, zero cost on every path that doesn't crash); a present
|
|
8
|
+
* marker means the operation MUST be resumed before any other blob work on
|
|
9
|
+
* that record proceeds.
|
|
10
|
+
*
|
|
11
|
+
* **Pattern honesty (C9):** this is the family's FIRST content-bearing
|
|
12
|
+
* ENCRYPTED reserved marker. The prior reserved-collection precedent,
|
|
13
|
+
* `_mv_stale` (`with-formula/materialized-views/stale.ts`), is
|
|
14
|
+
* content-free plaintext (`{ _iv: '', _data: '{}' }` — the row's only
|
|
15
|
+
* payload is its key). `_blob_intent` carries real operational metadata
|
|
16
|
+
* (op, tiers, eTags + counts) and is encrypted, so its codec/CAS/backup/
|
|
17
|
+
* sweep conventions below are new to the codebase, not a copy of an
|
|
18
|
+
* existing pattern.
|
|
19
|
+
*
|
|
20
|
+
* **Key management (spec §4):** encrypted under the record's OWNING
|
|
21
|
+
* collection's tier-0 DEK — `getDEK(collection)`, which resolves to
|
|
22
|
+
* `dekKey(collection, 0) === collection` (see `with-party/team/tiers.ts`).
|
|
23
|
+
* Every legitimate resumer (forget()-retry, blob write entry points,
|
|
24
|
+
* tier ops) already holds this DEK; minting an elevated DEK for the
|
|
25
|
+
* marker itself would make resume impossible for a tier-0-only resumer.
|
|
26
|
+
* Documented residue: a tier-0 DEK holder can learn that an elevated
|
|
27
|
+
* record's shred/rehome was in flight and which eTags it touched —
|
|
28
|
+
* existence-adjacent, consistent with the already-documented
|
|
29
|
+
* dedup-policy residue (#741).
|
|
30
|
+
*
|
|
31
|
+
* **Key grammar (C11):** `{collection}::{recordId}`. Blob-bearing record
|
|
32
|
+
* ids already refuse `::` at the write surface (`blob-set.ts`'s
|
|
33
|
+
* `assertKeyPartSafe`, #752) — re-validated here defensively so the
|
|
34
|
+
* sweep's prefix-free key list stays unambiguous regardless of caller.
|
|
35
|
+
*
|
|
36
|
+
* Wired by `shredAllForRecord`/`forget()` (PR-1 Task 3, shred) and by
|
|
37
|
+
* `BlobSet.syncTierMove`/`rehomeForTier`/`resolvePendingIntent` (PR-2 Task
|
|
38
|
+
* 2, rehome — mint/resume/consume for `op:'rehome'` markers).
|
|
39
|
+
*/
|
|
40
|
+
import type { NoydbStore } from '../../kernel/types.js';
|
|
41
|
+
import { type EnclaveKey } from '../../kernel/enclave/index.js';
|
|
42
|
+
/** Reserved collection holding `_blob_intent` marker rows. */
|
|
43
|
+
export declare const BLOB_INTENT_COLLECTION = "_blob_intent";
|
|
44
|
+
/**
|
|
45
|
+
* One captured refCount hold on an eTag, authoritative on resume (spec §2a,
|
|
46
|
+
* amended by C5). `chunkCount` lets a resumed release complete chunk
|
|
47
|
+
* cleanup even if the `_blob_index` row itself is already gone (mirrors
|
|
48
|
+
* `releaseRef`'s `chunkCountHint` parameter, Task 1).
|
|
49
|
+
*/
|
|
50
|
+
export interface BlobIntentHold {
|
|
51
|
+
readonly eTag: string;
|
|
52
|
+
readonly n: number;
|
|
53
|
+
readonly chunkCount: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The intent marker's decrypted payload (spec §2a, amended by C5: `holds`
|
|
57
|
+
* entries carry `{ eTag, n, chunkCount }` rather than the original bare
|
|
58
|
+
* `{ eTag, n }`).
|
|
59
|
+
*/
|
|
60
|
+
export interface BlobIntent {
|
|
61
|
+
readonly op: 'shred' | 'rehome';
|
|
62
|
+
/** Random nonce minted at marker creation (`crypto.getRandomValues`) — the op-stamp identity. */
|
|
63
|
+
readonly opId: string;
|
|
64
|
+
/** Captured ONCE at marker creation; authoritative on every resume. */
|
|
65
|
+
readonly holds?: readonly BlobIntentHold[];
|
|
66
|
+
readonly ownerTier?: number;
|
|
67
|
+
readonly fromTier?: number;
|
|
68
|
+
readonly toTier?: number;
|
|
69
|
+
readonly policy?: 'isolate' | 'dedup';
|
|
70
|
+
/**
|
|
71
|
+
* Rehome destination `+1` row-stamps (`${opId}:${slotName}` /
|
|
72
|
+
* `${opId}:${versionKey}`) CONFIRMED applied — #746 whole-branch review
|
|
73
|
+
* (K=8 stamp-ring blocker). `BlobObject.lastOps` is a BOUNDED ring (K=8,
|
|
74
|
+
* spec C2) — correct as the sole idempotency source for SHRED (one
|
|
75
|
+
* decrement per eTag, count captured in `holds`) but not for REHOME,
|
|
76
|
+
* whose row-scoped increments can exceed K on a single destination via
|
|
77
|
+
* either ≥9 rows of identical content on ONE record, or ≥8 CONCURRENT
|
|
78
|
+
* rehomes converging on one shared destination between a crashed op's
|
|
79
|
+
* stamp-write and its resume — either way, an evicted-but-unapplied-
|
|
80
|
+
* looking stamp gets re-applied, over-counting the destination's
|
|
81
|
+
* refCount and permanently stranding it above 0. This field is THIS
|
|
82
|
+
* op's own UNBOUNDED, per-record backstop: appended (via
|
|
83
|
+
* {@link recordAppliedStamp}) immediately after a stamped `+1` lands
|
|
84
|
+
* (whether freshly applied or found already in the ring), consulted
|
|
85
|
+
* BEFORE attempting the CAS so a resume that finds its own row-stamp
|
|
86
|
+
* here skips the increment entirely — independent of the shared
|
|
87
|
+
* destination object's bounded ring and of how many OTHER ops have
|
|
88
|
+
* touched it since. Absent/empty is the normal case (no rehome — or no
|
|
89
|
+
* increments yet — in flight).
|
|
90
|
+
*/
|
|
91
|
+
readonly appliedStamps?: readonly string[];
|
|
92
|
+
}
|
|
93
|
+
/** Resume callback signature for {@link sweepBlobIntents} — supplied by the caller (Task 3 / PR-2). */
|
|
94
|
+
export type BlobIntentResume = (collection: string, recordId: string, intent: BlobIntent) => Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Read the `_blob_intent` marker for `{collection}::{recordId}`, or `null`
|
|
97
|
+
* if none is pending. The resume-gate check every future blob mutator
|
|
98
|
+
* (Task 3 / PR-2) runs before proceeding.
|
|
99
|
+
*/
|
|
100
|
+
export declare function getIntent(adapter: NoydbStore, vault: string, collection: string, recordId: string, getDEK: (collection: string) => Promise<EnclaveKey>): Promise<BlobIntent | null>;
|
|
101
|
+
/**
|
|
102
|
+
* Create a `_blob_intent` marker for `{collection}::{recordId}` — CAS
|
|
103
|
+
* create-if-absent (C8). `store.put(..., expectedVersion: 0)` succeeds only
|
|
104
|
+
* when no row is present (every real adapter treats a missing record +
|
|
105
|
+
* `expectedVersion: 0` as a no-conflict write — the same convention
|
|
106
|
+
* `loadOrCreateSigner` uses in `with-audit/attestation/signer.ts`); a
|
|
107
|
+
* present row always carries `_v >= 1`, so the CAS throws `ConflictError`
|
|
108
|
+
* and is converted here into {@link BlobIntentPendingError} — the signal
|
|
109
|
+
* a caller catches to resume the PENDING operation before starting a new
|
|
110
|
+
* one, rather than silently clobbering it (which would orphan the prior
|
|
111
|
+
* op's op-stamps).
|
|
112
|
+
*/
|
|
113
|
+
export declare function createIntent(adapter: NoydbStore, vault: string, collection: string, recordId: string, getDEK: (collection: string) => Promise<EnclaveKey>, intent: BlobIntent): Promise<void>;
|
|
114
|
+
/** Delete the `_blob_intent` marker for `{collection}::{recordId}` — the operation's LAST step. */
|
|
115
|
+
export declare function deleteIntent(adapter: NoydbStore, vault: string, collection: string, recordId: string): Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* Durably record that a rehome destination row-stamp has been CONFIRMED
|
|
118
|
+
* applied — #746 whole-branch review (K=8 stamp-ring blocker). Appends
|
|
119
|
+
* `stamp` to the marker's `appliedStamps` (idempotent no-op if already
|
|
120
|
+
* present), CAS-retried against concurrent resumers touching the SAME
|
|
121
|
+
* marker (mirrors `BlobSet.casUpdateSlots`'s retry shape).
|
|
122
|
+
*
|
|
123
|
+
* A no-op — never throws — when the marker is already gone: that only
|
|
124
|
+
* happens if the whole op already completed and deleted it (this call is
|
|
125
|
+
* racing its own operation's tail) or a sibling resumer already finished
|
|
126
|
+
* it; either way there is nothing left to record against. A marker whose
|
|
127
|
+
* `op` is NOT `'rehome'` (should be structurally impossible — only
|
|
128
|
+
* `BlobSet`'s own rehome machinery calls this, against its own marker) is
|
|
129
|
+
* treated the same way: nothing to record.
|
|
130
|
+
*/
|
|
131
|
+
export declare function recordAppliedStamp(adapter: NoydbStore, vault: string, collection: string, recordId: string, getDEK: (collection: string) => Promise<EnclaveKey>, stamp: string): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* List every persisted `_blob_intent` marker and resume each via the
|
|
134
|
+
* caller-supplied `resume` callback (the forget-entry / vault-open sweep,
|
|
135
|
+
* mirroring `_mv_stale`'s `hydratePersistedStaleMarkers`/orphan-sweep
|
|
136
|
+
* pattern).
|
|
137
|
+
*
|
|
138
|
+
* **Per-marker isolation (#753 Task 3, carried finding from the Task 2
|
|
139
|
+
* review):** each marker's decode + `resume` runs in its own try/catch — a
|
|
140
|
+
* corrupt/DEK-mismatched marker, or a marker whose resume genuinely fails
|
|
141
|
+
* (e.g. a store error mid-release), is reported via `onResumeError` and
|
|
142
|
+
* skipped, never aborting the loop and never silently swallowed. Earlier
|
|
143
|
+
* (Task 2) this loop had no try/catch at all; that was safe only because
|
|
144
|
+
* nothing called `resume` with real logic yet — `BlobSet.mintShredIntent`'s
|
|
145
|
+
* collection-scoped sweep (this function's first real caller, Task 3) can
|
|
146
|
+
* hit a genuinely corrupt sibling marker and must not let it block a
|
|
147
|
+
* healthy one (mirrors `stale.ts`'s orphan-sweep defensive posture, #736).
|
|
148
|
+
*
|
|
149
|
+
* `resume` is the per-marker resume action (Task 3: `BlobSet`'s
|
|
150
|
+
* `resolvePendingIntent`-equivalent for a shred marker; a rehome marker is
|
|
151
|
+
* PR-2 scope and should be skipped by the callback, not thrown into here).
|
|
152
|
+
*/
|
|
153
|
+
export declare function sweepBlobIntents(adapter: NoydbStore, vault: string, getDEK: (collection: string) => Promise<EnclaveKey>, resume: BlobIntentResume, onResumeError?: (collection: string, recordId: string, err: unknown) => void): Promise<void>;
|