trellis 3.2.0 → 3.2.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/CHANGELOG.md +7 -0
- package/README.md +61 -171
- package/dist/browser/index.d.ts +18 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +95 -0
- package/dist/{chunk-3ETIIPPB.js → chunk-2W3MHTMG.js} +190 -27
- package/dist/{chunk-MBNBTJCR.js → chunk-6GEZ6DN5.js} +2 -2
- package/dist/chunk-7ZFRVCUE.js +211 -0
- package/dist/chunk-CAG4FULI.js +121 -0
- package/dist/{chunk-GD5QJXSS.js → chunk-FWRS4CSC.js} +2 -2
- package/dist/{chunk-36VSSTL7.js → chunk-GU374KWZ.js} +1 -1
- package/dist/{chunk-3GDRZTAW.js → chunk-H6JB3PZ3.js} +1 -1
- package/dist/{chunk-ZHVI7BQN.js → chunk-IZX2PLIB.js} +191 -28
- package/dist/chunk-J2TXUFCZ.js +289 -0
- package/dist/{chunk-BJ7EZ7FP.js → chunk-JHHEXEDM.js} +17 -0
- package/dist/{chunk-X762XWVC.js → chunk-KIJITUZB.js} +1 -1
- package/dist/{chunk-7WQGTFVJ.js → chunk-L5N5PR2S.js} +130 -75
- package/dist/{chunk-BXSTJVE3.js → chunk-N6MYZT4O.js} +2 -1
- package/dist/{chunk-KKDRMIV6.js → chunk-QB5ISE47.js} +1 -1
- package/dist/{chunk-HO4TURXW.js → chunk-S3XLFZ27.js} +28 -11
- package/dist/{chunk-BQBUVMRN.js → chunk-S6GBJ2ZB.js} +25 -14
- package/dist/{chunk-IUWVEEOX.js → chunk-SGMDTWJJ.js} +4 -0
- package/dist/{chunk-FZVCXDYW.js → chunk-UQISRW6T.js} +65 -12
- package/dist/{chunk-B3ZIFA2O.js → chunk-VBYUTRXX.js} +1 -1
- package/dist/cli/deploy-cli.d.ts +18 -0
- package/dist/cli/deploy-cli.d.ts.map +1 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +153 -100
- package/dist/client/index.browser.d.ts +29 -0
- package/dist/client/index.browser.d.ts.map +1 -0
- package/dist/client/index.browser.js +64 -0
- package/dist/client/index.js +10 -10
- package/dist/client/sdk.browser.d.ts +104 -0
- package/dist/client/sdk.browser.d.ts.map +1 -0
- package/dist/{sdk-LZ7W3EEY.js → client/sdk.browser.js} +2 -2
- package/dist/client/sdk.js +1 -1
- package/dist/core/index.js +6 -6
- package/dist/db/index.js +18 -18
- package/dist/db/inspector.js +6 -6
- package/dist/db/trellis.css +1 -1
- package/dist/decisions/index.js +2 -2
- package/dist/deploy-LQ7GUGJ5.js +9 -0
- package/dist/{deploy-66EC752W.js → deploy-YNVG5E4E.js} +1 -1
- package/dist/deploy-cli-24BCHAXY.js +67 -0
- package/dist/deploy-cli-C35HTITO.js +67 -0
- package/dist/embeddings/auto-embed.d.ts.map +1 -1
- package/dist/embeddings/index.js +1 -1
- package/dist/embeddings/store.d.ts.map +1 -1
- package/dist/engine.d.ts +27 -7
- package/dist/engine.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/react/index.js +2 -2
- package/dist/react/schema-hooks.js +7 -7
- package/dist/{remote-manager-SG545WNY.js → remote-manager-ODE3T2KR.js} +5 -5
- package/dist/scaffold/seed.d.ts.map +1 -1
- package/dist/scaffold/write.d.ts +1 -1
- package/dist/scaffold/write.d.ts.map +1 -1
- package/dist/schema/index.js +4 -3
- package/dist/server/deploy-meta.d.ts +25 -0
- package/dist/server/deploy-meta.d.ts.map +1 -0
- package/dist/server/deploy.d.ts +7 -2
- package/dist/server/deploy.d.ts.map +1 -1
- package/dist/server/index.d.ts +5 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +52 -18
- package/dist/server/realtime.d.ts +4 -1
- package/dist/server/realtime.d.ts.map +1 -1
- package/dist/server/server.d.ts +1 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/sprites.d.ts +18 -1
- package/dist/server/sprites.d.ts.map +1 -1
- package/dist/server/tenancy.d.ts +4 -0
- package/dist/server/tenancy.d.ts.map +1 -1
- package/dist/server/usage-meter.d.ts +53 -0
- package/dist/server/usage-meter.d.ts.map +1 -0
- package/dist/{server-IGBIIJSF.js → server-MB3OPSPI.js} +6 -1
- package/dist/{server-BOH7M4TT.js → server-ZVWGLLRF.js} +7 -2
- package/dist/sprites-KQGTGM6W.js +27 -0
- package/dist/{tenancy-S676GLID.js → tenancy-NBLLGCNJ.js} +1 -1
- package/dist/{tenancy-OP34NFFS.js → tenancy-POMWQP6M.js} +5 -3
- package/dist/vcs/index.js +3 -3
- package/dist/vcs/types.d.ts +2 -0
- package/dist/vcs/types.d.ts.map +1 -1
- package/package.json +17 -6
- package/dist/better-sqlite-backend-PII64VE6.js +0 -159
- package/dist/better-sqlite-backend-VUBVQ7SN.js +0 -209
- package/dist/better-sqlite-backend-Y3AQN65E.js +0 -159
- package/dist/better-sqlite-backend-Y3M7DXOX.js +0 -159
- package/dist/boot-middleware-FJDGVUM4.js +0 -9
- package/dist/boot-middleware-MLJUTLUM.js +0 -9
- package/dist/boot-middleware-ZZPDZNYR.js +0 -9
- package/dist/chunk-276LKLZR.js +0 -359
- package/dist/chunk-2CX5UCRV.js +0 -111
- package/dist/chunk-2ESYSVXG.js +0 -48
- package/dist/chunk-2I5JU73Y.js +0 -340
- package/dist/chunk-2UNSRRNB.js +0 -171
- package/dist/chunk-335UEHQR.js +0 -907
- package/dist/chunk-4CF2L46N.js +0 -29
- package/dist/chunk-5CINODKT.js +0 -41
- package/dist/chunk-5ESGWJ5L.js +0 -45
- package/dist/chunk-5FGLQGMF.js +0 -1676
- package/dist/chunk-5IQZFITY.js +0 -357
- package/dist/chunk-5JWFAQDW.js +0 -150
- package/dist/chunk-62UHTVU3.js +0 -141
- package/dist/chunk-66UJQT2I.js +0 -35
- package/dist/chunk-6RNNVOAS.js +0 -355
- package/dist/chunk-6X23PZ6H.js +0 -969
- package/dist/chunk-7C7LL6MB.js +0 -315
- package/dist/chunk-7F4HM3JG.js +0 -394
- package/dist/chunk-7KJ7T4D3.js +0 -910
- package/dist/chunk-7QQQNUB5.js +0 -92
- package/dist/chunk-7QWJBS72.js +0 -946
- package/dist/chunk-7R36OZK3.js +0 -957
- package/dist/chunk-7UPKCOHE.js +0 -946
- package/dist/chunk-A3BONW34.js +0 -35
- package/dist/chunk-AHMDVHBL.js +0 -141
- package/dist/chunk-AW6A4OZY.js +0 -171
- package/dist/chunk-BJ5EKCJ5.js +0 -458
- package/dist/chunk-BRGYDO7D.js +0 -223
- package/dist/chunk-C5KPSEDN.js +0 -359
- package/dist/chunk-CCTNYTUC.js +0 -940
- package/dist/chunk-CYQ2OVD2.js +0 -35
- package/dist/chunk-DDAK7YOE.js +0 -299
- package/dist/chunk-DRILKBY6.js +0 -965
- package/dist/chunk-DXE24HNN.js +0 -45
- package/dist/chunk-E7ICUNW7.js +0 -945
- package/dist/chunk-EAL2PUOE.js +0 -10022
- package/dist/chunk-ELT7MLZB.js +0 -109
- package/dist/chunk-EWEIRF6G.js +0 -171
- package/dist/chunk-F5VDGQJC.js +0 -120
- package/dist/chunk-FCPL3IEQ.js +0 -98
- package/dist/chunk-FDPKYSV7.js +0 -96
- package/dist/chunk-FES3TC24.js +0 -171
- package/dist/chunk-FOWKR5TW.js +0 -129
- package/dist/chunk-G3XIHPSQ.js +0 -361
- package/dist/chunk-GKJLYLVU.js +0 -359
- package/dist/chunk-GNXHXNVQ.js +0 -113
- package/dist/chunk-GSR6ILTM.js +0 -277
- package/dist/chunk-GWRIMRSX.js +0 -45
- package/dist/chunk-H72UW2HJ.js +0 -45
- package/dist/chunk-HGI74PXB.js +0 -7417
- package/dist/chunk-HGRA37LO.js +0 -145
- package/dist/chunk-HXSRQO3L.js +0 -145
- package/dist/chunk-IG2X2BVO.js +0 -373
- package/dist/chunk-IIJVDD54.js +0 -373
- package/dist/chunk-IX4BBOIT.js +0 -394
- package/dist/chunk-IXU4RMQM.js +0 -349
- package/dist/chunk-J6OUIION.js +0 -857
- package/dist/chunk-K46RNFDE.js +0 -171
- package/dist/chunk-KAGU2XMY.js +0 -359
- package/dist/chunk-KCVYJNVX.js +0 -394
- package/dist/chunk-KYLIQ3YI.js +0 -399
- package/dist/chunk-KZYJJIRN.js +0 -946
- package/dist/chunk-LFOX744K.js +0 -96
- package/dist/chunk-LHOVJEWR.js +0 -7403
- package/dist/chunk-LOPXTPF3.js +0 -120
- package/dist/chunk-LZR3VIFU.js +0 -907
- package/dist/chunk-MCKGQKYU.js +0 -15
- package/dist/chunk-MU6EOS6F.js +0 -0
- package/dist/chunk-MWRQ2TXT.js +0 -357
- package/dist/chunk-NO73OK2I.js +0 -102
- package/dist/chunk-NPE2YJHK.js +0 -394
- package/dist/chunk-NSDXU6QX.js +0 -2213
- package/dist/chunk-NXIRDSZ6.js +0 -145
- package/dist/chunk-NZWFNHCS.js +0 -407
- package/dist/chunk-OB4CMMX6.js +0 -260
- package/dist/chunk-OBKU74FU.js +0 -7411
- package/dist/chunk-OQNDWRDE.js +0 -145
- package/dist/chunk-OVWAJMB6.js +0 -394
- package/dist/chunk-PDHLBTTD.js +0 -45
- package/dist/chunk-PEQNRKLG.js +0 -210
- package/dist/chunk-QERIS7QY.js +0 -7403
- package/dist/chunk-QHA2DM3C.js +0 -373
- package/dist/chunk-QLM4HSYC.js +0 -45
- package/dist/chunk-S2OT3TQJ.js +0 -283
- package/dist/chunk-S5PHSN5K.js +0 -907
- package/dist/chunk-SIAA4J6H.js +0 -21
- package/dist/chunk-SQBNLUIA.js +0 -171
- package/dist/chunk-STLN6A6B.js +0 -355
- package/dist/chunk-U7HV7GU2.js +0 -17
- package/dist/chunk-UGZHOGRA.js +0 -39
- package/dist/chunk-UICF66FS.js +0 -359
- package/dist/chunk-UK3EQREE.js +0 -1078
- package/dist/chunk-UPKPURVY.js +0 -394
- package/dist/chunk-URC74VCN.js +0 -959
- package/dist/chunk-V3G7Q3QQ.js +0 -102
- package/dist/chunk-VUJ6SA55.js +0 -303
- package/dist/chunk-VUJQLIML.js +0 -141
- package/dist/chunk-VWKLKLYB.js +0 -267
- package/dist/chunk-VYC3RZMK.js +0 -277
- package/dist/chunk-WMERMSWI.js +0 -154
- package/dist/chunk-WMR777PO.js +0 -2213
- package/dist/chunk-X46IECAC.js +0 -517
- package/dist/chunk-XEN3OEYY.js +0 -2234
- package/dist/chunk-XZQ66K7A.js +0 -124
- package/dist/chunk-YY3GF7DH.js +0 -7411
- package/dist/chunk-ZE5R3H4C.js +0 -907
- package/dist/chunk-ZMN5LUB7.js +0 -7411
- package/dist/chunk-ZUV4MD5U.js +0 -94
- package/dist/config-KMY6RRK3.js +0 -19
- package/dist/config-VDMK2DUJ.js +0 -19
- package/dist/deploy-SO7AFK6L.js +0 -9
- package/dist/engine-7GXBM4IT.js +0 -7
- package/dist/engine-NSR6BIF7.js +0 -7
- package/dist/factory-2P64OC7M.js +0 -34
- package/dist/factory-YP4ZZK5K.js +0 -34
- package/dist/import-AJLYHFIA.js +0 -10
- package/dist/launch-explorer-AI6L2AWJ.js +0 -91
- package/dist/query-GJL3CVGN.js +0 -31
- package/dist/query-MBJSK3EB.js +0 -31
- package/dist/query-TJHEVSKL.js +0 -31
- package/dist/query-WUIKOHPE.js +0 -524
- package/dist/remote-manager-BLJZ6KF6.js +0 -224
- package/dist/remote-manager-BPYE46SI.js +0 -223
- package/dist/remote-manager-FMESEAFA.js +0 -224
- package/dist/remote-manager-K2WZ5RHY.js +0 -223
- package/dist/remote-manager-SG5PQKCH.js +0 -224
- package/dist/remote-manager-WGP6MIQZ.js +0 -224
- package/dist/sdk-3KBB3E6V.js +0 -16
- package/dist/sdk-4ZQO6UTU.js +0 -16
- package/dist/sdk-6JA6GV3G.js +0 -16
- package/dist/sdk-6VTP5UTZ.js +0 -16
- package/dist/sdk-7VS6TYCD.js +0 -16
- package/dist/sdk-HQ3FTLRT.js +0 -16
- package/dist/sdk-HZ7NU3XE.js +0 -15
- package/dist/sdk-KBQE2OXT.js +0 -16
- package/dist/sdk-MSCDZ7JU.js +0 -15
- package/dist/sdk-WNZOGO3S.js +0 -15
- package/dist/sdk-YLV33ZCW.js +0 -9
- package/dist/sdk-ZSUD2LLJ.js +0 -16
- package/dist/server-AFXFFBWA.js +0 -11
- package/dist/server-AGHC4VUL.js +0 -11
- package/dist/server-DBUY2KY5.js +0 -11
- package/dist/server-GWBQJPRG.js +0 -11
- package/dist/server-JZZFULVU.js +0 -11
- package/dist/server-L5XAF4M5.js +0 -11
- package/dist/server-NYFUHANA.js +0 -12
- package/dist/server-OABUB2LO.js +0 -12
- package/dist/server-P2CKZWWV.js +0 -12
- package/dist/sprites-XVSMQLSI.js +0 -15
- package/dist/sql-wasm-FORCZFHZ.js +0 -2148
- package/dist/sql-wasm-JZH76RLO.js +0 -2148
- package/dist/sqljs-backend-CJQOIQYE.js +0 -266
- package/dist/sqljs-backend-KWXBUDUO.js +0 -266
- package/dist/state-demo.bundle.js +0 -1033
- package/dist/tenancy-CBWYEVUB.js +0 -11
- package/dist/tenancy-FO6WVYFN.js +0 -13
- package/dist/tenancy-FWDBESMX.js +0 -14
- package/dist/tenancy-HGPIS5QA.js +0 -13
- package/dist/tenancy-HXFALA37.js +0 -14
- package/dist/tenancy-V7ARWNNV.js +0 -14
- package/dist/tenancy-ZDN4GXK5.js +0 -13
- package/dist/tenancy-ZT5VMY7B.js +0 -14
- package/dist/vm-config-YZRJ3KUB.js +0 -21
- /package/dist/{chunk-2BRTCEHB.js → chunk-PZ4XYZ4J.js} +0 -0
- /package/dist/{chunk-EO3J7GRP.js → chunk-WXSWA3MV.js} +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// src/server/sprites.ts
|
|
2
|
+
import { execFile, spawn } from "child_process";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
var execFileAsync = promisify(execFile);
|
|
5
|
+
async function runSpriteCmd(args) {
|
|
6
|
+
try {
|
|
7
|
+
const { stdout } = await execFileAsync("sprite", args);
|
|
8
|
+
return stdout.trim();
|
|
9
|
+
} catch (err) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
`sprite ${args[0]} failed (exit ${err.code ?? "?"}): ${err.stderr ?? err.message}`
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async function runSpriteExec(spriteName, shellCommand, extraArgs = []) {
|
|
16
|
+
return runSpriteCmd([
|
|
17
|
+
"exec",
|
|
18
|
+
"-s",
|
|
19
|
+
spriteName,
|
|
20
|
+
...extraArgs,
|
|
21
|
+
"--",
|
|
22
|
+
"bash",
|
|
23
|
+
"-c",
|
|
24
|
+
shellCommand
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
async function runSpriteCopy(localPath, spriteName, remotePath) {
|
|
28
|
+
try {
|
|
29
|
+
await execFileAsync("sprite", [
|
|
30
|
+
"exec",
|
|
31
|
+
"-s",
|
|
32
|
+
spriteName,
|
|
33
|
+
"--file",
|
|
34
|
+
`${localPath}:${remotePath}`,
|
|
35
|
+
"echo",
|
|
36
|
+
"uploaded"
|
|
37
|
+
]);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`sprite file upload failed (exit ${err.code ?? "?"}): ${err.stderr ?? err.message}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
var SPRITE_ENSURE_BUN_SH = `
|
|
45
|
+
set -e
|
|
46
|
+
export PATH="$HOME/.bun/bin:$PATH"
|
|
47
|
+
if ! command -v bun >/dev/null 2>&1; then
|
|
48
|
+
curl -fsSL https://bun.sh/install | bash
|
|
49
|
+
export PATH="$HOME/.bun/bin:$PATH"
|
|
50
|
+
fi
|
|
51
|
+
command -v bun
|
|
52
|
+
`.trim();
|
|
53
|
+
function parseSpriteUrlOutput(stdout) {
|
|
54
|
+
const match = stdout.match(/URL:\s*(https:\/\/\S+)/i);
|
|
55
|
+
if (!match) {
|
|
56
|
+
throw new Error(`Could not parse sprite URL from output: ${stdout.trim()}`);
|
|
57
|
+
}
|
|
58
|
+
return match[1].replace(/\/$/, "");
|
|
59
|
+
}
|
|
60
|
+
async function resolveSpritePublicUrl(spriteName) {
|
|
61
|
+
const out = await runSpriteCmd(["url", "-s", spriteName]);
|
|
62
|
+
return parseSpriteUrlOutput(out);
|
|
63
|
+
}
|
|
64
|
+
async function ensureSpritePublicAccess(spriteName) {
|
|
65
|
+
try {
|
|
66
|
+
await runSpriteCmd(["url", "update", "--auth", "public", "-s", spriteName]);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
const msg = String(err.message ?? err);
|
|
69
|
+
if (/already|unchanged|public/i.test(msg)) return;
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function ensureSprite(spriteName) {
|
|
74
|
+
try {
|
|
75
|
+
await runSpriteCmd(["create", spriteName, "--skip-console"]);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
const msg = String(err.message ?? err);
|
|
78
|
+
if (/already exists|duplicate|conflict/i.test(msg)) return;
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function runSpriteInteractive(args) {
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
const proc = spawn("sprite", args, { stdio: "inherit" });
|
|
85
|
+
proc.on("close", (code) => {
|
|
86
|
+
if (code !== 0)
|
|
87
|
+
reject(new Error(`sprite ${args[0]} failed (exit ${code})`));
|
|
88
|
+
else resolve();
|
|
89
|
+
});
|
|
90
|
+
proc.on("error", reject);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async function assertSpriteCli() {
|
|
94
|
+
await runSpriteCmd(["--version"]).catch(() => {
|
|
95
|
+
throw new Error(
|
|
96
|
+
"`sprite` CLI not found. Install it from https://docs.sprites.dev and authenticate."
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function resolveSprite(explicitName) {
|
|
101
|
+
if (explicitName) {
|
|
102
|
+
return explicitName;
|
|
103
|
+
}
|
|
104
|
+
throw new Error(
|
|
105
|
+
"No sprite specified. Use --sprite <name> or set an active sprite with `trellis vm use <name>`"
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export {
|
|
110
|
+
runSpriteCmd,
|
|
111
|
+
runSpriteExec,
|
|
112
|
+
runSpriteCopy,
|
|
113
|
+
SPRITE_ENSURE_BUN_SH,
|
|
114
|
+
parseSpriteUrlOutput,
|
|
115
|
+
resolveSpritePublicUrl,
|
|
116
|
+
ensureSpritePublicAccess,
|
|
117
|
+
ensureSprite,
|
|
118
|
+
runSpriteInteractive,
|
|
119
|
+
assertSpriteCli,
|
|
120
|
+
resolveSprite
|
|
121
|
+
};
|
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
init_merge,
|
|
11
11
|
init_milestone,
|
|
12
12
|
init_op_log
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-VBYUTRXX.js";
|
|
14
14
|
import {
|
|
15
15
|
init_types
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-N6MYZT4O.js";
|
|
17
17
|
import {
|
|
18
18
|
init_ops,
|
|
19
19
|
isVcsOp
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_TENANT
|
|
3
|
+
} from "./chunk-SGMDTWJJ.js";
|
|
1
4
|
import {
|
|
2
5
|
rel
|
|
3
6
|
} from "./chunk-44FFQKQX.js";
|
|
@@ -5,14 +8,14 @@ import {
|
|
|
5
8
|
entityRecordToPlain,
|
|
6
9
|
hydrateBindings,
|
|
7
10
|
resolveRelations
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-JUEMDWLU.js";
|
|
9
12
|
import {
|
|
10
13
|
parseSimple
|
|
11
14
|
} from "./chunk-X5FLTRUB.js";
|
|
12
15
|
|
|
13
16
|
// src/server/server.ts
|
|
14
|
-
import { existsSync, readFileSync } from "fs";
|
|
15
|
-
import { dirname, join } from "path";
|
|
17
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
18
|
+
import { dirname, join as join2 } from "path";
|
|
16
19
|
import { fileURLToPath } from "url";
|
|
17
20
|
|
|
18
21
|
// src/server/auth.ts
|
|
@@ -367,14 +370,124 @@ async function hydrateAndResolve(kernel, bindings, entityType, resolve) {
|
|
|
367
370
|
});
|
|
368
371
|
}
|
|
369
372
|
|
|
373
|
+
// src/server/usage-meter.ts
|
|
374
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
375
|
+
import { join } from "path";
|
|
376
|
+
var EMPTY_BUCKET = () => ({
|
|
377
|
+
graph_io: 0,
|
|
378
|
+
storage_bytes: 0,
|
|
379
|
+
egress_bytes: 0
|
|
380
|
+
});
|
|
381
|
+
function resolveUsageTenantId(tenantId) {
|
|
382
|
+
return tenantId ?? DEFAULT_TENANT;
|
|
383
|
+
}
|
|
384
|
+
function dayKey(date) {
|
|
385
|
+
return date.toISOString().slice(0, 10);
|
|
386
|
+
}
|
|
387
|
+
function dirSize(dir) {
|
|
388
|
+
let total = 0;
|
|
389
|
+
for (const ent of readdirSync(dir, { withFileTypes: true })) {
|
|
390
|
+
const p = join(dir, ent.name);
|
|
391
|
+
if (ent.isDirectory()) total += dirSize(p);
|
|
392
|
+
else if (ent.isFile()) total += statSync(p).size;
|
|
393
|
+
}
|
|
394
|
+
return total;
|
|
395
|
+
}
|
|
396
|
+
function sampleTenantStorage(pool, tenantId) {
|
|
397
|
+
let total = 0;
|
|
398
|
+
const sqlitePath = pool.dbFilePath(tenantId);
|
|
399
|
+
if (existsSync(sqlitePath)) {
|
|
400
|
+
total += statSync(sqlitePath).size;
|
|
401
|
+
}
|
|
402
|
+
const blobDir = join(pool.dataPath(), "blobs");
|
|
403
|
+
if (existsSync(blobDir)) {
|
|
404
|
+
total += dirSize(blobDir);
|
|
405
|
+
}
|
|
406
|
+
return total;
|
|
407
|
+
}
|
|
408
|
+
function verifyAdminKey(req) {
|
|
409
|
+
const expected = process.env.TURTLEDB_ADMIN_KEY;
|
|
410
|
+
if (!expected) return false;
|
|
411
|
+
const auth = req.headers.get("authorization");
|
|
412
|
+
if (auth?.startsWith("Bearer ")) {
|
|
413
|
+
return auth.slice("Bearer ".length) === expected;
|
|
414
|
+
}
|
|
415
|
+
return req.headers.get("x-turtledb-admin-key") === expected;
|
|
416
|
+
}
|
|
417
|
+
var UsageMeter = class {
|
|
418
|
+
buckets = /* @__PURE__ */ new Map();
|
|
419
|
+
storageSampledAt = /* @__PURE__ */ new Map();
|
|
420
|
+
now;
|
|
421
|
+
constructor(opts = {}) {
|
|
422
|
+
this.now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
423
|
+
}
|
|
424
|
+
recordGraphIo(tenantId, count = 1) {
|
|
425
|
+
if (count <= 0) return;
|
|
426
|
+
const bucket = this._bucket(tenantId);
|
|
427
|
+
bucket.graph_io += count;
|
|
428
|
+
}
|
|
429
|
+
recordEgress(tenantId, bytes) {
|
|
430
|
+
if (bytes <= 0) return;
|
|
431
|
+
const bucket = this._bucket(tenantId);
|
|
432
|
+
bucket.egress_bytes += bytes;
|
|
433
|
+
}
|
|
434
|
+
/** Set storage gauge for the current day (from sampler, not incremental). */
|
|
435
|
+
recordStorage(tenantId, bytes) {
|
|
436
|
+
const bucket = this._bucket(tenantId);
|
|
437
|
+
bucket.storage_bytes = bytes;
|
|
438
|
+
this.storageSampledAt.set(`${tenantId}:${this._today()}`, this.now().toISOString());
|
|
439
|
+
}
|
|
440
|
+
sampleStorage(pool, tenantId) {
|
|
441
|
+
const id = resolveUsageTenantId(tenantId);
|
|
442
|
+
const bytes = sampleTenantStorage(pool, id);
|
|
443
|
+
this.recordStorage(id, bytes);
|
|
444
|
+
return bytes;
|
|
445
|
+
}
|
|
446
|
+
getUsage(tenantId, day) {
|
|
447
|
+
const d = day ?? this._today();
|
|
448
|
+
const tenantBuckets = this.buckets.get(tenantId);
|
|
449
|
+
const meters = tenantBuckets?.get(d) ?? EMPTY_BUCKET();
|
|
450
|
+
const sampledAt = this.storageSampledAt.get(`${tenantId}:${d}`);
|
|
451
|
+
return {
|
|
452
|
+
tenantId,
|
|
453
|
+
day: d,
|
|
454
|
+
meters: { ...meters },
|
|
455
|
+
...sampledAt ? { storageSampledAt: sampledAt } : {}
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
/** All day keys recorded for a tenant (sorted ascending). */
|
|
459
|
+
listDays(tenantId) {
|
|
460
|
+
const tenantBuckets = this.buckets.get(tenantId);
|
|
461
|
+
if (!tenantBuckets) return [];
|
|
462
|
+
return Array.from(tenantBuckets.keys()).sort();
|
|
463
|
+
}
|
|
464
|
+
_today() {
|
|
465
|
+
return dayKey(this.now());
|
|
466
|
+
}
|
|
467
|
+
_bucket(tenantId) {
|
|
468
|
+
const id = resolveUsageTenantId(tenantId);
|
|
469
|
+
const d = this._today();
|
|
470
|
+
if (!this.buckets.has(id)) {
|
|
471
|
+
this.buckets.set(id, /* @__PURE__ */ new Map());
|
|
472
|
+
}
|
|
473
|
+
const tenantBuckets = this.buckets.get(id);
|
|
474
|
+
if (!tenantBuckets.has(d)) {
|
|
475
|
+
tenantBuckets.set(d, EMPTY_BUCKET());
|
|
476
|
+
}
|
|
477
|
+
return tenantBuckets.get(d);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
370
481
|
// src/server/realtime.ts
|
|
371
482
|
var SubscriptionManager = class {
|
|
372
483
|
clients = /* @__PURE__ */ new Map();
|
|
373
484
|
pool;
|
|
374
485
|
permissions;
|
|
375
|
-
|
|
486
|
+
meter;
|
|
487
|
+
constructor(pool, permissions = null, meter = null) {
|
|
376
488
|
this.pool = pool;
|
|
377
489
|
this.permissions = permissions;
|
|
490
|
+
this.meter = meter;
|
|
378
491
|
}
|
|
379
492
|
// -------------------------------------------------------------------------
|
|
380
493
|
// Client lifecycle
|
|
@@ -463,10 +576,11 @@ var SubscriptionManager = class {
|
|
|
463
576
|
});
|
|
464
577
|
return;
|
|
465
578
|
}
|
|
466
|
-
const kernel = this.pool.
|
|
579
|
+
const kernel = await this.pool.preload(tid);
|
|
467
580
|
let result;
|
|
468
581
|
try {
|
|
469
582
|
const qr = await kernel.query(parsedQuery);
|
|
583
|
+
this._recordGraphIo(tid);
|
|
470
584
|
result = await hydrateAndResolve(
|
|
471
585
|
kernel,
|
|
472
586
|
qr.bindings,
|
|
@@ -502,11 +616,12 @@ var SubscriptionManager = class {
|
|
|
502
616
|
});
|
|
503
617
|
}
|
|
504
618
|
async _pushUpdate(client, sub) {
|
|
505
|
-
const kernel = this.pool.
|
|
619
|
+
const kernel = await this.pool.preload(sub.tenantId);
|
|
506
620
|
let newResult;
|
|
507
621
|
try {
|
|
508
622
|
const parsed = parseSimple(sub.query);
|
|
509
623
|
const qr = await kernel.query(parsed);
|
|
624
|
+
this._recordGraphIo(sub.tenantId);
|
|
510
625
|
newResult = await hydrateAndResolve(
|
|
511
626
|
kernel,
|
|
512
627
|
qr.bindings,
|
|
@@ -530,9 +645,17 @@ var SubscriptionManager = class {
|
|
|
530
645
|
...resolved ? { resolved: true } : {}
|
|
531
646
|
});
|
|
532
647
|
}
|
|
648
|
+
_recordGraphIo(tenantId) {
|
|
649
|
+
this.meter?.recordGraphIo(resolveUsageTenantId(tenantId));
|
|
650
|
+
}
|
|
533
651
|
_send(client, payload) {
|
|
652
|
+
const data = JSON.stringify(payload);
|
|
653
|
+
if (this.meter) {
|
|
654
|
+
const tid = resolveUsageTenantId(client.tenantId ?? DEFAULT_TENANT);
|
|
655
|
+
this.meter.recordEgress(tid, new TextEncoder().encode(data).length);
|
|
656
|
+
}
|
|
534
657
|
try {
|
|
535
|
-
client.ws.send(
|
|
658
|
+
client.ws.send(data);
|
|
536
659
|
} catch {
|
|
537
660
|
}
|
|
538
661
|
}
|
|
@@ -573,7 +696,8 @@ function buildServerContext(opts) {
|
|
|
573
696
|
apiKey: config.apiKey,
|
|
574
697
|
allowPublic: true
|
|
575
698
|
};
|
|
576
|
-
const
|
|
699
|
+
const meter = new UsageMeter();
|
|
700
|
+
const subs = new SubscriptionManager(pool, permissions ?? null, meter);
|
|
577
701
|
const handleHttp = async (req) => {
|
|
578
702
|
const url = new URL(req.url);
|
|
579
703
|
const path = url.pathname;
|
|
@@ -587,6 +711,7 @@ function buildServerContext(opts) {
|
|
|
587
711
|
pool,
|
|
588
712
|
permissions: permissions ?? null,
|
|
589
713
|
subs,
|
|
714
|
+
meter,
|
|
590
715
|
authConfig,
|
|
591
716
|
config,
|
|
592
717
|
oauthProviders: opts.oauthProviders ?? {}
|
|
@@ -638,17 +763,20 @@ async function startServerNode(opts) {
|
|
|
638
763
|
}
|
|
639
764
|
});
|
|
640
765
|
}
|
|
766
|
+
function recordGraphIo(ctx, tenantId) {
|
|
767
|
+
ctx.meter.recordGraphIo(resolveUsageTenantId(tenantId));
|
|
768
|
+
}
|
|
641
769
|
async function route(req, url, path, auth, tenantId, ctx) {
|
|
642
770
|
const method = req.method.toUpperCase();
|
|
643
771
|
if (method === "GET" && path === "/__trellis/inspector.js") {
|
|
644
772
|
const candidates = [
|
|
645
|
-
|
|
773
|
+
join2(__moduleDir, "db", "inspector.js"),
|
|
646
774
|
// chunk lives in dist/, inspector in dist/db/
|
|
647
|
-
|
|
775
|
+
join2(__moduleDir, "inspector.js")
|
|
648
776
|
// chunk lives in dist/db/, inspector alongside
|
|
649
777
|
];
|
|
650
778
|
for (const p of candidates) {
|
|
651
|
-
if (
|
|
779
|
+
if (existsSync2(p)) {
|
|
652
780
|
return new Response(readFileSync(p, "utf-8"), {
|
|
653
781
|
headers: { "Content-Type": "application/javascript; charset=utf-8" }
|
|
654
782
|
});
|
|
@@ -663,11 +791,11 @@ async function route(req, url, path, auth, tenantId, ctx) {
|
|
|
663
791
|
}
|
|
664
792
|
if (method === "GET" && path === "/__trellis/trellis.css") {
|
|
665
793
|
const candidates = [
|
|
666
|
-
|
|
667
|
-
|
|
794
|
+
join2(__moduleDir, "db", "trellis.css"),
|
|
795
|
+
join2(__moduleDir, "trellis.css")
|
|
668
796
|
];
|
|
669
797
|
for (const p of candidates) {
|
|
670
|
-
if (
|
|
798
|
+
if (existsSync2(p)) {
|
|
671
799
|
return new Response(readFileSync(p, "utf-8"), {
|
|
672
800
|
headers: { "Content-Type": "text/css; charset=utf-8" }
|
|
673
801
|
});
|
|
@@ -695,7 +823,7 @@ async function route(req, url, path, auth, tenantId, ctx) {
|
|
|
695
823
|
});
|
|
696
824
|
}
|
|
697
825
|
if (method === "GET" && path === "/health") {
|
|
698
|
-
const kernel = ctx.pool.
|
|
826
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
699
827
|
const ops = kernel.readAllOps().length;
|
|
700
828
|
return json({
|
|
701
829
|
status: "ok",
|
|
@@ -703,6 +831,9 @@ async function route(req, url, path, auth, tenantId, ctx) {
|
|
|
703
831
|
tenants: ctx.pool.activeTenants().length
|
|
704
832
|
});
|
|
705
833
|
}
|
|
834
|
+
if (method === "GET" && path === "/admin/usage") {
|
|
835
|
+
return handleAdminUsage(req, url, ctx);
|
|
836
|
+
}
|
|
706
837
|
if (path === "/entities" || path === "/entities/") {
|
|
707
838
|
if (method === "POST") return handleCreate(req, auth, tenantId, ctx);
|
|
708
839
|
if (method === "GET") return handleList(url, auth, tenantId, ctx);
|
|
@@ -748,7 +879,7 @@ async function handleCreate(req, auth, tenantId, ctx) {
|
|
|
748
879
|
const body = await req.json();
|
|
749
880
|
if (!body.type) return json({ error: "type is required" }, 400);
|
|
750
881
|
ctx.permissions?.assert(auth, body.type, "create");
|
|
751
|
-
const kernel = ctx.pool.
|
|
882
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
752
883
|
const entityId2 = `${body.type.toLowerCase()}:${crypto.randomUUID()}`;
|
|
753
884
|
const attrs = {
|
|
754
885
|
...body.attributes
|
|
@@ -761,32 +892,35 @@ async function handleCreate(req, auth, tenantId, ctx) {
|
|
|
761
892
|
attrs,
|
|
762
893
|
body.links
|
|
763
894
|
);
|
|
895
|
+
recordGraphIo(ctx, tenantId);
|
|
764
896
|
await ctx.subs.notify(tenantId);
|
|
765
897
|
return json({ id: entityId2, op: result.op.hash }, 201);
|
|
766
898
|
}
|
|
767
899
|
async function handleRead(id, auth, tenantId, ctx) {
|
|
768
|
-
const kernel = ctx.pool.
|
|
900
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
769
901
|
const entity = kernel.getEntity(id);
|
|
770
902
|
if (!entity) return json({ error: "Not Found" }, 404);
|
|
771
903
|
ctx.permissions?.assert(auth, entity.type, "read", entity);
|
|
772
904
|
return json(entityToJson(entity));
|
|
773
905
|
}
|
|
774
906
|
async function handleUpdate(req, id, auth, tenantId, ctx) {
|
|
775
|
-
const kernel = ctx.pool.
|
|
907
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
776
908
|
const entity = kernel.getEntity(id);
|
|
777
909
|
if (!entity) return json({ error: "Not Found" }, 404);
|
|
778
910
|
ctx.permissions?.assert(auth, entity.type, "update", entity);
|
|
779
911
|
const updates = await req.json();
|
|
780
912
|
await kernel.updateEntity(id, updates);
|
|
913
|
+
recordGraphIo(ctx, tenantId);
|
|
781
914
|
await ctx.subs.notify(tenantId);
|
|
782
915
|
return json({ id, updated: true });
|
|
783
916
|
}
|
|
784
917
|
async function handleDelete(id, auth, tenantId, ctx) {
|
|
785
|
-
const kernel = ctx.pool.
|
|
918
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
786
919
|
const entity = kernel.getEntity(id);
|
|
787
920
|
if (!entity) return json({ error: "Not Found" }, 404);
|
|
788
921
|
ctx.permissions?.assert(auth, entity.type, "delete", entity);
|
|
789
922
|
await kernel.deleteEntity(id);
|
|
923
|
+
recordGraphIo(ctx, tenantId);
|
|
790
924
|
await ctx.subs.notify(tenantId);
|
|
791
925
|
return json({ id, deleted: true });
|
|
792
926
|
}
|
|
@@ -794,7 +928,7 @@ async function handleList(url, auth, tenantId, ctx) {
|
|
|
794
928
|
const type = url.searchParams.get("type") ?? void 0;
|
|
795
929
|
const limit = parseInt(url.searchParams.get("limit") ?? "100");
|
|
796
930
|
const offset = parseInt(url.searchParams.get("offset") ?? "0");
|
|
797
|
-
const kernel = ctx.pool.
|
|
931
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
798
932
|
let entities = kernel.listEntities(type);
|
|
799
933
|
if (ctx.permissions && type) {
|
|
800
934
|
entities = entities.filter(
|
|
@@ -818,8 +952,9 @@ async function handleQuery(req, auth, tenantId, ctx) {
|
|
|
818
952
|
} catch (err) {
|
|
819
953
|
return json({ error: "Invalid query", message: String(err) }, 400);
|
|
820
954
|
}
|
|
821
|
-
const kernel = ctx.pool.
|
|
955
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
822
956
|
const result = await kernel.query(parsed);
|
|
957
|
+
recordGraphIo(ctx, tenantId);
|
|
823
958
|
const bindings = hydrateBindings(
|
|
824
959
|
kernel,
|
|
825
960
|
result.bindings
|
|
@@ -832,18 +967,22 @@ async function handleQuery(req, auth, tenantId, ctx) {
|
|
|
832
967
|
async function handleCreateOntology(req, auth, tenantId, ctx) {
|
|
833
968
|
const schema = await req.json();
|
|
834
969
|
if (!schema || !schema["@id"] || !Array.isArray(schema.fields)) {
|
|
835
|
-
return json(
|
|
970
|
+
return json(
|
|
971
|
+
{ error: "A SchemaDefinition (with @id and fields) is required" },
|
|
972
|
+
400
|
|
973
|
+
);
|
|
836
974
|
}
|
|
837
975
|
if ((schema.tier ?? "user") === "core") {
|
|
838
976
|
return json({ error: "Core ontologies are immutable" }, 403);
|
|
839
977
|
}
|
|
840
|
-
const kernel = ctx.pool.
|
|
978
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
841
979
|
const exists = kernel.listOntologies().some((ont) => ont["@id"] === schema["@id"]);
|
|
842
980
|
if (exists) {
|
|
843
981
|
return json({ id: schema["@id"], registered: false, existed: true }, 200);
|
|
844
982
|
}
|
|
845
983
|
try {
|
|
846
984
|
kernel.createOntology(schema);
|
|
985
|
+
recordGraphIo(ctx, tenantId);
|
|
847
986
|
} catch (err) {
|
|
848
987
|
const msg = err instanceof Error ? err.message : String(err);
|
|
849
988
|
if (msg.includes("already exists")) {
|
|
@@ -861,7 +1000,7 @@ async function handleUpload(req, auth, tenantId, ctx) {
|
|
|
861
1000
|
const buffer = new Uint8Array(await req.arrayBuffer());
|
|
862
1001
|
const hashBuf = await crypto.subtle.digest("SHA-256", buffer);
|
|
863
1002
|
const hash = `blob:${Array.from(new Uint8Array(hashBuf)).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
864
|
-
const kernel = ctx.pool.
|
|
1003
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
865
1004
|
const backend = kernel.getBackend();
|
|
866
1005
|
if (!backend.hasBlob(hash)) {
|
|
867
1006
|
backend.putBlob(hash, buffer);
|
|
@@ -869,7 +1008,7 @@ async function handleUpload(req, auth, tenantId, ctx) {
|
|
|
869
1008
|
return json({ hash, size: buffer.length, contentType }, 201);
|
|
870
1009
|
}
|
|
871
1010
|
async function handleFileDownload(hash, ctx, tenantId) {
|
|
872
|
-
const kernel = ctx.pool.
|
|
1011
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
873
1012
|
const backend = kernel.getBackend();
|
|
874
1013
|
const blob = backend.getBlob(hash);
|
|
875
1014
|
if (!blob) return json({ error: "Not Found" }, 404);
|
|
@@ -877,6 +1016,7 @@ async function handleFileDownload(hash, ctx, tenantId) {
|
|
|
877
1016
|
blob.byteOffset,
|
|
878
1017
|
blob.byteOffset + blob.byteLength
|
|
879
1018
|
);
|
|
1019
|
+
ctx.meter.recordEgress(resolveUsageTenantId(tenantId), blob.byteLength);
|
|
880
1020
|
return new Response(cleanBuf, {
|
|
881
1021
|
headers: { "Content-Type": "application/octet-stream" }
|
|
882
1022
|
});
|
|
@@ -889,7 +1029,7 @@ async function handleRegister(req, tenantId, ctx) {
|
|
|
889
1029
|
if (!body.email || !body.password) {
|
|
890
1030
|
return json({ error: "email and password are required" }, 400);
|
|
891
1031
|
}
|
|
892
|
-
const kernel = ctx.pool.
|
|
1032
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
893
1033
|
const existing = kernel.listEntities("User", { email: body.email });
|
|
894
1034
|
if (existing.length > 0) {
|
|
895
1035
|
return json({ error: "Email already registered" }, 409);
|
|
@@ -917,7 +1057,7 @@ async function handleLogin(req, tenantId, ctx) {
|
|
|
917
1057
|
if (!body.email || !body.password) {
|
|
918
1058
|
return json({ error: "email and password are required" }, 400);
|
|
919
1059
|
}
|
|
920
|
-
const kernel = ctx.pool.
|
|
1060
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
921
1061
|
const users = kernel.listEntities("User", { email: body.email });
|
|
922
1062
|
if (users.length === 0) {
|
|
923
1063
|
return json({ error: "Invalid credentials" }, 401);
|
|
@@ -960,7 +1100,7 @@ async function handleOAuthCallback(url, providerName, tenantId, ctx) {
|
|
|
960
1100
|
} catch (err) {
|
|
961
1101
|
return json({ error: "OAuth exchange failed", message: String(err) }, 400);
|
|
962
1102
|
}
|
|
963
|
-
const kernel = ctx.pool.
|
|
1103
|
+
const kernel = await ctx.pool.preload(tenantId);
|
|
964
1104
|
const oauthId = `oauth:${providerName}:${profile.id}`;
|
|
965
1105
|
let users = kernel.listEntities("User", { oauthId });
|
|
966
1106
|
let userId;
|
|
@@ -1046,6 +1186,24 @@ async function verifyPassword(password, stored) {
|
|
|
1046
1186
|
const hash = Array.from(new Uint8Array(bits)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1047
1187
|
return hash === expectedHash;
|
|
1048
1188
|
}
|
|
1189
|
+
function handleAdminUsage(req, url, ctx) {
|
|
1190
|
+
if (!process.env.TURTLEDB_ADMIN_KEY) {
|
|
1191
|
+
return json({ error: "Admin usage not configured" }, 501);
|
|
1192
|
+
}
|
|
1193
|
+
if (!verifyAdminKey(req)) {
|
|
1194
|
+
return json({ error: "Unauthorized" }, 401);
|
|
1195
|
+
}
|
|
1196
|
+
const tenant = url.searchParams.get("tenant");
|
|
1197
|
+
if (!tenant) {
|
|
1198
|
+
return json({ error: "tenant query param is required" }, 400);
|
|
1199
|
+
}
|
|
1200
|
+
const refresh = url.searchParams.get("refresh") === "1";
|
|
1201
|
+
if (refresh) {
|
|
1202
|
+
ctx.meter.sampleStorage(ctx.pool, tenant);
|
|
1203
|
+
}
|
|
1204
|
+
const day = url.searchParams.get("day") ?? void 0;
|
|
1205
|
+
return json(ctx.meter.getUsage(tenant, day));
|
|
1206
|
+
}
|
|
1049
1207
|
function entityToJson(entity) {
|
|
1050
1208
|
return entityRecordToPlain(entity);
|
|
1051
1209
|
}
|
|
@@ -1071,6 +1229,11 @@ export {
|
|
|
1071
1229
|
FULLY_PUBLIC,
|
|
1072
1230
|
OWNER_ONLY,
|
|
1073
1231
|
ADMIN_ONLY,
|
|
1232
|
+
resolveUsageTenantId,
|
|
1233
|
+
dayKey,
|
|
1234
|
+
sampleTenantStorage,
|
|
1235
|
+
verifyAdminKey,
|
|
1236
|
+
UsageMeter,
|
|
1074
1237
|
SubscriptionManager,
|
|
1075
1238
|
startServer,
|
|
1076
1239
|
startServerCrossRuntime
|