wrangler 0.0.0-e928f94 → 0.0.0-e9a1820

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/src/kv.tsx CHANGED
@@ -10,20 +10,57 @@ type KvArgs = {
10
10
  config?: Config;
11
11
  };
12
12
 
13
- export async function listNamespaces(accountId: string) {
14
- let page = 1,
15
- done = false,
16
- results = [];
17
- while (!(done || results.length % 100 !== 0)) {
18
- const json = await cfetch<
19
- { id: string; title: string; supports_url_encoding: boolean }[]
20
- >(
21
- `/accounts/${accountId}/storage/kv/namespaces?per_page=100&order=title&direction=asc&page=${page}`
13
+ /**
14
+ * Create a new namespace under the given `accountId` with the given `title`.
15
+ *
16
+ * @returns the generated id of the created namespace.
17
+ */
18
+ export async function createNamespace(
19
+ accountId: string,
20
+ title: string
21
+ ): Promise<string> {
22
+ const response = await cfetch<{ id: string }>(
23
+ `/accounts/${accountId}/storage/kv/namespaces`,
24
+ {
25
+ method: "POST",
26
+ headers: {
27
+ "Content-Type": "application/json",
28
+ },
29
+ body: JSON.stringify({
30
+ title,
31
+ }),
32
+ }
33
+ );
34
+
35
+ return response.id;
36
+ }
37
+
38
+ /**
39
+ * The information about a namespace that is returned from `listNamespaces()`.
40
+ */
41
+ export interface KVNamespaceInfo {
42
+ id: string;
43
+ title: string;
44
+ supports_url_encoding?: boolean;
45
+ }
46
+
47
+ /**
48
+ * Fetch a list of all the namespaces under the given `accountId`.
49
+ */
50
+ export async function listNamespaces(
51
+ accountId: string
52
+ ): Promise<KVNamespaceInfo[]> {
53
+ const pageSize = 100;
54
+ let page = 1;
55
+ const results: KVNamespaceInfo[] = [];
56
+ while (results.length % pageSize === 0) {
57
+ const json = await cfetch<KVNamespaceInfo[]>(
58
+ `/accounts/${accountId}/storage/kv/namespaces?per_page=${pageSize}&order=title&direction=asc&page=${page}`
22
59
  );
23
60
  page++;
24
- results = [...results, ...json];
25
- if (json.length === 0) {
26
- done = true;
61
+ results.push(...json);
62
+ if (json.length < pageSize) {
63
+ break;
27
64
  }
28
65
  }
29
66
  return results;
@@ -130,7 +167,7 @@ export function getNamespaceId({
130
167
  );
131
168
  }
132
169
 
133
- // TODO: either a bespoke arg type for this function to avoid undefineds or a EnvOrConfig type
170
+ // TODO: either a bespoke arg type for this function to avoid `undefined`s or an `EnvOrConfig` type
134
171
  return getNamespaceId({
135
172
  binding,
136
173
  "namespace-id": namespaceId,
@@ -153,9 +190,7 @@ export function getNamespaceId({
153
190
  );
154
191
  }
155
192
 
156
- const namespace = config.kv_namespaces.find(
157
- (namespace) => namespace.binding === binding
158
- );
193
+ const namespace = config.kv_namespaces.find((ns) => ns.binding === binding);
159
194
 
160
195
  // we couldn't find a namespace with that binding
161
196
  if (!namespace) {
@@ -164,10 +199,12 @@ export function getNamespaceId({
164
199
 
165
200
  // end pre-flight checks
166
201
 
167
- // we're in preview mode, `--preview true` or `--preivew` was passed
202
+ // we're in preview mode, `--preview true` or `--preview` was passed
168
203
  if (preview && namespace.preview_id) {
169
204
  namespaceId = namespace.preview_id;
170
- } else {
205
+ // We don't want to execute code below if preview is set to true, so we just return. Otherwise we will get errors!
206
+ return namespaceId;
207
+ } else if (preview) {
171
208
  throw new Error(
172
209
  `No preview ID found for ${binding}. Add one to your wrangler config file to use a separate namespace for previewing your worker.`
173
210
  );
@@ -180,7 +217,9 @@ export function getNamespaceId({
180
217
  // --preview false was passed
181
218
  if (previewIsDefined && namespace.id) {
182
219
  namespaceId = namespace.id;
183
- } else {
220
+ // We don't want to execute code below if preview is set to true, so we just return. Otherwise we can get error!
221
+ return namespaceId;
222
+ } else if (previewIsDefined) {
184
223
  throw new Error(
185
224
  `No namespace ID found for ${binding}. Add one to your wrangler config file to use a separate namespace for previewing your worker.`
186
225
  );
@@ -209,3 +248,10 @@ export function getNamespaceId({
209
248
 
210
249
  return namespaceId;
211
250
  }
251
+
252
+ /**
253
+ * KV namespace binding names must be valid JS identifiers.
254
+ */
255
+ export function isValidNamespaceBinding(binding: string): boolean {
256
+ return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(binding);
257
+ }
package/src/pages.tsx CHANGED
@@ -1,14 +1,22 @@
1
+ /* eslint-disable no-shadow */
2
+
3
+ import assert from "assert";
1
4
  import type { BuilderCallback } from "yargs";
2
5
  import { join } from "path";
3
6
  import { tmpdir } from "os";
4
7
  import { existsSync, lstatSync, readFileSync } from "fs";
5
8
  import { execSync, spawn } from "child_process";
6
- import { Headers, Request, Response } from "undici";
7
- import type { MiniflareOptions } from "miniflare";
8
- import type { RequestInfo, RequestInit } from "@miniflare/core";
9
+ import { URL } from "url";
10
+ import { getType } from "mime";
9
11
  import open from "open";
10
12
  import { watch } from "chokidar";
11
13
 
14
+ // Defer importing miniflare until we really need it. This takes ~0.5s
15
+ // and also modifies some `stream/web` and `undici` prototypes, so we
16
+ // don't want to do this if pages commands aren't being called.
17
+ import type { Headers, Request, fetch } from "@miniflare/core";
18
+ import type { MiniflareOptions } from "miniflare";
19
+
12
20
  type Exit = (message?: string) => undefined;
13
21
 
14
22
  const isWindows = () => process.platform === "win32";
@@ -370,6 +378,9 @@ const hasFileExtension = (pathname: string) =>
370
378
  const generateAssetsFetch = async (
371
379
  directory: string
372
380
  ): Promise<typeof fetch> => {
381
+ // Defer importing miniflare until we really need it
382
+ const { Headers, Request, Response } = await import("@miniflare/core");
383
+
373
384
  const headersFile = join(directory, "_headers");
374
385
  const redirectsFile = join(directory, "_redirects");
375
386
  const workerFile = join(directory, "_worker.js");
@@ -466,14 +477,25 @@ const generateAssetsFetch = async (
466
477
  if ((asset = getAsset(`${cwd}/404.html`))) {
467
478
  deconstructedResponse.status = 404;
468
479
  deconstructedResponse.body = serveAsset(asset);
480
+ deconstructedResponse.headers.set(
481
+ "Content-Type",
482
+ getType(asset) || "application/octet-stream"
483
+ );
469
484
  return deconstructedResponse;
470
485
  }
471
486
  }
472
487
 
473
488
  if ((asset = getAsset(`/index.html`))) {
474
489
  deconstructedResponse.body = serveAsset(asset);
490
+ deconstructedResponse.headers.set(
491
+ "Content-Type",
492
+ getType(asset) || "application/octet-stream"
493
+ );
475
494
  return deconstructedResponse;
476
495
  }
496
+
497
+ deconstructedResponse.status = 404;
498
+ return deconstructedResponse;
477
499
  };
478
500
 
479
501
  let asset;
@@ -481,6 +503,10 @@ const generateAssetsFetch = async (
481
503
  if (url.pathname.endsWith("/")) {
482
504
  if ((asset = getAsset(`${url.pathname}/index.html`))) {
483
505
  deconstructedResponse.body = serveAsset(asset);
506
+ deconstructedResponse.headers.set(
507
+ "Content-Type",
508
+ getType(asset) || "application/octet-stream"
509
+ );
484
510
  return deconstructedResponse;
485
511
  } else if (
486
512
  (asset = getAsset(`${url.pathname.replace(/\/$/, ".html")}`))
@@ -508,6 +534,10 @@ const generateAssetsFetch = async (
508
534
  const extensionlessPath = url.pathname.slice(0, -".html".length);
509
535
  if (getAsset(extensionlessPath) || extensionlessPath === "/") {
510
536
  deconstructedResponse.body = serveAsset(asset);
537
+ deconstructedResponse.headers.set(
538
+ "Content-Type",
539
+ getType(asset) || "application/octet-stream"
540
+ );
511
541
  return deconstructedResponse;
512
542
  } else {
513
543
  deconstructedResponse.status = 301;
@@ -519,6 +549,10 @@ const generateAssetsFetch = async (
519
549
  }
520
550
  } else {
521
551
  deconstructedResponse.body = serveAsset(asset);
552
+ deconstructedResponse.headers.set(
553
+ "Content-Type",
554
+ getType(asset) || "application/octet-stream"
555
+ );
522
556
  return deconstructedResponse;
523
557
  }
524
558
  } else if (hasFileExtension(url.pathname)) {
@@ -528,6 +562,10 @@ const generateAssetsFetch = async (
528
562
 
529
563
  if ((asset = getAsset(`${url.pathname}.html`))) {
530
564
  deconstructedResponse.body = serveAsset(asset);
565
+ deconstructedResponse.headers.set(
566
+ "Content-Type",
567
+ getType(asset) || "application/octet-stream"
568
+ );
531
569
  return deconstructedResponse;
532
570
  }
533
571
 
@@ -569,7 +607,7 @@ const generateAssetsFetch = async (
569
607
  });
570
608
  };
571
609
 
572
- return (async (input, init) => {
610
+ return async (input, init) => {
573
611
  const request = new Request(input, init);
574
612
  const deconstructedResponse = generateResponse(request);
575
613
  attachHeaders(request, deconstructedResponse);
@@ -584,7 +622,7 @@ const generateAssetsFetch = async (
584
622
  headers,
585
623
  status: deconstructedResponse.status,
586
624
  });
587
- }) as any;
625
+ };
588
626
  };
589
627
 
590
628
  export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
@@ -710,56 +748,23 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
710
748
  };
711
749
  }
712
750
 
751
+ // Defer importing miniflare until we really need it
713
752
  const { Miniflare, Log, LogLevel } = await import("miniflare");
714
- const { fetch } = await import("@miniflare/core");
715
-
716
- class MiniflareLogger extends Log {
717
- logWithLevel(level: number, message: string) {
718
- if (level <= this.level) {
719
- switch (level) {
720
- case LogLevel.NONE: {
721
- break;
722
- }
723
- case LogLevel.ERROR: {
724
- console.error(message);
725
- break;
726
- }
727
- case LogLevel.WARN: {
728
- console.warn(message);
729
- break;
730
- }
731
- case LogLevel.INFO: {
732
- console.info(message);
733
- break;
734
- }
735
- case LogLevel.DEBUG: {
736
- console.debug(message);
737
- break;
738
- }
739
- case LogLevel.VERBOSE: {
740
- console.debug(message);
741
- break;
742
- }
743
- }
744
- }
745
- }
746
-
747
- log(message: string) {}
748
-
749
- error(message: Error) {
750
- this.logWithLevel(
751
- LogLevel.ERROR,
752
- message.stack ? message.stack : `${message}`
753
- );
754
- }
755
- }
753
+ const { Response, fetch } = await import("@miniflare/core");
756
754
 
755
+ // Should only be called if no proxyPort, using `assert.fail()` here
756
+ // means the type of `assetsFetch` is still `typeof fetch`
757
+ const assetsFetch = proxyPort
758
+ ? () => assert.fail()
759
+ : await generateAssetsFetch(directory);
757
760
  const miniflare = new Miniflare({
758
761
  port,
759
762
  watch: true,
760
763
  modules: true,
761
764
 
762
- log: new MiniflareLogger(LogLevel.ERROR),
765
+ log: new Log(LogLevel.ERROR, { prefix: "pages" }),
766
+ logUnhandledRejections: true,
767
+ sourceMap: true,
763
768
 
764
769
  kvNamespaces: kvs.map((kv) => kv.toString()),
765
770
 
@@ -769,50 +774,43 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
769
774
  )
770
775
  ),
771
776
 
777
+ // User bindings
772
778
  bindings: {
773
- // User bindings
774
779
  ...Object.fromEntries(
775
780
  bindings.map((binding) => binding.toString().split("="))
776
781
  ),
782
+ },
777
783
 
778
- // env.ASSETS.fetch
779
- ASSETS: {
780
- fetch: async (
781
- input: RequestInfo,
782
- init?: RequestInit | undefined
783
- ) => {
784
- if (proxyPort) {
785
- try {
786
- let request = new Request(input as any, init);
787
- const url = new URL(request.url);
788
- url.host = `127.0.0.1:${proxyPort}`;
789
- request = new Request(url.toString(), request);
790
- return await fetch(request);
791
- } catch (thrown) {
792
- console.error(`Could not proxy request: ${thrown}`);
793
-
794
- // TODO: Pretty error page
795
- return new Response(
796
- `[wrangler] Could not proxy request: ${thrown}`,
797
- { status: 502 }
798
- );
799
- }
800
- } else {
801
- try {
802
- return await (
803
- await generateAssetsFetch(directory)
804
- )(input as any, init as any);
805
- } catch (thrown) {
806
- console.error(`Could not serve static asset: ${thrown}`);
807
-
808
- // TODO: Pretty error page
809
- return new Response(
810
- `[wrangler] Could not serve static asset: ${thrown}`,
811
- { status: 502 }
812
- );
813
- }
784
+ // env.ASSETS.fetch
785
+ serviceBindings: {
786
+ async ASSETS(request) {
787
+ if (proxyPort) {
788
+ try {
789
+ const url = new URL(request.url);
790
+ url.host = `127.0.0.1:${proxyPort}`;
791
+ return await fetch(url, request);
792
+ } catch (thrown) {
793
+ console.error(`Could not proxy request: ${thrown}`);
794
+
795
+ // TODO: Pretty error page
796
+ return new Response(
797
+ `[wrangler] Could not proxy request: ${thrown}`,
798
+ { status: 502 }
799
+ );
800
+ }
801
+ } else {
802
+ try {
803
+ return await assetsFetch(request);
804
+ } catch (thrown) {
805
+ console.error(`Could not serve static asset: ${thrown}`);
806
+
807
+ // TODO: Pretty error page
808
+ return new Response(
809
+ `[wrangler] Could not serve static asset: ${thrown}`,
810
+ { status: 502 }
811
+ );
814
812
  }
815
- },
813
+ }
816
814
  },
817
815
  },
818
816
 
@@ -823,21 +821,31 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
823
821
  ...miniflareArgs,
824
822
  });
825
823
 
826
- const server = await miniflare.startServer();
827
- console.log(`Serving at http://127.0.0.1:${port}/`);
824
+ try {
825
+ // `startServer` might throw if user code contains errors
826
+ const server = await miniflare.startServer();
827
+ console.log(`Serving at http://127.0.0.1:${port}/`);
828
828
 
829
- if (process.env.BROWSER !== "none") {
830
- open(`http://127.0.0.1:${port}/`);
831
- }
829
+ if (process.env.BROWSER !== "none") {
830
+ await open(`http://127.0.0.1:${port}/`);
831
+ }
832
832
 
833
- process.on("SIGINT", () => {
834
- server.close();
835
- miniflare.dispose();
836
- });
837
- process.on("SIGTERM", () => {
838
- server.close();
839
- miniflare.dispose();
840
- });
833
+ process.on("SIGINT", () => {
834
+ server.close();
835
+ miniflare.dispose().catch((err) => {
836
+ miniflare.log.error(err);
837
+ });
838
+ });
839
+ process.on("SIGTERM", () => {
840
+ server.close();
841
+ miniflare.dispose().catch((err) => {
842
+ miniflare.log.error(err);
843
+ });
844
+ });
845
+ } catch (e) {
846
+ miniflare.log.error(e);
847
+ process.exitCode = 1;
848
+ }
841
849
  }
842
850
  );
843
851
  };
package/src/proxy.ts ADDED
@@ -0,0 +1,103 @@
1
+ import { connect } from "node:http2";
2
+ import { createServer } from "node:http";
3
+ import type { Server, IncomingHttpHeaders, RequestListener } from "node:http";
4
+ import WebSocket from "faye-websocket";
5
+ import serveStatic from "serve-static";
6
+
7
+ export interface HttpProxyInit {
8
+ host: string;
9
+ assetPath?: string | null;
10
+ onRequest?: (headers: IncomingHttpHeaders) => void;
11
+ onResponse?: (headers: IncomingHttpHeaders) => void;
12
+ }
13
+
14
+ /**
15
+ * Creates a HTTP/1 proxy that sends requests over HTTP/2.
16
+ */
17
+ export function createHttpProxy(init: HttpProxyInit): Server {
18
+ const { host, assetPath, onRequest = () => {}, onResponse = () => {} } = init;
19
+ const remote = connect(`https://${host}`);
20
+ const local = createServer();
21
+ // HTTP/2 -> HTTP/2
22
+ local.on("stream", (stream, headers: IncomingHttpHeaders) => {
23
+ onRequest(headers);
24
+ headers[":authority"] = host;
25
+ const request = stream.pipe(remote.request(headers));
26
+ request.on("response", (responseHeaders: IncomingHttpHeaders) => {
27
+ onResponse(responseHeaders);
28
+ stream.respond(responseHeaders);
29
+ request.pipe(stream, { end: true });
30
+ });
31
+ });
32
+ // HTTP/1 -> HTTP/2
33
+ const handleRequest: RequestListener = (message, response) => {
34
+ const { httpVersionMajor, headers, method, url } = message;
35
+ if (httpVersionMajor >= 2) {
36
+ return; // Already handled by the "stream" event.
37
+ }
38
+ onRequest(headers);
39
+ headers[":method"] = method;
40
+ headers[":path"] = url;
41
+ headers[":authority"] = host;
42
+ headers[":scheme"] = "https";
43
+ for (const name of Object.keys(headers)) {
44
+ if (HTTP1_HEADERS.has(name.toLowerCase())) {
45
+ delete headers[name];
46
+ }
47
+ }
48
+ const request = message.pipe(remote.request(headers));
49
+ request.on("response", (responseHeaders) => {
50
+ const status = responseHeaders[":status"];
51
+ onResponse(responseHeaders);
52
+ for (const name of Object.keys(responseHeaders)) {
53
+ if (name.startsWith(":")) {
54
+ delete responseHeaders[name];
55
+ }
56
+ }
57
+ response.writeHead(status, responseHeaders);
58
+ request.pipe(response, { end: true });
59
+ });
60
+ };
61
+ // If an asset path is defined, check the file system
62
+ // for a file first and serve if it exists.
63
+ if (assetPath) {
64
+ const handleAsset = serveStatic(assetPath, {
65
+ cacheControl: false,
66
+ });
67
+ local.on("request", (request, response) => {
68
+ handleAsset(request, response, () => {
69
+ handleRequest(request, response);
70
+ });
71
+ });
72
+ } else {
73
+ local.on("request", handleRequest);
74
+ }
75
+ // HTTP/1 -> WebSocket (over HTTP/1)
76
+ local.on("upgrade", (message, socket, body) => {
77
+ const { headers, url } = message;
78
+ onRequest(headers);
79
+ headers["host"] = host;
80
+ const localWebsocket = new WebSocket(message, socket, body);
81
+ // TODO(soon): Custom WebSocket protocol is not working?
82
+ const remoteWebsocketClient = new WebSocket.Client(
83
+ `wss://${host}${url}`,
84
+ [],
85
+ { headers }
86
+ );
87
+ localWebsocket.pipe(remoteWebsocketClient).pipe(localWebsocket);
88
+ });
89
+ remote.on("close", () => {
90
+ local.close();
91
+ });
92
+ return local;
93
+ }
94
+
95
+ const HTTP1_HEADERS = new Set([
96
+ "host",
97
+ "connection",
98
+ "upgrade",
99
+ "keep-alive",
100
+ "proxy-connection",
101
+ "transfer-encoding",
102
+ "http2-settings",
103
+ ]);
package/src/publish.ts CHANGED
@@ -9,6 +9,7 @@ import cfetch from "./cfetch";
9
9
  import assert from "node:assert";
10
10
  import { syncAssets } from "./sites";
11
11
  import makeModuleCollector from "./module-collection";
12
+ import { execa } from "execa";
12
13
 
13
14
  type CfScriptFormat = void | "modules" | "service-worker";
14
15
 
@@ -76,6 +77,17 @@ export default async function publish(props: Props): Promise<void> {
76
77
 
77
78
  const destination = await tmp.dir({ unsafeCleanup: true });
78
79
 
80
+ if (props.config.build?.command) {
81
+ // TODO: add a deprecation message here?
82
+ console.log("running:", props.config.build.command);
83
+ const buildCommandPieces = props.config.build.command.split(" ");
84
+ await execa(buildCommandPieces[0], buildCommandPieces.slice(1), {
85
+ stdout: "inherit",
86
+ stderr: "inherit",
87
+ ...(props.config.build?.cwd && { cwd: props.config.build.cwd }),
88
+ });
89
+ }
90
+
79
91
  const moduleCollector = makeModuleCollector();
80
92
  const result = await esbuild.build({
81
93
  ...(props.public
@@ -137,7 +149,7 @@ export default async function publish(props: Props): Promise<void> {
137
149
  }
138
150
 
139
151
  const content = await readFile(chunks[0], { encoding: "utf-8" });
140
- destination.cleanup();
152
+ await destination.cleanup();
141
153
 
142
154
  // if config.migrations
143
155
  // get current migration tag
@@ -146,7 +158,7 @@ export default async function publish(props: Props): Promise<void> {
146
158
  const scripts = await cfetch<{ id: string; migration_tag: string }[]>(
147
159
  `/accounts/${accountId}/workers/scripts`
148
160
  );
149
- const script = scripts.find((script) => script.id === scriptName);
161
+ const script = scripts.find(({ id }) => id === scriptName);
150
162
  if (script?.migration_tag) {
151
163
  // was already published once
152
164
  const foundIndex = config.migrations.findIndex(
@@ -154,7 +166,7 @@ export default async function publish(props: Props): Promise<void> {
154
166
  );
155
167
  if (foundIndex === -1) {
156
168
  console.warn(
157
- `The published script ${scriptName} has a migration tag "${script.migration_tag}, which was not found in wrangler.toml. You may have already delated it. Applying all available migrations to the script...`
169
+ `The published script ${scriptName} has a migration tag "${script.migration_tag}, which was not found in wrangler.toml. You may have already deleted it. Applying all available migrations to the script...`
158
170
  );
159
171
  migrations = {
160
172
  old_tag: script.migration_tag,
@@ -189,6 +201,16 @@ export default async function publish(props: Props): Promise<void> {
189
201
  : { manifest: undefined, namespace: undefined };
190
202
 
191
203
  const envRootObj = props.env ? config.env[props.env] || {} : config;
204
+ const bindings: CfWorkerInit["bindings"] = {
205
+ kv_namespaces: envRootObj.kv_namespaces?.concat(
206
+ assets.namespace
207
+ ? { binding: "__STATIC_CONTENT", id: assets.namespace }
208
+ : []
209
+ ),
210
+ vars: envRootObj.vars,
211
+ durable_objects: envRootObj.durable_objects,
212
+ services: envRootObj.experimental_services,
213
+ };
192
214
 
193
215
  const worker: CfWorkerInit = {
194
216
  name: scriptName,
@@ -197,35 +219,17 @@ export default async function publish(props: Props): Promise<void> {
197
219
  content: content,
198
220
  type: bundle.type === "esm" ? "esm" : "commonjs",
199
221
  },
200
- variables: {
201
- ...(envRootObj?.vars || {}),
202
- ...(envRootObj?.kv_namespaces || []).reduce(
203
- (obj, { binding, preview_id: _preview_id, id }) => {
204
- return { ...obj, [binding]: { namespaceId: id } };
205
- },
206
- {}
207
- ),
208
- ...(envRootObj?.durable_objects?.bindings || []).reduce(
209
- (obj, { name, class_name, script_name }) => {
210
- return {
211
- ...obj,
212
- [name]: { class_name, ...(script_name && { script_name }) },
213
- };
214
- },
215
- {}
216
- ),
217
- ...(assets.namespace
218
- ? { __STATIC_CONTENT: { namespaceId: assets.namespace } }
219
- : {}),
220
- },
222
+ bindings,
221
223
  ...(migrations && { migrations }),
222
- modules: assets.manifest
223
- ? moduleCollector.modules.concat({
224
- name: "__STATIC_CONTENT_MANIFEST",
225
- content: JSON.stringify(assets.manifest),
226
- type: "text",
227
- })
228
- : moduleCollector.modules,
224
+ modules: moduleCollector.modules.concat(
225
+ assets.manifest
226
+ ? {
227
+ name: "__STATIC_CONTENT_MANIFEST",
228
+ content: JSON.stringify(assets.manifest),
229
+ type: "text",
230
+ }
231
+ : []
232
+ ),
229
233
  compatibility_date: config.compatibility_date,
230
234
  compatibility_flags: config.compatibility_flags,
231
235
  usage_model: config.usage_model,
@@ -283,8 +287,8 @@ export default async function publish(props: Props): Promise<void> {
283
287
  // This is to prevent an issue where a negative cache-hit
284
288
  // causes the subdomain to be unavailable for 30 seconds.
285
289
  // This is a temporary measure until we fix this on the edge.
286
- .then((url) => {
287
- sleep(3000);
290
+ .then(async (url) => {
291
+ await sleep(3000);
288
292
  return url;
289
293
  })
290
294
  );