wrangler 2.0.24 → 2.0.25

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.
Files changed (39) hide show
  1. package/miniflare-dist/index.mjs +130 -16
  2. package/package.json +1 -1
  3. package/src/__tests__/configuration.test.ts +1 -1
  4. package/src/__tests__/dev.test.tsx +26 -4
  5. package/src/__tests__/helpers/mock-cfetch.ts +2 -2
  6. package/src/__tests__/r2.test.ts +18 -0
  7. package/src/__tests__/tail.test.ts +93 -39
  8. package/src/api/dev.ts +6 -0
  9. package/src/bundle.ts +3 -2
  10. package/src/config/config.ts +1 -1
  11. package/src/config/validation.ts +1 -1
  12. package/src/dev/dev.tsx +12 -2
  13. package/src/dev/local.tsx +69 -5
  14. package/src/dev/use-esbuild.ts +3 -0
  15. package/src/dev-registry.tsx +3 -0
  16. package/src/dev.tsx +26 -17
  17. package/src/index.tsx +51 -21
  18. package/src/inspect.ts +1 -4
  19. package/src/miniflare-cli/assets.ts +19 -16
  20. package/src/miniflare-cli/index.ts +121 -2
  21. package/src/pages/build.tsx +36 -28
  22. package/src/pages/constants.ts +3 -0
  23. package/src/pages/deployments.tsx +9 -9
  24. package/src/pages/dev.tsx +85 -27
  25. package/src/pages/functions/buildPlugin.ts +4 -0
  26. package/src/pages/functions/buildWorker.ts +4 -0
  27. package/src/pages/functions/routes-consolidation.test.ts +66 -0
  28. package/src/pages/functions/routes-consolidation.ts +29 -0
  29. package/src/pages/functions/routes-transformation.test.ts +271 -0
  30. package/src/pages/functions/routes-transformation.ts +125 -0
  31. package/src/pages/projects.tsx +9 -3
  32. package/src/pages/publish.tsx +56 -14
  33. package/src/pages/types.ts +9 -0
  34. package/src/pages/upload.tsx +6 -8
  35. package/src/r2.ts +13 -0
  36. package/src/tail/index.ts +15 -2
  37. package/src/tail/printing.ts +41 -3
  38. package/wrangler-dist/cli.d.ts +6 -0
  39. package/wrangler-dist/cli.js +385 -89
package/src/dev/dev.tsx CHANGED
@@ -40,6 +40,7 @@ import type { CfWorkerInit } from "../worker";
40
40
  function useDevRegistry(
41
41
  name: string | undefined,
42
42
  services: Config["services"] | undefined,
43
+ durableObjects: Config["durable_objects"] | undefined,
43
44
  mode: "local" | "remote"
44
45
  ): WorkerRegistry {
45
46
  const [workers, setWorkers] = useState<WorkerRegistry>({});
@@ -55,6 +56,9 @@ function useDevRegistry(
55
56
  const serviceNames = (services || []).map(
56
57
  (serviceBinding) => serviceBinding.service
57
58
  );
59
+ const durableObjectServices = (
60
+ durableObjects || { bindings: [] }
61
+ ).bindings.map((durableObjectBinding) => durableObjectBinding.script_name);
58
62
 
59
63
  const interval =
60
64
  // TODO: enable this for remote mode as well
@@ -67,7 +71,9 @@ function useDevRegistry(
67
71
  // so let's filter out the others
68
72
  const filteredWorkers = Object.fromEntries(
69
73
  Object.entries(workerDefinitions || {}).filter(
70
- ([key, _value]) => serviceNames.includes(key)
74
+ ([key, _value]) =>
75
+ serviceNames.includes(key) ||
76
+ durableObjectServices.includes(key)
71
77
  )
72
78
  );
73
79
  setWorkers((prevWorkers) => {
@@ -109,7 +115,7 @@ function useDevRegistry(
109
115
  }
110
116
  );
111
117
  };
112
- }, [name, services, mode]);
118
+ }, [name, services, durableObjects, mode]);
113
119
 
114
120
  return workers;
115
121
  }
@@ -228,6 +234,7 @@ function DevSession(props: DevSessionProps) {
228
234
  const workerDefinitions = useDevRegistry(
229
235
  props.name,
230
236
  props.bindings.services,
237
+ props.bindings.durable_objects,
231
238
  props.local ? "local" : "remote"
232
239
  );
233
240
 
@@ -248,6 +255,7 @@ function DevSession(props: DevSessionProps) {
248
255
  assets: props.assetsConfig,
249
256
  workerDefinitions,
250
257
  services: props.bindings.services,
258
+ durableObjects: props.bindings.durable_objects || { bindings: [] },
251
259
  firstPartyWorkerDevFacade: props.firstPartyWorker,
252
260
  });
253
261
 
@@ -258,7 +266,9 @@ function DevSession(props: DevSessionProps) {
258
266
  format={props.entry.format}
259
267
  compatibilityDate={props.compatibilityDate}
260
268
  compatibilityFlags={props.compatibilityFlags}
269
+ usageModel={props.usageModel}
261
270
  bindings={props.bindings}
271
+ workerDefinitions={workerDefinitions}
262
272
  assetPaths={props.assetPaths}
263
273
  port={props.port}
264
274
  ip={props.ip}
package/src/dev/local.tsx CHANGED
@@ -10,6 +10,7 @@ import { logger } from "../logger";
10
10
  import { DEFAULT_MODULE_RULES } from "../module-collection";
11
11
  import { waitForPortToBeAvailable } from "../proxy";
12
12
  import type { Config } from "../config";
13
+ import type { WorkerRegistry } from "../dev-registry";
13
14
  import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli";
14
15
  import type { AssetPaths } from "../sites";
15
16
  import type { CfWorkerInit, CfScriptFormat } from "../worker";
@@ -22,7 +23,9 @@ interface LocalProps {
22
23
  format: CfScriptFormat | undefined;
23
24
  compatibilityDate: string;
24
25
  compatibilityFlags: string[] | undefined;
26
+ usageModel: "bundled" | "unbound" | undefined;
25
27
  bindings: CfWorkerInit["bindings"];
28
+ workerDefinitions: WorkerRegistry;
26
29
  assetPaths: AssetPaths | undefined;
27
30
  port: number;
28
31
  ip: string;
@@ -56,7 +59,9 @@ function useLocalWorker({
56
59
  format,
57
60
  compatibilityDate,
58
61
  compatibilityFlags,
62
+ usageModel,
59
63
  bindings,
64
+ workerDefinitions,
60
65
  assetPaths,
61
66
  port,
62
67
  inspectorPort,
@@ -96,6 +101,18 @@ function useLocalWorker({
96
101
  }
97
102
  }, [bindings.services]);
98
103
 
104
+ useEffect(() => {
105
+ const externalDurableObjects = (
106
+ bindings.durable_objects?.bindings || []
107
+ ).filter((binding) => binding.script_name);
108
+
109
+ if (externalDurableObjects.length > 0) {
110
+ logger.warn(
111
+ "⎔ Support for external Durable Objects in local mode is experimental and may change."
112
+ );
113
+ }
114
+ }, [bindings.durable_objects?.bindings]);
115
+
99
116
  useEffect(() => {
100
117
  const abortController = new AbortController();
101
118
  async function startLocalWorker() {
@@ -180,6 +197,15 @@ function useLocalWorker({
180
197
  ? `${localProtocol}://${localUpstream}`
181
198
  : undefined;
182
199
 
200
+ const internalDurableObjects = (
201
+ bindings.durable_objects?.bindings || []
202
+ ).filter((binding) => !binding.script_name);
203
+ const externalDurableObjects = (
204
+ bindings.durable_objects?.bindings || []
205
+ ).filter((binding) => binding.script_name);
206
+
207
+ // TODO: This was already messy with the custom `disableLogs` and `logOptions`.
208
+ // It's now getting _really_ messy now with Pages ASSETS binding outside and the external Durable Objects inside.
183
209
  const options: MiniflareOptions = {
184
210
  name: workerName,
185
211
  port,
@@ -196,12 +222,37 @@ function useLocalWorker({
196
222
  })),
197
223
  compatibilityDate,
198
224
  compatibilityFlags,
225
+ usageModel,
199
226
  kvNamespaces: bindings.kv_namespaces?.map((kv) => kv.binding),
200
227
  r2Buckets: bindings.r2_buckets?.map((r2) => r2.binding),
201
228
  durableObjects: Object.fromEntries(
202
- (bindings.durable_objects?.bindings ?? []).map<[string, string]>(
203
- (value) => [value.name, value.class_name]
204
- )
229
+ internalDurableObjects.map((binding) => [
230
+ binding.name,
231
+ binding.class_name,
232
+ ])
233
+ ),
234
+ externalDurableObjects: Object.fromEntries(
235
+ externalDurableObjects
236
+ .map((binding) => {
237
+ const service = workerDefinitions[binding.script_name as string];
238
+ if (!service) return [binding.name, undefined];
239
+
240
+ const name = service.durableObjects.find(
241
+ (durableObject) =>
242
+ durableObject.className === binding.class_name
243
+ )?.name;
244
+ if (!name) return [binding.name, undefined];
245
+
246
+ return [
247
+ binding.name,
248
+ {
249
+ name,
250
+ host: service.durableObjectsHost,
251
+ port: service.durableObjectsPort,
252
+ },
253
+ ];
254
+ })
255
+ .filter(([_, details]) => !!details)
205
256
  ),
206
257
  ...(localPersistencePath
207
258
  ? {
@@ -275,8 +326,9 @@ function useLocalWorker({
275
326
  stdio: "pipe",
276
327
  }));
277
328
 
278
- child.on("message", async (message) => {
279
- if (message === "ready") {
329
+ child.on("message", async (messageString) => {
330
+ const message = JSON.parse(messageString as string);
331
+ if (message.ready) {
280
332
  // Let's register our presence in the dev registry
281
333
  if (workerName) {
282
334
  await registerWorker(workerName, {
@@ -284,6 +336,16 @@ function useLocalWorker({
284
336
  mode: "local",
285
337
  port,
286
338
  host: ip,
339
+ durableObjects: internalDurableObjects.map((binding) => ({
340
+ name: binding.name,
341
+ className: binding.class_name,
342
+ })),
343
+ ...(message.durableObjectsPort
344
+ ? {
345
+ durableObjectsHost: ip,
346
+ durableObjectsPort: message.durableObjectsPort,
347
+ }
348
+ : {}),
287
349
  });
288
350
  }
289
351
  onReady?.();
@@ -363,8 +425,10 @@ function useLocalWorker({
363
425
  bindings.r2_buckets,
364
426
  bindings.vars,
365
427
  bindings.services,
428
+ workerDefinitions,
366
429
  compatibilityDate,
367
430
  compatibilityFlags,
431
+ usageModel,
368
432
  localPersistencePath,
369
433
  liveReload,
370
434
  assetPaths,
@@ -34,6 +34,7 @@ export function useEsbuild({
34
34
  noBundle,
35
35
  workerDefinitions,
36
36
  services,
37
+ durableObjects,
37
38
  firstPartyWorkerDevFacade,
38
39
  }: {
39
40
  entry: Entry;
@@ -50,6 +51,7 @@ export function useEsbuild({
50
51
  nodeCompat: boolean | undefined;
51
52
  noBundle: boolean;
52
53
  workerDefinitions: WorkerRegistry;
54
+ durableObjects: Config["durable_objects"];
53
55
  firstPartyWorkerDevFacade: boolean | undefined;
54
56
  }): EsbuildBundle | undefined {
55
57
  const [bundle, setBundle] = useState<EsbuildBundle>();
@@ -168,6 +170,7 @@ export function useEsbuild({
168
170
  define,
169
171
  assets,
170
172
  services,
173
+ durableObjects,
171
174
  workerDefinitions,
172
175
  firstPartyWorkerDevFacade,
173
176
  ]);
@@ -22,6 +22,9 @@ type WorkerDefinition = {
22
22
  host: string | undefined;
23
23
  mode: "local" | "remote";
24
24
  headers?: Record<string, string>;
25
+ durableObjects: { name: string; className: string }[];
26
+ durableObjectsHost?: string;
27
+ durableObjectsPort?: number;
25
28
  };
26
29
 
27
30
  /**
package/src/dev.tsx CHANGED
@@ -127,9 +127,9 @@ export function devOptions(yargs: Argv): Argv<DevArgs> {
127
127
  default: true,
128
128
  })
129
129
  .option("ip", {
130
- describe: "IP address to listen on, defaults to `localhost`",
130
+ describe: "IP address to listen on",
131
131
  type: "string",
132
- requiresArg: true,
132
+ default: "0.0.0.0",
133
133
  })
134
134
  .option("port", {
135
135
  describe: "Port to listen on",
@@ -275,6 +275,11 @@ export type AdditionalDevProps = {
275
275
  script_name?: string | undefined;
276
276
  environment?: string | undefined;
277
277
  }[];
278
+ r2?: {
279
+ binding: string;
280
+ bucket_name: string;
281
+ preview_bucket_name?: string;
282
+ }[];
278
283
  };
279
284
  type StartDevOptions = ArgumentsCamelCase<DevArgs> &
280
285
  // These options can be passed in directly when called with the `wrangler.dev()` API.
@@ -429,6 +434,7 @@ export async function startDev(args: StartDevOptions) {
429
434
  kv: args.kv,
430
435
  vars: args.vars,
431
436
  durableObjects: args.durableObjects,
437
+ r2: args.r2,
432
438
  });
433
439
 
434
440
  // mask anything that was overridden in .dev.vars
@@ -489,7 +495,7 @@ export async function startDev(args: StartDevOptions) {
489
495
  port={args.port || config.dev.port || (await getLocalPort())}
490
496
  ip={args.ip || config.dev.ip}
491
497
  inspectorPort={
492
- args["inspector-port"] ||
498
+ args.inspectorPort ||
493
499
  config.dev.inspector_port ||
494
500
  (await getInspectorPort())
495
501
  }
@@ -591,21 +597,24 @@ async function getBindings(
591
597
  ...(args.durableObjects || []),
592
598
  ],
593
599
  },
594
- r2_buckets: configParam.r2_buckets?.map(
595
- ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
596
- // same idea as kv namespace preview id,
597
- // same copy-on-write TODO
598
- if (!preview_bucket_name) {
599
- throw new Error(
600
- `In development, you should use a separate r2 bucket than the one you'd use in production. Please create a new r2 bucket with "wrangler r2 bucket create <name>" and add its name as preview_bucket_name to the r2_buckets "${binding}" in your wrangler.toml`
601
- );
600
+ r2_buckets: [
601
+ ...(configParam.r2_buckets?.map(
602
+ ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
603
+ // same idea as kv namespace preview id,
604
+ // same copy-on-write TODO
605
+ if (!preview_bucket_name) {
606
+ throw new Error(
607
+ `In development, you should use a separate r2 bucket than the one you'd use in production. Please create a new r2 bucket with "wrangler r2 bucket create <name>" and add its name as preview_bucket_name to the r2_buckets "${binding}" in your wrangler.toml`
608
+ );
609
+ }
610
+ return {
611
+ binding,
612
+ bucket_name: preview_bucket_name,
613
+ };
602
614
  }
603
- return {
604
- binding,
605
- bucket_name: preview_bucket_name,
606
- };
607
- }
608
- ),
615
+ ) || []),
616
+ ...(args.r2 || []),
617
+ ],
609
618
  worker_namespaces: configParam.worker_namespaces,
610
619
  services: configParam.services,
611
620
  unsafe: configParam.unsafe?.bindings,
package/src/index.tsx CHANGED
@@ -51,6 +51,7 @@ import {
51
51
  bucketAndKeyFromObjectPath,
52
52
  createR2Bucket,
53
53
  deleteR2Bucket,
54
+ deleteR2Object,
54
55
  getR2Object,
55
56
  listR2Buckets,
56
57
  putR2Object,
@@ -1731,6 +1732,7 @@ function createCLIParser(argv: string[]) {
1731
1732
  );
1732
1733
  }
1733
1734
  }
1735
+
1734
1736
  if (errors.length > 0) {
1735
1737
  throw new Error(
1736
1738
  `Unexpected JSON input from "${filename}".\n` +
@@ -1768,13 +1770,12 @@ function createCLIParser(argv: string[]) {
1768
1770
  .command(
1769
1771
  "get <objectPath>",
1770
1772
  "Fetch an object from an R2 bucket",
1771
- (yargs) => {
1772
- return yargs
1773
- .positional("objectPath", {
1774
- describe:
1775
- "The source object path in the form of {bucket}/{key}",
1776
- type: "string",
1777
- })
1773
+ (Objectyargs) => {
1774
+ return Objectyargs.positional("objectPath", {
1775
+ describe:
1776
+ "The source object path in the form of {bucket}/{key}",
1777
+ type: "string",
1778
+ })
1778
1779
  .option("file", {
1779
1780
  describe: "The destination file to create",
1780
1781
  alias: "f",
@@ -1790,13 +1791,16 @@ function createCLIParser(argv: string[]) {
1790
1791
  type: "boolean",
1791
1792
  });
1792
1793
  },
1793
- async (args) => {
1794
- const config = readConfig(args.config as ConfigPath, args);
1794
+ async (objectGetYargs) => {
1795
+ const config = readConfig(
1796
+ objectGetYargs.config as ConfigPath,
1797
+ objectGetYargs
1798
+ );
1795
1799
  const accountId = await requireAuth(config);
1796
- const { objectPath, pipe } = args;
1800
+ const { objectPath, pipe } = objectGetYargs;
1797
1801
  const { bucket, key } = bucketAndKeyFromObjectPath(objectPath);
1798
1802
 
1799
- let file = args.file;
1803
+ let file = objectGetYargs.file;
1800
1804
  if (!file && !pipe) {
1801
1805
  file = key;
1802
1806
  }
@@ -1817,13 +1821,12 @@ function createCLIParser(argv: string[]) {
1817
1821
  .command(
1818
1822
  "put <objectPath>",
1819
1823
  "Create an object in an R2 bucket",
1820
- (yargs) => {
1821
- return yargs
1822
- .positional("objectPath", {
1823
- describe:
1824
- "The destination object path in the form of {bucket}/{key}",
1825
- type: "string",
1826
- })
1824
+ (Objectyargs) => {
1825
+ return Objectyargs.positional("objectPath", {
1826
+ describe:
1827
+ "The destination object path in the form of {bucket}/{key}",
1828
+ type: "string",
1829
+ })
1827
1830
  .option("file", {
1828
1831
  describe: "The path of the file to upload",
1829
1832
  alias: "f",
@@ -1880,12 +1883,15 @@ function createCLIParser(argv: string[]) {
1880
1883
  type: "string",
1881
1884
  });
1882
1885
  },
1883
- async (args) => {
1886
+ async (objectPutYargs) => {
1884
1887
  await printWranglerBanner();
1885
1888
 
1886
- const config = readConfig(args.config as ConfigPath, args);
1889
+ const config = readConfig(
1890
+ objectPutYargs.config as ConfigPath,
1891
+ objectPutYargs
1892
+ );
1887
1893
  const accountId = await requireAuth(config);
1888
- const { objectPath, file, pipe, ...options } = args;
1894
+ const { objectPath, file, pipe, ...options } = objectPutYargs;
1889
1895
  const { bucket, key } = bucketAndKeyFromObjectPath(objectPath);
1890
1896
  if (!file && !pipe) {
1891
1897
  throw new CommandLineArgsError(
@@ -1922,8 +1928,32 @@ function createCLIParser(argv: string[]) {
1922
1928
  });
1923
1929
  logger.log("Upload complete.");
1924
1930
  }
1931
+ )
1932
+ .command(
1933
+ "delete <objectPath>",
1934
+ "Delete an object in an R2 bucket",
1935
+ (objectDeleteYargs) => {
1936
+ return objectDeleteYargs.positional("objectPath", {
1937
+ describe:
1938
+ "The destination object path in the form of {bucket}/{key}",
1939
+ type: "string",
1940
+ });
1941
+ },
1942
+ async (args) => {
1943
+ const { objectPath } = args;
1944
+ await printWranglerBanner();
1945
+
1946
+ const config = readConfig(args.config as ConfigPath, args);
1947
+ const accountId = await requireAuth(config);
1948
+ const { bucket, key } = bucketAndKeyFromObjectPath(objectPath);
1949
+ logger.log(`Deleting object "${key}" from bucket "${bucket}".`);
1950
+
1951
+ await deleteR2Object(accountId, bucket, key);
1952
+ logger.log("Delete complete.");
1953
+ }
1925
1954
  );
1926
1955
  })
1956
+
1927
1957
  .command("bucket", "Manage R2 buckets", (r2BucketYargs) => {
1928
1958
  r2BucketYargs.command(
1929
1959
  "create <name>",
package/src/inspect.ts CHANGED
@@ -320,10 +320,7 @@ export default function useInspector(props: InspectorProps) {
320
320
  const convertedFnName =
321
321
  pos.name || functionName || "";
322
322
  exceptionLines.push(
323
- ` at ${convertedFnName} (${pos.source?.replace(
324
- `${mapContent.sourceRoot}/`,
325
- ""
326
- )}:${pos.line}:${pos.column})`
323
+ ` at ${convertedFnName} (${pos.source}:${pos.line}:${pos.column})`
327
324
  );
328
325
  }
329
326
  }
@@ -335,6 +335,11 @@ async function generateAssetsFetch(
335
335
 
336
336
  const generateResponse = (request: MiniflareRequest) => {
337
337
  const url = new URL(request.url);
338
+ let assetName = url.pathname;
339
+ try {
340
+ //it's possible for someone to send a URL like http://fakehost/abc%2 which would fail to decode
341
+ assetName = decodeURIComponent(url.pathname);
342
+ } catch {}
338
343
 
339
344
  const deconstructedResponse: {
340
345
  status: number;
@@ -377,7 +382,7 @@ async function generateAssetsFetch(
377
382
  }
378
383
 
379
384
  const notFound = () => {
380
- let cwd = url.pathname;
385
+ let cwd = assetName;
381
386
  while (cwd) {
382
387
  cwd = cwd.slice(0, cwd.lastIndexOf("/"));
383
388
 
@@ -407,38 +412,36 @@ async function generateAssetsFetch(
407
412
 
408
413
  let asset;
409
414
 
410
- if (url.pathname.endsWith("/")) {
411
- if ((asset = getAsset(`${url.pathname}/index.html`))) {
415
+ if (assetName.endsWith("/")) {
416
+ if ((asset = getAsset(`${assetName}/index.html`))) {
412
417
  deconstructedResponse.body = serveAsset(asset);
413
418
  deconstructedResponse.headers.set(
414
419
  "Content-Type",
415
420
  getType(asset) || "application/octet-stream"
416
421
  );
417
422
  return deconstructedResponse;
418
- } else if (
419
- (asset = getAsset(`${url.pathname.replace(/\/$/, ".html")}`))
420
- ) {
423
+ } else if ((asset = getAsset(`${assetName.replace(/\/$/, ".html")}`))) {
421
424
  deconstructedResponse.status = 301;
422
425
  deconstructedResponse.headers.set(
423
426
  "Location",
424
- `${url.pathname.slice(0, -1)}${url.search}`
427
+ `${assetName.slice(0, -1)}${url.search}`
425
428
  );
426
429
  return deconstructedResponse;
427
430
  }
428
431
  }
429
432
 
430
- if (url.pathname.endsWith("/index")) {
433
+ if (assetName.endsWith("/index")) {
431
434
  deconstructedResponse.status = 301;
432
435
  deconstructedResponse.headers.set(
433
436
  "Location",
434
- `${url.pathname.slice(0, -"index".length)}${url.search}`
437
+ `${assetName.slice(0, -"index".length)}${url.search}`
435
438
  );
436
439
  return deconstructedResponse;
437
440
  }
438
441
 
439
- if ((asset = getAsset(url.pathname))) {
440
- if (url.pathname.endsWith(".html")) {
441
- const extensionlessPath = url.pathname.slice(0, -".html".length);
442
+ if ((asset = getAsset(assetName))) {
443
+ if (assetName.endsWith(".html")) {
444
+ const extensionlessPath = assetName.slice(0, -".html".length);
442
445
  if (getAsset(extensionlessPath) || extensionlessPath === "/") {
443
446
  deconstructedResponse.body = serveAsset(asset);
444
447
  deconstructedResponse.headers.set(
@@ -462,12 +465,12 @@ async function generateAssetsFetch(
462
465
  );
463
466
  return deconstructedResponse;
464
467
  }
465
- } else if (hasFileExtension(url.pathname)) {
468
+ } else if (hasFileExtension(assetName)) {
466
469
  notFound();
467
470
  return deconstructedResponse;
468
471
  }
469
472
 
470
- if ((asset = getAsset(`${url.pathname}.html`))) {
473
+ if ((asset = getAsset(`${assetName}.html`))) {
471
474
  deconstructedResponse.body = serveAsset(asset);
472
475
  deconstructedResponse.headers.set(
473
476
  "Content-Type",
@@ -476,11 +479,11 @@ async function generateAssetsFetch(
476
479
  return deconstructedResponse;
477
480
  }
478
481
 
479
- if ((asset = getAsset(`${url.pathname}/index.html`))) {
482
+ if ((asset = getAsset(`${assetName}/index.html`))) {
480
483
  deconstructedResponse.status = 301;
481
484
  deconstructedResponse.headers.set(
482
485
  "Location",
483
- `${url.pathname}/${url.search}`
486
+ `${assetName}/${url.search}`
484
487
  );
485
488
  return deconstructedResponse;
486
489
  } else {