@noy-db/hub 0.2.0-pre.10 → 0.2.0-pre.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -0
- package/dist/aggregate/index.cjs +289 -12
- package/dist/aggregate/index.cjs.map +1 -1
- package/dist/aggregate/index.d.cts +2 -2
- package/dist/aggregate/index.d.ts +2 -2
- package/dist/aggregate/index.js +7 -7
- package/dist/aggregate/index.js.map +1 -1
- package/dist/attestation/index.cjs.map +1 -1
- package/dist/attestation/index.d.cts +3 -3
- package/dist/attestation/index.d.ts +3 -3
- package/dist/attestation/index.js +6 -6
- package/dist/blobs/index.cjs +28 -0
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +4 -4
- package/dist/blobs/index.d.ts +4 -4
- package/dist/blobs/index.js +5 -5
- package/dist/bundle/index.cjs +1468 -19
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +5 -5
- package/dist/bundle/index.d.ts +5 -5
- package/dist/bundle/index.js +9 -9
- package/dist/{chunk-7CEGU63S.js → chunk-4BHFNKTP.js} +2 -2
- package/dist/{chunk-5OEJ6GOT.js → chunk-5ARRXIVR.js} +2 -2
- package/dist/{chunk-DRXIZOFV.js → chunk-6AD5TBF2.js} +31 -3
- package/dist/chunk-6AD5TBF2.js.map +1 -0
- package/dist/{chunk-YM7LFCG7.js → chunk-6BYBVRZU.js} +3 -3
- package/dist/{chunk-5IXJGFF2.js → chunk-7JJE3OMJ.js} +5 -5
- package/dist/{chunk-HHOO7HGH.js → chunk-7LVRIW4G.js} +4 -4
- package/dist/{chunk-O6EJ6WTI.js → chunk-AGRC7NQQ.js} +62 -2
- package/dist/chunk-AGRC7NQQ.js.map +1 -0
- package/dist/{chunk-IMYKDWB4.js → chunk-B7GGYNKQ.js} +2 -2
- package/dist/{chunk-BDV7INMP.js → chunk-BXOUVUES.js} +4 -4
- package/dist/{chunk-FO3UEG4S.js → chunk-C2CIIQRG.js} +2 -2
- package/dist/{chunk-ZROPXHJY.js → chunk-CHBXWJZQ.js} +2 -2
- package/dist/{chunk-RYIL3PI2.js → chunk-CILT6V3V.js} +2 -2
- package/dist/{chunk-PXTQPZO4.js → chunk-DLTU4M2I.js} +6 -6
- package/dist/{chunk-GAUEWM7D.js → chunk-EKNUBIIQ.js} +4 -4
- package/dist/{chunk-HQSQC2XL.js → chunk-GFPR7VJS.js} +17 -4
- package/dist/chunk-GFPR7VJS.js.map +1 -0
- package/dist/{chunk-6EOXTJS2.js → chunk-HBAJDI2N.js} +5 -5
- package/dist/{chunk-PVUUIWHY.js → chunk-HLGDYFWR.js} +10 -3
- package/dist/chunk-HLGDYFWR.js.map +1 -0
- package/dist/{chunk-RRNA5GKT.js → chunk-IEPT7HVP.js} +2 -2
- package/dist/{chunk-R233SLY3.js → chunk-IUBHXEPJ.js} +2 -2
- package/dist/{chunk-CH22FZHT.js → chunk-L6BYRCYB.js} +2 -2
- package/dist/{chunk-5OX6XVNS.js → chunk-LOA2VCMS.js} +5 -5
- package/dist/{chunk-BB27JMWB.js → chunk-LSEW3ZZ2.js} +3 -3
- package/dist/{chunk-Y26YV5R3.js → chunk-LWSD4QPT.js} +3 -3
- package/dist/{chunk-WIRRPTFH.js → chunk-LYNNZEQD.js} +1 -1
- package/dist/chunk-LYNNZEQD.js.map +1 -0
- package/dist/{chunk-26NK23DZ.js → chunk-M45IRXDM.js} +3 -3
- package/dist/{chunk-CXJG63MA.js → chunk-NP6EZT44.js} +20 -6
- package/dist/chunk-NP6EZT44.js.map +1 -0
- package/dist/{chunk-GNHAC43Q.js → chunk-O53RIZCC.js} +5 -5
- package/dist/chunk-OPDTLHFA.js +783 -0
- package/dist/chunk-OPDTLHFA.js.map +1 -0
- package/dist/{chunk-LSTBFLL2.js → chunk-P3Z5Y2TS.js} +2 -2
- package/dist/{chunk-QSOYKKMD.js → chunk-P4EDT5ZP.js} +2 -2
- package/dist/{chunk-PC6ZEDRL.js → chunk-RHQYVHFH.js} +2 -2
- package/dist/{chunk-3LPV6BXR.js → chunk-RRDWXNBQ.js} +3 -3
- package/dist/{chunk-4CLICFEY.js → chunk-SJJQKNMP.js} +4 -4
- package/dist/{chunk-TY32C732.js → chunk-SZ4N3IL5.js} +5 -5
- package/dist/{chunk-4USCAEDT.js → chunk-TMHJEYW7.js} +502 -60
- package/dist/chunk-TMHJEYW7.js.map +1 -0
- package/dist/{chunk-2N62W5YP.js → chunk-UA6G45ME.js} +3 -3
- package/dist/{chunk-6YLPHBKR.js → chunk-UOC7JMZO.js} +13 -4
- package/dist/chunk-UOC7JMZO.js.map +1 -0
- package/dist/{chunk-DAP2XL7Q.js → chunk-VOXMU6LB.js} +2 -2
- package/dist/chunk-WNRGOVLG.js +64 -0
- package/dist/chunk-WNRGOVLG.js.map +1 -0
- package/dist/{chunk-DJRWA3Q5.js → chunk-WUG3E423.js} +4 -4
- package/dist/{chunk-PM3QYWUU.js → chunk-XHM2SARW.js} +3 -3
- package/dist/{chunk-RC6SU5NO.js → chunk-XSIFXX54.js} +2 -2
- package/dist/{chunk-CXFOITNS.js → chunk-ZC7MNVYN.js} +2 -2
- package/dist/{chunk-6T2UDBKG.js → chunk-ZCFS7U4J.js} +2 -2
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +4 -4
- package/dist/consent/index.d.ts +4 -4
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-2CRLG4F4.js → crypto-AJB72OKN.js} +3 -3
- package/dist/{delegation-ZTRT2PRV.js → delegation-6FCWDRUS.js} +5 -5
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +5 -5
- package/dist/derivations/index.d.ts +5 -5
- package/dist/derivations/index.js +4 -4
- package/dist/{dev-unlock-BOEYl1xl.d.ts → dev-unlock-D3mpVFRc.d.ts} +1 -1
- package/dist/{dev-unlock-AglVnkPY.d.cts → dev-unlock-ckqa_Nso.d.cts} +1 -1
- package/dist/executor-7KSCEIFA.js +8 -0
- package/dist/executor-D2QMNGRJ.js +8 -0
- package/dist/executor-O5AZK7UW.js +11 -0
- package/dist/{fanout-sidecar-OKPMMPLG.js → fanout-sidecar-ZSKEQ6NI.js} +2 -2
- package/dist/guards/index.cjs +53 -1
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +12 -6
- package/dist/guards/index.d.ts +12 -6
- package/dist/guards/index.js +5 -3
- package/dist/{hash-B9m3_fhj.d.ts → hash-CTZVkXLx.d.ts} +1 -1
- package/dist/{hash-RVqz2zi8.d.cts → hash-rDSSd_oW.d.cts} +1 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +5 -5
- package/dist/history/index.d.ts +5 -5
- package/dist/history/index.js +5 -5
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +4 -4
- package/dist/i18n/index.d.ts +4 -4
- package/dist/i18n/index.js +6 -6
- package/dist/immutable-guard-C51vAHuh.d.cts +67 -0
- package/dist/immutable-guard-DyD0qg2k.d.ts +67 -0
- package/dist/index-CkFHr4OP.d.ts +1190 -0
- package/dist/index-Cmop06zJ.d.cts +1190 -0
- package/dist/index.cjs +1636 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -13
- package/dist/index.d.ts +46 -13
- package/dist/index.js +76 -44
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.js +2 -2
- package/dist/issue-YIYG4OW5.js +12 -0
- package/dist/{ledger-O7FXOG3D.js → ledger-5JMVF7PY.js} +5 -5
- package/dist/materialized-views/index.cjs.map +1 -1
- package/dist/materialized-views/index.d.cts +5 -6
- package/dist/materialized-views/index.d.ts +5 -6
- package/dist/materialized-views/index.js +6 -6
- package/dist/noydb-D5SLAJ6V.js +34 -0
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +5 -5
- package/dist/overlay-views/index.d.ts +5 -5
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +4 -4
- package/dist/periods/index.d.ts +4 -4
- package/dist/periods/index.js +5 -5
- package/dist/{public-envelope-HMYHZIRH.js → public-envelope-PFLZI5MO.js} +4 -4
- package/dist/query/index.cjs +293 -10
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +2 -2
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +4 -4
- package/dist/registry-BVQ5ITMF.js +8 -0
- package/dist/registry-JLP3QOLD.js +8 -0
- package/dist/{registry-ST2VNFZC.js → registry-NCY445U5.js} +3 -3
- package/dist/{revoke-S6JMSLUN.js → revoke-7RLGQWZ7.js} +6 -6
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +5 -5
- package/dist/session/index.d.ts +5 -5
- package/dist/session/index.js +3 -3
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +4 -4
- package/dist/shadow/index.d.ts +4 -4
- package/dist/shadow/index.js +2 -2
- package/dist/{signer-7NPTB3SQ.js → signer-6JF44I4A.js} +5 -5
- package/dist/snapshots/index.cjs.map +1 -1
- package/dist/snapshots/index.d.cts +4 -4
- package/dist/snapshots/index.d.ts +4 -4
- package/dist/snapshots/index.js +4 -4
- package/dist/{stale-VKXSXJF4.js → stale-UBLP3RJ3.js} +2 -2
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +4 -4
- package/dist/store/index.d.ts +4 -4
- package/dist/store/index.js +2 -2
- package/dist/strategy-rtpKDfTC.d.cts +2029 -0
- package/dist/strategy-rtpKDfTC.d.ts +2029 -0
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +3 -3
- package/dist/sync/index.d.ts +3 -3
- package/dist/sync/index.js +4 -4
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +4 -4
- package/dist/team/index.d.ts +4 -4
- package/dist/team/index.js +8 -8
- package/dist/tx/index.cjs +8 -1
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +4 -4
- package/dist/tx/index.d.ts +4 -4
- package/dist/tx/index.js +3 -3
- package/dist/{types-n2_IfwlQ.d.cts → types-BGwjsDef.d.cts} +520 -6
- package/dist/{types-CaNQm4i8.d.ts → types-DRdfwgTG.d.ts} +520 -6
- package/dist/{ulid-CLMjmyhG.d.cts → ulid-D4d0Xto3.d.cts} +1 -1
- package/dist/{ulid-B9SMWj5i.d.ts → ulid-DOTPZ5_h.d.ts} +1 -1
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/vault-group-Z4KB75ZH.js +450 -0
- package/dist/vault-group-Z4KB75ZH.js.map +1 -0
- package/dist/{with-derivation-CVIOPTUf.d.ts → with-derivation-B082Y_WQ.d.ts} +1 -1
- package/dist/{with-derivation-aKrtS7Jj.d.cts → with-derivation-CB1EdcFF.d.cts} +1 -1
- package/dist/{with-materialized-view-C1eA1_T_.d.cts → with-materialized-view-CzRg1Dpr.d.cts} +1 -1
- package/dist/{with-materialized-view-DaYaE8-Q.d.ts → with-materialized-view-Dw4SwjKl.d.ts} +1 -1
- package/dist/{with-overlayed-view-DleJfKcV.d.cts → with-overlayed-view-C9YFKXzn.d.cts} +1 -1
- package/dist/{with-overlayed-view-DQsh2p8H.d.ts → with-overlayed-view-CaCXeW26.d.ts} +1 -1
- package/package.json +3 -3
- package/dist/chunk-2LPPNWF6.js +0 -340
- package/dist/chunk-2LPPNWF6.js.map +0 -1
- package/dist/chunk-4USCAEDT.js.map +0 -1
- package/dist/chunk-6YLPHBKR.js.map +0 -1
- package/dist/chunk-C3WE6UJY.js +0 -19
- package/dist/chunk-C3WE6UJY.js.map +0 -1
- package/dist/chunk-CXJG63MA.js.map +0 -1
- package/dist/chunk-DRXIZOFV.js.map +0 -1
- package/dist/chunk-HQSQC2XL.js.map +0 -1
- package/dist/chunk-O6EJ6WTI.js.map +0 -1
- package/dist/chunk-PVUUIWHY.js.map +0 -1
- package/dist/chunk-WIRRPTFH.js.map +0 -1
- package/dist/executor-S76VN45G.js +0 -8
- package/dist/executor-UCXLIGLW.js +0 -11
- package/dist/executor-ZCNZJMGR.js +0 -8
- package/dist/index-B8bjExET.d.cts +0 -2434
- package/dist/index-DfUbNad8.d.ts +0 -2434
- package/dist/issue-3W6IVLKH.js +0 -12
- package/dist/noydb-YAZNH5TI.js +0 -34
- package/dist/registry-UFIK7CSR.js +0 -8
- package/dist/registry-ZGYYSM5I.js +0 -8
- package/dist/strategy-CT2LCKAX.d.cts +0 -613
- package/dist/strategy-CT2LCKAX.d.ts +0 -613
- package/dist/with-guard-DZQbPzoP.d.cts +0 -18
- package/dist/with-guard-DseETUrF.d.ts +0 -18
- /package/dist/{chunk-7CEGU63S.js.map → chunk-4BHFNKTP.js.map} +0 -0
- /package/dist/{chunk-5OEJ6GOT.js.map → chunk-5ARRXIVR.js.map} +0 -0
- /package/dist/{chunk-YM7LFCG7.js.map → chunk-6BYBVRZU.js.map} +0 -0
- /package/dist/{chunk-5IXJGFF2.js.map → chunk-7JJE3OMJ.js.map} +0 -0
- /package/dist/{chunk-HHOO7HGH.js.map → chunk-7LVRIW4G.js.map} +0 -0
- /package/dist/{chunk-IMYKDWB4.js.map → chunk-B7GGYNKQ.js.map} +0 -0
- /package/dist/{chunk-BDV7INMP.js.map → chunk-BXOUVUES.js.map} +0 -0
- /package/dist/{chunk-FO3UEG4S.js.map → chunk-C2CIIQRG.js.map} +0 -0
- /package/dist/{chunk-ZROPXHJY.js.map → chunk-CHBXWJZQ.js.map} +0 -0
- /package/dist/{chunk-RYIL3PI2.js.map → chunk-CILT6V3V.js.map} +0 -0
- /package/dist/{chunk-PXTQPZO4.js.map → chunk-DLTU4M2I.js.map} +0 -0
- /package/dist/{chunk-GAUEWM7D.js.map → chunk-EKNUBIIQ.js.map} +0 -0
- /package/dist/{chunk-6EOXTJS2.js.map → chunk-HBAJDI2N.js.map} +0 -0
- /package/dist/{chunk-RRNA5GKT.js.map → chunk-IEPT7HVP.js.map} +0 -0
- /package/dist/{chunk-R233SLY3.js.map → chunk-IUBHXEPJ.js.map} +0 -0
- /package/dist/{chunk-CH22FZHT.js.map → chunk-L6BYRCYB.js.map} +0 -0
- /package/dist/{chunk-5OX6XVNS.js.map → chunk-LOA2VCMS.js.map} +0 -0
- /package/dist/{chunk-BB27JMWB.js.map → chunk-LSEW3ZZ2.js.map} +0 -0
- /package/dist/{chunk-Y26YV5R3.js.map → chunk-LWSD4QPT.js.map} +0 -0
- /package/dist/{chunk-26NK23DZ.js.map → chunk-M45IRXDM.js.map} +0 -0
- /package/dist/{chunk-GNHAC43Q.js.map → chunk-O53RIZCC.js.map} +0 -0
- /package/dist/{chunk-LSTBFLL2.js.map → chunk-P3Z5Y2TS.js.map} +0 -0
- /package/dist/{chunk-QSOYKKMD.js.map → chunk-P4EDT5ZP.js.map} +0 -0
- /package/dist/{chunk-PC6ZEDRL.js.map → chunk-RHQYVHFH.js.map} +0 -0
- /package/dist/{chunk-3LPV6BXR.js.map → chunk-RRDWXNBQ.js.map} +0 -0
- /package/dist/{chunk-4CLICFEY.js.map → chunk-SJJQKNMP.js.map} +0 -0
- /package/dist/{chunk-TY32C732.js.map → chunk-SZ4N3IL5.js.map} +0 -0
- /package/dist/{chunk-2N62W5YP.js.map → chunk-UA6G45ME.js.map} +0 -0
- /package/dist/{chunk-DAP2XL7Q.js.map → chunk-VOXMU6LB.js.map} +0 -0
- /package/dist/{chunk-DJRWA3Q5.js.map → chunk-WUG3E423.js.map} +0 -0
- /package/dist/{chunk-PM3QYWUU.js.map → chunk-XHM2SARW.js.map} +0 -0
- /package/dist/{chunk-RC6SU5NO.js.map → chunk-XSIFXX54.js.map} +0 -0
- /package/dist/{chunk-CXFOITNS.js.map → chunk-ZC7MNVYN.js.map} +0 -0
- /package/dist/{chunk-6T2UDBKG.js.map → chunk-ZCFS7U4J.js.map} +0 -0
- /package/dist/{crypto-2CRLG4F4.js.map → crypto-AJB72OKN.js.map} +0 -0
- /package/dist/{delegation-ZTRT2PRV.js.map → delegation-6FCWDRUS.js.map} +0 -0
- /package/dist/{executor-S76VN45G.js.map → executor-7KSCEIFA.js.map} +0 -0
- /package/dist/{executor-UCXLIGLW.js.map → executor-D2QMNGRJ.js.map} +0 -0
- /package/dist/{executor-ZCNZJMGR.js.map → executor-O5AZK7UW.js.map} +0 -0
- /package/dist/{fanout-sidecar-OKPMMPLG.js.map → fanout-sidecar-ZSKEQ6NI.js.map} +0 -0
- /package/dist/{issue-3W6IVLKH.js.map → issue-YIYG4OW5.js.map} +0 -0
- /package/dist/{ledger-O7FXOG3D.js.map → ledger-5JMVF7PY.js.map} +0 -0
- /package/dist/{noydb-YAZNH5TI.js.map → noydb-D5SLAJ6V.js.map} +0 -0
- /package/dist/{public-envelope-HMYHZIRH.js.map → public-envelope-PFLZI5MO.js.map} +0 -0
- /package/dist/{registry-ST2VNFZC.js.map → registry-BVQ5ITMF.js.map} +0 -0
- /package/dist/{registry-UFIK7CSR.js.map → registry-JLP3QOLD.js.map} +0 -0
- /package/dist/{registry-ZGYYSM5I.js.map → registry-NCY445U5.js.map} +0 -0
- /package/dist/{revoke-S6JMSLUN.js.map → revoke-7RLGQWZ7.js.map} +0 -0
- /package/dist/{signer-7NPTB3SQ.js.map → signer-6JF44I4A.js.map} +0 -0
- /package/dist/{stale-VKXSXJF4.js.map → stale-UBLP3RJ3.js.map} +0 -0
package/dist/bundle/index.cjs
CHANGED
|
@@ -31,7 +31,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
33
|
// src/errors.ts
|
|
34
|
-
var NoydbError, DecryptionError, TamperedError, InvalidKeyError, KeyringCorruptError, NoAccessError, ReadOnlyError, PermissionDeniedError, ExportCapabilityError, KeyringExpiredError, ImportCapabilityError, StoreCapabilityError, PrivilegeEscalationError, FieldFrozenError, InvariantError, AmendmentForbiddenError, TierNotGrantedError, ElevationExpiredError, AlreadyElevatedError, TierDemoteDeniedError, DelegationTargetMissingError, ConflictError, LedgerContentionError, BundleVersionConflictError, ValidationError, SchemaValidationError, SchemaUpdateError, SchemaFenceError, MigrationRequiredError, QuiesceTimeoutError, GroupCardinalityError, IndexRequiredError, UniqueConstraintError, UnsupportedIndexOptionError, IndexWriteFailureError, BundleIntegrityError, BundleSealMismatchError, ReservedCollectionNameError, LocaleNotSpecifiedError, TranslatorNotConfiguredError, BackupLedgerError, BackupCorruptedError, PartitionExtractionError, TransferSealError, AdoptionStateError, AttestationError, JoinTooLargeError, CrossJoinTooLargeError, CrossJoinSourceUnknownError, DanglingReferenceError, DerivationCycleError, DerivationOutputShapeError, DerivationCapExceededError, MaterializedViewCycleError, MaterializedViewSourceUnknownError, MaterializedViewTooLargeError, OverlayBaseIsVirtualError, OverlayCollectionUnavailableError, OverlayNameCollisionError, OverlayIdMismatchError;
|
|
34
|
+
var NoydbError, DecryptionError, TamperedError, InvalidKeyError, KeyringCorruptError, NoAccessError, ReadOnlyError, PermissionDeniedError, ExportCapabilityError, KeyringExpiredError, ImportCapabilityError, StoreCapabilityError, PrivilegeEscalationError, FieldFrozenError, InvariantError, AmendmentForbiddenError, TierNotGrantedError, ElevationExpiredError, AlreadyElevatedError, TierDemoteDeniedError, DelegationTargetMissingError, ConflictError, LedgerContentionError, SequenceContentionError, SequenceOfflineError, BundleVersionConflictError, ValidationError, SchemaValidationError, SchemaUpdateError, SchemaFenceError, MigrationRequiredError, QuiesceTimeoutError, GroupCardinalityError, IndexRequiredError, UniqueConstraintError, UnsupportedIndexOptionError, IndexWriteFailureError, BundleIntegrityError, BundleSealMismatchError, ReservedCollectionNameError, LocaleNotSpecifiedError, TranslatorNotConfiguredError, BackupLedgerError, BackupCorruptedError, PartitionExtractionError, TransferSealError, AdoptionStateError, AttestationError, JoinTooLargeError, CrossJoinTooLargeError, CrossJoinSourceUnknownError, DanglingReferenceError, DerivationCycleError, DerivationOutputShapeError, DerivationCapExceededError, MaterializedViewCycleError, MaterializedViewSourceUnknownError, MaterializedViewTooLargeError, OverlayBaseIsVirtualError, OverlayCollectionUnavailableError, OverlayNameCollisionError, OverlayIdMismatchError, UnknownShardError, ShardProvisioningError, VaultTemplateNotFoundError;
|
|
35
35
|
var init_errors = __esm({
|
|
36
36
|
"src/errors.ts"() {
|
|
37
37
|
"use strict";
|
|
@@ -266,6 +266,28 @@ var init_errors = __esm({
|
|
|
266
266
|
this.attempts = attempts;
|
|
267
267
|
}
|
|
268
268
|
};
|
|
269
|
+
SequenceContentionError = class extends NoydbError {
|
|
270
|
+
sequence;
|
|
271
|
+
attempts;
|
|
272
|
+
constructor(sequence, attempts) {
|
|
273
|
+
super(
|
|
274
|
+
"SEQUENCE_CONTENTION",
|
|
275
|
+
`vault.sequence("${sequence}").next(): failed to allocate after ${attempts} optimistic-CAS retries`
|
|
276
|
+
);
|
|
277
|
+
this.name = "SequenceContentionError";
|
|
278
|
+
this.sequence = sequence;
|
|
279
|
+
this.attempts = attempts;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
SequenceOfflineError = class extends NoydbError {
|
|
283
|
+
constructor() {
|
|
284
|
+
super(
|
|
285
|
+
"SEQUENCE_OFFLINE",
|
|
286
|
+
"vault.sequence().next() requires an online CAS-capable store (capabilities.casAtomic). Gap-free numbering cannot be serialized offline."
|
|
287
|
+
);
|
|
288
|
+
this.name = "SequenceOfflineError";
|
|
289
|
+
}
|
|
290
|
+
};
|
|
269
291
|
BundleVersionConflictError = class extends NoydbError {
|
|
270
292
|
/** The bundle handle of the newer remote version that rejected the push. */
|
|
271
293
|
remoteVersion;
|
|
@@ -679,6 +701,39 @@ Resolutions:
|
|
|
679
701
|
this.expected = expected;
|
|
680
702
|
}
|
|
681
703
|
};
|
|
704
|
+
UnknownShardError = class extends NoydbError {
|
|
705
|
+
partitionKey;
|
|
706
|
+
constructor(partitionKey, groupName) {
|
|
707
|
+
super(
|
|
708
|
+
"SHARD_UNKNOWN",
|
|
709
|
+
`No shard for partition key "${partitionKey}" in vault group "${groupName}" and autoCreate is disabled. Call group.createShard(${JSON.stringify(partitionKey)}) first, or enable sharding.autoCreate.`
|
|
710
|
+
);
|
|
711
|
+
this.name = "UnknownShardError";
|
|
712
|
+
this.partitionKey = partitionKey;
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
ShardProvisioningError = class extends NoydbError {
|
|
716
|
+
vaultId;
|
|
717
|
+
constructor(vaultId, partitionKey) {
|
|
718
|
+
super(
|
|
719
|
+
"SHARD_PROVISIONING",
|
|
720
|
+
`Registry has a row for partition "${partitionKey}" (vault "${vaultId}") but that vault is not provisioned in the store. Refusing to recreate it \u2014 the registry and store have diverged. Investigate before retrying.`
|
|
721
|
+
);
|
|
722
|
+
this.name = "ShardProvisioningError";
|
|
723
|
+
this.vaultId = vaultId;
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
VaultTemplateNotFoundError = class extends NoydbError {
|
|
727
|
+
templateName;
|
|
728
|
+
constructor(templateName) {
|
|
729
|
+
super(
|
|
730
|
+
"VAULT_TEMPLATE_NOT_FOUND",
|
|
731
|
+
`No vault template registered under "${templateName}". Register it with db.withVaultTemplate(${JSON.stringify(templateName)}, { version, configure }) before opening the vault group.`
|
|
732
|
+
);
|
|
733
|
+
this.name = "VaultTemplateNotFoundError";
|
|
734
|
+
this.templateName = templateName;
|
|
735
|
+
}
|
|
736
|
+
};
|
|
682
737
|
}
|
|
683
738
|
});
|
|
684
739
|
|
|
@@ -1498,6 +1553,18 @@ async function loadKeyring(adapter, vault, userId, passphrase) {
|
|
|
1498
1553
|
...keyringFile.policy !== void 0 && { policy: keyringFile.policy }
|
|
1499
1554
|
};
|
|
1500
1555
|
}
|
|
1556
|
+
async function assertKeyringOpenAllowed(store, vault, userId, create) {
|
|
1557
|
+
const keyringUsers = await store.list(vault, "_keyring");
|
|
1558
|
+
if (keyringUsers.includes(userId)) return;
|
|
1559
|
+
if (!create) {
|
|
1560
|
+
throw new NoAccessError(`Vault "${vault}" not opened: create disabled and no keyring for "${userId}".`);
|
|
1561
|
+
}
|
|
1562
|
+
if (keyringUsers.length > 0) {
|
|
1563
|
+
throw new NoAccessError(
|
|
1564
|
+
`No keyring for user "${userId}" in vault "${vault}" (held by other principals) \u2014 refusing to self-provision.`
|
|
1565
|
+
);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1501
1568
|
async function createOwnerKeyring(adapter, vault, userId, passphrase, passphraseOpts) {
|
|
1502
1569
|
if (passphraseOpts?.validate && !passphraseOpts.allowWeakPassphrase) {
|
|
1503
1570
|
assertStrongPassphrase(passphrase, passphraseOpts);
|
|
@@ -3591,6 +3658,328 @@ var init_core = __esm({
|
|
|
3591
3658
|
}
|
|
3592
3659
|
});
|
|
3593
3660
|
|
|
3661
|
+
// src/money/fixed-point.ts
|
|
3662
|
+
function expandExponent(s) {
|
|
3663
|
+
const m = /^([+-]?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
|
|
3664
|
+
if (!m) return s;
|
|
3665
|
+
const sign = m[1] === "-" ? "-" : "";
|
|
3666
|
+
const intp = m[2];
|
|
3667
|
+
const frac = m[3] ?? "";
|
|
3668
|
+
const exp = Number(m[4]);
|
|
3669
|
+
const digits = intp + frac;
|
|
3670
|
+
const pointPos = intp.length + exp;
|
|
3671
|
+
let body;
|
|
3672
|
+
if (pointPos <= 0) {
|
|
3673
|
+
body = "0." + "0".repeat(-pointPos) + digits;
|
|
3674
|
+
} else if (pointPos >= digits.length) {
|
|
3675
|
+
body = digits + "0".repeat(pointPos - digits.length);
|
|
3676
|
+
} else {
|
|
3677
|
+
body = digits.slice(0, pointPos) + "." + digits.slice(pointPos);
|
|
3678
|
+
}
|
|
3679
|
+
return sign + body;
|
|
3680
|
+
}
|
|
3681
|
+
function toCanonicalDecimalString(input) {
|
|
3682
|
+
let s;
|
|
3683
|
+
if (typeof input === "number") {
|
|
3684
|
+
if (!Number.isFinite(input)) return null;
|
|
3685
|
+
s = String(input);
|
|
3686
|
+
} else {
|
|
3687
|
+
s = input.trim();
|
|
3688
|
+
}
|
|
3689
|
+
s = expandExponent(s);
|
|
3690
|
+
if (s.startsWith("+")) s = s.slice(1);
|
|
3691
|
+
if (!/^-?(\d+(\.\d*)?|\.\d+)$/.test(s)) return null;
|
|
3692
|
+
return s;
|
|
3693
|
+
}
|
|
3694
|
+
function shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, mode) {
|
|
3695
|
+
switch (mode) {
|
|
3696
|
+
case "up":
|
|
3697
|
+
return true;
|
|
3698
|
+
case "down":
|
|
3699
|
+
return false;
|
|
3700
|
+
case "ceil":
|
|
3701
|
+
return !negative;
|
|
3702
|
+
case "floor":
|
|
3703
|
+
return negative;
|
|
3704
|
+
case "half-up":
|
|
3705
|
+
return firstDiscarded >= 5;
|
|
3706
|
+
case "half-down":
|
|
3707
|
+
return firstDiscarded > 5 || firstDiscarded === 5 && hasMoreNonZeroAfterFirst;
|
|
3708
|
+
case "half-even":
|
|
3709
|
+
if (firstDiscarded > 5) return true;
|
|
3710
|
+
if (firstDiscarded < 5) return false;
|
|
3711
|
+
return hasMoreNonZeroAfterFirst || lastKeptDigit % 2 === 1;
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
function parseToScaledInt(input, scale, rounding) {
|
|
3715
|
+
const canonical = toCanonicalDecimalString(input);
|
|
3716
|
+
if (canonical === null) return { ok: false, reason: "nonfinite" };
|
|
3717
|
+
const negative = canonical.startsWith("-");
|
|
3718
|
+
const unsigned = negative ? canonical.slice(1) : canonical;
|
|
3719
|
+
const dot = unsigned.indexOf(".");
|
|
3720
|
+
const intPart = dot === -1 ? unsigned : unsigned.slice(0, dot);
|
|
3721
|
+
const fracPart = dot === -1 ? "" : unsigned.slice(dot + 1);
|
|
3722
|
+
const intDigits = intPart === "" ? "0" : intPart;
|
|
3723
|
+
if (fracPart.length <= scale) {
|
|
3724
|
+
const keep2 = fracPart.padEnd(scale, "0");
|
|
3725
|
+
const magnitude2 = BigInt(intDigits + keep2);
|
|
3726
|
+
return { ok: true, value: negative && magnitude2 !== 0n ? -magnitude2 : magnitude2 };
|
|
3727
|
+
}
|
|
3728
|
+
const keep = fracPart.slice(0, scale);
|
|
3729
|
+
const tail = fracPart.slice(scale);
|
|
3730
|
+
const magnitudeDigits = intDigits + keep;
|
|
3731
|
+
let magnitude = BigInt(magnitudeDigits);
|
|
3732
|
+
if (/^0+$/.test(tail)) {
|
|
3733
|
+
return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
|
|
3734
|
+
}
|
|
3735
|
+
if (rounding === void 0) return { ok: false, reason: "precision" };
|
|
3736
|
+
const lastKeptDigit = Number(magnitudeDigits[magnitudeDigits.length - 1]);
|
|
3737
|
+
const firstDiscarded = Number(tail[0]);
|
|
3738
|
+
const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1));
|
|
3739
|
+
if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {
|
|
3740
|
+
magnitude += 1n;
|
|
3741
|
+
}
|
|
3742
|
+
return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
|
|
3743
|
+
}
|
|
3744
|
+
function formatScaledInt(value, scale) {
|
|
3745
|
+
const negative = value < 0n;
|
|
3746
|
+
const abs = (negative ? -value : value).toString();
|
|
3747
|
+
if (scale === 0) return (negative ? "-" : "") + abs;
|
|
3748
|
+
const padded = abs.padStart(scale + 1, "0");
|
|
3749
|
+
const cut = padded.length - scale;
|
|
3750
|
+
const intPart = padded.slice(0, cut);
|
|
3751
|
+
const fracPart = padded.slice(cut);
|
|
3752
|
+
return (negative ? "-" : "") + intPart + "." + fracPart;
|
|
3753
|
+
}
|
|
3754
|
+
var init_fixed_point = __esm({
|
|
3755
|
+
"src/money/fixed-point.ts"() {
|
|
3756
|
+
"use strict";
|
|
3757
|
+
}
|
|
3758
|
+
});
|
|
3759
|
+
|
|
3760
|
+
// src/money/iso4217.ts
|
|
3761
|
+
function scaleForCurrency(code) {
|
|
3762
|
+
const v = MINOR_UNITS[code];
|
|
3763
|
+
return v === void 0 ? null : v;
|
|
3764
|
+
}
|
|
3765
|
+
var MINOR_UNITS;
|
|
3766
|
+
var init_iso4217 = __esm({
|
|
3767
|
+
"src/money/iso4217.ts"() {
|
|
3768
|
+
"use strict";
|
|
3769
|
+
MINOR_UNITS = {
|
|
3770
|
+
// 2-decimal majors
|
|
3771
|
+
EUR: 2,
|
|
3772
|
+
USD: 2,
|
|
3773
|
+
GBP: 2,
|
|
3774
|
+
CHF: 2,
|
|
3775
|
+
CAD: 2,
|
|
3776
|
+
AUD: 2,
|
|
3777
|
+
NZD: 2,
|
|
3778
|
+
SGD: 2,
|
|
3779
|
+
HKD: 2,
|
|
3780
|
+
CNY: 2,
|
|
3781
|
+
INR: 2,
|
|
3782
|
+
BRL: 2,
|
|
3783
|
+
MXN: 2,
|
|
3784
|
+
ZAR: 2,
|
|
3785
|
+
RUB: 2,
|
|
3786
|
+
TRY: 2,
|
|
3787
|
+
PLN: 2,
|
|
3788
|
+
SEK: 2,
|
|
3789
|
+
NOK: 2,
|
|
3790
|
+
DKK: 2,
|
|
3791
|
+
CZK: 2,
|
|
3792
|
+
HUF: 2,
|
|
3793
|
+
RON: 2,
|
|
3794
|
+
ILS: 2,
|
|
3795
|
+
THB: 2,
|
|
3796
|
+
PHP: 2,
|
|
3797
|
+
MYR: 2,
|
|
3798
|
+
IDR: 2,
|
|
3799
|
+
AED: 2,
|
|
3800
|
+
SAR: 2,
|
|
3801
|
+
QAR: 2,
|
|
3802
|
+
EGP: 2,
|
|
3803
|
+
// 0-decimal
|
|
3804
|
+
JPY: 0,
|
|
3805
|
+
KRW: 0,
|
|
3806
|
+
ISK: 0,
|
|
3807
|
+
CLP: 0,
|
|
3808
|
+
VND: 0,
|
|
3809
|
+
XOF: 0,
|
|
3810
|
+
XAF: 0,
|
|
3811
|
+
PYG: 0,
|
|
3812
|
+
// 3-decimal
|
|
3813
|
+
BHD: 3,
|
|
3814
|
+
KWD: 3,
|
|
3815
|
+
OMR: 3,
|
|
3816
|
+
TND: 3,
|
|
3817
|
+
JOD: 3,
|
|
3818
|
+
IQD: 3,
|
|
3819
|
+
LYD: 3
|
|
3820
|
+
};
|
|
3821
|
+
}
|
|
3822
|
+
});
|
|
3823
|
+
|
|
3824
|
+
// src/money/descriptor.ts
|
|
3825
|
+
var MoneyPrecisionError, MoneyUnsupportedError;
|
|
3826
|
+
var init_descriptor = __esm({
|
|
3827
|
+
"src/money/descriptor.ts"() {
|
|
3828
|
+
"use strict";
|
|
3829
|
+
init_errors();
|
|
3830
|
+
MoneyPrecisionError = class extends NoydbError {
|
|
3831
|
+
constructor(field, value, scale) {
|
|
3832
|
+
super(
|
|
3833
|
+
"MONEY_PRECISION",
|
|
3834
|
+
`money: value ${JSON.stringify(value)} for field "${field}" exceeds scale ${scale} and no rounding mode is configured`
|
|
3835
|
+
);
|
|
3836
|
+
this.field = field;
|
|
3837
|
+
this.value = value;
|
|
3838
|
+
this.scale = scale;
|
|
3839
|
+
this.name = "MoneyPrecisionError";
|
|
3840
|
+
}
|
|
3841
|
+
field;
|
|
3842
|
+
value;
|
|
3843
|
+
scale;
|
|
3844
|
+
};
|
|
3845
|
+
MoneyUnsupportedError = class extends NoydbError {
|
|
3846
|
+
constructor(field, message) {
|
|
3847
|
+
super(
|
|
3848
|
+
"MONEY_UNSUPPORTED",
|
|
3849
|
+
message ?? `money: operation is not supported on field "${field}" \u2014 use sum() and count() and divide at the boundary`
|
|
3850
|
+
);
|
|
3851
|
+
this.field = field;
|
|
3852
|
+
this.name = "MoneyUnsupportedError";
|
|
3853
|
+
}
|
|
3854
|
+
field;
|
|
3855
|
+
};
|
|
3856
|
+
}
|
|
3857
|
+
});
|
|
3858
|
+
|
|
3859
|
+
// src/money/normalize.ts
|
|
3860
|
+
function isMoneyValueObject(v) {
|
|
3861
|
+
return typeof v === "object" && v !== null && "currency" in v;
|
|
3862
|
+
}
|
|
3863
|
+
function quantizeAmount(field, input, scale, rounding) {
|
|
3864
|
+
const r = parseToScaledInt(input, scale, rounding);
|
|
3865
|
+
if (!r.ok) {
|
|
3866
|
+
if (r.reason === "precision") throw new MoneyPrecisionError(field, input, scale);
|
|
3867
|
+
throw new TypeError(`money: field "${field}" value ${JSON.stringify(input)} is not a finite decimal`);
|
|
3868
|
+
}
|
|
3869
|
+
return r.value.toString();
|
|
3870
|
+
}
|
|
3871
|
+
function quantizeMoneyFields(record, moneyFields) {
|
|
3872
|
+
const out = { ...record };
|
|
3873
|
+
for (const [field, desc] of Object.entries(moneyFields)) {
|
|
3874
|
+
const raw = out[field];
|
|
3875
|
+
if (raw === null || raw === void 0) continue;
|
|
3876
|
+
if (desc.mode === "fixed") {
|
|
3877
|
+
const currency2 = desc.fixedCurrency;
|
|
3878
|
+
out[field] = quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
|
|
3879
|
+
continue;
|
|
3880
|
+
}
|
|
3881
|
+
let amount;
|
|
3882
|
+
let currency;
|
|
3883
|
+
if (isMoneyValueObject(raw)) {
|
|
3884
|
+
currency = String(raw.currency);
|
|
3885
|
+
amount = raw.amount;
|
|
3886
|
+
} else {
|
|
3887
|
+
const sole = desc.soleCurrency();
|
|
3888
|
+
if (sole === void 0) {
|
|
3889
|
+
throw new TypeError(
|
|
3890
|
+
`money: field "${field}" is multi-currency \u2014 write { amount, currency }, not a bare amount`
|
|
3891
|
+
);
|
|
3892
|
+
}
|
|
3893
|
+
currency = sole;
|
|
3894
|
+
amount = raw;
|
|
3895
|
+
}
|
|
3896
|
+
const scale = desc.scaleFor(currency);
|
|
3897
|
+
out[field] = { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
|
|
3898
|
+
}
|
|
3899
|
+
return out;
|
|
3900
|
+
}
|
|
3901
|
+
function formatCurrency(decimal, currency, scale, locale) {
|
|
3902
|
+
const fmt = new Intl.NumberFormat(locale, {
|
|
3903
|
+
style: "currency",
|
|
3904
|
+
currency,
|
|
3905
|
+
minimumFractionDigits: scale,
|
|
3906
|
+
maximumFractionDigits: scale
|
|
3907
|
+
});
|
|
3908
|
+
return fmt.format(decimal);
|
|
3909
|
+
}
|
|
3910
|
+
function decodeMoneyFields(record, moneyFields, locale) {
|
|
3911
|
+
const out = { ...record };
|
|
3912
|
+
const format = locale !== "raw";
|
|
3913
|
+
const fmtLocale = typeof locale === "string" && locale !== "raw" ? locale : "en-US";
|
|
3914
|
+
for (const [field, desc] of Object.entries(moneyFields)) {
|
|
3915
|
+
const stored = out[field];
|
|
3916
|
+
if (stored === null || stored === void 0) continue;
|
|
3917
|
+
let currency;
|
|
3918
|
+
let scaledIntString;
|
|
3919
|
+
if (desc.mode === "fixed") {
|
|
3920
|
+
if (typeof stored !== "string" && typeof stored !== "number") continue;
|
|
3921
|
+
currency = desc.fixedCurrency;
|
|
3922
|
+
scaledIntString = String(stored);
|
|
3923
|
+
} else {
|
|
3924
|
+
if (!isMoneyValueObject(stored)) continue;
|
|
3925
|
+
const amount = stored.amount;
|
|
3926
|
+
if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") continue;
|
|
3927
|
+
currency = stored.currency;
|
|
3928
|
+
scaledIntString = String(amount);
|
|
3929
|
+
}
|
|
3930
|
+
const scale = desc.scaleFor(currency);
|
|
3931
|
+
const decimal = formatScaledInt(BigInt(scaledIntString), scale);
|
|
3932
|
+
out[field] = desc.mode === "fixed" ? decimal : { amount: decimal, currency };
|
|
3933
|
+
if (format) {
|
|
3934
|
+
out[`${field}Formatted`] = formatCurrency(decimal, currency, scale, fmtLocale);
|
|
3935
|
+
out[`${field}Number`] = Number(decimal);
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
return out;
|
|
3939
|
+
}
|
|
3940
|
+
var init_normalize = __esm({
|
|
3941
|
+
"src/money/normalize.ts"() {
|
|
3942
|
+
"use strict";
|
|
3943
|
+
init_fixed_point();
|
|
3944
|
+
init_descriptor();
|
|
3945
|
+
}
|
|
3946
|
+
});
|
|
3947
|
+
|
|
3948
|
+
// src/computed/index.ts
|
|
3949
|
+
function evalComputedFields(record, computed, id) {
|
|
3950
|
+
const out = { ...record };
|
|
3951
|
+
for (const [field, fn] of Object.entries(computed)) {
|
|
3952
|
+
try {
|
|
3953
|
+
out[field] = fn(out);
|
|
3954
|
+
} catch (cause) {
|
|
3955
|
+
throw new ComputedFieldError(field, id, cause);
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
return out;
|
|
3959
|
+
}
|
|
3960
|
+
var ComputedFieldError;
|
|
3961
|
+
var init_computed = __esm({
|
|
3962
|
+
"src/computed/index.ts"() {
|
|
3963
|
+
"use strict";
|
|
3964
|
+
init_errors();
|
|
3965
|
+
ComputedFieldError = class extends NoydbError {
|
|
3966
|
+
constructor(field, id, cause) {
|
|
3967
|
+
super(
|
|
3968
|
+
"COMPUTED_FIELD",
|
|
3969
|
+
`computed field "${field}" threw for record "${id}": ` + (cause instanceof Error ? cause.message : String(cause))
|
|
3970
|
+
);
|
|
3971
|
+
this.field = field;
|
|
3972
|
+
this.id = id;
|
|
3973
|
+
this.cause = cause;
|
|
3974
|
+
this.name = "ComputedFieldError";
|
|
3975
|
+
}
|
|
3976
|
+
field;
|
|
3977
|
+
id;
|
|
3978
|
+
cause;
|
|
3979
|
+
};
|
|
3980
|
+
}
|
|
3981
|
+
});
|
|
3982
|
+
|
|
3594
3983
|
// src/i18n/strategy.ts
|
|
3595
3984
|
function notEnabled(op) {
|
|
3596
3985
|
return new Error(
|
|
@@ -4075,6 +4464,189 @@ var init_strategy4 = __esm({
|
|
|
4075
4464
|
}
|
|
4076
4465
|
});
|
|
4077
4466
|
|
|
4467
|
+
// src/money/money-reducer.ts
|
|
4468
|
+
function toScaledInt(v) {
|
|
4469
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "bigint") {
|
|
4470
|
+
try {
|
|
4471
|
+
return BigInt(v);
|
|
4472
|
+
} catch {
|
|
4473
|
+
return null;
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
return null;
|
|
4477
|
+
}
|
|
4478
|
+
function readMoney(record, field, desc) {
|
|
4479
|
+
const raw = readPath(record, field);
|
|
4480
|
+
if (raw === null || raw === void 0) return null;
|
|
4481
|
+
if (desc.mode === "fixed") {
|
|
4482
|
+
const value2 = toScaledInt(raw);
|
|
4483
|
+
return value2 === null ? null : { currency: desc.fixedCurrency, value: value2 };
|
|
4484
|
+
}
|
|
4485
|
+
if (typeof raw !== "object") return null;
|
|
4486
|
+
const o = raw;
|
|
4487
|
+
if (typeof o.currency !== "string") return null;
|
|
4488
|
+
const value = toScaledInt(o.amount);
|
|
4489
|
+
return value === null ? null : { currency: o.currency, value };
|
|
4490
|
+
}
|
|
4491
|
+
function targetScaleFor(desc, currency) {
|
|
4492
|
+
if (desc.allows(currency)) return desc.scaleFor(currency);
|
|
4493
|
+
const s = scaleForCurrency(currency);
|
|
4494
|
+
if (s === null) {
|
|
4495
|
+
throw new Error(`money: cannot determine scale for conversion target "${currency}"`);
|
|
4496
|
+
}
|
|
4497
|
+
return s;
|
|
4498
|
+
}
|
|
4499
|
+
function parseRate(rate) {
|
|
4500
|
+
const s = String(rate).trim();
|
|
4501
|
+
const neg = s.startsWith("-");
|
|
4502
|
+
const body = neg ? s.slice(1) : s;
|
|
4503
|
+
const dot = body.indexOf(".");
|
|
4504
|
+
const intPart = dot === -1 ? body : body.slice(0, dot);
|
|
4505
|
+
const fracPart = dot === -1 ? "" : body.slice(dot + 1);
|
|
4506
|
+
const int = BigInt((intPart === "" ? "0" : intPart) + fracPart);
|
|
4507
|
+
return { int: neg ? -int : int, scale: fracPart.length };
|
|
4508
|
+
}
|
|
4509
|
+
function divRoundHalfEven(n, d) {
|
|
4510
|
+
const q = n / d;
|
|
4511
|
+
const r = n % d;
|
|
4512
|
+
const twiceR = (r < 0n ? -r : r) * 2n;
|
|
4513
|
+
if (twiceR < d) return q;
|
|
4514
|
+
if (twiceR > d) return q + (n < 0n ? -1n : 1n);
|
|
4515
|
+
return q % 2n === 0n ? q : q + (n < 0n ? -1n : 1n);
|
|
4516
|
+
}
|
|
4517
|
+
function convertScaled(value, srcScale, rate, targetScale) {
|
|
4518
|
+
const { int: rateInt, scale: rateScale } = parseRate(rate);
|
|
4519
|
+
const product = value * rateInt;
|
|
4520
|
+
const curScale = srcScale + rateScale;
|
|
4521
|
+
if (curScale === targetScale) return product;
|
|
4522
|
+
if (curScale < targetScale) return product * 10n ** BigInt(targetScale - curScale);
|
|
4523
|
+
return divRoundHalfEven(product, 10n ** BigInt(curScale - targetScale));
|
|
4524
|
+
}
|
|
4525
|
+
function finalizeSum(state, desc, convertTo, fx) {
|
|
4526
|
+
if (convertTo !== void 0) {
|
|
4527
|
+
if (fx === void 0) {
|
|
4528
|
+
throw new Error(`money: sum convertTo "${convertTo}" requires an fx rate map`);
|
|
4529
|
+
}
|
|
4530
|
+
const targetScale = targetScaleFor(desc, convertTo);
|
|
4531
|
+
let total = 0n;
|
|
4532
|
+
for (const [cur, v] of state) {
|
|
4533
|
+
if (cur === convertTo) {
|
|
4534
|
+
total += convertScaled(v, desc.scaleFor(cur), 1, targetScale);
|
|
4535
|
+
continue;
|
|
4536
|
+
}
|
|
4537
|
+
const rate = fx[`${cur}->${convertTo}`];
|
|
4538
|
+
if (rate === void 0) {
|
|
4539
|
+
throw new Error(`money: no fx rate for "${cur}->${convertTo}"`);
|
|
4540
|
+
}
|
|
4541
|
+
total += convertScaled(v, desc.scaleFor(cur), rate, targetScale);
|
|
4542
|
+
}
|
|
4543
|
+
return formatScaledInt(total, targetScale);
|
|
4544
|
+
}
|
|
4545
|
+
if (desc.mode === "fixed") {
|
|
4546
|
+
const cur = desc.fixedCurrency;
|
|
4547
|
+
return formatScaledInt(state.get(cur) ?? 0n, desc.scaleFor(cur));
|
|
4548
|
+
}
|
|
4549
|
+
const out = {};
|
|
4550
|
+
for (const [cur, v] of state) out[cur] = formatScaledInt(v, desc.scaleFor(cur));
|
|
4551
|
+
return out;
|
|
4552
|
+
}
|
|
4553
|
+
function moneySumReducer(field, desc, convertTo, fx) {
|
|
4554
|
+
return {
|
|
4555
|
+
op: "sum",
|
|
4556
|
+
field,
|
|
4557
|
+
init: () => /* @__PURE__ */ new Map(),
|
|
4558
|
+
step: (state, record) => {
|
|
4559
|
+
const m = readMoney(record, field, desc);
|
|
4560
|
+
if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) + m.value);
|
|
4561
|
+
return state;
|
|
4562
|
+
},
|
|
4563
|
+
remove: (state, record) => {
|
|
4564
|
+
const m = readMoney(record, field, desc);
|
|
4565
|
+
if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) - m.value);
|
|
4566
|
+
return state;
|
|
4567
|
+
},
|
|
4568
|
+
finalize: (state) => finalizeSum(state, desc, convertTo, fx)
|
|
4569
|
+
};
|
|
4570
|
+
}
|
|
4571
|
+
function extremum(values, op) {
|
|
4572
|
+
let out = values[0];
|
|
4573
|
+
for (let i = 1; i < values.length; i++) {
|
|
4574
|
+
const v = values[i];
|
|
4575
|
+
if (op === "min" ? v < out : v > out) out = v;
|
|
4576
|
+
}
|
|
4577
|
+
return out;
|
|
4578
|
+
}
|
|
4579
|
+
function moneyMinMaxReducer(op, field, desc) {
|
|
4580
|
+
return {
|
|
4581
|
+
op,
|
|
4582
|
+
field,
|
|
4583
|
+
init: () => /* @__PURE__ */ new Map(),
|
|
4584
|
+
step: (state, record) => {
|
|
4585
|
+
const m = readMoney(record, field, desc);
|
|
4586
|
+
if (m) {
|
|
4587
|
+
const arr = state.get(m.currency);
|
|
4588
|
+
if (arr) arr.push(m.value);
|
|
4589
|
+
else state.set(m.currency, [m.value]);
|
|
4590
|
+
}
|
|
4591
|
+
return state;
|
|
4592
|
+
},
|
|
4593
|
+
remove: (state, record) => {
|
|
4594
|
+
const m = readMoney(record, field, desc);
|
|
4595
|
+
if (m) {
|
|
4596
|
+
const arr = state.get(m.currency);
|
|
4597
|
+
if (arr) {
|
|
4598
|
+
const idx = arr.indexOf(m.value);
|
|
4599
|
+
if (idx >= 0) arr.splice(idx, 1);
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4602
|
+
return state;
|
|
4603
|
+
},
|
|
4604
|
+
finalize: (state) => {
|
|
4605
|
+
if (desc.mode === "fixed") {
|
|
4606
|
+
const cur = desc.fixedCurrency;
|
|
4607
|
+
const arr = state.get(cur);
|
|
4608
|
+
if (!arr || arr.length === 0) return null;
|
|
4609
|
+
return formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
|
|
4610
|
+
}
|
|
4611
|
+
const out = {};
|
|
4612
|
+
for (const [cur, arr] of state) {
|
|
4613
|
+
if (arr.length > 0) out[cur] = formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
|
|
4614
|
+
}
|
|
4615
|
+
return out;
|
|
4616
|
+
}
|
|
4617
|
+
};
|
|
4618
|
+
}
|
|
4619
|
+
function wrapMoneyReducers(spec, moneyFields) {
|
|
4620
|
+
let changed = false;
|
|
4621
|
+
const out = {};
|
|
4622
|
+
for (const [key, reducer] of Object.entries(spec)) {
|
|
4623
|
+
const field = reducer.field;
|
|
4624
|
+
const desc = field ? moneyFields[field] : void 0;
|
|
4625
|
+
if (desc && reducer.op === "avg") {
|
|
4626
|
+
throw new MoneyUnsupportedError(
|
|
4627
|
+
field,
|
|
4628
|
+
`avg() is not supported on money field "${field}" in v1 \u2014 use sum() and count() and divide at the boundary.`
|
|
4629
|
+
);
|
|
4630
|
+
}
|
|
4631
|
+
if (desc && (reducer.op === "sum" || reducer.op === "min" || reducer.op === "max")) {
|
|
4632
|
+
changed = true;
|
|
4633
|
+
out[key] = reducer.op === "sum" ? moneySumReducer(field, desc, reducer.convertTo, reducer.fx) : moneyMinMaxReducer(reducer.op, field, desc);
|
|
4634
|
+
} else {
|
|
4635
|
+
out[key] = reducer;
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
return changed ? out : spec;
|
|
4639
|
+
}
|
|
4640
|
+
var init_money_reducer = __esm({
|
|
4641
|
+
"src/money/money-reducer.ts"() {
|
|
4642
|
+
"use strict";
|
|
4643
|
+
init_predicate();
|
|
4644
|
+
init_fixed_point();
|
|
4645
|
+
init_iso4217();
|
|
4646
|
+
init_descriptor();
|
|
4647
|
+
}
|
|
4648
|
+
});
|
|
4649
|
+
|
|
4078
4650
|
// src/query/builder.ts
|
|
4079
4651
|
function executePlanWithSource(source, plan, joinContext) {
|
|
4080
4652
|
const hasCrossJoins = plan.clauses.some((c) => c.type === "crossJoin");
|
|
@@ -4351,6 +4923,7 @@ var init_builder = __esm({
|
|
|
4351
4923
|
init_errors();
|
|
4352
4924
|
init_live();
|
|
4353
4925
|
init_strategy4();
|
|
4926
|
+
init_money_reducer();
|
|
4354
4927
|
EMPTY_PLAN = {
|
|
4355
4928
|
clauses: [],
|
|
4356
4929
|
orderBy: [],
|
|
@@ -4796,6 +5369,10 @@ var init_builder = __esm({
|
|
|
4796
5369
|
* partition boundaries without an API break.
|
|
4797
5370
|
*/
|
|
4798
5371
|
aggregate(spec) {
|
|
5372
|
+
const moneyFields = this.source.moneyFields;
|
|
5373
|
+
if (moneyFields) {
|
|
5374
|
+
spec = wrapMoneyReducers(spec, moneyFields);
|
|
5375
|
+
}
|
|
4799
5376
|
const source = this.source;
|
|
4800
5377
|
const clauses = this.plan.clauses;
|
|
4801
5378
|
const joinCtx = this.joinContext;
|
|
@@ -4843,13 +5420,15 @@ var init_builder = __esm({
|
|
|
4843
5420
|
executeRecords,
|
|
4844
5421
|
field,
|
|
4845
5422
|
upstreams,
|
|
4846
|
-
dictLabelResolver
|
|
5423
|
+
dictLabelResolver,
|
|
5424
|
+
this.source.moneyFields
|
|
4847
5425
|
);
|
|
4848
5426
|
}
|
|
4849
5427
|
return this.aggregateStrategy.groupByN(
|
|
4850
5428
|
executeRecords,
|
|
4851
5429
|
fields,
|
|
4852
|
-
upstreams
|
|
5430
|
+
upstreams,
|
|
5431
|
+
this.source.moneyFields
|
|
4853
5432
|
);
|
|
4854
5433
|
}
|
|
4855
5434
|
/**
|
|
@@ -4970,6 +5549,29 @@ var init_builder = __esm({
|
|
|
4970
5549
|
}
|
|
4971
5550
|
});
|
|
4972
5551
|
|
|
5552
|
+
// src/aggregate/aggregation.ts
|
|
5553
|
+
function reduceRecords(records, spec) {
|
|
5554
|
+
const state = {};
|
|
5555
|
+
for (const key of Object.keys(spec)) {
|
|
5556
|
+
state[key] = spec[key].init();
|
|
5557
|
+
}
|
|
5558
|
+
for (const record of records) {
|
|
5559
|
+
for (const key of Object.keys(spec)) {
|
|
5560
|
+
state[key] = spec[key].step(state[key], record);
|
|
5561
|
+
}
|
|
5562
|
+
}
|
|
5563
|
+
const result = {};
|
|
5564
|
+
for (const key of Object.keys(spec)) {
|
|
5565
|
+
result[key] = spec[key].finalize(state[key]);
|
|
5566
|
+
}
|
|
5567
|
+
return result;
|
|
5568
|
+
}
|
|
5569
|
+
var init_aggregation = __esm({
|
|
5570
|
+
"src/aggregate/aggregation.ts"() {
|
|
5571
|
+
"use strict";
|
|
5572
|
+
}
|
|
5573
|
+
});
|
|
5574
|
+
|
|
4973
5575
|
// src/aggregate/canonical-key.ts
|
|
4974
5576
|
function canonicalGroupKey(fields, row) {
|
|
4975
5577
|
const sorted = [...fields].sort();
|
|
@@ -7005,6 +7607,8 @@ var init_collection = __esm({
|
|
|
7005
7607
|
init_types();
|
|
7006
7608
|
init_strategy();
|
|
7007
7609
|
init_core();
|
|
7610
|
+
init_normalize();
|
|
7611
|
+
init_computed();
|
|
7008
7612
|
init_strategy2();
|
|
7009
7613
|
init_policy();
|
|
7010
7614
|
init_crypto();
|
|
@@ -7173,6 +7777,18 @@ var init_collection = __esm({
|
|
|
7173
7777
|
* fields when a locale is requested.
|
|
7174
7778
|
*/
|
|
7175
7779
|
dictKeyFields;
|
|
7780
|
+
/**
|
|
7781
|
+
* Money field descriptors keyed by field path. Declared via the
|
|
7782
|
+
* `moneyFields` collection option: `put()` quantizes to a scaled-int
|
|
7783
|
+
* string, `get()`/`list()` decode back. Mutable so {@link _applyMoneyFields}
|
|
7784
|
+
* can attach descriptors to a collection MV-analysis pre-created.
|
|
7785
|
+
*/
|
|
7786
|
+
moneyFields;
|
|
7787
|
+
/**
|
|
7788
|
+
* Computed scalar fields, evaluated first on every `put()`. Mutable for
|
|
7789
|
+
* the same MV-pre-creation reconcile as {@link moneyFields}.
|
|
7790
|
+
*/
|
|
7791
|
+
computed;
|
|
7176
7792
|
/**
|
|
7177
7793
|
* Async callback provided by the Vault that resolves a dict key
|
|
7178
7794
|
* to its label for a given locale. Used by the locale-read path for
|
|
@@ -7314,6 +7930,8 @@ var init_collection = __esm({
|
|
|
7314
7930
|
this.joinResolver = opts.joinResolver;
|
|
7315
7931
|
this.i18nFields = opts.i18nFields;
|
|
7316
7932
|
this.dictKeyFields = opts.dictKeyFields;
|
|
7933
|
+
this.moneyFields = opts.moneyFields;
|
|
7934
|
+
this.computed = opts.computed;
|
|
7317
7935
|
this.dictLabelResolver = opts.dictLabelResolver;
|
|
7318
7936
|
this.i18nPutValidator = opts.i18nPutValidator;
|
|
7319
7937
|
this.autoTranslateHook = opts.autoTranslateHook;
|
|
@@ -7438,6 +8056,19 @@ var init_collection = __esm({
|
|
|
7438
8056
|
getSchema() {
|
|
7439
8057
|
return this.schema;
|
|
7440
8058
|
}
|
|
8059
|
+
/**
|
|
8060
|
+
* @internal — attach money descriptors post-construction. MV dependency
|
|
8061
|
+
* analysis auto-creates a source collection (without options) during
|
|
8062
|
+
* `openVault`, before the user's `collection(name, { moneyFields })`
|
|
8063
|
+
* declaration; this reconciles that ordering. First-wins. Not public.
|
|
8064
|
+
*/
|
|
8065
|
+
_applyMoneyFields(moneyFields) {
|
|
8066
|
+
if (this.moneyFields === void 0) this.moneyFields = moneyFields;
|
|
8067
|
+
}
|
|
8068
|
+
/** @internal — attach computed fields post-construction. See {@link _applyMoneyFields}. */
|
|
8069
|
+
_applyComputed(computed) {
|
|
8070
|
+
if (this.computed === void 0) this.computed = computed;
|
|
8071
|
+
}
|
|
7441
8072
|
/**
|
|
7442
8073
|
* Get a single record by ID.
|
|
7443
8074
|
*
|
|
@@ -7609,7 +8240,7 @@ var init_collection = __esm({
|
|
|
7609
8240
|
existingRecord = null;
|
|
7610
8241
|
}
|
|
7611
8242
|
}
|
|
7612
|
-
|
|
8243
|
+
const gateEvent = {
|
|
7613
8244
|
op: existingEnv ? "update" : "create",
|
|
7614
8245
|
vault: this.vault,
|
|
7615
8246
|
collection: this.name,
|
|
@@ -7619,12 +8250,20 @@ var init_collection = __esm({
|
|
|
7619
8250
|
existingVersion: existingEnv?._v ?? 0,
|
|
7620
8251
|
existingTs: existingEnv?._ts,
|
|
7621
8252
|
userId: this.keyring.userId,
|
|
7622
|
-
role: this.keyring.role
|
|
7623
|
-
|
|
8253
|
+
role: this.keyring.role,
|
|
8254
|
+
...this.computed !== void 0 ? { computedFieldNames: new Set(Object.keys(this.computed)) } : {}
|
|
8255
|
+
};
|
|
8256
|
+
await this.subsystemBus.dispatchGate("beforePut", gateEvent);
|
|
8257
|
+
}
|
|
8258
|
+
if (this.computed !== void 0) {
|
|
8259
|
+
record = evalComputedFields(record, this.computed, id);
|
|
7624
8260
|
}
|
|
7625
8261
|
if (this.schema !== void 0) {
|
|
7626
8262
|
record = await validateSchemaInput(this.schema, record, `put(${id})`);
|
|
7627
8263
|
}
|
|
8264
|
+
if (this.moneyFields) {
|
|
8265
|
+
record = quantizeMoneyFields(record, this.moneyFields);
|
|
8266
|
+
}
|
|
7628
8267
|
if (this.i18nFields) {
|
|
7629
8268
|
const obj = record;
|
|
7630
8269
|
for (const [field, descriptor] of Object.entries(this.i18nFields)) {
|
|
@@ -8446,7 +9085,8 @@ var init_collection = __esm({
|
|
|
8446
9085
|
// fields. The Query builder consults these when present and falls
|
|
8447
9086
|
// back to a linear scan otherwise.
|
|
8448
9087
|
getIndexes: () => this.getIndexes(),
|
|
8449
|
-
lookupById: (id) => this.cache.get(id)?.record
|
|
9088
|
+
lookupById: (id) => this.cache.get(id)?.record,
|
|
9089
|
+
...this.moneyFields ? { moneyFields: this.moneyFields } : {}
|
|
8450
9090
|
};
|
|
8451
9091
|
const resolver = this.joinResolver;
|
|
8452
9092
|
const leftCollection = this.name;
|
|
@@ -9128,10 +9768,14 @@ var init_collection = __esm({
|
|
|
9128
9768
|
async applyLocaleToRecord(record, localeOpts) {
|
|
9129
9769
|
const hasI18n = this.i18nFields && Object.keys(this.i18nFields).length > 0;
|
|
9130
9770
|
const hasDict = this.dictKeyFields && Object.keys(this.dictKeyFields).length > 0;
|
|
9131
|
-
|
|
9771
|
+
const hasMoney = this.moneyFields && Object.keys(this.moneyFields).length > 0;
|
|
9772
|
+
if (!hasI18n && !hasDict && !hasMoney) return record;
|
|
9132
9773
|
const locale = localeOpts?.locale ?? this.defaultLocale;
|
|
9133
|
-
if (!locale) return record;
|
|
9134
9774
|
let result = record;
|
|
9775
|
+
if (hasMoney && this.moneyFields) {
|
|
9776
|
+
result = decodeMoneyFields(result, this.moneyFields, typeof locale === "string" ? locale : void 0);
|
|
9777
|
+
}
|
|
9778
|
+
if (!locale) return result;
|
|
9135
9779
|
if (hasI18n && this.i18nFields) {
|
|
9136
9780
|
result = this.i18nStrategy.applyI18nLocale(result, this.i18nFields, locale, localeOpts?.fallback);
|
|
9137
9781
|
}
|
|
@@ -9970,6 +10614,182 @@ var init_virtual_collection = __esm({
|
|
|
9970
10614
|
}
|
|
9971
10615
|
});
|
|
9972
10616
|
|
|
10617
|
+
// src/archive/engine.ts
|
|
10618
|
+
function isHeld(policy, record) {
|
|
10619
|
+
if (!policy.legalHold) return false;
|
|
10620
|
+
try {
|
|
10621
|
+
return policy.legalHold(record);
|
|
10622
|
+
} catch {
|
|
10623
|
+
return true;
|
|
10624
|
+
}
|
|
10625
|
+
}
|
|
10626
|
+
async function runArchive(ctx, options = {}) {
|
|
10627
|
+
const maxArchives = options.maxArchives ?? Infinity;
|
|
10628
|
+
const dryRun = options.dryRun === true;
|
|
10629
|
+
let archived = 0;
|
|
10630
|
+
let held = 0;
|
|
10631
|
+
let scanned = 0;
|
|
10632
|
+
const byCollection = {};
|
|
10633
|
+
outer: for (const collection of ctx.collectionsWithPolicy()) {
|
|
10634
|
+
const policy = ctx.getPolicy(collection);
|
|
10635
|
+
if (!policy) continue;
|
|
10636
|
+
byCollection[collection] = { archived: 0, held: 0 };
|
|
10637
|
+
for (const id of await ctx.listRecordIds(collection)) {
|
|
10638
|
+
if (archived >= maxArchives) break outer;
|
|
10639
|
+
const record = await ctx.getRecord(collection, id).catch(() => null);
|
|
10640
|
+
if (record === null) continue;
|
|
10641
|
+
scanned += 1;
|
|
10642
|
+
let eligible = false;
|
|
10643
|
+
try {
|
|
10644
|
+
eligible = policy.archiveWhen(record);
|
|
10645
|
+
} catch {
|
|
10646
|
+
eligible = false;
|
|
10647
|
+
}
|
|
10648
|
+
if (!eligible) continue;
|
|
10649
|
+
if (isHeld(policy, record)) {
|
|
10650
|
+
held += 1;
|
|
10651
|
+
byCollection[collection].held += 1;
|
|
10652
|
+
continue;
|
|
10653
|
+
}
|
|
10654
|
+
if (!dryRun) {
|
|
10655
|
+
const env = await ctx.getEnvelope(collection, id);
|
|
10656
|
+
if (!env) continue;
|
|
10657
|
+
await ctx.archiveStore.put(ctx.vaultId, collection, id, env);
|
|
10658
|
+
await ctx.removeFromPrimary(collection, id);
|
|
10659
|
+
}
|
|
10660
|
+
archived += 1;
|
|
10661
|
+
byCollection[collection].archived += 1;
|
|
10662
|
+
}
|
|
10663
|
+
}
|
|
10664
|
+
return { archived, held, scanned, byCollection };
|
|
10665
|
+
}
|
|
10666
|
+
async function runRestore(ctx, collection, id) {
|
|
10667
|
+
const env = await ctx.archiveStore.get(ctx.vaultId, collection, id);
|
|
10668
|
+
if (!env) return false;
|
|
10669
|
+
await ctx.restoreToPrimary(collection, id, env);
|
|
10670
|
+
await ctx.archiveStore.delete(ctx.vaultId, collection, id);
|
|
10671
|
+
return true;
|
|
10672
|
+
}
|
|
10673
|
+
async function runListArchived(ctx, collection) {
|
|
10674
|
+
const collections = collection ? [collection] : ctx.collectionsWithPolicy();
|
|
10675
|
+
const out = [];
|
|
10676
|
+
for (const c of collections) {
|
|
10677
|
+
const ids = await ctx.archiveStore.list(ctx.vaultId, c);
|
|
10678
|
+
for (const id of ids) out.push({ collection: c, id });
|
|
10679
|
+
}
|
|
10680
|
+
return out;
|
|
10681
|
+
}
|
|
10682
|
+
var init_engine = __esm({
|
|
10683
|
+
"src/archive/engine.ts"() {
|
|
10684
|
+
"use strict";
|
|
10685
|
+
}
|
|
10686
|
+
});
|
|
10687
|
+
|
|
10688
|
+
// src/archive/index.ts
|
|
10689
|
+
var init_archive = __esm({
|
|
10690
|
+
"src/archive/index.ts"() {
|
|
10691
|
+
"use strict";
|
|
10692
|
+
init_engine();
|
|
10693
|
+
}
|
|
10694
|
+
});
|
|
10695
|
+
|
|
10696
|
+
// src/sequence/index.ts
|
|
10697
|
+
async function sleepBackoff2(attempt) {
|
|
10698
|
+
const ceil = Math.min(2 ** attempt, 32);
|
|
10699
|
+
const ms = Math.floor(Math.random() * ceil);
|
|
10700
|
+
await new Promise((r) => setTimeout(r, ms));
|
|
10701
|
+
}
|
|
10702
|
+
var SEQUENCE_COLLECTION, MAX_NEXT_ATTEMPTS, SequenceStore;
|
|
10703
|
+
var init_sequence = __esm({
|
|
10704
|
+
"src/sequence/index.ts"() {
|
|
10705
|
+
"use strict";
|
|
10706
|
+
init_types();
|
|
10707
|
+
init_crypto();
|
|
10708
|
+
init_errors();
|
|
10709
|
+
SEQUENCE_COLLECTION = "_sequences";
|
|
10710
|
+
MAX_NEXT_ATTEMPTS = 16;
|
|
10711
|
+
SequenceStore = class {
|
|
10712
|
+
adapter;
|
|
10713
|
+
vault;
|
|
10714
|
+
encrypted;
|
|
10715
|
+
getDEK;
|
|
10716
|
+
actor;
|
|
10717
|
+
/**
|
|
10718
|
+
* Memoized DEK promise. The `_sequences` collection DEK is created on
|
|
10719
|
+
* first access; without sharing one promise, a burst of concurrent
|
|
10720
|
+
* `next()` calls would each trigger DEK creation and diverge (one
|
|
10721
|
+
* writer's ciphertext unreadable by another). One shared promise → one
|
|
10722
|
+
* DEK.
|
|
10723
|
+
*/
|
|
10724
|
+
dekPromise = null;
|
|
10725
|
+
constructor(opts) {
|
|
10726
|
+
this.adapter = opts.adapter;
|
|
10727
|
+
this.vault = opts.vault;
|
|
10728
|
+
this.encrypted = opts.encrypted;
|
|
10729
|
+
this.getDEK = opts.getDEK;
|
|
10730
|
+
this.actor = opts.actor;
|
|
10731
|
+
}
|
|
10732
|
+
/** A handle bound to one sequence name. */
|
|
10733
|
+
handle(name) {
|
|
10734
|
+
return {
|
|
10735
|
+
next: () => this.next(name),
|
|
10736
|
+
peek: () => this.peek(name)
|
|
10737
|
+
};
|
|
10738
|
+
}
|
|
10739
|
+
assertOnline() {
|
|
10740
|
+
if (this.adapter.capabilities?.casAtomic !== true) {
|
|
10741
|
+
throw new SequenceOfflineError();
|
|
10742
|
+
}
|
|
10743
|
+
}
|
|
10744
|
+
dek() {
|
|
10745
|
+
if (!this.dekPromise) this.dekPromise = this.getDEK(SEQUENCE_COLLECTION);
|
|
10746
|
+
return this.dekPromise;
|
|
10747
|
+
}
|
|
10748
|
+
async read(name) {
|
|
10749
|
+
const env = await this.adapter.get(this.vault, SEQUENCE_COLLECTION, name);
|
|
10750
|
+
if (!env) return { env: null, value: 0 };
|
|
10751
|
+
const json = this.encrypted ? await decrypt(env._iv, env._data, await this.dek()) : env._data;
|
|
10752
|
+
const state = JSON.parse(json);
|
|
10753
|
+
return { env, value: state.value };
|
|
10754
|
+
}
|
|
10755
|
+
async encryptState(state, version) {
|
|
10756
|
+
const json = JSON.stringify(state);
|
|
10757
|
+
if (!this.encrypted) {
|
|
10758
|
+
return { _noydb: NOYDB_FORMAT_VERSION, _v: version, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: "", _data: json, _by: this.actor };
|
|
10759
|
+
}
|
|
10760
|
+
const { iv, data } = await encrypt(json, await this.dek());
|
|
10761
|
+
return { _noydb: NOYDB_FORMAT_VERSION, _v: version, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: iv, _data: data, _by: this.actor };
|
|
10762
|
+
}
|
|
10763
|
+
async peek(name) {
|
|
10764
|
+
return (await this.read(name)).value;
|
|
10765
|
+
}
|
|
10766
|
+
async next(name) {
|
|
10767
|
+
this.assertOnline();
|
|
10768
|
+
let lastConflict;
|
|
10769
|
+
for (let attempt = 0; attempt < MAX_NEXT_ATTEMPTS; attempt++) {
|
|
10770
|
+
const { env, value } = await this.read(name);
|
|
10771
|
+
const nextValue = value + 1;
|
|
10772
|
+
const expectedVersion = env?._v ?? 0;
|
|
10773
|
+
const envelope = await this.encryptState({ value: nextValue }, expectedVersion + 1);
|
|
10774
|
+
try {
|
|
10775
|
+
await this.adapter.put(this.vault, SEQUENCE_COLLECTION, name, envelope, expectedVersion);
|
|
10776
|
+
return nextValue;
|
|
10777
|
+
} catch (err) {
|
|
10778
|
+
if (err instanceof ConflictError) {
|
|
10779
|
+
lastConflict = err;
|
|
10780
|
+
if (attempt < MAX_NEXT_ATTEMPTS - 1) await sleepBackoff2(attempt);
|
|
10781
|
+
continue;
|
|
10782
|
+
}
|
|
10783
|
+
throw err;
|
|
10784
|
+
}
|
|
10785
|
+
}
|
|
10786
|
+
void lastConflict;
|
|
10787
|
+
throw new SequenceContentionError(name, MAX_NEXT_ATTEMPTS);
|
|
10788
|
+
}
|
|
10789
|
+
};
|
|
10790
|
+
}
|
|
10791
|
+
});
|
|
10792
|
+
|
|
9973
10793
|
// src/shadow/strategy.ts
|
|
9974
10794
|
var NOT_ENABLED3, NO_SHADOW;
|
|
9975
10795
|
var init_strategy7 = __esm({
|
|
@@ -10262,6 +11082,7 @@ async function runCompaction(ctx, options = {}) {
|
|
|
10262
11082
|
let evicted = 0;
|
|
10263
11083
|
let records = 0;
|
|
10264
11084
|
let auditEntries = 0;
|
|
11085
|
+
let held = 0;
|
|
10265
11086
|
let collectionsWithPolicy = 0;
|
|
10266
11087
|
outer: for (const collectionName of allCollections) {
|
|
10267
11088
|
if (collectionName.startsWith("_")) continue;
|
|
@@ -10285,6 +11106,10 @@ async function runCompaction(ctx, options = {}) {
|
|
|
10285
11106
|
if (!policy) continue;
|
|
10286
11107
|
const reason = evaluatePolicy(policy, record, slot, now);
|
|
10287
11108
|
if (!reason) continue;
|
|
11109
|
+
if (isHeld2(policy, record, now)) {
|
|
11110
|
+
held += 1;
|
|
11111
|
+
continue;
|
|
11112
|
+
}
|
|
10288
11113
|
if (!dryRun) {
|
|
10289
11114
|
await ctx.deleteSlot(collectionName, recordId2, slot.name);
|
|
10290
11115
|
await writeAuditEntry(ctx, {
|
|
@@ -10309,9 +11134,32 @@ async function runCompaction(ctx, options = {}) {
|
|
|
10309
11134
|
records,
|
|
10310
11135
|
collections: collectionsWithPolicy,
|
|
10311
11136
|
auditEntries,
|
|
11137
|
+
held,
|
|
10312
11138
|
byCollection
|
|
10313
11139
|
};
|
|
10314
11140
|
}
|
|
11141
|
+
function isHeld2(policy, record, now) {
|
|
11142
|
+
if (policy.legalHold) {
|
|
11143
|
+
try {
|
|
11144
|
+
if (policy.legalHold(record)) return true;
|
|
11145
|
+
} catch {
|
|
11146
|
+
return true;
|
|
11147
|
+
}
|
|
11148
|
+
}
|
|
11149
|
+
if (policy.retainUntil) {
|
|
11150
|
+
try {
|
|
11151
|
+
const until = policy.retainUntil(record);
|
|
11152
|
+
if (until !== null && until !== void 0) {
|
|
11153
|
+
const t = until instanceof Date ? until.getTime() : typeof until === "number" ? until : Date.parse(String(until));
|
|
11154
|
+
if (!Number.isFinite(t)) return true;
|
|
11155
|
+
if (t > now.getTime()) return true;
|
|
11156
|
+
}
|
|
11157
|
+
} catch {
|
|
11158
|
+
return true;
|
|
11159
|
+
}
|
|
11160
|
+
}
|
|
11161
|
+
return false;
|
|
11162
|
+
}
|
|
10315
11163
|
function evaluatePolicy(policy, record, slot, now) {
|
|
10316
11164
|
let ttlTriggered = false;
|
|
10317
11165
|
let predicateTriggered = false;
|
|
@@ -12069,6 +12917,8 @@ var init_vault = __esm({
|
|
|
12069
12917
|
init_keyring();
|
|
12070
12918
|
init_errors();
|
|
12071
12919
|
init_errors();
|
|
12920
|
+
init_archive();
|
|
12921
|
+
init_sequence();
|
|
12072
12922
|
init_constants();
|
|
12073
12923
|
init_entry();
|
|
12074
12924
|
init_strategy3();
|
|
@@ -12127,6 +12977,10 @@ var init_vault = __esm({
|
|
|
12127
12977
|
* call throws with a pointer at `@noy-db/hub/blobs`.
|
|
12128
12978
|
*/
|
|
12129
12979
|
blobStrategy;
|
|
12980
|
+
/** Cold-storage archival strategy (the archive target store). */
|
|
12981
|
+
archiveStrategy;
|
|
12982
|
+
/** Per-collection record archival policies. Indexed by collection name. */
|
|
12983
|
+
archiveRegistry = /* @__PURE__ */ new Map();
|
|
12130
12984
|
indexStrategy;
|
|
12131
12985
|
aggregateStrategy;
|
|
12132
12986
|
crdtStrategy;
|
|
@@ -12230,6 +13084,8 @@ var init_vault = __esm({
|
|
|
12230
13084
|
* docstring.
|
|
12231
13085
|
*/
|
|
12232
13086
|
ledgerStore = null;
|
|
13087
|
+
/** Lazily-built atomic-sequence store. See {@link sequence}. */
|
|
13088
|
+
sequenceStore = null;
|
|
12233
13089
|
/**
|
|
12234
13090
|
* Background writes for persisted-schema envelopes (#schema-dump v0
|
|
12235
13091
|
* slice 1). One promise per `collection({ persistJsonSchema: true })`
|
|
@@ -12331,6 +13187,7 @@ var init_vault = __esm({
|
|
|
12331
13187
|
this.onRegisterConflictResolver = opts.onRegisterConflictResolver;
|
|
12332
13188
|
this.syncAdapter = opts.syncAdapter;
|
|
12333
13189
|
this.blobStrategy = opts.blobStrategy;
|
|
13190
|
+
this.archiveStrategy = opts.archiveStrategy;
|
|
12334
13191
|
this.indexStrategy = opts.indexStrategy;
|
|
12335
13192
|
this.aggregateStrategy = opts.aggregateStrategy;
|
|
12336
13193
|
this.crdtStrategy = opts.crdtStrategy;
|
|
@@ -12393,8 +13250,9 @@ var init_vault = __esm({
|
|
|
12393
13250
|
*. `put()` validates keys against the declared set; reads
|
|
12394
13251
|
* with `{ locale }` add `<field>Label` virtual fields.
|
|
12395
13252
|
*
|
|
12396
|
-
* Throws `ReservedCollectionNameError` for names starting with `_dict_
|
|
12397
|
-
* Use `vault.dictionary(name)`
|
|
13253
|
+
* Throws `ReservedCollectionNameError` for names starting with `_dict_` or
|
|
13254
|
+
* equal to `_sequences`. Use `vault.dictionary(name)` for dict collections
|
|
13255
|
+
* and `vault.sequence(name)` for sequence counters.
|
|
12398
13256
|
*
|
|
12399
13257
|
* Lazy mode + indexes is rejected at construction time — see the
|
|
12400
13258
|
* Collection constructor for the rationale.
|
|
@@ -12413,7 +13271,16 @@ var init_vault = __esm({
|
|
|
12413
13271
|
if (isDictCollectionName(collectionName)) {
|
|
12414
13272
|
throw new ReservedCollectionNameError(collectionName);
|
|
12415
13273
|
}
|
|
13274
|
+
if (collectionName === SEQUENCE_COLLECTION) {
|
|
13275
|
+
throw new ReservedCollectionNameError(collectionName);
|
|
13276
|
+
}
|
|
12416
13277
|
let coll = this.collectionCache.get(collectionName);
|
|
13278
|
+
if (coll && options?.moneyFields) {
|
|
13279
|
+
coll._applyMoneyFields(options.moneyFields);
|
|
13280
|
+
}
|
|
13281
|
+
if (coll && options?.computed) {
|
|
13282
|
+
coll._applyComputed(options.computed);
|
|
13283
|
+
}
|
|
12417
13284
|
if (!coll) {
|
|
12418
13285
|
if (options?.refs) {
|
|
12419
13286
|
this.refRegistry.register(collectionName, options.refs);
|
|
@@ -12424,6 +13291,9 @@ var init_vault = __esm({
|
|
|
12424
13291
|
if (options?.blobFields) {
|
|
12425
13292
|
this.blobFieldsRegistry.set(collectionName, options.blobFields);
|
|
12426
13293
|
}
|
|
13294
|
+
if (options?.archive) {
|
|
13295
|
+
this.archiveRegistry.set(collectionName, options.archive);
|
|
13296
|
+
}
|
|
12427
13297
|
if (options?.attestation !== void 0) {
|
|
12428
13298
|
this.attestationRegistry.set(collectionName, options.attestation);
|
|
12429
13299
|
}
|
|
@@ -12539,6 +13409,8 @@ var init_vault = __esm({
|
|
|
12539
13409
|
collOpts.onCrossTierAccess = (event) => this.emitCrossTier(event);
|
|
12540
13410
|
if (this.syncAdapter !== void 0) collOpts.syncAdapter = this.syncAdapter;
|
|
12541
13411
|
if (options?.i18nFields !== void 0) collOpts.i18nFields = options.i18nFields;
|
|
13412
|
+
if (options?.moneyFields !== void 0) collOpts.moneyFields = options.moneyFields;
|
|
13413
|
+
if (options?.computed !== void 0) collOpts.computed = options.computed;
|
|
12542
13414
|
if (options?.dictKeyFields !== void 0) {
|
|
12543
13415
|
collOpts.dictLabelResolver = async (dictName, key, locale, fallback) => {
|
|
12544
13416
|
const handle = this.dictionary(dictName);
|
|
@@ -12958,6 +13830,33 @@ var init_vault = __esm({
|
|
|
12958
13830
|
* await vault.compact({ maxEvictions: 1000 }) // cap batch
|
|
12959
13831
|
* ```
|
|
12960
13832
|
*/
|
|
13833
|
+
/**
|
|
13834
|
+
* Atomic, gap-free numbering. `vault.sequence('invoice-2026').next()`
|
|
13835
|
+
* returns 1, 2, 3, … with no gaps or duplicates under concurrency, via
|
|
13836
|
+
* an optimistic-CAS counter at `_sequences/<name>`. Each name is an
|
|
13837
|
+
* independent sequence.
|
|
13838
|
+
*
|
|
13839
|
+
* **Online-only:** `next()` throws `SequenceOfflineError` unless the
|
|
13840
|
+
* store advertises `capabilities.casAtomic` — gap-free numbering cannot
|
|
13841
|
+
* be serialized by an offline / non-CAS writer.
|
|
13842
|
+
*
|
|
13843
|
+
* ```ts
|
|
13844
|
+
* const n = await vault.sequence('invoice-2026').next() // 1, then 2, …
|
|
13845
|
+
* const cur = await vault.sequence('invoice-2026').peek() // current value, no allocation
|
|
13846
|
+
* ```
|
|
13847
|
+
*/
|
|
13848
|
+
sequence(name) {
|
|
13849
|
+
if (!this.sequenceStore) {
|
|
13850
|
+
this.sequenceStore = new SequenceStore({
|
|
13851
|
+
adapter: this.adapter,
|
|
13852
|
+
vault: this.name,
|
|
13853
|
+
encrypted: this.encrypted,
|
|
13854
|
+
getDEK: this.getDEK,
|
|
13855
|
+
actor: this.keyring.userId
|
|
13856
|
+
});
|
|
13857
|
+
}
|
|
13858
|
+
return this.sequenceStore.handle(name);
|
|
13859
|
+
}
|
|
12961
13860
|
async compact(options = {}) {
|
|
12962
13861
|
return runCompaction({
|
|
12963
13862
|
adapter: this.adapter,
|
|
@@ -12982,6 +13881,48 @@ var init_vault = __esm({
|
|
|
12982
13881
|
}
|
|
12983
13882
|
}, options);
|
|
12984
13883
|
}
|
|
13884
|
+
/**
|
|
13885
|
+
* Sweep records eligible by their collection's `archive` policy into the
|
|
13886
|
+
* cold archive store. Relocation is envelope-level (no re-encryption) and
|
|
13887
|
+
* bypasses guards + materialized-view dispatch, so issued/immutable
|
|
13888
|
+
* records over a sealed period can be archived without recomputing
|
|
13889
|
+
* finalized aggregates. A `legalHold` predicate blocks archival.
|
|
13890
|
+
* Requires `archiveStrategy: withArchive({ store })` in `createNoydb`.
|
|
13891
|
+
*/
|
|
13892
|
+
async archive(options = {}) {
|
|
13893
|
+
return runArchive(this._archiveContext(), options);
|
|
13894
|
+
}
|
|
13895
|
+
/** Relocate one archived record back to the primary store. Returns false if it was not archived. */
|
|
13896
|
+
async restore(collection, id) {
|
|
13897
|
+
return runRestore(this._archiveContext(), collection, id);
|
|
13898
|
+
}
|
|
13899
|
+
/** List archived record ids for a collection (or all collections with an archive policy). */
|
|
13900
|
+
async listArchived(collection) {
|
|
13901
|
+
return runListArchived(this._archiveContext(), collection);
|
|
13902
|
+
}
|
|
13903
|
+
_archiveContext() {
|
|
13904
|
+
const strategy = this.archiveStrategy;
|
|
13905
|
+
if (!strategy) {
|
|
13906
|
+
throw new Error(
|
|
13907
|
+
"vault.archive/restore/listArchived require `archiveStrategy: withArchive({ store })` in createNoydb"
|
|
13908
|
+
);
|
|
13909
|
+
}
|
|
13910
|
+
const archiveStore = strategy.store;
|
|
13911
|
+
return {
|
|
13912
|
+
vaultId: this.name,
|
|
13913
|
+
archiveStore,
|
|
13914
|
+
collectionsWithPolicy: () => [...this.archiveRegistry.keys()],
|
|
13915
|
+
getPolicy: (c) => this.archiveRegistry.get(c) ?? null,
|
|
13916
|
+
listRecordIds: (c) => this.adapter.list(this.name, c),
|
|
13917
|
+
getRecord: async (c, id) => await this.collection(c).get(id, { locale: "raw" }),
|
|
13918
|
+
getEnvelope: (c, id) => this.adapter.get(this.name, c, id),
|
|
13919
|
+
removeFromPrimary: (c, id) => this.collection(c)._internalDelete(id),
|
|
13920
|
+
restoreToPrimary: async (c, id, env) => {
|
|
13921
|
+
await this.adapter.put(this.name, c, id, env);
|
|
13922
|
+
await this.collection(c)._invalidateCacheEntry(id);
|
|
13923
|
+
}
|
|
13924
|
+
};
|
|
13925
|
+
}
|
|
12985
13926
|
exportBlobs(options = {}) {
|
|
12986
13927
|
this.assertCanExport("plaintext", "blob");
|
|
12987
13928
|
return createExportBlobsHandle(
|
|
@@ -14243,7 +15184,7 @@ var init_vault = __esm({
|
|
|
14243
15184
|
}
|
|
14244
15185
|
}
|
|
14245
15186
|
const internalSnapshot = {};
|
|
14246
|
-
for (const internalName of [LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION, SCHEMAS_COLLECTION]) {
|
|
15187
|
+
for (const internalName of [LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION, SCHEMAS_COLLECTION, SEQUENCE_COLLECTION]) {
|
|
14247
15188
|
const ids = await this.adapter.list(this.name, internalName);
|
|
14248
15189
|
if (ids.length === 0) continue;
|
|
14249
15190
|
const records = {};
|
|
@@ -15858,7 +16799,7 @@ function deny(gate, reason, required) {
|
|
|
15858
16799
|
return new PolicyDeniedError(gate, reason, required);
|
|
15859
16800
|
}
|
|
15860
16801
|
var DEFAULT_FRESHNESS_MS;
|
|
15861
|
-
var
|
|
16802
|
+
var init_engine2 = __esm({
|
|
15862
16803
|
"src/policy/engine.ts"() {
|
|
15863
16804
|
"use strict";
|
|
15864
16805
|
init_errors2();
|
|
@@ -15871,7 +16812,7 @@ var init_policy3 = __esm({
|
|
|
15871
16812
|
"src/policy/index.ts"() {
|
|
15872
16813
|
"use strict";
|
|
15873
16814
|
init_presets();
|
|
15874
|
-
|
|
16815
|
+
init_engine2();
|
|
15875
16816
|
init_storage5();
|
|
15876
16817
|
}
|
|
15877
16818
|
});
|
|
@@ -15915,14 +16856,21 @@ var init_executor3 = __esm({
|
|
|
15915
16856
|
* Compare existing vs incoming for each `frozenFields.fields` entry
|
|
15916
16857
|
* when `frozenFields.when(existing)` is true. Throws
|
|
15917
16858
|
* `FieldFrozenError` listing every changed frozen field.
|
|
16859
|
+
*
|
|
16860
|
+
* @param skipFields — field names that are schema-owned computed fields.
|
|
16861
|
+
* These are excluded from the comparison because `incoming` carries the
|
|
16862
|
+
* raw user input (computed fields not yet evaluated), so comparing
|
|
16863
|
+
* `existing[field]` vs `incoming[field]` would always look like a
|
|
16864
|
+
* change even when the computed result is unchanged.
|
|
15918
16865
|
*/
|
|
15919
|
-
async checkFrozenFields(guard, id, existing, incoming) {
|
|
16866
|
+
async checkFrozenFields(guard, id, existing, incoming, skipFields) {
|
|
15920
16867
|
const ff = guard.frozenFields;
|
|
15921
16868
|
if (!ff) return;
|
|
15922
16869
|
if (existing === null) return;
|
|
15923
16870
|
if (!ff.when(existing)) return;
|
|
15924
16871
|
const changed = [];
|
|
15925
16872
|
for (const f of ff.fields) {
|
|
16873
|
+
if (skipFields?.has(String(f))) continue;
|
|
15926
16874
|
if (existing[f] !== incoming[f]) {
|
|
15927
16875
|
if (!deepEqual(existing[f], incoming[f])) changed.push(String(f));
|
|
15928
16876
|
}
|
|
@@ -15951,6 +16899,476 @@ var init_executor3 = __esm({
|
|
|
15951
16899
|
}
|
|
15952
16900
|
});
|
|
15953
16901
|
|
|
16902
|
+
// src/federation/classify-skip.ts
|
|
16903
|
+
function classifyShardSkip(err) {
|
|
16904
|
+
return err instanceof NoAccessError ? "no-grant" : "error";
|
|
16905
|
+
}
|
|
16906
|
+
var init_classify_skip = __esm({
|
|
16907
|
+
"src/federation/classify-skip.ts"() {
|
|
16908
|
+
"use strict";
|
|
16909
|
+
init_errors();
|
|
16910
|
+
}
|
|
16911
|
+
});
|
|
16912
|
+
|
|
16913
|
+
// src/federation/cross-vault-live.ts
|
|
16914
|
+
var CrossVaultLive;
|
|
16915
|
+
var init_cross_vault_live = __esm({
|
|
16916
|
+
"src/federation/cross-vault-live.ts"() {
|
|
16917
|
+
"use strict";
|
|
16918
|
+
CrossVaultLive = class {
|
|
16919
|
+
snapshot;
|
|
16920
|
+
error = null;
|
|
16921
|
+
ready;
|
|
16922
|
+
subs = /* @__PURE__ */ new Set();
|
|
16923
|
+
unsubChange;
|
|
16924
|
+
opts;
|
|
16925
|
+
stopped = false;
|
|
16926
|
+
computing = false;
|
|
16927
|
+
dirty = false;
|
|
16928
|
+
scheduled = false;
|
|
16929
|
+
timer = null;
|
|
16930
|
+
resolveReady;
|
|
16931
|
+
settledOnce = false;
|
|
16932
|
+
constructor(opts) {
|
|
16933
|
+
this.opts = opts;
|
|
16934
|
+
this.snapshot = opts.initialSnapshot;
|
|
16935
|
+
this.ready = new Promise((res) => {
|
|
16936
|
+
this.resolveReady = res;
|
|
16937
|
+
});
|
|
16938
|
+
this.unsubChange = opts.subscribeToChanges((e) => {
|
|
16939
|
+
if (this.stopped || !opts.isRelevant(e)) return;
|
|
16940
|
+
this.schedule();
|
|
16941
|
+
});
|
|
16942
|
+
this.schedule();
|
|
16943
|
+
}
|
|
16944
|
+
subscribe(cb) {
|
|
16945
|
+
if (this.stopped) return () => {
|
|
16946
|
+
};
|
|
16947
|
+
this.subs.add(cb);
|
|
16948
|
+
return () => this.subs.delete(cb);
|
|
16949
|
+
}
|
|
16950
|
+
stop() {
|
|
16951
|
+
if (this.stopped) return;
|
|
16952
|
+
this.stopped = true;
|
|
16953
|
+
this.unsubChange();
|
|
16954
|
+
if (this.timer !== null) clearTimeout(this.timer);
|
|
16955
|
+
this.subs.clear();
|
|
16956
|
+
if (!this.settledOnce) this.resolveReady();
|
|
16957
|
+
}
|
|
16958
|
+
schedule() {
|
|
16959
|
+
if (this.stopped) return;
|
|
16960
|
+
if (this.computing) {
|
|
16961
|
+
this.dirty = true;
|
|
16962
|
+
return;
|
|
16963
|
+
}
|
|
16964
|
+
if (this.scheduled) return;
|
|
16965
|
+
this.scheduled = true;
|
|
16966
|
+
const run = () => {
|
|
16967
|
+
this.scheduled = false;
|
|
16968
|
+
void this.runCompute();
|
|
16969
|
+
};
|
|
16970
|
+
const ms = this.opts.debounceMs ?? 0;
|
|
16971
|
+
if (ms > 0) this.timer = setTimeout(run, ms);
|
|
16972
|
+
else queueMicrotask(run);
|
|
16973
|
+
}
|
|
16974
|
+
async runCompute() {
|
|
16975
|
+
if (this.stopped) return;
|
|
16976
|
+
this.computing = true;
|
|
16977
|
+
this.dirty = false;
|
|
16978
|
+
try {
|
|
16979
|
+
const next = await this.opts.compute();
|
|
16980
|
+
if (this.stopped) return;
|
|
16981
|
+
this.snapshot = next;
|
|
16982
|
+
this.error = null;
|
|
16983
|
+
} catch (err) {
|
|
16984
|
+
if (this.stopped) return;
|
|
16985
|
+
this.error = err instanceof Error ? err : new Error(String(err));
|
|
16986
|
+
} finally {
|
|
16987
|
+
this.computing = false;
|
|
16988
|
+
if (!this.stopped) {
|
|
16989
|
+
if (!this.settledOnce) {
|
|
16990
|
+
this.settledOnce = true;
|
|
16991
|
+
this.resolveReady();
|
|
16992
|
+
}
|
|
16993
|
+
for (const cb of this.subs) cb();
|
|
16994
|
+
if (this.dirty) this.schedule();
|
|
16995
|
+
}
|
|
16996
|
+
}
|
|
16997
|
+
}
|
|
16998
|
+
};
|
|
16999
|
+
}
|
|
17000
|
+
});
|
|
17001
|
+
|
|
17002
|
+
// src/federation/aggregate-across.ts
|
|
17003
|
+
var CrossVaultAggregation, CrossVaultGroupedAggregation;
|
|
17004
|
+
var init_aggregate_across = __esm({
|
|
17005
|
+
"src/federation/aggregate-across.ts"() {
|
|
17006
|
+
"use strict";
|
|
17007
|
+
init_aggregation();
|
|
17008
|
+
init_groupby();
|
|
17009
|
+
init_cross_vault_live();
|
|
17010
|
+
CrossVaultAggregation = class {
|
|
17011
|
+
constructor(src, spec, bind) {
|
|
17012
|
+
this.src = src;
|
|
17013
|
+
this.spec = spec;
|
|
17014
|
+
this.bind = bind;
|
|
17015
|
+
}
|
|
17016
|
+
src;
|
|
17017
|
+
spec;
|
|
17018
|
+
bind;
|
|
17019
|
+
async run(options = {}) {
|
|
17020
|
+
const { records, skippedVaults } = await this.src.fanoutRecords(options);
|
|
17021
|
+
return { result: reduceRecords(records, this.spec), skippedVaults };
|
|
17022
|
+
}
|
|
17023
|
+
live(options = {}) {
|
|
17024
|
+
if (!this.bind) throw new Error("CrossVaultAggregation: live() requires a LiveBinding \u2014 use ShardedQuery.aggregate()");
|
|
17025
|
+
const spec = this.spec;
|
|
17026
|
+
const src = this.src;
|
|
17027
|
+
const core = new CrossVaultLive({
|
|
17028
|
+
subscribeToChanges: this.bind.subscribeToChanges,
|
|
17029
|
+
isRelevant: this.bind.isRelevant,
|
|
17030
|
+
compute: async () => {
|
|
17031
|
+
const { records, skippedVaults } = await src.fanoutRecords(options);
|
|
17032
|
+
return { value: reduceRecords(records, spec), skipped: skippedVaults };
|
|
17033
|
+
},
|
|
17034
|
+
initialSnapshot: { value: void 0, skipped: [] },
|
|
17035
|
+
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
17036
|
+
});
|
|
17037
|
+
return {
|
|
17038
|
+
get value() {
|
|
17039
|
+
return core.snapshot.value;
|
|
17040
|
+
},
|
|
17041
|
+
get skippedVaults() {
|
|
17042
|
+
return core.snapshot.skipped;
|
|
17043
|
+
},
|
|
17044
|
+
get error() {
|
|
17045
|
+
return core.error;
|
|
17046
|
+
},
|
|
17047
|
+
ready: core.ready,
|
|
17048
|
+
subscribe: (cb) => core.subscribe(cb),
|
|
17049
|
+
stop: () => core.stop()
|
|
17050
|
+
};
|
|
17051
|
+
}
|
|
17052
|
+
};
|
|
17053
|
+
CrossVaultGroupedAggregation = class {
|
|
17054
|
+
constructor(src, field, spec, bind) {
|
|
17055
|
+
this.src = src;
|
|
17056
|
+
this.field = field;
|
|
17057
|
+
this.spec = spec;
|
|
17058
|
+
this.bind = bind;
|
|
17059
|
+
}
|
|
17060
|
+
src;
|
|
17061
|
+
field;
|
|
17062
|
+
spec;
|
|
17063
|
+
bind;
|
|
17064
|
+
async run(options = {}) {
|
|
17065
|
+
const { records, skippedVaults } = await this.src.fanoutRecords(options);
|
|
17066
|
+
return {
|
|
17067
|
+
results: groupAndReduce(records, this.field, this.spec),
|
|
17068
|
+
skippedVaults
|
|
17069
|
+
};
|
|
17070
|
+
}
|
|
17071
|
+
live(options = {}) {
|
|
17072
|
+
if (!this.bind) throw new Error("CrossVaultGroupedAggregation: live() requires a LiveBinding \u2014 use ShardedQuery.groupBy().aggregate()");
|
|
17073
|
+
const field = this.field;
|
|
17074
|
+
const spec = this.spec;
|
|
17075
|
+
const src = this.src;
|
|
17076
|
+
const core = new CrossVaultLive({
|
|
17077
|
+
subscribeToChanges: this.bind.subscribeToChanges,
|
|
17078
|
+
isRelevant: this.bind.isRelevant,
|
|
17079
|
+
compute: async () => {
|
|
17080
|
+
const { records, skippedVaults } = await src.fanoutRecords(options);
|
|
17081
|
+
return {
|
|
17082
|
+
records: groupAndReduce(records, field, spec),
|
|
17083
|
+
skipped: skippedVaults
|
|
17084
|
+
};
|
|
17085
|
+
},
|
|
17086
|
+
initialSnapshot: { records: [], skipped: [] },
|
|
17087
|
+
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
17088
|
+
});
|
|
17089
|
+
return {
|
|
17090
|
+
get value() {
|
|
17091
|
+
return core.snapshot.records;
|
|
17092
|
+
},
|
|
17093
|
+
get skippedVaults() {
|
|
17094
|
+
return core.snapshot.skipped;
|
|
17095
|
+
},
|
|
17096
|
+
get error() {
|
|
17097
|
+
return core.error;
|
|
17098
|
+
},
|
|
17099
|
+
ready: core.ready,
|
|
17100
|
+
subscribe: (cb) => core.subscribe(cb),
|
|
17101
|
+
stop: () => core.stop()
|
|
17102
|
+
};
|
|
17103
|
+
}
|
|
17104
|
+
};
|
|
17105
|
+
}
|
|
17106
|
+
});
|
|
17107
|
+
|
|
17108
|
+
// src/federation/vault-group.ts
|
|
17109
|
+
var vault_group_exports = {};
|
|
17110
|
+
__export(vault_group_exports, {
|
|
17111
|
+
ShardedCollection: () => ShardedCollection,
|
|
17112
|
+
ShardedGroupedQuery: () => ShardedGroupedQuery,
|
|
17113
|
+
ShardedQuery: () => ShardedQuery,
|
|
17114
|
+
VaultGroup: () => VaultGroup
|
|
17115
|
+
});
|
|
17116
|
+
function assertSafePartitionKey(partitionKey) {
|
|
17117
|
+
if (partitionKey.length === 0) {
|
|
17118
|
+
throw new ValidationError("partitionKey must be a non-empty string");
|
|
17119
|
+
}
|
|
17120
|
+
if (!SAFE_PARTITION_KEY.test(partitionKey)) {
|
|
17121
|
+
throw new ValidationError(
|
|
17122
|
+
`partitionKey "${partitionKey}" contains characters outside [A-Za-z0-9._-]. Map your records to a store-safe key in sharding.keyOf.`
|
|
17123
|
+
);
|
|
17124
|
+
}
|
|
17125
|
+
if (partitionKey.includes(SHARD_SEPARATOR)) {
|
|
17126
|
+
throw new ValidationError(
|
|
17127
|
+
`partitionKey "${partitionKey}" must not contain "--" \u2014 it is reserved as the shard vault-id separator and would risk shard-id collisions.`
|
|
17128
|
+
);
|
|
17129
|
+
}
|
|
17130
|
+
}
|
|
17131
|
+
var SHARD_SEPARATOR, SAFE_PARTITION_KEY, VaultGroup, ShardedCollection, ShardedQuery, ShardedGroupedQuery;
|
|
17132
|
+
var init_vault_group = __esm({
|
|
17133
|
+
"src/federation/vault-group.ts"() {
|
|
17134
|
+
"use strict";
|
|
17135
|
+
init_errors();
|
|
17136
|
+
init_classify_skip();
|
|
17137
|
+
init_cross_vault_live();
|
|
17138
|
+
init_aggregate_across();
|
|
17139
|
+
SHARD_SEPARATOR = "--";
|
|
17140
|
+
SAFE_PARTITION_KEY = /^[A-Za-z0-9._-]+$/;
|
|
17141
|
+
VaultGroup = class {
|
|
17142
|
+
constructor(db, name, registry, sharding, template) {
|
|
17143
|
+
this.db = db;
|
|
17144
|
+
this.name = name;
|
|
17145
|
+
this.registry = registry;
|
|
17146
|
+
this.sharding = sharding;
|
|
17147
|
+
this.template = template;
|
|
17148
|
+
if (name.includes(SHARD_SEPARATOR)) {
|
|
17149
|
+
throw new ValidationError(
|
|
17150
|
+
`VaultGroup name "${name}" must not contain "--" (reserved shard vault-id separator).`
|
|
17151
|
+
);
|
|
17152
|
+
}
|
|
17153
|
+
}
|
|
17154
|
+
db;
|
|
17155
|
+
name;
|
|
17156
|
+
registry;
|
|
17157
|
+
sharding;
|
|
17158
|
+
template;
|
|
17159
|
+
/** Deterministic vault name for a partition key, namespaced by the group. */
|
|
17160
|
+
shardVaultId(partitionKey) {
|
|
17161
|
+
assertSafePartitionKey(partitionKey);
|
|
17162
|
+
return `${this.name}${SHARD_SEPARATOR}${partitionKey}`;
|
|
17163
|
+
}
|
|
17164
|
+
/** All registry rows (hydrates the registry collection first). */
|
|
17165
|
+
async allRows() {
|
|
17166
|
+
await this.registry.list();
|
|
17167
|
+
return this.registry.query().toArray();
|
|
17168
|
+
}
|
|
17169
|
+
/** Open an existing shard and apply the template. */
|
|
17170
|
+
async openShard(partitionKey) {
|
|
17171
|
+
const vault = await this.db.openVault(this.shardVaultId(partitionKey), { create: false });
|
|
17172
|
+
this.template.configure(vault);
|
|
17173
|
+
return vault;
|
|
17174
|
+
}
|
|
17175
|
+
/**
|
|
17176
|
+
* Idempotently provision a shard for `partitionKey`. Returns the
|
|
17177
|
+
* configured vault handle.
|
|
17178
|
+
*
|
|
17179
|
+
* - row + vault present → no-op, return handle
|
|
17180
|
+
* - row present, vault gone → ShardProvisioningError
|
|
17181
|
+
* - row absent (vault present or not) → open-or-create, configure, write row
|
|
17182
|
+
*/
|
|
17183
|
+
async createShard(partitionKey) {
|
|
17184
|
+
const vaultId = this.shardVaultId(partitionKey);
|
|
17185
|
+
const row = await this.registry.get(partitionKey);
|
|
17186
|
+
const provisioned = await this.db._shardVaultProvisioned(vaultId);
|
|
17187
|
+
if (row && !provisioned) throw new ShardProvisioningError(vaultId, partitionKey);
|
|
17188
|
+
if (row && provisioned) return this.openShard(partitionKey);
|
|
17189
|
+
const vault = await this.db.openVault(vaultId);
|
|
17190
|
+
this.template.configure(vault);
|
|
17191
|
+
await this.registry.put(partitionKey, {
|
|
17192
|
+
vaultId,
|
|
17193
|
+
partitionKey,
|
|
17194
|
+
templateName: this.sharding.vaultTemplate,
|
|
17195
|
+
schemaVersion: this.template.version,
|
|
17196
|
+
createdAt: Date.now()
|
|
17197
|
+
});
|
|
17198
|
+
return vault;
|
|
17199
|
+
}
|
|
17200
|
+
/**
|
|
17201
|
+
* Drill down to a single shard's full Collection API. Throws if the shard is unknown.
|
|
17202
|
+
* Also throws ShardProvisioningError if the registry row exists but the vault has been deleted
|
|
17203
|
+
* (registry/store divergence).
|
|
17204
|
+
*/
|
|
17205
|
+
async shard(partitionKey) {
|
|
17206
|
+
const vaultId = this.shardVaultId(partitionKey);
|
|
17207
|
+
const row = await this.registry.get(partitionKey);
|
|
17208
|
+
if (!row) throw new UnknownShardError(partitionKey, this.name);
|
|
17209
|
+
const provisioned = await this.db._shardVaultProvisioned(vaultId);
|
|
17210
|
+
if (!provisioned) throw new ShardProvisioningError(vaultId, partitionKey);
|
|
17211
|
+
return this.openShard(partitionKey);
|
|
17212
|
+
}
|
|
17213
|
+
/** A sharded view over one logical collection across all shards. */
|
|
17214
|
+
collection(collectionName) {
|
|
17215
|
+
return new ShardedCollection(this, collectionName);
|
|
17216
|
+
}
|
|
17217
|
+
/** @internal — eligible (openable-candidate) rows + drift/divergence skips. */
|
|
17218
|
+
async resolveEligible(options = {}) {
|
|
17219
|
+
const rows = await this.allRows();
|
|
17220
|
+
const skipped = [];
|
|
17221
|
+
const versionOk = [];
|
|
17222
|
+
for (const row of rows) {
|
|
17223
|
+
if (options.minVersion !== void 0 && row.schemaVersion < options.minVersion) {
|
|
17224
|
+
skipped.push({ vaultId: row.vaultId, reason: "schema-drift" });
|
|
17225
|
+
} else versionOk.push(row);
|
|
17226
|
+
}
|
|
17227
|
+
const provisioned = await Promise.all(versionOk.map((r) => this.db._shardVaultProvisioned(r.vaultId)));
|
|
17228
|
+
const eligible = [];
|
|
17229
|
+
versionOk.forEach((row, i) => {
|
|
17230
|
+
if (provisioned[i]) eligible.push(row);
|
|
17231
|
+
else skipped.push({ vaultId: row.vaultId, reason: "error", error: new ShardProvisioningError(row.vaultId, row.partitionKey) });
|
|
17232
|
+
});
|
|
17233
|
+
return { eligible, skipped };
|
|
17234
|
+
}
|
|
17235
|
+
};
|
|
17236
|
+
ShardedCollection = class {
|
|
17237
|
+
constructor(group, collectionName) {
|
|
17238
|
+
this.group = group;
|
|
17239
|
+
this.collectionName = collectionName;
|
|
17240
|
+
}
|
|
17241
|
+
group;
|
|
17242
|
+
collectionName;
|
|
17243
|
+
/** Route a write to the shard owning `keyOf(record)`. */
|
|
17244
|
+
async put(id, record) {
|
|
17245
|
+
const key = this.group.sharding.keyOf(record);
|
|
17246
|
+
const row = await this.group.registry.get(key);
|
|
17247
|
+
let vault;
|
|
17248
|
+
if (!row) {
|
|
17249
|
+
if (this.group.sharding.autoCreate === false) {
|
|
17250
|
+
throw new UnknownShardError(key, this.group.name);
|
|
17251
|
+
}
|
|
17252
|
+
vault = await this.group.createShard(key);
|
|
17253
|
+
} else {
|
|
17254
|
+
vault = await this.group.openShard(key);
|
|
17255
|
+
}
|
|
17256
|
+
await vault.collection(this.collectionName).put(id, record);
|
|
17257
|
+
}
|
|
17258
|
+
/** Begin a cross-shard fan-out query. */
|
|
17259
|
+
query() {
|
|
17260
|
+
return new ShardedQuery(this.group, this.collectionName, []);
|
|
17261
|
+
}
|
|
17262
|
+
};
|
|
17263
|
+
ShardedQuery = class _ShardedQuery {
|
|
17264
|
+
constructor(group, collectionName, clauses) {
|
|
17265
|
+
this.group = group;
|
|
17266
|
+
this.collectionName = collectionName;
|
|
17267
|
+
this.clauses = clauses;
|
|
17268
|
+
}
|
|
17269
|
+
group;
|
|
17270
|
+
collectionName;
|
|
17271
|
+
clauses;
|
|
17272
|
+
where(field, op, value) {
|
|
17273
|
+
return new _ShardedQuery(this.group, this.collectionName, [
|
|
17274
|
+
...this.clauses,
|
|
17275
|
+
{ field, op, value }
|
|
17276
|
+
]);
|
|
17277
|
+
}
|
|
17278
|
+
/** @internal — fan out the where-filtered records across eligible shards. */
|
|
17279
|
+
async fanoutRecords(options = {}) {
|
|
17280
|
+
const { eligible, skipped } = await this.group.resolveEligible(options);
|
|
17281
|
+
const across = await this.group.db.queryAcross(
|
|
17282
|
+
eligible.map((r) => r.vaultId),
|
|
17283
|
+
async (vault) => {
|
|
17284
|
+
this.group.template.configure(vault);
|
|
17285
|
+
const coll = vault.collection(this.collectionName);
|
|
17286
|
+
await coll.list();
|
|
17287
|
+
let q = coll.query();
|
|
17288
|
+
for (const c of this.clauses) q = q.where(c.field, c.op, c.value);
|
|
17289
|
+
return q.toArray();
|
|
17290
|
+
},
|
|
17291
|
+
{ concurrency: options.concurrency ?? 1, create: false }
|
|
17292
|
+
);
|
|
17293
|
+
const results = [];
|
|
17294
|
+
for (const r of across) {
|
|
17295
|
+
if (r.error) skipped.push({ vaultId: r.vault, reason: classifyShardSkip(r.error), error: r.error });
|
|
17296
|
+
else for (const item of r.result) results.push(item);
|
|
17297
|
+
}
|
|
17298
|
+
return { records: results, skippedVaults: skipped };
|
|
17299
|
+
}
|
|
17300
|
+
/** Fan out across eligible shards and merge results. */
|
|
17301
|
+
async toArray(options = {}) {
|
|
17302
|
+
const { records, skippedVaults } = await this.fanoutRecords(options);
|
|
17303
|
+
return { results: records, skippedVaults };
|
|
17304
|
+
}
|
|
17305
|
+
/** @internal — build the change-subscription + relevance binding for this query's group+collection. */
|
|
17306
|
+
liveBinding() {
|
|
17307
|
+
const group = this.group;
|
|
17308
|
+
const collectionName = this.collectionName;
|
|
17309
|
+
return {
|
|
17310
|
+
subscribeToChanges: (h) => {
|
|
17311
|
+
group.db.on("change", h);
|
|
17312
|
+
return () => group.db.off("change", h);
|
|
17313
|
+
},
|
|
17314
|
+
isRelevant: (e) => e.collection === collectionName && e.vault.startsWith(`${group.name}--`)
|
|
17315
|
+
};
|
|
17316
|
+
}
|
|
17317
|
+
/** Returns a reactive cross-shard live query — a facade over CrossVaultLive. */
|
|
17318
|
+
live(options = {}) {
|
|
17319
|
+
const bind = this.liveBinding();
|
|
17320
|
+
const core = new CrossVaultLive({
|
|
17321
|
+
...bind,
|
|
17322
|
+
compute: async () => {
|
|
17323
|
+
const { records, skippedVaults } = await this.fanoutRecords(options);
|
|
17324
|
+
return { records, skipped: skippedVaults };
|
|
17325
|
+
},
|
|
17326
|
+
initialSnapshot: { records: [], skipped: [] },
|
|
17327
|
+
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
17328
|
+
});
|
|
17329
|
+
return {
|
|
17330
|
+
get value() {
|
|
17331
|
+
return core.snapshot.records;
|
|
17332
|
+
},
|
|
17333
|
+
get skippedVaults() {
|
|
17334
|
+
return core.snapshot.skipped;
|
|
17335
|
+
},
|
|
17336
|
+
get error() {
|
|
17337
|
+
return core.error;
|
|
17338
|
+
},
|
|
17339
|
+
ready: core.ready,
|
|
17340
|
+
subscribe: (cb) => core.subscribe(cb),
|
|
17341
|
+
stop: () => core.stop()
|
|
17342
|
+
};
|
|
17343
|
+
}
|
|
17344
|
+
/** One-shot distributed aggregate — central reduce over all shard records. */
|
|
17345
|
+
aggregate(spec) {
|
|
17346
|
+
return new CrossVaultAggregation(this, spec, this.liveBinding());
|
|
17347
|
+
}
|
|
17348
|
+
/** Begin a grouped cross-shard aggregate. */
|
|
17349
|
+
groupBy(field) {
|
|
17350
|
+
return new ShardedGroupedQuery(this, field);
|
|
17351
|
+
}
|
|
17352
|
+
};
|
|
17353
|
+
ShardedGroupedQuery = class {
|
|
17354
|
+
constructor(query, field) {
|
|
17355
|
+
this.query = query;
|
|
17356
|
+
this.field = field;
|
|
17357
|
+
}
|
|
17358
|
+
query;
|
|
17359
|
+
field;
|
|
17360
|
+
aggregate(spec) {
|
|
17361
|
+
return new CrossVaultGroupedAggregation(
|
|
17362
|
+
{ fanoutRecords: (o) => this.query.fanoutRecords(o) },
|
|
17363
|
+
this.field,
|
|
17364
|
+
spec,
|
|
17365
|
+
this.query.liveBinding()
|
|
17366
|
+
);
|
|
17367
|
+
}
|
|
17368
|
+
};
|
|
17369
|
+
}
|
|
17370
|
+
});
|
|
17371
|
+
|
|
15954
17372
|
// src/noydb.ts
|
|
15955
17373
|
var noydb_exports = {};
|
|
15956
17374
|
__export(noydb_exports, {
|
|
@@ -16089,6 +17507,7 @@ var init_noydb = __esm({
|
|
|
16089
17507
|
writeRelay;
|
|
16090
17508
|
/** Per-vault policy enforcers. */
|
|
16091
17509
|
policyEnforcers = /* @__PURE__ */ new Map();
|
|
17510
|
+
vaultTemplates = /* @__PURE__ */ new Map();
|
|
16092
17511
|
txStrategy;
|
|
16093
17512
|
sessionStrategy;
|
|
16094
17513
|
syncStrategy;
|
|
@@ -16158,7 +17577,7 @@ var init_noydb = __esm({
|
|
|
16158
17577
|
await registry.runChecks(e.collection, incoming, ctx);
|
|
16159
17578
|
const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor3(), executor_exports3));
|
|
16160
17579
|
for (const g of guards) {
|
|
16161
|
-
await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming);
|
|
17580
|
+
await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming, e.computedFieldNames);
|
|
16162
17581
|
}
|
|
16163
17582
|
});
|
|
16164
17583
|
this.subsystemBus.registerGate("beforeDelete", async (e) => {
|
|
@@ -16279,7 +17698,7 @@ var init_noydb = __esm({
|
|
|
16279
17698
|
}
|
|
16280
17699
|
return comp;
|
|
16281
17700
|
}
|
|
16282
|
-
const keyring = await this.getKeyringInternal(name);
|
|
17701
|
+
const keyring = await this.getKeyringInternal(name, { create: opts?.create !== false });
|
|
16283
17702
|
if (!this.activeTier.has(name)) {
|
|
16284
17703
|
this.activeTier.set(name, 1);
|
|
16285
17704
|
}
|
|
@@ -16337,6 +17756,7 @@ var init_noydb = __esm({
|
|
|
16337
17756
|
syncAdapter: targets.length > 0 ? targets[0].store : void 0,
|
|
16338
17757
|
historyConfig: this.options.history,
|
|
16339
17758
|
...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
|
|
17759
|
+
...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
|
|
16340
17760
|
...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
|
|
16341
17761
|
...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
|
|
16342
17762
|
...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
|
|
@@ -16390,6 +17810,7 @@ var init_noydb = __esm({
|
|
|
16390
17810
|
emitter: this.emitter,
|
|
16391
17811
|
historyConfig: this.options.history,
|
|
16392
17812
|
...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
|
|
17813
|
+
...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
|
|
16393
17814
|
...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
|
|
16394
17815
|
...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
|
|
16395
17816
|
...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
|
|
@@ -16418,6 +17839,7 @@ var init_noydb = __esm({
|
|
|
16418
17839
|
encrypted: true,
|
|
16419
17840
|
historyConfig: this.options.history,
|
|
16420
17841
|
...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
|
|
17842
|
+
...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
|
|
16421
17843
|
...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
|
|
16422
17844
|
...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
|
|
16423
17845
|
...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
|
|
@@ -16707,7 +18129,7 @@ var init_noydb = __esm({
|
|
|
16707
18129
|
const vaultId = vaultIds[idx];
|
|
16708
18130
|
const task = (async () => {
|
|
16709
18131
|
try {
|
|
16710
|
-
const comp = await this.openVault(vaultId);
|
|
18132
|
+
const comp = await this.openVault(vaultId, { create: options.create !== false });
|
|
16711
18133
|
const result = await fn(comp);
|
|
16712
18134
|
results[idx] = { vault: vaultId, result };
|
|
16713
18135
|
} catch (err) {
|
|
@@ -16732,6 +18154,32 @@ var init_noydb = __esm({
|
|
|
16732
18154
|
}
|
|
16733
18155
|
return results;
|
|
16734
18156
|
}
|
|
18157
|
+
/**
|
|
18158
|
+
* Register a shard schema blueprint. `createShard` / `openVaultGroup`
|
|
18159
|
+
* stamp shards from the named template. See the MVF design spec.
|
|
18160
|
+
*/
|
|
18161
|
+
withVaultTemplate(name, template) {
|
|
18162
|
+
this.vaultTemplates.set(name, template);
|
|
18163
|
+
}
|
|
18164
|
+
/**
|
|
18165
|
+
* Open a VaultGroup — transparent routing over per-partition shard
|
|
18166
|
+
* vaults, with shard discovery backed by the supplied `vault-registry`
|
|
18167
|
+
* collection.
|
|
18168
|
+
*/
|
|
18169
|
+
async openVaultGroup(name, opts) {
|
|
18170
|
+
if (this.closed) throw new ValidationError("Instance is closed");
|
|
18171
|
+
const template = this.vaultTemplates.get(opts.sharding.vaultTemplate);
|
|
18172
|
+
if (!template) throw new VaultTemplateNotFoundError(opts.sharding.vaultTemplate);
|
|
18173
|
+
const { VaultGroup: VaultGroup2 } = await Promise.resolve().then(() => (init_vault_group(), vault_group_exports));
|
|
18174
|
+
return new VaultGroup2(this, name, opts.registry, opts.sharding, template);
|
|
18175
|
+
}
|
|
18176
|
+
/**
|
|
18177
|
+
* @internal — true when an encrypted shard vault is provisioned
|
|
18178
|
+
* (its keyring exists in the store).
|
|
18179
|
+
*/
|
|
18180
|
+
async _shardVaultProvisioned(vaultId) {
|
|
18181
|
+
return (await this.options.store.list(vaultId, "_keyring")).length > 0;
|
|
18182
|
+
}
|
|
16735
18183
|
/**
|
|
16736
18184
|
* Change the current user's passphrase for a vault.
|
|
16737
18185
|
*
|
|
@@ -18049,7 +19497,7 @@ var init_noydb = __esm({
|
|
|
18049
19497
|
* accesses see them. Not exposed publicly — callers outside hub
|
|
18050
19498
|
* should use {@link getKeyring}, which returns a defensive copy.
|
|
18051
19499
|
*/
|
|
18052
|
-
async getKeyringInternal(vault) {
|
|
19500
|
+
async getKeyringInternal(vault, opts = { create: true }) {
|
|
18053
19501
|
if (this.options.encrypt === false) {
|
|
18054
19502
|
return createPlaintextKeyring(this.options.user);
|
|
18055
19503
|
}
|
|
@@ -18060,6 +19508,7 @@ var init_noydb = __esm({
|
|
|
18060
19508
|
this.keyringCache.set(vault, keyring2);
|
|
18061
19509
|
return keyring2;
|
|
18062
19510
|
}
|
|
19511
|
+
await assertKeyringOpenAllowed(this.options.store, vault, this.options.user, opts.create);
|
|
18063
19512
|
let effectiveSecret;
|
|
18064
19513
|
if (this.options.passphraseMode === "managed") {
|
|
18065
19514
|
effectiveSecret = await resolveManagedSecret(
|