wrangler 0.0.0-e178d6fb → 0.0.0-e1c2f5b9
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 +16 -2
- package/bin/wrangler.js +31 -11
- package/miniflare-dist/index.mjs +1340 -330
- package/package.json +40 -13
- package/src/__tests__/access.test.ts +25 -0
- package/src/__tests__/api-dev.test.ts +238 -0
- package/src/__tests__/api-devregistry.test.js +64 -0
- package/src/__tests__/api.test.ts +80 -0
- package/src/__tests__/configuration.test.ts +683 -470
- package/src/__tests__/d1.test.ts +123 -0
- package/src/__tests__/delete.test.ts +263 -0
- package/src/__tests__/deployments.test.ts +105 -0
- package/src/__tests__/dev.test.tsx +453 -70
- package/src/__tests__/dialogs.test.tsx +40 -0
- package/src/__tests__/generate.test.ts +235 -0
- package/src/__tests__/get-host-from-url.test.ts +16 -0
- package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
- package/src/__tests__/helpers/mock-cfetch.ts +92 -26
- package/src/__tests__/helpers/mock-console.ts +11 -2
- package/src/__tests__/helpers/mock-dialogs.ts +1 -1
- package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
- package/src/__tests__/helpers/mock-known-routes.ts +7 -0
- package/src/__tests__/helpers/mock-kv.ts +1 -1
- package/src/__tests__/helpers/mock-oauth-flow.ts +74 -53
- package/src/__tests__/helpers/mock-process.ts +8 -13
- package/src/__tests__/helpers/msw/handlers/access.ts +13 -0
- package/src/__tests__/helpers/msw/handlers/deployments.ts +59 -0
- package/src/__tests__/helpers/msw/handlers/namespaces.ts +104 -0
- package/src/__tests__/helpers/msw/handlers/oauth.ts +31 -0
- package/src/__tests__/helpers/msw/handlers/r2.ts +80 -0
- package/src/__tests__/helpers/msw/handlers/script.ts +75 -0
- package/src/__tests__/helpers/msw/handlers/user.ts +63 -0
- package/src/__tests__/helpers/msw/handlers/zones.ts +22 -0
- package/src/__tests__/helpers/msw/index.ts +21 -0
- package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +1 -0
- package/src/__tests__/helpers/worker-scripts/hello-world-worker.js +5 -0
- package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +1 -0
- package/src/__tests__/helpers/worker-scripts/parent-worker.js +8 -0
- package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +5 -0
- package/src/__tests__/index.test.ts +103 -97
- package/src/__tests__/init.test.ts +1484 -382
- package/src/__tests__/jest.setup.ts +88 -5
- package/src/__tests__/kv.test.ts +655 -540
- package/src/__tests__/logout.test.ts +47 -0
- package/src/__tests__/metrics.test.ts +95 -45
- package/src/__tests__/middleware.scheduled.test.ts +145 -0
- package/src/__tests__/middleware.test.ts +750 -0
- package/src/__tests__/pages-deployment-tail.test.ts +884 -0
- package/src/__tests__/pages.test.ts +1301 -119
- package/src/__tests__/paths.test.ts +39 -0
- package/src/__tests__/publish.test.ts +2266 -1180
- package/src/__tests__/pubsub.test.ts +211 -88
- package/src/__tests__/queues.test.ts +522 -0
- package/src/__tests__/r2.test.ts +363 -203
- package/src/__tests__/secret.test.ts +261 -59
- package/src/__tests__/tail.test.ts +352 -151
- package/src/__tests__/test-old-node-version.js +3 -3
- 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 +233 -0
- package/src/__tests__/user.test.ts +59 -73
- package/src/__tests__/utils-collectKeyValues.test.ts +47 -0
- package/src/__tests__/validate-dev-props.test.ts +56 -0
- package/src/__tests__/whoami.test.tsx +114 -66
- package/src/__tests__/worker-namespace.test.ts +154 -155
- package/src/api/dev.ts +170 -38
- package/src/api/index.ts +1 -0
- package/src/bundle-reporter.tsx +41 -2
- package/src/bundle.ts +668 -81
- package/src/cfetch/index.ts +40 -3
- package/src/cfetch/internal.ts +144 -32
- package/src/cli.ts +4 -3
- package/src/config/config.ts +27 -3
- package/src/config/environment.ts +131 -7
- package/src/config/index.ts +128 -8
- package/src/config/validation-helpers.ts +19 -6
- package/src/config/validation.ts +411 -14
- package/src/config-cache.ts +3 -2
- package/src/create-worker-preview.ts +31 -6
- package/src/create-worker-upload-form.ts +84 -22
- package/src/d1/backups.tsx +223 -0
- package/src/d1/constants.ts +2 -0
- package/src/d1/create.tsx +63 -0
- package/src/d1/delete.tsx +56 -0
- package/src/d1/execute.tsx +352 -0
- package/src/d1/formatTimeAgo.ts +14 -0
- package/src/d1/index.ts +101 -0
- package/src/d1/list.tsx +48 -0
- package/src/d1/migrations/apply.tsx +196 -0
- package/src/d1/migrations/create.tsx +77 -0
- package/src/d1/migrations/helpers.ts +147 -0
- package/src/d1/migrations/index.tsx +3 -0
- package/src/d1/migrations/list.tsx +79 -0
- package/src/d1/migrations/options.ts +18 -0
- package/src/d1/options.ts +22 -0
- package/src/d1/types.tsx +24 -0
- package/src/d1/utils.ts +48 -0
- package/src/delete.ts +108 -0
- package/src/deployments.ts +83 -0
- package/src/deprecated/index.ts +145 -0
- package/src/dev/dev-vars.ts +11 -8
- package/src/dev/dev.tsx +220 -58
- package/src/dev/get-local-persistence-path.tsx +31 -0
- package/src/dev/local.tsx +740 -181
- package/src/dev/remote.tsx +340 -123
- package/src/dev/start-server.ts +543 -0
- package/src/dev/use-esbuild.ts +61 -7
- package/src/dev/validate-dev-props.ts +31 -0
- package/src/dev-registry.tsx +201 -0
- package/src/dev.tsx +614 -258
- package/src/dialogs.tsx +41 -8
- package/src/{worker-namespace.ts → dispatch-namespace.ts} +23 -22
- package/src/docs/index.ts +97 -0
- package/src/entry.ts +3 -2
- package/src/environment-variables/factory.ts +88 -0
- package/src/environment-variables/misc-variables.ts +30 -0
- package/src/generate/index.ts +300 -0
- package/src/git-client.ts +100 -7
- package/src/index.tsx +249 -1560
- package/src/init.ts +626 -150
- package/src/inspect.ts +137 -32
- package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
- package/src/is-interactive.ts +4 -0
- package/src/jest.d.ts +4 -0
- package/src/{kv.ts → kv/helpers.ts} +3 -3
- package/src/kv/index.ts +596 -0
- package/src/logger.ts +27 -8
- package/src/metrics/index.ts +1 -0
- package/src/metrics/metrics-config.ts +4 -4
- package/src/metrics/metrics-dispatcher.ts +1 -0
- package/src/metrics/metrics-usage-headers.ts +24 -0
- package/src/metrics/send-event.ts +13 -8
- package/src/miniflare-cli/assets.ts +172 -465
- package/src/miniflare-cli/index.ts +149 -24
- 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 +12 -3
- package/src/pages/build.tsx +108 -42
- package/src/pages/constants.ts +11 -0
- package/src/pages/deployment-tails.tsx +284 -0
- package/src/pages/deployments.tsx +9 -31
- package/src/pages/dev.tsx +417 -108
- package/src/pages/errors.ts +66 -0
- package/src/pages/functions/buildPlugin.ts +86 -84
- package/src/pages/functions/buildWorker.ts +121 -106
- package/src/pages/functions/routes-consolidation.test.ts +250 -0
- package/src/pages/functions/routes-consolidation.ts +73 -0
- package/src/pages/functions/routes-transformation.test.ts +282 -0
- package/src/pages/functions/routes-transformation.ts +115 -0
- package/src/pages/functions/routes-validation.test.ts +403 -0
- package/src/pages/functions/routes-validation.ts +202 -0
- package/src/pages/functions/tsconfig.json +8 -0
- package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
- package/src/pages/functions.tsx +82 -0
- package/src/pages/hash.tsx +13 -0
- package/src/pages/index.tsx +73 -55
- package/src/pages/projects.tsx +9 -3
- package/src/pages/prompt-select-project.tsx +31 -0
- package/src/pages/publish.tsx +183 -60
- package/src/pages/types.ts +1 -0
- package/src/pages/upload.tsx +107 -56
- package/src/pages/utils.ts +13 -2
- package/src/paths.ts +49 -4
- package/src/proxy.ts +375 -157
- package/src/publish/index.ts +270 -0
- package/src/{publish.ts → publish/publish.ts} +431 -188
- package/src/pubsub/pubsub-commands.tsx +4 -3
- package/src/queues/cli/commands/consumer/add.ts +71 -0
- package/src/queues/cli/commands/consumer/index.ts +22 -0
- package/src/queues/cli/commands/consumer/remove.ts +38 -0
- package/src/queues/cli/commands/create.ts +25 -0
- package/src/queues/cli/commands/delete.ts +26 -0
- package/src/queues/cli/commands/index.ts +36 -0
- package/src/queues/cli/commands/list.ts +25 -0
- package/src/queues/client.ts +135 -0
- package/src/queues/utils.ts +18 -0
- package/src/r2/constants.ts +4 -0
- package/src/r2/helpers.ts +131 -0
- package/src/r2/index.ts +289 -0
- package/src/routes.ts +140 -0
- package/src/secret/index.ts +379 -0
- package/src/sites.tsx +3 -3
- package/src/tail/createTail.ts +379 -0
- package/src/tail/filters.ts +4 -6
- package/src/tail/index.ts +199 -284
- package/src/tail/printing.ts +43 -3
- package/src/tsconfig-sanity.ts +16 -0
- package/src/type-generation.ts +181 -0
- package/src/user/access.ts +68 -0
- package/src/user/auth-variables.ts +113 -0
- package/src/user/choose-account.tsx +1 -1
- package/src/user/index.ts +0 -1
- package/src/user/user.tsx +116 -49
- package/src/utils/collectKeyValues.ts +14 -0
- package/src/whoami.tsx +80 -2
- package/src/worker.ts +69 -9
- package/src/yargs-types.ts +24 -0
- package/src/zones.ts +124 -9
- package/templates/__tests__/pages-dev-util.test.ts +128 -0
- package/templates/__tests__/tsconfig-sanity.ts +12 -0
- package/templates/__tests__/tsconfig.json +8 -0
- package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
- package/templates/d1-beta-facade.js +205 -0
- package/templates/facade.d.ts +14 -0
- package/templates/first-party-worker-module-facade.ts +19 -0
- package/templates/format-dev-errors.ts +33 -0
- package/templates/init-tests/test-jest-new-worker.js +23 -0
- package/templates/init-tests/test-vitest-new-worker.js +24 -0
- package/templates/init-tests/test-vitest-new-worker.ts +25 -0
- package/templates/middleware/common.ts +62 -0
- package/templates/middleware/loader-modules.ts +82 -0
- package/templates/middleware/loader-sw.ts +233 -0
- package/templates/middleware/middleware-miniflare3-json-error.ts +20 -0
- package/templates/middleware/middleware-pretty-error.ts +40 -0
- package/templates/middleware/middleware-scheduled.ts +15 -0
- package/templates/new-worker.js +1 -1
- package/templates/pages-dev-pipeline.ts +31 -0
- package/templates/pages-dev-util.ts +52 -0
- package/templates/pages-shim.ts +4 -1
- package/templates/pages-template-plugin.ts +21 -5
- package/templates/pages-template-worker.ts +37 -9
- package/templates/{static-asset-facade.js → serve-static-assets.ts} +23 -7
- package/templates/service-bindings-module-facade.js +54 -0
- package/templates/service-bindings-sw-facade.js +42 -0
- package/templates/tsconfig-sanity.ts +11 -0
- package/templates/tsconfig.init.json +106 -0
- package/templates/tsconfig.json +5 -102
- package/templates/tsconfig.tsbuildinfo +1 -0
- package/wrangler-dist/cli.d.ts +621 -42
- package/wrangler-dist/cli.js +144441 -102606
- package/src/environment-variables.ts +0 -35
- package/src/faye-websocket.d.ts +0 -6
- package/src/generate.ts +0 -33
- package/src/miniflare-cli/enum-keys.ts +0 -17
- package/src/preview.ts +0 -31
- package/src/r2.ts +0 -50
- package/src/user/env-vars.ts +0 -46
package/src/user/user.tsx
CHANGED
|
@@ -224,17 +224,52 @@ import {
|
|
|
224
224
|
saveToConfigCache,
|
|
225
225
|
} from "../config-cache";
|
|
226
226
|
import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
|
|
227
|
+
import { CI } from "../is-ci";
|
|
227
228
|
import isInteractive from "../is-interactive";
|
|
228
229
|
import { logger } from "../logger";
|
|
229
230
|
import openInBrowser from "../open-in-browser";
|
|
230
231
|
import { parseTOML, readFileSync } from "../parse";
|
|
232
|
+
import { domainUsesAccess } from "./access";
|
|
233
|
+
import {
|
|
234
|
+
getAuthDomainFromEnv,
|
|
235
|
+
getAuthUrlFromEnv,
|
|
236
|
+
getClientIdFromEnv,
|
|
237
|
+
getCloudflareAccessToken,
|
|
238
|
+
getCloudflareAPITokenFromEnv,
|
|
239
|
+
getCloudflareGlobalAuthEmailFromEnv,
|
|
240
|
+
getCloudflareGlobalAuthKeyFromEnv,
|
|
241
|
+
getRevokeUrlFromEnv,
|
|
242
|
+
getTokenUrlFromEnv,
|
|
243
|
+
} from "./auth-variables";
|
|
231
244
|
import { ChooseAccount, getAccountChoices } from "./choose-account";
|
|
232
|
-
import { getAuthFromEnv } from "./env-vars";
|
|
233
245
|
import { generateAuthUrl } from "./generate-auth-url";
|
|
234
246
|
import { generateRandomState } from "./generate-random-state";
|
|
235
|
-
import type { ApiCredentials } from "./env-vars";
|
|
236
247
|
import type { ParsedUrlQuery } from "node:querystring";
|
|
237
248
|
|
|
249
|
+
export type ApiCredentials =
|
|
250
|
+
| {
|
|
251
|
+
apiToken: string;
|
|
252
|
+
}
|
|
253
|
+
| {
|
|
254
|
+
authKey: string;
|
|
255
|
+
authEmail: string;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Try to read an API token or Global Auth from the environment.
|
|
260
|
+
*/
|
|
261
|
+
export function getAuthFromEnv(): ApiCredentials | undefined {
|
|
262
|
+
const globalApiKey = getCloudflareGlobalAuthKeyFromEnv();
|
|
263
|
+
const globalApiEmail = getCloudflareGlobalAuthEmailFromEnv();
|
|
264
|
+
const apiToken = getCloudflareAPITokenFromEnv();
|
|
265
|
+
|
|
266
|
+
if (globalApiKey && globalApiEmail) {
|
|
267
|
+
return { authKey: globalApiKey, authEmail: globalApiEmail };
|
|
268
|
+
} else if (apiToken) {
|
|
269
|
+
return { apiToken };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
238
273
|
/**
|
|
239
274
|
* An implementation of rfc6749#section-4.1 and rfc7636.
|
|
240
275
|
*/
|
|
@@ -262,6 +297,7 @@ interface State extends AuthTokens {
|
|
|
262
297
|
interface AuthTokens {
|
|
263
298
|
accessToken?: AccessToken;
|
|
264
299
|
refreshToken?: RefreshToken;
|
|
300
|
+
scopes?: Scope[];
|
|
265
301
|
/** @deprecated - this field was only provided by the deprecated `wrangler1 config` command. */
|
|
266
302
|
apiToken?: string;
|
|
267
303
|
}
|
|
@@ -279,6 +315,7 @@ export interface UserAuthConfig {
|
|
|
279
315
|
oauth_token?: string;
|
|
280
316
|
refresh_token?: string;
|
|
281
317
|
expiration_time?: string;
|
|
318
|
+
scopes?: string[];
|
|
282
319
|
/** @deprecated - this field was only provided by the deprecated `wrangler1 config` command. */
|
|
283
320
|
api_token?: string;
|
|
284
321
|
}
|
|
@@ -306,6 +343,7 @@ const Scopes = {
|
|
|
306
343
|
"workers_scripts:write":
|
|
307
344
|
"See and change Cloudflare Workers scripts, durable objects, subdomains, triggers, and tail data.",
|
|
308
345
|
"workers_tail:read": "See Cloudflare Workers tail and script data.",
|
|
346
|
+
"d1:write": "See and change D1 Databases.",
|
|
309
347
|
"pages:write":
|
|
310
348
|
"See and change Cloudflare Pages projects, settings and deployments.",
|
|
311
349
|
"zone:read": "Grants read level access to account zone.",
|
|
@@ -326,11 +364,6 @@ export function validateScopeKeys(
|
|
|
326
364
|
return scopes.every((scope) => scope in Scopes);
|
|
327
365
|
}
|
|
328
366
|
|
|
329
|
-
const CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
|
|
330
|
-
const AUTH_URL = "https://dash.cloudflare.com/oauth2/auth";
|
|
331
|
-
const TOKEN_URL = "https://dash.cloudflare.com/oauth2/token";
|
|
332
|
-
const REVOKE_URL = "https://dash.cloudflare.com/oauth2/revoke";
|
|
333
|
-
|
|
334
367
|
/**
|
|
335
368
|
* To allow OAuth callbacks in environments such as WebContainer we need to
|
|
336
369
|
* create a host URL which only resolves `localhost` to a WebContainer
|
|
@@ -355,7 +388,7 @@ function getAuthTokens(config?: UserAuthConfig): AuthTokens | undefined {
|
|
|
355
388
|
if (getAuthFromEnv()) return;
|
|
356
389
|
|
|
357
390
|
// otherwise try loading from the user auth config file.
|
|
358
|
-
const { oauth_token, refresh_token, expiration_time, api_token } =
|
|
391
|
+
const { oauth_token, refresh_token, expiration_time, scopes, api_token } =
|
|
359
392
|
config || readAuthConfigFile();
|
|
360
393
|
|
|
361
394
|
if (oauth_token) {
|
|
@@ -366,6 +399,7 @@ function getAuthTokens(config?: UserAuthConfig): AuthTokens | undefined {
|
|
|
366
399
|
expiry: expiration_time ?? "2000-01-01:00:00:00+00:00",
|
|
367
400
|
},
|
|
368
401
|
refreshToken: { value: refresh_token ?? "" },
|
|
402
|
+
scopes: scopes as Scope[],
|
|
369
403
|
};
|
|
370
404
|
} else if (api_token) {
|
|
371
405
|
logger.warn(
|
|
@@ -381,7 +415,7 @@ function getAuthTokens(config?: UserAuthConfig): AuthTokens | undefined {
|
|
|
381
415
|
}
|
|
382
416
|
|
|
383
417
|
/**
|
|
384
|
-
* Run the
|
|
418
|
+
* Run the initialization of the auth state, in the case that something changed.
|
|
385
419
|
*
|
|
386
420
|
* This runs automatically whenever `writeAuthConfigFile` is run, so generally
|
|
387
421
|
* you won't need to call it yourself.
|
|
@@ -620,8 +654,8 @@ export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
|
|
|
620
654
|
});
|
|
621
655
|
|
|
622
656
|
return generateAuthUrl({
|
|
623
|
-
authUrl:
|
|
624
|
-
clientId:
|
|
657
|
+
authUrl: getAuthUrlFromEnv(),
|
|
658
|
+
clientId: getClientIdFromEnv(),
|
|
625
659
|
callbackUrl: CALLBACK_URL,
|
|
626
660
|
scopes,
|
|
627
661
|
stateQueryParam,
|
|
@@ -648,19 +682,14 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
|
|
|
648
682
|
logger.warn("No refresh token is present.");
|
|
649
683
|
}
|
|
650
684
|
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
const response = await fetch(TOKEN_URL, {
|
|
657
|
-
method: "POST",
|
|
658
|
-
body,
|
|
659
|
-
headers: {
|
|
660
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
661
|
-
},
|
|
685
|
+
const params = new URLSearchParams({
|
|
686
|
+
grant_type: "refresh_token",
|
|
687
|
+
refresh_token: LocalState.refreshToken?.value ?? "",
|
|
688
|
+
client_id: getClientIdFromEnv(),
|
|
662
689
|
});
|
|
663
690
|
|
|
691
|
+
const response = await fetchAuthToken(params);
|
|
692
|
+
|
|
664
693
|
if (response.status >= 400) {
|
|
665
694
|
let tokenExchangeResErr = undefined;
|
|
666
695
|
|
|
@@ -738,20 +767,15 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
738
767
|
logger.warn("No authorization grant code is being passed.");
|
|
739
768
|
}
|
|
740
769
|
|
|
741
|
-
const
|
|
742
|
-
`
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
const response = await fetch(TOKEN_URL, {
|
|
749
|
-
method: "POST",
|
|
750
|
-
body,
|
|
751
|
-
headers: {
|
|
752
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
753
|
-
},
|
|
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,
|
|
754
776
|
});
|
|
777
|
+
|
|
778
|
+
const response = await fetchAuthToken(params);
|
|
755
779
|
if (!response.ok) {
|
|
756
780
|
const { error } = (await response.json()) as { error: string };
|
|
757
781
|
// .catch((_) => ({ error: "invalid_json" }));
|
|
@@ -870,15 +894,17 @@ export function readAuthConfigFile(): UserAuthConfig {
|
|
|
870
894
|
|
|
871
895
|
type LoginProps = {
|
|
872
896
|
scopes?: Scope[];
|
|
897
|
+
browser: boolean;
|
|
873
898
|
};
|
|
874
899
|
|
|
875
900
|
export async function loginOrRefreshIfRequired(): Promise<boolean> {
|
|
876
901
|
// TODO: if there already is a token, then try refreshing
|
|
877
902
|
// TODO: ask permission before opening browser
|
|
903
|
+
const { isCI } = CI;
|
|
878
904
|
if (!getAPIToken()) {
|
|
879
905
|
// Not logged in.
|
|
880
906
|
// If we are not interactive, we cannot ask the user to login
|
|
881
|
-
return isInteractive() && (await login());
|
|
907
|
+
return isInteractive() && !isCI() && (await login());
|
|
882
908
|
} else if (isAccessTokenExpired()) {
|
|
883
909
|
// We're logged in, but the refresh token seems to have expired,
|
|
884
910
|
// so let's try to refresh it
|
|
@@ -888,14 +914,16 @@ export async function loginOrRefreshIfRequired(): Promise<boolean> {
|
|
|
888
914
|
return true;
|
|
889
915
|
} else {
|
|
890
916
|
// If the refresh token isn't valid, then we ask the user to login again
|
|
891
|
-
return isInteractive() && (await login());
|
|
917
|
+
return isInteractive() && !isCI() && (await login());
|
|
892
918
|
}
|
|
893
919
|
} else {
|
|
894
920
|
return true;
|
|
895
921
|
}
|
|
896
922
|
}
|
|
897
923
|
|
|
898
|
-
export async function login(
|
|
924
|
+
export async function login(
|
|
925
|
+
props: LoginProps = { browser: true }
|
|
926
|
+
): Promise<boolean> {
|
|
899
927
|
logger.log("Attempting to login via OAuth...");
|
|
900
928
|
const urlToOpen = await getAuthURL(props?.scopes);
|
|
901
929
|
let server: http.Server;
|
|
@@ -908,7 +936,7 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
908
936
|
server.close();
|
|
909
937
|
clearTimeout(loginTimeoutHandle);
|
|
910
938
|
resolve(false);
|
|
911
|
-
},
|
|
939
|
+
}, 120000); // wait for 120 seconds for the user to authorize
|
|
912
940
|
});
|
|
913
941
|
|
|
914
942
|
const loginPromise = new Promise<boolean>((resolve, reject) => {
|
|
@@ -959,6 +987,7 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
959
987
|
oauth_token: exchange.token?.value ?? "",
|
|
960
988
|
expiration_time: exchange.token?.expiry,
|
|
961
989
|
refresh_token: exchange.refreshToken?.value,
|
|
990
|
+
scopes: exchange.scopes,
|
|
962
991
|
});
|
|
963
992
|
res.writeHead(307, {
|
|
964
993
|
Location:
|
|
@@ -979,9 +1008,12 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
979
1008
|
|
|
980
1009
|
server.listen(8976);
|
|
981
1010
|
});
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
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
|
+
}
|
|
985
1017
|
|
|
986
1018
|
return Promise.race([timerPromise, loginPromise]);
|
|
987
1019
|
}
|
|
@@ -1003,8 +1035,14 @@ async function refreshToken(): Promise<boolean> {
|
|
|
1003
1035
|
expiry: "",
|
|
1004
1036
|
},
|
|
1005
1037
|
refreshToken: { value: refresh_token } = {},
|
|
1038
|
+
scopes,
|
|
1006
1039
|
} = await exchangeRefreshTokenForAccessToken();
|
|
1007
|
-
writeAuthConfigFile({
|
|
1040
|
+
writeAuthConfigFile({
|
|
1041
|
+
oauth_token,
|
|
1042
|
+
expiration_time,
|
|
1043
|
+
refresh_token,
|
|
1044
|
+
scopes,
|
|
1045
|
+
});
|
|
1008
1046
|
return true;
|
|
1009
1047
|
} catch (err) {
|
|
1010
1048
|
return false;
|
|
@@ -1019,11 +1057,11 @@ export async function logout(): Promise<void> {
|
|
|
1019
1057
|
}
|
|
1020
1058
|
|
|
1021
1059
|
const body =
|
|
1022
|
-
`client_id=${encodeURIComponent(
|
|
1060
|
+
`client_id=${encodeURIComponent(getClientIdFromEnv())}&` +
|
|
1023
1061
|
`token_type_hint=refresh_token&` +
|
|
1024
1062
|
`token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
|
|
1025
1063
|
|
|
1026
|
-
const response = await fetch(
|
|
1064
|
+
const response = await fetch(getRevokeUrlFromEnv(), {
|
|
1027
1065
|
method: "POST",
|
|
1028
1066
|
body,
|
|
1029
1067
|
headers: {
|
|
@@ -1036,11 +1074,11 @@ export async function logout(): Promise<void> {
|
|
|
1036
1074
|
);
|
|
1037
1075
|
}
|
|
1038
1076
|
const body =
|
|
1039
|
-
`client_id=${encodeURIComponent(
|
|
1077
|
+
`client_id=${encodeURIComponent(getClientIdFromEnv())}&` +
|
|
1040
1078
|
`token_type_hint=refresh_token&` +
|
|
1041
1079
|
`token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
|
|
1042
1080
|
|
|
1043
|
-
const response = await fetch(
|
|
1081
|
+
const response = await fetch(getRevokeUrlFromEnv(), {
|
|
1044
1082
|
method: "POST",
|
|
1045
1083
|
body,
|
|
1046
1084
|
headers: {
|
|
@@ -1078,7 +1116,7 @@ export async function getAccountId(): Promise<string | undefined> {
|
|
|
1078
1116
|
return accounts[0].id;
|
|
1079
1117
|
}
|
|
1080
1118
|
|
|
1081
|
-
if (isInteractive()) {
|
|
1119
|
+
if (isInteractive() && !CI.isCI()) {
|
|
1082
1120
|
const account = await new Promise<{ id: string; name: string }>(
|
|
1083
1121
|
(resolve, reject) => {
|
|
1084
1122
|
const { unmount } = render(
|
|
@@ -1118,7 +1156,7 @@ export async function requireAuth(config: {
|
|
|
1118
1156
|
}): Promise<string> {
|
|
1119
1157
|
const loggedIn = await loginOrRefreshIfRequired();
|
|
1120
1158
|
if (!loggedIn) {
|
|
1121
|
-
if (!isInteractive()) {
|
|
1159
|
+
if (!isInteractive() || CI.isCI()) {
|
|
1122
1160
|
throw new Error(
|
|
1123
1161
|
"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/tokens/create/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."
|
|
1124
1162
|
);
|
|
@@ -1169,3 +1207,32 @@ export function getAccountFromCache():
|
|
|
1169
1207
|
"wrangler-account.json"
|
|
1170
1208
|
).account;
|
|
1171
1209
|
}
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* Get the scopes of the following token, will only return scopes
|
|
1213
|
+
* if the token is an OAuth token.
|
|
1214
|
+
*/
|
|
1215
|
+
export function getScopes(): Scope[] | undefined {
|
|
1216
|
+
return LocalState.scopes;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
/**
|
|
1220
|
+
* Make a request to the Cloudflare OAuth endpoint to get a token.
|
|
1221
|
+
*
|
|
1222
|
+
* Note that the `body` of the POST request is form-urlencoded so
|
|
1223
|
+
* can be represented by a URLSearchParams object.
|
|
1224
|
+
*/
|
|
1225
|
+
async function fetchAuthToken(body: URLSearchParams) {
|
|
1226
|
+
const headers: Record<string, string> = {
|
|
1227
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1228
|
+
};
|
|
1229
|
+
if (await domainUsesAccess(getAuthDomainFromEnv())) {
|
|
1230
|
+
// We are trying to access the staging API so we need an "access token".
|
|
1231
|
+
headers["Cookie"] = `CF_Authorization=${await getCloudflareAccessToken()}`;
|
|
1232
|
+
}
|
|
1233
|
+
return await fetch(getTokenUrlFromEnv(), {
|
|
1234
|
+
method: "POST",
|
|
1235
|
+
body: body.toString(),
|
|
1236
|
+
headers,
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* a function that takes an array of strings in `key:value` format
|
|
3
|
+
* (typically from yargs or config) and returns back
|
|
4
|
+
* a neat object with the keys and values
|
|
5
|
+
*/
|
|
6
|
+
export function collectKeyValues(array?: string[]) {
|
|
7
|
+
return (
|
|
8
|
+
array?.reduce<Record<string, string>>((recordsToCollect, v) => {
|
|
9
|
+
const [key, ...value] = v.split(":");
|
|
10
|
+
recordsToCollect[key] = value.join(":");
|
|
11
|
+
return recordsToCollect;
|
|
12
|
+
}, {}) || {}
|
|
13
|
+
);
|
|
14
|
+
}
|
package/src/whoami.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Text, render } from "ink";
|
|
2
2
|
import Table from "ink-table";
|
|
3
|
-
import React from "react";
|
|
3
|
+
import React, { Fragment } from "react";
|
|
4
4
|
import { fetchListResult, fetchResult } from "./cfetch";
|
|
5
5
|
import { logger } from "./logger";
|
|
6
|
-
import { getAPIToken, getAuthFromEnv } from "./user";
|
|
6
|
+
import { getAPIToken, getAuthFromEnv, getScopes } from "./user";
|
|
7
7
|
|
|
8
8
|
export async function whoami() {
|
|
9
9
|
logger.log("Getting User settings...");
|
|
@@ -17,6 +17,10 @@ export function WhoAmI({ user }: { user: UserInfo | undefined }) {
|
|
|
17
17
|
<>
|
|
18
18
|
<Email tokenType={user.authType} email={user.email}></Email>
|
|
19
19
|
<Accounts accounts={user.accounts}></Accounts>
|
|
20
|
+
<Permissions
|
|
21
|
+
tokenType={user.authType}
|
|
22
|
+
tokenPermissions={user.tokenPermissions}
|
|
23
|
+
/>
|
|
20
24
|
</>
|
|
21
25
|
) : (
|
|
22
26
|
<Text>You are not authenticated. Please run `wrangler login`.</Text>
|
|
@@ -46,17 +50,51 @@ function Accounts(props: { accounts: AccountInfo[] }) {
|
|
|
46
50
|
return <Table data={accounts}></Table>;
|
|
47
51
|
}
|
|
48
52
|
|
|
53
|
+
function Permissions(props: {
|
|
54
|
+
tokenPermissions: string[] | undefined;
|
|
55
|
+
tokenType: string;
|
|
56
|
+
}) {
|
|
57
|
+
const permissions =
|
|
58
|
+
props.tokenPermissions?.map((scope) => scope.split(":")) || [];
|
|
59
|
+
return props.tokenType === "OAuth Token" ? (
|
|
60
|
+
props.tokenPermissions ? (
|
|
61
|
+
<>
|
|
62
|
+
<Text>
|
|
63
|
+
🔓 Token Permissions: If scopes are missing, you may need to logout
|
|
64
|
+
and re-login.
|
|
65
|
+
</Text>
|
|
66
|
+
<Text>Scope (Access)</Text>
|
|
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>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
49
84
|
export interface UserInfo {
|
|
50
85
|
apiToken: string;
|
|
51
86
|
authType: string;
|
|
52
87
|
email: string | undefined;
|
|
53
88
|
accounts: AccountInfo[];
|
|
89
|
+
tokenPermissions: string[] | undefined;
|
|
54
90
|
}
|
|
55
91
|
|
|
56
92
|
export async function getUserInfo(): Promise<UserInfo | undefined> {
|
|
57
93
|
const apiToken = getAPIToken();
|
|
58
94
|
if (!apiToken) return;
|
|
59
95
|
|
|
96
|
+
const tokenPermissions = await getTokenPermissions();
|
|
97
|
+
|
|
60
98
|
const usingEnvAuth = !!getAuthFromEnv();
|
|
61
99
|
const usingGlobalAuthKey = "authKey" in apiToken;
|
|
62
100
|
return {
|
|
@@ -68,6 +106,7 @@ export async function getUserInfo(): Promise<UserInfo | undefined> {
|
|
|
68
106
|
: "OAuth Token",
|
|
69
107
|
email: "authEmail" in apiToken ? apiToken.authEmail : await getEmail(),
|
|
70
108
|
accounts: await getAccounts(),
|
|
109
|
+
tokenPermissions,
|
|
71
110
|
};
|
|
72
111
|
}
|
|
73
112
|
|
|
@@ -89,3 +128,42 @@ type AccountInfo = { name: string; id: string };
|
|
|
89
128
|
async function getAccounts(): Promise<AccountInfo[]> {
|
|
90
129
|
return await fetchListResult<AccountInfo>("/accounts");
|
|
91
130
|
}
|
|
131
|
+
|
|
132
|
+
async function getTokenPermissions(): Promise<string[] | undefined> {
|
|
133
|
+
// Tokens can either be API tokens or Oauth tokens.
|
|
134
|
+
// Here we only extract permissions from OAuth tokens.
|
|
135
|
+
|
|
136
|
+
return getScopes() as string[];
|
|
137
|
+
|
|
138
|
+
// In future we may be able to get the token permissions on an API token,
|
|
139
|
+
// but currently we cannot as that permission is not able to be added to
|
|
140
|
+
// an API token.
|
|
141
|
+
|
|
142
|
+
// try {
|
|
143
|
+
// // First we get the token identifier (only returned on API tokens)
|
|
144
|
+
// const { id } = await fetchResult<{ id: string }>("/user/tokens/verify");
|
|
145
|
+
|
|
146
|
+
// // Get the token permissions for the current token
|
|
147
|
+
// const { policies } = await fetchResult<{
|
|
148
|
+
// policies: { id: string; name: string }[];
|
|
149
|
+
// }>(`/user/tokens/${id}`);
|
|
150
|
+
|
|
151
|
+
// return policies.map((p) => p.name);
|
|
152
|
+
// } catch (e) {
|
|
153
|
+
// if ((e as { code?: number }).code === 1000) {
|
|
154
|
+
// // Invalid token - Oauth token
|
|
155
|
+
|
|
156
|
+
// // Get scopes
|
|
157
|
+
// const scopes = getScopes() as string[];
|
|
158
|
+
// // We may not get scopes back if they are not currently cached,
|
|
159
|
+
// // however next time an access token is requested,
|
|
160
|
+
// // the scopes will be added to the cache.
|
|
161
|
+
// return scopes;
|
|
162
|
+
// } else if ((e as { code?: number }).code === 9109) {
|
|
163
|
+
// // Token cannot view its permissions
|
|
164
|
+
// return undefined;
|
|
165
|
+
// } else {
|
|
166
|
+
// throw e;
|
|
167
|
+
// }
|
|
168
|
+
// }
|
|
169
|
+
}
|
package/src/worker.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { Environment } from "./config";
|
|
1
2
|
import type { Route } from "./config/environment";
|
|
2
3
|
import type { ApiCredentials } from "./user";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* A Cloudflare account.
|
|
6
7
|
*/
|
|
8
|
+
|
|
7
9
|
export interface CfAccount {
|
|
8
10
|
/**
|
|
9
11
|
* An API token.
|
|
@@ -65,14 +67,14 @@ export interface CfModule {
|
|
|
65
67
|
/**
|
|
66
68
|
* A map of variable names to values.
|
|
67
69
|
*/
|
|
68
|
-
interface CfVars {
|
|
70
|
+
export interface CfVars {
|
|
69
71
|
[key: string]: unknown;
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
/**
|
|
73
75
|
* A KV namespace.
|
|
74
76
|
*/
|
|
75
|
-
interface CfKvNamespace {
|
|
77
|
+
export interface CfKvNamespace {
|
|
76
78
|
binding: string;
|
|
77
79
|
id: string;
|
|
78
80
|
}
|
|
@@ -81,7 +83,7 @@ interface CfKvNamespace {
|
|
|
81
83
|
* A binding to a wasm module (in service-worker format)
|
|
82
84
|
*/
|
|
83
85
|
|
|
84
|
-
interface CfWasmModuleBindings {
|
|
86
|
+
export interface CfWasmModuleBindings {
|
|
85
87
|
[key: string]: string;
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -89,7 +91,7 @@ interface CfWasmModuleBindings {
|
|
|
89
91
|
* A binding to a text blob (in service-worker format)
|
|
90
92
|
*/
|
|
91
93
|
|
|
92
|
-
interface CfTextBlobBindings {
|
|
94
|
+
export interface CfTextBlobBindings {
|
|
93
95
|
[key: string]: string;
|
|
94
96
|
}
|
|
95
97
|
|
|
@@ -97,36 +99,71 @@ interface CfTextBlobBindings {
|
|
|
97
99
|
* A binding to a data blob (in service-worker format)
|
|
98
100
|
*/
|
|
99
101
|
|
|
100
|
-
interface CfDataBlobBindings {
|
|
102
|
+
export interface CfDataBlobBindings {
|
|
101
103
|
[key: string]: string;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
/**
|
|
105
107
|
* A Durable Object.
|
|
106
108
|
*/
|
|
107
|
-
interface CfDurableObject {
|
|
109
|
+
export interface CfDurableObject {
|
|
108
110
|
name: string;
|
|
109
111
|
class_name: string;
|
|
110
112
|
script_name?: string;
|
|
111
113
|
environment?: string;
|
|
112
114
|
}
|
|
113
115
|
|
|
114
|
-
interface
|
|
116
|
+
export interface CfQueue {
|
|
117
|
+
binding: string;
|
|
118
|
+
queue_name: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface CfR2Bucket {
|
|
115
122
|
binding: string;
|
|
116
123
|
bucket_name: string;
|
|
117
124
|
}
|
|
118
125
|
|
|
126
|
+
export const D1_BETA_PREFIX = `__D1_BETA__` as const;
|
|
127
|
+
export type D1PrefixedBinding = `${typeof D1_BETA_PREFIX}${string}`;
|
|
128
|
+
|
|
129
|
+
// TODO: figure out if this is duplicated in packages/wrangler/src/config/environment.ts
|
|
130
|
+
export interface CfD1Database {
|
|
131
|
+
// For now, all D1 bindings are alpha
|
|
132
|
+
binding: D1PrefixedBinding;
|
|
133
|
+
database_id: string;
|
|
134
|
+
database_name?: string;
|
|
135
|
+
preview_database_id?: string;
|
|
136
|
+
database_internal_env?: string;
|
|
137
|
+
migrations_table?: string;
|
|
138
|
+
migrations_dir?: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
119
141
|
interface CfService {
|
|
120
142
|
binding: string;
|
|
121
143
|
service: string;
|
|
122
144
|
environment?: string;
|
|
123
145
|
}
|
|
124
146
|
|
|
125
|
-
interface
|
|
147
|
+
interface CfAnalyticsEngineDataset {
|
|
148
|
+
binding: string;
|
|
149
|
+
dataset?: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
interface CfDispatchNamespace {
|
|
126
153
|
binding: string;
|
|
127
154
|
namespace: string;
|
|
128
155
|
}
|
|
129
156
|
|
|
157
|
+
interface CfLogfwdr {
|
|
158
|
+
schema: string | undefined;
|
|
159
|
+
bindings: CfLogfwdrBinding[];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
interface CfLogfwdrBinding {
|
|
163
|
+
name: string;
|
|
164
|
+
destination: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
130
167
|
interface CfUnsafeBinding {
|
|
131
168
|
name: string;
|
|
132
169
|
type: string;
|
|
@@ -171,15 +208,21 @@ export interface CfWorkerInit {
|
|
|
171
208
|
text_blobs: CfTextBlobBindings | undefined;
|
|
172
209
|
data_blobs: CfDataBlobBindings | undefined;
|
|
173
210
|
durable_objects: { bindings: CfDurableObject[] } | undefined;
|
|
211
|
+
queues: CfQueue[] | undefined;
|
|
174
212
|
r2_buckets: CfR2Bucket[] | undefined;
|
|
213
|
+
d1_databases: CfD1Database[] | undefined;
|
|
175
214
|
services: CfService[] | undefined;
|
|
176
|
-
|
|
215
|
+
analytics_engine_datasets: CfAnalyticsEngineDataset[] | undefined;
|
|
216
|
+
dispatch_namespaces: CfDispatchNamespace[] | undefined;
|
|
217
|
+
logfwdr: CfLogfwdr | undefined;
|
|
177
218
|
unsafe: CfUnsafeBinding[] | undefined;
|
|
178
219
|
};
|
|
179
220
|
migrations: CfDurableObjectMigrations | undefined;
|
|
180
221
|
compatibility_date: string | undefined;
|
|
181
222
|
compatibility_flags: string[] | undefined;
|
|
182
223
|
usage_model: "bundled" | "unbound" | undefined;
|
|
224
|
+
keepVars: boolean | undefined;
|
|
225
|
+
logpush: boolean | undefined;
|
|
183
226
|
}
|
|
184
227
|
|
|
185
228
|
export interface CfWorkerContext {
|
|
@@ -188,4 +231,21 @@ export interface CfWorkerContext {
|
|
|
188
231
|
zone: string | undefined;
|
|
189
232
|
host: string | undefined;
|
|
190
233
|
routes: Route[] | undefined;
|
|
234
|
+
sendMetrics: boolean | undefined;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Prefix binding with identifier which will then get picked up by the D1 shim.
|
|
238
|
+
// Once the D1 Api is out of beta, this function can be removed.
|
|
239
|
+
export function identifyD1BindingsAsBeta(
|
|
240
|
+
dbs: Environment["d1_databases"]
|
|
241
|
+
): CfD1Database[] | undefined {
|
|
242
|
+
return dbs?.map((db) => ({
|
|
243
|
+
...db,
|
|
244
|
+
binding: `${D1_BETA_PREFIX}${db.binding}`,
|
|
245
|
+
}));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Remove beta prefix
|
|
249
|
+
export function removeD1BetaPrefix(binding: D1PrefixedBinding): string {
|
|
250
|
+
return binding.slice(D1_BETA_PREFIX.length);
|
|
191
251
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ArgumentsCamelCase, Argv } from "yargs";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Yargs options included in every wrangler command.
|
|
5
|
+
*/
|
|
6
|
+
export interface CommonYargsOptions {
|
|
7
|
+
v: boolean | undefined;
|
|
8
|
+
config: string | undefined;
|
|
9
|
+
env: string | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type YargvToInterface<T> = T extends Argv<infer P>
|
|
13
|
+
? ArgumentsCamelCase<P>
|
|
14
|
+
: never;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Given some Yargs Options function factory, extract the interface
|
|
18
|
+
* that corresponds to the yargs arguments
|
|
19
|
+
*/
|
|
20
|
+
export type YargsOptionsToInterface<
|
|
21
|
+
T extends (yargs: Argv<CommonYargsOptions>) => Argv
|
|
22
|
+
> = T extends (yargs: Argv<CommonYargsOptions>) => Argv<infer P>
|
|
23
|
+
? ArgumentsCamelCase<P>
|
|
24
|
+
: never;
|