wrangler 2.0.17 → 2.0.18
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 +2 -1
- package/src/__tests__/helpers/run-in-tmp.ts +19 -27
- package/src/__tests__/https-options.test.ts +50 -17
- package/src/__tests__/jest.setup.ts +13 -0
- package/src/__tests__/pages.test.ts +15 -8
- package/src/__tests__/publish.test.ts +37 -1
- package/src/__tests__/user.test.ts +5 -2
- package/src/global-wrangler-config-path.ts +26 -0
- package/src/https-options.ts +2 -1
- package/src/pages/upload.tsx +24 -12
- package/src/publish.ts +28 -15
- package/src/user/user.tsx +11 -5
- package/wrangler-dist/cli.js +1659 -1101
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wrangler",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.18",
|
|
4
4
|
"description": "Command-line interface for all things Cloudflare Workers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wrangler",
|
|
@@ -149,6 +149,7 @@
|
|
|
149
149
|
"undici": "^5.5.1",
|
|
150
150
|
"update-check": "^1.5.4",
|
|
151
151
|
"ws": "^8.5.0",
|
|
152
|
+
"xdg-app-paths": "^7.3.0",
|
|
152
153
|
"yargs": "^17.4.1"
|
|
153
154
|
},
|
|
154
155
|
"optionalDependencies": {
|
|
@@ -5,42 +5,34 @@ import { reinitialiseAuthTokens } from "../../user";
|
|
|
5
5
|
|
|
6
6
|
const originalCwd = process.cwd();
|
|
7
7
|
|
|
8
|
-
export function runInTempDir(
|
|
9
|
-
|
|
10
|
-
) {
|
|
11
|
-
let tmpDir: string;
|
|
12
|
-
|
|
13
|
-
beforeAll(() => {
|
|
14
|
-
if (tmpDir !== undefined) {
|
|
15
|
-
process.chdir(originalCwd);
|
|
16
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
17
|
-
}
|
|
18
|
-
});
|
|
8
|
+
export function runInTempDir({ homedir } = { homedir: "./home" }) {
|
|
9
|
+
let tmpDir: string | undefined;
|
|
19
10
|
|
|
20
11
|
beforeEach(() => {
|
|
21
12
|
// Use realpath because the temporary path can point to a symlink rather than the actual path.
|
|
22
13
|
tmpDir = fs.realpathSync(
|
|
23
14
|
fs.mkdtempSync(path.join(os.tmpdir(), "wrangler-tests"))
|
|
24
15
|
);
|
|
16
|
+
|
|
25
17
|
process.chdir(tmpDir);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
reinitialiseAuthTokens();
|
|
39
|
-
}
|
|
18
|
+
// The path that is returned from `homedir()` should be absolute.
|
|
19
|
+
const absHomedir = path.resolve(tmpDir, homedir);
|
|
20
|
+
// Override where the home directory is so that we can write our own user config,
|
|
21
|
+
// without destroying the real thing.
|
|
22
|
+
fs.mkdirSync(absHomedir, { recursive: true });
|
|
23
|
+
// Note it is very important that we use the "default" value from "node:os" (e.g. `import os from "node:os";`)
|
|
24
|
+
// rather than an alias to the module (e.g. `import * as os from "node:os";`).
|
|
25
|
+
// This is because the module gets transpiled so that the "method" `homedir()` gets converted to a
|
|
26
|
+
// getter that is not configurable (and so cannot be spied upon).
|
|
27
|
+
jest.spyOn(os, "homedir")?.mockReturnValue(absHomedir);
|
|
28
|
+
// Now that we have changed the home directory location, we must reinitialize the user auth state
|
|
29
|
+
reinitialiseAuthTokens();
|
|
40
30
|
});
|
|
41
31
|
|
|
42
32
|
afterEach(() => {
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
if (tmpDir && fs.existsSync(tmpDir)) {
|
|
34
|
+
process.chdir(originalCwd);
|
|
35
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
36
|
+
}
|
|
45
37
|
});
|
|
46
38
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
|
-
import
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
|
|
4
5
|
import { getHttpsOptions } from "../https-options";
|
|
5
6
|
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
6
7
|
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
@@ -10,15 +11,15 @@ describe("getHttpsOptions()", () => {
|
|
|
10
11
|
const std = mockConsoleMethods();
|
|
11
12
|
|
|
12
13
|
it("should use cached values if they have not expired", async () => {
|
|
13
|
-
fs.mkdirSync(resolve(
|
|
14
|
+
fs.mkdirSync(path.resolve(getGlobalWranglerConfigPath(), "local-cert"), {
|
|
14
15
|
recursive: true,
|
|
15
16
|
});
|
|
16
17
|
fs.writeFileSync(
|
|
17
|
-
resolve(
|
|
18
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem"),
|
|
18
19
|
"PRIVATE KEY"
|
|
19
20
|
);
|
|
20
21
|
fs.writeFileSync(
|
|
21
|
-
resolve(
|
|
22
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem"),
|
|
22
23
|
"PUBLIC KEY"
|
|
23
24
|
);
|
|
24
25
|
const result = await getHttpsOptions();
|
|
@@ -32,11 +33,11 @@ describe("getHttpsOptions()", () => {
|
|
|
32
33
|
it("should generate and cache new keys if none are cached", async () => {
|
|
33
34
|
const result = await getHttpsOptions();
|
|
34
35
|
const key = fs.readFileSync(
|
|
35
|
-
resolve(
|
|
36
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem"),
|
|
36
37
|
"utf8"
|
|
37
38
|
);
|
|
38
39
|
const cert = fs.readFileSync(
|
|
39
|
-
resolve(
|
|
40
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem"),
|
|
40
41
|
"utf8"
|
|
41
42
|
);
|
|
42
43
|
expect(result.key).toEqual(key);
|
|
@@ -49,28 +50,28 @@ describe("getHttpsOptions()", () => {
|
|
|
49
50
|
});
|
|
50
51
|
|
|
51
52
|
it("should generate and cache new keys if cached files have expired", async () => {
|
|
52
|
-
fs.mkdirSync(resolve(
|
|
53
|
+
fs.mkdirSync(path.resolve(getGlobalWranglerConfigPath(), "local-cert"), {
|
|
53
54
|
recursive: true,
|
|
54
55
|
});
|
|
55
56
|
const ORIGINAL_KEY = "EXPIRED PRIVATE KEY";
|
|
56
57
|
const ORIGINAL_CERT = "EXPIRED PUBLIC KEY";
|
|
57
58
|
fs.writeFileSync(
|
|
58
|
-
resolve(
|
|
59
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem"),
|
|
59
60
|
ORIGINAL_KEY
|
|
60
61
|
);
|
|
61
62
|
fs.writeFileSync(
|
|
62
|
-
resolve(
|
|
63
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem"),
|
|
63
64
|
ORIGINAL_CERT
|
|
64
65
|
);
|
|
65
66
|
mockStatSync(/\.pem$/, { mtimeMs: new Date(2000).valueOf() });
|
|
66
67
|
|
|
67
68
|
const result = await getHttpsOptions();
|
|
68
69
|
const key = fs.readFileSync(
|
|
69
|
-
resolve(
|
|
70
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem"),
|
|
70
71
|
"utf8"
|
|
71
72
|
);
|
|
72
73
|
const cert = fs.readFileSync(
|
|
73
|
-
resolve(
|
|
74
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem"),
|
|
74
75
|
"utf8"
|
|
75
76
|
);
|
|
76
77
|
expect(key).not.toEqual(ORIGINAL_KEY);
|
|
@@ -84,25 +85,57 @@ describe("getHttpsOptions()", () => {
|
|
|
84
85
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
85
86
|
});
|
|
86
87
|
|
|
88
|
+
it("should warn if not able to write to the cache (legacy config path)", async () => {
|
|
89
|
+
fs.mkdirSync(path.join(os.homedir(), ".wrangler"));
|
|
90
|
+
mockWriteFileSyncThrow(/\.pem$/);
|
|
91
|
+
await getHttpsOptions();
|
|
92
|
+
expect(
|
|
93
|
+
fs.existsSync(
|
|
94
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem")
|
|
95
|
+
)
|
|
96
|
+
).toBe(false);
|
|
97
|
+
expect(
|
|
98
|
+
fs.existsSync(
|
|
99
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem")
|
|
100
|
+
)
|
|
101
|
+
).toBe(false);
|
|
102
|
+
expect(std.out).toMatchInlineSnapshot(
|
|
103
|
+
`"Generating new self-signed certificate..."`
|
|
104
|
+
);
|
|
105
|
+
expect(std.warn).toMatchInlineSnapshot(`
|
|
106
|
+
"[33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1mUnable to cache generated self-signed certificate in home/.wrangler/local-cert.[0m
|
|
107
|
+
|
|
108
|
+
ERROR: Cannot write file
|
|
109
|
+
|
|
110
|
+
"
|
|
111
|
+
`);
|
|
112
|
+
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
113
|
+
fs.rmSync(path.join(os.homedir(), ".wrangler"), { recursive: true });
|
|
114
|
+
});
|
|
115
|
+
|
|
87
116
|
it("should warn if not able to write to the cache", async () => {
|
|
88
117
|
mockWriteFileSyncThrow(/\.pem$/);
|
|
89
118
|
await getHttpsOptions();
|
|
90
119
|
expect(
|
|
91
|
-
fs.existsSync(
|
|
120
|
+
fs.existsSync(
|
|
121
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem")
|
|
122
|
+
)
|
|
92
123
|
).toBe(false);
|
|
93
124
|
expect(
|
|
94
|
-
fs.existsSync(
|
|
125
|
+
fs.existsSync(
|
|
126
|
+
path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem")
|
|
127
|
+
)
|
|
95
128
|
).toBe(false);
|
|
96
129
|
expect(std.out).toMatchInlineSnapshot(
|
|
97
130
|
`"Generating new self-signed certificate..."`
|
|
98
131
|
);
|
|
99
132
|
expect(std.warn).toMatchInlineSnapshot(`
|
|
100
|
-
|
|
133
|
+
"[33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1mUnable to cache generated self-signed certificate in test-xdg-config/local-cert.[0m
|
|
101
134
|
|
|
102
|
-
|
|
135
|
+
ERROR: Cannot write file
|
|
103
136
|
|
|
104
|
-
|
|
105
|
-
|
|
137
|
+
"
|
|
138
|
+
`);
|
|
106
139
|
expect(std.err).toMatchInlineSnapshot(`""`);
|
|
107
140
|
});
|
|
108
141
|
});
|
|
@@ -105,3 +105,16 @@ jest.mock("../user/generate-random-state", () => {
|
|
|
105
105
|
generateRandomState: jest.fn().mockImplementation(() => "MOCK_STATE_PARAM"),
|
|
106
106
|
};
|
|
107
107
|
});
|
|
108
|
+
|
|
109
|
+
jest.mock("xdg-app-paths", () => {
|
|
110
|
+
return {
|
|
111
|
+
__esModule: true,
|
|
112
|
+
default: jest.fn().mockImplementation(() => {
|
|
113
|
+
return {
|
|
114
|
+
config() {
|
|
115
|
+
return jest.requireActual("node:path").resolve("test-xdg-config");
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
});
|
|
@@ -45,6 +45,13 @@ describe("pages", () => {
|
|
|
45
45
|
outOfBandTests.forEach((fn) => fn());
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
// @ts-expect-error we're using a very simple setTimeout mock here
|
|
50
|
+
jest.spyOn(global, "setTimeout").mockImplementation((fn, _period) => {
|
|
51
|
+
setImmediate(fn);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
48
55
|
it("should should display a list of available subcommands, for pages with no subcommand", async () => {
|
|
49
56
|
await runWrangler("pages");
|
|
50
57
|
await endEventLoop();
|
|
@@ -645,8 +652,8 @@ describe("pages", () => {
|
|
|
645
652
|
});
|
|
646
653
|
bodies.push(JSON.parse(init.body as string) as UploadPayloadFile[]);
|
|
647
654
|
}
|
|
648
|
-
//
|
|
649
|
-
expect(bodies.map((b) => b.length)).toEqual([
|
|
655
|
+
// One bucket should end up with 2 files
|
|
656
|
+
expect(bodies.map((b) => b.length).sort()).toEqual([1, 1, 2]);
|
|
650
657
|
// But we don't know the order, so flatten and test without ordering
|
|
651
658
|
expect(bodies.flatMap((b) => b)).toEqual(
|
|
652
659
|
expect.arrayContaining([
|
|
@@ -759,8 +766,8 @@ describe("pages", () => {
|
|
|
759
766
|
});
|
|
760
767
|
bodies.push(JSON.parse(init.body as string) as UploadPayloadFile[]);
|
|
761
768
|
}
|
|
762
|
-
//
|
|
763
|
-
expect(bodies.map((b) => b.length)).toEqual([
|
|
769
|
+
// One bucket should end up with 2 files
|
|
770
|
+
expect(bodies.map((b) => b.length).sort()).toEqual([1, 1, 2]);
|
|
764
771
|
// But we don't know the order, so flatten and test without ordering
|
|
765
772
|
expect(bodies.flatMap((b) => b)).toEqual(
|
|
766
773
|
expect.arrayContaining([
|
|
@@ -874,8 +881,8 @@ describe("pages", () => {
|
|
|
874
881
|
});
|
|
875
882
|
bodies.push(JSON.parse(init.body as string) as UploadPayloadFile[]);
|
|
876
883
|
}
|
|
877
|
-
//
|
|
878
|
-
expect(bodies.map((b) => b.length)).toEqual([
|
|
884
|
+
// One bucket should end up with 2 files
|
|
885
|
+
expect(bodies.map((b) => b.length).sort()).toEqual([1, 1, 2]);
|
|
879
886
|
// But we don't know the order, so flatten and test without ordering
|
|
880
887
|
expect(bodies.flatMap((b) => b)).toEqual(
|
|
881
888
|
expect.arrayContaining([
|
|
@@ -1163,8 +1170,8 @@ describe("pages", () => {
|
|
|
1163
1170
|
});
|
|
1164
1171
|
bodies.push(JSON.parse(init.body as string) as UploadPayloadFile[]);
|
|
1165
1172
|
}
|
|
1166
|
-
//
|
|
1167
|
-
expect(bodies.map((b) => b.length)).toEqual([
|
|
1173
|
+
// One bucket should end up with 2 files
|
|
1174
|
+
expect(bodies.map((b) => b.length).sort()).toEqual([1, 1, 2]);
|
|
1168
1175
|
// But we don't know the order, so flatten and test without ordering
|
|
1169
1176
|
expect(bodies.flatMap((b) => b)).toEqual(
|
|
1170
1177
|
expect.arrayContaining([
|
|
@@ -51,6 +51,31 @@ describe("publish", () => {
|
|
|
51
51
|
unsetMockFetchKVGetValues();
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
+
describe("output additional script information", () => {
|
|
55
|
+
mockApiToken();
|
|
56
|
+
|
|
57
|
+
it("should print worker information at log level", async () => {
|
|
58
|
+
setIsTTY(false);
|
|
59
|
+
writeWranglerToml();
|
|
60
|
+
writeWorkerSource();
|
|
61
|
+
mockSubDomainRequest();
|
|
62
|
+
mockUploadWorkerRequest({ expectedType: "esm", sendScriptIds: true });
|
|
63
|
+
mockOAuthServerCallback();
|
|
64
|
+
|
|
65
|
+
await runWrangler("publish ./index");
|
|
66
|
+
|
|
67
|
+
expect(std.out).toMatchInlineSnapshot(`
|
|
68
|
+
"Total Upload: 0xx KiB / gzip: 0xx KiB
|
|
69
|
+
Worker ID: abc12345
|
|
70
|
+
Worker ETag: etag98765
|
|
71
|
+
Worker PipelineHash: hash9999
|
|
72
|
+
Uploaded test-name (TIMINGS)
|
|
73
|
+
Published test-name (TIMINGS)
|
|
74
|
+
test-name.test-sub-domain.workers.dev"
|
|
75
|
+
`);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
54
79
|
describe("authentication", () => {
|
|
55
80
|
mockApiToken({ apiToken: null });
|
|
56
81
|
beforeEach(() => {
|
|
@@ -6113,6 +6138,7 @@ function mockUploadWorkerRequest(
|
|
|
6113
6138
|
expectedMigrations?: CfWorkerInit["migrations"];
|
|
6114
6139
|
env?: string;
|
|
6115
6140
|
legacyEnv?: boolean;
|
|
6141
|
+
sendScriptIds?: boolean;
|
|
6116
6142
|
} = {}
|
|
6117
6143
|
) {
|
|
6118
6144
|
const {
|
|
@@ -6127,6 +6153,7 @@ function mockUploadWorkerRequest(
|
|
|
6127
6153
|
env = undefined,
|
|
6128
6154
|
legacyEnv = false,
|
|
6129
6155
|
expectedMigrations,
|
|
6156
|
+
sendScriptIds,
|
|
6130
6157
|
} = options;
|
|
6131
6158
|
setMockResponse(
|
|
6132
6159
|
env && !legacyEnv
|
|
@@ -6175,7 +6202,16 @@ function mockUploadWorkerRequest(
|
|
|
6175
6202
|
expect(await (formBody.get(name) as File).text()).toEqual(content);
|
|
6176
6203
|
}
|
|
6177
6204
|
|
|
6178
|
-
return {
|
|
6205
|
+
return {
|
|
6206
|
+
available_on_subdomain,
|
|
6207
|
+
...(sendScriptIds
|
|
6208
|
+
? {
|
|
6209
|
+
id: "abc12345",
|
|
6210
|
+
etag: "etag98765",
|
|
6211
|
+
pipeline_hash: "hash9999",
|
|
6212
|
+
}
|
|
6213
|
+
: {}),
|
|
6214
|
+
};
|
|
6179
6215
|
}
|
|
6180
6216
|
);
|
|
6181
6217
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import fetchMock from "jest-fetch-mock";
|
|
4
|
+
import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
|
|
5
5
|
import {
|
|
6
6
|
loginOrRefreshIfRequired,
|
|
7
7
|
readAuthConfigFile,
|
|
@@ -86,7 +86,10 @@ describe("User", () => {
|
|
|
86
86
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
87
87
|
|
|
88
88
|
// Make sure that logout removed the config file containing the auth tokens.
|
|
89
|
-
const config = path.join(
|
|
89
|
+
const config = path.join(
|
|
90
|
+
getGlobalWranglerConfigPath(),
|
|
91
|
+
USER_AUTH_CONFIG_FILE
|
|
92
|
+
);
|
|
90
93
|
expect(fs.existsSync(config)).toBeFalsy();
|
|
91
94
|
});
|
|
92
95
|
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import xdgAppPaths from "xdg-app-paths";
|
|
5
|
+
|
|
6
|
+
function isDirectory(configPath: string) {
|
|
7
|
+
try {
|
|
8
|
+
return fs.statSync(configPath).isDirectory();
|
|
9
|
+
} catch (error) {
|
|
10
|
+
// ignore error
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getGlobalWranglerConfigPath() {
|
|
16
|
+
//TODO: We should implement a custom path --global-config and/or the WRANGLER_HOME type environment variable
|
|
17
|
+
const configDir = xdgAppPaths(".wrangler").config(); // New XDG compliant config path
|
|
18
|
+
const legacyConfigDir = path.join(os.homedir(), ".wrangler"); // Legacy config in user's home directory
|
|
19
|
+
|
|
20
|
+
// Check for the .wrangler directory in root if it is not there then use the XDG compliant path.
|
|
21
|
+
if (isDirectory(legacyConfigDir)) {
|
|
22
|
+
return legacyConfigDir;
|
|
23
|
+
} else {
|
|
24
|
+
return configDir;
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/https-options.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
|
+
import { getGlobalWranglerConfigPath } from "./global-wrangler-config-path";
|
|
5
6
|
import { logger } from "./logger";
|
|
6
7
|
import type { Attributes, Options } from "selfsigned";
|
|
7
8
|
|
|
@@ -17,7 +18,7 @@ const ONE_DAY_IN_MS = 86400000;
|
|
|
17
18
|
* The certificates are self-signed and generated locally, and cached in the `CERT_ROOT` directory.
|
|
18
19
|
*/
|
|
19
20
|
export async function getHttpsOptions() {
|
|
20
|
-
const certDirectory = path.join(
|
|
21
|
+
const certDirectory = path.join(getGlobalWranglerConfigPath(), "local-cert");
|
|
21
22
|
const keyPath = path.join(certDirectory, "key.pem");
|
|
22
23
|
const certPath = path.join(certDirectory, "cert.pem");
|
|
23
24
|
|
package/src/pages/upload.tsx
CHANGED
|
@@ -95,7 +95,7 @@ export const upload = async (
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
type FileContainer = {
|
|
98
|
-
|
|
98
|
+
path: string;
|
|
99
99
|
contentType: string;
|
|
100
100
|
sizeInBytes: number;
|
|
101
101
|
hash: string;
|
|
@@ -112,6 +112,14 @@ export const upload = async (
|
|
|
112
112
|
|
|
113
113
|
const directory = resolve(args.directory);
|
|
114
114
|
|
|
115
|
+
// TODO(future): Use this to more efficiently load files in and speed up uploading
|
|
116
|
+
// Limit memory to 1 GB unless more is specified
|
|
117
|
+
// let maxMemory = 1_000_000_000;
|
|
118
|
+
// if (process.env.NODE_OPTIONS && (process.env.NODE_OPTIONS.includes('--max-old-space-size=') || process.env.NODE_OPTIONS.includes('--max_old_space_size='))) {
|
|
119
|
+
// const parsed = parser(process.env.NODE_OPTIONS);
|
|
120
|
+
// maxMemory = (parsed['max-old-space-size'] ? parsed['max-old-space-size'] : parsed['max_old_space_size']) * 1000 * 1000; // Turn MB into bytes
|
|
121
|
+
// }
|
|
122
|
+
|
|
115
123
|
const walk = async (
|
|
116
124
|
dir: string,
|
|
117
125
|
fileMap: Map<string, FileContainer> = new Map(),
|
|
@@ -137,7 +145,6 @@ export const upload = async (
|
|
|
137
145
|
} else {
|
|
138
146
|
const name = relative(startingDir, filepath).split(sep).join("/");
|
|
139
147
|
|
|
140
|
-
// TODO: Move this to later so we don't hold as much in memory
|
|
141
148
|
const fileContent = await readFile(filepath);
|
|
142
149
|
|
|
143
150
|
const base64Content = fileContent.toString("base64");
|
|
@@ -152,8 +159,9 @@ export const upload = async (
|
|
|
152
159
|
);
|
|
153
160
|
}
|
|
154
161
|
|
|
162
|
+
// We don't want to hold the content in memory. We instead only want to read it when it's needed
|
|
155
163
|
fileMap.set(name, {
|
|
156
|
-
|
|
164
|
+
path: filepath,
|
|
157
165
|
contentType: getType(name) || "application/octet-stream",
|
|
158
166
|
sizeInBytes: filestat.size,
|
|
159
167
|
hash: blake3hash(base64Content + extension)
|
|
@@ -248,17 +256,21 @@ export const upload = async (
|
|
|
248
256
|
// Don't upload empty buckets (can happen for tiny projects)
|
|
249
257
|
if (bucket.files.length === 0) continue;
|
|
250
258
|
|
|
251
|
-
const payload: UploadPayloadFile[] = bucket.files.map((file) => ({
|
|
252
|
-
key: file.hash,
|
|
253
|
-
value: file.content,
|
|
254
|
-
metadata: {
|
|
255
|
-
contentType: file.contentType,
|
|
256
|
-
},
|
|
257
|
-
base64: true,
|
|
258
|
-
}));
|
|
259
|
-
|
|
260
259
|
let attempts = 0;
|
|
261
260
|
const doUpload = async (): Promise<void> => {
|
|
261
|
+
// Populate the payload only when actually uploading (this is limited to 3 concurrent uploads at 50 MiB per bucket meaning we'd only load in a max of ~150 MiB)
|
|
262
|
+
// This is so we don't run out of memory trying to upload the files.
|
|
263
|
+
const payload: UploadPayloadFile[] = await Promise.all(
|
|
264
|
+
bucket.files.map(async (file) => ({
|
|
265
|
+
key: file.hash,
|
|
266
|
+
value: (await readFile(file.path)).toString("base64"),
|
|
267
|
+
metadata: {
|
|
268
|
+
contentType: file.contentType,
|
|
269
|
+
},
|
|
270
|
+
base64: true,
|
|
271
|
+
}))
|
|
272
|
+
);
|
|
273
|
+
|
|
262
274
|
try {
|
|
263
275
|
return await fetchResult(`/pages/assets/upload`, {
|
|
264
276
|
method: "POST",
|
package/src/publish.ts
CHANGED
|
@@ -469,21 +469,34 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
469
469
|
if (!props.dryRun) {
|
|
470
470
|
// Upload the script so it has time to propagate.
|
|
471
471
|
// We can also now tell whether available_on_subdomain is set
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
472
|
+
const result = await fetchResult<{
|
|
473
|
+
available_on_subdomain: boolean;
|
|
474
|
+
id: string | null;
|
|
475
|
+
etag: string | null;
|
|
476
|
+
pipeline_hash: string | null;
|
|
477
|
+
}>(
|
|
478
|
+
workerUrl,
|
|
479
|
+
{
|
|
480
|
+
method: "PUT",
|
|
481
|
+
body: createWorkerUploadForm(worker),
|
|
482
|
+
},
|
|
483
|
+
new URLSearchParams({
|
|
484
|
+
include_subdomain_availability: "true",
|
|
485
|
+
// pass excludeScript so the whole body of the
|
|
486
|
+
// script doesn't get included in the response
|
|
487
|
+
excludeScript: "true",
|
|
488
|
+
})
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
available_on_subdomain = result.available_on_subdomain;
|
|
492
|
+
|
|
493
|
+
// Print some useful information returned after publishing
|
|
494
|
+
// Not all fields will be populated for every worker
|
|
495
|
+
// These fields are likely to be scraped by tools, so do not rename
|
|
496
|
+
if (result.id) logger.log("Worker ID: ", result.id);
|
|
497
|
+
if (result.etag) logger.log("Worker ETag: ", result.etag);
|
|
498
|
+
if (result.pipeline_hash)
|
|
499
|
+
logger.log("Worker PipelineHash: ", result.pipeline_hash);
|
|
487
500
|
}
|
|
488
501
|
} finally {
|
|
489
502
|
if (typeof destination !== "string") {
|
package/src/user/user.tsx
CHANGED
|
@@ -209,7 +209,6 @@ import assert from "node:assert";
|
|
|
209
209
|
import { webcrypto as crypto } from "node:crypto";
|
|
210
210
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
211
211
|
import http from "node:http";
|
|
212
|
-
import os from "node:os";
|
|
213
212
|
import path from "node:path";
|
|
214
213
|
import url from "node:url";
|
|
215
214
|
import { TextEncoder } from "node:util";
|
|
@@ -224,6 +223,7 @@ import {
|
|
|
224
223
|
purgeConfigCaches,
|
|
225
224
|
saveToConfigCache,
|
|
226
225
|
} from "../config-cache";
|
|
226
|
+
import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
|
|
227
227
|
import isInteractive from "../is-interactive";
|
|
228
228
|
import { logger } from "../logger";
|
|
229
229
|
import openInBrowser from "../open-in-browser";
|
|
@@ -270,7 +270,7 @@ interface AuthTokens {
|
|
|
270
270
|
* The path to the config file that holds user authentication data,
|
|
271
271
|
* relative to the user's home directory.
|
|
272
272
|
*/
|
|
273
|
-
export const USER_AUTH_CONFIG_FILE = "
|
|
273
|
+
export const USER_AUTH_CONFIG_FILE = "config/default.toml";
|
|
274
274
|
|
|
275
275
|
/**
|
|
276
276
|
* The data that may be read from the `USER_CONFIG_FILE`.
|
|
@@ -843,7 +843,10 @@ async function generatePKCECodes(): Promise<PKCECodes> {
|
|
|
843
843
|
* and updates the user auth state with the new credentials.
|
|
844
844
|
*/
|
|
845
845
|
export function writeAuthConfigFile(config: UserAuthConfig) {
|
|
846
|
-
const authConfigFilePath = path.join(
|
|
846
|
+
const authConfigFilePath = path.join(
|
|
847
|
+
getGlobalWranglerConfigPath(),
|
|
848
|
+
USER_AUTH_CONFIG_FILE
|
|
849
|
+
);
|
|
847
850
|
mkdirSync(path.dirname(authConfigFilePath), {
|
|
848
851
|
recursive: true,
|
|
849
852
|
});
|
|
@@ -857,7 +860,10 @@ export function writeAuthConfigFile(config: UserAuthConfig) {
|
|
|
857
860
|
}
|
|
858
861
|
|
|
859
862
|
export function readAuthConfigFile(): UserAuthConfig {
|
|
860
|
-
const authConfigFilePath = path.join(
|
|
863
|
+
const authConfigFilePath = path.join(
|
|
864
|
+
getGlobalWranglerConfigPath(),
|
|
865
|
+
USER_AUTH_CONFIG_FILE
|
|
866
|
+
);
|
|
861
867
|
const toml = parseTOML(readFileSync(authConfigFilePath));
|
|
862
868
|
return toml;
|
|
863
869
|
}
|
|
@@ -1042,7 +1048,7 @@ export async function logout(): Promise<void> {
|
|
|
1042
1048
|
},
|
|
1043
1049
|
});
|
|
1044
1050
|
await response.text(); // blank text? would be nice if it was something meaningful
|
|
1045
|
-
rmSync(path.join(
|
|
1051
|
+
rmSync(path.join(getGlobalWranglerConfigPath(), USER_AUTH_CONFIG_FILE));
|
|
1046
1052
|
logger.log(`Successfully logged out.`);
|
|
1047
1053
|
}
|
|
1048
1054
|
|