@noy-db/hub 0.2.0-pre.11 → 0.2.0-pre.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +2024 -69
- 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-LSTBFLL2.js → chunk-3OUCWHV6.js} +2 -2
- package/dist/{chunk-7CEGU63S.js → chunk-3XZRRBFW.js} +2 -2
- package/dist/{chunk-RC6SU5NO.js → chunk-4VCQH32J.js} +2 -2
- package/dist/{chunk-ZROPXHJY.js → chunk-4YDZ7JPZ.js} +2 -2
- package/dist/{chunk-IMYKDWB4.js → chunk-5NISHSBO.js} +2 -2
- package/dist/{chunk-ZBBW7YQN.js → chunk-7TEI2K2A.js} +5 -5
- package/dist/{chunk-6T2UDBKG.js → chunk-AYNF7PVX.js} +2 -2
- package/dist/{chunk-QCXNMCHN.js → chunk-C3WRKABE.js} +4 -4
- package/dist/{chunk-GAUEWM7D.js → chunk-CWFQTAD4.js} +4 -4
- package/dist/{chunk-QSOYKKMD.js → chunk-D5Y3HIC6.js} +2 -2
- package/dist/{chunk-R233SLY3.js → chunk-E3DIBDKA.js} +2 -2
- package/dist/chunk-FNVFT4HZ.js +64 -0
- package/dist/chunk-FNVFT4HZ.js.map +1 -0
- package/dist/{chunk-SLV4LAKX.js → chunk-GC4V7RU7.js} +1 -1
- package/dist/chunk-GC4V7RU7.js.map +1 -0
- package/dist/{chunk-5OEJ6GOT.js → chunk-GL3Z7LH7.js} +2 -2
- package/dist/{chunk-YW5DBAPG.js → chunk-GZJ5JBED.js} +4 -4
- package/dist/{chunk-LJXYPGRH.js → chunk-HHZ77DHM.js} +3 -3
- package/dist/{chunk-6MFH4BMK.js → chunk-HQXOEWLZ.js} +4 -4
- package/dist/{chunk-RYIL3PI2.js → chunk-ILWQGTNH.js} +2 -2
- package/dist/{chunk-3DGHRDCX.js → chunk-J67BP5EP.js} +3 -3
- package/dist/{chunk-PVUUIWHY.js → chunk-JPOQMXGT.js} +10 -3
- package/dist/chunk-JPOQMXGT.js.map +1 -0
- package/dist/{chunk-RRNA5GKT.js → chunk-JWFNOD2T.js} +2 -2
- package/dist/{chunk-BDV7INMP.js → chunk-KHQ3N5AB.js} +4 -4
- package/dist/{chunk-JQ4NEJJ6.js → chunk-KJF7EPUE.js} +3 -3
- package/dist/{chunk-FO3UEG4S.js → chunk-KKB42D3Q.js} +2 -2
- package/dist/{chunk-26NK23DZ.js → chunk-M6KXHRIA.js} +3 -3
- package/dist/{chunk-DAP2XL7Q.js → chunk-NIUXQDWD.js} +2 -2
- package/dist/{chunk-6YLPHBKR.js → chunk-NJMKHRQI.js} +145 -11
- package/dist/chunk-NJMKHRQI.js.map +1 -0
- package/dist/{chunk-O6EJ6WTI.js → chunk-NKGY3C53.js} +87 -2
- package/dist/chunk-NKGY3C53.js.map +1 -0
- package/dist/{chunk-CXJG63MA.js → chunk-NP6EZT44.js} +20 -6
- package/dist/chunk-NP6EZT44.js.map +1 -0
- package/dist/{chunk-Y26YV5R3.js → chunk-O2JW656W.js} +3 -3
- package/dist/{chunk-MPOLUAMI.js → chunk-P5MW7BG2.js} +665 -60
- package/dist/chunk-P5MW7BG2.js.map +1 -0
- package/dist/{chunk-TGALXXLV.js → chunk-PW26DAXS.js} +3 -3
- package/dist/{chunk-V2PZC6AW.js → chunk-QAWCVWCX.js} +5 -5
- package/dist/{chunk-CH22FZHT.js → chunk-QFYVGJLI.js} +2 -2
- package/dist/{chunk-YM7LFCG7.js → chunk-QIVFGU2M.js} +3 -3
- package/dist/{chunk-CXFOITNS.js → chunk-SJ24GHID.js} +2 -2
- package/dist/{chunk-EBVBE7UK.js → chunk-SOU42FGB.js} +5 -5
- package/dist/{chunk-YVZRTCGG.js → chunk-SYSKC237.js} +6 -6
- package/dist/{chunk-77DWLQRY.js → chunk-TDECYU4Y.js} +31 -3
- package/dist/chunk-TDECYU4Y.js.map +1 -0
- package/dist/{chunk-73YLDCNF.js → chunk-TEQGXA7L.js} +5 -5
- package/dist/{chunk-PC6ZEDRL.js → chunk-UNQEWORI.js} +2 -2
- package/dist/{chunk-PC3ZZBTO.js → chunk-VAK6NQAK.js} +5 -5
- package/dist/{chunk-GKI4SDP7.js → chunk-WQKZIQIL.js} +4 -4
- package/dist/chunk-YWBHS25M.js +783 -0
- package/dist/chunk-YWBHS25M.js.map +1 -0
- package/dist/chunk-ZC7J6ZYV.js +7 -0
- package/dist/chunk-ZC7J6ZYV.js.map +1 -0
- 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-YXH6SAOW.js} +3 -3
- package/dist/{delegation-ZTRT2PRV.js → delegation-K5ERUH6A.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-BH6y3Hx0.d.ts → dev-unlock-BW0GNBEV.d.ts} +1 -1
- package/dist/{dev-unlock-H1Xwxc3U.d.cts → dev-unlock-a7SOtaV0.d.cts} +1 -1
- package/dist/executor-AVJ7UEWA.js +8 -0
- package/dist/executor-IQO3KGXQ.js +11 -0
- package/dist/executor-VT7TKGE4.js +8 -0
- package/dist/{fanout-sidecar-F3ZRFU4H.js → fanout-sidecar-N6OJX6QR.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-_sDFvtmX.d.cts → hash-B0cLQcq_.d.cts} +1 -1
- package/dist/{hash-D89JdDbj.d.ts → hash-uMNIAAW8.d.ts} +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-B0h-ipLz.d.ts +67 -0
- package/dist/immutable-guard-BZIcYhYX.d.cts +67 -0
- package/dist/index-CUVOMtgg.d.cts +1216 -0
- package/dist/index-Cqzp4tt9.d.ts +1216 -0
- package/dist/index.cjs +2200 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -13
- package/dist/index.d.ts +55 -13
- package/dist/index.js +95 -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-ZH27C23Y.js +12 -0
- package/dist/{ledger-NYCGJX2D.js → ledger-64TTQMRS.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-O76SKBST.js +35 -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-QOXZEHKH.js → public-envelope-MHG6YVXW.js} +4 -4
- package/dist/query/index.cjs +382 -17
- 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-2PKBQDCH.js +8 -0
- package/dist/registry-4VXFKCBJ.js +8 -0
- package/dist/{registry-ST2VNFZC.js → registry-PV4G3OPA.js} +3 -3
- package/dist/{revoke-RT7QYB4G.js → revoke-5BOLVJ3N.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-QNU66JF5.js → signer-GRIYBA22.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-LZYMMDDS.js} +2 -2
- package/dist/state-vault-QFJWU23A.js +147 -0
- package/dist/state-vault-QFJWU23A.js.map +1 -0
- 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-CrS7PnbE.d.cts +2048 -0
- package/dist/strategy-CrS7PnbE.d.ts +2048 -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-DiSXn3a4.d.cts → types-CDwSSXiI.d.cts} +709 -6
- package/dist/{types-CD8mc8zR.d.ts → types-pax34sec.d.ts} +709 -6
- package/dist/{ulid-DQ1hcJvZ.d.cts → ulid-7bCSgIgb.d.cts} +1 -1
- package/dist/{ulid-8p83wbR4.d.ts → ulid-C_t4hL3d.d.ts} +1 -1
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/vault-group-UO4YUZOG.js +493 -0
- package/dist/vault-group-UO4YUZOG.js.map +1 -0
- package/dist/{with-derivation-DWMTpgEH.d.ts → with-derivation-BjdOxUBn.d.ts} +1 -1
- package/dist/{with-derivation-CVT7-dUt.d.cts → with-derivation-D8wFlb6V.d.cts} +1 -1
- package/dist/{with-materialized-view-BTTU3BNK.d.cts → with-materialized-view-5QMF1rS_.d.cts} +1 -1
- package/dist/{with-materialized-view-X0CoL8-L.d.ts → with-materialized-view-DJb-HO65.d.ts} +1 -1
- package/dist/{with-overlayed-view-DQjO_DSG.d.ts → with-overlayed-view-CkqTefbz.d.ts} +1 -1
- package/dist/{with-overlayed-view-DcacRRsS.d.cts → with-overlayed-view-DDNflPvC.d.cts} +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-6YLPHBKR.js.map +0 -1
- package/dist/chunk-77DWLQRY.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-MPOLUAMI.js.map +0 -1
- package/dist/chunk-O6EJ6WTI.js.map +0 -1
- package/dist/chunk-PVUUIWHY.js.map +0 -1
- package/dist/chunk-SLV4LAKX.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-IVTVSKWW.js +0 -12
- package/dist/noydb-SH4RLE47.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-BRvt53da.d.ts +0 -18
- package/dist/with-guard-Dx2zZnTA.d.cts +0 -18
- /package/dist/{chunk-LSTBFLL2.js.map → chunk-3OUCWHV6.js.map} +0 -0
- /package/dist/{chunk-7CEGU63S.js.map → chunk-3XZRRBFW.js.map} +0 -0
- /package/dist/{chunk-RC6SU5NO.js.map → chunk-4VCQH32J.js.map} +0 -0
- /package/dist/{chunk-ZROPXHJY.js.map → chunk-4YDZ7JPZ.js.map} +0 -0
- /package/dist/{chunk-IMYKDWB4.js.map → chunk-5NISHSBO.js.map} +0 -0
- /package/dist/{chunk-ZBBW7YQN.js.map → chunk-7TEI2K2A.js.map} +0 -0
- /package/dist/{chunk-6T2UDBKG.js.map → chunk-AYNF7PVX.js.map} +0 -0
- /package/dist/{chunk-QCXNMCHN.js.map → chunk-C3WRKABE.js.map} +0 -0
- /package/dist/{chunk-GAUEWM7D.js.map → chunk-CWFQTAD4.js.map} +0 -0
- /package/dist/{chunk-QSOYKKMD.js.map → chunk-D5Y3HIC6.js.map} +0 -0
- /package/dist/{chunk-R233SLY3.js.map → chunk-E3DIBDKA.js.map} +0 -0
- /package/dist/{chunk-5OEJ6GOT.js.map → chunk-GL3Z7LH7.js.map} +0 -0
- /package/dist/{chunk-YW5DBAPG.js.map → chunk-GZJ5JBED.js.map} +0 -0
- /package/dist/{chunk-LJXYPGRH.js.map → chunk-HHZ77DHM.js.map} +0 -0
- /package/dist/{chunk-6MFH4BMK.js.map → chunk-HQXOEWLZ.js.map} +0 -0
- /package/dist/{chunk-RYIL3PI2.js.map → chunk-ILWQGTNH.js.map} +0 -0
- /package/dist/{chunk-3DGHRDCX.js.map → chunk-J67BP5EP.js.map} +0 -0
- /package/dist/{chunk-RRNA5GKT.js.map → chunk-JWFNOD2T.js.map} +0 -0
- /package/dist/{chunk-BDV7INMP.js.map → chunk-KHQ3N5AB.js.map} +0 -0
- /package/dist/{chunk-JQ4NEJJ6.js.map → chunk-KJF7EPUE.js.map} +0 -0
- /package/dist/{chunk-FO3UEG4S.js.map → chunk-KKB42D3Q.js.map} +0 -0
- /package/dist/{chunk-26NK23DZ.js.map → chunk-M6KXHRIA.js.map} +0 -0
- /package/dist/{chunk-DAP2XL7Q.js.map → chunk-NIUXQDWD.js.map} +0 -0
- /package/dist/{chunk-Y26YV5R3.js.map → chunk-O2JW656W.js.map} +0 -0
- /package/dist/{chunk-TGALXXLV.js.map → chunk-PW26DAXS.js.map} +0 -0
- /package/dist/{chunk-V2PZC6AW.js.map → chunk-QAWCVWCX.js.map} +0 -0
- /package/dist/{chunk-CH22FZHT.js.map → chunk-QFYVGJLI.js.map} +0 -0
- /package/dist/{chunk-YM7LFCG7.js.map → chunk-QIVFGU2M.js.map} +0 -0
- /package/dist/{chunk-CXFOITNS.js.map → chunk-SJ24GHID.js.map} +0 -0
- /package/dist/{chunk-EBVBE7UK.js.map → chunk-SOU42FGB.js.map} +0 -0
- /package/dist/{chunk-YVZRTCGG.js.map → chunk-SYSKC237.js.map} +0 -0
- /package/dist/{chunk-73YLDCNF.js.map → chunk-TEQGXA7L.js.map} +0 -0
- /package/dist/{chunk-PC6ZEDRL.js.map → chunk-UNQEWORI.js.map} +0 -0
- /package/dist/{chunk-PC3ZZBTO.js.map → chunk-VAK6NQAK.js.map} +0 -0
- /package/dist/{chunk-GKI4SDP7.js.map → chunk-WQKZIQIL.js.map} +0 -0
- /package/dist/{crypto-2CRLG4F4.js.map → crypto-YXH6SAOW.js.map} +0 -0
- /package/dist/{delegation-ZTRT2PRV.js.map → delegation-K5ERUH6A.js.map} +0 -0
- /package/dist/{executor-S76VN45G.js.map → executor-AVJ7UEWA.js.map} +0 -0
- /package/dist/{executor-UCXLIGLW.js.map → executor-IQO3KGXQ.js.map} +0 -0
- /package/dist/{executor-ZCNZJMGR.js.map → executor-VT7TKGE4.js.map} +0 -0
- /package/dist/{fanout-sidecar-F3ZRFU4H.js.map → fanout-sidecar-N6OJX6QR.js.map} +0 -0
- /package/dist/{issue-IVTVSKWW.js.map → issue-ZH27C23Y.js.map} +0 -0
- /package/dist/{ledger-NYCGJX2D.js.map → ledger-64TTQMRS.js.map} +0 -0
- /package/dist/{noydb-SH4RLE47.js.map → noydb-O76SKBST.js.map} +0 -0
- /package/dist/{public-envelope-QOXZEHKH.js.map → public-envelope-MHG6YVXW.js.map} +0 -0
- /package/dist/{registry-ST2VNFZC.js.map → registry-2PKBQDCH.js.map} +0 -0
- /package/dist/{registry-UFIK7CSR.js.map → registry-4VXFKCBJ.js.map} +0 -0
- /package/dist/{registry-ZGYYSM5I.js.map → registry-PV4G3OPA.js.map} +0 -0
- /package/dist/{revoke-RT7QYB4G.js.map → revoke-5BOLVJ3N.js.map} +0 -0
- /package/dist/{signer-QNU66JF5.js.map → signer-GRIYBA22.js.map} +0 -0
- /package/dist/{stale-VKXSXJF4.js.map → stale-LZYMMDDS.js.map} +0 -0
package/dist/bundle/index.cjs
CHANGED
|
@@ -31,7 +31,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
33
|
// src/errors.ts
|
|
34
|
-
var NoydbError, DecryptionError, TamperedError, InvalidKeyError, KeyringCorruptError, NoAccessError, ReadOnlyError, PermissionDeniedError, ExportCapabilityError, KeyringExpiredError, ImportCapabilityError, StoreCapabilityError, PrivilegeEscalationError, FieldFrozenError, InvariantError, AmendmentForbiddenError, TierNotGrantedError, ElevationExpiredError, AlreadyElevatedError, TierDemoteDeniedError, DelegationTargetMissingError, ConflictError, LedgerContentionError, BundleVersionConflictError, ValidationError, SchemaValidationError, SchemaUpdateError, SchemaFenceError, MigrationRequiredError, QuiesceTimeoutError, GroupCardinalityError, IndexRequiredError, UniqueConstraintError, UnsupportedIndexOptionError, IndexWriteFailureError, BundleIntegrityError, BundleSealMismatchError, ReservedCollectionNameError, LocaleNotSpecifiedError, TranslatorNotConfiguredError, BackupLedgerError, BackupCorruptedError, PartitionExtractionError, TransferSealError, AdoptionStateError, AttestationError, JoinTooLargeError, CrossJoinTooLargeError, CrossJoinSourceUnknownError, DanglingReferenceError, DerivationCycleError, DerivationOutputShapeError, DerivationCapExceededError, MaterializedViewCycleError, MaterializedViewSourceUnknownError, MaterializedViewTooLargeError, OverlayBaseIsVirtualError, OverlayCollectionUnavailableError, OverlayNameCollisionError, OverlayIdMismatchError;
|
|
34
|
+
var NoydbError, DecryptionError, TamperedError, InvalidKeyError, KeyringCorruptError, NoAccessError, ReadOnlyError, PermissionDeniedError, ExportCapabilityError, KeyringExpiredError, ImportCapabilityError, StoreCapabilityError, PrivilegeEscalationError, ReservedVaultNameError, FieldFrozenError, InvariantError, AmendmentForbiddenError, TierNotGrantedError, ElevationExpiredError, AlreadyElevatedError, TierDemoteDeniedError, DelegationTargetMissingError, ConflictError, LedgerContentionError, SequenceContentionError, SequenceOfflineError, NumberingUncertaintyError, BundleVersionConflictError, ValidationError, SchemaValidationError, SchemaUpdateError, SchemaFenceError, MigrationRequiredError, QuiesceTimeoutError, GroupCardinalityError, IndexRequiredError, UniqueConstraintError, UnsupportedIndexOptionError, IndexWriteFailureError, BundleIntegrityError, BundleSealMismatchError, ReservedCollectionNameError, LocaleNotSpecifiedError, TranslatorNotConfiguredError, BackupLedgerError, BackupCorruptedError, PartitionExtractionError, TransferSealError, AdoptionStateError, AttestationError, JoinTooLargeError, CrossJoinTooLargeError, CrossJoinSourceUnknownError, DanglingReferenceError, DerivationCycleError, DerivationOutputShapeError, DerivationCapExceededError, MaterializedViewCycleError, MaterializedViewSourceUnknownError, MaterializedViewTooLargeError, OverlayBaseIsVirtualError, OverlayCollectionUnavailableError, OverlayNameCollisionError, OverlayIdMismatchError, UnknownShardError, ShardProvisioningError, VaultTemplateNotFoundError;
|
|
35
35
|
var init_errors = __esm({
|
|
36
36
|
"src/errors.ts"() {
|
|
37
37
|
"use strict";
|
|
@@ -155,6 +155,18 @@ var init_errors = __esm({
|
|
|
155
155
|
this.offendingCollection = offendingCollection;
|
|
156
156
|
}
|
|
157
157
|
};
|
|
158
|
+
ReservedVaultNameError = class extends NoydbError {
|
|
159
|
+
/** The rejected vault name. */
|
|
160
|
+
vaultName;
|
|
161
|
+
constructor(vaultName) {
|
|
162
|
+
super(
|
|
163
|
+
"RESERVED_VAULT_NAME",
|
|
164
|
+
`"${vaultName}" is a reserved internal vault name and cannot be used as a group name or partition key`
|
|
165
|
+
);
|
|
166
|
+
this.name = "ReservedVaultNameError";
|
|
167
|
+
this.vaultName = vaultName;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
158
170
|
FieldFrozenError = class extends NoydbError {
|
|
159
171
|
collection;
|
|
160
172
|
id;
|
|
@@ -266,6 +278,39 @@ var init_errors = __esm({
|
|
|
266
278
|
this.attempts = attempts;
|
|
267
279
|
}
|
|
268
280
|
};
|
|
281
|
+
SequenceContentionError = class extends NoydbError {
|
|
282
|
+
sequence;
|
|
283
|
+
attempts;
|
|
284
|
+
constructor(sequence, attempts) {
|
|
285
|
+
super(
|
|
286
|
+
"SEQUENCE_CONTENTION",
|
|
287
|
+
`vault.sequence("${sequence}").next(): failed to allocate after ${attempts} optimistic-CAS retries`
|
|
288
|
+
);
|
|
289
|
+
this.name = "SequenceContentionError";
|
|
290
|
+
this.sequence = sequence;
|
|
291
|
+
this.attempts = attempts;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
SequenceOfflineError = class extends NoydbError {
|
|
295
|
+
constructor() {
|
|
296
|
+
super(
|
|
297
|
+
"SEQUENCE_OFFLINE",
|
|
298
|
+
"vault.sequence().next() requires an online CAS-capable store (capabilities.casAtomic). Gap-free numbering cannot be serialized offline."
|
|
299
|
+
);
|
|
300
|
+
this.name = "SequenceOfflineError";
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
NumberingUncertaintyError = class extends NoydbError {
|
|
304
|
+
series;
|
|
305
|
+
constructor(series) {
|
|
306
|
+
super(
|
|
307
|
+
"NUMBERING_UNCERTAINTY",
|
|
308
|
+
`Deferred numbering for series "${series}" cannot run: the store does not expose getStoreTime() (capabilities.serverWriteTime). Use a CAS sequence or a store with serverWriteTime.`
|
|
309
|
+
);
|
|
310
|
+
this.name = "NumberingUncertaintyError";
|
|
311
|
+
this.series = series;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
269
314
|
BundleVersionConflictError = class extends NoydbError {
|
|
270
315
|
/** The bundle handle of the newer remote version that rejected the push. */
|
|
271
316
|
remoteVersion;
|
|
@@ -679,6 +724,39 @@ Resolutions:
|
|
|
679
724
|
this.expected = expected;
|
|
680
725
|
}
|
|
681
726
|
};
|
|
727
|
+
UnknownShardError = class extends NoydbError {
|
|
728
|
+
partitionKey;
|
|
729
|
+
constructor(partitionKey, groupName) {
|
|
730
|
+
super(
|
|
731
|
+
"SHARD_UNKNOWN",
|
|
732
|
+
`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.`
|
|
733
|
+
);
|
|
734
|
+
this.name = "UnknownShardError";
|
|
735
|
+
this.partitionKey = partitionKey;
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
ShardProvisioningError = class extends NoydbError {
|
|
739
|
+
vaultId;
|
|
740
|
+
constructor(vaultId, partitionKey) {
|
|
741
|
+
super(
|
|
742
|
+
"SHARD_PROVISIONING",
|
|
743
|
+
`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.`
|
|
744
|
+
);
|
|
745
|
+
this.name = "ShardProvisioningError";
|
|
746
|
+
this.vaultId = vaultId;
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
VaultTemplateNotFoundError = class extends NoydbError {
|
|
750
|
+
templateName;
|
|
751
|
+
constructor(templateName) {
|
|
752
|
+
super(
|
|
753
|
+
"VAULT_TEMPLATE_NOT_FOUND",
|
|
754
|
+
`No vault template registered under "${templateName}". Register it with db.withVaultTemplate(${JSON.stringify(templateName)}, { version, configure }) before opening the vault group.`
|
|
755
|
+
);
|
|
756
|
+
this.name = "VaultTemplateNotFoundError";
|
|
757
|
+
this.templateName = templateName;
|
|
758
|
+
}
|
|
759
|
+
};
|
|
682
760
|
}
|
|
683
761
|
});
|
|
684
762
|
|
|
@@ -2661,6 +2739,15 @@ var init_store = __esm({
|
|
|
2661
2739
|
}
|
|
2662
2740
|
});
|
|
2663
2741
|
|
|
2742
|
+
// src/federation/constants.ts
|
|
2743
|
+
var STATE_VAULT_NAME;
|
|
2744
|
+
var init_constants2 = __esm({
|
|
2745
|
+
"src/federation/constants.ts"() {
|
|
2746
|
+
"use strict";
|
|
2747
|
+
STATE_VAULT_NAME = "__noydb_state__";
|
|
2748
|
+
}
|
|
2749
|
+
});
|
|
2750
|
+
|
|
2664
2751
|
// src/policy/errors.ts
|
|
2665
2752
|
var PolicyDeniedError, RecoveryNotEnrolledError, ManagedRecoveryNotEnrolledError, RecoveryProfileNotImplementedError;
|
|
2666
2753
|
var init_errors2 = __esm({
|
|
@@ -3603,6 +3690,328 @@ var init_core = __esm({
|
|
|
3603
3690
|
}
|
|
3604
3691
|
});
|
|
3605
3692
|
|
|
3693
|
+
// src/money/fixed-point.ts
|
|
3694
|
+
function expandExponent(s) {
|
|
3695
|
+
const m = /^([+-]?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
|
|
3696
|
+
if (!m) return s;
|
|
3697
|
+
const sign = m[1] === "-" ? "-" : "";
|
|
3698
|
+
const intp = m[2];
|
|
3699
|
+
const frac = m[3] ?? "";
|
|
3700
|
+
const exp = Number(m[4]);
|
|
3701
|
+
const digits = intp + frac;
|
|
3702
|
+
const pointPos = intp.length + exp;
|
|
3703
|
+
let body;
|
|
3704
|
+
if (pointPos <= 0) {
|
|
3705
|
+
body = "0." + "0".repeat(-pointPos) + digits;
|
|
3706
|
+
} else if (pointPos >= digits.length) {
|
|
3707
|
+
body = digits + "0".repeat(pointPos - digits.length);
|
|
3708
|
+
} else {
|
|
3709
|
+
body = digits.slice(0, pointPos) + "." + digits.slice(pointPos);
|
|
3710
|
+
}
|
|
3711
|
+
return sign + body;
|
|
3712
|
+
}
|
|
3713
|
+
function toCanonicalDecimalString(input) {
|
|
3714
|
+
let s;
|
|
3715
|
+
if (typeof input === "number") {
|
|
3716
|
+
if (!Number.isFinite(input)) return null;
|
|
3717
|
+
s = String(input);
|
|
3718
|
+
} else {
|
|
3719
|
+
s = input.trim();
|
|
3720
|
+
}
|
|
3721
|
+
s = expandExponent(s);
|
|
3722
|
+
if (s.startsWith("+")) s = s.slice(1);
|
|
3723
|
+
if (!/^-?(\d+(\.\d*)?|\.\d+)$/.test(s)) return null;
|
|
3724
|
+
return s;
|
|
3725
|
+
}
|
|
3726
|
+
function shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, mode) {
|
|
3727
|
+
switch (mode) {
|
|
3728
|
+
case "up":
|
|
3729
|
+
return true;
|
|
3730
|
+
case "down":
|
|
3731
|
+
return false;
|
|
3732
|
+
case "ceil":
|
|
3733
|
+
return !negative;
|
|
3734
|
+
case "floor":
|
|
3735
|
+
return negative;
|
|
3736
|
+
case "half-up":
|
|
3737
|
+
return firstDiscarded >= 5;
|
|
3738
|
+
case "half-down":
|
|
3739
|
+
return firstDiscarded > 5 || firstDiscarded === 5 && hasMoreNonZeroAfterFirst;
|
|
3740
|
+
case "half-even":
|
|
3741
|
+
if (firstDiscarded > 5) return true;
|
|
3742
|
+
if (firstDiscarded < 5) return false;
|
|
3743
|
+
return hasMoreNonZeroAfterFirst || lastKeptDigit % 2 === 1;
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
function parseToScaledInt(input, scale, rounding) {
|
|
3747
|
+
const canonical2 = toCanonicalDecimalString(input);
|
|
3748
|
+
if (canonical2 === null) return { ok: false, reason: "nonfinite" };
|
|
3749
|
+
const negative = canonical2.startsWith("-");
|
|
3750
|
+
const unsigned = negative ? canonical2.slice(1) : canonical2;
|
|
3751
|
+
const dot = unsigned.indexOf(".");
|
|
3752
|
+
const intPart = dot === -1 ? unsigned : unsigned.slice(0, dot);
|
|
3753
|
+
const fracPart = dot === -1 ? "" : unsigned.slice(dot + 1);
|
|
3754
|
+
const intDigits = intPart === "" ? "0" : intPart;
|
|
3755
|
+
if (fracPart.length <= scale) {
|
|
3756
|
+
const keep2 = fracPart.padEnd(scale, "0");
|
|
3757
|
+
const magnitude2 = BigInt(intDigits + keep2);
|
|
3758
|
+
return { ok: true, value: negative && magnitude2 !== 0n ? -magnitude2 : magnitude2 };
|
|
3759
|
+
}
|
|
3760
|
+
const keep = fracPart.slice(0, scale);
|
|
3761
|
+
const tail = fracPart.slice(scale);
|
|
3762
|
+
const magnitudeDigits = intDigits + keep;
|
|
3763
|
+
let magnitude = BigInt(magnitudeDigits);
|
|
3764
|
+
if (/^0+$/.test(tail)) {
|
|
3765
|
+
return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
|
|
3766
|
+
}
|
|
3767
|
+
if (rounding === void 0) return { ok: false, reason: "precision" };
|
|
3768
|
+
const lastKeptDigit = Number(magnitudeDigits[magnitudeDigits.length - 1]);
|
|
3769
|
+
const firstDiscarded = Number(tail[0]);
|
|
3770
|
+
const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1));
|
|
3771
|
+
if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {
|
|
3772
|
+
magnitude += 1n;
|
|
3773
|
+
}
|
|
3774
|
+
return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
|
|
3775
|
+
}
|
|
3776
|
+
function formatScaledInt(value, scale) {
|
|
3777
|
+
const negative = value < 0n;
|
|
3778
|
+
const abs = (negative ? -value : value).toString();
|
|
3779
|
+
if (scale === 0) return (negative ? "-" : "") + abs;
|
|
3780
|
+
const padded = abs.padStart(scale + 1, "0");
|
|
3781
|
+
const cut = padded.length - scale;
|
|
3782
|
+
const intPart = padded.slice(0, cut);
|
|
3783
|
+
const fracPart = padded.slice(cut);
|
|
3784
|
+
return (negative ? "-" : "") + intPart + "." + fracPart;
|
|
3785
|
+
}
|
|
3786
|
+
var init_fixed_point = __esm({
|
|
3787
|
+
"src/money/fixed-point.ts"() {
|
|
3788
|
+
"use strict";
|
|
3789
|
+
}
|
|
3790
|
+
});
|
|
3791
|
+
|
|
3792
|
+
// src/money/iso4217.ts
|
|
3793
|
+
function scaleForCurrency(code) {
|
|
3794
|
+
const v = MINOR_UNITS[code];
|
|
3795
|
+
return v === void 0 ? null : v;
|
|
3796
|
+
}
|
|
3797
|
+
var MINOR_UNITS;
|
|
3798
|
+
var init_iso4217 = __esm({
|
|
3799
|
+
"src/money/iso4217.ts"() {
|
|
3800
|
+
"use strict";
|
|
3801
|
+
MINOR_UNITS = {
|
|
3802
|
+
// 2-decimal majors
|
|
3803
|
+
EUR: 2,
|
|
3804
|
+
USD: 2,
|
|
3805
|
+
GBP: 2,
|
|
3806
|
+
CHF: 2,
|
|
3807
|
+
CAD: 2,
|
|
3808
|
+
AUD: 2,
|
|
3809
|
+
NZD: 2,
|
|
3810
|
+
SGD: 2,
|
|
3811
|
+
HKD: 2,
|
|
3812
|
+
CNY: 2,
|
|
3813
|
+
INR: 2,
|
|
3814
|
+
BRL: 2,
|
|
3815
|
+
MXN: 2,
|
|
3816
|
+
ZAR: 2,
|
|
3817
|
+
RUB: 2,
|
|
3818
|
+
TRY: 2,
|
|
3819
|
+
PLN: 2,
|
|
3820
|
+
SEK: 2,
|
|
3821
|
+
NOK: 2,
|
|
3822
|
+
DKK: 2,
|
|
3823
|
+
CZK: 2,
|
|
3824
|
+
HUF: 2,
|
|
3825
|
+
RON: 2,
|
|
3826
|
+
ILS: 2,
|
|
3827
|
+
THB: 2,
|
|
3828
|
+
PHP: 2,
|
|
3829
|
+
MYR: 2,
|
|
3830
|
+
IDR: 2,
|
|
3831
|
+
AED: 2,
|
|
3832
|
+
SAR: 2,
|
|
3833
|
+
QAR: 2,
|
|
3834
|
+
EGP: 2,
|
|
3835
|
+
// 0-decimal
|
|
3836
|
+
JPY: 0,
|
|
3837
|
+
KRW: 0,
|
|
3838
|
+
ISK: 0,
|
|
3839
|
+
CLP: 0,
|
|
3840
|
+
VND: 0,
|
|
3841
|
+
XOF: 0,
|
|
3842
|
+
XAF: 0,
|
|
3843
|
+
PYG: 0,
|
|
3844
|
+
// 3-decimal
|
|
3845
|
+
BHD: 3,
|
|
3846
|
+
KWD: 3,
|
|
3847
|
+
OMR: 3,
|
|
3848
|
+
TND: 3,
|
|
3849
|
+
JOD: 3,
|
|
3850
|
+
IQD: 3,
|
|
3851
|
+
LYD: 3
|
|
3852
|
+
};
|
|
3853
|
+
}
|
|
3854
|
+
});
|
|
3855
|
+
|
|
3856
|
+
// src/money/descriptor.ts
|
|
3857
|
+
var MoneyPrecisionError, MoneyUnsupportedError;
|
|
3858
|
+
var init_descriptor = __esm({
|
|
3859
|
+
"src/money/descriptor.ts"() {
|
|
3860
|
+
"use strict";
|
|
3861
|
+
init_errors();
|
|
3862
|
+
MoneyPrecisionError = class extends NoydbError {
|
|
3863
|
+
constructor(field, value, scale) {
|
|
3864
|
+
super(
|
|
3865
|
+
"MONEY_PRECISION",
|
|
3866
|
+
`money: value ${JSON.stringify(value)} for field "${field}" exceeds scale ${scale} and no rounding mode is configured`
|
|
3867
|
+
);
|
|
3868
|
+
this.field = field;
|
|
3869
|
+
this.value = value;
|
|
3870
|
+
this.scale = scale;
|
|
3871
|
+
this.name = "MoneyPrecisionError";
|
|
3872
|
+
}
|
|
3873
|
+
field;
|
|
3874
|
+
value;
|
|
3875
|
+
scale;
|
|
3876
|
+
};
|
|
3877
|
+
MoneyUnsupportedError = class extends NoydbError {
|
|
3878
|
+
constructor(field, message) {
|
|
3879
|
+
super(
|
|
3880
|
+
"MONEY_UNSUPPORTED",
|
|
3881
|
+
message ?? `money: operation is not supported on field "${field}" \u2014 use sum() and count() and divide at the boundary`
|
|
3882
|
+
);
|
|
3883
|
+
this.field = field;
|
|
3884
|
+
this.name = "MoneyUnsupportedError";
|
|
3885
|
+
}
|
|
3886
|
+
field;
|
|
3887
|
+
};
|
|
3888
|
+
}
|
|
3889
|
+
});
|
|
3890
|
+
|
|
3891
|
+
// src/money/normalize.ts
|
|
3892
|
+
function isMoneyValueObject(v) {
|
|
3893
|
+
return typeof v === "object" && v !== null && "currency" in v;
|
|
3894
|
+
}
|
|
3895
|
+
function quantizeAmount(field, input, scale, rounding) {
|
|
3896
|
+
const r = parseToScaledInt(input, scale, rounding);
|
|
3897
|
+
if (!r.ok) {
|
|
3898
|
+
if (r.reason === "precision") throw new MoneyPrecisionError(field, input, scale);
|
|
3899
|
+
throw new TypeError(`money: field "${field}" value ${JSON.stringify(input)} is not a finite decimal`);
|
|
3900
|
+
}
|
|
3901
|
+
return r.value.toString();
|
|
3902
|
+
}
|
|
3903
|
+
function quantizeMoneyFields(record, moneyFields) {
|
|
3904
|
+
const out = { ...record };
|
|
3905
|
+
for (const [field, desc] of Object.entries(moneyFields)) {
|
|
3906
|
+
const raw = out[field];
|
|
3907
|
+
if (raw === null || raw === void 0) continue;
|
|
3908
|
+
if (desc.mode === "fixed") {
|
|
3909
|
+
const currency2 = desc.fixedCurrency;
|
|
3910
|
+
out[field] = quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
|
|
3911
|
+
continue;
|
|
3912
|
+
}
|
|
3913
|
+
let amount;
|
|
3914
|
+
let currency;
|
|
3915
|
+
if (isMoneyValueObject(raw)) {
|
|
3916
|
+
currency = String(raw.currency);
|
|
3917
|
+
amount = raw.amount;
|
|
3918
|
+
} else {
|
|
3919
|
+
const sole = desc.soleCurrency();
|
|
3920
|
+
if (sole === void 0) {
|
|
3921
|
+
throw new TypeError(
|
|
3922
|
+
`money: field "${field}" is multi-currency \u2014 write { amount, currency }, not a bare amount`
|
|
3923
|
+
);
|
|
3924
|
+
}
|
|
3925
|
+
currency = sole;
|
|
3926
|
+
amount = raw;
|
|
3927
|
+
}
|
|
3928
|
+
const scale = desc.scaleFor(currency);
|
|
3929
|
+
out[field] = { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
|
|
3930
|
+
}
|
|
3931
|
+
return out;
|
|
3932
|
+
}
|
|
3933
|
+
function formatCurrency(decimal, currency, scale, locale) {
|
|
3934
|
+
const fmt = new Intl.NumberFormat(locale, {
|
|
3935
|
+
style: "currency",
|
|
3936
|
+
currency,
|
|
3937
|
+
minimumFractionDigits: scale,
|
|
3938
|
+
maximumFractionDigits: scale
|
|
3939
|
+
});
|
|
3940
|
+
return fmt.format(decimal);
|
|
3941
|
+
}
|
|
3942
|
+
function decodeMoneyFields(record, moneyFields, locale) {
|
|
3943
|
+
const out = { ...record };
|
|
3944
|
+
const format = locale !== "raw";
|
|
3945
|
+
const fmtLocale = typeof locale === "string" && locale !== "raw" ? locale : "en-US";
|
|
3946
|
+
for (const [field, desc] of Object.entries(moneyFields)) {
|
|
3947
|
+
const stored = out[field];
|
|
3948
|
+
if (stored === null || stored === void 0) continue;
|
|
3949
|
+
let currency;
|
|
3950
|
+
let scaledIntString;
|
|
3951
|
+
if (desc.mode === "fixed") {
|
|
3952
|
+
if (typeof stored !== "string" && typeof stored !== "number") continue;
|
|
3953
|
+
currency = desc.fixedCurrency;
|
|
3954
|
+
scaledIntString = String(stored);
|
|
3955
|
+
} else {
|
|
3956
|
+
if (!isMoneyValueObject(stored)) continue;
|
|
3957
|
+
const amount = stored.amount;
|
|
3958
|
+
if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") continue;
|
|
3959
|
+
currency = stored.currency;
|
|
3960
|
+
scaledIntString = String(amount);
|
|
3961
|
+
}
|
|
3962
|
+
const scale = desc.scaleFor(currency);
|
|
3963
|
+
const decimal = formatScaledInt(BigInt(scaledIntString), scale);
|
|
3964
|
+
out[field] = desc.mode === "fixed" ? decimal : { amount: decimal, currency };
|
|
3965
|
+
if (format) {
|
|
3966
|
+
out[`${field}Formatted`] = formatCurrency(decimal, currency, scale, fmtLocale);
|
|
3967
|
+
out[`${field}Number`] = Number(decimal);
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
return out;
|
|
3971
|
+
}
|
|
3972
|
+
var init_normalize = __esm({
|
|
3973
|
+
"src/money/normalize.ts"() {
|
|
3974
|
+
"use strict";
|
|
3975
|
+
init_fixed_point();
|
|
3976
|
+
init_descriptor();
|
|
3977
|
+
}
|
|
3978
|
+
});
|
|
3979
|
+
|
|
3980
|
+
// src/computed/index.ts
|
|
3981
|
+
function evalComputedFields(record, computed, id) {
|
|
3982
|
+
const out = { ...record };
|
|
3983
|
+
for (const [field, fn] of Object.entries(computed)) {
|
|
3984
|
+
try {
|
|
3985
|
+
out[field] = fn(out);
|
|
3986
|
+
} catch (cause) {
|
|
3987
|
+
throw new ComputedFieldError(field, id, cause);
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
return out;
|
|
3991
|
+
}
|
|
3992
|
+
var ComputedFieldError;
|
|
3993
|
+
var init_computed = __esm({
|
|
3994
|
+
"src/computed/index.ts"() {
|
|
3995
|
+
"use strict";
|
|
3996
|
+
init_errors();
|
|
3997
|
+
ComputedFieldError = class extends NoydbError {
|
|
3998
|
+
constructor(field, id, cause) {
|
|
3999
|
+
super(
|
|
4000
|
+
"COMPUTED_FIELD",
|
|
4001
|
+
`computed field "${field}" threw for record "${id}": ` + (cause instanceof Error ? cause.message : String(cause))
|
|
4002
|
+
);
|
|
4003
|
+
this.field = field;
|
|
4004
|
+
this.id = id;
|
|
4005
|
+
this.cause = cause;
|
|
4006
|
+
this.name = "ComputedFieldError";
|
|
4007
|
+
}
|
|
4008
|
+
field;
|
|
4009
|
+
id;
|
|
4010
|
+
cause;
|
|
4011
|
+
};
|
|
4012
|
+
}
|
|
4013
|
+
});
|
|
4014
|
+
|
|
3606
4015
|
// src/i18n/strategy.ts
|
|
3607
4016
|
function notEnabled(op) {
|
|
3608
4017
|
return new Error(
|
|
@@ -4087,6 +4496,189 @@ var init_strategy4 = __esm({
|
|
|
4087
4496
|
}
|
|
4088
4497
|
});
|
|
4089
4498
|
|
|
4499
|
+
// src/money/money-reducer.ts
|
|
4500
|
+
function toScaledInt(v) {
|
|
4501
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "bigint") {
|
|
4502
|
+
try {
|
|
4503
|
+
return BigInt(v);
|
|
4504
|
+
} catch {
|
|
4505
|
+
return null;
|
|
4506
|
+
}
|
|
4507
|
+
}
|
|
4508
|
+
return null;
|
|
4509
|
+
}
|
|
4510
|
+
function readMoney(record, field, desc) {
|
|
4511
|
+
const raw = readPath(record, field);
|
|
4512
|
+
if (raw === null || raw === void 0) return null;
|
|
4513
|
+
if (desc.mode === "fixed") {
|
|
4514
|
+
const value2 = toScaledInt(raw);
|
|
4515
|
+
return value2 === null ? null : { currency: desc.fixedCurrency, value: value2 };
|
|
4516
|
+
}
|
|
4517
|
+
if (typeof raw !== "object") return null;
|
|
4518
|
+
const o = raw;
|
|
4519
|
+
if (typeof o.currency !== "string") return null;
|
|
4520
|
+
const value = toScaledInt(o.amount);
|
|
4521
|
+
return value === null ? null : { currency: o.currency, value };
|
|
4522
|
+
}
|
|
4523
|
+
function targetScaleFor(desc, currency) {
|
|
4524
|
+
if (desc.allows(currency)) return desc.scaleFor(currency);
|
|
4525
|
+
const s = scaleForCurrency(currency);
|
|
4526
|
+
if (s === null) {
|
|
4527
|
+
throw new Error(`money: cannot determine scale for conversion target "${currency}"`);
|
|
4528
|
+
}
|
|
4529
|
+
return s;
|
|
4530
|
+
}
|
|
4531
|
+
function parseRate(rate) {
|
|
4532
|
+
const s = String(rate).trim();
|
|
4533
|
+
const neg = s.startsWith("-");
|
|
4534
|
+
const body = neg ? s.slice(1) : s;
|
|
4535
|
+
const dot = body.indexOf(".");
|
|
4536
|
+
const intPart = dot === -1 ? body : body.slice(0, dot);
|
|
4537
|
+
const fracPart = dot === -1 ? "" : body.slice(dot + 1);
|
|
4538
|
+
const int = BigInt((intPart === "" ? "0" : intPart) + fracPart);
|
|
4539
|
+
return { int: neg ? -int : int, scale: fracPart.length };
|
|
4540
|
+
}
|
|
4541
|
+
function divRoundHalfEven(n, d) {
|
|
4542
|
+
const q = n / d;
|
|
4543
|
+
const r = n % d;
|
|
4544
|
+
const twiceR = (r < 0n ? -r : r) * 2n;
|
|
4545
|
+
if (twiceR < d) return q;
|
|
4546
|
+
if (twiceR > d) return q + (n < 0n ? -1n : 1n);
|
|
4547
|
+
return q % 2n === 0n ? q : q + (n < 0n ? -1n : 1n);
|
|
4548
|
+
}
|
|
4549
|
+
function convertScaled(value, srcScale, rate, targetScale) {
|
|
4550
|
+
const { int: rateInt, scale: rateScale } = parseRate(rate);
|
|
4551
|
+
const product = value * rateInt;
|
|
4552
|
+
const curScale = srcScale + rateScale;
|
|
4553
|
+
if (curScale === targetScale) return product;
|
|
4554
|
+
if (curScale < targetScale) return product * 10n ** BigInt(targetScale - curScale);
|
|
4555
|
+
return divRoundHalfEven(product, 10n ** BigInt(curScale - targetScale));
|
|
4556
|
+
}
|
|
4557
|
+
function finalizeSum(state, desc, convertTo, fx) {
|
|
4558
|
+
if (convertTo !== void 0) {
|
|
4559
|
+
if (fx === void 0) {
|
|
4560
|
+
throw new Error(`money: sum convertTo "${convertTo}" requires an fx rate map`);
|
|
4561
|
+
}
|
|
4562
|
+
const targetScale = targetScaleFor(desc, convertTo);
|
|
4563
|
+
let total = 0n;
|
|
4564
|
+
for (const [cur, v] of state) {
|
|
4565
|
+
if (cur === convertTo) {
|
|
4566
|
+
total += convertScaled(v, desc.scaleFor(cur), 1, targetScale);
|
|
4567
|
+
continue;
|
|
4568
|
+
}
|
|
4569
|
+
const rate = fx[`${cur}->${convertTo}`];
|
|
4570
|
+
if (rate === void 0) {
|
|
4571
|
+
throw new Error(`money: no fx rate for "${cur}->${convertTo}"`);
|
|
4572
|
+
}
|
|
4573
|
+
total += convertScaled(v, desc.scaleFor(cur), rate, targetScale);
|
|
4574
|
+
}
|
|
4575
|
+
return formatScaledInt(total, targetScale);
|
|
4576
|
+
}
|
|
4577
|
+
if (desc.mode === "fixed") {
|
|
4578
|
+
const cur = desc.fixedCurrency;
|
|
4579
|
+
return formatScaledInt(state.get(cur) ?? 0n, desc.scaleFor(cur));
|
|
4580
|
+
}
|
|
4581
|
+
const out = {};
|
|
4582
|
+
for (const [cur, v] of state) out[cur] = formatScaledInt(v, desc.scaleFor(cur));
|
|
4583
|
+
return out;
|
|
4584
|
+
}
|
|
4585
|
+
function moneySumReducer(field, desc, convertTo, fx) {
|
|
4586
|
+
return {
|
|
4587
|
+
op: "sum",
|
|
4588
|
+
field,
|
|
4589
|
+
init: () => /* @__PURE__ */ new Map(),
|
|
4590
|
+
step: (state, record) => {
|
|
4591
|
+
const m = readMoney(record, field, desc);
|
|
4592
|
+
if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) + m.value);
|
|
4593
|
+
return state;
|
|
4594
|
+
},
|
|
4595
|
+
remove: (state, record) => {
|
|
4596
|
+
const m = readMoney(record, field, desc);
|
|
4597
|
+
if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) - m.value);
|
|
4598
|
+
return state;
|
|
4599
|
+
},
|
|
4600
|
+
finalize: (state) => finalizeSum(state, desc, convertTo, fx)
|
|
4601
|
+
};
|
|
4602
|
+
}
|
|
4603
|
+
function extremum(values, op) {
|
|
4604
|
+
let out = values[0];
|
|
4605
|
+
for (let i = 1; i < values.length; i++) {
|
|
4606
|
+
const v = values[i];
|
|
4607
|
+
if (op === "min" ? v < out : v > out) out = v;
|
|
4608
|
+
}
|
|
4609
|
+
return out;
|
|
4610
|
+
}
|
|
4611
|
+
function moneyMinMaxReducer(op, field, desc) {
|
|
4612
|
+
return {
|
|
4613
|
+
op,
|
|
4614
|
+
field,
|
|
4615
|
+
init: () => /* @__PURE__ */ new Map(),
|
|
4616
|
+
step: (state, record) => {
|
|
4617
|
+
const m = readMoney(record, field, desc);
|
|
4618
|
+
if (m) {
|
|
4619
|
+
const arr = state.get(m.currency);
|
|
4620
|
+
if (arr) arr.push(m.value);
|
|
4621
|
+
else state.set(m.currency, [m.value]);
|
|
4622
|
+
}
|
|
4623
|
+
return state;
|
|
4624
|
+
},
|
|
4625
|
+
remove: (state, record) => {
|
|
4626
|
+
const m = readMoney(record, field, desc);
|
|
4627
|
+
if (m) {
|
|
4628
|
+
const arr = state.get(m.currency);
|
|
4629
|
+
if (arr) {
|
|
4630
|
+
const idx = arr.indexOf(m.value);
|
|
4631
|
+
if (idx >= 0) arr.splice(idx, 1);
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4634
|
+
return state;
|
|
4635
|
+
},
|
|
4636
|
+
finalize: (state) => {
|
|
4637
|
+
if (desc.mode === "fixed") {
|
|
4638
|
+
const cur = desc.fixedCurrency;
|
|
4639
|
+
const arr = state.get(cur);
|
|
4640
|
+
if (!arr || arr.length === 0) return null;
|
|
4641
|
+
return formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
|
|
4642
|
+
}
|
|
4643
|
+
const out = {};
|
|
4644
|
+
for (const [cur, arr] of state) {
|
|
4645
|
+
if (arr.length > 0) out[cur] = formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
|
|
4646
|
+
}
|
|
4647
|
+
return out;
|
|
4648
|
+
}
|
|
4649
|
+
};
|
|
4650
|
+
}
|
|
4651
|
+
function wrapMoneyReducers(spec, moneyFields) {
|
|
4652
|
+
let changed = false;
|
|
4653
|
+
const out = {};
|
|
4654
|
+
for (const [key, reducer] of Object.entries(spec)) {
|
|
4655
|
+
const field = reducer.field;
|
|
4656
|
+
const desc = field ? moneyFields[field] : void 0;
|
|
4657
|
+
if (desc && reducer.op === "avg") {
|
|
4658
|
+
throw new MoneyUnsupportedError(
|
|
4659
|
+
field,
|
|
4660
|
+
`avg() is not supported on money field "${field}" in v1 \u2014 use sum() and count() and divide at the boundary.`
|
|
4661
|
+
);
|
|
4662
|
+
}
|
|
4663
|
+
if (desc && (reducer.op === "sum" || reducer.op === "min" || reducer.op === "max")) {
|
|
4664
|
+
changed = true;
|
|
4665
|
+
out[key] = reducer.op === "sum" ? moneySumReducer(field, desc, reducer.convertTo, reducer.fx) : moneyMinMaxReducer(reducer.op, field, desc);
|
|
4666
|
+
} else {
|
|
4667
|
+
out[key] = reducer;
|
|
4668
|
+
}
|
|
4669
|
+
}
|
|
4670
|
+
return changed ? out : spec;
|
|
4671
|
+
}
|
|
4672
|
+
var init_money_reducer = __esm({
|
|
4673
|
+
"src/money/money-reducer.ts"() {
|
|
4674
|
+
"use strict";
|
|
4675
|
+
init_predicate();
|
|
4676
|
+
init_fixed_point();
|
|
4677
|
+
init_iso4217();
|
|
4678
|
+
init_descriptor();
|
|
4679
|
+
}
|
|
4680
|
+
});
|
|
4681
|
+
|
|
4090
4682
|
// src/query/builder.ts
|
|
4091
4683
|
function executePlanWithSource(source, plan, joinContext) {
|
|
4092
4684
|
const hasCrossJoins = plan.clauses.some((c) => c.type === "crossJoin");
|
|
@@ -4309,7 +4901,7 @@ function serializeClause(clause) {
|
|
|
4309
4901
|
}
|
|
4310
4902
|
function canonicalCtxHash(ctx) {
|
|
4311
4903
|
if (ctx === void 0) return "";
|
|
4312
|
-
const
|
|
4904
|
+
const canonical2 = JSON.stringify(ctx, (_key, value) => {
|
|
4313
4905
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
4314
4906
|
const sorted = {};
|
|
4315
4907
|
for (const k of Object.keys(value).sort()) {
|
|
@@ -4320,8 +4912,8 @@ function canonicalCtxHash(ctx) {
|
|
|
4320
4912
|
return value;
|
|
4321
4913
|
});
|
|
4322
4914
|
let h = 5381;
|
|
4323
|
-
for (let i = 0; i <
|
|
4324
|
-
h = (h << 5) + h ^
|
|
4915
|
+
for (let i = 0; i < canonical2.length; i++) {
|
|
4916
|
+
h = (h << 5) + h ^ canonical2.charCodeAt(i);
|
|
4325
4917
|
}
|
|
4326
4918
|
return (h >>> 0).toString(16).padStart(8, "0");
|
|
4327
4919
|
}
|
|
@@ -4363,6 +4955,8 @@ var init_builder = __esm({
|
|
|
4363
4955
|
init_errors();
|
|
4364
4956
|
init_live();
|
|
4365
4957
|
init_strategy4();
|
|
4958
|
+
init_money_reducer();
|
|
4959
|
+
init_normalize();
|
|
4366
4960
|
EMPTY_PLAN = {
|
|
4367
4961
|
clauses: [],
|
|
4368
4962
|
orderBy: [],
|
|
@@ -4731,7 +5325,7 @@ var init_builder = __esm({
|
|
|
4731
5325
|
* for the ordering rationale.
|
|
4732
5326
|
*/
|
|
4733
5327
|
toArray() {
|
|
4734
|
-
const base = executePlanWithSource(this.source, this.plan, this.joinContext);
|
|
5328
|
+
const base = this.decodeMoney(executePlanWithSource(this.source, this.plan, this.joinContext));
|
|
4735
5329
|
if (this.plan.joins.length === 0) return base;
|
|
4736
5330
|
if (!this.joinContext) {
|
|
4737
5331
|
throw new Error(
|
|
@@ -4740,6 +5334,23 @@ var init_builder = __esm({
|
|
|
4740
5334
|
}
|
|
4741
5335
|
return applyJoins(base, this.plan.joins, this.joinContext);
|
|
4742
5336
|
}
|
|
5337
|
+
/**
|
|
5338
|
+
* Decode this source's money fields on read (stored scaled-int → canonical
|
|
5339
|
+
* decimal), so `query().toArray()` agrees with `get()`/`sum()` on the value.
|
|
5340
|
+
* No-op when the source declares no money fields.
|
|
5341
|
+
*
|
|
5342
|
+
* The query layer carries no locale context, so we decode with `'raw'` —
|
|
5343
|
+
* canonical decimal, WITHOUT fabricating locale-formatted `<field>Formatted`
|
|
5344
|
+
* / `<field>Number` virtuals. Producing a guessed-locale string here would
|
|
5345
|
+
* just reintroduce #322's "two read paths disagree" failure on the virtual
|
|
5346
|
+
* field (e.g. it-IT via `get()` vs en-US here). Consumers who need formatted
|
|
5347
|
+
* money read through `get()`/`list()` with a locale.
|
|
5348
|
+
*/
|
|
5349
|
+
decodeMoney(records) {
|
|
5350
|
+
const moneyFields = this.source.moneyFields;
|
|
5351
|
+
if (!moneyFields || Object.keys(moneyFields).length === 0) return records;
|
|
5352
|
+
return records.map((r) => decodeMoneyFields(r, moneyFields, "raw"));
|
|
5353
|
+
}
|
|
4743
5354
|
/** Return the first matching record, or null. Joins are applied. */
|
|
4744
5355
|
first() {
|
|
4745
5356
|
const arr = this.limit(1).toArray();
|
|
@@ -4808,6 +5419,10 @@ var init_builder = __esm({
|
|
|
4808
5419
|
* partition boundaries without an API break.
|
|
4809
5420
|
*/
|
|
4810
5421
|
aggregate(spec) {
|
|
5422
|
+
const moneyFields = this.source.moneyFields;
|
|
5423
|
+
if (moneyFields) {
|
|
5424
|
+
spec = wrapMoneyReducers(spec, moneyFields);
|
|
5425
|
+
}
|
|
4811
5426
|
const source = this.source;
|
|
4812
5427
|
const clauses = this.plan.clauses;
|
|
4813
5428
|
const joinCtx = this.joinContext;
|
|
@@ -4855,13 +5470,15 @@ var init_builder = __esm({
|
|
|
4855
5470
|
executeRecords,
|
|
4856
5471
|
field,
|
|
4857
5472
|
upstreams,
|
|
4858
|
-
dictLabelResolver
|
|
5473
|
+
dictLabelResolver,
|
|
5474
|
+
this.source.moneyFields
|
|
4859
5475
|
);
|
|
4860
5476
|
}
|
|
4861
5477
|
return this.aggregateStrategy.groupByN(
|
|
4862
5478
|
executeRecords,
|
|
4863
5479
|
fields,
|
|
4864
|
-
upstreams
|
|
5480
|
+
upstreams,
|
|
5481
|
+
this.source.moneyFields
|
|
4865
5482
|
);
|
|
4866
5483
|
}
|
|
4867
5484
|
/**
|
|
@@ -4982,6 +5599,29 @@ var init_builder = __esm({
|
|
|
4982
5599
|
}
|
|
4983
5600
|
});
|
|
4984
5601
|
|
|
5602
|
+
// src/aggregate/aggregation.ts
|
|
5603
|
+
function reduceRecords(records, spec) {
|
|
5604
|
+
const state = {};
|
|
5605
|
+
for (const key of Object.keys(spec)) {
|
|
5606
|
+
state[key] = spec[key].init();
|
|
5607
|
+
}
|
|
5608
|
+
for (const record of records) {
|
|
5609
|
+
for (const key of Object.keys(spec)) {
|
|
5610
|
+
state[key] = spec[key].step(state[key], record);
|
|
5611
|
+
}
|
|
5612
|
+
}
|
|
5613
|
+
const result = {};
|
|
5614
|
+
for (const key of Object.keys(spec)) {
|
|
5615
|
+
result[key] = spec[key].finalize(state[key]);
|
|
5616
|
+
}
|
|
5617
|
+
return result;
|
|
5618
|
+
}
|
|
5619
|
+
var init_aggregation = __esm({
|
|
5620
|
+
"src/aggregate/aggregation.ts"() {
|
|
5621
|
+
"use strict";
|
|
5622
|
+
}
|
|
5623
|
+
});
|
|
5624
|
+
|
|
4985
5625
|
// src/aggregate/canonical-key.ts
|
|
4986
5626
|
function canonicalGroupKey(fields, row) {
|
|
4987
5627
|
const sorted = [...fields].sort();
|
|
@@ -5088,6 +5728,7 @@ var init_scan_builder = __esm({
|
|
|
5088
5728
|
"use strict";
|
|
5089
5729
|
init_predicate();
|
|
5090
5730
|
init_errors();
|
|
5731
|
+
init_normalize();
|
|
5091
5732
|
DEFAULT_SCAN_PAGE_SIZE = 100;
|
|
5092
5733
|
ScanBuilder = class _ScanBuilder {
|
|
5093
5734
|
pageProvider;
|
|
@@ -5113,12 +5754,29 @@ var init_scan_builder = __esm({
|
|
|
5113
5754
|
* context throws with an actionable error.
|
|
5114
5755
|
*/
|
|
5115
5756
|
joinContext;
|
|
5116
|
-
|
|
5757
|
+
/**
|
|
5758
|
+
* Money field descriptors for the backing collection. When present, yielded
|
|
5759
|
+
* records are decoded (stored scaled-int → canonical decimal) so `scan()`
|
|
5760
|
+
* agrees with `get()`/`list()`/`query().toArray()` — #322. Decoded with
|
|
5761
|
+
* `'raw'` (canonical decimal, no locale-formatted virtuals) since the scan
|
|
5762
|
+
* stream carries no locale context, mirroring `Query.toArray()`.
|
|
5763
|
+
*/
|
|
5764
|
+
moneyFields;
|
|
5765
|
+
constructor(pageProvider, pageSize = DEFAULT_SCAN_PAGE_SIZE, clauses = [], joins = [], joinContext, moneyFields) {
|
|
5117
5766
|
this.pageProvider = pageProvider;
|
|
5118
5767
|
this.pageSize = pageSize;
|
|
5119
5768
|
this.clauses = clauses;
|
|
5120
5769
|
this.joins = joins;
|
|
5121
5770
|
this.joinContext = joinContext;
|
|
5771
|
+
this.moneyFields = moneyFields;
|
|
5772
|
+
}
|
|
5773
|
+
/**
|
|
5774
|
+
* Decode this scan's money fields on a record (stored scaled-int → canonical
|
|
5775
|
+
* decimal). No-op when no money fields are declared. See {@link moneyFields}.
|
|
5776
|
+
*/
|
|
5777
|
+
decodeMoney(record) {
|
|
5778
|
+
if (!this.moneyFields || Object.keys(this.moneyFields).length === 0) return record;
|
|
5779
|
+
return decodeMoneyFields(record, this.moneyFields, "raw");
|
|
5122
5780
|
}
|
|
5123
5781
|
/**
|
|
5124
5782
|
* Add a field comparison. Runs per record as the scan stream
|
|
@@ -5140,7 +5798,8 @@ var init_scan_builder = __esm({
|
|
|
5140
5798
|
this.pageSize,
|
|
5141
5799
|
[...this.clauses, clause],
|
|
5142
5800
|
this.joins,
|
|
5143
|
-
this.joinContext
|
|
5801
|
+
this.joinContext,
|
|
5802
|
+
this.moneyFields
|
|
5144
5803
|
);
|
|
5145
5804
|
}
|
|
5146
5805
|
/**
|
|
@@ -5159,7 +5818,8 @@ var init_scan_builder = __esm({
|
|
|
5159
5818
|
this.pageSize,
|
|
5160
5819
|
[...this.clauses, clause],
|
|
5161
5820
|
this.joins,
|
|
5162
|
-
this.joinContext
|
|
5821
|
+
this.joinContext,
|
|
5822
|
+
this.moneyFields
|
|
5163
5823
|
);
|
|
5164
5824
|
}
|
|
5165
5825
|
/**
|
|
@@ -5270,7 +5930,8 @@ var init_scan_builder = __esm({
|
|
|
5270
5930
|
this.pageSize,
|
|
5271
5931
|
this.clauses,
|
|
5272
5932
|
[...this.joins, leg],
|
|
5273
|
-
this.joinContext
|
|
5933
|
+
this.joinContext,
|
|
5934
|
+
this.moneyFields
|
|
5274
5935
|
);
|
|
5275
5936
|
}
|
|
5276
5937
|
/**
|
|
@@ -5287,10 +5948,11 @@ var init_scan_builder = __esm({
|
|
|
5287
5948
|
while (true) {
|
|
5288
5949
|
for (const record of page.items) {
|
|
5289
5950
|
if (!this.recordMatches(record)) continue;
|
|
5951
|
+
const decoded = this.decodeMoney(record);
|
|
5290
5952
|
if (joinResolvers === null) {
|
|
5291
|
-
yield
|
|
5953
|
+
yield decoded;
|
|
5292
5954
|
} else {
|
|
5293
|
-
let attached =
|
|
5955
|
+
let attached = decoded;
|
|
5294
5956
|
for (const resolver of joinResolvers) {
|
|
5295
5957
|
attached = this.applyOneJoinStreaming(attached, resolver);
|
|
5296
5958
|
}
|
|
@@ -6458,12 +7120,12 @@ var init_dependency_analyzer = __esm({
|
|
|
6458
7120
|
|
|
6459
7121
|
// src/materialized-views/query-hash.ts
|
|
6460
7122
|
async function computeQueryHash(mvName, dependencies, queryPlanSummary) {
|
|
6461
|
-
const
|
|
7123
|
+
const canonical2 = JSON.stringify({
|
|
6462
7124
|
mvName,
|
|
6463
7125
|
dependencies: [...dependencies].sort(),
|
|
6464
7126
|
queryPlanSummary
|
|
6465
7127
|
});
|
|
6466
|
-
const bytes = new TextEncoder().encode(
|
|
7128
|
+
const bytes = new TextEncoder().encode(canonical2);
|
|
6467
7129
|
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
6468
7130
|
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
6469
7131
|
}
|
|
@@ -7017,6 +7679,8 @@ var init_collection = __esm({
|
|
|
7017
7679
|
init_types();
|
|
7018
7680
|
init_strategy();
|
|
7019
7681
|
init_core();
|
|
7682
|
+
init_normalize();
|
|
7683
|
+
init_computed();
|
|
7020
7684
|
init_strategy2();
|
|
7021
7685
|
init_policy();
|
|
7022
7686
|
init_crypto();
|
|
@@ -7185,6 +7849,18 @@ var init_collection = __esm({
|
|
|
7185
7849
|
* fields when a locale is requested.
|
|
7186
7850
|
*/
|
|
7187
7851
|
dictKeyFields;
|
|
7852
|
+
/**
|
|
7853
|
+
* Money field descriptors keyed by field path. Declared via the
|
|
7854
|
+
* `moneyFields` collection option: `put()` quantizes to a scaled-int
|
|
7855
|
+
* string, `get()`/`list()` decode back. Mutable so {@link _applyMoneyFields}
|
|
7856
|
+
* can attach descriptors to a collection MV-analysis pre-created.
|
|
7857
|
+
*/
|
|
7858
|
+
moneyFields;
|
|
7859
|
+
/**
|
|
7860
|
+
* Computed scalar fields, evaluated first on every `put()`. Mutable for
|
|
7861
|
+
* the same MV-pre-creation reconcile as {@link moneyFields}.
|
|
7862
|
+
*/
|
|
7863
|
+
computed;
|
|
7188
7864
|
/**
|
|
7189
7865
|
* Async callback provided by the Vault that resolves a dict key
|
|
7190
7866
|
* to its label for a given locale. Used by the locale-read path for
|
|
@@ -7326,6 +8002,8 @@ var init_collection = __esm({
|
|
|
7326
8002
|
this.joinResolver = opts.joinResolver;
|
|
7327
8003
|
this.i18nFields = opts.i18nFields;
|
|
7328
8004
|
this.dictKeyFields = opts.dictKeyFields;
|
|
8005
|
+
this.moneyFields = opts.moneyFields;
|
|
8006
|
+
this.computed = opts.computed;
|
|
7329
8007
|
this.dictLabelResolver = opts.dictLabelResolver;
|
|
7330
8008
|
this.i18nPutValidator = opts.i18nPutValidator;
|
|
7331
8009
|
this.autoTranslateHook = opts.autoTranslateHook;
|
|
@@ -7450,6 +8128,19 @@ var init_collection = __esm({
|
|
|
7450
8128
|
getSchema() {
|
|
7451
8129
|
return this.schema;
|
|
7452
8130
|
}
|
|
8131
|
+
/**
|
|
8132
|
+
* @internal — attach money descriptors post-construction. MV dependency
|
|
8133
|
+
* analysis auto-creates a source collection (without options) during
|
|
8134
|
+
* `openVault`, before the user's `collection(name, { moneyFields })`
|
|
8135
|
+
* declaration; this reconciles that ordering. First-wins. Not public.
|
|
8136
|
+
*/
|
|
8137
|
+
_applyMoneyFields(moneyFields) {
|
|
8138
|
+
if (this.moneyFields === void 0) this.moneyFields = moneyFields;
|
|
8139
|
+
}
|
|
8140
|
+
/** @internal — attach computed fields post-construction. See {@link _applyMoneyFields}. */
|
|
8141
|
+
_applyComputed(computed) {
|
|
8142
|
+
if (this.computed === void 0) this.computed = computed;
|
|
8143
|
+
}
|
|
7453
8144
|
/**
|
|
7454
8145
|
* Get a single record by ID.
|
|
7455
8146
|
*
|
|
@@ -7621,7 +8312,7 @@ var init_collection = __esm({
|
|
|
7621
8312
|
existingRecord = null;
|
|
7622
8313
|
}
|
|
7623
8314
|
}
|
|
7624
|
-
|
|
8315
|
+
const gateEvent = {
|
|
7625
8316
|
op: existingEnv ? "update" : "create",
|
|
7626
8317
|
vault: this.vault,
|
|
7627
8318
|
collection: this.name,
|
|
@@ -7631,12 +8322,20 @@ var init_collection = __esm({
|
|
|
7631
8322
|
existingVersion: existingEnv?._v ?? 0,
|
|
7632
8323
|
existingTs: existingEnv?._ts,
|
|
7633
8324
|
userId: this.keyring.userId,
|
|
7634
|
-
role: this.keyring.role
|
|
7635
|
-
|
|
8325
|
+
role: this.keyring.role,
|
|
8326
|
+
...this.computed !== void 0 ? { computedFieldNames: new Set(Object.keys(this.computed)) } : {}
|
|
8327
|
+
};
|
|
8328
|
+
await this.subsystemBus.dispatchGate("beforePut", gateEvent);
|
|
8329
|
+
}
|
|
8330
|
+
if (this.computed !== void 0) {
|
|
8331
|
+
record = evalComputedFields(record, this.computed, id);
|
|
7636
8332
|
}
|
|
7637
8333
|
if (this.schema !== void 0) {
|
|
7638
8334
|
record = await validateSchemaInput(this.schema, record, `put(${id})`);
|
|
7639
8335
|
}
|
|
8336
|
+
if (this.moneyFields) {
|
|
8337
|
+
record = quantizeMoneyFields(record, this.moneyFields);
|
|
8338
|
+
}
|
|
7640
8339
|
if (this.i18nFields) {
|
|
7641
8340
|
const obj = record;
|
|
7642
8341
|
for (const [field, descriptor] of Object.entries(this.i18nFields)) {
|
|
@@ -8287,9 +8986,17 @@ var init_collection = __esm({
|
|
|
8287
8986
|
}
|
|
8288
8987
|
await this.ensureHydrated();
|
|
8289
8988
|
const records = [...this.cache.values()].map((e) => e.record);
|
|
8290
|
-
if (!
|
|
8989
|
+
if (!this.hasReadTransforms()) return records;
|
|
8291
8990
|
return Promise.all(records.map((r) => this.applyLocaleToRecord(r, locale)));
|
|
8292
8991
|
}
|
|
8992
|
+
/**
|
|
8993
|
+
* @internal — whether any read-side record transform is registered
|
|
8994
|
+
* (money decode, i18nText resolution, dictKey labels). Gates the
|
|
8995
|
+
* no-transform fast path in {@link list}.
|
|
8996
|
+
*/
|
|
8997
|
+
hasReadTransforms() {
|
|
8998
|
+
return this.moneyFields !== void 0 && Object.keys(this.moneyFields).length > 0 || this.i18nFields !== void 0 && Object.keys(this.i18nFields).length > 0 || this.dictKeyFields !== void 0 && Object.keys(this.dictKeyFields).length > 0;
|
|
8999
|
+
}
|
|
8293
9000
|
// ─── Bulk operations ─────────────────────────────────────
|
|
8294
9001
|
/**
|
|
8295
9002
|
* Put many records in one call. Each item is processed sequentially
|
|
@@ -8458,7 +9165,8 @@ var init_collection = __esm({
|
|
|
8458
9165
|
// fields. The Query builder consults these when present and falls
|
|
8459
9166
|
// back to a linear scan otherwise.
|
|
8460
9167
|
getIndexes: () => this.getIndexes(),
|
|
8461
|
-
lookupById: (id) => this.cache.get(id)?.record
|
|
9168
|
+
lookupById: (id) => this.cache.get(id)?.record,
|
|
9169
|
+
...this.moneyFields ? { moneyFields: this.moneyFields } : {}
|
|
8462
9170
|
};
|
|
8463
9171
|
const resolver = this.joinResolver;
|
|
8464
9172
|
const leftCollection = this.name;
|
|
@@ -8794,7 +9502,8 @@ var init_collection = __esm({
|
|
|
8794
9502
|
pageSize,
|
|
8795
9503
|
[],
|
|
8796
9504
|
[],
|
|
8797
|
-
joinContext
|
|
9505
|
+
joinContext,
|
|
9506
|
+
this.moneyFields
|
|
8798
9507
|
);
|
|
8799
9508
|
}
|
|
8800
9509
|
/** Decrypt a page of envelopes returned by `adapter.listPage`. */
|
|
@@ -9140,10 +9849,14 @@ var init_collection = __esm({
|
|
|
9140
9849
|
async applyLocaleToRecord(record, localeOpts) {
|
|
9141
9850
|
const hasI18n = this.i18nFields && Object.keys(this.i18nFields).length > 0;
|
|
9142
9851
|
const hasDict = this.dictKeyFields && Object.keys(this.dictKeyFields).length > 0;
|
|
9143
|
-
|
|
9852
|
+
const hasMoney = this.moneyFields && Object.keys(this.moneyFields).length > 0;
|
|
9853
|
+
if (!hasI18n && !hasDict && !hasMoney) return record;
|
|
9144
9854
|
const locale = localeOpts?.locale ?? this.defaultLocale;
|
|
9145
|
-
if (!locale) return record;
|
|
9146
9855
|
let result = record;
|
|
9856
|
+
if (hasMoney && this.moneyFields) {
|
|
9857
|
+
result = decodeMoneyFields(result, this.moneyFields, typeof locale === "string" ? locale : void 0);
|
|
9858
|
+
}
|
|
9859
|
+
if (!locale) return result;
|
|
9147
9860
|
if (hasI18n && this.i18nFields) {
|
|
9148
9861
|
result = this.i18nStrategy.applyI18nLocale(result, this.i18nFields, locale, localeOpts?.fallback);
|
|
9149
9862
|
}
|
|
@@ -9982,49 +10695,387 @@ var init_virtual_collection = __esm({
|
|
|
9982
10695
|
}
|
|
9983
10696
|
});
|
|
9984
10697
|
|
|
9985
|
-
// src/
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
10698
|
+
// src/archive/engine.ts
|
|
10699
|
+
function isHeld(policy, record) {
|
|
10700
|
+
if (!policy.legalHold) return false;
|
|
10701
|
+
try {
|
|
10702
|
+
return policy.legalHold(record);
|
|
10703
|
+
} catch {
|
|
10704
|
+
return true;
|
|
10705
|
+
}
|
|
10706
|
+
}
|
|
10707
|
+
async function runArchive(ctx, options = {}) {
|
|
10708
|
+
const maxArchives = options.maxArchives ?? Infinity;
|
|
10709
|
+
const dryRun = options.dryRun === true;
|
|
10710
|
+
let archived = 0;
|
|
10711
|
+
let held = 0;
|
|
10712
|
+
let scanned = 0;
|
|
10713
|
+
const byCollection = {};
|
|
10714
|
+
outer: for (const collection of ctx.collectionsWithPolicy()) {
|
|
10715
|
+
const policy = ctx.getPolicy(collection);
|
|
10716
|
+
if (!policy) continue;
|
|
10717
|
+
byCollection[collection] = { archived: 0, held: 0 };
|
|
10718
|
+
for (const id of await ctx.listRecordIds(collection)) {
|
|
10719
|
+
if (archived >= maxArchives) break outer;
|
|
10720
|
+
const record = await ctx.getRecord(collection, id).catch(() => null);
|
|
10721
|
+
if (record === null) continue;
|
|
10722
|
+
scanned += 1;
|
|
10723
|
+
let eligible = false;
|
|
10724
|
+
try {
|
|
10725
|
+
eligible = policy.archiveWhen(record);
|
|
10726
|
+
} catch {
|
|
10727
|
+
eligible = false;
|
|
9996
10728
|
}
|
|
9997
|
-
|
|
10729
|
+
if (!eligible) continue;
|
|
10730
|
+
if (isHeld(policy, record)) {
|
|
10731
|
+
held += 1;
|
|
10732
|
+
byCollection[collection].held += 1;
|
|
10733
|
+
continue;
|
|
10734
|
+
}
|
|
10735
|
+
if (!dryRun) {
|
|
10736
|
+
const env = await ctx.getEnvelope(collection, id);
|
|
10737
|
+
if (!env) continue;
|
|
10738
|
+
await ctx.archiveStore.put(ctx.vaultId, collection, id, env);
|
|
10739
|
+
await ctx.removeFromPrimary(collection, id);
|
|
10740
|
+
}
|
|
10741
|
+
archived += 1;
|
|
10742
|
+
byCollection[collection].archived += 1;
|
|
10743
|
+
}
|
|
10744
|
+
}
|
|
10745
|
+
return { archived, held, scanned, byCollection };
|
|
10746
|
+
}
|
|
10747
|
+
async function runRestore(ctx, collection, id) {
|
|
10748
|
+
const env = await ctx.archiveStore.get(ctx.vaultId, collection, id);
|
|
10749
|
+
if (!env) return false;
|
|
10750
|
+
await ctx.restoreToPrimary(collection, id, env);
|
|
10751
|
+
await ctx.archiveStore.delete(ctx.vaultId, collection, id);
|
|
10752
|
+
return true;
|
|
10753
|
+
}
|
|
10754
|
+
async function runListArchived(ctx, collection) {
|
|
10755
|
+
const collections = collection ? [collection] : ctx.collectionsWithPolicy();
|
|
10756
|
+
const out = [];
|
|
10757
|
+
for (const c of collections) {
|
|
10758
|
+
const ids = await ctx.archiveStore.list(ctx.vaultId, c);
|
|
10759
|
+
for (const id of ids) out.push({ collection: c, id });
|
|
10760
|
+
}
|
|
10761
|
+
return out;
|
|
10762
|
+
}
|
|
10763
|
+
var init_engine = __esm({
|
|
10764
|
+
"src/archive/engine.ts"() {
|
|
10765
|
+
"use strict";
|
|
9998
10766
|
}
|
|
9999
10767
|
});
|
|
10000
10768
|
|
|
10001
|
-
// src/
|
|
10002
|
-
var
|
|
10003
|
-
|
|
10004
|
-
"src/consent/strategy.ts"() {
|
|
10769
|
+
// src/archive/index.ts
|
|
10770
|
+
var init_archive = __esm({
|
|
10771
|
+
"src/archive/index.ts"() {
|
|
10005
10772
|
"use strict";
|
|
10006
|
-
|
|
10007
|
-
async write() {
|
|
10008
|
-
},
|
|
10009
|
-
async read() {
|
|
10010
|
-
return [];
|
|
10011
|
-
}
|
|
10012
|
-
};
|
|
10773
|
+
init_engine();
|
|
10013
10774
|
}
|
|
10014
10775
|
});
|
|
10015
10776
|
|
|
10016
|
-
// src/
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10777
|
+
// src/sequence/index.ts
|
|
10778
|
+
async function sleepBackoff2(attempt) {
|
|
10779
|
+
const ceil = Math.min(2 ** attempt, 32);
|
|
10780
|
+
const ms = Math.floor(Math.random() * ceil);
|
|
10781
|
+
await new Promise((r) => setTimeout(r, ms));
|
|
10782
|
+
}
|
|
10783
|
+
var SEQUENCE_COLLECTION, MAX_NEXT_ATTEMPTS, SequenceStore;
|
|
10784
|
+
var init_sequence = __esm({
|
|
10785
|
+
"src/sequence/index.ts"() {
|
|
10020
10786
|
"use strict";
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
);
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10787
|
+
init_types();
|
|
10788
|
+
init_crypto();
|
|
10789
|
+
init_errors();
|
|
10790
|
+
SEQUENCE_COLLECTION = "_sequences";
|
|
10791
|
+
MAX_NEXT_ATTEMPTS = 16;
|
|
10792
|
+
SequenceStore = class {
|
|
10793
|
+
adapter;
|
|
10794
|
+
vault;
|
|
10795
|
+
encrypted;
|
|
10796
|
+
getDEK;
|
|
10797
|
+
actor;
|
|
10798
|
+
/**
|
|
10799
|
+
* Memoized DEK promise. The `_sequences` collection DEK is created on
|
|
10800
|
+
* first access; without sharing one promise, a burst of concurrent
|
|
10801
|
+
* `next()` calls would each trigger DEK creation and diverge (one
|
|
10802
|
+
* writer's ciphertext unreadable by another). One shared promise → one
|
|
10803
|
+
* DEK.
|
|
10804
|
+
*/
|
|
10805
|
+
dekPromise = null;
|
|
10806
|
+
constructor(opts) {
|
|
10807
|
+
this.adapter = opts.adapter;
|
|
10808
|
+
this.vault = opts.vault;
|
|
10809
|
+
this.encrypted = opts.encrypted;
|
|
10810
|
+
this.getDEK = opts.getDEK;
|
|
10811
|
+
this.actor = opts.actor;
|
|
10812
|
+
}
|
|
10813
|
+
/** A handle bound to one sequence name. */
|
|
10814
|
+
handle(name) {
|
|
10815
|
+
return {
|
|
10816
|
+
next: () => this.next(name),
|
|
10817
|
+
peek: () => this.peek(name)
|
|
10818
|
+
};
|
|
10819
|
+
}
|
|
10820
|
+
assertOnline() {
|
|
10821
|
+
if (this.adapter.capabilities?.casAtomic !== true) {
|
|
10822
|
+
throw new SequenceOfflineError();
|
|
10823
|
+
}
|
|
10824
|
+
}
|
|
10825
|
+
dek() {
|
|
10826
|
+
if (!this.dekPromise) this.dekPromise = this.getDEK(SEQUENCE_COLLECTION);
|
|
10827
|
+
return this.dekPromise;
|
|
10828
|
+
}
|
|
10829
|
+
async read(name) {
|
|
10830
|
+
const env = await this.adapter.get(this.vault, SEQUENCE_COLLECTION, name);
|
|
10831
|
+
if (!env) return { env: null, value: 0 };
|
|
10832
|
+
const json = this.encrypted ? await decrypt(env._iv, env._data, await this.dek()) : env._data;
|
|
10833
|
+
const state = JSON.parse(json);
|
|
10834
|
+
return { env, value: state.value };
|
|
10835
|
+
}
|
|
10836
|
+
async encryptState(state, version) {
|
|
10837
|
+
const json = JSON.stringify(state);
|
|
10838
|
+
if (!this.encrypted) {
|
|
10839
|
+
return { _noydb: NOYDB_FORMAT_VERSION, _v: version, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: "", _data: json, _by: this.actor };
|
|
10840
|
+
}
|
|
10841
|
+
const { iv, data } = await encrypt(json, await this.dek());
|
|
10842
|
+
return { _noydb: NOYDB_FORMAT_VERSION, _v: version, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: iv, _data: data, _by: this.actor };
|
|
10843
|
+
}
|
|
10844
|
+
async peek(name) {
|
|
10845
|
+
return (await this.read(name)).value;
|
|
10846
|
+
}
|
|
10847
|
+
async next(name) {
|
|
10848
|
+
this.assertOnline();
|
|
10849
|
+
let lastConflict;
|
|
10850
|
+
for (let attempt = 0; attempt < MAX_NEXT_ATTEMPTS; attempt++) {
|
|
10851
|
+
const { env, value } = await this.read(name);
|
|
10852
|
+
const nextValue = value + 1;
|
|
10853
|
+
const expectedVersion = env?._v ?? 0;
|
|
10854
|
+
const envelope = await this.encryptState({ value: nextValue }, expectedVersion + 1);
|
|
10855
|
+
try {
|
|
10856
|
+
await this.adapter.put(this.vault, SEQUENCE_COLLECTION, name, envelope, expectedVersion);
|
|
10857
|
+
return nextValue;
|
|
10858
|
+
} catch (err) {
|
|
10859
|
+
if (err instanceof ConflictError) {
|
|
10860
|
+
lastConflict = err;
|
|
10861
|
+
if (attempt < MAX_NEXT_ATTEMPTS - 1) await sleepBackoff2(attempt);
|
|
10862
|
+
continue;
|
|
10863
|
+
}
|
|
10864
|
+
throw err;
|
|
10865
|
+
}
|
|
10866
|
+
}
|
|
10867
|
+
void lastConflict;
|
|
10868
|
+
throw new SequenceContentionError(name, MAX_NEXT_ATTEMPTS);
|
|
10869
|
+
}
|
|
10870
|
+
};
|
|
10871
|
+
}
|
|
10872
|
+
});
|
|
10873
|
+
|
|
10874
|
+
// src/numbering/index.ts
|
|
10875
|
+
var NUMBERING_HEAD_COLLECTION, NUMBERING_PENDING_COLLECTION, DeferredNumberingStore;
|
|
10876
|
+
var init_numbering = __esm({
|
|
10877
|
+
"src/numbering/index.ts"() {
|
|
10878
|
+
"use strict";
|
|
10879
|
+
init_types();
|
|
10880
|
+
init_crypto();
|
|
10881
|
+
init_errors();
|
|
10882
|
+
NUMBERING_HEAD_COLLECTION = "_numbering_head";
|
|
10883
|
+
NUMBERING_PENDING_COLLECTION = "_numbering_pending";
|
|
10884
|
+
DeferredNumberingStore = class {
|
|
10885
|
+
adapter;
|
|
10886
|
+
vault;
|
|
10887
|
+
encrypted;
|
|
10888
|
+
getDEK;
|
|
10889
|
+
actor;
|
|
10890
|
+
configs;
|
|
10891
|
+
/**
|
|
10892
|
+
* Stamp a serial onto a USER record THROUGH the Collection layer (so the
|
|
10893
|
+
* cache, indexes, and MVs stay coherent — the engine must NOT write user
|
|
10894
|
+
* collections at the raw adapter level). Returns false if the record is
|
|
10895
|
+
* gone (the engine then skips it without burning a serial). Provided by the
|
|
10896
|
+
* vault; unit tests pass a Map-backed double.
|
|
10897
|
+
*/
|
|
10898
|
+
stamp;
|
|
10899
|
+
/** In-process registry: `${series}::${recordId}` → resolver for the live next() Promise. */
|
|
10900
|
+
waiters = /* @__PURE__ */ new Map();
|
|
10901
|
+
dekCache = /* @__PURE__ */ new Map();
|
|
10902
|
+
constructor(opts) {
|
|
10903
|
+
this.adapter = opts.adapter;
|
|
10904
|
+
this.vault = opts.vault;
|
|
10905
|
+
this.encrypted = opts.encrypted;
|
|
10906
|
+
this.getDEK = opts.getDEK;
|
|
10907
|
+
this.actor = opts.actor;
|
|
10908
|
+
this.configs = opts.configs;
|
|
10909
|
+
this.stamp = opts.stamp;
|
|
10910
|
+
}
|
|
10911
|
+
has(series) {
|
|
10912
|
+
return this.configs.has(series);
|
|
10913
|
+
}
|
|
10914
|
+
dek(collection) {
|
|
10915
|
+
let p = this.dekCache.get(collection);
|
|
10916
|
+
if (!p) {
|
|
10917
|
+
p = this.getDEK(collection);
|
|
10918
|
+
this.dekCache.set(collection, p);
|
|
10919
|
+
}
|
|
10920
|
+
return p;
|
|
10921
|
+
}
|
|
10922
|
+
async readJson(collection, id) {
|
|
10923
|
+
const env = await this.adapter.get(this.vault, collection, id);
|
|
10924
|
+
if (!env) return { env: null, value: null };
|
|
10925
|
+
const json = this.encrypted ? await decrypt(env._iv, env._data, await this.dek(collection)) : env._data;
|
|
10926
|
+
return { env, value: JSON.parse(json) };
|
|
10927
|
+
}
|
|
10928
|
+
async writeJson(collection, id, value, expectedVersion) {
|
|
10929
|
+
const json = JSON.stringify(value);
|
|
10930
|
+
let env;
|
|
10931
|
+
if (!this.encrypted) {
|
|
10932
|
+
env = { _noydb: NOYDB_FORMAT_VERSION, _v: expectedVersion + 1, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: "", _data: json, _by: this.actor };
|
|
10933
|
+
} else {
|
|
10934
|
+
const { iv, data } = await encrypt(json, await this.dek(collection));
|
|
10935
|
+
env = { _noydb: NOYDB_FORMAT_VERSION, _v: expectedVersion + 1, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: iv, _data: data, _by: this.actor };
|
|
10936
|
+
}
|
|
10937
|
+
await this.adapter.put(this.vault, collection, id, env, expectedVersion);
|
|
10938
|
+
}
|
|
10939
|
+
pendingId(series, recordId2) {
|
|
10940
|
+
return `${series}::${recordId2}`;
|
|
10941
|
+
}
|
|
10942
|
+
/** Current last-assigned serial for a series (0 if none). */
|
|
10943
|
+
async peek(series) {
|
|
10944
|
+
const { value } = await this.readJson(NUMBERING_HEAD_COLLECTION, series);
|
|
10945
|
+
return value?.lastSerial ?? 0;
|
|
10946
|
+
}
|
|
10947
|
+
/**
|
|
10948
|
+
* Enqueue a record for numbering: stamp it with the current store clock and
|
|
10949
|
+
* durably write a pending entry. The returned Promise resolves once the
|
|
10950
|
+
* record is durably enqueued; its `assigned` field resolves with the serial
|
|
10951
|
+
* at the next pass (the record's `field` is the durable source of truth —
|
|
10952
|
+
* `assigned` is an in-process convenience that a crash may drop).
|
|
10953
|
+
*/
|
|
10954
|
+
async enqueue(series, recordId2) {
|
|
10955
|
+
const cfg = this.configs.get(series);
|
|
10956
|
+
if (!cfg) throw new NumberingUncertaintyError(series);
|
|
10957
|
+
if (typeof this.adapter.getStoreTime !== "function") throw new NumberingUncertaintyError(series);
|
|
10958
|
+
const st = await this.adapter.getStoreTime();
|
|
10959
|
+
const id = this.pendingId(series, recordId2);
|
|
10960
|
+
const { env } = await this.readJson(NUMBERING_PENDING_COLLECTION, id);
|
|
10961
|
+
const entry = {
|
|
10962
|
+
series,
|
|
10963
|
+
recordId: recordId2,
|
|
10964
|
+
collection: cfg.collection,
|
|
10965
|
+
field: cfg.field,
|
|
10966
|
+
storeEarliest: st.earliest,
|
|
10967
|
+
storeLatest: st.latest,
|
|
10968
|
+
enqueuedAt: Date.now()
|
|
10969
|
+
};
|
|
10970
|
+
await this.writeJson(NUMBERING_PENDING_COLLECTION, id, entry, env?._v ?? 0);
|
|
10971
|
+
const assigned = new Promise((resolve, reject) => {
|
|
10972
|
+
this.waiters.set(id, { resolve, reject });
|
|
10973
|
+
});
|
|
10974
|
+
return { assigned };
|
|
10975
|
+
}
|
|
10976
|
+
async listPending(series) {
|
|
10977
|
+
const ids = await this.adapter.list(this.vault, NUMBERING_PENDING_COLLECTION);
|
|
10978
|
+
const prefix = `${series}::`;
|
|
10979
|
+
const out = [];
|
|
10980
|
+
for (const id of ids) {
|
|
10981
|
+
if (!id.startsWith(prefix)) continue;
|
|
10982
|
+
const { value } = await this.readJson(NUMBERING_PENDING_COLLECTION, id);
|
|
10983
|
+
if (value) out.push({ id, entry: value });
|
|
10984
|
+
}
|
|
10985
|
+
return out;
|
|
10986
|
+
}
|
|
10987
|
+
/**
|
|
10988
|
+
* Run a numbering pass for `series`: select entries provably settled
|
|
10989
|
+
* (`storeLatest ≤ now.earliest` — commit-wait), order by
|
|
10990
|
+
* `(storeEarliest, recordId)`, assign serials after the head, stamp each
|
|
10991
|
+
* record's field, advance the head with one CAS, and consume the entries.
|
|
10992
|
+
* Idempotent/convergent: a losing concurrent pass returns `[]` and the next
|
|
10993
|
+
* pass reconciles. Resolves any in-process enqueue() `assigned` Promises.
|
|
10994
|
+
*/
|
|
10995
|
+
async runPass(series) {
|
|
10996
|
+
const cfg = this.configs.get(series);
|
|
10997
|
+
if (!cfg) throw new NumberingUncertaintyError(series);
|
|
10998
|
+
if (typeof this.adapter.getStoreTime !== "function") throw new NumberingUncertaintyError(series);
|
|
10999
|
+
const now = await this.adapter.getStoreTime();
|
|
11000
|
+
const settled = (await this.listPending(series)).filter((p) => p.entry.storeLatest <= now.earliest).sort(
|
|
11001
|
+
(a, b) => a.entry.storeEarliest - b.entry.storeEarliest || (a.entry.recordId < b.entry.recordId ? -1 : a.entry.recordId > b.entry.recordId ? 1 : 0)
|
|
11002
|
+
);
|
|
11003
|
+
if (settled.length === 0) return [];
|
|
11004
|
+
const { env: headEnv, value: head } = await this.readJson(NUMBERING_HEAD_COLLECTION, series);
|
|
11005
|
+
let serial = head?.lastSerial ?? 0;
|
|
11006
|
+
const assignments = [];
|
|
11007
|
+
for (const { entry } of settled) {
|
|
11008
|
+
serial += 1;
|
|
11009
|
+
const ok = await this.stamp(entry.collection, entry.recordId, entry.field, serial);
|
|
11010
|
+
if (!ok) {
|
|
11011
|
+
serial -= 1;
|
|
11012
|
+
continue;
|
|
11013
|
+
}
|
|
11014
|
+
assignments.push({ recordId: entry.recordId, serial });
|
|
11015
|
+
}
|
|
11016
|
+
try {
|
|
11017
|
+
await this.writeJson(NUMBERING_HEAD_COLLECTION, series, { series, lastSerial: serial, watermark: now.earliest }, headEnv?._v ?? 0);
|
|
11018
|
+
} catch (err) {
|
|
11019
|
+
if (err instanceof ConflictError) return [];
|
|
11020
|
+
throw err;
|
|
11021
|
+
}
|
|
11022
|
+
for (const { id, entry } of settled) {
|
|
11023
|
+
await this.adapter.delete(this.vault, NUMBERING_PENDING_COLLECTION, id);
|
|
11024
|
+
const a = assignments.find((x) => x.recordId === entry.recordId);
|
|
11025
|
+
if (a) {
|
|
11026
|
+
this.waiters.get(id)?.resolve(a.serial);
|
|
11027
|
+
this.waiters.delete(id);
|
|
11028
|
+
}
|
|
11029
|
+
}
|
|
11030
|
+
return assignments;
|
|
11031
|
+
}
|
|
11032
|
+
};
|
|
11033
|
+
}
|
|
11034
|
+
});
|
|
11035
|
+
|
|
11036
|
+
// src/shadow/strategy.ts
|
|
11037
|
+
var NOT_ENABLED3, NO_SHADOW;
|
|
11038
|
+
var init_strategy7 = __esm({
|
|
11039
|
+
"src/shadow/strategy.ts"() {
|
|
11040
|
+
"use strict";
|
|
11041
|
+
NOT_ENABLED3 = new Error(
|
|
11042
|
+
'VaultFrame requires the shadow strategy. Import `{ withShadow }` from "@noy-db/hub/shadow" and pass it to `createNoydb({ shadowStrategy: withShadow() })`.'
|
|
11043
|
+
);
|
|
11044
|
+
NO_SHADOW = {
|
|
11045
|
+
buildFrame() {
|
|
11046
|
+
throw NOT_ENABLED3;
|
|
11047
|
+
}
|
|
11048
|
+
};
|
|
11049
|
+
}
|
|
11050
|
+
});
|
|
11051
|
+
|
|
11052
|
+
// src/consent/strategy.ts
|
|
11053
|
+
var NO_CONSENT;
|
|
11054
|
+
var init_strategy8 = __esm({
|
|
11055
|
+
"src/consent/strategy.ts"() {
|
|
11056
|
+
"use strict";
|
|
11057
|
+
NO_CONSENT = {
|
|
11058
|
+
async write() {
|
|
11059
|
+
},
|
|
11060
|
+
async read() {
|
|
11061
|
+
return [];
|
|
11062
|
+
}
|
|
11063
|
+
};
|
|
11064
|
+
}
|
|
11065
|
+
});
|
|
11066
|
+
|
|
11067
|
+
// src/periods/strategy.ts
|
|
11068
|
+
var NOT_ENABLED4, NO_PERIODS;
|
|
11069
|
+
var init_strategy9 = __esm({
|
|
11070
|
+
"src/periods/strategy.ts"() {
|
|
11071
|
+
"use strict";
|
|
11072
|
+
NOT_ENABLED4 = new Error(
|
|
11073
|
+
'Accounting periods require the periods strategy. Import `{ withPeriods }` from "@noy-db/hub/periods" and pass it to `createNoydb({ periodsStrategy: withPeriods() })`.'
|
|
11074
|
+
);
|
|
11075
|
+
NO_PERIODS = {
|
|
11076
|
+
async loadPeriods() {
|
|
11077
|
+
return [];
|
|
11078
|
+
},
|
|
10028
11079
|
async chainAnchor() {
|
|
10029
11080
|
return { priorPeriodHash: "" };
|
|
10030
11081
|
},
|
|
@@ -10274,6 +11325,7 @@ async function runCompaction(ctx, options = {}) {
|
|
|
10274
11325
|
let evicted = 0;
|
|
10275
11326
|
let records = 0;
|
|
10276
11327
|
let auditEntries = 0;
|
|
11328
|
+
let held = 0;
|
|
10277
11329
|
let collectionsWithPolicy = 0;
|
|
10278
11330
|
outer: for (const collectionName of allCollections) {
|
|
10279
11331
|
if (collectionName.startsWith("_")) continue;
|
|
@@ -10297,6 +11349,10 @@ async function runCompaction(ctx, options = {}) {
|
|
|
10297
11349
|
if (!policy) continue;
|
|
10298
11350
|
const reason = evaluatePolicy(policy, record, slot, now);
|
|
10299
11351
|
if (!reason) continue;
|
|
11352
|
+
if (isHeld2(policy, record, now)) {
|
|
11353
|
+
held += 1;
|
|
11354
|
+
continue;
|
|
11355
|
+
}
|
|
10300
11356
|
if (!dryRun) {
|
|
10301
11357
|
await ctx.deleteSlot(collectionName, recordId2, slot.name);
|
|
10302
11358
|
await writeAuditEntry(ctx, {
|
|
@@ -10321,9 +11377,32 @@ async function runCompaction(ctx, options = {}) {
|
|
|
10321
11377
|
records,
|
|
10322
11378
|
collections: collectionsWithPolicy,
|
|
10323
11379
|
auditEntries,
|
|
11380
|
+
held,
|
|
10324
11381
|
byCollection
|
|
10325
11382
|
};
|
|
10326
11383
|
}
|
|
11384
|
+
function isHeld2(policy, record, now) {
|
|
11385
|
+
if (policy.legalHold) {
|
|
11386
|
+
try {
|
|
11387
|
+
if (policy.legalHold(record)) return true;
|
|
11388
|
+
} catch {
|
|
11389
|
+
return true;
|
|
11390
|
+
}
|
|
11391
|
+
}
|
|
11392
|
+
if (policy.retainUntil) {
|
|
11393
|
+
try {
|
|
11394
|
+
const until = policy.retainUntil(record);
|
|
11395
|
+
if (until !== null && until !== void 0) {
|
|
11396
|
+
const t = until instanceof Date ? until.getTime() : typeof until === "number" ? until : Date.parse(String(until));
|
|
11397
|
+
if (!Number.isFinite(t)) return true;
|
|
11398
|
+
if (t > now.getTime()) return true;
|
|
11399
|
+
}
|
|
11400
|
+
} catch {
|
|
11401
|
+
return true;
|
|
11402
|
+
}
|
|
11403
|
+
}
|
|
11404
|
+
return false;
|
|
11405
|
+
}
|
|
10327
11406
|
function evaluatePolicy(policy, record, slot, now) {
|
|
10328
11407
|
let ttlTriggered = false;
|
|
10329
11408
|
let predicateTriggered = false;
|
|
@@ -10762,8 +11841,8 @@ async function derivePersistedSchema(validator) {
|
|
|
10762
11841
|
if (kind === "Zod") {
|
|
10763
11842
|
const convert = await loadZodConverter();
|
|
10764
11843
|
const jsonSchema = convert(validator);
|
|
10765
|
-
const
|
|
10766
|
-
const hash = await sha256Hex2(new TextEncoder().encode(
|
|
11844
|
+
const canonical2 = canonicalize(jsonSchema);
|
|
11845
|
+
const hash = await sha256Hex2(new TextEncoder().encode(canonical2));
|
|
10767
11846
|
return { _noydb_schema: 1, kind, jsonSchema, hash, derivedAt };
|
|
10768
11847
|
}
|
|
10769
11848
|
return {
|
|
@@ -11803,12 +12882,12 @@ var init_read_only_facade = __esm({
|
|
|
11803
12882
|
|
|
11804
12883
|
// src/derivations/strategy-hash.ts
|
|
11805
12884
|
async function computeStrategyHash(source, outputKeys, derive) {
|
|
11806
|
-
const
|
|
12885
|
+
const canonical2 = JSON.stringify({
|
|
11807
12886
|
source,
|
|
11808
12887
|
outputs: [...outputKeys].sort(),
|
|
11809
12888
|
derive: derive.toString()
|
|
11810
12889
|
});
|
|
11811
|
-
const bytes = new TextEncoder().encode(
|
|
12890
|
+
const bytes = new TextEncoder().encode(canonical2);
|
|
11812
12891
|
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
11813
12892
|
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
11814
12893
|
}
|
|
@@ -12081,6 +13160,9 @@ var init_vault = __esm({
|
|
|
12081
13160
|
init_keyring();
|
|
12082
13161
|
init_errors();
|
|
12083
13162
|
init_errors();
|
|
13163
|
+
init_archive();
|
|
13164
|
+
init_sequence();
|
|
13165
|
+
init_numbering();
|
|
12084
13166
|
init_constants();
|
|
12085
13167
|
init_entry();
|
|
12086
13168
|
init_strategy3();
|
|
@@ -12139,6 +13221,10 @@ var init_vault = __esm({
|
|
|
12139
13221
|
* call throws with a pointer at `@noy-db/hub/blobs`.
|
|
12140
13222
|
*/
|
|
12141
13223
|
blobStrategy;
|
|
13224
|
+
/** Cold-storage archival strategy (the archive target store). */
|
|
13225
|
+
archiveStrategy;
|
|
13226
|
+
/** Per-collection record archival policies. Indexed by collection name. */
|
|
13227
|
+
archiveRegistry = /* @__PURE__ */ new Map();
|
|
12142
13228
|
indexStrategy;
|
|
12143
13229
|
aggregateStrategy;
|
|
12144
13230
|
crdtStrategy;
|
|
@@ -12242,6 +13328,12 @@ var init_vault = __esm({
|
|
|
12242
13328
|
* docstring.
|
|
12243
13329
|
*/
|
|
12244
13330
|
ledgerStore = null;
|
|
13331
|
+
/** Lazily-built atomic-sequence store. See {@link sequence}. */
|
|
13332
|
+
sequenceStore = null;
|
|
13333
|
+
/** Lazily-built deferred-numbering engine. See {@link runNumberingPass}. */
|
|
13334
|
+
deferredNumbering = null;
|
|
13335
|
+
/** Registered deferred-numbering series, keyed by series name. */
|
|
13336
|
+
numberingConfigs;
|
|
12245
13337
|
/**
|
|
12246
13338
|
* Background writes for persisted-schema envelopes (#schema-dump v0
|
|
12247
13339
|
* slice 1). One promise per `collection({ persistJsonSchema: true })`
|
|
@@ -12328,6 +13420,7 @@ var init_vault = __esm({
|
|
|
12328
13420
|
constructor(opts) {
|
|
12329
13421
|
this.adapter = opts.adapter;
|
|
12330
13422
|
this.name = opts.name;
|
|
13423
|
+
this.numberingConfigs = new Map((opts.numberingConfigs ?? []).map((c) => [c.series, c]));
|
|
12331
13424
|
this.noydb = opts.noydb;
|
|
12332
13425
|
this.keyring = opts.keyring;
|
|
12333
13426
|
this.encrypted = opts.encrypted;
|
|
@@ -12343,6 +13436,7 @@ var init_vault = __esm({
|
|
|
12343
13436
|
this.onRegisterConflictResolver = opts.onRegisterConflictResolver;
|
|
12344
13437
|
this.syncAdapter = opts.syncAdapter;
|
|
12345
13438
|
this.blobStrategy = opts.blobStrategy;
|
|
13439
|
+
this.archiveStrategy = opts.archiveStrategy;
|
|
12346
13440
|
this.indexStrategy = opts.indexStrategy;
|
|
12347
13441
|
this.aggregateStrategy = opts.aggregateStrategy;
|
|
12348
13442
|
this.crdtStrategy = opts.crdtStrategy;
|
|
@@ -12405,8 +13499,9 @@ var init_vault = __esm({
|
|
|
12405
13499
|
*. `put()` validates keys against the declared set; reads
|
|
12406
13500
|
* with `{ locale }` add `<field>Label` virtual fields.
|
|
12407
13501
|
*
|
|
12408
|
-
* Throws `ReservedCollectionNameError` for names starting with `_dict_
|
|
12409
|
-
* Use `vault.dictionary(name)`
|
|
13502
|
+
* Throws `ReservedCollectionNameError` for names starting with `_dict_` or
|
|
13503
|
+
* equal to `_sequences`. Use `vault.dictionary(name)` for dict collections
|
|
13504
|
+
* and `vault.sequence(name)` for sequence counters.
|
|
12410
13505
|
*
|
|
12411
13506
|
* Lazy mode + indexes is rejected at construction time — see the
|
|
12412
13507
|
* Collection constructor for the rationale.
|
|
@@ -12425,7 +13520,16 @@ var init_vault = __esm({
|
|
|
12425
13520
|
if (isDictCollectionName(collectionName)) {
|
|
12426
13521
|
throw new ReservedCollectionNameError(collectionName);
|
|
12427
13522
|
}
|
|
13523
|
+
if (collectionName === SEQUENCE_COLLECTION) {
|
|
13524
|
+
throw new ReservedCollectionNameError(collectionName);
|
|
13525
|
+
}
|
|
12428
13526
|
let coll = this.collectionCache.get(collectionName);
|
|
13527
|
+
if (coll && options?.moneyFields) {
|
|
13528
|
+
coll._applyMoneyFields(options.moneyFields);
|
|
13529
|
+
}
|
|
13530
|
+
if (coll && options?.computed) {
|
|
13531
|
+
coll._applyComputed(options.computed);
|
|
13532
|
+
}
|
|
12429
13533
|
if (!coll) {
|
|
12430
13534
|
if (options?.refs) {
|
|
12431
13535
|
this.refRegistry.register(collectionName, options.refs);
|
|
@@ -12436,6 +13540,9 @@ var init_vault = __esm({
|
|
|
12436
13540
|
if (options?.blobFields) {
|
|
12437
13541
|
this.blobFieldsRegistry.set(collectionName, options.blobFields);
|
|
12438
13542
|
}
|
|
13543
|
+
if (options?.archive) {
|
|
13544
|
+
this.archiveRegistry.set(collectionName, options.archive);
|
|
13545
|
+
}
|
|
12439
13546
|
if (options?.attestation !== void 0) {
|
|
12440
13547
|
this.attestationRegistry.set(collectionName, options.attestation);
|
|
12441
13548
|
}
|
|
@@ -12551,6 +13658,8 @@ var init_vault = __esm({
|
|
|
12551
13658
|
collOpts.onCrossTierAccess = (event) => this.emitCrossTier(event);
|
|
12552
13659
|
if (this.syncAdapter !== void 0) collOpts.syncAdapter = this.syncAdapter;
|
|
12553
13660
|
if (options?.i18nFields !== void 0) collOpts.i18nFields = options.i18nFields;
|
|
13661
|
+
if (options?.moneyFields !== void 0) collOpts.moneyFields = options.moneyFields;
|
|
13662
|
+
if (options?.computed !== void 0) collOpts.computed = options.computed;
|
|
12554
13663
|
if (options?.dictKeyFields !== void 0) {
|
|
12555
13664
|
collOpts.dictLabelResolver = async (dictName, key, locale, fallback) => {
|
|
12556
13665
|
const handle = this.dictionary(dictName);
|
|
@@ -12970,6 +14079,77 @@ var init_vault = __esm({
|
|
|
12970
14079
|
* await vault.compact({ maxEvictions: 1000 }) // cap batch
|
|
12971
14080
|
* ```
|
|
12972
14081
|
*/
|
|
14082
|
+
/**
|
|
14083
|
+
* Atomic, gap-free numbering. `vault.sequence('invoice-2026').next()`
|
|
14084
|
+
* returns 1, 2, 3, … with no gaps or duplicates under concurrency, via
|
|
14085
|
+
* an optimistic-CAS counter at `_sequences/<name>`. Each name is an
|
|
14086
|
+
* independent sequence.
|
|
14087
|
+
*
|
|
14088
|
+
* **Online-only:** `next()` throws `SequenceOfflineError` unless the
|
|
14089
|
+
* store advertises `capabilities.casAtomic` — gap-free numbering cannot
|
|
14090
|
+
* be serialized by an offline / non-CAS writer.
|
|
14091
|
+
*
|
|
14092
|
+
* ```ts
|
|
14093
|
+
* const n = await vault.sequence('invoice-2026').next() // 1, then 2, …
|
|
14094
|
+
* const cur = await vault.sequence('invoice-2026').peek() // current value, no allocation
|
|
14095
|
+
* ```
|
|
14096
|
+
*/
|
|
14097
|
+
sequence(name) {
|
|
14098
|
+
if (this.numberingConfigs.has(name)) {
|
|
14099
|
+
const eng = this.deferred();
|
|
14100
|
+
return {
|
|
14101
|
+
next: async (opts) => {
|
|
14102
|
+
if (!opts?.for) {
|
|
14103
|
+
throw new ValidationError(`sequence("${name}") is a deferred-numbering series; call next({ for: recordId }).`);
|
|
14104
|
+
}
|
|
14105
|
+
return (await eng.enqueue(name, opts.for)).assigned;
|
|
14106
|
+
},
|
|
14107
|
+
peek: () => eng.peek(name)
|
|
14108
|
+
};
|
|
14109
|
+
}
|
|
14110
|
+
if (!this.sequenceStore) {
|
|
14111
|
+
this.sequenceStore = new SequenceStore({
|
|
14112
|
+
adapter: this.adapter,
|
|
14113
|
+
vault: this.name,
|
|
14114
|
+
encrypted: this.encrypted,
|
|
14115
|
+
getDEK: this.getDEK,
|
|
14116
|
+
actor: this.keyring.userId
|
|
14117
|
+
});
|
|
14118
|
+
}
|
|
14119
|
+
return this.sequenceStore.handle(name);
|
|
14120
|
+
}
|
|
14121
|
+
/** @internal — lazily build the deferred-numbering engine with a cache-coherent stamp. */
|
|
14122
|
+
deferred() {
|
|
14123
|
+
if (!this.deferredNumbering) {
|
|
14124
|
+
this.deferredNumbering = new DeferredNumberingStore({
|
|
14125
|
+
adapter: this.adapter,
|
|
14126
|
+
vault: this.name,
|
|
14127
|
+
encrypted: this.encrypted,
|
|
14128
|
+
getDEK: this.getDEK,
|
|
14129
|
+
actor: this.keyring.userId,
|
|
14130
|
+
configs: this.numberingConfigs,
|
|
14131
|
+
// Stamp THROUGH the Collection layer so cache/indexes/MVs stay coherent —
|
|
14132
|
+
// `this.collection(name)` returns the shared cached instance, so a
|
|
14133
|
+
// subsequent user `collection.get(id)` sees the assigned serial.
|
|
14134
|
+
stamp: async (collection, recordId2, field, serial) => {
|
|
14135
|
+
const coll = this.collection(collection);
|
|
14136
|
+
const rec = await coll.get(recordId2);
|
|
14137
|
+
if (!rec) return false;
|
|
14138
|
+
await coll.put(recordId2, { ...rec, [field]: serial });
|
|
14139
|
+
return true;
|
|
14140
|
+
}
|
|
14141
|
+
});
|
|
14142
|
+
}
|
|
14143
|
+
return this.deferredNumbering;
|
|
14144
|
+
}
|
|
14145
|
+
/**
|
|
14146
|
+
* Run a deferred-numbering pass for `series`: assign gap-free serials to all
|
|
14147
|
+
* records whose store-commit-time interval has settled, in store-time order.
|
|
14148
|
+
* Returns the assignments made. See {@link sequence} / `withDeferredNumbering`.
|
|
14149
|
+
*/
|
|
14150
|
+
async runNumberingPass(series) {
|
|
14151
|
+
return this.deferred().runPass(series);
|
|
14152
|
+
}
|
|
12973
14153
|
async compact(options = {}) {
|
|
12974
14154
|
return runCompaction({
|
|
12975
14155
|
adapter: this.adapter,
|
|
@@ -12994,6 +14174,48 @@ var init_vault = __esm({
|
|
|
12994
14174
|
}
|
|
12995
14175
|
}, options);
|
|
12996
14176
|
}
|
|
14177
|
+
/**
|
|
14178
|
+
* Sweep records eligible by their collection's `archive` policy into the
|
|
14179
|
+
* cold archive store. Relocation is envelope-level (no re-encryption) and
|
|
14180
|
+
* bypasses guards + materialized-view dispatch, so issued/immutable
|
|
14181
|
+
* records over a sealed period can be archived without recomputing
|
|
14182
|
+
* finalized aggregates. A `legalHold` predicate blocks archival.
|
|
14183
|
+
* Requires `archiveStrategy: withArchive({ store })` in `createNoydb`.
|
|
14184
|
+
*/
|
|
14185
|
+
async archive(options = {}) {
|
|
14186
|
+
return runArchive(this._archiveContext(), options);
|
|
14187
|
+
}
|
|
14188
|
+
/** Relocate one archived record back to the primary store. Returns false if it was not archived. */
|
|
14189
|
+
async restore(collection, id) {
|
|
14190
|
+
return runRestore(this._archiveContext(), collection, id);
|
|
14191
|
+
}
|
|
14192
|
+
/** List archived record ids for a collection (or all collections with an archive policy). */
|
|
14193
|
+
async listArchived(collection) {
|
|
14194
|
+
return runListArchived(this._archiveContext(), collection);
|
|
14195
|
+
}
|
|
14196
|
+
_archiveContext() {
|
|
14197
|
+
const strategy = this.archiveStrategy;
|
|
14198
|
+
if (!strategy) {
|
|
14199
|
+
throw new Error(
|
|
14200
|
+
"vault.archive/restore/listArchived require `archiveStrategy: withArchive({ store })` in createNoydb"
|
|
14201
|
+
);
|
|
14202
|
+
}
|
|
14203
|
+
const archiveStore = strategy.store;
|
|
14204
|
+
return {
|
|
14205
|
+
vaultId: this.name,
|
|
14206
|
+
archiveStore,
|
|
14207
|
+
collectionsWithPolicy: () => [...this.archiveRegistry.keys()],
|
|
14208
|
+
getPolicy: (c) => this.archiveRegistry.get(c) ?? null,
|
|
14209
|
+
listRecordIds: (c) => this.adapter.list(this.name, c),
|
|
14210
|
+
getRecord: async (c, id) => await this.collection(c).get(id, { locale: "raw" }),
|
|
14211
|
+
getEnvelope: (c, id) => this.adapter.get(this.name, c, id),
|
|
14212
|
+
removeFromPrimary: (c, id) => this.collection(c)._internalDelete(id),
|
|
14213
|
+
restoreToPrimary: async (c, id, env) => {
|
|
14214
|
+
await this.adapter.put(this.name, c, id, env);
|
|
14215
|
+
await this.collection(c)._invalidateCacheEntry(id);
|
|
14216
|
+
}
|
|
14217
|
+
};
|
|
14218
|
+
}
|
|
12997
14219
|
exportBlobs(options = {}) {
|
|
12998
14220
|
this.assertCanExport("plaintext", "blob");
|
|
12999
14221
|
return createExportBlobsHandle(
|
|
@@ -14255,7 +15477,7 @@ var init_vault = __esm({
|
|
|
14255
15477
|
}
|
|
14256
15478
|
}
|
|
14257
15479
|
const internalSnapshot = {};
|
|
14258
|
-
for (const internalName of [LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION, SCHEMAS_COLLECTION]) {
|
|
15480
|
+
for (const internalName of [LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION, SCHEMAS_COLLECTION, SEQUENCE_COLLECTION]) {
|
|
14259
15481
|
const ids = await this.adapter.list(this.name, internalName);
|
|
14260
15482
|
if (ids.length === 0) continue;
|
|
14261
15483
|
const records = {};
|
|
@@ -15870,7 +17092,7 @@ function deny(gate, reason, required) {
|
|
|
15870
17092
|
return new PolicyDeniedError(gate, reason, required);
|
|
15871
17093
|
}
|
|
15872
17094
|
var DEFAULT_FRESHNESS_MS;
|
|
15873
|
-
var
|
|
17095
|
+
var init_engine2 = __esm({
|
|
15874
17096
|
"src/policy/engine.ts"() {
|
|
15875
17097
|
"use strict";
|
|
15876
17098
|
init_errors2();
|
|
@@ -15883,7 +17105,7 @@ var init_policy3 = __esm({
|
|
|
15883
17105
|
"src/policy/index.ts"() {
|
|
15884
17106
|
"use strict";
|
|
15885
17107
|
init_presets();
|
|
15886
|
-
|
|
17108
|
+
init_engine2();
|
|
15887
17109
|
init_storage5();
|
|
15888
17110
|
}
|
|
15889
17111
|
});
|
|
@@ -15927,14 +17149,21 @@ var init_executor3 = __esm({
|
|
|
15927
17149
|
* Compare existing vs incoming for each `frozenFields.fields` entry
|
|
15928
17150
|
* when `frozenFields.when(existing)` is true. Throws
|
|
15929
17151
|
* `FieldFrozenError` listing every changed frozen field.
|
|
17152
|
+
*
|
|
17153
|
+
* @param skipFields — field names that are schema-owned computed fields.
|
|
17154
|
+
* These are excluded from the comparison because `incoming` carries the
|
|
17155
|
+
* raw user input (computed fields not yet evaluated), so comparing
|
|
17156
|
+
* `existing[field]` vs `incoming[field]` would always look like a
|
|
17157
|
+
* change even when the computed result is unchanged.
|
|
15930
17158
|
*/
|
|
15931
|
-
async checkFrozenFields(guard, id, existing, incoming) {
|
|
17159
|
+
async checkFrozenFields(guard, id, existing, incoming, skipFields) {
|
|
15932
17160
|
const ff = guard.frozenFields;
|
|
15933
17161
|
if (!ff) return;
|
|
15934
17162
|
if (existing === null) return;
|
|
15935
17163
|
if (!ff.when(existing)) return;
|
|
15936
17164
|
const changed = [];
|
|
15937
17165
|
for (const f of ff.fields) {
|
|
17166
|
+
if (skipFields?.has(String(f))) continue;
|
|
15938
17167
|
if (existing[f] !== incoming[f]) {
|
|
15939
17168
|
if (!deepEqual(existing[f], incoming[f])) changed.push(String(f));
|
|
15940
17169
|
}
|
|
@@ -15963,6 +17192,669 @@ var init_executor3 = __esm({
|
|
|
15963
17192
|
}
|
|
15964
17193
|
});
|
|
15965
17194
|
|
|
17195
|
+
// src/federation/classify-skip.ts
|
|
17196
|
+
function classifyShardSkip(err) {
|
|
17197
|
+
return err instanceof NoAccessError ? "no-grant" : "error";
|
|
17198
|
+
}
|
|
17199
|
+
var init_classify_skip = __esm({
|
|
17200
|
+
"src/federation/classify-skip.ts"() {
|
|
17201
|
+
"use strict";
|
|
17202
|
+
init_errors();
|
|
17203
|
+
}
|
|
17204
|
+
});
|
|
17205
|
+
|
|
17206
|
+
// src/federation/cross-vault-live.ts
|
|
17207
|
+
var CrossVaultLive;
|
|
17208
|
+
var init_cross_vault_live = __esm({
|
|
17209
|
+
"src/federation/cross-vault-live.ts"() {
|
|
17210
|
+
"use strict";
|
|
17211
|
+
CrossVaultLive = class {
|
|
17212
|
+
snapshot;
|
|
17213
|
+
error = null;
|
|
17214
|
+
ready;
|
|
17215
|
+
subs = /* @__PURE__ */ new Set();
|
|
17216
|
+
unsubChange;
|
|
17217
|
+
opts;
|
|
17218
|
+
stopped = false;
|
|
17219
|
+
computing = false;
|
|
17220
|
+
dirty = false;
|
|
17221
|
+
scheduled = false;
|
|
17222
|
+
timer = null;
|
|
17223
|
+
resolveReady;
|
|
17224
|
+
settledOnce = false;
|
|
17225
|
+
constructor(opts) {
|
|
17226
|
+
this.opts = opts;
|
|
17227
|
+
this.snapshot = opts.initialSnapshot;
|
|
17228
|
+
this.ready = new Promise((res) => {
|
|
17229
|
+
this.resolveReady = res;
|
|
17230
|
+
});
|
|
17231
|
+
this.unsubChange = opts.subscribeToChanges((e) => {
|
|
17232
|
+
if (this.stopped || !opts.isRelevant(e)) return;
|
|
17233
|
+
this.schedule();
|
|
17234
|
+
});
|
|
17235
|
+
this.schedule();
|
|
17236
|
+
}
|
|
17237
|
+
subscribe(cb) {
|
|
17238
|
+
if (this.stopped) return () => {
|
|
17239
|
+
};
|
|
17240
|
+
this.subs.add(cb);
|
|
17241
|
+
return () => this.subs.delete(cb);
|
|
17242
|
+
}
|
|
17243
|
+
stop() {
|
|
17244
|
+
if (this.stopped) return;
|
|
17245
|
+
this.stopped = true;
|
|
17246
|
+
this.unsubChange();
|
|
17247
|
+
if (this.timer !== null) clearTimeout(this.timer);
|
|
17248
|
+
this.subs.clear();
|
|
17249
|
+
if (!this.settledOnce) this.resolveReady();
|
|
17250
|
+
}
|
|
17251
|
+
schedule() {
|
|
17252
|
+
if (this.stopped) return;
|
|
17253
|
+
if (this.computing) {
|
|
17254
|
+
this.dirty = true;
|
|
17255
|
+
return;
|
|
17256
|
+
}
|
|
17257
|
+
if (this.scheduled) return;
|
|
17258
|
+
this.scheduled = true;
|
|
17259
|
+
const run = () => {
|
|
17260
|
+
this.scheduled = false;
|
|
17261
|
+
void this.runCompute();
|
|
17262
|
+
};
|
|
17263
|
+
const ms = this.opts.debounceMs ?? 0;
|
|
17264
|
+
if (ms > 0) this.timer = setTimeout(run, ms);
|
|
17265
|
+
else queueMicrotask(run);
|
|
17266
|
+
}
|
|
17267
|
+
async runCompute() {
|
|
17268
|
+
if (this.stopped) return;
|
|
17269
|
+
this.computing = true;
|
|
17270
|
+
this.dirty = false;
|
|
17271
|
+
try {
|
|
17272
|
+
const next = await this.opts.compute();
|
|
17273
|
+
if (this.stopped) return;
|
|
17274
|
+
this.snapshot = next;
|
|
17275
|
+
this.error = null;
|
|
17276
|
+
} catch (err) {
|
|
17277
|
+
if (this.stopped) return;
|
|
17278
|
+
this.error = err instanceof Error ? err : new Error(String(err));
|
|
17279
|
+
} finally {
|
|
17280
|
+
this.computing = false;
|
|
17281
|
+
if (!this.stopped) {
|
|
17282
|
+
if (!this.settledOnce) {
|
|
17283
|
+
this.settledOnce = true;
|
|
17284
|
+
this.resolveReady();
|
|
17285
|
+
}
|
|
17286
|
+
for (const cb of this.subs) cb();
|
|
17287
|
+
if (this.dirty) this.schedule();
|
|
17288
|
+
}
|
|
17289
|
+
}
|
|
17290
|
+
}
|
|
17291
|
+
};
|
|
17292
|
+
}
|
|
17293
|
+
});
|
|
17294
|
+
|
|
17295
|
+
// src/federation/aggregate-across.ts
|
|
17296
|
+
var CrossVaultAggregation, CrossVaultGroupedAggregation;
|
|
17297
|
+
var init_aggregate_across = __esm({
|
|
17298
|
+
"src/federation/aggregate-across.ts"() {
|
|
17299
|
+
"use strict";
|
|
17300
|
+
init_aggregation();
|
|
17301
|
+
init_groupby();
|
|
17302
|
+
init_cross_vault_live();
|
|
17303
|
+
CrossVaultAggregation = class {
|
|
17304
|
+
constructor(src, spec, bind) {
|
|
17305
|
+
this.src = src;
|
|
17306
|
+
this.spec = spec;
|
|
17307
|
+
this.bind = bind;
|
|
17308
|
+
}
|
|
17309
|
+
src;
|
|
17310
|
+
spec;
|
|
17311
|
+
bind;
|
|
17312
|
+
async run(options = {}) {
|
|
17313
|
+
const { records, skippedVaults } = await this.src.fanoutRecords(options);
|
|
17314
|
+
return { result: reduceRecords(records, this.spec), skippedVaults };
|
|
17315
|
+
}
|
|
17316
|
+
live(options = {}) {
|
|
17317
|
+
if (!this.bind) throw new Error("CrossVaultAggregation: live() requires a LiveBinding \u2014 use ShardedQuery.aggregate()");
|
|
17318
|
+
const spec = this.spec;
|
|
17319
|
+
const src = this.src;
|
|
17320
|
+
const core = new CrossVaultLive({
|
|
17321
|
+
subscribeToChanges: this.bind.subscribeToChanges,
|
|
17322
|
+
isRelevant: this.bind.isRelevant,
|
|
17323
|
+
compute: async () => {
|
|
17324
|
+
const { records, skippedVaults } = await src.fanoutRecords(options);
|
|
17325
|
+
return { value: reduceRecords(records, spec), skipped: skippedVaults };
|
|
17326
|
+
},
|
|
17327
|
+
initialSnapshot: { value: void 0, skipped: [] },
|
|
17328
|
+
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
17329
|
+
});
|
|
17330
|
+
return {
|
|
17331
|
+
get value() {
|
|
17332
|
+
return core.snapshot.value;
|
|
17333
|
+
},
|
|
17334
|
+
get skippedVaults() {
|
|
17335
|
+
return core.snapshot.skipped;
|
|
17336
|
+
},
|
|
17337
|
+
get error() {
|
|
17338
|
+
return core.error;
|
|
17339
|
+
},
|
|
17340
|
+
ready: core.ready,
|
|
17341
|
+
subscribe: (cb) => core.subscribe(cb),
|
|
17342
|
+
stop: () => core.stop()
|
|
17343
|
+
};
|
|
17344
|
+
}
|
|
17345
|
+
};
|
|
17346
|
+
CrossVaultGroupedAggregation = class {
|
|
17347
|
+
constructor(src, field, spec, bind) {
|
|
17348
|
+
this.src = src;
|
|
17349
|
+
this.field = field;
|
|
17350
|
+
this.spec = spec;
|
|
17351
|
+
this.bind = bind;
|
|
17352
|
+
}
|
|
17353
|
+
src;
|
|
17354
|
+
field;
|
|
17355
|
+
spec;
|
|
17356
|
+
bind;
|
|
17357
|
+
async run(options = {}) {
|
|
17358
|
+
const { records, skippedVaults } = await this.src.fanoutRecords(options);
|
|
17359
|
+
return {
|
|
17360
|
+
results: groupAndReduce(records, this.field, this.spec),
|
|
17361
|
+
skippedVaults
|
|
17362
|
+
};
|
|
17363
|
+
}
|
|
17364
|
+
live(options = {}) {
|
|
17365
|
+
if (!this.bind) throw new Error("CrossVaultGroupedAggregation: live() requires a LiveBinding \u2014 use ShardedQuery.groupBy().aggregate()");
|
|
17366
|
+
const field = this.field;
|
|
17367
|
+
const spec = this.spec;
|
|
17368
|
+
const src = this.src;
|
|
17369
|
+
const core = new CrossVaultLive({
|
|
17370
|
+
subscribeToChanges: this.bind.subscribeToChanges,
|
|
17371
|
+
isRelevant: this.bind.isRelevant,
|
|
17372
|
+
compute: async () => {
|
|
17373
|
+
const { records, skippedVaults } = await src.fanoutRecords(options);
|
|
17374
|
+
return {
|
|
17375
|
+
records: groupAndReduce(records, field, spec),
|
|
17376
|
+
skipped: skippedVaults
|
|
17377
|
+
};
|
|
17378
|
+
},
|
|
17379
|
+
initialSnapshot: { records: [], skipped: [] },
|
|
17380
|
+
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
17381
|
+
});
|
|
17382
|
+
return {
|
|
17383
|
+
get value() {
|
|
17384
|
+
return core.snapshot.records;
|
|
17385
|
+
},
|
|
17386
|
+
get skippedVaults() {
|
|
17387
|
+
return core.snapshot.skipped;
|
|
17388
|
+
},
|
|
17389
|
+
get error() {
|
|
17390
|
+
return core.error;
|
|
17391
|
+
},
|
|
17392
|
+
ready: core.ready,
|
|
17393
|
+
subscribe: (cb) => core.subscribe(cb),
|
|
17394
|
+
stop: () => core.stop()
|
|
17395
|
+
};
|
|
17396
|
+
}
|
|
17397
|
+
};
|
|
17398
|
+
}
|
|
17399
|
+
});
|
|
17400
|
+
|
|
17401
|
+
// src/federation/vault-group.ts
|
|
17402
|
+
var vault_group_exports = {};
|
|
17403
|
+
__export(vault_group_exports, {
|
|
17404
|
+
ShardedCollection: () => ShardedCollection,
|
|
17405
|
+
ShardedGroupedQuery: () => ShardedGroupedQuery,
|
|
17406
|
+
ShardedQuery: () => ShardedQuery,
|
|
17407
|
+
VaultGroup: () => VaultGroup
|
|
17408
|
+
});
|
|
17409
|
+
function assertSafePartitionKey(partitionKey) {
|
|
17410
|
+
if (partitionKey.length === 0) {
|
|
17411
|
+
throw new ValidationError("partitionKey must be a non-empty string");
|
|
17412
|
+
}
|
|
17413
|
+
if (partitionKey === STATE_VAULT_NAME) {
|
|
17414
|
+
throw new ReservedVaultNameError(partitionKey);
|
|
17415
|
+
}
|
|
17416
|
+
if (!SAFE_PARTITION_KEY.test(partitionKey)) {
|
|
17417
|
+
throw new ValidationError(
|
|
17418
|
+
`partitionKey "${partitionKey}" contains characters outside [A-Za-z0-9._-]. Map your records to a store-safe key in sharding.keyOf.`
|
|
17419
|
+
);
|
|
17420
|
+
}
|
|
17421
|
+
if (partitionKey.includes(SHARD_SEPARATOR)) {
|
|
17422
|
+
throw new ValidationError(
|
|
17423
|
+
`partitionKey "${partitionKey}" must not contain "--" \u2014 it is reserved as the shard vault-id separator and would risk shard-id collisions.`
|
|
17424
|
+
);
|
|
17425
|
+
}
|
|
17426
|
+
}
|
|
17427
|
+
var SHARD_SEPARATOR, SAFE_PARTITION_KEY, VaultGroup, ShardedCollection, ShardedQuery, ShardedGroupedQuery;
|
|
17428
|
+
var init_vault_group = __esm({
|
|
17429
|
+
"src/federation/vault-group.ts"() {
|
|
17430
|
+
"use strict";
|
|
17431
|
+
init_errors();
|
|
17432
|
+
init_constants2();
|
|
17433
|
+
init_classify_skip();
|
|
17434
|
+
init_cross_vault_live();
|
|
17435
|
+
init_aggregate_across();
|
|
17436
|
+
SHARD_SEPARATOR = "--";
|
|
17437
|
+
SAFE_PARTITION_KEY = /^[A-Za-z0-9._-]+$/;
|
|
17438
|
+
VaultGroup = class {
|
|
17439
|
+
constructor(db, name, registry, sharding, template) {
|
|
17440
|
+
this.db = db;
|
|
17441
|
+
this.name = name;
|
|
17442
|
+
this.registry = registry;
|
|
17443
|
+
this.sharding = sharding;
|
|
17444
|
+
this.template = template;
|
|
17445
|
+
if (name.includes(SHARD_SEPARATOR)) {
|
|
17446
|
+
throw new ValidationError(
|
|
17447
|
+
`VaultGroup name "${name}" must not contain "--" (reserved shard vault-id separator).`
|
|
17448
|
+
);
|
|
17449
|
+
}
|
|
17450
|
+
}
|
|
17451
|
+
db;
|
|
17452
|
+
name;
|
|
17453
|
+
registry;
|
|
17454
|
+
sharding;
|
|
17455
|
+
template;
|
|
17456
|
+
/** @internal — set when the group is managed (no explicit registry). */
|
|
17457
|
+
stateVault;
|
|
17458
|
+
/** @internal */
|
|
17459
|
+
_attachStateVault(sv) {
|
|
17460
|
+
this.stateVault = sv;
|
|
17461
|
+
}
|
|
17462
|
+
/** Deterministic vault name for a partition key, namespaced by the group. */
|
|
17463
|
+
shardVaultId(partitionKey) {
|
|
17464
|
+
assertSafePartitionKey(partitionKey);
|
|
17465
|
+
return `${this.name}${SHARD_SEPARATOR}${partitionKey}`;
|
|
17466
|
+
}
|
|
17467
|
+
/**
|
|
17468
|
+
* @internal — group-qualified registry record key (avoids cross-group key
|
|
17469
|
+
* collisions). Identical to the shard vault id by design — the registry row
|
|
17470
|
+
* for a shard is keyed by that shard's vault id — so it delegates to
|
|
17471
|
+
* `shardVaultId`, reusing its partition-key validation.
|
|
17472
|
+
*/
|
|
17473
|
+
registryId(partitionKey) {
|
|
17474
|
+
return this.shardVaultId(partitionKey);
|
|
17475
|
+
}
|
|
17476
|
+
/**
|
|
17477
|
+
* Registry rows for THIS group (hydrates the registry collection first).
|
|
17478
|
+
* The registry may be shared across groups (the auto-wired StateManagement
|
|
17479
|
+
* vault holds one `vaultRegistry` for the whole instance), so rows are
|
|
17480
|
+
* filtered by `group` — without this, a group's fan-out reads would leak
|
|
17481
|
+
* across into other groups' shards. Mirrors the `${group}--` scoping that
|
|
17482
|
+
* `liveBinding().isRelevant` already applies to the reactive path.
|
|
17483
|
+
*/
|
|
17484
|
+
async allRows() {
|
|
17485
|
+
await this.registry.list();
|
|
17486
|
+
const rows = this.registry.query().toArray();
|
|
17487
|
+
return rows.filter((r) => r.group === this.name);
|
|
17488
|
+
}
|
|
17489
|
+
/** Open an existing shard and apply the template. */
|
|
17490
|
+
async openShard(partitionKey) {
|
|
17491
|
+
const vault = await this.db.openVault(this.shardVaultId(partitionKey), { create: false });
|
|
17492
|
+
this.template.configure(vault);
|
|
17493
|
+
return vault;
|
|
17494
|
+
}
|
|
17495
|
+
/**
|
|
17496
|
+
* Idempotently provision a shard for `partitionKey`. Returns the
|
|
17497
|
+
* configured vault handle.
|
|
17498
|
+
*
|
|
17499
|
+
* - row + vault present → no-op, return handle
|
|
17500
|
+
* - row present, vault gone → ShardProvisioningError
|
|
17501
|
+
* - row absent (vault present or not) → open-or-create, configure, write row
|
|
17502
|
+
*/
|
|
17503
|
+
async createShard(partitionKey) {
|
|
17504
|
+
const vaultId = this.shardVaultId(partitionKey);
|
|
17505
|
+
const row = await this.registry.get(this.registryId(partitionKey));
|
|
17506
|
+
const provisioned = await this.db._shardVaultProvisioned(vaultId);
|
|
17507
|
+
if (row && !provisioned) throw new ShardProvisioningError(vaultId, partitionKey);
|
|
17508
|
+
if (row && provisioned) return this.openShard(partitionKey);
|
|
17509
|
+
const vault = await this.db.openVault(vaultId);
|
|
17510
|
+
this.template.configure(vault);
|
|
17511
|
+
await this.registry.put(this.registryId(partitionKey), {
|
|
17512
|
+
vaultId,
|
|
17513
|
+
partitionKey,
|
|
17514
|
+
templateName: this.sharding.vaultTemplate,
|
|
17515
|
+
schemaVersion: this.template.version,
|
|
17516
|
+
createdAt: Date.now(),
|
|
17517
|
+
group: this.name
|
|
17518
|
+
});
|
|
17519
|
+
if (this.stateVault) {
|
|
17520
|
+
try {
|
|
17521
|
+
await this.stateVault.appendEvent({
|
|
17522
|
+
type: "shard-created",
|
|
17523
|
+
group: this.name,
|
|
17524
|
+
vaultId,
|
|
17525
|
+
templateName: this.sharding.vaultTemplate,
|
|
17526
|
+
version: this.template.version
|
|
17527
|
+
});
|
|
17528
|
+
} catch {
|
|
17529
|
+
}
|
|
17530
|
+
}
|
|
17531
|
+
return vault;
|
|
17532
|
+
}
|
|
17533
|
+
/**
|
|
17534
|
+
* Drill down to a single shard's full Collection API. Throws if the shard is unknown.
|
|
17535
|
+
* Also throws ShardProvisioningError if the registry row exists but the vault has been deleted
|
|
17536
|
+
* (registry/store divergence).
|
|
17537
|
+
*/
|
|
17538
|
+
async shard(partitionKey) {
|
|
17539
|
+
const vaultId = this.shardVaultId(partitionKey);
|
|
17540
|
+
const row = await this.registry.get(this.registryId(partitionKey));
|
|
17541
|
+
if (!row) throw new UnknownShardError(partitionKey, this.name);
|
|
17542
|
+
const provisioned = await this.db._shardVaultProvisioned(vaultId);
|
|
17543
|
+
if (!provisioned) throw new ShardProvisioningError(vaultId, partitionKey);
|
|
17544
|
+
return this.openShard(partitionKey);
|
|
17545
|
+
}
|
|
17546
|
+
/** A sharded view over one logical collection across all shards. */
|
|
17547
|
+
collection(collectionName) {
|
|
17548
|
+
return new ShardedCollection(this, collectionName);
|
|
17549
|
+
}
|
|
17550
|
+
/** @internal — eligible (openable-candidate) rows + drift/divergence skips. */
|
|
17551
|
+
async resolveEligible(options = {}) {
|
|
17552
|
+
const rows = await this.allRows();
|
|
17553
|
+
const skipped = [];
|
|
17554
|
+
const versionOk = [];
|
|
17555
|
+
for (const row of rows) {
|
|
17556
|
+
if (options.minVersion !== void 0 && row.schemaVersion < options.minVersion) {
|
|
17557
|
+
skipped.push({ vaultId: row.vaultId, reason: "schema-drift" });
|
|
17558
|
+
} else versionOk.push(row);
|
|
17559
|
+
}
|
|
17560
|
+
const provisioned = await Promise.all(versionOk.map((r) => this.db._shardVaultProvisioned(r.vaultId)));
|
|
17561
|
+
const eligible = [];
|
|
17562
|
+
versionOk.forEach((row, i) => {
|
|
17563
|
+
if (provisioned[i]) eligible.push(row);
|
|
17564
|
+
else skipped.push({ vaultId: row.vaultId, reason: "error", error: new ShardProvisioningError(row.vaultId, row.partitionKey) });
|
|
17565
|
+
});
|
|
17566
|
+
return { eligible, skipped };
|
|
17567
|
+
}
|
|
17568
|
+
};
|
|
17569
|
+
ShardedCollection = class {
|
|
17570
|
+
constructor(group, collectionName) {
|
|
17571
|
+
this.group = group;
|
|
17572
|
+
this.collectionName = collectionName;
|
|
17573
|
+
}
|
|
17574
|
+
group;
|
|
17575
|
+
collectionName;
|
|
17576
|
+
/** Route a write to the shard owning `keyOf(record)`. */
|
|
17577
|
+
async put(id, record) {
|
|
17578
|
+
const key = this.group.sharding.keyOf(record);
|
|
17579
|
+
const row = await this.group.registry.get(this.group.registryId(key));
|
|
17580
|
+
let vault;
|
|
17581
|
+
if (!row) {
|
|
17582
|
+
if (this.group.sharding.autoCreate === false) {
|
|
17583
|
+
throw new UnknownShardError(key, this.group.name);
|
|
17584
|
+
}
|
|
17585
|
+
vault = await this.group.createShard(key);
|
|
17586
|
+
} else {
|
|
17587
|
+
vault = await this.group.openShard(key);
|
|
17588
|
+
}
|
|
17589
|
+
await vault.collection(this.collectionName).put(id, record);
|
|
17590
|
+
}
|
|
17591
|
+
/** Begin a cross-shard fan-out query. */
|
|
17592
|
+
query() {
|
|
17593
|
+
return new ShardedQuery(this.group, this.collectionName, []);
|
|
17594
|
+
}
|
|
17595
|
+
};
|
|
17596
|
+
ShardedQuery = class _ShardedQuery {
|
|
17597
|
+
constructor(group, collectionName, clauses) {
|
|
17598
|
+
this.group = group;
|
|
17599
|
+
this.collectionName = collectionName;
|
|
17600
|
+
this.clauses = clauses;
|
|
17601
|
+
}
|
|
17602
|
+
group;
|
|
17603
|
+
collectionName;
|
|
17604
|
+
clauses;
|
|
17605
|
+
where(field, op, value) {
|
|
17606
|
+
return new _ShardedQuery(this.group, this.collectionName, [
|
|
17607
|
+
...this.clauses,
|
|
17608
|
+
{ field, op, value }
|
|
17609
|
+
]);
|
|
17610
|
+
}
|
|
17611
|
+
/** @internal — fan out the where-filtered records across eligible shards. */
|
|
17612
|
+
async fanoutRecords(options = {}) {
|
|
17613
|
+
const { eligible, skipped } = await this.group.resolveEligible(options);
|
|
17614
|
+
const across = await this.group.db.queryAcross(
|
|
17615
|
+
eligible.map((r) => r.vaultId),
|
|
17616
|
+
async (vault) => {
|
|
17617
|
+
this.group.template.configure(vault);
|
|
17618
|
+
const coll = vault.collection(this.collectionName);
|
|
17619
|
+
await coll.list();
|
|
17620
|
+
let q = coll.query();
|
|
17621
|
+
for (const c of this.clauses) q = q.where(c.field, c.op, c.value);
|
|
17622
|
+
return q.toArray();
|
|
17623
|
+
},
|
|
17624
|
+
{ concurrency: options.concurrency ?? 1, create: false }
|
|
17625
|
+
);
|
|
17626
|
+
const results = [];
|
|
17627
|
+
for (const r of across) {
|
|
17628
|
+
if (r.error) skipped.push({ vaultId: r.vault, reason: classifyShardSkip(r.error), error: r.error });
|
|
17629
|
+
else for (const item of r.result) results.push(item);
|
|
17630
|
+
}
|
|
17631
|
+
return { records: results, skippedVaults: skipped };
|
|
17632
|
+
}
|
|
17633
|
+
/** Fan out across eligible shards and merge results. */
|
|
17634
|
+
async toArray(options = {}) {
|
|
17635
|
+
const { records, skippedVaults } = await this.fanoutRecords(options);
|
|
17636
|
+
return { results: records, skippedVaults };
|
|
17637
|
+
}
|
|
17638
|
+
/** @internal — build the change-subscription + relevance binding for this query's group+collection. */
|
|
17639
|
+
liveBinding() {
|
|
17640
|
+
const group = this.group;
|
|
17641
|
+
const collectionName = this.collectionName;
|
|
17642
|
+
return {
|
|
17643
|
+
subscribeToChanges: (h) => {
|
|
17644
|
+
group.db.on("change", h);
|
|
17645
|
+
return () => group.db.off("change", h);
|
|
17646
|
+
},
|
|
17647
|
+
isRelevant: (e) => e.collection === collectionName && e.vault.startsWith(`${group.name}--`)
|
|
17648
|
+
};
|
|
17649
|
+
}
|
|
17650
|
+
/** Returns a reactive cross-shard live query — a facade over CrossVaultLive. */
|
|
17651
|
+
live(options = {}) {
|
|
17652
|
+
const bind = this.liveBinding();
|
|
17653
|
+
const core = new CrossVaultLive({
|
|
17654
|
+
...bind,
|
|
17655
|
+
compute: async () => {
|
|
17656
|
+
const { records, skippedVaults } = await this.fanoutRecords(options);
|
|
17657
|
+
return { records, skipped: skippedVaults };
|
|
17658
|
+
},
|
|
17659
|
+
initialSnapshot: { records: [], skipped: [] },
|
|
17660
|
+
...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
|
|
17661
|
+
});
|
|
17662
|
+
return {
|
|
17663
|
+
get value() {
|
|
17664
|
+
return core.snapshot.records;
|
|
17665
|
+
},
|
|
17666
|
+
get skippedVaults() {
|
|
17667
|
+
return core.snapshot.skipped;
|
|
17668
|
+
},
|
|
17669
|
+
get error() {
|
|
17670
|
+
return core.error;
|
|
17671
|
+
},
|
|
17672
|
+
ready: core.ready,
|
|
17673
|
+
subscribe: (cb) => core.subscribe(cb),
|
|
17674
|
+
stop: () => core.stop()
|
|
17675
|
+
};
|
|
17676
|
+
}
|
|
17677
|
+
/** One-shot distributed aggregate — central reduce over all shard records. */
|
|
17678
|
+
aggregate(spec) {
|
|
17679
|
+
return new CrossVaultAggregation(this, spec, this.liveBinding());
|
|
17680
|
+
}
|
|
17681
|
+
/** Begin a grouped cross-shard aggregate. */
|
|
17682
|
+
groupBy(field) {
|
|
17683
|
+
return new ShardedGroupedQuery(this, field);
|
|
17684
|
+
}
|
|
17685
|
+
};
|
|
17686
|
+
ShardedGroupedQuery = class {
|
|
17687
|
+
constructor(query, field) {
|
|
17688
|
+
this.query = query;
|
|
17689
|
+
this.field = field;
|
|
17690
|
+
}
|
|
17691
|
+
query;
|
|
17692
|
+
field;
|
|
17693
|
+
aggregate(spec) {
|
|
17694
|
+
return new CrossVaultGroupedAggregation(
|
|
17695
|
+
{ fanoutRecords: (o) => this.query.fanoutRecords(o) },
|
|
17696
|
+
this.field,
|
|
17697
|
+
spec,
|
|
17698
|
+
this.query.liveBinding()
|
|
17699
|
+
);
|
|
17700
|
+
}
|
|
17701
|
+
};
|
|
17702
|
+
}
|
|
17703
|
+
});
|
|
17704
|
+
|
|
17705
|
+
// src/federation/schema-manifest.ts
|
|
17706
|
+
function captureBlueprint(configure) {
|
|
17707
|
+
const recorded = [];
|
|
17708
|
+
const collectionStub = new Proxy(
|
|
17709
|
+
{},
|
|
17710
|
+
{
|
|
17711
|
+
get: () => () => collectionStub
|
|
17712
|
+
}
|
|
17713
|
+
);
|
|
17714
|
+
const proxy = new Proxy(
|
|
17715
|
+
{},
|
|
17716
|
+
{
|
|
17717
|
+
get: (_t, prop) => {
|
|
17718
|
+
if (prop === "collection") {
|
|
17719
|
+
return (name, opts) => {
|
|
17720
|
+
recorded.push({
|
|
17721
|
+
name,
|
|
17722
|
+
indexes: opts?.indexes ?? [],
|
|
17723
|
+
persistJsonSchema: !!opts?.persistJsonSchema
|
|
17724
|
+
});
|
|
17725
|
+
return collectionStub;
|
|
17726
|
+
};
|
|
17727
|
+
}
|
|
17728
|
+
return () => proxy;
|
|
17729
|
+
}
|
|
17730
|
+
}
|
|
17731
|
+
);
|
|
17732
|
+
configure(proxy);
|
|
17733
|
+
const sorted = [...recorded].sort((a, b) => a.name.localeCompare(b.name));
|
|
17734
|
+
const indexes = {};
|
|
17735
|
+
const persistJsonSchema = [];
|
|
17736
|
+
for (const c of sorted) {
|
|
17737
|
+
indexes[c.name] = c.indexes;
|
|
17738
|
+
if (c.persistJsonSchema) persistJsonSchema.push(c.name);
|
|
17739
|
+
}
|
|
17740
|
+
return {
|
|
17741
|
+
// `persistJsonSchema` is already name-sorted: it is populated while
|
|
17742
|
+
// iterating `sorted` (collections in name order).
|
|
17743
|
+
collections: sorted.map((c) => c.name),
|
|
17744
|
+
indexes,
|
|
17745
|
+
persistJsonSchema
|
|
17746
|
+
};
|
|
17747
|
+
}
|
|
17748
|
+
function canonical(value) {
|
|
17749
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
17750
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
17751
|
+
const obj = value;
|
|
17752
|
+
const keys = Object.keys(obj).sort();
|
|
17753
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(obj[k])}`).join(",")}}`;
|
|
17754
|
+
}
|
|
17755
|
+
async function fingerprintBlueprint(bp) {
|
|
17756
|
+
return sha256Hex2(new TextEncoder().encode(canonical(bp)));
|
|
17757
|
+
}
|
|
17758
|
+
var init_schema_manifest = __esm({
|
|
17759
|
+
"src/federation/schema-manifest.ts"() {
|
|
17760
|
+
"use strict";
|
|
17761
|
+
init_crypto();
|
|
17762
|
+
}
|
|
17763
|
+
});
|
|
17764
|
+
|
|
17765
|
+
// src/federation/state-vault.ts
|
|
17766
|
+
var state_vault_exports = {};
|
|
17767
|
+
__export(state_vault_exports, {
|
|
17768
|
+
STATE_VAULT_NAME: () => STATE_VAULT_NAME,
|
|
17769
|
+
StateManagementVault: () => StateManagementVault
|
|
17770
|
+
});
|
|
17771
|
+
var REGISTRY, MANIFEST, EVENTS, StateManagementVault;
|
|
17772
|
+
var init_state_vault = __esm({
|
|
17773
|
+
"src/federation/state-vault.ts"() {
|
|
17774
|
+
"use strict";
|
|
17775
|
+
init_schema_manifest();
|
|
17776
|
+
init_constants2();
|
|
17777
|
+
init_ulid();
|
|
17778
|
+
init_constants2();
|
|
17779
|
+
REGISTRY = "vaultRegistry";
|
|
17780
|
+
MANIFEST = "schemaManifest";
|
|
17781
|
+
EVENTS = "deploymentEvents";
|
|
17782
|
+
StateManagementVault = class _StateManagementVault {
|
|
17783
|
+
constructor(registry, schemaManifest, events) {
|
|
17784
|
+
this.registry = registry;
|
|
17785
|
+
this.schemaManifest = schemaManifest;
|
|
17786
|
+
this.#events = events;
|
|
17787
|
+
}
|
|
17788
|
+
registry;
|
|
17789
|
+
schemaManifest;
|
|
17790
|
+
/**
|
|
17791
|
+
* The append-only deployment-events log is kept truly private so the raw
|
|
17792
|
+
* mutable Collection is never surfaced — events may only be written via
|
|
17793
|
+
* `appendEvent` and read via `queryEvents`. (`registry` and
|
|
17794
|
+
* `schemaManifest` are deliberately public: consumers read and write them.)
|
|
17795
|
+
*/
|
|
17796
|
+
#events;
|
|
17797
|
+
/** Idempotently open the reserved state vault and bind the three control-plane collections. */
|
|
17798
|
+
static async open(db) {
|
|
17799
|
+
const vault = await db.openVault(STATE_VAULT_NAME);
|
|
17800
|
+
return new _StateManagementVault(
|
|
17801
|
+
vault.collection(REGISTRY),
|
|
17802
|
+
vault.collection(MANIFEST),
|
|
17803
|
+
vault.collection(EVENTS)
|
|
17804
|
+
);
|
|
17805
|
+
}
|
|
17806
|
+
/** Read-only query over the append-only deployment-events log. */
|
|
17807
|
+
queryEvents() {
|
|
17808
|
+
return this.#events.query();
|
|
17809
|
+
}
|
|
17810
|
+
/**
|
|
17811
|
+
* Append a deployment event with a fresh unique (ULID) id. This is the
|
|
17812
|
+
* only write path to the events log; no update/delete is exposed.
|
|
17813
|
+
* Callers should treat failures as non-fatal — this method does not
|
|
17814
|
+
* swallow errors, so wrap the call site in try/catch where appropriate.
|
|
17815
|
+
*/
|
|
17816
|
+
async appendEvent(event) {
|
|
17817
|
+
const ts = event.ts ?? Date.now();
|
|
17818
|
+
const id = generateULID();
|
|
17819
|
+
await this.#events.put(id, { ...event, id, ts });
|
|
17820
|
+
}
|
|
17821
|
+
/**
|
|
17822
|
+
* Ensure a manifest row exists for `(templateName, template.version)`.
|
|
17823
|
+
* Safe to call repeatedly: the `fingerprint` is a deterministic hash of
|
|
17824
|
+
* the template's declared shape (stable across calls), though each call
|
|
17825
|
+
* refreshes `recordedAt`.
|
|
17826
|
+
*/
|
|
17827
|
+
async recordManifest(templateName, template) {
|
|
17828
|
+
const bp = captureBlueprint(template.configure);
|
|
17829
|
+
const fingerprint = await fingerprintBlueprint(bp);
|
|
17830
|
+
await this.schemaManifest.put(`${templateName}:${template.version}`, {
|
|
17831
|
+
templateName,
|
|
17832
|
+
version: template.version,
|
|
17833
|
+
collections: bp.collections,
|
|
17834
|
+
indexes: bp.indexes,
|
|
17835
|
+
persistJsonSchema: bp.persistJsonSchema,
|
|
17836
|
+
fingerprint,
|
|
17837
|
+
recordedAt: Date.now()
|
|
17838
|
+
});
|
|
17839
|
+
return fingerprint;
|
|
17840
|
+
}
|
|
17841
|
+
/**
|
|
17842
|
+
* True when `template`'s current declared shape does not match the recorded
|
|
17843
|
+
* manifest for `(templateName, template.version)`. Because shards carry no
|
|
17844
|
+
* schema state independent of their template, this catches "a template's
|
|
17845
|
+
* shape changed without bumping `version`" — not independent per-shard drift.
|
|
17846
|
+
* A missing manifest is treated as drift (nothing to verify against).
|
|
17847
|
+
*/
|
|
17848
|
+
async detectDrift(templateName, template) {
|
|
17849
|
+
const row = await this.schemaManifest.get(`${templateName}:${template.version}`);
|
|
17850
|
+
if (!row) return true;
|
|
17851
|
+
const current = await fingerprintBlueprint(captureBlueprint(template.configure));
|
|
17852
|
+
return current !== row.fingerprint;
|
|
17853
|
+
}
|
|
17854
|
+
};
|
|
17855
|
+
}
|
|
17856
|
+
});
|
|
17857
|
+
|
|
15966
17858
|
// src/noydb.ts
|
|
15967
17859
|
var noydb_exports = {};
|
|
15968
17860
|
__export(noydb_exports, {
|
|
@@ -16022,6 +17914,7 @@ var init_noydb = __esm({
|
|
|
16022
17914
|
"src/noydb.ts"() {
|
|
16023
17915
|
"use strict";
|
|
16024
17916
|
init_errors();
|
|
17917
|
+
init_constants2();
|
|
16025
17918
|
init_storage3();
|
|
16026
17919
|
init_rotate_recover();
|
|
16027
17920
|
init_peer_recover();
|
|
@@ -16101,6 +17994,7 @@ var init_noydb = __esm({
|
|
|
16101
17994
|
writeRelay;
|
|
16102
17995
|
/** Per-vault policy enforcers. */
|
|
16103
17996
|
policyEnforcers = /* @__PURE__ */ new Map();
|
|
17997
|
+
vaultTemplates = /* @__PURE__ */ new Map();
|
|
16104
17998
|
txStrategy;
|
|
16105
17999
|
sessionStrategy;
|
|
16106
18000
|
syncStrategy;
|
|
@@ -16170,7 +18064,7 @@ var init_noydb = __esm({
|
|
|
16170
18064
|
await registry.runChecks(e.collection, incoming, ctx);
|
|
16171
18065
|
const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor3(), executor_exports3));
|
|
16172
18066
|
for (const g of guards) {
|
|
16173
|
-
await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming);
|
|
18067
|
+
await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming, e.computedFieldNames);
|
|
16174
18068
|
}
|
|
16175
18069
|
});
|
|
16176
18070
|
this.subsystemBus.registerGate("beforeDelete", async (e) => {
|
|
@@ -16349,6 +18243,7 @@ var init_noydb = __esm({
|
|
|
16349
18243
|
syncAdapter: targets.length > 0 ? targets[0].store : void 0,
|
|
16350
18244
|
historyConfig: this.options.history,
|
|
16351
18245
|
...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
|
|
18246
|
+
...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
|
|
16352
18247
|
...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
|
|
16353
18248
|
...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
|
|
16354
18249
|
...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
|
|
@@ -16359,6 +18254,7 @@ var init_noydb = __esm({
|
|
|
16359
18254
|
...this.options.i18nStrategy !== void 0 ? { i18nStrategy: this.options.i18nStrategy } : {},
|
|
16360
18255
|
...this.options.syncStrategy !== void 0 ? { syncStrategy: this.options.syncStrategy } : {},
|
|
16361
18256
|
...this.options.guardStrategies !== void 0 ? { guardStrategies: this.options.guardStrategies } : {},
|
|
18257
|
+
...this.options.numbering !== void 0 ? { numberingConfigs: this.options.numbering } : {},
|
|
16362
18258
|
locale: opts?.locale,
|
|
16363
18259
|
// Thread the translator hook so Collection.put() can invoke it
|
|
16364
18260
|
plaintextTranslator: this.options.plaintextTranslator ? (text, from, to, field, collection) => this.invokeTranslator(text, from, to, field, collection) : void 0,
|
|
@@ -16402,6 +18298,7 @@ var init_noydb = __esm({
|
|
|
16402
18298
|
emitter: this.emitter,
|
|
16403
18299
|
historyConfig: this.options.history,
|
|
16404
18300
|
...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
|
|
18301
|
+
...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
|
|
16405
18302
|
...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
|
|
16406
18303
|
...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
|
|
16407
18304
|
...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
|
|
@@ -16411,7 +18308,8 @@ var init_noydb = __esm({
|
|
|
16411
18308
|
...this.options.historyStrategy !== void 0 ? { historyStrategy: this.options.historyStrategy } : {},
|
|
16412
18309
|
...this.options.i18nStrategy !== void 0 ? { i18nStrategy: this.options.i18nStrategy } : {},
|
|
16413
18310
|
...this.options.syncStrategy !== void 0 ? { syncStrategy: this.options.syncStrategy } : {},
|
|
16414
|
-
...this.options.guardStrategies !== void 0 ? { guardStrategies: this.options.guardStrategies } : {}
|
|
18311
|
+
...this.options.guardStrategies !== void 0 ? { guardStrategies: this.options.guardStrategies } : {},
|
|
18312
|
+
...this.options.numbering !== void 0 ? { numberingConfigs: this.options.numbering } : {}
|
|
16415
18313
|
});
|
|
16416
18314
|
this.vaultCache.set(name, comp2);
|
|
16417
18315
|
return comp2;
|
|
@@ -16430,6 +18328,7 @@ var init_noydb = __esm({
|
|
|
16430
18328
|
encrypted: true,
|
|
16431
18329
|
historyConfig: this.options.history,
|
|
16432
18330
|
...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
|
|
18331
|
+
...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
|
|
16433
18332
|
...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
|
|
16434
18333
|
...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
|
|
16435
18334
|
...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
|
|
@@ -16440,6 +18339,7 @@ var init_noydb = __esm({
|
|
|
16440
18339
|
...this.options.i18nStrategy !== void 0 ? { i18nStrategy: this.options.i18nStrategy } : {},
|
|
16441
18340
|
...this.options.syncStrategy !== void 0 ? { syncStrategy: this.options.syncStrategy } : {},
|
|
16442
18341
|
...this.options.guardStrategies !== void 0 ? { guardStrategies: this.options.guardStrategies } : {},
|
|
18342
|
+
...this.options.numbering !== void 0 ? { numberingConfigs: this.options.numbering } : {},
|
|
16443
18343
|
emitter: this.emitter
|
|
16444
18344
|
});
|
|
16445
18345
|
this.vaultCache.set(name, comp);
|
|
@@ -16744,6 +18644,61 @@ var init_noydb = __esm({
|
|
|
16744
18644
|
}
|
|
16745
18645
|
return results;
|
|
16746
18646
|
}
|
|
18647
|
+
/**
|
|
18648
|
+
* Register a shard schema blueprint. `createShard` / `openVaultGroup`
|
|
18649
|
+
* stamp shards from the named template. See the MVF design spec.
|
|
18650
|
+
*/
|
|
18651
|
+
withVaultTemplate(name, template) {
|
|
18652
|
+
this.vaultTemplates.set(name, template);
|
|
18653
|
+
}
|
|
18654
|
+
/**
|
|
18655
|
+
* Open a VaultGroup — transparent routing over per-partition shard
|
|
18656
|
+
* vaults, with shard discovery backed by the supplied `vault-registry`
|
|
18657
|
+
* collection.
|
|
18658
|
+
*/
|
|
18659
|
+
async openVaultGroup(name, opts) {
|
|
18660
|
+
if (this.closed) throw new ValidationError("Instance is closed");
|
|
18661
|
+
if (name === STATE_VAULT_NAME) throw new ReservedVaultNameError(name);
|
|
18662
|
+
const template = this.vaultTemplates.get(opts.sharding.vaultTemplate);
|
|
18663
|
+
if (!template) throw new VaultTemplateNotFoundError(opts.sharding.vaultTemplate);
|
|
18664
|
+
const { VaultGroup: VaultGroup2 } = await Promise.resolve().then(() => (init_vault_group(), vault_group_exports));
|
|
18665
|
+
const { StateManagementVault: StateManagementVault2 } = await Promise.resolve().then(() => (init_state_vault(), state_vault_exports));
|
|
18666
|
+
const stateVault = opts.registry ? void 0 : await StateManagementVault2.open(this);
|
|
18667
|
+
const registry = opts.registry ?? stateVault.registry;
|
|
18668
|
+
const group = new VaultGroup2(this, name, registry, opts.sharding, template);
|
|
18669
|
+
if (stateVault) {
|
|
18670
|
+
group._attachStateVault(stateVault);
|
|
18671
|
+
await stateVault.recordManifest(opts.sharding.vaultTemplate, template);
|
|
18672
|
+
try {
|
|
18673
|
+
await stateVault.appendEvent({
|
|
18674
|
+
type: "manifest-recorded",
|
|
18675
|
+
group: name,
|
|
18676
|
+
templateName: opts.sharding.vaultTemplate,
|
|
18677
|
+
version: template.version
|
|
18678
|
+
});
|
|
18679
|
+
await stateVault.appendEvent({ type: "group-opened", group: name });
|
|
18680
|
+
} catch {
|
|
18681
|
+
}
|
|
18682
|
+
}
|
|
18683
|
+
return group;
|
|
18684
|
+
}
|
|
18685
|
+
/**
|
|
18686
|
+
* Open the reserved StateManagement control-plane vault (registry +
|
|
18687
|
+
* schema-manifest + deployment-events). Lazy-loaded so the federation
|
|
18688
|
+
* chunk stays out of the core graph until used.
|
|
18689
|
+
*/
|
|
18690
|
+
async openStateManagementVault() {
|
|
18691
|
+
if (this.closed) throw new ValidationError("Instance is closed");
|
|
18692
|
+
const { StateManagementVault: StateManagementVault2 } = await Promise.resolve().then(() => (init_state_vault(), state_vault_exports));
|
|
18693
|
+
return StateManagementVault2.open(this);
|
|
18694
|
+
}
|
|
18695
|
+
/**
|
|
18696
|
+
* @internal — true when an encrypted shard vault is provisioned
|
|
18697
|
+
* (its keyring exists in the store).
|
|
18698
|
+
*/
|
|
18699
|
+
async _shardVaultProvisioned(vaultId) {
|
|
18700
|
+
return (await this.options.store.list(vaultId, "_keyring")).length > 0;
|
|
18701
|
+
}
|
|
16747
18702
|
/**
|
|
16748
18703
|
* Change the current user's passphrase for a vault.
|
|
16749
18704
|
*
|