void 0.7.13 → 0.8.0
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/{agents-DqkFfc2c.mjs → agents-Dkgafwfr.mjs} +1 -1
- package/dist/{auth-cmd-bS88H4Lb.mjs → auth-cmd-B1-7r6ng.mjs} +3 -3
- package/dist/{better-auth-shared-D2JaoCs4.mjs → better-auth-shared-CbAF6sOM.mjs} +1 -1
- package/dist/{better-auth-shared-HXlHRw02.d.mts → better-auth-shared-Fzyk5yS1.d.mts} +2 -7
- package/dist/{cache-DUDCNty6.mjs → cache-CGYC-2LW.mjs} +5 -5
- package/dist/{cancel-deploy-DOuSbPwX.mjs → cancel-deploy-B4j31z7d.mjs} +4 -4
- package/dist/canonical-json-CEyQaVDa.mjs +13 -0
- package/dist/cli/cli.mjs +44 -31
- package/dist/{client-C_DYiXeu.mjs → client-C-nH6yz3.mjs} +5 -5
- package/dist/{collect-HqAuilH6.mjs → collect-9rO3JFNM.mjs} +1 -1
- package/dist/{config-DyUSwbeX.mjs → config--wXmVOoe.mjs} +10 -8
- package/dist/{config-CeRQg3Lz.mjs → config-BVEC0lti.mjs} +11 -2
- package/dist/{config-CvHtTM0q.mjs → config-hhMYPVRT.mjs} +3 -12
- package/dist/{create-project-hvFe6rbI.mjs → create-project-BGnQMy5N.mjs} +44 -18
- package/dist/{db-YEYSTfGw.mjs → db-CT5Xg6ds.mjs} +42 -37
- package/dist/{delete-XyM_VD9o.mjs → delete-eMGTHHB9.mjs} +5 -5
- package/dist/{deploy-CPAEgVQm.mjs → deploy-7EA_lKq9.mjs} +169 -99
- package/dist/{discover-CKGQpis8.mjs → discover-C8N1I4tK.mjs} +9 -6
- package/dist/{domain-Dvj7u8tr.mjs → domain-UfLrp2vc.mjs} +64 -22
- package/dist/{drizzle-Cx7cVwrE.mjs → drizzle-DmaANP4-.mjs} +1 -1
- package/dist/{env-Bjl0rzuf.mjs → env-Cvdbbm_N.mjs} +7 -7
- package/dist/{env-helpers-Dw95CU8e.d.mts → env-helpers-Bt9yYg5I.d.mts} +1 -1
- package/dist/{env-types-BSQghXbx.mjs → env-types-D6qI1ThV.mjs} +8 -19
- package/dist/{env-validation-Buy5pe3u.mjs → env-validation-DBJsxZLz.mjs} +4 -4
- package/dist/{gen-DU8NIdrH.mjs → gen-Cn2hZ8RR.mjs} +37 -31
- package/dist/{handler-DJTuxB10.d.mts → handler-BPhL8AmU.d.mts} +3 -3
- package/dist/{headers-CuVHn6Kl.mjs → headers-wAPJigtE.mjs} +3 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +273 -104
- package/dist/{init-Br5DTaG7.mjs → init-DL6syXuF.mjs} +43 -31
- package/dist/{link-DdXqlUiJ.mjs → link-C5x1E-XZ.mjs} +5 -5
- package/dist/{list-9Bxa5Odn.mjs → list-ezL7hqDV.mjs} +5 -5
- package/dist/{login-C_MfDjhu.mjs → login-CkLr-AaI.mjs} +3 -3
- package/dist/{logs-DjqCpEER.mjs → logs-MnGILmLz.mjs} +4 -4
- package/dist/{mcp-vA0880_K.mjs → mcp-B84MmVAW.mjs} +3 -3
- package/dist/{node-gxRQXIRT.mjs → node-fpywdcbN.mjs} +6 -6
- package/dist/{output-D3g5otMC.mjs → output-DeiS4oEX.mjs} +1 -1
- package/dist/pages/client.d.mts +1 -1
- package/dist/pages/head-client.d.mts +1 -1
- package/dist/pages/head.d.mts +1 -1
- package/dist/pages/index.d.mts +28 -3
- package/dist/pages/index.mjs +6 -5
- package/dist/pages/islands-plugin.d.mts +1 -1
- package/dist/pages/islands-plugin.mjs +7 -5
- package/dist/pages/protocol.d.mts +2 -2
- package/dist/pages/protocol.mjs +1 -1
- package/dist/{pathe.M-eThtNZ-D-kmWkCS.mjs → pathe.M-eThtNZ-BrPhGF_K.mjs} +1 -1
- package/dist/plan-aD3wRDqF.mjs +271 -0
- package/dist/{plugin-inference-BexV6LL3.mjs → plugin-inference-Ba1uGdIy.mjs} +26 -20
- package/dist/{prepare-BEkH89oX.mjs → prepare-BrAISonG.mjs} +29 -19
- package/dist/{preset-CYQh93UF.mjs → preset-BRsCi8ZB.mjs} +10 -8
- package/dist/{project-DhB4A8_g.mjs → project-CH9pdo16.mjs} +1 -1
- package/dist/{project-cmd-CeXO06hG.mjs → project-cmd-JQUzv3qF.mjs} +10 -10
- package/dist/project-paths-CCMrHYQm.mjs +100 -0
- package/dist/{project-slug-KRvHQEQI.mjs → project-slug-De4UjtD9.mjs} +0 -1
- package/dist/{project-tsconfig-Bfy55qvs.mjs → project-tsconfig-HAGjfY6w.mjs} +10 -5
- package/dist/{protocol-D7r_deVZ.d.mts → protocol-mcSsdMij.d.mts} +3 -3
- package/dist/providers--_pvxpNN.d.mts +7 -0
- package/dist/{resolve-project-DdjLQ2tB.mjs → resolve-project-CNpUbUHd.mjs} +1 -1
- package/dist/{rollback-C8OSMxmq.mjs → rollback-CoAAvC18.mjs} +4 -4
- package/dist/{route-types-qVL-v-CB.mjs → route-types-jxRfWuCb.mjs} +1 -1
- package/dist/{runner-C0d3lJo1.mjs → runner-BkGBfr4e.mjs} +4 -2
- package/dist/{runner-pg-Bd_172vF.mjs → runner-pg-BXP0JpHW.mjs} +1 -1
- package/dist/runtime/ai.mjs +2 -2
- package/dist/runtime/auth.d.mts +1 -1
- package/dist/runtime/better-auth-pg.d.mts +1 -1
- package/dist/runtime/better-auth-pg.mjs +2 -2
- package/dist/runtime/better-auth.d.mts +1 -1
- package/dist/runtime/better-auth.mjs +2 -2
- package/dist/runtime/client.d.mts +2 -2
- package/dist/runtime/client.mjs +1 -1
- package/dist/runtime/env-helpers.d.mts +1 -1
- package/dist/runtime/env-public-client.d.mts +1 -1
- package/dist/runtime/env-public.d.mts +2 -2
- package/dist/runtime/env-public.mjs +1 -1
- package/dist/runtime/env.mjs +1 -1
- package/dist/runtime/fetch-stream.d.mts +1 -1
- package/dist/runtime/fetch-stream.mjs +1 -1
- package/dist/runtime/fetch.d.mts +1 -1
- package/dist/runtime/fetch.mjs +1 -1
- package/dist/runtime/handler.d.mts +1 -1
- package/dist/runtime/handler.mjs +1 -1
- package/dist/runtime/isr.mjs +1 -1
- package/dist/runtime/live.d.mts +2 -2
- package/dist/runtime/live.mjs +1 -1
- package/dist/runtime/migration-handler.d.mts +33 -1
- package/dist/runtime/migration-handler.mjs +332 -24
- package/dist/runtime/remote/index.mjs +1 -1
- package/dist/runtime/sandbox.d.mts +73 -3
- package/dist/runtime/sandbox.mjs +239 -3
- package/dist/runtime/validator.d.mts +1 -1
- package/dist/runtime/ws-server.d.mts +2 -2
- package/dist/runtime/ws.d.mts +3 -3
- package/dist/{scan-CKpylFj9.mjs → scan-2YmJkYAf.mjs} +4 -3
- package/dist/{scan-CkK1Hzpa.mjs → scan-DzzHqtiT.mjs} +82 -43
- package/dist/{secret-NDvlqAN2.mjs → secret-mz8t_CML.mjs} +5 -5
- package/dist/{skills-SAG0WyVn.mjs → skills-B_ynF4lA.mjs} +3 -3
- package/dist/{subcommand-prompt-q9T06dpg.mjs → subcommand-prompt-CPuV7tPW.mjs} +2 -2
- package/dist/sveltekit.mjs +1 -1
- package/dist/{validate-DW3ViKTA.mjs → validate-CNWm-PsL.mjs} +49 -9
- package/dist/{yarn-pnp-6LD6_3Ej.mjs → yarn-pnp-QgEYVC3V.mjs} +1 -1
- package/package.json +3 -2
- package/schema.json +5 -0
- package/skills/void/docs/guide/ai.md +1 -1
- package/skills/void/docs/guide/app-types.md +1 -1
- package/skills/void/docs/guide/database/postgresql.md +1 -1
- package/skills/void/docs/guide/edge/headers.md +1 -1
- package/skills/void/docs/guide/edge/static-assets.md +34 -0
- package/skills/void/docs/guide/sandboxes.md +3 -2
- package/skills/void/docs/guide/ssr.md +25 -2
- package/skills/void/docs/integrations/cloudflare.md +5 -3
- package/skills/void/docs/integrations/frameworks/overview.md +1 -1
- package/skills/void/docs/integrations/nodejs-bun-deno.md +10 -9
- package/skills/void/docs/reference/api.md +204 -13
- package/skills/void/docs/reference/cli.md +52 -6
- package/skills/void/docs/reference/config.md +27 -4
- package/skills/void/docs/reference/resource-inference.md +1 -1
- /package/dist/{auth-DmuALf16.d.mts → auth-SfRBhXan.d.mts} +0 -0
- /package/dist/{auth-migrations-BwLPwRgH.mjs → auth-migrations-BqJoGqGQ.mjs} +0 -0
- /package/dist/{cf-access-BW8K93Fm.mjs → cf-access-DKDsgwOU.mjs} +0 -0
- /package/dist/{defer-2ARBu8Et.mjs → defer-DkoEwda-.mjs} +0 -0
- /package/dist/{dist-q8b2Mjgb.mjs → dist-DR9sIMbM.mjs} +0 -0
- /package/dist/{dist-D2L3_KTK.mjs → dist-abzUneor.mjs} +0 -0
- /package/dist/{dotenv-HQNhRalS.mjs → dotenv-Bkoqyq9r.mjs} +0 -0
- /package/dist/{env-raw-DtfQ9E31.mjs → env-raw-zk5JQKZe.mjs} +0 -0
- /package/dist/{fetch-error-DZ868Xq4.d.mts → fetch-error-BbixZ9vW.d.mts} +0 -0
- /package/dist/{fetch-error-CEr0ACTl.mjs → fetch-error-rXermVU3.mjs} +0 -0
- /package/dist/{head-C7QW7UY1.d.mts → head-CZs8dMxG.d.mts} +0 -0
- /package/dist/{log-7ChR5Fbc.mjs → log-CWWZV4V1.mjs} +0 -0
- /package/dist/{magic-string.es-DS_lmuBe.mjs → magic-string.es-C1Fb0uxq.mjs} +0 -0
- /package/dist/{providers-BlFNPYen.mjs → providers-uC0PJg1c.mjs} +0 -0
- /package/dist/{standard-schema-D2qvCEYV.d.mts → standard-schema-WhHcCaqJ.d.mts} +0 -0
- /package/dist/{types-DG_Ynnyd.d.mts → types-C8wsi1hv.d.mts} +0 -0
package/dist/runtime/sandbox.mjs
CHANGED
|
@@ -1,19 +1,255 @@
|
|
|
1
|
+
import { i as getRawRuntimeEnv } from "../env-raw-zk5JQKZe.mjs";
|
|
1
2
|
import { requireRuntimeBinding } from "./env.mjs";
|
|
2
3
|
import { Sandbox, getSandbox as getSandbox$1 } from "@cloudflare/sandbox";
|
|
3
4
|
//#region src/runtime/sandbox.ts
|
|
4
5
|
const DEFAULT_SANDBOX_ID = "default";
|
|
5
6
|
const DEFAULT_SANDBOX_BINDING = "SANDBOX";
|
|
7
|
+
const SANDBOX_USAGE_QUEUE_BINDING = "USAGE_EVENTS_QUEUE";
|
|
8
|
+
const SANDBOX_GATE_BINDING = "__VOID_PROXY";
|
|
9
|
+
const HEARTBEAT_MS = 5 * 6e4;
|
|
10
|
+
/**
|
|
11
|
+
* Thrown by `getSandbox()` when the platform gate denies a new acquire because
|
|
12
|
+
* the user has hit a concurrency or runtime budget cap. Surface this to the
|
|
13
|
+
* caller so they can render a friendly error; do not retry transparently.
|
|
14
|
+
*/
|
|
15
|
+
var SandboxLimitError = class extends Error {
|
|
16
|
+
name = "SandboxLimitError";
|
|
17
|
+
reason;
|
|
18
|
+
constructor(reason, message) {
|
|
19
|
+
super(message ?? `Sandbox limit reached: ${reason}`);
|
|
20
|
+
this.reason = reason;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
6
23
|
let sandboxBindingName = DEFAULT_SANDBOX_BINDING;
|
|
7
24
|
function __setSandboxBindingName(bindingName) {
|
|
8
25
|
sandboxBindingName = bindingName;
|
|
9
26
|
}
|
|
10
|
-
|
|
27
|
+
const STORAGE_KEY_START_TS = "__voidStartTs";
|
|
28
|
+
const STORAGE_KEY_LAST_FLUSH_TS = "__voidLastFlushTs";
|
|
29
|
+
function getUsageContext() {
|
|
30
|
+
const runtimeEnv = getRawRuntimeEnv();
|
|
31
|
+
return {
|
|
32
|
+
userId: runtimeEnv.__VOID_USER_ID,
|
|
33
|
+
projectId: runtimeEnv.__PROJECT_ID,
|
|
34
|
+
slug: runtimeEnv.__VOID_PROJECT_SLUG
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function getSandboxInstanceId(ctx) {
|
|
38
|
+
return ctx.id.name ?? ctx.id.toString();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Flush the accumulated runtime delta to the usage queue. No-op when the
|
|
42
|
+
* usage-queue binding is not bound (e.g. local dev without the platform
|
|
43
|
+
* proxy). Errors are swallowed and logged — usage emission must never crash
|
|
44
|
+
* the sandbox lifecycle.
|
|
45
|
+
*/
|
|
46
|
+
async function emitRuntimeSeconds(ctx, sandboxRuntimeSeconds) {
|
|
47
|
+
if (sandboxRuntimeSeconds <= 0) return;
|
|
48
|
+
const queue = getRawRuntimeEnv()[SANDBOX_USAGE_QUEUE_BINDING];
|
|
49
|
+
if (!queue) return;
|
|
50
|
+
const event = {
|
|
51
|
+
requests: 0,
|
|
52
|
+
responseBytes: 0,
|
|
53
|
+
sandboxRuntimeSeconds,
|
|
54
|
+
sandboxInstanceId: getSandboxInstanceId(ctx),
|
|
55
|
+
...getUsageContext()
|
|
56
|
+
};
|
|
57
|
+
try {
|
|
58
|
+
await queue.send(event);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error("[void/sandbox] failed to emit usage event:", error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the `{ gate, projectId, aiToken }` triple needed to call a gate
|
|
65
|
+
* route. Returns `null` when any piece is missing — local dev (no proxy
|
|
66
|
+
* binding) and partial deploys (binding present but identity vars absent)
|
|
67
|
+
* both fall through to the no-op path so a missing platform setup never
|
|
68
|
+
* blocks sandbox acquisition.
|
|
69
|
+
*
|
|
70
|
+
* Wire field is named `aiToken` because the shared proxy auth middleware
|
|
71
|
+
* (packages/proxy/src/index.ts) verifies HMAC against `body.aiToken` for
|
|
72
|
+
* every route. Naming it `proxyToken` here would diverge from the existing
|
|
73
|
+
* AI/ISR convention without changing the on-the-wire contract.
|
|
74
|
+
*/
|
|
75
|
+
function resolveGateIdentity() {
|
|
76
|
+
const runtimeEnv = getRawRuntimeEnv();
|
|
77
|
+
const gate = runtimeEnv[SANDBOX_GATE_BINDING];
|
|
78
|
+
if (!gate) return null;
|
|
79
|
+
const projectId = runtimeEnv.__PROJECT_ID;
|
|
80
|
+
const aiToken = runtimeEnv.__VOID_PROXY_TOKEN;
|
|
81
|
+
if (!projectId || !aiToken) return null;
|
|
82
|
+
return {
|
|
83
|
+
gate,
|
|
84
|
+
projectId,
|
|
85
|
+
aiToken
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Release notification to the gate so it can drop the active lease counter.
|
|
90
|
+
* Returns the in-flight fetch as a Promise (errors swallowed) so callers can
|
|
91
|
+
* route it through `ctx.waitUntil()` and keep it alive past lifecycle
|
|
92
|
+
* teardown. Failure is silent — the gate maintains its own TTL fallback.
|
|
93
|
+
*/
|
|
94
|
+
function notifyGateRelease(ctx) {
|
|
95
|
+
const identity = resolveGateIdentity();
|
|
96
|
+
if (!identity) return Promise.resolve();
|
|
97
|
+
const sandboxId = getSandboxInstanceId(ctx);
|
|
98
|
+
return identity.gate.fetch("https://gate/sandbox/release", {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: { "content-type": "application/json" },
|
|
101
|
+
body: JSON.stringify({
|
|
102
|
+
projectId: identity.projectId,
|
|
103
|
+
aiToken: identity.aiToken,
|
|
104
|
+
sandboxId
|
|
105
|
+
})
|
|
106
|
+
}).then(() => void 0).catch(() => {});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Heartbeat-driven keepalive ping for the gate's concurrency slot. The DO's
|
|
110
|
+
* `STALE_MS` (1h) sweep would otherwise reap slots for long-running sandbox
|
|
111
|
+
* sessions whose `acquiredAt` ages past the threshold between acquire and
|
|
112
|
+
* release, letting new acquires exceed `sandboxMaxConcurrentInstances`. The
|
|
113
|
+
* 5 min heartbeat sits well inside the sweep window, so each successful
|
|
114
|
+
* refresh resets the stale clock. Returns the in-flight fetch as a Promise
|
|
115
|
+
* (errors swallowed) so callers can route it through `ctx.waitUntil()` and
|
|
116
|
+
* keep the alarm path itself off the critical path — a failed refresh just
|
|
117
|
+
* means the slot ages out naturally, which matches the pre-existing failure
|
|
118
|
+
* mode if the gate goes down entirely.
|
|
119
|
+
*/
|
|
120
|
+
function notifyGateRefresh(ctx) {
|
|
121
|
+
const identity = resolveGateIdentity();
|
|
122
|
+
if (!identity) return Promise.resolve();
|
|
123
|
+
const sandboxId = getSandboxInstanceId(ctx);
|
|
124
|
+
return identity.gate.fetch("https://gate/sandbox/refresh", {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: { "content-type": "application/json" },
|
|
127
|
+
body: JSON.stringify({
|
|
128
|
+
projectId: identity.projectId,
|
|
129
|
+
aiToken: identity.aiToken,
|
|
130
|
+
sandboxId
|
|
131
|
+
})
|
|
132
|
+
}).then(() => void 0).catch(() => {});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Sandbox variant that participates in Void platform metering: it records
|
|
136
|
+
* container runtime seconds into DO storage, emits periodic heartbeats to
|
|
137
|
+
* the usage queue, and notifies the gate when the container stops.
|
|
138
|
+
*
|
|
139
|
+
* Generated code from `void inference` wires user `Sandbox` subclasses to
|
|
140
|
+
* extend this class on platform deploys so per-tenant accounting works
|
|
141
|
+
* without user opt-in. On local dev (no usage-queue / gate bindings), all
|
|
142
|
+
* overrides degrade to no-ops and the sandbox behaves like the upstream
|
|
143
|
+
* `Sandbox` class.
|
|
144
|
+
*/
|
|
145
|
+
var VoidPlatformSandbox = class extends Sandbox {
|
|
146
|
+
async onStart() {
|
|
147
|
+
const now = Date.now();
|
|
148
|
+
const stored = await this.ctx.storage.get([STORAGE_KEY_START_TS, STORAGE_KEY_LAST_FLUSH_TS]);
|
|
149
|
+
const orphanedAnchor = stored.get(STORAGE_KEY_LAST_FLUSH_TS) ?? stored.get(STORAGE_KEY_START_TS);
|
|
150
|
+
if (orphanedAnchor !== void 0 && orphanedAnchor < now) {
|
|
151
|
+
const sandboxRuntimeSeconds = (now - orphanedAnchor) / 1e3;
|
|
152
|
+
await emitRuntimeSeconds(this.ctx, sandboxRuntimeSeconds);
|
|
153
|
+
await this.ctx.storage.delete([STORAGE_KEY_START_TS, STORAGE_KEY_LAST_FLUSH_TS]);
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
await super.onStart();
|
|
157
|
+
} catch (err) {
|
|
158
|
+
this.ctx.waitUntil(notifyGateRelease(this.ctx));
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
const startedAt = Date.now();
|
|
162
|
+
await this.ctx.storage.put({
|
|
163
|
+
[STORAGE_KEY_START_TS]: startedAt,
|
|
164
|
+
[STORAGE_KEY_LAST_FLUSH_TS]: startedAt
|
|
165
|
+
});
|
|
166
|
+
await this.ctx.storage.setAlarm(startedAt + HEARTBEAT_MS);
|
|
167
|
+
}
|
|
168
|
+
async alarm(alarmProps) {
|
|
169
|
+
await super.alarm(alarmProps);
|
|
170
|
+
const lastFlush = await this.ctx.storage.get(STORAGE_KEY_LAST_FLUSH_TS);
|
|
171
|
+
if (typeof lastFlush !== "number") return;
|
|
172
|
+
const now = Date.now();
|
|
173
|
+
const sandboxRuntimeSeconds = Math.max(0, (now - lastFlush) / 1e3);
|
|
174
|
+
await emitRuntimeSeconds(this.ctx, sandboxRuntimeSeconds);
|
|
175
|
+
this.ctx.waitUntil(notifyGateRefresh(this.ctx));
|
|
176
|
+
await Promise.all([this.ctx.storage.put(STORAGE_KEY_LAST_FLUSH_TS, now), this.ctx.storage.setAlarm(now + HEARTBEAT_MS)]);
|
|
177
|
+
}
|
|
178
|
+
async onStop() {
|
|
179
|
+
await this.flushFinal();
|
|
180
|
+
this.ctx.waitUntil(notifyGateRelease(this.ctx));
|
|
181
|
+
await super.onStop();
|
|
182
|
+
}
|
|
183
|
+
async onActivityExpired() {
|
|
184
|
+
await this.flushFinal();
|
|
185
|
+
this.ctx.waitUntil(notifyGateRelease(this.ctx));
|
|
186
|
+
await super.onActivityExpired();
|
|
187
|
+
}
|
|
188
|
+
async flushFinal() {
|
|
189
|
+
const lastFlush = await this.ctx.storage.get(STORAGE_KEY_LAST_FLUSH_TS);
|
|
190
|
+
if (typeof lastFlush === "number") {
|
|
191
|
+
const sandboxRuntimeSeconds = Math.max(0, (Date.now() - lastFlush) / 1e3);
|
|
192
|
+
await emitRuntimeSeconds(this.ctx, sandboxRuntimeSeconds);
|
|
193
|
+
}
|
|
194
|
+
await this.ctx.storage.delete(STORAGE_KEY_START_TS);
|
|
195
|
+
await this.ctx.storage.delete(STORAGE_KEY_LAST_FLUSH_TS);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Resolve the platform-provided sandbox stub for the given id, optionally
|
|
200
|
+
* coordinating with the platform gate to enforce per-user concurrency and
|
|
201
|
+
* runtime budgets.
|
|
202
|
+
*
|
|
203
|
+
* When the gate binding (`__VOID_PROXY` by default) is present AND the
|
|
204
|
+
* platform's `__PROJECT_ID` + `__VOID_PROXY_TOKEN` identity vars are
|
|
205
|
+
* available, we POST to `https://gate/sandbox/acquire` with the HMAC-signed
|
|
206
|
+
* body the shared proxy auth middleware expects. A `409` response means the
|
|
207
|
+
* user has exceeded a budget — we throw `SandboxLimitError` so the caller
|
|
208
|
+
* can render a clear message. Any other non-OK status (or a thrown transport
|
|
209
|
+
* error from the service binding) is treated as "gate unavailable" and we
|
|
210
|
+
* fall through to returning the stub so a broken gate doesn't break sandbox
|
|
211
|
+
* access entirely.
|
|
212
|
+
*
|
|
213
|
+
* When the gate binding is absent (local dev) or the identity vars are
|
|
214
|
+
* missing (partial deploy), the gate check is skipped entirely and we
|
|
215
|
+
* return the stub directly.
|
|
216
|
+
*
|
|
217
|
+
* The stub is constructed BEFORE calling the gate so we can derive the
|
|
218
|
+
* normalized DO name (`stub.id.name`) and pass it to the gate as the
|
|
219
|
+
* `sandboxId`. This keeps acquire and release using the same key — without
|
|
220
|
+
* it, mixed-case caller ids (e.g. `MyProject-123`) would acquire a lease
|
|
221
|
+
* under the raw id but release it under the normalized lowercase form that
|
|
222
|
+
* `ctx.id.name` reports inside the DO, leaking the lease until TTL.
|
|
223
|
+
* Constructing the stub does NOT start a container — that only happens on
|
|
224
|
+
* the first method call against the stub.
|
|
225
|
+
*/
|
|
226
|
+
async function getSandbox(id = DEFAULT_SANDBOX_ID, options) {
|
|
11
227
|
const { binding = sandboxBindingName, ...sandboxOptions } = options ?? {};
|
|
12
|
-
|
|
228
|
+
const stub = getSandbox$1(requireRuntimeBinding(binding), id, {
|
|
13
229
|
normalizeId: true,
|
|
14
230
|
...sandboxOptions
|
|
15
231
|
});
|
|
232
|
+
const sandboxId = stub.id.name ?? id;
|
|
233
|
+
const identity = resolveGateIdentity();
|
|
234
|
+
if (identity) try {
|
|
235
|
+
const res = await identity.gate.fetch("https://gate/sandbox/acquire", {
|
|
236
|
+
method: "POST",
|
|
237
|
+
headers: { "content-type": "application/json" },
|
|
238
|
+
body: JSON.stringify({
|
|
239
|
+
projectId: identity.projectId,
|
|
240
|
+
aiToken: identity.aiToken,
|
|
241
|
+
sandboxId
|
|
242
|
+
})
|
|
243
|
+
});
|
|
244
|
+
if (res.status === 409) {
|
|
245
|
+
const body = await res.json().catch(() => ({}));
|
|
246
|
+
throw new SandboxLimitError(body.reason === "concurrency" || body.reason === "runtime_budget" ? body.reason : "concurrency");
|
|
247
|
+
}
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (error instanceof SandboxLimitError) throw error;
|
|
250
|
+
}
|
|
251
|
+
return stub;
|
|
16
252
|
}
|
|
17
253
|
const sandbox = { get: getSandbox };
|
|
18
254
|
//#endregion
|
|
19
|
-
export { DEFAULT_SANDBOX_BINDING, DEFAULT_SANDBOX_ID, Sandbox, __setSandboxBindingName, getSandbox, sandbox };
|
|
255
|
+
export { DEFAULT_SANDBOX_BINDING, DEFAULT_SANDBOX_ID, HEARTBEAT_MS, SANDBOX_GATE_BINDING, SANDBOX_USAGE_QUEUE_BINDING, Sandbox, SandboxLimitError, VoidPlatformSandbox, __setSandboxBindingName, getSandbox, sandbox };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as HandlerInput, T as runValidation, w as ValidatorSlots } from "../handler-
|
|
1
|
+
import { C as HandlerInput, T as runValidation, w as ValidatorSlots } from "../handler-BPhL8AmU.mjs";
|
|
2
2
|
export { HandlerInput, ValidatorSlots, runValidation };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as AuthState } from "../auth-
|
|
2
|
-
import { i as VoidAuthRuntimeOptions, n as VoidAuthEnv } from "../better-auth-shared-
|
|
1
|
+
import { n as AuthState } from "../auth-SfRBhXan.mjs";
|
|
2
|
+
import { i as VoidAuthRuntimeOptions, n as VoidAuthEnv } from "../better-auth-shared-Fzyk5yS1.mjs";
|
|
3
3
|
import { AnyWebSocketDefinition } from "./ws.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/runtime/ws-server.d.ts
|
package/dist/runtime/ws.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { r as AuthUser } from "../auth-
|
|
2
|
-
import { t as StandardSchemaV1 } from "../standard-schema-
|
|
3
|
-
import { i as CloudEnv } from "../handler-
|
|
1
|
+
import { r as AuthUser } from "../auth-SfRBhXan.mjs";
|
|
2
|
+
import { t as StandardSchemaV1 } from "../standard-schema-WhHcCaqJ.mjs";
|
|
3
|
+
import { i as CloudEnv } from "../handler-BPhL8AmU.mjs";
|
|
4
4
|
import { WebSocketRouteMap } from "void/routes";
|
|
5
5
|
|
|
6
6
|
//#region src/runtime/ws.d.ts
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { a as join, n as dirname, r as extname, t as basename } from "./pathe.M-eThtNZ-
|
|
2
|
-
import {
|
|
1
|
+
import { a as join, n as dirname, r as extname, t as basename } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
|
|
2
|
+
import { n as readProjectPaths } from "./project-paths-CCMrHYQm.mjs";
|
|
3
|
+
import { t as glob } from "./dist-DR9sIMbM.mjs";
|
|
3
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
5
|
//#region src/router/parse-filename.ts
|
|
5
6
|
const EXTENSIONS = [
|
|
@@ -167,7 +168,7 @@ const LAYOUT_STRING_RE = /export\s+const\s+layout\s*=\s*["']([^"']+)["']\s*;?/;
|
|
|
167
168
|
const LAYOUT_FALSE_RE = /export\s+const\s+layout\s*=\s*false\s*;?/;
|
|
168
169
|
const MAX_REVALIDATE = 31536e3;
|
|
169
170
|
async function scanPages(root, options) {
|
|
170
|
-
const pagesDir =
|
|
171
|
+
const pagesDir = options?.pagesDir ?? options?.paths?.pagesDir ?? readProjectPaths(root).pagesDir;
|
|
171
172
|
if (!existsSync(pagesDir)) return {
|
|
172
173
|
pages: [],
|
|
173
174
|
layouts: [],
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { a as join, t as basename } from "./pathe.M-eThtNZ-
|
|
2
|
-
import { n as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { a as join, i as isAbsolute, t as basename } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
|
|
2
|
+
import { n as readProjectPaths } from "./project-paths-CCMrHYQm.mjs";
|
|
3
|
+
import { n as globSync, t as glob } from "./dist-DR9sIMbM.mjs";
|
|
4
|
+
import { d as isCallExpression, f as isIdentifier, h as isObjectExpression, p as isLiteral } from "./plugin-inference-Ba1uGdIy.mjs";
|
|
5
|
+
import { o as parseRouteFilename, s as parseWebSocketFilename, t as scanPages } from "./scan-2YmJkYAf.mjs";
|
|
5
6
|
import { readFileSync } from "node:fs";
|
|
6
7
|
import { parseSync } from "vite";
|
|
7
8
|
//#region src/crons/scan.ts
|
|
@@ -31,9 +32,13 @@ function extractCronExpressions(source, filePath) {
|
|
|
31
32
|
}
|
|
32
33
|
throw new Error(`cron: Invalid job file '${filePath}'. Add \`export const cron = "<schedule>"\`.`);
|
|
33
34
|
}
|
|
35
|
+
function resolveDir(root, dir) {
|
|
36
|
+
return isAbsolute(dir) ? dir : join(root, dir);
|
|
37
|
+
}
|
|
34
38
|
function parseJobFiles(root, jobsDir, files) {
|
|
39
|
+
const jobsDirPath = resolveDir(root, jobsDir);
|
|
35
40
|
return files.filter((filePath) => !filePath.split("/").some((segment) => segment.startsWith("_"))).sort((leftFilePath, rightFilePath) => leftFilePath.localeCompare(rightFilePath)).map((filePath) => {
|
|
36
|
-
const source = readFileSync(join(
|
|
41
|
+
const source = readFileSync(join(jobsDirPath, filePath), "utf-8");
|
|
37
42
|
return {
|
|
38
43
|
filePath,
|
|
39
44
|
name: extractJobName(filePath),
|
|
@@ -43,7 +48,7 @@ function parseJobFiles(root, jobsDir, files) {
|
|
|
43
48
|
}
|
|
44
49
|
async function scanJobs(root, jobsDir = "crons") {
|
|
45
50
|
return parseJobFiles(root, jobsDir, await glob(`**/*.{${JOB_EXTENSIONS.join(",")}}`, {
|
|
46
|
-
cwd:
|
|
51
|
+
cwd: resolveDir(root, jobsDir),
|
|
47
52
|
absolute: false
|
|
48
53
|
}).catch(() => []));
|
|
49
54
|
}
|
|
@@ -52,7 +57,7 @@ function scanJobsSync(root, jobsDir = "crons") {
|
|
|
52
57
|
let files;
|
|
53
58
|
try {
|
|
54
59
|
files = globSync(pattern, {
|
|
55
|
-
cwd:
|
|
60
|
+
cwd: resolveDir(root, jobsDir),
|
|
56
61
|
absolute: false
|
|
57
62
|
});
|
|
58
63
|
} catch {
|
|
@@ -80,8 +85,9 @@ function extractConsumerOption(source, name) {
|
|
|
80
85
|
return match ? Number(match[1]) : void 0;
|
|
81
86
|
}
|
|
82
87
|
function parseQueueFiles(root, queuesDir, files) {
|
|
88
|
+
const queuesDirPath = isAbsolute(queuesDir) ? queuesDir : join(root, queuesDir);
|
|
83
89
|
return files.filter((filePath) => !filePath.split("/").some((segment) => segment.startsWith("_"))).sort((leftFilePath, rightFilePath) => leftFilePath.localeCompare(rightFilePath)).map((filePath) => {
|
|
84
|
-
const source = readFileSync(join(
|
|
90
|
+
const source = readFileSync(join(queuesDirPath, filePath), "utf-8");
|
|
85
91
|
const name = extractName(filePath);
|
|
86
92
|
return {
|
|
87
93
|
filePath,
|
|
@@ -96,7 +102,7 @@ function parseQueueFiles(root, queuesDir, files) {
|
|
|
96
102
|
}
|
|
97
103
|
async function scanQueues(root, queuesDir = "queues") {
|
|
98
104
|
return parseQueueFiles(root, queuesDir, await glob(`**/*.{${QUEUE_EXTENSIONS.join(",")}}`, {
|
|
99
|
-
cwd:
|
|
105
|
+
cwd: isAbsolute(queuesDir) ? queuesDir : join(root, queuesDir),
|
|
100
106
|
absolute: false
|
|
101
107
|
}).catch(() => []));
|
|
102
108
|
}
|
|
@@ -105,7 +111,7 @@ function scanQueuesSync(root, queuesDir = "queues") {
|
|
|
105
111
|
let files;
|
|
106
112
|
try {
|
|
107
113
|
files = globSync(pattern, {
|
|
108
|
-
cwd:
|
|
114
|
+
cwd: isAbsolute(queuesDir) ? queuesDir : join(root, queuesDir),
|
|
109
115
|
absolute: false
|
|
110
116
|
});
|
|
111
117
|
} catch {
|
|
@@ -159,7 +165,7 @@ function detectHttpExports(absPath) {
|
|
|
159
165
|
}
|
|
160
166
|
//#endregion
|
|
161
167
|
//#region src/router/websocket.ts
|
|
162
|
-
const HANDLER_EXTENSIONS
|
|
168
|
+
const HANDLER_EXTENSIONS = [
|
|
163
169
|
"ts",
|
|
164
170
|
"mts",
|
|
165
171
|
"js",
|
|
@@ -223,9 +229,9 @@ function detectWebSocketDefinition(absPath) {
|
|
|
223
229
|
}
|
|
224
230
|
return null;
|
|
225
231
|
}
|
|
226
|
-
function buildWebSocketRoute(root, filePath) {
|
|
232
|
+
function buildWebSocketRoute(root, filePath, paths = readProjectPaths(root)) {
|
|
227
233
|
const parsed = parseWebSocketFilename(filePath);
|
|
228
|
-
const info = detectWebSocketDefinition(join(
|
|
234
|
+
const info = detectWebSocketDefinition(join(paths.routesDir, filePath));
|
|
229
235
|
const className = deriveWebSocketClassName(filePath);
|
|
230
236
|
return {
|
|
231
237
|
...parsed,
|
|
@@ -235,63 +241,75 @@ function buildWebSocketRoute(root, filePath) {
|
|
|
235
241
|
hasAuthGate: info?.hasAuthGate ?? false
|
|
236
242
|
};
|
|
237
243
|
}
|
|
238
|
-
function parseWebSocketFiles(root, files) {
|
|
239
|
-
return files.filter((f) => isWebSocketRouteFile(f)).filter((f) => !f.split("/").some((segment) => segment.startsWith("_"))).map((filePath) => buildWebSocketRoute(root, filePath)).sort((a, b) => {
|
|
244
|
+
function parseWebSocketFiles(root, files, paths = readProjectPaths(root)) {
|
|
245
|
+
return files.filter((f) => isWebSocketRouteFile(f)).filter((f) => !f.split("/").some((segment) => segment.startsWith("_"))).map((filePath) => buildWebSocketRoute(root, filePath, paths)).sort((a, b) => {
|
|
240
246
|
if (a.catchAll !== b.catchAll) return a.catchAll ? 1 : -1;
|
|
241
247
|
return a.pattern.localeCompare(b.pattern);
|
|
242
248
|
});
|
|
243
249
|
}
|
|
244
|
-
async function scanWebSocketRoutes(root) {
|
|
245
|
-
return parseWebSocketFiles(root, await glob(`**/*.{${HANDLER_EXTENSIONS
|
|
246
|
-
cwd:
|
|
250
|
+
async function scanWebSocketRoutes(root, paths = readProjectPaths(root)) {
|
|
251
|
+
return parseWebSocketFiles(root, await glob(`**/*.{${HANDLER_EXTENSIONS.join(",")}}`, {
|
|
252
|
+
cwd: paths.routesDir,
|
|
247
253
|
absolute: false
|
|
248
|
-
}).catch(() => []));
|
|
254
|
+
}).catch(() => []), paths);
|
|
249
255
|
}
|
|
250
|
-
function scanWebSocketRoutesSync(root) {
|
|
251
|
-
const pattern = `**/*.{${HANDLER_EXTENSIONS
|
|
256
|
+
function scanWebSocketRoutesSync(root, paths = readProjectPaths(root)) {
|
|
257
|
+
const pattern = `**/*.{${HANDLER_EXTENSIONS.join(",")}}`;
|
|
252
258
|
let files;
|
|
253
259
|
try {
|
|
254
260
|
files = globSync(pattern, {
|
|
255
|
-
cwd:
|
|
261
|
+
cwd: paths.routesDir,
|
|
256
262
|
absolute: false
|
|
257
263
|
});
|
|
258
264
|
} catch {
|
|
259
265
|
files = [];
|
|
260
266
|
}
|
|
261
|
-
return parseWebSocketFiles(root, files);
|
|
267
|
+
return parseWebSocketFiles(root, files, paths);
|
|
262
268
|
}
|
|
263
269
|
function isWebSocketPatternFile(filePath) {
|
|
264
270
|
return isWebSocketRouteFile(filePath);
|
|
265
271
|
}
|
|
266
272
|
//#endregion
|
|
267
273
|
//#region src/router/scan.ts
|
|
268
|
-
const
|
|
274
|
+
const ROUTE_PATTERN = `**/*.{${[
|
|
269
275
|
"ts",
|
|
270
276
|
"mts",
|
|
271
277
|
"js",
|
|
272
278
|
"mjs"
|
|
273
|
-
]
|
|
279
|
+
].join(",")}}`;
|
|
280
|
+
function parseMiddlewareDefinitions(files) {
|
|
281
|
+
return files.filter((f) => !f.startsWith("_")).map((f) => {
|
|
282
|
+
const match = f.split("/").pop().match(/^(\d+)\./);
|
|
283
|
+
return {
|
|
284
|
+
order: match ? parseInt(match[1], 10) : Infinity,
|
|
285
|
+
filePath: f
|
|
286
|
+
};
|
|
287
|
+
}).sort((a, b) => a.order - b.order);
|
|
288
|
+
}
|
|
274
289
|
/**
|
|
275
290
|
* Scan routes/ and middleware/ directories and return a structured manifest.
|
|
276
291
|
* Files starting with _ are ignored.
|
|
277
292
|
*/
|
|
278
293
|
async function scanRoutes(root, options) {
|
|
279
|
-
const
|
|
294
|
+
const paths = options?.paths ?? readProjectPaths(root);
|
|
280
295
|
const [routeFiles, websockets, middlewareFiles, jobs, queues, pages] = await Promise.all([
|
|
281
|
-
glob(
|
|
282
|
-
cwd:
|
|
296
|
+
glob(ROUTE_PATTERN, {
|
|
297
|
+
cwd: paths.routesDir,
|
|
283
298
|
absolute: false
|
|
284
299
|
}),
|
|
285
|
-
scanWebSocketRoutes(root),
|
|
286
|
-
glob(
|
|
287
|
-
cwd:
|
|
300
|
+
scanWebSocketRoutes(root, paths),
|
|
301
|
+
glob(ROUTE_PATTERN, {
|
|
302
|
+
cwd: paths.middlewareDir,
|
|
288
303
|
absolute: false
|
|
289
304
|
}).catch(() => []),
|
|
290
|
-
scanJobs(root),
|
|
291
|
-
scanQueues(root),
|
|
292
|
-
scanPages(root, {
|
|
305
|
+
scanJobs(root, paths.cronsDir),
|
|
306
|
+
scanQueues(root, paths.queuesDir),
|
|
307
|
+
scanPages(root, {
|
|
308
|
+
output: options?.output,
|
|
309
|
+
paths
|
|
310
|
+
})
|
|
293
311
|
]);
|
|
294
|
-
const routesDir =
|
|
312
|
+
const routesDir = paths.routesDir;
|
|
295
313
|
return {
|
|
296
314
|
routes: routeFiles.filter((f) => !isWebSocketPatternFile(f)).filter((f) => !f.split("/").some((s) => s.startsWith("_"))).map((f) => {
|
|
297
315
|
const route = parseRouteFilename(f);
|
|
@@ -302,17 +320,38 @@ async function scanRoutes(root, options) {
|
|
|
302
320
|
return a.pattern.localeCompare(b.pattern);
|
|
303
321
|
}),
|
|
304
322
|
websockets,
|
|
305
|
-
middleware: middlewareFiles
|
|
306
|
-
const match = f.split("/").pop().match(/^(\d+)\./);
|
|
307
|
-
return {
|
|
308
|
-
order: match ? parseInt(match[1], 10) : Infinity,
|
|
309
|
-
filePath: f
|
|
310
|
-
};
|
|
311
|
-
}).sort((a, b) => a.order - b.order),
|
|
323
|
+
middleware: parseMiddlewareDefinitions(middlewareFiles),
|
|
312
324
|
jobs,
|
|
313
325
|
queues,
|
|
314
326
|
pages
|
|
315
327
|
};
|
|
316
328
|
}
|
|
329
|
+
function scanMiddlewareSync(root, paths = readProjectPaths(root)) {
|
|
330
|
+
let middlewareFiles;
|
|
331
|
+
try {
|
|
332
|
+
middlewareFiles = globSync(ROUTE_PATTERN, {
|
|
333
|
+
cwd: paths.middlewareDir,
|
|
334
|
+
absolute: false
|
|
335
|
+
});
|
|
336
|
+
} catch {
|
|
337
|
+
middlewareFiles = [];
|
|
338
|
+
}
|
|
339
|
+
return parseMiddlewareDefinitions(middlewareFiles);
|
|
340
|
+
}
|
|
341
|
+
function scanRoutePatternsSync(root, paths = readProjectPaths(root)) {
|
|
342
|
+
let routeFiles;
|
|
343
|
+
try {
|
|
344
|
+
routeFiles = globSync(ROUTE_PATTERN, {
|
|
345
|
+
cwd: paths.routesDir,
|
|
346
|
+
absolute: false
|
|
347
|
+
});
|
|
348
|
+
} catch {
|
|
349
|
+
routeFiles = [];
|
|
350
|
+
}
|
|
351
|
+
return routeFiles.filter((f) => !isWebSocketPatternFile(f)).filter((f) => !f.split("/").some((s) => s.startsWith("_"))).map((f) => parseRouteFilename(f)).sort((a, b) => {
|
|
352
|
+
if (a.catchAll !== b.catchAll) return a.catchAll ? 1 : -1;
|
|
353
|
+
return a.pattern.localeCompare(b.pattern);
|
|
354
|
+
});
|
|
355
|
+
}
|
|
317
356
|
//#endregion
|
|
318
|
-
export {
|
|
357
|
+
export { scanQueuesSync as a, scanWebSocketRoutesSync as i, scanRoutePatternsSync as n, scanJobsSync as o, scanRoutes as r, scanMiddlewareSync as t };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { n as cliTitle } from "./output-
|
|
2
|
-
import { c as getToken, n as PlatformClient } from "./client-
|
|
3
|
-
import { _ as me, c as R, g as ge, n as Ce, u as Se, v as ue, x as q, y as ye } from "./dist-
|
|
4
|
-
import { r as readProjectConfig } from "./project-
|
|
5
|
-
import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-
|
|
1
|
+
import { n as cliTitle } from "./output-DeiS4oEX.mjs";
|
|
2
|
+
import { c as getToken, n as PlatformClient } from "./client-C-nH6yz3.mjs";
|
|
3
|
+
import { _ as me, c as R, g as ge, n as Ce, u as Se, v as ue, x as q, y as ye } from "./dist-abzUneor.mjs";
|
|
4
|
+
import { r as readProjectConfig } from "./project-CH9pdo16.mjs";
|
|
5
|
+
import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-CNpUbUHd.mjs";
|
|
6
6
|
import { readFileSync } from "node:fs";
|
|
7
7
|
//#region src/cli/secret.ts
|
|
8
8
|
async function runSecretCommand(root, args) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as join,
|
|
2
|
-
import { c as R } from "./dist-
|
|
3
|
-
import { r as getPackageDir } from "./agents-
|
|
1
|
+
import { a as join, s as relative } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
|
|
2
|
+
import { c as R } from "./dist-abzUneor.mjs";
|
|
3
|
+
import { r as getPackageDir } from "./agents-Dkgafwfr.mjs";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, symlinkSync } from "node:fs";
|
|
5
5
|
//#region src/cli/skills.ts
|
|
6
6
|
function parseSkills(skillsDir) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { s as import_picocolors } from "./output-
|
|
2
|
-
import { a as F, b as z, d as _t, m as et, o as H, r as E, s as J, t as $ } from "./dist-
|
|
1
|
+
import { s as import_picocolors } from "./output-DeiS4oEX.mjs";
|
|
2
|
+
import { a as F, b as z, d as _t, m as et, o as H, r as E, s as J, t as $ } from "./dist-abzUneor.mjs";
|
|
3
3
|
//#region src/cli/subcommand-prompt.ts
|
|
4
4
|
function promptSubcommand(command, subcommands) {
|
|
5
5
|
return new Promise((resolve) => {
|
package/dist/sveltekit.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as join,
|
|
1
|
+
import { a as join, c as resolve, i as isAbsolute, s as relative } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
//#region src/sveltekit.ts
|
|
4
4
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { n as __exportAll } from "./chunk-DJd-R1mw.mjs";
|
|
2
|
-
import { a as join } from "./pathe.M-eThtNZ-
|
|
3
|
-
import { i as stripSqlExt, r as readJournal } from "./collect-
|
|
2
|
+
import { a as join } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
|
|
3
|
+
import { i as stripSqlExt, r as readJournal } from "./collect-9rO3JFNM.mjs";
|
|
4
|
+
import { a as tokenizeSql, t as DESTRUCTIVE_PRAGMA } from "./plan-aD3wRDqF.mjs";
|
|
4
5
|
import { existsSync, readdirSync } from "node:fs";
|
|
5
6
|
//#region src/migrations/validate.ts
|
|
6
7
|
var validate_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -15,16 +16,55 @@ var validate_exports = /* @__PURE__ */ __exportAll({
|
|
|
15
16
|
*/
|
|
16
17
|
const DESTRUCTIVE_PATTERNS = [
|
|
17
18
|
/\bDROP\s+TABLE\b/i,
|
|
18
|
-
/\bDROP\s+
|
|
19
|
-
/\
|
|
20
|
-
/\
|
|
19
|
+
/\bDROP\s+VIEW\b/i,
|
|
20
|
+
/\bDROP\s+INDEX\b/i,
|
|
21
|
+
/\bDROP\s+TRIGGER\b/i,
|
|
22
|
+
/\bALTER\s+TABLE\b[\s\S]*\bDROP\s+COLUMN\b/i,
|
|
23
|
+
/\bALTER\s+TABLE\b[\s\S]*\bRENAME\s+TO\b/i,
|
|
24
|
+
/\bALTER\s+TABLE\b[\s\S]*\bRENAME\s+COLUMN\b/i,
|
|
25
|
+
/\bALTER\s+TABLE\b[\s\S]*\bRENAME\b[\s\S]*\bTO\b/i,
|
|
26
|
+
/\bTRUNCATE\b/i,
|
|
27
|
+
/\bINSERT\s+OR\s+REPLACE\b/i
|
|
21
28
|
];
|
|
22
|
-
|
|
29
|
+
function splitExecutableStatements(sql) {
|
|
30
|
+
return sql.split(";").map((statement) => statement.trim()).filter(Boolean);
|
|
31
|
+
}
|
|
32
|
+
function hasTopLevelKeyword(statement, keyword) {
|
|
33
|
+
let depth = 0;
|
|
34
|
+
for (let index = 0; index < statement.length; index++) {
|
|
35
|
+
const character = statement[index];
|
|
36
|
+
if (character === "(") {
|
|
37
|
+
depth++;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (character === ")") {
|
|
41
|
+
depth = Math.max(0, depth - 1);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (depth !== 0) continue;
|
|
45
|
+
if (statement.slice(index, index + keyword.length).toUpperCase() === keyword && !/[A-Z0-9_]/i.test(statement[index - 1] ?? "") && !/[A-Z0-9_]/i.test(statement[index + keyword.length] ?? "")) return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
function hasUnboundedWrite(statement) {
|
|
50
|
+
if (/\bDELETE\s+FROM\b/i.test(statement)) return !hasTopLevelKeyword(statement, "WHERE");
|
|
51
|
+
if (/\bUPDATE\b[\s\S]*\bSET\b/i.test(statement)) return !hasTopLevelKeyword(statement, "WHERE");
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
23
54
|
/**
|
|
24
55
|
* Check if a SQL migration contains destructive operations.
|
|
25
56
|
*/
|
|
26
57
|
function isDestructive(sql) {
|
|
27
|
-
|
|
58
|
+
const normalized = sql.replace(/\r\n?/g, "\n");
|
|
59
|
+
let executable = "";
|
|
60
|
+
for (const segment of tokenizeSql(normalized)) if (segment.kind === "sql") executable += normalized.slice(segment.start, segment.end);
|
|
61
|
+
else executable += " ".repeat(segment.end - segment.start);
|
|
62
|
+
return splitExecutableStatements(executable).some((statement) => {
|
|
63
|
+
return DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(statement)) || hasUnboundedWrite(statement);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function hasDestructivePragma(sql) {
|
|
67
|
+
return sql.replace(/\r\n?/g, "\n").split("\n").some((line) => line.trim() === DESTRUCTIVE_PRAGMA);
|
|
28
68
|
}
|
|
29
69
|
/**
|
|
30
70
|
* Validate migration files. Returns the validated list with destructive flags.
|
|
@@ -35,14 +75,14 @@ function validateMigrations(files) {
|
|
|
35
75
|
const errors = [];
|
|
36
76
|
for (const file of files) {
|
|
37
77
|
const destructive = isDestructive(file.sql);
|
|
38
|
-
if (destructive && !file.sql
|
|
78
|
+
if (destructive && !hasDestructivePragma(file.sql)) errors.push(file.name);
|
|
39
79
|
results.push({
|
|
40
80
|
name: file.name,
|
|
41
81
|
sql: file.sql,
|
|
42
82
|
destructive
|
|
43
83
|
});
|
|
44
84
|
}
|
|
45
|
-
if (errors.length > 0) throw new Error(`Destructive migrations detected without pragma:\n` + errors.map((f) => ` - ${f}`).join("\n") + `\n\nAdd "${
|
|
85
|
+
if (errors.length > 0) throw new Error(`Destructive migrations detected without pragma:\n` + errors.map((f) => ` - ${f}`).join("\n") + `\n\nAdd "${DESTRUCTIVE_PRAGMA}" to each file to allow the migration locally and on deploy.`);
|
|
46
86
|
return results;
|
|
47
87
|
}
|
|
48
88
|
/**
|