wrangler 0.0.0-e2f9bb2 → 0.0.0-e322475
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/Cloudflare_CA.pem +18 -0
- package/README.md +3 -1
- package/bin/wrangler.js +36 -4
- package/kv-asset-handler.js +1 -0
- package/package.json +15 -7
- package/pages/functions/buildPlugin.ts +13 -0
- package/pages/functions/buildWorker.ts +14 -1
- package/pages/functions/template-plugin.ts +3 -2
- package/src/__tests__/configuration.test.ts +652 -229
- package/src/__tests__/dev.test.tsx +372 -79
- package/src/__tests__/helpers/mock-cfetch.ts +2 -2
- package/src/__tests__/helpers/mock-console.ts +11 -1
- package/src/__tests__/helpers/mock-dialogs.ts +75 -11
- package/src/__tests__/helpers/mock-process.ts +24 -0
- package/src/__tests__/index.test.ts +81 -72
- package/src/__tests__/init.test.ts +1360 -410
- package/src/__tests__/jest.setup.ts +38 -0
- package/src/__tests__/kv.test.ts +218 -152
- package/src/__tests__/logger.test.ts +26 -0
- package/src/__tests__/package-manager.test.ts +154 -7
- package/src/__tests__/pages.test.ts +478 -53
- package/src/__tests__/parse.test.ts +8 -0
- package/src/__tests__/publish.test.ts +1614 -333
- package/src/__tests__/r2.test.ts +55 -36
- package/src/__tests__/secret.test.ts +39 -4
- package/src/__tests__/user.test.ts +1 -0
- package/src/__tests__/whoami.test.tsx +34 -0
- package/src/abort.d.ts +3 -0
- package/src/bundle-reporter.tsx +29 -0
- package/src/bundle.ts +42 -7
- package/src/cfetch/index.ts +21 -4
- package/src/cfetch/internal.ts +25 -20
- package/src/config/config.ts +11 -2
- package/src/config/environment.ts +41 -15
- package/src/config/index.ts +162 -0
- package/src/config/validation-helpers.ts +10 -1
- package/src/config/validation.ts +234 -78
- package/src/create-worker-preview.ts +32 -22
- package/src/create-worker-upload-form.ts +22 -8
- package/src/dev/dev.tsx +48 -67
- package/src/dev/local.tsx +16 -7
- package/src/dev/remote.tsx +57 -21
- package/src/dev/use-esbuild.ts +1 -7
- package/src/dialogs.tsx +48 -0
- package/src/durable.ts +102 -0
- package/src/entry.ts +71 -10
- package/src/generate-auth-url.ts +33 -0
- package/src/generate-random-state.ts +16 -0
- package/src/git-client.ts +42 -0
- package/src/index.tsx +619 -338
- package/src/inspect.ts +39 -0
- package/src/kv.ts +116 -27
- package/src/logger.ts +8 -1
- package/src/open-in-browser.ts +3 -12
- package/src/package-manager.ts +50 -3
- package/src/pages.tsx +434 -207
- package/src/parse.ts +44 -5
- package/src/proxy.ts +66 -44
- package/src/publish.ts +407 -137
- package/src/sites.tsx +103 -28
- package/src/user.tsx +35 -26
- package/src/whoami.tsx +3 -2
- package/src/worker.ts +10 -1
- package/src/zones.ts +73 -0
- package/templates/new-worker-scheduled.js +17 -0
- package/templates/new-worker-scheduled.ts +32 -0
- package/templates/new-worker.ts +16 -1
- package/templates/no-op-worker.js +10 -0
- package/templates/static-asset-facade.js +2 -6
- package/wrangler-dist/cli.js +46122 -32396
- package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
- package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
- package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
- package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
- package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
- package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
- package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
- package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
- package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
- package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
- package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
- package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
- package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
- package/vendor/wrangler-mime/CHANGELOG.md +0 -289
- package/vendor/wrangler-mime/LICENSE +0 -21
- package/vendor/wrangler-mime/Mime.js +0 -97
- package/vendor/wrangler-mime/README.md +0 -187
- package/vendor/wrangler-mime/cli.js +0 -46
- package/vendor/wrangler-mime/index.js +0 -4
- package/vendor/wrangler-mime/lite.js +0 -4
- package/vendor/wrangler-mime/package.json +0 -52
- package/vendor/wrangler-mime/types/other.js +0 -1
- package/vendor/wrangler-mime/types/standard.js +0 -1
package/src/sites.tsx
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
1
2
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import ignore from "ignore";
|
|
4
5
|
import xxhash from "xxhash-wasm";
|
|
5
6
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
createKVNamespace,
|
|
8
|
+
listKVNamespaceKeys,
|
|
9
|
+
listKVNamespaces,
|
|
10
|
+
putKVBulkKeyValue,
|
|
11
|
+
deleteKVBulkKeyValue,
|
|
11
12
|
} from "./kv";
|
|
12
13
|
import { logger } from "./logger";
|
|
13
14
|
import type { Config } from "./config";
|
|
@@ -75,14 +76,14 @@ async function createKVNamespaceIfNotAlreadyExisting(
|
|
|
75
76
|
) {
|
|
76
77
|
// check if it already exists
|
|
77
78
|
// TODO: this is super inefficient, should be made better
|
|
78
|
-
const namespaces = await
|
|
79
|
+
const namespaces = await listKVNamespaces(accountId);
|
|
79
80
|
const found = namespaces.find((x) => x.title === title);
|
|
80
81
|
if (found) {
|
|
81
82
|
return { created: false, id: found.id };
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
// else we make the namespace
|
|
85
|
-
const id = await
|
|
86
|
+
const id = await createKVNamespace(accountId, title);
|
|
86
87
|
logger.log(`🌀 Created namespace for Workers Site "${title}"`);
|
|
87
88
|
|
|
88
89
|
return {
|
|
@@ -103,7 +104,7 @@ async function createKVNamespaceIfNotAlreadyExisting(
|
|
|
103
104
|
* asset in the KV namespace.
|
|
104
105
|
*/
|
|
105
106
|
export async function syncAssets(
|
|
106
|
-
accountId: string,
|
|
107
|
+
accountId: string | undefined,
|
|
107
108
|
scriptName: string,
|
|
108
109
|
siteAssets: AssetPaths | undefined,
|
|
109
110
|
preview: boolean,
|
|
@@ -120,6 +121,7 @@ export async function syncAssets(
|
|
|
120
121
|
logger.log("(Note: doing a dry run, not uploading or deleting anything.)");
|
|
121
122
|
return { manifest: undefined, namespace: undefined };
|
|
122
123
|
}
|
|
124
|
+
assert(accountId, "Missing accountId");
|
|
123
125
|
|
|
124
126
|
const title = `__${scriptName}-workers_sites_assets${
|
|
125
127
|
preview ? "_preview" : ""
|
|
@@ -131,11 +133,17 @@ export async function syncAssets(
|
|
|
131
133
|
);
|
|
132
134
|
|
|
133
135
|
// let's get all the keys in this namespace
|
|
134
|
-
const namespaceKeysResponse = await
|
|
136
|
+
const namespaceKeysResponse = await listKVNamespaceKeys(accountId, namespace);
|
|
135
137
|
const namespaceKeys = new Set(namespaceKeysResponse.map((x) => x.name));
|
|
136
138
|
|
|
137
139
|
const manifest: Record<string, string> = {};
|
|
138
|
-
|
|
140
|
+
|
|
141
|
+
// A batch of uploads where each bucket has to be less than 100mb
|
|
142
|
+
const uploadBuckets: KeyValue[][] = [];
|
|
143
|
+
// The "live" bucket that we'll keep filling until it's just below 100mb
|
|
144
|
+
let uploadBucket: KeyValue[] = [];
|
|
145
|
+
// A size counter for the live bucket
|
|
146
|
+
let uploadBucketSize = 0;
|
|
139
147
|
|
|
140
148
|
const include = createPatternMatcher(siteAssets.includePatterns, false);
|
|
141
149
|
const exclude = createPatternMatcher(siteAssets.excludePatterns, true);
|
|
@@ -146,7 +154,7 @@ export async function syncAssets(
|
|
|
146
154
|
siteAssets.assetDirectory
|
|
147
155
|
);
|
|
148
156
|
for await (const absAssetFile of getFilesInFolder(assetDirectory)) {
|
|
149
|
-
const assetFile = path.relative(
|
|
157
|
+
const assetFile = path.relative(assetDirectory, absAssetFile);
|
|
150
158
|
if (!include(assetFile)) {
|
|
151
159
|
continue;
|
|
152
160
|
}
|
|
@@ -154,17 +162,32 @@ export async function syncAssets(
|
|
|
154
162
|
continue;
|
|
155
163
|
}
|
|
156
164
|
|
|
157
|
-
await validateAssetSize(absAssetFile, assetFile);
|
|
158
165
|
logger.log(`Reading ${assetFile}...`);
|
|
159
166
|
const content = await readFile(absAssetFile, "base64");
|
|
160
|
-
|
|
167
|
+
await validateAssetSize(absAssetFile, assetFile);
|
|
168
|
+
// while KV accepts files that are 25 MiB **before** b64 encoding
|
|
169
|
+
// the overall bucket size must be below 100 MiB **after** b64 encoding
|
|
170
|
+
const assetSize = Buffer.from(content).length;
|
|
161
171
|
const assetKey = hashAsset(hasher, assetFile, content);
|
|
162
172
|
validateAssetKey(assetKey);
|
|
163
173
|
|
|
164
174
|
// now put each of the files into kv
|
|
165
175
|
if (!namespaceKeys.has(assetKey)) {
|
|
166
176
|
logger.log(`Uploading as ${assetKey}...`);
|
|
167
|
-
|
|
177
|
+
|
|
178
|
+
// Check if adding this asset to the bucket would
|
|
179
|
+
// push it over the 100 MiB limit KV bulk API limit
|
|
180
|
+
if (uploadBucketSize + assetSize > 100 * 1024 * 1024) {
|
|
181
|
+
// If so, move the current bucket into the batch,
|
|
182
|
+
// and reset the counter/bucket
|
|
183
|
+
uploadBuckets.push(uploadBucket);
|
|
184
|
+
uploadBucketSize = 0;
|
|
185
|
+
uploadBucket = [];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Update the bucket and the size counter
|
|
189
|
+
uploadBucketSize += assetSize;
|
|
190
|
+
uploadBucket.push({
|
|
168
191
|
key: assetKey,
|
|
169
192
|
value: content,
|
|
170
193
|
base64: true,
|
|
@@ -173,27 +196,31 @@ export async function syncAssets(
|
|
|
173
196
|
logger.log(`Skipping - already uploaded.`);
|
|
174
197
|
}
|
|
175
198
|
|
|
176
|
-
//
|
|
199
|
+
// Remove the key from the set so we know what we've already uploaded
|
|
177
200
|
namespaceKeys.delete(assetKey);
|
|
178
|
-
|
|
201
|
+
|
|
202
|
+
// Prevent different manifest keys on windows
|
|
203
|
+
const manifestKey = urlSafe(path.relative(assetDirectory, absAssetFile));
|
|
204
|
+
manifest[manifestKey] = assetKey;
|
|
179
205
|
}
|
|
180
206
|
|
|
207
|
+
// Add the last (potentially only) bucket to the batch
|
|
208
|
+
uploadBuckets.push(uploadBucket);
|
|
209
|
+
|
|
181
210
|
// keys now contains all the files we're deleting
|
|
182
211
|
for (const key of namespaceKeys) {
|
|
183
212
|
logger.log(`Deleting ${key} from the asset store...`);
|
|
184
213
|
}
|
|
185
214
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
),
|
|
196
|
-
]);
|
|
215
|
+
// upload each bucket in parallel
|
|
216
|
+
const bucketsToPut = [];
|
|
217
|
+
for (const bucket of uploadBuckets) {
|
|
218
|
+
bucketsToPut.push(putKVBulkKeyValue(accountId, namespace, bucket));
|
|
219
|
+
}
|
|
220
|
+
await Promise.all(bucketsToPut);
|
|
221
|
+
|
|
222
|
+
// then delete all the assets that aren't used anymore
|
|
223
|
+
await deleteKVBulkKeyValue(accountId, namespace, Array.from(namespaceKeys));
|
|
197
224
|
|
|
198
225
|
logger.log("↗️ Done syncing assets");
|
|
199
226
|
|
|
@@ -212,10 +239,16 @@ function createPatternMatcher(
|
|
|
212
239
|
}
|
|
213
240
|
}
|
|
214
241
|
|
|
242
|
+
/**
|
|
243
|
+
* validate that the passed-in file is below 25 MiB
|
|
244
|
+
* **PRIOR** to base64 encoding. 25 MiB is a KV limit
|
|
245
|
+
* @param absFilePath
|
|
246
|
+
* @param relativeFilePath
|
|
247
|
+
*/
|
|
215
248
|
async function validateAssetSize(
|
|
216
249
|
absFilePath: string,
|
|
217
250
|
relativeFilePath: string
|
|
218
|
-
) {
|
|
251
|
+
): Promise<void> {
|
|
219
252
|
const { size } = await stat(absFilePath);
|
|
220
253
|
if (size > 25 * 1024 * 1024) {
|
|
221
254
|
throw new Error(
|
|
@@ -271,8 +304,50 @@ export interface AssetPaths {
|
|
|
271
304
|
* Uses the args (passed from the command line) if available,
|
|
272
305
|
* falling back to those defined in the config.
|
|
273
306
|
*
|
|
307
|
+
* (This function corresponds to --assets/config.assets)
|
|
308
|
+
*
|
|
274
309
|
*/
|
|
275
310
|
export function getAssetPaths(
|
|
311
|
+
config: Config,
|
|
312
|
+
assetDirectory: string | undefined
|
|
313
|
+
): AssetPaths | undefined {
|
|
314
|
+
const baseDirectory = path.resolve(
|
|
315
|
+
path.dirname(config.configPath ?? "wrangler.toml")
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
assetDirectory ??=
|
|
319
|
+
typeof config.assets === "string"
|
|
320
|
+
? config.assets
|
|
321
|
+
: config.assets !== undefined
|
|
322
|
+
? config.assets.bucket
|
|
323
|
+
: undefined;
|
|
324
|
+
|
|
325
|
+
const includePatterns =
|
|
326
|
+
(typeof config.assets !== "string" && config.assets?.include) || [];
|
|
327
|
+
|
|
328
|
+
const excludePatterns =
|
|
329
|
+
(typeof config.assets !== "string" && config.assets?.exclude) || [];
|
|
330
|
+
|
|
331
|
+
return assetDirectory
|
|
332
|
+
? {
|
|
333
|
+
baseDirectory,
|
|
334
|
+
assetDirectory,
|
|
335
|
+
includePatterns,
|
|
336
|
+
excludePatterns,
|
|
337
|
+
}
|
|
338
|
+
: undefined;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Get an object that describes what site assets to upload, if any.
|
|
343
|
+
*
|
|
344
|
+
* Uses the args (passed from the command line) if available,
|
|
345
|
+
* falling back to those defined in the config.
|
|
346
|
+
*
|
|
347
|
+
* (This function corresponds to --site/config.site)
|
|
348
|
+
*
|
|
349
|
+
*/
|
|
350
|
+
export function getSiteAssetPaths(
|
|
276
351
|
config: Config,
|
|
277
352
|
assetDirectory = config.site?.bucket,
|
|
278
353
|
includePatterns = config.site?.include ?? [],
|
package/src/user.tsx
CHANGED
|
@@ -214,6 +214,7 @@ import path from "node:path";
|
|
|
214
214
|
import url from "node:url";
|
|
215
215
|
import { TextEncoder } from "node:util";
|
|
216
216
|
import TOML from "@iarna/toml";
|
|
217
|
+
import { HostURL } from "@webcontainer/env";
|
|
217
218
|
import { render, Text } from "ink";
|
|
218
219
|
import SelectInput from "ink-select-input";
|
|
219
220
|
import Table from "ink-table";
|
|
@@ -222,6 +223,8 @@ import { fetch } from "undici";
|
|
|
222
223
|
import { getCloudflareApiBaseUrl } from "./cfetch";
|
|
223
224
|
import { purgeConfigCaches } from "./config-cache";
|
|
224
225
|
import { getEnvironmentVariableFactory } from "./environment-variables";
|
|
226
|
+
import { generateAuthUrl } from "./generate-auth-url";
|
|
227
|
+
import { generateRandomState } from "./generate-random-state";
|
|
225
228
|
import { logger } from "./logger";
|
|
226
229
|
import openInBrowser from "./open-in-browser";
|
|
227
230
|
import { parseTOML, readFileSync } from "./parse";
|
|
@@ -232,7 +235,7 @@ import type { Response } from "undici";
|
|
|
232
235
|
/**
|
|
233
236
|
* Try to read the API token from the environment.
|
|
234
237
|
*/
|
|
235
|
-
const getCloudflareAPITokenFromEnv = getEnvironmentVariableFactory({
|
|
238
|
+
export const getCloudflareAPITokenFromEnv = getEnvironmentVariableFactory({
|
|
236
239
|
variableName: "CLOUDFLARE_API_TOKEN",
|
|
237
240
|
deprecatedName: "CF_API_TOKEN",
|
|
238
241
|
});
|
|
@@ -339,9 +342,18 @@ export function validateScopeKeys(
|
|
|
339
342
|
const CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
|
|
340
343
|
const AUTH_URL = "https://dash.cloudflare.com/oauth2/auth";
|
|
341
344
|
const TOKEN_URL = "https://dash.cloudflare.com/oauth2/token";
|
|
342
|
-
const CALLBACK_URL = "http://localhost:8976/oauth/callback";
|
|
343
345
|
const REVOKE_URL = "https://dash.cloudflare.com/oauth2/revoke";
|
|
344
346
|
|
|
347
|
+
/**
|
|
348
|
+
* To allow OAuth callbacks in environments such as WebContainer we need to
|
|
349
|
+
* create a host URL which only resolves `localhost` to a WebContainer
|
|
350
|
+
* hostname if the process is running in a WebContainer. On local this will
|
|
351
|
+
* be a no-op and it leaves the URL unmodified.
|
|
352
|
+
*
|
|
353
|
+
* @see https://www.npmjs.com/package/@webcontainer/env
|
|
354
|
+
*/
|
|
355
|
+
const CALLBACK_URL = HostURL.parse("http://localhost:8976/oauth/callback").href;
|
|
356
|
+
|
|
345
357
|
let LocalState: State = getAuthTokens();
|
|
346
358
|
|
|
347
359
|
/**
|
|
@@ -581,7 +593,7 @@ const RECOMMENDED_STATE_LENGTH = 32;
|
|
|
581
593
|
/**
|
|
582
594
|
* Character set to generate code verifier defined in rfc7636.
|
|
583
595
|
*/
|
|
584
|
-
const PKCE_CHARSET =
|
|
596
|
+
export const PKCE_CHARSET =
|
|
585
597
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
586
598
|
|
|
587
599
|
/**
|
|
@@ -631,17 +643,14 @@ export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
|
|
|
631
643
|
stateQueryParam,
|
|
632
644
|
});
|
|
633
645
|
|
|
634
|
-
return (
|
|
635
|
-
AUTH_URL
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
`code_challenge=${encodeURIComponent(codeChallenge)}&` +
|
|
643
|
-
`code_challenge_method=S256`
|
|
644
|
-
);
|
|
646
|
+
return generateAuthUrl({
|
|
647
|
+
authUrl: AUTH_URL,
|
|
648
|
+
clientId: CLIENT_ID,
|
|
649
|
+
callbackUrl: CALLBACK_URL,
|
|
650
|
+
scopes,
|
|
651
|
+
stateQueryParam,
|
|
652
|
+
codeChallenge,
|
|
653
|
+
});
|
|
645
654
|
}
|
|
646
655
|
|
|
647
656
|
type TokenResponse =
|
|
@@ -853,18 +862,6 @@ async function generatePKCECodes(): Promise<PKCECodes> {
|
|
|
853
862
|
return { codeChallenge, codeVerifier };
|
|
854
863
|
}
|
|
855
864
|
|
|
856
|
-
/**
|
|
857
|
-
* Generates random state to be passed for anti-csrf.
|
|
858
|
-
*/
|
|
859
|
-
function generateRandomState(lengthOfState: number): string {
|
|
860
|
-
const output = new Uint32Array(lengthOfState);
|
|
861
|
-
// @ts-expect-error crypto's types aren't there yet
|
|
862
|
-
crypto.getRandomValues(output);
|
|
863
|
-
return Array.from(output)
|
|
864
|
-
.map((num: number) => PKCE_CHARSET[num % PKCE_CHARSET.length])
|
|
865
|
-
.join("");
|
|
866
|
-
}
|
|
867
|
-
|
|
868
865
|
/**
|
|
869
866
|
* Writes a a wrangler config file (auth credentials) to disk,
|
|
870
867
|
* and updates the user auth state with the new credentials.
|
|
@@ -1003,6 +1000,7 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
1003
1000
|
server.listen(8976);
|
|
1004
1001
|
});
|
|
1005
1002
|
|
|
1003
|
+
logger.log(`Opening a link in your default browser: ${urlToOpen}`);
|
|
1006
1004
|
await openInBrowser(urlToOpen);
|
|
1007
1005
|
|
|
1008
1006
|
return Promise.race([timerPromise, loginPromise]);
|
|
@@ -1191,3 +1189,14 @@ export async function requireAuth(config: {
|
|
|
1191
1189
|
|
|
1192
1190
|
return accountId;
|
|
1193
1191
|
}
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* Throw an error if there is no API token available.
|
|
1195
|
+
*/
|
|
1196
|
+
export function requireApiToken(): string {
|
|
1197
|
+
const authToken = getAPIToken();
|
|
1198
|
+
if (!authToken) {
|
|
1199
|
+
throw new Error("No API token found.");
|
|
1200
|
+
}
|
|
1201
|
+
return authToken;
|
|
1202
|
+
}
|
package/src/whoami.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import Table from "ink-table";
|
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { fetchListResult, fetchResult } from "./cfetch";
|
|
5
5
|
import { logger } from "./logger";
|
|
6
|
-
import { getAPIToken } from "./user";
|
|
6
|
+
import { getAPIToken, getCloudflareAPITokenFromEnv } from "./user";
|
|
7
7
|
|
|
8
8
|
export async function whoami() {
|
|
9
9
|
logger.log("Getting User settings...");
|
|
@@ -48,10 +48,11 @@ export interface UserInfo {
|
|
|
48
48
|
|
|
49
49
|
export async function getUserInfo(): Promise<UserInfo | undefined> {
|
|
50
50
|
const apiToken = getAPIToken();
|
|
51
|
+
const apiTokenFromEnv = getCloudflareAPITokenFromEnv();
|
|
51
52
|
return apiToken
|
|
52
53
|
? {
|
|
53
54
|
apiToken,
|
|
54
|
-
authType: "OAuth",
|
|
55
|
+
authType: apiTokenFromEnv ? "API" : "OAuth",
|
|
55
56
|
email: await getEmail(),
|
|
56
57
|
accounts: await getAccounts(),
|
|
57
58
|
}
|
package/src/worker.ts
CHANGED
|
@@ -105,6 +105,7 @@ interface CfDurableObject {
|
|
|
105
105
|
name: string;
|
|
106
106
|
class_name: string;
|
|
107
107
|
script_name?: string;
|
|
108
|
+
environment?: string;
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
interface CfR2Bucket {
|
|
@@ -112,6 +113,12 @@ interface CfR2Bucket {
|
|
|
112
113
|
bucket_name: string;
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
interface CfService {
|
|
117
|
+
binding: string;
|
|
118
|
+
service: string;
|
|
119
|
+
environment?: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
115
122
|
interface CfUnsafeBinding {
|
|
116
123
|
name: string;
|
|
117
124
|
type: string;
|
|
@@ -157,6 +164,7 @@ export interface CfWorkerInit {
|
|
|
157
164
|
data_blobs: CfDataBlobBindings | undefined;
|
|
158
165
|
durable_objects: { bindings: CfDurableObject[] } | undefined;
|
|
159
166
|
r2_buckets: CfR2Bucket[] | undefined;
|
|
167
|
+
services: CfService[] | undefined;
|
|
160
168
|
unsafe: CfUnsafeBinding[] | undefined;
|
|
161
169
|
};
|
|
162
170
|
migrations: CfDurableObjectMigrations | undefined;
|
|
@@ -168,5 +176,6 @@ export interface CfWorkerInit {
|
|
|
168
176
|
export interface CfWorkerContext {
|
|
169
177
|
env: string | undefined;
|
|
170
178
|
legacyEnv: boolean | undefined;
|
|
171
|
-
zone:
|
|
179
|
+
zone: string | undefined;
|
|
180
|
+
host: string | undefined;
|
|
172
181
|
}
|
package/src/zones.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { fetchListResult } from "./cfetch";
|
|
2
|
+
import type { Route } from "./config/environment";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* An object holding information about a zone for publishing.
|
|
6
|
+
*/
|
|
7
|
+
export interface Zone {
|
|
8
|
+
id: string;
|
|
9
|
+
host: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Try to compute the a zone ID and host name for one or more routes.
|
|
14
|
+
*
|
|
15
|
+
* When we're given a route, we do 2 things:
|
|
16
|
+
* - We try to extract a host from it
|
|
17
|
+
* - We try to get a zone id from the host
|
|
18
|
+
*/
|
|
19
|
+
export async function getZoneForRoute(route: Route): Promise<Zone | undefined> {
|
|
20
|
+
const host =
|
|
21
|
+
typeof route === "string"
|
|
22
|
+
? getHostFromUrl(route)
|
|
23
|
+
: typeof route === "object"
|
|
24
|
+
? "zone_name" in route
|
|
25
|
+
? getHostFromUrl(route.zone_name)
|
|
26
|
+
: getHostFromUrl(route.pattern)
|
|
27
|
+
: undefined;
|
|
28
|
+
const id =
|
|
29
|
+
typeof route === "object" && "zone_id" in route
|
|
30
|
+
? route.zone_id
|
|
31
|
+
: host
|
|
32
|
+
? await getZoneIdFromHost(host)
|
|
33
|
+
: undefined;
|
|
34
|
+
return id && host ? { id, host } : undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Given something that resembles a URL, try to extract a host from it.
|
|
39
|
+
*/
|
|
40
|
+
function getHostFromUrl(urlLike: string): string | undefined {
|
|
41
|
+
// strip leading * / *.
|
|
42
|
+
urlLike = urlLike.replace(/^\*(\.)?/g, "");
|
|
43
|
+
|
|
44
|
+
if (!(urlLike.startsWith("http://") || urlLike.startsWith("https://"))) {
|
|
45
|
+
urlLike = "http://" + urlLike;
|
|
46
|
+
}
|
|
47
|
+
return new URL(urlLike).host;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Given something that resembles a host, try to infer a zone id from it.
|
|
52
|
+
*
|
|
53
|
+
* It's hard to get a 'valid' domain from a string, so we don't even try to validate TLDs, etc.
|
|
54
|
+
* For each domain-like part of the host (e.g. w.x.y.z) try to get a zone id for it by
|
|
55
|
+
* lopping off subdomains until we get a hit from the API.
|
|
56
|
+
*/
|
|
57
|
+
export async function getZoneIdFromHost(host: string): Promise<string> {
|
|
58
|
+
const hostPieces = host.split(".");
|
|
59
|
+
|
|
60
|
+
while (hostPieces.length > 1) {
|
|
61
|
+
const zones = await fetchListResult<{ id: string }>(
|
|
62
|
+
`/zones`,
|
|
63
|
+
{},
|
|
64
|
+
new URLSearchParams({ name: hostPieces.join(".") })
|
|
65
|
+
);
|
|
66
|
+
if (zones.length > 0) {
|
|
67
|
+
return zones[0].id;
|
|
68
|
+
}
|
|
69
|
+
hostPieces.shift();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
throw new Error(`Could not find zone for ${host}`);
|
|
73
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Welcome to Cloudflare Workers! This is your first scheduled worker.
|
|
3
|
+
*
|
|
4
|
+
* - Run `wrangler dev --local` in your terminal to start a development server
|
|
5
|
+
* - Run `curl "http://localhost:8787/cdn-cgi/mf/scheduled"` to trigger the scheduled event
|
|
6
|
+
* - Go back to the console to see what your worker has logged
|
|
7
|
+
* - Update the Cron trigger in wrangler.toml (see https://developers.cloudflare.com/workers/wrangler/configuration/#triggers)
|
|
8
|
+
* - Run `wrangler publish --name my-worker` to publish your worker
|
|
9
|
+
*
|
|
10
|
+
* Learn more at https://developers.cloudflare.com/workers/runtime-apis/scheduled-event/
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
async scheduled(controller, env, ctx) {
|
|
15
|
+
console.log(`Hello World!`);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Welcome to Cloudflare Workers! This is your first scheduled worker.
|
|
3
|
+
*
|
|
4
|
+
* - Run `wrangler dev --local` in your terminal to start a development server
|
|
5
|
+
* - Run `curl "http://localhost:8787/cdn-cgi/mf/scheduled"` to trigger the scheduled event
|
|
6
|
+
* - Go back to the console to see what your worker has logged
|
|
7
|
+
* - Update the Cron trigger in wrangler.toml (see https://developers.cloudflare.com/workers/wrangler/configuration/#triggers)
|
|
8
|
+
* - Run `wrangler publish --name my-worker` to publish your worker
|
|
9
|
+
*
|
|
10
|
+
* Learn more at https://developers.cloudflare.com/workers/runtime-apis/scheduled-event/
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface Env {
|
|
14
|
+
// Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
|
|
15
|
+
// MY_KV_NAMESPACE: KVNamespace;
|
|
16
|
+
//
|
|
17
|
+
// Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
|
|
18
|
+
// MY_DURABLE_OBJECT: DurableObjectNamespace;
|
|
19
|
+
//
|
|
20
|
+
// Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
|
|
21
|
+
// MY_BUCKET: R2Bucket;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default {
|
|
25
|
+
async scheduled(
|
|
26
|
+
controller: ScheduledController,
|
|
27
|
+
env: Env,
|
|
28
|
+
ctx: ExecutionContext
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
console.log(`Hello World!`);
|
|
31
|
+
},
|
|
32
|
+
};
|
package/templates/new-worker.ts
CHANGED
|
@@ -8,8 +8,23 @@
|
|
|
8
8
|
* Learn more at https://developers.cloudflare.com/workers/
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
export interface Env {
|
|
12
|
+
// Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
|
|
13
|
+
// MY_KV_NAMESPACE: KVNamespace;
|
|
14
|
+
//
|
|
15
|
+
// Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
|
|
16
|
+
// MY_DURABLE_OBJECT: DurableObjectNamespace;
|
|
17
|
+
//
|
|
18
|
+
// Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
|
|
19
|
+
// MY_BUCKET: R2Bucket;
|
|
20
|
+
}
|
|
21
|
+
|
|
11
22
|
export default {
|
|
12
|
-
async fetch(
|
|
23
|
+
async fetch(
|
|
24
|
+
request: Request,
|
|
25
|
+
env: Env,
|
|
26
|
+
ctx: ExecutionContext
|
|
27
|
+
): Promise<Response> {
|
|
13
28
|
return new Response("Hello World!");
|
|
14
29
|
},
|
|
15
30
|
};
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
// DO NOT IMPORT THIS DIRECTLY
|
|
2
2
|
import worker from "__ENTRY_POINT__";
|
|
3
|
-
import { getAssetFromKV } from "
|
|
3
|
+
import { getAssetFromKV } from "__KV_ASSET_HANDLER__";
|
|
4
4
|
import manifest from "__STATIC_CONTENT_MANIFEST";
|
|
5
5
|
const ASSET_MANIFEST = JSON.parse(manifest);
|
|
6
6
|
|
|
7
|
-
// TODO: remove this
|
|
8
|
-
globalThis.__STATIC_CONTENT = undefined;
|
|
9
|
-
globalThis.__STATIC_CONTENT_MANIFEST = undefined;
|
|
10
|
-
|
|
11
7
|
export default {
|
|
12
8
|
async fetch(request, env, ctx) {
|
|
13
9
|
let options = {
|
|
@@ -39,7 +35,7 @@ export default {
|
|
|
39
35
|
} catch (e) {
|
|
40
36
|
console.error(e);
|
|
41
37
|
// if an error is thrown then serve from actual worker
|
|
42
|
-
return worker.fetch(request);
|
|
38
|
+
return worker.fetch(request, env, ctx);
|
|
43
39
|
// TODO: throw here if worker is not available
|
|
44
40
|
// (which implies it may be a service-worker)
|
|
45
41
|
}
|