@noy-db/hub 0.1.0-pre.9 → 0.2.0-pre.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aggregate/index.cjs +91 -36
- 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 +16 -9
- package/dist/aggregate/index.js.map +1 -1
- package/dist/attestation/index.cjs +305 -0
- package/dist/attestation/index.cjs.map +1 -0
- package/dist/attestation/index.d.cts +52 -0
- package/dist/attestation/index.d.ts +52 -0
- package/dist/attestation/index.js +36 -0
- package/dist/attestation/index.js.map +1 -0
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +7 -6
- package/dist/blobs/index.d.ts +7 -6
- package/dist/blobs/index.js +10 -8
- package/dist/blobs/index.js.map +1 -1
- package/dist/bundle/index.cjs +16923 -60
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +175 -6
- package/dist/bundle/index.d.ts +175 -6
- package/dist/bundle/index.js +543 -4
- package/dist/bundle/index.js.map +1 -1
- package/dist/{chunk-PTVMYYON.js → chunk-243PNUA6.js} +3 -3
- package/dist/{chunk-MR4424N3.js → chunk-2PAQNPE3.js} +2 -2
- package/dist/chunk-3QAKZ37R.js +83 -0
- package/dist/chunk-3QAKZ37R.js.map +1 -0
- package/dist/chunk-3S4BJX25.js +36 -0
- package/dist/chunk-3S4BJX25.js.map +1 -0
- package/dist/chunk-3XHOCQK4.js +118 -0
- package/dist/chunk-3XHOCQK4.js.map +1 -0
- package/dist/{chunk-AVVPZ4BC.js → chunk-3Y53S2SA.js} +4 -4
- package/dist/chunk-3Z2TPHC4.js +291 -0
- package/dist/chunk-3Z2TPHC4.js.map +1 -0
- package/dist/chunk-4HIL6AHQ.js +57 -0
- package/dist/chunk-4HIL6AHQ.js.map +1 -0
- package/dist/chunk-5ZGZ6HIZ.js +100 -0
- package/dist/chunk-5ZGZ6HIZ.js.map +1 -0
- package/dist/{chunk-ZFKD4QMV.js → chunk-7BRE6EUA.js} +3 -3
- package/dist/chunk-7BUTTVMR.js +34 -0
- package/dist/chunk-7BUTTVMR.js.map +1 -0
- package/dist/{chunk-VQBTTTUN.js → chunk-7Q5PLD5C.js} +4 -4
- package/dist/{chunk-VQBTTTUN.js.map → chunk-7Q5PLD5C.js.map} +1 -1
- package/dist/{chunk-QAVUREFT.js → chunk-7Z23ZFLV.js} +12 -6
- package/dist/chunk-7Z23ZFLV.js.map +1 -0
- package/dist/chunk-AHPFONIL.js +59 -0
- package/dist/chunk-AHPFONIL.js.map +1 -0
- package/dist/chunk-CXSCDO5T.js +51 -0
- package/dist/chunk-CXSCDO5T.js.map +1 -0
- package/dist/chunk-E535SAN4.js +8834 -0
- package/dist/chunk-E535SAN4.js.map +1 -0
- package/dist/chunk-EUYOGYGV.js +830 -0
- package/dist/chunk-EUYOGYGV.js.map +1 -0
- package/dist/chunk-FAQVNJD4.js +61 -0
- package/dist/chunk-FAQVNJD4.js.map +1 -0
- package/dist/{chunk-SCZXXXU4.js → chunk-G6FRSBKK.js} +7 -32
- package/dist/chunk-G6FRSBKK.js.map +1 -0
- package/dist/chunk-GIV6DWBG.js +79 -0
- package/dist/chunk-GIV6DWBG.js.map +1 -0
- package/dist/chunk-HXJXPZRE.js +73 -0
- package/dist/chunk-HXJXPZRE.js.map +1 -0
- package/dist/{chunk-GOUT6DND.js → chunk-J4KLMEUL.js} +173 -91
- package/dist/chunk-J4KLMEUL.js.map +1 -0
- package/dist/{chunk-2CSJGFCB.js → chunk-JYQTXEIO.js} +6 -229
- package/dist/chunk-JYQTXEIO.js.map +1 -0
- package/dist/{chunk-MDDTIZUO.js → chunk-LRAZDV5X.js} +7 -119
- package/dist/chunk-LRAZDV5X.js.map +1 -0
- package/dist/{chunk-M5INGEFC.js → chunk-MRIBLZL3.js} +3 -1
- package/dist/chunk-MRIBLZL3.js.map +1 -0
- package/dist/{chunk-USKYUS74.js → chunk-MUWOSVEP.js} +2 -2
- package/dist/{chunk-4PWAI7Q4.js → chunk-NWZ3I6R6.js} +5 -5
- package/dist/chunk-OVZDFEOR.js +124 -0
- package/dist/chunk-OVZDFEOR.js.map +1 -0
- package/dist/chunk-PEULZC6M.js +118 -0
- package/dist/chunk-PEULZC6M.js.map +1 -0
- package/dist/chunk-PFSNOPBQ.js +233 -0
- package/dist/chunk-PFSNOPBQ.js.map +1 -0
- package/dist/chunk-PLI5TV7N.js +53 -0
- package/dist/chunk-PLI5TV7N.js.map +1 -0
- package/dist/{chunk-WDM5XGGS.js → chunk-Q6W2CMEJ.js} +181 -11
- package/dist/chunk-Q6W2CMEJ.js.map +1 -0
- package/dist/{chunk-QGZRWRSL.js → chunk-QPEXPHJR.js} +4 -4
- package/dist/{chunk-R36SIKES.js → chunk-QXQRKXCU.js} +2 -2
- package/dist/chunk-RTZVQAJ7.js +82 -0
- package/dist/chunk-RTZVQAJ7.js.map +1 -0
- package/dist/chunk-TBKOGSYR.js +296 -0
- package/dist/chunk-TBKOGSYR.js.map +1 -0
- package/dist/chunk-UMLVJTYV.js +20 -0
- package/dist/chunk-UMLVJTYV.js.map +1 -0
- package/dist/chunk-UND4XIB6.js +251 -0
- package/dist/chunk-UND4XIB6.js.map +1 -0
- package/dist/chunk-VCGTOS2A.js +795 -0
- package/dist/chunk-VCGTOS2A.js.map +1 -0
- package/dist/chunk-VE6YVP32.js +19 -0
- package/dist/chunk-VE6YVP32.js.map +1 -0
- package/dist/{chunk-M62XNWRA.js → chunk-VK5EER6C.js} +2 -2
- package/dist/{chunk-NXFEYLVG.js → chunk-VPSUZLOJ.js} +4 -3
- package/dist/{chunk-NXFEYLVG.js.map → chunk-VPSUZLOJ.js.map} +1 -1
- package/dist/{chunk-TDR6T5CJ.js → chunk-VRBCTEKQ.js} +91 -132
- package/dist/chunk-VRBCTEKQ.js.map +1 -0
- package/dist/{chunk-ACLDOTNQ.js → chunk-W3XXT26A.js} +303 -3
- package/dist/chunk-W3XXT26A.js.map +1 -0
- package/dist/{chunk-CIMZBAZB.js → chunk-XG3PTSCD.js} +1 -1
- package/dist/chunk-XG3PTSCD.js.map +1 -0
- package/dist/chunk-Y2RKOPNC.js +145 -0
- package/dist/chunk-Y2RKOPNC.js.map +1 -0
- package/dist/{chunk-NPC4LFV5.js → chunk-YMYK7US4.js} +2 -2
- package/dist/{chunk-RKJ6OL7K.js → chunk-YS3POABP.js} +1 -1
- package/dist/chunk-YS3POABP.js.map +1 -0
- package/dist/chunk-YTXSFG3C.js +179 -0
- package/dist/chunk-YTXSFG3C.js.map +1 -0
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +7 -6
- package/dist/consent/index.d.ts +7 -6
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-IVKU7YTT.js → crypto-5ZDIY3NG.js} +3 -3
- package/dist/{delegation-2DBS2EOH.js → delegation-QYXZW25W.js} +5 -4
- package/dist/derivations/index.cjs +351 -0
- package/dist/derivations/index.cjs.map +1 -0
- package/dist/derivations/index.d.cts +72 -0
- package/dist/derivations/index.d.ts +72 -0
- package/dist/derivations/index.js +27 -0
- package/dist/{dev-unlock-Da1B0TIK.d.cts → dev-unlock-DQCNDfFp.d.cts} +1 -1
- package/dist/{dev-unlock-BdPp68qn.d.ts → dev-unlock-utkybTKb.d.ts} +1 -1
- package/dist/executor-AS2IDHKZ.js +11 -0
- package/dist/executor-HLXFXNFM.js +8 -0
- package/dist/executor-HLXFXNFM.js.map +1 -0
- package/dist/executor-HN6YBHZ5.js +8 -0
- package/dist/executor-HN6YBHZ5.js.map +1 -0
- package/dist/fanout-sidecar-VJ52RIEY.js +51 -0
- package/dist/fanout-sidecar-VJ52RIEY.js.map +1 -0
- package/dist/guards/index.cjs +315 -0
- package/dist/guards/index.cjs.map +1 -0
- package/dist/guards/index.d.cts +31 -0
- package/dist/guards/index.d.ts +31 -0
- package/dist/guards/index.js +29 -0
- package/dist/guards/index.js.map +1 -0
- package/dist/{hash-lsoL3eEW.d.ts → hash-DcoYWfJ_.d.ts} +1 -1
- package/dist/{hash-BEfzPKwo.d.cts → hash-jDowCrK2.d.cts} +1 -1
- package/dist/history/index.cjs +8 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +8 -7
- package/dist/history/index.d.ts +8 -7
- package/dist/history/index.js +6 -6
- package/dist/i18n/index.cjs +81 -0
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +7 -6
- package/dist/i18n/index.d.ts +7 -6
- package/dist/i18n/index.js +27 -12
- package/dist/i18n/index.js.map +1 -1
- package/dist/{index-6xNpPsxR.d.cts → index-BCKdioeh.d.ts} +331 -5
- package/dist/{index-DJTf9yxn.d.ts → index-BMjrzNZr.d.cts} +331 -5
- package/dist/index.cjs +6065 -959
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +208 -16
- package/dist/index.d.ts +208 -16
- package/dist/index.js +242 -7392
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs +2 -0
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.d.cts +3 -3
- package/dist/indexing/index.d.ts +3 -3
- package/dist/indexing/index.js +4 -4
- package/dist/issue-ORP37MVW.js +12 -0
- package/dist/issue-ORP37MVW.js.map +1 -0
- package/dist/{lazy-builder-CZVLKh0Z.d.cts → lazy-builder-C-rPfWG0.d.cts} +1 -1
- package/dist/{lazy-builder-BwEoBQZ9.d.ts → lazy-builder-Rpd-V3jP.d.ts} +1 -1
- package/dist/{ledger-QZTTHQAQ.js → ledger-3IU5GMXA.js} +6 -6
- package/dist/ledger-3IU5GMXA.js.map +1 -0
- package/dist/materialized-views/index.cjs +837 -0
- package/dist/materialized-views/index.cjs.map +1 -0
- package/dist/materialized-views/index.d.cts +184 -0
- package/dist/materialized-views/index.d.ts +184 -0
- package/dist/materialized-views/index.js +45 -0
- package/dist/materialized-views/index.js.map +1 -0
- package/dist/noydb-5H3C24GG.js +34 -0
- package/dist/noydb-5H3C24GG.js.map +1 -0
- package/dist/overlay-views/index.cjs +359 -0
- package/dist/overlay-views/index.cjs.map +1 -0
- package/dist/overlay-views/index.d.cts +82 -0
- package/dist/overlay-views/index.d.ts +82 -0
- package/dist/overlay-views/index.js +25 -0
- package/dist/overlay-views/index.js.map +1 -0
- package/dist/periods/index.cjs +7 -1
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +7 -6
- package/dist/periods/index.d.ts +7 -6
- package/dist/periods/index.js +6 -6
- package/dist/{predicate-SBHmi6D0.d.cts → predicate-Dnu81tsS.d.cts} +25 -1
- package/dist/{predicate-SBHmi6D0.d.ts → predicate-Dnu81tsS.d.ts} +25 -1
- package/dist/{public-envelope-6JTACYJV.js → public-envelope-U3CMEOMV.js} +4 -4
- package/dist/public-envelope-U3CMEOMV.js.map +1 -0
- package/dist/query/index.cjs +302 -124
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +3 -3
- package/dist/query/index.d.ts +3 -3
- package/dist/query/index.js +26 -11
- package/dist/read-only-facade-ITU6L7BL.js +7 -0
- package/dist/read-only-facade-ITU6L7BL.js.map +1 -0
- package/dist/registry-3ALP62P6.js +10 -0
- package/dist/registry-3ALP62P6.js.map +1 -0
- package/dist/registry-7HE6VJGC.js +8 -0
- package/dist/registry-7HE6VJGC.js.map +1 -0
- package/dist/registry-PSIPG2QR.js +8 -0
- package/dist/registry-PSIPG2QR.js.map +1 -0
- package/dist/registry-RFGGMVNJ.js +7 -0
- package/dist/registry-RFGGMVNJ.js.map +1 -0
- package/dist/revoke-KY2GB4KP.js +17 -0
- package/dist/revoke-KY2GB4KP.js.map +1 -0
- package/dist/session/index.cjs +7 -1
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +8 -7
- package/dist/session/index.d.ts +8 -7
- package/dist/session/index.js +10 -3
- package/dist/session/index.js.map +1 -1
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +7 -6
- package/dist/shadow/index.d.ts +7 -6
- package/dist/shadow/index.js +2 -2
- package/dist/signer-GRI5TZKH.js +18 -0
- package/dist/signer-GRI5TZKH.js.map +1 -0
- package/dist/stale-OTOF3FH7.js +13 -0
- package/dist/stale-OTOF3FH7.js.map +1 -0
- package/dist/store/index.cjs +14 -0
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +7 -6
- package/dist/store/index.d.ts +7 -6
- package/dist/store/index.js +5 -2
- package/dist/{strategy-D-SrOLCl.d.cts → strategy-DSTrsZ8t.d.cts} +72 -19
- package/dist/{strategy-D-SrOLCl.d.ts → strategy-DSTrsZ8t.d.ts} +72 -19
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +6 -5
- package/dist/sync/index.d.ts +6 -5
- package/dist/sync/index.js +4 -4
- package/dist/team/index.cjs +1554 -2
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +7 -6
- package/dist/team/index.d.ts +7 -6
- package/dist/team/index.js +77 -8
- package/dist/tx/index.cjs +296 -44
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +7 -6
- package/dist/tx/index.d.ts +7 -6
- package/dist/tx/index.js +2 -2
- package/dist/{types-Bo7NSXJr.d.ts → types-BoFFiskX.d.ts} +2714 -321
- package/dist/{types-Bnb82f5R.d.cts → types-DJG8HG6F.d.cts} +2714 -321
- package/dist/{index-CywCC1qZ.d.cts → ulid-BmBgooGm.d.ts} +215 -26
- package/dist/{index-8QDuznDr.d.ts → ulid-C7ms9oli.d.cts} +215 -26
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.js +1 -1
- package/dist/with-derivation-BKXXa8Vt.d.ts +13 -0
- package/dist/with-derivation-BjQ7q4NE.d.cts +13 -0
- package/dist/with-guard-C25yNjzd.d.ts +18 -0
- package/dist/with-guard-DQme5DKE.d.cts +18 -0
- package/dist/with-materialized-view-BbEPFIIJ.d.cts +27 -0
- package/dist/with-materialized-view-CqnRwI2S.d.ts +27 -0
- package/dist/with-overlayed-view-Ct1fSJt-.d.ts +13 -0
- package/dist/with-overlayed-view-bwlmmFjx.d.cts +13 -0
- package/package.json +65 -2
- package/dist/chunk-2CSJGFCB.js.map +0 -1
- package/dist/chunk-ACLDOTNQ.js.map +0 -1
- package/dist/chunk-BTDCBVJW.js +0 -160
- package/dist/chunk-BTDCBVJW.js.map +0 -1
- package/dist/chunk-CIMZBAZB.js.map +0 -1
- package/dist/chunk-EXHNQEV4.js +0 -392
- package/dist/chunk-EXHNQEV4.js.map +0 -1
- package/dist/chunk-GOUT6DND.js.map +0 -1
- package/dist/chunk-M5INGEFC.js.map +0 -1
- package/dist/chunk-MDDTIZUO.js.map +0 -1
- package/dist/chunk-QAVUREFT.js.map +0 -1
- package/dist/chunk-RKJ6OL7K.js.map +0 -1
- package/dist/chunk-SCZXXXU4.js.map +0 -1
- package/dist/chunk-TDR6T5CJ.js.map +0 -1
- package/dist/chunk-WDM5XGGS.js.map +0 -1
- /package/dist/{chunk-PTVMYYON.js.map → chunk-243PNUA6.js.map} +0 -0
- /package/dist/{chunk-MR4424N3.js.map → chunk-2PAQNPE3.js.map} +0 -0
- /package/dist/{chunk-AVVPZ4BC.js.map → chunk-3Y53S2SA.js.map} +0 -0
- /package/dist/{chunk-ZFKD4QMV.js.map → chunk-7BRE6EUA.js.map} +0 -0
- /package/dist/{chunk-USKYUS74.js.map → chunk-MUWOSVEP.js.map} +0 -0
- /package/dist/{chunk-4PWAI7Q4.js.map → chunk-NWZ3I6R6.js.map} +0 -0
- /package/dist/{chunk-QGZRWRSL.js.map → chunk-QPEXPHJR.js.map} +0 -0
- /package/dist/{chunk-R36SIKES.js.map → chunk-QXQRKXCU.js.map} +0 -0
- /package/dist/{chunk-M62XNWRA.js.map → chunk-VK5EER6C.js.map} +0 -0
- /package/dist/{chunk-NPC4LFV5.js.map → chunk-YMYK7US4.js.map} +0 -0
- /package/dist/{crypto-IVKU7YTT.js.map → crypto-5ZDIY3NG.js.map} +0 -0
- /package/dist/{delegation-2DBS2EOH.js.map → delegation-QYXZW25W.js.map} +0 -0
- /package/dist/{ledger-QZTTHQAQ.js.map → derivations/index.js.map} +0 -0
- /package/dist/{public-envelope-6JTACYJV.js.map → executor-AS2IDHKZ.js.map} +0 -0
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
|
|
23
|
+
// src/errors.ts
|
|
24
|
+
var NoydbError, ValidationError, GroupCardinalityError, MaterializedViewCycleError, MaterializedViewSourceUnknownError, MaterializedViewTooLargeError, MaterializedViewConfigError;
|
|
25
|
+
var init_errors = __esm({
|
|
26
|
+
"src/errors.ts"() {
|
|
27
|
+
"use strict";
|
|
28
|
+
NoydbError = class extends Error {
|
|
29
|
+
/** Machine-readable error code. Stable across library versions. */
|
|
30
|
+
code;
|
|
31
|
+
constructor(code, message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "NoydbError";
|
|
34
|
+
this.code = code;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
ValidationError = class extends NoydbError {
|
|
38
|
+
constructor(message = "Validation error") {
|
|
39
|
+
super("VALIDATION_ERROR", message);
|
|
40
|
+
this.name = "ValidationError";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
GroupCardinalityError = class extends NoydbError {
|
|
44
|
+
/** The field being grouped on. */
|
|
45
|
+
field;
|
|
46
|
+
/** Observed number of distinct groups at the moment the cap tripped. */
|
|
47
|
+
cardinality;
|
|
48
|
+
/** The cap that was exceeded. */
|
|
49
|
+
maxGroups;
|
|
50
|
+
constructor(field, cardinality, maxGroups) {
|
|
51
|
+
super(
|
|
52
|
+
"GROUP_CARDINALITY",
|
|
53
|
+
`.groupBy("${field}") produced ${cardinality} distinct groups, exceeding the ${maxGroups}-group ceiling. This is almost always a query mistake \u2014 grouping on a high-uniqueness field like "id" or "createdAt" produces one bucket per record. Narrow the query with .where() before grouping, or group on a lower-cardinality field (status, category, clientId). If you genuinely need high-cardinality grouping, file an issue with your use case.`
|
|
54
|
+
);
|
|
55
|
+
this.name = "GroupCardinalityError";
|
|
56
|
+
this.field = field;
|
|
57
|
+
this.cardinality = cardinality;
|
|
58
|
+
this.maxGroups = maxGroups;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
MaterializedViewCycleError = class extends NoydbError {
|
|
62
|
+
path;
|
|
63
|
+
constructor(path) {
|
|
64
|
+
super(
|
|
65
|
+
"MATERIALIZED_VIEW_CYCLE",
|
|
66
|
+
`Materialized-view graph contains a cycle: ${path.join(" \u2192 ")}. Refusing to open vault \u2014 break the cycle before retrying.`
|
|
67
|
+
);
|
|
68
|
+
this.name = "MaterializedViewCycleError";
|
|
69
|
+
this.path = path;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
MaterializedViewSourceUnknownError = class extends NoydbError {
|
|
73
|
+
mvName;
|
|
74
|
+
collection;
|
|
75
|
+
constructor(mvName, collection) {
|
|
76
|
+
super(
|
|
77
|
+
"MATERIALIZED_VIEW_SOURCE_UNKNOWN",
|
|
78
|
+
`Materialized view "${mvName}" references unknown source collection "${collection}". Declare the collection (e.g. via schema or by writing to it once) before registering the MV.`
|
|
79
|
+
);
|
|
80
|
+
this.name = "MaterializedViewSourceUnknownError";
|
|
81
|
+
this.mvName = mvName;
|
|
82
|
+
this.collection = collection;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
MaterializedViewTooLargeError = class extends NoydbError {
|
|
86
|
+
mvName;
|
|
87
|
+
expected;
|
|
88
|
+
limit;
|
|
89
|
+
constructor(mvName, expected, limit) {
|
|
90
|
+
super(
|
|
91
|
+
"MATERIALIZED_VIEW_TOO_LARGE",
|
|
92
|
+
`Materialized view "${mvName}" would emit ${expected} rows, exceeding the configured limit of ${limit}. Override via { maxRows: N } on the MV strategy if intentional, or tighten the query's filter/groupBy.`
|
|
93
|
+
);
|
|
94
|
+
this.name = "MaterializedViewTooLargeError";
|
|
95
|
+
this.mvName = mvName;
|
|
96
|
+
this.expected = expected;
|
|
97
|
+
this.limit = limit;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
MaterializedViewConfigError = class extends NoydbError {
|
|
101
|
+
constructor(message) {
|
|
102
|
+
super(
|
|
103
|
+
"MATERIALIZED_VIEW_CONFIG",
|
|
104
|
+
`[noy-db] withMaterializedView: ${message}`
|
|
105
|
+
);
|
|
106
|
+
this.name = "MaterializedViewConfigError";
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// src/materialized-views/dependency-analyzer.ts
|
|
113
|
+
function analyzeDependencies(query) {
|
|
114
|
+
const deps = /* @__PURE__ */ new Set();
|
|
115
|
+
const plan = query._plan();
|
|
116
|
+
const ctx = query._joinContext();
|
|
117
|
+
if (ctx?.leftCollection) {
|
|
118
|
+
deps.add(ctx.leftCollection);
|
|
119
|
+
}
|
|
120
|
+
for (const leg of plan.joins) {
|
|
121
|
+
deps.add(leg.target);
|
|
122
|
+
}
|
|
123
|
+
walkClausesForJoins(plan, deps, ctx);
|
|
124
|
+
return deps;
|
|
125
|
+
}
|
|
126
|
+
function walkClausesForJoins(plan, deps, ctx) {
|
|
127
|
+
void ctx;
|
|
128
|
+
for (const clause of plan.clauses) {
|
|
129
|
+
if (clause.type === "group") {
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function summarizeQueryPlan(query) {
|
|
134
|
+
const plan = query._plan();
|
|
135
|
+
const ctx = query._joinContext();
|
|
136
|
+
return JSON.stringify({
|
|
137
|
+
root: ctx?.leftCollection ?? null,
|
|
138
|
+
clauses: plan.clauses,
|
|
139
|
+
orderBy: plan.orderBy,
|
|
140
|
+
limit: plan.limit ?? null,
|
|
141
|
+
offset: plan.offset,
|
|
142
|
+
joins: plan.joins.map((j) => ({ field: j.field, as: j.as, target: j.target, mode: j.mode }))
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function summarizeUnionPlan(spec) {
|
|
146
|
+
const arms = (spec.unionSources ?? []).map((s) => s.collection).join(",");
|
|
147
|
+
const groupBy = Array.isArray(spec.groupBy) ? [...spec.groupBy].sort().join(",") : typeof spec.groupBy === "string" ? spec.groupBy : "";
|
|
148
|
+
const aggKeys = spec.aggregate ? Object.keys(spec.aggregate).sort().join(",") : "";
|
|
149
|
+
return `union(${arms})|groupBy(${groupBy})|aggregate(${aggKeys})`;
|
|
150
|
+
}
|
|
151
|
+
var init_dependency_analyzer = __esm({
|
|
152
|
+
"src/materialized-views/dependency-analyzer.ts"() {
|
|
153
|
+
"use strict";
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// src/materialized-views/query-hash.ts
|
|
158
|
+
async function computeQueryHash(mvName, dependencies, queryPlanSummary) {
|
|
159
|
+
const canonical = JSON.stringify({
|
|
160
|
+
mvName,
|
|
161
|
+
dependencies: [...dependencies].sort(),
|
|
162
|
+
queryPlanSummary
|
|
163
|
+
});
|
|
164
|
+
const bytes = new TextEncoder().encode(canonical);
|
|
165
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
166
|
+
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
167
|
+
}
|
|
168
|
+
function canonicalizeQueryPlan(plan) {
|
|
169
|
+
return JSON.stringify(plan, (_key, value) => {
|
|
170
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
171
|
+
const sorted = {};
|
|
172
|
+
for (const k of Object.keys(value).sort()) {
|
|
173
|
+
sorted[k] = value[k];
|
|
174
|
+
}
|
|
175
|
+
return sorted;
|
|
176
|
+
}
|
|
177
|
+
return value;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
var init_query_hash = __esm({
|
|
181
|
+
"src/materialized-views/query-hash.ts"() {
|
|
182
|
+
"use strict";
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// src/materialized-views/registry.ts
|
|
187
|
+
function isFieldClauseOnField(clause, field) {
|
|
188
|
+
return clause.type === "field" && clause.field === field;
|
|
189
|
+
}
|
|
190
|
+
function wrapDbWithPredicates(db, predicates) {
|
|
191
|
+
const map = /* @__PURE__ */ new Map();
|
|
192
|
+
for (const [name, decl] of Object.entries(predicates)) {
|
|
193
|
+
map.set(name, {
|
|
194
|
+
hash: decl.hash,
|
|
195
|
+
fn: decl.fn
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
200
|
+
collection(name) {
|
|
201
|
+
const c = db.collection(name);
|
|
202
|
+
return new Proxy(c, {
|
|
203
|
+
get(target, prop, receiver) {
|
|
204
|
+
if (prop === "query") {
|
|
205
|
+
return (...args) => {
|
|
206
|
+
const q = target.query(...args);
|
|
207
|
+
if (q && typeof q._withPredicates === "function") {
|
|
208
|
+
return q._withPredicates(map);
|
|
209
|
+
}
|
|
210
|
+
return q;
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return Reflect.get(target, prop, receiver);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function extractPredicateRefs(plan) {
|
|
220
|
+
const refs = [];
|
|
221
|
+
const walk = (clauses) => {
|
|
222
|
+
for (const c of clauses) {
|
|
223
|
+
if (c.type === "wherePredicate") {
|
|
224
|
+
refs.push({ name: c.name, predicateHash: c.predicateHash, ctxHash: c.ctxHash });
|
|
225
|
+
} else if (c.type === "group") {
|
|
226
|
+
walk(c.clauses);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
walk(plan.clauses);
|
|
231
|
+
refs.sort((a, b) => {
|
|
232
|
+
if (a.name !== b.name) return a.name < b.name ? -1 : 1;
|
|
233
|
+
if (a.predicateHash !== b.predicateHash) return a.predicateHash < b.predicateHash ? -1 : 1;
|
|
234
|
+
return a.ctxHash < b.ctxHash ? -1 : a.ctxHash > b.ctxHash ? 1 : 0;
|
|
235
|
+
});
|
|
236
|
+
return refs;
|
|
237
|
+
}
|
|
238
|
+
function partitionDisjoint(reg) {
|
|
239
|
+
const partition = reg.spec.output?.partition;
|
|
240
|
+
if (partition === void 0) return false;
|
|
241
|
+
const value = partition.value;
|
|
242
|
+
for (const c of reg.partitionClauses) {
|
|
243
|
+
if (c.op === "==" && c.value !== value) return true;
|
|
244
|
+
if (c.op === "!=" && c.value === value) return true;
|
|
245
|
+
if (c.op === "in" && Array.isArray(c.value)) {
|
|
246
|
+
const list = c.value;
|
|
247
|
+
if (!list.includes(value)) return true;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
var MaterializedViewRegistry;
|
|
253
|
+
var init_registry = __esm({
|
|
254
|
+
"src/materialized-views/registry.ts"() {
|
|
255
|
+
"use strict";
|
|
256
|
+
init_errors();
|
|
257
|
+
init_dependency_analyzer();
|
|
258
|
+
init_query_hash();
|
|
259
|
+
MaterializedViewRegistry = class {
|
|
260
|
+
/** Keyed by `spec.name`. */
|
|
261
|
+
_byName = /* @__PURE__ */ new Map();
|
|
262
|
+
/** Keyed by dependency source-collection → MVs that depend on it. */
|
|
263
|
+
_bySource = /* @__PURE__ */ new Map();
|
|
264
|
+
/**
|
|
265
|
+
* Register an MV. Invokes `spec.query()` once at registration time to
|
|
266
|
+
* read the plan + join context; the resulting `Query<T>` is discarded
|
|
267
|
+
* after dependency extraction. `vault.collection(...)` must therefore
|
|
268
|
+
* be functional by the time this runs — typically wired from
|
|
269
|
+
* `Vault._initMaterializedViews` after collection bootstrap.
|
|
270
|
+
*
|
|
271
|
+
* Throws `MaterializedViewSourceUnknownError` if the analyzer
|
|
272
|
+
* surfaces a dependency the vault doesn't know about (when a
|
|
273
|
+
* `knownCollections` checker is supplied).
|
|
274
|
+
*/
|
|
275
|
+
async register(spec, db, options) {
|
|
276
|
+
const dbForQuery = spec.predicates ? wrapDbWithPredicates(db, spec.predicates) : db;
|
|
277
|
+
let dependencies;
|
|
278
|
+
let queryPlanSummary;
|
|
279
|
+
let qAny = null;
|
|
280
|
+
let isQuery = false;
|
|
281
|
+
if (spec.unionSources) {
|
|
282
|
+
dependencies = new Set(spec.unionSources.map((s) => s.collection));
|
|
283
|
+
queryPlanSummary = summarizeUnionPlan(spec);
|
|
284
|
+
} else {
|
|
285
|
+
const q = spec.query(dbForQuery);
|
|
286
|
+
qAny = q;
|
|
287
|
+
isQuery = typeof qAny._plan === "function";
|
|
288
|
+
if (isQuery) {
|
|
289
|
+
dependencies = analyzeDependencies(q);
|
|
290
|
+
queryPlanSummary = summarizeQueryPlan(q);
|
|
291
|
+
const predicateRefs = extractPredicateRefs(qAny._plan());
|
|
292
|
+
if (predicateRefs.length > 0) {
|
|
293
|
+
queryPlanSummary = JSON.stringify({ plan: queryPlanSummary, predicates: predicateRefs });
|
|
294
|
+
}
|
|
295
|
+
if (spec.sources) for (const s of spec.sources) dependencies.add(s);
|
|
296
|
+
} else {
|
|
297
|
+
if (!spec.sources || spec.sources.length === 0) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
`withMaterializedView "${spec.name}": query() returned an aggregate (Aggregation or GroupedAggregation) but no \`sources\` field is declared. The dependency analyzer cannot walk through groupBy().aggregate() back to the source \u2014 declare sources: [...] explicitly.`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
dependencies = new Set(spec.sources);
|
|
303
|
+
queryPlanSummary = JSON.stringify({ aggregate: true, sources: [...spec.sources].sort() });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (options?.knownCollections) {
|
|
307
|
+
for (const dep of dependencies) {
|
|
308
|
+
if (!options.knownCollections(dep)) {
|
|
309
|
+
throw new MaterializedViewSourceUnknownError(spec.name, dep);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const outputCollection = spec.output?.collection ?? spec.name;
|
|
314
|
+
const queryHash = await computeQueryHash(spec.name, dependencies, queryPlanSummary);
|
|
315
|
+
const partitionClauses = [];
|
|
316
|
+
const partitionField = spec.output?.partition?.field;
|
|
317
|
+
if (partitionField !== void 0 && isQuery) {
|
|
318
|
+
const plan = qAny._plan();
|
|
319
|
+
for (const clause of plan.clauses) {
|
|
320
|
+
if (isFieldClauseOnField(clause, partitionField)) partitionClauses.push(clause);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const reg = { spec, outputCollection, dependencies, queryHash, partitionClauses };
|
|
324
|
+
this._byName.set(spec.name, reg);
|
|
325
|
+
for (const dep of dependencies) {
|
|
326
|
+
const arr = this._bySource.get(dep);
|
|
327
|
+
if (arr) arr.push(reg);
|
|
328
|
+
else this._bySource.set(dep, [reg]);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/** All MVs that depend on `source`, in registration order. */
|
|
332
|
+
mvsForSource(source) {
|
|
333
|
+
return this._bySource.get(source) ?? [];
|
|
334
|
+
}
|
|
335
|
+
/** Single MV by name, or `undefined`. */
|
|
336
|
+
byName(name) {
|
|
337
|
+
return this._byName.get(name);
|
|
338
|
+
}
|
|
339
|
+
/** Iterate over every registered MV. */
|
|
340
|
+
all() {
|
|
341
|
+
return [...this._byName.values()];
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Cycle detection over the combined derivation + MV graph. Edges:
|
|
345
|
+
* - Derivation: derivation.source → output.collection (each output)
|
|
346
|
+
* - MV: every dep in MV.dependencies → MV.outputCollection
|
|
347
|
+
*
|
|
348
|
+
* Throws `MaterializedViewCycleError` if the cycle's terminal node
|
|
349
|
+
* is an MV output collection; otherwise (a pure-derivation cycle)
|
|
350
|
+
* the caller's `DerivationRegistry.validate()` will surface
|
|
351
|
+
* `DerivationCycleError` separately at vault open.
|
|
352
|
+
*
|
|
353
|
+
* Call AFTER all `register()` calls complete.
|
|
354
|
+
*/
|
|
355
|
+
validate(derivationRegistry) {
|
|
356
|
+
const visited = /* @__PURE__ */ new Set();
|
|
357
|
+
const stack = [];
|
|
358
|
+
const mvOutputs = /* @__PURE__ */ new Set();
|
|
359
|
+
for (const reg of this._byName.values()) mvOutputs.add(reg.outputCollection);
|
|
360
|
+
const edges = /* @__PURE__ */ new Map();
|
|
361
|
+
for (const reg of this._byName.values()) {
|
|
362
|
+
for (const dep of reg.dependencies) {
|
|
363
|
+
if (dep === reg.outputCollection && partitionDisjoint(reg)) continue;
|
|
364
|
+
const arr = edges.get(dep);
|
|
365
|
+
if (arr) arr.push(reg.outputCollection);
|
|
366
|
+
else edges.set(dep, [reg.outputCollection]);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (derivationRegistry) {
|
|
370
|
+
for (const reg of this._byName.values()) {
|
|
371
|
+
void reg;
|
|
372
|
+
}
|
|
373
|
+
const sourcesToScan = /* @__PURE__ */ new Set();
|
|
374
|
+
for (const reg of this._byName.values()) {
|
|
375
|
+
for (const dep of reg.dependencies) sourcesToScan.add(dep);
|
|
376
|
+
sourcesToScan.add(reg.outputCollection);
|
|
377
|
+
}
|
|
378
|
+
for (const src of sourcesToScan) {
|
|
379
|
+
const strategies = derivationRegistry.strategiesForSource(src);
|
|
380
|
+
if (strategies.length === 0) continue;
|
|
381
|
+
for (const s of strategies) {
|
|
382
|
+
for (const key of Object.keys(s.spec.outputs)) {
|
|
383
|
+
const o = s.spec.outputs[key];
|
|
384
|
+
if (!o) continue;
|
|
385
|
+
const arr = edges.get(src);
|
|
386
|
+
if (arr) arr.push(o.collection);
|
|
387
|
+
else edges.set(src, [o.collection]);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const visit = (node) => {
|
|
393
|
+
if (stack.includes(node)) {
|
|
394
|
+
const cycle = stack.slice(stack.indexOf(node)).concat(node);
|
|
395
|
+
if (cycle.some((n) => mvOutputs.has(n))) {
|
|
396
|
+
throw new MaterializedViewCycleError(cycle);
|
|
397
|
+
}
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (visited.has(node)) return;
|
|
401
|
+
stack.push(node);
|
|
402
|
+
const outs = edges.get(node);
|
|
403
|
+
if (outs) for (const o of outs) visit(o);
|
|
404
|
+
stack.pop();
|
|
405
|
+
visited.add(node);
|
|
406
|
+
};
|
|
407
|
+
for (const node of edges.keys()) visit(node);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// src/query/predicate.ts
|
|
414
|
+
function readPath(record, path) {
|
|
415
|
+
if (record === null || record === void 0) return void 0;
|
|
416
|
+
if (!path.includes(".")) {
|
|
417
|
+
return record[path];
|
|
418
|
+
}
|
|
419
|
+
const segments = path.split(".");
|
|
420
|
+
let cursor = record;
|
|
421
|
+
for (const segment of segments) {
|
|
422
|
+
if (cursor === null || cursor === void 0) return void 0;
|
|
423
|
+
cursor = cursor[segment];
|
|
424
|
+
}
|
|
425
|
+
return cursor;
|
|
426
|
+
}
|
|
427
|
+
var init_predicate = __esm({
|
|
428
|
+
"src/query/predicate.ts"() {
|
|
429
|
+
"use strict";
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// src/aggregate/canonical-key.ts
|
|
434
|
+
function canonicalGroupKey(fields, row) {
|
|
435
|
+
const sorted = [...fields].sort();
|
|
436
|
+
const parts = [];
|
|
437
|
+
for (const name of sorted) {
|
|
438
|
+
const v = row[name];
|
|
439
|
+
const serialised = v === void 0 ? "undefined" : JSON.stringify(v);
|
|
440
|
+
parts.push(`${name}=${serialised}`);
|
|
441
|
+
}
|
|
442
|
+
return parts.join("|");
|
|
443
|
+
}
|
|
444
|
+
var init_canonical_key = __esm({
|
|
445
|
+
"src/aggregate/canonical-key.ts"() {
|
|
446
|
+
"use strict";
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// src/aggregate/groupby.ts
|
|
451
|
+
function warnCardinalityApproaching(fields, observed) {
|
|
452
|
+
const key = JSON.stringify([...fields].sort());
|
|
453
|
+
if (warnedCardinalityFields.has(key)) return;
|
|
454
|
+
warnedCardinalityFields.add(key);
|
|
455
|
+
const label = `[${fields.join(", ")}]`;
|
|
456
|
+
console.warn(
|
|
457
|
+
`[noy-db] .groupBy(${label}) produced ${observed} distinct groups, ${Math.round(observed / GROUPBY_MAX_CARDINALITY * 100)}% of the ${GROUPBY_MAX_CARDINALITY}-group ceiling. Narrow the query with .where() before grouping, or switch to a lower-cardinality field.`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
function groupAndReduce(records, fieldOrFields, spec) {
|
|
461
|
+
const fields = typeof fieldOrFields === "string" ? [fieldOrFields] : fieldOrFields;
|
|
462
|
+
if (fields.length === 0) {
|
|
463
|
+
throw new Error(".groupBy() requires at least one field");
|
|
464
|
+
}
|
|
465
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
466
|
+
const fieldLabel = fields.length === 1 ? fields[0] : `[${fields.join(", ")}]`;
|
|
467
|
+
for (const record of records) {
|
|
468
|
+
const keyValues = {};
|
|
469
|
+
for (const f of fields) {
|
|
470
|
+
keyValues[f] = readPath(record, f);
|
|
471
|
+
}
|
|
472
|
+
const dedupKey = canonicalGroupKey(fields, keyValues);
|
|
473
|
+
let bucket = buckets.get(dedupKey);
|
|
474
|
+
if (bucket === void 0) {
|
|
475
|
+
if (buckets.size >= GROUPBY_MAX_CARDINALITY) {
|
|
476
|
+
throw new GroupCardinalityError(
|
|
477
|
+
fieldLabel,
|
|
478
|
+
buckets.size + 1,
|
|
479
|
+
GROUPBY_MAX_CARDINALITY
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
bucket = { keyValues, records: [] };
|
|
483
|
+
buckets.set(dedupKey, bucket);
|
|
484
|
+
}
|
|
485
|
+
bucket.records.push(record);
|
|
486
|
+
}
|
|
487
|
+
if (buckets.size >= GROUPBY_WARN_CARDINALITY) {
|
|
488
|
+
warnCardinalityApproaching(fields, buckets.size);
|
|
489
|
+
}
|
|
490
|
+
const reducerKeys = Object.keys(spec);
|
|
491
|
+
const out = [];
|
|
492
|
+
for (const bucket of buckets.values()) {
|
|
493
|
+
const state = {};
|
|
494
|
+
for (const rk of reducerKeys) {
|
|
495
|
+
state[rk] = spec[rk].init();
|
|
496
|
+
}
|
|
497
|
+
for (const record of bucket.records) {
|
|
498
|
+
for (const rk of reducerKeys) {
|
|
499
|
+
state[rk] = spec[rk].step(state[rk], record);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
const row = {};
|
|
503
|
+
for (const f of fields) {
|
|
504
|
+
row[f] = bucket.keyValues[f];
|
|
505
|
+
}
|
|
506
|
+
for (const rk of reducerKeys) {
|
|
507
|
+
row[rk] = spec[rk].finalize(state[rk]);
|
|
508
|
+
}
|
|
509
|
+
out.push(row);
|
|
510
|
+
}
|
|
511
|
+
return out;
|
|
512
|
+
}
|
|
513
|
+
var GROUPBY_WARN_CARDINALITY, GROUPBY_MAX_CARDINALITY, warnedCardinalityFields;
|
|
514
|
+
var init_groupby = __esm({
|
|
515
|
+
"src/aggregate/groupby.ts"() {
|
|
516
|
+
"use strict";
|
|
517
|
+
init_predicate();
|
|
518
|
+
init_canonical_key();
|
|
519
|
+
init_errors();
|
|
520
|
+
GROUPBY_WARN_CARDINALITY = 1e4;
|
|
521
|
+
GROUPBY_MAX_CARDINALITY = 1e5;
|
|
522
|
+
warnedCardinalityFields = /* @__PURE__ */ new Set();
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
// src/materialized-views/executor.ts
|
|
527
|
+
var executor_exports = {};
|
|
528
|
+
__export(executor_exports, {
|
|
529
|
+
MaterializedViewExecutor: () => MaterializedViewExecutor
|
|
530
|
+
});
|
|
531
|
+
async function materializeQueryResult(q, mvName) {
|
|
532
|
+
if (typeof q?.toArray === "function") {
|
|
533
|
+
return await q.toArray();
|
|
534
|
+
}
|
|
535
|
+
if (typeof q?.run === "function") {
|
|
536
|
+
const result = await Promise.resolve(q.run());
|
|
537
|
+
if (Array.isArray(result)) {
|
|
538
|
+
return result;
|
|
539
|
+
}
|
|
540
|
+
return [result];
|
|
541
|
+
}
|
|
542
|
+
throw new Error(
|
|
543
|
+
`MV "${mvName}": query() must return a Query<T>, Aggregation, or GroupedAggregation. Got something without a .toArray() or .run() terminal.`
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
async function materializeUnionResult(spec, db) {
|
|
547
|
+
const unified = [];
|
|
548
|
+
for (const arm of spec.unionSources) {
|
|
549
|
+
const coll = db.collection(arm.collection);
|
|
550
|
+
const sourceRows = coll.query().toArray();
|
|
551
|
+
for (const r of sourceRows) {
|
|
552
|
+
unified.push(arm.map(r));
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (!spec.groupBy) return unified;
|
|
556
|
+
const groupFields = typeof spec.groupBy === "string" ? [spec.groupBy] : spec.groupBy;
|
|
557
|
+
if (!spec.aggregate) {
|
|
558
|
+
const seen = /* @__PURE__ */ new Map();
|
|
559
|
+
for (const row of unified) {
|
|
560
|
+
const k = canonicalGroupKey(groupFields, row);
|
|
561
|
+
if (!seen.has(k)) seen.set(k, row);
|
|
562
|
+
}
|
|
563
|
+
return [...seen.values()];
|
|
564
|
+
}
|
|
565
|
+
return groupAndReduce(unified, groupFields, spec.aggregate);
|
|
566
|
+
}
|
|
567
|
+
async function listOutputIds(outputColl) {
|
|
568
|
+
const cAny = outputColl;
|
|
569
|
+
const adapter = cAny.adapter;
|
|
570
|
+
const vault = cAny.vault;
|
|
571
|
+
const name = cAny.name;
|
|
572
|
+
if (typeof adapter?.list !== "function") return [];
|
|
573
|
+
try {
|
|
574
|
+
const ids = await adapter.list(vault, name);
|
|
575
|
+
return [...ids];
|
|
576
|
+
} catch {
|
|
577
|
+
return [];
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
var DEFAULT_MAX_ROWS, MaterializedViewExecutor;
|
|
581
|
+
var init_executor = __esm({
|
|
582
|
+
"src/materialized-views/executor.ts"() {
|
|
583
|
+
"use strict";
|
|
584
|
+
init_errors();
|
|
585
|
+
init_registry();
|
|
586
|
+
init_groupby();
|
|
587
|
+
init_canonical_key();
|
|
588
|
+
DEFAULT_MAX_ROWS = 1e5;
|
|
589
|
+
MaterializedViewExecutor = {
|
|
590
|
+
async refresh(reg, accessor) {
|
|
591
|
+
const spec = reg.spec;
|
|
592
|
+
const outputColl = accessor.getCollection(reg.outputCollection);
|
|
593
|
+
const maxRows = spec.maxRows ?? DEFAULT_MAX_ROWS;
|
|
594
|
+
const onEmpty = spec.onEmpty ?? "delete";
|
|
595
|
+
const strict = spec.strict ?? false;
|
|
596
|
+
const baseCtx = accessor.getQueryContext();
|
|
597
|
+
const ctxForQuery = spec.predicates ? wrapDbWithPredicates(baseCtx, spec.predicates) : baseCtx;
|
|
598
|
+
let rows;
|
|
599
|
+
if (spec.unionSources) {
|
|
600
|
+
rows = await materializeUnionResult(spec, ctxForQuery);
|
|
601
|
+
} else {
|
|
602
|
+
const q = spec.query(ctxForQuery);
|
|
603
|
+
rows = await materializeQueryResult(q, spec.name);
|
|
604
|
+
}
|
|
605
|
+
if (rows.length > maxRows) {
|
|
606
|
+
throw new MaterializedViewTooLargeError(spec.name, rows.length, maxRows);
|
|
607
|
+
}
|
|
608
|
+
const txCtx = accessor.getActiveTxContext();
|
|
609
|
+
const adapter = outputColl.adapter;
|
|
610
|
+
const vaultName = outputColl.vault;
|
|
611
|
+
const newIds = /* @__PURE__ */ new Set();
|
|
612
|
+
const enrichedRows = [];
|
|
613
|
+
for (const row of rows) {
|
|
614
|
+
const id = spec.rowKey(row);
|
|
615
|
+
newIds.add(id);
|
|
616
|
+
const meta = {
|
|
617
|
+
mvName: spec.name,
|
|
618
|
+
queryHash: reg.queryHash,
|
|
619
|
+
sourceVersions: {},
|
|
620
|
+
materializedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
621
|
+
};
|
|
622
|
+
enrichedRows.push({ id, record: { ...row, _materializedFrom: meta } });
|
|
623
|
+
}
|
|
624
|
+
let written = 0;
|
|
625
|
+
let failed = 0;
|
|
626
|
+
for (const { id, record } of enrichedRows) {
|
|
627
|
+
try {
|
|
628
|
+
if (txCtx !== null) {
|
|
629
|
+
const prior = await adapter.get(vaultName, reg.outputCollection, id);
|
|
630
|
+
txCtx._executed.push({
|
|
631
|
+
op: { type: "put", vaultName, collectionName: reg.outputCollection, id },
|
|
632
|
+
priorEnvelope: prior
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
await outputColl.put(id, record);
|
|
636
|
+
written++;
|
|
637
|
+
} catch (err) {
|
|
638
|
+
failed++;
|
|
639
|
+
if (strict) throw err;
|
|
640
|
+
console.warn(`[mv] "${spec.name}" row write failed:`, err);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
let deleted = 0;
|
|
644
|
+
if (onEmpty === "delete") {
|
|
645
|
+
const priorIds = await listOutputIds(outputColl);
|
|
646
|
+
for (const priorId of priorIds) {
|
|
647
|
+
if (newIds.has(priorId)) continue;
|
|
648
|
+
try {
|
|
649
|
+
const outAny = outputColl;
|
|
650
|
+
if (typeof outAny._internalDelete === "function") {
|
|
651
|
+
await outAny._internalDelete(priorId, txCtx);
|
|
652
|
+
deleted++;
|
|
653
|
+
} else {
|
|
654
|
+
await outputColl.delete(priorId);
|
|
655
|
+
deleted++;
|
|
656
|
+
}
|
|
657
|
+
} catch (err) {
|
|
658
|
+
failed++;
|
|
659
|
+
if (strict) throw err;
|
|
660
|
+
console.warn(`[mv] "${spec.name}" tombstone failed for id="${priorId}":`, err);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return { written, deleted, failed };
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
// src/materialized-views/index.ts
|
|
671
|
+
var materialized_views_exports = {};
|
|
672
|
+
__export(materialized_views_exports, {
|
|
673
|
+
MaterializedViewConfigError: () => MaterializedViewConfigError,
|
|
674
|
+
MaterializedViewCycleError: () => MaterializedViewCycleError,
|
|
675
|
+
MaterializedViewExecutor: () => MaterializedViewExecutor,
|
|
676
|
+
MaterializedViewRegistry: () => MaterializedViewRegistry,
|
|
677
|
+
MaterializedViewSourceUnknownError: () => MaterializedViewSourceUnknownError,
|
|
678
|
+
MaterializedViewTooLargeError: () => MaterializedViewTooLargeError,
|
|
679
|
+
analyzeDependencies: () => analyzeDependencies,
|
|
680
|
+
canonicalizeQueryPlan: () => canonicalizeQueryPlan,
|
|
681
|
+
clearMVStale: () => clearMVStale,
|
|
682
|
+
computeQueryHash: () => computeQueryHash,
|
|
683
|
+
isMVStale: () => isMVStale,
|
|
684
|
+
markMVStale: () => markMVStale,
|
|
685
|
+
resolveStaleMVOnRead: () => resolveStaleMVOnRead,
|
|
686
|
+
summarizeQueryPlan: () => summarizeQueryPlan,
|
|
687
|
+
withMaterializedView: () => withMaterializedView
|
|
688
|
+
});
|
|
689
|
+
module.exports = __toCommonJS(materialized_views_exports);
|
|
690
|
+
|
|
691
|
+
// src/materialized-views/with-materialized-view.ts
|
|
692
|
+
init_errors();
|
|
693
|
+
function withMaterializedView(spec) {
|
|
694
|
+
if (!spec.name || spec.name.length === 0) {
|
|
695
|
+
throw new ValidationError("withMaterializedView: name is required");
|
|
696
|
+
}
|
|
697
|
+
if (spec.query && spec.unionSources) {
|
|
698
|
+
throw new MaterializedViewConfigError(
|
|
699
|
+
"query and unionSources are mutually exclusive \u2014 pick one"
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
if (!spec.query && !spec.unionSources) {
|
|
703
|
+
throw new MaterializedViewConfigError(
|
|
704
|
+
"strategy must declare either query or unionSources"
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
if (spec.query !== void 0 && typeof spec.query !== "function") {
|
|
708
|
+
throw new ValidationError("withMaterializedView: query must be a function returning a Query<T>");
|
|
709
|
+
}
|
|
710
|
+
if (spec.unionSources) {
|
|
711
|
+
if (spec.unionSources.length < 2) {
|
|
712
|
+
throw new MaterializedViewConfigError(
|
|
713
|
+
"unionSources requires at least 2 source collections"
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
const seen = /* @__PURE__ */ new Set();
|
|
717
|
+
for (const s of spec.unionSources) {
|
|
718
|
+
if (typeof s?.collection !== "string" || s.collection.length === 0) {
|
|
719
|
+
throw new MaterializedViewConfigError(
|
|
720
|
+
"each unionSources entry must declare a non-empty `collection` string"
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
if (typeof s.map !== "function") {
|
|
724
|
+
throw new MaterializedViewConfigError(
|
|
725
|
+
`unionSources entry for "${s.collection}" is missing a \`map\` function`
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
if (seen.has(s.collection)) {
|
|
729
|
+
throw new MaterializedViewConfigError(
|
|
730
|
+
`unionSources must reference distinct collections (duplicate: "${s.collection}")`
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
seen.add(s.collection);
|
|
734
|
+
}
|
|
735
|
+
if (Array.isArray(spec.groupBy) && spec.groupBy.length === 0) {
|
|
736
|
+
throw new MaterializedViewConfigError(
|
|
737
|
+
`withMaterializedView "${spec.name}": groupBy must not be an empty array \u2014 omit it or provide at least one field name`
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
if (spec.aggregate && !spec.groupBy) {
|
|
741
|
+
throw new MaterializedViewConfigError(
|
|
742
|
+
`withMaterializedView "${spec.name}": UNION strategy with aggregate requires groupBy \u2014 use groupBy to declare the bucketing keys, or remove aggregate for a pure dedup MV`
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
if (spec.predicates) {
|
|
746
|
+
throw new MaterializedViewConfigError(
|
|
747
|
+
`withMaterializedView "${spec.name}": predicates are not supported on UNION strategies \u2014 UNION mode does not use a Query<T> chain, so .wherePredicate() cannot fire. Use the query() form, or open an issue if per-arm predicates are needed`
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
if (typeof spec.rowKey !== "function") {
|
|
752
|
+
throw new ValidationError("withMaterializedView: rowKey is required (no default; see spec \xA7 Type surface)");
|
|
753
|
+
}
|
|
754
|
+
if (spec.refresh !== "eager" && spec.refresh !== "lazy" && spec.refresh !== "manual") {
|
|
755
|
+
throw new ValidationError(
|
|
756
|
+
`withMaterializedView: refresh must be 'eager' | 'lazy' | 'manual', got "${String(spec.refresh)}"`
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
return {
|
|
760
|
+
__noydb_strategy: "materialized-view",
|
|
761
|
+
spec
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/materialized-views/index.ts
|
|
766
|
+
init_registry();
|
|
767
|
+
init_executor();
|
|
768
|
+
init_dependency_analyzer();
|
|
769
|
+
init_query_hash();
|
|
770
|
+
|
|
771
|
+
// src/materialized-views/stale.ts
|
|
772
|
+
var _staleByRegistry = /* @__PURE__ */ new WeakMap();
|
|
773
|
+
function markMVStale(registry, mvName) {
|
|
774
|
+
let set = _staleByRegistry.get(registry);
|
|
775
|
+
if (!set) {
|
|
776
|
+
set = /* @__PURE__ */ new Set();
|
|
777
|
+
_staleByRegistry.set(registry, set);
|
|
778
|
+
}
|
|
779
|
+
set.add(mvName);
|
|
780
|
+
}
|
|
781
|
+
function isMVStale(registry, mvName) {
|
|
782
|
+
return _staleByRegistry.get(registry)?.has(mvName) ?? false;
|
|
783
|
+
}
|
|
784
|
+
async function resolveStaleMVOnRead(accessor, outputCollection) {
|
|
785
|
+
const registry = accessor.registry();
|
|
786
|
+
const pending = _staleByRegistry.get(registry);
|
|
787
|
+
if (!pending || pending.size === 0) return;
|
|
788
|
+
const candidates = [];
|
|
789
|
+
for (const mv of registry.all()) {
|
|
790
|
+
if (mv.outputCollection !== outputCollection) continue;
|
|
791
|
+
if (!pending.has(mv.spec.name)) continue;
|
|
792
|
+
candidates.push(mv.spec.name);
|
|
793
|
+
}
|
|
794
|
+
if (candidates.length === 0) return;
|
|
795
|
+
let executor = null;
|
|
796
|
+
for (const name of candidates) {
|
|
797
|
+
const reg = registry.byName(name);
|
|
798
|
+
if (!reg) {
|
|
799
|
+
pending.delete(name);
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
if (executor === null) {
|
|
803
|
+
({ MaterializedViewExecutor: executor } = await Promise.resolve().then(() => (init_executor(), executor_exports)));
|
|
804
|
+
}
|
|
805
|
+
await executor.refresh(reg, {
|
|
806
|
+
getCollection: (n) => accessor.getCollection(n),
|
|
807
|
+
getActiveTxContext: () => accessor.getActiveTxContext(),
|
|
808
|
+
getQueryContext: () => accessor.getQueryContext()
|
|
809
|
+
});
|
|
810
|
+
pending.delete(name);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
function clearMVStale(registry, mvName) {
|
|
814
|
+
_staleByRegistry.get(registry)?.delete(mvName);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// src/materialized-views/index.ts
|
|
818
|
+
init_errors();
|
|
819
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
820
|
+
0 && (module.exports = {
|
|
821
|
+
MaterializedViewConfigError,
|
|
822
|
+
MaterializedViewCycleError,
|
|
823
|
+
MaterializedViewExecutor,
|
|
824
|
+
MaterializedViewRegistry,
|
|
825
|
+
MaterializedViewSourceUnknownError,
|
|
826
|
+
MaterializedViewTooLargeError,
|
|
827
|
+
analyzeDependencies,
|
|
828
|
+
canonicalizeQueryPlan,
|
|
829
|
+
clearMVStale,
|
|
830
|
+
computeQueryHash,
|
|
831
|
+
isMVStale,
|
|
832
|
+
markMVStale,
|
|
833
|
+
resolveStaleMVOnRead,
|
|
834
|
+
summarizeQueryPlan,
|
|
835
|
+
withMaterializedView
|
|
836
|
+
});
|
|
837
|
+
//# sourceMappingURL=index.cjs.map
|