void 0.24.0 → 0.26.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/README.md +19 -106
- package/dist/{account-cmd-vJhh5lrb.mjs → account-cmd-CDry3B5V.mjs} +5 -5
- package/dist/{application-analysis-BVsVqc11.mjs → application-analysis-DC2yPvzY.mjs} +1 -1
- package/dist/{application-routing-B7QEjqSR.mjs → application-routing-C9khgLiS.mjs} +1 -1
- package/dist/{auth-DX3JURcX.mjs → auth-B60QDbjg.mjs} +1 -1
- package/dist/{auth-CQkSDS50.mjs → auth-D52yG7hl.mjs} +3 -3
- package/dist/{auth-link-D75t9wO2.mjs → auth-link-DjeOptYi.mjs} +5 -6
- package/dist/{auth-router-ClfhL1J7.mjs → auth-router-BRxIuqDi.mjs} +7 -7
- package/dist/{build-cmd-zMi1iFnS.mjs → build-cmd-Ce3C8huo.mjs} +6 -7
- package/dist/{cache-ZtQmyxVV.mjs → cache-CS8Yxr5a.mjs} +6 -7
- package/dist/{cancel-deploy-Box0kkEV.mjs → cancel-deploy-mZsD88Xa.mjs} +4 -6
- package/dist/{cf-access-BGe5sWXF.mjs → cf-access-YTlqvAsk.mjs} +1 -1
- package/dist/cf-build-output-ChY6-XWP.mjs +2 -0
- package/dist/cli/cli.mjs +45 -45
- package/dist/cli/cloudflare-operation-process.mjs +3 -3
- package/dist/cli/env-schema-probe.d.mts +1 -1
- package/dist/cli/env-schema-probe.mjs +2 -2
- package/dist/client-BxVixJBI.mjs +2 -0
- package/dist/{client-DKAsFA4X.mjs → client-mC-dFGDD.mjs} +103 -25
- package/dist/{cloudflare-cmd-CmY8dGkd.mjs → cloudflare-cmd-bhwCxAN6.mjs} +2 -2
- package/dist/{cloudflare-connect-IiR3ZieC.mjs → cloudflare-connect-CuhRgk3k.mjs} +2 -2
- package/dist/{cloudflare-operations-DzH-A966.mjs → cloudflare-operations-DYa3coIk.mjs} +2 -2
- package/dist/{cloudflare-operations-BCdXSsmo.mjs → cloudflare-operations-DoQrVHAt.mjs} +1 -1
- package/dist/{connect-B-usTnlF.mjs → connect-H6EvP62X.mjs} +12 -9
- package/dist/{create-project-CYOOAYcn.mjs → create-project-B2kk9Q0o.mjs} +6 -6
- package/dist/{create-project-FWbf5Olo.mjs → create-project-Dbb4uiN-.mjs} +1 -1
- package/dist/{db-Ck2eZ2iB.mjs → db-DpCKZto0.mjs} +35 -38
- package/dist/{delete-sK56qtzZ.mjs → delete-CxlSvsvW.mjs} +6 -7
- package/dist/{deploy-gCVaiJbS.mjs → deploy-CeEbnv4b.mjs} +9249 -8925
- package/dist/deploy-qmiLE1G1.mjs +2 -0
- package/dist/{dev-inbox-P0u4tM8Y.mjs → dev-inbox-D64ZKUod.mjs} +5 -6
- package/dist/{domain-DFfQE8jg.mjs → domain-B_9VgOm6.mjs} +8 -8
- package/dist/{email-CdnhUHvJ.mjs → email-BokHgjKj.mjs} +7 -7
- package/dist/{env-DmGdHi5i.mjs → env-BdTc-K2L.mjs} +7 -8
- package/dist/{env-public-BX_r8HR6.d.mts → env-public-DR7ZM6d-.d.mts} +1 -1
- package/dist/{env-validation-zZOCcQVZ.mjs → env-validation-cZBvGWaP.mjs} +1 -1
- package/dist/{fetch-stream-IvCYKyQL.mjs → fetch-stream-Nm3hkZbT.mjs} +8 -1
- package/dist/{gen-CshAkukR.mjs → gen-BhDlTpwW.mjs} +2 -4
- package/dist/gen-qCBwH3fZ.mjs +2 -0
- package/dist/{github-cmd-DftuEvIt.mjs → github-cmd-hhkTzlGS.mjs} +7 -16
- package/dist/{handler-CZ4nAylQ.d.mts → handler-BeK3weD4.d.mts} +1 -1
- package/dist/{help-Dotbf6iz.mjs → help-Bv44e2tm.mjs} +13 -9
- package/dist/help-Dw5f9Xrg.mjs +2 -0
- package/dist/index.d.mts +19 -15
- package/dist/index.mjs +2225 -2142
- package/dist/{info-B97bTX9N.mjs → info-DhtTKh65.mjs} +2 -4
- package/dist/{init-BTQEZZGJ.mjs → init-BMf4T03q.mjs} +17 -18
- package/dist/{limits-Bq5LG8Id.d.mts → limits-CFsDKQvU.d.mts} +1 -10
- package/dist/{limits-Cjuk2VPm.mjs → limits-D0vD1M9A.mjs} +1 -22
- package/dist/{link-B46sYH0K.mjs → link-BqYsSS_I.mjs} +6 -7
- package/dist/{list-BnPrIsU_.mjs → list-_kBToO-l.mjs} +10 -10
- package/dist/{live-Chw1eIMv.d.mts → live-iAuEH1hd.d.mts} +1 -1
- package/dist/{local-d1-2CMnpuW_.mjs → local-d1-fGxYrVD6.mjs} +2 -2
- package/dist/login-D7meQg_3.mjs +2 -0
- package/dist/{login-BuiMyUrx.mjs → login-DFuo-RWO.mjs} +9 -7
- package/dist/{logs-CZG9d-ic.mjs → logs-DYgnh6dH.mjs} +7 -8
- package/dist/migrate-CEu2X5DB.mjs +2 -0
- package/dist/{migrate-q4_njtaW.mjs → migrate-CV1on-yJ.mjs} +2 -4
- package/dist/{node-BkyRWRx8.mjs → node-C6Q6e1Es.mjs} +1 -1
- package/dist/{operator-args-B3BTmSoi.mjs → operator-args-DdY54Bgb.mjs} +1 -1
- package/dist/{operator-auth-7erYc6Lt.mjs → operator-auth-2dsGlTT5.mjs} +4 -8
- package/dist/{operator-client-CpHjTd0t.mjs → operator-client-BFqpp6z0.mjs} +3 -3
- package/dist/{operator-cmd-DGqPJUD3.mjs → operator-cmd-CUGHXrQf.mjs} +27 -9
- package/dist/operator-cmd-DRPvhpjM.mjs +2 -0
- package/dist/{output-CbM85T4k.mjs → output-BvdaehtE.mjs} +28 -4
- package/dist/pages/client.d.mts +4 -1
- package/dist/pages/client.mjs +120 -29
- package/dist/pages/index.mjs +1 -1
- package/dist/pages/islands-plugin.mjs +1 -1
- package/dist/pages/protocol.d.mts +2 -2
- package/dist/{plan-NPpwZ_kc.mjs → plan-CJGEeTEI.mjs} +2 -2
- package/dist/plan-COtUvdMA.mjs +2 -0
- package/dist/{platform-args-3k8X9-H_.mjs → platform-args-DSV2p4nY.mjs} +4 -2
- package/dist/platform-args-Dcy_5b_Y.mjs +2 -0
- package/dist/{platform-auth-config-DDrxUy4m.mjs → platform-auth-config-Bosx7U1h.mjs} +20 -19
- package/dist/{platform-auth-protection-BLadnnct.mjs → platform-auth-protection-DcbL_LW_.mjs} +8 -8
- package/dist/{platform-auth-recovery-CmCzX2Fl.mjs → platform-auth-recovery-WfTZ8K6o.mjs} +7 -7
- package/dist/{platform-cmd-DMDEc9uS.mjs → platform-cmd-DbKtQb_4.mjs} +73 -38
- package/dist/{platform-cmd-pMTRhvYs.mjs → platform-cmd-dyFUof0t.mjs} +1 -1
- package/dist/platform-discovery-BC0HCKYt.mjs +41 -0
- package/dist/{platform-domain-Di6EYrQl.mjs → platform-domain-Bsq43AJp.mjs} +5 -4
- package/dist/{platform-lifecycle-BFdSi9Qt.mjs → platform-lifecycle--5gWmDoI.mjs} +1 -1
- package/dist/{platform-lifecycle-BBoNzojY.mjs → platform-lifecycle-Cfi0o6xM.mjs} +4605 -3527
- package/dist/platform-management-Bbwf6-Dq.mjs +2 -0
- package/dist/{platform-management-CWOQOWXm.mjs → platform-management-BrF9STZp.mjs} +211 -31
- package/dist/{platform-plans-config-Vl9BP3H4.mjs → platform-plans-config-DU-8nC2Y.mjs} +3 -3
- package/dist/{platform-recovery-BJnQJEtQ.mjs → platform-recovery-DQAqAQYu.mjs} +3 -3
- package/dist/{platform-registry-DkPPXfp_.mjs → platform-registry-P387v42A.mjs} +18 -11
- package/dist/prepare-BBknoZI1.mjs +2 -0
- package/dist/{prepare-CBLT3CHH.mjs → prepare-BMrRWLwP.mjs} +3 -4
- package/dist/{prepare-DnaKBi--.mjs → prepare-WoUubFKL.mjs} +1 -1
- package/dist/{project-DsblYifa.mjs → project-BiaPQ7ur.mjs} +1 -1
- package/dist/{project-DKG5Ra8p.mjs → project-DQstYTxO.mjs} +1 -1
- package/dist/{project-cmd-DxnXstAq.mjs → project-cmd-BbH2LptN.mjs} +21 -22
- package/dist/{project-team-BmHcaALJ.mjs → project-team-Yiww3-go.mjs} +8 -9
- package/dist/{project-token-GionZlBI.mjs → project-token-BrtT2NkY.mjs} +6 -9
- package/dist/{project-zero-trust-JLCjVQA7.mjs → project-zero-trust-nYJ2V2nc.mjs} +5 -6
- package/dist/{prompts-BNUoTiaf.mjs → prompts-6OSVA7DJ.mjs} +58 -15
- package/dist/{prompts-2TGE6oXx.mjs → prompts-ZAuJlyCj.mjs} +1 -1
- package/dist/{protocol-CjF_iI9X.d.mts → protocol-BX8t2jiw.d.mts} +1 -1
- package/dist/{provision-CVku1JFU.mjs → provision-DMTPG8Rh.mjs} +1 -1
- package/dist/{provision-CaoxO_U7.mjs → provision-VeqAC77A.mjs} +18 -8
- package/dist/{requests-BOjIVAeF.mjs → requests-TIz_jtSu.mjs} +5 -6
- package/dist/{rollback-CwR3pLj3.mjs → rollback-dBqICPzj.mjs} +9 -10
- package/dist/{runner-Ol0TNjk6.mjs → runner--2KF6Kua.mjs} +1 -1
- package/dist/{runner-CXA9Fh8h.mjs → runner-voIltZrY.mjs} +1 -1
- package/dist/runtime/ai.d.mts +10 -11
- package/dist/runtime/ai.mjs +13 -18
- package/dist/runtime/client-react.mjs +1 -1
- package/dist/runtime/client-solid.mjs +1 -1
- package/dist/runtime/client-svelte.mjs +1 -1
- package/dist/runtime/client-vue.mjs +1 -1
- package/dist/runtime/client.mjs +1 -1
- package/dist/runtime/email/testing.mjs +1 -1
- package/dist/runtime/email.mjs +35 -17
- package/dist/runtime/env-public.d.mts +1 -1
- package/dist/runtime/fetch-stream.mjs +1 -1
- package/dist/runtime/handler.d.mts +1 -1
- package/dist/runtime/limits.d.mts +2 -2
- package/dist/runtime/limits.mjs +2 -2
- package/dist/runtime/live-client.d.mts +1 -1
- package/dist/runtime/live.d.mts +1 -1
- package/dist/runtime/migration-handler.mjs +33 -7
- package/dist/runtime/routing.d.mts +1 -1
- package/dist/runtime/routing.mjs +1 -1
- package/dist/runtime/sandbox.d.mts +1 -1
- package/dist/runtime/sandbox.mjs +12 -22
- package/dist/runtime/validator.d.mts +1 -1
- package/dist/runtime/ws-server.d.mts +1 -1
- package/dist/runtime/ws.d.mts +1 -1
- package/dist/{sandbox-XZAqzFlG.d.mts → sandbox-C5LFoJMf.d.mts} +7 -7
- package/dist/{scan-C7okrLyM.mjs → scan-C8dA7iM4.mjs} +10 -6
- package/dist/{secret-ByWJWBUG.mjs → secret-AKnNXNP5.mjs} +15 -16
- package/dist/{skills-C1PlclPt.mjs → skills-BPQUGClu.mjs} +1 -1
- package/dist/{subcommand-prompt-klFCfcor.mjs → subcommand-prompt-B6WS9Aoz.mjs} +2 -2
- package/dist/sveltekit.d.mts +1 -1
- package/dist/sveltekit.mjs +4 -9
- package/dist/validate-pna7R3Pv.mjs +2 -0
- package/dist/{validate-ctOrgiS3.mjs → validate-ymhap6M_.mjs} +1 -1
- package/dist/{wrangler-7K-bW_DL.mjs → wrangler-DhUerFkW.mjs} +10 -15
- package/dist/{ws-CL1w7GXU.d.mts → ws-C79j4aED.d.mts} +1 -1
- package/package.json +21 -21
- package/sandbox.Dockerfile +1 -1
- package/skills/void/SKILL.md +5 -2
- package/skills/void/docs/guide/ai.md +78 -137
- package/skills/void/docs/guide/app-types.md +1 -1
- package/skills/void/docs/guide/auth.md +0 -2
- package/skills/void/docs/guide/database/d1.md +7 -7
- package/skills/void/docs/guide/database.md +15 -0
- package/skills/void/docs/guide/deployment.md +58 -124
- package/skills/void/docs/guide/env-vars.md +1 -5
- package/skills/void/docs/guide/index.md +19 -18
- package/skills/void/docs/guide/kv.md +0 -2
- package/skills/void/docs/guide/live.md +2 -0
- package/skills/void/docs/guide/pages-routing/actions-and-forms.md +49 -47
- package/skills/void/docs/guide/pages-routing/overview.md +2 -2
- package/skills/void/docs/guide/platform/administration/email.md +0 -2
- package/skills/void/docs/guide/platform/administration/operations.md +11 -22
- package/skills/void/docs/guide/platform/administration/plans.md +1 -5
- package/skills/void/docs/guide/platform/administration/zero-trust.md +1 -4
- package/skills/void/docs/guide/platform/development/schema-ci.md +7 -1
- package/skills/void/docs/guide/platform/installation/credentials.md +2 -2
- package/skills/void/docs/guide/platform/installation/first-deployment.md +5 -1
- package/skills/void/docs/guide/platform/installation/maintenance.md +33 -11
- package/skills/void/docs/guide/platform/installation/prerequisites.md +79 -7
- package/skills/void/docs/guide/platform/installation/setup.md +1 -1
- package/skills/void/docs/guide/platform/installation/uninstall.md +44 -11
- package/skills/void/docs/guide/quickstart.md +37 -80
- package/skills/void/docs/guide/sandboxes.md +75 -66
- package/skills/void/docs/guide/self-hosted-platform.md +20 -0
- package/skills/void/docs/guide/websockets.md +0 -2
- package/skills/void/docs/integrations/frameworks/overview.md +2 -2
- package/skills/void/docs/integrations/frameworks/sveltekit.md +40 -25
- package/skills/void/docs/integrations/nodejs-bun-deno.md +7 -2
- package/skills/void/docs/reference/api/resources.md +38 -14
- package/skills/void/docs/reference/api.md +35 -34
- package/skills/void/docs/reference/cli/auth.md +3 -1
- package/skills/void/docs/reference/cli/deploy.md +1 -1
- package/skills/void/docs/reference/cli/platform-config.md +2 -2
- package/skills/void/docs/reference/cli/platform-installation.md +14 -12
- package/skills/void/docs/reference/cli/platform-operations.md +4 -8
- package/skills/void/docs/reference/cli/platform.md +9 -2
- package/skills/void/docs/reference/cli.md +1 -1
- package/skills/void/docs/reference/resource-inference.md +12 -8
- package/dist/cf-build-output-_0HNWysu.mjs +0 -2
- package/dist/client-B9UBI03V.mjs +0 -2
- package/dist/deploy-irP5q0-x.mjs +0 -2
- package/dist/gen-DvQTWzDU.mjs +0 -2
- package/dist/help-mabuIV98.mjs +0 -2
- package/dist/login-DIy9Sb-U.mjs +0 -2
- package/dist/migrate-BCiJ8w0f.mjs +0 -2
- package/dist/plan-D1Q5rf-r.mjs +0 -2
- package/dist/platform-args-DdnM6WIf.mjs +0 -2
- package/dist/platform-management-DG47jys8.mjs +0 -2
- package/dist/prepare-CnjcTV2s.mjs +0 -2
- package/dist/validate-Dq_L3s0S.mjs +0 -2
- package/skills/void/docs/guide/env-migration.md +0 -109
- package/dist/{cf-build-output-BPqOT964.mjs → cf-build-output-U8o1Gl6X.mjs} +1 -1
- package/dist/{env-validation-C_8tPdhs.mjs → env-validation-vBSQ8gw5.mjs} +1 -1
package/dist/index.mjs
CHANGED
|
@@ -7,102 +7,47 @@ import { n as loadVoidAuthConfig } from "./config-DPByhVck.mjs";
|
|
|
7
7
|
import { d as isNodeTarget, m as readConfig, u as getDatabaseDialect } from "./config-Br_JZD6u.mjs";
|
|
8
8
|
import { i as isMemberExpression, n as isIdentifier, r as isLiteral } from "./ast-CPGPjb0V.mjs";
|
|
9
9
|
import { t as isRouteEnabledForEnvironment } from "./parse-filename-CUbj-1MP.mjs";
|
|
10
|
-
import { r as scanQueuesSync, s as scanJobsSync, t as analyzeApplicationSync } from "./application-analysis-
|
|
10
|
+
import { r as scanQueuesSync, s as scanJobsSync, t as analyzeApplicationSync } from "./application-analysis-DC2yPvzY.mjs";
|
|
11
11
|
import { a as validateSsrEntries, n as SSR_SERVER_ENTRY_RELATIVE_PATHS, o as validateSsrEntry, t as SSR_CLIENT_ENTRY_RELATIVE_PATHS } from "./entry-CF5wvH4w.mjs";
|
|
12
12
|
import { r as hasEmailHandlers } from "./email--tEtMoj_.mjs";
|
|
13
|
-
import { i as resolveLayoutIds } from "./scan-
|
|
13
|
+
import { i as resolveLayoutIds } from "./scan-C8dA7iM4.mjs";
|
|
14
14
|
import { a as hasSandboxSqliteMigration, c as resolveSandboxConfig, i as assertSandboxNamespaceConfig, l as sandboxContainerConfig, n as DEFAULT_SANDBOX_CLASS_NAME, r as SANDBOX_MIGRATION_TAG } from "./config-C_XRIPx2.mjs";
|
|
15
|
-
import { Ct as hasDocumentRoutes,
|
|
16
|
-
import { n as scopedAccessCredentials } from "./cf-access-TWNzXltp.mjs";
|
|
17
|
-
import { r as ensurePlatformProxyAccess } from "./cf-access-BGe5sWXF.mjs";
|
|
18
|
-
import { n as readDeploymentPlatform, r as readProjectConfig } from "./project-DKG5Ra8p.mjs";
|
|
19
|
-
import { a as getApiUrl, c as getToken, o as getProxyUrl } from "./auth-CQkSDS50.mjs";
|
|
20
|
-
import { a as restoreCloudflarePreviewPointer, r as materializeCloudflareConfig, s as stashCloudflareDeployPointer } from "./cloudflare-config-Bktvwtpf.mjs";
|
|
21
|
-
import { D as syncWranglerDurableObjects, E as syncWranglerBindings, O as syncWranglerWebSockets, T as resolveLiveBindingConfig, _ as readExistingLiveBindingConfig, a as LIVE_MIGRATION_TAG, b as readWranglerCompat, d as ensureWranglerCompatibilityDate, f as ensureWranglerNodejsAlsFlag, g as normalizeWranglerCompatibilityFlags, h as nextDurableObjectMigrationTag, m as hasDurableObjectSqliteMigration, p as findBindingNameOwner, u as ensureWranglerCompat, w as readWranglerWebSocketTombstones } from "./wrangler-7K-bW_DL.mjs";
|
|
15
|
+
import { Ct as hasDocumentRoutes, H as selectNativeVersionMetadataBinding, J as collectPages, St as hasApiRoutes, Tt as resolveAssetRoutingPolicy, V as CLOUDFLARE_PRESERVE_BINDINGS_BUILD_ENV, X as hasRevalidate, Y as collectRevalidate, at as resolveDistDir, lt as resolveWorkerDirName, o as prerenderPages, s as prerenderPagesNode, u as SEND_EMAIL_BINDING, xt as generateQueueResolverSource } from "./deploy-CeEbnv4b.mjs";
|
|
22
16
|
import { i as voidWarn, n as voidError, r as voidLog, t as setShowTimestamp } from "./log-vqY3pqwI.mjs";
|
|
23
17
|
import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-2rKda9Y6.mjs";
|
|
24
|
-
import { n as
|
|
25
|
-
import {
|
|
18
|
+
import { n as readDeploymentPlatform, r as readProjectConfig } from "./project-DQstYTxO.mjs";
|
|
19
|
+
import { a as restoreCloudflarePreviewPointer, r as materializeCloudflareConfig, s as stashCloudflareDeployPointer } from "./cloudflare-config-Bktvwtpf.mjs";
|
|
20
|
+
import { D as syncWranglerDurableObjects, E as syncWranglerBindings, O as syncWranglerWebSockets, T as resolveLiveBindingConfig, _ as readExistingLiveBindingConfig, a as LIVE_MIGRATION_TAG, b as readWranglerCompat, d as ensureWranglerCompatibilityDate, f as ensureWranglerNodejsAlsFlag, g as normalizeWranglerCompatibilityFlags, h as nextDurableObjectMigrationTag, m as hasDurableObjectSqliteMigration, p as findBindingNameOwner, u as ensureWranglerCompat, w as readWranglerWebSocketTombstones } from "./wrangler-DhUerFkW.mjs";
|
|
21
|
+
import { n as writeLocalD1PersistState, t as resolveLocalD1Target } from "./local-d1-fGxYrVD6.mjs";
|
|
22
|
+
import { n as scopedAccessCredentials } from "./cf-access-TWNzXltp.mjs";
|
|
23
|
+
import { r as ensurePlatformProxyAccess } from "./cf-access-YTlqvAsk.mjs";
|
|
24
|
+
import { a as getApiUrl, c as getToken, o as getProxyUrl } from "./auth-D52yG7hl.mjs";
|
|
26
25
|
import { n as parseDotenvFile } from "./dotenv-VQxupEUv.mjs";
|
|
26
|
+
import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-B8GeM0RM.mjs";
|
|
27
27
|
import { i as generateDbDts, n as generateQueueTypes, r as DRIZZLE_OPERATORS, t as generateRouteTypes } from "./route-types-Clm4WD0B.mjs";
|
|
28
28
|
import { t as discoverSchema } from "./discover-BcPPiF1o.mjs";
|
|
29
|
-
import { _ as parseCapturedMessage, a as DEV_INBOX_ENV_VAR, c as DEV_INBOX_TOKEN_ENV_VAR, f as clearDevBuffer, g as listDevMessages, l as DEV_INBOX_TOKEN_HEADER, m as findDevMessage, n as DEV_INBOX_ACK_VALUE, o as DEV_INBOX_MAX_CAPTURE_BYTES, s as DEV_INBOX_PREFIX, t as DEV_INBOX_ACK_HEADER, u as DEV_INBOX_TOKEN_QUERY, v as pushToDevBuffer, y as registerEmailSink } from "./dev-inbox-
|
|
29
|
+
import { _ as parseCapturedMessage, a as DEV_INBOX_ENV_VAR, c as DEV_INBOX_TOKEN_ENV_VAR, f as clearDevBuffer, g as listDevMessages, l as DEV_INBOX_TOKEN_HEADER, m as findDevMessage, n as DEV_INBOX_ACK_VALUE, o as DEV_INBOX_MAX_CAPTURE_BYTES, s as DEV_INBOX_PREFIX, t as DEV_INBOX_ACK_HEADER, u as DEV_INBOX_TOKEN_QUERY, v as pushToDevBuffer, y as registerEmailSink } from "./dev-inbox-D64ZKUod.mjs";
|
|
30
30
|
import { t as MagicString } from "./dist-AoCzRTJE.mjs";
|
|
31
31
|
import { n as findEnvFileForPaths, r as generateEnvTypes, t as findEnvFile } from "./env-types-D5kds_OZ.mjs";
|
|
32
|
-
import { y as loadUserEnvFile } from "./env-validation-
|
|
32
|
+
import { y as loadUserEnvFile } from "./env-validation-vBSQ8gw5.mjs";
|
|
33
33
|
import { a as loadLocalEnv, o as resolveRemoteMode, r as findLegacyEnvFiles, s as stripInternalEnvKeys, t as CLIENT_ENV_PREFIX$2 } from "./env-_v349JBd.mjs";
|
|
34
|
-
import { n as completeApplicationAnalysis } from "./application-routing-B7QEjqSR.mjs";
|
|
35
|
-
import { n as generateVoidAuthDrizzleSchema, t as generateVoidAuthDatabaseModule } from "./drizzle-Beb2Am5S.mjs";
|
|
36
34
|
import { n as INTERNAL_BINDING_PREFIXES, t as INTERNAL_BINDING_EXACT_NAMES } from "./env-raw-Cx8ElDdj.mjs";
|
|
35
|
+
import { n as completeApplicationAnalysis } from "./application-routing-C9khgLiS.mjs";
|
|
36
|
+
import { n as generateVoidAuthDrizzleSchema, t as generateVoidAuthDatabaseModule } from "./drizzle-Beb2Am5S.mjs";
|
|
37
37
|
import { n as defer } from "./defer-YsYDUoii.mjs";
|
|
38
38
|
import { a as defineMiddleware, c as defineScheduled, i as defineHead, n as basicAuth, o as defineQueue, r as defineHandler, s as defineRender, t as VoidAssetRewriteError } from "./handler-DghKr6dU.mjs";
|
|
39
39
|
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync, writeSync } from "node:fs";
|
|
40
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
41
40
|
import { cloudflare } from "@cloudflare/vite-plugin";
|
|
42
|
-
import {
|
|
41
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
43
42
|
import { normalizePath, parseSync } from "vite";
|
|
43
|
+
import { appendSqliteClassMigration } from "@void/deploy-core";
|
|
44
44
|
import { timingSafeEqual } from "node:crypto";
|
|
45
45
|
import { homedir } from "node:os";
|
|
46
46
|
import { isDeepStrictEqual } from "node:util";
|
|
47
47
|
import { init, parse } from "es-module-lexer";
|
|
48
48
|
import { compilePattern } from "@void/edge";
|
|
49
|
-
//#region src/cloudflare-durable-patch.ts
|
|
50
|
-
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
51
|
-
function entries(value, label) {
|
|
52
|
-
if (value === void 0) return [];
|
|
53
|
-
if (!Array.isArray(value) || value.some((entry) => !entry || typeof entry !== "object" || Array.isArray(entry))) throw new Error(`Cloudflare ${label} configuration must be an array of objects.`);
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
function byIdentity(rows, field, label, unique = false) {
|
|
57
|
-
const map = /* @__PURE__ */ new Map();
|
|
58
|
-
for (const row of rows) {
|
|
59
|
-
const id = row[field];
|
|
60
|
-
if (typeof id !== "string" || !id) throw new Error(`Cloudflare ${label} is missing its ${field}.`);
|
|
61
|
-
const previous = map.get(id);
|
|
62
|
-
if (previous && unique) throw new Error(`Cloudflare ${label} ${JSON.stringify(id)} is configured more than once.`);
|
|
63
|
-
if (previous && !isDeepStrictEqual(previous, row)) throw new Error(`Cloudflare ${label} ${JSON.stringify(id)} has conflicting configuration.`);
|
|
64
|
-
if (!previous) map.set(id, row);
|
|
65
|
-
}
|
|
66
|
-
return map;
|
|
67
|
-
}
|
|
68
|
-
/** Finish cumulative feature planning without repeating the Cloudflare plugin's array fallback. */
|
|
69
|
-
function finalizeDurableObjectPatch(resolved, patch, generatedLocalBindings) {
|
|
70
|
-
const baselineObjects = resolved.durable_objects;
|
|
71
|
-
const pendingObjects = patch.durable_objects;
|
|
72
|
-
const baselineBindings = byIdentity(entries(baselineObjects?.bindings, "Durable Object bindings"), "name", "Durable Object binding", true);
|
|
73
|
-
const pendingBindings = byIdentity(entries(pendingObjects?.bindings, "Durable Object bindings"), "name", "Durable Object binding");
|
|
74
|
-
const history = entries(resolved.migrations, "Durable Object migrations");
|
|
75
|
-
const baselineMigrations = byIdentity(history, "tag", "Durable Object migration", true);
|
|
76
|
-
for (const local of generatedLocalBindings) for (const map of [baselineBindings, pendingBindings]) {
|
|
77
|
-
const existing = map.get(local.name);
|
|
78
|
-
if (existing && (existing.class_name !== local.class_name || existing.script_name !== void 0 || existing.environment !== void 0)) throw new Error(`Cloudflare generated Durable Object binding ${JSON.stringify(local.name)} conflicts with its configured identity.`);
|
|
79
|
-
}
|
|
80
|
-
if (pendingObjects?.bindings !== void 0) {
|
|
81
|
-
const additions = [];
|
|
82
|
-
for (const [name, row] of pendingBindings) {
|
|
83
|
-
const existing = baselineBindings.get(name);
|
|
84
|
-
if (existing && !isDeepStrictEqual(existing, row)) throw new Error(`Cloudflare Durable Object binding ${JSON.stringify(name)} has conflicting configuration.`);
|
|
85
|
-
if (!existing) additions.push(row);
|
|
86
|
-
}
|
|
87
|
-
patch.durable_objects = {
|
|
88
|
-
...pendingObjects,
|
|
89
|
-
bindings: additions
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
if (patch.migrations !== void 0) {
|
|
93
|
-
const pending = byIdentity(entries(patch.migrations, "Durable Object migrations"), "tag", "Durable Object migration");
|
|
94
|
-
const additions = [];
|
|
95
|
-
for (const [tag, row] of pending) {
|
|
96
|
-
const previous = baselineMigrations.get(tag);
|
|
97
|
-
if (previous && !isDeepStrictEqual(previous, row)) throw new Error(`Cloudflare Durable Object migration ${JSON.stringify(tag)} has conflicting configuration.`);
|
|
98
|
-
if (!previous) additions.push(row);
|
|
99
|
-
}
|
|
100
|
-
patch.migrations = [...history, ...additions];
|
|
101
|
-
resolved.migrations = [];
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
//#endregion
|
|
105
49
|
//#region src/pages/dev-css-assets.ts
|
|
50
|
+
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
106
51
|
const DEV_CSS_ASSET_MODULE_ID = "virtual:void-pages-dev-assets";
|
|
107
52
|
const DEV_CSS_ASSET_ENDPOINT = "/__void/dev/pages-css";
|
|
108
53
|
const DEV_CSS_ASSET_GUARD_HEADER = "X-Void-Dev-Asset-Request";
|
|
@@ -501,6 +446,124 @@ export async function getVoidPagesDevCss(request, env, payload) {
|
|
|
501
446
|
}];
|
|
502
447
|
}
|
|
503
448
|
//#endregion
|
|
449
|
+
//#region src/plugin-ai-setup.ts
|
|
450
|
+
/**
|
|
451
|
+
* Decide whether to inject a Workers AI binding into the built wrangler config.
|
|
452
|
+
*
|
|
453
|
+
* Builds use the production binding. During dev, a project explicitly saved
|
|
454
|
+
* with the direct Cloudflare destination uses Cloudflare's remote binding;
|
|
455
|
+
* managed and unsaved projects keep using the Void HTTPS proxy.
|
|
456
|
+
*
|
|
457
|
+
* `!resolvedAi` is load-bearing: mergeBindings' return is defu-merged over the
|
|
458
|
+
* resolved root-wrangler config and wins, so skipping when the user already
|
|
459
|
+
* declared an `ai` binding avoids clobbering theirs. `!resultAi` is harmless
|
|
460
|
+
* defense-in-depth (nothing else in mergeBindings sets `result.ai`).
|
|
461
|
+
*/
|
|
462
|
+
function shouldBindWorkersAi(command, needsAI, resolvedAi, resultAi, directCloudflareDev = false) {
|
|
463
|
+
return needsAI && (command === "build" || command === "serve" && directCloudflareDev) && !resultAi && !resolvedAi;
|
|
464
|
+
}
|
|
465
|
+
/** Resolve the runtime Workers AI binding, preserving a root Wrangler declaration. */
|
|
466
|
+
function resolveWorkersAiBindingName(resolvedAi, configuredName) {
|
|
467
|
+
if (typeof resolvedAi === "object" && resolvedAi !== null) {
|
|
468
|
+
const binding = resolvedAi.binding;
|
|
469
|
+
if (typeof binding === "string") return binding;
|
|
470
|
+
}
|
|
471
|
+
return configuredName ?? "AI";
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Read Void auth credentials for local dev AI proxy access.
|
|
475
|
+
* Returns a discriminated union so callers can distinguish
|
|
476
|
+
* "no token" from "no project" for targeted onboarding guidance.
|
|
477
|
+
*/
|
|
478
|
+
function readVoidDevCredentials(root) {
|
|
479
|
+
const token = getToken(root);
|
|
480
|
+
if (!token) return { status: "no-token" };
|
|
481
|
+
const apiUrl = getApiUrl(root);
|
|
482
|
+
const project = readProjectConfig(root);
|
|
483
|
+
if (!project) return {
|
|
484
|
+
status: "no-project",
|
|
485
|
+
apiUrl,
|
|
486
|
+
token
|
|
487
|
+
};
|
|
488
|
+
const proxyUrl = getProxyUrl(root);
|
|
489
|
+
ensurePlatformProxyAccess(apiUrl, proxyUrl);
|
|
490
|
+
return {
|
|
491
|
+
status: "ok",
|
|
492
|
+
token,
|
|
493
|
+
projectId: project.projectId,
|
|
494
|
+
proxyUrl,
|
|
495
|
+
accessCredentials: scopedAccessCredentials(process.env, proxyUrl)
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Interactive AI setup prompt shown during dev server startup when the user
|
|
500
|
+
* has a token but no linked project. Runs as fire-and-forget so it doesn't
|
|
501
|
+
* block the server. On completion, writes project.json and restarts the server.
|
|
502
|
+
*/
|
|
503
|
+
async function runAiSetupPrompt(root, token, apiUrl, server) {
|
|
504
|
+
const { select, text, isCancel, log } = await import("./prompts-ZAuJlyCj.mjs");
|
|
505
|
+
const { PlatformClient } = await import("./client-BxVixJBI.mjs");
|
|
506
|
+
const { attachSelectedPlatform, writeProjectConfig } = await import("./project-BiaPQ7ur.mjs");
|
|
507
|
+
const { inferDefaultSlug } = await import("./resolve-project-DtbidV4y.mjs");
|
|
508
|
+
const client = new PlatformClient(token, apiUrl);
|
|
509
|
+
let projects;
|
|
510
|
+
try {
|
|
511
|
+
projects = await client.listProjects();
|
|
512
|
+
} catch {
|
|
513
|
+
voidWarn("Failed to fetch projects. Run `void project link` to link a project manually.");
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const options = [{
|
|
517
|
+
value: "__create__",
|
|
518
|
+
label: "Create new project"
|
|
519
|
+
}, ...projects.map((p) => ({
|
|
520
|
+
value: p.id,
|
|
521
|
+
label: p.slug
|
|
522
|
+
}))];
|
|
523
|
+
console.log();
|
|
524
|
+
log.info("[void] AI requires a linked project. Let's set one up:");
|
|
525
|
+
const result = await select({
|
|
526
|
+
message: "Select a project:",
|
|
527
|
+
options
|
|
528
|
+
});
|
|
529
|
+
if (isCancel(result)) return;
|
|
530
|
+
let projectId;
|
|
531
|
+
let slug;
|
|
532
|
+
if (result === "__create__") {
|
|
533
|
+
const { validateProjectSlug } = await import("./project-slug-DofjTFd-.mjs");
|
|
534
|
+
const { createProjectWithSlugRetry } = await import("./create-project-Dbb4uiN-.mjs");
|
|
535
|
+
let created;
|
|
536
|
+
try {
|
|
537
|
+
created = await createProjectWithSlugRetry(client, inferDefaultSlug(root), async (defaultSlug) => {
|
|
538
|
+
const slugResult = await text({
|
|
539
|
+
message: "Choose a slug for your new Void project:",
|
|
540
|
+
placeholder: "my-app",
|
|
541
|
+
initialValue: defaultSlug,
|
|
542
|
+
validate: validateProjectSlug
|
|
543
|
+
});
|
|
544
|
+
if (isCancel(slugResult) || typeof slugResult === "symbol") return;
|
|
545
|
+
return slugResult.trim();
|
|
546
|
+
});
|
|
547
|
+
} catch (err) {
|
|
548
|
+
voidError(`project: Failed to create project: '${err instanceof Error ? err.message : String(err)}'.`);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
if (!created) return;
|
|
552
|
+
projectId = created.id;
|
|
553
|
+
slug = created.slug;
|
|
554
|
+
} else {
|
|
555
|
+
const project = projects.find((p) => p.id === result);
|
|
556
|
+
projectId = project.id;
|
|
557
|
+
slug = project.slug;
|
|
558
|
+
}
|
|
559
|
+
writeProjectConfig(root, attachSelectedPlatform(root, {
|
|
560
|
+
projectId,
|
|
561
|
+
slug
|
|
562
|
+
}, client.apiUrl));
|
|
563
|
+
log.success(`Linked to ${import_picocolors.default.blue(slug)}`);
|
|
564
|
+
await server.restart();
|
|
565
|
+
}
|
|
566
|
+
//#endregion
|
|
504
567
|
//#region src/plugin-binding-runtime.ts
|
|
505
568
|
const envModuleResolved$1 = fileURLToPath(import.meta.resolve("#self/_env"));
|
|
506
569
|
const kvModuleResolved = fileURLToPath(import.meta.resolve("void/kv"));
|
|
@@ -554,31 +617,206 @@ export const storage = new Proxy({}, {
|
|
|
554
617
|
};
|
|
555
618
|
}
|
|
556
619
|
//#endregion
|
|
557
|
-
//#region src/plugin-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
620
|
+
//#region src/plugin-client-stubs.ts
|
|
621
|
+
function createClientStubsPlugin() {
|
|
622
|
+
const STUB_PREFIX = "\0void-client-stub:";
|
|
623
|
+
const SERVER_ONLY_STUBS = {
|
|
624
|
+
"void/db": "export const db = {}; export const createDb = () => ({});",
|
|
625
|
+
"void/kv": "export const kv = {}; export const createKV = () => ({});",
|
|
626
|
+
"void/ai": "export const ai = {};",
|
|
627
|
+
"void/queues": "export const queues = {};",
|
|
628
|
+
"void/storage": "export const storage = {}; export const createStorage = () => ({});",
|
|
629
|
+
"void/isr": "export const revalidate = async () => {};",
|
|
630
|
+
"void/sandbox": `
|
|
631
|
+
export class SandboxLimitError extends Error {
|
|
632
|
+
constructor(reason = 'concurrency', message) {
|
|
633
|
+
super(message ?? 'Sandbox limit reached: ' + reason);
|
|
634
|
+
this.name = 'SandboxLimitError';
|
|
635
|
+
this.reason = reason;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
export class SandboxUnavailableError extends Error {
|
|
639
|
+
constructor(message = 'Managed Sandbox is unavailable on this Void platform.') {
|
|
640
|
+
super(message);
|
|
641
|
+
this.name = 'SandboxUnavailableError';
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
export async function getSandbox() {
|
|
645
|
+
throw new Error('void/sandbox is server-only and cannot be imported by browser code.');
|
|
646
|
+
}
|
|
647
|
+
export const sandbox = { get: getSandbox };
|
|
648
|
+
`,
|
|
649
|
+
"void/email": `
|
|
650
|
+
const serverOnly = (name) => () => {
|
|
651
|
+
throw new Error(name + ' is server-only and cannot be imported by browser code.');
|
|
652
|
+
};
|
|
653
|
+
export const sendEmail = serverOnly('sendEmail');
|
|
654
|
+
export const createEmail = serverOnly('createEmail');
|
|
655
|
+
export const defineEmail = serverOnly('defineEmail');
|
|
656
|
+
export const parseEmail = serverOnly('parseEmail');
|
|
657
|
+
export const replyEmail = serverOnly('replyEmail');
|
|
658
|
+
export const createMessageProxy = serverOnly('createMessageProxy');
|
|
659
|
+
`,
|
|
660
|
+
"void/email/testing": `
|
|
661
|
+
const serverOnly = (name) => () => {
|
|
662
|
+
throw new Error(name + ' is server-only and cannot be imported by browser code.');
|
|
663
|
+
};
|
|
664
|
+
export const createEmailTestHarness = serverOnly('createEmailTestHarness');
|
|
665
|
+
export const createInboundTestHarness = serverOnly('createInboundTestHarness');
|
|
666
|
+
`,
|
|
667
|
+
"void/live": "",
|
|
668
|
+
"void/durable": ""
|
|
669
|
+
};
|
|
670
|
+
const STUB_RESOLVE_FILTER = new RegExp(`^(${Object.keys(SERVER_ONLY_STUBS).join("|").replaceAll("/", "\\/")})$`);
|
|
671
|
+
const STUB_LOAD_FILTER = new RegExp(`^\\0void-client-stub:`);
|
|
566
672
|
return {
|
|
567
|
-
name: "void:
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
673
|
+
name: "void:client-stubs",
|
|
674
|
+
enforce: "pre",
|
|
675
|
+
resolveId: {
|
|
676
|
+
filter: { id: STUB_RESOLVE_FILTER },
|
|
677
|
+
handler(id) {
|
|
678
|
+
if (this.environment?.name === "client") {
|
|
679
|
+
if (id === "void/live" || id === "void/durable") this.error(id === "void/live" ? "void/live is server-only and cannot be imported by browser code. Import void/live/client instead." : "void/durable is server-only and cannot be imported by browser code.");
|
|
680
|
+
return STUB_PREFIX + id;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
},
|
|
684
|
+
load: {
|
|
685
|
+
filter: { id: STUB_LOAD_FILTER },
|
|
686
|
+
handler(id) {
|
|
687
|
+
return SERVER_ONLY_STUBS[id.slice(18)] ?? "export default {};";
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
//#endregion
|
|
693
|
+
//#region src/plugin-dev-config.ts
|
|
694
|
+
/**
|
|
695
|
+
* Persist the dev-trigger token at `<root>/.void/dev-trigger-token` so
|
|
696
|
+
* every voidPlugin() invocation against the same project reads the same
|
|
697
|
+
* value. This matters because `@cloudflare/vite-plugin` spawns a
|
|
698
|
+
* worker-environment child Vite that re-imports vite.config.ts and calls
|
|
699
|
+
* voidPlugin() again — without persistence the parent and child would
|
|
700
|
+
* generate different UUIDs, the dispatch file would embed one and the
|
|
701
|
+
* hint plugin would print the other, and curls would land on
|
|
702
|
+
* `unauthorized`.
|
|
703
|
+
*
|
|
704
|
+
* The file persists across dev sessions by design — it lives under `.void/`
|
|
705
|
+
* (gitignored), and `cleanupWrapper` in `void deploy` explicitly preserves
|
|
706
|
+
* it across its own wipe so saved curl scripts keep working between deploys.
|
|
707
|
+
* The token is local to the project and only honored by a worker bound to
|
|
708
|
+
* localhost; treat it like the rest of `.void/` — disposable scratch state,
|
|
709
|
+
* safe to remove manually if desired.
|
|
710
|
+
*
|
|
711
|
+
* The file is owner-only (0600). It is the one credential the dev-inbox
|
|
712
|
+
* routes accept, and loopback is reachable by every account on the host,
|
|
713
|
+
* so a default-umask 0644 file would hand any other local user the raw MIME,
|
|
714
|
+
* the DELETE, and the capture endpoint. A file persisted by an earlier
|
|
715
|
+
* release is tightened in place, never rotated. Windows has no group/other
|
|
716
|
+
* bits — `mode` and `chmod` only toggle write there — so it is left alone.
|
|
717
|
+
*/
|
|
718
|
+
/**
|
|
719
|
+
* Best-effort detection of `vite build` (and `vite preview`) so we can skip
|
|
720
|
+
* dev-only side effects — writing the dev-trigger token and the dispatch
|
|
721
|
+
* file — when no dev server will run. Vite's CLI passes the subcommand as
|
|
722
|
+
* `process.argv[2]`; programmatic API users (and `vite` with no subcommand)
|
|
723
|
+
* default to `dev`, which we detect as the absence of a build-shaped first
|
|
724
|
+
* positional. This is intentionally permissive: when in doubt, we behave as
|
|
725
|
+
* if we're in dev so first-request correctness is preserved.
|
|
726
|
+
*/
|
|
727
|
+
function isViteBuildOrPreview() {
|
|
728
|
+
const subcommand = process.argv[2];
|
|
729
|
+
return subcommand === "build" || subcommand === "preview";
|
|
730
|
+
}
|
|
731
|
+
const DEV_TRIGGER_TOKEN_FILE_MODE = 384;
|
|
732
|
+
function getOrCreateDevTriggerToken(root) {
|
|
733
|
+
const tokenPath = join(root, ".void", "dev-trigger-token");
|
|
734
|
+
const readExisting = () => {
|
|
735
|
+
try {
|
|
736
|
+
const existing = readFileSync(tokenPath, "utf-8").trim();
|
|
737
|
+
return existing.length > 0 ? existing : void 0;
|
|
738
|
+
} catch {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
const tightenExisting = () => {
|
|
743
|
+
if (process.platform === "win32") return;
|
|
744
|
+
try {
|
|
745
|
+
if ((statSync(tokenPath).mode & 63) !== 0) chmodSync(tokenPath, DEV_TRIGGER_TOKEN_FILE_MODE);
|
|
746
|
+
} catch {}
|
|
747
|
+
};
|
|
748
|
+
const existing = readExisting();
|
|
749
|
+
if (existing) {
|
|
750
|
+
tightenExisting();
|
|
751
|
+
return existing;
|
|
752
|
+
}
|
|
753
|
+
mkdirSync(join(root, ".void"), { recursive: true });
|
|
754
|
+
const fresh = crypto.randomUUID();
|
|
755
|
+
try {
|
|
756
|
+
const fd = openSync(tokenPath, "wx", DEV_TRIGGER_TOKEN_FILE_MODE);
|
|
757
|
+
try {
|
|
758
|
+
writeSync(fd, fresh);
|
|
759
|
+
} finally {
|
|
760
|
+
closeSync(fd);
|
|
761
|
+
}
|
|
762
|
+
return fresh;
|
|
763
|
+
} catch (err) {
|
|
764
|
+
if (err.code !== "EEXIST") throw err;
|
|
765
|
+
const winner = readExisting();
|
|
766
|
+
if (winner) return winner;
|
|
767
|
+
return fresh;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Warn when the project's `package.json` does not declare `"type": "module"`.
|
|
772
|
+
*
|
|
773
|
+
* Vite's default `configLoader: "bundle"` bundles `vite.config.ts`,
|
|
774
|
+
* externalizes deps, and then loads the bundle. When `package.json` is non-ESM
|
|
775
|
+
* the bundle is written as `.js` and loaded via CJS `require()`, which throws
|
|
776
|
+
* `ERR_REQUIRE_ASYNC_MODULE` if any externalized dep's ESM graph contains
|
|
777
|
+
* top-level await. `@cloudflare/vite-plugin` (which voidPlugin pulls in) has
|
|
778
|
+
* exactly such a TLA, so non-ESM consumers hit this on every Vite invocation.
|
|
779
|
+
*
|
|
780
|
+
* Runs once per Vite process (factory time), single sync read.
|
|
781
|
+
*/
|
|
782
|
+
function warnIfPackageJsonIsNotEsm(root) {
|
|
783
|
+
const pkgPath = join(root, "package.json");
|
|
784
|
+
if (!existsSync(pkgPath)) return;
|
|
785
|
+
let pkg;
|
|
786
|
+
try {
|
|
787
|
+
pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
788
|
+
} catch {
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
if (pkg.type === "module") return;
|
|
792
|
+
voidWarn("package.json is missing `\"type\": \"module\"`. Vite's default config loader will fail with ERR_REQUIRE_ASYNC_MODULE on transitive top-level await (e.g. @cloudflare/vite-plugin). Add `\"type\": \"module\"` to package.json, or run vite build with `--configLoader runner`.");
|
|
793
|
+
}
|
|
794
|
+
//#endregion
|
|
795
|
+
//#region src/plugin-dev-triggers-hint.ts
|
|
796
|
+
/**
|
|
797
|
+
* Prints a one-shot hint on dev server start when the project has crons
|
|
798
|
+
* or queues, explaining how to trigger them locally. Both default Void mode
|
|
799
|
+
* and framework mode (SvelteKit, Nuxt, Analog, Astro, TanStack Start, React
|
|
800
|
+
* Router, vinext) expose `/__void/scheduled` and `/__void/queue`.
|
|
801
|
+
*/
|
|
802
|
+
function devTriggersHintPlugin(root, options) {
|
|
803
|
+
const { devTriggerToken } = options;
|
|
804
|
+
return {
|
|
805
|
+
name: "void:dev-triggers-hint",
|
|
806
|
+
apply: "serve",
|
|
807
|
+
configureServer(server) {
|
|
808
|
+
const paths = readProjectPaths(root);
|
|
809
|
+
const jobs = scanJobsSync(root, paths.cronsDir);
|
|
810
|
+
const queues = scanQueuesSync(root, paths.queuesDir);
|
|
811
|
+
if (jobs.length === 0 && queues.length === 0) return;
|
|
812
|
+
const afterReady = (fn) => {
|
|
813
|
+
if (server.httpServer) server.httpServer.once("listening", () => setTimeout(fn, 100));
|
|
814
|
+
else fn();
|
|
815
|
+
};
|
|
816
|
+
afterReady(() => {
|
|
817
|
+
const address = server.httpServer?.address();
|
|
818
|
+
const port = typeof address === "object" && address ? address.port : null;
|
|
819
|
+
if (port == null) return;
|
|
582
820
|
const baseUrl = `http://localhost:${port}`;
|
|
583
821
|
const summary = [];
|
|
584
822
|
if (jobs.length > 0) summary.push(`${jobs.length} cron${jobs.length === 1 ? "" : "s"}`);
|
|
@@ -783,7 +1021,7 @@ function createPrimaryDb(d1) {
|
|
|
783
1021
|
const migrationsDir = paths.migrationsDir;
|
|
784
1022
|
if (!(existsSync(migrationsDir) && readdirSync(migrationsDir).some((f) => f.endsWith(".sql")))) {
|
|
785
1023
|
if (dialect === "sqlite") {
|
|
786
|
-
const { getApplicationTables, getMiniflareDatabasePath } = await import("./runner-
|
|
1024
|
+
const { getApplicationTables, getMiniflareDatabasePath } = await import("./runner-voIltZrY.mjs");
|
|
787
1025
|
if ((await getApplicationTables(getMiniflareDatabasePath(options.localD1PersistRoot ?? join(root, ".void"), options.localD1DatabaseId ?? "local")).catch(() => [])).length > 0) return;
|
|
788
1026
|
}
|
|
789
1027
|
voidWarn(`Schema found but no migrations in ${paths.sourceRelative("db/migrations")}/.\n \`void db push\` syncs the schema to your local DB without creating migration files.
|
|
@@ -1426,8 +1664,7 @@ function envSchemaPlugin(root, options = {}) {
|
|
|
1426
1664
|
const requiredServerKeys = [];
|
|
1427
1665
|
for (const key of serverKeys) {
|
|
1428
1666
|
const result = schema[key]["~standard"].validate(void 0);
|
|
1429
|
-
|
|
1430
|
-
if (resolved && typeof resolved === "object" && "issues" in resolved && resolved.issues) requiredServerKeys.push(key);
|
|
1667
|
+
if ((result instanceof Promise ? await result : result).issues) requiredServerKeys.push(key);
|
|
1431
1668
|
}
|
|
1432
1669
|
options.onSchema({
|
|
1433
1670
|
keys,
|
|
@@ -1697,12 +1934,17 @@ function __wrapMessage(m, decisions) {
|
|
|
1697
1934
|
|
|
1698
1935
|
async function __dispatchScheduled(request, env, ctx) {
|
|
1699
1936
|
const { cron, scheduledTime } = await request.json();
|
|
1700
|
-
const
|
|
1701
|
-
if (
|
|
1937
|
+
const jobs = __jobs.filter((j) => j.crons.includes(cron));
|
|
1938
|
+
if (jobs.length === 0) {
|
|
1702
1939
|
return __json({ error: \`No cron handler matches "\${cron}"\` }, 404);
|
|
1703
1940
|
}
|
|
1704
1941
|
const controller = { cron, scheduledTime, noRetry() {} };
|
|
1705
|
-
|
|
1942
|
+
const executionContext = ctx ?? { waitUntil() {}, passThroughOnException() {} };
|
|
1943
|
+
const results = await Promise.allSettled(jobs.map(async (job) => {
|
|
1944
|
+
await job.handler(controller, env, executionContext);
|
|
1945
|
+
}));
|
|
1946
|
+
const errors = results.filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
1947
|
+
if (errors.length) throw new AggregateError(errors, "Scheduled jobs failed");
|
|
1706
1948
|
return __json({ ok: true });
|
|
1707
1949
|
}
|
|
1708
1950
|
|
|
@@ -1728,17 +1970,15 @@ async function __dispatchQueue(request, env) {
|
|
|
1728
1970
|
for (const m of messages) m.retry(opts);
|
|
1729
1971
|
},
|
|
1730
1972
|
};
|
|
1973
|
+
let ok = true;
|
|
1731
1974
|
try {
|
|
1732
1975
|
await q.handler(batch, env);
|
|
1733
1976
|
} catch (err) {
|
|
1977
|
+
ok = false;
|
|
1734
1978
|
console.error("[void] queue handler threw:", err);
|
|
1735
|
-
// CF queues retry the entire batch when a consumer throws, regardless
|
|
1736
|
-
// of any ack/retry calls made before the throw — drop partial decisions
|
|
1737
|
-
// so dev-mode response semantics match prod.
|
|
1738
|
-
return __json({ ok: false });
|
|
1739
1979
|
}
|
|
1740
1980
|
return __json({
|
|
1741
|
-
ok
|
|
1981
|
+
ok,
|
|
1742
1982
|
...(decisions.size > 0 ? { decisions: Object.fromEntries(decisions) } : {}),
|
|
1743
1983
|
});
|
|
1744
1984
|
}
|
|
@@ -2035,591 +2275,224 @@ function astroDevTriggerInjectPlugin(root) {
|
|
|
2035
2275
|
};
|
|
2036
2276
|
}
|
|
2037
2277
|
//#endregion
|
|
2038
|
-
//#region src/
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
name: "void:migrations",
|
|
2050
|
-
apply: "serve",
|
|
2051
|
-
async configureServer(server) {
|
|
2052
|
-
const migrationsDir = readProjectPaths(root).migrationsDir;
|
|
2053
|
-
const hasSqlMigrations = existsSync(migrationsDir);
|
|
2054
|
-
if (!hasSqlMigrations && !auth?.enabled) return;
|
|
2055
|
-
try {
|
|
2056
|
-
if (hasSqlMigrations && dialect !== "sqlite") {
|
|
2057
|
-
const url = readDatabaseUrl(root);
|
|
2058
|
-
if (!url) {
|
|
2059
|
-
const warn = () => {
|
|
2060
|
-
console.log();
|
|
2061
|
-
voidError(`DATABASE_URL not found in .env — skipping ${dialect === "mysql" ? "MySQL" : "PostgreSQL"} migrations.`);
|
|
2062
|
-
};
|
|
2063
|
-
if (server.httpServer) server.httpServer.once("listening", () => setTimeout(warn, 100));
|
|
2064
|
-
else warn();
|
|
2065
|
-
return;
|
|
2066
|
-
}
|
|
2067
|
-
const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
|
|
2068
|
-
const { validateMigrations } = await import("./validate-Dq_L3s0S.mjs");
|
|
2069
|
-
const allMigrations = collectMigrations(migrationsDir);
|
|
2070
|
-
const appliedNames = dialect === "mysql" ? await import("./runner-mysql-CEG5JozK.mjs").then((runner) => runner.getMySqlAppliedMigrations(url)) : await import("./runner-pg-Bh4AgZdj.mjs").then((runner) => runner.getPgAppliedMigrations(url));
|
|
2071
|
-
const appliedSet = new Set(appliedNames);
|
|
2072
|
-
const pending = allMigrations.filter((migration) => !appliedSet.has(migration.name));
|
|
2073
|
-
if (pending.length > 0) try {
|
|
2074
|
-
validateMigrations(pending);
|
|
2075
|
-
} catch (err) {
|
|
2076
|
-
voidWarn(err instanceof Error ? err.message : String(err));
|
|
2077
|
-
}
|
|
2078
|
-
const applied = dialect === "mysql" ? await import("./runner-mysql-CEG5JozK.mjs").then((runner) => runner.runMySqlMigrations(migrationsDir, url)) : await import("./runner-pg-Bh4AgZdj.mjs").then((runner) => runner.runPgMigrations(migrationsDir, url));
|
|
2079
|
-
if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
|
|
2080
|
-
} else if (hasSqlMigrations) {
|
|
2081
|
-
const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-CXA9Fh8h.mjs");
|
|
2082
|
-
const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
|
|
2083
|
-
const { validateMigrations } = await import("./validate-Dq_L3s0S.mjs");
|
|
2084
|
-
const dbPath = getMiniflareDatabasePath(persistRoot, databaseId);
|
|
2085
|
-
const allMigrations = collectMigrations(migrationsDir);
|
|
2086
|
-
const appliedRows = await getAppliedMigrations(dbPath);
|
|
2087
|
-
const appliedSet = new Set(appliedRows.map((r) => r.name));
|
|
2088
|
-
const pending = allMigrations.filter((m) => !appliedSet.has(m.name));
|
|
2089
|
-
if (pending.length > 0) try {
|
|
2090
|
-
validateMigrations(pending);
|
|
2091
|
-
} catch (err) {
|
|
2092
|
-
voidWarn(err instanceof Error ? err.message : String(err));
|
|
2093
|
-
}
|
|
2094
|
-
const applied = await runMigrations(migrationsDir, dbPath);
|
|
2095
|
-
if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
|
|
2096
|
-
}
|
|
2097
|
-
if (auth?.enabled) {
|
|
2098
|
-
const { runLocalVoidAuthMigrations } = await import("./node-BkyRWRx8.mjs");
|
|
2099
|
-
await runLocalVoidAuthMigrations(root, dialect, persistRoot, databaseId, auth.configPath);
|
|
2100
|
-
voidLog("Applied Better Auth migrations");
|
|
2101
|
-
}
|
|
2102
|
-
} catch (err) {
|
|
2103
|
-
voidError(`Migration error: ${err}`);
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
};
|
|
2278
|
+
//#region src/router/compile.ts
|
|
2279
|
+
const ROUTE_URL_PATH = fileURLToPath(import.meta.resolve("#self/runtime/route-url"));
|
|
2280
|
+
const ROUTING_RUNTIME_PATH = fileURLToPath(import.meta.resolve("#self/runtime/routing"));
|
|
2281
|
+
function pushDefaultErrorPageHelpers(lines, options) {
|
|
2282
|
+
lines.push(`const __errorRenderer = __createErrorRenderer(${JSON.stringify({
|
|
2283
|
+
dev: options.dev === true,
|
|
2284
|
+
projectRoot: options.projectRoot,
|
|
2285
|
+
homeDir: options.homeDir
|
|
2286
|
+
})});`);
|
|
2287
|
+
lines.push("const serveCustomErrorPage = __errorRenderer.serveCustomPage;");
|
|
2288
|
+
lines.push("app.onError(__errorRenderer.onError);");
|
|
2107
2289
|
}
|
|
2108
|
-
const CF_ONLY_REGEX = new RegExp(`^(${[
|
|
2109
|
-
"void/db",
|
|
2110
|
-
"void/kv",
|
|
2111
|
-
"void/auth",
|
|
2112
|
-
"void/storage",
|
|
2113
|
-
"void/ai",
|
|
2114
|
-
"void/sandbox",
|
|
2115
|
-
"void/env"
|
|
2116
|
-
].map((s) => s.replace("/", "\\/")).join("|")})$`);
|
|
2117
2290
|
/**
|
|
2118
|
-
*
|
|
2119
|
-
*
|
|
2291
|
+
* Generate the code for the virtual route table module (virtual:void-routes).
|
|
2292
|
+
* This module creates a Hono app with:
|
|
2293
|
+
* - Middleware registered via app.use()
|
|
2294
|
+
* - Routes registered with lazy-loaded handlers and auto-conversion
|
|
2120
2295
|
*/
|
|
2121
|
-
function
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2296
|
+
function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, options, pages = null, mergedRevalidate, isrPages, isrPathRevalidate) {
|
|
2297
|
+
const lines = [];
|
|
2298
|
+
const auth = options.auth;
|
|
2299
|
+
const webSockets = scan.websockets ?? [];
|
|
2300
|
+
const managedDispatch = options.nodeTarget !== true && options.dev !== true && options.managedDispatch === true;
|
|
2301
|
+
lines.push(`import { AsyncLocalStorage } from "node:async_hooks";`);
|
|
2302
|
+
lines.push(`import { Hono } from ${JSON.stringify(options.honoPath)};`);
|
|
2303
|
+
lines.push(`import { createRoutingRuntime as __createRoutingRuntime, createHttpRouteHandler as __createHttpRouteHandler, createErrorRenderer as __createErrorRenderer, createPageGetHandler as __createPageGetHandler, createPageMutationHandler as __createPageMutationHandler, createPagesFallback as __createPagesFallback, createModuleLoader as __createModuleLoader, createClientAssetTags as __createClientAssetTags, createManifestLoader as __createManifestLoader, createSsrFallback as __createSsrFallback, createAssetNotFound as __createAssetNotFound } from ${JSON.stringify(ROUTING_RUNTIME_PATH)};`);
|
|
2304
|
+
if (options.nodeTarget) lines.push(`const withRuntimeEnv = (_env, fn) => fn();`);
|
|
2305
|
+
else lines.push(`import { withRuntimeEnv } from ${JSON.stringify(options.runtimeEnvPath)};`);
|
|
2306
|
+
if (auth) {
|
|
2307
|
+
const authImports = [
|
|
2308
|
+
"runVoidAuthMiddleware",
|
|
2309
|
+
"handleVoidAuthRequest",
|
|
2310
|
+
"runVoidAuthMigrations"
|
|
2311
|
+
];
|
|
2312
|
+
if (webSockets.length > 0) authImports.push("resolveVoidAuthState as __resolveVoidAuthStateRuntime");
|
|
2313
|
+
lines.push(`import { ${authImports.join(", ")} } from ${JSON.stringify(auth.authRuntimePath)};`);
|
|
2314
|
+
if (auth.authDatabasePath) lines.push(`import { __voidAuthDatabase } from ${JSON.stringify(auth.authDatabasePath)};`);
|
|
2315
|
+
if (auth.authSchemaPath) lines.push(`import { migrationSchema as __voidAuthMigrationSchema } from ${JSON.stringify(auth.authSchemaPath)};`);
|
|
2316
|
+
if (auth.authConfigPath) lines.push(`import __voidAuthConfig from ${JSON.stringify(auth.authConfigPath)};`);
|
|
2317
|
+
}
|
|
2318
|
+
if ((webSockets.length > 0 || options.webSocketTombstones?.length) && options.webSocketRuntimePath) lines.push(`import { createWebSocketDurableObject as __createWebSocketDurableObject, createWebSocketTombstoneDurableObject as __createWebSocketTombstoneDurableObject, forwardWebSocketRequest as __forwardWebSocketRequest } from ${JSON.stringify(options.webSocketRuntimePath)};`);
|
|
2319
|
+
if (options.hasLive && options.liveRuntimePath) lines.push(`import { createLiveDurableObject as __createLiveDurableObject } from ${JSON.stringify(options.liveRuntimePath)};`);
|
|
2320
|
+
lines.push("");
|
|
2321
|
+
for (let i = 0; i < scan.middleware.length; i++) {
|
|
2322
|
+
const mw = scan.middleware[i];
|
|
2323
|
+
lines.push(`import middleware${i} from ${JSON.stringify(`${middlewareDir}/${mw.filePath}`)};`);
|
|
2324
|
+
}
|
|
2325
|
+
for (let i = 0; i < webSockets.length; i++) {
|
|
2326
|
+
const route = webSockets[i];
|
|
2327
|
+
lines.push(`import websocket${i} from ${JSON.stringify(`${routesDir}/${route.filePath}`)};`);
|
|
2328
|
+
}
|
|
2329
|
+
lines.push("");
|
|
2330
|
+
if (auth) {
|
|
2331
|
+
lines.push(`const __voidAuthOptions = { dialect: ${JSON.stringify(auth.dialect)}, providers: ${JSON.stringify(auth.providers)}, userConfig: ${auth.authConfigPath ? "__voidAuthConfig" : "undefined"}, dev: ${JSON.stringify(options.dev === true)} };`);
|
|
2332
|
+
if (webSockets.length > 0) lines.push(`const __resolveVoidAuthState = (request, env, runtime) => __resolveVoidAuthStateRuntime(request, env, runtime${auth.authDatabasePath ? ", __voidAuthDatabase" : ""});`);
|
|
2333
|
+
lines.push("");
|
|
2334
|
+
}
|
|
2335
|
+
lines.push(`const app = new Hono();`);
|
|
2336
|
+
const ASSET_EXTENSIONS = [
|
|
2337
|
+
"png",
|
|
2338
|
+
"jpg",
|
|
2339
|
+
"jpeg",
|
|
2340
|
+
"gif",
|
|
2341
|
+
"webp",
|
|
2342
|
+
"avif",
|
|
2343
|
+
"svg",
|
|
2344
|
+
"ico",
|
|
2345
|
+
"css",
|
|
2346
|
+
"js",
|
|
2347
|
+
"mjs",
|
|
2348
|
+
"cjs",
|
|
2349
|
+
"woff",
|
|
2350
|
+
"woff2",
|
|
2351
|
+
"ttf",
|
|
2352
|
+
"otf",
|
|
2353
|
+
"eot",
|
|
2354
|
+
"mp4",
|
|
2355
|
+
"webm",
|
|
2356
|
+
"mp3",
|
|
2357
|
+
"wav",
|
|
2358
|
+
"pdf",
|
|
2359
|
+
"txt",
|
|
2360
|
+
"xml",
|
|
2361
|
+
"json",
|
|
2362
|
+
"wasm",
|
|
2363
|
+
"map"
|
|
2364
|
+
];
|
|
2365
|
+
const routePatterns = [];
|
|
2366
|
+
for (const r of scan.routes) routePatterns.push(r.pattern);
|
|
2367
|
+
if (pages && scan.pages.pages.length > 0) for (const p of scan.pages.pages) routePatterns.push(p.pattern);
|
|
2368
|
+
const usedExts = /* @__PURE__ */ new Set();
|
|
2369
|
+
for (const p of routePatterns) {
|
|
2370
|
+
const m = /\.([A-Za-z0-9]+)$/.exec(p);
|
|
2371
|
+
if (m) usedExts.add(m[1].toLowerCase());
|
|
2372
|
+
}
|
|
2373
|
+
const blockedExts = ASSET_EXTENSIONS.filter((e) => !usedExts.has(e));
|
|
2374
|
+
lines.push(`const __routingRuntime = __createRoutingRuntime(app, ${JSON.stringify({
|
|
2375
|
+
dev: options.dev === true,
|
|
2376
|
+
nodeTarget: options.nodeTarget === true,
|
|
2377
|
+
managedDispatch,
|
|
2378
|
+
blockedAssetExtensions: blockedExts
|
|
2379
|
+
})});`);
|
|
2380
|
+
lines.push("export const requestContext = __routingRuntime.requestContext;");
|
|
2381
|
+
lines.push("app.use(__routingRuntime.contextMiddleware);");
|
|
2382
|
+
lines.push("app.use(__routingRuntime.requestMiddleware);");
|
|
2383
|
+
lines.push("");
|
|
2384
|
+
const emitEdgeRules = !managedDispatch;
|
|
2385
|
+
if (emitEdgeRules && options.redirectRules?.length) compileRoutingRulesMiddleware(lines, options.redirectRules, options.dev === true, options.nodeTarget === true);
|
|
2386
|
+
if (emitEdgeRules && options.headerRules?.length) compileHeaderMiddleware(lines, options.headerRules);
|
|
2387
|
+
if (auth) {
|
|
2388
|
+
lines.push("app.use(async (c, next) => {");
|
|
2389
|
+
lines.push(` return runVoidAuthMiddleware(c, next, __voidAuthOptions${auth.authDatabasePath ? ", __voidAuthDatabase" : ""});`);
|
|
2390
|
+
lines.push("});");
|
|
2391
|
+
lines.push("");
|
|
2392
|
+
}
|
|
2393
|
+
for (let i = 0; i < scan.middleware.length; i++) lines.push(`app.use(middleware${i});`);
|
|
2394
|
+
if (scan.middleware.length > 0) lines.push("");
|
|
2395
|
+
for (let i = 0; i < webSockets.length; i++) {
|
|
2396
|
+
const route = webSockets[i];
|
|
2397
|
+
lines.push(`app.all(${JSON.stringify(route.pattern)}, async (c, next) => {`);
|
|
2398
|
+
lines.push(` if (c.req.raw.headers.get("upgrade")?.toLowerCase() !== "websocket") return next();`);
|
|
2399
|
+
lines.push(` return __forwardWebSocketRequest(c.req.raw, c.env, __voidWebSocketRoutes[${i}]);`);
|
|
2400
|
+
lines.push("});");
|
|
2401
|
+
}
|
|
2402
|
+
if (webSockets.length > 0) lines.push("");
|
|
2403
|
+
if (auth) {
|
|
2404
|
+
lines.push("app.all(\"/api/auth\", async (c) => {");
|
|
2405
|
+
lines.push(` return handleVoidAuthRequest(c, __voidAuthOptions${auth.authDatabasePath ? ", __voidAuthDatabase" : ""});`);
|
|
2406
|
+
lines.push("});");
|
|
2407
|
+
lines.push("");
|
|
2408
|
+
lines.push("app.all(\"/api/auth/*\", async (c) => {");
|
|
2409
|
+
lines.push(` return handleVoidAuthRequest(c, __voidAuthOptions${auth.authDatabasePath ? ", __voidAuthDatabase" : ""});`);
|
|
2410
|
+
lines.push("});");
|
|
2411
|
+
lines.push("");
|
|
2412
|
+
}
|
|
2413
|
+
const seenFiles = /* @__PURE__ */ new Set();
|
|
2414
|
+
for (const route of scan.routes) {
|
|
2415
|
+
const filePath = `${routesDir}/${route.filePath}`;
|
|
2416
|
+
const loaderName = `_load_${route.filePath.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
2417
|
+
if (!seenFiles.has(route.filePath)) {
|
|
2418
|
+
seenFiles.add(route.filePath);
|
|
2419
|
+
lines.push(`const ${loaderName} = __createModuleLoader(() => import(${JSON.stringify(filePath)}), ${JSON.stringify(options.dev !== true)});`);
|
|
2163
2420
|
}
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
*
|
|
2169
|
-
* Replaces `@cloudflare/vite-plugin` in the plugin composition. Provides:
|
|
2170
|
-
* 1. Binding guard — compile-time errors for CF-only void/* imports
|
|
2171
|
-
* 2. Dev server — Vite SSR middleware to run Hono in Node.js
|
|
2172
|
-
* 3. Preview server — serves from the built SSR entry
|
|
2173
|
-
* 4. Build — dual-environment build (client + SSR) for pages apps,
|
|
2174
|
-
* single SSR build for API-only apps
|
|
2175
|
-
*/
|
|
2176
|
-
function nodeTargetPlugin(target) {
|
|
2177
|
-
return [{
|
|
2178
|
-
name: "void:node-target",
|
|
2179
|
-
enforce: "pre",
|
|
2180
|
-
resolveId: {
|
|
2181
|
-
filter: { id: CF_ONLY_REGEX },
|
|
2182
|
-
handler(id) {
|
|
2183
|
-
throw new Error(`target: '${id}' is not available with target '${target}'. CF bindings require target: "cloudflare".`);
|
|
2184
|
-
}
|
|
2185
|
-
},
|
|
2186
|
-
renderChunk(code, chunk) {
|
|
2187
|
-
if (!code.includes("import.meta.voidClientDirectory")) return null;
|
|
2188
|
-
const clientPath = relative(dirname(chunk.fileName), "../client");
|
|
2189
|
-
return {
|
|
2190
|
-
code: code.replaceAll("import.meta.voidClientDirectory", `new URL(${JSON.stringify(`${clientPath}/`)}, import.meta.url)`),
|
|
2191
|
-
map: null
|
|
2192
|
-
};
|
|
2193
|
-
},
|
|
2194
|
-
config(_, { command }) {
|
|
2195
|
-
if (command !== "build") return;
|
|
2196
|
-
const root = process.cwd();
|
|
2197
|
-
const voidDir = join(root, ".void");
|
|
2198
|
-
const ssrBuildOptions = {
|
|
2199
|
-
outDir: "dist/ssr",
|
|
2200
|
-
rollupOptions: {
|
|
2201
|
-
input: {
|
|
2202
|
-
app: join(voidDir, "app.ts"),
|
|
2203
|
-
index: join(voidDir, "index.ts")
|
|
2204
|
-
},
|
|
2205
|
-
external: [
|
|
2206
|
-
/^node:/,
|
|
2207
|
-
"@hono/node-server",
|
|
2208
|
-
"@hono/node-server/serve-static"
|
|
2209
|
-
]
|
|
2210
|
-
}
|
|
2211
|
-
};
|
|
2212
|
-
if (existsSync(readProjectPaths(root).pagesDir)) return {
|
|
2213
|
-
build: { outDir: "dist/client" },
|
|
2214
|
-
environments: { ssr: { build: ssrBuildOptions } },
|
|
2215
|
-
builder: {}
|
|
2216
|
-
};
|
|
2217
|
-
return { build: {
|
|
2218
|
-
ssr: true,
|
|
2219
|
-
...ssrBuildOptions
|
|
2220
|
-
} };
|
|
2221
|
-
},
|
|
2222
|
-
async buildApp(builder) {
|
|
2223
|
-
await builder.build(builder.environments.ssr);
|
|
2224
|
-
await builder.build(builder.environments.client);
|
|
2225
|
-
},
|
|
2226
|
-
configureServer(server) {
|
|
2227
|
-
server.middlewares.use(honoMiddleware(async () => {
|
|
2228
|
-
return (await server.ssrLoadModule("virtual:void-routes")).default;
|
|
2229
|
-
}));
|
|
2230
|
-
},
|
|
2231
|
-
configurePreviewServer(server) {
|
|
2232
|
-
const entryPath = join(process.cwd(), "dist", "ssr", "app.js");
|
|
2233
|
-
let appPromise = null;
|
|
2234
|
-
server.middlewares.use(honoMiddleware(() => {
|
|
2235
|
-
if (!appPromise) appPromise = import(
|
|
2236
|
-
/* @vite-ignore */
|
|
2237
|
-
pathToFileURL(entryPath).href
|
|
2238
|
-
).then((m) => m.default, () => null);
|
|
2239
|
-
return appPromise;
|
|
2240
|
-
}));
|
|
2421
|
+
if (route.methods.length === 0) lines.push(`app.all(${JSON.stringify(route.pattern)}, __createHttpRouteHandler(${loaderName}, "default"));`);
|
|
2422
|
+
else for (const method of route.methods) {
|
|
2423
|
+
const honoMethod = method.toLowerCase();
|
|
2424
|
+
lines.push(`app.${honoMethod}(${JSON.stringify(route.pattern)}, __createHttpRouteHandler(${loaderName}, ${JSON.stringify(method)}));`);
|
|
2241
2425
|
}
|
|
2242
|
-
}];
|
|
2243
|
-
}
|
|
2244
|
-
//#endregion
|
|
2245
|
-
//#region src/plugin-prerender.ts
|
|
2246
|
-
/**
|
|
2247
|
-
* Vite plugin that prerenders pages at build time when `output: "static"`.
|
|
2248
|
-
*
|
|
2249
|
-
* Runs in the `closeBundle` hook so prerender logs appear after Vite's
|
|
2250
|
-
* asset summary. Gated on the "client" environment so it only runs once.
|
|
2251
|
-
*/
|
|
2252
|
-
function prerenderPlugin(root, config) {
|
|
2253
|
-
return {
|
|
2254
|
-
name: "void:prerender",
|
|
2255
|
-
apply: "build",
|
|
2256
|
-
async closeBundle() {
|
|
2257
|
-
if (config.output !== "static") return;
|
|
2258
|
-
if (process.env.VOID_DEPLOY_BUILD === "1") return;
|
|
2259
|
-
if (this.environment?.name !== "client") return;
|
|
2260
|
-
setShowTimestamp(false);
|
|
2261
|
-
const distDir = resolveDistDir(root);
|
|
2262
|
-
const clientDir = join(distDir, "client");
|
|
2263
|
-
let result;
|
|
2264
|
-
if (isNodeTarget(config.target)) result = await prerenderPagesNode({
|
|
2265
|
-
root,
|
|
2266
|
-
ssrDir: join(distDir, "ssr"),
|
|
2267
|
-
clientDir
|
|
2268
|
-
});
|
|
2269
|
-
else {
|
|
2270
|
-
const persistDir = join(root, ".void");
|
|
2271
|
-
const workerDirName = resolveWorkerDirName(distDir);
|
|
2272
|
-
const workerDir = join(distDir, workerDirName);
|
|
2273
|
-
const remoteMode = resolveRemoteMode(config.remote);
|
|
2274
|
-
result = await prerenderPages({
|
|
2275
|
-
root,
|
|
2276
|
-
workerDir,
|
|
2277
|
-
clientDir,
|
|
2278
|
-
persistDir,
|
|
2279
|
-
remoteMode,
|
|
2280
|
-
projectId: process.env.VOID_DEPLOY_PROJECT_ID || void 0
|
|
2281
|
-
});
|
|
2282
|
-
}
|
|
2283
|
-
if (result.paths.length === 0) {
|
|
2284
|
-
voidLog("No pages to prerender");
|
|
2285
|
-
return;
|
|
2286
|
-
}
|
|
2287
|
-
if (result.failed.length > 0) voidWarn(`${result.failed.length} page(s) failed to prerender`);
|
|
2288
|
-
}
|
|
2289
|
-
};
|
|
2290
|
-
}
|
|
2291
|
-
//#endregion
|
|
2292
|
-
//#region src/plugin-restart.ts
|
|
2293
|
-
const BINDING_HINT_RESOLVED = "\0virtual:void/binding-hint";
|
|
2294
|
-
const RESTARTING_HTML = `<!DOCTYPE html>
|
|
2295
|
-
<html lang="en">
|
|
2296
|
-
<head>
|
|
2297
|
-
<meta charset="UTF-8">
|
|
2298
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2299
|
-
<title>Restarting</title>
|
|
2300
|
-
<style>
|
|
2301
|
-
body{margin:0;height:100vh;display:flex;align-items:center;justify-content:center;background:#1a1a2e;color:#e0def4;font:14px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace}
|
|
2302
|
-
.tag{color:#c4a7e7;font-weight:700}
|
|
2303
|
-
@keyframes d{0%,80%{opacity:0}40%{opacity:1}}
|
|
2304
|
-
.dots span{opacity:0;animation:d 1.4s infinite}
|
|
2305
|
-
.dots span:nth-child(2){animation-delay:.2s}
|
|
2306
|
-
.dots span:nth-child(3){animation-delay:.4s}
|
|
2307
|
-
</style>
|
|
2308
|
-
</head>
|
|
2309
|
-
<body>
|
|
2310
|
-
<div><span class="tag">[void]</span> Restarting dev server<span class="dots"><span>.</span><span>.</span><span>.</span></span></div>
|
|
2311
|
-
<script>
|
|
2312
|
-
fetch("/__void/restart",{method:"POST"}).catch(function(){});
|
|
2313
|
-
setTimeout(function p(){
|
|
2314
|
-
fetch("/__void/ping").then(function(r){
|
|
2315
|
-
if(r.ok)location.href=new URLSearchParams(location.search).get("return")||"/";
|
|
2316
|
-
else setTimeout(p,150);
|
|
2317
|
-
}).catch(function(){setTimeout(p,150)});
|
|
2318
|
-
},300);
|
|
2319
|
-
<\/script>
|
|
2320
|
-
</body>
|
|
2321
|
-
</html>`;
|
|
2322
|
-
const CLIENT_MODULE = `if (import.meta.hot) {
|
|
2323
|
-
import.meta.hot.on("void:new-binding", (d) => {
|
|
2324
|
-
if (document.getElementById("void-binding-hint")) return;
|
|
2325
|
-
const e = document.createElement("div");
|
|
2326
|
-
e.id = "void-binding-hint";
|
|
2327
|
-
e.style.cssText = "position:fixed;top:16px;right:16px;z-index:99999;padding:12px 16px;background:rgba(26,26,46,0.95);color:#e0def4;font:13px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;border-radius:8px;border:1px solid rgba(255,255,255,0.08);box-shadow:0 8px 32px rgba(0,0,0,0.4);backdrop-filter:blur(8px);cursor:pointer;max-width:300px;transform:translateX(calc(100% + 16px));transition:transform .3s ease";
|
|
2328
|
-
e.innerHTML = "<strong style=\\"color:#c4a7e7\\">[void]</strong> " + d.message + " Click to restart.";
|
|
2329
|
-
e.onclick = () => { location.href = "/__void/restarting?return=" + encodeURIComponent(location.pathname + location.search); };
|
|
2330
|
-
document.body.appendChild(e);
|
|
2331
|
-
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
2332
|
-
e.style.transform = "translateX(0)";
|
|
2333
|
-
}));
|
|
2334
|
-
});
|
|
2335
|
-
}`;
|
|
2336
|
-
/**
|
|
2337
|
-
* Vite plugin that provides controlled dev-server restart via the browser.
|
|
2338
|
-
*
|
|
2339
|
-
* Serves three endpoints:
|
|
2340
|
-
* - `/__void/restarting` — standalone loading page (no Vite HMR client)
|
|
2341
|
-
* - `/__void/restart` — POST triggers `server.restart()` after a short delay
|
|
2342
|
-
* - `/__void/ping` — health check; returns 503 on the old server instance
|
|
2343
|
-
* (after restart is triggered) and 200 on the new one
|
|
2344
|
-
*
|
|
2345
|
-
* Also injects a virtual module (`virtual:void/binding-hint`) that listens for
|
|
2346
|
-
* `void:new-binding` HMR events and shows a toast with a click-to-restart action.
|
|
2347
|
-
*/
|
|
2348
|
-
function restartPlugin() {
|
|
2349
|
-
return {
|
|
2350
|
-
name: "void:restart",
|
|
2351
|
-
apply: "serve",
|
|
2352
|
-
resolveId: {
|
|
2353
|
-
filter: { id: /^virtual:void\/binding-hint$/ },
|
|
2354
|
-
handler() {
|
|
2355
|
-
return BINDING_HINT_RESOLVED;
|
|
2356
|
-
}
|
|
2357
|
-
},
|
|
2358
|
-
load: {
|
|
2359
|
-
filter: { id: /^\0virtual:void\/binding-hint$/ },
|
|
2360
|
-
handler() {
|
|
2361
|
-
return CLIENT_MODULE;
|
|
2362
|
-
}
|
|
2363
|
-
},
|
|
2364
|
-
configureServer(server) {
|
|
2365
|
-
let restartPending = false;
|
|
2366
|
-
server.middlewares.use("/__void/ping", (_req, res) => {
|
|
2367
|
-
res.writeHead(restartPending ? 503 : 200);
|
|
2368
|
-
res.end();
|
|
2369
|
-
});
|
|
2370
|
-
server.middlewares.use("/__void/restart", (req, res) => {
|
|
2371
|
-
if (req.method !== "POST") {
|
|
2372
|
-
res.writeHead(405);
|
|
2373
|
-
res.end();
|
|
2374
|
-
return;
|
|
2375
|
-
}
|
|
2376
|
-
restartPending = true;
|
|
2377
|
-
res.writeHead(200);
|
|
2378
|
-
res.end();
|
|
2379
|
-
setTimeout(() => server.restart(), 50);
|
|
2380
|
-
});
|
|
2381
|
-
server.middlewares.use("/__void/restarting", (_req, res) => {
|
|
2382
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
2383
|
-
res.end(RESTARTING_HTML);
|
|
2384
|
-
});
|
|
2385
|
-
}
|
|
2386
|
-
};
|
|
2387
|
-
}
|
|
2388
|
-
//#endregion
|
|
2389
|
-
//#region src/client-asset-manifest.ts
|
|
2390
|
-
/** Attach cache provenance to the existing manifest without changing its entry inventory. */
|
|
2391
|
-
function stampClientAssetCachePolicy(bundle, manifestPath, headerRules = []) {
|
|
2392
|
-
const asset = bundle[manifestPath];
|
|
2393
|
-
if (asset?.type !== "asset") return;
|
|
2394
|
-
const fingerprinted = /* @__PURE__ */ new Set();
|
|
2395
|
-
const authoredCachePatterns = headerRules.filter((rule) => rule.headers.some(([name]) => name.toLowerCase() === "cache-control")).map((rule) => compilePattern(rule.pattern).regex);
|
|
2396
|
-
for (const item of Object.values(bundle)) if (item.type === "chunk" && typeof item.preliminaryFileName === "string" && item.preliminaryFileName !== item.fileName) {
|
|
2397
|
-
if (!authoredCachePatterns.some((pattern) => pattern.test("/" + item.fileName))) fingerprinted.add(item.fileName);
|
|
2398
|
-
}
|
|
2399
|
-
const manifest = JSON.parse(String(asset.source));
|
|
2400
|
-
for (const entry of Object.values(manifest)) if (entry.file && fingerprinted.has(entry.file)) entry.immutableFiles = [entry.file];
|
|
2401
|
-
else delete entry.immutableFiles;
|
|
2402
|
-
asset.source = JSON.stringify(manifest, null, 2);
|
|
2403
|
-
}
|
|
2404
|
-
//#endregion
|
|
2405
|
-
//#region src/router/compile.ts
|
|
2406
|
-
const ROUTE_URL_PATH = fileURLToPath(import.meta.resolve("#self/runtime/route-url"));
|
|
2407
|
-
const ROUTING_RUNTIME_PATH = fileURLToPath(import.meta.resolve("#self/runtime/routing"));
|
|
2408
|
-
function pushDefaultErrorPageHelpers(lines, options) {
|
|
2409
|
-
lines.push(`const __errorRenderer = __createErrorRenderer(${JSON.stringify({
|
|
2410
|
-
dev: options.dev === true,
|
|
2411
|
-
projectRoot: options.projectRoot,
|
|
2412
|
-
homeDir: options.homeDir
|
|
2413
|
-
})});`);
|
|
2414
|
-
lines.push("const serveCustomErrorPage = __errorRenderer.serveCustomPage;");
|
|
2415
|
-
lines.push("app.onError(__errorRenderer.onError);");
|
|
2416
|
-
}
|
|
2417
|
-
/**
|
|
2418
|
-
* Generate the code for the virtual route table module (virtual:void-routes).
|
|
2419
|
-
* This module creates a Hono app with:
|
|
2420
|
-
* - Middleware registered via app.use()
|
|
2421
|
-
* - Routes registered with lazy-loaded handlers and auto-conversion
|
|
2422
|
-
*/
|
|
2423
|
-
function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, options, pages = null, mergedRevalidate, isrPages, isrPathRevalidate) {
|
|
2424
|
-
const lines = [];
|
|
2425
|
-
const auth = options.auth;
|
|
2426
|
-
const webSockets = scan.websockets ?? [];
|
|
2427
|
-
const managedDispatch = options.nodeTarget !== true && options.dev !== true && options.managedDispatch === true;
|
|
2428
|
-
lines.push(`import { AsyncLocalStorage } from "node:async_hooks";`);
|
|
2429
|
-
lines.push(`import { Hono } from ${JSON.stringify(options.honoPath)};`);
|
|
2430
|
-
lines.push(`import { createRoutingRuntime as __createRoutingRuntime, createHttpRouteHandler as __createHttpRouteHandler, createErrorRenderer as __createErrorRenderer, createPageGetHandler as __createPageGetHandler, createPageMutationHandler as __createPageMutationHandler, createPagesFallback as __createPagesFallback, createModuleLoader as __createModuleLoader, createClientAssetTags as __createClientAssetTags, createManifestLoader as __createManifestLoader, createSsrFallback as __createSsrFallback, createAssetNotFound as __createAssetNotFound } from ${JSON.stringify(ROUTING_RUNTIME_PATH)};`);
|
|
2431
|
-
if (options.nodeTarget) lines.push(`const withRuntimeEnv = (_env, fn) => fn();`);
|
|
2432
|
-
else lines.push(`import { withRuntimeEnv } from ${JSON.stringify(options.runtimeEnvPath)};`);
|
|
2433
|
-
if (auth) {
|
|
2434
|
-
const authImports = [
|
|
2435
|
-
"runVoidAuthMiddleware",
|
|
2436
|
-
"handleVoidAuthRequest",
|
|
2437
|
-
"runVoidAuthMigrations"
|
|
2438
|
-
];
|
|
2439
|
-
if (webSockets.length > 0) authImports.push("resolveVoidAuthState as __resolveVoidAuthStateRuntime");
|
|
2440
|
-
lines.push(`import { ${authImports.join(", ")} } from ${JSON.stringify(auth.authRuntimePath)};`);
|
|
2441
|
-
if (auth.authDatabasePath) lines.push(`import { __voidAuthDatabase } from ${JSON.stringify(auth.authDatabasePath)};`);
|
|
2442
|
-
if (auth.authSchemaPath) lines.push(`import { migrationSchema as __voidAuthMigrationSchema } from ${JSON.stringify(auth.authSchemaPath)};`);
|
|
2443
|
-
if (auth.authConfigPath) lines.push(`import __voidAuthConfig from ${JSON.stringify(auth.authConfigPath)};`);
|
|
2444
2426
|
}
|
|
2445
|
-
if ((webSockets.length > 0 || options.webSocketTombstones?.length) && options.webSocketRuntimePath) lines.push(`import { createWebSocketDurableObject as __createWebSocketDurableObject, createWebSocketTombstoneDurableObject as __createWebSocketTombstoneDurableObject, forwardWebSocketRequest as __forwardWebSocketRequest } from ${JSON.stringify(options.webSocketRuntimePath)};`);
|
|
2446
|
-
if (options.hasLive && options.liveRuntimePath) lines.push(`import { createLiveDurableObject as __createLiveDurableObject } from ${JSON.stringify(options.liveRuntimePath)};`);
|
|
2447
2427
|
lines.push("");
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2428
|
+
if (webSockets.length > 0) {
|
|
2429
|
+
for (let i = 0; i < webSockets.length; i++) {
|
|
2430
|
+
const route = webSockets[i];
|
|
2431
|
+
lines.push(`export const ${route.className} = __createWebSocketDurableObject(websocket${i}, ${JSON.stringify({
|
|
2432
|
+
pattern: route.pattern,
|
|
2433
|
+
params: route.params,
|
|
2434
|
+
className: route.className,
|
|
2435
|
+
bindingName: route.bindingName,
|
|
2436
|
+
...route.name !== void 0 && { name: route.name }
|
|
2437
|
+
})}, ${auth ? "__voidAuthOptions" : "undefined"}, ${auth ? "__resolveVoidAuthState" : "undefined"});`);
|
|
2438
|
+
}
|
|
2439
|
+
lines.push("");
|
|
2440
|
+
lines.push(`const __voidWebSocketRoutes = ${JSON.stringify(webSockets.map((route) => ({
|
|
2441
|
+
pattern: route.pattern,
|
|
2442
|
+
params: route.params,
|
|
2443
|
+
className: route.className,
|
|
2444
|
+
bindingName: route.bindingName,
|
|
2445
|
+
...route.name !== void 0 && { name: route.name }
|
|
2446
|
+
})))};`);
|
|
2447
|
+
lines.push("");
|
|
2451
2448
|
}
|
|
2452
|
-
for (let i = 0; i < webSockets.length; i++) {
|
|
2453
|
-
|
|
2454
|
-
|
|
2449
|
+
for (let i = 0; i < webSockets.length; i++) lines.push(`__voidWebSocketRoutes[${i}].key = websocket${i}.key;`);
|
|
2450
|
+
for (const className of options.webSocketTombstones ?? []) lines.push(`export const ${className} = __createWebSocketTombstoneDurableObject(${JSON.stringify(className)});`);
|
|
2451
|
+
if (options.webSocketTombstones?.length) lines.push("");
|
|
2452
|
+
if (options.hasLive) {
|
|
2453
|
+
lines.push("export const VoidLiveStreamDurableObject = __createLiveDurableObject();");
|
|
2454
|
+
lines.push("");
|
|
2455
2455
|
}
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
"
|
|
2477
|
-
"
|
|
2478
|
-
"
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
"
|
|
2490
|
-
"map"
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
for (const p of routePatterns) {
|
|
2497
|
-
const m = /\.([A-Za-z0-9]+)$/.exec(p);
|
|
2498
|
-
if (m) usedExts.add(m[1].toLowerCase());
|
|
2499
|
-
}
|
|
2500
|
-
const blockedExts = ASSET_EXTENSIONS.filter((e) => !usedExts.has(e));
|
|
2501
|
-
lines.push(`const __routingRuntime = __createRoutingRuntime(app, ${JSON.stringify({
|
|
2502
|
-
dev: options.dev === true,
|
|
2503
|
-
nodeTarget: options.nodeTarget === true,
|
|
2504
|
-
managedDispatch,
|
|
2505
|
-
blockedAssetExtensions: blockedExts
|
|
2506
|
-
})});`);
|
|
2507
|
-
lines.push("export const requestContext = __routingRuntime.requestContext;");
|
|
2508
|
-
lines.push("app.use(__routingRuntime.contextMiddleware);");
|
|
2509
|
-
lines.push("app.use(__routingRuntime.requestMiddleware);");
|
|
2510
|
-
lines.push("");
|
|
2511
|
-
const emitEdgeRules = !managedDispatch;
|
|
2512
|
-
if (emitEdgeRules && options.redirectRules?.length) compileRoutingRulesMiddleware(lines, options.redirectRules, options.dev === true, options.nodeTarget === true);
|
|
2513
|
-
if (emitEdgeRules && options.headerRules?.length) compileHeaderMiddleware(lines, options.headerRules);
|
|
2514
|
-
if (auth) {
|
|
2515
|
-
lines.push("app.use(async (c, next) => {");
|
|
2516
|
-
lines.push(` return runVoidAuthMiddleware(c, next, __voidAuthOptions${auth.authDatabasePath ? ", __voidAuthDatabase" : ""});`);
|
|
2517
|
-
lines.push("});");
|
|
2518
|
-
lines.push("");
|
|
2519
|
-
}
|
|
2520
|
-
for (let i = 0; i < scan.middleware.length; i++) lines.push(`app.use(middleware${i});`);
|
|
2521
|
-
if (scan.middleware.length > 0) lines.push("");
|
|
2522
|
-
for (let i = 0; i < webSockets.length; i++) {
|
|
2523
|
-
const route = webSockets[i];
|
|
2524
|
-
lines.push(`app.all(${JSON.stringify(route.pattern)}, async (c, next) => {`);
|
|
2525
|
-
lines.push(` if (c.req.raw.headers.get("upgrade")?.toLowerCase() !== "websocket") return next();`);
|
|
2526
|
-
lines.push(` return __forwardWebSocketRequest(c.req.raw, c.env, __voidWebSocketRoutes[${i}]);`);
|
|
2527
|
-
lines.push("});");
|
|
2528
|
-
}
|
|
2529
|
-
if (webSockets.length > 0) lines.push("");
|
|
2530
|
-
if (auth) {
|
|
2531
|
-
lines.push("app.all(\"/api/auth\", async (c) => {");
|
|
2532
|
-
lines.push(` return handleVoidAuthRequest(c, __voidAuthOptions${auth.authDatabasePath ? ", __voidAuthDatabase" : ""});`);
|
|
2533
|
-
lines.push("});");
|
|
2534
|
-
lines.push("");
|
|
2535
|
-
lines.push("app.all(\"/api/auth/*\", async (c) => {");
|
|
2536
|
-
lines.push(` return handleVoidAuthRequest(c, __voidAuthOptions${auth.authDatabasePath ? ", __voidAuthDatabase" : ""});`);
|
|
2537
|
-
lines.push("});");
|
|
2538
|
-
lines.push("");
|
|
2539
|
-
}
|
|
2540
|
-
const seenFiles = /* @__PURE__ */ new Set();
|
|
2541
|
-
for (const route of scan.routes) {
|
|
2542
|
-
const filePath = `${routesDir}/${route.filePath}`;
|
|
2543
|
-
const loaderName = `_load_${route.filePath.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
2544
|
-
if (!seenFiles.has(route.filePath)) {
|
|
2545
|
-
seenFiles.add(route.filePath);
|
|
2546
|
-
lines.push(`const ${loaderName} = __createModuleLoader(() => import(${JSON.stringify(filePath)}), ${JSON.stringify(options.dev !== true)});`);
|
|
2547
|
-
}
|
|
2548
|
-
if (route.methods.length === 0) lines.push(`app.all(${JSON.stringify(route.pattern)}, __createHttpRouteHandler(${loaderName}, "default"));`);
|
|
2549
|
-
else for (const method of route.methods) {
|
|
2550
|
-
const honoMethod = method.toLowerCase();
|
|
2551
|
-
lines.push(`app.${honoMethod}(${JSON.stringify(route.pattern)}, __createHttpRouteHandler(${loaderName}, ${JSON.stringify(method)}));`);
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2554
|
-
lines.push("");
|
|
2555
|
-
if (webSockets.length > 0) {
|
|
2556
|
-
for (let i = 0; i < webSockets.length; i++) {
|
|
2557
|
-
const route = webSockets[i];
|
|
2558
|
-
lines.push(`export const ${route.className} = __createWebSocketDurableObject(websocket${i}, ${JSON.stringify({
|
|
2559
|
-
pattern: route.pattern,
|
|
2560
|
-
params: route.params,
|
|
2561
|
-
className: route.className,
|
|
2562
|
-
bindingName: route.bindingName,
|
|
2563
|
-
...route.name !== void 0 && { name: route.name }
|
|
2564
|
-
})}, ${auth ? "__voidAuthOptions" : "undefined"}, ${auth ? "__resolveVoidAuthState" : "undefined"});`);
|
|
2565
|
-
}
|
|
2566
|
-
lines.push("");
|
|
2567
|
-
lines.push(`const __voidWebSocketRoutes = ${JSON.stringify(webSockets.map((route) => ({
|
|
2568
|
-
pattern: route.pattern,
|
|
2569
|
-
params: route.params,
|
|
2570
|
-
className: route.className,
|
|
2571
|
-
bindingName: route.bindingName,
|
|
2572
|
-
...route.name !== void 0 && { name: route.name }
|
|
2573
|
-
})))};`);
|
|
2574
|
-
lines.push("");
|
|
2575
|
-
}
|
|
2576
|
-
for (let i = 0; i < webSockets.length; i++) lines.push(`__voidWebSocketRoutes[${i}].key = websocket${i}.key;`);
|
|
2577
|
-
for (const className of options.webSocketTombstones ?? []) lines.push(`export const ${className} = __createWebSocketTombstoneDurableObject(${JSON.stringify(className)});`);
|
|
2578
|
-
if (options.webSocketTombstones?.length) lines.push("");
|
|
2579
|
-
if (options.hasLive) {
|
|
2580
|
-
lines.push("export const VoidLiveStreamDurableObject = __createLiveDurableObject();");
|
|
2581
|
-
lines.push("");
|
|
2582
|
-
}
|
|
2583
|
-
if (pages && scan.pages.pages.length > 0) compilePageRoutes(lines, scan, pages, options);
|
|
2584
|
-
for (let i = 0; i < webSockets.length; i++) lines.push(`app.all(${JSON.stringify(webSockets[i].pattern)}, (c) => __forwardWebSocketRequest(c.req.raw, c.env, __voidWebSocketRoutes[${i}]));`);
|
|
2585
|
-
if (webSockets.length > 0) lines.push("");
|
|
2586
|
-
if (pages && scan.pages.pages.length > 0) lines.push(`app.notFound(__createPagesFallback({ routing: __routingRuntime, errors: __errorRenderer, assetsDir: ${JSON.stringify(options.assetsDir)}, fallbackRules: ${JSON.stringify(emitEdgeRules && options.fallbackRules?.length ? options.dev ? options.fallbackRules : stripOriginForProd(options.fallbackRules) : void 0)} }));`);
|
|
2587
|
-
compilePrerenderCollector(lines, scan, ssr, options);
|
|
2588
|
-
if (ssr) {
|
|
2589
|
-
lines.push(`const getClientAssetTags = __createClientAssetTags(${JSON.stringify({
|
|
2590
|
-
entryId: ssr.clientEntryRelativePath,
|
|
2591
|
-
entryUrl: "/" + ssr.clientEntryRelativePath,
|
|
2592
|
-
kind: "ssr",
|
|
2593
|
-
dev: options.dev === true,
|
|
2594
|
-
reactRefresh: options.reactRefresh
|
|
2595
|
-
})});`);
|
|
2596
|
-
lines.push(`app.notFound(__createSsrFallback({ routing: __routingRuntime, nodeTarget: ${JSON.stringify(options.nodeTarget === true)}, assetsDir: ${JSON.stringify(options.assetsDir)}, fallbackRules: ${JSON.stringify(emitEdgeRules && options.fallbackRules?.length ? options.dev ? options.fallbackRules : stripOriginForProd(options.fallbackRules) : void 0)}, load: () => import(${JSON.stringify(ssr.serverEntryPath)}), assets: getClientAssetTags }));`);
|
|
2597
|
-
} else if (emitEdgeRules && (options.fallbackRules?.length || !options.nodeTarget && options.headerRules?.length) && !(pages && scan.pages.pages.length > 0)) lines.push(`app.notFound(__createAssetNotFound({ routing: __routingRuntime, nodeTarget: ${JSON.stringify(options.nodeTarget === true)}, assetsDir: ${JSON.stringify(options.assetsDir)}, fallbackRules: ${JSON.stringify(options.fallbackRules?.length ? options.dev ? options.fallbackRules : stripOriginForProd(options.fallbackRules) : void 0)} }));`);
|
|
2598
|
-
if (scan.jobs.length > 0) {
|
|
2599
|
-
for (let i = 0; i < scan.jobs.length; i++) {
|
|
2600
|
-
const job = scan.jobs[i];
|
|
2601
|
-
lines.push(`const job${i}Crons = ${JSON.stringify(job.crons)};`);
|
|
2602
|
-
}
|
|
2603
|
-
lines.push("");
|
|
2604
|
-
lines.push("export async function scheduled(controller, env, ctx) {");
|
|
2605
|
-
lines.push(" const tasks = [];");
|
|
2606
|
-
for (let i = 0; i < scan.jobs.length; i++) {
|
|
2607
|
-
const job = scan.jobs[i];
|
|
2608
|
-
const filePath = JSON.stringify(`${jobsDir}/${job.filePath}`);
|
|
2609
|
-
lines.push(` if (job${i}Crons.includes(controller.cron)) {`);
|
|
2610
|
-
lines.push(" tasks.push((async () => {");
|
|
2611
|
-
lines.push(` const mod = await import(${filePath});`);
|
|
2612
|
-
lines.push(" if (typeof mod.default === 'function') await mod.default(controller, env, ctx);");
|
|
2613
|
-
lines.push(" })());");
|
|
2614
|
-
lines.push(" }");
|
|
2615
|
-
}
|
|
2616
|
-
lines.push(" const results = await Promise.allSettled(tasks);");
|
|
2617
|
-
lines.push(" const errors = results.filter(result => result.status === \"rejected\").map(result => result.reason);");
|
|
2618
|
-
lines.push(" if (errors.length) throw new AggregateError(errors, \"Scheduled jobs failed\");");
|
|
2619
|
-
lines.push("}");
|
|
2620
|
-
lines.push("");
|
|
2621
|
-
} else {
|
|
2622
|
-
lines.push("export async function scheduled() {}");
|
|
2456
|
+
if (pages && scan.pages.pages.length > 0) compilePageRoutes(lines, scan, pages, options);
|
|
2457
|
+
for (let i = 0; i < webSockets.length; i++) lines.push(`app.all(${JSON.stringify(webSockets[i].pattern)}, (c) => __forwardWebSocketRequest(c.req.raw, c.env, __voidWebSocketRoutes[${i}]));`);
|
|
2458
|
+
if (webSockets.length > 0) lines.push("");
|
|
2459
|
+
if (pages && scan.pages.pages.length > 0) lines.push(`app.notFound(__createPagesFallback({ routing: __routingRuntime, errors: __errorRenderer, assetsDir: ${JSON.stringify(options.assetsDir)}, fallbackRules: ${JSON.stringify(emitEdgeRules && options.fallbackRules?.length ? options.dev ? options.fallbackRules : stripOriginForProd(options.fallbackRules) : void 0)} }));`);
|
|
2460
|
+
compilePrerenderCollector(lines, scan, ssr, options);
|
|
2461
|
+
if (ssr) {
|
|
2462
|
+
lines.push(`const getClientAssetTags = __createClientAssetTags(${JSON.stringify({
|
|
2463
|
+
entryId: ssr.clientEntryRelativePath,
|
|
2464
|
+
entryUrl: "/" + ssr.clientEntryRelativePath,
|
|
2465
|
+
kind: "ssr",
|
|
2466
|
+
dev: options.dev === true,
|
|
2467
|
+
reactRefresh: options.reactRefresh
|
|
2468
|
+
})});`);
|
|
2469
|
+
lines.push(`app.notFound(__createSsrFallback({ routing: __routingRuntime, nodeTarget: ${JSON.stringify(options.nodeTarget === true)}, assetsDir: ${JSON.stringify(options.assetsDir)}, fallbackRules: ${JSON.stringify(emitEdgeRules && options.fallbackRules?.length ? options.dev ? options.fallbackRules : stripOriginForProd(options.fallbackRules) : void 0)}, load: () => import(${JSON.stringify(ssr.serverEntryPath)}), assets: getClientAssetTags }));`);
|
|
2470
|
+
} else if (emitEdgeRules && (options.fallbackRules?.length || !options.nodeTarget && options.headerRules?.length) && !(pages && scan.pages.pages.length > 0)) lines.push(`app.notFound(__createAssetNotFound({ routing: __routingRuntime, nodeTarget: ${JSON.stringify(options.nodeTarget === true)}, assetsDir: ${JSON.stringify(options.assetsDir)}, fallbackRules: ${JSON.stringify(options.fallbackRules?.length ? options.dev ? options.fallbackRules : stripOriginForProd(options.fallbackRules) : void 0)} }));`);
|
|
2471
|
+
if (scan.jobs.length > 0) {
|
|
2472
|
+
for (let i = 0; i < scan.jobs.length; i++) {
|
|
2473
|
+
const job = scan.jobs[i];
|
|
2474
|
+
lines.push(`const job${i}Crons = ${JSON.stringify(job.crons)};`);
|
|
2475
|
+
}
|
|
2476
|
+
lines.push("");
|
|
2477
|
+
lines.push("export async function scheduled(controller, env, ctx) {");
|
|
2478
|
+
lines.push(" const tasks = [];");
|
|
2479
|
+
for (let i = 0; i < scan.jobs.length; i++) {
|
|
2480
|
+
const job = scan.jobs[i];
|
|
2481
|
+
const filePath = JSON.stringify(`${jobsDir}/${job.filePath}`);
|
|
2482
|
+
lines.push(` if (job${i}Crons.includes(controller.cron)) {`);
|
|
2483
|
+
lines.push(" tasks.push((async () => {");
|
|
2484
|
+
lines.push(` const mod = await import(${filePath});`);
|
|
2485
|
+
lines.push(" if (typeof mod.default === 'function') await mod.default(controller, env, ctx);");
|
|
2486
|
+
lines.push(" })());");
|
|
2487
|
+
lines.push(" }");
|
|
2488
|
+
}
|
|
2489
|
+
lines.push(" const results = await Promise.allSettled(tasks);");
|
|
2490
|
+
lines.push(" const errors = results.filter(result => result.status === \"rejected\").map(result => result.reason);");
|
|
2491
|
+
lines.push(" if (errors.length) throw new AggregateError(errors, \"Scheduled jobs failed\");");
|
|
2492
|
+
lines.push("}");
|
|
2493
|
+
lines.push("");
|
|
2494
|
+
} else {
|
|
2495
|
+
lines.push("export async function scheduled() {}");
|
|
2623
2496
|
lines.push("");
|
|
2624
2497
|
}
|
|
2625
2498
|
if (scan.jobs.length > 0) {
|
|
@@ -2791,644 +2664,1352 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
|
|
|
2791
2664
|
lines.push(" return __voidCreateMigrationRequestHandler(request, true, true)(request);");
|
|
2792
2665
|
lines.push(" }");
|
|
2793
2666
|
}
|
|
2794
|
-
lines.push(" return null;");
|
|
2795
|
-
lines.push("}");
|
|
2796
|
-
lines.push("app.post(\"/__void/migrate\", async (c) => {");
|
|
2797
|
-
lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
|
|
2798
|
-
lines.push(" __voidGetMigrationEnv(),");
|
|
2799
|
-
lines.push(" __migrations,");
|
|
2800
|
-
lines.push(" __migrationDialect,");
|
|
2801
|
-
if (auth) lines.push(` () => runVoidAuthMigrations(new Request(c.req.url), __voidGetMigrationEnv(), __voidAuthOptions${auth.authSchemaPath ? ", __voidAuthMigrationSchema" : ""}),`);
|
|
2802
|
-
else lines.push(" undefined,");
|
|
2803
|
-
lines.push(" );");
|
|
2804
|
-
lines.push(" return __voidMigrationHandler(c.req.raw);");
|
|
2805
|
-
lines.push("});");
|
|
2806
|
-
lines.push("app.get(\"/__void/migrate/capabilities\", async (c) => {");
|
|
2807
|
-
lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
|
|
2808
|
-
lines.push(" __voidGetMigrationEnv(),");
|
|
2809
|
-
lines.push(" __migrations,");
|
|
2810
|
-
lines.push(" __migrationDialect,");
|
|
2811
|
-
if (auth) lines.push(` () => runVoidAuthMigrations(new Request(c.req.url), __voidGetMigrationEnv(), __voidAuthOptions${auth.authSchemaPath ? ", __voidAuthMigrationSchema" : ""}),`);
|
|
2812
|
-
else lines.push(" undefined,");
|
|
2813
|
-
lines.push(" __migrationPlanBundle,");
|
|
2814
|
-
lines.push(" );");
|
|
2815
|
-
lines.push(" return __voidMigrationHandler(c.req.raw);");
|
|
2816
|
-
lines.push("});");
|
|
2817
|
-
lines.push("app.post(\"/__void/migrate/app\", async (c) => {");
|
|
2818
|
-
lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
|
|
2819
|
-
lines.push(" __voidGetMigrationEnv(),");
|
|
2820
|
-
lines.push(" __migrations,");
|
|
2821
|
-
lines.push(" __migrationDialect,");
|
|
2822
|
-
lines.push(" undefined,");
|
|
2823
|
-
lines.push(" __migrationPlanBundle,");
|
|
2824
|
-
lines.push(" );");
|
|
2825
|
-
lines.push(" return __voidMigrationHandler(c.req.raw);");
|
|
2826
|
-
lines.push("});");
|
|
2827
|
-
lines.push("app.get(\"/__void/migrate/app/attempt/:attemptId\", async (c) => {");
|
|
2828
|
-
lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
|
|
2829
|
-
lines.push(" __voidGetMigrationEnv(),");
|
|
2830
|
-
lines.push(" __migrations,");
|
|
2831
|
-
lines.push(" __migrationDialect,");
|
|
2832
|
-
lines.push(" undefined,");
|
|
2833
|
-
lines.push(" __migrationPlanBundle,");
|
|
2834
|
-
lines.push(" );");
|
|
2835
|
-
lines.push(" return __voidMigrationHandler(c.req.raw);");
|
|
2836
|
-
lines.push("});");
|
|
2837
|
-
if (auth) {
|
|
2838
|
-
lines.push("app.post(\"/__void/migrate/auth\", async (c) => {");
|
|
2839
|
-
lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
|
|
2840
|
-
lines.push(" __voidGetMigrationEnv(),");
|
|
2841
|
-
lines.push(" __migrations,");
|
|
2842
|
-
lines.push(" __migrationDialect,");
|
|
2843
|
-
lines.push(` () => runVoidAuthMigrations(new Request(c.req.url), __voidGetMigrationEnv(), __voidAuthOptions${auth.authSchemaPath ? ", __voidAuthMigrationSchema" : ""}),`);
|
|
2844
|
-
lines.push(" __migrationPlanBundle,");
|
|
2845
|
-
lines.push(" );");
|
|
2846
|
-
lines.push(" return __voidMigrationHandler(c.req.raw);");
|
|
2847
|
-
lines.push("});");
|
|
2667
|
+
lines.push(" return null;");
|
|
2668
|
+
lines.push("}");
|
|
2669
|
+
lines.push("app.post(\"/__void/migrate\", async (c) => {");
|
|
2670
|
+
lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
|
|
2671
|
+
lines.push(" __voidGetMigrationEnv(),");
|
|
2672
|
+
lines.push(" __migrations,");
|
|
2673
|
+
lines.push(" __migrationDialect,");
|
|
2674
|
+
if (auth) lines.push(` () => runVoidAuthMigrations(new Request(c.req.url), __voidGetMigrationEnv(), __voidAuthOptions${auth.authSchemaPath ? ", __voidAuthMigrationSchema" : ""}),`);
|
|
2675
|
+
else lines.push(" undefined,");
|
|
2676
|
+
lines.push(" );");
|
|
2677
|
+
lines.push(" return __voidMigrationHandler(c.req.raw);");
|
|
2678
|
+
lines.push("});");
|
|
2679
|
+
lines.push("app.get(\"/__void/migrate/capabilities\", async (c) => {");
|
|
2680
|
+
lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
|
|
2681
|
+
lines.push(" __voidGetMigrationEnv(),");
|
|
2682
|
+
lines.push(" __migrations,");
|
|
2683
|
+
lines.push(" __migrationDialect,");
|
|
2684
|
+
if (auth) lines.push(` () => runVoidAuthMigrations(new Request(c.req.url), __voidGetMigrationEnv(), __voidAuthOptions${auth.authSchemaPath ? ", __voidAuthMigrationSchema" : ""}),`);
|
|
2685
|
+
else lines.push(" undefined,");
|
|
2686
|
+
lines.push(" __migrationPlanBundle,");
|
|
2687
|
+
lines.push(" );");
|
|
2688
|
+
lines.push(" return __voidMigrationHandler(c.req.raw);");
|
|
2689
|
+
lines.push("});");
|
|
2690
|
+
lines.push("app.post(\"/__void/migrate/app\", async (c) => {");
|
|
2691
|
+
lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
|
|
2692
|
+
lines.push(" __voidGetMigrationEnv(),");
|
|
2693
|
+
lines.push(" __migrations,");
|
|
2694
|
+
lines.push(" __migrationDialect,");
|
|
2695
|
+
lines.push(" undefined,");
|
|
2696
|
+
lines.push(" __migrationPlanBundle,");
|
|
2697
|
+
lines.push(" );");
|
|
2698
|
+
lines.push(" return __voidMigrationHandler(c.req.raw);");
|
|
2699
|
+
lines.push("});");
|
|
2700
|
+
lines.push("app.get(\"/__void/migrate/app/attempt/:attemptId\", async (c) => {");
|
|
2701
|
+
lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
|
|
2702
|
+
lines.push(" __voidGetMigrationEnv(),");
|
|
2703
|
+
lines.push(" __migrations,");
|
|
2704
|
+
lines.push(" __migrationDialect,");
|
|
2705
|
+
lines.push(" undefined,");
|
|
2706
|
+
lines.push(" __migrationPlanBundle,");
|
|
2707
|
+
lines.push(" );");
|
|
2708
|
+
lines.push(" return __voidMigrationHandler(c.req.raw);");
|
|
2709
|
+
lines.push("});");
|
|
2710
|
+
if (auth) {
|
|
2711
|
+
lines.push("app.post(\"/__void/migrate/auth\", async (c) => {");
|
|
2712
|
+
lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
|
|
2713
|
+
lines.push(" __voidGetMigrationEnv(),");
|
|
2714
|
+
lines.push(" __migrations,");
|
|
2715
|
+
lines.push(" __migrationDialect,");
|
|
2716
|
+
lines.push(` () => runVoidAuthMigrations(new Request(c.req.url), __voidGetMigrationEnv(), __voidAuthOptions${auth.authSchemaPath ? ", __voidAuthMigrationSchema" : ""}),`);
|
|
2717
|
+
lines.push(" __migrationPlanBundle,");
|
|
2718
|
+
lines.push(" );");
|
|
2719
|
+
lines.push(" return __voidMigrationHandler(c.req.raw);");
|
|
2720
|
+
lines.push("});");
|
|
2721
|
+
}
|
|
2722
|
+
lines.push("");
|
|
2723
|
+
} else {
|
|
2724
|
+
lines.push("export async function __voidHandleInternalRoute() { return null; }");
|
|
2725
|
+
lines.push("");
|
|
2726
|
+
}
|
|
2727
|
+
if (options.bindingHandlerPath) {
|
|
2728
|
+
lines.push(`import { createBindingHandler as __createBindingHandler } from ${JSON.stringify(options.bindingHandlerPath)};`);
|
|
2729
|
+
lines.push("let __voidBindingApp;");
|
|
2730
|
+
lines.push("app.all(\"/__void/*\", async (c) => {");
|
|
2731
|
+
lines.push(" if (!__voidBindingApp) __voidBindingApp = __createBindingHandler(__getRawEnv());");
|
|
2732
|
+
lines.push(" const url = new URL(c.req.url);");
|
|
2733
|
+
lines.push(" url.pathname = url.pathname.slice(7);");
|
|
2734
|
+
lines.push(" return __voidBindingApp.fetch(new Request(url.toString(), c.req.raw));");
|
|
2735
|
+
lines.push("});");
|
|
2736
|
+
lines.push("");
|
|
2737
|
+
}
|
|
2738
|
+
lines.push("const __rawEnvStore = new AsyncLocalStorage();");
|
|
2739
|
+
lines.push("export function __withRawEnv(env, fn) { return __rawEnvStore.run(env, fn); }");
|
|
2740
|
+
lines.push("function __getRawEnv() { return __rawEnvStore.getStore(); }");
|
|
2741
|
+
lines.push(`export const __VOID_BUILD_KEY = ${JSON.stringify(crypto.randomUUID())};`);
|
|
2742
|
+
lines.push(`export const __VOID_DEV_TRIGGER_TOKEN = ${JSON.stringify(options.devTriggerToken ?? crypto.randomUUID())};`);
|
|
2743
|
+
lines.push(`export const __VOID_ISR_REVALIDATE = ${JSON.stringify(mergedRevalidate)};`);
|
|
2744
|
+
lines.push(`export const __VOID_ISR_PAGES = ${JSON.stringify(isrPages)};`);
|
|
2745
|
+
lines.push(`export const __VOID_ISR_PATH_REVALIDATE = ${JSON.stringify(isrPathRevalidate)};`);
|
|
2746
|
+
lines.push(`export const __VOID_ISR_DEPLOYMENT_ID = ${JSON.stringify(crypto.randomUUID())};`);
|
|
2747
|
+
lines.push("");
|
|
2748
|
+
lines.push(`export default app;`);
|
|
2749
|
+
return lines.join("\n");
|
|
2750
|
+
}
|
|
2751
|
+
function compilePrerenderCollector(lines, scan, ssr, options) {
|
|
2752
|
+
const staticPaths = scan.pages.pages.filter((page) => page.shouldPrerender && page.params.length === 0 && !page.catchAll).map((page) => page.pattern);
|
|
2753
|
+
const dynamicPages = scan.pages.pages.filter((page) => page.shouldPrerender && page.hasGetPrerenderPaths && (page.params.length > 0 || page.catchAll));
|
|
2754
|
+
const explicitPaths = options.explicitPrerenderPaths ?? [];
|
|
2755
|
+
lines.push(`function __voidBuildPrerenderPath(pattern, params) {`);
|
|
2756
|
+
lines.push(` let path = pattern;`);
|
|
2757
|
+
lines.push(` for (const [key, value] of Object.entries(params)) {`);
|
|
2758
|
+
lines.push(` path = path.replace(":" + key + "{.+}", value);`);
|
|
2759
|
+
lines.push(` path = path.replace(":" + key, value);`);
|
|
2760
|
+
lines.push(` }`);
|
|
2761
|
+
lines.push(` return path;`);
|
|
2762
|
+
lines.push(`}`);
|
|
2763
|
+
lines.push("");
|
|
2764
|
+
lines.push(`export async function __voidCollectPrerenderPaths(request, env, ctx) {`);
|
|
2765
|
+
lines.push(` return withRuntimeEnv(env, async () => {`);
|
|
2766
|
+
if (options.nodeTarget) lines.push(` return requestContext.run({ request }, async () => {`);
|
|
2767
|
+
else lines.push(` return requestContext.run({ request, executionCtx: ctx }, async () => {`);
|
|
2768
|
+
lines.push(` const paths = [];`);
|
|
2769
|
+
for (const path of staticPaths) lines.push(` paths.push(${JSON.stringify(path)});`);
|
|
2770
|
+
for (const page of dynamicPages) {
|
|
2771
|
+
const loaderName = `_pload_${page.componentId.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
2772
|
+
lines.push(` {`);
|
|
2773
|
+
lines.push(` const mod = await ${loaderName}();`);
|
|
2774
|
+
lines.push(` if (typeof mod.getPrerenderPaths === "function") {`);
|
|
2775
|
+
lines.push(` const paramSets = await mod.getPrerenderPaths();`);
|
|
2776
|
+
lines.push(` if (Array.isArray(paramSets)) {`);
|
|
2777
|
+
lines.push(` for (const params of paramSets) {`);
|
|
2778
|
+
lines.push(` paths.push(__voidBuildPrerenderPath(${JSON.stringify(page.pattern)}, params));`);
|
|
2779
|
+
lines.push(` }`);
|
|
2780
|
+
lines.push(` }`);
|
|
2781
|
+
lines.push(` }`);
|
|
2782
|
+
lines.push(` }`);
|
|
2783
|
+
}
|
|
2784
|
+
if (ssr) {
|
|
2785
|
+
lines.push(` {`);
|
|
2786
|
+
lines.push(` const mod = await import(${JSON.stringify(ssr.serverEntryPath)});`);
|
|
2787
|
+
lines.push(` if (typeof mod.getPrerenderPaths === "function") {`);
|
|
2788
|
+
lines.push(` const ssrPaths = await mod.getPrerenderPaths();`);
|
|
2789
|
+
lines.push(` if (Array.isArray(ssrPaths)) {`);
|
|
2790
|
+
lines.push(` for (const path of ssrPaths) {`);
|
|
2791
|
+
lines.push(` if (typeof path === "string") paths.push(path);`);
|
|
2792
|
+
lines.push(` }`);
|
|
2793
|
+
lines.push(` }`);
|
|
2794
|
+
lines.push(` }`);
|
|
2795
|
+
lines.push(` }`);
|
|
2796
|
+
}
|
|
2797
|
+
for (const path of explicitPaths) lines.push(` paths.push(${JSON.stringify(path)});`);
|
|
2798
|
+
lines.push(` return [...new Set(paths)];`);
|
|
2799
|
+
lines.push(` });`);
|
|
2800
|
+
lines.push(` });`);
|
|
2801
|
+
lines.push(`}`);
|
|
2802
|
+
lines.push("");
|
|
2803
|
+
}
|
|
2804
|
+
function compilePageRoutes(lines, scan, pages, options) {
|
|
2805
|
+
lines.push(`import { renderPage } from "virtual:void-pages-renderer";`);
|
|
2806
|
+
if (options.dev) lines.push(`import { getVoidPagesDevCss } from "virtual:void-pages-dev-assets";`);
|
|
2807
|
+
const hasIslandPages = scan.pages.pages.some((p) => p.renderMode === "island");
|
|
2808
|
+
if (hasIslandPages) lines.push(`import { renderIslandPage } from "virtual:void-islands-renderer";`);
|
|
2809
|
+
lines.push("");
|
|
2810
|
+
lines.push(`const __voidHeadConfig = ${JSON.stringify(pages.headConfig ?? null)};`);
|
|
2811
|
+
lines.push("");
|
|
2812
|
+
pushDefaultErrorPageHelpers(lines, options);
|
|
2813
|
+
const relPagesDir = pages.pagesDirRelative;
|
|
2814
|
+
const pageCssKeys = {};
|
|
2815
|
+
const pageDevRootPhases = {};
|
|
2816
|
+
const projectRoot = options.projectRoot;
|
|
2817
|
+
const toDevRootUrl = (absolutePath) => "/" + absolutePath.replaceAll("\\", "/").slice(projectRoot.replaceAll("\\", "/").length + 1);
|
|
2818
|
+
const virtualDevUrl = (id) => "/@id/__x00__" + id;
|
|
2819
|
+
for (const page of scan.pages.pages) {
|
|
2820
|
+
const keys = [];
|
|
2821
|
+
const routeRootPhases = [[virtualDevUrl(page.island ? pages.islandClientEntryId ?? pages.clientEntryId : pages.clientEntryId)]];
|
|
2822
|
+
if (page.island && pages.mdClientEntryId) routeRootPhases[0].push(virtualDevUrl(pages.mdClientEntryId));
|
|
2823
|
+
const layoutIds = resolveLayoutIds(page, scan.pages.layouts, scan.pages.namedLayouts);
|
|
2824
|
+
const layoutRoots = [];
|
|
2825
|
+
for (const layoutId of layoutIds) {
|
|
2826
|
+
const layout = scan.pages.layouts.find((l) => (l.directory ? `${l.directory}/layout` : "layout") === layoutId);
|
|
2827
|
+
if (layout) {
|
|
2828
|
+
keys.push(`${relPagesDir}/${layout.filePath}`);
|
|
2829
|
+
layoutRoots.push(toDevRootUrl(`${pages.pagesDir}/${layout.filePath}`));
|
|
2830
|
+
continue;
|
|
2831
|
+
}
|
|
2832
|
+
const named = scan.pages.namedLayouts.find((nl) => {
|
|
2833
|
+
return (nl.directory ? `${nl.directory}/_layouts/${nl.name}` : `_layouts/${nl.name}`) === layoutId;
|
|
2834
|
+
});
|
|
2835
|
+
if (named) {
|
|
2836
|
+
keys.push(`${relPagesDir}/${named.filePath}`);
|
|
2837
|
+
layoutRoots.push(toDevRootUrl(`${pages.pagesDir}/${named.filePath}`));
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
keys.push(`${relPagesDir}/${page.componentPath}`);
|
|
2841
|
+
routeRootPhases.push(layoutRoots);
|
|
2842
|
+
routeRootPhases.push([toDevRootUrl(`${pages.pagesDir}/${page.componentPath}`)]);
|
|
2843
|
+
pageCssKeys[page.componentId] = keys;
|
|
2844
|
+
pageDevRootPhases[page.componentId] = routeRootPhases.filter((phase) => phase.length > 0);
|
|
2845
|
+
}
|
|
2846
|
+
lines.push("const __loadPagesManifest = __createManifestLoader();");
|
|
2847
|
+
const assetMetadata = {
|
|
2848
|
+
pageCssKeys,
|
|
2849
|
+
devRootPhases: options.dev ? pageDevRootPhases : void 0,
|
|
2850
|
+
dev: options.dev === true
|
|
2851
|
+
};
|
|
2852
|
+
const devCssOption = options.dev ? ", devCss: getVoidPagesDevCss" : "";
|
|
2853
|
+
lines.push(`const getPagesAssetTags = __createClientAssetTags({ ...${JSON.stringify({
|
|
2854
|
+
...assetMetadata,
|
|
2855
|
+
entryId: pages.clientEntryId,
|
|
2856
|
+
entryUrl: "/@id/__x00__" + pages.clientEntryId,
|
|
2857
|
+
kind: "pages"
|
|
2858
|
+
})}${devCssOption} }, __loadPagesManifest);`);
|
|
2859
|
+
if (hasIslandPages && pages.islandClientEntryId) lines.push(`const getIslandsAssetTags = __createClientAssetTags({ ...${JSON.stringify({
|
|
2860
|
+
...assetMetadata,
|
|
2861
|
+
entryId: pages.islandClientEntryId,
|
|
2862
|
+
entryUrl: "/@id/__x00__" + pages.islandClientEntryId,
|
|
2863
|
+
extraEntryId: pages.mdClientEntryId,
|
|
2864
|
+
extraEntryUrl: pages.mdClientEntryId ? "/@id/__x00__" + pages.mdClientEntryId : void 0,
|
|
2865
|
+
kind: "islands"
|
|
2866
|
+
})}${devCssOption} }, __loadPagesManifest);`);
|
|
2867
|
+
const seenServerFiles = /* @__PURE__ */ new Set();
|
|
2868
|
+
for (const page of scan.pages.pages) {
|
|
2869
|
+
const safeId = page.componentId.replace(/[^a-zA-Z0-9]/g, "_");
|
|
2870
|
+
let serverImportPath = null;
|
|
2871
|
+
if (page.serverPath) serverImportPath = `${pages.pagesDir}/${page.serverPath}`;
|
|
2872
|
+
if (serverImportPath && !seenServerFiles.has(serverImportPath)) {
|
|
2873
|
+
seenServerFiles.add(serverImportPath);
|
|
2874
|
+
const loaderName = `_pload_${safeId}`;
|
|
2875
|
+
lines.push(`const ${loaderName} = __createModuleLoader(() => import(${JSON.stringify(serverImportPath)}));`);
|
|
2876
|
+
}
|
|
2877
|
+
let mdHeadRef = null;
|
|
2878
|
+
if (!serverImportPath && page.metadata && (page.metadata.title || page.metadata.description)) {
|
|
2879
|
+
const headObj = {};
|
|
2880
|
+
if (page.metadata.title) headObj.title = page.metadata.title;
|
|
2881
|
+
if (page.metadata.description) headObj.meta = [{
|
|
2882
|
+
name: "description",
|
|
2883
|
+
content: page.metadata.description
|
|
2884
|
+
}];
|
|
2885
|
+
const headVarName = `__mdHead_${safeId}`;
|
|
2886
|
+
lines.push(`const ${headVarName} = () => (${JSON.stringify(headObj)});`);
|
|
2887
|
+
mdHeadRef = headVarName;
|
|
2888
|
+
}
|
|
2889
|
+
const renderer = page.renderMode === "island" ? "renderIslandPage" : "renderPage";
|
|
2890
|
+
const assetTags = page.renderMode === "island" && pages.islandClientEntryId ? "getIslandsAssetTags" : "getPagesAssetTags";
|
|
2891
|
+
const routeOptions = `{ ${serverImportPath ? "load: _pload_" + safeId + ", " : ""}${mdHeadRef ? "head: " + mdHeadRef + ", " : ""}componentId: ${JSON.stringify(page.componentId)}, render: ${renderer}, assets: ${assetTags}, headConfig: __voidHeadConfig, renderOptions: ${JSON.stringify({ renderMode: page.renderMode })} }`;
|
|
2892
|
+
lines.push(`app.get(${JSON.stringify(page.pattern)}, __createPageGetHandler(${routeOptions}));`);
|
|
2893
|
+
if (page.methods.includes("action") || page.methods.includes("actions")) lines.push(`app.on(["POST", "PUT", "PATCH", "DELETE"], ${JSON.stringify(page.pattern)}, __createPageMutationHandler({ ...${routeOptions}, namedActions: ${JSON.stringify(page.methods.includes("actions"))} }));`);
|
|
2894
|
+
}
|
|
2895
|
+
lines.push("");
|
|
2896
|
+
}
|
|
2897
|
+
/**
|
|
2898
|
+
* Generate the asset fallthrough helper shared by native Void workers and
|
|
2899
|
+
* Class A framework wrappers. If the app returns 404, check ASSETS binding
|
|
2900
|
+
* for static files and set cache headers. Edge caching of hashed assets
|
|
2901
|
+
* happens at the dispatch layer (see design doc 0042).
|
|
2902
|
+
*
|
|
2903
|
+
* `notFound` is the RESOLVED `assetConfig.not_found_handling` for this build
|
|
2904
|
+
* (`resolveAssetRoutingPolicy`), and it decides what happens once the asset
|
|
2905
|
+
* binding has also answered 404. It is a separate argument from
|
|
2906
|
+
* `serveSpaFallback` because the two are not derived from each other: the
|
|
2907
|
+
* common worker-first app runs with `not_found_handling: 'none'` and STILL
|
|
2908
|
+
* wants the worker-side shell fallback (that is the whole reason the flag
|
|
2909
|
+
* exists). They are mutually exclusive in practice — `resolveAssetRoutingPolicy`
|
|
2910
|
+
* clears `workerSpaFallback` whenever an explicit `routing.notFound` asks for
|
|
2911
|
+
* anything other than the SPA shell.
|
|
2912
|
+
*/
|
|
2913
|
+
function compileAssetFallbackHelper(assetsDir = "assets", serveSpaFallback = false, notFound = "none") {
|
|
2914
|
+
return `import { createAssetFallback as __createAssetFallback } from ${JSON.stringify(ROUTING_RUNTIME_PATH)};
|
|
2915
|
+
const serveWithAssets = __createAssetFallback(${JSON.stringify({
|
|
2916
|
+
assetsDir,
|
|
2917
|
+
serveSpaFallback,
|
|
2918
|
+
notFound
|
|
2919
|
+
})});
|
|
2920
|
+
`;
|
|
2921
|
+
}
|
|
2922
|
+
/**
|
|
2923
|
+
* Generate the Worker entry module code.
|
|
2924
|
+
* Re-exports the Hono app from the virtual module with an asset-serving
|
|
2925
|
+
* fallthrough: when the app returns 404, the entry tries env.ASSETS.fetch()
|
|
2926
|
+
* and adds proper Cache-Control headers (immutable for Vite-hashed /assets/*).
|
|
2927
|
+
*
|
|
2928
|
+
* When `hasQueues` is true, also exports a `queue()` handler that
|
|
2929
|
+
* serializes the native MessageBatch and dispatches it to the
|
|
2930
|
+
* `/__void/queue` Hono route. In local dev Miniflare delivers batches
|
|
2931
|
+
* natively to this export; in production the relay worker does it.
|
|
2932
|
+
*
|
|
2933
|
+
* When `remotePath` is provided, wraps the fetch handler to detect
|
|
2934
|
+
* remote mode (`__VOID_REMOTE`) and replace local bindings with
|
|
2935
|
+
* proxy-backed versions.
|
|
2936
|
+
*/
|
|
2937
|
+
function deriveEmailRuntimePath(runtimeEnvPath) {
|
|
2938
|
+
return runtimeEnvPath.replace(/_?env(\.[^/]+)?$/, (_m, ext) => "email" + (ext ?? ""));
|
|
2939
|
+
}
|
|
2940
|
+
function compileWorkerEntry(options = {}) {
|
|
2941
|
+
const { hasQueues = false, versionMetadataBinding, remote, assetsDir = "assets", webSocketClasses = [], runtimeEnvPath = "void/_env", runtimeEmailPath, envSchemaModule, sandbox, serveSpaFallback = false, hasEmail = false, live, isr, notFound = "none", durableObjects = [] } = options;
|
|
2942
|
+
const remotePath = remote?.remotePath;
|
|
2943
|
+
const remoteBindingNames = JSON.stringify(remote?.bindingNames);
|
|
2944
|
+
const createRemoteEnvCall = `createRemoteEnv(env${remote?.bindingNames ? `, ${remoteBindingNames}` : ""})`;
|
|
2945
|
+
const hasSandbox = sandbox === true || typeof sandbox === "object";
|
|
2946
|
+
const sandboxClassName = typeof sandbox === "object" ? sandbox.className ?? "SandboxV1" : DEFAULT_SANDBOX_CLASS_NAME;
|
|
2947
|
+
const sandboxBindingName = typeof sandbox === "object" ? sandbox.bindingName ?? "SANDBOX" : "SANDBOX";
|
|
2948
|
+
const sandboxRuntimePath = typeof sandbox === "object" ? sandbox.runtimePath ?? "void/sandbox" : "void/sandbox";
|
|
2949
|
+
const sandboxImport = hasSandbox ? `import { VoidPlatformSandbox as __VoidSandbox, __setSandboxBindingName } from ${JSON.stringify(sandboxRuntimePath)};\n` : "";
|
|
2950
|
+
const sandboxInstanceType = typeof sandbox === "object" ? sandbox.instanceType : void 0;
|
|
2951
|
+
const sandboxExport = hasSandbox ? sandboxClassName === "SandboxV1" && !sandboxInstanceType ? `export { __VoidSandbox as SandboxV1 };\n__setSandboxBindingName(${JSON.stringify(sandboxBindingName)});\n` : `export class ${sandboxClassName} extends __VoidSandbox {${sandboxInstanceType ? ` protected override instanceType = ${JSON.stringify(sandboxInstanceType)} as const; ` : ""}}\n__setSandboxBindingName(${JSON.stringify(sandboxBindingName)});\n` : "";
|
|
2952
|
+
const hasLive = live === true || typeof live === "object";
|
|
2953
|
+
const liveBindingName = typeof live === "object" ? live.bindingName ?? "VOID_LIVE" : "VOID_LIVE";
|
|
2954
|
+
const liveClassName = typeof live === "object" ? live.className ?? "VoidLiveStreamDurableObject" : "VoidLiveStreamDurableObject";
|
|
2955
|
+
const liveRouteImport = hasLive ? ", VoidLiveStreamDurableObject" : "";
|
|
2956
|
+
const liveRuntimeImport = hasLive ? `import { __setLiveBindingName } from "void/live";\n` : "";
|
|
2957
|
+
const liveExport = hasLive ? liveClassName === "VoidLiveStreamDurableObject" ? `export { VoidLiveStreamDurableObject };\n__setLiveBindingName(${JSON.stringify(liveBindingName)});\n` : `export { VoidLiveStreamDurableObject as ${liveClassName} };\n__setLiveBindingName(${JSON.stringify(liveBindingName)});\n` : "";
|
|
2958
|
+
const durableObjectImports = durableObjects.map(({ filePath }, index) => `import __voidDurableObject${index} from ${JSON.stringify(filePath)};`).join("\n");
|
|
2959
|
+
const durableObjectExports = durableObjects.map(({ bindingName, className, name }, index) => `__voidDurableObject${index}.__bind(${JSON.stringify(bindingName)}${name !== void 0 ? `, ${JSON.stringify(name)}` : ""});\nexport const ${className} = __voidDurableObject${index}.class;`).join("\n");
|
|
2960
|
+
const runtimeEnvImport = `import { withRuntimeEnv } from ${JSON.stringify(runtimeEnvPath)};\n`;
|
|
2961
|
+
const remoteImport = remote ? `import { createRemoteEnv } from ${JSON.stringify(remote.remotePath)};\n` : "";
|
|
2962
|
+
const envSchemaImport = envSchemaModule ? `import ${JSON.stringify(envSchemaModule.importPath)};\nimport { _validateEnvOnce } from ${JSON.stringify(envSchemaModule.helperPath)};\n` : "";
|
|
2963
|
+
const envValidateCall = envSchemaModule ? " await _validateEnvOnce(env);\n" : "";
|
|
2964
|
+
const exactNames = isr ? INTERNAL_BINDING_EXACT_NAMES : [];
|
|
2965
|
+
const filterBindingsHelper = `import { filterInternalBindings as __filterBindings, createWorkerFetch as __createWorkerFetch } from ${JSON.stringify(ROUTING_RUNTIME_PATH)};
|
|
2966
|
+
const __filterInternalBindings = (env) => __filterBindings(env, ${JSON.stringify(INTERNAL_BINDING_PREFIXES)}, ${JSON.stringify(exactNames)});
|
|
2967
|
+
`;
|
|
2968
|
+
const assetHelper = compileAssetFallbackHelper(assetsDir, serveSpaFallback, notFound);
|
|
2969
|
+
const isrImport = isr ? `import { handleIsr } from ${JSON.stringify(isr.runtimePath)};\nimport { __VOID_ISR_REVALIDATE, __VOID_ISR_PAGES, __VOID_ISR_PATH_REVALIDATE, __VOID_ISR_DEPLOYMENT_ID } from 'virtual:void-routes';\n` : "";
|
|
2970
|
+
const isrDeploymentStamp = isr ? ` env.__VOID_ISR_DEPLOYMENT_ID ??= __VOID_ISR_DEPLOYMENT_ID;\n` : "";
|
|
2971
|
+
const fetchHandler = `__createWorkerFetch({
|
|
2972
|
+
app, assets: serveWithAssets, withRawEnv: __withRawEnv, withRuntimeEnv,
|
|
2973
|
+
filterBindings: __filterInternalBindings,
|
|
2974
|
+
internalRoute: __voidHandleInternalRoute,
|
|
2975
|
+
collectPrerenderPaths: __voidCollectPrerenderPaths${versionMetadataBinding ? ", versionMetadataBinding: " + JSON.stringify(versionMetadataBinding) : ""}${envSchemaModule ? ", validateEnv: _validateEnvOnce" : ""}${remotePath ? ", remoteEnv: (env) => " + createRemoteEnvCall : ""}${hasEmail ? ", emailReceipt: __handleEmailReceipt" : ""}${isr ? ", isr: { deploymentId: __VOID_ISR_DEPLOYMENT_ID, handle: (request, env, ctx, render) => handleIsr(request, env, ctx, { revalidate: __VOID_ISR_REVALIDATE, pages: __VOID_ISR_PAGES, pathRevalidate: __VOID_ISR_PATH_REVALIDATE, deploymentId: __VOID_ISR_DEPLOYMENT_ID, queryAllowlist: " + JSON.stringify(isr.queryAllowlist) + " }, render) }" : ""}
|
|
2976
|
+
})`;
|
|
2977
|
+
const scheduledHandler = remotePath ? `async function handleScheduled(controller, env, ctx) {
|
|
2978
|
+
${envValidateCall} if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};
|
|
2979
|
+
${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
|
|
2980
|
+
return withRuntimeEnv(env, () => scheduled(controller, userEnv, ctx));
|
|
2981
|
+
}` : `async function handleScheduled(controller, env, ctx) {
|
|
2982
|
+
${envValidateCall}${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
|
|
2983
|
+
return withRuntimeEnv(env, () => scheduled(controller, userEnv, ctx));
|
|
2984
|
+
}`;
|
|
2985
|
+
const emailImport = hasEmail ? ", __emailHandler" : "";
|
|
2986
|
+
const emailRuntimePath = runtimeEmailPath ?? deriveEmailRuntimePath(runtimeEnvPath);
|
|
2987
|
+
const emailProxyImport = hasEmail ? `import { createMessageProxy as __createMessageProxy, enactEmailActions as __enactEmailActions, isForwardableEmailMessage as __isForwardableEmailMessage } from ${JSON.stringify(emailRuntimePath)};\nimport { EMAIL_PROTOCOL_VERSION as __EMAIL_PROTOCOL_VERSION, encodeEmailActions as __encodeEmailActions, readEmailEnvelope as __readEmailEnvelope, verifyEmailReceiptThroughProxy as __verifyEmailReceiptThroughProxy } from "void/_email-protocol";\n` : "";
|
|
2988
|
+
const emailHandler = hasEmail ? remotePath ? `async function handleEmail(envelope, env, ctx) {
|
|
2989
|
+
${envValidateCall} if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};
|
|
2990
|
+
const userEnv = __filterInternalBindings(env);
|
|
2991
|
+
const __relayed = typeof envelope.viaDomain === "string" && envelope.viaDomain.length > 0;
|
|
2992
|
+
const __slug = __relayed ? undefined : (typeof env.__VOID_PROJECT_SLUG === "string" && env.__VOID_PROJECT_SLUG.length > 0 ? env.__VOID_PROJECT_SLUG : undefined);
|
|
2993
|
+
const { message, getActions } = __createMessageProxy(envelope, { slug: __slug });
|
|
2994
|
+
await __withRawEnv(env, () => withRuntimeEnv(env, () => __emailHandler(message, userEnv, ctx, { slug: __slug })));
|
|
2995
|
+
const __actions = getActions();
|
|
2996
|
+
if (__isForwardableEmailMessage(envelope)) await __enactEmailActions(envelope, __actions);
|
|
2997
|
+
return __actions;
|
|
2998
|
+
}` : `async function handleEmail(envelope, env, ctx) {
|
|
2999
|
+
${envValidateCall} const userEnv = __filterInternalBindings(env);
|
|
3000
|
+
const __relayed = typeof envelope.viaDomain === "string" && envelope.viaDomain.length > 0;
|
|
3001
|
+
const __slug = __relayed ? undefined : (typeof env.__VOID_PROJECT_SLUG === "string" && env.__VOID_PROJECT_SLUG.length > 0 ? env.__VOID_PROJECT_SLUG : undefined);
|
|
3002
|
+
const { message, getActions } = __createMessageProxy(envelope, { slug: __slug });
|
|
3003
|
+
await __withRawEnv(env, () => withRuntimeEnv(env, () => __emailHandler(message, userEnv, ctx, { slug: __slug })));
|
|
3004
|
+
const __actions = getActions();
|
|
3005
|
+
if (__isForwardableEmailMessage(envelope)) await __enactEmailActions(envelope, __actions);
|
|
3006
|
+
return __actions;
|
|
3007
|
+
}` : "";
|
|
3008
|
+
const emailReceiptHandler = hasEmail ? `async function __handleEmailReceipt(request, env, ctx) {
|
|
3009
|
+
if (request.method !== "POST" || request.headers.get("content-encoding")) return new Response(null, { status: 404 });
|
|
3010
|
+
const __token = request.headers.get("x-void-email-receipt");
|
|
3011
|
+
const __recipient = request.headers.get("x-void-email-recipient");
|
|
3012
|
+
if (!__token || __token.length > 1536 || !__recipient || __recipient.length > 320 ||
|
|
3013
|
+
!env.__VOID_PROXY_TOKEN || !env.__PROJECT_ID || !env.__VOID_EMAIL_DEPLOYMENT_ID || !env.__VOID_PROXY) {
|
|
3014
|
+
return new Response(null, { status: 404 });
|
|
3015
|
+
}
|
|
3016
|
+
const __verified = await __verifyEmailReceiptThroughProxy(env.__VOID_PROXY, env.__VOID_PROXY_TOKEN, {
|
|
3017
|
+
receipt: __token, projectId: env.__PROJECT_ID,
|
|
3018
|
+
deploymentId: env.__VOID_EMAIL_DEPLOYMENT_ID, recipient: __recipient,
|
|
3019
|
+
});
|
|
3020
|
+
if (!__verified) return new Response(null, { status: 404 });
|
|
3021
|
+
let __envelope;
|
|
3022
|
+
try { __envelope = await __readEmailEnvelope(request.body); }
|
|
3023
|
+
catch { return new Response(null, { status: 400 }); }
|
|
3024
|
+
if (__envelope.to !== __recipient) return new Response(null, { status: 400 });
|
|
3025
|
+
const __actions = await handleEmail(__envelope, env, ctx);
|
|
3026
|
+
return new Response(__encodeEmailActions(__actions), {
|
|
3027
|
+
headers: { "content-type": "application/vnd.void.email-actions; version=" + __EMAIL_PROTOCOL_VERSION,
|
|
3028
|
+
"cache-control": "no-store" },
|
|
3029
|
+
});
|
|
3030
|
+
}` : "";
|
|
3031
|
+
const entryImport = `import { WorkerEntrypoint } from "cloudflare:workers";\n`;
|
|
3032
|
+
const fetchDeclaration = `const __voidFetch = ${fetchHandler};`;
|
|
3033
|
+
const entryClass = `export default class extends WorkerEntrypoint {
|
|
3034
|
+
async fetch(request) { return __voidFetch(request, this.env, this.ctx); }
|
|
3035
|
+
async scheduled(controller) { return handleScheduled(controller, this.env, this.ctx); }
|
|
3036
|
+
${hasQueues ? ` async queue(batch) { return __voidQueue(batch, this.env, this.ctx); }\n` : ""}${hasEmail ? ` async email(message) {
|
|
3037
|
+
if (this.env.__VOID_EMAIL_DEPLOYMENT_ID || !__isForwardableEmailMessage(message)) {
|
|
3038
|
+
throw new TypeError("Native email event required");
|
|
3039
|
+
}
|
|
3040
|
+
await handleEmail(message, this.env, this.ctx);
|
|
3041
|
+
}\n` : ""}}`;
|
|
3042
|
+
if (hasQueues) return `${sandboxImport}${entryImport}${liveRuntimeImport}import app, { scheduled, __voidCollectPrerenderPaths, __voidHandleInternalRoute, __withRawEnv, __processQueue${emailImport}${webSocketClasses.length > 0 ? `, ${webSocketClasses.join(", ")}` : ""}${liveRouteImport} } from 'virtual:void-routes';
|
|
3043
|
+
${durableObjectImports}\n${runtimeEnvImport}${emailProxyImport}${envSchemaImport}${remoteImport}${isrImport}${filterBindingsHelper}${assetHelper}
|
|
3044
|
+
${fetchDeclaration}
|
|
3045
|
+
${scheduledHandler}
|
|
3046
|
+
${emailHandler}
|
|
3047
|
+
${emailReceiptHandler}
|
|
3048
|
+
async function __voidQueue(batch, env, ctx) {
|
|
3049
|
+
${envValidateCall}${remotePath ? ` if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};\n` : ""}${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
|
|
3050
|
+
const messages = [...batch.messages].map(m => ({
|
|
3051
|
+
id: m.id, timestamp: m.timestamp.toISOString(),
|
|
3052
|
+
body: m.body, attempts: m.attempts,
|
|
3053
|
+
}));
|
|
3054
|
+
const payload = { queue: batch.queue, messages };
|
|
3055
|
+
const __qr = await __withRawEnv(env, () => withRuntimeEnv(env, () => __processQueue(payload, userEnv)));
|
|
3056
|
+
if (__qr.decisions) {
|
|
3057
|
+
const __msgMap = new Map();
|
|
3058
|
+
for (const m of batch.messages) __msgMap.set(m.id, m);
|
|
3059
|
+
for (const [id, d] of Object.entries(__qr.decisions)) {
|
|
3060
|
+
const m = __msgMap.get(id);
|
|
3061
|
+
if (!m) continue;
|
|
3062
|
+
if (d.action === "ack") m.ack();
|
|
3063
|
+
else if (d.action === "retry") m.retry(d.delaySeconds != null ? { delaySeconds: d.delaySeconds } : undefined);
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
if (!__qr.ok) throw new Error("queue: Handler failed.");
|
|
3067
|
+
}
|
|
3068
|
+
|
|
3069
|
+
${webSocketClasses.map((name) => `export { ${name} };`).join("\n")}
|
|
3070
|
+
${liveExport}
|
|
3071
|
+
${sandboxExport}
|
|
3072
|
+
${durableObjectExports}
|
|
3073
|
+
${entryClass}`;
|
|
3074
|
+
return `${sandboxImport}${entryImport}${liveRuntimeImport}import app, { scheduled, __voidCollectPrerenderPaths, __voidHandleInternalRoute, __withRawEnv${emailImport}${webSocketClasses.length > 0 ? `, ${webSocketClasses.join(", ")}` : ""}${liveRouteImport} } from 'virtual:void-routes';
|
|
3075
|
+
${durableObjectImports}\n${runtimeEnvImport}${emailProxyImport}${envSchemaImport}${remoteImport}${isrImport}${filterBindingsHelper}${assetHelper}
|
|
3076
|
+
${fetchDeclaration}
|
|
3077
|
+
${scheduledHandler}
|
|
3078
|
+
${emailHandler}
|
|
3079
|
+
${emailReceiptHandler}
|
|
3080
|
+
${webSocketClasses.map((name) => `export { ${name} };`).join("\n")}
|
|
3081
|
+
${liveExport}
|
|
3082
|
+
${sandboxExport}
|
|
3083
|
+
${durableObjectExports}
|
|
3084
|
+
${entryClass}`;
|
|
3085
|
+
}
|
|
3086
|
+
/**
|
|
3087
|
+
* Generate the app module — exports the Hono app with static asset serving.
|
|
3088
|
+
* This is the entry imported by `vite preview` and can be used programmatically.
|
|
3089
|
+
*
|
|
3090
|
+
* When `envSchemaModule` is provided, splices boot-time env validation so it
|
|
3091
|
+
* runs on the first request (including the prerender child process spawned
|
|
3092
|
+
* by `prerenderPagesNode`). Mirrors `compileWorkerEntry`: a side-effect import
|
|
3093
|
+
* registers the schema, and `_validateEnvOnce` throws `EnvValidationError` on
|
|
3094
|
+
* bad values. Env source on Node/Bun is `process.env`; on Deno it's
|
|
3095
|
+
* `Deno.env.toObject()`.
|
|
3096
|
+
*/
|
|
3097
|
+
function compileNodeApp(target, honoPath, envSchemaModule) {
|
|
3098
|
+
const staticImport = target === "bun" ? `import { serveStatic } from "hono/bun";` : target === "deno" ? `import { serveStatic } from "hono/deno";` : `import { serveStatic } from "@hono/node-server/serve-static";`;
|
|
3099
|
+
const envSchemaImport = envSchemaModule ? `import ${JSON.stringify(envSchemaModule.importPath)};\nimport { _validateEnvOnce } from ${JSON.stringify(envSchemaModule.helperPath)};\n` : "";
|
|
3100
|
+
const envMiddleware = envSchemaModule ? `app.use("*", async (c, next) => { if (c.req.method !== "POST" || c.req.path !== "/__void/prerender-paths") await _validateEnvOnce(${target === "deno" ? "Deno.env.toObject()" : "process.env"}); return next(); });\n` : "";
|
|
3101
|
+
return `${staticImport}
|
|
3102
|
+
import { fileURLToPath } from "node:url";
|
|
3103
|
+
import { Hono } from ${JSON.stringify(honoPath)};
|
|
3104
|
+
import innerApp, { __voidCollectPrerenderPaths } from "virtual:void-routes";
|
|
3105
|
+
${envSchemaImport}
|
|
3106
|
+
// Resolved by nodeTargetPlugin for this emitted chunk, including shared chunks.
|
|
3107
|
+
const clientRoot = fileURLToPath(import.meta.voidClientDirectory);
|
|
3108
|
+
const assetApp = new Hono();
|
|
3109
|
+
assetApp.get("*", serveStatic({ root: clientRoot }));
|
|
3110
|
+
const assets = { fetch: (request) => assetApp.fetch(request) };
|
|
3111
|
+
const app = new Hono();
|
|
3112
|
+
${envMiddleware}app.post("/__void/prerender-paths", async (c) => {
|
|
3113
|
+
const __expected = c.env?.__VOID_PROXY_TOKEN;
|
|
3114
|
+
if (__expected) {
|
|
3115
|
+
const __token = c.req.header("x-void-internal");
|
|
3116
|
+
if (!__token || __token !== __expected) return c.json({ error: "unauthorized" }, 401);
|
|
3117
|
+
}
|
|
3118
|
+
const paths = await __voidCollectPrerenderPaths(c.req.raw);
|
|
3119
|
+
return c.json({ paths });
|
|
3120
|
+
});
|
|
3121
|
+
app.get("/assets/*", (c) => assets.fetch(c.req.raw));
|
|
3122
|
+
app.all("*", (c) => innerApp.fetch(c.req.raw, { ...c.env, ASSETS: assets }));
|
|
3123
|
+
export default app;`;
|
|
3124
|
+
}
|
|
3125
|
+
/**
|
|
3126
|
+
* Generate the server entry — imports the app and starts an HTTP server.
|
|
3127
|
+
* This is what users run with `node dist/ssr/index.js`.
|
|
3128
|
+
*/
|
|
3129
|
+
function compileNodeServer(target) {
|
|
3130
|
+
if (target === "bun") return `import app from "./app.js";
|
|
3131
|
+
|
|
3132
|
+
const port = Number(process.env.PORT || 3000);
|
|
3133
|
+
Bun.serve({ fetch: app.fetch, port });
|
|
3134
|
+
console.log("Listening on http://localhost:" + port);`;
|
|
3135
|
+
if (target === "deno") return `import app from "./app.js";
|
|
3136
|
+
|
|
3137
|
+
const port = Number(Deno.env.get("PORT") || 3000);
|
|
3138
|
+
Deno.serve({ port }, app.fetch);`;
|
|
3139
|
+
return `import { serve } from "@hono/node-server";
|
|
3140
|
+
import app from "./app.js";
|
|
3141
|
+
|
|
3142
|
+
const port = Number(process.env.PORT || 3000);
|
|
3143
|
+
serve({ fetch: app.fetch, port }, (info) => {
|
|
3144
|
+
console.log("Listening on http://localhost:" + info.port);
|
|
3145
|
+
});`;
|
|
3146
|
+
}
|
|
3147
|
+
/**
|
|
3148
|
+
* Generate the server-side fetch client module for non-CF targets.
|
|
3149
|
+
* Same as compileWorkerClient but without cloudflare:workers dependency.
|
|
3150
|
+
* Calls app.fetch() directly (no HTTP round-trip) with ALS context forwarding.
|
|
3151
|
+
*/
|
|
3152
|
+
function compileBrowserClient(authClientPath, fetchPath, fetchStreamPath) {
|
|
3153
|
+
return `export { fetch, FetchError } from ${JSON.stringify(fetchPath)};
|
|
3154
|
+
export { fetchStream, StreamError } from ${JSON.stringify(fetchStreamPath)};
|
|
3155
|
+
export { auth, createAuthClient } from ${JSON.stringify(authClientPath)};`;
|
|
3156
|
+
}
|
|
3157
|
+
function compileNodeClient(authClientPath) {
|
|
3158
|
+
return `import app, { requestContext } from "virtual:void-routes";
|
|
3159
|
+
import { interpolateRouteParams } from ${JSON.stringify(ROUTE_URL_PATH)};
|
|
3160
|
+
export { auth, createAuthClient } from ${JSON.stringify(authClientPath)};
|
|
3161
|
+
|
|
3162
|
+
export class FetchError extends Error {
|
|
3163
|
+
constructor(status, statusText, data) {
|
|
3164
|
+
super(\`fetch: Request failed with status \${status}.\`);
|
|
3165
|
+
this.name = "FetchError";
|
|
3166
|
+
this.status = status;
|
|
3167
|
+
this.statusText = statusText;
|
|
3168
|
+
this.data = data;
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
export async function fetch(path, options) {
|
|
3173
|
+
let url = interpolateRouteParams(path, options?.params);
|
|
3174
|
+
|
|
3175
|
+
if (options?.query) {
|
|
3176
|
+
const qs = new URLSearchParams(options.query).toString();
|
|
3177
|
+
if (qs) url += "?" + qs;
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
const method = options?.method ?? "GET";
|
|
3181
|
+
const headers = new Headers(options?.headers);
|
|
3182
|
+
|
|
3183
|
+
const store = requestContext.getStore();
|
|
3184
|
+
if (store?.request) {
|
|
3185
|
+
const outerReq = store.request;
|
|
3186
|
+
if (!headers.has("cookie")) {
|
|
3187
|
+
const cookie = outerReq.headers.get("cookie");
|
|
3188
|
+
if (cookie) headers.set("cookie", cookie);
|
|
3189
|
+
}
|
|
3190
|
+
if (!headers.has("authorization")) {
|
|
3191
|
+
const auth = outerReq.headers.get("authorization");
|
|
3192
|
+
if (auth) headers.set("authorization", auth);
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
if (options?.body != null && !headers.has("content-type")) {
|
|
3197
|
+
headers.set("content-type", "application/json");
|
|
3198
|
+
}
|
|
3199
|
+
|
|
3200
|
+
const request = new Request("http://localhost" + url, {
|
|
3201
|
+
method,
|
|
3202
|
+
headers,
|
|
3203
|
+
body: options?.body != null ? JSON.stringify(options.body) : undefined,
|
|
3204
|
+
});
|
|
3205
|
+
|
|
3206
|
+
const response = await app.fetch(request);
|
|
3207
|
+
|
|
3208
|
+
if (response.status === 204) return undefined;
|
|
3209
|
+
|
|
3210
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
3211
|
+
const data = contentType.includes("application/json") ? await response.json() : await response.text();
|
|
3212
|
+
|
|
3213
|
+
if (!response.ok) {
|
|
3214
|
+
throw new FetchError(response.status, response.statusText, data);
|
|
3215
|
+
}
|
|
3216
|
+
|
|
3217
|
+
return data;
|
|
3218
|
+
}`;
|
|
3219
|
+
}
|
|
3220
|
+
function stripOriginForProd(rules) {
|
|
3221
|
+
return rules.map(({ origin: _origin, ...rest }) => rest);
|
|
3222
|
+
}
|
|
3223
|
+
function compileRoutingRulesMiddleware(lines, rules, dev, _nodeTarget) {
|
|
3224
|
+
lines.push("// Routing rules: redirects + rewrites (applied before route handlers)");
|
|
3225
|
+
lines.push(`const __routingRules = ${JSON.stringify(dev ? rules : stripOriginForProd(rules))};`);
|
|
3226
|
+
lines.push("app.use(\"*\", __routingRuntime.routingMiddleware(__routingRules));");
|
|
3227
|
+
}
|
|
3228
|
+
function compileHeaderMiddleware(lines, rules) {
|
|
3229
|
+
lines.push("// Header rules (applied to responses)");
|
|
3230
|
+
lines.push(`const __headerRules = ${JSON.stringify(rules)};`);
|
|
3231
|
+
lines.push("app.use(\"*\", __routingRuntime.headerMiddleware(__headerRules));");
|
|
3232
|
+
}
|
|
3233
|
+
function compileWorkerClient(authClientPath) {
|
|
3234
|
+
return `import app, { requestContext } from "virtual:void-routes";
|
|
3235
|
+
import { interpolateRouteParams } from ${JSON.stringify(ROUTE_URL_PATH)};
|
|
3236
|
+
import { env } from "cloudflare:workers";
|
|
3237
|
+
export { auth, createAuthClient } from ${JSON.stringify(authClientPath)};
|
|
3238
|
+
|
|
3239
|
+
export class FetchError extends Error {
|
|
3240
|
+
constructor(status, statusText, data) {
|
|
3241
|
+
super(\`fetch: Request failed with status \${status}.\`);
|
|
3242
|
+
this.name = "FetchError";
|
|
3243
|
+
this.status = status;
|
|
3244
|
+
this.statusText = statusText;
|
|
3245
|
+
this.data = data;
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3249
|
+
export async function fetch(path, options) {
|
|
3250
|
+
let url = interpolateRouteParams(path, options?.params);
|
|
3251
|
+
|
|
3252
|
+
if (options?.query) {
|
|
3253
|
+
const qs = new URLSearchParams(options.query).toString();
|
|
3254
|
+
if (qs) url += "?" + qs;
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
const method = options?.method ?? "GET";
|
|
3258
|
+
const headers = new Headers(options?.headers);
|
|
3259
|
+
|
|
3260
|
+
// Auto-forward cookie and authorization from the outer request via ALS
|
|
3261
|
+
const store = requestContext.getStore();
|
|
3262
|
+
if (store?.request) {
|
|
3263
|
+
const outerReq = store.request;
|
|
3264
|
+
if (!headers.has("cookie")) {
|
|
3265
|
+
const cookie = outerReq.headers.get("cookie");
|
|
3266
|
+
if (cookie) headers.set("cookie", cookie);
|
|
3267
|
+
}
|
|
3268
|
+
if (!headers.has("authorization")) {
|
|
3269
|
+
const auth = outerReq.headers.get("authorization");
|
|
3270
|
+
if (auth) headers.set("authorization", auth);
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
if (options?.body != null && !headers.has("content-type")) {
|
|
3275
|
+
headers.set("content-type", "application/json");
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3278
|
+
const request = new Request("http://worker.local" + url, {
|
|
3279
|
+
method,
|
|
3280
|
+
headers,
|
|
3281
|
+
body: options?.body != null ? JSON.stringify(options.body) : undefined,
|
|
3282
|
+
});
|
|
3283
|
+
|
|
3284
|
+
// Use real executionCtx from ALS if available, otherwise provide a no-op fallback
|
|
3285
|
+
const executionCtx = store?.executionCtx ?? { waitUntil() {}, passThroughOnException() {} };
|
|
3286
|
+
const response = await app.fetch(request, env, executionCtx);
|
|
3287
|
+
|
|
3288
|
+
if (response.status === 204) return undefined;
|
|
3289
|
+
|
|
3290
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
3291
|
+
const data = contentType.includes("application/json") ? await response.json() : await response.text();
|
|
3292
|
+
|
|
3293
|
+
if (!response.ok) {
|
|
3294
|
+
throw new FetchError(response.status, response.statusText, data);
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
return data;
|
|
3298
|
+
}
|
|
3299
|
+
`;
|
|
3300
|
+
}
|
|
3301
|
+
//#endregion
|
|
3302
|
+
//#region src/plugin-framework-entry.ts
|
|
3303
|
+
/**
|
|
3304
|
+
* Resolve the server entry module for a Class A framework. We always wrap
|
|
3305
|
+
* the framework's own entry with a tiny shim that intercepts
|
|
3306
|
+
* `/__void/{scheduled,queue}` in dev so cron and queue handlers run with
|
|
3307
|
+
* the worker's full CF bindings. The interception block is gated on
|
|
3308
|
+
* `import.meta.env.DEV` and the dispatch module is loaded via dynamic
|
|
3309
|
+
* `import()` so the entire dev path (including user cron imports) is
|
|
3310
|
+
* dead-code-eliminated in `vite build`. Production deploys go through
|
|
3311
|
+
* `cli/wrapper.ts` instead.
|
|
3312
|
+
*/
|
|
3313
|
+
function getFrameworkEntry(framework, root, devTriggerToken, notFound) {
|
|
3314
|
+
const isBuild = isViteBuildOrPreview();
|
|
3315
|
+
if (!isBuild) writeFrameworkTriggersDispatch(root, devTriggerToken);
|
|
3316
|
+
return generateClassATriggerWrapper(root, framework, getOriginalFrameworkEntry(framework, root), isBuild, void 0, notFound);
|
|
3317
|
+
}
|
|
3318
|
+
/** The framework's own server entry, before our trigger wrapper. */
|
|
3319
|
+
function getOriginalFrameworkEntry(framework, root) {
|
|
3320
|
+
switch (framework) {
|
|
3321
|
+
case "tanstack-start": return "@tanstack/react-start/server-entry";
|
|
3322
|
+
case "react-router": return generateReactRouterEntry(root);
|
|
3323
|
+
case "vinext-app": return "vinext/server/app-router-entry";
|
|
3324
|
+
case "vinext-pages": return generateVinextPagesEntry(root);
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
/**
|
|
3328
|
+
* Generate `<root>/.void/<framework>-trigger-wrapper.ts`. This file becomes
|
|
3329
|
+
* `workerConfig.main` so the dev miniflare/workerd evaluates *our* fetch
|
|
3330
|
+
* first, dispatches `/__void/{scheduled,queue}` if it matches, and otherwise
|
|
3331
|
+
* delegates to the framework's original entry.
|
|
3332
|
+
*
|
|
3333
|
+
* We emit two shapes of the wrapper depending on the Vite command:
|
|
3334
|
+
*
|
|
3335
|
+
* - Serve: dispatch is dynamically imported and gets a chance to handle
|
|
3336
|
+
* `/__void/{scheduled,queue}` before falling through to the framework.
|
|
3337
|
+
* - Build: dispatch is omitted entirely. The wrapper just delegates fetch.
|
|
3338
|
+
*
|
|
3339
|
+
* We can't simply gate the dynamic import on `import.meta.env.DEV` and
|
|
3340
|
+
* trust DCE: rolldown emits chunks for dynamic imports during chunk
|
|
3341
|
+
* splitting, *before* constant folding removes the dead branch, so the
|
|
3342
|
+
* dispatch file (and its transitive user-cron/queue imports + dev token)
|
|
3343
|
+
* would still ship in the production bundle.
|
|
3344
|
+
*/
|
|
3345
|
+
function generateClassATriggerWrapper(root, framework, original, isBuild, assetsDir = "assets", notFound) {
|
|
3346
|
+
const outDir = join(root, ".void");
|
|
3347
|
+
mkdirSync(outDir, { recursive: true });
|
|
3348
|
+
const wrapperPath = join(outDir, `${framework}-trigger-wrapper.ts`);
|
|
3349
|
+
const body = isBuild ? `// @ts-nocheck — Auto-generated by Void for vite build. Do NOT edit.
|
|
3350
|
+
import original from ${JSON.stringify(original)};
|
|
3351
|
+
|
|
3352
|
+
export default {
|
|
3353
|
+
...original,
|
|
3354
|
+
async fetch(request, env, ctx) {
|
|
3355
|
+
return serveWithAssets(request, env, await original.fetch(request, env, ctx));
|
|
3356
|
+
},
|
|
3357
|
+
};
|
|
3358
|
+
${compileAssetFallbackHelper(assetsDir, false, notFound)}
|
|
3359
|
+
` : `// @ts-nocheck — Auto-generated by Void in dev. Do NOT edit.
|
|
3360
|
+
import original from ${JSON.stringify(original)};
|
|
3361
|
+
|
|
3362
|
+
export default {
|
|
3363
|
+
...original,
|
|
3364
|
+
async fetch(request, env, ctx) {
|
|
3365
|
+
if (request.method === "POST") {
|
|
3366
|
+
const pathname = new URL(request.url).pathname;
|
|
3367
|
+
if (pathname === "/__void/scheduled" || pathname === "/__void/queue") {
|
|
3368
|
+
const { handleDevTrigger } = await import(${JSON.stringify(frameworkTriggersDispatchPath(root))});
|
|
3369
|
+
const response = await handleDevTrigger(request, env, ctx);
|
|
3370
|
+
if (response) return response;
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
return original.fetch(request, env, ctx);
|
|
3374
|
+
},
|
|
3375
|
+
};
|
|
3376
|
+
`;
|
|
3377
|
+
writeFileIfChanged(wrapperPath, body);
|
|
3378
|
+
return wrapperPath;
|
|
3379
|
+
}
|
|
3380
|
+
/** Generate a worker entry for vinext Pages Router projects. */
|
|
3381
|
+
function generateVinextPagesEntry(root) {
|
|
3382
|
+
const outDir = join(root, ".void");
|
|
3383
|
+
mkdirSync(outDir, { recursive: true });
|
|
3384
|
+
const entryPath = join(outDir, "vinext-pages-entry.ts");
|
|
3385
|
+
writeFileIfChanged(entryPath, `// Auto-generated by Void — vinext Pages Router worker entry
|
|
3386
|
+
// @ts-expect-error virtual module resolved by vinext at build time
|
|
3387
|
+
import { renderPage, handleApiRoute } from "virtual:vinext-server-entry";
|
|
3388
|
+
|
|
3389
|
+
export default {
|
|
3390
|
+
async fetch(request: Request): Promise<Response> {
|
|
3391
|
+
const url = new URL(request.url);
|
|
3392
|
+
const pathname = url.pathname;
|
|
3393
|
+
if (pathname.replaceAll("\\\\", "/").startsWith("//")) {
|
|
3394
|
+
return new Response("404 Not Found", { status: 404 });
|
|
3395
|
+
}
|
|
3396
|
+
const urlWithQuery = pathname + url.search;
|
|
3397
|
+
if (pathname.startsWith("/api/") || pathname === "/api") {
|
|
3398
|
+
return await handleApiRoute(request, urlWithQuery);
|
|
3399
|
+
}
|
|
3400
|
+
return await renderPage(request, urlWithQuery, null);
|
|
3401
|
+
},
|
|
3402
|
+
};
|
|
3403
|
+
`);
|
|
3404
|
+
return entryPath;
|
|
3405
|
+
}
|
|
3406
|
+
/** Generate a worker entry for React Router v7 projects. */
|
|
3407
|
+
function generateReactRouterEntry(root) {
|
|
3408
|
+
const outDir = join(root, ".void");
|
|
3409
|
+
mkdirSync(outDir, { recursive: true });
|
|
3410
|
+
const entryPath = join(outDir, "react-router-entry.ts");
|
|
3411
|
+
writeFileIfChanged(entryPath, `// Auto-generated by Void — React Router v7 worker entry
|
|
3412
|
+
import { createRequestHandler } from "react-router";
|
|
3413
|
+
|
|
3414
|
+
const requestHandler = createRequestHandler(
|
|
3415
|
+
// @ts-expect-error virtual module resolved by @react-router/dev at build time
|
|
3416
|
+
() => import("virtual:react-router/server-build"),
|
|
3417
|
+
import.meta.env.MODE,
|
|
3418
|
+
);
|
|
3419
|
+
|
|
3420
|
+
export default {
|
|
3421
|
+
async fetch(request: Request): Promise<Response> {
|
|
3422
|
+
return requestHandler(request);
|
|
3423
|
+
},
|
|
3424
|
+
};
|
|
3425
|
+
`);
|
|
3426
|
+
return entryPath;
|
|
3427
|
+
}
|
|
3428
|
+
//#endregion
|
|
3429
|
+
//#region src/plugin-migration.ts
|
|
3430
|
+
function readDatabaseUrl(root) {
|
|
3431
|
+
return loadLocalEnv(root).DATABASE_URL;
|
|
3432
|
+
}
|
|
3433
|
+
/**
|
|
3434
|
+
* Vite plugin that runs local migrations on dev server startup.
|
|
3435
|
+
* For SQLite/D1: applies migrations to the local miniflare database.
|
|
3436
|
+
* For PostgreSQL/MySQL: connects via DATABASE_URL from .env.
|
|
3437
|
+
*/
|
|
3438
|
+
function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join(root, ".void"), auth) {
|
|
3439
|
+
return {
|
|
3440
|
+
name: "void:migrations",
|
|
3441
|
+
apply: "serve",
|
|
3442
|
+
async configureServer(server) {
|
|
3443
|
+
const migrationsDir = readProjectPaths(root).migrationsDir;
|
|
3444
|
+
const hasSqlMigrations = existsSync(migrationsDir);
|
|
3445
|
+
if (!hasSqlMigrations && !auth?.enabled) return;
|
|
3446
|
+
try {
|
|
3447
|
+
if (hasSqlMigrations && dialect !== "sqlite") {
|
|
3448
|
+
const url = readDatabaseUrl(root);
|
|
3449
|
+
if (!url) {
|
|
3450
|
+
const warn = () => {
|
|
3451
|
+
console.log();
|
|
3452
|
+
voidError(`DATABASE_URL not found in .env — skipping ${dialect === "mysql" ? "MySQL" : "PostgreSQL"} migrations.`);
|
|
3453
|
+
};
|
|
3454
|
+
if (server.httpServer) server.httpServer.once("listening", () => setTimeout(warn, 100));
|
|
3455
|
+
else warn();
|
|
3456
|
+
return;
|
|
3457
|
+
}
|
|
3458
|
+
const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
|
|
3459
|
+
const { validateMigrations } = await import("./validate-pna7R3Pv.mjs");
|
|
3460
|
+
const allMigrations = collectMigrations(migrationsDir);
|
|
3461
|
+
const appliedNames = dialect === "mysql" ? await import("./runner-mysql-CEG5JozK.mjs").then((runner) => runner.getMySqlAppliedMigrations(url)) : await import("./runner-pg-Bh4AgZdj.mjs").then((runner) => runner.getPgAppliedMigrations(url));
|
|
3462
|
+
const appliedSet = new Set(appliedNames);
|
|
3463
|
+
const pending = allMigrations.filter((migration) => !appliedSet.has(migration.name));
|
|
3464
|
+
if (pending.length > 0) try {
|
|
3465
|
+
validateMigrations(pending);
|
|
3466
|
+
} catch (err) {
|
|
3467
|
+
voidWarn(err instanceof Error ? err.message : String(err));
|
|
3468
|
+
}
|
|
3469
|
+
const applied = dialect === "mysql" ? await import("./runner-mysql-CEG5JozK.mjs").then((runner) => runner.runMySqlMigrations(migrationsDir, url)) : await import("./runner-pg-Bh4AgZdj.mjs").then((runner) => runner.runPgMigrations(migrationsDir, url));
|
|
3470
|
+
if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
|
|
3471
|
+
} else if (hasSqlMigrations) {
|
|
3472
|
+
const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-voIltZrY.mjs");
|
|
3473
|
+
const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
|
|
3474
|
+
const { validateMigrations } = await import("./validate-pna7R3Pv.mjs");
|
|
3475
|
+
const dbPath = getMiniflareDatabasePath(persistRoot, databaseId);
|
|
3476
|
+
const allMigrations = collectMigrations(migrationsDir);
|
|
3477
|
+
const appliedRows = await getAppliedMigrations(dbPath);
|
|
3478
|
+
const appliedSet = new Set(appliedRows.map((r) => r.name));
|
|
3479
|
+
const pending = allMigrations.filter((m) => !appliedSet.has(m.name));
|
|
3480
|
+
if (pending.length > 0) try {
|
|
3481
|
+
validateMigrations(pending);
|
|
3482
|
+
} catch (err) {
|
|
3483
|
+
voidWarn(err instanceof Error ? err.message : String(err));
|
|
3484
|
+
}
|
|
3485
|
+
const applied = await runMigrations(migrationsDir, dbPath);
|
|
3486
|
+
if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
|
|
3487
|
+
}
|
|
3488
|
+
if (auth?.enabled) {
|
|
3489
|
+
const { runLocalVoidAuthMigrations } = await import("./node-C6Q6e1Es.mjs");
|
|
3490
|
+
await runLocalVoidAuthMigrations(root, dialect, persistRoot, databaseId, auth.configPath);
|
|
3491
|
+
voidLog("Applied Better Auth migrations");
|
|
3492
|
+
}
|
|
3493
|
+
} catch (err) {
|
|
3494
|
+
voidError(`Migration error: ${err}`);
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
};
|
|
3498
|
+
}
|
|
3499
|
+
//#endregion
|
|
3500
|
+
//#region src/plugin-worker-environment.ts
|
|
3501
|
+
const CLOUDFLARE_BUILTIN_IMPORT = /^cloudflare:/;
|
|
3502
|
+
const SANDBOX_SDK_IMPORT = "@cloudflare/sandbox";
|
|
3503
|
+
const SANDBOX_SDK_IMPORT_FILTER = /^@cloudflare\/sandbox$/;
|
|
3504
|
+
const DEFAULT_WORKER_ENVIRONMENT_NAME = "void_worker";
|
|
3505
|
+
const DEFAULT_WORKER_OUT_DIR = "dist/ssr";
|
|
3506
|
+
const DEV_ASSET_ROUTING_EXCLUSIONS = [
|
|
3507
|
+
"!/@vite/*",
|
|
3508
|
+
"!/@id/*",
|
|
3509
|
+
"!/@fs/*",
|
|
3510
|
+
"!/@react-refresh",
|
|
3511
|
+
"!/@svelte/*",
|
|
3512
|
+
"!/node_modules/*",
|
|
3513
|
+
"!/__uno.css"
|
|
3514
|
+
];
|
|
3515
|
+
function resolveSandboxSdkEntry() {
|
|
3516
|
+
return fileURLToPath(import.meta.resolve(SANDBOX_SDK_IMPORT));
|
|
3517
|
+
}
|
|
3518
|
+
function createCloudflareBuiltinsExternalPlugin() {
|
|
3519
|
+
return {
|
|
3520
|
+
name: "void:cloudflare-builtins-external",
|
|
3521
|
+
config(_, env) {
|
|
3522
|
+
if (env.command !== "build") return;
|
|
3523
|
+
return { build: { rollupOptions: { external: [CLOUDFLARE_BUILTIN_IMPORT] } } };
|
|
2848
3524
|
}
|
|
2849
|
-
|
|
2850
|
-
} else {
|
|
2851
|
-
lines.push("export async function __voidHandleInternalRoute() { return null; }");
|
|
2852
|
-
lines.push("");
|
|
2853
|
-
}
|
|
2854
|
-
if (options.bindingHandlerPath) {
|
|
2855
|
-
lines.push(`import { createBindingHandler as __createBindingHandler } from ${JSON.stringify(options.bindingHandlerPath)};`);
|
|
2856
|
-
lines.push("let __voidBindingApp;");
|
|
2857
|
-
lines.push("app.all(\"/__void/*\", async (c) => {");
|
|
2858
|
-
lines.push(" if (!__voidBindingApp) __voidBindingApp = __createBindingHandler(__getRawEnv());");
|
|
2859
|
-
lines.push(" const url = new URL(c.req.url);");
|
|
2860
|
-
lines.push(" url.pathname = url.pathname.slice(7);");
|
|
2861
|
-
lines.push(" return __voidBindingApp.fetch(new Request(url.toString(), c.req.raw));");
|
|
2862
|
-
lines.push("});");
|
|
2863
|
-
lines.push("");
|
|
2864
|
-
}
|
|
2865
|
-
lines.push("const __rawEnvStore = new AsyncLocalStorage();");
|
|
2866
|
-
lines.push("export function __withRawEnv(env, fn) { return __rawEnvStore.run(env, fn); }");
|
|
2867
|
-
lines.push("function __getRawEnv() { return __rawEnvStore.getStore(); }");
|
|
2868
|
-
lines.push(`export const __VOID_BUILD_KEY = ${JSON.stringify(crypto.randomUUID())};`);
|
|
2869
|
-
lines.push(`export const __VOID_DEV_TRIGGER_TOKEN = ${JSON.stringify(options.devTriggerToken ?? crypto.randomUUID())};`);
|
|
2870
|
-
lines.push(`export const __VOID_ISR_REVALIDATE = ${JSON.stringify(mergedRevalidate)};`);
|
|
2871
|
-
lines.push(`export const __VOID_ISR_PAGES = ${JSON.stringify(isrPages)};`);
|
|
2872
|
-
lines.push(`export const __VOID_ISR_PATH_REVALIDATE = ${JSON.stringify(isrPathRevalidate)};`);
|
|
2873
|
-
lines.push(`export const __VOID_ISR_DEPLOYMENT_ID = ${JSON.stringify(crypto.randomUUID())};`);
|
|
2874
|
-
lines.push("");
|
|
2875
|
-
lines.push(`export default app;`);
|
|
2876
|
-
return lines.join("\n");
|
|
3525
|
+
};
|
|
2877
3526
|
}
|
|
2878
|
-
function
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
lines.push(` return withRuntimeEnv(env, async () => {`);
|
|
2893
|
-
if (options.nodeTarget) lines.push(` return requestContext.run({ request }, async () => {`);
|
|
2894
|
-
else lines.push(` return requestContext.run({ request, executionCtx: ctx }, async () => {`);
|
|
2895
|
-
lines.push(` const paths = [];`);
|
|
2896
|
-
for (const path of staticPaths) lines.push(` paths.push(${JSON.stringify(path)});`);
|
|
2897
|
-
for (const page of dynamicPages) {
|
|
2898
|
-
const loaderName = `_pload_${page.componentId.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
2899
|
-
lines.push(` {`);
|
|
2900
|
-
lines.push(` const mod = await ${loaderName}();`);
|
|
2901
|
-
lines.push(` if (typeof mod.getPrerenderPaths === "function") {`);
|
|
2902
|
-
lines.push(` const paramSets = await mod.getPrerenderPaths();`);
|
|
2903
|
-
lines.push(` if (Array.isArray(paramSets)) {`);
|
|
2904
|
-
lines.push(` for (const params of paramSets) {`);
|
|
2905
|
-
lines.push(` paths.push(__voidBuildPrerenderPath(${JSON.stringify(page.pattern)}, params));`);
|
|
2906
|
-
lines.push(` }`);
|
|
2907
|
-
lines.push(` }`);
|
|
2908
|
-
lines.push(` }`);
|
|
2909
|
-
lines.push(` }`);
|
|
2910
|
-
}
|
|
2911
|
-
if (ssr) {
|
|
2912
|
-
lines.push(` {`);
|
|
2913
|
-
lines.push(` const mod = await import(${JSON.stringify(ssr.serverEntryPath)});`);
|
|
2914
|
-
lines.push(` if (typeof mod.getPrerenderPaths === "function") {`);
|
|
2915
|
-
lines.push(` const ssrPaths = await mod.getPrerenderPaths();`);
|
|
2916
|
-
lines.push(` if (Array.isArray(ssrPaths)) {`);
|
|
2917
|
-
lines.push(` for (const path of ssrPaths) {`);
|
|
2918
|
-
lines.push(` if (typeof path === "string") paths.push(path);`);
|
|
2919
|
-
lines.push(` }`);
|
|
2920
|
-
lines.push(` }`);
|
|
2921
|
-
lines.push(` }`);
|
|
2922
|
-
lines.push(` }`);
|
|
2923
|
-
}
|
|
2924
|
-
for (const path of explicitPaths) lines.push(` paths.push(${JSON.stringify(path)});`);
|
|
2925
|
-
lines.push(` return [...new Set(paths)];`);
|
|
2926
|
-
lines.push(` });`);
|
|
2927
|
-
lines.push(` });`);
|
|
2928
|
-
lines.push(`}`);
|
|
2929
|
-
lines.push("");
|
|
3527
|
+
function createDefaultWorkerEnvironmentPlugin() {
|
|
3528
|
+
return {
|
|
3529
|
+
name: "void:default-worker-environment",
|
|
3530
|
+
config(config) {
|
|
3531
|
+
const currentEnvironment = config.environments?.[DEFAULT_WORKER_ENVIRONMENT_NAME];
|
|
3532
|
+
return { environments: { [DEFAULT_WORKER_ENVIRONMENT_NAME]: {
|
|
3533
|
+
...currentEnvironment,
|
|
3534
|
+
build: {
|
|
3535
|
+
...currentEnvironment?.build,
|
|
3536
|
+
outDir: currentEnvironment?.build?.outDir ?? DEFAULT_WORKER_OUT_DIR
|
|
3537
|
+
}
|
|
3538
|
+
} } };
|
|
3539
|
+
}
|
|
3540
|
+
};
|
|
2930
3541
|
}
|
|
2931
|
-
function
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
lines.push(`}`);
|
|
2941
|
-
lines.push("");
|
|
2942
|
-
lines.push(`const __voidHeadConfig = ${JSON.stringify(pages.headConfig ?? null)};`);
|
|
2943
|
-
lines.push("");
|
|
2944
|
-
pushDefaultErrorPageHelpers(lines, options);
|
|
2945
|
-
const relPagesDir = pages.pagesDirRelative;
|
|
2946
|
-
const pageCssKeys = {};
|
|
2947
|
-
const pageDevRootPhases = {};
|
|
2948
|
-
const projectRoot = options.projectRoot;
|
|
2949
|
-
const toDevRootUrl = (absolutePath) => "/" + absolutePath.replaceAll("\\", "/").slice(projectRoot.replaceAll("\\", "/").length + 1);
|
|
2950
|
-
const virtualDevUrl = (id) => "/@id/__x00__" + id;
|
|
2951
|
-
for (const page of scan.pages.pages) {
|
|
2952
|
-
const keys = [];
|
|
2953
|
-
const routeRootPhases = [[virtualDevUrl(page.island ? pages.islandClientEntryId ?? pages.clientEntryId : pages.clientEntryId)]];
|
|
2954
|
-
if (page.island && pages.mdClientEntryId) routeRootPhases[0].push(virtualDevUrl(pages.mdClientEntryId));
|
|
2955
|
-
const layoutIds = resolveLayoutIds(page, scan.pages.layouts, scan.pages.namedLayouts);
|
|
2956
|
-
const layoutRoots = [];
|
|
2957
|
-
for (const layoutId of layoutIds) {
|
|
2958
|
-
const layout = scan.pages.layouts.find((l) => (l.directory ? `${l.directory}/layout` : "layout") === layoutId);
|
|
2959
|
-
if (layout) {
|
|
2960
|
-
keys.push(`${relPagesDir}/${layout.filePath}`);
|
|
2961
|
-
layoutRoots.push(toDevRootUrl(`${pages.pagesDir}/${layout.filePath}`));
|
|
2962
|
-
continue;
|
|
3542
|
+
function createWorkerEnvironmentSanitizerPlugin(environmentNames) {
|
|
3543
|
+
const names = new Set(environmentNames);
|
|
3544
|
+
return {
|
|
3545
|
+
name: "void:worker-environment-sanitizer",
|
|
3546
|
+
enforce: "pre",
|
|
3547
|
+
configResolved(config) {
|
|
3548
|
+
for (const name of names) {
|
|
3549
|
+
const resolve = config.environments[name]?.resolve;
|
|
3550
|
+
if (resolve && "external" in resolve) resolve.external = [];
|
|
2963
3551
|
}
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
3552
|
+
}
|
|
3553
|
+
};
|
|
3554
|
+
}
|
|
3555
|
+
function createSandboxSdkResolvePlugin() {
|
|
3556
|
+
let resolved;
|
|
3557
|
+
return {
|
|
3558
|
+
name: "void:sandbox-sdk-resolve",
|
|
3559
|
+
enforce: "pre",
|
|
3560
|
+
resolveId: {
|
|
3561
|
+
filter: { id: SANDBOX_SDK_IMPORT_FILTER },
|
|
3562
|
+
handler(id) {
|
|
3563
|
+
if (id !== SANDBOX_SDK_IMPORT) return;
|
|
3564
|
+
resolved ??= resolveSandboxSdkEntry();
|
|
3565
|
+
return resolved;
|
|
2970
3566
|
}
|
|
2971
3567
|
}
|
|
2972
|
-
keys.push(`${relPagesDir}/${page.componentPath}`);
|
|
2973
|
-
routeRootPhases.push(layoutRoots);
|
|
2974
|
-
routeRootPhases.push([toDevRootUrl(`${pages.pagesDir}/${page.componentPath}`)]);
|
|
2975
|
-
pageCssKeys[page.componentId] = keys;
|
|
2976
|
-
pageDevRootPhases[page.componentId] = routeRootPhases.filter((phase) => phase.length > 0);
|
|
2977
|
-
}
|
|
2978
|
-
lines.push("const __loadPagesManifest = __createManifestLoader();");
|
|
2979
|
-
const assetMetadata = {
|
|
2980
|
-
pageCssKeys,
|
|
2981
|
-
devRootPhases: options.dev ? pageDevRootPhases : void 0,
|
|
2982
|
-
dev: options.dev === true
|
|
2983
3568
|
};
|
|
2984
|
-
const devCssOption = options.dev ? ", devCss: getVoidPagesDevCss" : "";
|
|
2985
|
-
lines.push(`const getPagesAssetTags = __createClientAssetTags({ ...${JSON.stringify({
|
|
2986
|
-
...assetMetadata,
|
|
2987
|
-
entryId: pages.clientEntryId,
|
|
2988
|
-
entryUrl: "/@id/__x00__" + pages.clientEntryId,
|
|
2989
|
-
kind: "pages"
|
|
2990
|
-
})}${devCssOption} }, __loadPagesManifest);`);
|
|
2991
|
-
if (hasIslandPages && pages.islandClientEntryId) lines.push(`const getIslandsAssetTags = __createClientAssetTags({ ...${JSON.stringify({
|
|
2992
|
-
...assetMetadata,
|
|
2993
|
-
entryId: pages.islandClientEntryId,
|
|
2994
|
-
entryUrl: "/@id/__x00__" + pages.islandClientEntryId,
|
|
2995
|
-
extraEntryId: pages.mdClientEntryId,
|
|
2996
|
-
extraEntryUrl: pages.mdClientEntryId ? "/@id/__x00__" + pages.mdClientEntryId : void 0,
|
|
2997
|
-
kind: "islands"
|
|
2998
|
-
})}${devCssOption} }, __loadPagesManifest);`);
|
|
2999
|
-
const seenServerFiles = /* @__PURE__ */ new Set();
|
|
3000
|
-
for (const page of scan.pages.pages) {
|
|
3001
|
-
const safeId = page.componentId.replace(/[^a-zA-Z0-9]/g, "_");
|
|
3002
|
-
let serverImportPath = null;
|
|
3003
|
-
if (page.serverPath) serverImportPath = `${pages.pagesDir}/${page.serverPath}`;
|
|
3004
|
-
if (serverImportPath && !seenServerFiles.has(serverImportPath)) {
|
|
3005
|
-
seenServerFiles.add(serverImportPath);
|
|
3006
|
-
const loaderName = `_pload_${safeId}`;
|
|
3007
|
-
lines.push(`const ${loaderName} = __createModuleLoader(() => import(${JSON.stringify(serverImportPath)}));`);
|
|
3008
|
-
}
|
|
3009
|
-
let mdHeadRef = null;
|
|
3010
|
-
if (!serverImportPath && page.metadata && (page.metadata.title || page.metadata.description)) {
|
|
3011
|
-
const headObj = {};
|
|
3012
|
-
if (page.metadata.title) headObj.title = page.metadata.title;
|
|
3013
|
-
if (page.metadata.description) headObj.meta = [{
|
|
3014
|
-
name: "description",
|
|
3015
|
-
content: page.metadata.description
|
|
3016
|
-
}];
|
|
3017
|
-
const headVarName = `__mdHead_${safeId}`;
|
|
3018
|
-
lines.push(`const ${headVarName} = () => (${JSON.stringify(headObj)});`);
|
|
3019
|
-
mdHeadRef = headVarName;
|
|
3020
|
-
}
|
|
3021
|
-
const renderer = page.renderMode === "island" ? "renderIslandPage" : "renderPage";
|
|
3022
|
-
const assetTags = page.renderMode === "island" && pages.islandClientEntryId ? "getIslandsAssetTags" : "getPagesAssetTags";
|
|
3023
|
-
const routeOptions = `{ ${serverImportPath ? "load: _pload_" + safeId + ", " : ""}${mdHeadRef ? "head: " + mdHeadRef + ", " : ""}componentId: ${JSON.stringify(page.componentId)}, render: ${renderer}, assets: ${assetTags}, headConfig: __voidHeadConfig, renderOptions: ${JSON.stringify({ renderMode: page.renderMode })} }`;
|
|
3024
|
-
lines.push(`app.get(${JSON.stringify(page.pattern)}, __createPageGetHandler(${routeOptions}));`);
|
|
3025
|
-
if (page.methods.includes("action") || page.methods.includes("actions")) lines.push(`app.on(["POST", "PUT", "PATCH", "DELETE"], ${JSON.stringify(page.pattern)}, __createPageMutationHandler({ ...${routeOptions}, namedActions: ${JSON.stringify(page.methods.includes("actions"))} }));`);
|
|
3026
|
-
}
|
|
3027
|
-
lines.push("");
|
|
3028
3569
|
}
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
function compileAssetFallbackHelper(assetsDir = "assets", serveSpaFallback = false, notFound = "none") {
|
|
3046
|
-
return `import { createAssetFallback as __createAssetFallback } from ${JSON.stringify(ROUTING_RUNTIME_PATH)};
|
|
3047
|
-
const serveWithAssets = __createAssetFallback(${JSON.stringify({
|
|
3048
|
-
assetsDir,
|
|
3049
|
-
serveSpaFallback,
|
|
3050
|
-
notFound
|
|
3051
|
-
})});
|
|
3052
|
-
`;
|
|
3570
|
+
function createWranglerCompatPersistPlugin(root, config) {
|
|
3571
|
+
let persisted = false;
|
|
3572
|
+
const persist = () => {
|
|
3573
|
+
if (persisted || isNodeTarget(config.target)) return;
|
|
3574
|
+
persisted = true;
|
|
3575
|
+
ensureWranglerCompat(root, config);
|
|
3576
|
+
};
|
|
3577
|
+
return {
|
|
3578
|
+
name: "void:wrangler-compat-persist",
|
|
3579
|
+
buildStart() {
|
|
3580
|
+
persist();
|
|
3581
|
+
},
|
|
3582
|
+
configureServer() {
|
|
3583
|
+
persist();
|
|
3584
|
+
}
|
|
3585
|
+
};
|
|
3053
3586
|
}
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
* serializes the native MessageBatch and dispatches it to the
|
|
3062
|
-
* `/__void/queue` Hono route. In local dev Miniflare delivers batches
|
|
3063
|
-
* natively to this export; in production the relay worker does it.
|
|
3064
|
-
*
|
|
3065
|
-
* When `remotePath` is provided, wraps the fetch handler to detect
|
|
3066
|
-
* remote mode (`__VOID_REMOTE`) and replace local bindings with
|
|
3067
|
-
* proxy-backed versions.
|
|
3068
|
-
*/
|
|
3069
|
-
function deriveEmailRuntimePath(runtimeEnvPath) {
|
|
3070
|
-
return runtimeEnvPath.replace(/_?env(\.[^/]+)?$/, (_m, ext) => "email" + (ext ?? ""));
|
|
3587
|
+
function createServerRuntimeOptimizerPlugin() {
|
|
3588
|
+
return {
|
|
3589
|
+
name: "void:server-runtime-optimizer",
|
|
3590
|
+
configEnvironment(name) {
|
|
3591
|
+
if (name !== "client") return { optimizeDeps: { exclude: ["void"] } };
|
|
3592
|
+
}
|
|
3593
|
+
};
|
|
3071
3594
|
}
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
const
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
const
|
|
3083
|
-
const
|
|
3084
|
-
|
|
3085
|
-
const
|
|
3086
|
-
const
|
|
3087
|
-
const
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
const runtimeEnvImport = `import { withRuntimeEnv } from ${JSON.stringify(runtimeEnvPath)};\n`;
|
|
3093
|
-
const remoteImport = remote ? `import { createRemoteEnv } from ${JSON.stringify(remote.remotePath)};\n` : "";
|
|
3094
|
-
const envSchemaImport = envSchemaModule ? `import ${JSON.stringify(envSchemaModule.importPath)};\nimport { _validateEnvOnce } from ${JSON.stringify(envSchemaModule.helperPath)};\n` : "";
|
|
3095
|
-
const envValidateCall = envSchemaModule ? " await _validateEnvOnce(env);\n" : "";
|
|
3096
|
-
const exactNames = isr ? INTERNAL_BINDING_EXACT_NAMES : [];
|
|
3097
|
-
const filterBindingsHelper = `import { filterInternalBindings as __filterBindings, createWorkerFetch as __createWorkerFetch } from ${JSON.stringify(ROUTING_RUNTIME_PATH)};
|
|
3098
|
-
const __filterInternalBindings = (env) => __filterBindings(env, ${JSON.stringify(INTERNAL_BINDING_PREFIXES)}, ${JSON.stringify(exactNames)});
|
|
3099
|
-
`;
|
|
3100
|
-
const assetHelper = compileAssetFallbackHelper(assetsDir, serveSpaFallback, notFound);
|
|
3101
|
-
const isrImport = isr ? `import { handleIsr } from ${JSON.stringify(isr.runtimePath)};\nimport { __VOID_ISR_REVALIDATE, __VOID_ISR_PAGES, __VOID_ISR_PATH_REVALIDATE, __VOID_ISR_DEPLOYMENT_ID } from 'virtual:void-routes';\n` : "";
|
|
3102
|
-
const isrDeploymentStamp = isr ? ` env.__VOID_ISR_DEPLOYMENT_ID ??= __VOID_ISR_DEPLOYMENT_ID;\n` : "";
|
|
3103
|
-
const fetchHandler = `__createWorkerFetch({
|
|
3104
|
-
app, assets: serveWithAssets, withRawEnv: __withRawEnv, withRuntimeEnv,
|
|
3105
|
-
filterBindings: __filterInternalBindings,
|
|
3106
|
-
internalRoute: __voidHandleInternalRoute,
|
|
3107
|
-
collectPrerenderPaths: __voidCollectPrerenderPaths${versionMetadataBinding ? ", versionMetadataBinding: " + JSON.stringify(versionMetadataBinding) : ""}${envSchemaModule ? ", validateEnv: _validateEnvOnce" : ""}${remotePath ? ", remoteEnv: (env) => " + createRemoteEnvCall : ""}${hasEmail ? ", emailReceipt: __handleEmailReceipt" : ""}${isr ? ", isr: { deploymentId: __VOID_ISR_DEPLOYMENT_ID, handle: (request, env, ctx, render) => handleIsr(request, env, ctx, { revalidate: __VOID_ISR_REVALIDATE, pages: __VOID_ISR_PAGES, pathRevalidate: __VOID_ISR_PATH_REVALIDATE, deploymentId: __VOID_ISR_DEPLOYMENT_ID, queryAllowlist: " + JSON.stringify(isr.queryAllowlist) + " }, render) }" : ""}
|
|
3108
|
-
})`;
|
|
3109
|
-
const scheduledHandler = remotePath ? `async function handleScheduled(controller, env, ctx) {
|
|
3110
|
-
${envValidateCall} if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};
|
|
3111
|
-
${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
|
|
3112
|
-
return withRuntimeEnv(env, () => scheduled(controller, userEnv, ctx));
|
|
3113
|
-
}` : `async function handleScheduled(controller, env, ctx) {
|
|
3114
|
-
${envValidateCall}${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
|
|
3115
|
-
return withRuntimeEnv(env, () => scheduled(controller, userEnv, ctx));
|
|
3116
|
-
}`;
|
|
3117
|
-
const emailImport = hasEmail ? ", __emailHandler" : "";
|
|
3118
|
-
const emailRuntimePath = runtimeEmailPath ?? deriveEmailRuntimePath(runtimeEnvPath);
|
|
3119
|
-
const emailProxyImport = hasEmail ? `import { createMessageProxy as __createMessageProxy, enactEmailActions as __enactEmailActions, isForwardableEmailMessage as __isForwardableEmailMessage } from ${JSON.stringify(emailRuntimePath)};\nimport { EMAIL_PROTOCOL_VERSION as __EMAIL_PROTOCOL_VERSION, encodeEmailActions as __encodeEmailActions, readEmailEnvelope as __readEmailEnvelope, verifyEmailReceiptThroughProxy as __verifyEmailReceiptThroughProxy } from "void/_email-protocol";\n` : "";
|
|
3120
|
-
const emailHandler = hasEmail ? remotePath ? `async function handleEmail(envelope, env, ctx) {
|
|
3121
|
-
${envValidateCall} if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};
|
|
3122
|
-
const userEnv = __filterInternalBindings(env);
|
|
3123
|
-
const __relayed = typeof envelope.viaDomain === "string" && envelope.viaDomain.length > 0;
|
|
3124
|
-
const __slug = __relayed ? undefined : (typeof env.__VOID_PROJECT_SLUG === "string" && env.__VOID_PROJECT_SLUG.length > 0 ? env.__VOID_PROJECT_SLUG : undefined);
|
|
3125
|
-
const { message, getActions } = __createMessageProxy(envelope, { slug: __slug });
|
|
3126
|
-
await __withRawEnv(env, () => withRuntimeEnv(env, () => __emailHandler(message, userEnv, ctx, { slug: __slug })));
|
|
3127
|
-
const __actions = getActions();
|
|
3128
|
-
if (__isForwardableEmailMessage(envelope)) await __enactEmailActions(envelope, __actions);
|
|
3129
|
-
return __actions;
|
|
3130
|
-
}` : `async function handleEmail(envelope, env, ctx) {
|
|
3131
|
-
${envValidateCall} const userEnv = __filterInternalBindings(env);
|
|
3132
|
-
const __relayed = typeof envelope.viaDomain === "string" && envelope.viaDomain.length > 0;
|
|
3133
|
-
const __slug = __relayed ? undefined : (typeof env.__VOID_PROJECT_SLUG === "string" && env.__VOID_PROJECT_SLUG.length > 0 ? env.__VOID_PROJECT_SLUG : undefined);
|
|
3134
|
-
const { message, getActions } = __createMessageProxy(envelope, { slug: __slug });
|
|
3135
|
-
await __withRawEnv(env, () => withRuntimeEnv(env, () => __emailHandler(message, userEnv, ctx, { slug: __slug })));
|
|
3136
|
-
const __actions = getActions();
|
|
3137
|
-
if (__isForwardableEmailMessage(envelope)) await __enactEmailActions(envelope, __actions);
|
|
3138
|
-
return __actions;
|
|
3139
|
-
}` : "";
|
|
3140
|
-
const emailReceiptHandler = hasEmail ? `async function __handleEmailReceipt(request, env, ctx) {
|
|
3141
|
-
if (request.method !== "POST" || request.headers.get("content-encoding")) return new Response(null, { status: 404 });
|
|
3142
|
-
const __token = request.headers.get("x-void-email-receipt");
|
|
3143
|
-
const __recipient = request.headers.get("x-void-email-recipient");
|
|
3144
|
-
if (!__token || __token.length > 1536 || !__recipient || __recipient.length > 320 ||
|
|
3145
|
-
!env.__VOID_PROXY_TOKEN || !env.__PROJECT_ID || !env.__VOID_EMAIL_DEPLOYMENT_ID || !env.__VOID_PROXY) {
|
|
3146
|
-
return new Response(null, { status: 404 });
|
|
3147
|
-
}
|
|
3148
|
-
const __verified = await __verifyEmailReceiptThroughProxy(env.__VOID_PROXY, env.__VOID_PROXY_TOKEN, {
|
|
3149
|
-
receipt: __token, projectId: env.__PROJECT_ID,
|
|
3150
|
-
deploymentId: env.__VOID_EMAIL_DEPLOYMENT_ID, recipient: __recipient,
|
|
3151
|
-
});
|
|
3152
|
-
if (!__verified) return new Response(null, { status: 404 });
|
|
3153
|
-
let __envelope;
|
|
3154
|
-
try { __envelope = await __readEmailEnvelope(request.body); }
|
|
3155
|
-
catch { return new Response(null, { status: 400 }); }
|
|
3156
|
-
if (__envelope.to !== __recipient) return new Response(null, { status: 400 });
|
|
3157
|
-
const __actions = await handleEmail(__envelope, env, ctx);
|
|
3158
|
-
return new Response(__encodeEmailActions(__actions), {
|
|
3159
|
-
headers: { "content-type": "application/vnd.void.email-actions; version=" + __EMAIL_PROTOCOL_VERSION,
|
|
3160
|
-
"cache-control": "no-store" },
|
|
3161
|
-
});
|
|
3162
|
-
}` : "";
|
|
3163
|
-
const entryImport = `import { WorkerEntrypoint } from "cloudflare:workers";\n`;
|
|
3164
|
-
const fetchDeclaration = `const __voidFetch = ${fetchHandler};`;
|
|
3165
|
-
const entryClass = `export default class extends WorkerEntrypoint {
|
|
3166
|
-
async fetch(request) { return __voidFetch(request, this.env, this.ctx); }
|
|
3167
|
-
async scheduled(controller) { return handleScheduled(controller, this.env, this.ctx); }
|
|
3168
|
-
${hasQueues ? ` async queue(batch) { return __voidQueue(batch, this.env, this.ctx); }\n` : ""}${hasEmail ? ` async email(message) {
|
|
3169
|
-
if (this.env.__VOID_EMAIL_DEPLOYMENT_ID || !__isForwardableEmailMessage(message)) {
|
|
3170
|
-
throw new TypeError("Native email event required");
|
|
3171
|
-
}
|
|
3172
|
-
await handleEmail(message, this.env, this.ctx);
|
|
3173
|
-
}\n` : ""}}`;
|
|
3174
|
-
if (hasQueues) return `${sandboxImport}${entryImport}${liveRuntimeImport}import app, { scheduled, __voidCollectPrerenderPaths, __voidHandleInternalRoute, __withRawEnv, __processQueue${emailImport}${webSocketClasses.length > 0 ? `, ${webSocketClasses.join(", ")}` : ""}${liveRouteImport} } from 'virtual:void-routes';
|
|
3175
|
-
${durableObjectImports}\n${runtimeEnvImport}${emailProxyImport}${envSchemaImport}${remoteImport}${isrImport}${filterBindingsHelper}${assetHelper}
|
|
3176
|
-
${fetchDeclaration}
|
|
3177
|
-
${scheduledHandler}
|
|
3178
|
-
${emailHandler}
|
|
3179
|
-
${emailReceiptHandler}
|
|
3180
|
-
async function __voidQueue(batch, env, ctx) {
|
|
3181
|
-
${envValidateCall}${remotePath ? ` if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};\n` : ""}${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
|
|
3182
|
-
const messages = [...batch.messages].map(m => ({
|
|
3183
|
-
id: m.id, timestamp: m.timestamp.toISOString(),
|
|
3184
|
-
body: m.body, attempts: m.attempts,
|
|
3185
|
-
}));
|
|
3186
|
-
const payload = { queue: batch.queue, messages };
|
|
3187
|
-
const __qr = await __withRawEnv(env, () => withRuntimeEnv(env, () => __processQueue(payload, userEnv)));
|
|
3188
|
-
if (__qr.decisions) {
|
|
3189
|
-
const __msgMap = new Map();
|
|
3190
|
-
for (const m of batch.messages) __msgMap.set(m.id, m);
|
|
3191
|
-
for (const [id, d] of Object.entries(__qr.decisions)) {
|
|
3192
|
-
const m = __msgMap.get(id);
|
|
3193
|
-
if (!m) continue;
|
|
3194
|
-
if (d.action === "ack") m.ack();
|
|
3195
|
-
else if (d.action === "retry") m.retry(d.delaySeconds != null ? { delaySeconds: d.delaySeconds } : undefined);
|
|
3196
|
-
}
|
|
3197
|
-
}
|
|
3198
|
-
if (!__qr.ok) throw new Error("queue: Handler failed.");
|
|
3595
|
+
//#endregion
|
|
3596
|
+
//#region src/sveltekit-hooks-transform.ts
|
|
3597
|
+
/** Add Void's dev bindings to SvelteKit's generated get_hooks function. */
|
|
3598
|
+
function injectSvelteKitHooks(code, id, dispatchPath) {
|
|
3599
|
+
if (!code.includes("get_hooks")) return;
|
|
3600
|
+
const hooks = (parseSync(id, code).program.body ?? []).filter((node) => node.type === "ExportNamedDeclaration").map((node) => node.declaration).filter((node) => {
|
|
3601
|
+
const name = node?.id;
|
|
3602
|
+
return node?.type === "FunctionDeclaration" && isIdentifier(name) && name.name === "get_hooks";
|
|
3603
|
+
});
|
|
3604
|
+
if (hooks.length !== 1) throw new Error("sveltekit: Expected one generated get_hooks function.");
|
|
3605
|
+
const body = hooks[0].body;
|
|
3606
|
+
const returns = (body?.body)?.filter((node) => node.type === "ReturnStatement" && node.argument?.type === "ObjectExpression") ?? [];
|
|
3607
|
+
if (body?.type !== "BlockStatement" || body.start === void 0 || returns.length !== 1 || returns[0]?.start === void 0) throw new Error("sveltekit: Expected one object return in generated get_hooks.");
|
|
3608
|
+
const ms = new MagicString(code);
|
|
3609
|
+
ms.appendLeft(body.start + 1, `\n const { withRuntimeEnv } = await import("void/_env");\n const { handleDevTrigger } = await import(${JSON.stringify(dispatchPath)});`);
|
|
3610
|
+
ms.appendLeft(returns[0].start, "const __origHandle = handle ?? (({ event, resolve }) => resolve(event));\n handle = async ({ event, resolve }) => {\n const __voidEnv = event.platform?.env ?? (await import('cloudflare:workers')).env;\n return withRuntimeEnv(__voidEnv, async () => {\n const __triggerResp = await handleDevTrigger(event.request, __voidEnv, undefined);\n if (__triggerResp) return __triggerResp;\n return __origHandle({ event, resolve });\n });\n };\n ");
|
|
3611
|
+
return {
|
|
3612
|
+
code: ms.toString(),
|
|
3613
|
+
map: ms.generateMap({ hires: true })
|
|
3614
|
+
};
|
|
3199
3615
|
}
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3616
|
+
//#endregion
|
|
3617
|
+
//#region src/plugin-framework.ts
|
|
3618
|
+
function createFrameworkPlugins(input) {
|
|
3619
|
+
const { localD1, root, directCloudflareDestination, bindings, config, dialect, application, bindingNames, websocketRoutes, sandboxConfig, liveConfig, detected, clientStubsPlugin, serverRuntimeOptimizerPlugin, authEnabled, devTriggerToken, implicitDeployGuard } = input;
|
|
3620
|
+
const frameworkPersistRoot = localD1.persistRoot;
|
|
3621
|
+
const syncFrameworkCompat = (compat) => {
|
|
3622
|
+
if (ensureWranglerCompatibilityDate(root, compat.compatibilityDate)) voidLog("Synced compatibility_date to Cloudflare config");
|
|
3623
|
+
};
|
|
3624
|
+
const wranglerCompatPlugin = {
|
|
3625
|
+
name: "void:wrangler-compat",
|
|
3626
|
+
enforce: "pre",
|
|
3627
|
+
config(_, env) {
|
|
3628
|
+
if (env.command === "serve" && directCloudflareDestination && bindings.needsAI) process.env.CLOUDFLARE_AUTH_USE_KEYRING = "true";
|
|
3629
|
+
syncFrameworkCompat(ensureWranglerCompat(root, config));
|
|
3630
|
+
if (env.command !== "serve") return;
|
|
3631
|
+
if (ensureWranglerNodejsAlsFlag(root)) voidLog("Synced nodejs_als to Cloudflare config");
|
|
3632
|
+
},
|
|
3633
|
+
buildStart() {
|
|
3634
|
+
syncFrameworkCompat(ensureWranglerCompat(root, config));
|
|
3635
|
+
},
|
|
3636
|
+
configureServer() {
|
|
3637
|
+
syncFrameworkCompat(ensureWranglerCompat(root, config));
|
|
3638
|
+
}
|
|
3639
|
+
};
|
|
3640
|
+
const syncFrameworkBindings = (command) => {
|
|
3641
|
+
if (command !== "serve") return;
|
|
3642
|
+
if (syncWranglerBindings(root, {
|
|
3643
|
+
...bindings,
|
|
3644
|
+
needsD1: dialect === "sqlite" && application.needsDatabase
|
|
3645
|
+
}, {
|
|
3646
|
+
bindingNames,
|
|
3647
|
+
...directCloudflareDestination && bindings.needsAI ? { directCloudflareAi: { gateway: config.ai?.gateway } } : {},
|
|
3648
|
+
websockets: websocketRoutes,
|
|
3649
|
+
sandbox: sandboxConfig,
|
|
3650
|
+
live: liveConfig
|
|
3651
|
+
})) voidLog("Synced bindings to Cloudflare config");
|
|
3652
|
+
};
|
|
3653
|
+
const wranglerSyncPlugin = {
|
|
3654
|
+
name: "void:wrangler-sync",
|
|
3655
|
+
enforce: "pre",
|
|
3656
|
+
config(_, env) {
|
|
3657
|
+
syncFrameworkBindings(env.command);
|
|
3658
|
+
},
|
|
3659
|
+
configResolved(resolvedConfig) {
|
|
3660
|
+
syncFrameworkCompat(readWranglerCompat(root, config));
|
|
3661
|
+
syncFrameworkBindings(resolvedConfig.command);
|
|
3662
|
+
}
|
|
3663
|
+
};
|
|
3664
|
+
const nuxtVite8WebSocketCompatPlugin = detected.name === "nuxt" ? {
|
|
3665
|
+
name: "void:nuxt-vite8-websocket-compat",
|
|
3666
|
+
enforce: "post",
|
|
3667
|
+
configResolved(resolvedConfig) {
|
|
3668
|
+
if (!resolvedConfig.build.ssr) return;
|
|
3669
|
+
if (typeof resolvedConfig.server.hmr === "object" && resolvedConfig.server.hmr.port !== void 0) resolvedConfig.server.hmr = {
|
|
3670
|
+
...resolvedConfig.server.hmr,
|
|
3671
|
+
server: void 0
|
|
3672
|
+
};
|
|
3673
|
+
if (typeof resolvedConfig.server.ws === "object" && resolvedConfig.server.ws.port !== void 0) resolvedConfig.server.ws = {
|
|
3674
|
+
...resolvedConfig.server.ws,
|
|
3675
|
+
server: void 0
|
|
3676
|
+
};
|
|
3677
|
+
}
|
|
3678
|
+
} : void 0;
|
|
3679
|
+
const svelteKitHooksInjectPlugin = {
|
|
3680
|
+
name: "void:sveltekit-hooks-inject",
|
|
3681
|
+
apply: "serve",
|
|
3682
|
+
transform(code, id) {
|
|
3683
|
+
if (detected.class !== "b") return;
|
|
3684
|
+
if (!id.includes(".svelte-kit/generated/server/internal") && !id.includes(".svelte-kit/generated/dev/server.js")) return;
|
|
3685
|
+
return injectSvelteKitHooks(code, id, frameworkTriggersDispatchPath(root));
|
|
3686
|
+
}
|
|
3687
|
+
};
|
|
3688
|
+
return [
|
|
3689
|
+
createCloudflareBuiltinsExternalPlugin(),
|
|
3690
|
+
wranglerCompatPlugin,
|
|
3691
|
+
wranglerSyncPlugin,
|
|
3692
|
+
...nuxtVite8WebSocketCompatPlugin ? [nuxtVite8WebSocketCompatPlugin] : [],
|
|
3693
|
+
svelteKitHooksInjectPlugin,
|
|
3694
|
+
clientStubsPlugin,
|
|
3695
|
+
serverRuntimeOptimizerPlugin,
|
|
3696
|
+
bindingRuntimePlugin(bindingNames),
|
|
3697
|
+
envSchemaPlugin(root),
|
|
3698
|
+
envClientGuardPlugin(root),
|
|
3699
|
+
envClientFoldPlugin(),
|
|
3700
|
+
drizzlePlugin(root, false, dialect, {
|
|
3701
|
+
d1BindingName: bindingNames?.d1,
|
|
3702
|
+
forceVirtualDb: authEnabled || bindingNames?.d1 !== void 0,
|
|
3703
|
+
localD1DatabaseId: localD1.databaseId,
|
|
3704
|
+
localD1PersistRoot: frameworkPersistRoot
|
|
3705
|
+
}),
|
|
3706
|
+
migrationPlugin(root, dialect, localD1.databaseId, frameworkPersistRoot, { enabled: false }),
|
|
3707
|
+
devTriggersHintPlugin(root, {
|
|
3708
|
+
framework: true,
|
|
3709
|
+
devTriggerToken
|
|
3710
|
+
}),
|
|
3711
|
+
frameworkTriggersPlugin(root, { devTriggerToken }),
|
|
3712
|
+
...detected.name === "nuxt" ? [nitroDevTriggerInjectPlugin(root, { serverDir: "server" })] : [],
|
|
3713
|
+
...detected.name === "analog" ? [analogDevTriggerInjectPlugin(root)] : [],
|
|
3714
|
+
...detected.name === "astro" ? [astroDevTriggerInjectPlugin(root)] : [],
|
|
3715
|
+
implicitDeployGuard
|
|
3716
|
+
];
|
|
3217
3717
|
}
|
|
3718
|
+
const CF_ONLY_REGEX = new RegExp(`^(${[
|
|
3719
|
+
"void/db",
|
|
3720
|
+
"void/kv",
|
|
3721
|
+
"void/auth",
|
|
3722
|
+
"void/storage",
|
|
3723
|
+
"void/ai",
|
|
3724
|
+
"void/sandbox",
|
|
3725
|
+
"void/env"
|
|
3726
|
+
].map((s) => s.replace("/", "\\/")).join("|")})$`);
|
|
3218
3727
|
/**
|
|
3219
|
-
*
|
|
3220
|
-
*
|
|
3221
|
-
*
|
|
3222
|
-
* When `envSchemaModule` is provided, splices boot-time env validation so it
|
|
3223
|
-
* runs on the first request (including the prerender child process spawned
|
|
3224
|
-
* by `prerenderPagesNode`). Mirrors `compileWorkerEntry`: a side-effect import
|
|
3225
|
-
* registers the schema, and `_validateEnvOnce` throws `EnvValidationError` on
|
|
3226
|
-
* bad values. Env source on Node/Bun is `process.env`; on Deno it's
|
|
3227
|
-
* `Deno.env.toObject()`.
|
|
3728
|
+
* Bridge Node.js HTTP to Web API and serve via a Hono app.
|
|
3729
|
+
* Used by both dev server and preview server middleware.
|
|
3228
3730
|
*/
|
|
3229
|
-
function
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
const
|
|
3243
|
-
const
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3731
|
+
function honoMiddleware(getApp) {
|
|
3732
|
+
return async (req, res, next) => {
|
|
3733
|
+
try {
|
|
3734
|
+
if (!req.url || req.url.startsWith("/@") || req.url.startsWith("/__") || req.url.startsWith("/node_modules/")) {
|
|
3735
|
+
next();
|
|
3736
|
+
return;
|
|
3737
|
+
}
|
|
3738
|
+
const app = await getApp();
|
|
3739
|
+
if (!app?.fetch) {
|
|
3740
|
+
next();
|
|
3741
|
+
return;
|
|
3742
|
+
}
|
|
3743
|
+
const url = new URL(req.url, `http://${req.headers.host || "localhost"}`);
|
|
3744
|
+
const headers = new Headers();
|
|
3745
|
+
for (const [key, value] of Object.entries(req.headers)) if (value) headers.set(key, Array.isArray(value) ? value.join(", ") : value);
|
|
3746
|
+
const hasBody = req.method !== "GET" && req.method !== "HEAD";
|
|
3747
|
+
const request = new Request(url.toString(), {
|
|
3748
|
+
method: req.method,
|
|
3749
|
+
headers,
|
|
3750
|
+
body: hasBody ? req : void 0,
|
|
3751
|
+
duplex: hasBody ? "half" : void 0
|
|
3752
|
+
});
|
|
3753
|
+
const response = await app.fetch(request);
|
|
3754
|
+
if (response.status === 404) {
|
|
3755
|
+
next();
|
|
3756
|
+
return;
|
|
3757
|
+
}
|
|
3758
|
+
res.statusCode = response.status;
|
|
3759
|
+
response.headers.forEach((value, key) => {
|
|
3760
|
+
res.setHeader(key, value);
|
|
3761
|
+
});
|
|
3762
|
+
if (response.body) {
|
|
3763
|
+
const reader = response.body.getReader();
|
|
3764
|
+
while (true) {
|
|
3765
|
+
const { done, value } = await reader.read();
|
|
3766
|
+
if (done) break;
|
|
3767
|
+
res.write(value);
|
|
3768
|
+
}
|
|
3769
|
+
res.end();
|
|
3770
|
+
} else res.end();
|
|
3771
|
+
} catch (err) {
|
|
3772
|
+
next(err);
|
|
3773
|
+
}
|
|
3774
|
+
};
|
|
3256
3775
|
}
|
|
3257
3776
|
/**
|
|
3258
|
-
*
|
|
3259
|
-
*
|
|
3777
|
+
* Vite plugin for non-Cloudflare deploy targets (Node.js, Bun, Deno).
|
|
3778
|
+
*
|
|
3779
|
+
* Replaces `@cloudflare/vite-plugin` in the plugin composition. Provides:
|
|
3780
|
+
* 1. Binding guard — compile-time errors for CF-only void/* imports
|
|
3781
|
+
* 2. Dev server — Vite SSR middleware to run Hono in Node.js
|
|
3782
|
+
* 3. Preview server — serves from the built SSR entry
|
|
3783
|
+
* 4. Build — dual-environment build (client + SSR) for pages apps,
|
|
3784
|
+
* single SSR build for API-only apps
|
|
3260
3785
|
*/
|
|
3261
|
-
function
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
}
|
|
3786
|
+
function nodeTargetPlugin(target) {
|
|
3787
|
+
return [{
|
|
3788
|
+
name: "void:node-target",
|
|
3789
|
+
enforce: "pre",
|
|
3790
|
+
resolveId: {
|
|
3791
|
+
filter: { id: CF_ONLY_REGEX },
|
|
3792
|
+
handler(id) {
|
|
3793
|
+
throw new Error(`target: '${id}' is not available with target '${target}'. CF bindings require target: "cloudflare".`);
|
|
3794
|
+
}
|
|
3795
|
+
},
|
|
3796
|
+
renderChunk(code, chunk) {
|
|
3797
|
+
if (!code.includes("import.meta.voidClientDirectory")) return null;
|
|
3798
|
+
const clientPath = relative(dirname(chunk.fileName), "../client");
|
|
3799
|
+
return {
|
|
3800
|
+
code: code.replaceAll("import.meta.voidClientDirectory", `new URL(${JSON.stringify(`${clientPath}/`)}, import.meta.url)`),
|
|
3801
|
+
map: null
|
|
3802
|
+
};
|
|
3803
|
+
},
|
|
3804
|
+
config(_, { command }) {
|
|
3805
|
+
if (command !== "build") return;
|
|
3806
|
+
const root = process.cwd();
|
|
3807
|
+
const voidDir = join(root, ".void");
|
|
3808
|
+
const ssrBuildOptions = {
|
|
3809
|
+
outDir: "dist/ssr",
|
|
3810
|
+
rollupOptions: {
|
|
3811
|
+
input: {
|
|
3812
|
+
app: join(voidDir, "app.ts"),
|
|
3813
|
+
index: join(voidDir, "index.ts")
|
|
3814
|
+
},
|
|
3815
|
+
external: [
|
|
3816
|
+
/^node:/,
|
|
3817
|
+
"@hono/node-server",
|
|
3818
|
+
"@hono/node-server/serve-static"
|
|
3819
|
+
]
|
|
3820
|
+
}
|
|
3821
|
+
};
|
|
3822
|
+
if (existsSync(readProjectPaths(root).pagesDir)) return {
|
|
3823
|
+
build: { outDir: "dist/client" },
|
|
3824
|
+
environments: { ssr: { build: ssrBuildOptions } },
|
|
3825
|
+
builder: {}
|
|
3826
|
+
};
|
|
3827
|
+
return { build: {
|
|
3828
|
+
ssr: true,
|
|
3829
|
+
...ssrBuildOptions
|
|
3830
|
+
} };
|
|
3831
|
+
},
|
|
3832
|
+
async buildApp(builder) {
|
|
3833
|
+
await builder.build(builder.environments.ssr);
|
|
3834
|
+
await builder.build(builder.environments.client);
|
|
3835
|
+
},
|
|
3836
|
+
configureServer(server) {
|
|
3837
|
+
server.middlewares.use(honoMiddleware(async () => {
|
|
3838
|
+
return (await server.ssrLoadModule("virtual:void-routes")).default;
|
|
3839
|
+
}));
|
|
3840
|
+
},
|
|
3841
|
+
configurePreviewServer(server) {
|
|
3842
|
+
const entryPath = join(process.cwd(), "dist", "ssr", "app.js");
|
|
3843
|
+
let appPromise = null;
|
|
3844
|
+
server.middlewares.use(honoMiddleware(() => {
|
|
3845
|
+
if (!appPromise) appPromise = import(
|
|
3846
|
+
/* @vite-ignore */
|
|
3847
|
+
pathToFileURL(entryPath).href
|
|
3848
|
+
).then((m) => m.default, () => null);
|
|
3849
|
+
return appPromise;
|
|
3850
|
+
}));
|
|
3851
|
+
}
|
|
3852
|
+
}];
|
|
3278
3853
|
}
|
|
3854
|
+
//#endregion
|
|
3855
|
+
//#region src/plugin-prerender.ts
|
|
3279
3856
|
/**
|
|
3280
|
-
*
|
|
3281
|
-
*
|
|
3282
|
-
*
|
|
3857
|
+
* Vite plugin that prerenders pages at build time when `output: "static"`.
|
|
3858
|
+
*
|
|
3859
|
+
* Runs in the `closeBundle` hook so prerender logs appear after Vite's
|
|
3860
|
+
* asset summary. Gated on the "client" environment so it only runs once.
|
|
3283
3861
|
*/
|
|
3284
|
-
function
|
|
3285
|
-
return
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3862
|
+
function prerenderPlugin(root, config) {
|
|
3863
|
+
return {
|
|
3864
|
+
name: "void:prerender",
|
|
3865
|
+
apply: "build",
|
|
3866
|
+
async closeBundle() {
|
|
3867
|
+
if (config.output !== "static") return;
|
|
3868
|
+
if (process.env.VOID_DEPLOY_BUILD === "1") return;
|
|
3869
|
+
if (this.environment?.name !== "client") return;
|
|
3870
|
+
setShowTimestamp(false);
|
|
3871
|
+
const distDir = resolveDistDir(root);
|
|
3872
|
+
const clientDir = join(distDir, "client");
|
|
3873
|
+
let result;
|
|
3874
|
+
if (isNodeTarget(config.target)) result = await prerenderPagesNode({
|
|
3875
|
+
root,
|
|
3876
|
+
ssrDir: join(distDir, "ssr"),
|
|
3877
|
+
clientDir
|
|
3878
|
+
});
|
|
3879
|
+
else {
|
|
3880
|
+
const persistDir = join(root, ".void");
|
|
3881
|
+
const workerDirName = resolveWorkerDirName(distDir);
|
|
3882
|
+
const workerDir = join(distDir, workerDirName);
|
|
3883
|
+
const remoteMode = resolveRemoteMode(config.remote);
|
|
3884
|
+
result = await prerenderPages({
|
|
3885
|
+
root,
|
|
3886
|
+
workerDir,
|
|
3887
|
+
clientDir,
|
|
3888
|
+
persistDir,
|
|
3889
|
+
remoteMode,
|
|
3890
|
+
projectId: process.env.VOID_DEPLOY_PROJECT_ID || void 0
|
|
3891
|
+
});
|
|
3892
|
+
}
|
|
3893
|
+
if (result.paths.length === 0) {
|
|
3894
|
+
voidLog("No pages to prerender");
|
|
3895
|
+
return;
|
|
3896
|
+
}
|
|
3897
|
+
if (result.failed.length > 0) voidWarn(`${result.failed.length} page(s) failed to prerender`);
|
|
3898
|
+
}
|
|
3899
|
+
};
|
|
3302
3900
|
}
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3901
|
+
//#endregion
|
|
3902
|
+
//#region src/plugin-restart.ts
|
|
3903
|
+
const BINDING_HINT_RESOLVED = "\0virtual:void/binding-hint";
|
|
3904
|
+
const RESTARTING_HTML = `<!DOCTYPE html>
|
|
3905
|
+
<html lang="en">
|
|
3906
|
+
<head>
|
|
3907
|
+
<meta charset="UTF-8">
|
|
3908
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3909
|
+
<title>Restarting</title>
|
|
3910
|
+
<style>
|
|
3911
|
+
body{margin:0;height:100vh;display:flex;align-items:center;justify-content:center;background:#1a1a2e;color:#e0def4;font:14px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace}
|
|
3912
|
+
.tag{color:#c4a7e7;font-weight:700}
|
|
3913
|
+
@keyframes d{0%,80%{opacity:0}40%{opacity:1}}
|
|
3914
|
+
.dots span{opacity:0;animation:d 1.4s infinite}
|
|
3915
|
+
.dots span:nth-child(2){animation-delay:.2s}
|
|
3916
|
+
.dots span:nth-child(3){animation-delay:.4s}
|
|
3917
|
+
</style>
|
|
3918
|
+
</head>
|
|
3919
|
+
<body>
|
|
3920
|
+
<div><span class="tag">[void]</span> Restarting dev server<span class="dots"><span>.</span><span>.</span><span>.</span></span></div>
|
|
3921
|
+
<script>
|
|
3922
|
+
fetch("/__void/restart",{method:"POST"}).catch(function(){});
|
|
3923
|
+
setTimeout(function p(){
|
|
3924
|
+
fetch("/__void/ping").then(function(r){
|
|
3925
|
+
if(r.ok)location.href=new URLSearchParams(location.search).get("return")||"/";
|
|
3926
|
+
else setTimeout(p,150);
|
|
3927
|
+
}).catch(function(){setTimeout(p,150)});
|
|
3928
|
+
},300);
|
|
3929
|
+
<\/script>
|
|
3930
|
+
</body>
|
|
3931
|
+
</html>`;
|
|
3932
|
+
const CLIENT_MODULE = `if (import.meta.hot) {
|
|
3933
|
+
import.meta.hot.on("void:new-binding", (d) => {
|
|
3934
|
+
if (document.getElementById("void-binding-hint")) return;
|
|
3935
|
+
const e = document.createElement("div");
|
|
3936
|
+
e.id = "void-binding-hint";
|
|
3937
|
+
e.style.cssText = "position:fixed;top:16px;right:16px;z-index:99999;padding:12px 16px;background:rgba(26,26,46,0.95);color:#e0def4;font:13px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;border-radius:8px;border:1px solid rgba(255,255,255,0.08);box-shadow:0 8px 32px rgba(0,0,0,0.4);backdrop-filter:blur(8px);cursor:pointer;max-width:300px;transform:translateX(calc(100% + 16px));transition:transform .3s ease";
|
|
3938
|
+
e.innerHTML = "<strong style=\\"color:#c4a7e7\\">[void]</strong> " + d.message + " Click to restart.";
|
|
3939
|
+
e.onclick = () => { location.href = "/__void/restarting?return=" + encodeURIComponent(location.pathname + location.search); };
|
|
3940
|
+
document.body.appendChild(e);
|
|
3941
|
+
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
3942
|
+
e.style.transform = "translateX(0)";
|
|
3943
|
+
}));
|
|
3336
3944
|
});
|
|
3337
|
-
|
|
3338
|
-
const response = await app.fetch(request);
|
|
3339
|
-
|
|
3340
|
-
if (response.status === 204) return undefined;
|
|
3341
|
-
|
|
3342
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
3343
|
-
const data = contentType.includes("application/json") ? await response.json() : await response.text();
|
|
3344
|
-
|
|
3345
|
-
if (!response.ok) {
|
|
3346
|
-
throw new FetchError(response.status, response.statusText, data);
|
|
3347
|
-
}
|
|
3348
|
-
|
|
3349
|
-
return data;
|
|
3350
3945
|
}`;
|
|
3946
|
+
/**
|
|
3947
|
+
* Vite plugin that provides controlled dev-server restart via the browser.
|
|
3948
|
+
*
|
|
3949
|
+
* Serves three endpoints:
|
|
3950
|
+
* - `/__void/restarting` — standalone loading page (no Vite HMR client)
|
|
3951
|
+
* - `/__void/restart` — POST triggers `server.restart()` after a short delay
|
|
3952
|
+
* - `/__void/ping` — health check; returns 503 on the old server instance
|
|
3953
|
+
* (after restart is triggered) and 200 on the new one
|
|
3954
|
+
*
|
|
3955
|
+
* Also injects a virtual module (`virtual:void/binding-hint`) that listens for
|
|
3956
|
+
* `void:new-binding` HMR events and shows a toast with a click-to-restart action.
|
|
3957
|
+
*/
|
|
3958
|
+
function restartPlugin() {
|
|
3959
|
+
return {
|
|
3960
|
+
name: "void:restart",
|
|
3961
|
+
apply: "serve",
|
|
3962
|
+
resolveId: {
|
|
3963
|
+
filter: { id: /^virtual:void\/binding-hint$/ },
|
|
3964
|
+
handler() {
|
|
3965
|
+
return BINDING_HINT_RESOLVED;
|
|
3966
|
+
}
|
|
3967
|
+
},
|
|
3968
|
+
load: {
|
|
3969
|
+
filter: { id: /^\0virtual:void\/binding-hint$/ },
|
|
3970
|
+
handler() {
|
|
3971
|
+
return CLIENT_MODULE;
|
|
3972
|
+
}
|
|
3973
|
+
},
|
|
3974
|
+
configureServer(server) {
|
|
3975
|
+
let restartPending = false;
|
|
3976
|
+
server.middlewares.use("/__void/ping", (_req, res) => {
|
|
3977
|
+
res.writeHead(restartPending ? 503 : 200);
|
|
3978
|
+
res.end();
|
|
3979
|
+
});
|
|
3980
|
+
server.middlewares.use("/__void/restart", (req, res) => {
|
|
3981
|
+
if (req.method !== "POST") {
|
|
3982
|
+
res.writeHead(405);
|
|
3983
|
+
res.end();
|
|
3984
|
+
return;
|
|
3985
|
+
}
|
|
3986
|
+
restartPending = true;
|
|
3987
|
+
res.writeHead(200);
|
|
3988
|
+
res.end();
|
|
3989
|
+
setTimeout(() => server.restart(), 50);
|
|
3990
|
+
});
|
|
3991
|
+
server.middlewares.use("/__void/restarting", (_req, res) => {
|
|
3992
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
3993
|
+
res.end(RESTARTING_HTML);
|
|
3994
|
+
});
|
|
3995
|
+
}
|
|
3996
|
+
};
|
|
3351
3997
|
}
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
function
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
import { interpolateRouteParams } from ${JSON.stringify(ROUTE_URL_PATH)};
|
|
3368
|
-
import { env } from "cloudflare:workers";
|
|
3369
|
-
export { auth, createAuthClient } from ${JSON.stringify(authClientPath)};
|
|
3370
|
-
|
|
3371
|
-
export class FetchError extends Error {
|
|
3372
|
-
constructor(status, statusText, data) {
|
|
3373
|
-
super(\`fetch: Request failed with status \${status}.\`);
|
|
3374
|
-
this.name = "FetchError";
|
|
3375
|
-
this.status = status;
|
|
3376
|
-
this.statusText = statusText;
|
|
3377
|
-
this.data = data;
|
|
3378
|
-
}
|
|
3379
|
-
}
|
|
3380
|
-
|
|
3381
|
-
export async function fetch(path, options) {
|
|
3382
|
-
let url = interpolateRouteParams(path, options?.params);
|
|
3383
|
-
|
|
3384
|
-
if (options?.query) {
|
|
3385
|
-
const qs = new URLSearchParams(options.query).toString();
|
|
3386
|
-
if (qs) url += "?" + qs;
|
|
3387
|
-
}
|
|
3388
|
-
|
|
3389
|
-
const method = options?.method ?? "GET";
|
|
3390
|
-
const headers = new Headers(options?.headers);
|
|
3391
|
-
|
|
3392
|
-
// Auto-forward cookie and authorization from the outer request via ALS
|
|
3393
|
-
const store = requestContext.getStore();
|
|
3394
|
-
if (store?.request) {
|
|
3395
|
-
const outerReq = store.request;
|
|
3396
|
-
if (!headers.has("cookie")) {
|
|
3397
|
-
const cookie = outerReq.headers.get("cookie");
|
|
3398
|
-
if (cookie) headers.set("cookie", cookie);
|
|
3399
|
-
}
|
|
3400
|
-
if (!headers.has("authorization")) {
|
|
3401
|
-
const auth = outerReq.headers.get("authorization");
|
|
3402
|
-
if (auth) headers.set("authorization", auth);
|
|
3403
|
-
}
|
|
3404
|
-
}
|
|
3405
|
-
|
|
3406
|
-
if (options?.body != null && !headers.has("content-type")) {
|
|
3407
|
-
headers.set("content-type", "application/json");
|
|
3408
|
-
}
|
|
3409
|
-
|
|
3410
|
-
const request = new Request("http://worker.local" + url, {
|
|
3411
|
-
method,
|
|
3412
|
-
headers,
|
|
3413
|
-
body: options?.body != null ? JSON.stringify(options.body) : undefined,
|
|
3414
|
-
});
|
|
3415
|
-
|
|
3416
|
-
// Use real executionCtx from ALS if available, otherwise provide a no-op fallback
|
|
3417
|
-
const executionCtx = store?.executionCtx ?? { waitUntil() {}, passThroughOnException() {} };
|
|
3418
|
-
const response = await app.fetch(request, env, executionCtx);
|
|
3419
|
-
|
|
3420
|
-
if (response.status === 204) return undefined;
|
|
3421
|
-
|
|
3422
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
3423
|
-
const data = contentType.includes("application/json") ? await response.json() : await response.text();
|
|
3424
|
-
|
|
3425
|
-
if (!response.ok) {
|
|
3426
|
-
throw new FetchError(response.status, response.statusText, data);
|
|
3427
|
-
}
|
|
3428
|
-
|
|
3429
|
-
return data;
|
|
3430
|
-
}
|
|
3431
|
-
`;
|
|
3998
|
+
//#endregion
|
|
3999
|
+
//#region src/client-asset-manifest.ts
|
|
4000
|
+
/** Attach cache provenance to the existing manifest without changing its entry inventory. */
|
|
4001
|
+
function stampClientAssetCachePolicy(bundle, manifestPath, headerRules = []) {
|
|
4002
|
+
const asset = bundle[manifestPath];
|
|
4003
|
+
if (asset?.type !== "asset") return;
|
|
4004
|
+
const fingerprinted = /* @__PURE__ */ new Set();
|
|
4005
|
+
const authoredCachePatterns = headerRules.filter((rule) => rule.headers.some(([name]) => name.toLowerCase() === "cache-control")).map((rule) => compilePattern(rule.pattern).regex);
|
|
4006
|
+
for (const item of Object.values(bundle)) if (item.type === "chunk" && typeof item.preliminaryFileName === "string" && item.preliminaryFileName !== item.fileName) {
|
|
4007
|
+
if (!authoredCachePatterns.some((pattern) => pattern.test("/" + item.fileName))) fingerprinted.add(item.fileName);
|
|
4008
|
+
}
|
|
4009
|
+
const manifest = JSON.parse(String(asset.source));
|
|
4010
|
+
for (const entry of Object.values(manifest)) if (entry.file && fingerprinted.has(entry.file)) entry.immutableFiles = [entry.file];
|
|
4011
|
+
else delete entry.immutableFiles;
|
|
4012
|
+
asset.source = JSON.stringify(manifest, null, 2);
|
|
3432
4013
|
}
|
|
3433
4014
|
//#endregion
|
|
3434
4015
|
//#region src/plugin-routing.ts
|
|
@@ -3764,7 +4345,7 @@ function routingPlugin(root, options) {
|
|
|
3764
4345
|
const migrationsDir = paths.migrationsDir;
|
|
3765
4346
|
if (existsSync(migrationsDir) || compileOptions.auth) {
|
|
3766
4347
|
const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
|
|
3767
|
-
const { buildMigrationPlanBundle } = await import("./plan-
|
|
4348
|
+
const { buildMigrationPlanBundle } = await import("./plan-COtUvdMA.mjs");
|
|
3768
4349
|
const migrations = existsSync(migrationsDir) ? collectMigrations(migrationsDir) : [];
|
|
3769
4350
|
const dialect = getDatabaseDialect(readConfig(root));
|
|
3770
4351
|
compileOptions.migrationHandlerPath = resolveFile(dialect === "postgresql" ? "#self/runtime/migration-handler-pg" : dialect === "mysql" ? "#self/runtime/migration-handler-mysql" : "#self/runtime/migration-handler");
|
|
@@ -3902,289 +4483,83 @@ function routingPlugin(root, options) {
|
|
|
3902
4483
|
const handleRulesFileChange = (path) => {
|
|
3903
4484
|
if (path !== redirectsPath && path !== headersPath) return;
|
|
3904
4485
|
reloadFileRules();
|
|
3905
|
-
for (const env of Object.values(devServer.environments)) {
|
|
3906
|
-
const mod = env.moduleGraph.getModuleById(VIRTUAL_ROUTES_ID);
|
|
3907
|
-
if (mod) {
|
|
3908
|
-
env.moduleGraph.invalidateModule(mod);
|
|
3909
|
-
env.hot.send({ type: "full-reload" });
|
|
3910
|
-
}
|
|
3911
|
-
}
|
|
3912
|
-
};
|
|
3913
|
-
watcher.on("add", handleRulesFileChange);
|
|
3914
|
-
watcher.on("change", handleRulesFileChange);
|
|
3915
|
-
watcher.on("unlink", handleRulesFileChange);
|
|
3916
|
-
}
|
|
3917
|
-
}
|
|
3918
|
-
};
|
|
3919
|
-
}
|
|
3920
|
-
//#endregion
|
|
3921
|
-
//#region src/sveltekit-hooks-transform.ts
|
|
3922
|
-
/** Add Void's dev bindings to SvelteKit's generated get_hooks function. */
|
|
3923
|
-
function injectSvelteKitHooks(code, id, dispatchPath) {
|
|
3924
|
-
if (!code.includes("get_hooks")) return;
|
|
3925
|
-
const hooks = (parseSync(id, code).program.body ?? []).filter((node) => node.type === "ExportNamedDeclaration").map((node) => node.declaration).filter((node) => {
|
|
3926
|
-
const name = node?.id;
|
|
3927
|
-
return node?.type === "FunctionDeclaration" && isIdentifier(name) && name.name === "get_hooks";
|
|
3928
|
-
});
|
|
3929
|
-
if (hooks.length !== 1) throw new Error("sveltekit: Expected one generated get_hooks function.");
|
|
3930
|
-
const body = hooks[0].body;
|
|
3931
|
-
const returns = (body?.body)?.filter((node) => node.type === "ReturnStatement" && node.argument?.type === "ObjectExpression") ?? [];
|
|
3932
|
-
if (body?.type !== "BlockStatement" || body.start === void 0 || returns.length !== 1 || returns[0]?.start === void 0) throw new Error("sveltekit: Expected one object return in generated get_hooks.");
|
|
3933
|
-
const ms = new MagicString(code);
|
|
3934
|
-
ms.appendLeft(body.start + 1, `\n const { withRuntimeEnv } = await import("void/_env");\n const { handleDevTrigger } = await import(${JSON.stringify(dispatchPath)});`);
|
|
3935
|
-
ms.appendLeft(returns[0].start, "const __origHandle = handle ?? (({ event, resolve }) => resolve(event));\n handle = ({ event, resolve }) => withRuntimeEnv(event.platform?.env, async () => {\n const __triggerResp = await handleDevTrigger(event.request, event.platform?.env, undefined);\n if (__triggerResp) return __triggerResp;\n return __origHandle({ event, resolve });\n });\n ");
|
|
3936
|
-
return {
|
|
3937
|
-
code: ms.toString(),
|
|
3938
|
-
map: ms.generateMap({ hires: true })
|
|
3939
|
-
};
|
|
3940
|
-
}
|
|
3941
|
-
//#endregion
|
|
3942
|
-
//#region src/index.ts
|
|
3943
|
-
/**
|
|
3944
|
-
* Persist the dev-trigger token at `<root>/.void/dev-trigger-token` so
|
|
3945
|
-
* every voidPlugin() invocation against the same project reads the same
|
|
3946
|
-
* value. This matters because `@cloudflare/vite-plugin` spawns a
|
|
3947
|
-
* worker-environment child Vite that re-imports vite.config.ts and calls
|
|
3948
|
-
* voidPlugin() again — without persistence the parent and child would
|
|
3949
|
-
* generate different UUIDs, the dispatch file would embed one and the
|
|
3950
|
-
* hint plugin would print the other, and curls would land on
|
|
3951
|
-
* `unauthorized`.
|
|
3952
|
-
*
|
|
3953
|
-
* The file persists across dev sessions by design — it lives under `.void/`
|
|
3954
|
-
* (gitignored), and `cleanupWrapper` in `void deploy` explicitly preserves
|
|
3955
|
-
* it across its own wipe so saved curl scripts keep working between deploys.
|
|
3956
|
-
* The token is local to the project and only honored by a worker bound to
|
|
3957
|
-
* localhost; treat it like the rest of `.void/` — disposable scratch state,
|
|
3958
|
-
* safe to remove manually if desired.
|
|
3959
|
-
*
|
|
3960
|
-
* The file is owner-only (0600). It is the one credential the dev-inbox
|
|
3961
|
-
* routes accept, and loopback is reachable by every account on the host,
|
|
3962
|
-
* so a default-umask 0644 file would hand any other local user the raw MIME,
|
|
3963
|
-
* the DELETE, and the capture endpoint. A file persisted by an earlier
|
|
3964
|
-
* release is tightened in place, never rotated. Windows has no group/other
|
|
3965
|
-
* bits — `mode` and `chmod` only toggle write there — so it is left alone.
|
|
3966
|
-
*/
|
|
3967
|
-
/**
|
|
3968
|
-
* Best-effort detection of `vite build` (and `vite preview`) so we can skip
|
|
3969
|
-
* dev-only side effects — writing the dev-trigger token and the dispatch
|
|
3970
|
-
* file — when no dev server will run. Vite's CLI passes the subcommand as
|
|
3971
|
-
* `process.argv[2]`; programmatic API users (and `vite` with no subcommand)
|
|
3972
|
-
* default to `dev`, which we detect as the absence of a build-shaped first
|
|
3973
|
-
* positional. This is intentionally permissive: when in doubt, we behave as
|
|
3974
|
-
* if we're in dev so first-request correctness is preserved.
|
|
3975
|
-
*/
|
|
3976
|
-
function isViteBuildOrPreview() {
|
|
3977
|
-
const subcommand = process.argv[2];
|
|
3978
|
-
return subcommand === "build" || subcommand === "preview";
|
|
3979
|
-
}
|
|
3980
|
-
const DEV_TRIGGER_TOKEN_FILE_MODE = 384;
|
|
3981
|
-
function getOrCreateDevTriggerToken(root) {
|
|
3982
|
-
const tokenPath = join(root, ".void", "dev-trigger-token");
|
|
3983
|
-
const readExisting = () => {
|
|
3984
|
-
try {
|
|
3985
|
-
const existing = readFileSync(tokenPath, "utf-8").trim();
|
|
3986
|
-
return existing.length > 0 ? existing : void 0;
|
|
3987
|
-
} catch {
|
|
3988
|
-
return;
|
|
3989
|
-
}
|
|
3990
|
-
};
|
|
3991
|
-
const tightenExisting = () => {
|
|
3992
|
-
if (process.platform === "win32") return;
|
|
3993
|
-
try {
|
|
3994
|
-
if ((statSync(tokenPath).mode & 63) !== 0) chmodSync(tokenPath, DEV_TRIGGER_TOKEN_FILE_MODE);
|
|
3995
|
-
} catch {}
|
|
3996
|
-
};
|
|
3997
|
-
const existing = readExisting();
|
|
3998
|
-
if (existing) {
|
|
3999
|
-
tightenExisting();
|
|
4000
|
-
return existing;
|
|
4001
|
-
}
|
|
4002
|
-
mkdirSync(join(root, ".void"), { recursive: true });
|
|
4003
|
-
const fresh = crypto.randomUUID();
|
|
4004
|
-
try {
|
|
4005
|
-
const fd = openSync(tokenPath, "wx", DEV_TRIGGER_TOKEN_FILE_MODE);
|
|
4006
|
-
try {
|
|
4007
|
-
writeSync(fd, fresh);
|
|
4008
|
-
} finally {
|
|
4009
|
-
closeSync(fd);
|
|
4010
|
-
}
|
|
4011
|
-
return fresh;
|
|
4012
|
-
} catch (err) {
|
|
4013
|
-
if (err.code !== "EEXIST") throw err;
|
|
4014
|
-
const winner = readExisting();
|
|
4015
|
-
if (winner) return winner;
|
|
4016
|
-
return fresh;
|
|
4017
|
-
}
|
|
4018
|
-
}
|
|
4019
|
-
/**
|
|
4020
|
-
* Warn when the project's `package.json` does not declare `"type": "module"`.
|
|
4021
|
-
*
|
|
4022
|
-
* Vite's default `configLoader: "bundle"` bundles `vite.config.ts`,
|
|
4023
|
-
* externalizes deps, and then loads the bundle. When `package.json` is non-ESM
|
|
4024
|
-
* the bundle is written as `.js` and loaded via CJS `require()`, which throws
|
|
4025
|
-
* `ERR_REQUIRE_ASYNC_MODULE` if any externalized dep's ESM graph contains
|
|
4026
|
-
* top-level await. `@cloudflare/vite-plugin` (which voidPlugin pulls in) has
|
|
4027
|
-
* exactly such a TLA, so non-ESM consumers hit this on every Vite invocation.
|
|
4028
|
-
*
|
|
4029
|
-
* Runs once per Vite process (factory time), single sync read.
|
|
4030
|
-
*/
|
|
4031
|
-
function warnIfPackageJsonIsNotEsm(root) {
|
|
4032
|
-
const pkgPath = join(root, "package.json");
|
|
4033
|
-
if (!existsSync(pkgPath)) return;
|
|
4034
|
-
let pkg;
|
|
4035
|
-
try {
|
|
4036
|
-
pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
4037
|
-
} catch {
|
|
4038
|
-
return;
|
|
4039
|
-
}
|
|
4040
|
-
if (pkg.type === "module") return;
|
|
4041
|
-
voidWarn("package.json is missing `\"type\": \"module\"`. Vite's default config loader will fail with ERR_REQUIRE_ASYNC_MODULE on transitive top-level await (e.g. @cloudflare/vite-plugin). Add `\"type\": \"module\"` to package.json, or run vite build with `--configLoader runner`.");
|
|
4042
|
-
}
|
|
4043
|
-
/**
|
|
4044
|
-
* Resolve the server entry module for a Class A framework. We always wrap
|
|
4045
|
-
* the framework's own entry with a tiny shim that intercepts
|
|
4046
|
-
* `/__void/{scheduled,queue}` in dev so cron and queue handlers run with
|
|
4047
|
-
* the worker's full CF bindings. The interception block is gated on
|
|
4048
|
-
* `import.meta.env.DEV` and the dispatch module is loaded via dynamic
|
|
4049
|
-
* `import()` so the entire dev path (including user cron imports) is
|
|
4050
|
-
* dead-code-eliminated in `vite build`. Production deploys go through
|
|
4051
|
-
* `cli/wrapper.ts` instead.
|
|
4052
|
-
*/
|
|
4053
|
-
function getFrameworkEntry(framework, root, devTriggerToken, notFound) {
|
|
4054
|
-
const isBuild = isViteBuildOrPreview();
|
|
4055
|
-
if (!isBuild) writeFrameworkTriggersDispatch(root, devTriggerToken);
|
|
4056
|
-
return generateClassATriggerWrapper(root, framework, getOriginalFrameworkEntry(framework, root), isBuild, void 0, notFound);
|
|
4057
|
-
}
|
|
4058
|
-
/** The framework's own server entry, before our trigger wrapper. */
|
|
4059
|
-
function getOriginalFrameworkEntry(framework, root) {
|
|
4060
|
-
switch (framework) {
|
|
4061
|
-
case "tanstack-start": return "@tanstack/react-start/server-entry";
|
|
4062
|
-
case "react-router": return generateReactRouterEntry(root);
|
|
4063
|
-
case "vinext-app": return "vinext/server/app-router-entry";
|
|
4064
|
-
case "vinext-pages": return generateVinextPagesEntry(root);
|
|
4065
|
-
}
|
|
4066
|
-
}
|
|
4067
|
-
/**
|
|
4068
|
-
* Generate `<root>/.void/<framework>-trigger-wrapper.ts`. This file becomes
|
|
4069
|
-
* `workerConfig.main` so the dev miniflare/workerd evaluates *our* fetch
|
|
4070
|
-
* first, dispatches `/__void/{scheduled,queue}` if it matches, and otherwise
|
|
4071
|
-
* delegates to the framework's original entry.
|
|
4072
|
-
*
|
|
4073
|
-
* We emit two shapes of the wrapper depending on the Vite command:
|
|
4074
|
-
*
|
|
4075
|
-
* - Serve: dispatch is dynamically imported and gets a chance to handle
|
|
4076
|
-
* `/__void/{scheduled,queue}` before falling through to the framework.
|
|
4077
|
-
* - Build: dispatch is omitted entirely. The wrapper just delegates fetch.
|
|
4078
|
-
*
|
|
4079
|
-
* We can't simply gate the dynamic import on `import.meta.env.DEV` and
|
|
4080
|
-
* trust DCE: rolldown emits chunks for dynamic imports during chunk
|
|
4081
|
-
* splitting, *before* constant folding removes the dead branch, so the
|
|
4082
|
-
* dispatch file (and its transitive user-cron/queue imports + dev token)
|
|
4083
|
-
* would still ship in the production bundle.
|
|
4084
|
-
*/
|
|
4085
|
-
function generateClassATriggerWrapper(root, framework, original, isBuild, assetsDir = "assets", notFound) {
|
|
4086
|
-
const outDir = join(root, ".void");
|
|
4087
|
-
mkdirSync(outDir, { recursive: true });
|
|
4088
|
-
const wrapperPath = join(outDir, `${framework}-trigger-wrapper.ts`);
|
|
4089
|
-
const body = isBuild ? `// @ts-nocheck — Auto-generated by Void for vite build. Do NOT edit.
|
|
4090
|
-
import original from ${JSON.stringify(original)};
|
|
4091
|
-
|
|
4092
|
-
export default {
|
|
4093
|
-
...original,
|
|
4094
|
-
async fetch(request, env, ctx) {
|
|
4095
|
-
return serveWithAssets(request, env, await original.fetch(request, env, ctx));
|
|
4096
|
-
},
|
|
4097
|
-
};
|
|
4098
|
-
${compileAssetFallbackHelper(assetsDir, false, notFound)}
|
|
4099
|
-
` : `// @ts-nocheck — Auto-generated by Void in dev. Do NOT edit.
|
|
4100
|
-
import original from ${JSON.stringify(original)};
|
|
4101
|
-
|
|
4102
|
-
export default {
|
|
4103
|
-
...original,
|
|
4104
|
-
async fetch(request, env, ctx) {
|
|
4105
|
-
if (request.method === "POST") {
|
|
4106
|
-
const pathname = new URL(request.url).pathname;
|
|
4107
|
-
if (pathname === "/__void/scheduled" || pathname === "/__void/queue") {
|
|
4108
|
-
const { handleDevTrigger } = await import(${JSON.stringify(frameworkTriggersDispatchPath(root))});
|
|
4109
|
-
const response = await handleDevTrigger(request, env, ctx);
|
|
4110
|
-
if (response) return response;
|
|
4111
|
-
}
|
|
4112
|
-
}
|
|
4113
|
-
return original.fetch(request, env, ctx);
|
|
4114
|
-
},
|
|
4115
|
-
};
|
|
4116
|
-
`;
|
|
4117
|
-
writeFileIfChanged(wrapperPath, body);
|
|
4118
|
-
return wrapperPath;
|
|
4486
|
+
for (const env of Object.values(devServer.environments)) {
|
|
4487
|
+
const mod = env.moduleGraph.getModuleById(VIRTUAL_ROUTES_ID);
|
|
4488
|
+
if (mod) {
|
|
4489
|
+
env.moduleGraph.invalidateModule(mod);
|
|
4490
|
+
env.hot.send({ type: "full-reload" });
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
};
|
|
4494
|
+
watcher.on("add", handleRulesFileChange);
|
|
4495
|
+
watcher.on("change", handleRulesFileChange);
|
|
4496
|
+
watcher.on("unlink", handleRulesFileChange);
|
|
4497
|
+
}
|
|
4498
|
+
}
|
|
4499
|
+
};
|
|
4119
4500
|
}
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
// @ts-expect-error virtual module resolved by vinext at build time
|
|
4127
|
-
import { renderPage, handleApiRoute } from "virtual:vinext-server-entry";
|
|
4128
|
-
|
|
4129
|
-
export default {
|
|
4130
|
-
async fetch(request: Request): Promise<Response> {
|
|
4131
|
-
const url = new URL(request.url);
|
|
4132
|
-
const pathname = url.pathname;
|
|
4133
|
-
if (pathname.replaceAll("\\\\", "/").startsWith("//")) {
|
|
4134
|
-
return new Response("404 Not Found", { status: 404 });
|
|
4135
|
-
}
|
|
4136
|
-
const urlWithQuery = pathname + url.search;
|
|
4137
|
-
if (pathname.startsWith("/api/") || pathname === "/api") {
|
|
4138
|
-
return await handleApiRoute(request, urlWithQuery);
|
|
4139
|
-
}
|
|
4140
|
-
return await renderPage(request, urlWithQuery, null);
|
|
4141
|
-
},
|
|
4142
|
-
};
|
|
4143
|
-
`);
|
|
4144
|
-
return entryPath;
|
|
4501
|
+
//#endregion
|
|
4502
|
+
//#region src/cloudflare-durable-patch.ts
|
|
4503
|
+
function entries(value, label) {
|
|
4504
|
+
if (value === void 0) return [];
|
|
4505
|
+
if (!Array.isArray(value) || value.some((entry) => !entry || typeof entry !== "object" || Array.isArray(entry))) throw new Error(`Cloudflare ${label} configuration must be an array of objects.`);
|
|
4506
|
+
return value;
|
|
4145
4507
|
}
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
const
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4508
|
+
function byIdentity(rows, field, label, unique = false) {
|
|
4509
|
+
const map = /* @__PURE__ */ new Map();
|
|
4510
|
+
for (const row of rows) {
|
|
4511
|
+
const id = row[field];
|
|
4512
|
+
if (typeof id !== "string" || !id) throw new Error(`Cloudflare ${label} is missing its ${field}.`);
|
|
4513
|
+
const previous = map.get(id);
|
|
4514
|
+
if (previous && unique) throw new Error(`Cloudflare ${label} ${JSON.stringify(id)} is configured more than once.`);
|
|
4515
|
+
if (previous && !isDeepStrictEqual(previous, row)) throw new Error(`Cloudflare ${label} ${JSON.stringify(id)} has conflicting configuration.`);
|
|
4516
|
+
if (!previous) map.set(id, row);
|
|
4517
|
+
}
|
|
4518
|
+
return map;
|
|
4519
|
+
}
|
|
4520
|
+
/** Finish cumulative feature planning without repeating the Cloudflare plugin's array fallback. */
|
|
4521
|
+
function finalizeDurableObjectPatch(resolved, patch, generatedLocalBindings) {
|
|
4522
|
+
const baselineObjects = resolved.durable_objects;
|
|
4523
|
+
const pendingObjects = patch.durable_objects;
|
|
4524
|
+
const baselineBindings = byIdentity(entries(baselineObjects?.bindings, "Durable Object bindings"), "name", "Durable Object binding", true);
|
|
4525
|
+
const pendingBindings = byIdentity(entries(pendingObjects?.bindings, "Durable Object bindings"), "name", "Durable Object binding");
|
|
4526
|
+
const history = entries(resolved.migrations, "Durable Object migrations");
|
|
4527
|
+
const baselineMigrations = byIdentity(history, "tag", "Durable Object migration", true);
|
|
4528
|
+
for (const local of generatedLocalBindings) for (const map of [baselineBindings, pendingBindings]) {
|
|
4529
|
+
const existing = map.get(local.name);
|
|
4530
|
+
if (existing && (existing.class_name !== local.class_name || existing.script_name !== void 0 || existing.environment !== void 0)) throw new Error(`Cloudflare generated Durable Object binding ${JSON.stringify(local.name)} conflicts with its configured identity.`);
|
|
4531
|
+
}
|
|
4532
|
+
if (pendingObjects?.bindings !== void 0) {
|
|
4533
|
+
const additions = [];
|
|
4534
|
+
for (const [name, row] of pendingBindings) {
|
|
4535
|
+
const existing = baselineBindings.get(name);
|
|
4536
|
+
if (existing && !isDeepStrictEqual(existing, row)) throw new Error(`Cloudflare Durable Object binding ${JSON.stringify(name)} has conflicting configuration.`);
|
|
4537
|
+
if (!existing) additions.push(row);
|
|
4538
|
+
}
|
|
4539
|
+
patch.durable_objects = {
|
|
4540
|
+
...pendingObjects,
|
|
4541
|
+
bindings: additions
|
|
4542
|
+
};
|
|
4543
|
+
}
|
|
4544
|
+
if (patch.migrations !== void 0) {
|
|
4545
|
+
const pending = byIdentity(entries(patch.migrations, "Durable Object migrations"), "tag", "Durable Object migration");
|
|
4546
|
+
const additions = [];
|
|
4547
|
+
for (const [tag, row] of pending) {
|
|
4548
|
+
const previous = baselineMigrations.get(tag);
|
|
4549
|
+
if (previous && !isDeepStrictEqual(previous, row)) throw new Error(`Cloudflare Durable Object migration ${JSON.stringify(tag)} has conflicting configuration.`);
|
|
4550
|
+
if (!previous) additions.push(row);
|
|
4551
|
+
}
|
|
4552
|
+
patch.migrations = [...history, ...additions];
|
|
4553
|
+
resolved.migrations = [];
|
|
4554
|
+
}
|
|
4167
4555
|
}
|
|
4556
|
+
//#endregion
|
|
4557
|
+
//#region src/plugin-binding-utils.ts
|
|
4168
4558
|
/** Check whether a binding name already exists in a resolved bindings array. */
|
|
4169
4559
|
function hasBinding(list, name) {
|
|
4170
4560
|
return Array.isArray(list) && list.some((b) => b.binding === name);
|
|
4171
4561
|
}
|
|
4172
4562
|
/**
|
|
4173
|
-
* Decide whether to inject a Workers AI binding into the built wrangler config.
|
|
4174
|
-
*
|
|
4175
|
-
* Builds use the production binding. During dev, a project explicitly saved
|
|
4176
|
-
* with the direct Cloudflare destination uses Cloudflare's remote binding;
|
|
4177
|
-
* managed and unsaved projects keep using the Void HTTPS proxy.
|
|
4178
|
-
*
|
|
4179
|
-
* `!resolvedAi` is load-bearing: mergeBindings' return is defu-merged over the
|
|
4180
|
-
* resolved root-wrangler config and wins, so skipping when the user already
|
|
4181
|
-
* declared an `ai` binding avoids clobbering theirs. `!resultAi` is harmless
|
|
4182
|
-
* defense-in-depth (nothing else in mergeBindings sets `result.ai`).
|
|
4183
|
-
*/
|
|
4184
|
-
function shouldBindWorkersAi(command, needsAI, resolvedAi, resultAi, directCloudflareDev = false) {
|
|
4185
|
-
return needsAI && (command === "build" || command === "serve" && directCloudflareDev) && !resultAi && !resolvedAi;
|
|
4186
|
-
}
|
|
4187
|
-
/**
|
|
4188
4563
|
* Decide whether to inject the `void dev` email-inbox loopback var.
|
|
4189
4564
|
*
|
|
4190
4565
|
* The dev inbox buffer lives in the Vite dev server's Node process;
|
|
@@ -4205,196 +4580,227 @@ function shouldBindWorkersAi(command, needsAI, resolvedAi, resultAi, directCloud
|
|
|
4205
4580
|
* Without an assets binding there is no loopback, so injecting the var would
|
|
4206
4581
|
* only arm a path that must then fail; gate on it instead.
|
|
4207
4582
|
*/
|
|
4208
|
-
function shouldInjectEmailDevInboxVar(routeEnvironment, needsEmail, assetsBinding) {
|
|
4209
|
-
return routeEnvironment === "dev" && !!needsEmail && typeof assetsBinding === "string" && !!assetsBinding;
|
|
4210
|
-
}
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
}
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
const
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
const
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
function resolveSandboxSdkEntry() {
|
|
4234
|
-
return fileURLToPath(import.meta.resolve(SANDBOX_SDK_IMPORT));
|
|
4235
|
-
}
|
|
4236
|
-
function createCloudflareBuiltinsExternalPlugin() {
|
|
4237
|
-
return {
|
|
4238
|
-
name: "void:cloudflare-builtins-external",
|
|
4239
|
-
config(_, env) {
|
|
4240
|
-
if (env.command !== "build") return;
|
|
4241
|
-
return { build: { rollupOptions: { external: [CLOUDFLARE_BUILTIN_IMPORT] } } };
|
|
4242
|
-
}
|
|
4243
|
-
};
|
|
4244
|
-
}
|
|
4245
|
-
function createDefaultWorkerEnvironmentPlugin() {
|
|
4246
|
-
return {
|
|
4247
|
-
name: "void:default-worker-environment",
|
|
4248
|
-
config(config) {
|
|
4249
|
-
const currentEnvironment = config.environments?.[DEFAULT_WORKER_ENVIRONMENT_NAME];
|
|
4250
|
-
return { environments: { [DEFAULT_WORKER_ENVIRONMENT_NAME]: {
|
|
4251
|
-
...currentEnvironment,
|
|
4252
|
-
build: {
|
|
4253
|
-
...currentEnvironment?.build,
|
|
4254
|
-
outDir: currentEnvironment?.build?.outDir ?? DEFAULT_WORKER_OUT_DIR
|
|
4255
|
-
}
|
|
4256
|
-
} } };
|
|
4257
|
-
}
|
|
4258
|
-
};
|
|
4259
|
-
}
|
|
4260
|
-
function createWorkerEnvironmentSanitizerPlugin(environmentNames) {
|
|
4261
|
-
const names = new Set(environmentNames);
|
|
4262
|
-
return {
|
|
4263
|
-
name: "void:worker-environment-sanitizer",
|
|
4264
|
-
enforce: "pre",
|
|
4265
|
-
configResolved(config) {
|
|
4266
|
-
for (const name of names) {
|
|
4267
|
-
const resolve = config.environments[name]?.resolve;
|
|
4268
|
-
if (resolve && "external" in resolve) resolve.external = [];
|
|
4269
|
-
}
|
|
4270
|
-
}
|
|
4271
|
-
};
|
|
4272
|
-
}
|
|
4273
|
-
function createSandboxSdkResolvePlugin() {
|
|
4274
|
-
let resolved;
|
|
4275
|
-
return {
|
|
4276
|
-
name: "void:sandbox-sdk-resolve",
|
|
4277
|
-
enforce: "pre",
|
|
4278
|
-
resolveId: {
|
|
4279
|
-
filter: { id: SANDBOX_SDK_IMPORT_FILTER },
|
|
4280
|
-
handler(id) {
|
|
4281
|
-
if (id !== SANDBOX_SDK_IMPORT) return;
|
|
4282
|
-
resolved ??= resolveSandboxSdkEntry();
|
|
4283
|
-
return resolved;
|
|
4284
|
-
}
|
|
4285
|
-
}
|
|
4286
|
-
};
|
|
4287
|
-
}
|
|
4288
|
-
function createWranglerCompatPersistPlugin(root, config) {
|
|
4289
|
-
let persisted = false;
|
|
4290
|
-
const persist = () => {
|
|
4291
|
-
if (persisted || isNodeTarget(config.target)) return;
|
|
4292
|
-
persisted = true;
|
|
4293
|
-
ensureWranglerCompat(root, config);
|
|
4294
|
-
};
|
|
4295
|
-
return {
|
|
4296
|
-
name: "void:wrangler-compat-persist",
|
|
4297
|
-
buildStart() {
|
|
4298
|
-
persist();
|
|
4299
|
-
},
|
|
4300
|
-
configureServer() {
|
|
4301
|
-
persist();
|
|
4302
|
-
}
|
|
4303
|
-
};
|
|
4304
|
-
}
|
|
4305
|
-
/**
|
|
4306
|
-
* Read Void auth credentials for local dev AI proxy access.
|
|
4307
|
-
* Returns a discriminated union so callers can distinguish
|
|
4308
|
-
* "no token" from "no project" for targeted onboarding guidance.
|
|
4309
|
-
*/
|
|
4310
|
-
function readVoidDevCredentials(root) {
|
|
4311
|
-
const token = getToken(root);
|
|
4312
|
-
if (!token) return { status: "no-token" };
|
|
4313
|
-
const apiUrl = getApiUrl(root);
|
|
4314
|
-
const project = readProjectConfig(root);
|
|
4315
|
-
if (!project) return {
|
|
4316
|
-
status: "no-project",
|
|
4317
|
-
apiUrl,
|
|
4318
|
-
token
|
|
4319
|
-
};
|
|
4320
|
-
const proxyUrl = getProxyUrl(root);
|
|
4321
|
-
ensurePlatformProxyAccess(apiUrl, proxyUrl);
|
|
4322
|
-
return {
|
|
4323
|
-
status: "ok",
|
|
4324
|
-
token,
|
|
4325
|
-
projectId: project.projectId,
|
|
4326
|
-
proxyUrl,
|
|
4327
|
-
accessCredentials: scopedAccessCredentials(process.env, proxyUrl)
|
|
4328
|
-
};
|
|
4329
|
-
}
|
|
4330
|
-
/**
|
|
4331
|
-
* Interactive AI setup prompt shown during dev server startup when the user
|
|
4332
|
-
* has a token but no linked project. Runs as fire-and-forget so it doesn't
|
|
4333
|
-
* block the server. On completion, writes project.json and restarts the server.
|
|
4334
|
-
*/
|
|
4335
|
-
async function runAiSetupPrompt(root, token, apiUrl, server) {
|
|
4336
|
-
const { select, text, isCancel, log } = await import("./prompts-2TGE6oXx.mjs");
|
|
4337
|
-
const { PlatformClient } = await import("./client-B9UBI03V.mjs");
|
|
4338
|
-
const { attachSelectedPlatform, writeProjectConfig } = await import("./project-DsblYifa.mjs");
|
|
4339
|
-
const { inferDefaultSlug } = await import("./resolve-project-DtbidV4y.mjs");
|
|
4340
|
-
const client = new PlatformClient(token, apiUrl);
|
|
4341
|
-
let projects;
|
|
4342
|
-
try {
|
|
4343
|
-
projects = await client.listProjects();
|
|
4344
|
-
} catch {
|
|
4345
|
-
voidWarn("Failed to fetch projects. Run `void project link` to link a project manually.");
|
|
4346
|
-
return;
|
|
4583
|
+
function shouldInjectEmailDevInboxVar(routeEnvironment, needsEmail, assetsBinding) {
|
|
4584
|
+
return routeEnvironment === "dev" && !!needsEmail && typeof assetsBinding === "string" && !!assetsBinding;
|
|
4585
|
+
}
|
|
4586
|
+
//#endregion
|
|
4587
|
+
//#region src/plugin-worker-bindings.ts
|
|
4588
|
+
function mergeWorkerBindings(resolved, input) {
|
|
4589
|
+
const { websocketRoutes, routeEnvironment, workerConfig, envSchemaFacts, authEnabled, viteCommand, bindingNames, dialect, application, bindings, directCloudflareDestination, config, devTriggerToken, queues, durableObjects, sandboxConfig, shouldEmitLive, liveConfig, root, remoteMode } = input;
|
|
4590
|
+
const environmentWebsocketRoutes = websocketRoutes.filter((route) => isRouteEnabledForEnvironment(route, routeEnvironment));
|
|
4591
|
+
const result = { ...workerConfig };
|
|
4592
|
+
result.compatibility_flags = [.../* @__PURE__ */ new Set([...result.compatibility_flags ?? [], ...resolved.compatibility_flags ?? []])];
|
|
4593
|
+
resolved.compatibility_flags = [];
|
|
4594
|
+
if (envSchemaFacts || authEnabled && process.env["VOID_CLOUDFLARE_DEPLOY_BUILD"] === "1") {
|
|
4595
|
+
const plaintextVars = {
|
|
4596
|
+
...resolved.vars ?? {},
|
|
4597
|
+
...result.vars ?? {}
|
|
4598
|
+
};
|
|
4599
|
+
const plaintextServerKeys = (envSchemaFacts?.serverKeys ?? []).filter((key) => key in plaintextVars);
|
|
4600
|
+
if (viteCommand === "build" && plaintextServerKeys.length > 0) throw new Error(`Server env keys cannot be Worker plaintext vars: ${plaintextServerKeys.join(", ")}. Remove them from worker.vars/wrangler vars and set them with the remote secret store.`);
|
|
4601
|
+
const configuredRequired = resolved.secrets?.required ?? [];
|
|
4602
|
+
const required = [.../* @__PURE__ */ new Set([
|
|
4603
|
+
...configuredRequired,
|
|
4604
|
+
...envSchemaFacts?.requiredServerKeys ?? [],
|
|
4605
|
+
...authEnabled && process.env["VOID_CLOUDFLARE_DEPLOY_BUILD"] === "1" ? ["BETTER_AUTH_SECRET"] : []
|
|
4606
|
+
])];
|
|
4607
|
+
if (required.length > 0) result.secrets = { required };
|
|
4347
4608
|
}
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
}
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
message: "Select a project:",
|
|
4359
|
-
options
|
|
4609
|
+
if (workerConfig.assets && resolved.assets) {
|
|
4610
|
+
delete resolved.assets.not_found_handling;
|
|
4611
|
+
delete resolved.assets.run_worker_first;
|
|
4612
|
+
}
|
|
4613
|
+
const d1Name = bindingNames?.d1 ?? "DB";
|
|
4614
|
+
const d1 = [];
|
|
4615
|
+
if (dialect === "sqlite" && application.needsDatabase && !hasBinding(resolved.d1_databases, d1Name)) d1.push({
|
|
4616
|
+
binding: d1Name,
|
|
4617
|
+
database_name: "default",
|
|
4618
|
+
database_id: "local"
|
|
4360
4619
|
});
|
|
4361
|
-
if (
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
if (
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4620
|
+
if (d1.length > 0) result.d1_databases = d1;
|
|
4621
|
+
const kvName = bindingNames?.kv ?? "KV";
|
|
4622
|
+
const kv = [];
|
|
4623
|
+
if (bindings.needsKV && !hasBinding(resolved.kv_namespaces, kvName)) kv.push({
|
|
4624
|
+
binding: kvName,
|
|
4625
|
+
id: "local"
|
|
4626
|
+
});
|
|
4627
|
+
if (kv.length > 0) result.kv_namespaces = kv;
|
|
4628
|
+
const r2Name = bindingNames?.r2 ?? "STORAGE";
|
|
4629
|
+
if (bindings.needsR2 && !hasBinding(resolved.r2_buckets, r2Name)) result.r2_buckets = [{
|
|
4630
|
+
binding: r2Name,
|
|
4631
|
+
bucket_name: "default"
|
|
4632
|
+
}];
|
|
4633
|
+
const resolvedAi = resolved.ai;
|
|
4634
|
+
const resultAi = result.ai;
|
|
4635
|
+
const aiBindingName = resolveWorkersAiBindingName(resolvedAi, bindingNames?.ai);
|
|
4636
|
+
if (viteCommand === "serve" && bindings.needsAI && directCloudflareDestination && !resultAi && typeof resolvedAi === "object" && resolvedAi !== null && "binding" in resolvedAi && typeof resolvedAi.binding === "string") result.ai = {
|
|
4637
|
+
...resolvedAi,
|
|
4638
|
+
remote: true
|
|
4639
|
+
};
|
|
4640
|
+
else if (shouldBindWorkersAi(viteCommand, bindings.needsAI, resolvedAi, resultAi, directCloudflareDestination)) result.ai = {
|
|
4641
|
+
binding: aiBindingName,
|
|
4642
|
+
...viteCommand === "serve" && { remote: true }
|
|
4643
|
+
};
|
|
4644
|
+
if (aiBindingName !== "AI" || config.ai?.gateway) {
|
|
4645
|
+
const vars = result.vars ?? {};
|
|
4646
|
+
if (aiBindingName !== "AI") vars.__VOID_AI_BINDING = aiBindingName;
|
|
4647
|
+
if (config.ai?.gateway) vars.__VOID_AI_GATEWAY = config.ai.gateway;
|
|
4648
|
+
result.vars = vars;
|
|
4649
|
+
}
|
|
4650
|
+
const emailDevInboxBinding = workerConfig.assets?.binding ?? resolved.assets?.binding;
|
|
4651
|
+
if (shouldInjectEmailDevInboxVar(routeEnvironment, bindings.needsEmail, emailDevInboxBinding)) {
|
|
4652
|
+
const vars = result.vars ?? {};
|
|
4653
|
+
vars[DEV_INBOX_ENV_VAR] = emailDevInboxBinding;
|
|
4654
|
+
vars[DEV_INBOX_TOKEN_ENV_VAR] = devTriggerToken;
|
|
4655
|
+
result.vars = vars;
|
|
4656
|
+
}
|
|
4657
|
+
if (viteCommand === "serve" && bindings.needsEmail) {
|
|
4658
|
+
const sendEmail = resolved.send_email;
|
|
4659
|
+
if (Array.isArray(sendEmail)) {
|
|
4660
|
+
const kept = sendEmail.filter((entry) => entry?.name !== SEND_EMAIL_BINDING);
|
|
4661
|
+
if (kept.length > 0) resolved.send_email = kept;
|
|
4662
|
+
else delete resolved.send_email;
|
|
4382
4663
|
}
|
|
4383
|
-
if (!created) return;
|
|
4384
|
-
projectId = created.id;
|
|
4385
|
-
slug = created.slug;
|
|
4386
|
-
} else {
|
|
4387
|
-
const project = projects.find((p) => p.id === result);
|
|
4388
|
-
projectId = project.id;
|
|
4389
|
-
slug = project.slug;
|
|
4390
4664
|
}
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4665
|
+
if (viteCommand === "serve" && config.email?.from) {
|
|
4666
|
+
const vars = result.vars ?? {};
|
|
4667
|
+
vars.__VOID_EMAIL_FROM = config.email.from;
|
|
4668
|
+
result.vars = vars;
|
|
4669
|
+
}
|
|
4670
|
+
if (queues.length > 0) result.queues = {
|
|
4671
|
+
producers: queues.map((q) => ({
|
|
4672
|
+
queue: q.name,
|
|
4673
|
+
binding: q.bindingName
|
|
4674
|
+
})),
|
|
4675
|
+
consumers: queues.map((q) => ({
|
|
4676
|
+
queue: q.name,
|
|
4677
|
+
...q.maxBatchSize != null && { max_batch_size: q.maxBatchSize },
|
|
4678
|
+
...q.maxBatchTimeout != null && { max_batch_timeout: q.maxBatchTimeout },
|
|
4679
|
+
...q.maxRetries != null && { max_retries: q.maxRetries },
|
|
4680
|
+
...q.retryDelay != null && { retry_delay: q.retryDelay }
|
|
4681
|
+
}))
|
|
4682
|
+
};
|
|
4683
|
+
if (environmentWebsocketRoutes.length > 0) {
|
|
4684
|
+
const existingDurableObjectBindings = resolved.durable_objects?.bindings ?? [];
|
|
4685
|
+
for (const route of environmentWebsocketRoutes) {
|
|
4686
|
+
const conflict = existingDurableObjectBindings.find((binding) => binding.name === route.bindingName && binding.class_name !== route.className);
|
|
4687
|
+
if (conflict) throw new Error(`websocket: binding '${route.bindingName}' is already configured for class '${conflict.class_name}', but '${route.filePath}' requires '${route.className}'.`);
|
|
4688
|
+
}
|
|
4689
|
+
const missingDurableObjectBindings = environmentWebsocketRoutes.filter((route) => !existingDurableObjectBindings.some((binding) => binding.name === route.bindingName)).map((route) => ({
|
|
4690
|
+
name: route.bindingName,
|
|
4691
|
+
class_name: route.className
|
|
4692
|
+
}));
|
|
4693
|
+
if (missingDurableObjectBindings.length > 0) result.durable_objects = { bindings: [...existingDurableObjectBindings, ...missingDurableObjectBindings] };
|
|
4694
|
+
const currentMigrations = resolved.migrations;
|
|
4695
|
+
const planned = appendSqliteClassMigration(Array.isArray(currentMigrations) ? currentMigrations : [], environmentWebsocketRoutes.map((route) => route.className), "void-ws-v1", existingDurableObjectBindings.map((binding) => binding.class_name));
|
|
4696
|
+
if (planned.added) result.migrations = planned.migrations;
|
|
4697
|
+
}
|
|
4698
|
+
if (durableObjects.length > 0) {
|
|
4699
|
+
const existingBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
|
|
4700
|
+
for (const module of durableObjects) {
|
|
4701
|
+
const conflict = existingBindings.find((binding) => binding.name === module.bindingName && binding.class_name !== module.className);
|
|
4702
|
+
if (conflict) throw new Error(`durable: binding '${module.bindingName}' is already configured for class '${conflict.class_name}', but durable-objects/ requires '${module.className}'.`);
|
|
4703
|
+
}
|
|
4704
|
+
const additions = durableObjects.filter((module) => !existingBindings.some((binding) => binding.name === module.bindingName)).map((module) => ({
|
|
4705
|
+
name: module.bindingName,
|
|
4706
|
+
class_name: module.className
|
|
4707
|
+
}));
|
|
4708
|
+
if (additions.length > 0) result.durable_objects = { bindings: [...existingBindings, ...additions] };
|
|
4709
|
+
const currentMigrations = result.migrations ?? resolved.migrations;
|
|
4710
|
+
const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
|
|
4711
|
+
for (const module of durableObjects) if (existingMigrations.find((migration) => migration.tag === module.migrationTag && !migration.new_sqlite_classes?.includes(module.className))) throw new Error(`durable: migration tag '${module.migrationTag}' is already used by another Durable Object migration.`);
|
|
4712
|
+
const migrationAdditions = durableObjects.filter((module) => !existingMigrations.some((migration) => migration.tag === module.migrationTag || migration.new_sqlite_classes?.includes(module.className))).map((module) => ({
|
|
4713
|
+
tag: module.migrationTag,
|
|
4714
|
+
new_sqlite_classes: [module.className]
|
|
4715
|
+
}));
|
|
4716
|
+
if (migrationAdditions.length > 0) result.migrations = [...existingMigrations, ...migrationAdditions];
|
|
4717
|
+
}
|
|
4718
|
+
if (bindings.needsSandbox && sandboxConfig) {
|
|
4719
|
+
assertSandboxNamespaceConfig({
|
|
4720
|
+
...resolved,
|
|
4721
|
+
...result
|
|
4722
|
+
}, sandboxConfig);
|
|
4723
|
+
const existingDurableObjectBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
|
|
4724
|
+
if (!existingDurableObjectBindings.some((binding) => binding.name === sandboxConfig.binding)) result.durable_objects = { bindings: [...existingDurableObjectBindings, {
|
|
4725
|
+
name: sandboxConfig.binding,
|
|
4726
|
+
class_name: sandboxConfig.className
|
|
4727
|
+
}] };
|
|
4728
|
+
result.containers = [...(result.containers ?? resolved.containers ?? []).filter((container) => container.class_name !== sandboxConfig.className), sandboxContainerConfig(sandboxConfig)];
|
|
4729
|
+
resolved.containers = [];
|
|
4730
|
+
const currentMigrations = result.migrations ?? resolved.migrations;
|
|
4731
|
+
const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
|
|
4732
|
+
if (!hasSandboxSqliteMigration(existingMigrations, sandboxConfig.className)) result.migrations = [...existingMigrations, {
|
|
4733
|
+
tag: SANDBOX_MIGRATION_TAG,
|
|
4734
|
+
new_sqlite_classes: [sandboxConfig.className]
|
|
4735
|
+
}];
|
|
4736
|
+
}
|
|
4737
|
+
if (shouldEmitLive() && liveConfig) {
|
|
4738
|
+
const existingDurableObjectBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
|
|
4739
|
+
if (!existingDurableObjectBindings.some((binding) => binding.name === liveConfig.bindingName)) result.durable_objects = { bindings: [...existingDurableObjectBindings, {
|
|
4740
|
+
name: liveConfig.bindingName,
|
|
4741
|
+
class_name: liveConfig.className
|
|
4742
|
+
}] };
|
|
4743
|
+
const currentMigrations = result.migrations ?? resolved.migrations;
|
|
4744
|
+
const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
|
|
4745
|
+
if (!hasDurableObjectSqliteMigration(existingMigrations, liveConfig.className)) result.migrations = [...existingMigrations, {
|
|
4746
|
+
tag: nextDurableObjectMigrationTag(existingMigrations, LIVE_MIGRATION_TAG),
|
|
4747
|
+
new_sqlite_classes: [liveConfig.className]
|
|
4748
|
+
}];
|
|
4749
|
+
}
|
|
4750
|
+
if (viteCommand === "serve" && bindings.needsAI && !directCloudflareDestination) {
|
|
4751
|
+
const credentials = readVoidDevCredentials(root);
|
|
4752
|
+
if (credentials.status === "ok") {
|
|
4753
|
+
const vars = result.vars ?? {};
|
|
4754
|
+
vars.__VOID_TOKEN = credentials.token;
|
|
4755
|
+
vars.__VOID_PROJECT_ID = credentials.projectId;
|
|
4756
|
+
vars.__VOID_PROXY_URL = credentials.proxyUrl;
|
|
4757
|
+
if (credentials.accessCredentials) vars.__VOID_ACCESS_CREDENTIALS = credentials.accessCredentials;
|
|
4758
|
+
result.vars = vars;
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
if (viteCommand === "serve" && remoteMode && (bindings.needsD1 || bindings.needsKV || bindings.needsR2)) {
|
|
4762
|
+
const credentials = readVoidDevCredentials(root);
|
|
4763
|
+
if (credentials.status === "ok") {
|
|
4764
|
+
const vars = result.vars ?? {};
|
|
4765
|
+
vars.__VOID_PROXY_URL = credentials.proxyUrl;
|
|
4766
|
+
if (credentials.accessCredentials) vars.__VOID_ACCESS_CREDENTIALS = credentials.accessCredentials;
|
|
4767
|
+
vars.__VOID_TOKEN = credentials.token;
|
|
4768
|
+
vars.__VOID_PROJECT_ID = credentials.projectId;
|
|
4769
|
+
vars.__VOID_REMOTE = "1";
|
|
4770
|
+
result.vars = vars;
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
input.versionMetadataBinding = selectNativeVersionMetadataBinding(resolved, {
|
|
4774
|
+
nativeBuild: viteCommand === "build" && process.env["VOID_CLOUDFLARE_DEPLOY_BUILD"] === "1",
|
|
4775
|
+
preserveBindings: process.env[CLOUDFLARE_PRESERVE_BINDINGS_BUILD_ENV] === "1"
|
|
4776
|
+
});
|
|
4777
|
+
if (input.versionMetadataBinding) {
|
|
4778
|
+
const owner = findBindingNameOwner(result, "version_metadata", input.versionMetadataBinding);
|
|
4779
|
+
if (owner) throw new Error(`Cloudflare version metadata binding ${input.versionMetadataBinding} conflicts with ${owner}.`);
|
|
4780
|
+
if (!resolved.version_metadata) result.version_metadata = { binding: input.versionMetadataBinding };
|
|
4781
|
+
}
|
|
4782
|
+
finalizeDurableObjectPatch(resolved, result, [
|
|
4783
|
+
...environmentWebsocketRoutes.map((route) => ({
|
|
4784
|
+
name: route.bindingName,
|
|
4785
|
+
class_name: route.className
|
|
4786
|
+
})),
|
|
4787
|
+
...durableObjects.map((module) => ({
|
|
4788
|
+
name: module.bindingName,
|
|
4789
|
+
class_name: module.className
|
|
4790
|
+
})),
|
|
4791
|
+
...bindings.needsSandbox && sandboxConfig ? [{
|
|
4792
|
+
name: sandboxConfig.binding,
|
|
4793
|
+
class_name: sandboxConfig.className
|
|
4794
|
+
}] : [],
|
|
4795
|
+
...shouldEmitLive() && liveConfig ? [{
|
|
4796
|
+
name: liveConfig.bindingName,
|
|
4797
|
+
class_name: liveConfig.className
|
|
4798
|
+
}] : []
|
|
4799
|
+
]);
|
|
4800
|
+
return result;
|
|
4397
4801
|
}
|
|
4802
|
+
//#endregion
|
|
4803
|
+
//#region src/index.ts
|
|
4398
4804
|
/**
|
|
4399
4805
|
* Void plugin — the main entry point.
|
|
4400
4806
|
*
|
|
@@ -4484,81 +4890,8 @@ function voidPlugin(options) {
|
|
|
4484
4890
|
if (detected && bindings.needsLive) throw new Error("live: void/live is currently supported in Void apps only.");
|
|
4485
4891
|
const hasInboundEmail = hasEmailHandlers(application.email);
|
|
4486
4892
|
if (detected && hasInboundEmail) throw new Error("email: inbound email handlers are only supported in Void apps. Remove the email/ directory — `sendEmail` from void/email keeps working.");
|
|
4487
|
-
const
|
|
4488
|
-
const
|
|
4489
|
-
"void/db": "export const db = {}; export const createDb = () => ({});",
|
|
4490
|
-
"void/kv": "export const kv = {}; export const createKV = () => ({});",
|
|
4491
|
-
"void/ai": "export const ai = {};",
|
|
4492
|
-
"void/queues": "export const queues = {};",
|
|
4493
|
-
"void/storage": "export const storage = {}; export const createStorage = () => ({});",
|
|
4494
|
-
"void/isr": "export const revalidate = async () => {};",
|
|
4495
|
-
"void/sandbox": `
|
|
4496
|
-
export class SandboxLimitError extends Error {
|
|
4497
|
-
constructor(reason = 'concurrency', message) {
|
|
4498
|
-
super(message ?? 'Sandbox limit reached: ' + reason);
|
|
4499
|
-
this.name = 'SandboxLimitError';
|
|
4500
|
-
this.reason = reason;
|
|
4501
|
-
}
|
|
4502
|
-
}
|
|
4503
|
-
export class SandboxUnavailableError extends Error {
|
|
4504
|
-
constructor(message = 'Managed Sandbox is unavailable on this Void platform.') {
|
|
4505
|
-
super(message);
|
|
4506
|
-
this.name = 'SandboxUnavailableError';
|
|
4507
|
-
}
|
|
4508
|
-
}
|
|
4509
|
-
export async function getSandbox() {
|
|
4510
|
-
throw new Error('void/sandbox is server-only and cannot be imported by browser code.');
|
|
4511
|
-
}
|
|
4512
|
-
export const sandbox = { get: getSandbox };
|
|
4513
|
-
`,
|
|
4514
|
-
"void/email": `
|
|
4515
|
-
const serverOnly = (name) => () => {
|
|
4516
|
-
throw new Error(name + ' is server-only and cannot be imported by browser code.');
|
|
4517
|
-
};
|
|
4518
|
-
export const sendEmail = serverOnly('sendEmail');
|
|
4519
|
-
export const createEmail = serverOnly('createEmail');
|
|
4520
|
-
export const defineEmail = serverOnly('defineEmail');
|
|
4521
|
-
export const parseEmail = serverOnly('parseEmail');
|
|
4522
|
-
export const replyEmail = serverOnly('replyEmail');
|
|
4523
|
-
export const createMessageProxy = serverOnly('createMessageProxy');
|
|
4524
|
-
`,
|
|
4525
|
-
"void/email/testing": `
|
|
4526
|
-
const serverOnly = (name) => () => {
|
|
4527
|
-
throw new Error(name + ' is server-only and cannot be imported by browser code.');
|
|
4528
|
-
};
|
|
4529
|
-
export const createEmailTestHarness = serverOnly('createEmailTestHarness');
|
|
4530
|
-
export const createInboundTestHarness = serverOnly('createInboundTestHarness');
|
|
4531
|
-
`,
|
|
4532
|
-
"void/live": "",
|
|
4533
|
-
"void/durable": ""
|
|
4534
|
-
};
|
|
4535
|
-
const STUB_RESOLVE_FILTER = new RegExp(`^(${Object.keys(SERVER_ONLY_STUBS).join("|").replaceAll("/", "\\/")})$`);
|
|
4536
|
-
const STUB_LOAD_FILTER = new RegExp(`^\\0void-client-stub:`);
|
|
4537
|
-
const clientStubsPlugin = {
|
|
4538
|
-
name: "void:client-stubs",
|
|
4539
|
-
enforce: "pre",
|
|
4540
|
-
resolveId: {
|
|
4541
|
-
filter: { id: STUB_RESOLVE_FILTER },
|
|
4542
|
-
handler(id) {
|
|
4543
|
-
if (this.environment?.name === "client") {
|
|
4544
|
-
if (id === "void/live" || id === "void/durable") this.error(id === "void/live" ? "void/live is server-only and cannot be imported by browser code. Import void/live/client instead." : "void/durable is server-only and cannot be imported by browser code.");
|
|
4545
|
-
return STUB_PREFIX + id;
|
|
4546
|
-
}
|
|
4547
|
-
}
|
|
4548
|
-
},
|
|
4549
|
-
load: {
|
|
4550
|
-
filter: { id: STUB_LOAD_FILTER },
|
|
4551
|
-
handler(id) {
|
|
4552
|
-
return SERVER_ONLY_STUBS[id.slice(18)] ?? "export default {};";
|
|
4553
|
-
}
|
|
4554
|
-
}
|
|
4555
|
-
};
|
|
4556
|
-
const serverRuntimeOptimizerPlugin = {
|
|
4557
|
-
name: "void:server-runtime-optimizer",
|
|
4558
|
-
configEnvironment(name) {
|
|
4559
|
-
if (name !== "client") return { optimizeDeps: { exclude: ["void"] } };
|
|
4560
|
-
}
|
|
4561
|
-
};
|
|
4893
|
+
const clientStubsPlugin = createClientStubsPlugin();
|
|
4894
|
+
const serverRuntimeOptimizerPlugin = createServerRuntimeOptimizerPlugin();
|
|
4562
4895
|
const queues = framework ? [] : application.queues;
|
|
4563
4896
|
const websocketRoutes = framework ? [] : application.allWebsockets;
|
|
4564
4897
|
const durableObjects = application.durableObjects;
|
|
@@ -4585,105 +4918,25 @@ function voidPlugin(options) {
|
|
|
4585
4918
|
]
|
|
4586
4919
|
}) : existingLiveConfig : void 0;
|
|
4587
4920
|
const shouldEmitLive = () => bindings.needsLive || existingLiveConfig !== void 0;
|
|
4588
|
-
if (detected && (detected.class === "b" || detected.class === "c")) {
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
}
|
|
4608
|
-
};
|
|
4609
|
-
const syncFrameworkBindings = (command) => {
|
|
4610
|
-
if (command !== "serve") return;
|
|
4611
|
-
if (syncWranglerBindings(root, {
|
|
4612
|
-
...bindings,
|
|
4613
|
-
needsD1: dialect === "sqlite" && application.needsDatabase
|
|
4614
|
-
}, {
|
|
4615
|
-
bindingNames,
|
|
4616
|
-
...directCloudflareDestination && bindings.needsAI ? { directCloudflareAi: { gateway: config.ai?.gateway } } : {},
|
|
4617
|
-
websockets: websocketRoutes,
|
|
4618
|
-
sandbox: sandboxConfig,
|
|
4619
|
-
live: liveConfig
|
|
4620
|
-
})) voidLog("Synced bindings to Cloudflare config");
|
|
4621
|
-
};
|
|
4622
|
-
const wranglerSyncPlugin = {
|
|
4623
|
-
name: "void:wrangler-sync",
|
|
4624
|
-
enforce: "pre",
|
|
4625
|
-
config(_, env) {
|
|
4626
|
-
syncFrameworkBindings(env.command);
|
|
4627
|
-
},
|
|
4628
|
-
configResolved(resolvedConfig) {
|
|
4629
|
-
syncFrameworkCompat(readWranglerCompat(root, config));
|
|
4630
|
-
syncFrameworkBindings(resolvedConfig.command);
|
|
4631
|
-
}
|
|
4632
|
-
};
|
|
4633
|
-
const nuxtVite8WebSocketCompatPlugin = detected.name === "nuxt" ? {
|
|
4634
|
-
name: "void:nuxt-vite8-websocket-compat",
|
|
4635
|
-
enforce: "post",
|
|
4636
|
-
configResolved(resolvedConfig) {
|
|
4637
|
-
if (!resolvedConfig.build.ssr) return;
|
|
4638
|
-
if (typeof resolvedConfig.server.hmr === "object" && resolvedConfig.server.hmr.port !== void 0) resolvedConfig.server.hmr = {
|
|
4639
|
-
...resolvedConfig.server.hmr,
|
|
4640
|
-
server: void 0
|
|
4641
|
-
};
|
|
4642
|
-
if (typeof resolvedConfig.server.ws === "object" && resolvedConfig.server.ws.port !== void 0) resolvedConfig.server.ws = {
|
|
4643
|
-
...resolvedConfig.server.ws,
|
|
4644
|
-
server: void 0
|
|
4645
|
-
};
|
|
4646
|
-
}
|
|
4647
|
-
} : void 0;
|
|
4648
|
-
const svelteKitHooksInjectPlugin = {
|
|
4649
|
-
name: "void:sveltekit-hooks-inject",
|
|
4650
|
-
apply: "serve",
|
|
4651
|
-
transform(code, id) {
|
|
4652
|
-
if (detected.class !== "b") return;
|
|
4653
|
-
if (!id.includes(".svelte-kit/generated/server/internal")) return;
|
|
4654
|
-
return injectSvelteKitHooks(code, id, frameworkTriggersDispatchPath(root));
|
|
4655
|
-
}
|
|
4656
|
-
};
|
|
4657
|
-
return [
|
|
4658
|
-
createCloudflareBuiltinsExternalPlugin(),
|
|
4659
|
-
wranglerCompatPlugin,
|
|
4660
|
-
wranglerSyncPlugin,
|
|
4661
|
-
...nuxtVite8WebSocketCompatPlugin ? [nuxtVite8WebSocketCompatPlugin] : [],
|
|
4662
|
-
svelteKitHooksInjectPlugin,
|
|
4663
|
-
clientStubsPlugin,
|
|
4664
|
-
serverRuntimeOptimizerPlugin,
|
|
4665
|
-
bindingRuntimePlugin(bindingNames),
|
|
4666
|
-
envSchemaPlugin(root),
|
|
4667
|
-
envClientGuardPlugin(root),
|
|
4668
|
-
envClientFoldPlugin(),
|
|
4669
|
-
drizzlePlugin(root, false, dialect, {
|
|
4670
|
-
d1BindingName: bindingNames?.d1,
|
|
4671
|
-
forceVirtualDb: authEnabled || bindingNames?.d1 !== void 0,
|
|
4672
|
-
localD1DatabaseId: localD1.databaseId,
|
|
4673
|
-
localD1PersistRoot: frameworkPersistRoot
|
|
4674
|
-
}),
|
|
4675
|
-
migrationPlugin(root, dialect, localD1.databaseId, frameworkPersistRoot, { enabled: false }),
|
|
4676
|
-
devTriggersHintPlugin(root, {
|
|
4677
|
-
framework: true,
|
|
4678
|
-
devTriggerToken
|
|
4679
|
-
}),
|
|
4680
|
-
frameworkTriggersPlugin(root, { devTriggerToken }),
|
|
4681
|
-
...detected.name === "nuxt" ? [nitroDevTriggerInjectPlugin(root, { serverDir: "server" })] : [],
|
|
4682
|
-
...detected.name === "analog" ? [analogDevTriggerInjectPlugin(root)] : [],
|
|
4683
|
-
...detected.name === "astro" ? [astroDevTriggerInjectPlugin(root)] : [],
|
|
4684
|
-
implicitDeployGuard
|
|
4685
|
-
];
|
|
4686
|
-
}
|
|
4921
|
+
if (detected && (detected.class === "b" || detected.class === "c")) return createFrameworkPlugins({
|
|
4922
|
+
localD1,
|
|
4923
|
+
root,
|
|
4924
|
+
directCloudflareDestination,
|
|
4925
|
+
bindings,
|
|
4926
|
+
config,
|
|
4927
|
+
dialect,
|
|
4928
|
+
application,
|
|
4929
|
+
bindingNames,
|
|
4930
|
+
websocketRoutes,
|
|
4931
|
+
sandboxConfig,
|
|
4932
|
+
liveConfig,
|
|
4933
|
+
detected,
|
|
4934
|
+
clientStubsPlugin,
|
|
4935
|
+
serverRuntimeOptimizerPlugin,
|
|
4936
|
+
authEnabled,
|
|
4937
|
+
devTriggerToken,
|
|
4938
|
+
implicitDeployGuard
|
|
4939
|
+
});
|
|
4687
4940
|
const remoteMode = resolveRemoteMode(config.remote);
|
|
4688
4941
|
const workerConfig = {};
|
|
4689
4942
|
const jobs = application.jobs;
|
|
@@ -4705,217 +4958,34 @@ function voidPlugin(options) {
|
|
|
4705
4958
|
const outDir = join(root, ".void");
|
|
4706
4959
|
mkdirSync(outDir, { recursive: true });
|
|
4707
4960
|
function mergeBindings(resolved) {
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
binding: d1Name,
|
|
4735
|
-
database_name: "default",
|
|
4736
|
-
database_id: "local"
|
|
4737
|
-
});
|
|
4738
|
-
if (d1.length > 0) result.d1_databases = d1;
|
|
4739
|
-
const kvName = bindingNames?.kv ?? "KV";
|
|
4740
|
-
const kv = [];
|
|
4741
|
-
if (bindings.needsKV && !hasBinding(resolved.kv_namespaces, kvName)) kv.push({
|
|
4742
|
-
binding: kvName,
|
|
4743
|
-
id: "local"
|
|
4744
|
-
});
|
|
4745
|
-
if (kv.length > 0) result.kv_namespaces = kv;
|
|
4746
|
-
const r2Name = bindingNames?.r2 ?? "STORAGE";
|
|
4747
|
-
if (bindings.needsR2 && !hasBinding(resolved.r2_buckets, r2Name)) result.r2_buckets = [{
|
|
4748
|
-
binding: r2Name,
|
|
4749
|
-
bucket_name: "default"
|
|
4750
|
-
}];
|
|
4751
|
-
const resolvedAi = resolved.ai;
|
|
4752
|
-
const resultAi = result.ai;
|
|
4753
|
-
const aiBindingName = resolveWorkersAiBindingName(resolvedAi, bindingNames?.ai);
|
|
4754
|
-
if (viteCommand === "serve" && bindings.needsAI && directCloudflareDestination && !resultAi && typeof resolvedAi === "object" && resolvedAi !== null && "binding" in resolvedAi && typeof resolvedAi.binding === "string") result.ai = {
|
|
4755
|
-
...resolvedAi,
|
|
4756
|
-
remote: true
|
|
4757
|
-
};
|
|
4758
|
-
else if (shouldBindWorkersAi(viteCommand, bindings.needsAI, resolvedAi, resultAi, directCloudflareDestination)) result.ai = {
|
|
4759
|
-
binding: aiBindingName,
|
|
4760
|
-
...viteCommand === "serve" && { remote: true }
|
|
4761
|
-
};
|
|
4762
|
-
if (aiBindingName !== "AI" || config.ai?.gateway) {
|
|
4763
|
-
const vars = result.vars ?? {};
|
|
4764
|
-
if (aiBindingName !== "AI") vars.__VOID_AI_BINDING = aiBindingName;
|
|
4765
|
-
if (config.ai?.gateway) vars.__VOID_AI_GATEWAY = config.ai.gateway;
|
|
4766
|
-
result.vars = vars;
|
|
4767
|
-
}
|
|
4768
|
-
const emailDevInboxBinding = workerConfig.assets?.binding ?? resolved.assets?.binding;
|
|
4769
|
-
if (shouldInjectEmailDevInboxVar(routeEnvironment, bindings.needsEmail, emailDevInboxBinding)) {
|
|
4770
|
-
const vars = result.vars ?? {};
|
|
4771
|
-
vars[DEV_INBOX_ENV_VAR] = emailDevInboxBinding;
|
|
4772
|
-
vars[DEV_INBOX_TOKEN_ENV_VAR] = devTriggerToken;
|
|
4773
|
-
result.vars = vars;
|
|
4774
|
-
}
|
|
4775
|
-
if (viteCommand === "serve" && bindings.needsEmail) {
|
|
4776
|
-
const sendEmail = resolved.send_email;
|
|
4777
|
-
if (Array.isArray(sendEmail)) {
|
|
4778
|
-
const kept = sendEmail.filter((entry) => entry?.name !== SEND_EMAIL_BINDING);
|
|
4779
|
-
if (kept.length > 0) resolved.send_email = kept;
|
|
4780
|
-
else delete resolved.send_email;
|
|
4781
|
-
}
|
|
4782
|
-
}
|
|
4783
|
-
if (viteCommand === "serve" && config.email?.from) {
|
|
4784
|
-
const vars = result.vars ?? {};
|
|
4785
|
-
vars.__VOID_EMAIL_FROM = config.email.from;
|
|
4786
|
-
result.vars = vars;
|
|
4787
|
-
}
|
|
4788
|
-
if (queues.length > 0) result.queues = {
|
|
4789
|
-
producers: queues.map((q) => ({
|
|
4790
|
-
queue: q.name,
|
|
4791
|
-
binding: q.bindingName
|
|
4792
|
-
})),
|
|
4793
|
-
consumers: queues.map((q) => ({
|
|
4794
|
-
queue: q.name,
|
|
4795
|
-
...q.maxBatchSize != null && { max_batch_size: q.maxBatchSize },
|
|
4796
|
-
...q.maxBatchTimeout != null && { max_batch_timeout: q.maxBatchTimeout },
|
|
4797
|
-
...q.maxRetries != null && { max_retries: q.maxRetries },
|
|
4798
|
-
...q.retryDelay != null && { retry_delay: q.retryDelay }
|
|
4799
|
-
}))
|
|
4800
|
-
};
|
|
4801
|
-
if (environmentWebsocketRoutes.length > 0) {
|
|
4802
|
-
const existingDurableObjectBindings = resolved.durable_objects?.bindings ?? [];
|
|
4803
|
-
for (const route of environmentWebsocketRoutes) {
|
|
4804
|
-
const conflict = existingDurableObjectBindings.find((binding) => binding.name === route.bindingName && binding.class_name !== route.className);
|
|
4805
|
-
if (conflict) throw new Error(`websocket: binding '${route.bindingName}' is already configured for class '${conflict.class_name}', but '${route.filePath}' requires '${route.className}'.`);
|
|
4806
|
-
}
|
|
4807
|
-
const missingDurableObjectBindings = environmentWebsocketRoutes.filter((route) => !existingDurableObjectBindings.some((binding) => binding.name === route.bindingName)).map((route) => ({
|
|
4808
|
-
name: route.bindingName,
|
|
4809
|
-
class_name: route.className
|
|
4810
|
-
}));
|
|
4811
|
-
if (missingDurableObjectBindings.length > 0) result.durable_objects = { bindings: [...existingDurableObjectBindings, ...missingDurableObjectBindings] };
|
|
4812
|
-
const currentMigrations = resolved.migrations;
|
|
4813
|
-
const planned = appendSqliteClassMigration(Array.isArray(currentMigrations) ? currentMigrations : [], environmentWebsocketRoutes.map((route) => route.className), "void-ws-v1", existingDurableObjectBindings.map((binding) => binding.class_name));
|
|
4814
|
-
if (planned.added) result.migrations = planned.migrations;
|
|
4815
|
-
}
|
|
4816
|
-
if (durableObjects.length > 0) {
|
|
4817
|
-
const existingBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
|
|
4818
|
-
for (const module of durableObjects) {
|
|
4819
|
-
const conflict = existingBindings.find((binding) => binding.name === module.bindingName && binding.class_name !== module.className);
|
|
4820
|
-
if (conflict) throw new Error(`durable: binding '${module.bindingName}' is already configured for class '${conflict.class_name}', but durable-objects/ requires '${module.className}'.`);
|
|
4821
|
-
}
|
|
4822
|
-
const additions = durableObjects.filter((module) => !existingBindings.some((binding) => binding.name === module.bindingName)).map((module) => ({
|
|
4823
|
-
name: module.bindingName,
|
|
4824
|
-
class_name: module.className
|
|
4825
|
-
}));
|
|
4826
|
-
if (additions.length > 0) result.durable_objects = { bindings: [...existingBindings, ...additions] };
|
|
4827
|
-
const currentMigrations = result.migrations ?? resolved.migrations;
|
|
4828
|
-
const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
|
|
4829
|
-
for (const module of durableObjects) if (existingMigrations.find((migration) => migration.tag === module.migrationTag && !migration.new_sqlite_classes?.includes(module.className))) throw new Error(`durable: migration tag '${module.migrationTag}' is already used by another Durable Object migration.`);
|
|
4830
|
-
const migrationAdditions = durableObjects.filter((module) => !existingMigrations.some((migration) => migration.tag === module.migrationTag || migration.new_sqlite_classes?.includes(module.className))).map((module) => ({
|
|
4831
|
-
tag: module.migrationTag,
|
|
4832
|
-
new_sqlite_classes: [module.className]
|
|
4833
|
-
}));
|
|
4834
|
-
if (migrationAdditions.length > 0) result.migrations = [...existingMigrations, ...migrationAdditions];
|
|
4835
|
-
}
|
|
4836
|
-
if (bindings.needsSandbox && sandboxConfig) {
|
|
4837
|
-
assertSandboxNamespaceConfig({
|
|
4838
|
-
...resolved,
|
|
4839
|
-
...result
|
|
4840
|
-
}, sandboxConfig);
|
|
4841
|
-
const existingDurableObjectBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
|
|
4842
|
-
if (!existingDurableObjectBindings.some((binding) => binding.name === sandboxConfig.binding)) result.durable_objects = { bindings: [...existingDurableObjectBindings, {
|
|
4843
|
-
name: sandboxConfig.binding,
|
|
4844
|
-
class_name: sandboxConfig.className
|
|
4845
|
-
}] };
|
|
4846
|
-
result.containers = [...(result.containers ?? resolved.containers ?? []).filter((container) => container.class_name !== sandboxConfig.className), sandboxContainerConfig(sandboxConfig)];
|
|
4847
|
-
resolved.containers = [];
|
|
4848
|
-
const currentMigrations = result.migrations ?? resolved.migrations;
|
|
4849
|
-
const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
|
|
4850
|
-
if (!hasSandboxSqliteMigration(existingMigrations, sandboxConfig.className)) result.migrations = [...existingMigrations, {
|
|
4851
|
-
tag: SANDBOX_MIGRATION_TAG,
|
|
4852
|
-
new_sqlite_classes: [sandboxConfig.className]
|
|
4853
|
-
}];
|
|
4854
|
-
}
|
|
4855
|
-
if (shouldEmitLive() && liveConfig) {
|
|
4856
|
-
const existingDurableObjectBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
|
|
4857
|
-
if (!existingDurableObjectBindings.some((binding) => binding.name === liveConfig.bindingName)) result.durable_objects = { bindings: [...existingDurableObjectBindings, {
|
|
4858
|
-
name: liveConfig.bindingName,
|
|
4859
|
-
class_name: liveConfig.className
|
|
4860
|
-
}] };
|
|
4861
|
-
const currentMigrations = result.migrations ?? resolved.migrations;
|
|
4862
|
-
const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
|
|
4863
|
-
if (!hasDurableObjectSqliteMigration(existingMigrations, liveConfig.className)) result.migrations = [...existingMigrations, {
|
|
4864
|
-
tag: nextDurableObjectMigrationTag(existingMigrations, LIVE_MIGRATION_TAG),
|
|
4865
|
-
new_sqlite_classes: [liveConfig.className]
|
|
4866
|
-
}];
|
|
4867
|
-
}
|
|
4868
|
-
if (viteCommand === "serve" && bindings.needsAI && !directCloudflareDestination) {
|
|
4869
|
-
const credentials = readVoidDevCredentials(root);
|
|
4870
|
-
if (credentials.status === "ok") {
|
|
4871
|
-
const vars = result.vars ?? {};
|
|
4872
|
-
vars.__VOID_TOKEN = credentials.token;
|
|
4873
|
-
vars.__VOID_PROJECT_ID = credentials.projectId;
|
|
4874
|
-
vars.__VOID_PROXY_URL = credentials.proxyUrl;
|
|
4875
|
-
if (credentials.accessCredentials) vars.__VOID_ACCESS_CREDENTIALS = credentials.accessCredentials;
|
|
4876
|
-
result.vars = vars;
|
|
4877
|
-
}
|
|
4878
|
-
}
|
|
4879
|
-
if (viteCommand === "serve" && remoteMode && (bindings.needsD1 || bindings.needsKV || bindings.needsR2)) {
|
|
4880
|
-
const credentials = readVoidDevCredentials(root);
|
|
4881
|
-
if (credentials.status === "ok") {
|
|
4882
|
-
const vars = result.vars ?? {};
|
|
4883
|
-
vars.__VOID_PROXY_URL = credentials.proxyUrl;
|
|
4884
|
-
if (credentials.accessCredentials) vars.__VOID_ACCESS_CREDENTIALS = credentials.accessCredentials;
|
|
4885
|
-
vars.__VOID_TOKEN = credentials.token;
|
|
4886
|
-
vars.__VOID_PROJECT_ID = credentials.projectId;
|
|
4887
|
-
vars.__VOID_REMOTE = "1";
|
|
4888
|
-
result.vars = vars;
|
|
4961
|
+
return mergeWorkerBindings(resolved, {
|
|
4962
|
+
websocketRoutes,
|
|
4963
|
+
routeEnvironment,
|
|
4964
|
+
workerConfig,
|
|
4965
|
+
envSchemaFacts,
|
|
4966
|
+
authEnabled,
|
|
4967
|
+
viteCommand,
|
|
4968
|
+
bindingNames,
|
|
4969
|
+
dialect,
|
|
4970
|
+
application,
|
|
4971
|
+
bindings,
|
|
4972
|
+
directCloudflareDestination,
|
|
4973
|
+
config,
|
|
4974
|
+
devTriggerToken,
|
|
4975
|
+
queues,
|
|
4976
|
+
durableObjects,
|
|
4977
|
+
sandboxConfig,
|
|
4978
|
+
shouldEmitLive,
|
|
4979
|
+
liveConfig,
|
|
4980
|
+
root,
|
|
4981
|
+
remoteMode,
|
|
4982
|
+
get versionMetadataBinding() {
|
|
4983
|
+
return versionMetadataBinding;
|
|
4984
|
+
},
|
|
4985
|
+
set versionMetadataBinding(value) {
|
|
4986
|
+
versionMetadataBinding = value;
|
|
4889
4987
|
}
|
|
4890
|
-
}
|
|
4891
|
-
versionMetadataBinding = selectNativeVersionMetadataBinding(resolved, {
|
|
4892
|
-
nativeBuild: viteCommand === "build" && process.env["VOID_CLOUDFLARE_DEPLOY_BUILD"] === "1",
|
|
4893
|
-
preserveBindings: process.env[CLOUDFLARE_PRESERVE_BINDINGS_BUILD_ENV] === "1"
|
|
4894
4988
|
});
|
|
4895
|
-
if (versionMetadataBinding) {
|
|
4896
|
-
const owner = findBindingNameOwner(result, "version_metadata", versionMetadataBinding);
|
|
4897
|
-
if (owner) throw new Error(`Cloudflare version metadata binding ${versionMetadataBinding} conflicts with ${owner}.`);
|
|
4898
|
-
if (!resolved.version_metadata) result.version_metadata = { binding: versionMetadataBinding };
|
|
4899
|
-
}
|
|
4900
|
-
finalizeDurableObjectPatch(resolved, result, [
|
|
4901
|
-
...environmentWebsocketRoutes.map((route) => ({
|
|
4902
|
-
name: route.bindingName,
|
|
4903
|
-
class_name: route.className
|
|
4904
|
-
})),
|
|
4905
|
-
...durableObjects.map((module) => ({
|
|
4906
|
-
name: module.bindingName,
|
|
4907
|
-
class_name: module.className
|
|
4908
|
-
})),
|
|
4909
|
-
...bindings.needsSandbox && sandboxConfig ? [{
|
|
4910
|
-
name: sandboxConfig.binding,
|
|
4911
|
-
class_name: sandboxConfig.className
|
|
4912
|
-
}] : [],
|
|
4913
|
-
...shouldEmitLive() && liveConfig ? [{
|
|
4914
|
-
name: liveConfig.bindingName,
|
|
4915
|
-
class_name: liveConfig.className
|
|
4916
|
-
}] : []
|
|
4917
|
-
]);
|
|
4918
|
-
return result;
|
|
4919
4989
|
}
|
|
4920
4990
|
function applyEnvSchema(facts) {
|
|
4921
4991
|
envSchemaFacts = facts;
|
|
@@ -5081,6 +5151,18 @@ function voidPlugin(options) {
|
|
|
5081
5151
|
}
|
|
5082
5152
|
}
|
|
5083
5153
|
} : void 0;
|
|
5154
|
+
const vinextWorkerEntry = framework === "vinext-app" || framework === "vinext-pages" ? {
|
|
5155
|
+
name: "void:vinext-worker-entry",
|
|
5156
|
+
enforce: "post",
|
|
5157
|
+
configEnvironment(name, environmentConfig) {
|
|
5158
|
+
if (name !== viteEnvironment.name) return;
|
|
5159
|
+
environmentConfig.build ??= {};
|
|
5160
|
+
environmentConfig.build.rolldownOptions = {
|
|
5161
|
+
...environmentConfig.build.rolldownOptions,
|
|
5162
|
+
input: { index: "virtual:cloudflare/worker-entry" }
|
|
5163
|
+
};
|
|
5164
|
+
}
|
|
5165
|
+
} : void 0;
|
|
5084
5166
|
return [
|
|
5085
5167
|
createCloudflareBuiltinsExternalPlugin(),
|
|
5086
5168
|
createWorkerEnvironmentSanitizerPlugin([viteEnvironment.name, ...viteEnvironment.childEnvironments ?? []]),
|
|
@@ -5104,6 +5186,7 @@ function voidPlugin(options) {
|
|
|
5104
5186
|
}),
|
|
5105
5187
|
migrationPlugin(root, dialect, localD1.databaseId, localD1.persistRoot),
|
|
5106
5188
|
...vinextCompat ? [vinextCompat] : [],
|
|
5189
|
+
...vinextWorkerEntry ? [vinextWorkerEntry] : [],
|
|
5107
5190
|
frameworkBuildWrapperPlugin,
|
|
5108
5191
|
frameworkTriggersPlugin(root, { devTriggerToken }),
|
|
5109
5192
|
devTriggersHintPlugin(root, {
|