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/package.json +13 -7
- package/src/__tests__/index.test.ts +254 -120
- package/src/__tests__/jest.setup.ts +18 -0
- package/src/__tests__/kv.test.ts +322 -0
- package/src/__tests__/mock-cfetch.ts +63 -0
- package/src/__tests__/mock-dialogs.ts +65 -0
- package/src/__tests__/run-in-tmp.ts +19 -0
- package/src/__tests__/run-wrangler.ts +32 -0
- package/src/api/form_data.ts +65 -75
- package/src/api/inspect.ts +0 -11
- package/src/api/worker.ts +33 -39
- package/src/cli.ts +6 -7
- package/src/config.ts +14 -5
- package/src/dev.tsx +264 -109
- package/src/dialogs.tsx +3 -11
- package/src/index.tsx +254 -210
- package/src/kv.tsx +65 -19
- package/src/pages.tsx +107 -99
- package/src/proxy.ts +103 -0
- package/src/publish.ts +37 -33
- package/src/user.tsx +9 -16
- package/wrangler-dist/cli.js +20754 -26926
- package/wrangler-dist/cli.js.map +3 -3
- package/src/__tests__/fixtures/init/.gitkeep +0 -0
- package/src/__tests__/mock-cfetch.js +0 -46
package/src/kv.tsx
CHANGED
|
@@ -10,20 +10,57 @@ type KvArgs = {
|
|
|
10
10
|
config?: Config;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
25
|
-
if (json.length
|
|
26
|
-
|
|
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
|
|
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 `--
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
7
|
-
import
|
|
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
|
|
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
|
-
}
|
|
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
|
|
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
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
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
|
-
|
|
827
|
-
|
|
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
|
-
|
|
830
|
-
|
|
831
|
-
|
|
829
|
+
if (process.env.BROWSER !== "none") {
|
|
830
|
+
await open(`http://127.0.0.1:${port}/`);
|
|
831
|
+
}
|
|
832
832
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
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((
|
|
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
|
|
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
|
-
|
|
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:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
);
|