wrangler 0.0.0-e7a0cd5b → 0.0.0-e7cf0caa
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/README.md +3 -3
- package/bin/wrangler.js +30 -10
- package/miniflare-dist/index.mjs +126 -74
- package/package.json +27 -19
- package/src/__tests__/api-dev.test.ts +65 -36
- package/src/__tests__/api-devregistry.test.ts +121 -0
- package/src/__tests__/api.test.ts +27 -5
- package/src/__tests__/configuration.test.ts +567 -86
- package/src/__tests__/d1/d1.test.ts +82 -0
- package/src/__tests__/d1/execute.test.ts +66 -0
- package/src/__tests__/d1/migrate.test.ts +157 -0
- package/src/__tests__/d1/splitter.test.ts +255 -0
- package/src/__tests__/delete.test.ts +91 -49
- package/src/__tests__/deployments.test.ts +330 -56
- package/src/__tests__/dev.test.tsx +198 -90
- package/src/__tests__/generate.test.ts +12 -10
- package/src/__tests__/guess-worker-format.test.ts +35 -0
- package/src/__tests__/helpers/end-event-loop.ts +6 -0
- package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
- package/src/__tests__/helpers/mock-console.ts +6 -0
- package/src/__tests__/helpers/mock-dialogs.ts +71 -85
- package/src/__tests__/helpers/mock-get-pages-upload-token.ts +25 -0
- package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -5
- package/src/__tests__/helpers/mock-known-routes.ts +7 -2
- package/src/__tests__/helpers/mock-kv.ts +29 -16
- package/src/__tests__/helpers/mock-oauth-flow.ts +103 -92
- package/src/__tests__/helpers/mock-process.ts +8 -13
- package/src/__tests__/helpers/mock-set-timeout.ts +16 -0
- package/src/__tests__/helpers/msw/blob-worker.cjs +19 -0
- package/src/__tests__/helpers/msw/handlers/deployments.ts +128 -27
- package/src/__tests__/helpers/msw/handlers/namespaces.ts +18 -41
- package/src/__tests__/helpers/msw/handlers/oauth.ts +13 -18
- package/src/__tests__/helpers/msw/handlers/r2.ts +14 -34
- package/src/__tests__/helpers/msw/handlers/script.ts +9 -28
- package/src/__tests__/helpers/msw/handlers/user.ts +13 -24
- package/src/__tests__/helpers/msw/handlers/zones.ts +6 -8
- package/src/__tests__/helpers/msw/index.ts +32 -1
- package/src/__tests__/helpers/msw/read-file-sync.js +61 -0
- package/src/__tests__/helpers/string-dynamic-values-matcher.ts +28 -0
- package/src/__tests__/helpers/worker-scripts/parent-worker.js +4 -1
- package/src/__tests__/index.test.ts +90 -72
- package/src/__tests__/init.test.ts +927 -631
- package/src/__tests__/jest.setup.ts +19 -42
- package/src/__tests__/kv.test.ts +355 -231
- package/src/__tests__/logger.test.ts +68 -2
- package/src/__tests__/logout.test.ts +47 -0
- package/src/__tests__/metrics.test.ts +94 -51
- package/src/__tests__/middleware.scheduled.test.ts +42 -32
- package/src/__tests__/middleware.test.ts +212 -122
- package/src/__tests__/mtls-certificates.test.ts +588 -0
- package/src/__tests__/pages/deployment-list.test.ts +80 -0
- package/src/__tests__/pages/functions-build.test.ts +452 -0
- package/src/__tests__/pages/pages.test.ts +81 -0
- package/src/__tests__/pages/project-create.test.ts +63 -0
- package/src/__tests__/pages/project-list.test.ts +110 -0
- package/src/__tests__/pages/project-upload.test.ts +500 -0
- package/src/__tests__/pages/publish.test.ts +2864 -0
- package/src/__tests__/pages-deployment-tail.test.ts +955 -0
- package/src/__tests__/parse.test.ts +106 -0
- package/src/__tests__/publish.test.ts +1615 -852
- package/src/__tests__/pubsub.test.ts +226 -100
- package/src/__tests__/queues.test.ts +238 -77
- package/src/__tests__/r2.test.ts +55 -57
- package/src/__tests__/secret.test.ts +179 -130
- package/src/__tests__/tail.test.ts +267 -109
- package/src/__tests__/test-old-node-version.js +3 -3
- package/src/__tests__/traverse-module-graph.test.ts +220 -0
- package/src/__tests__/tsconfig-sanity.ts +12 -0
- package/src/__tests__/tsconfig.json +8 -0
- package/src/__tests__/tsconfig.tsbuildinfo +1 -0
- package/src/__tests__/type-generation.test.ts +43 -26
- package/src/__tests__/user.test.ts +35 -72
- package/src/__tests__/whoami.test.tsx +8 -114
- package/src/__tests__/worker-namespace.test.ts +83 -69
- package/src/api/dev.ts +168 -60
- package/src/api/index.ts +10 -1
- package/src/api/mtls-certificate.ts +148 -0
- package/src/api/pages/create-worker-bundle-contents.ts +76 -0
- package/src/api/pages/index.ts +5 -0
- package/src/api/pages/publish.tsx +355 -0
- package/src/bundle.ts +263 -140
- package/src/cfetch/index.ts +0 -2
- package/src/cfetch/internal.ts +16 -21
- package/src/cli.ts +4 -4
- package/src/config/config.ts +14 -7
- package/src/config/environment.ts +79 -24
- package/src/config/index.ts +99 -38
- package/src/config/validation-helpers.ts +1 -1
- package/src/config/validation.ts +248 -19
- package/src/create-worker-preview.ts +19 -8
- package/src/create-worker-upload-form.ts +60 -22
- package/src/d1/backups.tsx +43 -42
- package/src/d1/constants.ts +2 -0
- package/src/d1/create.tsx +44 -30
- package/src/d1/{delete.tsx → delete.ts} +11 -14
- package/src/d1/execute.tsx +209 -151
- package/src/d1/index.ts +31 -7
- package/src/d1/list.tsx +22 -10
- package/src/d1/migrations/apply.tsx +212 -0
- package/src/d1/migrations/create.tsx +79 -0
- package/src/d1/migrations/helpers.ts +172 -0
- package/src/d1/migrations/index.ts +3 -0
- package/src/d1/migrations/list.tsx +95 -0
- package/src/d1/migrations/options.ts +23 -0
- package/src/d1/options.ts +12 -2
- package/src/d1/splitter.ts +161 -0
- package/src/d1/{types.tsx → types.ts} +11 -0
- package/src/d1/utils.ts +12 -2
- package/src/delete.ts +8 -16
- package/src/deployments.ts +340 -46
- package/src/deprecated/index.ts +8 -302
- package/src/dev/dev.tsx +57 -44
- package/src/dev/local.tsx +294 -91
- package/src/dev/remote.tsx +32 -3
- package/src/dev/start-server.ts +89 -68
- package/src/dev/use-esbuild.ts +17 -18
- package/src/dev/validate-dev-props.ts +9 -0
- package/src/{dev-registry.tsx → dev-registry.ts} +2 -1
- package/src/dev.tsx +178 -118
- package/src/dialogs.ts +136 -0
- package/src/dispatch-namespace.ts +21 -17
- package/src/docs/index.ts +97 -0
- package/src/entry.ts +19 -11
- package/src/environment-variables/factory.ts +88 -0
- package/src/environment-variables/misc-variables.ts +30 -0
- package/src/generate/index.ts +298 -0
- package/src/git-client.ts +15 -4
- package/src/{index.tsx → index.ts} +137 -63
- package/src/init.ts +249 -187
- package/src/inspect.ts +16 -1
- package/src/jest.d.ts +4 -0
- package/src/kv/index.ts +15 -17
- package/src/logger.ts +32 -5
- package/src/metrics/metrics-config.ts +3 -3
- package/src/metrics/send-event.ts +6 -1
- package/src/miniflare-cli/assets.ts +64 -28
- package/src/miniflare-cli/index.ts +4 -7
- package/src/miniflare-cli/tsconfig.json +9 -0
- package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
- package/src/miniflare-cli/types.ts +11 -0
- package/src/module-collection.ts +91 -25
- package/src/mtls-certificate/cli.ts +155 -0
- package/src/pages/build.ts +410 -0
- package/src/pages/buildFunctions.ts +140 -0
- package/src/pages/deployment-tails.ts +281 -0
- package/src/pages/deployments.tsx +11 -33
- package/src/pages/{dev.tsx → dev.ts} +147 -124
- package/src/pages/errors.ts +1 -0
- package/src/pages/functions/buildPlugin.ts +72 -69
- package/src/pages/functions/buildWorker.ts +249 -114
- package/src/pages/functions/tsconfig.json +8 -0
- package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
- package/src/pages/{functions.tsx → functions.ts} +8 -4
- package/src/pages/{index.tsx → index.ts} +11 -3
- package/src/pages/projects.tsx +12 -22
- package/src/pages/prompt-select-project.tsx +31 -0
- package/src/pages/publish.tsx +45 -214
- package/src/pages/types.ts +6 -9
- package/src/pages/upload.tsx +58 -14
- package/src/pages/utils.ts +14 -3
- package/src/parse.ts +23 -1
- package/src/paths.ts +1 -1
- package/src/proxy.ts +5 -0
- package/src/publish/index.ts +20 -16
- package/src/publish/publish.ts +72 -47
- package/src/pubsub/{pubsub-commands.tsx → pubsub-commands.ts} +19 -20
- package/src/queues/cli/commands/consumer/add.ts +24 -24
- package/src/queues/cli/commands/consumer/index.ts +3 -6
- package/src/queues/cli/commands/consumer/remove.ts +11 -18
- package/src/queues/cli/commands/create.ts +8 -8
- package/src/queues/cli/commands/delete.ts +8 -8
- package/src/queues/cli/commands/index.ts +6 -4
- package/src/queues/cli/commands/list.ts +8 -8
- package/src/queues/client.ts +1 -0
- package/src/queues/utils.ts +18 -0
- package/src/r2/constants.ts +4 -0
- package/src/r2/helpers.ts +1 -0
- package/src/r2/index.ts +40 -28
- package/src/secret/index.ts +14 -17
- package/src/{sites.tsx → sites.ts} +1 -1
- package/src/tail/createTail.ts +94 -3
- package/src/tail/filters.ts +1 -1
- package/src/tail/index.ts +8 -11
- package/src/tail/printing.ts +15 -0
- package/src/traverse-module-graph.ts +53 -0
- package/src/tsconfig-sanity.ts +16 -0
- package/src/type-generation.ts +79 -57
- package/src/user/access.ts +0 -1
- package/src/user/auth-variables.ts +113 -0
- package/src/user/choose-account.tsx +1 -31
- package/src/user/index.ts +0 -1
- package/src/user/{user.tsx → user.ts} +123 -82
- package/src/utils/render.ts +93 -0
- package/src/{whoami.tsx → whoami.ts} +37 -71
- package/src/worker.ts +35 -3
- package/src/yargs-types.ts +22 -5
- package/templates/__tests__/tsconfig-sanity.ts +12 -0
- package/templates/__tests__/tsconfig.json +8 -0
- package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
- package/templates/checked-fetch.js +9 -3
- package/templates/d1-beta-facade.js +95 -25
- package/templates/facade.d.ts +14 -0
- package/templates/first-party-worker-module-facade.ts +4 -3
- package/templates/format-dev-errors.ts +8 -6
- package/templates/gitignore +1 -0
- package/templates/init-tests/test-jest-new-worker.js +3 -5
- package/templates/init-tests/test-vitest-new-worker.js +3 -5
- package/templates/init-tests/test-vitest-new-worker.ts +25 -0
- package/templates/middleware/loader-modules.ts +2 -2
- package/templates/middleware/loader-sw.ts +51 -31
- package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
- package/templates/new-worker.ts +3 -0
- package/templates/pages-dev-pipeline.ts +4 -1
- package/templates/pages-shim.ts +4 -1
- package/templates/pages-template-plugin.ts +19 -10
- package/templates/serve-static-assets.ts +17 -14
- package/templates/service-bindings-module-facade.js +1 -0
- package/templates/tsconfig-sanity.ts +11 -0
- package/templates/tsconfig.init.json +105 -0
- package/templates/tsconfig.json +5 -103
- package/templates/tsconfig.tsbuildinfo +1 -0
- package/wrangler-dist/cli.d.ts +826 -77
- package/wrangler-dist/cli.js +130734 -140838
- package/wrangler-dist/wasm-sync.wasm +0 -0
- package/src/__tests__/api-devregistry.test.js +0 -57
- package/src/__tests__/d1.test.ts +0 -72
- package/src/__tests__/dialogs.test.tsx +0 -40
- package/src/__tests__/helpers/mock-cfetch.ts +0 -333
- package/src/__tests__/pages.test.ts +0 -2223
- package/src/dialogs.tsx +0 -163
- package/src/environment-variables.ts +0 -50
- package/src/pages/build.tsx +0 -238
- package/src/user/env-vars.ts +0 -46
- package/templates/experimental-local-cache-stubs.js +0 -27
- /package/src/{bundle-reporter.tsx → bundle-reporter.ts} +0 -0
- /package/src/dev/{get-local-persistence-path.tsx → get-local-persistence-path.ts} +0 -0
- /package/src/pages/{hash.tsx → hash.ts} +0 -0
|
@@ -214,28 +214,61 @@ import url from "node:url";
|
|
|
214
214
|
import { TextEncoder } from "node:util";
|
|
215
215
|
import TOML from "@iarna/toml";
|
|
216
216
|
import { HostURL } from "@webcontainer/env";
|
|
217
|
-
import { render } from "ink";
|
|
218
|
-
import Table from "ink-table";
|
|
219
|
-
import React from "react";
|
|
220
217
|
import { fetch } from "undici";
|
|
221
218
|
import {
|
|
222
219
|
getConfigCache,
|
|
223
220
|
purgeConfigCaches,
|
|
224
221
|
saveToConfigCache,
|
|
225
222
|
} from "../config-cache";
|
|
223
|
+
import { NoDefaultValueProvided, select } from "../dialogs";
|
|
226
224
|
import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
|
|
227
225
|
import { CI } from "../is-ci";
|
|
228
226
|
import isInteractive from "../is-interactive";
|
|
229
227
|
import { logger } from "../logger";
|
|
230
228
|
import openInBrowser from "../open-in-browser";
|
|
231
229
|
import { parseTOML, readFileSync } from "../parse";
|
|
232
|
-
import {
|
|
233
|
-
import {
|
|
230
|
+
import { domainUsesAccess } from "./access";
|
|
231
|
+
import {
|
|
232
|
+
getAuthDomainFromEnv,
|
|
233
|
+
getAuthUrlFromEnv,
|
|
234
|
+
getClientIdFromEnv,
|
|
235
|
+
getCloudflareAccessToken,
|
|
236
|
+
getCloudflareAPITokenFromEnv,
|
|
237
|
+
getCloudflareGlobalAuthEmailFromEnv,
|
|
238
|
+
getCloudflareGlobalAuthKeyFromEnv,
|
|
239
|
+
getRevokeUrlFromEnv,
|
|
240
|
+
getTokenUrlFromEnv,
|
|
241
|
+
} from "./auth-variables";
|
|
242
|
+
import { getAccountChoices } from "./choose-account";
|
|
234
243
|
import { generateAuthUrl } from "./generate-auth-url";
|
|
235
244
|
import { generateRandomState } from "./generate-random-state";
|
|
236
|
-
import type {
|
|
245
|
+
import type { ChooseAccountItem } from "./choose-account";
|
|
237
246
|
import type { ParsedUrlQuery } from "node:querystring";
|
|
238
247
|
|
|
248
|
+
export type ApiCredentials =
|
|
249
|
+
| {
|
|
250
|
+
apiToken: string;
|
|
251
|
+
}
|
|
252
|
+
| {
|
|
253
|
+
authKey: string;
|
|
254
|
+
authEmail: string;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Try to read an API token or Global Auth from the environment.
|
|
259
|
+
*/
|
|
260
|
+
export function getAuthFromEnv(): ApiCredentials | undefined {
|
|
261
|
+
const globalApiKey = getCloudflareGlobalAuthKeyFromEnv();
|
|
262
|
+
const globalApiEmail = getCloudflareGlobalAuthEmailFromEnv();
|
|
263
|
+
const apiToken = getCloudflareAPITokenFromEnv();
|
|
264
|
+
|
|
265
|
+
if (globalApiKey && globalApiEmail) {
|
|
266
|
+
return { authKey: globalApiKey, authEmail: globalApiEmail };
|
|
267
|
+
} else if (apiToken) {
|
|
268
|
+
return { apiToken };
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
239
272
|
/**
|
|
240
273
|
* An implementation of rfc6749#section-4.1 and rfc7636.
|
|
241
274
|
*/
|
|
@@ -264,7 +297,7 @@ interface AuthTokens {
|
|
|
264
297
|
accessToken?: AccessToken;
|
|
265
298
|
refreshToken?: RefreshToken;
|
|
266
299
|
scopes?: Scope[];
|
|
267
|
-
/** @deprecated - this field was only provided by the deprecated `
|
|
300
|
+
/** @deprecated - this field was only provided by the deprecated v1 `wrangler config` command. */
|
|
268
301
|
apiToken?: string;
|
|
269
302
|
}
|
|
270
303
|
|
|
@@ -282,7 +315,7 @@ export interface UserAuthConfig {
|
|
|
282
315
|
refresh_token?: string;
|
|
283
316
|
expiration_time?: string;
|
|
284
317
|
scopes?: string[];
|
|
285
|
-
/** @deprecated - this field was only provided by the deprecated `
|
|
318
|
+
/** @deprecated - this field was only provided by the deprecated v1 `wrangler config` command. */
|
|
286
319
|
api_token?: string;
|
|
287
320
|
}
|
|
288
321
|
|
|
@@ -313,6 +346,7 @@ const Scopes = {
|
|
|
313
346
|
"pages:write":
|
|
314
347
|
"See and change Cloudflare Pages projects, settings and deployments.",
|
|
315
348
|
"zone:read": "Grants read level access to account zone.",
|
|
349
|
+
"ssl_certs:write": "See and manage mTLS certificates for your account",
|
|
316
350
|
} as const;
|
|
317
351
|
|
|
318
352
|
/**
|
|
@@ -330,11 +364,6 @@ export function validateScopeKeys(
|
|
|
330
364
|
return scopes.every((scope) => scope in Scopes);
|
|
331
365
|
}
|
|
332
366
|
|
|
333
|
-
const CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
|
|
334
|
-
const AUTH_URL = "https://dash.cloudflare.com/oauth2/auth";
|
|
335
|
-
const TOKEN_URL = "https://dash.cloudflare.com/oauth2/token";
|
|
336
|
-
const REVOKE_URL = "https://dash.cloudflare.com/oauth2/revoke";
|
|
337
|
-
|
|
338
367
|
/**
|
|
339
368
|
* To allow OAuth callbacks in environments such as WebContainer we need to
|
|
340
369
|
* create a host URL which only resolves `localhost` to a WebContainer
|
|
@@ -374,7 +403,7 @@ function getAuthTokens(config?: UserAuthConfig): AuthTokens | undefined {
|
|
|
374
403
|
};
|
|
375
404
|
} else if (api_token) {
|
|
376
405
|
logger.warn(
|
|
377
|
-
"It looks like you have used Wrangler
|
|
406
|
+
"It looks like you have used Wrangler v1's `config` command to login with an API token.\n" +
|
|
378
407
|
"This is no longer supported in the current version of Wrangler.\n" +
|
|
379
408
|
"If you wish to authenticate via an API token then please set the `CLOUDFLARE_API_TOKEN` environment variable."
|
|
380
409
|
);
|
|
@@ -386,7 +415,7 @@ function getAuthTokens(config?: UserAuthConfig): AuthTokens | undefined {
|
|
|
386
415
|
}
|
|
387
416
|
|
|
388
417
|
/**
|
|
389
|
-
* Run the
|
|
418
|
+
* Run the initialization of the auth state, in the case that something changed.
|
|
390
419
|
*
|
|
391
420
|
* This runs automatically whenever `writeAuthConfigFile` is run, so generally
|
|
392
421
|
* you won't need to call it yourself.
|
|
@@ -625,8 +654,8 @@ export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
|
|
|
625
654
|
});
|
|
626
655
|
|
|
627
656
|
return generateAuthUrl({
|
|
628
|
-
authUrl:
|
|
629
|
-
clientId:
|
|
657
|
+
authUrl: getAuthUrlFromEnv(),
|
|
658
|
+
clientId: getClientIdFromEnv(),
|
|
630
659
|
callbackUrl: CALLBACK_URL,
|
|
631
660
|
scopes,
|
|
632
661
|
stateQueryParam,
|
|
@@ -653,19 +682,14 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
|
|
|
653
682
|
logger.warn("No refresh token is present.");
|
|
654
683
|
}
|
|
655
684
|
|
|
656
|
-
const
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
const response = await fetch(TOKEN_URL, {
|
|
662
|
-
method: "POST",
|
|
663
|
-
body,
|
|
664
|
-
headers: {
|
|
665
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
666
|
-
},
|
|
685
|
+
const params = new URLSearchParams({
|
|
686
|
+
grant_type: "refresh_token",
|
|
687
|
+
refresh_token: LocalState.refreshToken?.value ?? "",
|
|
688
|
+
client_id: getClientIdFromEnv(),
|
|
667
689
|
});
|
|
668
690
|
|
|
691
|
+
const response = await fetchAuthToken(params);
|
|
692
|
+
|
|
669
693
|
if (response.status >= 400) {
|
|
670
694
|
let tokenExchangeResErr = undefined;
|
|
671
695
|
|
|
@@ -743,20 +767,15 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
743
767
|
logger.warn("No authorization grant code is being passed.");
|
|
744
768
|
}
|
|
745
769
|
|
|
746
|
-
const
|
|
747
|
-
`
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
const response = await fetch(TOKEN_URL, {
|
|
754
|
-
method: "POST",
|
|
755
|
-
body,
|
|
756
|
-
headers: {
|
|
757
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
758
|
-
},
|
|
770
|
+
const params = new URLSearchParams({
|
|
771
|
+
grant_type: `authorization_code`,
|
|
772
|
+
code: authorizationCode ?? "",
|
|
773
|
+
redirect_uri: CALLBACK_URL,
|
|
774
|
+
client_id: getClientIdFromEnv(),
|
|
775
|
+
code_verifier: codeVerifier,
|
|
759
776
|
});
|
|
777
|
+
|
|
778
|
+
const response = await fetchAuthToken(params);
|
|
760
779
|
if (!response.ok) {
|
|
761
780
|
const { error } = (await response.json()) as { error: string };
|
|
762
781
|
// .catch((_) => ({ error: "invalid_json" }));
|
|
@@ -875,6 +894,7 @@ export function readAuthConfigFile(): UserAuthConfig {
|
|
|
875
894
|
|
|
876
895
|
type LoginProps = {
|
|
877
896
|
scopes?: Scope[];
|
|
897
|
+
browser: boolean;
|
|
878
898
|
};
|
|
879
899
|
|
|
880
900
|
export async function loginOrRefreshIfRequired(): Promise<boolean> {
|
|
@@ -901,7 +921,9 @@ export async function loginOrRefreshIfRequired(): Promise<boolean> {
|
|
|
901
921
|
}
|
|
902
922
|
}
|
|
903
923
|
|
|
904
|
-
export async function login(
|
|
924
|
+
export async function login(
|
|
925
|
+
props: LoginProps = { browser: true }
|
|
926
|
+
): Promise<boolean> {
|
|
905
927
|
logger.log("Attempting to login via OAuth...");
|
|
906
928
|
const urlToOpen = await getAuthURL(props?.scopes);
|
|
907
929
|
let server: http.Server;
|
|
@@ -914,7 +936,7 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
914
936
|
server.close();
|
|
915
937
|
clearTimeout(loginTimeoutHandle);
|
|
916
938
|
resolve(false);
|
|
917
|
-
},
|
|
939
|
+
}, 120000); // wait for 120 seconds for the user to authorize
|
|
918
940
|
});
|
|
919
941
|
|
|
920
942
|
const loginPromise = new Promise<boolean>((resolve, reject) => {
|
|
@@ -986,9 +1008,12 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
986
1008
|
|
|
987
1009
|
server.listen(8976);
|
|
988
1010
|
});
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1011
|
+
if (props?.browser) {
|
|
1012
|
+
logger.log(`Opening a link in your default browser: ${urlToOpen}`);
|
|
1013
|
+
await openInBrowser(urlToOpen);
|
|
1014
|
+
} else {
|
|
1015
|
+
logger.log(`Visit this link to authenticate: ${urlToOpen}`);
|
|
1016
|
+
}
|
|
992
1017
|
|
|
993
1018
|
return Promise.race([timerPromise, loginPromise]);
|
|
994
1019
|
}
|
|
@@ -1032,11 +1057,11 @@ export async function logout(): Promise<void> {
|
|
|
1032
1057
|
}
|
|
1033
1058
|
|
|
1034
1059
|
const body =
|
|
1035
|
-
`client_id=${encodeURIComponent(
|
|
1060
|
+
`client_id=${encodeURIComponent(getClientIdFromEnv())}&` +
|
|
1036
1061
|
`token_type_hint=refresh_token&` +
|
|
1037
1062
|
`token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
|
|
1038
1063
|
|
|
1039
|
-
const response = await fetch(
|
|
1064
|
+
const response = await fetch(getRevokeUrlFromEnv(), {
|
|
1040
1065
|
method: "POST",
|
|
1041
1066
|
body,
|
|
1042
1067
|
headers: {
|
|
@@ -1049,11 +1074,11 @@ export async function logout(): Promise<void> {
|
|
|
1049
1074
|
);
|
|
1050
1075
|
}
|
|
1051
1076
|
const body =
|
|
1052
|
-
`client_id=${encodeURIComponent(
|
|
1077
|
+
`client_id=${encodeURIComponent(getClientIdFromEnv())}&` +
|
|
1053
1078
|
`token_type_hint=refresh_token&` +
|
|
1054
1079
|
`token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
|
|
1055
1080
|
|
|
1056
|
-
const response = await fetch(
|
|
1081
|
+
const response = await fetch(getRevokeUrlFromEnv(), {
|
|
1057
1082
|
method: "POST",
|
|
1058
1083
|
body,
|
|
1059
1084
|
headers: {
|
|
@@ -1071,8 +1096,7 @@ export function listScopes(message = "💁 Available scopes:"): void {
|
|
|
1071
1096
|
Scope: scope,
|
|
1072
1097
|
Description: Scopes[scope],
|
|
1073
1098
|
}));
|
|
1074
|
-
|
|
1075
|
-
unmount();
|
|
1099
|
+
logger.table(data);
|
|
1076
1100
|
// TODO: maybe a good idea to show usage here
|
|
1077
1101
|
}
|
|
1078
1102
|
|
|
@@ -1091,36 +1115,32 @@ export async function getAccountId(): Promise<string | undefined> {
|
|
|
1091
1115
|
return accounts[0].id;
|
|
1092
1116
|
}
|
|
1093
1117
|
|
|
1094
|
-
|
|
1095
|
-
const
|
|
1096
|
-
(
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1118
|
+
try {
|
|
1119
|
+
const accountID = await select("Select an account", {
|
|
1120
|
+
choices: accounts.map((account) => ({
|
|
1121
|
+
title: account.name,
|
|
1122
|
+
value: account.id,
|
|
1123
|
+
})),
|
|
1124
|
+
});
|
|
1125
|
+
const account = accounts.find(
|
|
1126
|
+
(a) => a.id === accountID
|
|
1127
|
+
) as ChooseAccountItem;
|
|
1128
|
+
saveAccountToCache({ id: account.id, name: account.name });
|
|
1129
|
+
return accountID;
|
|
1130
|
+
} catch (e) {
|
|
1131
|
+
// Did we try to select an account in CI or a non-interactive terminal?
|
|
1132
|
+
if (e instanceof NoDefaultValueProvided) {
|
|
1133
|
+
throw new Error(
|
|
1134
|
+
`More than one account available but unable to select one in non-interactive mode.
|
|
1135
|
+
Please set the appropriate \`account_id\` in your \`wrangler.toml\` file.
|
|
1136
|
+
Available accounts are (\`<name>\`: \`<account_id>\`):
|
|
1137
|
+
${accounts
|
|
1138
|
+
.map((account) => ` \`${account.name}\`: \`${account.id}\``)
|
|
1139
|
+
.join("\n")}`
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
throw e;
|
|
1114
1143
|
}
|
|
1115
|
-
|
|
1116
|
-
throw new Error(
|
|
1117
|
-
"More than one account available but unable to select one in non-interactive mode.\n" +
|
|
1118
|
-
`Please set the appropriate \`account_id\` in your \`wrangler.toml\` file.\n` +
|
|
1119
|
-
`Available accounts are ("<name>" - "<id>"):\n` +
|
|
1120
|
-
accounts
|
|
1121
|
-
.map((account) => ` "${account.name}" - "${account.id}")`)
|
|
1122
|
-
.join("\n")
|
|
1123
|
-
);
|
|
1124
1144
|
}
|
|
1125
1145
|
|
|
1126
1146
|
/**
|
|
@@ -1133,7 +1153,7 @@ export async function requireAuth(config: {
|
|
|
1133
1153
|
if (!loggedIn) {
|
|
1134
1154
|
if (!isInteractive() || CI.isCI()) {
|
|
1135
1155
|
throw new Error(
|
|
1136
|
-
"In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/
|
|
1156
|
+
"In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/fundamentals/api/get-started/create-token/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."
|
|
1137
1157
|
);
|
|
1138
1158
|
} else {
|
|
1139
1159
|
// didn't login, let's just quit
|
|
@@ -1190,3 +1210,24 @@ export function getAccountFromCache():
|
|
|
1190
1210
|
export function getScopes(): Scope[] | undefined {
|
|
1191
1211
|
return LocalState.scopes;
|
|
1192
1212
|
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* Make a request to the Cloudflare OAuth endpoint to get a token.
|
|
1216
|
+
*
|
|
1217
|
+
* Note that the `body` of the POST request is form-urlencoded so
|
|
1218
|
+
* can be represented by a URLSearchParams object.
|
|
1219
|
+
*/
|
|
1220
|
+
async function fetchAuthToken(body: URLSearchParams) {
|
|
1221
|
+
const headers: Record<string, string> = {
|
|
1222
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1223
|
+
};
|
|
1224
|
+
if (await domainUsesAccess(getAuthDomainFromEnv())) {
|
|
1225
|
+
// We are trying to access the staging API so we need an "access token".
|
|
1226
|
+
headers["Cookie"] = `CF_Authorization=${await getCloudflareAccessToken()}`;
|
|
1227
|
+
}
|
|
1228
|
+
return await fetch(getTokenUrlFromEnv(), {
|
|
1229
|
+
method: "POST",
|
|
1230
|
+
body: body.toString(),
|
|
1231
|
+
headers,
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { render as inkRender } from "ink";
|
|
3
|
+
import type { ReactElement } from "react";
|
|
4
|
+
import type { WriteStream } from "tty";
|
|
5
|
+
|
|
6
|
+
export function renderToString(tree: ReactElement): string {
|
|
7
|
+
const { output, cleanup } = render(tree);
|
|
8
|
+
cleanup();
|
|
9
|
+
return output;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// The code below is mostly copied from ink-render-string.
|
|
13
|
+
// See https://github.com/zhanwang626/ink-render-string.git.
|
|
14
|
+
|
|
15
|
+
// MIT License
|
|
16
|
+
|
|
17
|
+
// Copyright (c) 2021 Zhan Wang
|
|
18
|
+
|
|
19
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
20
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
21
|
+
// in the Software without restriction, including without limitation the rights
|
|
22
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
23
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
24
|
+
// furnished to do so, subject to the following conditions:
|
|
25
|
+
|
|
26
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
27
|
+
// copies or substantial portions of the Software.
|
|
28
|
+
|
|
29
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
30
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
31
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
32
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
33
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
34
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
35
|
+
// SOFTWARE.
|
|
36
|
+
|
|
37
|
+
interface Instance {
|
|
38
|
+
output: string;
|
|
39
|
+
unmount: () => void;
|
|
40
|
+
cleanup: () => void;
|
|
41
|
+
stdout: OutputStream;
|
|
42
|
+
stderr: OutputStream;
|
|
43
|
+
frames: string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const LASTFRAME_UNDEFINED = "value of stdout.lastframe() is undefined";
|
|
47
|
+
|
|
48
|
+
export class OutputStream extends EventEmitter {
|
|
49
|
+
readonly frames: string[] = [];
|
|
50
|
+
private _lastFrame?: string;
|
|
51
|
+
|
|
52
|
+
get columns(): number {
|
|
53
|
+
return this._originalStream.columns;
|
|
54
|
+
}
|
|
55
|
+
get rows(): number {
|
|
56
|
+
return this._originalStream.rows;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
constructor(private _originalStream: WriteStream) {
|
|
60
|
+
super();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
write = (frame: string) => {
|
|
64
|
+
this.frames.push(frame);
|
|
65
|
+
this._lastFrame = frame;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
lastFrame = () => {
|
|
69
|
+
return this._lastFrame;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const render = (tree: ReactElement): Instance => {
|
|
74
|
+
const stdout = new OutputStream(process.stdout);
|
|
75
|
+
const stderr = new OutputStream(process.stderr);
|
|
76
|
+
|
|
77
|
+
const instance = inkRender(tree, {
|
|
78
|
+
stdout: stdout as unknown as WriteStream,
|
|
79
|
+
stderr: stderr as unknown as WriteStream,
|
|
80
|
+
debug: true,
|
|
81
|
+
exitOnCtrlC: false,
|
|
82
|
+
patchConsole: false,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
output: stdout.lastFrame() || LASTFRAME_UNDEFINED,
|
|
87
|
+
stdout,
|
|
88
|
+
stderr,
|
|
89
|
+
cleanup: instance.cleanup,
|
|
90
|
+
unmount: instance.unmount,
|
|
91
|
+
frames: stdout.frames,
|
|
92
|
+
};
|
|
93
|
+
};
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import Table from "ink-table";
|
|
3
|
-
import React, { Fragment } from "react";
|
|
1
|
+
import chalk from "chalk";
|
|
4
2
|
import { fetchListResult, fetchResult } from "./cfetch";
|
|
5
3
|
import { logger } from "./logger";
|
|
6
4
|
import { getAPIToken, getAuthFromEnv, getScopes } from "./user";
|
|
@@ -8,82 +6,50 @@ import { getAPIToken, getAuthFromEnv, getScopes } from "./user";
|
|
|
8
6
|
export async function whoami() {
|
|
9
7
|
logger.log("Getting User settings...");
|
|
10
8
|
const user = await getUserInfo();
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return user ? (
|
|
17
|
-
<>
|
|
18
|
-
<Email tokenType={user.authType} email={user.email}></Email>
|
|
19
|
-
<Accounts accounts={user.accounts}></Accounts>
|
|
20
|
-
<Permissions
|
|
21
|
-
tokenType={user.authType}
|
|
22
|
-
tokenPermissions={user.tokenPermissions}
|
|
23
|
-
/>
|
|
24
|
-
</>
|
|
25
|
-
) : (
|
|
26
|
-
<Text>You are not authenticated. Please run `wrangler login`.</Text>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
9
|
+
if (user === undefined) {
|
|
10
|
+
return void logger.log(
|
|
11
|
+
"You are not authenticated. Please run `wrangler login`."
|
|
12
|
+
);
|
|
13
|
+
}
|
|
29
14
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
15
|
+
if (user.email !== undefined) {
|
|
16
|
+
logger.log(
|
|
17
|
+
`👋 You are logged in with an ${
|
|
18
|
+
user.authType
|
|
19
|
+
}, associated with the email ${chalk.blue(user.email)}!`
|
|
20
|
+
);
|
|
21
|
+
} else {
|
|
22
|
+
logger.log(
|
|
23
|
+
`👋 You are logged in with an ${user.authType}. Unable to retrieve email for this user. Are you missing the \`User->User Details->Read\` permission?`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
logger.table(
|
|
27
|
+
user.accounts.map((account) => ({
|
|
28
|
+
"Account Name": account.name,
|
|
29
|
+
"Account ID": account.id,
|
|
30
|
+
}))
|
|
42
31
|
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function Accounts(props: { accounts: AccountInfo[] }) {
|
|
46
|
-
const accounts = props.accounts.map((account) => ({
|
|
47
|
-
"Account Name": account.name,
|
|
48
|
-
"Account ID": account.id,
|
|
49
|
-
}));
|
|
50
|
-
return <Table data={accounts}></Table>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function Permissions(props: {
|
|
54
|
-
tokenPermissions: string[] | undefined;
|
|
55
|
-
tokenType: string;
|
|
56
|
-
}) {
|
|
57
32
|
const permissions =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
{permissions.map(([scope, access], index) => (
|
|
68
|
-
<Fragment key={`${scope}${index}`}>
|
|
69
|
-
<Text>
|
|
70
|
-
- {scope} {access && `(${access})`}
|
|
71
|
-
</Text>
|
|
72
|
-
</Fragment>
|
|
73
|
-
))}
|
|
74
|
-
</>
|
|
75
|
-
) : null
|
|
76
|
-
) : (
|
|
77
|
-
<Text>
|
|
78
|
-
🔓 To see token permissions visit
|
|
79
|
-
https://dash.cloudflare.com/profile/api-tokens
|
|
80
|
-
</Text>
|
|
33
|
+
user.tokenPermissions?.map((scope) => scope.split(":")) ?? [];
|
|
34
|
+
|
|
35
|
+
if (user.authType !== "OAuth Token") {
|
|
36
|
+
return void logger.log(
|
|
37
|
+
`🔓 To see token permissions visit https://dash.cloudflare.com/profile/api-tokens`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
logger.log(
|
|
41
|
+
`🔓 Token Permissions: If scopes are missing, you may need to logout and re-login.`
|
|
81
42
|
);
|
|
43
|
+
logger.log(`Scope (Access)`);
|
|
44
|
+
for (const [scope, access] of permissions) {
|
|
45
|
+
logger.log(`- ${scope} ${access ? `(${access})` : ``}`);
|
|
46
|
+
}
|
|
82
47
|
}
|
|
83
48
|
|
|
49
|
+
type AuthType = "Global API Key" | "API Token" | "OAuth Token";
|
|
84
50
|
export interface UserInfo {
|
|
85
51
|
apiToken: string;
|
|
86
|
-
authType:
|
|
52
|
+
authType: AuthType;
|
|
87
53
|
email: string | undefined;
|
|
88
54
|
accounts: AccountInfo[];
|
|
89
55
|
tokenPermissions: string[] | undefined;
|
package/src/worker.ts
CHANGED
|
@@ -79,6 +79,15 @@ export interface CfKvNamespace {
|
|
|
79
79
|
id: string;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* A binding to send email.
|
|
84
|
+
*/
|
|
85
|
+
export interface CfSendEmailBindings {
|
|
86
|
+
name: string;
|
|
87
|
+
destination_address?: string;
|
|
88
|
+
allowed_destination_addresses?: string[];
|
|
89
|
+
}
|
|
90
|
+
|
|
82
91
|
/**
|
|
83
92
|
* A binding to a wasm module (in service-worker format)
|
|
84
93
|
*/
|
|
@@ -126,12 +135,16 @@ export interface CfR2Bucket {
|
|
|
126
135
|
export const D1_BETA_PREFIX = `__D1_BETA__` as const;
|
|
127
136
|
export type D1PrefixedBinding = `${typeof D1_BETA_PREFIX}${string}`;
|
|
128
137
|
|
|
138
|
+
// TODO: figure out if this is duplicated in packages/wrangler/src/config/environment.ts
|
|
129
139
|
export interface CfD1Database {
|
|
130
|
-
// For now, all D1 bindings are
|
|
140
|
+
// For now, all D1 bindings are alpha
|
|
131
141
|
binding: D1PrefixedBinding;
|
|
132
142
|
database_id: string;
|
|
133
143
|
database_name?: string;
|
|
134
144
|
preview_database_id?: string;
|
|
145
|
+
database_internal_env?: string;
|
|
146
|
+
migrations_table?: string;
|
|
147
|
+
migrations_dir?: string;
|
|
135
148
|
}
|
|
136
149
|
|
|
137
150
|
interface CfService {
|
|
@@ -140,11 +153,21 @@ interface CfService {
|
|
|
140
153
|
environment?: string;
|
|
141
154
|
}
|
|
142
155
|
|
|
156
|
+
interface CfAnalyticsEngineDataset {
|
|
157
|
+
binding: string;
|
|
158
|
+
dataset?: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
143
161
|
interface CfDispatchNamespace {
|
|
144
162
|
binding: string;
|
|
145
163
|
namespace: string;
|
|
146
164
|
}
|
|
147
165
|
|
|
166
|
+
interface CfMTlsCertificate {
|
|
167
|
+
binding: string;
|
|
168
|
+
certificate_id: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
148
171
|
interface CfLogfwdr {
|
|
149
172
|
schema: string | undefined;
|
|
150
173
|
bindings: CfLogfwdrBinding[];
|
|
@@ -160,6 +183,13 @@ interface CfUnsafeBinding {
|
|
|
160
183
|
type: string;
|
|
161
184
|
}
|
|
162
185
|
|
|
186
|
+
type CfUnsafeMetadata = Record<string, unknown>;
|
|
187
|
+
|
|
188
|
+
interface CfUnsafe {
|
|
189
|
+
bindings: CfUnsafeBinding[] | undefined;
|
|
190
|
+
metadata: CfUnsafeMetadata | undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
163
193
|
export interface CfDurableObjectMigrations {
|
|
164
194
|
old_tag?: string;
|
|
165
195
|
new_tag: string;
|
|
@@ -194,8 +224,8 @@ export interface CfWorkerInit {
|
|
|
194
224
|
*/
|
|
195
225
|
bindings: {
|
|
196
226
|
vars: CfVars | undefined;
|
|
197
|
-
metadata_binding: string | undefined;
|
|
198
227
|
kv_namespaces: CfKvNamespace[] | undefined;
|
|
228
|
+
send_email: CfSendEmailBindings[] | undefined;
|
|
199
229
|
wasm_modules: CfWasmModuleBindings | undefined;
|
|
200
230
|
text_blobs: CfTextBlobBindings | undefined;
|
|
201
231
|
data_blobs: CfDataBlobBindings | undefined;
|
|
@@ -204,9 +234,11 @@ export interface CfWorkerInit {
|
|
|
204
234
|
r2_buckets: CfR2Bucket[] | undefined;
|
|
205
235
|
d1_databases: CfD1Database[] | undefined;
|
|
206
236
|
services: CfService[] | undefined;
|
|
237
|
+
analytics_engine_datasets: CfAnalyticsEngineDataset[] | undefined;
|
|
207
238
|
dispatch_namespaces: CfDispatchNamespace[] | undefined;
|
|
239
|
+
mtls_certificates: CfMTlsCertificate[] | undefined;
|
|
208
240
|
logfwdr: CfLogfwdr | undefined;
|
|
209
|
-
unsafe:
|
|
241
|
+
unsafe: CfUnsafe | undefined;
|
|
210
242
|
};
|
|
211
243
|
migrations: CfDurableObjectMigrations | undefined;
|
|
212
244
|
compatibility_date: string | undefined;
|