@void/platform 0.20.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/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/index.d.mts +264 -0
- package/dist/index.mjs +895 -0
- package/dist/installer.d.mts +49 -0
- package/dist/installer.mjs +176 -0
- package/dist/runtime/api/README.md +1 -0
- package/dist/runtime/api/index.js +70824 -0
- package/dist/runtime/api/index.js.map +8 -0
- package/dist/runtime/dispatch/README.md +1 -0
- package/dist/runtime/dispatch/index.js +3289 -0
- package/dist/runtime/dispatch/index.js.map +8 -0
- package/dist/runtime/drizzle/0000_confused_starjammers.sql +66 -0
- package/dist/runtime/drizzle/0001_famous_magus.sql +15 -0
- package/dist/runtime/drizzle/0002_glorious_morgan_stark.sql +17 -0
- package/dist/runtime/drizzle/0003_awesome_ares.sql +3 -0
- package/dist/runtime/drizzle/0004_talented_mimic.sql +1 -0
- package/dist/runtime/drizzle/0005_legal_red_ghost.sql +15 -0
- package/dist/runtime/drizzle/0006_wonderful_madame_masque.sql +17 -0
- package/dist/runtime/drizzle/0007_normal_skin.sql +4 -0
- package/dist/runtime/drizzle/0008_auth_codes.sql +5 -0
- package/dist/runtime/drizzle/0009_chubby_masked_marvel.sql +1 -0
- package/dist/runtime/drizzle/0010_wild_aqueduct.sql +10 -0
- package/dist/runtime/drizzle/0011_curly_stryfe.sql +2 -0
- package/dist/runtime/drizzle/0012_eminent_mystique.sql +1 -0
- package/dist/runtime/drizzle/0013_quick_selene.sql +3 -0
- package/dist/runtime/drizzle/0014_mysterious_medusa.sql +1 -0
- package/dist/runtime/drizzle/0015_backfill_accepted_invitations.sql +14 -0
- package/dist/runtime/drizzle/0016_verified_phoenix.sql +1 -0
- package/dist/runtime/drizzle/0017_gorgeous_johnny_blaze.sql +3 -0
- package/dist/runtime/drizzle/0018_project_migrations.sql +21 -0
- package/dist/runtime/drizzle/0019_project_database_credentials.sql +10 -0
- package/dist/runtime/drizzle/0027_github_deploy.sql +82 -0
- package/dist/runtime/drizzle/0028_check_runs.sql +1 -0
- package/dist/runtime/drizzle/0029_fluffy_justin_hammer.sql +1 -0
- package/dist/runtime/drizzle/0030_smooth_marvel_boy.sql +13 -0
- package/dist/runtime/drizzle/0031_thankful_madame_masque.sql +9 -0
- package/dist/runtime/drizzle/0032_remarkable_skullbuster.sql +10 -0
- package/dist/runtime/drizzle/0033_operator_sessions_and_events.sql +21 -0
- package/dist/runtime/drizzle/0034_email_destinations.sql +21 -0
- package/dist/runtime/drizzle/0035_invitation_actor_history.sql +5 -0
- package/dist/runtime/drizzle/0036_flowery_master_chief.sql +2 -0
- package/dist/runtime/drizzle/0037_unusual_bloodaxe.sql +14 -0
- package/dist/runtime/drizzle/0038_deployment_admission_protocol.sql +25 -0
- package/dist/runtime/drizzle/compatibility.json +26 -0
- package/dist/runtime/drizzle/meta/_journal.json +230 -0
- package/dist/runtime/manifest.json +254 -0
- package/dist/runtime/proxy/README.md +1 -0
- package/dist/runtime/proxy/index.js +5416 -0
- package/dist/runtime/proxy/index.js.map +8 -0
- package/dist/runtime/tail/README.md +1 -0
- package/dist/runtime/tail/index.js +61 -0
- package/dist/runtime/tail/index.js.map +8 -0
- package/package.json +40 -0
|
@@ -0,0 +1,3289 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/packages/platform/dist/index.mjs
|
|
5
|
+
var CORE_CAPABILITIES = {
|
|
6
|
+
ai: true,
|
|
7
|
+
customDomains: false,
|
|
8
|
+
email: false,
|
|
9
|
+
githubActions: false,
|
|
10
|
+
githubOAuth: true,
|
|
11
|
+
googleOAuth: false,
|
|
12
|
+
managedBuilds: false,
|
|
13
|
+
sandbox: false
|
|
14
|
+
};
|
|
15
|
+
var CONTROL_PLANE_DEFAULTS = {
|
|
16
|
+
apiHostname: null,
|
|
17
|
+
dashboardHostname: null,
|
|
18
|
+
proxyHostname: null
|
|
19
|
+
};
|
|
20
|
+
function requiredString$1(value, label) {
|
|
21
|
+
if (typeof value !== "string" || value.trim() === "") throw new Error(`${label} must be a non-empty string.`);
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
__name(requiredString$1, "requiredString$1");
|
|
25
|
+
function normalizeInstallationSlug(value) {
|
|
26
|
+
const slug = value.trim().toLowerCase().replaceAll(/[^a-z0-9-]+/g, "-").replaceAll(/^-+|-+$/g, "").replaceAll(/-{2,}/g, "-");
|
|
27
|
+
if (!slug) throw new Error("The installation name must contain a letter or number.");
|
|
28
|
+
if (slug.length > 40) throw new Error("The installation name must be 40 characters or fewer after normalization.");
|
|
29
|
+
return slug;
|
|
30
|
+
}
|
|
31
|
+
__name(normalizeInstallationSlug, "normalizeInstallationSlug");
|
|
32
|
+
function normalizeApplicationDomain(value) {
|
|
33
|
+
let domain = value.trim().toLowerCase();
|
|
34
|
+
if (domain.endsWith(".")) domain = domain.slice(0, -1);
|
|
35
|
+
if (domain.length > 253 || !domain.includes(".") || domain.split(".").some((label) => !/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label))) throw new Error(`Invalid domain: ${JSON.stringify(value)}.`);
|
|
36
|
+
return domain;
|
|
37
|
+
}
|
|
38
|
+
__name(normalizeApplicationDomain, "normalizeApplicationDomain");
|
|
39
|
+
function createPlatformResourceNames(installationSlug) {
|
|
40
|
+
const prefix = `void-${normalizeInstallationSlug(installationSlug)}`;
|
|
41
|
+
const datasetPrefix = prefix.replaceAll("-", "_");
|
|
42
|
+
return {
|
|
43
|
+
aiGateway: prefix,
|
|
44
|
+
apiWorker: `${prefix}-api`,
|
|
45
|
+
assetsBucket: `${prefix}-assets`,
|
|
46
|
+
cronQueue: `${prefix}-cron-fires`,
|
|
47
|
+
dashboardWorker: `${prefix}-dashboard`,
|
|
48
|
+
dispatchNamespace: `${prefix}-users`,
|
|
49
|
+
dispatchWorker: `${prefix}-dispatch`,
|
|
50
|
+
hyperdrive: `${prefix}-database`,
|
|
51
|
+
isrCacheKv: `${prefix}-isr-cache`,
|
|
52
|
+
legacyAssetsKv: `${prefix}-legacy-assets`,
|
|
53
|
+
logsDataset: `${datasetPrefix}_logs`,
|
|
54
|
+
proxyWorker: `${prefix}-proxy`,
|
|
55
|
+
requestsDataset: `${datasetPrefix}_requests`,
|
|
56
|
+
routingKv: `${prefix}-routing`,
|
|
57
|
+
tailWorker: `${prefix}-tail`,
|
|
58
|
+
usageQueue: `${prefix}-usage-events`
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
__name(createPlatformResourceNames, "createPlatformResourceNames");
|
|
62
|
+
function createApplicationRoutes(application) {
|
|
63
|
+
if (!application.zoneName && application.workersDev) {
|
|
64
|
+
if (application.dedicatedZone) throw new Error("workers.dev cannot use a dedicated-zone catch-all.");
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
const baseDomain = normalizeApplicationDomain(application.baseDomain);
|
|
68
|
+
const zoneName = normalizeApplicationDomain(application.zoneName);
|
|
69
|
+
if (baseDomain !== zoneName && !baseDomain.endsWith(`.${zoneName}`)) throw new Error(`Application domain ${baseDomain} is not inside zone ${zoneName}.`);
|
|
70
|
+
const routes = [{
|
|
71
|
+
pattern: `*.${baseDomain}/*`,
|
|
72
|
+
zoneName
|
|
73
|
+
}, {
|
|
74
|
+
pattern: `*.${baseDomain}`,
|
|
75
|
+
zoneName
|
|
76
|
+
}];
|
|
77
|
+
if (application.dedicatedZone) {
|
|
78
|
+
if (baseDomain !== zoneName) throw new Error("A dedicated-zone catch-all requires the application domain to be the zone.");
|
|
79
|
+
routes.push({
|
|
80
|
+
pattern: "*/*",
|
|
81
|
+
zoneName
|
|
82
|
+
}, {
|
|
83
|
+
pattern: "*",
|
|
84
|
+
zoneName
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return routes;
|
|
88
|
+
}
|
|
89
|
+
__name(createApplicationRoutes, "createApplicationRoutes");
|
|
90
|
+
function createPlatformDeploymentManifest(input) {
|
|
91
|
+
const installationSlug = normalizeInstallationSlug(input.installationSlug);
|
|
92
|
+
const baseDomain = normalizeApplicationDomain(input.application.baseDomain);
|
|
93
|
+
const workersDev = input.application.workersDev;
|
|
94
|
+
if (workersDev && (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(workersDev.subdomain) || !/^[a-z0-9][a-z0-9-]{0,80}$/.test(workersDev.installationId))) throw new Error("Invalid workers.dev installation identity.");
|
|
95
|
+
const zoneName = workersDev && input.application.zoneName === "" ? "" : normalizeApplicationDomain(input.application.zoneName);
|
|
96
|
+
if (!zoneName && baseDomain !== `${workersDev.subdomain}.workers.dev`) throw new Error("workers.dev application domain must match its account subdomain.");
|
|
97
|
+
const resources = {
|
|
98
|
+
...createPlatformResourceNames(installationSlug),
|
|
99
|
+
...input.resources
|
|
100
|
+
};
|
|
101
|
+
const capabilities = {
|
|
102
|
+
...CORE_CAPABILITIES,
|
|
103
|
+
...input.capabilities
|
|
104
|
+
};
|
|
105
|
+
if (capabilities.managedBuilds && !capabilities.githubActions) throw new Error("Managed builds require the GitHub Actions capability.");
|
|
106
|
+
const providers = input.authentication?.providers ?? ["github"];
|
|
107
|
+
if (providers.length === 0) throw new Error("At least one authentication provider is required.");
|
|
108
|
+
if (providers.includes("github") !== capabilities.githubOAuth) throw new Error("The GitHub provider and githubOAuth capability must agree.");
|
|
109
|
+
if (providers.includes("google") !== capabilities.googleOAuth) throw new Error("The Google provider and googleOAuth capability must agree.");
|
|
110
|
+
const manifest = {
|
|
111
|
+
application: {
|
|
112
|
+
baseDomain,
|
|
113
|
+
dedicatedZone: input.application.dedicatedZone ?? false,
|
|
114
|
+
zoneName,
|
|
115
|
+
...workersDev ? { workersDev: { ...workersDev } } : {},
|
|
116
|
+
...input.application.zoneId ? { zoneId: requiredString$1(input.application.zoneId, "application.zoneId") } : {}
|
|
117
|
+
},
|
|
118
|
+
authentication: { providers: [...providers] },
|
|
119
|
+
capabilities,
|
|
120
|
+
controlPlane: {
|
|
121
|
+
...CONTROL_PLANE_DEFAULTS,
|
|
122
|
+
...input.controlPlane
|
|
123
|
+
},
|
|
124
|
+
displayName: requiredString$1(input.displayName, "displayName").trim(),
|
|
125
|
+
installationSlug,
|
|
126
|
+
resources,
|
|
127
|
+
schemaVersion: workersDev ? 2 : 1
|
|
128
|
+
};
|
|
129
|
+
createApplicationRoutes(manifest.application);
|
|
130
|
+
for (const [key, value] of Object.entries(manifest.controlPlane)) {
|
|
131
|
+
if (value !== null) normalizeApplicationDomain(value);
|
|
132
|
+
if (value === "") throw new Error(`controlPlane.${key} must be a hostname or null.`);
|
|
133
|
+
}
|
|
134
|
+
for (const [key, value] of Object.entries(manifest.resources)) requiredString$1(value, `resources.${key}`);
|
|
135
|
+
return manifest;
|
|
136
|
+
}
|
|
137
|
+
__name(createPlatformDeploymentManifest, "createPlatformDeploymentManifest");
|
|
138
|
+
var PLATFORM_USAGE_PROTOCOL_VERSION = 1;
|
|
139
|
+
function readPlatformApplication(env) {
|
|
140
|
+
const baseDomain = env.SITE_DOMAIN ?? env.BASE_HOSTNAME ?? "void.app";
|
|
141
|
+
if (!env.PLATFORM_APPLICATION) return {
|
|
142
|
+
baseDomain,
|
|
143
|
+
zoneName: baseDomain,
|
|
144
|
+
dedicatedZone: false
|
|
145
|
+
};
|
|
146
|
+
return createPlatformDeploymentManifest({
|
|
147
|
+
installationSlug: "runtime",
|
|
148
|
+
displayName: "Runtime",
|
|
149
|
+
application: JSON.parse(env.PLATFORM_APPLICATION)
|
|
150
|
+
}).application;
|
|
151
|
+
}
|
|
152
|
+
__name(readPlatformApplication, "readPlatformApplication");
|
|
153
|
+
function platformWorkersDevAliasKey(hostname) {
|
|
154
|
+
return `workers-dev:${hostname}`;
|
|
155
|
+
}
|
|
156
|
+
__name(platformWorkersDevAliasKey, "platformWorkersDevAliasKey");
|
|
157
|
+
var PLATFORM_APPLICATION_KEY = "__void/platform/application/v1";
|
|
158
|
+
async function resolvePlatformApplicationEnvironment(env) {
|
|
159
|
+
if (!env.PLATFORM_APPLICATION) return env;
|
|
160
|
+
const raw = await env.ROUTING.get(PLATFORM_APPLICATION_KEY);
|
|
161
|
+
if (!raw) return env;
|
|
162
|
+
const previous = readPlatformApplication(env);
|
|
163
|
+
const next = readPlatformApplication({ PLATFORM_APPLICATION: raw });
|
|
164
|
+
if (!previous.workersDev || !next.workersDev || previous.workersDev.installationId !== next.workersDev.installationId || previous.workersDev.subdomain !== next.workersDev.subdomain) throw new Error("Application routing belongs to another platform installation.");
|
|
165
|
+
return {
|
|
166
|
+
...env,
|
|
167
|
+
PLATFORM_APPLICATION: raw,
|
|
168
|
+
SITE_DOMAIN: next.baseDomain,
|
|
169
|
+
BASE_HOSTNAME: next.baseDomain,
|
|
170
|
+
...next.zoneId ? { CF_ZONE_ID: next.zoneId } : {}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
__name(resolvePlatformApplicationEnvironment, "resolvePlatformApplicationEnvironment");
|
|
174
|
+
function platformProjectIngressName(project, workersDev) {
|
|
175
|
+
if (!/^proj_[a-z0-9]{1,32}$/.test(project.id) || !/^[a-z0-9][a-z0-9-]*$/.test(project.slug)) throw new Error("Invalid platform project identity.");
|
|
176
|
+
const prefix = `void-${workersDev.installationId.slice(-12)}-`;
|
|
177
|
+
const suffix = `-${project.id.slice(5)}`;
|
|
178
|
+
return `${prefix}${project.slug.slice(0, 63 - prefix.length - suffix.length).replace(/-+$/, "")}${suffix}`;
|
|
179
|
+
}
|
|
180
|
+
__name(platformProjectIngressName, "platformProjectIngressName");
|
|
181
|
+
function platformProjectWorkersDevHostname(project, workersDev) {
|
|
182
|
+
return `${platformProjectIngressName(project, workersDev)}.${workersDev.subdomain}.workers.dev`;
|
|
183
|
+
}
|
|
184
|
+
__name(platformProjectWorkersDevHostname, "platformProjectWorkersDevHostname");
|
|
185
|
+
|
|
186
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/platform/packages/dispatch/src/error-page.ts
|
|
187
|
+
var DEFAULT_FONT_STACK = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif';
|
|
188
|
+
function escapeHtml(value) {
|
|
189
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
190
|
+
}
|
|
191
|
+
__name(escapeHtml, "escapeHtml");
|
|
192
|
+
function renderDefaultErrorPage(status, title) {
|
|
193
|
+
const heading = `${status}: ${title}`;
|
|
194
|
+
const prefix = `${status}: `;
|
|
195
|
+
return `<!doctype html>
|
|
196
|
+
<html lang="en">
|
|
197
|
+
<head>
|
|
198
|
+
<meta charset="utf-8">
|
|
199
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
200
|
+
<title>${escapeHtml(heading)}</title>
|
|
201
|
+
<style>
|
|
202
|
+
:root {
|
|
203
|
+
color-scheme: light;
|
|
204
|
+
font-family: ${DEFAULT_FONT_STACK};
|
|
205
|
+
background: #f5f5f5;
|
|
206
|
+
color: #171717;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
* {
|
|
210
|
+
box-sizing: border-box;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
body {
|
|
214
|
+
margin: 0;
|
|
215
|
+
min-block-size: 100dvh;
|
|
216
|
+
display: grid;
|
|
217
|
+
place-items: center;
|
|
218
|
+
padding: 24px;
|
|
219
|
+
background:
|
|
220
|
+
radial-gradient(circle at top, rgba(255, 255, 255, 0.96), rgba(245, 245, 245, 0.96) 52%),
|
|
221
|
+
linear-gradient(180deg, #fafafa 0%, #f1f1f1 100%);
|
|
222
|
+
font: 500 16px/1.5 ${DEFAULT_FONT_STACK};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
main {
|
|
226
|
+
inline-size: min(100%, 560px);
|
|
227
|
+
padding: 32px 36px;
|
|
228
|
+
border: 1px solid #d4d4d8;
|
|
229
|
+
border-radius: 32px;
|
|
230
|
+
corner-shape: squircle;
|
|
231
|
+
background: rgba(255, 255, 255, 0.88);
|
|
232
|
+
text-align: center;
|
|
233
|
+
backdrop-filter: blur(12px);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
h1 {
|
|
237
|
+
margin: 0;
|
|
238
|
+
font: 400 clamp(1.5rem, 1.2rem + 1vw, 2rem) / 1.1 ${DEFAULT_FONT_STACK};
|
|
239
|
+
letter-spacing: -0.03em;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
strong {
|
|
243
|
+
font-weight: 600;
|
|
244
|
+
}
|
|
245
|
+
</style>
|
|
246
|
+
</head>
|
|
247
|
+
<body>
|
|
248
|
+
<main>
|
|
249
|
+
<h1><strong>${escapeHtml(prefix)}</strong>${escapeHtml(title)}</h1>
|
|
250
|
+
</main>
|
|
251
|
+
</body>
|
|
252
|
+
</html>`;
|
|
253
|
+
}
|
|
254
|
+
__name(renderDefaultErrorPage, "renderDefaultErrorPage");
|
|
255
|
+
function shouldServeHtmlErrorPage(request) {
|
|
256
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
const url = new URL(request.url);
|
|
260
|
+
if (url.pathname === "/api" || url.pathname.startsWith("/api/")) {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
if (/\/[^/]+\.[^/]+$/.test(url.pathname)) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
const destination = request.headers.get("sec-fetch-dest");
|
|
267
|
+
if (destination && destination !== "document" && destination !== "empty") {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
const accept = request.headers.get("accept");
|
|
271
|
+
return !accept || accept.includes("text/html") || accept.includes("*/*");
|
|
272
|
+
}
|
|
273
|
+
__name(shouldServeHtmlErrorPage, "shouldServeHtmlErrorPage");
|
|
274
|
+
function createErrorResponse(request, status, title, headers) {
|
|
275
|
+
if (!shouldServeHtmlErrorPage(request)) {
|
|
276
|
+
return new Response(title, { status, headers });
|
|
277
|
+
}
|
|
278
|
+
const responseHeaders = new Headers(headers);
|
|
279
|
+
responseHeaders.set("content-type", "text/html; charset=utf-8");
|
|
280
|
+
return new Response(request.method === "HEAD" ? null : renderDefaultErrorPage(status, title), {
|
|
281
|
+
status,
|
|
282
|
+
headers: responseHeaders
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
__name(createErrorResponse, "createErrorResponse");
|
|
286
|
+
|
|
287
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/packages/edge/src/pattern.ts
|
|
288
|
+
var MAX_CACHE = 256;
|
|
289
|
+
var compileCache = /* @__PURE__ */ new Map();
|
|
290
|
+
var REGEX_SPECIAL = /[.+?^${}()|[\]\\]/;
|
|
291
|
+
var PARAM_CHAR = /[A-Za-z0-9_]/;
|
|
292
|
+
function compilePattern(source) {
|
|
293
|
+
const cached = compileCache.get(source);
|
|
294
|
+
if (cached) {
|
|
295
|
+
compileCache.delete(source);
|
|
296
|
+
compileCache.set(source, cached);
|
|
297
|
+
return cached;
|
|
298
|
+
}
|
|
299
|
+
const paramNames = [];
|
|
300
|
+
let legacySplatCaptured = false;
|
|
301
|
+
let pattern = "";
|
|
302
|
+
let index = 0;
|
|
303
|
+
while (index < source.length) {
|
|
304
|
+
const character = source[index];
|
|
305
|
+
if (character === ":") {
|
|
306
|
+
let nameEnd = index + 1;
|
|
307
|
+
while (nameEnd < source.length && PARAM_CHAR.test(source[nameEnd])) {
|
|
308
|
+
nameEnd++;
|
|
309
|
+
}
|
|
310
|
+
const name = source.slice(index + 1, nameEnd);
|
|
311
|
+
if (!name) {
|
|
312
|
+
pattern += "\\:";
|
|
313
|
+
index++;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (source[nameEnd] === "*") {
|
|
317
|
+
paramNames.push(name);
|
|
318
|
+
pattern += `(?<${name}>.*)`;
|
|
319
|
+
index = nameEnd + 1;
|
|
320
|
+
} else {
|
|
321
|
+
paramNames.push(name);
|
|
322
|
+
pattern += `(?<${name}>[^/]+)`;
|
|
323
|
+
index = nameEnd;
|
|
324
|
+
}
|
|
325
|
+
} else if (character === "*") {
|
|
326
|
+
if (legacySplatCaptured) {
|
|
327
|
+
pattern += `.*`;
|
|
328
|
+
} else {
|
|
329
|
+
paramNames.push("splat");
|
|
330
|
+
pattern += `(?<splat>.*)`;
|
|
331
|
+
legacySplatCaptured = true;
|
|
332
|
+
}
|
|
333
|
+
index++;
|
|
334
|
+
} else {
|
|
335
|
+
if (REGEX_SPECIAL.test(character)) {
|
|
336
|
+
pattern += "\\";
|
|
337
|
+
}
|
|
338
|
+
pattern += character;
|
|
339
|
+
index++;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const compiled = { regex: new RegExp(`^${pattern}$`), paramNames };
|
|
343
|
+
if (compileCache.size >= MAX_CACHE) {
|
|
344
|
+
const first = compileCache.keys().next();
|
|
345
|
+
if (!first.done) {
|
|
346
|
+
compileCache.delete(first.value);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
compileCache.set(source, compiled);
|
|
350
|
+
return compiled;
|
|
351
|
+
}
|
|
352
|
+
__name(compilePattern, "compilePattern");
|
|
353
|
+
var literalRegexCache = /* @__PURE__ */ new Map();
|
|
354
|
+
function matchLiteralPattern(pattern, pathname) {
|
|
355
|
+
let regex = literalRegexCache.get(pattern);
|
|
356
|
+
if (regex) {
|
|
357
|
+
literalRegexCache.delete(pattern);
|
|
358
|
+
literalRegexCache.set(pattern, regex);
|
|
359
|
+
} else {
|
|
360
|
+
try {
|
|
361
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*");
|
|
362
|
+
regex = new RegExp(`^${escaped}$`);
|
|
363
|
+
} catch {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
if (literalRegexCache.size >= MAX_CACHE) {
|
|
367
|
+
const first = literalRegexCache.keys().next();
|
|
368
|
+
if (!first.done) {
|
|
369
|
+
literalRegexCache.delete(first.value);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
literalRegexCache.set(pattern, regex);
|
|
373
|
+
}
|
|
374
|
+
try {
|
|
375
|
+
return regex.test(pathname);
|
|
376
|
+
} catch {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
__name(matchLiteralPattern, "matchLiteralPattern");
|
|
381
|
+
var segmentRegexCache = /* @__PURE__ */ new Map();
|
|
382
|
+
function matchSegmentPattern(pattern, pathname) {
|
|
383
|
+
let regex = segmentRegexCache.get(pattern);
|
|
384
|
+
if (regex) {
|
|
385
|
+
segmentRegexCache.delete(pattern);
|
|
386
|
+
segmentRegexCache.set(pattern, regex);
|
|
387
|
+
} else {
|
|
388
|
+
try {
|
|
389
|
+
const escaped = pattern.replace(/(^|\/):[^/{}]+\{\.\+\}(?=\/|$)/g, "$1\uE001").replace(/(^|\/):[^/]+(?=\/|$)/g, "$1\uE000").replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*").replaceAll("\uE000", "[^/]+").replaceAll("\uE001", ".+");
|
|
390
|
+
regex = new RegExp(`^${escaped}$`);
|
|
391
|
+
} catch {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
if (segmentRegexCache.size >= MAX_CACHE) {
|
|
395
|
+
const first = segmentRegexCache.keys().next();
|
|
396
|
+
if (!first.done) {
|
|
397
|
+
segmentRegexCache.delete(first.value);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
segmentRegexCache.set(pattern, regex);
|
|
401
|
+
}
|
|
402
|
+
try {
|
|
403
|
+
return regex.test(pathname);
|
|
404
|
+
} catch {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
__name(matchSegmentPattern, "matchSegmentPattern");
|
|
409
|
+
function substituteParams(destination, params) {
|
|
410
|
+
return destination.replace(/:([A-Za-z0-9_]+)\*?/g, (whole, name) => {
|
|
411
|
+
return params[name] ?? whole;
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
__name(substituteParams, "substituteParams");
|
|
415
|
+
|
|
416
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/packages/edge/src/headers.ts
|
|
417
|
+
var BLOCKED_HEADERS = /* @__PURE__ */ new Set(["set-cookie"]);
|
|
418
|
+
function parseQuotedString(member) {
|
|
419
|
+
if (member.length < 2 || member[0] !== '"' || member[member.length - 1] !== '"') {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
let content = "";
|
|
423
|
+
for (let i = 1; i < member.length - 1; i += 1) {
|
|
424
|
+
const char = member[i];
|
|
425
|
+
if (char === "\\") {
|
|
426
|
+
i += 1;
|
|
427
|
+
if (i >= member.length - 1) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
content += member[i];
|
|
431
|
+
} else if (char === '"') {
|
|
432
|
+
return null;
|
|
433
|
+
} else {
|
|
434
|
+
content += char;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return content;
|
|
438
|
+
}
|
|
439
|
+
__name(parseQuotedString, "parseQuotedString");
|
|
440
|
+
function parseClearSiteData(value) {
|
|
441
|
+
const members = [];
|
|
442
|
+
const pushMember = /* @__PURE__ */ __name((slice) => {
|
|
443
|
+
const raw = slice.trim();
|
|
444
|
+
if (raw.length > 0) {
|
|
445
|
+
members.push({ raw, directive: parseQuotedString(raw) });
|
|
446
|
+
}
|
|
447
|
+
}, "pushMember");
|
|
448
|
+
let start = 0;
|
|
449
|
+
let inQuotes = false;
|
|
450
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
451
|
+
const char = value[i];
|
|
452
|
+
if (inQuotes) {
|
|
453
|
+
if (char === "\\") {
|
|
454
|
+
i += 1;
|
|
455
|
+
} else if (char === '"') {
|
|
456
|
+
inQuotes = false;
|
|
457
|
+
}
|
|
458
|
+
} else if (char === '"') {
|
|
459
|
+
inQuotes = true;
|
|
460
|
+
} else if (char === ",") {
|
|
461
|
+
pushMember(value.slice(start, i));
|
|
462
|
+
start = i + 1;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
pushMember(value.slice(start));
|
|
466
|
+
return members;
|
|
467
|
+
}
|
|
468
|
+
__name(parseClearSiteData, "parseClearSiteData");
|
|
469
|
+
function sanitizeClearSiteData(value) {
|
|
470
|
+
const kept = [];
|
|
471
|
+
for (const { raw, directive } of parseClearSiteData(value)) {
|
|
472
|
+
const normalized = directive?.toLowerCase();
|
|
473
|
+
if (normalized === "*") {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
if (directive === null || normalized === "cookies") {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
kept.push(raw);
|
|
480
|
+
}
|
|
481
|
+
return kept.length > 0 ? kept.join(", ") : null;
|
|
482
|
+
}
|
|
483
|
+
__name(sanitizeClearSiteData, "sanitizeClearSiteData");
|
|
484
|
+
function applyHeaderRules(response, pathname, rules, policy) {
|
|
485
|
+
if (!rules || rules.length === 0) {
|
|
486
|
+
return response;
|
|
487
|
+
}
|
|
488
|
+
const matched = [];
|
|
489
|
+
for (const rule of rules) {
|
|
490
|
+
if (matchLiteralPattern(rule.pattern, pathname)) {
|
|
491
|
+
matched.push(...rule.headers);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
if (matched.length === 0) {
|
|
495
|
+
return response;
|
|
496
|
+
}
|
|
497
|
+
const newResponse = new Response(response.body, response);
|
|
498
|
+
const isolateSharedDomainCookies = policy?.cookieIsolation !== "origin";
|
|
499
|
+
const rawOriginClearSiteData = isolateSharedDomainCookies ? response.headers.get("clear-site-data") : null;
|
|
500
|
+
const originClearSiteData = rawOriginClearSiteData === null ? null : sanitizeClearSiteData(rawOriginClearSiteData);
|
|
501
|
+
for (const [name, value] of matched) {
|
|
502
|
+
const lowerName = name.toLowerCase();
|
|
503
|
+
if (isolateSharedDomainCookies && BLOCKED_HEADERS.has(lowerName)) {
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (isolateSharedDomainCookies && lowerName === "clear-site-data") {
|
|
507
|
+
const safe = sanitizeClearSiteData(value);
|
|
508
|
+
if (safe !== null) {
|
|
509
|
+
newResponse.headers.set(name, safe);
|
|
510
|
+
} else if (originClearSiteData !== null) {
|
|
511
|
+
newResponse.headers.set(name, originClearSiteData);
|
|
512
|
+
} else {
|
|
513
|
+
newResponse.headers.delete(name);
|
|
514
|
+
}
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
newResponse.headers.set(name, value);
|
|
518
|
+
}
|
|
519
|
+
return newResponse;
|
|
520
|
+
}
|
|
521
|
+
__name(applyHeaderRules, "applyHeaderRules");
|
|
522
|
+
|
|
523
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/packages/edge/src/routing-rules.ts
|
|
524
|
+
function applyRoutingRules(pathname, rules, requestHost) {
|
|
525
|
+
if (!rules?.length) {
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
for (const rule of rules) {
|
|
529
|
+
if (rule.host && rule.host !== requestHost) {
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
let regex;
|
|
533
|
+
try {
|
|
534
|
+
({ regex } = compilePattern(rule.source));
|
|
535
|
+
} catch (err) {
|
|
536
|
+
console.warn(
|
|
537
|
+
`[dispatch] skipping routing rule with invalid source ${JSON.stringify(rule.source)}: ${err instanceof Error ? err.message : String(err)}`
|
|
538
|
+
);
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
const match = regex.exec(pathname);
|
|
542
|
+
if (!match) {
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const destination = substituteParams(rule.destination, match.groups ?? {});
|
|
546
|
+
if (rule.status === 200) {
|
|
547
|
+
return { type: "rewrite", destination };
|
|
548
|
+
}
|
|
549
|
+
return { type: "redirect", destination, status: rule.status };
|
|
550
|
+
}
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
__name(applyRoutingRules, "applyRoutingRules");
|
|
554
|
+
function mergeQueryIntoLocation(destination, requestSearch) {
|
|
555
|
+
if (!requestSearch || requestSearch === "?") {
|
|
556
|
+
return destination;
|
|
557
|
+
}
|
|
558
|
+
const fragIdx = destination.indexOf("#");
|
|
559
|
+
const base = fragIdx === -1 ? destination : destination.slice(0, fragIdx);
|
|
560
|
+
const fragment = fragIdx === -1 ? "" : destination.slice(fragIdx);
|
|
561
|
+
const destQueryIdx = base.indexOf("?");
|
|
562
|
+
if (destQueryIdx === -1) {
|
|
563
|
+
return base + requestSearch + fragment;
|
|
564
|
+
}
|
|
565
|
+
if (destQueryIdx === base.length - 1) {
|
|
566
|
+
return base.slice(0, destQueryIdx) + fragment;
|
|
567
|
+
}
|
|
568
|
+
const reqParams = new URLSearchParams(requestSearch);
|
|
569
|
+
const destParams = new URLSearchParams(base.slice(destQueryIdx + 1));
|
|
570
|
+
for (const [key, value] of reqParams) {
|
|
571
|
+
if (!destParams.has(key)) {
|
|
572
|
+
destParams.append(key, value);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return base.slice(0, destQueryIdx) + "?" + destParams.toString() + fragment;
|
|
576
|
+
}
|
|
577
|
+
__name(mergeQueryIntoLocation, "mergeQueryIntoLocation");
|
|
578
|
+
|
|
579
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/packages/edge/src/static.ts
|
|
580
|
+
function safeDecodePathname(pathname) {
|
|
581
|
+
let decoded;
|
|
582
|
+
try {
|
|
583
|
+
decoded = decodeURIComponent(pathname);
|
|
584
|
+
} catch {
|
|
585
|
+
return pathname;
|
|
586
|
+
}
|
|
587
|
+
if (decoded === pathname) {
|
|
588
|
+
return pathname;
|
|
589
|
+
}
|
|
590
|
+
const canonical = reencodePathname(decoded);
|
|
591
|
+
if (canonical === null) {
|
|
592
|
+
return pathname;
|
|
593
|
+
}
|
|
594
|
+
return normalizePercentCase(canonical) === normalizePercentCase(pathname) ? decoded : pathname;
|
|
595
|
+
}
|
|
596
|
+
__name(safeDecodePathname, "safeDecodePathname");
|
|
597
|
+
function normalizePercentCase(pathname) {
|
|
598
|
+
return pathname.replace(/%[0-9a-fA-F]{2}/g, (escape) => escape.toUpperCase());
|
|
599
|
+
}
|
|
600
|
+
__name(normalizePercentCase, "normalizePercentCase");
|
|
601
|
+
function reencodePathname(pathname) {
|
|
602
|
+
try {
|
|
603
|
+
const encoder2 = new URL("https://void.invalid/");
|
|
604
|
+
encoder2.pathname = pathname;
|
|
605
|
+
return encoder2.pathname;
|
|
606
|
+
} catch {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
__name(reencodePathname, "reencodePathname");
|
|
611
|
+
function matchManifestEntry(pathname, manifest, htmlHandling) {
|
|
612
|
+
if (manifest[pathname]) {
|
|
613
|
+
return { path: pathname, hash: manifest[pathname] };
|
|
614
|
+
}
|
|
615
|
+
switch (htmlHandling) {
|
|
616
|
+
case "auto-trailing-slash": {
|
|
617
|
+
const withHtml = pathname + ".html";
|
|
618
|
+
if (manifest[withHtml]) {
|
|
619
|
+
return { path: withHtml, hash: manifest[withHtml] };
|
|
620
|
+
}
|
|
621
|
+
const withIndex = (pathname.endsWith("/") ? pathname : pathname + "/") + "index.html";
|
|
622
|
+
if (manifest[withIndex]) {
|
|
623
|
+
return { path: withIndex, hash: manifest[withIndex] };
|
|
624
|
+
}
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
case "force-trailing-slash": {
|
|
628
|
+
if (!pathname.endsWith("/") && pathname !== "/") {
|
|
629
|
+
const withIndex = pathname + "/index.html";
|
|
630
|
+
if (manifest[withIndex]) {
|
|
631
|
+
return {
|
|
632
|
+
path: withIndex,
|
|
633
|
+
hash: manifest[withIndex],
|
|
634
|
+
redirect: pathname + "/"
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
const indexPath = pathname + "index.html";
|
|
639
|
+
if (manifest[indexPath]) {
|
|
640
|
+
return { path: indexPath, hash: manifest[indexPath] };
|
|
641
|
+
}
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
case "drop-trailing-slash": {
|
|
645
|
+
if (pathname === "/") {
|
|
646
|
+
if (manifest["/index.html"]) {
|
|
647
|
+
return { path: "/index.html", hash: manifest["/index.html"] };
|
|
648
|
+
}
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
if (pathname.endsWith("/")) {
|
|
652
|
+
const withoutSlash = pathname.slice(0, -1);
|
|
653
|
+
const withHtml2 = withoutSlash + ".html";
|
|
654
|
+
if (manifest[withHtml2]) {
|
|
655
|
+
return {
|
|
656
|
+
path: withHtml2,
|
|
657
|
+
hash: manifest[withHtml2],
|
|
658
|
+
redirect: withoutSlash
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
const withHtml = pathname + ".html";
|
|
663
|
+
if (manifest[withHtml]) {
|
|
664
|
+
return { path: withHtml, hash: manifest[withHtml] };
|
|
665
|
+
}
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
case "none":
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
__name(matchManifestEntry, "matchManifestEntry");
|
|
674
|
+
function resolveStaticPath(pathname, manifest, config) {
|
|
675
|
+
const htmlHandling = config.html_handling ?? "auto-trailing-slash";
|
|
676
|
+
let matched = matchManifestEntry(pathname, manifest, htmlHandling);
|
|
677
|
+
if (!matched) {
|
|
678
|
+
const decoded = safeDecodePathname(pathname);
|
|
679
|
+
if (decoded !== pathname) {
|
|
680
|
+
matched = matchManifestEntry(decoded, manifest, htmlHandling);
|
|
681
|
+
if (matched?.redirect !== void 0) {
|
|
682
|
+
const safeRedirect = reencodePathname(matched.redirect);
|
|
683
|
+
if (safeRedirect !== null) {
|
|
684
|
+
matched = { ...matched, redirect: safeRedirect };
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
if (matched) {
|
|
690
|
+
return matched;
|
|
691
|
+
}
|
|
692
|
+
switch (config.not_found_handling ?? "none") {
|
|
693
|
+
case "single-page-application":
|
|
694
|
+
if (manifest["/index.html"]) {
|
|
695
|
+
return {
|
|
696
|
+
path: "/index.html",
|
|
697
|
+
hash: manifest["/index.html"],
|
|
698
|
+
spa: true
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
break;
|
|
702
|
+
case "404-page":
|
|
703
|
+
if (manifest["/404.html"]) {
|
|
704
|
+
return {
|
|
705
|
+
path: "/404.html",
|
|
706
|
+
hash: manifest["/404.html"],
|
|
707
|
+
notFound: true
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
__name(resolveStaticPath, "resolveStaticPath");
|
|
715
|
+
var MIME_TYPES = {
|
|
716
|
+
html: "text/html; charset=utf-8",
|
|
717
|
+
htm: "text/html; charset=utf-8",
|
|
718
|
+
css: "text/css; charset=utf-8",
|
|
719
|
+
js: "text/javascript; charset=utf-8",
|
|
720
|
+
mjs: "text/javascript; charset=utf-8",
|
|
721
|
+
json: "application/json; charset=utf-8",
|
|
722
|
+
xml: "application/xml; charset=utf-8",
|
|
723
|
+
svg: "image/svg+xml; charset=utf-8",
|
|
724
|
+
txt: "text/plain; charset=utf-8",
|
|
725
|
+
csv: "text/csv; charset=utf-8",
|
|
726
|
+
md: "text/markdown; charset=utf-8",
|
|
727
|
+
markdown: "text/markdown; charset=utf-8",
|
|
728
|
+
yaml: "text/yaml; charset=utf-8",
|
|
729
|
+
yml: "text/yaml; charset=utf-8",
|
|
730
|
+
toml: "application/toml",
|
|
731
|
+
png: "image/png",
|
|
732
|
+
jpg: "image/jpeg",
|
|
733
|
+
jpeg: "image/jpeg",
|
|
734
|
+
gif: "image/gif",
|
|
735
|
+
ico: "image/x-icon",
|
|
736
|
+
webp: "image/webp",
|
|
737
|
+
avif: "image/avif",
|
|
738
|
+
woff: "font/woff",
|
|
739
|
+
woff2: "font/woff2",
|
|
740
|
+
ttf: "font/ttf",
|
|
741
|
+
otf: "font/otf",
|
|
742
|
+
eot: "application/vnd.ms-fontobject",
|
|
743
|
+
pdf: "application/pdf",
|
|
744
|
+
wasm: "application/wasm",
|
|
745
|
+
webmanifest: "application/manifest+json; charset=utf-8",
|
|
746
|
+
map: "application/json; charset=utf-8",
|
|
747
|
+
mp4: "video/mp4",
|
|
748
|
+
webm: "video/webm",
|
|
749
|
+
mp3: "audio/mpeg",
|
|
750
|
+
ogg: "audio/ogg"
|
|
751
|
+
};
|
|
752
|
+
function getContentType(path) {
|
|
753
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
754
|
+
return MIME_TYPES[ext] ?? "application/octet-stream";
|
|
755
|
+
}
|
|
756
|
+
__name(getContentType, "getContentType");
|
|
757
|
+
function resolveAssetContentType(stored, path) {
|
|
758
|
+
if (stored === "application/null") {
|
|
759
|
+
return void 0;
|
|
760
|
+
}
|
|
761
|
+
if (!stored || stored === "application/octet-stream") {
|
|
762
|
+
return getContentType(path);
|
|
763
|
+
}
|
|
764
|
+
return stored;
|
|
765
|
+
}
|
|
766
|
+
__name(resolveAssetContentType, "resolveAssetContentType");
|
|
767
|
+
|
|
768
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/packages/isr/src/index.ts
|
|
769
|
+
var ISR_CACHE_KEY_VERSION = "v3";
|
|
770
|
+
function normalizeIsrPath(pathname) {
|
|
771
|
+
let normalized = pathname;
|
|
772
|
+
if (normalized === "/") {
|
|
773
|
+
normalized = "__index__";
|
|
774
|
+
} else {
|
|
775
|
+
normalized = normalized.startsWith("/") ? normalized.slice(1) : normalized;
|
|
776
|
+
if (normalized.endsWith("/")) {
|
|
777
|
+
normalized = normalized.slice(0, -1);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return normalized;
|
|
781
|
+
}
|
|
782
|
+
__name(normalizeIsrPath, "normalizeIsrPath");
|
|
783
|
+
function denormalizeIsrPath(normalized) {
|
|
784
|
+
return normalized === "__index__" ? "/" : `/${normalized}`;
|
|
785
|
+
}
|
|
786
|
+
__name(denormalizeIsrPath, "denormalizeIsrPath");
|
|
787
|
+
function canonicalIsrPathname(pathname) {
|
|
788
|
+
return denormalizeIsrPath(normalizeIsrPath(pathname));
|
|
789
|
+
}
|
|
790
|
+
__name(canonicalIsrPathname, "canonicalIsrPathname");
|
|
791
|
+
function isrCacheVersionPrefix(projectId) {
|
|
792
|
+
return `${projectId}/isr/${ISR_CACHE_KEY_VERSION}/`;
|
|
793
|
+
}
|
|
794
|
+
__name(isrCacheVersionPrefix, "isrCacheVersionPrefix");
|
|
795
|
+
function isrCachePrefix(projectId, deploymentId) {
|
|
796
|
+
if (deploymentId !== void 0) {
|
|
797
|
+
return `${isrCacheVersionPrefix(projectId)}${deploymentId}/`;
|
|
798
|
+
}
|
|
799
|
+
return `${projectId}/isr/`;
|
|
800
|
+
}
|
|
801
|
+
__name(isrCachePrefix, "isrCachePrefix");
|
|
802
|
+
var ISR_VARIANT_SEPARATOR = "#rw=";
|
|
803
|
+
function isrCacheKey(projectId, deploymentId, host, pathname, originalUrl) {
|
|
804
|
+
const base = `${isrCachePrefix(projectId, deploymentId)}${host}/${normalizeIsrPath(pathname)}`;
|
|
805
|
+
if (!originalUrl) {
|
|
806
|
+
return base;
|
|
807
|
+
}
|
|
808
|
+
return `${base}${ISR_VARIANT_SEPARATOR}${hashOriginalUrl(originalUrl)}`;
|
|
809
|
+
}
|
|
810
|
+
__name(isrCacheKey, "isrCacheKey");
|
|
811
|
+
function isrCacheVariantPrefix(projectId, deploymentId, host, pathname) {
|
|
812
|
+
return `${isrCachePrefix(projectId, deploymentId)}${host}/${normalizeIsrPath(pathname)}${ISR_VARIANT_SEPARATOR}`;
|
|
813
|
+
}
|
|
814
|
+
__name(isrCacheVariantPrefix, "isrCacheVariantPrefix");
|
|
815
|
+
var FNV_OFFSET_BASIS = 0xcbf29ce484222325n;
|
|
816
|
+
var FNV_PRIME = 0x100000001b3n;
|
|
817
|
+
var FNV_MASK = 0xffffffffffffffffn;
|
|
818
|
+
function hashOriginalUrl(originalUrl) {
|
|
819
|
+
const bytes = new TextEncoder().encode(originalUrl);
|
|
820
|
+
let h = FNV_OFFSET_BASIS;
|
|
821
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
822
|
+
h = (h ^ BigInt(bytes[i])) & FNV_MASK;
|
|
823
|
+
h = h * FNV_PRIME & FNV_MASK;
|
|
824
|
+
}
|
|
825
|
+
return h.toString(16).padStart(16, "0");
|
|
826
|
+
}
|
|
827
|
+
__name(hashOriginalUrl, "hashOriginalUrl");
|
|
828
|
+
function isrEdgeCacheUrl(hostname, pathname, deploymentId, variant, originalUrl) {
|
|
829
|
+
return buildIsrEdgeCacheUrl(
|
|
830
|
+
hostname,
|
|
831
|
+
pathname,
|
|
832
|
+
deploymentId,
|
|
833
|
+
variant,
|
|
834
|
+
originalUrl ? hashOriginalUrl(originalUrl) : null
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
__name(isrEdgeCacheUrl, "isrEdgeCacheUrl");
|
|
838
|
+
function buildIsrEdgeCacheUrl(hostname, pathname, deploymentId, variant, variantToken) {
|
|
839
|
+
const normalizedPathname = canonicalIsrPathname(pathname);
|
|
840
|
+
const url = new URL(`https://${hostname}${normalizedPathname}`);
|
|
841
|
+
if (variant === "json") {
|
|
842
|
+
url.searchParams.set("__void", "json");
|
|
843
|
+
}
|
|
844
|
+
url.searchParams.set("__void_isr", deploymentId);
|
|
845
|
+
if (variantToken) {
|
|
846
|
+
url.searchParams.set("__void_orig", variantToken);
|
|
847
|
+
}
|
|
848
|
+
return url.toString();
|
|
849
|
+
}
|
|
850
|
+
__name(buildIsrEdgeCacheUrl, "buildIsrEdgeCacheUrl");
|
|
851
|
+
|
|
852
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/packages/isr/src/core.ts
|
|
853
|
+
var SHARED_CACHE_POLICY_VERSION = 1;
|
|
854
|
+
var SHARED_CACHE_POLICY_HEADER = "x-void-shared-cache-policy";
|
|
855
|
+
function markSharedCachePolicy(headers) {
|
|
856
|
+
headers.set(SHARED_CACHE_POLICY_HEADER, String(SHARED_CACHE_POLICY_VERSION));
|
|
857
|
+
}
|
|
858
|
+
__name(markSharedCachePolicy, "markSharedCachePolicy");
|
|
859
|
+
function hasCurrentSharedCachePolicy(response) {
|
|
860
|
+
return response.headers.get(SHARED_CACHE_POLICY_HEADER) === String(SHARED_CACHE_POLICY_VERSION);
|
|
861
|
+
}
|
|
862
|
+
__name(hasCurrentSharedCachePolicy, "hasCurrentSharedCachePolicy");
|
|
863
|
+
var PAGE_DATA_SCRIPT_RE = /<script\b[^>]*\bid=(["'])__VOID_PAGE_DATA__\1[^>]*>([\s\S]*?)<\/script>/i;
|
|
864
|
+
var SKIP_HEADERS = /* @__PURE__ */ new Set(["cache-control", "x-isr-cache", "x-void-page-data", "set-cookie"]);
|
|
865
|
+
function buildVariantOriginalUrl(requestUrl, pathname, queryAllowlist) {
|
|
866
|
+
const url = new URL(requestUrl);
|
|
867
|
+
if (!url.search) {
|
|
868
|
+
return url.toString();
|
|
869
|
+
}
|
|
870
|
+
const allowed = resolveQueryAllowlist(queryAllowlist, pathname);
|
|
871
|
+
if (allowed.size === 0) {
|
|
872
|
+
url.search = "";
|
|
873
|
+
return url.toString();
|
|
874
|
+
}
|
|
875
|
+
const keys = [...new Set(url.searchParams.keys())].filter((key) => allowed.has(key)).sort();
|
|
876
|
+
const filtered = new URLSearchParams();
|
|
877
|
+
for (const key of keys) {
|
|
878
|
+
for (const value of url.searchParams.getAll(key)) {
|
|
879
|
+
filtered.append(key, value);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
const filteredString = filtered.toString();
|
|
883
|
+
url.search = filteredString ? `?${filteredString}` : "";
|
|
884
|
+
return url.toString();
|
|
885
|
+
}
|
|
886
|
+
__name(buildVariantOriginalUrl, "buildVariantOriginalUrl");
|
|
887
|
+
function exactQueryAllowlist(allowlist) {
|
|
888
|
+
if (!allowlist) {
|
|
889
|
+
return void 0;
|
|
890
|
+
}
|
|
891
|
+
const exact = Object.fromEntries(
|
|
892
|
+
Object.entries(allowlist).filter(([pattern]) => !pattern.includes("*"))
|
|
893
|
+
);
|
|
894
|
+
return Object.keys(exact).length > 0 ? exact : void 0;
|
|
895
|
+
}
|
|
896
|
+
__name(exactQueryAllowlist, "exactQueryAllowlist");
|
|
897
|
+
function resolveQueryAllowlist(allowlist, pathname) {
|
|
898
|
+
if (!allowlist) {
|
|
899
|
+
return /* @__PURE__ */ new Set();
|
|
900
|
+
}
|
|
901
|
+
for (const [pattern, params] of Object.entries(allowlist)) {
|
|
902
|
+
if (matchLiteralPattern(pattern, pathname)) {
|
|
903
|
+
return new Set(params);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return /* @__PURE__ */ new Set();
|
|
907
|
+
}
|
|
908
|
+
__name(resolveQueryAllowlist, "resolveQueryAllowlist");
|
|
909
|
+
function packIsrValue(headers, body) {
|
|
910
|
+
const headerJson = new TextEncoder().encode(JSON.stringify(headers));
|
|
911
|
+
const buffer = new ArrayBuffer(4 + headerJson.byteLength + body.byteLength);
|
|
912
|
+
const view = new DataView(buffer);
|
|
913
|
+
view.setUint32(0, headerJson.byteLength, true);
|
|
914
|
+
new Uint8Array(buffer, 4, headerJson.byteLength).set(headerJson);
|
|
915
|
+
new Uint8Array(buffer, 4 + headerJson.byteLength).set(new Uint8Array(body));
|
|
916
|
+
return buffer;
|
|
917
|
+
}
|
|
918
|
+
__name(packIsrValue, "packIsrValue");
|
|
919
|
+
function unpackIsrValue(buffer) {
|
|
920
|
+
if (buffer.byteLength < 4) {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
const view = new DataView(buffer);
|
|
924
|
+
const headerLength = view.getUint32(0, true);
|
|
925
|
+
if (buffer.byteLength < 4 + headerLength) {
|
|
926
|
+
return null;
|
|
927
|
+
}
|
|
928
|
+
try {
|
|
929
|
+
const headerBytes = new Uint8Array(buffer, 4, headerLength);
|
|
930
|
+
const parsed = JSON.parse(new TextDecoder().decode(headerBytes));
|
|
931
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
const headers = {};
|
|
935
|
+
for (const [name, value] of Object.entries(parsed)) {
|
|
936
|
+
if (typeof value !== "string") {
|
|
937
|
+
return null;
|
|
938
|
+
}
|
|
939
|
+
headers[name] = value;
|
|
940
|
+
}
|
|
941
|
+
return { headers, body: buffer.slice(4 + headerLength) };
|
|
942
|
+
} catch {
|
|
943
|
+
return null;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
__name(unpackIsrValue, "unpackIsrValue");
|
|
947
|
+
function decodeIsrCacheEntry(value, metadata) {
|
|
948
|
+
if (!value || !metadata) {
|
|
949
|
+
return null;
|
|
950
|
+
}
|
|
951
|
+
const unpacked = unpackIsrValue(value);
|
|
952
|
+
if (!unpacked) {
|
|
953
|
+
return null;
|
|
954
|
+
}
|
|
955
|
+
if (!Number.isInteger(metadata.s) || metadata.s < 200 || metadata.s > 599) {
|
|
956
|
+
return null;
|
|
957
|
+
}
|
|
958
|
+
if (!Number.isFinite(metadata.c) || metadata.c <= 0 || !Number.isFinite(metadata.t) || metadata.t <= 0 || metadata.p !== SHARED_CACHE_POLICY_VERSION) {
|
|
959
|
+
return null;
|
|
960
|
+
}
|
|
961
|
+
try {
|
|
962
|
+
new Headers(unpacked.headers);
|
|
963
|
+
} catch {
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
return {
|
|
967
|
+
body: new ReadableStream({
|
|
968
|
+
start(controller) {
|
|
969
|
+
controller.enqueue(new Uint8Array(unpacked.body));
|
|
970
|
+
controller.close();
|
|
971
|
+
}
|
|
972
|
+
}),
|
|
973
|
+
status: metadata.s,
|
|
974
|
+
headers: unpacked.headers,
|
|
975
|
+
createdAt: metadata.c,
|
|
976
|
+
ttl: metadata.t
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
__name(decodeIsrCacheEntry, "decodeIsrCacheEntry");
|
|
980
|
+
function isStale(entry, now = Date.now()) {
|
|
981
|
+
return now - entry.createdAt > entry.ttl * 1e3;
|
|
982
|
+
}
|
|
983
|
+
__name(isStale, "isStale");
|
|
984
|
+
function isrCacheResponse(entry, stale, now = Date.now()) {
|
|
985
|
+
const headers = new Headers(entry.headers);
|
|
986
|
+
headers.set("x-isr-cache", stale ? "STALE" : "HIT");
|
|
987
|
+
if (stale) {
|
|
988
|
+
headers.set(
|
|
989
|
+
"Cache-Control",
|
|
990
|
+
`public, s-maxage=${Math.min(10, entry.ttl)}, max-age=0, must-revalidate`
|
|
991
|
+
);
|
|
992
|
+
} else {
|
|
993
|
+
const remainingSeconds = Math.max(
|
|
994
|
+
0,
|
|
995
|
+
Math.floor((entry.createdAt + entry.ttl * 1e3 - now) / 1e3)
|
|
996
|
+
);
|
|
997
|
+
headers.set(
|
|
998
|
+
"Cache-Control",
|
|
999
|
+
`public, s-maxage=${remainingSeconds}, max-age=0, must-revalidate`
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
markSharedCachePolicy(headers);
|
|
1003
|
+
return new Response(entry.body, { status: entry.status, headers });
|
|
1004
|
+
}
|
|
1005
|
+
__name(isrCacheResponse, "isrCacheResponse");
|
|
1006
|
+
function resolveRevalidateTtl(response, defaultTtl) {
|
|
1007
|
+
const header = response.headers.get("x-revalidate");
|
|
1008
|
+
if (header === null) {
|
|
1009
|
+
return defaultTtl;
|
|
1010
|
+
}
|
|
1011
|
+
const value = Number.parseInt(header, 10);
|
|
1012
|
+
return Number.isNaN(value) || value < 0 ? defaultTtl : value;
|
|
1013
|
+
}
|
|
1014
|
+
__name(resolveRevalidateTtl, "resolveRevalidateTtl");
|
|
1015
|
+
function acceptsEventStream(request) {
|
|
1016
|
+
return request.headers.get("Accept")?.toLowerCase().split(",").some((part) => part.split(";", 1)[0]?.trim() === "text/event-stream") ?? false;
|
|
1017
|
+
}
|
|
1018
|
+
__name(acceptsEventStream, "acceptsEventStream");
|
|
1019
|
+
function shouldBypassIsr(request) {
|
|
1020
|
+
return request.headers.has("Cookie") || request.headers.has("Authorization") || acceptsEventStream(request);
|
|
1021
|
+
}
|
|
1022
|
+
__name(shouldBypassIsr, "shouldBypassIsr");
|
|
1023
|
+
function hasSharedCacheOptOut(response) {
|
|
1024
|
+
if (response.headers.has("Set-Cookie")) {
|
|
1025
|
+
return true;
|
|
1026
|
+
}
|
|
1027
|
+
const cacheControl = response.headers.get("Cache-Control");
|
|
1028
|
+
if (!cacheControl) {
|
|
1029
|
+
return false;
|
|
1030
|
+
}
|
|
1031
|
+
return cacheControl.split(",").some((part) => {
|
|
1032
|
+
const directive = part.split("=", 1)[0]?.trim().toLowerCase();
|
|
1033
|
+
return directive === "private" || directive === "no-store" || directive === "no-cache";
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
__name(hasSharedCacheOptOut, "hasSharedCacheOptOut");
|
|
1037
|
+
function resolvePathTtl(revalidate, pathname) {
|
|
1038
|
+
if (revalidate == null) {
|
|
1039
|
+
return 0;
|
|
1040
|
+
}
|
|
1041
|
+
if (typeof revalidate === "number") {
|
|
1042
|
+
return revalidate;
|
|
1043
|
+
}
|
|
1044
|
+
for (const [pattern, ttl] of Object.entries(revalidate)) {
|
|
1045
|
+
if (matchSegmentPattern(pattern, pathname)) {
|
|
1046
|
+
return ttl;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
return 0;
|
|
1050
|
+
}
|
|
1051
|
+
__name(resolvePathTtl, "resolvePathTtl");
|
|
1052
|
+
function resolvePageTtl(pages, revalidate, pathRevalidate, pathname) {
|
|
1053
|
+
if (pages) {
|
|
1054
|
+
for (const page of pages) {
|
|
1055
|
+
if (!matchSegmentPattern(page.pattern, pathname)) {
|
|
1056
|
+
continue;
|
|
1057
|
+
}
|
|
1058
|
+
return page.revalidate === void 0 ? resolvePathTtl(pathRevalidate, pathname) : page.revalidate > 0 ? page.revalidate : 0;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
return resolvePathTtl(revalidate, pathname);
|
|
1062
|
+
}
|
|
1063
|
+
__name(resolvePageTtl, "resolvePageTtl");
|
|
1064
|
+
function serializeHeaders(response) {
|
|
1065
|
+
const result = {};
|
|
1066
|
+
response.headers.forEach((value, key) => {
|
|
1067
|
+
if (!SKIP_HEADERS.has(key.toLowerCase())) {
|
|
1068
|
+
result[key] = value;
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
return result;
|
|
1072
|
+
}
|
|
1073
|
+
__name(serializeHeaders, "serializeHeaders");
|
|
1074
|
+
function normalizePageDataForJsonCache(raw) {
|
|
1075
|
+
try {
|
|
1076
|
+
const parsed = JSON.parse(raw);
|
|
1077
|
+
return parsed.deferred === void 0 && parsed.deferredKeys === void 0 ? raw : null;
|
|
1078
|
+
} catch {
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
__name(normalizePageDataForJsonCache, "normalizePageDataForJsonCache");
|
|
1083
|
+
function extractPageDataFromHtml(html) {
|
|
1084
|
+
const match = html.match(PAGE_DATA_SCRIPT_RE);
|
|
1085
|
+
return match ? normalizePageDataForJsonCache(match[2]) : null;
|
|
1086
|
+
}
|
|
1087
|
+
__name(extractPageDataFromHtml, "extractPageDataFromHtml");
|
|
1088
|
+
|
|
1089
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/platform/packages/dispatch/src/isr.ts
|
|
1090
|
+
var MAX_ISR_VARIANTS_PER_PATH = 200;
|
|
1091
|
+
async function readIsrCache(env, projectId, deploymentId, host, pathname, originalUrl) {
|
|
1092
|
+
const key = isrCacheKey(projectId, deploymentId, host, pathname, originalUrl);
|
|
1093
|
+
const { value, metadata } = await env.ISR_CACHE.getWithMetadata(
|
|
1094
|
+
key,
|
|
1095
|
+
"arrayBuffer"
|
|
1096
|
+
);
|
|
1097
|
+
return decodeIsrCacheEntry(value, metadata);
|
|
1098
|
+
}
|
|
1099
|
+
__name(readIsrCache, "readIsrCache");
|
|
1100
|
+
async function deleteIsrCache(env, projectId, deploymentId, host, pathname, originalUrl) {
|
|
1101
|
+
const key = isrCacheKey(projectId, deploymentId, host, pathname, originalUrl);
|
|
1102
|
+
await env.ISR_CACHE.delete(key);
|
|
1103
|
+
}
|
|
1104
|
+
__name(deleteIsrCache, "deleteIsrCache");
|
|
1105
|
+
async function writeIsrCache(env, projectId, deploymentId, host, pathname, response, ttl, originalUrl) {
|
|
1106
|
+
const key = isrCacheKey(projectId, deploymentId, host, pathname, originalUrl);
|
|
1107
|
+
const responseClone = response.clone();
|
|
1108
|
+
const headers = serializeHeaders(responseClone);
|
|
1109
|
+
const status = responseClone.status;
|
|
1110
|
+
if (originalUrl && !await isVariantWriteAllowed(env, projectId, deploymentId, host, pathname, key)) {
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
const body = await responseClone.arrayBuffer();
|
|
1114
|
+
const packed = packIsrValue(headers, body);
|
|
1115
|
+
await env.ISR_CACHE.put(key, packed, {
|
|
1116
|
+
metadata: {
|
|
1117
|
+
s: status,
|
|
1118
|
+
c: Date.now(),
|
|
1119
|
+
t: ttl,
|
|
1120
|
+
p: SHARED_CACHE_POLICY_VERSION
|
|
1121
|
+
}
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
__name(writeIsrCache, "writeIsrCache");
|
|
1125
|
+
async function isVariantWriteAllowed(env, projectId, deploymentId, host, pathname, key) {
|
|
1126
|
+
const prefix = isrCacheVariantPrefix(projectId, deploymentId, host, pathname);
|
|
1127
|
+
const listed = await env.ISR_CACHE.list({ prefix, limit: 1e3 });
|
|
1128
|
+
if (listed.keys.length < MAX_ISR_VARIANTS_PER_PATH) {
|
|
1129
|
+
return true;
|
|
1130
|
+
}
|
|
1131
|
+
return listed.keys.some((cacheKey) => cacheKey.name === key);
|
|
1132
|
+
}
|
|
1133
|
+
__name(isVariantWriteAllowed, "isVariantWriteAllowed");
|
|
1134
|
+
function matchedPageTtl(page, pathRevalidate, pathname) {
|
|
1135
|
+
if (page.revalidate === void 0) {
|
|
1136
|
+
return resolvePathTtl(pathRevalidate, pathname);
|
|
1137
|
+
}
|
|
1138
|
+
return page.revalidate > 0 ? page.revalidate : 0;
|
|
1139
|
+
}
|
|
1140
|
+
__name(matchedPageTtl, "matchedPageTtl");
|
|
1141
|
+
function isLiteralPattern(pattern) {
|
|
1142
|
+
return !/[:{*]/.test(pattern);
|
|
1143
|
+
}
|
|
1144
|
+
__name(isLiteralPattern, "isLiteralPattern");
|
|
1145
|
+
function matchesLiteralPage(pages, target, pathRevalidate) {
|
|
1146
|
+
if (!pages) {
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
for (const page of pages) {
|
|
1150
|
+
if (matchSegmentPattern(page.pattern, target)) {
|
|
1151
|
+
return isLiteralPattern(page.pattern) && matchedPageTtl(page, pathRevalidate, target) > 0;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return false;
|
|
1155
|
+
}
|
|
1156
|
+
__name(matchesLiteralPage, "matchesLiteralPage");
|
|
1157
|
+
function isBroadCatchAll(pattern) {
|
|
1158
|
+
return pattern.includes("*") || pattern.includes("{.+}");
|
|
1159
|
+
}
|
|
1160
|
+
__name(isBroadCatchAll, "isBroadCatchAll");
|
|
1161
|
+
function isDottedPathIsrEligible(pages, revalidate, pathRevalidate, pathname) {
|
|
1162
|
+
if (pages && pages.length > 0) {
|
|
1163
|
+
for (const page of pages) {
|
|
1164
|
+
if (matchSegmentPattern(page.pattern, pathname)) {
|
|
1165
|
+
return matchedPageTtl(page, pathRevalidate, pathname) > 0 && !isBroadCatchAll(page.pattern);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
if (revalidate && typeof revalidate === "object") {
|
|
1170
|
+
for (const [pattern, ttl] of Object.entries(revalidate)) {
|
|
1171
|
+
if (matchSegmentPattern(pattern, pathname)) {
|
|
1172
|
+
return ttl > 0 && !isBroadCatchAll(pattern);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
return false;
|
|
1177
|
+
}
|
|
1178
|
+
__name(isDottedPathIsrEligible, "isDottedPathIsrEligible");
|
|
1179
|
+
function isrEdgeCacheKey(request, pathname, deploymentId, originalUrl) {
|
|
1180
|
+
return new Request(
|
|
1181
|
+
isrEdgeCacheUrl(new URL(request.url).hostname, pathname, deploymentId, "html", originalUrl),
|
|
1182
|
+
{ method: "GET" }
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
__name(isrEdgeCacheKey, "isrEdgeCacheKey");
|
|
1186
|
+
function isrEdgeCacheKeyJson(request, pathname, deploymentId, originalUrl) {
|
|
1187
|
+
return new Request(
|
|
1188
|
+
isrEdgeCacheUrl(new URL(request.url).hostname, pathname, deploymentId, "json", originalUrl),
|
|
1189
|
+
{ method: "GET" }
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
__name(isrEdgeCacheKeyJson, "isrEdgeCacheKeyJson");
|
|
1193
|
+
|
|
1194
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/platform/packages/api/src/usage-auth.ts
|
|
1195
|
+
var USAGE_PROTOCOL_VERSION = PLATFORM_USAGE_PROTOCOL_VERSION;
|
|
1196
|
+
var USAGE_SIGNING_KEY_KV_KEY = "__void:usage-signing-key:v1";
|
|
1197
|
+
var SANDBOX_USAGE_RETENTION_MS = 8 * 24 * 60 * 6e4;
|
|
1198
|
+
var encoder = new TextEncoder();
|
|
1199
|
+
function base64url(bytes) {
|
|
1200
|
+
let binary = "";
|
|
1201
|
+
for (const byte of new Uint8Array(bytes)) {
|
|
1202
|
+
binary += String.fromCharCode(byte);
|
|
1203
|
+
}
|
|
1204
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1205
|
+
}
|
|
1206
|
+
__name(base64url, "base64url");
|
|
1207
|
+
async function hmac(data, secret) {
|
|
1208
|
+
const key = await crypto.subtle.importKey(
|
|
1209
|
+
"raw",
|
|
1210
|
+
encoder.encode(secret),
|
|
1211
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1212
|
+
false,
|
|
1213
|
+
["sign"]
|
|
1214
|
+
);
|
|
1215
|
+
return crypto.subtle.sign("HMAC", key, encoder.encode(data));
|
|
1216
|
+
}
|
|
1217
|
+
__name(hmac, "hmac");
|
|
1218
|
+
async function readUsageSigningKey(routing) {
|
|
1219
|
+
return routing.get(USAGE_SIGNING_KEY_KV_KEY, { cacheTtl: 60 });
|
|
1220
|
+
}
|
|
1221
|
+
__name(readUsageSigningKey, "readUsageSigningKey");
|
|
1222
|
+
function signedPayload(source, eventId, event) {
|
|
1223
|
+
return JSON.stringify([USAGE_PROTOCOL_VERSION, source, eventId, event]);
|
|
1224
|
+
}
|
|
1225
|
+
__name(signedPayload, "signedPayload");
|
|
1226
|
+
async function signUsageEvent(key, source, eventId, event) {
|
|
1227
|
+
return {
|
|
1228
|
+
version: USAGE_PROTOCOL_VERSION,
|
|
1229
|
+
source,
|
|
1230
|
+
eventId,
|
|
1231
|
+
event,
|
|
1232
|
+
signature: base64url(await hmac(signedPayload(source, eventId, event), key))
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
__name(signUsageEvent, "signUsageEvent");
|
|
1236
|
+
|
|
1237
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/platform/packages/dispatch/src/metering.ts
|
|
1238
|
+
function recordRequest(env, data) {
|
|
1239
|
+
env.ANALYTICS.writeDataPoint({
|
|
1240
|
+
indexes: [data.projectId],
|
|
1241
|
+
blobs: [
|
|
1242
|
+
data.projectId,
|
|
1243
|
+
data.type,
|
|
1244
|
+
data.method,
|
|
1245
|
+
String(data.status),
|
|
1246
|
+
data.slug,
|
|
1247
|
+
data.failureKind ?? ""
|
|
1248
|
+
],
|
|
1249
|
+
doubles: [1, data.responseBytes, data.durationMs]
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
__name(recordRequest, "recordRequest");
|
|
1253
|
+
function splitWorkerName(workerName) {
|
|
1254
|
+
if (!workerName.startsWith("user-")) {
|
|
1255
|
+
return null;
|
|
1256
|
+
}
|
|
1257
|
+
const dplIndex = workerName.lastIndexOf("-dpl_");
|
|
1258
|
+
if (dplIndex > 4) {
|
|
1259
|
+
return {
|
|
1260
|
+
projectKey: workerName.slice(0, dplIndex),
|
|
1261
|
+
deployId: workerName.slice(dplIndex + 1)
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
return { projectKey: workerName, deployId: "" };
|
|
1265
|
+
}
|
|
1266
|
+
__name(splitWorkerName, "splitWorkerName");
|
|
1267
|
+
var MAX_LOG_URL_BYTES = 2048;
|
|
1268
|
+
var MAX_LOG_PATH_BYTES = 1024;
|
|
1269
|
+
function truncateUtf8(value, maxBytes) {
|
|
1270
|
+
const encoded = new TextEncoder().encode(value);
|
|
1271
|
+
if (encoded.length <= maxBytes) {
|
|
1272
|
+
return value;
|
|
1273
|
+
}
|
|
1274
|
+
const ellipsis = "\u2026";
|
|
1275
|
+
if (maxBytes < 3) {
|
|
1276
|
+
return "";
|
|
1277
|
+
}
|
|
1278
|
+
let end = maxBytes - 3;
|
|
1279
|
+
while (end > 0 && (encoded[end] & 192) === 128) {
|
|
1280
|
+
end--;
|
|
1281
|
+
}
|
|
1282
|
+
return new TextDecoder().decode(encoded.slice(0, end)) + ellipsis;
|
|
1283
|
+
}
|
|
1284
|
+
__name(truncateUtf8, "truncateUtf8");
|
|
1285
|
+
function recordPlatformLog(env, data) {
|
|
1286
|
+
if (!data.workerName) {
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
const parsed = splitWorkerName(data.workerName);
|
|
1290
|
+
if (!parsed) {
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
let pathname = data.url;
|
|
1294
|
+
try {
|
|
1295
|
+
pathname = new URL(data.url).pathname;
|
|
1296
|
+
} catch {
|
|
1297
|
+
}
|
|
1298
|
+
const message = `Void platform: your worker did not respond within ${data.timeoutMs} ms. ${data.method} ${truncateUtf8(pathname, MAX_LOG_PATH_BYTES)} was cut short and returned ${data.status}. Your code keeps running to completion; the platform does not cancel it.`;
|
|
1299
|
+
try {
|
|
1300
|
+
env.LOGS.writeDataPoint({
|
|
1301
|
+
// `projectKey` is NOT truncated. It is the read key, so a shortened one
|
|
1302
|
+
// would produce a row `GET /projects/:id/logs` can never select — worse
|
|
1303
|
+
// than no row. It is bounded upstream by the project slug, and the tail
|
|
1304
|
+
// worker writes the same value whole.
|
|
1305
|
+
indexes: [parsed.projectKey],
|
|
1306
|
+
blobs: [
|
|
1307
|
+
"canceled",
|
|
1308
|
+
JSON.stringify([{ level: "error", message: [message], timestamp: Date.now() }]),
|
|
1309
|
+
"[]",
|
|
1310
|
+
truncateUtf8(data.url, MAX_LOG_URL_BYTES),
|
|
1311
|
+
data.method,
|
|
1312
|
+
String(data.status),
|
|
1313
|
+
parsed.deployId
|
|
1314
|
+
],
|
|
1315
|
+
doubles: [Date.now(), data.status]
|
|
1316
|
+
});
|
|
1317
|
+
} catch {
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
__name(recordPlatformLog, "recordPlatformLog");
|
|
1321
|
+
async function sendUsageEvent(env, data) {
|
|
1322
|
+
const key = await readUsageSigningKey(env.ROUTING);
|
|
1323
|
+
if (!key) {
|
|
1324
|
+
throw new Error("Usage metering is not initialized. Check the platform API /health endpoint.");
|
|
1325
|
+
}
|
|
1326
|
+
const eventId = crypto.randomUUID();
|
|
1327
|
+
await env.USAGE_QUEUE.send(await signUsageEvent(key, "dispatch", eventId, data));
|
|
1328
|
+
}
|
|
1329
|
+
__name(sendUsageEvent, "sendUsageEvent");
|
|
1330
|
+
|
|
1331
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/platform/packages/dispatch/src/platform-lifecycle.ts
|
|
1332
|
+
var PLATFORM_DISABLED_KEY = "__void/platform/disabled/v1";
|
|
1333
|
+
var PLATFORM_DISABLED_VALUE = "disabled";
|
|
1334
|
+
|
|
1335
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/platform/packages/dispatch/src/constants.ts
|
|
1336
|
+
var SITE_DOMAIN = "void.app";
|
|
1337
|
+
|
|
1338
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/platform/packages/dispatch/src/hostname.ts
|
|
1339
|
+
function extractSlug(hostname, baseHostname = SITE_DOMAIN) {
|
|
1340
|
+
const suffix = `.${baseHostname}`;
|
|
1341
|
+
if (!hostname.endsWith(suffix)) {
|
|
1342
|
+
return null;
|
|
1343
|
+
}
|
|
1344
|
+
const slug = hostname.slice(0, -suffix.length);
|
|
1345
|
+
if (!slug || slug.includes(".")) {
|
|
1346
|
+
return null;
|
|
1347
|
+
}
|
|
1348
|
+
if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(slug)) {
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
return slug;
|
|
1352
|
+
}
|
|
1353
|
+
__name(extractSlug, "extractSlug");
|
|
1354
|
+
|
|
1355
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/platform/packages/dispatch/src/project-hostname.ts
|
|
1356
|
+
async function resolveProjectHostname(hostname, env) {
|
|
1357
|
+
const application = readPlatformApplication(env);
|
|
1358
|
+
const isWorkersDev = !!application.workersDev && hostname.endsWith(`.${application.workersDev.subdomain}.workers.dev`);
|
|
1359
|
+
if (!isWorkersDev) {
|
|
1360
|
+
return { slug: extractSlug(hostname, env.BASE_HOSTNAME), workersDev: false };
|
|
1361
|
+
}
|
|
1362
|
+
const raw = await env.ROUTING.get(platformWorkersDevAliasKey(hostname));
|
|
1363
|
+
if (raw) {
|
|
1364
|
+
const project = JSON.parse(raw);
|
|
1365
|
+
if (platformProjectWorkersDevHostname(project, application.workersDev) === hostname) {
|
|
1366
|
+
return { slug: project.slug, projectId: project.id, workersDev: true };
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
return { slug: null, workersDev: true };
|
|
1370
|
+
}
|
|
1371
|
+
__name(resolveProjectHostname, "resolveProjectHostname");
|
|
1372
|
+
function isTestHostname(request) {
|
|
1373
|
+
return new URL(
|
|
1374
|
+
typeof request === "string" || request instanceof URL ? request : request.url
|
|
1375
|
+
).hostname.endsWith(".workers.dev");
|
|
1376
|
+
}
|
|
1377
|
+
__name(isTestHostname, "isTestHostname");
|
|
1378
|
+
var applicationEdgeCache = {
|
|
1379
|
+
async match(request, options) {
|
|
1380
|
+
return isTestHostname(request) ? void 0 : caches.default.match(request, options);
|
|
1381
|
+
},
|
|
1382
|
+
async put(request, response) {
|
|
1383
|
+
if (!isTestHostname(request)) {
|
|
1384
|
+
await caches.default.put(request, response);
|
|
1385
|
+
}
|
|
1386
|
+
},
|
|
1387
|
+
async delete(request, options) {
|
|
1388
|
+
return isTestHostname(request) ? false : caches.default.delete(request, options);
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
|
|
1392
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/platform/packages/dispatch/src/request-type.ts
|
|
1393
|
+
function hasExtension(pathname) {
|
|
1394
|
+
return pathname.slice(pathname.lastIndexOf("/") + 1).includes(".");
|
|
1395
|
+
}
|
|
1396
|
+
__name(hasExtension, "hasExtension");
|
|
1397
|
+
function classifyDynamicRequestType(pathname, ssr) {
|
|
1398
|
+
if (pathname.startsWith("/api/") || pathname === "/api") {
|
|
1399
|
+
return "api";
|
|
1400
|
+
}
|
|
1401
|
+
if (ssr && !hasExtension(pathname)) {
|
|
1402
|
+
return "ssr";
|
|
1403
|
+
}
|
|
1404
|
+
return null;
|
|
1405
|
+
}
|
|
1406
|
+
__name(classifyDynamicRequestType, "classifyDynamicRequestType");
|
|
1407
|
+
|
|
1408
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/platform/packages/dispatch/src/with-timeout.ts
|
|
1409
|
+
var DispatchTimeoutError = class extends Error {
|
|
1410
|
+
static {
|
|
1411
|
+
__name(this, "DispatchTimeoutError");
|
|
1412
|
+
}
|
|
1413
|
+
label;
|
|
1414
|
+
constructor(label) {
|
|
1415
|
+
super(`Dispatch operation timed out: ${label}`);
|
|
1416
|
+
this.name = "DispatchTimeoutError";
|
|
1417
|
+
this.label = label;
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
function withTimeout(promise, ms, label) {
|
|
1421
|
+
if (!(ms > 0) || !Number.isFinite(ms)) {
|
|
1422
|
+
return promise;
|
|
1423
|
+
}
|
|
1424
|
+
let timer = null;
|
|
1425
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
1426
|
+
timer = setTimeout(() => {
|
|
1427
|
+
reject(new DispatchTimeoutError(label));
|
|
1428
|
+
}, ms);
|
|
1429
|
+
});
|
|
1430
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
1431
|
+
clearTimeout(timer);
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
__name(withTimeout, "withTimeout");
|
|
1435
|
+
async function withDelayedHedge(operation, options) {
|
|
1436
|
+
const hedgeEnabled = options.delayMs > 0 && Number.isFinite(options.delayMs);
|
|
1437
|
+
const timeoutEnabled = options.timeoutMs > 0 && Number.isFinite(options.timeoutMs);
|
|
1438
|
+
const run = /* @__PURE__ */ __name(async () => {
|
|
1439
|
+
try {
|
|
1440
|
+
return { ok: true, value: await operation() };
|
|
1441
|
+
} catch (error) {
|
|
1442
|
+
return { ok: false, error };
|
|
1443
|
+
}
|
|
1444
|
+
}, "run");
|
|
1445
|
+
const attempts = [];
|
|
1446
|
+
const startAttempt = /* @__PURE__ */ __name(() => {
|
|
1447
|
+
const attempt = { id: attempts.length + 1, promise: run() };
|
|
1448
|
+
attempts.push(attempt);
|
|
1449
|
+
return attempt;
|
|
1450
|
+
}, "startAttempt");
|
|
1451
|
+
let hedgeTimer = null;
|
|
1452
|
+
const hedgeSignal = hedgeEnabled ? new Promise((resolve) => {
|
|
1453
|
+
hedgeTimer = setTimeout(() => resolve({ kind: "hedge" }), options.delayMs);
|
|
1454
|
+
}) : null;
|
|
1455
|
+
let timeoutTimer = null;
|
|
1456
|
+
const timeoutSignal = timeoutEnabled ? new Promise((resolve) => {
|
|
1457
|
+
timeoutTimer = setTimeout(() => resolve({ kind: "timeout" }), options.timeoutMs);
|
|
1458
|
+
}) : null;
|
|
1459
|
+
const clearTimers = /* @__PURE__ */ __name(() => {
|
|
1460
|
+
clearTimeout(hedgeTimer);
|
|
1461
|
+
clearTimeout(timeoutTimer);
|
|
1462
|
+
}, "clearTimers");
|
|
1463
|
+
const discardAttempt = /* @__PURE__ */ __name((attempt) => {
|
|
1464
|
+
void attempt.promise.then(async (outcome) => {
|
|
1465
|
+
if (outcome.ok && outcome.value !== null) {
|
|
1466
|
+
await options.discard?.(outcome.value);
|
|
1467
|
+
}
|
|
1468
|
+
}).catch(() => {
|
|
1469
|
+
});
|
|
1470
|
+
}, "discardAttempt");
|
|
1471
|
+
const finish = /* @__PURE__ */ __name((winnerId) => {
|
|
1472
|
+
clearTimers();
|
|
1473
|
+
for (const attempt of attempts) {
|
|
1474
|
+
if (attempt.id !== winnerId) {
|
|
1475
|
+
discardAttempt(attempt);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}, "finish");
|
|
1479
|
+
const timeout = /* @__PURE__ */ __name(() => {
|
|
1480
|
+
finish();
|
|
1481
|
+
throw new DispatchTimeoutError(options.label);
|
|
1482
|
+
}, "timeout");
|
|
1483
|
+
const waitForAttempt = /* @__PURE__ */ __name((attempt) => attempt.promise.then((outcome) => ({
|
|
1484
|
+
kind: "attempt",
|
|
1485
|
+
attempt,
|
|
1486
|
+
outcome
|
|
1487
|
+
})), "waitForAttempt");
|
|
1488
|
+
const first = startAttempt();
|
|
1489
|
+
const initial = await Promise.race([
|
|
1490
|
+
waitForAttempt(first),
|
|
1491
|
+
...hedgeSignal ? [hedgeSignal] : [],
|
|
1492
|
+
...timeoutSignal ? [timeoutSignal] : []
|
|
1493
|
+
]);
|
|
1494
|
+
if (initial.kind === "timeout") {
|
|
1495
|
+
return timeout();
|
|
1496
|
+
}
|
|
1497
|
+
if (initial.kind === "attempt") {
|
|
1498
|
+
clearTimeout(hedgeTimer);
|
|
1499
|
+
if (initial.outcome.ok) {
|
|
1500
|
+
finish(first.id);
|
|
1501
|
+
return initial.outcome.value;
|
|
1502
|
+
}
|
|
1503
|
+
if (!hedgeEnabled) {
|
|
1504
|
+
finish();
|
|
1505
|
+
throw initial.outcome.error;
|
|
1506
|
+
}
|
|
1507
|
+
options.onHedge?.();
|
|
1508
|
+
const retry = startAttempt();
|
|
1509
|
+
const retried = await Promise.race([
|
|
1510
|
+
waitForAttempt(retry),
|
|
1511
|
+
...timeoutSignal ? [timeoutSignal] : []
|
|
1512
|
+
]);
|
|
1513
|
+
if (retried.kind === "timeout") {
|
|
1514
|
+
return timeout();
|
|
1515
|
+
}
|
|
1516
|
+
finish(retry.id);
|
|
1517
|
+
if (retried.outcome.ok) {
|
|
1518
|
+
return retried.outcome.value;
|
|
1519
|
+
}
|
|
1520
|
+
throw retried.outcome.error;
|
|
1521
|
+
}
|
|
1522
|
+
options.onHedge?.();
|
|
1523
|
+
const second = startAttempt();
|
|
1524
|
+
const winner = await Promise.race([
|
|
1525
|
+
waitForAttempt(first),
|
|
1526
|
+
waitForAttempt(second),
|
|
1527
|
+
...timeoutSignal ? [timeoutSignal] : []
|
|
1528
|
+
]);
|
|
1529
|
+
if (winner.kind === "timeout") {
|
|
1530
|
+
return timeout();
|
|
1531
|
+
}
|
|
1532
|
+
if (winner.outcome.ok && winner.outcome.value !== null) {
|
|
1533
|
+
finish(winner.attempt.id);
|
|
1534
|
+
return winner.outcome.value;
|
|
1535
|
+
}
|
|
1536
|
+
const peer = winner.attempt.id === first.id ? second : first;
|
|
1537
|
+
const peerResult = await Promise.race([
|
|
1538
|
+
waitForAttempt(peer),
|
|
1539
|
+
...timeoutSignal ? [timeoutSignal] : []
|
|
1540
|
+
]);
|
|
1541
|
+
if (peerResult.kind === "timeout") {
|
|
1542
|
+
return timeout();
|
|
1543
|
+
}
|
|
1544
|
+
if (peerResult.outcome.ok && peerResult.outcome.value !== null) {
|
|
1545
|
+
finish(peer.id);
|
|
1546
|
+
return peerResult.outcome.value;
|
|
1547
|
+
}
|
|
1548
|
+
finish();
|
|
1549
|
+
if (winner.outcome.ok || peerResult.outcome.ok) {
|
|
1550
|
+
return null;
|
|
1551
|
+
}
|
|
1552
|
+
throw winner.outcome.error;
|
|
1553
|
+
}
|
|
1554
|
+
__name(withDelayedHedge, "withDelayedHedge");
|
|
1555
|
+
function resolveTimeoutMs(raw, fallback = 1e4) {
|
|
1556
|
+
return raw === void 0 ? fallback : Number(raw);
|
|
1557
|
+
}
|
|
1558
|
+
__name(resolveTimeoutMs, "resolveTimeoutMs");
|
|
1559
|
+
|
|
1560
|
+
// ../../../../../../../Users/cnakazawa/Projects/void/platform/packages/dispatch/src/index.ts
|
|
1561
|
+
var CLIENT_REVALIDATE_CACHE_CONTROL = "public, max-age=0, must-revalidate";
|
|
1562
|
+
var STATIC_ASSET_HEDGE_DELAY_MS = 1e3;
|
|
1563
|
+
var PLATFORM_QUEUE_RETRY_DELAY_SECONDS = 300;
|
|
1564
|
+
var PLATFORM_STATIC_ASSET_TIMEOUT = "platform-static-asset-timeout";
|
|
1565
|
+
var PLATFORM_STATIC_ASSET_ERROR = "platform-static-asset-error";
|
|
1566
|
+
var PLATFORM_STATIC_ASSET_MISSING = "platform-static-asset-missing";
|
|
1567
|
+
var ISR_STALE_EVICT_GRACE_SECONDS = 86400;
|
|
1568
|
+
function clientRevalidateResponse(response) {
|
|
1569
|
+
const res = new Response(response.body, response);
|
|
1570
|
+
res.headers.set("Cache-Control", CLIENT_REVALIDATE_CACHE_CONTROL);
|
|
1571
|
+
res.headers.delete(SHARED_CACHE_POLICY_HEADER);
|
|
1572
|
+
return res;
|
|
1573
|
+
}
|
|
1574
|
+
__name(clientRevalidateResponse, "clientRevalidateResponse");
|
|
1575
|
+
function timeoutResponse(status, label) {
|
|
1576
|
+
return new Response(null, {
|
|
1577
|
+
status,
|
|
1578
|
+
headers: {
|
|
1579
|
+
"X-Void-Timeout": label,
|
|
1580
|
+
...status === 503 ? { "Retry-After": "5" } : {}
|
|
1581
|
+
}
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
__name(timeoutResponse, "timeoutResponse");
|
|
1585
|
+
function staticAssetFailureResponse(kind) {
|
|
1586
|
+
if (kind === PLATFORM_STATIC_ASSET_TIMEOUT) {
|
|
1587
|
+
return timeoutResponse(503, "static-asset");
|
|
1588
|
+
}
|
|
1589
|
+
return new Response(null, {
|
|
1590
|
+
status: 503,
|
|
1591
|
+
headers: {
|
|
1592
|
+
"X-Void-Storage-Error": kind === PLATFORM_STATIC_ASSET_MISSING ? "static-asset-missing" : "static-asset-read",
|
|
1593
|
+
"Retry-After": "5"
|
|
1594
|
+
}
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
__name(staticAssetFailureResponse, "staticAssetFailureResponse");
|
|
1598
|
+
function isEventStreamResponse(response) {
|
|
1599
|
+
return response.headers.get("content-type")?.toLowerCase().split(";", 1)[0]?.trim() === "text/event-stream";
|
|
1600
|
+
}
|
|
1601
|
+
__name(isEventStreamResponse, "isEventStreamResponse");
|
|
1602
|
+
function isHtmlResponse(response) {
|
|
1603
|
+
return response.headers.get("content-type")?.toLowerCase().split(";", 1)[0]?.trim() === "text/html";
|
|
1604
|
+
}
|
|
1605
|
+
__name(isHtmlResponse, "isHtmlResponse");
|
|
1606
|
+
function jsonCacheResponse(pageData, ttl, state) {
|
|
1607
|
+
const response = new Response(pageData, {
|
|
1608
|
+
headers: {
|
|
1609
|
+
"content-type": "application/json",
|
|
1610
|
+
"Cache-Control": `public, s-maxage=${ttl}, max-age=0, must-revalidate`,
|
|
1611
|
+
"x-isr-cache": state
|
|
1612
|
+
}
|
|
1613
|
+
});
|
|
1614
|
+
markSharedCachePolicy(response.headers);
|
|
1615
|
+
return response;
|
|
1616
|
+
}
|
|
1617
|
+
__name(jsonCacheResponse, "jsonCacheResponse");
|
|
1618
|
+
async function populateJsonEdgeCache(cache, cacheKey, response, ttl, state) {
|
|
1619
|
+
const fromHeader = response.headers.get("x-void-page-data");
|
|
1620
|
+
const pageData = fromHeader ? normalizePageDataForJsonCache(fromHeader) : extractPageDataFromHtml(await response.clone().text());
|
|
1621
|
+
if (!pageData) {
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
await cache.put(cacheKey, jsonCacheResponse(pageData, ttl, state));
|
|
1625
|
+
}
|
|
1626
|
+
__name(populateJsonEdgeCache, "populateJsonEdgeCache");
|
|
1627
|
+
async function deleteSharedIsrEntry(env, entry) {
|
|
1628
|
+
await Promise.all([
|
|
1629
|
+
deleteIsrCache(
|
|
1630
|
+
env,
|
|
1631
|
+
entry.projectId,
|
|
1632
|
+
entry.deploymentId,
|
|
1633
|
+
entry.host,
|
|
1634
|
+
entry.pathname,
|
|
1635
|
+
entry.originalUrl
|
|
1636
|
+
),
|
|
1637
|
+
applicationEdgeCache.delete(entry.htmlKey),
|
|
1638
|
+
applicationEdgeCache.delete(entry.jsonKey)
|
|
1639
|
+
]);
|
|
1640
|
+
}
|
|
1641
|
+
__name(deleteSharedIsrEntry, "deleteSharedIsrEntry");
|
|
1642
|
+
async function applyWorkerFallbackRules(opts) {
|
|
1643
|
+
if (opts.response.status !== 404 || !opts.routing.fallbackRules?.length) {
|
|
1644
|
+
return {
|
|
1645
|
+
response: opts.response,
|
|
1646
|
+
pathname: opts.pathname,
|
|
1647
|
+
originalUrlVariant: opts.originalUrlVariant,
|
|
1648
|
+
applied: false
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
if (opts.routing.type === "void" && opts.response.headers.get("X-Void-No-Match") !== "1") {
|
|
1652
|
+
return {
|
|
1653
|
+
response: opts.response,
|
|
1654
|
+
pathname: opts.pathname,
|
|
1655
|
+
originalUrlVariant: opts.originalUrlVariant,
|
|
1656
|
+
applied: false
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
const fallback = applyRoutingRules(opts.pathname, opts.routing.fallbackRules, opts.url.hostname);
|
|
1660
|
+
if (fallback?.type !== "rewrite") {
|
|
1661
|
+
return {
|
|
1662
|
+
response: opts.response,
|
|
1663
|
+
pathname: opts.pathname,
|
|
1664
|
+
originalUrlVariant: opts.originalUrlVariant,
|
|
1665
|
+
applied: false
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
const fallbackHeader = opts.originalUrlHeader ?? opts.originalRequestUrl;
|
|
1669
|
+
const originalUrlVariant = opts.originalUrlVariant ?? buildVariantOriginalUrl(opts.originalRequestUrl, opts.pathname, opts.routing.queryAllowlist);
|
|
1670
|
+
const fallbackUrl = new URL(opts.url.toString());
|
|
1671
|
+
fallbackUrl.pathname = fallback.destination;
|
|
1672
|
+
const fallbackRequest = rebuildRequest(
|
|
1673
|
+
opts.dispatchReq,
|
|
1674
|
+
fallbackUrl.toString(),
|
|
1675
|
+
(headers) => headers.set("X-Void-Original-URL", fallbackHeader)
|
|
1676
|
+
);
|
|
1677
|
+
return {
|
|
1678
|
+
response: opts.deadline === void 0 ? await opts.worker.fetch(fallbackRequest) : await withTimeout(
|
|
1679
|
+
opts.worker.fetch(fallbackRequest),
|
|
1680
|
+
remainingTimeoutBudget(opts.deadline.startedAt, opts.deadline.timeoutMs, "dispatch"),
|
|
1681
|
+
"dispatch"
|
|
1682
|
+
),
|
|
1683
|
+
pathname: fallback.destination,
|
|
1684
|
+
originalUrlVariant,
|
|
1685
|
+
applied: true
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
__name(applyWorkerFallbackRules, "applyWorkerFallbackRules");
|
|
1689
|
+
function buildFallbackOriginalUrlVariant(requestUrl, pathname, requestHost, routing, originalUrlVariant) {
|
|
1690
|
+
if (originalUrlVariant || !routing.fallbackRules?.length) {
|
|
1691
|
+
return originalUrlVariant;
|
|
1692
|
+
}
|
|
1693
|
+
const fallback = applyRoutingRules(pathname, routing.fallbackRules, requestHost);
|
|
1694
|
+
if (fallback?.type !== "rewrite") {
|
|
1695
|
+
return void 0;
|
|
1696
|
+
}
|
|
1697
|
+
return buildVariantOriginalUrl(requestUrl, pathname, routing.queryAllowlist);
|
|
1698
|
+
}
|
|
1699
|
+
__name(buildFallbackOriginalUrlVariant, "buildFallbackOriginalUrlVariant");
|
|
1700
|
+
function resolveCachedWfpFallbackPathname(pathname, routing, requestHost) {
|
|
1701
|
+
const fallback = applyRoutingRules(pathname, routing.fallbackRules, requestHost);
|
|
1702
|
+
return fallback?.type === "rewrite" ? fallback.destination : void 0;
|
|
1703
|
+
}
|
|
1704
|
+
__name(resolveCachedWfpFallbackPathname, "resolveCachedWfpFallbackPathname");
|
|
1705
|
+
var CF_ASSETS_STUB_MARKER = "Assets have not yet been deployed to this script";
|
|
1706
|
+
function isLikelyCfAssetStub(response) {
|
|
1707
|
+
if (response.status !== 200) {
|
|
1708
|
+
return false;
|
|
1709
|
+
}
|
|
1710
|
+
const contentType = response.headers.get("content-type")?.toLowerCase();
|
|
1711
|
+
if (!contentType?.startsWith("text/plain")) {
|
|
1712
|
+
return false;
|
|
1713
|
+
}
|
|
1714
|
+
const contentLengthRaw = response.headers.get("content-length");
|
|
1715
|
+
if (!contentLengthRaw) {
|
|
1716
|
+
return false;
|
|
1717
|
+
}
|
|
1718
|
+
const contentLength = Number.parseInt(contentLengthRaw, 10);
|
|
1719
|
+
return contentLength > 0 && contentLength < 200;
|
|
1720
|
+
}
|
|
1721
|
+
__name(isLikelyCfAssetStub, "isLikelyCfAssetStub");
|
|
1722
|
+
function remainingTimeoutBudget(startedAt, timeoutMs, label = "static-asset") {
|
|
1723
|
+
if (!(timeoutMs > 0 && Number.isFinite(timeoutMs))) {
|
|
1724
|
+
return timeoutMs;
|
|
1725
|
+
}
|
|
1726
|
+
const remainingMs = timeoutMs - (Date.now() - startedAt);
|
|
1727
|
+
if (remainingMs <= 0) {
|
|
1728
|
+
throw new DispatchTimeoutError(label);
|
|
1729
|
+
}
|
|
1730
|
+
return remainingMs;
|
|
1731
|
+
}
|
|
1732
|
+
__name(remainingTimeoutBudget, "remainingTimeoutBudget");
|
|
1733
|
+
async function fetchAssetManifest(env, deploymentId, projectId, timeoutMs) {
|
|
1734
|
+
const hedgeDelayMs = timeoutMs > 0 && Number.isFinite(timeoutMs) ? Math.min(STATIC_ASSET_HEDGE_DELAY_MS, timeoutMs / 2) : 0;
|
|
1735
|
+
try {
|
|
1736
|
+
const raw = await withDelayedHedge(
|
|
1737
|
+
() => env.ROUTING.get(`manifest:${deploymentId}`, { cacheTtl: 86400 }),
|
|
1738
|
+
{
|
|
1739
|
+
delayMs: hedgeDelayMs,
|
|
1740
|
+
timeoutMs,
|
|
1741
|
+
label: "static-asset",
|
|
1742
|
+
onHedge: /* @__PURE__ */ __name(() => {
|
|
1743
|
+
console.warn(
|
|
1744
|
+
JSON.stringify({
|
|
1745
|
+
event: "wfp-asset-manifest-read-hedge",
|
|
1746
|
+
projectId,
|
|
1747
|
+
deploymentId,
|
|
1748
|
+
delayMs: hedgeDelayMs,
|
|
1749
|
+
ts: Date.now()
|
|
1750
|
+
})
|
|
1751
|
+
);
|
|
1752
|
+
}, "onHedge")
|
|
1753
|
+
}
|
|
1754
|
+
);
|
|
1755
|
+
if (!raw) {
|
|
1756
|
+
return null;
|
|
1757
|
+
}
|
|
1758
|
+
return JSON.parse(raw);
|
|
1759
|
+
} catch (error) {
|
|
1760
|
+
console.error(
|
|
1761
|
+
JSON.stringify({
|
|
1762
|
+
event: error instanceof DispatchTimeoutError ? "wfp-asset-manifest-timeout" : "wfp-asset-manifest-read-error",
|
|
1763
|
+
projectId,
|
|
1764
|
+
deploymentId,
|
|
1765
|
+
error: error instanceof DispatchTimeoutError ? void 0 : error instanceof Error ? error.message : String(error),
|
|
1766
|
+
ts: Date.now()
|
|
1767
|
+
})
|
|
1768
|
+
);
|
|
1769
|
+
if (error instanceof DispatchTimeoutError) {
|
|
1770
|
+
throw error;
|
|
1771
|
+
}
|
|
1772
|
+
throw new StaticAssetReadError(error);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
__name(fetchAssetManifest, "fetchAssetManifest");
|
|
1776
|
+
async function serveWfpAssetFallback(env, routing, pathname, timeoutMs = 1e4, fallbackPathname) {
|
|
1777
|
+
if (!routing.assetManifestId) {
|
|
1778
|
+
return null;
|
|
1779
|
+
}
|
|
1780
|
+
const startedAt = Date.now();
|
|
1781
|
+
const assetManifest = await fetchAssetManifest(
|
|
1782
|
+
env,
|
|
1783
|
+
routing.assetManifestId,
|
|
1784
|
+
routing.projectId,
|
|
1785
|
+
remainingTimeoutBudget(startedAt, timeoutMs)
|
|
1786
|
+
);
|
|
1787
|
+
if (!assetManifest) {
|
|
1788
|
+
return null;
|
|
1789
|
+
}
|
|
1790
|
+
const assetConfig = routing.assetConfig ?? { not_found_handling: "none" };
|
|
1791
|
+
let resolvedPathname = pathname;
|
|
1792
|
+
let resolved = resolveStaticPath(pathname, assetManifest, assetConfig);
|
|
1793
|
+
if ((!resolved || resolved.notFound) && fallbackPathname && fallbackPathname !== pathname) {
|
|
1794
|
+
const fallbackResolved = resolveStaticPath(fallbackPathname, assetManifest, assetConfig);
|
|
1795
|
+
if (fallbackResolved && !fallbackResolved.notFound) {
|
|
1796
|
+
resolved = fallbackResolved;
|
|
1797
|
+
resolvedPathname = fallbackPathname;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
if (!resolved) {
|
|
1801
|
+
return null;
|
|
1802
|
+
}
|
|
1803
|
+
if (resolved.redirect) {
|
|
1804
|
+
return {
|
|
1805
|
+
response: new Response(null, {
|
|
1806
|
+
status: 308,
|
|
1807
|
+
headers: { Location: resolved.redirect }
|
|
1808
|
+
}),
|
|
1809
|
+
pathname: resolvedPathname
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
const assetResponse = await fetchStaticAsset(
|
|
1813
|
+
env,
|
|
1814
|
+
resolved.hash,
|
|
1815
|
+
resolved.path,
|
|
1816
|
+
routing.projectId,
|
|
1817
|
+
routing.assetManifestId,
|
|
1818
|
+
remainingTimeoutBudget(startedAt, timeoutMs)
|
|
1819
|
+
);
|
|
1820
|
+
if (!assetResponse) {
|
|
1821
|
+
return null;
|
|
1822
|
+
}
|
|
1823
|
+
return {
|
|
1824
|
+
response: new Response(assetResponse.body, {
|
|
1825
|
+
status: resolved.notFound ? 404 : 200,
|
|
1826
|
+
headers: assetResponse.headers
|
|
1827
|
+
}),
|
|
1828
|
+
pathname: resolvedPathname
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
__name(serveWfpAssetFallback, "serveWfpAssetFallback");
|
|
1832
|
+
function logWfpAssetFallback(routing, pathname, userAgent) {
|
|
1833
|
+
console.log(
|
|
1834
|
+
JSON.stringify({
|
|
1835
|
+
event: "wfp-asset-fallback",
|
|
1836
|
+
projectId: routing.projectId,
|
|
1837
|
+
deploymentId: routing.assetManifestId,
|
|
1838
|
+
pathname,
|
|
1839
|
+
userAgent,
|
|
1840
|
+
ts: Date.now()
|
|
1841
|
+
})
|
|
1842
|
+
);
|
|
1843
|
+
}
|
|
1844
|
+
__name(logWfpAssetFallback, "logWfpAssetFallback");
|
|
1845
|
+
async function maybeServeWfpAssetFallback(opts) {
|
|
1846
|
+
const startedAt = opts.deadline?.startedAt ?? Date.now();
|
|
1847
|
+
const timeoutMs = opts.deadline?.timeoutMs ?? opts.timeoutMs ?? 1e4;
|
|
1848
|
+
if (isEventStreamResponse(opts.response)) {
|
|
1849
|
+
return { response: opts.response, pathname: opts.pathname };
|
|
1850
|
+
}
|
|
1851
|
+
if (!isLikelyCfAssetStub(opts.response)) {
|
|
1852
|
+
return { response: opts.response, pathname: opts.pathname };
|
|
1853
|
+
}
|
|
1854
|
+
let stubConfirmed = false;
|
|
1855
|
+
let fallback;
|
|
1856
|
+
try {
|
|
1857
|
+
const body = await withTimeout(
|
|
1858
|
+
opts.response.clone().text(),
|
|
1859
|
+
remainingTimeoutBudget(startedAt, timeoutMs),
|
|
1860
|
+
"static-asset"
|
|
1861
|
+
);
|
|
1862
|
+
if (!body.includes(CF_ASSETS_STUB_MARKER)) {
|
|
1863
|
+
return { response: opts.response, pathname: opts.pathname };
|
|
1864
|
+
}
|
|
1865
|
+
stubConfirmed = true;
|
|
1866
|
+
fallback = await serveWfpAssetFallback(
|
|
1867
|
+
opts.env,
|
|
1868
|
+
opts.routing,
|
|
1869
|
+
opts.pathname,
|
|
1870
|
+
remainingTimeoutBudget(startedAt, timeoutMs),
|
|
1871
|
+
opts.fallbackPathname
|
|
1872
|
+
);
|
|
1873
|
+
} catch (error) {
|
|
1874
|
+
if (error instanceof DispatchTimeoutError) {
|
|
1875
|
+
if (!stubConfirmed) {
|
|
1876
|
+
return {
|
|
1877
|
+
response: timeoutResponse(503, "dispatch"),
|
|
1878
|
+
pathname: opts.pathname
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
return {
|
|
1882
|
+
response: staticAssetFailureResponse(PLATFORM_STATIC_ASSET_TIMEOUT),
|
|
1883
|
+
failureKind: PLATFORM_STATIC_ASSET_TIMEOUT,
|
|
1884
|
+
pathname: opts.pathname
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
if (error instanceof StaticAssetReadError) {
|
|
1888
|
+
return {
|
|
1889
|
+
response: staticAssetFailureResponse(PLATFORM_STATIC_ASSET_ERROR),
|
|
1890
|
+
failureKind: PLATFORM_STATIC_ASSET_ERROR,
|
|
1891
|
+
pathname: opts.pathname
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
throw error;
|
|
1895
|
+
}
|
|
1896
|
+
if (!fallback) {
|
|
1897
|
+
console.error(
|
|
1898
|
+
JSON.stringify({
|
|
1899
|
+
event: "wfp-asset-fallback-missing",
|
|
1900
|
+
projectId: opts.routing.projectId,
|
|
1901
|
+
deploymentId: opts.routing.assetManifestId,
|
|
1902
|
+
pathname: opts.pathname,
|
|
1903
|
+
ts: Date.now()
|
|
1904
|
+
})
|
|
1905
|
+
);
|
|
1906
|
+
return {
|
|
1907
|
+
response: staticAssetFailureResponse(PLATFORM_STATIC_ASSET_MISSING),
|
|
1908
|
+
failureKind: PLATFORM_STATIC_ASSET_MISSING,
|
|
1909
|
+
pathname: opts.pathname
|
|
1910
|
+
};
|
|
1911
|
+
}
|
|
1912
|
+
logWfpAssetFallback(opts.routing, fallback.pathname, opts.userAgent);
|
|
1913
|
+
return fallback;
|
|
1914
|
+
}
|
|
1915
|
+
__name(maybeServeWfpAssetFallback, "maybeServeWfpAssetFallback");
|
|
1916
|
+
function rebuildRequest(request, url, mutateHeaders) {
|
|
1917
|
+
const headers = new Headers(request.headers);
|
|
1918
|
+
mutateHeaders(headers);
|
|
1919
|
+
const cfProperties = request.cf;
|
|
1920
|
+
if (request.bodyUsed || request.body?.locked) {
|
|
1921
|
+
headers.delete("content-length");
|
|
1922
|
+
headers.delete("transfer-encoding");
|
|
1923
|
+
return new Request(url, { method: request.method, headers, cf: cfProperties });
|
|
1924
|
+
}
|
|
1925
|
+
if (request.method === "GET" || request.method === "HEAD") {
|
|
1926
|
+
return new Request(url, { method: request.method, headers, cf: cfProperties });
|
|
1927
|
+
}
|
|
1928
|
+
return new Request(url, {
|
|
1929
|
+
method: request.method,
|
|
1930
|
+
headers,
|
|
1931
|
+
body: request.body,
|
|
1932
|
+
cf: cfProperties,
|
|
1933
|
+
// @ts-expect-error -- duplex required for streaming bodies in CF Workers
|
|
1934
|
+
duplex: "half"
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
__name(rebuildRequest, "rebuildRequest");
|
|
1938
|
+
var index_default = {
|
|
1939
|
+
async fetch(request, env, ctx) {
|
|
1940
|
+
env = await resolvePlatformApplicationEnvironment(env);
|
|
1941
|
+
const url = new URL(request.url);
|
|
1942
|
+
if (!env.PLATFORM_INSTALLATION_ID && url.pathname === "/health" && request.headers.get("X-Health-Check") === "void-admin") {
|
|
1943
|
+
return Response.json({ ok: true });
|
|
1944
|
+
}
|
|
1945
|
+
if (env.PLATFORM_HEALTH_PROBE_TOKEN && url.pathname === "/health" && request.headers.get("X-Void-Platform-Probe") === env.PLATFORM_HEALTH_PROBE_TOKEN) {
|
|
1946
|
+
return Response.json({
|
|
1947
|
+
ok: true,
|
|
1948
|
+
...env.PLATFORM_APPLICATION ? { installationId: env.PLATFORM_INSTALLATION_ID } : {}
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
if (env.PLATFORM_TRAFFIC_STATE === "staging" || env.PLATFORM_TRAFFIC_STATE === "sandbox-drain" || env.PLATFORM_INSTALLATION_ID && await env.ROUTING.get(PLATFORM_DISABLED_KEY) === PLATFORM_DISABLED_VALUE) {
|
|
1952
|
+
return createErrorResponse(request, 503, "Platform disabled", {
|
|
1953
|
+
"Cache-Control": "no-store"
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
const startTime = Date.now();
|
|
1957
|
+
const timeoutMs = resolveTimeoutMs(env.DISPATCH_TIMEOUT_MS);
|
|
1958
|
+
if (url.pathname.startsWith("/__void/") || // Legacy: old SDK versions register these top-level paths. Block them
|
|
1959
|
+
// at the edge so we don't rely solely on worker-side token checks.
|
|
1960
|
+
// TODO: remove once all deployed workers use /__void/* paths. See #88
|
|
1961
|
+
url.pathname === "/__scheduled" || url.pathname === "/__queue") {
|
|
1962
|
+
return createErrorResponse(request, 404, "Not Found");
|
|
1963
|
+
}
|
|
1964
|
+
const selected = await resolveProjectHostname(url.hostname, env);
|
|
1965
|
+
const slug = selected.slug;
|
|
1966
|
+
if (selected.workersDev && !selected.projectId) {
|
|
1967
|
+
return createErrorResponse(request, 404, "Not Found");
|
|
1968
|
+
}
|
|
1969
|
+
const routingKey = slug ?? `domain:${url.hostname}`;
|
|
1970
|
+
const routingRaw = await env.ROUTING.get(routingKey, { cacheTtl: 60 });
|
|
1971
|
+
if (!routingRaw) {
|
|
1972
|
+
return createErrorResponse(request, 404, "Not Found", {
|
|
1973
|
+
"Cache-Control": "public, max-age=30"
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
const routing = JSON.parse(routingRaw);
|
|
1977
|
+
if (selected.projectId && selected.projectId !== routing.projectId) {
|
|
1978
|
+
return createErrorResponse(request, 404, "Not Found");
|
|
1979
|
+
}
|
|
1980
|
+
if (routing.suspended) {
|
|
1981
|
+
return Response.json(
|
|
1982
|
+
{ error: "usage limit exceeded", reason: routing.suspended },
|
|
1983
|
+
{ status: 429, headers: { "Cache-Control": "public, s-maxage=30, max-age=0" } }
|
|
1984
|
+
);
|
|
1985
|
+
}
|
|
1986
|
+
if (request.headers.has("X-Void-Original-URL")) {
|
|
1987
|
+
request = rebuildRequest(
|
|
1988
|
+
request,
|
|
1989
|
+
request.url,
|
|
1990
|
+
(headers) => headers.delete("X-Void-Original-URL")
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
let pathname = url.pathname;
|
|
1994
|
+
let sharedIsrEntry;
|
|
1995
|
+
let requestType = request.headers.get("upgrade")?.toLowerCase() === "websocket" ? "websocket" : "static";
|
|
1996
|
+
const meter = /* @__PURE__ */ __name((response, options) => {
|
|
1997
|
+
const responseBytes = isEventStreamResponse(response) ? 0 : Number(response.headers.get("content-length") ?? 0);
|
|
1998
|
+
recordRequest(env, {
|
|
1999
|
+
projectId: routing.projectId,
|
|
2000
|
+
type: requestType,
|
|
2001
|
+
method: request.method,
|
|
2002
|
+
status: response.status,
|
|
2003
|
+
responseBytes,
|
|
2004
|
+
durationMs: Date.now() - startTime,
|
|
2005
|
+
slug: slug ?? url.hostname,
|
|
2006
|
+
...options?.failureKind && { failureKind: options.failureKind }
|
|
2007
|
+
});
|
|
2008
|
+
if (routing.userId) {
|
|
2009
|
+
ctx.waitUntil(
|
|
2010
|
+
sendUsageEvent(env, {
|
|
2011
|
+
projectId: routing.projectId,
|
|
2012
|
+
userId: routing.userId,
|
|
2013
|
+
slug: slug ?? url.hostname,
|
|
2014
|
+
requests: 1,
|
|
2015
|
+
responseBytes,
|
|
2016
|
+
...response.status >= 500 && { status: response.status },
|
|
2017
|
+
...options?.failureKind && { failureKind: options.failureKind }
|
|
2018
|
+
})
|
|
2019
|
+
);
|
|
2020
|
+
}
|
|
2021
|
+
const res = new Response(response.body, response);
|
|
2022
|
+
res.headers.delete("X-Void-No-Match");
|
|
2023
|
+
res.headers.set("X-Hosted-By", "void.cloud");
|
|
2024
|
+
return res;
|
|
2025
|
+
}, "meter");
|
|
2026
|
+
const meterWorkerTimeout = /* @__PURE__ */ __name(() => {
|
|
2027
|
+
recordPlatformLog(env, {
|
|
2028
|
+
workerName: routing.workerName,
|
|
2029
|
+
method: request.method,
|
|
2030
|
+
url: request.url,
|
|
2031
|
+
status: 503,
|
|
2032
|
+
timeoutMs
|
|
2033
|
+
});
|
|
2034
|
+
return meter(timeoutResponse(503, "dispatch"));
|
|
2035
|
+
}, "meterWorkerTimeout");
|
|
2036
|
+
let originalUrlVariant;
|
|
2037
|
+
let originalUrlHeader;
|
|
2038
|
+
if (routing.redirectRules?.length) {
|
|
2039
|
+
const result = applyRoutingRules(pathname, routing.redirectRules, url.hostname);
|
|
2040
|
+
if (result?.type === "redirect") {
|
|
2041
|
+
const location = mergeQueryIntoLocation(result.destination, url.search);
|
|
2042
|
+
return meter(
|
|
2043
|
+
new Response(null, { status: result.status, headers: { Location: location } })
|
|
2044
|
+
);
|
|
2045
|
+
}
|
|
2046
|
+
if (result?.type === "rewrite") {
|
|
2047
|
+
originalUrlHeader = request.url;
|
|
2048
|
+
originalUrlVariant = buildVariantOriginalUrl(request.url, pathname, routing.queryAllowlist);
|
|
2049
|
+
pathname = result.destination;
|
|
2050
|
+
url.pathname = pathname;
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
let workerRequest = request;
|
|
2054
|
+
if (originalUrlHeader) {
|
|
2055
|
+
workerRequest = rebuildRequest(
|
|
2056
|
+
request,
|
|
2057
|
+
url.toString(),
|
|
2058
|
+
(headers) => headers.set("X-Void-Original-URL", originalUrlHeader)
|
|
2059
|
+
);
|
|
2060
|
+
}
|
|
2061
|
+
if (routing.hashedAssetsPrefix && isSharedStaticCacheRequestEligible(request)) {
|
|
2062
|
+
const prefix = "/" + routing.hashedAssetsPrefix + "/";
|
|
2063
|
+
if (pathname.startsWith(prefix)) {
|
|
2064
|
+
const cacheUrl = new URL(url.toString());
|
|
2065
|
+
cacheUrl.search = "";
|
|
2066
|
+
const cacheKey = new Request(cacheUrl.toString());
|
|
2067
|
+
const MANAGED_ASSET_HEADERS = [
|
|
2068
|
+
"cache-control",
|
|
2069
|
+
"content-type",
|
|
2070
|
+
"content-encoding",
|
|
2071
|
+
"content-length",
|
|
2072
|
+
"content-range",
|
|
2073
|
+
"accept-ranges",
|
|
2074
|
+
"transfer-encoding"
|
|
2075
|
+
];
|
|
2076
|
+
const finalizeAsset = /* @__PURE__ */ __name((assetResponse, failureKind) => {
|
|
2077
|
+
const ruled = applyHeaderRules(assetResponse, pathname, routing.headerRules);
|
|
2078
|
+
if (ruled !== assetResponse) {
|
|
2079
|
+
for (const name of MANAGED_ASSET_HEADERS) {
|
|
2080
|
+
const managed = assetResponse.headers.get(name);
|
|
2081
|
+
if (managed === null) {
|
|
2082
|
+
ruled.headers.delete(name);
|
|
2083
|
+
} else {
|
|
2084
|
+
ruled.headers.set(name, managed);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
ruled.headers.delete(SHARED_CACHE_POLICY_HEADER);
|
|
2089
|
+
return meter(ruled, failureKind ? { failureKind } : void 0);
|
|
2090
|
+
}, "finalizeAsset");
|
|
2091
|
+
const cache = applicationEdgeCache;
|
|
2092
|
+
const cached = await cache.match(cacheKey);
|
|
2093
|
+
if (cached?.headers.get("Cache-Control")?.includes("immutable") && isCurrentStaticCacheResponse(cached)) {
|
|
2094
|
+
if (!routing.staticDeploymentId) {
|
|
2095
|
+
const repairResult = await maybeServeWfpAssetFallback({
|
|
2096
|
+
env,
|
|
2097
|
+
routing,
|
|
2098
|
+
pathname,
|
|
2099
|
+
response: cached,
|
|
2100
|
+
userAgent: request.headers.get("user-agent"),
|
|
2101
|
+
timeoutMs
|
|
2102
|
+
});
|
|
2103
|
+
if (repairResult.response !== cached) {
|
|
2104
|
+
if (isStaticCacheResponseEligible(repairResult.response)) {
|
|
2105
|
+
const repaired = new Response(repairResult.response.body, repairResult.response);
|
|
2106
|
+
repaired.headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
|
2107
|
+
markSharedCachePolicy(repaired.headers);
|
|
2108
|
+
const repairedForCache = repaired.clone();
|
|
2109
|
+
ctx.waitUntil(
|
|
2110
|
+
cache.delete(cacheKey).then(() => cache.put(cacheKey, repairedForCache))
|
|
2111
|
+
);
|
|
2112
|
+
return finalizeAsset(repaired, repairResult.failureKind);
|
|
2113
|
+
}
|
|
2114
|
+
ctx.waitUntil(cache.delete(cacheKey));
|
|
2115
|
+
return finalizeAsset(repairResult.response, repairResult.failureKind);
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
return finalizeAsset(cached);
|
|
2119
|
+
}
|
|
2120
|
+
if (cached) {
|
|
2121
|
+
await cache.delete(cacheKey);
|
|
2122
|
+
}
|
|
2123
|
+
if (routing.staticDeploymentId) {
|
|
2124
|
+
const staticAssetStartedAt = Date.now();
|
|
2125
|
+
let assetManifest;
|
|
2126
|
+
try {
|
|
2127
|
+
assetManifest = await fetchAssetManifest(
|
|
2128
|
+
env,
|
|
2129
|
+
routing.staticDeploymentId,
|
|
2130
|
+
routing.projectId,
|
|
2131
|
+
remainingTimeoutBudget(staticAssetStartedAt, timeoutMs)
|
|
2132
|
+
);
|
|
2133
|
+
} catch (err) {
|
|
2134
|
+
if (err instanceof DispatchTimeoutError) {
|
|
2135
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_TIMEOUT), {
|
|
2136
|
+
failureKind: PLATFORM_STATIC_ASSET_TIMEOUT
|
|
2137
|
+
});
|
|
2138
|
+
}
|
|
2139
|
+
if (err instanceof StaticAssetReadError) {
|
|
2140
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_ERROR), {
|
|
2141
|
+
failureKind: PLATFORM_STATIC_ASSET_ERROR
|
|
2142
|
+
});
|
|
2143
|
+
}
|
|
2144
|
+
throw err;
|
|
2145
|
+
}
|
|
2146
|
+
if (!assetManifest) {
|
|
2147
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_MISSING), {
|
|
2148
|
+
failureKind: PLATFORM_STATIC_ASSET_MISSING
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
let assetPath = pathname;
|
|
2152
|
+
let hash = assetManifest[pathname];
|
|
2153
|
+
if (!hash) {
|
|
2154
|
+
const decoded = safeDecodePathname(pathname);
|
|
2155
|
+
if (decoded !== pathname && assetManifest[decoded]) {
|
|
2156
|
+
assetPath = decoded;
|
|
2157
|
+
hash = assetManifest[decoded];
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
let response;
|
|
2161
|
+
if (hash) {
|
|
2162
|
+
try {
|
|
2163
|
+
const assetResponse = await fetchStaticAsset(
|
|
2164
|
+
env,
|
|
2165
|
+
hash,
|
|
2166
|
+
assetPath,
|
|
2167
|
+
routing.projectId,
|
|
2168
|
+
routing.staticDeploymentId,
|
|
2169
|
+
remainingTimeoutBudget(staticAssetStartedAt, timeoutMs)
|
|
2170
|
+
);
|
|
2171
|
+
if (!assetResponse) {
|
|
2172
|
+
logStaticAssetMissing(routing, assetPath, hash);
|
|
2173
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_MISSING), {
|
|
2174
|
+
failureKind: PLATFORM_STATIC_ASSET_MISSING
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2177
|
+
response = assetResponse;
|
|
2178
|
+
} catch (err) {
|
|
2179
|
+
if (err instanceof DispatchTimeoutError) {
|
|
2180
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_TIMEOUT), {
|
|
2181
|
+
failureKind: PLATFORM_STATIC_ASSET_TIMEOUT
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
if (err instanceof StaticAssetReadError) {
|
|
2185
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_ERROR), {
|
|
2186
|
+
failureKind: PLATFORM_STATIC_ASSET_ERROR
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
2189
|
+
throw err;
|
|
2190
|
+
}
|
|
2191
|
+
} else {
|
|
2192
|
+
response = createErrorResponse(request, 404, "Not Found");
|
|
2193
|
+
}
|
|
2194
|
+
if (isStaticCacheResponseEligible(response)) {
|
|
2195
|
+
const res = new Response(response.body, response);
|
|
2196
|
+
res.headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
|
2197
|
+
markSharedCachePolicy(res.headers);
|
|
2198
|
+
ctx.waitUntil(cache.put(cacheKey, res.clone()));
|
|
2199
|
+
return finalizeAsset(res);
|
|
2200
|
+
}
|
|
2201
|
+
return finalizeAsset(response);
|
|
2202
|
+
}
|
|
2203
|
+
try {
|
|
2204
|
+
const worker2 = getWorker(env, routing);
|
|
2205
|
+
const wfpAssetStartedAt = Date.now();
|
|
2206
|
+
let response = await withTimeout(
|
|
2207
|
+
worker2.fetch(workerRequest),
|
|
2208
|
+
remainingTimeoutBudget(wfpAssetStartedAt, timeoutMs),
|
|
2209
|
+
"static-asset"
|
|
2210
|
+
);
|
|
2211
|
+
const fallbackResult = await maybeServeWfpAssetFallback({
|
|
2212
|
+
env,
|
|
2213
|
+
routing,
|
|
2214
|
+
pathname,
|
|
2215
|
+
response,
|
|
2216
|
+
userAgent: request.headers.get("user-agent"),
|
|
2217
|
+
deadline: { startedAt: wfpAssetStartedAt, timeoutMs }
|
|
2218
|
+
});
|
|
2219
|
+
response = fallbackResult.response;
|
|
2220
|
+
const failureKind = fallbackResult.failureKind;
|
|
2221
|
+
if (isStaticCacheResponseEligible(response)) {
|
|
2222
|
+
const res = new Response(response.body, response);
|
|
2223
|
+
res.headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
|
2224
|
+
markSharedCachePolicy(res.headers);
|
|
2225
|
+
ctx.waitUntil(cache.put(cacheKey, res.clone()));
|
|
2226
|
+
return finalizeAsset(res, failureKind);
|
|
2227
|
+
}
|
|
2228
|
+
return finalizeAsset(response, failureKind);
|
|
2229
|
+
} catch (err) {
|
|
2230
|
+
if (err instanceof DispatchTimeoutError) {
|
|
2231
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_TIMEOUT), {
|
|
2232
|
+
failureKind: PLATFORM_STATIC_ASSET_TIMEOUT
|
|
2233
|
+
});
|
|
2234
|
+
}
|
|
2235
|
+
if (err instanceof Error && err.message.includes("Worker exceeded")) {
|
|
2236
|
+
return meter(Response.json({ error: "CPU limit exceeded" }, { status: 429 }));
|
|
2237
|
+
}
|
|
2238
|
+
throw err;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
const isSsrPath = routing.ssr === true && isIsrEligiblePath(pathname, routing.pages, routing.revalidate, routing.pathRevalidate);
|
|
2243
|
+
if (isSsrPath && (routing.revalidate || routing.pages?.length)) {
|
|
2244
|
+
const isrTtl = resolvePageTtl(
|
|
2245
|
+
routing.pages,
|
|
2246
|
+
routing.revalidate,
|
|
2247
|
+
routing.pathRevalidate,
|
|
2248
|
+
pathname
|
|
2249
|
+
);
|
|
2250
|
+
if (isrTtl > 0 && request.method === "GET" && !request.headers.has("Range") && !shouldBypassIsr(request)) {
|
|
2251
|
+
const isPagesRequest = request.headers.has("X-VoidPages");
|
|
2252
|
+
const isrDeploymentId = routing.version ?? "legacy";
|
|
2253
|
+
const cache = applicationEdgeCache;
|
|
2254
|
+
const isrOriginalUrlVariant = buildFallbackOriginalUrlVariant(
|
|
2255
|
+
request.url,
|
|
2256
|
+
pathname,
|
|
2257
|
+
url.hostname,
|
|
2258
|
+
routing,
|
|
2259
|
+
originalUrlVariant
|
|
2260
|
+
);
|
|
2261
|
+
const cacheKey = isrEdgeCacheKey(request, pathname, isrDeploymentId, isrOriginalUrlVariant);
|
|
2262
|
+
const jsonCacheKey = isrEdgeCacheKeyJson(
|
|
2263
|
+
request,
|
|
2264
|
+
pathname,
|
|
2265
|
+
isrDeploymentId,
|
|
2266
|
+
isrOriginalUrlVariant
|
|
2267
|
+
);
|
|
2268
|
+
sharedIsrEntry = {
|
|
2269
|
+
projectId: routing.projectId,
|
|
2270
|
+
deploymentId: isrDeploymentId,
|
|
2271
|
+
host: url.hostname,
|
|
2272
|
+
pathname,
|
|
2273
|
+
originalUrl: isrOriginalUrlVariant,
|
|
2274
|
+
htmlKey: cacheKey,
|
|
2275
|
+
jsonKey: jsonCacheKey
|
|
2276
|
+
};
|
|
2277
|
+
if (isPagesRequest) {
|
|
2278
|
+
const jsonCached = await cache.match(jsonCacheKey);
|
|
2279
|
+
if (jsonCached && hasCurrentSharedCachePolicy(jsonCached)) {
|
|
2280
|
+
requestType = "isr";
|
|
2281
|
+
return meter(clientRevalidateResponse(jsonCached));
|
|
2282
|
+
}
|
|
2283
|
+
if (jsonCached) {
|
|
2284
|
+
await cache.delete(jsonCacheKey);
|
|
2285
|
+
}
|
|
2286
|
+
} else {
|
|
2287
|
+
const purgePoisonedIsrEntry = /* @__PURE__ */ __name(async () => {
|
|
2288
|
+
await deleteSharedIsrEntry(env, sharedIsrEntry);
|
|
2289
|
+
}, "purgePoisonedIsrEntry");
|
|
2290
|
+
const edgeCached = await cache.match(cacheKey);
|
|
2291
|
+
if (edgeCached && hasCurrentSharedCachePolicy(edgeCached)) {
|
|
2292
|
+
requestType = "isr";
|
|
2293
|
+
const repairResult = await maybeServeWfpAssetFallback({
|
|
2294
|
+
env,
|
|
2295
|
+
routing,
|
|
2296
|
+
pathname,
|
|
2297
|
+
response: edgeCached,
|
|
2298
|
+
userAgent: request.headers.get("user-agent"),
|
|
2299
|
+
timeoutMs,
|
|
2300
|
+
fallbackPathname: resolveCachedWfpFallbackPathname(pathname, routing, url.hostname)
|
|
2301
|
+
});
|
|
2302
|
+
if (repairResult.response !== edgeCached) {
|
|
2303
|
+
ctx.waitUntil(purgePoisonedIsrEntry());
|
|
2304
|
+
return meter(
|
|
2305
|
+
repairResult.response.ok ? clientRevalidateResponse(repairResult.response) : repairResult.response,
|
|
2306
|
+
repairResult.failureKind ? { failureKind: repairResult.failureKind } : void 0
|
|
2307
|
+
);
|
|
2308
|
+
}
|
|
2309
|
+
return meter(clientRevalidateResponse(edgeCached));
|
|
2310
|
+
}
|
|
2311
|
+
if (edgeCached) {
|
|
2312
|
+
await cache.delete(cacheKey);
|
|
2313
|
+
}
|
|
2314
|
+
const cached = await readIsrCache(
|
|
2315
|
+
env,
|
|
2316
|
+
routing.projectId,
|
|
2317
|
+
isrDeploymentId,
|
|
2318
|
+
url.hostname,
|
|
2319
|
+
pathname,
|
|
2320
|
+
isrOriginalUrlVariant
|
|
2321
|
+
);
|
|
2322
|
+
if (cached) {
|
|
2323
|
+
const stale = isStale(cached);
|
|
2324
|
+
const cacheResponse = isrCacheResponse(cached, stale);
|
|
2325
|
+
const repairResult = await maybeServeWfpAssetFallback({
|
|
2326
|
+
env,
|
|
2327
|
+
routing,
|
|
2328
|
+
pathname,
|
|
2329
|
+
response: cacheResponse,
|
|
2330
|
+
userAgent: request.headers.get("user-agent"),
|
|
2331
|
+
timeoutMs,
|
|
2332
|
+
fallbackPathname: resolveCachedWfpFallbackPathname(pathname, routing, url.hostname)
|
|
2333
|
+
});
|
|
2334
|
+
if (repairResult.response !== cacheResponse) {
|
|
2335
|
+
ctx.waitUntil(purgePoisonedIsrEntry());
|
|
2336
|
+
requestType = "isr";
|
|
2337
|
+
return meter(
|
|
2338
|
+
repairResult.response.ok ? clientRevalidateResponse(repairResult.response) : repairResult.response,
|
|
2339
|
+
repairResult.failureKind ? { failureKind: repairResult.failureKind } : void 0
|
|
2340
|
+
);
|
|
2341
|
+
}
|
|
2342
|
+
if (stale) {
|
|
2343
|
+
const staleCacheResponse = cacheResponse.clone();
|
|
2344
|
+
ctx.waitUntil(
|
|
2345
|
+
revalidateInBackground(
|
|
2346
|
+
env,
|
|
2347
|
+
routing,
|
|
2348
|
+
workerRequest,
|
|
2349
|
+
url.hostname,
|
|
2350
|
+
pathname,
|
|
2351
|
+
isrTtl,
|
|
2352
|
+
cacheKey,
|
|
2353
|
+
jsonCacheKey,
|
|
2354
|
+
cached.createdAt + cached.ttl * 1e3,
|
|
2355
|
+
isrOriginalUrlVariant,
|
|
2356
|
+
timeoutMs
|
|
2357
|
+
).then(async (sharedEntryChanged) => {
|
|
2358
|
+
if (!sharedEntryChanged) {
|
|
2359
|
+
await cache.put(cacheKey, staleCacheResponse);
|
|
2360
|
+
}
|
|
2361
|
+
})
|
|
2362
|
+
);
|
|
2363
|
+
}
|
|
2364
|
+
requestType = "isr";
|
|
2365
|
+
if (!stale) {
|
|
2366
|
+
ctx.waitUntil(cache.put(cacheKey, cacheResponse.clone()));
|
|
2367
|
+
}
|
|
2368
|
+
return meter(clientRevalidateResponse(cacheResponse));
|
|
2369
|
+
}
|
|
2370
|
+
requestType = "isr";
|
|
2371
|
+
const worker2 = getWorker(env, routing);
|
|
2372
|
+
const isrWorkerStartedAt = Date.now();
|
|
2373
|
+
let workerResponse;
|
|
2374
|
+
try {
|
|
2375
|
+
workerResponse = await withTimeout(
|
|
2376
|
+
worker2.fetch(workerRequest),
|
|
2377
|
+
remainingTimeoutBudget(isrWorkerStartedAt, timeoutMs, "dispatch"),
|
|
2378
|
+
"dispatch"
|
|
2379
|
+
);
|
|
2380
|
+
} catch (error) {
|
|
2381
|
+
if (error instanceof DispatchTimeoutError) {
|
|
2382
|
+
return meterWorkerTimeout();
|
|
2383
|
+
}
|
|
2384
|
+
throw error;
|
|
2385
|
+
}
|
|
2386
|
+
let isWorkerEventStream = isEventStreamResponse(workerResponse);
|
|
2387
|
+
let workerResponseFailureKind;
|
|
2388
|
+
const cachePathname = pathname;
|
|
2389
|
+
const cacheOriginalUrlVariant = isrOriginalUrlVariant;
|
|
2390
|
+
const cacheKeyForWrite = cacheKey;
|
|
2391
|
+
const jsonCacheKeyForWrite = jsonCacheKey;
|
|
2392
|
+
let effectiveMissPathname = cachePathname;
|
|
2393
|
+
let missFallbackApplied = false;
|
|
2394
|
+
if (!isWorkerEventStream) {
|
|
2395
|
+
const assetFallbackResult = await maybeServeWfpAssetFallback({
|
|
2396
|
+
env,
|
|
2397
|
+
routing,
|
|
2398
|
+
pathname,
|
|
2399
|
+
response: workerResponse,
|
|
2400
|
+
userAgent: request.headers.get("user-agent"),
|
|
2401
|
+
deadline: { startedAt: isrWorkerStartedAt, timeoutMs }
|
|
2402
|
+
});
|
|
2403
|
+
workerResponse = assetFallbackResult.response;
|
|
2404
|
+
workerResponseFailureKind = assetFallbackResult.failureKind;
|
|
2405
|
+
isWorkerEventStream = isEventStreamResponse(workerResponse);
|
|
2406
|
+
}
|
|
2407
|
+
if (!isWorkerEventStream) {
|
|
2408
|
+
let fallbackResult;
|
|
2409
|
+
try {
|
|
2410
|
+
fallbackResult = await applyWorkerFallbackRules({
|
|
2411
|
+
response: workerResponse,
|
|
2412
|
+
worker: worker2,
|
|
2413
|
+
dispatchReq: workerRequest,
|
|
2414
|
+
routing,
|
|
2415
|
+
pathname,
|
|
2416
|
+
url,
|
|
2417
|
+
originalRequestUrl: request.url,
|
|
2418
|
+
originalUrlHeader,
|
|
2419
|
+
originalUrlVariant: isrOriginalUrlVariant,
|
|
2420
|
+
deadline: { startedAt: isrWorkerStartedAt, timeoutMs }
|
|
2421
|
+
});
|
|
2422
|
+
} catch (error) {
|
|
2423
|
+
if (error instanceof DispatchTimeoutError) {
|
|
2424
|
+
return meterWorkerTimeout();
|
|
2425
|
+
}
|
|
2426
|
+
throw error;
|
|
2427
|
+
}
|
|
2428
|
+
if (fallbackResult.applied) {
|
|
2429
|
+
missFallbackApplied = true;
|
|
2430
|
+
effectiveMissPathname = fallbackResult.pathname;
|
|
2431
|
+
workerResponse = fallbackResult.response;
|
|
2432
|
+
isWorkerEventStream = isEventStreamResponse(workerResponse);
|
|
2433
|
+
if (!isWorkerEventStream) {
|
|
2434
|
+
const assetFallbackResult = await maybeServeWfpAssetFallback({
|
|
2435
|
+
env,
|
|
2436
|
+
routing,
|
|
2437
|
+
pathname: fallbackResult.pathname,
|
|
2438
|
+
response: workerResponse,
|
|
2439
|
+
userAgent: request.headers.get("user-agent"),
|
|
2440
|
+
deadline: { startedAt: isrWorkerStartedAt, timeoutMs }
|
|
2441
|
+
});
|
|
2442
|
+
workerResponse = assetFallbackResult.response;
|
|
2443
|
+
workerResponseFailureKind = assetFallbackResult.failureKind;
|
|
2444
|
+
isWorkerEventStream = isEventStreamResponse(workerResponse);
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
if (isWorkerEventStream) {
|
|
2449
|
+
return meter(workerResponse);
|
|
2450
|
+
}
|
|
2451
|
+
const ttl = resolveRevalidateTtl(workerResponse, isrTtl);
|
|
2452
|
+
if (ttl > 0 && workerResponse.status === 200 && !isApiPath(effectiveMissPathname) && !hasSharedCacheOptOut(workerResponse) && (!missFallbackApplied || isFallbackTargetCacheable(
|
|
2453
|
+
routing.pages,
|
|
2454
|
+
effectiveMissPathname,
|
|
2455
|
+
workerResponse,
|
|
2456
|
+
routing.pathRevalidate
|
|
2457
|
+
))) {
|
|
2458
|
+
ctx.waitUntil(
|
|
2459
|
+
writeIsrCache(
|
|
2460
|
+
env,
|
|
2461
|
+
routing.projectId,
|
|
2462
|
+
isrDeploymentId,
|
|
2463
|
+
url.hostname,
|
|
2464
|
+
cachePathname,
|
|
2465
|
+
workerResponse,
|
|
2466
|
+
ttl,
|
|
2467
|
+
cacheOriginalUrlVariant
|
|
2468
|
+
)
|
|
2469
|
+
);
|
|
2470
|
+
ctx.waitUntil(
|
|
2471
|
+
populateJsonEdgeCache(cache, jsonCacheKeyForWrite, workerResponse, ttl, "MISS")
|
|
2472
|
+
);
|
|
2473
|
+
const cacheHeaders = new Headers(workerResponse.headers);
|
|
2474
|
+
cacheHeaders.set(
|
|
2475
|
+
"Cache-Control",
|
|
2476
|
+
`public, s-maxage=${ttl}, max-age=0, must-revalidate`
|
|
2477
|
+
);
|
|
2478
|
+
cacheHeaders.set("x-isr-cache", "MISS");
|
|
2479
|
+
markSharedCachePolicy(cacheHeaders);
|
|
2480
|
+
cacheHeaders.delete("x-void-page-data");
|
|
2481
|
+
cacheHeaders.delete("set-cookie");
|
|
2482
|
+
const cacheResponse = new Response(workerResponse.clone().body, {
|
|
2483
|
+
status: workerResponse.status,
|
|
2484
|
+
headers: cacheHeaders
|
|
2485
|
+
});
|
|
2486
|
+
ctx.waitUntil(cache.put(cacheKeyForWrite, cacheResponse.clone()));
|
|
2487
|
+
const clientHeaders = new Headers(cacheHeaders);
|
|
2488
|
+
clientHeaders.set("Cache-Control", CLIENT_REVALIDATE_CACHE_CONTROL);
|
|
2489
|
+
clientHeaders.delete(SHARED_CACHE_POLICY_HEADER);
|
|
2490
|
+
const clientResponse = new Response(workerResponse.body, {
|
|
2491
|
+
status: workerResponse.status,
|
|
2492
|
+
headers: clientHeaders
|
|
2493
|
+
});
|
|
2494
|
+
return meter(
|
|
2495
|
+
clientResponse,
|
|
2496
|
+
workerResponseFailureKind ? { failureKind: workerResponseFailureKind } : void 0
|
|
2497
|
+
);
|
|
2498
|
+
}
|
|
2499
|
+
if (workerResponse.status === 200 && !missFallbackApplied && (ttl === 0 || hasSharedCacheOptOut(workerResponse))) {
|
|
2500
|
+
ctx.waitUntil(purgePoisonedIsrEntry());
|
|
2501
|
+
}
|
|
2502
|
+
return meter(
|
|
2503
|
+
workerResponse,
|
|
2504
|
+
workerResponseFailureKind ? { failureKind: workerResponseFailureKind } : void 0
|
|
2505
|
+
);
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
const staticCachePathname = pathname;
|
|
2510
|
+
const isStaticCandidate = isStaticCacheRequestEligible(request, staticCachePathname, routing) && await isKnownStaticAssetPath(env, staticCachePathname, routing, timeoutMs);
|
|
2511
|
+
let staticCacheKey;
|
|
2512
|
+
if (isStaticCandidate) {
|
|
2513
|
+
const cacheUrl = new URL(
|
|
2514
|
+
buildVariantOriginalUrl(
|
|
2515
|
+
request.url,
|
|
2516
|
+
new URL(request.url).pathname,
|
|
2517
|
+
exactQueryAllowlist(routing.queryAllowlist)
|
|
2518
|
+
)
|
|
2519
|
+
);
|
|
2520
|
+
cacheUrl.searchParams.set("_v", String(routing.version));
|
|
2521
|
+
const candidateCacheKey = new Request(cacheUrl.toString());
|
|
2522
|
+
staticCacheKey = candidateCacheKey;
|
|
2523
|
+
const staticCache = applicationEdgeCache;
|
|
2524
|
+
const cached = await staticCache.match(candidateCacheKey);
|
|
2525
|
+
if (cached && isCurrentStaticCacheResponse(cached)) {
|
|
2526
|
+
requestType = "static";
|
|
2527
|
+
if (!routing.staticDeploymentId) {
|
|
2528
|
+
const repairResult = await maybeServeWfpAssetFallback({
|
|
2529
|
+
env,
|
|
2530
|
+
routing,
|
|
2531
|
+
pathname,
|
|
2532
|
+
response: cached,
|
|
2533
|
+
userAgent: request.headers.get("user-agent"),
|
|
2534
|
+
timeoutMs,
|
|
2535
|
+
fallbackPathname: resolveCachedWfpFallbackPathname(pathname, routing, url.hostname)
|
|
2536
|
+
});
|
|
2537
|
+
if (repairResult.response !== cached) {
|
|
2538
|
+
if (isStaticCacheResponseEligible(repairResult.response)) {
|
|
2539
|
+
const finalRes = applyHeaderRules(
|
|
2540
|
+
repairResult.response,
|
|
2541
|
+
repairResult.pathname,
|
|
2542
|
+
routing.headerRules
|
|
2543
|
+
);
|
|
2544
|
+
const repaired = new Response(finalRes.clone().body, finalRes);
|
|
2545
|
+
repaired.headers.set(
|
|
2546
|
+
"Cache-Control",
|
|
2547
|
+
"public, s-maxage=31536000, max-age=0, must-revalidate"
|
|
2548
|
+
);
|
|
2549
|
+
markSharedCachePolicy(repaired.headers);
|
|
2550
|
+
const repairedForCache = repaired.clone();
|
|
2551
|
+
ctx.waitUntil(
|
|
2552
|
+
staticCache.delete(candidateCacheKey).then(() => staticCache.put(candidateCacheKey, repairedForCache))
|
|
2553
|
+
);
|
|
2554
|
+
return meter(clientRevalidateResponse(finalRes));
|
|
2555
|
+
}
|
|
2556
|
+
ctx.waitUntil(staticCache.delete(candidateCacheKey));
|
|
2557
|
+
return meter(
|
|
2558
|
+
repairResult.response,
|
|
2559
|
+
repairResult.failureKind ? { failureKind: repairResult.failureKind } : void 0
|
|
2560
|
+
);
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
return meter(clientRevalidateResponse(cached));
|
|
2564
|
+
}
|
|
2565
|
+
if (cached) {
|
|
2566
|
+
await staticCache.delete(candidateCacheKey);
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
if (routing.staticDeploymentId) {
|
|
2570
|
+
const staticAssetStartedAt = Date.now();
|
|
2571
|
+
let assetManifest;
|
|
2572
|
+
try {
|
|
2573
|
+
assetManifest = await fetchAssetManifest(
|
|
2574
|
+
env,
|
|
2575
|
+
routing.staticDeploymentId,
|
|
2576
|
+
routing.projectId,
|
|
2577
|
+
remainingTimeoutBudget(staticAssetStartedAt, timeoutMs)
|
|
2578
|
+
);
|
|
2579
|
+
} catch (err) {
|
|
2580
|
+
if (err instanceof DispatchTimeoutError) {
|
|
2581
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_TIMEOUT), {
|
|
2582
|
+
failureKind: PLATFORM_STATIC_ASSET_TIMEOUT
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
if (err instanceof StaticAssetReadError) {
|
|
2586
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_ERROR), {
|
|
2587
|
+
failureKind: PLATFORM_STATIC_ASSET_ERROR
|
|
2588
|
+
});
|
|
2589
|
+
}
|
|
2590
|
+
throw err;
|
|
2591
|
+
}
|
|
2592
|
+
if (!assetManifest) {
|
|
2593
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_MISSING), {
|
|
2594
|
+
failureKind: PLATFORM_STATIC_ASSET_MISSING
|
|
2595
|
+
});
|
|
2596
|
+
}
|
|
2597
|
+
const assetConfig = routing.assetConfig ?? {};
|
|
2598
|
+
const fallbackOriginPathname = pathname;
|
|
2599
|
+
let resolved = resolveStaticPath(pathname, assetManifest, assetConfig);
|
|
2600
|
+
if (!resolved && routing.fallbackRules?.length) {
|
|
2601
|
+
const fallback = applyRoutingRules(pathname, routing.fallbackRules, url.hostname);
|
|
2602
|
+
if (fallback?.type === "rewrite") {
|
|
2603
|
+
pathname = fallback.destination;
|
|
2604
|
+
url.pathname = pathname;
|
|
2605
|
+
resolved = resolveStaticPath(pathname, assetManifest, assetConfig);
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
if (!resolved) {
|
|
2609
|
+
return meter(createErrorResponse(request, 404, "Not Found"));
|
|
2610
|
+
}
|
|
2611
|
+
if (resolved.redirect) {
|
|
2612
|
+
return meter(
|
|
2613
|
+
new Response(null, {
|
|
2614
|
+
status: 308,
|
|
2615
|
+
headers: { Location: resolved.redirect }
|
|
2616
|
+
})
|
|
2617
|
+
);
|
|
2618
|
+
}
|
|
2619
|
+
let assetResponse;
|
|
2620
|
+
try {
|
|
2621
|
+
assetResponse = await fetchStaticAsset(
|
|
2622
|
+
env,
|
|
2623
|
+
resolved.hash,
|
|
2624
|
+
resolved.path,
|
|
2625
|
+
routing.projectId,
|
|
2626
|
+
routing.staticDeploymentId,
|
|
2627
|
+
remainingTimeoutBudget(staticAssetStartedAt, timeoutMs)
|
|
2628
|
+
);
|
|
2629
|
+
} catch (err) {
|
|
2630
|
+
if (err instanceof DispatchTimeoutError) {
|
|
2631
|
+
return meter(
|
|
2632
|
+
timeoutResponse(resolved.notFound ? 404 : 503, "static-asset"),
|
|
2633
|
+
resolved.notFound ? void 0 : { failureKind: PLATFORM_STATIC_ASSET_TIMEOUT }
|
|
2634
|
+
);
|
|
2635
|
+
}
|
|
2636
|
+
if (err instanceof StaticAssetReadError) {
|
|
2637
|
+
return meter(
|
|
2638
|
+
resolved.notFound ? createErrorResponse(request, 404, "Not Found") : staticAssetFailureResponse(PLATFORM_STATIC_ASSET_ERROR),
|
|
2639
|
+
resolved.notFound ? void 0 : { failureKind: PLATFORM_STATIC_ASSET_ERROR }
|
|
2640
|
+
);
|
|
2641
|
+
}
|
|
2642
|
+
throw err;
|
|
2643
|
+
}
|
|
2644
|
+
if (!assetResponse) {
|
|
2645
|
+
if (resolved.notFound) {
|
|
2646
|
+
return meter(createErrorResponse(request, 404, "Not Found"));
|
|
2647
|
+
}
|
|
2648
|
+
logStaticAssetMissing(routing, resolved.path, resolved.hash);
|
|
2649
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_MISSING), {
|
|
2650
|
+
failureKind: PLATFORM_STATIC_ASSET_MISSING
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
const status = resolved.notFound ? 404 : 200;
|
|
2654
|
+
const response = new Response(assetResponse.body, {
|
|
2655
|
+
status,
|
|
2656
|
+
headers: assetResponse.headers
|
|
2657
|
+
});
|
|
2658
|
+
const finalRes = applyHeaderRules(response, pathname, routing.headerRules);
|
|
2659
|
+
const edgeCacheKey = staticCacheKey ?? (isSharedStaticCacheRequestEligible(request) ? new Request(`${url.origin}${fallbackOriginPathname}?_v=${routing.staticDeploymentId}`) : void 0);
|
|
2660
|
+
if (edgeCacheKey && isStaticCacheResponseEligible(finalRes)) {
|
|
2661
|
+
const cached = new Response(finalRes.clone().body, finalRes);
|
|
2662
|
+
cached.headers.set(
|
|
2663
|
+
"Cache-Control",
|
|
2664
|
+
"public, s-maxage=31536000, max-age=0, must-revalidate"
|
|
2665
|
+
);
|
|
2666
|
+
markSharedCachePolicy(cached.headers);
|
|
2667
|
+
ctx.waitUntil(applicationEdgeCache.put(edgeCacheKey, cached.clone()));
|
|
2668
|
+
}
|
|
2669
|
+
return meter(clientRevalidateResponse(finalRes));
|
|
2670
|
+
}
|
|
2671
|
+
const worker = getWorker(env, routing);
|
|
2672
|
+
try {
|
|
2673
|
+
let dispatchReq = workerRequest;
|
|
2674
|
+
if (dispatchReq.bodyUsed) {
|
|
2675
|
+
dispatchReq = rebuildRequest(dispatchReq, dispatchReq.url, () => {
|
|
2676
|
+
});
|
|
2677
|
+
}
|
|
2678
|
+
const hasBodyMethod = dispatchReq.method !== "GET" && dispatchReq.method !== "HEAD";
|
|
2679
|
+
const dispatchReqForFallback = hasBodyMethod && routing.fallbackRules?.length ? dispatchReq.clone() : dispatchReq;
|
|
2680
|
+
const workerDispatchStartedAt = Date.now();
|
|
2681
|
+
let response = await withTimeout(
|
|
2682
|
+
worker.fetch(dispatchReq),
|
|
2683
|
+
remainingTimeoutBudget(workerDispatchStartedAt, timeoutMs, "dispatch"),
|
|
2684
|
+
"dispatch"
|
|
2685
|
+
);
|
|
2686
|
+
let isEventStream = isEventStreamResponse(response);
|
|
2687
|
+
let responseFailureKind;
|
|
2688
|
+
if (!isEventStream) {
|
|
2689
|
+
const assetFallbackResult = await maybeServeWfpAssetFallback({
|
|
2690
|
+
env,
|
|
2691
|
+
routing,
|
|
2692
|
+
pathname,
|
|
2693
|
+
response,
|
|
2694
|
+
userAgent: request.headers.get("user-agent"),
|
|
2695
|
+
deadline: { startedAt: workerDispatchStartedAt, timeoutMs }
|
|
2696
|
+
});
|
|
2697
|
+
response = assetFallbackResult.response;
|
|
2698
|
+
responseFailureKind = assetFallbackResult.failureKind;
|
|
2699
|
+
isEventStream = isEventStreamResponse(response);
|
|
2700
|
+
}
|
|
2701
|
+
let writebackFallbackApplied = false;
|
|
2702
|
+
if (!isEventStream) {
|
|
2703
|
+
const fallbackResult = await applyWorkerFallbackRules({
|
|
2704
|
+
response,
|
|
2705
|
+
worker,
|
|
2706
|
+
dispatchReq: dispatchReqForFallback,
|
|
2707
|
+
routing,
|
|
2708
|
+
pathname,
|
|
2709
|
+
url,
|
|
2710
|
+
originalRequestUrl: request.url,
|
|
2711
|
+
originalUrlHeader,
|
|
2712
|
+
originalUrlVariant,
|
|
2713
|
+
deadline: { startedAt: workerDispatchStartedAt, timeoutMs }
|
|
2714
|
+
});
|
|
2715
|
+
if (fallbackResult.applied) {
|
|
2716
|
+
writebackFallbackApplied = true;
|
|
2717
|
+
pathname = fallbackResult.pathname;
|
|
2718
|
+
url.pathname = pathname;
|
|
2719
|
+
originalUrlVariant = fallbackResult.originalUrlVariant;
|
|
2720
|
+
response = fallbackResult.response;
|
|
2721
|
+
isEventStream = isEventStreamResponse(response);
|
|
2722
|
+
if (!isEventStream) {
|
|
2723
|
+
const assetFallbackResult = await maybeServeWfpAssetFallback({
|
|
2724
|
+
env,
|
|
2725
|
+
routing,
|
|
2726
|
+
pathname,
|
|
2727
|
+
response,
|
|
2728
|
+
userAgent: request.headers.get("user-agent"),
|
|
2729
|
+
deadline: { startedAt: workerDispatchStartedAt, timeoutMs }
|
|
2730
|
+
});
|
|
2731
|
+
response = assetFallbackResult.response;
|
|
2732
|
+
responseFailureKind = assetFallbackResult.failureKind;
|
|
2733
|
+
isEventStream = isEventStreamResponse(response);
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
if (sharedIsrEntry && response.status === 200 && !writebackFallbackApplied && (resolveRevalidateTtl(
|
|
2738
|
+
response,
|
|
2739
|
+
resolvePageTtl(
|
|
2740
|
+
routing.pages,
|
|
2741
|
+
routing.revalidate,
|
|
2742
|
+
routing.pathRevalidate,
|
|
2743
|
+
sharedIsrEntry.pathname
|
|
2744
|
+
)
|
|
2745
|
+
) === 0 || hasSharedCacheOptOut(response))) {
|
|
2746
|
+
ctx.waitUntil(deleteSharedIsrEntry(env, sharedIsrEntry));
|
|
2747
|
+
}
|
|
2748
|
+
if (staticCacheKey && !writebackFallbackApplied && isStaticCacheResponseEligible(response)) {
|
|
2749
|
+
const res = new Response(response.body, response);
|
|
2750
|
+
res.headers.delete("x-void-page-data");
|
|
2751
|
+
const finalRes = applyHeaderRules(res, pathname, routing.headerRules);
|
|
2752
|
+
const cacheRes = new Response(finalRes.clone().body, finalRes);
|
|
2753
|
+
cacheRes.headers.set(
|
|
2754
|
+
"Cache-Control",
|
|
2755
|
+
"public, s-maxage=31536000, max-age=0, must-revalidate"
|
|
2756
|
+
);
|
|
2757
|
+
markSharedCachePolicy(cacheRes.headers);
|
|
2758
|
+
ctx.waitUntil(applicationEdgeCache.put(staticCacheKey, cacheRes.clone()));
|
|
2759
|
+
return meter(
|
|
2760
|
+
clientRevalidateResponse(finalRes),
|
|
2761
|
+
responseFailureKind ? { failureKind: responseFailureKind } : void 0
|
|
2762
|
+
);
|
|
2763
|
+
}
|
|
2764
|
+
const dynamicType = classifyDynamicRequestType(pathname, routing.ssr);
|
|
2765
|
+
if (dynamicType) {
|
|
2766
|
+
requestType = dynamicType;
|
|
2767
|
+
}
|
|
2768
|
+
if ((routing.revalidate || routing.pages?.length) && !isEventStream && !request.headers.has("X-VoidPages") && request.method === "GET" && !request.headers.has("Range") && !shouldBypassIsr(request)) {
|
|
2769
|
+
const configTtl = resolvePageTtl(
|
|
2770
|
+
routing.pages,
|
|
2771
|
+
routing.revalidate,
|
|
2772
|
+
routing.pathRevalidate,
|
|
2773
|
+
pathname
|
|
2774
|
+
);
|
|
2775
|
+
const isrTtl = configTtl > 0 ? resolveRevalidateTtl(response, configTtl) : 0;
|
|
2776
|
+
if (isrTtl > 0 && response.status === 200 && requestType === "ssr" && !hasSharedCacheOptOut(response) && (!writebackFallbackApplied || isFallbackTargetCacheable(routing.pages, pathname, response, routing.pathRevalidate))) {
|
|
2777
|
+
const isrDeploymentId = routing.version ?? "legacy";
|
|
2778
|
+
ctx.waitUntil(
|
|
2779
|
+
writeIsrCache(
|
|
2780
|
+
env,
|
|
2781
|
+
routing.projectId,
|
|
2782
|
+
isrDeploymentId,
|
|
2783
|
+
url.hostname,
|
|
2784
|
+
pathname,
|
|
2785
|
+
response,
|
|
2786
|
+
isrTtl,
|
|
2787
|
+
originalUrlVariant
|
|
2788
|
+
)
|
|
2789
|
+
);
|
|
2790
|
+
const jsonCacheKey = isrEdgeCacheKeyJson(
|
|
2791
|
+
request,
|
|
2792
|
+
pathname,
|
|
2793
|
+
isrDeploymentId,
|
|
2794
|
+
originalUrlVariant
|
|
2795
|
+
);
|
|
2796
|
+
ctx.waitUntil(
|
|
2797
|
+
populateJsonEdgeCache(applicationEdgeCache, jsonCacheKey, response, isrTtl, "MISS")
|
|
2798
|
+
);
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
if (response.headers.has("x-void-page-data")) {
|
|
2802
|
+
const stripped = new Response(response.body, response);
|
|
2803
|
+
stripped.headers.delete("x-void-page-data");
|
|
2804
|
+
return meter(
|
|
2805
|
+
applyHeaderRules(stripped, pathname, routing.headerRules),
|
|
2806
|
+
responseFailureKind ? { failureKind: responseFailureKind } : void 0
|
|
2807
|
+
);
|
|
2808
|
+
}
|
|
2809
|
+
return meter(
|
|
2810
|
+
applyHeaderRules(response, pathname, routing.headerRules),
|
|
2811
|
+
responseFailureKind ? { failureKind: responseFailureKind } : void 0
|
|
2812
|
+
);
|
|
2813
|
+
} catch (err) {
|
|
2814
|
+
if (err instanceof DispatchTimeoutError) {
|
|
2815
|
+
if (err.label === "static-asset") {
|
|
2816
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_TIMEOUT), {
|
|
2817
|
+
failureKind: PLATFORM_STATIC_ASSET_TIMEOUT
|
|
2818
|
+
});
|
|
2819
|
+
}
|
|
2820
|
+
const dynamicType = classifyDynamicRequestType(pathname, routing.ssr);
|
|
2821
|
+
if (dynamicType) {
|
|
2822
|
+
requestType = dynamicType;
|
|
2823
|
+
}
|
|
2824
|
+
return meterWorkerTimeout();
|
|
2825
|
+
}
|
|
2826
|
+
if (err instanceof StaticAssetReadError) {
|
|
2827
|
+
return meter(staticAssetFailureResponse(PLATFORM_STATIC_ASSET_ERROR), {
|
|
2828
|
+
failureKind: PLATFORM_STATIC_ASSET_ERROR
|
|
2829
|
+
});
|
|
2830
|
+
}
|
|
2831
|
+
if (err instanceof Error && err.message.includes("Worker exceeded")) {
|
|
2832
|
+
return meter(Response.json({ error: "CPU limit exceeded" }, { status: 429 }));
|
|
2833
|
+
}
|
|
2834
|
+
throw err;
|
|
2835
|
+
}
|
|
2836
|
+
},
|
|
2837
|
+
async queue(batch, env) {
|
|
2838
|
+
if (env.PLATFORM_TRAFFIC_STATE === "staging" || env.PLATFORM_TRAFFIC_STATE === "sandbox-drain") {
|
|
2839
|
+
console.warn(
|
|
2840
|
+
`[queue-relay] platform traffic is staging; retrying queue="${batch.queue}" in ${PLATFORM_QUEUE_RETRY_DELAY_SECONDS}s`
|
|
2841
|
+
);
|
|
2842
|
+
batch.retryAll({ delaySeconds: PLATFORM_QUEUE_RETRY_DELAY_SECONDS });
|
|
2843
|
+
return;
|
|
2844
|
+
}
|
|
2845
|
+
if (env.PLATFORM_INSTALLATION_ID) {
|
|
2846
|
+
let disabled;
|
|
2847
|
+
try {
|
|
2848
|
+
disabled = await env.ROUTING.get(PLATFORM_DISABLED_KEY);
|
|
2849
|
+
} catch (error) {
|
|
2850
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2851
|
+
console.error(
|
|
2852
|
+
`[queue-relay] platform traffic gate read failed; retrying queue="${batch.queue}" in ${PLATFORM_QUEUE_RETRY_DELAY_SECONDS}s error=${JSON.stringify(message)}`
|
|
2853
|
+
);
|
|
2854
|
+
batch.retryAll({ delaySeconds: PLATFORM_QUEUE_RETRY_DELAY_SECONDS });
|
|
2855
|
+
return;
|
|
2856
|
+
}
|
|
2857
|
+
if (disabled === PLATFORM_DISABLED_VALUE) {
|
|
2858
|
+
console.warn(
|
|
2859
|
+
`[queue-relay] platform traffic is disabled; retrying queue="${batch.queue}" in ${PLATFORM_QUEUE_RETRY_DELAY_SECONDS}s`
|
|
2860
|
+
);
|
|
2861
|
+
batch.retryAll({ delaySeconds: PLATFORM_QUEUE_RETRY_DELAY_SECONDS });
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
const routingRaw = await env.ROUTING.get(`queue:${batch.queue}`, { cacheTtl: 60 });
|
|
2866
|
+
if (!routingRaw) {
|
|
2867
|
+
console.error(
|
|
2868
|
+
`[queue-relay] missing routing entry for queue="${batch.queue}"; retrying all messages`
|
|
2869
|
+
);
|
|
2870
|
+
batch.retryAll();
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
const { workerName, queueName, proxyToken } = JSON.parse(routingRaw);
|
|
2874
|
+
const messages = Array.from(batch.messages, (message) => ({
|
|
2875
|
+
id: message.id,
|
|
2876
|
+
timestamp: message.timestamp.toISOString(),
|
|
2877
|
+
body: message.body,
|
|
2878
|
+
attempts: message.attempts
|
|
2879
|
+
}));
|
|
2880
|
+
const worker = env.DISPATCH.get(workerName);
|
|
2881
|
+
try {
|
|
2882
|
+
const body = JSON.stringify({ queue: queueName, messages });
|
|
2883
|
+
const queueHeaders = { "Content-Type": "application/json" };
|
|
2884
|
+
if (proxyToken) {
|
|
2885
|
+
queueHeaders["x-void-internal"] = proxyToken;
|
|
2886
|
+
}
|
|
2887
|
+
let res = await worker.fetch(
|
|
2888
|
+
new Request("https://internal/__void/queue", {
|
|
2889
|
+
method: "POST",
|
|
2890
|
+
headers: queueHeaders,
|
|
2891
|
+
body
|
|
2892
|
+
})
|
|
2893
|
+
);
|
|
2894
|
+
if (res.status === 404) {
|
|
2895
|
+
res = await worker.fetch(
|
|
2896
|
+
new Request("https://internal/__queue", {
|
|
2897
|
+
method: "POST",
|
|
2898
|
+
headers: queueHeaders,
|
|
2899
|
+
body
|
|
2900
|
+
})
|
|
2901
|
+
);
|
|
2902
|
+
}
|
|
2903
|
+
if (!res.ok) {
|
|
2904
|
+
let snippet = "";
|
|
2905
|
+
try {
|
|
2906
|
+
snippet = (await res.clone().text()).slice(0, 200);
|
|
2907
|
+
} catch {
|
|
2908
|
+
}
|
|
2909
|
+
console.error(
|
|
2910
|
+
`[queue-relay] relay failed queue="${queueName}" worker="${workerName}" status=${res.status} body=${JSON.stringify(snippet)}`
|
|
2911
|
+
);
|
|
2912
|
+
batch.retryAll();
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
let result;
|
|
2916
|
+
try {
|
|
2917
|
+
result = await res.json();
|
|
2918
|
+
} catch {
|
|
2919
|
+
batch.ackAll();
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
if (!result.decisions) {
|
|
2923
|
+
if (result.ok !== false) {
|
|
2924
|
+
batch.ackAll();
|
|
2925
|
+
} else {
|
|
2926
|
+
batch.retryAll();
|
|
2927
|
+
}
|
|
2928
|
+
return;
|
|
2929
|
+
}
|
|
2930
|
+
const messageMap = new Map(batch.messages.map((message) => [message.id, message]));
|
|
2931
|
+
for (const [id, decision] of Object.entries(result.decisions)) {
|
|
2932
|
+
const cfMessage = messageMap.get(id);
|
|
2933
|
+
if (!cfMessage) {
|
|
2934
|
+
continue;
|
|
2935
|
+
}
|
|
2936
|
+
if (decision.action === "ack") {
|
|
2937
|
+
cfMessage.ack();
|
|
2938
|
+
} else if (decision.action === "retry") {
|
|
2939
|
+
cfMessage.retry(
|
|
2940
|
+
decision.delaySeconds != null ? { delaySeconds: decision.delaySeconds } : void 0
|
|
2941
|
+
);
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
if (result.ok === false) {
|
|
2945
|
+
batch.retryAll();
|
|
2946
|
+
}
|
|
2947
|
+
} catch (err) {
|
|
2948
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2949
|
+
console.error(
|
|
2950
|
+
`[queue-relay] relay threw queue="${queueName}" worker="${workerName}" error=${JSON.stringify(msg)}`
|
|
2951
|
+
);
|
|
2952
|
+
batch.retryAll();
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
};
|
|
2956
|
+
var StaticAssetReadError = class extends Error {
|
|
2957
|
+
static {
|
|
2958
|
+
__name(this, "StaticAssetReadError");
|
|
2959
|
+
}
|
|
2960
|
+
constructor(cause) {
|
|
2961
|
+
super("Static asset storage read failed", { cause });
|
|
2962
|
+
this.name = "StaticAssetReadError";
|
|
2963
|
+
}
|
|
2964
|
+
};
|
|
2965
|
+
function logStaticAssetMissing(routing, pathname, hash) {
|
|
2966
|
+
console.error(
|
|
2967
|
+
JSON.stringify({
|
|
2968
|
+
event: "static-asset-missing",
|
|
2969
|
+
store: hash.startsWith("!") ? "r2" : "kv",
|
|
2970
|
+
projectId: routing.projectId,
|
|
2971
|
+
deploymentId: routing.staticDeploymentId ?? routing.assetManifestId,
|
|
2972
|
+
pathname,
|
|
2973
|
+
ts: Date.now()
|
|
2974
|
+
})
|
|
2975
|
+
);
|
|
2976
|
+
}
|
|
2977
|
+
__name(logStaticAssetMissing, "logStaticAssetMissing");
|
|
2978
|
+
async function fetchStaticAsset(env, hash, path, projectId, deploymentId, timeoutMs = 1e4) {
|
|
2979
|
+
const hedgeDelayMs = timeoutMs > 0 && Number.isFinite(timeoutMs) ? Math.min(STATIC_ASSET_HEDGE_DELAY_MS, timeoutMs / 2) : 0;
|
|
2980
|
+
const startedAt = Date.now();
|
|
2981
|
+
let hedged = false;
|
|
2982
|
+
try {
|
|
2983
|
+
const result = await withDelayedHedge(() => fetchStaticAssetOnce(env, hash, path, projectId), {
|
|
2984
|
+
delayMs: hedgeDelayMs,
|
|
2985
|
+
timeoutMs,
|
|
2986
|
+
label: "static-asset",
|
|
2987
|
+
discard: /* @__PURE__ */ __name(async (late) => {
|
|
2988
|
+
await late.body?.cancel();
|
|
2989
|
+
}, "discard"),
|
|
2990
|
+
onHedge: /* @__PURE__ */ __name(() => {
|
|
2991
|
+
hedged = true;
|
|
2992
|
+
console.warn(
|
|
2993
|
+
JSON.stringify({
|
|
2994
|
+
event: "static-asset-read-hedge",
|
|
2995
|
+
store: hash.startsWith("!") ? "r2" : "kv",
|
|
2996
|
+
projectId,
|
|
2997
|
+
deploymentId,
|
|
2998
|
+
pathname: path,
|
|
2999
|
+
delayMs: hedgeDelayMs,
|
|
3000
|
+
ts: Date.now()
|
|
3001
|
+
})
|
|
3002
|
+
);
|
|
3003
|
+
}, "onHedge")
|
|
3004
|
+
});
|
|
3005
|
+
if (hedged) {
|
|
3006
|
+
console.warn(
|
|
3007
|
+
JSON.stringify({
|
|
3008
|
+
event: "static-asset-read-hedge-result",
|
|
3009
|
+
store: hash.startsWith("!") ? "r2" : "kv",
|
|
3010
|
+
projectId,
|
|
3011
|
+
deploymentId,
|
|
3012
|
+
pathname: path,
|
|
3013
|
+
outcome: result === null ? "miss" : "hit",
|
|
3014
|
+
elapsedMs: Date.now() - startedAt,
|
|
3015
|
+
ts: Date.now()
|
|
3016
|
+
})
|
|
3017
|
+
);
|
|
3018
|
+
}
|
|
3019
|
+
return result;
|
|
3020
|
+
} catch (error) {
|
|
3021
|
+
console.error(
|
|
3022
|
+
JSON.stringify({
|
|
3023
|
+
event: error instanceof DispatchTimeoutError ? "static-asset-timeout" : "static-asset-read-error",
|
|
3024
|
+
store: hash.startsWith("!") ? "r2" : "kv",
|
|
3025
|
+
projectId,
|
|
3026
|
+
deploymentId,
|
|
3027
|
+
pathname: path,
|
|
3028
|
+
hedged,
|
|
3029
|
+
elapsedMs: Date.now() - startedAt,
|
|
3030
|
+
error: error instanceof DispatchTimeoutError ? void 0 : error instanceof Error ? error.message : String(error),
|
|
3031
|
+
ts: Date.now()
|
|
3032
|
+
})
|
|
3033
|
+
);
|
|
3034
|
+
if (error instanceof DispatchTimeoutError) {
|
|
3035
|
+
throw error;
|
|
3036
|
+
}
|
|
3037
|
+
throw new StaticAssetReadError(error);
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
__name(fetchStaticAsset, "fetchStaticAsset");
|
|
3041
|
+
async function fetchStaticAssetOnce(env, hash, path, projectId) {
|
|
3042
|
+
const isLarge = hash.startsWith("!");
|
|
3043
|
+
const cleanHash = isLarge ? hash.slice(1) : hash;
|
|
3044
|
+
const scopedKey = projectId ? `assets/${projectId}/${cleanHash}` : null;
|
|
3045
|
+
const legacyKey = `assets/${cleanHash}`;
|
|
3046
|
+
if (isLarge) {
|
|
3047
|
+
const obj = scopedKey ? await env.STATIC_ASSETS_R2.get(scopedKey) ?? await env.STATIC_ASSETS_R2.get(legacyKey) : await env.STATIC_ASSETS_R2.get(legacyKey);
|
|
3048
|
+
if (!obj) {
|
|
3049
|
+
return null;
|
|
3050
|
+
}
|
|
3051
|
+
const contentType2 = resolveAssetContentType(obj.httpMetadata?.contentType, path);
|
|
3052
|
+
const headers2 = {
|
|
3053
|
+
ETag: obj.httpEtag,
|
|
3054
|
+
...obj.size ? { "Content-Length": String(obj.size) } : {}
|
|
3055
|
+
};
|
|
3056
|
+
if (contentType2) {
|
|
3057
|
+
headers2["Content-Type"] = contentType2;
|
|
3058
|
+
}
|
|
3059
|
+
return new Response(obj.body, { headers: headers2 });
|
|
3060
|
+
}
|
|
3061
|
+
if (scopedKey) {
|
|
3062
|
+
const scoped = await env.STATIC_ASSETS.getWithMetadata(scopedKey, {
|
|
3063
|
+
type: "arrayBuffer",
|
|
3064
|
+
cacheTtl: 86400
|
|
3065
|
+
});
|
|
3066
|
+
if (scoped.value) {
|
|
3067
|
+
const contentType2 = resolveAssetContentType(scoped.metadata?.ct, path);
|
|
3068
|
+
const headers2 = {
|
|
3069
|
+
"Content-Length": String(scoped.value.byteLength)
|
|
3070
|
+
};
|
|
3071
|
+
if (contentType2) {
|
|
3072
|
+
headers2["Content-Type"] = contentType2;
|
|
3073
|
+
}
|
|
3074
|
+
return new Response(scoped.value, { headers: headers2 });
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
const { value, metadata } = await env.STATIC_ASSETS.getWithMetadata(legacyKey, {
|
|
3078
|
+
type: "arrayBuffer",
|
|
3079
|
+
cacheTtl: 86400
|
|
3080
|
+
});
|
|
3081
|
+
if (!value) {
|
|
3082
|
+
return null;
|
|
3083
|
+
}
|
|
3084
|
+
const contentType = resolveAssetContentType(metadata?.ct, path);
|
|
3085
|
+
const headers = {
|
|
3086
|
+
"Content-Length": String(value.byteLength)
|
|
3087
|
+
};
|
|
3088
|
+
if (contentType) {
|
|
3089
|
+
headers["Content-Type"] = contentType;
|
|
3090
|
+
}
|
|
3091
|
+
return new Response(value, { headers });
|
|
3092
|
+
}
|
|
3093
|
+
__name(fetchStaticAssetOnce, "fetchStaticAssetOnce");
|
|
3094
|
+
function getWorker(env, routing) {
|
|
3095
|
+
if (routing.limits) {
|
|
3096
|
+
return env.DISPATCH.get(routing.workerName, {}, { limits: routing.limits });
|
|
3097
|
+
}
|
|
3098
|
+
return env.DISPATCH.get(routing.workerName);
|
|
3099
|
+
}
|
|
3100
|
+
__name(getWorker, "getWorker");
|
|
3101
|
+
async function revalidateInBackground(env, routing, request, host, pathname, defaultTtl, cacheKey, jsonCacheKey, staleSinceMs, originalUrl, timeoutMs = 1e4) {
|
|
3102
|
+
try {
|
|
3103
|
+
const worker = getWorker(env, routing);
|
|
3104
|
+
const revalidationStartedAt = Date.now();
|
|
3105
|
+
const cleanRequest = rebuildRequest(
|
|
3106
|
+
request,
|
|
3107
|
+
request.url,
|
|
3108
|
+
(headers) => headers.delete("X-VoidPages")
|
|
3109
|
+
);
|
|
3110
|
+
let response = await withTimeout(
|
|
3111
|
+
worker.fetch(cleanRequest),
|
|
3112
|
+
remainingTimeoutBudget(revalidationStartedAt, timeoutMs, "dispatch"),
|
|
3113
|
+
"dispatch"
|
|
3114
|
+
);
|
|
3115
|
+
if (isEventStreamResponse(response)) {
|
|
3116
|
+
return false;
|
|
3117
|
+
}
|
|
3118
|
+
const originalStatus = response.status;
|
|
3119
|
+
const fallbackResult = await applyWorkerFallbackRules({
|
|
3120
|
+
response,
|
|
3121
|
+
worker,
|
|
3122
|
+
dispatchReq: cleanRequest,
|
|
3123
|
+
routing,
|
|
3124
|
+
pathname,
|
|
3125
|
+
url: new URL(cleanRequest.url),
|
|
3126
|
+
originalRequestUrl: request.url,
|
|
3127
|
+
originalUrlHeader: request.headers.get("X-Void-Original-URL") ?? void 0,
|
|
3128
|
+
originalUrlVariant: originalUrl ?? void 0,
|
|
3129
|
+
deadline: { startedAt: revalidationStartedAt, timeoutMs }
|
|
3130
|
+
});
|
|
3131
|
+
response = fallbackResult.response;
|
|
3132
|
+
if (isEventStreamResponse(response)) {
|
|
3133
|
+
return false;
|
|
3134
|
+
}
|
|
3135
|
+
const assetFallbackResult = await maybeServeWfpAssetFallback({
|
|
3136
|
+
env,
|
|
3137
|
+
routing,
|
|
3138
|
+
pathname: fallbackResult.pathname,
|
|
3139
|
+
response,
|
|
3140
|
+
userAgent: request.headers.get("user-agent"),
|
|
3141
|
+
deadline: { startedAt: revalidationStartedAt, timeoutMs }
|
|
3142
|
+
});
|
|
3143
|
+
response = assetFallbackResult.response;
|
|
3144
|
+
if (isEventStreamResponse(response)) {
|
|
3145
|
+
return false;
|
|
3146
|
+
}
|
|
3147
|
+
const ttl = resolveRevalidateTtl(response, defaultTtl);
|
|
3148
|
+
const cache = applicationEdgeCache;
|
|
3149
|
+
const evictSharedEntry = /* @__PURE__ */ __name(async () => {
|
|
3150
|
+
await deleteIsrCache(
|
|
3151
|
+
env,
|
|
3152
|
+
routing.projectId,
|
|
3153
|
+
routing.version ?? "legacy",
|
|
3154
|
+
host,
|
|
3155
|
+
pathname,
|
|
3156
|
+
originalUrl
|
|
3157
|
+
);
|
|
3158
|
+
await cache.delete(cacheKey);
|
|
3159
|
+
await cache.delete(jsonCacheKey);
|
|
3160
|
+
}, "evictSharedEntry");
|
|
3161
|
+
const effectiveBgPathname = fallbackResult.applied ? fallbackResult.pathname : pathname;
|
|
3162
|
+
if (ttl > 0 && response.status === 200 && !isApiPath(effectiveBgPathname) && !hasSharedCacheOptOut(response) && (!fallbackResult.applied || isFallbackTargetCacheable(
|
|
3163
|
+
routing.pages,
|
|
3164
|
+
effectiveBgPathname,
|
|
3165
|
+
response,
|
|
3166
|
+
routing.pathRevalidate
|
|
3167
|
+
))) {
|
|
3168
|
+
await writeIsrCache(
|
|
3169
|
+
env,
|
|
3170
|
+
routing.projectId,
|
|
3171
|
+
routing.version ?? "legacy",
|
|
3172
|
+
host,
|
|
3173
|
+
pathname,
|
|
3174
|
+
response,
|
|
3175
|
+
ttl,
|
|
3176
|
+
originalUrl
|
|
3177
|
+
);
|
|
3178
|
+
const headers = new Headers(response.headers);
|
|
3179
|
+
headers.set("x-isr-cache", "HIT");
|
|
3180
|
+
headers.set("Cache-Control", `public, s-maxage=${ttl}, max-age=0, must-revalidate`);
|
|
3181
|
+
markSharedCachePolicy(headers);
|
|
3182
|
+
headers.delete("set-cookie");
|
|
3183
|
+
headers.delete("x-void-page-data");
|
|
3184
|
+
await cache.put(
|
|
3185
|
+
cacheKey,
|
|
3186
|
+
new Response(response.clone().body, {
|
|
3187
|
+
status: response.status,
|
|
3188
|
+
headers
|
|
3189
|
+
})
|
|
3190
|
+
);
|
|
3191
|
+
await populateJsonEdgeCache(cache, jsonCacheKey, response, ttl, "HIT");
|
|
3192
|
+
return true;
|
|
3193
|
+
} else if (response.status === 200 && !isApiPath(effectiveBgPathname) && !fallbackResult.applied && (ttl === 0 || hasSharedCacheOptOut(response))) {
|
|
3194
|
+
try {
|
|
3195
|
+
await evictSharedEntry();
|
|
3196
|
+
} catch (error) {
|
|
3197
|
+
console.error("ISR privacy eviction failed:", error);
|
|
3198
|
+
}
|
|
3199
|
+
return true;
|
|
3200
|
+
} else if ((originalStatus === 404 || originalStatus === 410) && Date.now() - staleSinceMs > ISR_STALE_EVICT_GRACE_SECONDS * 1e3) {
|
|
3201
|
+
try {
|
|
3202
|
+
await evictSharedEntry();
|
|
3203
|
+
} catch (error) {
|
|
3204
|
+
console.error("ISR stale-entry eviction failed:", error);
|
|
3205
|
+
}
|
|
3206
|
+
return true;
|
|
3207
|
+
}
|
|
3208
|
+
return false;
|
|
3209
|
+
} catch (err) {
|
|
3210
|
+
console.error("ISR revalidation failed:", err);
|
|
3211
|
+
return false;
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
__name(revalidateInBackground, "revalidateInBackground");
|
|
3215
|
+
function isApiPath(pathname) {
|
|
3216
|
+
return pathname === "/api" || pathname.startsWith("/api/");
|
|
3217
|
+
}
|
|
3218
|
+
__name(isApiPath, "isApiPath");
|
|
3219
|
+
function isIsrEligiblePath(pathname, pages, revalidate, pathRevalidate) {
|
|
3220
|
+
if (isApiPath(pathname)) {
|
|
3221
|
+
return false;
|
|
3222
|
+
}
|
|
3223
|
+
if (!hasExtension(pathname)) {
|
|
3224
|
+
return true;
|
|
3225
|
+
}
|
|
3226
|
+
return isDottedPathIsrEligible(pages, revalidate, pathRevalidate, pathname);
|
|
3227
|
+
}
|
|
3228
|
+
__name(isIsrEligiblePath, "isIsrEligiblePath");
|
|
3229
|
+
function isFallbackTargetCacheable(pages, targetPathname, response, pathRevalidate) {
|
|
3230
|
+
return isHtmlResponse(response) && matchesLiteralPage(pages, targetPathname, pathRevalidate);
|
|
3231
|
+
}
|
|
3232
|
+
__name(isFallbackTargetCacheable, "isFallbackTargetCacheable");
|
|
3233
|
+
function isSharedStaticCacheRequestEligible(request) {
|
|
3234
|
+
return Boolean(
|
|
3235
|
+
request.method === "GET" && !request.headers.has("Range") && !request.headers.has("X-VoidPages") && !shouldBypassIsr(request)
|
|
3236
|
+
);
|
|
3237
|
+
}
|
|
3238
|
+
__name(isSharedStaticCacheRequestEligible, "isSharedStaticCacheRequestEligible");
|
|
3239
|
+
function isStaticCacheRequestEligible(request, pathname, routing) {
|
|
3240
|
+
return Boolean(
|
|
3241
|
+
isSharedStaticCacheRequestEligible(request) && routing.version && !isApiPath(pathname) && (hasExtension(pathname) || !routing.ssr)
|
|
3242
|
+
);
|
|
3243
|
+
}
|
|
3244
|
+
__name(isStaticCacheRequestEligible, "isStaticCacheRequestEligible");
|
|
3245
|
+
async function isKnownStaticAssetPath(env, pathname, routing, timeoutMs) {
|
|
3246
|
+
if (routing.staticDeploymentId) {
|
|
3247
|
+
return true;
|
|
3248
|
+
}
|
|
3249
|
+
if (!routing.ssr) {
|
|
3250
|
+
return true;
|
|
3251
|
+
}
|
|
3252
|
+
if (!routing.assetManifestId) {
|
|
3253
|
+
return false;
|
|
3254
|
+
}
|
|
3255
|
+
try {
|
|
3256
|
+
const manifest = await fetchAssetManifest(
|
|
3257
|
+
env,
|
|
3258
|
+
routing.assetManifestId,
|
|
3259
|
+
routing.projectId,
|
|
3260
|
+
Math.min(timeoutMs, STATIC_ASSET_HEDGE_DELAY_MS)
|
|
3261
|
+
);
|
|
3262
|
+
if (!manifest) {
|
|
3263
|
+
return false;
|
|
3264
|
+
}
|
|
3265
|
+
if (Object.hasOwn(manifest, pathname)) {
|
|
3266
|
+
return true;
|
|
3267
|
+
}
|
|
3268
|
+
const decoded = safeDecodePathname(pathname);
|
|
3269
|
+
return decoded !== pathname && Object.hasOwn(manifest, decoded);
|
|
3270
|
+
} catch {
|
|
3271
|
+
return false;
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
__name(isKnownStaticAssetPath, "isKnownStaticAssetPath");
|
|
3275
|
+
function isStaticCacheResponseEligible(response) {
|
|
3276
|
+
return response.status === 200 && !isEventStreamResponse(response) && !hasSharedCacheOptOut(response);
|
|
3277
|
+
}
|
|
3278
|
+
__name(isStaticCacheResponseEligible, "isStaticCacheResponseEligible");
|
|
3279
|
+
function isCurrentStaticCacheResponse(response) {
|
|
3280
|
+
return hasCurrentSharedCachePolicy(response) && isStaticCacheResponseEligible(response);
|
|
3281
|
+
}
|
|
3282
|
+
__name(isCurrentStaticCacheResponse, "isCurrentStaticCacheResponse");
|
|
3283
|
+
export {
|
|
3284
|
+
index_default as default,
|
|
3285
|
+
isEventStreamResponse,
|
|
3286
|
+
isHtmlResponse,
|
|
3287
|
+
rebuildRequest
|
|
3288
|
+
};
|
|
3289
|
+
//# sourceMappingURL=index.js.map
|