wrangler 0.0.0-ece06ea → 0.0.0-edc4b53

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 (41) hide show
  1. package/package.json +24 -15
  2. package/pages/functions/buildWorker.ts +62 -0
  3. package/pages/functions/filepath-routing.test.ts +39 -0
  4. package/pages/functions/filepath-routing.ts +221 -0
  5. package/pages/functions/identifiers.ts +78 -0
  6. package/pages/functions/routes.ts +158 -0
  7. package/pages/functions/template-worker.ts +144 -0
  8. package/src/__tests__/dev.test.tsx +66 -0
  9. package/src/__tests__/index.test.ts +254 -120
  10. package/src/__tests__/jest.setup.ts +22 -0
  11. package/src/__tests__/kv.test.ts +1106 -0
  12. package/src/__tests__/mock-cfetch.ts +171 -0
  13. package/src/__tests__/mock-dialogs.ts +65 -0
  14. package/src/__tests__/run-in-tmp.ts +19 -0
  15. package/src/__tests__/run-wrangler.ts +32 -0
  16. package/src/api/form_data.ts +71 -98
  17. package/src/api/preview.ts +29 -24
  18. package/src/api/worker.ts +38 -44
  19. package/src/cfetch/index.ts +102 -0
  20. package/src/cfetch/internal.ts +65 -0
  21. package/src/cli.ts +6 -7
  22. package/src/config.ts +476 -113
  23. package/src/dev.tsx +291 -256
  24. package/src/dialogs.tsx +4 -12
  25. package/src/index.tsx +381 -252
  26. package/src/inspect.ts +524 -0
  27. package/src/kv.tsx +110 -45
  28. package/src/module-collection.ts +64 -0
  29. package/src/pages.tsx +900 -213
  30. package/src/proxy.ts +294 -0
  31. package/src/publish.ts +67 -63
  32. package/src/sites.tsx +10 -7
  33. package/src/tail.tsx +5 -3
  34. package/src/user.tsx +31 -33
  35. package/wrangler-dist/cli.js +43444 -56347
  36. package/wrangler-dist/cli.js.map +3 -3
  37. package/src/__tests__/fixtures/init/.gitkeep +0 -0
  38. package/src/__tests__/mock-cfetch.js +0 -46
  39. package/src/api/inspect.ts +0 -441
  40. package/src/cfetch.ts +0 -72
  41. package/src/util/fetch.ts +0 -74
package/src/proxy.ts ADDED
@@ -0,0 +1,294 @@
1
+ import { connect } from "node:http2";
2
+ import type { ServerHttp2Stream } from "node:http2";
3
+ import { createServer } from "node:http";
4
+ import type {
5
+ IncomingHttpHeaders,
6
+ RequestListener,
7
+ IncomingMessage,
8
+ ServerResponse,
9
+ Server,
10
+ } from "node:http";
11
+ import WebSocket from "faye-websocket";
12
+ import serveStatic from "serve-static";
13
+ import type { CfPreviewToken } from "./api/preview";
14
+ import { useEffect, useRef } from "react";
15
+
16
+ /**
17
+ * `usePreviewServer` is a React hook that creates a local development
18
+ * server that can be used to develop a Worker.
19
+ *
20
+ * When we run `wrangler dev`, we start by uploading the compiled worker
21
+ * to the preview service, which responds with a preview token.
22
+ * (see `useWorker`/`createWorker` for details.)
23
+ * We can then use that token to connect to the preview server for a
24
+ * great local development experience. Further, as we change the worker,
25
+ * we can update the preview token transparently without having to restart
26
+ * the development server.
27
+ */
28
+
29
+ /** Rewrite request headers to add the preview token. */
30
+ function addCfPreviewTokenHeader(
31
+ headers: IncomingHttpHeaders,
32
+ previewTokenValue: string
33
+ ) {
34
+ headers["cf-workers-preview-token"] = previewTokenValue;
35
+ }
36
+
37
+ /**
38
+ * Rewrite references in request headers
39
+ * from the preview host to the local host.
40
+ */
41
+ function rewriteRemoteHostToLocalHostInHeaders(
42
+ headers: IncomingHttpHeaders,
43
+ remoteHost: string,
44
+ localPort: number
45
+ ) {
46
+ for (const [name, value] of Object.entries(headers)) {
47
+ // Rewrite the remote host to the local host.
48
+ if (typeof value === "string" && value.includes(remoteHost)) {
49
+ headers[name] = value
50
+ .replaceAll(`https://${remoteHost}`, `http://localhost:${localPort}`)
51
+ .replaceAll(remoteHost, `localhost:${localPort}`);
52
+ }
53
+ }
54
+ }
55
+
56
+ export function usePreviewServer({
57
+ previewToken,
58
+ publicRoot,
59
+ port,
60
+ }: {
61
+ previewToken: CfPreviewToken | undefined;
62
+ publicRoot: undefined | string;
63
+ port: number;
64
+ }) {
65
+ /** Creates an HTTP/1 proxy that sends requests over HTTP/2. */
66
+ const proxyServer = useRef<Server>();
67
+ if (!proxyServer.current) {
68
+ proxyServer.current = createServer()
69
+ .on("request", function (req, res) {
70
+ // log all requests
71
+ console.log(
72
+ new Date().toLocaleTimeString(),
73
+ req.method,
74
+ req.url,
75
+ res.statusCode
76
+ );
77
+ })
78
+ .on("upgrade", (req) => {
79
+ // log all websocket connections
80
+ console.log(
81
+ new Date().toLocaleTimeString(),
82
+ req.method,
83
+ req.url,
84
+ 101,
85
+ "(WebSocket)"
86
+ );
87
+ })
88
+ .on("error", (err) => {
89
+ // log all connection errors
90
+ console.error(new Date().toLocaleTimeString(), err);
91
+ });
92
+ }
93
+
94
+ /**
95
+ * When we're not connected / getting a fresh token on changes,
96
+ * we'd like to buffer streams/requests until we're connected.
97
+ * Once connected, we can flush the buffered streams/requests.
98
+ * streamBufferRef is used to buffer http/2 streams, while
99
+ * requestResponseBufferRef is used to buffer http/1 requests.
100
+ */
101
+ const streamBufferRef = useRef<
102
+ { stream: ServerHttp2Stream; headers: IncomingHttpHeaders }[]
103
+ >([]);
104
+ const requestResponseBufferRef = useRef<
105
+ { request: IncomingMessage; response: ServerResponse }[]
106
+ >([]);
107
+
108
+ useEffect(() => {
109
+ const proxy = proxyServer.current;
110
+
111
+ // If we don't have a token, that means either we're just starting up,
112
+ // or we're refreshing the token.
113
+ if (!previewToken) {
114
+ const cleanupListeners: (() => void)[] = [];
115
+ const bufferStream = (
116
+ stream: ServerHttp2Stream,
117
+ headers: IncomingHttpHeaders
118
+ ) => {
119
+ // store the stream in a buffer so we can replay it later
120
+ streamBufferRef.current.push({ stream, headers });
121
+ };
122
+ proxy.on("stream", bufferStream);
123
+ cleanupListeners.push(() => proxy.off("stream", bufferStream));
124
+
125
+ const bufferRequestResponse = (
126
+ request: IncomingMessage,
127
+ response: ServerResponse
128
+ ) => {
129
+ // store the request and response in a buffer so we can replay it later
130
+ requestResponseBufferRef.current.push({ request, response });
131
+ };
132
+
133
+ proxy.on("request", bufferRequestResponse);
134
+ cleanupListeners.push(() => proxy.off("request", bufferRequestResponse));
135
+ return () => {
136
+ cleanupListeners.forEach((cleanup) => cleanup());
137
+ };
138
+ }
139
+
140
+ // We have a token. Let's proxy requests to the preview end point.
141
+ const cleanupListeners: (() => void)[] = [];
142
+
143
+ const assetPath = typeof publicRoot === "string" ? publicRoot : null;
144
+
145
+ // create a ClientHttp2Session
146
+ const remote = connect(`https://${previewToken.host}`);
147
+ cleanupListeners.push(() => remote.destroy());
148
+
149
+ /** HTTP/2 -> HTTP/2 */
150
+ function handleStream(
151
+ stream: ServerHttp2Stream,
152
+ headers: IncomingHttpHeaders
153
+ ) {
154
+ addCfPreviewTokenHeader(headers, previewToken.value);
155
+ headers[":authority"] = previewToken.host;
156
+ const request = stream.pipe(remote.request(headers));
157
+ request.on("response", (responseHeaders: IncomingHttpHeaders) => {
158
+ rewriteRemoteHostToLocalHostInHeaders(
159
+ responseHeaders,
160
+ previewToken.host,
161
+ port
162
+ );
163
+ stream.respond(responseHeaders);
164
+ request.pipe(stream, { end: true });
165
+ });
166
+ }
167
+ proxy.on("stream", handleStream);
168
+ cleanupListeners.push(() => proxy.off("stream", handleStream));
169
+
170
+ // flush and replay buffered streams
171
+ streamBufferRef.current.forEach((buffer) =>
172
+ handleStream(buffer.stream, buffer.headers)
173
+ );
174
+ streamBufferRef.current = [];
175
+
176
+ /** HTTP/1 -> HTTP/2 */
177
+ const handleRequest: RequestListener = (
178
+ message: IncomingMessage,
179
+ response: ServerResponse
180
+ ) => {
181
+ const { httpVersionMajor, headers, method, url } = message;
182
+ if (httpVersionMajor >= 2) {
183
+ return; // Already handled by the "stream" event.
184
+ }
185
+ addCfPreviewTokenHeader(headers, previewToken.value);
186
+ headers[":method"] = method;
187
+ headers[":path"] = url;
188
+ headers[":authority"] = previewToken.host;
189
+ headers[":scheme"] = "https";
190
+ for (const name of Object.keys(headers)) {
191
+ if (HTTP1_HEADERS.has(name.toLowerCase())) {
192
+ delete headers[name];
193
+ }
194
+ }
195
+ const request = message.pipe(remote.request(headers));
196
+ request.on("response", (responseHeaders) => {
197
+ const status = responseHeaders[":status"];
198
+ rewriteRemoteHostToLocalHostInHeaders(
199
+ responseHeaders,
200
+ previewToken.host,
201
+ port
202
+ );
203
+ for (const name of Object.keys(responseHeaders)) {
204
+ if (name.startsWith(":")) {
205
+ delete responseHeaders[name];
206
+ }
207
+ }
208
+ response.writeHead(status, responseHeaders);
209
+ request.pipe(response, { end: true });
210
+ });
211
+ };
212
+
213
+ // If an asset path is defined, check the file system
214
+ // for a file first and serve if it exists.
215
+ const actualHandleRequest = assetPath
216
+ ? createHandleAssetsRequest(assetPath, handleRequest)
217
+ : handleRequest;
218
+
219
+ proxy.on("request", actualHandleRequest);
220
+ cleanupListeners.push(() => proxy.off("request", actualHandleRequest));
221
+
222
+ // flush and replay buffered requests
223
+ requestResponseBufferRef.current.forEach(({ request, response }) =>
224
+ actualHandleRequest(request, response)
225
+ );
226
+ requestResponseBufferRef.current = [];
227
+
228
+ /** HTTP/1 -> WebSocket (over HTTP/1) */
229
+ const handleUpgrade = (
230
+ message: IncomingMessage,
231
+ socket: WebSocket,
232
+ body: Buffer
233
+ ) => {
234
+ const { headers, url } = message;
235
+ addCfPreviewTokenHeader(headers, previewToken.value);
236
+ headers["host"] = previewToken.host;
237
+ const localWebsocket = new WebSocket(message, socket, body);
238
+ // TODO(soon): Custom WebSocket protocol is not working?
239
+ const remoteWebsocketClient = new WebSocket.Client(
240
+ `wss://${previewToken.host}${url}`,
241
+ [],
242
+ { headers }
243
+ );
244
+ localWebsocket.pipe(remoteWebsocketClient).pipe(localWebsocket);
245
+ // We close down websockets whenever we refresh the token.
246
+ cleanupListeners.push(() => {
247
+ localWebsocket.destroy();
248
+ remoteWebsocketClient.destroy();
249
+ });
250
+ };
251
+ proxy.on("upgrade", handleUpgrade);
252
+ cleanupListeners.push(() => proxy.off("upgrade", handleUpgrade));
253
+
254
+ return () => {
255
+ cleanupListeners.forEach((d) => d());
256
+ };
257
+ }, [previewToken, publicRoot, port]);
258
+
259
+ // Start/stop the server whenever the
260
+ // containing component is mounted/unmounted.
261
+ useEffect(() => {
262
+ proxyServer.current.listen(port);
263
+ console.log(`⬣ Listening at http://localhost:${port}`);
264
+
265
+ return () => {
266
+ proxyServer.current.close();
267
+ };
268
+ }, [port]);
269
+ }
270
+
271
+ function createHandleAssetsRequest(
272
+ assetPath: string,
273
+ handleRequest: RequestListener
274
+ ) {
275
+ const handleAsset = serveStatic(assetPath, {
276
+ cacheControl: false,
277
+ });
278
+ return (request: IncomingMessage, response: ServerResponse) => {
279
+ handleAsset(request, response, () => {
280
+ handleRequest(request, response);
281
+ });
282
+ };
283
+ }
284
+
285
+ /** A Set of headers we want to remove from HTTP/1 requests. */
286
+ const HTTP1_HEADERS = new Set([
287
+ "host",
288
+ "connection",
289
+ "upgrade",
290
+ "keep-alive",
291
+ "proxy-connection",
292
+ "transfer-encoding",
293
+ "http2-settings",
294
+ ]);
package/src/publish.ts CHANGED
@@ -1,17 +1,17 @@
1
- import type { CfWorkerInit } from "./api/worker";
2
- import { toFormData } from "./api/form_data";
1
+ import assert from "node:assert";
2
+ import path from "node:path";
3
+ import { readFile } from "node:fs/promises";
3
4
  import esbuild from "esbuild";
5
+ import { execa } from "execa";
4
6
  import tmp from "tmp-promise";
7
+ import type { CfWorkerInit } from "./api/worker";
8
+ import { toFormData } from "./api/form_data";
9
+ import { fetchResult } from "./cfetch";
5
10
  import type { Config } from "./config";
6
- import path from "path";
7
- import { readFile } from "fs/promises";
8
- import cfetch from "./cfetch";
9
- import assert from "node:assert";
11
+ import makeModuleCollector from "./module-collection";
10
12
  import { syncAssets } from "./sites";
11
- import NodeModulesPolyfills from "@esbuild-plugins/node-modules-polyfill";
12
- import NodeGlobalsPolyfills from "@esbuild-plugins/node-globals-polyfill";
13
13
 
14
- type CfScriptFormat = void | "modules" | "service-worker";
14
+ type CfScriptFormat = undefined | "modules" | "service-worker";
15
15
 
16
16
  type Props = {
17
17
  config: Config;
@@ -19,14 +19,15 @@ type Props = {
19
19
  script?: string;
20
20
  name?: string;
21
21
  env?: string;
22
+ compatibilityDate?: string;
23
+ compatibilityFlags?: string[];
22
24
  public?: string;
23
25
  site?: string;
24
26
  triggers?: (string | number)[];
25
27
  routes?: (string | number)[];
26
28
  legacyEnv?: boolean;
27
- jsxFactory: void | string;
28
- jsxFragment: void | string;
29
- polyfillNode: void | boolean;
29
+ jsxFactory: undefined | string;
30
+ jsxFragment: undefined | string;
30
31
  };
31
32
 
32
33
  function sleep(ms: number) {
@@ -49,6 +50,13 @@ export default async function publish(props: Props): Promise<void> {
49
50
  __path__,
50
51
  } = config;
51
52
 
53
+ const envRootObj = props.env ? config.env[props.env] || {} : config;
54
+
55
+ assert(
56
+ envRootObj.compatibility_date || props["compatibility-date"],
57
+ "A compatibility_date is required when publishing. Add one to your wrangler.toml file, or pass it in your terminal as --compatibility_date. See https://developers.cloudflare.com/workers/platform/compatibility-dates for more information."
58
+ );
59
+
52
60
  const triggers = props.triggers || config.triggers?.crons;
53
61
  const routes = props.routes || config.routes;
54
62
 
@@ -78,6 +86,18 @@ export default async function publish(props: Props): Promise<void> {
78
86
 
79
87
  const destination = await tmp.dir({ unsafeCleanup: true });
80
88
 
89
+ if (props.config.build?.command) {
90
+ // TODO: add a deprecation message here?
91
+ console.log("running:", props.config.build.command);
92
+ const buildCommandPieces = props.config.build.command.split(" ");
93
+ await execa(buildCommandPieces[0], buildCommandPieces.slice(1), {
94
+ stdout: "inherit",
95
+ stderr: "inherit",
96
+ ...(props.config.build?.cwd && { cwd: props.config.build.cwd }),
97
+ });
98
+ }
99
+
100
+ const moduleCollector = makeModuleCollector();
81
101
  const result = await esbuild.build({
82
102
  ...(props.public
83
103
  ? {
@@ -94,24 +114,17 @@ export default async function publish(props: Props): Promise<void> {
94
114
  }
95
115
  : { entryPoints: [file] }),
96
116
  bundle: true,
97
- define: {
98
- ...((props.polyfillNode ?? config.polyfill_node) && {
99
- global: "globalThis",
100
- }),
101
- },
102
117
  nodePaths: props.public ? [path.join(__dirname, "../vendor")] : undefined,
103
118
  outdir: destination.path,
104
119
  external: ["__STATIC_CONTENT_MANIFEST"],
105
120
  format: "esm",
106
- plugins:
107
- props.polyfillNode ?? config.polyfill_node
108
- ? [NodeGlobalsPolyfills({ buffer: true }), NodeModulesPolyfills()]
109
- : undefined,
110
121
  sourcemap: true,
111
122
  metafile: true,
123
+ conditions: ["worker", "browser"],
112
124
  loader: {
113
125
  ".js": "jsx",
114
126
  },
127
+ plugins: [moduleCollector.plugin],
115
128
  ...(jsxFactory && { jsxFactory }),
116
129
  ...(jsxFragment && { jsxFragment }),
117
130
  });
@@ -121,7 +134,7 @@ export default async function publish(props: Props): Promise<void> {
121
134
  entryPoint ===
122
135
  (props.public
123
136
  ? path.join(path.dirname(file), "static-asset-facade.js")
124
- : file)
137
+ : Object.keys(result.metafile.inputs)[0])
125
138
  );
126
139
 
127
140
  const { format } = props;
@@ -145,16 +158,16 @@ export default async function publish(props: Props): Promise<void> {
145
158
  }
146
159
 
147
160
  const content = await readFile(chunks[0], { encoding: "utf-8" });
148
- destination.cleanup();
161
+ await destination.cleanup();
149
162
 
150
163
  // if config.migrations
151
164
  // get current migration tag
152
165
  let migrations;
153
166
  if ("migrations" in config) {
154
- const scripts = await cfetch<{ id: string; migration_tag: string }[]>(
167
+ const scripts = await fetchResult<{ id: string; migration_tag: string }[]>(
155
168
  `/accounts/${accountId}/workers/scripts`
156
169
  );
157
- const script = scripts.find((script) => script.id === scriptName);
170
+ const script = scripts.find(({ id }) => id === scriptName);
158
171
  if (script?.migration_tag) {
159
172
  // was already published once
160
173
  const foundIndex = config.migrations.findIndex(
@@ -162,7 +175,7 @@ export default async function publish(props: Props): Promise<void> {
162
175
  );
163
176
  if (foundIndex === -1) {
164
177
  console.warn(
165
- `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...`
178
+ `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...`
166
179
  );
167
180
  migrations = {
168
181
  old_tag: script.migration_tag,
@@ -196,7 +209,16 @@ export default async function publish(props: Props): Promise<void> {
196
209
  )
197
210
  : { manifest: undefined, namespace: undefined };
198
211
 
199
- const envRootObj = props.env ? config.env[props.env] || {} : config;
212
+ const bindings: CfWorkerInit["bindings"] = {
213
+ kv_namespaces: envRootObj.kv_namespaces?.concat(
214
+ assets.namespace
215
+ ? { binding: "__STATIC_CONTENT", id: assets.namespace }
216
+ : []
217
+ ),
218
+ vars: envRootObj.vars,
219
+ durable_objects: envRootObj.durable_objects,
220
+ services: envRootObj.experimental_services,
221
+ };
200
222
 
201
223
  const worker: CfWorkerInit = {
202
224
  name: scriptName,
@@ -205,35 +227,17 @@ export default async function publish(props: Props): Promise<void> {
205
227
  content: content,
206
228
  type: bundle.type === "esm" ? "esm" : "commonjs",
207
229
  },
208
- variables: {
209
- ...(envRootObj?.vars || {}),
210
- ...(envRootObj?.kv_namespaces || []).reduce(
211
- (obj, { binding, preview_id: _preview_id, id }) => {
212
- return { ...obj, [binding]: { namespaceId: id } };
213
- },
214
- {}
215
- ),
216
- ...(envRootObj?.durable_objects?.bindings || []).reduce(
217
- (obj, { name, class_name, script_name }) => {
218
- return {
219
- ...obj,
220
- [name]: { class_name, ...(script_name && { script_name }) },
221
- };
222
- },
223
- {}
224
- ),
225
- ...(assets.namespace
226
- ? { __STATIC_CONTENT: { namespaceId: assets.namespace } }
227
- : {}),
228
- },
230
+ bindings,
229
231
  ...(migrations && { migrations }),
230
- modules: assets.manifest
231
- ? [].concat({
232
- name: "__STATIC_CONTENT_MANIFEST",
233
- content: JSON.stringify(assets.manifest),
234
- type: "text",
235
- })
236
- : [],
232
+ modules: moduleCollector.modules.concat(
233
+ assets.manifest
234
+ ? {
235
+ name: "__STATIC_CONTENT_MANIFEST",
236
+ content: JSON.stringify(assets.manifest),
237
+ type: "text",
238
+ }
239
+ : []
240
+ ),
237
241
  compatibility_date: config.compatibility_date,
238
242
  compatibility_flags: config.compatibility_flags,
239
243
  usage_model: config.usage_model,
@@ -250,8 +254,8 @@ export default async function publish(props: Props): Promise<void> {
250
254
  ? `/accounts/${accountId}/workers/services/${scriptName}/environments/${envName}`
251
255
  : `/accounts/${accountId}/workers/scripts/${scriptName}`;
252
256
 
253
- // Upload the script so it has time to propogate.
254
- const { available_on_subdomain } = await cfetch(
257
+ // Upload the script so it has time to propagate.
258
+ const { available_on_subdomain } = await fetchResult(
255
259
  `${workerUrl}?available_on_subdomain=true`,
256
260
  {
257
261
  method: "PUT",
@@ -265,7 +269,7 @@ export default async function publish(props: Props): Promise<void> {
265
269
  const deployments: Promise<string[]>[] = [];
266
270
 
267
271
  const userSubdomain = (
268
- await cfetch<{ subdomain: string }>(
272
+ await fetchResult<{ subdomain: string }>(
269
273
  `/accounts/${accountId}/workers/subdomain`
270
274
  )
271
275
  ).subdomain;
@@ -279,7 +283,7 @@ export default async function publish(props: Props): Promise<void> {
279
283
  // TODO: Make this configurable.
280
284
  if (!available_on_subdomain) {
281
285
  deployments.push(
282
- cfetch(`${workerUrl}/subdomain`, {
286
+ fetchResult(`${workerUrl}/subdomain`, {
283
287
  method: "POST",
284
288
  body: JSON.stringify({ enabled: true }),
285
289
  headers: {
@@ -291,8 +295,8 @@ export default async function publish(props: Props): Promise<void> {
291
295
  // This is to prevent an issue where a negative cache-hit
292
296
  // causes the subdomain to be unavailable for 30 seconds.
293
297
  // This is a temporary measure until we fix this on the edge.
294
- .then((url) => {
295
- sleep(3000);
298
+ .then(async (url) => {
299
+ await sleep(3000);
296
300
  return url;
297
301
  })
298
302
  );
@@ -303,7 +307,7 @@ export default async function publish(props: Props): Promise<void> {
303
307
  // Update routing table for the script.
304
308
  if (routes && routes.length) {
305
309
  deployments.push(
306
- cfetch(`${workerUrl}/routes`, {
310
+ fetchResult(`${workerUrl}/routes`, {
307
311
  // TODO: PATCH will not delete previous routes on this script,
308
312
  // whereas PUT will. We need to decide on the default behaviour
309
313
  // and how to configure it.
@@ -328,7 +332,7 @@ export default async function publish(props: Props): Promise<void> {
328
332
  // TODO: rename this to `schedules`?
329
333
  if (triggers && triggers.length) {
330
334
  deployments.push(
331
- cfetch(`${workerUrl}/schedules`, {
335
+ fetchResult(`${workerUrl}/schedules`, {
332
336
  // TODO: Unlike routes, this endpoint does not support PATCH.
333
337
  // So technically, this will override any previous schedules.
334
338
  // We should change the endpoint to support PATCH.
package/src/sites.tsx CHANGED
@@ -1,11 +1,10 @@
1
- import { readdir, readFile } from "node:fs/promises";
1
+ import crypto from "node:crypto";
2
2
  import { createReadStream } from "node:fs";
3
- import cfetch from "./cfetch";
3
+ import * as path from "node:path";
4
+ import { readdir, readFile } from "node:fs/promises";
5
+ import { fetchResult } from "./cfetch";
4
6
  import { listNamespaceKeys, listNamespaces, putBulkKeyValue } from "./kv";
5
7
 
6
- import * as path from "path";
7
- import crypto from "node:crypto";
8
-
9
8
  async function* getFilesInFolder(dirPath: string): AsyncIterable<string> {
10
9
  const files = await readdir(dirPath, { withFileTypes: true });
11
10
  for (const file of files) {
@@ -54,7 +53,7 @@ async function createKVNamespaceIfNotAlreadyExisting(
54
53
 
55
54
  // else we make the namespace
56
55
  // TODO: use an export from ./kv
57
- const json = await cfetch<{ id: string }>(
56
+ const json = await fetchResult<{ id: string }>(
58
57
  `/accounts/${accountId}/storage/kv/namespaces`,
59
58
  {
60
59
  method: "POST",
@@ -90,7 +89,11 @@ export async function syncAssets(
90
89
  );
91
90
 
92
91
  const manifest = {};
93
- const upload = [];
92
+ const upload: {
93
+ key: string;
94
+ value: string;
95
+ base64: boolean;
96
+ }[] = [];
94
97
  // TODO: this can be more efficient by parallelising
95
98
  for await (const file of getFilesInFolder(dirPath)) {
96
99
  // TODO: "exclude:" config
package/src/tail.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import WebSocket from "ws";
2
- import cfetch from "./cfetch";
3
2
  import { version as packageVersion } from "../package.json";
3
+ import { fetchResult } from "./cfetch";
4
4
 
5
5
  export type TailApiResponse = {
6
6
  id: string;
@@ -27,7 +27,9 @@ async function createTailButDontConnect(
27
27
  ): Promise<TailApiResponse> {
28
28
  const createTailUrl = makeCreateTailUrl(accountId, workerName);
29
29
  /// https://api.cloudflare.com/#worker-tail-logs-start-tail
30
- return await cfetch<TailApiResponse>(createTailUrl, { method: "POST" });
30
+ return await fetchResult<TailApiResponse>(createTailUrl, {
31
+ method: "POST",
32
+ });
31
33
  }
32
34
 
33
35
  export async function createTail(
@@ -48,7 +50,7 @@ export async function createTail(
48
50
 
49
51
  // deletes the tail
50
52
  async function deleteTail() {
51
- await cfetch(deleteUrl, { method: "DELETE" });
53
+ await fetchResult(deleteUrl, { method: "DELETE" });
52
54
  }
53
55
 
54
56
  const tail = new WebSocket(websocketUrl, "trace-v1", {