void 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{account-cmd-CdUll_35.mjs → account-cmd-B4i71m7k.mjs} +3 -3
- package/dist/{application-analysis-fEqoSeMp.mjs → application-analysis-DYdVoMAY.mjs} +8 -6
- package/dist/{application-routing-DAhNP4u3.mjs → application-routing-C8EqRDfv.mjs} +3 -1
- package/dist/{auth-link-C87vGXM-.mjs → auth-link-BKwHPbdv.mjs} +3 -3
- package/dist/{auth-router-6I81r-ix.mjs → auth-router-DqLx8E0F.mjs} +2 -2
- package/dist/{better-auth-shared-D-6z0q0f.mjs → better-auth-shared-7FM3Uk2N.mjs} +48 -2
- package/dist/{build-cmd-ex1g-j_e.mjs → build-cmd-Dy4psnt1.mjs} +1 -1
- package/dist/{cache-oHzrnY3z.mjs → cache-DV37S6dl.mjs} +1 -1
- package/dist/{cancel-deploy-viAMD0Q_.mjs → cancel-deploy-DMPyu1Vt.mjs} +1 -1
- package/dist/{cf-build-output-BhVxldCw.mjs → cf-build-output-Bapzc_8Z.mjs} +102 -1
- package/dist/cf-build-output-DlBkWdNU.mjs +1 -0
- package/dist/cli/cli.mjs +41 -32
- package/dist/cli/cloudflare-operation-process.mjs +27 -6
- package/dist/cli/env-schema-probe.mjs +2 -2
- package/dist/{client-BEkdj1kx.mjs → client-BEMtySaV.mjs} +8 -1
- package/dist/client-DyxPQnF_.mjs +1 -0
- package/dist/{cloudflare-cmd-DMzCv12s.mjs → cloudflare-cmd-BaqIigwI.mjs} +1 -1
- package/dist/{cloudflare-connect-CCRTVg-I.mjs → cloudflare-connect-pNLQmr_e.mjs} +1 -1
- package/dist/{cloudflare-operations-DsSaw2BT.mjs → cloudflare-operations-PCM4wucv.mjs} +1 -1
- package/dist/{cloudflare-operations-Dh9dEHJj.mjs → cloudflare-operations-vP3b5CQf.mjs} +3 -3
- package/dist/config-BTu1o3Dc.mjs +49 -0
- package/dist/{config-DQfHOKh9.d.mts → config-Du88-7Y8.d.mts} +2 -1
- package/dist/config-entry.d.mts +1 -1
- package/dist/{connect-k7R2rPVA.mjs → connect-DwVsBvVh.mjs} +4 -4
- package/dist/{create-project-DTuy-l0z.mjs → create-project-CgrnvzyA.mjs} +1 -1
- package/dist/{create-project-DV71ol5d.mjs → create-project-CjzbBlTv.mjs} +1 -1
- package/dist/{db-DVOkLH5X.mjs → db-rvo64snS.mjs} +13 -13
- package/dist/{delete-BbP5eopu.mjs → delete-DWX6dP4M.mjs} +1 -1
- package/dist/deploy-CtHE2A3R.mjs +2 -0
- package/dist/{deploy-7H32EnUs.mjs → deploy-hRPvw8ci.mjs} +229 -69
- package/dist/{deploy-managed-static-DlvcvnQT.mjs → deploy-managed-static-CDYA2p3L.mjs} +30 -14
- package/dist/{deploy-transport-BzGEj0Ge.mjs → deploy-transport-JCxw7QDi.mjs} +3 -3
- package/dist/deploy.mjs +4 -3
- package/dist/{dispatch-DoUnxTNo.mjs → dispatch-OVLeWW8K.mjs} +71 -2
- package/dist/{domain-CA2wBgra.mjs → domain-CH5iBD9Y.mjs} +2 -2
- package/dist/{email-DXpCc77w.mjs → email-CbTO1l78.mjs} +58 -9
- package/dist/{email-cloudflare-D03g2TkS.mjs → email-cloudflare-B8-pq_-W.mjs} +106 -5
- package/dist/{env-DgOQ9_8S.mjs → env-BBdw236G.mjs} +3 -3
- package/dist/{env-BJjULvL0.mjs → env-BaMz0j7H.mjs} +6 -1
- package/dist/{env-validation-BmsdCZEk.mjs → env-validation-BfBVvBNe.mjs} +1 -1
- package/dist/{env-validation-Dwq3PJzK.mjs → env-validation-XcukUlFM.mjs} +1 -1
- package/dist/{gen-BfmcIi2g.mjs → gen-CCx3t2Ju.mjs} +1 -1
- package/dist/gen-TgXy93Rr.mjs +1 -0
- package/dist/{github-cmd-CY9LUqLh.mjs → github-cmd-BzkRLkbg.mjs} +1 -1
- package/dist/{handler-BbkSPzGF.d.mts → handler-CQv5YDS0.d.mts} +1 -1
- package/dist/{help-CxSlPpzJ.mjs → help-CKbrhZha.mjs} +16 -1
- package/dist/help-qOZr3wng.mjs +1 -0
- package/dist/index.d.mts +2 -4
- package/dist/index.mjs +317 -122
- package/dist/{info-7VDlhtyy.mjs → info-oBm8yK8J.mjs} +2 -2
- package/dist/{init-DxYyOs4-.mjs → init-CBp_uMve.mjs} +217 -197
- package/dist/{link-Ddmod1dZ.mjs → link-Dt_QXmM4.mjs} +2 -2
- package/dist/{list-MogaugiO.mjs → list-DF6asTdt.mjs} +2 -2
- package/dist/{live-Bt8Y_0bg.d.mts → live-CXJ9yC98.d.mts} +1 -1
- package/dist/{local-d1-BHBnKWRb.mjs → local-d1-CUhH3qtj.mjs} +1 -1
- package/dist/{login-Cttnagmm.mjs → login-BjSyhXlT.mjs} +2 -2
- package/dist/login-Cdo8Aco1.mjs +1 -0
- package/dist/{logs-CAszUWZI.mjs → logs-DmvBl6xA.mjs} +2 -2
- package/dist/{migrate-DyT2jJ8y.mjs → migrate-BXRHpYwR.mjs} +1 -1
- package/dist/migrate-COPncCGx.mjs +1 -0
- package/dist/nitro.mjs +2 -2
- package/dist/{node-WB7c2N4L.mjs → node-DtD7ZiPf.mjs} +4 -4
- package/dist/{operator-args-UnE8Y2eK.mjs → operator-args-BsJIYXaT.mjs} +8 -1
- package/dist/operator-cmd-DDiX7Y3f.mjs +1 -0
- package/dist/{operator-cmd-skshjMGl.mjs → operator-cmd-DjZKGGgN.mjs} +4 -3
- package/dist/pages/client.d.mts +17 -4
- package/dist/pages/client.mjs +161 -29
- package/dist/pages/index.d.mts +18 -2
- package/dist/pages/index.mjs +2 -52
- package/dist/pages/islands-plugin.d.mts +1 -1
- package/dist/pages/islands-plugin.mjs +9 -3
- package/dist/pages/protocol.d.mts +2 -2
- package/dist/platform-args-BC8_hsDj.mjs +1 -0
- package/dist/{platform-args-DVbVClZl.mjs → platform-args-BMD2gjc_.mjs} +1 -1
- package/dist/{platform-auth-config-C-pm5Qzx.mjs → platform-auth-config-BgnuMbml.mjs} +3 -3
- package/dist/{platform-auth-protection-SHhvim1L.mjs → platform-auth-protection-NjRejttU.mjs} +2 -2
- package/dist/{platform-auth-recovery-DXVWs-wA.mjs → platform-auth-recovery-Ug12d9W5.mjs} +2 -2
- package/dist/{platform-cmd-PiNRkopU.mjs → platform-cmd-BYKLMPCn.mjs} +1 -1
- package/dist/{platform-cmd-D5I1beUX.mjs → platform-cmd-HByuZAO7.mjs} +4 -4
- package/dist/{platform-domain-jpmBfZeb.mjs → platform-domain-DLSQ8GF8.mjs} +2 -2
- package/dist/{platform-lifecycle-DyKmuNY6.mjs → platform-lifecycle-3dwFJmnq.mjs} +102 -40
- package/dist/{platform-lifecycle-DH3Yr1qQ.mjs → platform-lifecycle-BRqcxerG.mjs} +1 -1
- package/dist/{platform-management-7m-PLaPJ.mjs → platform-management-ZK6XcbCo.mjs} +1 -1
- package/dist/{platform-management--vDwSn35.mjs → platform-management-ZZ6bQM5j.mjs} +1 -1
- package/dist/{platform-recovery-BiNpZZab.mjs → platform-recovery-8vNE5dNN.mjs} +2 -2
- package/dist/{plugin-inference-DUjCd55H.mjs → plugin-inference-CeiB6BG5.mjs} +53 -18
- package/dist/{prepare-BcAE5Sdj.mjs → prepare-5BR7Splm.mjs} +1 -1
- package/dist/prepare-B3VrSF1Z.mjs +1 -0
- package/dist/{prepare-DA-17DhO.mjs → prepare-Dseww74N.mjs} +4 -4
- package/dist/{prerender-render-Cijv7UFo.mjs → prerender-render-CaShbHJ3.mjs} +13 -1
- package/dist/prerender-render.d.mts +1 -0
- package/dist/prerender-render.mjs +1 -1
- package/dist/{project-cmd-DA4K5faU.mjs → project-cmd-CjLNNxwe.mjs} +15 -15
- package/dist/{project-team-CQBNveX3.mjs → project-team-DE5drjcl.mjs} +1 -1
- package/dist/{project-token-CCalFyQf.mjs → project-token-XEYEXfiJ.mjs} +1 -1
- package/dist/{project-zero-trust-DPnkiEuT.mjs → project-zero-trust-6VPU8zmV.mjs} +1 -1
- package/dist/{protocol-BBDpJz3G.d.mts → protocol-BekmFxCI.d.mts} +2 -2
- package/dist/{provision-DtROpNNI.mjs → provision-B7UFrBH7.mjs} +1 -1
- package/dist/{provision-BFLCrnUp.mjs → provision-Chq0NRAV.mjs} +1 -1
- package/dist/{requests-BpH0kImL.mjs → requests-CG-Pj8ze.mjs} +1 -1
- package/dist/{rollback-CXS1tAa-.mjs → rollback-CIrm2txZ.mjs} +2 -2
- package/dist/{route-types-BZi26DVS.mjs → route-types--PAOzHlH.mjs} +49 -28
- package/dist/routes-stub.d.mts +22 -13
- package/dist/runtime/better-auth-mysql.mjs +1 -1
- package/dist/runtime/better-auth-pg.mjs +1 -1
- package/dist/runtime/better-auth.mjs +1 -1
- package/dist/runtime/handler.d.mts +1 -1
- package/dist/runtime/live-client.d.mts +1 -1
- package/dist/runtime/live.d.mts +1 -1
- package/dist/runtime/log.mjs +19 -1
- package/dist/runtime/routing.d.mts +2 -2
- package/dist/runtime/routing.mjs +4 -3
- 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/{scan-8IzYXiU_.mjs → scan-N9WzdcAl.mjs} +302 -23
- package/dist/{secret-BbXHfLAt.mjs → secret-BL6g7mF5.mjs} +3 -3
- package/dist/static-asset-marker-Btf-6oCD.mjs +32 -0
- package/dist/{types-7AIz0OZu.d.mts → types-w72dljaA.d.mts} +6 -1
- package/dist/{version-metadata-tS39iW09.mjs → version-metadata-DmaZNTYW.mjs} +1 -1
- package/dist/worker-conditions-AWFRcFxR.mjs +11 -0
- package/dist/{wrangler-F96NnLyb.mjs → wrangler-CDiEBCdS.mjs} +2 -1
- package/dist/wrangler-DOPr4jyI.mjs +1 -0
- package/dist/{wrapper-D3sZyh2C.mjs → wrapper-CEiDeCqD.mjs} +6 -10
- package/dist/{ws-BH-im_tX.d.mts → ws-D1zWiB_P.d.mts} +1 -1
- package/docs/guide/auth.md +3 -1
- package/docs/guide/edge/static-assets.md +10 -0
- package/docs/guide/email/domains.md +1 -1
- package/docs/guide/email/receiving.md +2 -0
- package/docs/guide/email/sending.md +1 -1
- package/docs/guide/pages-routing/actions-and-forms.md +19 -13
- package/docs/guide/pages-routing/markdown.md +2 -0
- package/docs/guide/pages-routing/overview.md +33 -2
- package/docs/guide/platform/administration/email.md +18 -0
- package/docs/guide/platform/installation/prerequisites.md +9 -4
- package/docs/guide/platform/installation/uninstall.md +2 -0
- package/docs/guide/queues.md +13 -1
- package/docs/guide/server-routing.md +38 -2
- package/docs/guide/type-safety.md +7 -7
- package/docs/guide/websockets.md +4 -2
- package/docs/reference/api/pages.md +4 -4
- package/docs/reference/cli/email.md +13 -0
- package/docs/reference/cli/platform-email.md +2 -2
- package/docs/reference/cli.md +1 -0
- package/docs/reference/structure.md +3 -1
- package/package.json +8 -7
- package/templates/assets/icons.svg +49 -0
- package/templates/assets/react.svg +1 -0
- package/templates/assets/solid.svg +1 -0
- package/templates/assets/stack.svg +35 -0
- package/templates/assets/svelte.svg +1 -0
- package/templates/assets/void.svg +5 -0
- package/templates/assets/vue.svg +1 -0
- package/dist/cf-build-output-BJHuSsVN.mjs +0 -1
- package/dist/client-BiRDZTSd.mjs +0 -1
- package/dist/config-CyUJzi3q.mjs +0 -32
- package/dist/deploy-CE0Ev0Ob.mjs +0 -2
- package/dist/gen-NrOlWZaG.mjs +0 -1
- package/dist/help-BeAFFE5C.mjs +0 -1
- package/dist/login-DgDwlKc_.mjs +0 -1
- package/dist/migrate-n49ZeS1X.mjs +0 -1
- package/dist/operator-cmd-d2MvJOrl.mjs +0 -1
- package/dist/parse-filename-DioPHiR9.mjs +0 -77
- package/dist/platform-args-kQrgxScY.mjs +0 -1
- package/dist/prepare-C4I0f--h.mjs +0 -1
- package/dist/wrangler-BLb3_luP.mjs +0 -1
|
@@ -1,13 +1,136 @@
|
|
|
1
|
-
import { a as join, n as dirname, r as extname, t as basename } from "./dist-ipQi0Q8y.mjs";
|
|
1
|
+
import { a as join, l as toSlash, n as dirname, r as extname, t as basename } from "./dist-ipQi0Q8y.mjs";
|
|
2
2
|
import { n as readProjectPaths } from "./project-paths-DGu_QZ_Q.mjs";
|
|
3
3
|
import { t as glob } from "./dist-D15hFhP0.mjs";
|
|
4
4
|
import { n as isIdentifier, o as moduleExportName, r as isLiteral } from "./ast-CsZ_q_gc.mjs";
|
|
5
5
|
import { a as staticLiteral, i as staticExportInitializers, t as bindingNames } from "./ast-bindings-C3yaPs1y.mjs";
|
|
6
|
-
import { n as parseRouteFilename } from "./parse-filename-DioPHiR9.mjs";
|
|
7
6
|
import { i as voidWarn } from "./log-BMwmN5Ae.mjs";
|
|
8
7
|
import { t as MagicString } from "./dist-Cegxq8gs.mjs";
|
|
9
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
10
9
|
import { parseSync } from "vite";
|
|
10
|
+
//#region src/shared/route-extensions.ts
|
|
11
|
+
const ROUTE_EXTENSIONS = [
|
|
12
|
+
"ts",
|
|
13
|
+
"tsx",
|
|
14
|
+
"js",
|
|
15
|
+
"jsx",
|
|
16
|
+
"mjs",
|
|
17
|
+
"mts"
|
|
18
|
+
];
|
|
19
|
+
const ROUTE_EXTENSIONS_SET = new Set(ROUTE_EXTENSIONS.map((ext) => `.${ext}`));
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/router/parse-filename.ts
|
|
22
|
+
const ENV_SUFFIXES = ["dev", "prod"];
|
|
23
|
+
function parsePath(relativePath, isWebSocketRoute) {
|
|
24
|
+
const filePath = relativePath;
|
|
25
|
+
let path = relativePath.replace(/\\/g, "/");
|
|
26
|
+
for (const ext of ROUTE_EXTENSIONS) if (path.endsWith(`.${ext}`)) {
|
|
27
|
+
path = path.slice(0, -(ext.length + 1));
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
if (isWebSocketRoute && path.endsWith(".ws")) path = path.slice(0, -3);
|
|
31
|
+
const segments = path.split("/");
|
|
32
|
+
let filename = segments.pop();
|
|
33
|
+
let env = null;
|
|
34
|
+
for (const suffix of ENV_SUFFIXES) if (filename.endsWith(`.${suffix}`)) {
|
|
35
|
+
env = suffix;
|
|
36
|
+
filename = filename.slice(0, -(suffix.length + 1));
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
segments.push(filename);
|
|
40
|
+
if (segments[segments.length - 1] === "index") segments.pop();
|
|
41
|
+
const filtered = segments.filter((s) => !s.startsWith("(") || !s.endsWith(")"));
|
|
42
|
+
const params = [];
|
|
43
|
+
let catchAll = false;
|
|
44
|
+
return {
|
|
45
|
+
pattern: "/" + filtered.map((segment) => {
|
|
46
|
+
if (segment.startsWith("[...") && segment.endsWith("]")) {
|
|
47
|
+
const paramName = segment.slice(4, -1);
|
|
48
|
+
catchAll = true;
|
|
49
|
+
if (paramName) {
|
|
50
|
+
params.push(paramName);
|
|
51
|
+
return `:${paramName}{.+}`;
|
|
52
|
+
}
|
|
53
|
+
return "*";
|
|
54
|
+
}
|
|
55
|
+
if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
56
|
+
const paramName = segment.slice(1, -1);
|
|
57
|
+
params.push(paramName);
|
|
58
|
+
return `:${paramName}`;
|
|
59
|
+
}
|
|
60
|
+
return segment;
|
|
61
|
+
}).join("/"),
|
|
62
|
+
methods: [],
|
|
63
|
+
params,
|
|
64
|
+
catchAll,
|
|
65
|
+
env,
|
|
66
|
+
filePath
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Parse a route filename (relative to routes/) into a RouteDefinition.
|
|
71
|
+
*
|
|
72
|
+
* Rules (applied in order):
|
|
73
|
+
* 1. Strip file extension (.ts, .tsx, .js, .jsx, .mts, .mjs)
|
|
74
|
+
* 2. Check for env suffix (.dev, .prod) → strip and record
|
|
75
|
+
* 3. Strip trailing "index" segment
|
|
76
|
+
* 4. Remove route group directories: (groupname)
|
|
77
|
+
* 5. Convert [param] → :param, [...param] → :param{.+}, [...] → *
|
|
78
|
+
* 6. Prefix with /
|
|
79
|
+
*/
|
|
80
|
+
function parseRouteFilename(relativePath) {
|
|
81
|
+
return parsePath(relativePath, false);
|
|
82
|
+
}
|
|
83
|
+
/** Parse a `.ws.*` route filename into a regular route definition. */
|
|
84
|
+
function parseWebSocketFilename(relativePath) {
|
|
85
|
+
return parsePath(relativePath, true);
|
|
86
|
+
}
|
|
87
|
+
function isRouteEnabledForEnvironment(route, environment) {
|
|
88
|
+
return environment === void 0 || route.env === null || route.env === environment;
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/shared/source-analysis-cache.ts
|
|
92
|
+
/**
|
|
93
|
+
* How long after a file changes its stat is trusted to identify its contents.
|
|
94
|
+
* Some file systems keep coarse timestamps (FAT: 2 s, HFS+: 1 s, ext4: one
|
|
95
|
+
* kernel tick) and Linux reuses inodes, so a second write inside that window
|
|
96
|
+
* can leave the signature unchanged. Like Git's racily clean index entries,
|
|
97
|
+
* values derived from recently changed files are recomputed on the next read.
|
|
98
|
+
*/
|
|
99
|
+
const RACY_WINDOW_MS = 3e3;
|
|
100
|
+
const entries = /* @__PURE__ */ new Map();
|
|
101
|
+
/**
|
|
102
|
+
* Derive a value from a source file, reusing it while the file is unchanged.
|
|
103
|
+
* A dev server rescans every source file on each file event, and several
|
|
104
|
+
* plugins scan the same pages, but only edited files need to be read and
|
|
105
|
+
* parsed again. `kind` separates analyses of the same file. `derive` must be a
|
|
106
|
+
* pure function of the source, and callers must not mutate its result.
|
|
107
|
+
*/
|
|
108
|
+
function deriveFromSource(path, kind, derive) {
|
|
109
|
+
const key = toSlash(path);
|
|
110
|
+
const stat = statSync(key, {
|
|
111
|
+
bigint: true,
|
|
112
|
+
throwIfNoEntry: false
|
|
113
|
+
});
|
|
114
|
+
if (!stat) return derive(readFileSync(key, "utf-8"));
|
|
115
|
+
const signature = `${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
116
|
+
const changedAt = Number((stat.mtimeNs > stat.ctimeNs ? stat.mtimeNs : stat.ctimeNs) / 1000000n);
|
|
117
|
+
const trusted = Date.now() - changedAt > RACY_WINDOW_MS;
|
|
118
|
+
const entry = entries.get(key);
|
|
119
|
+
if (trusted && entry?.signature === signature && entry.derived.has(kind)) return entry.derived.get(kind);
|
|
120
|
+
const value = derive(readFileSync(key, "utf-8"));
|
|
121
|
+
if (!trusted) entries.delete(key);
|
|
122
|
+
else if (entry?.signature === signature) entry.derived.set(kind, value);
|
|
123
|
+
else entries.set(key, {
|
|
124
|
+
signature,
|
|
125
|
+
derived: /* @__PURE__ */ new Map([[kind, value]])
|
|
126
|
+
});
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
/** Forget values derived from a file. Dev servers call this for watcher events. */
|
|
130
|
+
function invalidateSourceAnalysis(path) {
|
|
131
|
+
entries.delete(toSlash(path));
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
11
134
|
//#region src/pages/island-imports.ts
|
|
12
135
|
/** The whole JS/TS module, or each `<script>` block of a Vue/Svelte SFC with its offset in the file. */
|
|
13
136
|
function scriptBlocks(code, filename) {
|
|
@@ -150,6 +273,99 @@ function resolveLayoutIds(page, layouts, namedLayouts) {
|
|
|
150
273
|
return resolveEffectiveLayoutEntries(page, layouts, namedLayouts).map((layout) => layout.id);
|
|
151
274
|
}
|
|
152
275
|
//#endregion
|
|
276
|
+
//#region src/pages/manifest.ts
|
|
277
|
+
/**
|
|
278
|
+
* Generates the component import map and layout tree as code strings.
|
|
279
|
+
* Used by both SSR renderer and client entry virtual modules.
|
|
280
|
+
*
|
|
281
|
+
* Client entries load this eagerly, so it grows with every page. Each page id
|
|
282
|
+
* is written once, identical layout chains are shared, and static routes are
|
|
283
|
+
* matched by exact lookup instead of a RegExp per page. The emitted bindings
|
|
284
|
+
* (`components`, `layoutTree`, `routeMeta`, `matchRoute`) keep their shape;
|
|
285
|
+
* `matchRoute` takes a decoded pathname (see `decodePagePath`).
|
|
286
|
+
*/
|
|
287
|
+
function generateComponentManifest(scan, pagesDir) {
|
|
288
|
+
const load = (filePath) => `() => import(${JSON.stringify(`${pagesDir}/${filePath}`)})`;
|
|
289
|
+
const pageIds = new Set(scan.pages.map((page) => page.componentId));
|
|
290
|
+
const layoutKey = (id) => {
|
|
291
|
+
if (!pageIds.has(id)) return id;
|
|
292
|
+
const slash = id.lastIndexOf("/");
|
|
293
|
+
return id.slice(0, slash + 1) + "_" + id.slice(slash + 1);
|
|
294
|
+
};
|
|
295
|
+
const chains = [];
|
|
296
|
+
const chainIndexes = /* @__PURE__ */ new Map();
|
|
297
|
+
const staticRows = [];
|
|
298
|
+
const dynamicRows = [];
|
|
299
|
+
let inStaticPrefix = true;
|
|
300
|
+
for (const page of scan.pages) {
|
|
301
|
+
const chain = JSON.stringify(resolveLayoutIds(page, scan.layouts, scan.namedLayouts).map(layoutKey));
|
|
302
|
+
let chainIndex = chain === "[]" ? -1 : chainIndexes.get(chain);
|
|
303
|
+
if (chainIndex === void 0) {
|
|
304
|
+
chainIndex = chains.push(chain) - 1;
|
|
305
|
+
chainIndexes.set(chain, chainIndex);
|
|
306
|
+
}
|
|
307
|
+
const id = JSON.stringify(page.componentId);
|
|
308
|
+
inStaticPrefix &&= isStaticPattern(page.pattern);
|
|
309
|
+
if (inStaticPrefix) {
|
|
310
|
+
const pattern = page.pattern === `/${page.componentId}` ? "" : `, ${JSON.stringify(page.pattern)}`;
|
|
311
|
+
staticRows.push(` [${id}, ${load(page.componentPath)}, ${chainIndex}${pattern}],`);
|
|
312
|
+
} else dynamicRows.push(` [${patternToRegex(page.pattern)}, ${id}, ${load(page.componentPath)}, ${chainIndex}],`);
|
|
313
|
+
}
|
|
314
|
+
const lines = [];
|
|
315
|
+
lines.push("const components = {};");
|
|
316
|
+
lines.push(`const __voidLayoutChains = [${chains.join(", ")}];`);
|
|
317
|
+
lines.push("const __voidStaticRoutes = [", ...staticRows, "];");
|
|
318
|
+
lines.push("const __voidDynamicRoutes = [", ...dynamicRows, "];");
|
|
319
|
+
lines.push(`const layoutTree = {};
|
|
320
|
+
const __voidStaticRouteIds = new Map();
|
|
321
|
+
for (const [id, load, chain, pattern = "/" + id] of __voidStaticRoutes) {
|
|
322
|
+
components[id] = load;
|
|
323
|
+
if (chain >= 0) layoutTree[id] = __voidLayoutChains[chain];
|
|
324
|
+
if (!__voidStaticRouteIds.has(pattern)) __voidStaticRouteIds.set(pattern, id);
|
|
325
|
+
}
|
|
326
|
+
const routes = [];
|
|
327
|
+
for (const [pattern, id, load, chain] of __voidDynamicRoutes) {
|
|
328
|
+
components[id] = load;
|
|
329
|
+
if (chain >= 0) layoutTree[id] = __voidLayoutChains[chain];
|
|
330
|
+
routes.push({ pattern, id });
|
|
331
|
+
}`);
|
|
332
|
+
for (const layout of [...scan.layouts, ...scan.namedLayouts]) lines.push(`components[${JSON.stringify(layoutKey(toLayoutId(layout)))}] = ${load(layout.filePath)};`);
|
|
333
|
+
lines.push("const routeMeta = {");
|
|
334
|
+
for (const page of scan.pages) if (page.island) lines.push(` ${JSON.stringify(page.componentId)}: { island: true },`);
|
|
335
|
+
lines.push("};");
|
|
336
|
+
lines.push(`function matchRoute(path) {
|
|
337
|
+
const id = __voidStaticRouteIds.get(path);
|
|
338
|
+
if (id !== undefined) return id;
|
|
339
|
+
for (const r of routes) {
|
|
340
|
+
if (r.pattern.test(path)) return r.id;
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}`);
|
|
344
|
+
return lines.join("\n");
|
|
345
|
+
}
|
|
346
|
+
const REGEX_SYNTAX = ".*+?^${}()|[]\\";
|
|
347
|
+
/** The capture group for `*`, `:name{.+}` and `:name`; other segments match as written. */
|
|
348
|
+
function captureGroup(segment) {
|
|
349
|
+
if (segment === "*") return "(.*)";
|
|
350
|
+
if (segment.length < 2 || segment[0] !== ":") return;
|
|
351
|
+
const name = segment.endsWith("{.+}") ? segment.slice(1, -4) : "";
|
|
352
|
+
return name && !name.includes("{") && !name.includes("}") ? "(.+)" : "([^\\/]+)";
|
|
353
|
+
}
|
|
354
|
+
/** Whether `patternToRegex` matches only the pattern's own text. */
|
|
355
|
+
function isStaticPattern(pattern) {
|
|
356
|
+
return pattern.split("/").every((segment) => captureGroup(segment) === void 0);
|
|
357
|
+
}
|
|
358
|
+
/** The RegExp literal a page pattern matches with, the same on the client and the server. */
|
|
359
|
+
function patternToRegex(pattern) {
|
|
360
|
+
return `/^${pattern.split("/").map((segment) => {
|
|
361
|
+
const group = captureGroup(segment);
|
|
362
|
+
if (group) return group;
|
|
363
|
+
let literal = "";
|
|
364
|
+
for (const char of segment) literal += REGEX_SYNTAX.includes(char) ? "\\" + char : char;
|
|
365
|
+
return literal;
|
|
366
|
+
}).join("\\/")}$/`;
|
|
367
|
+
}
|
|
368
|
+
//#endregion
|
|
153
369
|
//#region src/pages/scan.ts
|
|
154
370
|
const COMPONENT_EXTENSIONS = [
|
|
155
371
|
"vue",
|
|
@@ -158,13 +374,6 @@ const COMPONENT_EXTENSIONS = [
|
|
|
158
374
|
"svelte",
|
|
159
375
|
"md"
|
|
160
376
|
];
|
|
161
|
-
const SERVER_EXTENSIONS = [
|
|
162
|
-
"ts",
|
|
163
|
-
"tsx",
|
|
164
|
-
"js",
|
|
165
|
-
"mjs",
|
|
166
|
-
"mts"
|
|
167
|
-
];
|
|
168
377
|
const PAGE_METHODS = /* @__PURE__ */ new Set([
|
|
169
378
|
"loader",
|
|
170
379
|
"action",
|
|
@@ -173,6 +382,8 @@ const PAGE_METHODS = /* @__PURE__ */ new Set([
|
|
|
173
382
|
const MAX_REVALIDATE = 31536e3;
|
|
174
383
|
/** Layout paths already reported; a dev server rescans pages on every file change. */
|
|
175
384
|
const warnedLayoutPaths = /* @__PURE__ */ new Set();
|
|
385
|
+
/** Unreachable pages already reported, for the same reason. */
|
|
386
|
+
const warnedShadowedPages = /* @__PURE__ */ new Set();
|
|
176
387
|
function componentLayout(source, filename) {
|
|
177
388
|
if (!source.includes("layout")) return;
|
|
178
389
|
for (const script of scriptBlocks(source, filename)) {
|
|
@@ -255,6 +466,23 @@ function resolvePageRendering(page, options) {
|
|
|
255
466
|
shouldPrerender: prerenderMode === "force" || prerenderMode === "auto" && inferAutoPrerender(page, options)
|
|
256
467
|
};
|
|
257
468
|
}
|
|
469
|
+
/**
|
|
470
|
+
* The environment a file under pages/ is limited to, read from its name the way
|
|
471
|
+
* scanPages does: `home.dev.tsx`, `home.dev.island.vue` and `home.dev.server.ts`
|
|
472
|
+
* give 'dev'. Files the scanner skips (`_` segments, other extensions) give null.
|
|
473
|
+
*/
|
|
474
|
+
function pageFileEnvironment(relativePath) {
|
|
475
|
+
const segments = toSlash(relativePath).split("/");
|
|
476
|
+
if (segments.some((segment) => segment.startsWith("_"))) return null;
|
|
477
|
+
const file = segments[segments.length - 1];
|
|
478
|
+
const ext = extname(file);
|
|
479
|
+
let base = basename(file, ext);
|
|
480
|
+
if (ROUTE_EXTENSIONS_SET.has(ext) && base.endsWith(".server")) base = base.slice(0, -7);
|
|
481
|
+
else if (COMPONENT_EXTENSIONS.includes(ext.slice(1))) {
|
|
482
|
+
if (base.endsWith(".island")) base = base.slice(0, -7);
|
|
483
|
+
} else return null;
|
|
484
|
+
return base.endsWith(".dev") ? "dev" : base.endsWith(".prod") ? "prod" : null;
|
|
485
|
+
}
|
|
258
486
|
async function scanPages(root, options) {
|
|
259
487
|
const pagesDir = options?.pagesDir ?? options?.paths?.pagesDir ?? readProjectPaths(root).pagesDir;
|
|
260
488
|
if (!existsSync(pagesDir)) return {
|
|
@@ -270,11 +498,12 @@ async function scanPages(root, options) {
|
|
|
270
498
|
const pages = [];
|
|
271
499
|
const layouts = [];
|
|
272
500
|
const namedLayouts = [];
|
|
273
|
-
for (const file of files) {
|
|
501
|
+
for (const file of files.sort()) {
|
|
274
502
|
const segments = file.split("/");
|
|
275
503
|
if (segments.some((s) => s.startsWith("_") && s !== "_layouts")) continue;
|
|
276
504
|
const ext = extname(file);
|
|
277
505
|
let base = basename(file, ext);
|
|
506
|
+
if (base.endsWith(".server") && ROUTE_EXTENSIONS_SET.has(ext)) continue;
|
|
278
507
|
let island;
|
|
279
508
|
let hasIslandSuffix = false;
|
|
280
509
|
if (base.endsWith(".island")) {
|
|
@@ -302,8 +531,8 @@ async function scanPages(root, options) {
|
|
|
302
531
|
});
|
|
303
532
|
continue;
|
|
304
533
|
}
|
|
305
|
-
const
|
|
306
|
-
|
|
534
|
+
const route = parseRouteFilename(hasIslandSuffix ? file.replace(`.island${ext}`, ".ts") : file.replace(ext, ".ts"));
|
|
535
|
+
if (!isRouteEnabledForEnvironment(route, options?.environment)) continue;
|
|
307
536
|
const componentId = file.replace(ext, "").replace(/\.island$/, "").replace(/\/index$/, "").replace(/^index$/, "") || "index";
|
|
308
537
|
let serverPath = null;
|
|
309
538
|
const methods = [];
|
|
@@ -312,11 +541,11 @@ async function scanPages(root, options) {
|
|
|
312
541
|
let ssr;
|
|
313
542
|
let hasGetPrerenderPaths;
|
|
314
543
|
const serverBase = hasIslandSuffix ? file.replace(`.island${ext}`, "") : file.replace(ext, "");
|
|
315
|
-
for (const serverExt of
|
|
544
|
+
for (const serverExt of ROUTE_EXTENSIONS) {
|
|
316
545
|
const candidate = `${serverBase}.server.${serverExt}`;
|
|
317
546
|
if (existsSync(join(pagesDir, candidate))) {
|
|
318
547
|
serverPath = candidate;
|
|
319
|
-
const metadata =
|
|
548
|
+
const metadata = deriveFromSource(join(pagesDir, candidate), "page-server", (source) => detectPageServerMetadata(source, candidate));
|
|
320
549
|
methods.push(...metadata.methods);
|
|
321
550
|
revalidate = metadata.revalidate;
|
|
322
551
|
prerender = metadata.prerender;
|
|
@@ -338,11 +567,11 @@ async function scanPages(root, options) {
|
|
|
338
567
|
if (rendering.shouldPrerender && revalidate === void 0) revalidate = MAX_REVALIDATE;
|
|
339
568
|
let metadata;
|
|
340
569
|
let layout;
|
|
341
|
-
if (ext === ".md") {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
570
|
+
if (ext === ".md") ({metadata, layout} = deriveFromSource(join(pagesDir, file), "page-markdown", (source) => ({
|
|
571
|
+
metadata: extractFrontmatterMeta(source),
|
|
572
|
+
layout: extractFrontmatterLayout(source)
|
|
573
|
+
})));
|
|
574
|
+
else layout = deriveFromSource(join(pagesDir, file), "page-layout", (source) => componentLayout(source, file));
|
|
346
575
|
pages.push({
|
|
347
576
|
pattern: route.pattern,
|
|
348
577
|
params: route.params,
|
|
@@ -357,8 +586,9 @@ async function scanPages(root, options) {
|
|
|
357
586
|
...rendering,
|
|
358
587
|
hasGetPrerenderPaths,
|
|
359
588
|
island,
|
|
360
|
-
metadata,
|
|
361
|
-
layout
|
|
589
|
+
metadata: metadata ? { ...metadata } : void 0,
|
|
590
|
+
layout,
|
|
591
|
+
...route.env ? { env: route.env } : {}
|
|
362
592
|
});
|
|
363
593
|
}
|
|
364
594
|
pages.sort((a, b) => {
|
|
@@ -367,6 +597,14 @@ async function scanPages(root, options) {
|
|
|
367
597
|
if (aDynamic !== b.pattern.includes(":")) return aDynamic ? 1 : -1;
|
|
368
598
|
return a.pattern.localeCompare(b.pattern);
|
|
369
599
|
});
|
|
600
|
+
if (options?.checkRoutes !== false) {
|
|
601
|
+
const environments = options?.environment ? [options.environment] : ["dev", "prod"];
|
|
602
|
+
for (const environment of environments) {
|
|
603
|
+
const live = pages.filter((page) => isRouteEnabledForEnvironment({ env: page.env ?? null }, environment));
|
|
604
|
+
assertOnePagePerRoute(live);
|
|
605
|
+
warnShadowedPages(pagesDir, live);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
370
608
|
for (const page of pages) {
|
|
371
609
|
if (page.island) continue;
|
|
372
610
|
const dir = dirname(page.componentPath);
|
|
@@ -393,6 +631,47 @@ async function scanPages(root, options) {
|
|
|
393
631
|
namedLayouts
|
|
394
632
|
};
|
|
395
633
|
}
|
|
634
|
+
function compareText(left, right) {
|
|
635
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
636
|
+
}
|
|
637
|
+
/** Group sorted pages by `key`, keeping only groups with more than one page. */
|
|
638
|
+
function pagesSharing(pages, key) {
|
|
639
|
+
const groups = /* @__PURE__ */ new Map();
|
|
640
|
+
for (const page of pages) {
|
|
641
|
+
const group = groups.get(key(page));
|
|
642
|
+
if (group) group.push(page);
|
|
643
|
+
else groups.set(key(page), [page]);
|
|
644
|
+
}
|
|
645
|
+
return [...groups.values()].filter((group) => group.length > 1);
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Two files for one path have no stable winner: the server and the client can
|
|
649
|
+
* each pick a different one, and the order can change between runs.
|
|
650
|
+
*/
|
|
651
|
+
function assertOnePagePerRoute(pages) {
|
|
652
|
+
assertNoneShare(pages, (page) => page.pattern, "pages: Multiple page files resolve to the same route:", "Keep one page file per route and rename or remove the others.");
|
|
653
|
+
assertNoneShare(pages, (page) => page.componentId, "pages: Multiple page files resolve to the same page id:", "Rename or remove one of them.");
|
|
654
|
+
}
|
|
655
|
+
function assertNoneShare(pages, key, title, hint) {
|
|
656
|
+
const clashes = pagesSharing(pages, key).map((group) => ({
|
|
657
|
+
key: key(group[0]),
|
|
658
|
+
files: group.map((page) => page.componentPath).sort(compareText)
|
|
659
|
+
})).sort((left, right) => compareText(left.key, right.key)).map((clash) => ` ${JSON.stringify(clash.key)}: ${clash.files.map((file) => JSON.stringify(file)).join(", ")}`);
|
|
660
|
+
if (clashes.length > 0) throw new Error([
|
|
661
|
+
title,
|
|
662
|
+
...clashes,
|
|
663
|
+
hint
|
|
664
|
+
].join("\n"));
|
|
665
|
+
}
|
|
666
|
+
/** Dynamic routes that match the same URLs, such as [id] and [slug]: only the first is ever used. */
|
|
667
|
+
function warnShadowedPages(pagesDir, pages) {
|
|
668
|
+
for (const [used, ...shadowed] of pagesSharing(pages, (page) => patternToRegex(page.pattern))) for (const page of shadowed) {
|
|
669
|
+
const key = `${pagesDir}\0${used.componentPath}\0${page.componentPath}`;
|
|
670
|
+
if (warnedShadowedPages.has(key)) continue;
|
|
671
|
+
warnedShadowedPages.add(key);
|
|
672
|
+
voidWarn(`pages: Page '${page.componentPath}' (${page.pattern}) is never used: '${used.componentPath}' (${used.pattern}) matches the same URLs first. Remove one of them.`);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
396
675
|
const LEADING_SCRIPT_RE = /^<script\b[^>]*>[\s\S]*?<\/script>\s*\n?/;
|
|
397
676
|
/** Return the raw YAML frontmatter of markdown source, after any leading <script> block. */
|
|
398
677
|
function markdownFrontmatter(source) {
|
|
@@ -424,4 +703,4 @@ function extractFrontmatterLayout(source) {
|
|
|
424
703
|
return value;
|
|
425
704
|
}
|
|
426
705
|
//#endregion
|
|
427
|
-
export {
|
|
706
|
+
export { resolveLayoutChain as a, editIslandSource as c, deriveFromSource as d, invalidateSourceAnalysis as f, ROUTE_EXTENSIONS as g, parseWebSocketFilename as h, resolveEffectiveLayoutChain as i, findIslandImports as l, parseRouteFilename as m, scanPages as n, resolveLayoutIds as o, isRouteEnabledForEnvironment as p, generateComponentManifest as r, resolveNamedLayout as s, pageFileEnvironment as t, findStaticCssImports as u };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { n as readDeploymentPlatform, r as readProjectConfig } from "./project-rqDnVsJN.mjs";
|
|
2
2
|
import { c as getToken } from "./auth-B6iOCjKI.mjs";
|
|
3
3
|
import { A as confirm, H as outro, L as log, O as cancel, U as password, tt as isCancel } from "./dist-Cor9XGoz.mjs";
|
|
4
|
-
import { r as PlatformClient } from "./client-
|
|
4
|
+
import { r as PlatformClient } from "./client-BEMtySaV.mjs";
|
|
5
5
|
import { m as printSuccess, o as formatCount, p as note } from "./output-BpgYODYx.mjs";
|
|
6
6
|
import { a as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-Xoi6eTWY.mjs";
|
|
7
|
-
import { _ as validateSecretValues } from "./env-validation-
|
|
7
|
+
import { _ as validateSecretValues } from "./env-validation-XcukUlFM.mjs";
|
|
8
8
|
import { readFileSync } from "node:fs";
|
|
9
9
|
//#region src/cli/secret.ts
|
|
10
10
|
async function runSecretCommand(root, args) {
|
|
@@ -93,7 +93,7 @@ async function resolveSecretBackend(root, args) {
|
|
|
93
93
|
}
|
|
94
94
|
async function resolveCloudflareSecretBackend(root, args) {
|
|
95
95
|
if (args.projectName) throw new Error("secret: --project selects a Void platform project and is not used for direct Cloudflare apps.");
|
|
96
|
-
const { deleteCloudflareSecret, listCloudflareSecrets, putCloudflareSecret, resolveCloudflareOperationContext, syncCloudflareSecrets } = await import("./cloudflare-operations-
|
|
96
|
+
const { deleteCloudflareSecret, listCloudflareSecrets, putCloudflareSecret, resolveCloudflareOperationContext, syncCloudflareSecrets } = await import("./cloudflare-operations-PCM4wucv.mjs");
|
|
97
97
|
const context = resolveCloudflareOperationContext(root);
|
|
98
98
|
return {
|
|
99
99
|
target: "Cloudflare Worker",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/runtime/static-asset-marker.ts
|
|
2
|
+
const STATIC_ASSET_KEY = "__voidStaticAsset";
|
|
3
|
+
/**
|
|
4
|
+
* Records that the fallback answered with a file from the ASSETS binding: the
|
|
5
|
+
* Cache-Control it carried and its body stream. The value lives on the
|
|
6
|
+
* context, not on the response object: `c.header()` after the handler rebuilds
|
|
7
|
+
* `c.res`, so a response identity check would miss the file. The rebuild keeps
|
|
8
|
+
* the body stream, while a middleware that swaps the body does not.
|
|
9
|
+
*/
|
|
10
|
+
function markStaticAsset(c, response) {
|
|
11
|
+
const mark = {
|
|
12
|
+
policy: response.headers.get("cache-control") ?? "",
|
|
13
|
+
body: response.body
|
|
14
|
+
};
|
|
15
|
+
c.set(STATIC_ASSET_KEY, mark);
|
|
16
|
+
return response;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* True when the final response is still the marked file: a success status, the
|
|
20
|
+
* same body and Cache-Control the fallback served, and no cookie added by
|
|
21
|
+
* later middleware.
|
|
22
|
+
*/
|
|
23
|
+
function isUnchangedStaticAsset(c) {
|
|
24
|
+
const mark = c.get(STATIC_ASSET_KEY);
|
|
25
|
+
return mark !== void 0 && [
|
|
26
|
+
200,
|
|
27
|
+
206,
|
|
28
|
+
304
|
|
29
|
+
].includes(c.res.status) && c.res.body === mark.body && (c.res.headers.get("cache-control") ?? "") === mark.policy && !c.res.headers.has("set-cookie");
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { markStaticAsset as n, isUnchangedStaticAsset as t };
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
//#region src/router/parse-filename.d.ts
|
|
2
|
+
type RouteEnvironment = "dev" | "prod";
|
|
3
|
+
//#endregion
|
|
1
4
|
//#region src/pages/types.d.ts
|
|
2
5
|
type ExplicitBoolean = true | false | undefined;
|
|
3
6
|
type SsrMode = "auto" | "server" | "client";
|
|
@@ -43,6 +46,8 @@ type PageDefinition = {
|
|
|
43
46
|
};
|
|
44
47
|
/** Named layout selection: "name", "!name", or false */
|
|
45
48
|
layout?: string | false;
|
|
49
|
+
/** Environment from a `.dev` / `.prod` file suffix. Unset means both. */
|
|
50
|
+
env?: RouteEnvironment;
|
|
46
51
|
};
|
|
47
52
|
type LayoutDefinition = {
|
|
48
53
|
/** Directory path relative to pages/, e.g. "" (root) or "users" */
|
|
@@ -76,4 +81,4 @@ type DevCssAsset = {
|
|
|
76
81
|
css: string;
|
|
77
82
|
};
|
|
78
83
|
//#endregion
|
|
79
|
-
export { PageRenderMode as a, PageDefinition as i, LayoutDefinition as n, PageScanResult as o, NamedLayoutDefinition as r, DevCssAsset as t };
|
|
84
|
+
export { PageRenderMode as a, PageDefinition as i, LayoutDefinition as n, PageScanResult as o, NamedLayoutDefinition as r, RouteEnvironment as s, DevCssAsset as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as join } from "./dist-ipQi0Q8y.mjs";
|
|
2
|
-
import { l as bindingNamesForField, p as findBindingNameOwner } from "./wrangler-
|
|
2
|
+
import { l as bindingNamesForField, p as findBindingNameOwner } from "./wrangler-CDiEBCdS.mjs";
|
|
3
3
|
//#region src/shared/untrusted-env.ts
|
|
4
4
|
/** Authentication inputs that Wrangler may consume from the environment. */
|
|
5
5
|
const WRANGLER_AUTH_CREDENTIAL_ENV_VARS = [
|
|
@@ -666,9 +666,10 @@ function writeProvisionedWranglerConfig(root, patch, options) {
|
|
|
666
666
|
const matches = [];
|
|
667
667
|
for (let i = 0; i < currentList.length; i++) if (currentList[i].binding === entry.binding) matches.push(i);
|
|
668
668
|
if (matches.length > 1) throw new Error(`deploy: cannot update "${configPath}" — queue producer binding ${JSON.stringify(entry.binding)} is declared ${matches.length} times across producers. A producer binding must map to exactly one queue, so wrangler rejects this config. Remove the duplicate producers and retry.`);
|
|
669
|
+
const voidOwned = reconcileManagedQueues && managedProducerBindings.has(String(entry.binding));
|
|
669
670
|
if (matches.length === 1) {
|
|
670
671
|
const existing = currentList[matches[0]];
|
|
671
|
-
if (existing.queue !== entry.queue) throw new Error(`deploy: cannot update "${configPath}" — queue producer binding ${JSON.stringify(entry.binding)} is already bound to queue ${JSON.stringify(existing.queue)}, but queue ${JSON.stringify(entry.queue)} also requests it. A producer binding must map to exactly one queue. Rename or remove the conflicting producer and retry.`);
|
|
672
|
+
if (existing.queue !== entry.queue && !voidOwned) throw new Error(`deploy: cannot update "${configPath}" — queue producer binding ${JSON.stringify(entry.binding)} is already bound to queue ${JSON.stringify(existing.queue)}, but queue ${JSON.stringify(entry.queue)} also requests it. A producer binding must map to exactly one queue. Rename or remove the conflicting producer and retry.`);
|
|
672
673
|
}
|
|
673
674
|
index = matches.length === 1 ? matches[0] : -1;
|
|
674
675
|
workerBindingOwners ??= collectWorkerBindingNames(parseJsonc(text));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { b as readRootWranglerConfig } from "./wrangler-CDiEBCdS.mjs";
|
|
@@ -3,7 +3,8 @@ import { n as readProjectPaths } from "./project-paths-DGu_QZ_Q.mjs";
|
|
|
3
3
|
import { l as getDatabaseDialect, p as readConfig } from "./config-DHgSEWT7.mjs";
|
|
4
4
|
import { n as isIdentifier, r as isLiteral } from "./ast-CsZ_q_gc.mjs";
|
|
5
5
|
import { t as MagicString } from "./dist-Cegxq8gs.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { c as voidDbBundlerPlugin, r as cloudflareQueueName, t as generateQueueResolverSource } from "./dispatch-OVLeWW8K.mjs";
|
|
7
|
+
import { t as WORKER_CONDITIONS } from "./worker-conditions-AWFRcFxR.mjs";
|
|
7
8
|
import { createRequire } from "node:module";
|
|
8
9
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
@@ -237,14 +238,6 @@ async function bundleMigrationHandler(root, workerDir, handlerPath, dialect) {
|
|
|
237
238
|
if (isPostgresql || isMysql) patchWorkerRequireBundle(outDir, isPostgresql ? "PostgreSQL" : "MySQL", "migration bundle");
|
|
238
239
|
for (const file of readdirSync(outDir)) copyFileSync(join(outDir, file), join(workerDir, file));
|
|
239
240
|
}
|
|
240
|
-
const WORKER_CONDITIONS = [
|
|
241
|
-
"workerd",
|
|
242
|
-
"worker",
|
|
243
|
-
"browser",
|
|
244
|
-
"module",
|
|
245
|
-
"import",
|
|
246
|
-
"default"
|
|
247
|
-
];
|
|
248
241
|
const SQL_WORKER_EXTERNAL = [
|
|
249
242
|
/^cloudflare:/,
|
|
250
243
|
/^node:/,
|
|
@@ -414,6 +407,7 @@ async function bundleEntry(root, entry) {
|
|
|
414
407
|
await build({
|
|
415
408
|
root: tmpDir,
|
|
416
409
|
logLevel: "silent",
|
|
410
|
+
resolve: { conditions: [...WORKER_CONDITIONS, "production"] },
|
|
417
411
|
build: {
|
|
418
412
|
outDir,
|
|
419
413
|
emptyOutDir: true,
|
|
@@ -608,7 +602,9 @@ function generateHandlersSource(root, features, dialect) {
|
|
|
608
602
|
lines.push(`import queueHandler${i} from "${absPath}";`);
|
|
609
603
|
}
|
|
610
604
|
lines.push("");
|
|
611
|
-
|
|
605
|
+
const queueNames = features.queues.map((queue) => queue.name);
|
|
606
|
+
const cloudflareNames = features.cloudflareQueueNames;
|
|
607
|
+
lines.push(generateQueueResolverSource(queueNames, cloudflareNames && queueNames.map((name) => cloudflareQueueName(name, cloudflareNames))));
|
|
612
608
|
lines.push("");
|
|
613
609
|
lines.push(scoped ? "async function __runQueue(batch, env, ctx) {" : "export async function handleQueue(batch, env, ctx) {");
|
|
614
610
|
lines.push(" const queueSuffix = batch.queue;");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as StandardSchemaV1 } from "./standard-schema-Fo_vCAZh.mjs";
|
|
2
2
|
import { r as AuthUser } from "./auth-DkcFflXV.mjs";
|
|
3
|
-
import { a as CloudEnv } from "./handler-
|
|
3
|
+
import { a as CloudEnv } from "./handler-CQv5YDS0.mjs";
|
|
4
4
|
import { WebSocketRouteMap } from "void/routes";
|
|
5
5
|
//#region src/runtime/ws.d.ts
|
|
6
6
|
type MaybePromise<T> = T | Promise<T>;
|
package/docs/guide/auth.md
CHANGED
|
@@ -129,6 +129,8 @@ Local development needs no secret setup. To preview a production build locally,
|
|
|
129
129
|
|
|
130
130
|
Responses to signed-in requests get `Cache-Control: no-store`, so neither the browser cache nor a shared cache keeps a user's data. A route keeps its own policy when it sets `no-store`, or `private` together with `no-cache`. To answer repeat requests with `304`, use `private, no-cache` with Hono's [`etag()`](https://hono.dev/docs/middleware/builtin/etag) middleware. If you set the `ETag` yourself, make it differ per user, so the `304` doesn't reuse another account's page. The browser cache keeps a copy of a `private, no-cache` page, so the Back button can show it again after the user signs out. Keep the default `no-store` for pages that must not stay in the browser cache.
|
|
131
131
|
|
|
132
|
+
Static files from your build and `public/` keep their own cache policy for signed-in requests, so the browser reuses cached JavaScript and CSS. When your app handles the request, the policy is marked `private`, and a request that refreshes the session cookie gets `no-store`.
|
|
133
|
+
|
|
132
134
|
`POST`, `PUT`, `PATCH` and `DELETE` requests and WebSocket connections from another origin don't use the signed-in session unless that origin is in `trustedOrigins`. If a separate frontend calls your app, add its origin there (see [Customization](#customization)). If your routes use their own cookies, check the request `Origin` yourself, for example with Hono's [`csrf()`](https://hono.dev/docs/middleware/builtin/csrf) middleware.
|
|
133
135
|
|
|
134
136
|
Deployed apps on Cloudflare limit auth requests per client IP with Better Auth's defaults, for example 3 sign-in attempts per 10 seconds. Each Worker instance keeps its own count. Change or turn off the limits with `rateLimit` in `auth.ts`. To share one count across instances, set `rateLimit: { storage: 'database' }`, then run `void db generate` for a direct Cloudflare deploy.
|
|
@@ -184,7 +186,7 @@ For the full set of available options, see the official [Better Auth options ref
|
|
|
184
186
|
|
|
185
187
|
## Database and Migrations
|
|
186
188
|
|
|
187
|
-
Auth tables live alongside your app's tables. During local development, Void creates them automatically.
|
|
189
|
+
Auth tables live alongside your app's tables. During local development, Void creates them automatically. While the dev server runs, saving `auth.ts`, or a local file it imports, updates the auth schema Void generates for your app. New auth tables and columns are created in the local database the next time the dev server starts.
|
|
188
190
|
|
|
189
191
|
Void platforms create auth tables automatically. For a direct Cloudflare deploy, include them in your checked-in migrations:
|
|
190
192
|
|
|
@@ -37,6 +37,16 @@ With `void deploy --platform cloudflare`, generated JavaScript bundles with cont
|
|
|
37
37
|
|
|
38
38
|
Use [Custom Headers](./headers) to choose a cache policy for other files. Apply immutable caching only to URLs that change whenever their content changes.
|
|
39
39
|
|
|
40
|
+
Cloudflare serves these fingerprinted JavaScript bundles without starting your Worker, so they do not count as Worker requests, when your app:
|
|
41
|
+
|
|
42
|
+
- has no global middleware
|
|
43
|
+
- has no header, redirect, rewrite or fallback rules, and no `routing.notFound` setting
|
|
44
|
+
- uses the default `base` of `/`
|
|
45
|
+
- has no route or page that matches paths in the assets directory (`assets/` by default)
|
|
46
|
+
- builds at most 99 fingerprinted bundles
|
|
47
|
+
|
|
48
|
+
Otherwise these bundles go through your Worker and keep the same caching.
|
|
49
|
+
|
|
40
50
|
## Non-hashed assets
|
|
41
51
|
|
|
42
52
|
Other static files, such as `index.html` and `favicon.ico`, are cached until the next deploy. You do not need to purge them manually.
|
|
@@ -16,7 +16,7 @@ void email domain add acme.com
|
|
|
16
16
|
|
|
17
17
|
`add` opens a Cloudflare API-token template. Restrict the token to the selected account and mail zone before creating it; Workers Scripts permission applies across that account. The CLI accepts a masked paste or a newly copied token and asks you to confirm those restrictions. The platform encrypts the credential for the zone connection, so projects sharing that zone do not need separate ingress Workers.
|
|
18
18
|
|
|
19
|
-
Platform-managed custom inbound email requires the apex of a Cloudflare zone. Cloudflare’s catch-all only covers that apex, so a subdomain of the selected zone cannot receive arbitrary addresses. `add` refuses to register a new apex that already has MX records, or whose catch-all already routes elsewhere, so it never takes over mail hosted with another provider such as Microsoft 365 or Google Workspace. Use an apex that has no mail yet, or the platform’s shared mail address. Running `add` again for a registered apex, for example to renew its token, keeps working. Each custom apex belongs to one project; a project can register more than one apex.
|
|
19
|
+
Platform-managed custom inbound email requires the apex of a Cloudflare zone. Cloudflare’s catch-all only covers that apex, so a subdomain of the selected zone cannot receive arbitrary addresses. `add` refuses to register a new apex that already has MX records, or whose catch-all already routes elsewhere, so it never takes over mail hosted with another provider such as Microsoft 365 or Google Workspace. Use an apex that has no mail yet, or the platform’s shared mail address. To only send from an apex that keeps its current mail provider, ask your platform administrator to register it as [outbound-only](../platform/administration/email.md#sending-from-a-domain-that-already-receives-mail). Running `add` again for a registered apex, for example to renew its token, keeps working. Each custom apex belongs to one project; a project can register more than one apex.
|
|
20
20
|
|
|
21
21
|
If setup is interrupted, use `void email domain status <domain>` to inspect the saved operation before retrying.
|
|
22
22
|
|
|
@@ -94,6 +94,8 @@ deployed `email/` handlers without per-project DNS setup once the platform has v
|
|
|
94
94
|
handlers once its inbound readiness is `ready`. Use `void email domain status`
|
|
95
95
|
to check current provider routing and any remaining setup steps.
|
|
96
96
|
|
|
97
|
+
A project with an [email alias](../../reference/cli/email.md#void-email-alias) also receives at `<alias>+anything@<mail domain>`. Routing ignores which prefix was used: with the slug `acme-store` and the alias `acme`, both `acme-store+support@…` and `acme+support@…` reach `email/support.ts`.
|
|
98
|
+
|
|
97
99
|
For direct deployments, follow [Your own Cloudflare account](./domains.md#your-own-cloudflare-account) to configure inbound addresses.
|
|
98
100
|
|
|
99
101
|
The local dev server provides the outbound email inbox but has no inbound email trigger. Test inbound handlers with `createInboundTestHarness` below.
|