@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/index.cjs
CHANGED
|
@@ -46,7 +46,7 @@ var init_types = __esm({
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
// src/errors.ts
|
|
49
|
-
var NoydbError, DecryptionError, TamperedError, InvalidKeyError, KeyringCorruptError, NoAccessError, ReadOnlyError, ReadOnlyAtInstantError, ReadOnlyFrameError, PermissionDeniedError, ExportCapabilityError, KeyringExpiredError, ImportCapabilityError, StoreCapabilityError, PrivilegeEscalationError, PeriodClosedError, RecordLockedError, FieldFrozenError, InvariantError, AmendmentForbiddenError, DirectoryDisabledError, TierNotGrantedError, ElevationExpiredError, AlreadyElevatedError, TierDemoteDeniedError, DelegationTargetMissingError, ConflictError, LedgerContentionError, BundleVersionConflictError, NetworkError, NotFoundError, ValidationError, SchemaValidationError, SchemaUpdateError, NonAdditiveSchemaChangeError, SchemaLockedError, SchemaFenceError, MigrationRequiredError, QuiesceTimeoutError, GroupCardinalityError, IndexRequiredError, UniqueConstraintError, UnsupportedIndexOptionError, IndexWriteFailureError, BundleIntegrityError, BundleSealMismatchError, ReservedCollectionNameError, DictKeyMissingError, DictKeyInUseError, MissingTranslationError, LocaleNotSpecifiedError, ScriptViolationError, TranslatorNotConfiguredError, BackupLedgerError, BackupCorruptedError, AttestationError, SessionExpiredError, SessionNotFoundError, SessionPolicyError, JoinTooLargeError, CrossJoinTooLargeError, CrossJoinSourceUnknownError, DanglingReferenceError, FilenameSanitizationError, PathEscapeError, DerivationCycleError, DerivationDepthError, DerivationOutputUnknownError, DerivationOutputShapeError, DerivationCapExceededError, MaterializedViewCycleError, MaterializedViewSourceUnknownError, MaterializedViewTooLargeError, MaterializedViewConfigError, OverlayBaseIsVirtualError, OverlayCollectionUnavailableError, OverlayNameCollisionError, OverlayIdMismatchError, SnapshotNotFoundError;
|
|
49
|
+
var NoydbError, DecryptionError, TamperedError, InvalidKeyError, KeyringCorruptError, NoAccessError, ReadOnlyError, ReadOnlyAtInstantError, ReadOnlyFrameError, PermissionDeniedError, ExportCapabilityError, KeyringExpiredError, ImportCapabilityError, StoreCapabilityError, PrivilegeEscalationError, PeriodClosedError, RecordLockedError, FieldFrozenError, InvariantError, AmendmentForbiddenError, DirectoryDisabledError, TierNotGrantedError, ElevationExpiredError, AlreadyElevatedError, TierDemoteDeniedError, DelegationTargetMissingError, ConflictError, LedgerContentionError, SequenceContentionError, SequenceOfflineError, BundleVersionConflictError, NetworkError, NotFoundError, ValidationError, SchemaValidationError, SchemaUpdateError, NonAdditiveSchemaChangeError, SchemaLockedError, SchemaFenceError, MigrationRequiredError, QuiesceTimeoutError, GroupCardinalityError, IndexRequiredError, UniqueConstraintError, UnsupportedIndexOptionError, IndexWriteFailureError, BundleIntegrityError, BundleSealMismatchError, ReservedCollectionNameError, DictKeyMissingError, DictKeyInUseError, MissingTranslationError, LocaleNotSpecifiedError, ScriptViolationError, TranslatorNotConfiguredError, BackupLedgerError, BackupCorruptedError, AttestationError, SessionExpiredError, SessionNotFoundError, SessionPolicyError, JoinTooLargeError, CrossJoinTooLargeError, CrossJoinSourceUnknownError, DanglingReferenceError, FilenameSanitizationError, PathEscapeError, DerivationCycleError, DerivationDepthError, DerivationOutputUnknownError, DerivationOutputShapeError, DerivationCapExceededError, MaterializedViewCycleError, MaterializedViewSourceUnknownError, MaterializedViewTooLargeError, MaterializedViewConfigError, OverlayBaseIsVirtualError, OverlayCollectionUnavailableError, OverlayNameCollisionError, OverlayIdMismatchError, SnapshotNotFoundError, UnknownShardError, ShardProvisioningError, VaultTemplateNotFoundError;
|
|
50
50
|
var init_errors = __esm({
|
|
51
51
|
"src/errors.ts"() {
|
|
52
52
|
"use strict";
|
|
@@ -340,6 +340,28 @@ var init_errors = __esm({
|
|
|
340
340
|
this.attempts = attempts;
|
|
341
341
|
}
|
|
342
342
|
};
|
|
343
|
+
SequenceContentionError = class extends NoydbError {
|
|
344
|
+
sequence;
|
|
345
|
+
attempts;
|
|
346
|
+
constructor(sequence, attempts) {
|
|
347
|
+
super(
|
|
348
|
+
"SEQUENCE_CONTENTION",
|
|
349
|
+
`vault.sequence("${sequence}").next(): failed to allocate after ${attempts} optimistic-CAS retries`
|
|
350
|
+
);
|
|
351
|
+
this.name = "SequenceContentionError";
|
|
352
|
+
this.sequence = sequence;
|
|
353
|
+
this.attempts = attempts;
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
SequenceOfflineError = class extends NoydbError {
|
|
357
|
+
constructor() {
|
|
358
|
+
super(
|
|
359
|
+
"SEQUENCE_OFFLINE",
|
|
360
|
+
"vault.sequence().next() requires an online CAS-capable store (capabilities.casAtomic). Gap-free numbering cannot be serialized offline."
|
|
361
|
+
);
|
|
362
|
+
this.name = "SequenceOfflineError";
|
|
363
|
+
}
|
|
364
|
+
};
|
|
343
365
|
BundleVersionConflictError = class extends NoydbError {
|
|
344
366
|
/** The bundle handle of the newer remote version that rejected the push. */
|
|
345
367
|
remoteVersion;
|
|
@@ -451,14 +473,14 @@ var init_errors = __esm({
|
|
|
451
473
|
recordId;
|
|
452
474
|
fields;
|
|
453
475
|
conflictingId;
|
|
454
|
-
constructor(collection,
|
|
476
|
+
constructor(collection, recordId3, fields, conflictingId) {
|
|
455
477
|
super(
|
|
456
478
|
"UNIQUE_CONSTRAINT",
|
|
457
|
-
`Unique constraint on ${collection}.[${fields.join(", ")}] violated: record "${
|
|
479
|
+
`Unique constraint on ${collection}.[${fields.join(", ")}] violated: record "${recordId3}" duplicates a value already held by "${conflictingId}".`
|
|
458
480
|
);
|
|
459
481
|
this.name = "UniqueConstraintError";
|
|
460
482
|
this.collection = collection;
|
|
461
|
-
this.recordId =
|
|
483
|
+
this.recordId = recordId3;
|
|
462
484
|
this.fields = fields;
|
|
463
485
|
this.conflictingId = conflictingId;
|
|
464
486
|
}
|
|
@@ -921,6 +943,39 @@ Resolutions:
|
|
|
921
943
|
this.version = version;
|
|
922
944
|
}
|
|
923
945
|
};
|
|
946
|
+
UnknownShardError = class extends NoydbError {
|
|
947
|
+
partitionKey;
|
|
948
|
+
constructor(partitionKey, groupName) {
|
|
949
|
+
super(
|
|
950
|
+
"SHARD_UNKNOWN",
|
|
951
|
+
`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.`
|
|
952
|
+
);
|
|
953
|
+
this.name = "UnknownShardError";
|
|
954
|
+
this.partitionKey = partitionKey;
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
ShardProvisioningError = class extends NoydbError {
|
|
958
|
+
vaultId;
|
|
959
|
+
constructor(vaultId, partitionKey) {
|
|
960
|
+
super(
|
|
961
|
+
"SHARD_PROVISIONING",
|
|
962
|
+
`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.`
|
|
963
|
+
);
|
|
964
|
+
this.name = "ShardProvisioningError";
|
|
965
|
+
this.vaultId = vaultId;
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
VaultTemplateNotFoundError = class extends NoydbError {
|
|
969
|
+
templateName;
|
|
970
|
+
constructor(templateName) {
|
|
971
|
+
super(
|
|
972
|
+
"VAULT_TEMPLATE_NOT_FOUND",
|
|
973
|
+
`No vault template registered under "${templateName}". Register it with db.withVaultTemplate(${JSON.stringify(templateName)}, { version, configure }) before opening the vault group.`
|
|
974
|
+
);
|
|
975
|
+
this.name = "VaultTemplateNotFoundError";
|
|
976
|
+
this.templateName = templateName;
|
|
977
|
+
}
|
|
978
|
+
};
|
|
924
979
|
}
|
|
925
980
|
});
|
|
926
981
|
|
|
@@ -1685,7 +1740,7 @@ var init_hash = __esm({
|
|
|
1685
1740
|
});
|
|
1686
1741
|
|
|
1687
1742
|
// src/history/ledger/store.ts
|
|
1688
|
-
function
|
|
1743
|
+
function sleepBackoff2(attempt) {
|
|
1689
1744
|
const base = 5 * Math.pow(2, attempt);
|
|
1690
1745
|
const jitter = Math.random() * base;
|
|
1691
1746
|
return new Promise((resolve) => setTimeout(resolve, base + jitter));
|
|
@@ -1807,7 +1862,7 @@ var init_store = __esm({
|
|
|
1807
1862
|
if (err instanceof ConflictError) {
|
|
1808
1863
|
lastConflict = err;
|
|
1809
1864
|
if (attempt < MAX_APPEND_ATTEMPTS - 1) {
|
|
1810
|
-
await
|
|
1865
|
+
await sleepBackoff2(attempt);
|
|
1811
1866
|
}
|
|
1812
1867
|
continue;
|
|
1813
1868
|
}
|
|
@@ -2221,6 +2276,278 @@ var init_public_envelope = __esm({
|
|
|
2221
2276
|
}
|
|
2222
2277
|
});
|
|
2223
2278
|
|
|
2279
|
+
// src/money/fixed-point.ts
|
|
2280
|
+
function expandExponent(s) {
|
|
2281
|
+
const m = /^([+-]?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
|
|
2282
|
+
if (!m) return s;
|
|
2283
|
+
const sign = m[1] === "-" ? "-" : "";
|
|
2284
|
+
const intp = m[2];
|
|
2285
|
+
const frac = m[3] ?? "";
|
|
2286
|
+
const exp = Number(m[4]);
|
|
2287
|
+
const digits = intp + frac;
|
|
2288
|
+
const pointPos = intp.length + exp;
|
|
2289
|
+
let body;
|
|
2290
|
+
if (pointPos <= 0) {
|
|
2291
|
+
body = "0." + "0".repeat(-pointPos) + digits;
|
|
2292
|
+
} else if (pointPos >= digits.length) {
|
|
2293
|
+
body = digits + "0".repeat(pointPos - digits.length);
|
|
2294
|
+
} else {
|
|
2295
|
+
body = digits.slice(0, pointPos) + "." + digits.slice(pointPos);
|
|
2296
|
+
}
|
|
2297
|
+
return sign + body;
|
|
2298
|
+
}
|
|
2299
|
+
function toCanonicalDecimalString(input) {
|
|
2300
|
+
let s;
|
|
2301
|
+
if (typeof input === "number") {
|
|
2302
|
+
if (!Number.isFinite(input)) return null;
|
|
2303
|
+
s = String(input);
|
|
2304
|
+
} else {
|
|
2305
|
+
s = input.trim();
|
|
2306
|
+
}
|
|
2307
|
+
s = expandExponent(s);
|
|
2308
|
+
if (s.startsWith("+")) s = s.slice(1);
|
|
2309
|
+
if (!/^-?(\d+(\.\d*)?|\.\d+)$/.test(s)) return null;
|
|
2310
|
+
return s;
|
|
2311
|
+
}
|
|
2312
|
+
function shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, mode) {
|
|
2313
|
+
switch (mode) {
|
|
2314
|
+
case "up":
|
|
2315
|
+
return true;
|
|
2316
|
+
case "down":
|
|
2317
|
+
return false;
|
|
2318
|
+
case "ceil":
|
|
2319
|
+
return !negative;
|
|
2320
|
+
case "floor":
|
|
2321
|
+
return negative;
|
|
2322
|
+
case "half-up":
|
|
2323
|
+
return firstDiscarded >= 5;
|
|
2324
|
+
case "half-down":
|
|
2325
|
+
return firstDiscarded > 5 || firstDiscarded === 5 && hasMoreNonZeroAfterFirst;
|
|
2326
|
+
case "half-even":
|
|
2327
|
+
if (firstDiscarded > 5) return true;
|
|
2328
|
+
if (firstDiscarded < 5) return false;
|
|
2329
|
+
return hasMoreNonZeroAfterFirst || lastKeptDigit % 2 === 1;
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
function parseToScaledInt(input, scale, rounding) {
|
|
2333
|
+
const canonical = toCanonicalDecimalString(input);
|
|
2334
|
+
if (canonical === null) return { ok: false, reason: "nonfinite" };
|
|
2335
|
+
const negative = canonical.startsWith("-");
|
|
2336
|
+
const unsigned = negative ? canonical.slice(1) : canonical;
|
|
2337
|
+
const dot = unsigned.indexOf(".");
|
|
2338
|
+
const intPart = dot === -1 ? unsigned : unsigned.slice(0, dot);
|
|
2339
|
+
const fracPart = dot === -1 ? "" : unsigned.slice(dot + 1);
|
|
2340
|
+
const intDigits = intPart === "" ? "0" : intPart;
|
|
2341
|
+
if (fracPart.length <= scale) {
|
|
2342
|
+
const keep2 = fracPart.padEnd(scale, "0");
|
|
2343
|
+
const magnitude2 = BigInt(intDigits + keep2);
|
|
2344
|
+
return { ok: true, value: negative && magnitude2 !== 0n ? -magnitude2 : magnitude2 };
|
|
2345
|
+
}
|
|
2346
|
+
const keep = fracPart.slice(0, scale);
|
|
2347
|
+
const tail = fracPart.slice(scale);
|
|
2348
|
+
const magnitudeDigits = intDigits + keep;
|
|
2349
|
+
let magnitude = BigInt(magnitudeDigits);
|
|
2350
|
+
if (/^0+$/.test(tail)) {
|
|
2351
|
+
return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
|
|
2352
|
+
}
|
|
2353
|
+
if (rounding === void 0) return { ok: false, reason: "precision" };
|
|
2354
|
+
const lastKeptDigit = Number(magnitudeDigits[magnitudeDigits.length - 1]);
|
|
2355
|
+
const firstDiscarded = Number(tail[0]);
|
|
2356
|
+
const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1));
|
|
2357
|
+
if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {
|
|
2358
|
+
magnitude += 1n;
|
|
2359
|
+
}
|
|
2360
|
+
return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
|
|
2361
|
+
}
|
|
2362
|
+
function formatScaledInt(value, scale) {
|
|
2363
|
+
const negative = value < 0n;
|
|
2364
|
+
const abs = (negative ? -value : value).toString();
|
|
2365
|
+
if (scale === 0) return (negative ? "-" : "") + abs;
|
|
2366
|
+
const padded = abs.padStart(scale + 1, "0");
|
|
2367
|
+
const cut = padded.length - scale;
|
|
2368
|
+
const intPart = padded.slice(0, cut);
|
|
2369
|
+
const fracPart = padded.slice(cut);
|
|
2370
|
+
return (negative ? "-" : "") + intPart + "." + fracPart;
|
|
2371
|
+
}
|
|
2372
|
+
var init_fixed_point = __esm({
|
|
2373
|
+
"src/money/fixed-point.ts"() {
|
|
2374
|
+
"use strict";
|
|
2375
|
+
}
|
|
2376
|
+
});
|
|
2377
|
+
|
|
2378
|
+
// src/money/iso4217.ts
|
|
2379
|
+
function scaleForCurrency(code) {
|
|
2380
|
+
const v = MINOR_UNITS[code];
|
|
2381
|
+
return v === void 0 ? null : v;
|
|
2382
|
+
}
|
|
2383
|
+
var MINOR_UNITS;
|
|
2384
|
+
var init_iso4217 = __esm({
|
|
2385
|
+
"src/money/iso4217.ts"() {
|
|
2386
|
+
"use strict";
|
|
2387
|
+
MINOR_UNITS = {
|
|
2388
|
+
// 2-decimal majors
|
|
2389
|
+
EUR: 2,
|
|
2390
|
+
USD: 2,
|
|
2391
|
+
GBP: 2,
|
|
2392
|
+
CHF: 2,
|
|
2393
|
+
CAD: 2,
|
|
2394
|
+
AUD: 2,
|
|
2395
|
+
NZD: 2,
|
|
2396
|
+
SGD: 2,
|
|
2397
|
+
HKD: 2,
|
|
2398
|
+
CNY: 2,
|
|
2399
|
+
INR: 2,
|
|
2400
|
+
BRL: 2,
|
|
2401
|
+
MXN: 2,
|
|
2402
|
+
ZAR: 2,
|
|
2403
|
+
RUB: 2,
|
|
2404
|
+
TRY: 2,
|
|
2405
|
+
PLN: 2,
|
|
2406
|
+
SEK: 2,
|
|
2407
|
+
NOK: 2,
|
|
2408
|
+
DKK: 2,
|
|
2409
|
+
CZK: 2,
|
|
2410
|
+
HUF: 2,
|
|
2411
|
+
RON: 2,
|
|
2412
|
+
ILS: 2,
|
|
2413
|
+
THB: 2,
|
|
2414
|
+
PHP: 2,
|
|
2415
|
+
MYR: 2,
|
|
2416
|
+
IDR: 2,
|
|
2417
|
+
AED: 2,
|
|
2418
|
+
SAR: 2,
|
|
2419
|
+
QAR: 2,
|
|
2420
|
+
EGP: 2,
|
|
2421
|
+
// 0-decimal
|
|
2422
|
+
JPY: 0,
|
|
2423
|
+
KRW: 0,
|
|
2424
|
+
ISK: 0,
|
|
2425
|
+
CLP: 0,
|
|
2426
|
+
VND: 0,
|
|
2427
|
+
XOF: 0,
|
|
2428
|
+
XAF: 0,
|
|
2429
|
+
PYG: 0,
|
|
2430
|
+
// 3-decimal
|
|
2431
|
+
BHD: 3,
|
|
2432
|
+
KWD: 3,
|
|
2433
|
+
OMR: 3,
|
|
2434
|
+
TND: 3,
|
|
2435
|
+
JOD: 3,
|
|
2436
|
+
IQD: 3,
|
|
2437
|
+
LYD: 3
|
|
2438
|
+
};
|
|
2439
|
+
}
|
|
2440
|
+
});
|
|
2441
|
+
|
|
2442
|
+
// src/money/descriptor.ts
|
|
2443
|
+
function isMultiOptions(o) {
|
|
2444
|
+
return "currencies" in o;
|
|
2445
|
+
}
|
|
2446
|
+
function money(options) {
|
|
2447
|
+
const hasFixed = "currency" in options;
|
|
2448
|
+
const hasMulti = "currencies" in options;
|
|
2449
|
+
if (hasFixed && hasMulti) {
|
|
2450
|
+
throw new TypeError("money: `currency` and `currencies` are mutually exclusive");
|
|
2451
|
+
}
|
|
2452
|
+
if (!hasFixed && !hasMulti) {
|
|
2453
|
+
throw new TypeError("money: one of `currency` or `currencies` is required");
|
|
2454
|
+
}
|
|
2455
|
+
const rounding = options.rounding;
|
|
2456
|
+
if (isMultiOptions(options)) {
|
|
2457
|
+
const overrides = options.scaleOverrides ?? {};
|
|
2458
|
+
const allowList = options.currencies;
|
|
2459
|
+
const allows = (c) => allowList === "any" ? true : allowList.includes(c);
|
|
2460
|
+
const scaleFor = (c) => {
|
|
2461
|
+
if (!allows(c)) throw new MoneyCurrencyError(c, "not-allowed");
|
|
2462
|
+
const s = overrides[c] ?? scaleForCurrency(c);
|
|
2463
|
+
if (s === null || s === void 0) throw new MoneyCurrencyError(c, "unknown-scale");
|
|
2464
|
+
return s;
|
|
2465
|
+
};
|
|
2466
|
+
if (allowList !== "any") for (const c of allowList) scaleFor(c);
|
|
2467
|
+
const soleCurrency = () => allowList !== "any" && allowList.length === 1 ? allowList[0] : void 0;
|
|
2468
|
+
return {
|
|
2469
|
+
_noydbMoney: true,
|
|
2470
|
+
mode: "multi",
|
|
2471
|
+
options,
|
|
2472
|
+
rounding,
|
|
2473
|
+
fixedCurrency: void 0,
|
|
2474
|
+
scaleFor,
|
|
2475
|
+
allows,
|
|
2476
|
+
soleCurrency
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
const currency = options.currency;
|
|
2480
|
+
const resolvedScale = options.scale ?? scaleForCurrency(currency);
|
|
2481
|
+
if (resolvedScale === null || resolvedScale === void 0) {
|
|
2482
|
+
throw new MoneyCurrencyError(currency, "unknown-scale");
|
|
2483
|
+
}
|
|
2484
|
+
return {
|
|
2485
|
+
_noydbMoney: true,
|
|
2486
|
+
mode: "fixed",
|
|
2487
|
+
options,
|
|
2488
|
+
rounding,
|
|
2489
|
+
fixedCurrency: currency,
|
|
2490
|
+
scaleFor(c) {
|
|
2491
|
+
if (c !== currency) throw new MoneyCurrencyError(c, "not-allowed");
|
|
2492
|
+
return resolvedScale;
|
|
2493
|
+
},
|
|
2494
|
+
allows: (c) => c === currency,
|
|
2495
|
+
soleCurrency: () => currency
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
function isMoneyDescriptor(x) {
|
|
2499
|
+
return typeof x === "object" && x !== null && x._noydbMoney === true;
|
|
2500
|
+
}
|
|
2501
|
+
var MoneyPrecisionError, MoneyCurrencyError, MoneyUnsupportedError;
|
|
2502
|
+
var init_descriptor = __esm({
|
|
2503
|
+
"src/money/descriptor.ts"() {
|
|
2504
|
+
"use strict";
|
|
2505
|
+
init_iso4217();
|
|
2506
|
+
init_errors();
|
|
2507
|
+
MoneyPrecisionError = class extends NoydbError {
|
|
2508
|
+
constructor(field, value, scale) {
|
|
2509
|
+
super(
|
|
2510
|
+
"MONEY_PRECISION",
|
|
2511
|
+
`money: value ${JSON.stringify(value)} for field "${field}" exceeds scale ${scale} and no rounding mode is configured`
|
|
2512
|
+
);
|
|
2513
|
+
this.field = field;
|
|
2514
|
+
this.value = value;
|
|
2515
|
+
this.scale = scale;
|
|
2516
|
+
this.name = "MoneyPrecisionError";
|
|
2517
|
+
}
|
|
2518
|
+
field;
|
|
2519
|
+
value;
|
|
2520
|
+
scale;
|
|
2521
|
+
};
|
|
2522
|
+
MoneyCurrencyError = class extends NoydbError {
|
|
2523
|
+
constructor(currency, reason, field) {
|
|
2524
|
+
super(
|
|
2525
|
+
"MONEY_CURRENCY",
|
|
2526
|
+
reason === "not-allowed" ? `money: currency "${currency}" is not allowed${field ? ` for field "${field}"` : ""}` : `money: no scale known for currency "${currency}"${field ? ` (field "${field}")` : ""} \u2014 pass an explicit scale / scaleOverrides`
|
|
2527
|
+
);
|
|
2528
|
+
this.currency = currency;
|
|
2529
|
+
this.reason = reason;
|
|
2530
|
+
this.field = field;
|
|
2531
|
+
this.name = "MoneyCurrencyError";
|
|
2532
|
+
}
|
|
2533
|
+
currency;
|
|
2534
|
+
reason;
|
|
2535
|
+
field;
|
|
2536
|
+
};
|
|
2537
|
+
MoneyUnsupportedError = class extends NoydbError {
|
|
2538
|
+
constructor(field, message) {
|
|
2539
|
+
super(
|
|
2540
|
+
"MONEY_UNSUPPORTED",
|
|
2541
|
+
message ?? `money: operation is not supported on field "${field}" \u2014 use sum() and count() and divide at the boundary`
|
|
2542
|
+
);
|
|
2543
|
+
this.field = field;
|
|
2544
|
+
this.name = "MoneyUnsupportedError";
|
|
2545
|
+
}
|
|
2546
|
+
field;
|
|
2547
|
+
};
|
|
2548
|
+
}
|
|
2549
|
+
});
|
|
2550
|
+
|
|
2224
2551
|
// src/team/tiers.ts
|
|
2225
2552
|
function dekKey(collection, tier) {
|
|
2226
2553
|
if (tier <= 0) return collection;
|
|
@@ -2340,6 +2667,189 @@ var init_predicate = __esm({
|
|
|
2340
2667
|
}
|
|
2341
2668
|
});
|
|
2342
2669
|
|
|
2670
|
+
// src/money/money-reducer.ts
|
|
2671
|
+
function toScaledInt(v) {
|
|
2672
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "bigint") {
|
|
2673
|
+
try {
|
|
2674
|
+
return BigInt(v);
|
|
2675
|
+
} catch {
|
|
2676
|
+
return null;
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
return null;
|
|
2680
|
+
}
|
|
2681
|
+
function readMoney(record, field, desc) {
|
|
2682
|
+
const raw = readPath(record, field);
|
|
2683
|
+
if (raw === null || raw === void 0) return null;
|
|
2684
|
+
if (desc.mode === "fixed") {
|
|
2685
|
+
const value2 = toScaledInt(raw);
|
|
2686
|
+
return value2 === null ? null : { currency: desc.fixedCurrency, value: value2 };
|
|
2687
|
+
}
|
|
2688
|
+
if (typeof raw !== "object") return null;
|
|
2689
|
+
const o = raw;
|
|
2690
|
+
if (typeof o.currency !== "string") return null;
|
|
2691
|
+
const value = toScaledInt(o.amount);
|
|
2692
|
+
return value === null ? null : { currency: o.currency, value };
|
|
2693
|
+
}
|
|
2694
|
+
function targetScaleFor(desc, currency) {
|
|
2695
|
+
if (desc.allows(currency)) return desc.scaleFor(currency);
|
|
2696
|
+
const s = scaleForCurrency(currency);
|
|
2697
|
+
if (s === null) {
|
|
2698
|
+
throw new Error(`money: cannot determine scale for conversion target "${currency}"`);
|
|
2699
|
+
}
|
|
2700
|
+
return s;
|
|
2701
|
+
}
|
|
2702
|
+
function parseRate(rate) {
|
|
2703
|
+
const s = String(rate).trim();
|
|
2704
|
+
const neg = s.startsWith("-");
|
|
2705
|
+
const body = neg ? s.slice(1) : s;
|
|
2706
|
+
const dot = body.indexOf(".");
|
|
2707
|
+
const intPart = dot === -1 ? body : body.slice(0, dot);
|
|
2708
|
+
const fracPart = dot === -1 ? "" : body.slice(dot + 1);
|
|
2709
|
+
const int = BigInt((intPart === "" ? "0" : intPart) + fracPart);
|
|
2710
|
+
return { int: neg ? -int : int, scale: fracPart.length };
|
|
2711
|
+
}
|
|
2712
|
+
function divRoundHalfEven(n, d) {
|
|
2713
|
+
const q = n / d;
|
|
2714
|
+
const r = n % d;
|
|
2715
|
+
const twiceR = (r < 0n ? -r : r) * 2n;
|
|
2716
|
+
if (twiceR < d) return q;
|
|
2717
|
+
if (twiceR > d) return q + (n < 0n ? -1n : 1n);
|
|
2718
|
+
return q % 2n === 0n ? q : q + (n < 0n ? -1n : 1n);
|
|
2719
|
+
}
|
|
2720
|
+
function convertScaled(value, srcScale, rate, targetScale) {
|
|
2721
|
+
const { int: rateInt, scale: rateScale } = parseRate(rate);
|
|
2722
|
+
const product = value * rateInt;
|
|
2723
|
+
const curScale = srcScale + rateScale;
|
|
2724
|
+
if (curScale === targetScale) return product;
|
|
2725
|
+
if (curScale < targetScale) return product * 10n ** BigInt(targetScale - curScale);
|
|
2726
|
+
return divRoundHalfEven(product, 10n ** BigInt(curScale - targetScale));
|
|
2727
|
+
}
|
|
2728
|
+
function finalizeSum(state, desc, convertTo, fx) {
|
|
2729
|
+
if (convertTo !== void 0) {
|
|
2730
|
+
if (fx === void 0) {
|
|
2731
|
+
throw new Error(`money: sum convertTo "${convertTo}" requires an fx rate map`);
|
|
2732
|
+
}
|
|
2733
|
+
const targetScale = targetScaleFor(desc, convertTo);
|
|
2734
|
+
let total = 0n;
|
|
2735
|
+
for (const [cur, v] of state) {
|
|
2736
|
+
if (cur === convertTo) {
|
|
2737
|
+
total += convertScaled(v, desc.scaleFor(cur), 1, targetScale);
|
|
2738
|
+
continue;
|
|
2739
|
+
}
|
|
2740
|
+
const rate = fx[`${cur}->${convertTo}`];
|
|
2741
|
+
if (rate === void 0) {
|
|
2742
|
+
throw new Error(`money: no fx rate for "${cur}->${convertTo}"`);
|
|
2743
|
+
}
|
|
2744
|
+
total += convertScaled(v, desc.scaleFor(cur), rate, targetScale);
|
|
2745
|
+
}
|
|
2746
|
+
return formatScaledInt(total, targetScale);
|
|
2747
|
+
}
|
|
2748
|
+
if (desc.mode === "fixed") {
|
|
2749
|
+
const cur = desc.fixedCurrency;
|
|
2750
|
+
return formatScaledInt(state.get(cur) ?? 0n, desc.scaleFor(cur));
|
|
2751
|
+
}
|
|
2752
|
+
const out = {};
|
|
2753
|
+
for (const [cur, v] of state) out[cur] = formatScaledInt(v, desc.scaleFor(cur));
|
|
2754
|
+
return out;
|
|
2755
|
+
}
|
|
2756
|
+
function moneySumReducer(field, desc, convertTo, fx) {
|
|
2757
|
+
return {
|
|
2758
|
+
op: "sum",
|
|
2759
|
+
field,
|
|
2760
|
+
init: () => /* @__PURE__ */ new Map(),
|
|
2761
|
+
step: (state, record) => {
|
|
2762
|
+
const m = readMoney(record, field, desc);
|
|
2763
|
+
if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) + m.value);
|
|
2764
|
+
return state;
|
|
2765
|
+
},
|
|
2766
|
+
remove: (state, record) => {
|
|
2767
|
+
const m = readMoney(record, field, desc);
|
|
2768
|
+
if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) - m.value);
|
|
2769
|
+
return state;
|
|
2770
|
+
},
|
|
2771
|
+
finalize: (state) => finalizeSum(state, desc, convertTo, fx)
|
|
2772
|
+
};
|
|
2773
|
+
}
|
|
2774
|
+
function extremum(values, op) {
|
|
2775
|
+
let out = values[0];
|
|
2776
|
+
for (let i = 1; i < values.length; i++) {
|
|
2777
|
+
const v = values[i];
|
|
2778
|
+
if (op === "min" ? v < out : v > out) out = v;
|
|
2779
|
+
}
|
|
2780
|
+
return out;
|
|
2781
|
+
}
|
|
2782
|
+
function moneyMinMaxReducer(op, field, desc) {
|
|
2783
|
+
return {
|
|
2784
|
+
op,
|
|
2785
|
+
field,
|
|
2786
|
+
init: () => /* @__PURE__ */ new Map(),
|
|
2787
|
+
step: (state, record) => {
|
|
2788
|
+
const m = readMoney(record, field, desc);
|
|
2789
|
+
if (m) {
|
|
2790
|
+
const arr = state.get(m.currency);
|
|
2791
|
+
if (arr) arr.push(m.value);
|
|
2792
|
+
else state.set(m.currency, [m.value]);
|
|
2793
|
+
}
|
|
2794
|
+
return state;
|
|
2795
|
+
},
|
|
2796
|
+
remove: (state, record) => {
|
|
2797
|
+
const m = readMoney(record, field, desc);
|
|
2798
|
+
if (m) {
|
|
2799
|
+
const arr = state.get(m.currency);
|
|
2800
|
+
if (arr) {
|
|
2801
|
+
const idx = arr.indexOf(m.value);
|
|
2802
|
+
if (idx >= 0) arr.splice(idx, 1);
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
return state;
|
|
2806
|
+
},
|
|
2807
|
+
finalize: (state) => {
|
|
2808
|
+
if (desc.mode === "fixed") {
|
|
2809
|
+
const cur = desc.fixedCurrency;
|
|
2810
|
+
const arr = state.get(cur);
|
|
2811
|
+
if (!arr || arr.length === 0) return null;
|
|
2812
|
+
return formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
|
|
2813
|
+
}
|
|
2814
|
+
const out = {};
|
|
2815
|
+
for (const [cur, arr] of state) {
|
|
2816
|
+
if (arr.length > 0) out[cur] = formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
|
|
2817
|
+
}
|
|
2818
|
+
return out;
|
|
2819
|
+
}
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
function wrapMoneyReducers(spec, moneyFields) {
|
|
2823
|
+
let changed = false;
|
|
2824
|
+
const out = {};
|
|
2825
|
+
for (const [key, reducer] of Object.entries(spec)) {
|
|
2826
|
+
const field = reducer.field;
|
|
2827
|
+
const desc = field ? moneyFields[field] : void 0;
|
|
2828
|
+
if (desc && reducer.op === "avg") {
|
|
2829
|
+
throw new MoneyUnsupportedError(
|
|
2830
|
+
field,
|
|
2831
|
+
`avg() is not supported on money field "${field}" in v1 \u2014 use sum() and count() and divide at the boundary.`
|
|
2832
|
+
);
|
|
2833
|
+
}
|
|
2834
|
+
if (desc && (reducer.op === "sum" || reducer.op === "min" || reducer.op === "max")) {
|
|
2835
|
+
changed = true;
|
|
2836
|
+
out[key] = reducer.op === "sum" ? moneySumReducer(field, desc, reducer.convertTo, reducer.fx) : moneyMinMaxReducer(reducer.op, field, desc);
|
|
2837
|
+
} else {
|
|
2838
|
+
out[key] = reducer;
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
return changed ? out : spec;
|
|
2842
|
+
}
|
|
2843
|
+
var init_money_reducer = __esm({
|
|
2844
|
+
"src/money/money-reducer.ts"() {
|
|
2845
|
+
"use strict";
|
|
2846
|
+
init_predicate();
|
|
2847
|
+
init_fixed_point();
|
|
2848
|
+
init_iso4217();
|
|
2849
|
+
init_descriptor();
|
|
2850
|
+
}
|
|
2851
|
+
});
|
|
2852
|
+
|
|
2343
2853
|
// src/aggregate/aggregation.ts
|
|
2344
2854
|
function reduceRecords(records, spec) {
|
|
2345
2855
|
const state = {};
|
|
@@ -2557,19 +3067,22 @@ var init_groupby = __esm({
|
|
|
2557
3067
|
init_aggregation();
|
|
2558
3068
|
init_canonical_key();
|
|
2559
3069
|
init_errors();
|
|
3070
|
+
init_money_reducer();
|
|
2560
3071
|
GROUPBY_WARN_CARDINALITY = 1e4;
|
|
2561
3072
|
GROUPBY_MAX_CARDINALITY = 1e5;
|
|
2562
3073
|
warnedCardinalityFields = /* @__PURE__ */ new Set();
|
|
2563
3074
|
GroupedQueryBase = class {
|
|
2564
|
-
constructor(executeRecords, fieldOrFields, upstreams, dictLabelResolver) {
|
|
3075
|
+
constructor(executeRecords, fieldOrFields, upstreams, dictLabelResolver, moneyFields) {
|
|
2565
3076
|
this.executeRecords = executeRecords;
|
|
2566
3077
|
this.upstreams = upstreams;
|
|
2567
3078
|
this.dictLabelResolver = dictLabelResolver;
|
|
3079
|
+
this.moneyFields = moneyFields;
|
|
2568
3080
|
this.fields = typeof fieldOrFields === "string" ? [fieldOrFields] : [...fieldOrFields];
|
|
2569
3081
|
}
|
|
2570
3082
|
executeRecords;
|
|
2571
3083
|
upstreams;
|
|
2572
3084
|
dictLabelResolver;
|
|
3085
|
+
moneyFields;
|
|
2573
3086
|
/**
|
|
2574
3087
|
* Field set this grouped query buckets on. Stored in declaration
|
|
2575
3088
|
* order — the same order is preserved on every result row by
|
|
@@ -2577,6 +3090,10 @@ var init_groupby = __esm({
|
|
|
2577
3090
|
* `[field]`.
|
|
2578
3091
|
*/
|
|
2579
3092
|
fields;
|
|
3093
|
+
/** Apply money-aware reducer rewriting when money fields are declared. */
|
|
3094
|
+
wrapSpec(spec) {
|
|
3095
|
+
return this.moneyFields ? wrapMoneyReducers(spec, this.moneyFields) : spec;
|
|
3096
|
+
}
|
|
2580
3097
|
};
|
|
2581
3098
|
GroupedQuery = class extends GroupedQueryBase {
|
|
2582
3099
|
/**
|
|
@@ -2589,7 +3106,7 @@ var init_groupby = __esm({
|
|
|
2589
3106
|
return new GroupedAggregation(
|
|
2590
3107
|
this.executeRecords,
|
|
2591
3108
|
this.fields,
|
|
2592
|
-
spec,
|
|
3109
|
+
this.wrapSpec(spec),
|
|
2593
3110
|
this.upstreams,
|
|
2594
3111
|
this.dictLabelResolver
|
|
2595
3112
|
);
|
|
@@ -2600,7 +3117,7 @@ var init_groupby = __esm({
|
|
|
2600
3117
|
return new GroupedAggregation(
|
|
2601
3118
|
this.executeRecords,
|
|
2602
3119
|
this.fields,
|
|
2603
|
-
spec,
|
|
3120
|
+
this.wrapSpec(spec),
|
|
2604
3121
|
this.upstreams,
|
|
2605
3122
|
this.dictLabelResolver
|
|
2606
3123
|
);
|
|
@@ -2716,14 +3233,21 @@ var init_executor = __esm({
|
|
|
2716
3233
|
* Compare existing vs incoming for each `frozenFields.fields` entry
|
|
2717
3234
|
* when `frozenFields.when(existing)` is true. Throws
|
|
2718
3235
|
* `FieldFrozenError` listing every changed frozen field.
|
|
3236
|
+
*
|
|
3237
|
+
* @param skipFields — field names that are schema-owned computed fields.
|
|
3238
|
+
* These are excluded from the comparison because `incoming` carries the
|
|
3239
|
+
* raw user input (computed fields not yet evaluated), so comparing
|
|
3240
|
+
* `existing[field]` vs `incoming[field]` would always look like a
|
|
3241
|
+
* change even when the computed result is unchanged.
|
|
2719
3242
|
*/
|
|
2720
|
-
async checkFrozenFields(guard, id, existing, incoming) {
|
|
3243
|
+
async checkFrozenFields(guard, id, existing, incoming, skipFields) {
|
|
2721
3244
|
const ff = guard.frozenFields;
|
|
2722
3245
|
if (!ff) return;
|
|
2723
3246
|
if (existing === null) return;
|
|
2724
3247
|
if (!ff.when(existing)) return;
|
|
2725
3248
|
const changed = [];
|
|
2726
3249
|
for (const f of ff.fields) {
|
|
3250
|
+
if (skipFields?.has(String(f))) continue;
|
|
2727
3251
|
if (existing[f] !== incoming[f]) {
|
|
2728
3252
|
if (!deepEqual2(existing[f], incoming[f])) changed.push(String(f));
|
|
2729
3253
|
}
|
|
@@ -4052,6 +4576,476 @@ var init_delegation = __esm({
|
|
|
4052
4576
|
}
|
|
4053
4577
|
});
|
|
4054
4578
|
|
|
4579
|
+
// src/federation/classify-skip.ts
|
|
4580
|
+
function classifyShardSkip(err) {
|
|
4581
|
+
return err instanceof NoAccessError ? "no-grant" : "error";
|
|
4582
|
+
}
|
|
4583
|
+
var init_classify_skip = __esm({
|
|
4584
|
+
"src/federation/classify-skip.ts"() {
|
|
4585
|
+
"use strict";
|
|
4586
|
+
init_errors();
|
|
4587
|
+
}
|
|
4588
|
+
});
|
|
4589
|
+
|
|
4590
|
+
// src/federation/cross-vault-live.ts
|
|
4591
|
+
var CrossVaultLive;
|
|
4592
|
+
var init_cross_vault_live = __esm({
|
|
4593
|
+
"src/federation/cross-vault-live.ts"() {
|
|
4594
|
+
"use strict";
|
|
4595
|
+
CrossVaultLive = class {
|
|
4596
|
+
snapshot;
|
|
4597
|
+
error = null;
|
|
4598
|
+
ready;
|
|
4599
|
+
subs = /* @__PURE__ */ new Set();
|
|
4600
|
+
unsubChange;
|
|
4601
|
+
opts;
|
|
4602
|
+
stopped = false;
|
|
4603
|
+
computing = false;
|
|
4604
|
+
dirty = false;
|
|
4605
|
+
scheduled = false;
|
|
4606
|
+
timer = null;
|
|
4607
|
+
resolveReady;
|
|
4608
|
+
settledOnce = false;
|
|
4609
|
+
constructor(opts) {
|
|
4610
|
+
this.opts = opts;
|
|
4611
|
+
this.snapshot = opts.initialSnapshot;
|
|
4612
|
+
this.ready = new Promise((res) => {
|
|
4613
|
+
this.resolveReady = res;
|
|
4614
|
+
});
|
|
4615
|
+
this.unsubChange = opts.subscribeToChanges((e) => {
|
|
4616
|
+
if (this.stopped || !opts.isRelevant(e)) return;
|
|
4617
|
+
this.schedule();
|
|
4618
|
+
});
|
|
4619
|
+
this.schedule();
|
|
4620
|
+
}
|
|
4621
|
+
subscribe(cb) {
|
|
4622
|
+
if (this.stopped) return () => {
|
|
4623
|
+
};
|
|
4624
|
+
this.subs.add(cb);
|
|
4625
|
+
return () => this.subs.delete(cb);
|
|
4626
|
+
}
|
|
4627
|
+
stop() {
|
|
4628
|
+
if (this.stopped) return;
|
|
4629
|
+
this.stopped = true;
|
|
4630
|
+
this.unsubChange();
|
|
4631
|
+
if (this.timer !== null) clearTimeout(this.timer);
|
|
4632
|
+
this.subs.clear();
|
|
4633
|
+
if (!this.settledOnce) this.resolveReady();
|
|
4634
|
+
}
|
|
4635
|
+
schedule() {
|
|
4636
|
+
if (this.stopped) return;
|
|
4637
|
+
if (this.computing) {
|
|
4638
|
+
this.dirty = true;
|
|
4639
|
+
return;
|
|
4640
|
+
}
|
|
4641
|
+
if (this.scheduled) return;
|
|
4642
|
+
this.scheduled = true;
|
|
4643
|
+
const run = () => {
|
|
4644
|
+
this.scheduled = false;
|
|
4645
|
+
void this.runCompute();
|
|
4646
|
+
};
|
|
4647
|
+
const ms = this.opts.debounceMs ?? 0;
|
|
4648
|
+
if (ms > 0) this.timer = setTimeout(run, ms);
|
|
4649
|
+
else queueMicrotask(run);
|
|
4650
|
+
}
|
|
4651
|
+
async runCompute() {
|
|
4652
|
+
if (this.stopped) return;
|
|
4653
|
+
this.computing = true;
|
|
4654
|
+
this.dirty = false;
|
|
4655
|
+
try {
|
|
4656
|
+
const next = await this.opts.compute();
|
|
4657
|
+
if (this.stopped) return;
|
|
4658
|
+
this.snapshot = next;
|
|
4659
|
+
this.error = null;
|
|
4660
|
+
} catch (err) {
|
|
4661
|
+
if (this.stopped) return;
|
|
4662
|
+
this.error = err instanceof Error ? err : new Error(String(err));
|
|
4663
|
+
} finally {
|
|
4664
|
+
this.computing = false;
|
|
4665
|
+
if (!this.stopped) {
|
|
4666
|
+
if (!this.settledOnce) {
|
|
4667
|
+
this.settledOnce = true;
|
|
4668
|
+
this.resolveReady();
|
|
4669
|
+
}
|
|
4670
|
+
for (const cb of this.subs) cb();
|
|
4671
|
+
if (this.dirty) this.schedule();
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
};
|
|
4676
|
+
}
|
|
4677
|
+
});
|
|
4678
|
+
|
|
4679
|
+
// src/federation/aggregate-across.ts
|
|
4680
|
+
var CrossVaultAggregation, CrossVaultGroupedAggregation;
|
|
4681
|
+
var init_aggregate_across = __esm({
|
|
4682
|
+
"src/federation/aggregate-across.ts"() {
|
|
4683
|
+
"use strict";
|
|
4684
|
+
init_aggregation();
|
|
4685
|
+
init_groupby();
|
|
4686
|
+
init_cross_vault_live();
|
|
4687
|
+
CrossVaultAggregation = class {
|
|
4688
|
+
constructor(src, spec, bind) {
|
|
4689
|
+
this.src = src;
|
|
4690
|
+
this.spec = spec;
|
|
4691
|
+
this.bind = bind;
|
|
4692
|
+
}
|
|
4693
|
+
src;
|
|
4694
|
+
spec;
|
|
4695
|
+
bind;
|
|
4696
|
+
async run(options = {}) {
|
|
4697
|
+
const { records, skippedVaults } = await this.src.fanoutRecords(options);
|
|
4698
|
+
return { result: reduceRecords(records, this.spec), skippedVaults };
|
|
4699
|
+
}
|
|
4700
|
+
live(options = {}) {
|
|
4701
|
+
if (!this.bind) throw new Error("CrossVaultAggregation: live() requires a LiveBinding \u2014 use ShardedQuery.aggregate()");
|
|
4702
|
+
const spec = this.spec;
|
|
4703
|
+
const src = this.src;
|
|
4704
|
+
const core = new CrossVaultLive({
|
|
4705
|
+
subscribeToChanges: this.bind.subscribeToChanges,
|
|
4706
|
+
isRelevant: this.bind.isRelevant,
|
|
4707
|
+
compute: async () => {
|
|
4708
|
+
const { records, skippedVaults } = await src.fanoutRecords(options);
|
|
4709
|
+
return { value: reduceRecords(records, spec), skipped: skippedVaults };
|
|
4710
|
+
},
|
|
4711
|
+
initialSnapshot: { value: void 0, skipped: [] },
|
|
4712
|
+
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
4713
|
+
});
|
|
4714
|
+
return {
|
|
4715
|
+
get value() {
|
|
4716
|
+
return core.snapshot.value;
|
|
4717
|
+
},
|
|
4718
|
+
get skippedVaults() {
|
|
4719
|
+
return core.snapshot.skipped;
|
|
4720
|
+
},
|
|
4721
|
+
get error() {
|
|
4722
|
+
return core.error;
|
|
4723
|
+
},
|
|
4724
|
+
ready: core.ready,
|
|
4725
|
+
subscribe: (cb) => core.subscribe(cb),
|
|
4726
|
+
stop: () => core.stop()
|
|
4727
|
+
};
|
|
4728
|
+
}
|
|
4729
|
+
};
|
|
4730
|
+
CrossVaultGroupedAggregation = class {
|
|
4731
|
+
constructor(src, field, spec, bind) {
|
|
4732
|
+
this.src = src;
|
|
4733
|
+
this.field = field;
|
|
4734
|
+
this.spec = spec;
|
|
4735
|
+
this.bind = bind;
|
|
4736
|
+
}
|
|
4737
|
+
src;
|
|
4738
|
+
field;
|
|
4739
|
+
spec;
|
|
4740
|
+
bind;
|
|
4741
|
+
async run(options = {}) {
|
|
4742
|
+
const { records, skippedVaults } = await this.src.fanoutRecords(options);
|
|
4743
|
+
return {
|
|
4744
|
+
results: groupAndReduce(records, this.field, this.spec),
|
|
4745
|
+
skippedVaults
|
|
4746
|
+
};
|
|
4747
|
+
}
|
|
4748
|
+
live(options = {}) {
|
|
4749
|
+
if (!this.bind) throw new Error("CrossVaultGroupedAggregation: live() requires a LiveBinding \u2014 use ShardedQuery.groupBy().aggregate()");
|
|
4750
|
+
const field = this.field;
|
|
4751
|
+
const spec = this.spec;
|
|
4752
|
+
const src = this.src;
|
|
4753
|
+
const core = new CrossVaultLive({
|
|
4754
|
+
subscribeToChanges: this.bind.subscribeToChanges,
|
|
4755
|
+
isRelevant: this.bind.isRelevant,
|
|
4756
|
+
compute: async () => {
|
|
4757
|
+
const { records, skippedVaults } = await src.fanoutRecords(options);
|
|
4758
|
+
return {
|
|
4759
|
+
records: groupAndReduce(records, field, spec),
|
|
4760
|
+
skipped: skippedVaults
|
|
4761
|
+
};
|
|
4762
|
+
},
|
|
4763
|
+
initialSnapshot: { records: [], skipped: [] },
|
|
4764
|
+
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
4765
|
+
});
|
|
4766
|
+
return {
|
|
4767
|
+
get value() {
|
|
4768
|
+
return core.snapshot.records;
|
|
4769
|
+
},
|
|
4770
|
+
get skippedVaults() {
|
|
4771
|
+
return core.snapshot.skipped;
|
|
4772
|
+
},
|
|
4773
|
+
get error() {
|
|
4774
|
+
return core.error;
|
|
4775
|
+
},
|
|
4776
|
+
ready: core.ready,
|
|
4777
|
+
subscribe: (cb) => core.subscribe(cb),
|
|
4778
|
+
stop: () => core.stop()
|
|
4779
|
+
};
|
|
4780
|
+
}
|
|
4781
|
+
};
|
|
4782
|
+
}
|
|
4783
|
+
});
|
|
4784
|
+
|
|
4785
|
+
// src/federation/vault-group.ts
|
|
4786
|
+
var vault_group_exports = {};
|
|
4787
|
+
__export(vault_group_exports, {
|
|
4788
|
+
ShardedCollection: () => ShardedCollection,
|
|
4789
|
+
ShardedGroupedQuery: () => ShardedGroupedQuery,
|
|
4790
|
+
ShardedQuery: () => ShardedQuery,
|
|
4791
|
+
VaultGroup: () => VaultGroup
|
|
4792
|
+
});
|
|
4793
|
+
function assertSafePartitionKey(partitionKey) {
|
|
4794
|
+
if (partitionKey.length === 0) {
|
|
4795
|
+
throw new ValidationError("partitionKey must be a non-empty string");
|
|
4796
|
+
}
|
|
4797
|
+
if (!SAFE_PARTITION_KEY.test(partitionKey)) {
|
|
4798
|
+
throw new ValidationError(
|
|
4799
|
+
`partitionKey "${partitionKey}" contains characters outside [A-Za-z0-9._-]. Map your records to a store-safe key in sharding.keyOf.`
|
|
4800
|
+
);
|
|
4801
|
+
}
|
|
4802
|
+
if (partitionKey.includes(SHARD_SEPARATOR)) {
|
|
4803
|
+
throw new ValidationError(
|
|
4804
|
+
`partitionKey "${partitionKey}" must not contain "--" \u2014 it is reserved as the shard vault-id separator and would risk shard-id collisions.`
|
|
4805
|
+
);
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4808
|
+
var SHARD_SEPARATOR, SAFE_PARTITION_KEY, VaultGroup, ShardedCollection, ShardedQuery, ShardedGroupedQuery;
|
|
4809
|
+
var init_vault_group = __esm({
|
|
4810
|
+
"src/federation/vault-group.ts"() {
|
|
4811
|
+
"use strict";
|
|
4812
|
+
init_errors();
|
|
4813
|
+
init_classify_skip();
|
|
4814
|
+
init_cross_vault_live();
|
|
4815
|
+
init_aggregate_across();
|
|
4816
|
+
SHARD_SEPARATOR = "--";
|
|
4817
|
+
SAFE_PARTITION_KEY = /^[A-Za-z0-9._-]+$/;
|
|
4818
|
+
VaultGroup = class {
|
|
4819
|
+
constructor(db, name, registry, sharding, template) {
|
|
4820
|
+
this.db = db;
|
|
4821
|
+
this.name = name;
|
|
4822
|
+
this.registry = registry;
|
|
4823
|
+
this.sharding = sharding;
|
|
4824
|
+
this.template = template;
|
|
4825
|
+
if (name.includes(SHARD_SEPARATOR)) {
|
|
4826
|
+
throw new ValidationError(
|
|
4827
|
+
`VaultGroup name "${name}" must not contain "--" (reserved shard vault-id separator).`
|
|
4828
|
+
);
|
|
4829
|
+
}
|
|
4830
|
+
}
|
|
4831
|
+
db;
|
|
4832
|
+
name;
|
|
4833
|
+
registry;
|
|
4834
|
+
sharding;
|
|
4835
|
+
template;
|
|
4836
|
+
/** Deterministic vault name for a partition key, namespaced by the group. */
|
|
4837
|
+
shardVaultId(partitionKey) {
|
|
4838
|
+
assertSafePartitionKey(partitionKey);
|
|
4839
|
+
return `${this.name}${SHARD_SEPARATOR}${partitionKey}`;
|
|
4840
|
+
}
|
|
4841
|
+
/** All registry rows (hydrates the registry collection first). */
|
|
4842
|
+
async allRows() {
|
|
4843
|
+
await this.registry.list();
|
|
4844
|
+
return this.registry.query().toArray();
|
|
4845
|
+
}
|
|
4846
|
+
/** Open an existing shard and apply the template. */
|
|
4847
|
+
async openShard(partitionKey) {
|
|
4848
|
+
const vault = await this.db.openVault(this.shardVaultId(partitionKey), { create: false });
|
|
4849
|
+
this.template.configure(vault);
|
|
4850
|
+
return vault;
|
|
4851
|
+
}
|
|
4852
|
+
/**
|
|
4853
|
+
* Idempotently provision a shard for `partitionKey`. Returns the
|
|
4854
|
+
* configured vault handle.
|
|
4855
|
+
*
|
|
4856
|
+
* - row + vault present → no-op, return handle
|
|
4857
|
+
* - row present, vault gone → ShardProvisioningError
|
|
4858
|
+
* - row absent (vault present or not) → open-or-create, configure, write row
|
|
4859
|
+
*/
|
|
4860
|
+
async createShard(partitionKey) {
|
|
4861
|
+
const vaultId = this.shardVaultId(partitionKey);
|
|
4862
|
+
const row = await this.registry.get(partitionKey);
|
|
4863
|
+
const provisioned = await this.db._shardVaultProvisioned(vaultId);
|
|
4864
|
+
if (row && !provisioned) throw new ShardProvisioningError(vaultId, partitionKey);
|
|
4865
|
+
if (row && provisioned) return this.openShard(partitionKey);
|
|
4866
|
+
const vault = await this.db.openVault(vaultId);
|
|
4867
|
+
this.template.configure(vault);
|
|
4868
|
+
await this.registry.put(partitionKey, {
|
|
4869
|
+
vaultId,
|
|
4870
|
+
partitionKey,
|
|
4871
|
+
templateName: this.sharding.vaultTemplate,
|
|
4872
|
+
schemaVersion: this.template.version,
|
|
4873
|
+
createdAt: Date.now()
|
|
4874
|
+
});
|
|
4875
|
+
return vault;
|
|
4876
|
+
}
|
|
4877
|
+
/**
|
|
4878
|
+
* Drill down to a single shard's full Collection API. Throws if the shard is unknown.
|
|
4879
|
+
* Also throws ShardProvisioningError if the registry row exists but the vault has been deleted
|
|
4880
|
+
* (registry/store divergence).
|
|
4881
|
+
*/
|
|
4882
|
+
async shard(partitionKey) {
|
|
4883
|
+
const vaultId = this.shardVaultId(partitionKey);
|
|
4884
|
+
const row = await this.registry.get(partitionKey);
|
|
4885
|
+
if (!row) throw new UnknownShardError(partitionKey, this.name);
|
|
4886
|
+
const provisioned = await this.db._shardVaultProvisioned(vaultId);
|
|
4887
|
+
if (!provisioned) throw new ShardProvisioningError(vaultId, partitionKey);
|
|
4888
|
+
return this.openShard(partitionKey);
|
|
4889
|
+
}
|
|
4890
|
+
/** A sharded view over one logical collection across all shards. */
|
|
4891
|
+
collection(collectionName) {
|
|
4892
|
+
return new ShardedCollection(this, collectionName);
|
|
4893
|
+
}
|
|
4894
|
+
/** @internal — eligible (openable-candidate) rows + drift/divergence skips. */
|
|
4895
|
+
async resolveEligible(options = {}) {
|
|
4896
|
+
const rows = await this.allRows();
|
|
4897
|
+
const skipped = [];
|
|
4898
|
+
const versionOk = [];
|
|
4899
|
+
for (const row of rows) {
|
|
4900
|
+
if (options.minVersion !== void 0 && row.schemaVersion < options.minVersion) {
|
|
4901
|
+
skipped.push({ vaultId: row.vaultId, reason: "schema-drift" });
|
|
4902
|
+
} else versionOk.push(row);
|
|
4903
|
+
}
|
|
4904
|
+
const provisioned = await Promise.all(versionOk.map((r) => this.db._shardVaultProvisioned(r.vaultId)));
|
|
4905
|
+
const eligible = [];
|
|
4906
|
+
versionOk.forEach((row, i) => {
|
|
4907
|
+
if (provisioned[i]) eligible.push(row);
|
|
4908
|
+
else skipped.push({ vaultId: row.vaultId, reason: "error", error: new ShardProvisioningError(row.vaultId, row.partitionKey) });
|
|
4909
|
+
});
|
|
4910
|
+
return { eligible, skipped };
|
|
4911
|
+
}
|
|
4912
|
+
};
|
|
4913
|
+
ShardedCollection = class {
|
|
4914
|
+
constructor(group, collectionName) {
|
|
4915
|
+
this.group = group;
|
|
4916
|
+
this.collectionName = collectionName;
|
|
4917
|
+
}
|
|
4918
|
+
group;
|
|
4919
|
+
collectionName;
|
|
4920
|
+
/** Route a write to the shard owning `keyOf(record)`. */
|
|
4921
|
+
async put(id, record) {
|
|
4922
|
+
const key = this.group.sharding.keyOf(record);
|
|
4923
|
+
const row = await this.group.registry.get(key);
|
|
4924
|
+
let vault;
|
|
4925
|
+
if (!row) {
|
|
4926
|
+
if (this.group.sharding.autoCreate === false) {
|
|
4927
|
+
throw new UnknownShardError(key, this.group.name);
|
|
4928
|
+
}
|
|
4929
|
+
vault = await this.group.createShard(key);
|
|
4930
|
+
} else {
|
|
4931
|
+
vault = await this.group.openShard(key);
|
|
4932
|
+
}
|
|
4933
|
+
await vault.collection(this.collectionName).put(id, record);
|
|
4934
|
+
}
|
|
4935
|
+
/** Begin a cross-shard fan-out query. */
|
|
4936
|
+
query() {
|
|
4937
|
+
return new ShardedQuery(this.group, this.collectionName, []);
|
|
4938
|
+
}
|
|
4939
|
+
};
|
|
4940
|
+
ShardedQuery = class _ShardedQuery {
|
|
4941
|
+
constructor(group, collectionName, clauses) {
|
|
4942
|
+
this.group = group;
|
|
4943
|
+
this.collectionName = collectionName;
|
|
4944
|
+
this.clauses = clauses;
|
|
4945
|
+
}
|
|
4946
|
+
group;
|
|
4947
|
+
collectionName;
|
|
4948
|
+
clauses;
|
|
4949
|
+
where(field, op, value) {
|
|
4950
|
+
return new _ShardedQuery(this.group, this.collectionName, [
|
|
4951
|
+
...this.clauses,
|
|
4952
|
+
{ field, op, value }
|
|
4953
|
+
]);
|
|
4954
|
+
}
|
|
4955
|
+
/** @internal — fan out the where-filtered records across eligible shards. */
|
|
4956
|
+
async fanoutRecords(options = {}) {
|
|
4957
|
+
const { eligible, skipped } = await this.group.resolveEligible(options);
|
|
4958
|
+
const across = await this.group.db.queryAcross(
|
|
4959
|
+
eligible.map((r) => r.vaultId),
|
|
4960
|
+
async (vault) => {
|
|
4961
|
+
this.group.template.configure(vault);
|
|
4962
|
+
const coll = vault.collection(this.collectionName);
|
|
4963
|
+
await coll.list();
|
|
4964
|
+
let q = coll.query();
|
|
4965
|
+
for (const c of this.clauses) q = q.where(c.field, c.op, c.value);
|
|
4966
|
+
return q.toArray();
|
|
4967
|
+
},
|
|
4968
|
+
{ concurrency: options.concurrency ?? 1, create: false }
|
|
4969
|
+
);
|
|
4970
|
+
const results = [];
|
|
4971
|
+
for (const r of across) {
|
|
4972
|
+
if (r.error) skipped.push({ vaultId: r.vault, reason: classifyShardSkip(r.error), error: r.error });
|
|
4973
|
+
else for (const item of r.result) results.push(item);
|
|
4974
|
+
}
|
|
4975
|
+
return { records: results, skippedVaults: skipped };
|
|
4976
|
+
}
|
|
4977
|
+
/** Fan out across eligible shards and merge results. */
|
|
4978
|
+
async toArray(options = {}) {
|
|
4979
|
+
const { records, skippedVaults } = await this.fanoutRecords(options);
|
|
4980
|
+
return { results: records, skippedVaults };
|
|
4981
|
+
}
|
|
4982
|
+
/** @internal — build the change-subscription + relevance binding for this query's group+collection. */
|
|
4983
|
+
liveBinding() {
|
|
4984
|
+
const group = this.group;
|
|
4985
|
+
const collectionName = this.collectionName;
|
|
4986
|
+
return {
|
|
4987
|
+
subscribeToChanges: (h) => {
|
|
4988
|
+
group.db.on("change", h);
|
|
4989
|
+
return () => group.db.off("change", h);
|
|
4990
|
+
},
|
|
4991
|
+
isRelevant: (e) => e.collection === collectionName && e.vault.startsWith(`${group.name}--`)
|
|
4992
|
+
};
|
|
4993
|
+
}
|
|
4994
|
+
/** Returns a reactive cross-shard live query — a facade over CrossVaultLive. */
|
|
4995
|
+
live(options = {}) {
|
|
4996
|
+
const bind = this.liveBinding();
|
|
4997
|
+
const core = new CrossVaultLive({
|
|
4998
|
+
...bind,
|
|
4999
|
+
compute: async () => {
|
|
5000
|
+
const { records, skippedVaults } = await this.fanoutRecords(options);
|
|
5001
|
+
return { records, skipped: skippedVaults };
|
|
5002
|
+
},
|
|
5003
|
+
initialSnapshot: { records: [], skipped: [] },
|
|
5004
|
+
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
5005
|
+
});
|
|
5006
|
+
return {
|
|
5007
|
+
get value() {
|
|
5008
|
+
return core.snapshot.records;
|
|
5009
|
+
},
|
|
5010
|
+
get skippedVaults() {
|
|
5011
|
+
return core.snapshot.skipped;
|
|
5012
|
+
},
|
|
5013
|
+
get error() {
|
|
5014
|
+
return core.error;
|
|
5015
|
+
},
|
|
5016
|
+
ready: core.ready,
|
|
5017
|
+
subscribe: (cb) => core.subscribe(cb),
|
|
5018
|
+
stop: () => core.stop()
|
|
5019
|
+
};
|
|
5020
|
+
}
|
|
5021
|
+
/** One-shot distributed aggregate — central reduce over all shard records. */
|
|
5022
|
+
aggregate(spec) {
|
|
5023
|
+
return new CrossVaultAggregation(this, spec, this.liveBinding());
|
|
5024
|
+
}
|
|
5025
|
+
/** Begin a grouped cross-shard aggregate. */
|
|
5026
|
+
groupBy(field) {
|
|
5027
|
+
return new ShardedGroupedQuery(this, field);
|
|
5028
|
+
}
|
|
5029
|
+
};
|
|
5030
|
+
ShardedGroupedQuery = class {
|
|
5031
|
+
constructor(query, field) {
|
|
5032
|
+
this.query = query;
|
|
5033
|
+
this.field = field;
|
|
5034
|
+
}
|
|
5035
|
+
query;
|
|
5036
|
+
field;
|
|
5037
|
+
aggregate(spec) {
|
|
5038
|
+
return new CrossVaultGroupedAggregation(
|
|
5039
|
+
{ fanoutRecords: (o) => this.query.fanoutRecords(o) },
|
|
5040
|
+
this.field,
|
|
5041
|
+
spec,
|
|
5042
|
+
this.query.liveBinding()
|
|
5043
|
+
);
|
|
5044
|
+
}
|
|
5045
|
+
};
|
|
5046
|
+
}
|
|
5047
|
+
});
|
|
5048
|
+
|
|
4055
5049
|
// src/index.ts
|
|
4056
5050
|
var src_exports = {};
|
|
4057
5051
|
__export(src_exports, {
|
|
@@ -4076,6 +5070,7 @@ __export(src_exports, {
|
|
|
4076
5070
|
CollectionFrame: () => CollectionFrame,
|
|
4077
5071
|
CollectionIndexes: () => CollectionIndexes,
|
|
4078
5072
|
CollectionInstant: () => CollectionInstant,
|
|
5073
|
+
ComputedFieldError: () => ComputedFieldError,
|
|
4079
5074
|
ConflictError: () => ConflictError,
|
|
4080
5075
|
CrossJoinSourceUnknownError: () => CrossJoinSourceUnknownError,
|
|
4081
5076
|
CrossJoinTooLargeError: () => CrossJoinTooLargeError,
|
|
@@ -4139,6 +5134,9 @@ __export(src_exports, {
|
|
|
4139
5134
|
MemorySealingKeyProvider: () => MemorySealingKeyProvider,
|
|
4140
5135
|
MigrationRequiredError: () => MigrationRequiredError,
|
|
4141
5136
|
MissingTranslationError: () => MissingTranslationError,
|
|
5137
|
+
MoneyCurrencyError: () => MoneyCurrencyError,
|
|
5138
|
+
MoneyPrecisionError: () => MoneyPrecisionError,
|
|
5139
|
+
MoneyUnsupportedError: () => MoneyUnsupportedError,
|
|
4142
5140
|
NOYDB_BACKUP_VERSION: () => NOYDB_BACKUP_VERSION,
|
|
4143
5141
|
NOYDB_BUNDLE_FORMAT_VERSION: () => NOYDB_BUNDLE_FORMAT_VERSION,
|
|
4144
5142
|
NOYDB_BUNDLE_MAGIC: () => NOYDB_BUNDLE_MAGIC,
|
|
@@ -4191,9 +5189,13 @@ __export(src_exports, {
|
|
|
4191
5189
|
SchemaUpdateError: () => SchemaUpdateError,
|
|
4192
5190
|
SchemaValidationError: () => SchemaValidationError,
|
|
4193
5191
|
ScriptViolationError: () => ScriptViolationError,
|
|
5192
|
+
SequenceContentionError: () => SequenceContentionError,
|
|
5193
|
+
SequenceOfflineError: () => SequenceOfflineError,
|
|
5194
|
+
SequenceStore: () => SequenceStore,
|
|
4194
5195
|
SessionExpiredError: () => SessionExpiredError,
|
|
4195
5196
|
SessionNotFoundError: () => SessionNotFoundError,
|
|
4196
5197
|
SessionPolicyError: () => SessionPolicyError,
|
|
5198
|
+
ShardProvisioningError: () => ShardProvisioningError,
|
|
4197
5199
|
SnapshotNotFoundError: () => SnapshotNotFoundError,
|
|
4198
5200
|
StoreCapabilityError: () => StoreCapabilityError,
|
|
4199
5201
|
SyncEngine: () => SyncEngine,
|
|
@@ -4209,6 +5211,7 @@ __export(src_exports, {
|
|
|
4209
5211
|
USER_ENVELOPE_COLLECTION: () => USER_ENVELOPE_COLLECTION,
|
|
4210
5212
|
USER_ENVELOPE_MAX_BYTES: () => USER_ENVELOPE_MAX_BYTES,
|
|
4211
5213
|
UniqueConstraintError: () => UniqueConstraintError,
|
|
5214
|
+
UnknownShardError: () => UnknownShardError,
|
|
4212
5215
|
UnsupportedIndexOptionError: () => UnsupportedIndexOptionError,
|
|
4213
5216
|
UserApi: () => UserApi,
|
|
4214
5217
|
UserEnvelopeOversizedError: () => UserEnvelopeOversizedError,
|
|
@@ -4217,6 +5220,7 @@ __export(src_exports, {
|
|
|
4217
5220
|
Vault: () => Vault,
|
|
4218
5221
|
VaultFrame: () => VaultFrame,
|
|
4219
5222
|
VaultInstant: () => VaultInstant,
|
|
5223
|
+
VaultTemplateNotFoundError: () => VaultTemplateNotFoundError,
|
|
4220
5224
|
WeakPassphraseError: () => WeakPassphraseError,
|
|
4221
5225
|
activeSessionCount: () => activeSessionCount,
|
|
4222
5226
|
additiveOnly: () => additiveOnly,
|
|
@@ -4273,6 +5277,7 @@ __export(src_exports, {
|
|
|
4273
5277
|
envelopePayloadHash: () => envelopePayloadHash,
|
|
4274
5278
|
estimateEntropy: () => estimateEntropy,
|
|
4275
5279
|
estimateRecordBytes: () => estimateRecordBytes,
|
|
5280
|
+
evalComputedFields: () => evalComputedFields,
|
|
4276
5281
|
evaluateClause: () => evaluateClause,
|
|
4277
5282
|
evaluateExportCapability: () => evaluateExportCapability,
|
|
4278
5283
|
evaluateFieldClause: () => evaluateFieldClause,
|
|
@@ -4289,6 +5294,7 @@ __export(src_exports, {
|
|
|
4289
5294
|
hasRecoveryEnrolled: () => hasRecoveryEnrolled,
|
|
4290
5295
|
hashEntry: () => hashEntry,
|
|
4291
5296
|
i18nText: () => i18nText,
|
|
5297
|
+
immutableGuard: () => immutableGuard,
|
|
4292
5298
|
inferScripts: () => inferScripts,
|
|
4293
5299
|
isDevUnlockActive: () => isDevUnlockActive,
|
|
4294
5300
|
isDictCollectionName: () => isDictCollectionName,
|
|
@@ -4296,6 +5302,7 @@ __export(src_exports, {
|
|
|
4296
5302
|
isDiscriminant: () => isDiscriminant,
|
|
4297
5303
|
isI18nTextDescriptor: () => isI18nTextDescriptor,
|
|
4298
5304
|
isMagicLinkGrantExpired: () => isMagicLinkGrantExpired,
|
|
5305
|
+
isMoneyDescriptor: () => isMoneyDescriptor,
|
|
4299
5306
|
isPreCompressed: () => isPreCompressed,
|
|
4300
5307
|
isPublicEnvelope: () => isPublicEnvelope,
|
|
4301
5308
|
isSessionAlive: () => isSessionAlive,
|
|
@@ -4327,6 +5334,7 @@ __export(src_exports, {
|
|
|
4327
5334
|
mintPaperRecoveryEntry: () => mintPaperRecoveryEntry,
|
|
4328
5335
|
mintShamirRecoveryEntry: () => mintShamirRecoveryEntry,
|
|
4329
5336
|
mintWrappedDeksBlob: () => mintWrappedDeksBlob,
|
|
5337
|
+
money: () => money,
|
|
4330
5338
|
paddedIndex: () => paddedIndex,
|
|
4331
5339
|
parseBytes: () => parseBytes,
|
|
4332
5340
|
parseIndex: () => parseIndex,
|
|
@@ -4367,6 +5375,7 @@ __export(src_exports, {
|
|
|
4367
5375
|
saveShamirRecoveryEntries: () => saveShamirRecoveryEntries,
|
|
4368
5376
|
saveUserEnvelope: () => saveUserEnvelope,
|
|
4369
5377
|
saveVaultPolicy: () => saveVaultPolicy,
|
|
5378
|
+
scaleForCurrency: () => scaleForCurrency,
|
|
4370
5379
|
sha256Hex: () => sha256Hex3,
|
|
4371
5380
|
sum: () => sum,
|
|
4372
5381
|
unwrapDeksFromBlob: () => unwrapDeksFromBlob,
|
|
@@ -4380,6 +5389,7 @@ __export(src_exports, {
|
|
|
4380
5389
|
validateSchemaOutput: () => validateSchemaOutput,
|
|
4381
5390
|
validateSessionPolicy: () => validateSessionPolicy,
|
|
4382
5391
|
visibilityRecordId: () => visibilityRecordId,
|
|
5392
|
+
withArchive: () => withArchive,
|
|
4383
5393
|
withCache: () => withCache,
|
|
4384
5394
|
withCircuitBreaker: () => withCircuitBreaker,
|
|
4385
5395
|
withDerivation: () => withDerivation,
|
|
@@ -6371,6 +7381,171 @@ function withHealthCheck(opts = {}) {
|
|
|
6371
7381
|
init_errors();
|
|
6372
7382
|
init_errors();
|
|
6373
7383
|
|
|
7384
|
+
// src/archive/engine.ts
|
|
7385
|
+
function isHeld(policy, record) {
|
|
7386
|
+
if (!policy.legalHold) return false;
|
|
7387
|
+
try {
|
|
7388
|
+
return policy.legalHold(record);
|
|
7389
|
+
} catch {
|
|
7390
|
+
return true;
|
|
7391
|
+
}
|
|
7392
|
+
}
|
|
7393
|
+
async function runArchive(ctx, options = {}) {
|
|
7394
|
+
const maxArchives = options.maxArchives ?? Infinity;
|
|
7395
|
+
const dryRun = options.dryRun === true;
|
|
7396
|
+
let archived = 0;
|
|
7397
|
+
let held = 0;
|
|
7398
|
+
let scanned = 0;
|
|
7399
|
+
const byCollection = {};
|
|
7400
|
+
outer: for (const collection of ctx.collectionsWithPolicy()) {
|
|
7401
|
+
const policy = ctx.getPolicy(collection);
|
|
7402
|
+
if (!policy) continue;
|
|
7403
|
+
byCollection[collection] = { archived: 0, held: 0 };
|
|
7404
|
+
for (const id of await ctx.listRecordIds(collection)) {
|
|
7405
|
+
if (archived >= maxArchives) break outer;
|
|
7406
|
+
const record = await ctx.getRecord(collection, id).catch(() => null);
|
|
7407
|
+
if (record === null) continue;
|
|
7408
|
+
scanned += 1;
|
|
7409
|
+
let eligible = false;
|
|
7410
|
+
try {
|
|
7411
|
+
eligible = policy.archiveWhen(record);
|
|
7412
|
+
} catch {
|
|
7413
|
+
eligible = false;
|
|
7414
|
+
}
|
|
7415
|
+
if (!eligible) continue;
|
|
7416
|
+
if (isHeld(policy, record)) {
|
|
7417
|
+
held += 1;
|
|
7418
|
+
byCollection[collection].held += 1;
|
|
7419
|
+
continue;
|
|
7420
|
+
}
|
|
7421
|
+
if (!dryRun) {
|
|
7422
|
+
const env = await ctx.getEnvelope(collection, id);
|
|
7423
|
+
if (!env) continue;
|
|
7424
|
+
await ctx.archiveStore.put(ctx.vaultId, collection, id, env);
|
|
7425
|
+
await ctx.removeFromPrimary(collection, id);
|
|
7426
|
+
}
|
|
7427
|
+
archived += 1;
|
|
7428
|
+
byCollection[collection].archived += 1;
|
|
7429
|
+
}
|
|
7430
|
+
}
|
|
7431
|
+
return { archived, held, scanned, byCollection };
|
|
7432
|
+
}
|
|
7433
|
+
async function runRestore(ctx, collection, id) {
|
|
7434
|
+
const env = await ctx.archiveStore.get(ctx.vaultId, collection, id);
|
|
7435
|
+
if (!env) return false;
|
|
7436
|
+
await ctx.restoreToPrimary(collection, id, env);
|
|
7437
|
+
await ctx.archiveStore.delete(ctx.vaultId, collection, id);
|
|
7438
|
+
return true;
|
|
7439
|
+
}
|
|
7440
|
+
async function runListArchived(ctx, collection) {
|
|
7441
|
+
const collections = collection ? [collection] : ctx.collectionsWithPolicy();
|
|
7442
|
+
const out = [];
|
|
7443
|
+
for (const c of collections) {
|
|
7444
|
+
const ids = await ctx.archiveStore.list(ctx.vaultId, c);
|
|
7445
|
+
for (const id of ids) out.push({ collection: c, id });
|
|
7446
|
+
}
|
|
7447
|
+
return out;
|
|
7448
|
+
}
|
|
7449
|
+
|
|
7450
|
+
// src/archive/index.ts
|
|
7451
|
+
function withArchive(opts) {
|
|
7452
|
+
return { store: opts.store };
|
|
7453
|
+
}
|
|
7454
|
+
|
|
7455
|
+
// src/sequence/index.ts
|
|
7456
|
+
init_types();
|
|
7457
|
+
init_crypto();
|
|
7458
|
+
init_errors();
|
|
7459
|
+
var SEQUENCE_COLLECTION = "_sequences";
|
|
7460
|
+
var MAX_NEXT_ATTEMPTS = 16;
|
|
7461
|
+
async function sleepBackoff(attempt) {
|
|
7462
|
+
const ceil = Math.min(2 ** attempt, 32);
|
|
7463
|
+
const ms = Math.floor(Math.random() * ceil);
|
|
7464
|
+
await new Promise((r) => setTimeout(r, ms));
|
|
7465
|
+
}
|
|
7466
|
+
var SequenceStore = class {
|
|
7467
|
+
adapter;
|
|
7468
|
+
vault;
|
|
7469
|
+
encrypted;
|
|
7470
|
+
getDEK;
|
|
7471
|
+
actor;
|
|
7472
|
+
/**
|
|
7473
|
+
* Memoized DEK promise. The `_sequences` collection DEK is created on
|
|
7474
|
+
* first access; without sharing one promise, a burst of concurrent
|
|
7475
|
+
* `next()` calls would each trigger DEK creation and diverge (one
|
|
7476
|
+
* writer's ciphertext unreadable by another). One shared promise → one
|
|
7477
|
+
* DEK.
|
|
7478
|
+
*/
|
|
7479
|
+
dekPromise = null;
|
|
7480
|
+
constructor(opts) {
|
|
7481
|
+
this.adapter = opts.adapter;
|
|
7482
|
+
this.vault = opts.vault;
|
|
7483
|
+
this.encrypted = opts.encrypted;
|
|
7484
|
+
this.getDEK = opts.getDEK;
|
|
7485
|
+
this.actor = opts.actor;
|
|
7486
|
+
}
|
|
7487
|
+
/** A handle bound to one sequence name. */
|
|
7488
|
+
handle(name) {
|
|
7489
|
+
return {
|
|
7490
|
+
next: () => this.next(name),
|
|
7491
|
+
peek: () => this.peek(name)
|
|
7492
|
+
};
|
|
7493
|
+
}
|
|
7494
|
+
assertOnline() {
|
|
7495
|
+
if (this.adapter.capabilities?.casAtomic !== true) {
|
|
7496
|
+
throw new SequenceOfflineError();
|
|
7497
|
+
}
|
|
7498
|
+
}
|
|
7499
|
+
dek() {
|
|
7500
|
+
if (!this.dekPromise) this.dekPromise = this.getDEK(SEQUENCE_COLLECTION);
|
|
7501
|
+
return this.dekPromise;
|
|
7502
|
+
}
|
|
7503
|
+
async read(name) {
|
|
7504
|
+
const env = await this.adapter.get(this.vault, SEQUENCE_COLLECTION, name);
|
|
7505
|
+
if (!env) return { env: null, value: 0 };
|
|
7506
|
+
const json = this.encrypted ? await decrypt(env._iv, env._data, await this.dek()) : env._data;
|
|
7507
|
+
const state = JSON.parse(json);
|
|
7508
|
+
return { env, value: state.value };
|
|
7509
|
+
}
|
|
7510
|
+
async encryptState(state, version) {
|
|
7511
|
+
const json = JSON.stringify(state);
|
|
7512
|
+
if (!this.encrypted) {
|
|
7513
|
+
return { _noydb: NOYDB_FORMAT_VERSION, _v: version, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: "", _data: json, _by: this.actor };
|
|
7514
|
+
}
|
|
7515
|
+
const { iv, data } = await encrypt(json, await this.dek());
|
|
7516
|
+
return { _noydb: NOYDB_FORMAT_VERSION, _v: version, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: iv, _data: data, _by: this.actor };
|
|
7517
|
+
}
|
|
7518
|
+
async peek(name) {
|
|
7519
|
+
return (await this.read(name)).value;
|
|
7520
|
+
}
|
|
7521
|
+
async next(name) {
|
|
7522
|
+
this.assertOnline();
|
|
7523
|
+
let lastConflict;
|
|
7524
|
+
for (let attempt = 0; attempt < MAX_NEXT_ATTEMPTS; attempt++) {
|
|
7525
|
+
const { env, value } = await this.read(name);
|
|
7526
|
+
const nextValue = value + 1;
|
|
7527
|
+
const expectedVersion = env?._v ?? 0;
|
|
7528
|
+
const envelope = await this.encryptState({ value: nextValue }, expectedVersion + 1);
|
|
7529
|
+
try {
|
|
7530
|
+
await this.adapter.put(this.vault, SEQUENCE_COLLECTION, name, envelope, expectedVersion);
|
|
7531
|
+
return nextValue;
|
|
7532
|
+
} catch (err) {
|
|
7533
|
+
if (err instanceof ConflictError) {
|
|
7534
|
+
lastConflict = err;
|
|
7535
|
+
if (attempt < MAX_NEXT_ATTEMPTS - 1) await sleepBackoff(attempt);
|
|
7536
|
+
continue;
|
|
7537
|
+
}
|
|
7538
|
+
throw err;
|
|
7539
|
+
}
|
|
7540
|
+
}
|
|
7541
|
+
void lastConflict;
|
|
7542
|
+
throw new SequenceContentionError(name, MAX_NEXT_ATTEMPTS);
|
|
7543
|
+
}
|
|
7544
|
+
};
|
|
7545
|
+
|
|
7546
|
+
// src/index.ts
|
|
7547
|
+
init_errors();
|
|
7548
|
+
|
|
6374
7549
|
// src/bundle/format.ts
|
|
6375
7550
|
var NOYDB_BUNDLE_MAGIC = new Uint8Array([78, 68, 66, 49]);
|
|
6376
7551
|
var NOYDB_BUNDLE_PREFIX_BYTES = 10;
|
|
@@ -7251,15 +8426,15 @@ function isEquivalent(a, b) {
|
|
|
7251
8426
|
|
|
7252
8427
|
// src/history/history.ts
|
|
7253
8428
|
var HISTORY_COLLECTION = "_history";
|
|
7254
|
-
function matchesPrefix(id, collection,
|
|
7255
|
-
if (
|
|
7256
|
-
return id.startsWith(`${collection}:${
|
|
8429
|
+
function matchesPrefix(id, collection, recordId3) {
|
|
8430
|
+
if (recordId3) {
|
|
8431
|
+
return id.startsWith(`${collection}:${recordId3}:`);
|
|
7257
8432
|
}
|
|
7258
8433
|
return id.startsWith(`${collection}:`);
|
|
7259
8434
|
}
|
|
7260
|
-
async function getHistory(adapter, vault, collection,
|
|
8435
|
+
async function getHistory(adapter, vault, collection, recordId3, options) {
|
|
7261
8436
|
const allIds = await adapter.list(vault, HISTORY_COLLECTION);
|
|
7262
|
-
const matchingIds = allIds.filter((id) => matchesPrefix(id, collection,
|
|
8437
|
+
const matchingIds = allIds.filter((id) => matchesPrefix(id, collection, recordId3)).sort().reverse();
|
|
7263
8438
|
const entries = [];
|
|
7264
8439
|
for (const id of matchingIds) {
|
|
7265
8440
|
const envelope = await adapter.get(vault, HISTORY_COLLECTION, id);
|
|
@@ -7940,6 +9115,18 @@ async function loadKeyring(adapter, vault, userId, passphrase) {
|
|
|
7940
9115
|
...keyringFile.policy !== void 0 && { policy: keyringFile.policy }
|
|
7941
9116
|
};
|
|
7942
9117
|
}
|
|
9118
|
+
async function assertKeyringOpenAllowed(store, vault, userId, create) {
|
|
9119
|
+
const keyringUsers = await store.list(vault, "_keyring");
|
|
9120
|
+
if (keyringUsers.includes(userId)) return;
|
|
9121
|
+
if (!create) {
|
|
9122
|
+
throw new NoAccessError(`Vault "${vault}" not opened: create disabled and no keyring for "${userId}".`);
|
|
9123
|
+
}
|
|
9124
|
+
if (keyringUsers.length > 0) {
|
|
9125
|
+
throw new NoAccessError(
|
|
9126
|
+
`No keyring for user "${userId}" in vault "${vault}" (held by other principals) \u2014 refusing to self-provision.`
|
|
9127
|
+
);
|
|
9128
|
+
}
|
|
9129
|
+
}
|
|
7943
9130
|
async function createOwnerKeyring(adapter, vault, userId, passphrase, passphraseOpts) {
|
|
7944
9131
|
if (passphraseOpts?.validate && !passphraseOpts.allowWeakPassphrase) {
|
|
7945
9132
|
assertStrongPassphrase(passphrase, passphraseOpts);
|
|
@@ -10022,6 +11209,119 @@ function applyI18nLocale(record, i18nFields, locale, fallback, layer = "read") {
|
|
|
10022
11209
|
return result;
|
|
10023
11210
|
}
|
|
10024
11211
|
|
|
11212
|
+
// src/money/normalize.ts
|
|
11213
|
+
init_fixed_point();
|
|
11214
|
+
init_descriptor();
|
|
11215
|
+
function isMoneyValueObject(v) {
|
|
11216
|
+
return typeof v === "object" && v !== null && "currency" in v;
|
|
11217
|
+
}
|
|
11218
|
+
function quantizeAmount(field, input, scale, rounding) {
|
|
11219
|
+
const r = parseToScaledInt(input, scale, rounding);
|
|
11220
|
+
if (!r.ok) {
|
|
11221
|
+
if (r.reason === "precision") throw new MoneyPrecisionError(field, input, scale);
|
|
11222
|
+
throw new TypeError(`money: field "${field}" value ${JSON.stringify(input)} is not a finite decimal`);
|
|
11223
|
+
}
|
|
11224
|
+
return r.value.toString();
|
|
11225
|
+
}
|
|
11226
|
+
function quantizeMoneyFields(record, moneyFields) {
|
|
11227
|
+
const out = { ...record };
|
|
11228
|
+
for (const [field, desc] of Object.entries(moneyFields)) {
|
|
11229
|
+
const raw = out[field];
|
|
11230
|
+
if (raw === null || raw === void 0) continue;
|
|
11231
|
+
if (desc.mode === "fixed") {
|
|
11232
|
+
const currency2 = desc.fixedCurrency;
|
|
11233
|
+
out[field] = quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
|
|
11234
|
+
continue;
|
|
11235
|
+
}
|
|
11236
|
+
let amount;
|
|
11237
|
+
let currency;
|
|
11238
|
+
if (isMoneyValueObject(raw)) {
|
|
11239
|
+
currency = String(raw.currency);
|
|
11240
|
+
amount = raw.amount;
|
|
11241
|
+
} else {
|
|
11242
|
+
const sole = desc.soleCurrency();
|
|
11243
|
+
if (sole === void 0) {
|
|
11244
|
+
throw new TypeError(
|
|
11245
|
+
`money: field "${field}" is multi-currency \u2014 write { amount, currency }, not a bare amount`
|
|
11246
|
+
);
|
|
11247
|
+
}
|
|
11248
|
+
currency = sole;
|
|
11249
|
+
amount = raw;
|
|
11250
|
+
}
|
|
11251
|
+
const scale = desc.scaleFor(currency);
|
|
11252
|
+
out[field] = { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
|
|
11253
|
+
}
|
|
11254
|
+
return out;
|
|
11255
|
+
}
|
|
11256
|
+
function formatCurrency(decimal, currency, scale, locale) {
|
|
11257
|
+
const fmt = new Intl.NumberFormat(locale, {
|
|
11258
|
+
style: "currency",
|
|
11259
|
+
currency,
|
|
11260
|
+
minimumFractionDigits: scale,
|
|
11261
|
+
maximumFractionDigits: scale
|
|
11262
|
+
});
|
|
11263
|
+
return fmt.format(decimal);
|
|
11264
|
+
}
|
|
11265
|
+
function decodeMoneyFields(record, moneyFields, locale) {
|
|
11266
|
+
const out = { ...record };
|
|
11267
|
+
const format = locale !== "raw";
|
|
11268
|
+
const fmtLocale = typeof locale === "string" && locale !== "raw" ? locale : "en-US";
|
|
11269
|
+
for (const [field, desc] of Object.entries(moneyFields)) {
|
|
11270
|
+
const stored = out[field];
|
|
11271
|
+
if (stored === null || stored === void 0) continue;
|
|
11272
|
+
let currency;
|
|
11273
|
+
let scaledIntString;
|
|
11274
|
+
if (desc.mode === "fixed") {
|
|
11275
|
+
if (typeof stored !== "string" && typeof stored !== "number") continue;
|
|
11276
|
+
currency = desc.fixedCurrency;
|
|
11277
|
+
scaledIntString = String(stored);
|
|
11278
|
+
} else {
|
|
11279
|
+
if (!isMoneyValueObject(stored)) continue;
|
|
11280
|
+
const amount = stored.amount;
|
|
11281
|
+
if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") continue;
|
|
11282
|
+
currency = stored.currency;
|
|
11283
|
+
scaledIntString = String(amount);
|
|
11284
|
+
}
|
|
11285
|
+
const scale = desc.scaleFor(currency);
|
|
11286
|
+
const decimal = formatScaledInt(BigInt(scaledIntString), scale);
|
|
11287
|
+
out[field] = desc.mode === "fixed" ? decimal : { amount: decimal, currency };
|
|
11288
|
+
if (format) {
|
|
11289
|
+
out[`${field}Formatted`] = formatCurrency(decimal, currency, scale, fmtLocale);
|
|
11290
|
+
out[`${field}Number`] = Number(decimal);
|
|
11291
|
+
}
|
|
11292
|
+
}
|
|
11293
|
+
return out;
|
|
11294
|
+
}
|
|
11295
|
+
|
|
11296
|
+
// src/computed/index.ts
|
|
11297
|
+
init_errors();
|
|
11298
|
+
var ComputedFieldError = class extends NoydbError {
|
|
11299
|
+
constructor(field, id, cause) {
|
|
11300
|
+
super(
|
|
11301
|
+
"COMPUTED_FIELD",
|
|
11302
|
+
`computed field "${field}" threw for record "${id}": ` + (cause instanceof Error ? cause.message : String(cause))
|
|
11303
|
+
);
|
|
11304
|
+
this.field = field;
|
|
11305
|
+
this.id = id;
|
|
11306
|
+
this.cause = cause;
|
|
11307
|
+
this.name = "ComputedFieldError";
|
|
11308
|
+
}
|
|
11309
|
+
field;
|
|
11310
|
+
id;
|
|
11311
|
+
cause;
|
|
11312
|
+
};
|
|
11313
|
+
function evalComputedFields(record, computed, id) {
|
|
11314
|
+
const out = { ...record };
|
|
11315
|
+
for (const [field, fn] of Object.entries(computed)) {
|
|
11316
|
+
try {
|
|
11317
|
+
out[field] = fn(out);
|
|
11318
|
+
} catch (cause) {
|
|
11319
|
+
throw new ComputedFieldError(field, id, cause);
|
|
11320
|
+
}
|
|
11321
|
+
}
|
|
11322
|
+
return out;
|
|
11323
|
+
}
|
|
11324
|
+
|
|
10025
11325
|
// src/i18n/strategy.ts
|
|
10026
11326
|
function notEnabled(op) {
|
|
10027
11327
|
return new Error(
|
|
@@ -10340,6 +11640,7 @@ var NO_AGGREGATE = {
|
|
|
10340
11640
|
};
|
|
10341
11641
|
|
|
10342
11642
|
// src/query/builder.ts
|
|
11643
|
+
init_money_reducer();
|
|
10343
11644
|
var EMPTY_PLAN = {
|
|
10344
11645
|
clauses: [],
|
|
10345
11646
|
orderBy: [],
|
|
@@ -10785,6 +12086,10 @@ var Query = class _Query {
|
|
|
10785
12086
|
* partition boundaries without an API break.
|
|
10786
12087
|
*/
|
|
10787
12088
|
aggregate(spec) {
|
|
12089
|
+
const moneyFields = this.source.moneyFields;
|
|
12090
|
+
if (moneyFields) {
|
|
12091
|
+
spec = wrapMoneyReducers(spec, moneyFields);
|
|
12092
|
+
}
|
|
10788
12093
|
const source = this.source;
|
|
10789
12094
|
const clauses = this.plan.clauses;
|
|
10790
12095
|
const joinCtx = this.joinContext;
|
|
@@ -10832,13 +12137,15 @@ var Query = class _Query {
|
|
|
10832
12137
|
executeRecords,
|
|
10833
12138
|
field,
|
|
10834
12139
|
upstreams,
|
|
10835
|
-
dictLabelResolver
|
|
12140
|
+
dictLabelResolver,
|
|
12141
|
+
this.source.moneyFields
|
|
10836
12142
|
);
|
|
10837
12143
|
}
|
|
10838
12144
|
return this.aggregateStrategy.groupByN(
|
|
10839
12145
|
executeRecords,
|
|
10840
12146
|
fields,
|
|
10841
|
-
upstreams
|
|
12147
|
+
upstreams,
|
|
12148
|
+
this.source.moneyFields
|
|
10842
12149
|
);
|
|
10843
12150
|
}
|
|
10844
12151
|
/**
|
|
@@ -11379,7 +12686,8 @@ function count(opts) {
|
|
|
11379
12686
|
init: () => 0,
|
|
11380
12687
|
step: (state) => state + 1,
|
|
11381
12688
|
remove: (state) => state - 1,
|
|
11382
|
-
finalize: (state) => state
|
|
12689
|
+
finalize: (state) => state,
|
|
12690
|
+
merge: (a, b) => a + b
|
|
11383
12691
|
};
|
|
11384
12692
|
}
|
|
11385
12693
|
function sum(field, opts) {
|
|
@@ -11388,10 +12696,15 @@ function sum(field, opts) {
|
|
|
11388
12696
|
return {
|
|
11389
12697
|
op: "sum",
|
|
11390
12698
|
field,
|
|
12699
|
+
// Money-only metadata, read by `wrapMoneyReducers`. No effect on a
|
|
12700
|
+
// generic numeric sum.
|
|
12701
|
+
...opts?.convertTo !== void 0 ? { convertTo: opts.convertTo } : {},
|
|
12702
|
+
...opts?.fx !== void 0 ? { fx: opts.fx } : {},
|
|
11391
12703
|
init: () => 0,
|
|
11392
12704
|
step: (state, record) => state + readNumber(record, field),
|
|
11393
12705
|
remove: (state, record) => state - readNumber(record, field),
|
|
11394
|
-
finalize: (state) => state
|
|
12706
|
+
finalize: (state) => state,
|
|
12707
|
+
merge: (a, b) => a + b
|
|
11395
12708
|
};
|
|
11396
12709
|
}
|
|
11397
12710
|
function avg(field, opts) {
|
|
@@ -11409,7 +12722,8 @@ function avg(field, opts) {
|
|
|
11409
12722
|
sum: state.sum - readNumber(record, field),
|
|
11410
12723
|
count: state.count - 1
|
|
11411
12724
|
}),
|
|
11412
|
-
finalize: (state) => state.count === 0 ? null : state.sum / state.count
|
|
12725
|
+
finalize: (state) => state.count === 0 ? null : state.sum / state.count,
|
|
12726
|
+
merge: (a, b) => ({ sum: a.sum + b.sum, count: a.count + b.count })
|
|
11413
12727
|
};
|
|
11414
12728
|
}
|
|
11415
12729
|
function pushValue(state, value) {
|
|
@@ -11439,7 +12753,8 @@ function min(field, opts) {
|
|
|
11439
12753
|
if (v < out) out = v;
|
|
11440
12754
|
}
|
|
11441
12755
|
return out;
|
|
11442
|
-
}
|
|
12756
|
+
},
|
|
12757
|
+
merge: (a, b) => ({ values: [...a.values, ...b.values] })
|
|
11443
12758
|
};
|
|
11444
12759
|
}
|
|
11445
12760
|
function max(field, opts) {
|
|
@@ -11459,11 +12774,17 @@ function max(field, opts) {
|
|
|
11459
12774
|
if (v > out) out = v;
|
|
11460
12775
|
}
|
|
11461
12776
|
return out;
|
|
11462
|
-
}
|
|
12777
|
+
},
|
|
12778
|
+
merge: (a, b) => ({ values: [...a.values, ...b.values] })
|
|
11463
12779
|
};
|
|
11464
12780
|
}
|
|
11465
12781
|
function readNumber(record, field) {
|
|
11466
12782
|
const value = readPath(record, field);
|
|
12783
|
+
if (typeof value === "object" && value !== null && "amount" in value && "currency" in value) {
|
|
12784
|
+
throw new Error(
|
|
12785
|
+
`aggregate: field "${field}" holds a money value but was not money-aware \u2014 declare it in the collection's moneyFields so sum/min/max stay exact`
|
|
12786
|
+
);
|
|
12787
|
+
}
|
|
11467
12788
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
11468
12789
|
}
|
|
11469
12790
|
|
|
@@ -11877,8 +13198,8 @@ function coerceRefKey2(value) {
|
|
|
11877
13198
|
|
|
11878
13199
|
// src/indexing/persisted-indexes.ts
|
|
11879
13200
|
var IDX_PREFIX = "_idx/";
|
|
11880
|
-
function encodeIdxId(field,
|
|
11881
|
-
return `${IDX_PREFIX}${field}/${
|
|
13201
|
+
function encodeIdxId(field, recordId3) {
|
|
13202
|
+
return `${IDX_PREFIX}${field}/${recordId3}`;
|
|
11882
13203
|
}
|
|
11883
13204
|
function decodeIdxId(id) {
|
|
11884
13205
|
if (!id.startsWith(IDX_PREFIX)) return null;
|
|
@@ -11886,9 +13207,9 @@ function decodeIdxId(id) {
|
|
|
11886
13207
|
const firstSlash = rest.indexOf("/");
|
|
11887
13208
|
if (firstSlash <= 0) return null;
|
|
11888
13209
|
const field = rest.slice(0, firstSlash);
|
|
11889
|
-
const
|
|
11890
|
-
if (
|
|
11891
|
-
return { field, recordId:
|
|
13210
|
+
const recordId3 = rest.slice(firstSlash + 1);
|
|
13211
|
+
if (recordId3.length === 0) return null;
|
|
13212
|
+
return { field, recordId: recordId3 };
|
|
11892
13213
|
}
|
|
11893
13214
|
|
|
11894
13215
|
// src/indexing/lazy-builder.ts
|
|
@@ -12874,6 +14195,18 @@ var Collection = class {
|
|
|
12874
14195
|
* fields when a locale is requested.
|
|
12875
14196
|
*/
|
|
12876
14197
|
dictKeyFields;
|
|
14198
|
+
/**
|
|
14199
|
+
* Money field descriptors keyed by field path. Declared via the
|
|
14200
|
+
* `moneyFields` collection option: `put()` quantizes to a scaled-int
|
|
14201
|
+
* string, `get()`/`list()` decode back. Mutable so {@link _applyMoneyFields}
|
|
14202
|
+
* can attach descriptors to a collection MV-analysis pre-created.
|
|
14203
|
+
*/
|
|
14204
|
+
moneyFields;
|
|
14205
|
+
/**
|
|
14206
|
+
* Computed scalar fields, evaluated first on every `put()`. Mutable for
|
|
14207
|
+
* the same MV-pre-creation reconcile as {@link moneyFields}.
|
|
14208
|
+
*/
|
|
14209
|
+
computed;
|
|
12877
14210
|
/**
|
|
12878
14211
|
* Async callback provided by the Vault that resolves a dict key
|
|
12879
14212
|
* to its label for a given locale. Used by the locale-read path for
|
|
@@ -13015,6 +14348,8 @@ var Collection = class {
|
|
|
13015
14348
|
this.joinResolver = opts.joinResolver;
|
|
13016
14349
|
this.i18nFields = opts.i18nFields;
|
|
13017
14350
|
this.dictKeyFields = opts.dictKeyFields;
|
|
14351
|
+
this.moneyFields = opts.moneyFields;
|
|
14352
|
+
this.computed = opts.computed;
|
|
13018
14353
|
this.dictLabelResolver = opts.dictLabelResolver;
|
|
13019
14354
|
this.i18nPutValidator = opts.i18nPutValidator;
|
|
13020
14355
|
this.autoTranslateHook = opts.autoTranslateHook;
|
|
@@ -13139,6 +14474,19 @@ var Collection = class {
|
|
|
13139
14474
|
getSchema() {
|
|
13140
14475
|
return this.schema;
|
|
13141
14476
|
}
|
|
14477
|
+
/**
|
|
14478
|
+
* @internal — attach money descriptors post-construction. MV dependency
|
|
14479
|
+
* analysis auto-creates a source collection (without options) during
|
|
14480
|
+
* `openVault`, before the user's `collection(name, { moneyFields })`
|
|
14481
|
+
* declaration; this reconciles that ordering. First-wins. Not public.
|
|
14482
|
+
*/
|
|
14483
|
+
_applyMoneyFields(moneyFields) {
|
|
14484
|
+
if (this.moneyFields === void 0) this.moneyFields = moneyFields;
|
|
14485
|
+
}
|
|
14486
|
+
/** @internal — attach computed fields post-construction. See {@link _applyMoneyFields}. */
|
|
14487
|
+
_applyComputed(computed) {
|
|
14488
|
+
if (this.computed === void 0) this.computed = computed;
|
|
14489
|
+
}
|
|
13142
14490
|
/**
|
|
13143
14491
|
* Get a single record by ID.
|
|
13144
14492
|
*
|
|
@@ -13310,7 +14658,7 @@ var Collection = class {
|
|
|
13310
14658
|
existingRecord = null;
|
|
13311
14659
|
}
|
|
13312
14660
|
}
|
|
13313
|
-
|
|
14661
|
+
const gateEvent = {
|
|
13314
14662
|
op: existingEnv ? "update" : "create",
|
|
13315
14663
|
vault: this.vault,
|
|
13316
14664
|
collection: this.name,
|
|
@@ -13320,12 +14668,20 @@ var Collection = class {
|
|
|
13320
14668
|
existingVersion: existingEnv?._v ?? 0,
|
|
13321
14669
|
existingTs: existingEnv?._ts,
|
|
13322
14670
|
userId: this.keyring.userId,
|
|
13323
|
-
role: this.keyring.role
|
|
13324
|
-
|
|
14671
|
+
role: this.keyring.role,
|
|
14672
|
+
...this.computed !== void 0 ? { computedFieldNames: new Set(Object.keys(this.computed)) } : {}
|
|
14673
|
+
};
|
|
14674
|
+
await this.subsystemBus.dispatchGate("beforePut", gateEvent);
|
|
14675
|
+
}
|
|
14676
|
+
if (this.computed !== void 0) {
|
|
14677
|
+
record = evalComputedFields(record, this.computed, id);
|
|
13325
14678
|
}
|
|
13326
14679
|
if (this.schema !== void 0) {
|
|
13327
14680
|
record = await validateSchemaInput(this.schema, record, `put(${id})`);
|
|
13328
14681
|
}
|
|
14682
|
+
if (this.moneyFields) {
|
|
14683
|
+
record = quantizeMoneyFields(record, this.moneyFields);
|
|
14684
|
+
}
|
|
13329
14685
|
if (this.i18nFields) {
|
|
13330
14686
|
const obj = record;
|
|
13331
14687
|
for (const [field, descriptor] of Object.entries(this.i18nFields)) {
|
|
@@ -14147,7 +15503,8 @@ var Collection = class {
|
|
|
14147
15503
|
// fields. The Query builder consults these when present and falls
|
|
14148
15504
|
// back to a linear scan otherwise.
|
|
14149
15505
|
getIndexes: () => this.getIndexes(),
|
|
14150
|
-
lookupById: (id) => this.cache.get(id)?.record
|
|
15506
|
+
lookupById: (id) => this.cache.get(id)?.record,
|
|
15507
|
+
...this.moneyFields ? { moneyFields: this.moneyFields } : {}
|
|
14151
15508
|
};
|
|
14152
15509
|
const resolver = this.joinResolver;
|
|
14153
15510
|
const leftCollection = this.name;
|
|
@@ -14649,11 +16006,11 @@ var Collection = class {
|
|
|
14649
16006
|
}
|
|
14650
16007
|
}
|
|
14651
16008
|
persisted.clear();
|
|
14652
|
-
for (const
|
|
14653
|
-
const envelope = await this.adapter.get(this.vault, this.name,
|
|
16009
|
+
for (const recordId3 of canonicalIds) {
|
|
16010
|
+
const envelope = await this.adapter.get(this.vault, this.name, recordId3);
|
|
14654
16011
|
if (!envelope) continue;
|
|
14655
16012
|
const record = await this.decryptRecord(envelope, { skipValidation: true });
|
|
14656
|
-
await this.maintainPersistedIndexesOnPut(
|
|
16013
|
+
await this.maintainPersistedIndexesOnPut(recordId3, record, null, envelope._v);
|
|
14657
16014
|
}
|
|
14658
16015
|
this.persistedIndexesLoaded = true;
|
|
14659
16016
|
}
|
|
@@ -14829,10 +16186,14 @@ var Collection = class {
|
|
|
14829
16186
|
async applyLocaleToRecord(record, localeOpts) {
|
|
14830
16187
|
const hasI18n = this.i18nFields && Object.keys(this.i18nFields).length > 0;
|
|
14831
16188
|
const hasDict = this.dictKeyFields && Object.keys(this.dictKeyFields).length > 0;
|
|
14832
|
-
|
|
16189
|
+
const hasMoney = this.moneyFields && Object.keys(this.moneyFields).length > 0;
|
|
16190
|
+
if (!hasI18n && !hasDict && !hasMoney) return record;
|
|
14833
16191
|
const locale = localeOpts?.locale ?? this.defaultLocale;
|
|
14834
|
-
if (!locale) return record;
|
|
14835
16192
|
let result = record;
|
|
16193
|
+
if (hasMoney && this.moneyFields) {
|
|
16194
|
+
result = decodeMoneyFields(result, this.moneyFields, typeof locale === "string" ? locale : void 0);
|
|
16195
|
+
}
|
|
16196
|
+
if (!locale) return result;
|
|
14836
16197
|
if (hasI18n && this.i18nFields) {
|
|
14837
16198
|
result = this.i18nStrategy.applyI18nLocale(result, this.i18nFields, locale, localeOpts?.fallback);
|
|
14838
16199
|
}
|
|
@@ -16231,6 +17592,7 @@ async function runCompaction(ctx, options = {}) {
|
|
|
16231
17592
|
let evicted = 0;
|
|
16232
17593
|
let records = 0;
|
|
16233
17594
|
let auditEntries = 0;
|
|
17595
|
+
let held = 0;
|
|
16234
17596
|
let collectionsWithPolicy = 0;
|
|
16235
17597
|
outer: for (const collectionName of allCollections) {
|
|
16236
17598
|
if (collectionName.startsWith("_")) continue;
|
|
@@ -16241,25 +17603,29 @@ async function runCompaction(ctx, options = {}) {
|
|
|
16241
17603
|
collectionsWithPolicy += 1;
|
|
16242
17604
|
byCollection[collectionName] = { records: 0, evicted: 0 };
|
|
16243
17605
|
const ids = await ctx.listRecords(collectionName);
|
|
16244
|
-
for (const
|
|
17606
|
+
for (const recordId3 of ids) {
|
|
16245
17607
|
if (evicted >= maxEvictions) break outer;
|
|
16246
|
-
const record = await ctx.getRecord(collectionName,
|
|
17608
|
+
const record = await ctx.getRecord(collectionName, recordId3).catch(() => null);
|
|
16247
17609
|
if (record === null) continue;
|
|
16248
17610
|
records += 1;
|
|
16249
17611
|
byCollection[collectionName].records += 1;
|
|
16250
|
-
const slots = await ctx.listSlots(collectionName,
|
|
17612
|
+
const slots = await ctx.listSlots(collectionName, recordId3).catch(() => []);
|
|
16251
17613
|
for (const slot of slots) {
|
|
16252
17614
|
if (evicted >= maxEvictions) break outer;
|
|
16253
17615
|
const policy = config[slot.name];
|
|
16254
17616
|
if (!policy) continue;
|
|
16255
17617
|
const reason = evaluatePolicy(policy, record, slot, now);
|
|
16256
17618
|
if (!reason) continue;
|
|
17619
|
+
if (isHeld2(policy, record, now)) {
|
|
17620
|
+
held += 1;
|
|
17621
|
+
continue;
|
|
17622
|
+
}
|
|
16257
17623
|
if (!dryRun) {
|
|
16258
|
-
await ctx.deleteSlot(collectionName,
|
|
17624
|
+
await ctx.deleteSlot(collectionName, recordId3, slot.name);
|
|
16259
17625
|
await writeAuditEntry(ctx, {
|
|
16260
|
-
id: generateEvictionId(collectionName,
|
|
17626
|
+
id: generateEvictionId(collectionName, recordId3, slot.name),
|
|
16261
17627
|
collection: collectionName,
|
|
16262
|
-
recordId:
|
|
17628
|
+
recordId: recordId3,
|
|
16263
17629
|
slotName: slot.name,
|
|
16264
17630
|
blobHash: slot.eTag,
|
|
16265
17631
|
reason,
|
|
@@ -16278,9 +17644,32 @@ async function runCompaction(ctx, options = {}) {
|
|
|
16278
17644
|
records,
|
|
16279
17645
|
collections: collectionsWithPolicy,
|
|
16280
17646
|
auditEntries,
|
|
17647
|
+
held,
|
|
16281
17648
|
byCollection
|
|
16282
17649
|
};
|
|
16283
17650
|
}
|
|
17651
|
+
function isHeld2(policy, record, now) {
|
|
17652
|
+
if (policy.legalHold) {
|
|
17653
|
+
try {
|
|
17654
|
+
if (policy.legalHold(record)) return true;
|
|
17655
|
+
} catch {
|
|
17656
|
+
return true;
|
|
17657
|
+
}
|
|
17658
|
+
}
|
|
17659
|
+
if (policy.retainUntil) {
|
|
17660
|
+
try {
|
|
17661
|
+
const until = policy.retainUntil(record);
|
|
17662
|
+
if (until !== null && until !== void 0) {
|
|
17663
|
+
const t = until instanceof Date ? until.getTime() : typeof until === "number" ? until : Date.parse(String(until));
|
|
17664
|
+
if (!Number.isFinite(t)) return true;
|
|
17665
|
+
if (t > now.getTime()) return true;
|
|
17666
|
+
}
|
|
17667
|
+
} catch {
|
|
17668
|
+
return true;
|
|
17669
|
+
}
|
|
17670
|
+
}
|
|
17671
|
+
return false;
|
|
17672
|
+
}
|
|
16284
17673
|
function evaluatePolicy(policy, record, slot, now) {
|
|
16285
17674
|
let ttlTriggered = false;
|
|
16286
17675
|
let predicateTriggered = false;
|
|
@@ -16303,11 +17692,11 @@ function evaluatePolicy(policy, record, slot, now) {
|
|
|
16303
17692
|
if (predicateTriggered) return "predicate";
|
|
16304
17693
|
return null;
|
|
16305
17694
|
}
|
|
16306
|
-
function generateEvictionId(collection,
|
|
17695
|
+
function generateEvictionId(collection, recordId3, slotName) {
|
|
16307
17696
|
const rand = globalThis.crypto.getRandomValues(new Uint8Array(8));
|
|
16308
17697
|
let suffix = "";
|
|
16309
17698
|
for (const b of rand) suffix += b.toString(16).padStart(2, "0");
|
|
16310
|
-
return `${collection}__${
|
|
17699
|
+
return `${collection}__${recordId3}__${slotName}__${suffix}`;
|
|
16311
17700
|
}
|
|
16312
17701
|
async function writeAuditEntry(ctx, entry) {
|
|
16313
17702
|
const json = JSON.stringify(entry);
|
|
@@ -16358,7 +17747,7 @@ async function deriveMagicLinkContentKey(serverSecret, token, vault) {
|
|
|
16358
17747
|
["encrypt", "decrypt"]
|
|
16359
17748
|
);
|
|
16360
17749
|
}
|
|
16361
|
-
async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek,
|
|
17750
|
+
async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek, recordId3, opts) {
|
|
16362
17751
|
const collectionName = opts.collection ?? null;
|
|
16363
17752
|
const sourceKey = collectionName ? dekKey(collectionName, opts.tier) : `__any#${opts.tier}`;
|
|
16364
17753
|
const sourceDek = grantor.deks.get(sourceKey);
|
|
@@ -16371,7 +17760,7 @@ async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek,
|
|
|
16371
17760
|
const until = typeof opts.until === "string" ? opts.until : opts.until.toISOString();
|
|
16372
17761
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
16373
17762
|
const payload = {
|
|
16374
|
-
id:
|
|
17763
|
+
id: recordId3,
|
|
16375
17764
|
toUser: opts.toUser,
|
|
16376
17765
|
fromUser: grantor.userId,
|
|
16377
17766
|
tier: opts.tier,
|
|
@@ -16391,11 +17780,11 @@ async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek,
|
|
|
16391
17780
|
_data: data,
|
|
16392
17781
|
_by: grantor.userId
|
|
16393
17782
|
};
|
|
16394
|
-
await store.put(vault, MAGIC_LINK_GRANTS_COLLECTION,
|
|
16395
|
-
return { recordId:
|
|
17783
|
+
await store.put(vault, MAGIC_LINK_GRANTS_COLLECTION, recordId3, envelope);
|
|
17784
|
+
return { recordId: recordId3, payload };
|
|
16396
17785
|
}
|
|
16397
|
-
async function readMagicLinkGrantRecord(store, vault, contentKey,
|
|
16398
|
-
const env = await store.get(vault, MAGIC_LINK_GRANTS_COLLECTION,
|
|
17786
|
+
async function readMagicLinkGrantRecord(store, vault, contentKey, recordId3) {
|
|
17787
|
+
const env = await store.get(vault, MAGIC_LINK_GRANTS_COLLECTION, recordId3);
|
|
16399
17788
|
if (!env) return null;
|
|
16400
17789
|
try {
|
|
16401
17790
|
const json = await decrypt(env._iv, env._data, contentKey);
|
|
@@ -16997,6 +18386,10 @@ var Vault = class {
|
|
|
16997
18386
|
* call throws with a pointer at `@noy-db/hub/blobs`.
|
|
16998
18387
|
*/
|
|
16999
18388
|
blobStrategy;
|
|
18389
|
+
/** Cold-storage archival strategy (the archive target store). */
|
|
18390
|
+
archiveStrategy;
|
|
18391
|
+
/** Per-collection record archival policies. Indexed by collection name. */
|
|
18392
|
+
archiveRegistry = /* @__PURE__ */ new Map();
|
|
17000
18393
|
indexStrategy;
|
|
17001
18394
|
aggregateStrategy;
|
|
17002
18395
|
crdtStrategy;
|
|
@@ -17100,6 +18493,8 @@ var Vault = class {
|
|
|
17100
18493
|
* docstring.
|
|
17101
18494
|
*/
|
|
17102
18495
|
ledgerStore = null;
|
|
18496
|
+
/** Lazily-built atomic-sequence store. See {@link sequence}. */
|
|
18497
|
+
sequenceStore = null;
|
|
17103
18498
|
/**
|
|
17104
18499
|
* Background writes for persisted-schema envelopes (#schema-dump v0
|
|
17105
18500
|
* slice 1). One promise per `collection({ persistJsonSchema: true })`
|
|
@@ -17201,6 +18596,7 @@ var Vault = class {
|
|
|
17201
18596
|
this.onRegisterConflictResolver = opts.onRegisterConflictResolver;
|
|
17202
18597
|
this.syncAdapter = opts.syncAdapter;
|
|
17203
18598
|
this.blobStrategy = opts.blobStrategy;
|
|
18599
|
+
this.archiveStrategy = opts.archiveStrategy;
|
|
17204
18600
|
this.indexStrategy = opts.indexStrategy;
|
|
17205
18601
|
this.aggregateStrategy = opts.aggregateStrategy;
|
|
17206
18602
|
this.crdtStrategy = opts.crdtStrategy;
|
|
@@ -17263,8 +18659,9 @@ var Vault = class {
|
|
|
17263
18659
|
*. `put()` validates keys against the declared set; reads
|
|
17264
18660
|
* with `{ locale }` add `<field>Label` virtual fields.
|
|
17265
18661
|
*
|
|
17266
|
-
* Throws `ReservedCollectionNameError` for names starting with `_dict_
|
|
17267
|
-
* Use `vault.dictionary(name)`
|
|
18662
|
+
* Throws `ReservedCollectionNameError` for names starting with `_dict_` or
|
|
18663
|
+
* equal to `_sequences`. Use `vault.dictionary(name)` for dict collections
|
|
18664
|
+
* and `vault.sequence(name)` for sequence counters.
|
|
17268
18665
|
*
|
|
17269
18666
|
* Lazy mode + indexes is rejected at construction time — see the
|
|
17270
18667
|
* Collection constructor for the rationale.
|
|
@@ -17283,7 +18680,16 @@ var Vault = class {
|
|
|
17283
18680
|
if (isDictCollectionName(collectionName)) {
|
|
17284
18681
|
throw new ReservedCollectionNameError(collectionName);
|
|
17285
18682
|
}
|
|
18683
|
+
if (collectionName === SEQUENCE_COLLECTION) {
|
|
18684
|
+
throw new ReservedCollectionNameError(collectionName);
|
|
18685
|
+
}
|
|
17286
18686
|
let coll = this.collectionCache.get(collectionName);
|
|
18687
|
+
if (coll && options?.moneyFields) {
|
|
18688
|
+
coll._applyMoneyFields(options.moneyFields);
|
|
18689
|
+
}
|
|
18690
|
+
if (coll && options?.computed) {
|
|
18691
|
+
coll._applyComputed(options.computed);
|
|
18692
|
+
}
|
|
17287
18693
|
if (!coll) {
|
|
17288
18694
|
if (options?.refs) {
|
|
17289
18695
|
this.refRegistry.register(collectionName, options.refs);
|
|
@@ -17294,6 +18700,9 @@ var Vault = class {
|
|
|
17294
18700
|
if (options?.blobFields) {
|
|
17295
18701
|
this.blobFieldsRegistry.set(collectionName, options.blobFields);
|
|
17296
18702
|
}
|
|
18703
|
+
if (options?.archive) {
|
|
18704
|
+
this.archiveRegistry.set(collectionName, options.archive);
|
|
18705
|
+
}
|
|
17297
18706
|
if (options?.attestation !== void 0) {
|
|
17298
18707
|
this.attestationRegistry.set(collectionName, options.attestation);
|
|
17299
18708
|
}
|
|
@@ -17409,6 +18818,8 @@ var Vault = class {
|
|
|
17409
18818
|
collOpts.onCrossTierAccess = (event) => this.emitCrossTier(event);
|
|
17410
18819
|
if (this.syncAdapter !== void 0) collOpts.syncAdapter = this.syncAdapter;
|
|
17411
18820
|
if (options?.i18nFields !== void 0) collOpts.i18nFields = options.i18nFields;
|
|
18821
|
+
if (options?.moneyFields !== void 0) collOpts.moneyFields = options.moneyFields;
|
|
18822
|
+
if (options?.computed !== void 0) collOpts.computed = options.computed;
|
|
17412
18823
|
if (options?.dictKeyFields !== void 0) {
|
|
17413
18824
|
collOpts.dictLabelResolver = async (dictName, key, locale, fallback) => {
|
|
17414
18825
|
const handle = this.dictionary(dictName);
|
|
@@ -17828,6 +19239,33 @@ var Vault = class {
|
|
|
17828
19239
|
* await vault.compact({ maxEvictions: 1000 }) // cap batch
|
|
17829
19240
|
* ```
|
|
17830
19241
|
*/
|
|
19242
|
+
/**
|
|
19243
|
+
* Atomic, gap-free numbering. `vault.sequence('invoice-2026').next()`
|
|
19244
|
+
* returns 1, 2, 3, … with no gaps or duplicates under concurrency, via
|
|
19245
|
+
* an optimistic-CAS counter at `_sequences/<name>`. Each name is an
|
|
19246
|
+
* independent sequence.
|
|
19247
|
+
*
|
|
19248
|
+
* **Online-only:** `next()` throws `SequenceOfflineError` unless the
|
|
19249
|
+
* store advertises `capabilities.casAtomic` — gap-free numbering cannot
|
|
19250
|
+
* be serialized by an offline / non-CAS writer.
|
|
19251
|
+
*
|
|
19252
|
+
* ```ts
|
|
19253
|
+
* const n = await vault.sequence('invoice-2026').next() // 1, then 2, …
|
|
19254
|
+
* const cur = await vault.sequence('invoice-2026').peek() // current value, no allocation
|
|
19255
|
+
* ```
|
|
19256
|
+
*/
|
|
19257
|
+
sequence(name) {
|
|
19258
|
+
if (!this.sequenceStore) {
|
|
19259
|
+
this.sequenceStore = new SequenceStore({
|
|
19260
|
+
adapter: this.adapter,
|
|
19261
|
+
vault: this.name,
|
|
19262
|
+
encrypted: this.encrypted,
|
|
19263
|
+
getDEK: this.getDEK,
|
|
19264
|
+
actor: this.keyring.userId
|
|
19265
|
+
});
|
|
19266
|
+
}
|
|
19267
|
+
return this.sequenceStore.handle(name);
|
|
19268
|
+
}
|
|
17831
19269
|
async compact(options = {}) {
|
|
17832
19270
|
return runCompaction({
|
|
17833
19271
|
adapter: this.adapter,
|
|
@@ -17852,6 +19290,48 @@ var Vault = class {
|
|
|
17852
19290
|
}
|
|
17853
19291
|
}, options);
|
|
17854
19292
|
}
|
|
19293
|
+
/**
|
|
19294
|
+
* Sweep records eligible by their collection's `archive` policy into the
|
|
19295
|
+
* cold archive store. Relocation is envelope-level (no re-encryption) and
|
|
19296
|
+
* bypasses guards + materialized-view dispatch, so issued/immutable
|
|
19297
|
+
* records over a sealed period can be archived without recomputing
|
|
19298
|
+
* finalized aggregates. A `legalHold` predicate blocks archival.
|
|
19299
|
+
* Requires `archiveStrategy: withArchive({ store })` in `createNoydb`.
|
|
19300
|
+
*/
|
|
19301
|
+
async archive(options = {}) {
|
|
19302
|
+
return runArchive(this._archiveContext(), options);
|
|
19303
|
+
}
|
|
19304
|
+
/** Relocate one archived record back to the primary store. Returns false if it was not archived. */
|
|
19305
|
+
async restore(collection, id) {
|
|
19306
|
+
return runRestore(this._archiveContext(), collection, id);
|
|
19307
|
+
}
|
|
19308
|
+
/** List archived record ids for a collection (or all collections with an archive policy). */
|
|
19309
|
+
async listArchived(collection) {
|
|
19310
|
+
return runListArchived(this._archiveContext(), collection);
|
|
19311
|
+
}
|
|
19312
|
+
_archiveContext() {
|
|
19313
|
+
const strategy = this.archiveStrategy;
|
|
19314
|
+
if (!strategy) {
|
|
19315
|
+
throw new Error(
|
|
19316
|
+
"vault.archive/restore/listArchived require `archiveStrategy: withArchive({ store })` in createNoydb"
|
|
19317
|
+
);
|
|
19318
|
+
}
|
|
19319
|
+
const archiveStore = strategy.store;
|
|
19320
|
+
return {
|
|
19321
|
+
vaultId: this.name,
|
|
19322
|
+
archiveStore,
|
|
19323
|
+
collectionsWithPolicy: () => [...this.archiveRegistry.keys()],
|
|
19324
|
+
getPolicy: (c) => this.archiveRegistry.get(c) ?? null,
|
|
19325
|
+
listRecordIds: (c) => this.adapter.list(this.name, c),
|
|
19326
|
+
getRecord: async (c, id) => await this.collection(c).get(id, { locale: "raw" }),
|
|
19327
|
+
getEnvelope: (c, id) => this.adapter.get(this.name, c, id),
|
|
19328
|
+
removeFromPrimary: (c, id) => this.collection(c)._internalDelete(id),
|
|
19329
|
+
restoreToPrimary: async (c, id, env) => {
|
|
19330
|
+
await this.adapter.put(this.name, c, id, env);
|
|
19331
|
+
await this.collection(c)._invalidateCacheEntry(id);
|
|
19332
|
+
}
|
|
19333
|
+
};
|
|
19334
|
+
}
|
|
17855
19335
|
exportBlobs(options = {}) {
|
|
17856
19336
|
this.assertCanExport("plaintext", "blob");
|
|
17857
19337
|
return createExportBlobsHandle(
|
|
@@ -18550,7 +20030,7 @@ var Vault = class {
|
|
|
18550
20030
|
*
|
|
18551
20031
|
* @internal
|
|
18552
20032
|
*/
|
|
18553
|
-
async _logConsent(op, collection,
|
|
20033
|
+
async _logConsent(op, collection, recordId3) {
|
|
18554
20034
|
const ctx = this.consentContext;
|
|
18555
20035
|
if (!ctx) return;
|
|
18556
20036
|
await this.consentStrategy.write(
|
|
@@ -18563,7 +20043,7 @@ var Vault = class {
|
|
|
18563
20043
|
consentHash: ctx.consentHash,
|
|
18564
20044
|
op,
|
|
18565
20045
|
collection,
|
|
18566
|
-
recordId:
|
|
20046
|
+
recordId: recordId3
|
|
18567
20047
|
},
|
|
18568
20048
|
this.getDEK
|
|
18569
20049
|
);
|
|
@@ -18746,14 +20226,14 @@ var Vault = class {
|
|
|
18746
20226
|
* the HKDF derivation, record-id composition, and batch logic so the
|
|
18747
20227
|
* grantor doesn't touch this method directly.
|
|
18748
20228
|
*/
|
|
18749
|
-
async writeMagicLinkGrant(contentKey, grantKek,
|
|
20229
|
+
async writeMagicLinkGrant(contentKey, grantKek, recordId3, opts) {
|
|
18750
20230
|
return writeMagicLinkGrant(
|
|
18751
20231
|
this.adapter,
|
|
18752
20232
|
this.name,
|
|
18753
20233
|
this.keyring,
|
|
18754
20234
|
contentKey,
|
|
18755
20235
|
grantKek,
|
|
18756
|
-
|
|
20236
|
+
recordId3,
|
|
18757
20237
|
opts
|
|
18758
20238
|
);
|
|
18759
20239
|
}
|
|
@@ -19113,7 +20593,7 @@ var Vault = class {
|
|
|
19113
20593
|
}
|
|
19114
20594
|
}
|
|
19115
20595
|
const internalSnapshot = {};
|
|
19116
|
-
for (const internalName of [LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION, SCHEMAS_COLLECTION]) {
|
|
20596
|
+
for (const internalName of [LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION, SCHEMAS_COLLECTION, SEQUENCE_COLLECTION]) {
|
|
19117
20597
|
const ids = await this.adapter.list(this.name, internalName);
|
|
19118
20598
|
if (ids.length === 0) continue;
|
|
19119
20599
|
const records = {};
|
|
@@ -20589,6 +22069,7 @@ var Noydb = class {
|
|
|
20589
22069
|
writeRelay;
|
|
20590
22070
|
/** Per-vault policy enforcers. */
|
|
20591
22071
|
policyEnforcers = /* @__PURE__ */ new Map();
|
|
22072
|
+
vaultTemplates = /* @__PURE__ */ new Map();
|
|
20592
22073
|
txStrategy;
|
|
20593
22074
|
sessionStrategy;
|
|
20594
22075
|
syncStrategy;
|
|
@@ -20658,7 +22139,7 @@ var Noydb = class {
|
|
|
20658
22139
|
await registry.runChecks(e.collection, incoming, ctx);
|
|
20659
22140
|
const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor(), executor_exports));
|
|
20660
22141
|
for (const g of guards) {
|
|
20661
|
-
await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming);
|
|
22142
|
+
await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming, e.computedFieldNames);
|
|
20662
22143
|
}
|
|
20663
22144
|
});
|
|
20664
22145
|
this.subsystemBus.registerGate("beforeDelete", async (e) => {
|
|
@@ -20779,7 +22260,7 @@ var Noydb = class {
|
|
|
20779
22260
|
}
|
|
20780
22261
|
return comp;
|
|
20781
22262
|
}
|
|
20782
|
-
const keyring = await this.getKeyringInternal(name);
|
|
22263
|
+
const keyring = await this.getKeyringInternal(name, { create: opts?.create !== false });
|
|
20783
22264
|
if (!this.activeTier.has(name)) {
|
|
20784
22265
|
this.activeTier.set(name, 1);
|
|
20785
22266
|
}
|
|
@@ -20837,6 +22318,7 @@ var Noydb = class {
|
|
|
20837
22318
|
syncAdapter: targets.length > 0 ? targets[0].store : void 0,
|
|
20838
22319
|
historyConfig: this.options.history,
|
|
20839
22320
|
...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
|
|
22321
|
+
...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
|
|
20840
22322
|
...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
|
|
20841
22323
|
...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
|
|
20842
22324
|
...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
|
|
@@ -20890,6 +22372,7 @@ var Noydb = class {
|
|
|
20890
22372
|
emitter: this.emitter,
|
|
20891
22373
|
historyConfig: this.options.history,
|
|
20892
22374
|
...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
|
|
22375
|
+
...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
|
|
20893
22376
|
...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
|
|
20894
22377
|
...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
|
|
20895
22378
|
...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
|
|
@@ -20918,6 +22401,7 @@ var Noydb = class {
|
|
|
20918
22401
|
encrypted: true,
|
|
20919
22402
|
historyConfig: this.options.history,
|
|
20920
22403
|
...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
|
|
22404
|
+
...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
|
|
20921
22405
|
...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
|
|
20922
22406
|
...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
|
|
20923
22407
|
...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
|
|
@@ -21207,7 +22691,7 @@ var Noydb = class {
|
|
|
21207
22691
|
const vaultId = vaultIds[idx];
|
|
21208
22692
|
const task = (async () => {
|
|
21209
22693
|
try {
|
|
21210
|
-
const comp = await this.openVault(vaultId);
|
|
22694
|
+
const comp = await this.openVault(vaultId, { create: options.create !== false });
|
|
21211
22695
|
const result = await fn(comp);
|
|
21212
22696
|
results[idx] = { vault: vaultId, result };
|
|
21213
22697
|
} catch (err) {
|
|
@@ -21232,6 +22716,32 @@ var Noydb = class {
|
|
|
21232
22716
|
}
|
|
21233
22717
|
return results;
|
|
21234
22718
|
}
|
|
22719
|
+
/**
|
|
22720
|
+
* Register a shard schema blueprint. `createShard` / `openVaultGroup`
|
|
22721
|
+
* stamp shards from the named template. See the MVF design spec.
|
|
22722
|
+
*/
|
|
22723
|
+
withVaultTemplate(name, template) {
|
|
22724
|
+
this.vaultTemplates.set(name, template);
|
|
22725
|
+
}
|
|
22726
|
+
/**
|
|
22727
|
+
* Open a VaultGroup — transparent routing over per-partition shard
|
|
22728
|
+
* vaults, with shard discovery backed by the supplied `vault-registry`
|
|
22729
|
+
* collection.
|
|
22730
|
+
*/
|
|
22731
|
+
async openVaultGroup(name, opts) {
|
|
22732
|
+
if (this.closed) throw new ValidationError("Instance is closed");
|
|
22733
|
+
const template = this.vaultTemplates.get(opts.sharding.vaultTemplate);
|
|
22734
|
+
if (!template) throw new VaultTemplateNotFoundError(opts.sharding.vaultTemplate);
|
|
22735
|
+
const { VaultGroup: VaultGroup2 } = await Promise.resolve().then(() => (init_vault_group(), vault_group_exports));
|
|
22736
|
+
return new VaultGroup2(this, name, opts.registry, opts.sharding, template);
|
|
22737
|
+
}
|
|
22738
|
+
/**
|
|
22739
|
+
* @internal — true when an encrypted shard vault is provisioned
|
|
22740
|
+
* (its keyring exists in the store).
|
|
22741
|
+
*/
|
|
22742
|
+
async _shardVaultProvisioned(vaultId) {
|
|
22743
|
+
return (await this.options.store.list(vaultId, "_keyring")).length > 0;
|
|
22744
|
+
}
|
|
21235
22745
|
/**
|
|
21236
22746
|
* Change the current user's passphrase for a vault.
|
|
21237
22747
|
*
|
|
@@ -22549,7 +24059,7 @@ var Noydb = class {
|
|
|
22549
24059
|
* accesses see them. Not exposed publicly — callers outside hub
|
|
22550
24060
|
* should use {@link getKeyring}, which returns a defensive copy.
|
|
22551
24061
|
*/
|
|
22552
|
-
async getKeyringInternal(vault) {
|
|
24062
|
+
async getKeyringInternal(vault, opts = { create: true }) {
|
|
22553
24063
|
if (this.options.encrypt === false) {
|
|
22554
24064
|
return createPlaintextKeyring(this.options.user);
|
|
22555
24065
|
}
|
|
@@ -22560,6 +24070,7 @@ var Noydb = class {
|
|
|
22560
24070
|
this.keyringCache.set(vault, keyring2);
|
|
22561
24071
|
return keyring2;
|
|
22562
24072
|
}
|
|
24073
|
+
await assertKeyringOpenAllowed(this.options.store, vault, this.options.user, opts.create);
|
|
22563
24074
|
let effectiveSecret;
|
|
22564
24075
|
if (this.options.passphraseMode === "managed") {
|
|
22565
24076
|
effectiveSecret = await resolveManagedSecret(
|
|
@@ -23493,6 +25004,50 @@ function withGuard(strategy) {
|
|
|
23493
25004
|
};
|
|
23494
25005
|
}
|
|
23495
25006
|
|
|
25007
|
+
// src/guards/immutable-guard.ts
|
|
25008
|
+
init_errors();
|
|
25009
|
+
function recordId2(record) {
|
|
25010
|
+
const id = record?.id;
|
|
25011
|
+
return typeof id === "string" ? id : "";
|
|
25012
|
+
}
|
|
25013
|
+
function immutableGuard(config) {
|
|
25014
|
+
const { collection, after, appendOnly, amendmentRoles } = config;
|
|
25015
|
+
if (appendOnly && after !== void 0) {
|
|
25016
|
+
throw new ValidationError("immutableGuard: `after` and `appendOnly` are mutually exclusive");
|
|
25017
|
+
}
|
|
25018
|
+
if (!appendOnly && after === void 0) {
|
|
25019
|
+
throw new ValidationError("immutableGuard: provide `after` or `appendOnly: true`");
|
|
25020
|
+
}
|
|
25021
|
+
const isImmutable = appendOnly ? () => true : after;
|
|
25022
|
+
const reason = appendOnly ? "append-only collection" : "record is immutable after issue";
|
|
25023
|
+
const spec = {
|
|
25024
|
+
collection,
|
|
25025
|
+
// Block updates to an already-immutable record. Inserts (existing
|
|
25026
|
+
// null) and the transition write that first makes the record
|
|
25027
|
+
// immutable are allowed — `after` reads the prior state.
|
|
25028
|
+
check: (incoming, ctx) => {
|
|
25029
|
+
if (ctx.existing !== null && isImmutable(ctx.existing)) {
|
|
25030
|
+
throw new RecordLockedError(collection, recordId2(incoming), reason);
|
|
25031
|
+
}
|
|
25032
|
+
},
|
|
25033
|
+
// Block deletes of an immutable record.
|
|
25034
|
+
onDelete: (existing) => {
|
|
25035
|
+
if (isImmutable(existing)) {
|
|
25036
|
+
throw new RecordLockedError(collection, recordId2(existing), reason);
|
|
25037
|
+
}
|
|
25038
|
+
},
|
|
25039
|
+
// The authorized override: inside an amendment transaction the
|
|
25040
|
+
// check/onDelete are skipped and the change is ledgered. No extra
|
|
25041
|
+
// invariant — the amendment itself is the sanctioned exception.
|
|
25042
|
+
amendment: {
|
|
25043
|
+
roles: amendmentRoles ?? ["admin", "owner"],
|
|
25044
|
+
invariant: () => {
|
|
25045
|
+
}
|
|
25046
|
+
}
|
|
25047
|
+
};
|
|
25048
|
+
return withGuard(spec);
|
|
25049
|
+
}
|
|
25050
|
+
|
|
23496
25051
|
// src/derivations/with-derivation.ts
|
|
23497
25052
|
init_errors();
|
|
23498
25053
|
function withDerivation(spec) {
|
|
@@ -23650,6 +25205,10 @@ function withOverlayedView(spec) {
|
|
|
23650
25205
|
init_errors();
|
|
23651
25206
|
init_errors();
|
|
23652
25207
|
|
|
25208
|
+
// src/money/index.ts
|
|
25209
|
+
init_descriptor();
|
|
25210
|
+
init_iso4217();
|
|
25211
|
+
|
|
23653
25212
|
// src/i18n/script.ts
|
|
23654
25213
|
init_errors();
|
|
23655
25214
|
var LATIN_BASE = /* @__PURE__ */ new Set([
|
|
@@ -24435,6 +25994,7 @@ function shortJSON(value) {
|
|
|
24435
25994
|
CollectionFrame,
|
|
24436
25995
|
CollectionIndexes,
|
|
24437
25996
|
CollectionInstant,
|
|
25997
|
+
ComputedFieldError,
|
|
24438
25998
|
ConflictError,
|
|
24439
25999
|
CrossJoinSourceUnknownError,
|
|
24440
26000
|
CrossJoinTooLargeError,
|
|
@@ -24498,6 +26058,9 @@ function shortJSON(value) {
|
|
|
24498
26058
|
MemorySealingKeyProvider,
|
|
24499
26059
|
MigrationRequiredError,
|
|
24500
26060
|
MissingTranslationError,
|
|
26061
|
+
MoneyCurrencyError,
|
|
26062
|
+
MoneyPrecisionError,
|
|
26063
|
+
MoneyUnsupportedError,
|
|
24501
26064
|
NOYDB_BACKUP_VERSION,
|
|
24502
26065
|
NOYDB_BUNDLE_FORMAT_VERSION,
|
|
24503
26066
|
NOYDB_BUNDLE_MAGIC,
|
|
@@ -24550,9 +26113,13 @@ function shortJSON(value) {
|
|
|
24550
26113
|
SchemaUpdateError,
|
|
24551
26114
|
SchemaValidationError,
|
|
24552
26115
|
ScriptViolationError,
|
|
26116
|
+
SequenceContentionError,
|
|
26117
|
+
SequenceOfflineError,
|
|
26118
|
+
SequenceStore,
|
|
24553
26119
|
SessionExpiredError,
|
|
24554
26120
|
SessionNotFoundError,
|
|
24555
26121
|
SessionPolicyError,
|
|
26122
|
+
ShardProvisioningError,
|
|
24556
26123
|
SnapshotNotFoundError,
|
|
24557
26124
|
StoreCapabilityError,
|
|
24558
26125
|
SyncEngine,
|
|
@@ -24568,6 +26135,7 @@ function shortJSON(value) {
|
|
|
24568
26135
|
USER_ENVELOPE_COLLECTION,
|
|
24569
26136
|
USER_ENVELOPE_MAX_BYTES,
|
|
24570
26137
|
UniqueConstraintError,
|
|
26138
|
+
UnknownShardError,
|
|
24571
26139
|
UnsupportedIndexOptionError,
|
|
24572
26140
|
UserApi,
|
|
24573
26141
|
UserEnvelopeOversizedError,
|
|
@@ -24576,6 +26144,7 @@ function shortJSON(value) {
|
|
|
24576
26144
|
Vault,
|
|
24577
26145
|
VaultFrame,
|
|
24578
26146
|
VaultInstant,
|
|
26147
|
+
VaultTemplateNotFoundError,
|
|
24579
26148
|
WeakPassphraseError,
|
|
24580
26149
|
activeSessionCount,
|
|
24581
26150
|
additiveOnly,
|
|
@@ -24632,6 +26201,7 @@ function shortJSON(value) {
|
|
|
24632
26201
|
envelopePayloadHash,
|
|
24633
26202
|
estimateEntropy,
|
|
24634
26203
|
estimateRecordBytes,
|
|
26204
|
+
evalComputedFields,
|
|
24635
26205
|
evaluateClause,
|
|
24636
26206
|
evaluateExportCapability,
|
|
24637
26207
|
evaluateFieldClause,
|
|
@@ -24648,6 +26218,7 @@ function shortJSON(value) {
|
|
|
24648
26218
|
hasRecoveryEnrolled,
|
|
24649
26219
|
hashEntry,
|
|
24650
26220
|
i18nText,
|
|
26221
|
+
immutableGuard,
|
|
24651
26222
|
inferScripts,
|
|
24652
26223
|
isDevUnlockActive,
|
|
24653
26224
|
isDictCollectionName,
|
|
@@ -24655,6 +26226,7 @@ function shortJSON(value) {
|
|
|
24655
26226
|
isDiscriminant,
|
|
24656
26227
|
isI18nTextDescriptor,
|
|
24657
26228
|
isMagicLinkGrantExpired,
|
|
26229
|
+
isMoneyDescriptor,
|
|
24658
26230
|
isPreCompressed,
|
|
24659
26231
|
isPublicEnvelope,
|
|
24660
26232
|
isSessionAlive,
|
|
@@ -24686,6 +26258,7 @@ function shortJSON(value) {
|
|
|
24686
26258
|
mintPaperRecoveryEntry,
|
|
24687
26259
|
mintShamirRecoveryEntry,
|
|
24688
26260
|
mintWrappedDeksBlob,
|
|
26261
|
+
money,
|
|
24689
26262
|
paddedIndex,
|
|
24690
26263
|
parseBytes,
|
|
24691
26264
|
parseIndex,
|
|
@@ -24726,6 +26299,7 @@ function shortJSON(value) {
|
|
|
24726
26299
|
saveShamirRecoveryEntries,
|
|
24727
26300
|
saveUserEnvelope,
|
|
24728
26301
|
saveVaultPolicy,
|
|
26302
|
+
scaleForCurrency,
|
|
24729
26303
|
sha256Hex,
|
|
24730
26304
|
sum,
|
|
24731
26305
|
unwrapDeksFromBlob,
|
|
@@ -24739,6 +26313,7 @@ function shortJSON(value) {
|
|
|
24739
26313
|
validateSchemaOutput,
|
|
24740
26314
|
validateSessionPolicy,
|
|
24741
26315
|
visibilityRecordId,
|
|
26316
|
+
withArchive,
|
|
24742
26317
|
withCache,
|
|
24743
26318
|
withCircuitBreaker,
|
|
24744
26319
|
withDerivation,
|