@shopify/cli-kit 3.49.7 → 3.50.0
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/assets/cli-ruby/lib/project_types/theme/commands/console.rb +3 -1
- package/assets/cli-ruby/lib/shopify_cli/theme/file.rb +2 -0
- package/assets/cli-ruby/lib/shopify_cli/theme/repl/auth_dev_server.rb +2 -4
- package/assets/cli-ruby/lib/shopify_cli/theme/repl/remote_evaluator.rb +3 -0
- package/assets/cli-ruby/lib/shopify_cli/theme/repl.rb +4 -3
- package/dist/private/node/constants.d.ts +2 -0
- package/dist/private/node/constants.js +2 -0
- package/dist/private/node/constants.js.map +1 -1
- package/dist/private/node/context/utilities.js +2 -3
- package/dist/private/node/context/utilities.js.map +1 -1
- package/dist/private/node/otel-metrics.d.ts +32 -0
- package/dist/private/node/otel-metrics.js +131 -0
- package/dist/private/node/otel-metrics.js.map +1 -0
- package/dist/private/node/session/authorize.js +8 -5
- package/dist/private/node/session/authorize.js.map +1 -1
- package/dist/private/node/testing/ui.d.ts +4 -2
- package/dist/private/node/testing/ui.js +10 -3
- package/dist/private/node/testing/ui.js.map +1 -1
- package/dist/private/node/ui/components/AutocompletePrompt.js +6 -2
- package/dist/private/node/ui/components/AutocompletePrompt.js.map +1 -1
- package/dist/private/node/ui/components/AutocompletePrompt.test.js +5 -3
- package/dist/private/node/ui/components/AutocompletePrompt.test.js.map +1 -1
- package/dist/private/node/ui/components/DangerousConfirmationPrompt.js +12 -6
- package/dist/private/node/ui/components/DangerousConfirmationPrompt.js.map +1 -1
- package/dist/private/node/ui/components/DangerousConfirmationPrompt.test.js +1 -1
- package/dist/private/node/ui/components/DangerousConfirmationPrompt.test.js.map +1 -1
- package/dist/private/node/ui/components/Prompts/GitDiff.test.js +18 -11
- package/dist/private/node/ui/components/Prompts/GitDiff.test.js.map +1 -1
- package/dist/private/node/ui/components/SelectPrompt.js +8 -4
- package/dist/private/node/ui/components/SelectPrompt.js.map +1 -1
- package/dist/private/node/ui/components/SelectPrompt.test.js +5 -3
- package/dist/private/node/ui/components/SelectPrompt.test.js.map +1 -1
- package/dist/private/node/ui/components/Tasks.js +8 -1
- package/dist/private/node/ui/components/Tasks.js.map +1 -1
- package/dist/private/node/ui/components/TextPrompt.js +7 -3
- package/dist/private/node/ui/components/TextPrompt.js.map +1 -1
- package/dist/private/node/ui/components/TokenizedText.d.ts +10 -1
- package/dist/private/node/ui/components/TokenizedText.js +18 -0
- package/dist/private/node/ui/components/TokenizedText.js.map +1 -1
- package/dist/private/node/ui/components/TokenizedText.test.js +10 -1
- package/dist/private/node/ui/components/TokenizedText.test.js.map +1 -1
- package/dist/private/node/ui/utilities.d.ts +1 -1
- package/dist/public/common/version.d.ts +1 -1
- package/dist/public/common/version.js +1 -1
- package/dist/public/common/version.js.map +1 -1
- package/dist/public/node/analytics.d.ts +2 -0
- package/dist/public/node/analytics.js +48 -10
- package/dist/public/node/analytics.js.map +1 -1
- package/dist/public/node/api/graphql.js +8 -5
- package/dist/public/node/api/graphql.js.map +1 -1
- package/dist/public/node/base-command.js +4 -3
- package/dist/public/node/base-command.js.map +1 -1
- package/dist/public/node/cli.js +5 -2
- package/dist/public/node/cli.js.map +1 -1
- package/dist/public/node/context/local.d.ts +16 -0
- package/dist/public/node/context/local.js +23 -0
- package/dist/public/node/context/local.js.map +1 -1
- package/dist/public/node/error-handler.d.ts +4 -1
- package/dist/public/node/error-handler.js +26 -18
- package/dist/public/node/error-handler.js.map +1 -1
- package/dist/public/node/error.d.ts +1 -1
- package/dist/public/node/error.js +1 -1
- package/dist/public/node/error.js.map +1 -1
- package/dist/public/node/hooks/postrun.js +1 -1
- package/dist/public/node/hooks/postrun.js.map +1 -1
- package/dist/public/node/http.js +27 -22
- package/dist/public/node/http.js.map +1 -1
- package/dist/public/node/metadata.d.ts +12 -4
- package/dist/public/node/metadata.js +52 -4
- package/dist/public/node/metadata.js.map +1 -1
- package/dist/public/node/monorail.d.ts +8 -1
- package/dist/public/node/monorail.js +1 -1
- package/dist/public/node/monorail.js.map +1 -1
- package/dist/public/node/node-package-manager.d.ts +6 -0
- package/dist/public/node/node-package-manager.js +16 -2
- package/dist/public/node/node-package-manager.js.map +1 -1
- package/dist/public/node/testing/ui.d.ts +1 -1
- package/dist/public/node/testing/ui.js +1 -1
- package/dist/public/node/testing/ui.js.map +1 -1
- package/dist/public/node/ui.d.ts +1 -1
- package/dist/public/node/ui.js +63 -56
- package/dist/public/node/ui.js.map +1 -1
- package/dist/public/node/vendor/otel-js/export/InstantaneousMetricReader.d.ts +18 -0
- package/dist/public/node/vendor/otel-js/export/InstantaneousMetricReader.js +36 -0
- package/dist/public/node/vendor/otel-js/export/InstantaneousMetricReader.js.map +1 -0
- package/dist/public/node/vendor/otel-js/service/BaseOtelService/BaseOtelService.d.ts +46 -0
- package/dist/public/node/vendor/otel-js/service/BaseOtelService/BaseOtelService.js +112 -0
- package/dist/public/node/vendor/otel-js/service/BaseOtelService/BaseOtelService.js.map +1 -0
- package/dist/public/node/vendor/otel-js/service/DefaultOtelService/DefaultMeterProvider.d.ts +13 -0
- package/dist/public/node/vendor/otel-js/service/DefaultOtelService/DefaultMeterProvider.js +36 -0
- package/dist/public/node/vendor/otel-js/service/DefaultOtelService/DefaultMeterProvider.js.map +1 -0
- package/dist/public/node/vendor/otel-js/service/DefaultOtelService/DefaultOtelService.d.ts +24 -0
- package/dist/public/node/vendor/otel-js/service/DefaultOtelService/DefaultOtelService.js +33 -0
- package/dist/public/node/vendor/otel-js/service/DefaultOtelService/DefaultOtelService.js.map +1 -0
- package/dist/public/node/vendor/otel-js/service/types.d.ts +43 -0
- package/dist/public/node/vendor/otel-js/service/types.js +7 -0
- package/dist/public/node/vendor/otel-js/service/types.js.map +1 -0
- package/dist/public/node/vendor/otel-js/utils/throttle.d.ts +7 -0
- package/dist/public/node/vendor/otel-js/utils/throttle.js +42 -0
- package/dist/public/node/vendor/otel-js/utils/throttle.js.map +1 -0
- package/dist/public/node/vendor/otel-js/utils/validators.d.ts +1 -0
- package/dist/public/node/vendor/otel-js/utils/validators.js +11 -0
- package/dist/public/node/vendor/otel-js/utils/validators.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +25 -16
- package/assets/cli-ruby/RELEASING.md +0 -72
|
@@ -8,13 +8,15 @@ module Theme
|
|
|
8
8
|
options do |parser, flags|
|
|
9
9
|
parser.on("--url=URL") { |url| flags[:url] = url }
|
|
10
10
|
parser.on("--port=PORT") { |port| flags[:port] = port }
|
|
11
|
+
parser.on("--theme=THEME") { |theme| flags[:theme] = theme }
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def call(_args, _name)
|
|
14
15
|
url = options.flags[:url]
|
|
15
16
|
port = options.flags[:port]
|
|
17
|
+
theme = options.flags[:theme]
|
|
16
18
|
|
|
17
|
-
ShopifyCLI::Theme::Repl.new(@ctx, url, port).run
|
|
19
|
+
ShopifyCLI::Theme::Repl.new(@ctx, url, port, theme).run
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
end
|
|
@@ -6,13 +6,11 @@ module ShopifyCLI
|
|
|
6
6
|
class AuthDevServer < ShopifyCLI::Theme::DevServer
|
|
7
7
|
attr_accessor :app, :repl
|
|
8
8
|
|
|
9
|
-
REPL_THEME = "liquid-console-repl"
|
|
10
|
-
|
|
11
9
|
class << self
|
|
12
|
-
def start(ctx, repl, port)
|
|
10
|
+
def start(ctx, repl, port, theme = "liquid-console-repl")
|
|
13
11
|
instance.repl = repl
|
|
14
12
|
|
|
15
|
-
super(ctx, nil, port: port, theme:
|
|
13
|
+
super(ctx, nil, port: port, theme: theme)
|
|
16
14
|
end
|
|
17
15
|
end
|
|
18
16
|
|
|
@@ -17,12 +17,13 @@ require_relative "repl/snippet"
|
|
|
17
17
|
module ShopifyCLI
|
|
18
18
|
module Theme
|
|
19
19
|
class Repl
|
|
20
|
-
attr_reader :ctx, :url, :port, :session, :storefront_digest, :secure_session_id
|
|
20
|
+
attr_reader :ctx, :url, :port, :theme, :session, :storefront_digest, :secure_session_id
|
|
21
21
|
|
|
22
|
-
def initialize(ctx, url, port)
|
|
22
|
+
def initialize(ctx, url, port, theme)
|
|
23
23
|
@ctx = ctx
|
|
24
24
|
@url = url
|
|
25
25
|
@port = port
|
|
26
|
+
@theme = theme
|
|
26
27
|
@session = []
|
|
27
28
|
end
|
|
28
29
|
|
|
@@ -76,7 +77,7 @@ module ShopifyCLI
|
|
|
76
77
|
def authenticate!
|
|
77
78
|
# Currently, Shopify CLI can't bypass the store password, so the
|
|
78
79
|
# `AuthDevServer` gets the session to perform requests at the SFR.
|
|
79
|
-
ShopifyCLI::Theme::Repl::AuthDevServer.start(ctx, self, port)
|
|
80
|
+
ShopifyCLI::Theme::Repl::AuthDevServer.start(ctx, self, port, theme)
|
|
80
81
|
end
|
|
81
82
|
|
|
82
83
|
def api
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const environmentVariables: {
|
|
2
2
|
alwaysLogAnalytics: string;
|
|
3
|
+
alwaysLogMetrics: string;
|
|
3
4
|
deviceAuth: string;
|
|
4
5
|
enableCliRedirect: string;
|
|
5
6
|
env: string;
|
|
@@ -26,6 +27,7 @@ export declare const environmentVariables: {
|
|
|
26
27
|
organization: string;
|
|
27
28
|
identityToken: string;
|
|
28
29
|
refreshToken: string;
|
|
30
|
+
otelURL: string;
|
|
29
31
|
};
|
|
30
32
|
export declare const systemEnvironmentVariables: {
|
|
31
33
|
backendPort: string;
|
|
@@ -8,6 +8,7 @@ const cacheFolder = () => {
|
|
|
8
8
|
};
|
|
9
9
|
export const environmentVariables = {
|
|
10
10
|
alwaysLogAnalytics: 'SHOPIFY_CLI_ALWAYS_LOG_ANALYTICS',
|
|
11
|
+
alwaysLogMetrics: 'SHOPIFY_CLI_ALWAYS_LOG_METRICS',
|
|
11
12
|
deviceAuth: 'SHOPIFY_CLI_DEVICE_AUTH',
|
|
12
13
|
enableCliRedirect: 'SHOPIFY_CLI_ENABLE_CLI_REDIRECT',
|
|
13
14
|
env: 'SHOPIFY_CLI_ENV',
|
|
@@ -35,6 +36,7 @@ export const environmentVariables = {
|
|
|
35
36
|
organization: 'SHOPIFY_CLI_ORGANIZATION',
|
|
36
37
|
identityToken: 'SHOPIFY_CLI_IDENTITY_TOKEN',
|
|
37
38
|
refreshToken: 'SHOPIFY_CLI_REFRESH_TOKEN',
|
|
39
|
+
otelURL: 'SHOPIFY_CLI_OTEL_EXPORTER_OTLP_ENDPOINT',
|
|
38
40
|
};
|
|
39
41
|
export const systemEnvironmentVariables = {
|
|
40
42
|
backendPort: 'BACKEND_PORT',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/private/node/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,2BAA2B,CAAA;AAClD,OAAO,QAAQ,MAAM,WAAW,CAAA;AAEhC,MAAM,UAAU,GAAG,aAAa,CAAA;AAEhC,MAAM,WAAW,GAAG,GAAG,EAAE;IACvB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAA;IACjE,OAAO,QAAQ,CAAC,UAAU,CAAC,CAAC,KAAK,CAAA;AACnC,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,kBAAkB,EAAE,kCAAkC;IACtD,UAAU,EAAE,yBAAyB;IACrC,iBAAiB,EAAE,iCAAiC;IACpD,GAAG,EAAE,iBAAiB;IACtB,aAAa,EAAE,oBAAoB;IACnC,WAAW,EAAE,0BAA0B;IACvC,aAAa,EAAE,4BAA4B;IAC3C,SAAS,EAAE,qBAAqB;IAChC,UAAU,EAAE,qBAAqB;IACjC,eAAe,EAAE,+BAA+B;IAChD,YAAY,EAAE,eAAe;IAC7B,UAAU,EAAE,yBAAyB;IACrC,QAAQ,EAAE,mBAAmB;IAC7B,OAAO,EAAE,sBAAsB;IAC/B,eAAe,EAAE,+BAA+B;IAChD,eAAe,EAAE,+BAA+B;IAChD,mEAAmE;IACnE,UAAU,EAAE,YAAY;IACxB,aAAa,EAAE,gBAAgB;IAC/B,6BAA6B,EAAE,0CAA0C;IACzE,MAAM,EAAE,sBAAsB;IAC9B,UAAU,EAAE,aAAa;IACzB,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,aAAa;IAC1B,WAAW,EAAE,eAAe;IAC5B,YAAY,EAAE,0BAA0B;IACxC,aAAa,EAAE,4BAA4B;IAC3C,YAAY,EAAE,2BAA2B;
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/private/node/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,2BAA2B,CAAA;AAClD,OAAO,QAAQ,MAAM,WAAW,CAAA;AAEhC,MAAM,UAAU,GAAG,aAAa,CAAA;AAEhC,MAAM,WAAW,GAAG,GAAG,EAAE;IACvB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAA;IACjE,OAAO,QAAQ,CAAC,UAAU,CAAC,CAAC,KAAK,CAAA;AACnC,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,kBAAkB,EAAE,kCAAkC;IACtD,gBAAgB,EAAE,gCAAgC;IAClD,UAAU,EAAE,yBAAyB;IACrC,iBAAiB,EAAE,iCAAiC;IACpD,GAAG,EAAE,iBAAiB;IACtB,aAAa,EAAE,oBAAoB;IACnC,WAAW,EAAE,0BAA0B;IACvC,aAAa,EAAE,4BAA4B;IAC3C,SAAS,EAAE,qBAAqB;IAChC,UAAU,EAAE,qBAAqB;IACjC,eAAe,EAAE,+BAA+B;IAChD,YAAY,EAAE,eAAe;IAC7B,UAAU,EAAE,yBAAyB;IACrC,QAAQ,EAAE,mBAAmB;IAC7B,OAAO,EAAE,sBAAsB;IAC/B,eAAe,EAAE,+BAA+B;IAChD,eAAe,EAAE,+BAA+B;IAChD,mEAAmE;IACnE,UAAU,EAAE,YAAY;IACxB,aAAa,EAAE,gBAAgB;IAC/B,6BAA6B,EAAE,0CAA0C;IACzE,MAAM,EAAE,sBAAsB;IAC9B,UAAU,EAAE,aAAa;IACzB,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,aAAa;IAC1B,WAAW,EAAE,eAAe;IAC5B,YAAY,EAAE,0BAA0B;IACxC,aAAa,EAAE,4BAA4B;IAC3C,YAAY,EAAE,2BAA2B;IACzC,OAAO,EAAE,yCAAyC;CACnD,CAAA;AAED,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,WAAW,EAAE,cAAc;CAC5B,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,WAAW,EAAE;QACX,GAAG,EAAE,kBAAkB;KACxB;IACD,WAAW,EAAE;QACX,KAAK,EAAE;YACL,IAAI,EAAE,GAAG,EAAE;gBACT,OAAO,WAAW,EAAE,CAAA;YACtB,CAAC;YACD,MAAM,EAAE;gBACN,IAAI,EAAE,GAAG,EAAE;oBACT,OAAO,QAAQ,CAAC,WAAW,EAAE,EAAE,QAAQ,CAAC,CAAA;gBAC1C,CAAC;gBACD,QAAQ,EAAE,GAAG,EAAE;oBACb,OAAO,QAAQ,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;gBACtD,CAAC;aACF;SACF;KACF;CACF,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,OAAO,EAAE,aAAa;CACvB,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,6BAA6B,EAAE,CAAC;CACjC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,kCAAkC,CAAA","sourcesContent":["import {joinPath} from '../../public/node/path.js'\nimport envPaths from 'env-paths'\n\nconst identifier = 'shopify-cli'\n\nconst cacheFolder = () => {\n if (process.env.XDG_CACHE_HOME) return process.env.XDG_CACHE_HOME\n return envPaths(identifier).cache\n}\n\nexport const environmentVariables = {\n alwaysLogAnalytics: 'SHOPIFY_CLI_ALWAYS_LOG_ANALYTICS',\n alwaysLogMetrics: 'SHOPIFY_CLI_ALWAYS_LOG_METRICS',\n deviceAuth: 'SHOPIFY_CLI_DEVICE_AUTH',\n enableCliRedirect: 'SHOPIFY_CLI_ENABLE_CLI_REDIRECT',\n env: 'SHOPIFY_CLI_ENV',\n firstPartyDev: 'SHOPIFY_CLI_1P_DEV',\n noAnalytics: 'SHOPIFY_CLI_NO_ANALYTICS',\n partnersToken: 'SHOPIFY_CLI_PARTNERS_TOKEN',\n runAsUser: 'SHOPIFY_RUN_AS_USER',\n serviceEnv: 'SHOPIFY_SERVICE_ENV',\n skipCliRedirect: 'SHOPIFY_CLI_SKIP_CLI_REDIRECT',\n spinInstance: 'SPIN_INSTANCE',\n themeToken: 'SHOPIFY_CLI_THEME_TOKEN',\n unitTest: 'SHOPIFY_UNIT_TEST',\n verbose: 'SHOPIFY_FLAG_VERBOSE',\n noThemeBundling: 'SHOPIFY_CLI_NO_THEME_BUNDLING',\n bundledThemeCLI: 'SHOPIFY_CLI_BUNDLED_THEME_CLI',\n // Variables to detect if the CLI is running in a cloud environment\n codespaces: 'CODESPACES',\n codespaceName: 'CODESPACE_NAME',\n codespacePortForwardingDomain: 'GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN',\n gitpod: 'GITPOD_WORKSPACE_URL',\n cloudShell: 'CLOUD_SHELL',\n spin: 'SPIN',\n spinAppPort: 'SERVER_PORT',\n spinAppHost: 'SPIN_APP_HOST',\n organization: 'SHOPIFY_CLI_ORGANIZATION',\n identityToken: 'SHOPIFY_CLI_IDENTITY_TOKEN',\n refreshToken: 'SHOPIFY_CLI_REFRESH_TOKEN',\n otelURL: 'SHOPIFY_CLI_OTEL_EXPORTER_OTLP_ENDPOINT',\n}\n\nexport const systemEnvironmentVariables = {\n backendPort: 'BACKEND_PORT',\n}\n\nexport const pathConstants = {\n executables: {\n dev: '/opt/dev/bin/dev',\n },\n directories: {\n cache: {\n path: () => {\n return cacheFolder()\n },\n vendor: {\n path: () => {\n return joinPath(cacheFolder(), 'vendor')\n },\n binaries: () => {\n return joinPath(cacheFolder(), 'vendor', 'binaries')\n },\n },\n },\n },\n}\n\nexport const keychainConstants = {\n service: 'shopify-cli',\n}\n\nexport const sessionConstants = {\n expirationTimeMarginInMinutes: 4,\n}\n\nexport const bugsnagApiKey = '9e1e6889176fd0c795d5c659225e0fae'\n"]}
|
|
@@ -14,12 +14,11 @@ export function getCIMetadata(envName, envs) {
|
|
|
14
14
|
switch (envName) {
|
|
15
15
|
case 'bitbucket':
|
|
16
16
|
return {
|
|
17
|
-
actor: envs.BITBUCKET_COMMIT_AUTHOR,
|
|
18
17
|
branch: envs.BITBUCKET_BRANCH,
|
|
19
18
|
build: envs.BITBUCKET_BUILD_NUMBER,
|
|
20
19
|
commitSha: envs.BITBUCKET_COMMIT,
|
|
21
20
|
run: envs.BITBUCKET_BUILD_NUMBER,
|
|
22
|
-
url: envs.
|
|
21
|
+
url: `https://bitbucket.org/${envs.BITBUCKET_WORKSPACE}/${envs.BITBUCKET_REPO_SLUG}/pipelines/results/${envs.BITBUCKET_BUILD_NUMBER}`,
|
|
23
22
|
};
|
|
24
23
|
case 'circleci':
|
|
25
24
|
return {
|
|
@@ -49,7 +48,7 @@ export function getCIMetadata(envName, envs) {
|
|
|
49
48
|
commitSha: envs.CI_COMMIT_SHA,
|
|
50
49
|
commitMessage: envs.CI_COMMIT_MESSAGE,
|
|
51
50
|
run: envs.CI_RUNNER_ID,
|
|
52
|
-
url: envs.
|
|
51
|
+
url: envs.CI_PIPELINE_URL,
|
|
53
52
|
};
|
|
54
53
|
default:
|
|
55
54
|
return {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilities.js","sourceRoot":"","sources":["../../../../src/private/node/context/utilities.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,QAA4B;IAChD,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACpD,OAAO,KAAK,CAAA;KACb;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,IAAuB;IACpE,QAAQ,OAAO,EAAE;QACf,KAAK,WAAW;YACd,OAAO;gBACL,
|
|
1
|
+
{"version":3,"file":"utilities.js","sourceRoot":"","sources":["../../../../src/private/node/context/utilities.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,QAA4B;IAChD,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACpD,OAAO,KAAK,CAAA;KACb;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,IAAuB;IACpE,QAAQ,OAAO,EAAE;QACf,KAAK,WAAW;YACd,OAAO;gBACL,MAAM,EAAE,IAAI,CAAC,gBAAgB;gBAC7B,KAAK,EAAE,IAAI,CAAC,sBAAsB;gBAClC,SAAS,EAAE,IAAI,CAAC,gBAAgB;gBAChC,GAAG,EAAE,IAAI,CAAC,sBAAsB;gBAChC,GAAG,EAAE,yBAAyB,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,mBAAmB,sBAAsB,IAAI,CAAC,sBAAsB,EAAE;aACtI,CAAA;QACH,KAAK,UAAU;YACb,OAAO;gBACL,KAAK,EAAE,IAAI,CAAC,eAAe;gBAC3B,MAAM,EAAE,IAAI,CAAC,aAAa;gBAC1B,KAAK,EAAE,IAAI,CAAC,gBAAgB;gBAC5B,SAAS,EAAE,IAAI,CAAC,WAAW;gBAC3B,GAAG,EAAE,IAAI,CAAC,kBAAkB;gBAC5B,GAAG,EAAE,IAAI,CAAC,gBAAgB;aAC3B,CAAA;QACH,KAAK,QAAQ;YACX,OAAO;gBACL,KAAK,EAAE,IAAI,CAAC,YAAY;gBACxB,OAAO,EAAE,IAAI,CAAC,kBAAkB;gBAChC,MAAM,EAAE,IAAI,CAAC,eAAe;gBAC5B,KAAK,EAAE,IAAI,CAAC,aAAa;gBACzB,SAAS,EAAE,IAAI,CAAC,UAAU;gBAC1B,GAAG,EAAE,IAAI,CAAC,aAAa;gBACvB,SAAS,EAAE,IAAI,CAAC,iBAAiB;gBACjC,GAAG,EAAE,GAAG,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,iBAAiB,iBAAiB,IAAI,CAAC,aAAa,EAAE;aAC9F,CAAA;QACH,KAAK,QAAQ;YACX,OAAO;gBACL,KAAK,EAAE,IAAI,CAAC,iBAAiB;gBAC7B,MAAM,EAAE,IAAI,CAAC,kBAAkB;gBAC/B,KAAK,EAAE,IAAI,CAAC,cAAc;gBAC1B,SAAS,EAAE,IAAI,CAAC,aAAa;gBAC7B,aAAa,EAAE,IAAI,CAAC,iBAAiB;gBACrC,GAAG,EAAE,IAAI,CAAC,YAAY;gBACtB,GAAG,EAAE,IAAI,CAAC,eAAe;aAC1B,CAAA;QACH;YACE,OAAO,EAAE,CAAA;KACZ;AACH,CAAC","sourcesContent":["/**\n * Returns whether an environment variable has been set and is non-empty\n */\nexport function isSet(variable: string | undefined): boolean {\n if (variable === undefined || variable.trim() === '') {\n return false\n }\n return true\n}\n\n/**\n * Returns an object with environment variables from the specified CI environment.\n */\nexport function getCIMetadata(envName: string, envs: NodeJS.ProcessEnv): Metadata {\n switch (envName) {\n case 'bitbucket':\n return {\n branch: envs.BITBUCKET_BRANCH,\n build: envs.BITBUCKET_BUILD_NUMBER,\n commitSha: envs.BITBUCKET_COMMIT,\n run: envs.BITBUCKET_BUILD_NUMBER,\n url: `https://bitbucket.org/${envs.BITBUCKET_WORKSPACE}/${envs.BITBUCKET_REPO_SLUG}/pipelines/results/${envs.BITBUCKET_BUILD_NUMBER}`,\n }\n case 'circleci':\n return {\n actor: envs.CIRCLE_USERNAME,\n branch: envs.CIRCLE_BRANCH,\n build: envs.CIRCLE_BUILD_NUM,\n commitSha: envs.CIRCLE_SHA1,\n run: envs.CIRCLE_WORKFLOW_ID,\n url: envs.CIRCLE_BUILD_URL,\n }\n case 'github':\n return {\n actor: envs.GITHUB_ACTOR,\n attempt: envs.GITHUB_RUN_ATTEMPT,\n branch: envs.GITHUB_REF_NAME,\n build: envs.GITHUB_RUN_ID,\n commitSha: envs.GITHUB_SHA,\n run: envs.GITHUB_RUN_ID,\n runNumber: envs.GITHUB_RUN_NUMBER,\n url: `${envs.GITHUB_SERVER_URL}/${envs.GITHUB_REPOSITORY}/actions/runs/${envs.GITHUB_RUN_ID}`,\n }\n case 'gitlab':\n return {\n actor: envs.GITLAB_USER_LOGIN,\n branch: envs.CI_COMMIT_REF_NAME,\n build: envs.CI_PIPELINE_ID,\n commitSha: envs.CI_COMMIT_SHA,\n commitMessage: envs.CI_COMMIT_MESSAGE,\n run: envs.CI_RUNNER_ID,\n url: envs.CI_PIPELINE_URL,\n }\n default:\n return {}\n }\n}\n\nexport interface Metadata {\n actor?: string\n attempt?: string\n branch?: string\n build?: string\n commitMessage?: string\n commitSha?: string\n run?: string\n runNumber?: string\n url?: string\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { OtelService } from '../../public/node/vendor/otel-js/service/types.js';
|
|
2
|
+
import { DefaultOtelServiceOptions } from '../../public/node/vendor/otel-js/service/DefaultOtelService/DefaultOtelService.js';
|
|
3
|
+
type MetricRecorder = 'console' | {
|
|
4
|
+
type: 'otel';
|
|
5
|
+
otel: Pick<OtelService, 'record'>;
|
|
6
|
+
};
|
|
7
|
+
interface Timing {
|
|
8
|
+
active: number;
|
|
9
|
+
network: number;
|
|
10
|
+
prompt: number;
|
|
11
|
+
}
|
|
12
|
+
interface CreateMetricRecorderOptions {
|
|
13
|
+
skipMetricAnalytics: boolean;
|
|
14
|
+
otelOptions: Omit<DefaultOtelServiceOptions, 'env' | 'otelEndpoint'>;
|
|
15
|
+
}
|
|
16
|
+
interface RecordMetricsOptions {
|
|
17
|
+
/** If true, don't log anything */
|
|
18
|
+
skipMetricAnalytics: boolean;
|
|
19
|
+
/** The CLI version running the command */
|
|
20
|
+
cliVersion: string;
|
|
21
|
+
/** The plug-in that owns the command */
|
|
22
|
+
owningPlugin: string;
|
|
23
|
+
/** The command name, e.g. `app dev` */
|
|
24
|
+
command: string;
|
|
25
|
+
/** The exit mode for the command */
|
|
26
|
+
exitMode: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Record reliability metrics.
|
|
30
|
+
*/
|
|
31
|
+
export declare function recordMetrics(options: RecordMetricsOptions, timing: Timing, recorderFactory?: (options: CreateMetricRecorderOptions) => MetricRecorder): Promise<void>;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { MetricInstrumentType } from '../../public/node/vendor/otel-js/service/types.js';
|
|
2
|
+
import { outputContent, outputDebug, outputToken } from '../../public/node/output.js';
|
|
3
|
+
import { DefaultOtelService, } from '../../public/node/vendor/otel-js/service/DefaultOtelService/DefaultOtelService.js';
|
|
4
|
+
import { isUnitTest, opentelemetryDomain } from '../../public/node/context/local.js';
|
|
5
|
+
import { isSpinEnvironment } from '../../public/node/context/spin.js';
|
|
6
|
+
import { ValueType } from '@opentelemetry/api';
|
|
7
|
+
var Name;
|
|
8
|
+
(function (Name) {
|
|
9
|
+
Name["Counter"] = "cli_commands_total";
|
|
10
|
+
Name["Duration"] = "cli_commands_duration_ms";
|
|
11
|
+
Name["Elapsed"] = "cli_commands_wall_clock_elapsed_ms";
|
|
12
|
+
})(Name || (Name = {}));
|
|
13
|
+
/**
|
|
14
|
+
* Record reliability metrics.
|
|
15
|
+
*/
|
|
16
|
+
export async function recordMetrics(options, timing, recorderFactory = createMetricRecorder) {
|
|
17
|
+
const recorder = recorderFactory({
|
|
18
|
+
skipMetricAnalytics: options.skipMetricAnalytics,
|
|
19
|
+
otelOptions: defaultOtelOptions(),
|
|
20
|
+
});
|
|
21
|
+
let regularisedCliVersion = options.cliVersion;
|
|
22
|
+
if (options.cliVersion.includes('nightly')) {
|
|
23
|
+
regularisedCliVersion = 'nightly';
|
|
24
|
+
}
|
|
25
|
+
else if (options.cliVersion.includes('pre')) {
|
|
26
|
+
regularisedCliVersion = 'pre';
|
|
27
|
+
}
|
|
28
|
+
const labels = {
|
|
29
|
+
exit: options.exitMode,
|
|
30
|
+
job: `${options.owningPlugin}::${options.command}`,
|
|
31
|
+
cli_version: regularisedCliVersion,
|
|
32
|
+
};
|
|
33
|
+
recordCommandCounter(recorder, labels);
|
|
34
|
+
recordCommandTiming(recorder, labels, timing);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get the default options for the OTEL service. These are the same across environments.
|
|
38
|
+
*/
|
|
39
|
+
function defaultOtelOptions() {
|
|
40
|
+
return {
|
|
41
|
+
serviceName: 'shopify-cli',
|
|
42
|
+
throttleLimit: 1000,
|
|
43
|
+
prefixMetric: false,
|
|
44
|
+
metrics: {
|
|
45
|
+
[Name.Counter]: {
|
|
46
|
+
type: MetricInstrumentType.Counter,
|
|
47
|
+
description: 'Total number of CLI commands executed',
|
|
48
|
+
valueType: ValueType.INT,
|
|
49
|
+
},
|
|
50
|
+
[Name.Duration]: {
|
|
51
|
+
type: MetricInstrumentType.Histogram,
|
|
52
|
+
description: 'Total time spent in execution of CLI commands. Does not include time spent waiting for network, prompts, etc.',
|
|
53
|
+
valueType: ValueType.INT,
|
|
54
|
+
boundaries: [0, 100, 250, 500, 1000, 2000, 5000, 10000, 20000, 50000],
|
|
55
|
+
},
|
|
56
|
+
[Name.Elapsed]: {
|
|
57
|
+
type: MetricInstrumentType.Histogram,
|
|
58
|
+
description: 'Total time elapsed from start to finish of CLI commands. Includes time spent waiting for network, prompts, etc.',
|
|
59
|
+
valueType: ValueType.INT,
|
|
60
|
+
boundaries: [0, 100, 250, 500, 1000, 2000, 5000, 10000, 20000, 50000],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Create the metric recorder for this command.
|
|
67
|
+
*
|
|
68
|
+
* If metric logging is disabled, or we are running in a unit test or Spin, we record to the console.
|
|
69
|
+
*
|
|
70
|
+
*/
|
|
71
|
+
function createMetricRecorder(options) {
|
|
72
|
+
let recorder = 'console';
|
|
73
|
+
if (!(options.skipMetricAnalytics || isUnitTest() || isSpinEnvironment())) {
|
|
74
|
+
recorder = {
|
|
75
|
+
type: 'otel',
|
|
76
|
+
otel: globalOtelService(options),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return recorder;
|
|
80
|
+
}
|
|
81
|
+
let _otelService;
|
|
82
|
+
/**
|
|
83
|
+
* OTEL service singleton.
|
|
84
|
+
*
|
|
85
|
+
* The service is a singleton as it uses a global diagnostic logger that assumes its the only one in the process.
|
|
86
|
+
*/
|
|
87
|
+
function globalOtelService(options) {
|
|
88
|
+
if (!_otelService) {
|
|
89
|
+
_otelService = new DefaultOtelService({
|
|
90
|
+
...options.otelOptions,
|
|
91
|
+
env: undefined,
|
|
92
|
+
otelEndpoint: `${opentelemetryDomain()}/v1/metrics`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return _otelService;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Log command counter metrics.
|
|
99
|
+
*/
|
|
100
|
+
function recordCommandCounter(recorder, labels) {
|
|
101
|
+
if (recorder === 'console') {
|
|
102
|
+
outputDebug(outputContent `[OTEL] record ${Name.Counter} counter ${outputToken.json({ labels })}`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
recorder.otel.record(Name.Counter, 1, labels);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Log command timing metrics.
|
|
109
|
+
*/
|
|
110
|
+
function recordCommandTiming(recorder, labels, timing) {
|
|
111
|
+
if (recorder === 'console') {
|
|
112
|
+
outputDebug(outputContent `[OTEL] record ${Name.Duration} histogram ${timing.active.toString()}ms ${outputToken.json({
|
|
113
|
+
labels,
|
|
114
|
+
})}`);
|
|
115
|
+
outputDebug(outputContent `[OTEL] record ${Name.Elapsed} histogram stage="active" ${timing.active.toString()}ms`);
|
|
116
|
+
outputDebug(outputContent `[OTEL] record ${Name.Elapsed} histogram stage="network" ${timing.network.toString()}ms`);
|
|
117
|
+
outputDebug(outputContent `[OTEL] record ${Name.Elapsed} histogram stage="prompt" ${timing.prompt.toString()}ms`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (timing.active > 0) {
|
|
121
|
+
recorder.otel.record(Name.Duration, timing.active, labels);
|
|
122
|
+
recorder.otel.record(Name.Elapsed, timing.active, { ...labels, stage: 'active' });
|
|
123
|
+
}
|
|
124
|
+
if (timing.network > 0) {
|
|
125
|
+
recorder.otel.record(Name.Elapsed, timing.network, { ...labels, stage: 'network' });
|
|
126
|
+
}
|
|
127
|
+
if (timing.prompt > 0) {
|
|
128
|
+
recorder.otel.record(Name.Elapsed, timing.prompt, { ...labels, stage: 'prompt' });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=otel-metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otel-metrics.js","sourceRoot":"","sources":["../../../src/private/node/otel-metrics.ts"],"names":[],"mappings":"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","sourcesContent":["import {MetricInstrumentType, OtelService} from '../../public/node/vendor/otel-js/service/types.js'\nimport {outputContent, outputDebug, outputToken} from '../../public/node/output.js'\nimport {\n DefaultOtelService,\n DefaultOtelServiceOptions,\n} from '../../public/node/vendor/otel-js/service/DefaultOtelService/DefaultOtelService.js'\nimport {isUnitTest, opentelemetryDomain} from '../../public/node/context/local.js'\nimport {isSpinEnvironment} from '../../public/node/context/spin.js'\nimport {ValueType} from '@opentelemetry/api'\n\ntype MetricRecorder =\n | 'console'\n | {\n type: 'otel'\n otel: Pick<OtelService, 'record'>\n }\n\n// this should be type, not interface\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ntype Labels = {\n exit: string\n job: string\n cli_version: string\n}\n\ninterface Timing {\n active: number\n network: number\n prompt: number\n}\n\nenum Name {\n Counter = 'cli_commands_total',\n Duration = 'cli_commands_duration_ms',\n Elapsed = 'cli_commands_wall_clock_elapsed_ms',\n}\n\ninterface CreateMetricRecorderOptions {\n skipMetricAnalytics: boolean\n otelOptions: Omit<DefaultOtelServiceOptions, 'env' | 'otelEndpoint'>\n}\n\ninterface RecordMetricsOptions {\n /** If true, don't log anything */\n skipMetricAnalytics: boolean\n /** The CLI version running the command */\n cliVersion: string\n /** The plug-in that owns the command */\n owningPlugin: string\n /** The command name, e.g. `app dev` */\n command: string\n /** The exit mode for the command */\n exitMode: string\n}\n\n/**\n * Record reliability metrics.\n */\nexport async function recordMetrics(\n options: RecordMetricsOptions,\n timing: Timing,\n recorderFactory: (options: CreateMetricRecorderOptions) => MetricRecorder = createMetricRecorder,\n) {\n const recorder = recorderFactory({\n skipMetricAnalytics: options.skipMetricAnalytics,\n otelOptions: defaultOtelOptions(),\n })\n\n let regularisedCliVersion = options.cliVersion\n\n if (options.cliVersion.includes('nightly')) {\n regularisedCliVersion = 'nightly'\n } else if (options.cliVersion.includes('pre')) {\n regularisedCliVersion = 'pre'\n }\n const labels = {\n exit: options.exitMode,\n job: `${options.owningPlugin}::${options.command}`,\n cli_version: regularisedCliVersion,\n }\n\n recordCommandCounter(recorder, labels)\n recordCommandTiming(recorder, labels, timing)\n}\n\n/**\n * Get the default options for the OTEL service. These are the same across environments.\n */\nfunction defaultOtelOptions(): Omit<DefaultOtelServiceOptions, 'env' | 'otelEndpoint'> {\n return {\n serviceName: 'shopify-cli',\n throttleLimit: 1000,\n prefixMetric: false,\n metrics: {\n [Name.Counter]: {\n type: MetricInstrumentType.Counter,\n description: 'Total number of CLI commands executed',\n valueType: ValueType.INT,\n },\n [Name.Duration]: {\n type: MetricInstrumentType.Histogram,\n description:\n 'Total time spent in execution of CLI commands. Does not include time spent waiting for network, prompts, etc.',\n valueType: ValueType.INT,\n boundaries: [0, 100, 250, 500, 1000, 2000, 5000, 10_000, 20_000, 50_000],\n },\n [Name.Elapsed]: {\n type: MetricInstrumentType.Histogram,\n description:\n 'Total time elapsed from start to finish of CLI commands. Includes time spent waiting for network, prompts, etc.',\n valueType: ValueType.INT,\n boundaries: [0, 100, 250, 500, 1000, 2000, 5000, 10_000, 20_000, 50_000],\n },\n },\n }\n}\n\n/**\n * Create the metric recorder for this command.\n *\n * If metric logging is disabled, or we are running in a unit test or Spin, we record to the console.\n *\n */\nfunction createMetricRecorder(options: CreateMetricRecorderOptions): MetricRecorder {\n let recorder: MetricRecorder = 'console'\n if (!(options.skipMetricAnalytics || isUnitTest() || isSpinEnvironment())) {\n recorder = {\n type: 'otel',\n otel: globalOtelService(options),\n }\n }\n return recorder\n}\n\nlet _otelService: OtelService | undefined\n\n/**\n * OTEL service singleton.\n *\n * The service is a singleton as it uses a global diagnostic logger that assumes its the only one in the process.\n */\nfunction globalOtelService(options: CreateMetricRecorderOptions): OtelService {\n if (!_otelService) {\n _otelService = new DefaultOtelService({\n ...options.otelOptions,\n env: undefined,\n otelEndpoint: `${opentelemetryDomain()}/v1/metrics`,\n })\n }\n return _otelService\n}\n\n/**\n * Log command counter metrics.\n */\nfunction recordCommandCounter(recorder: MetricRecorder, labels: Labels) {\n if (recorder === 'console') {\n outputDebug(outputContent`[OTEL] record ${Name.Counter} counter ${outputToken.json({labels})}`)\n return\n }\n recorder.otel.record(Name.Counter, 1, labels)\n}\n\n/**\n * Log command timing metrics.\n */\nfunction recordCommandTiming(recorder: MetricRecorder, labels: Labels, timing: Timing) {\n if (recorder === 'console') {\n outputDebug(\n outputContent`[OTEL] record ${Name.Duration} histogram ${timing.active.toString()}ms ${outputToken.json({\n labels,\n })}`,\n )\n outputDebug(outputContent`[OTEL] record ${Name.Elapsed} histogram stage=\"active\" ${timing.active.toString()}ms`)\n outputDebug(outputContent`[OTEL] record ${Name.Elapsed} histogram stage=\"network\" ${timing.network.toString()}ms`)\n outputDebug(outputContent`[OTEL] record ${Name.Elapsed} histogram stage=\"prompt\" ${timing.prompt.toString()}ms`)\n return\n }\n\n if (timing.active > 0) {\n recorder.otel.record(Name.Duration, timing.active, labels)\n recorder.otel.record(Name.Elapsed, timing.active, {...labels, stage: 'active'})\n }\n if (timing.network > 0) {\n recorder.otel.record(Name.Elapsed, timing.network, {...labels, stage: 'network'})\n }\n if (timing.prompt > 0) {\n recorder.otel.record(Name.Elapsed, timing.prompt, {...labels, stage: 'prompt'})\n }\n}\n"]}
|
|
@@ -6,6 +6,7 @@ import { AbortError, CancelExecution } from '../../../public/node/error.js';
|
|
|
6
6
|
import { identityFqdn } from '../../../public/node/context/fqdn.js';
|
|
7
7
|
import { keypress, renderConfirmationPrompt } from '../../../public/node/ui.js';
|
|
8
8
|
import { outputInfo } from '../../../public/node/output.js';
|
|
9
|
+
import { runWithTimer } from '../../../public/node/metadata.js';
|
|
9
10
|
import { checkPort as isPortAvailable } from 'get-port-please';
|
|
10
11
|
import findProcess from 'find-process';
|
|
11
12
|
export async function authorize(scopes, state = randomHex(30)) {
|
|
@@ -31,11 +32,13 @@ export async function authorize(scopes, state = randomHex(30)) {
|
|
|
31
32
|
await keypress();
|
|
32
33
|
url = `${url}?${new URLSearchParams(params).toString()}`;
|
|
33
34
|
await openURL(url);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
return runWithTimer('cmd_all_timing_prompts_ms')(async () => {
|
|
36
|
+
const result = await listenRedirect(host, port, url);
|
|
37
|
+
if (result.state !== state) {
|
|
38
|
+
throw new AbortError("The state received from the authentication doesn't match the one that initiated the authentication process.");
|
|
39
|
+
}
|
|
40
|
+
return { code: result.code, codeVerifier };
|
|
41
|
+
});
|
|
39
42
|
}
|
|
40
43
|
function generateRandomChallengePair() {
|
|
41
44
|
const codeVerifier = base64URLEncode(randomBytes(32));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../../../src/private/node/session/authorize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,eAAe,CAAA;AACtC,OAAO,EAAC,cAAc,EAAC,MAAM,wBAAwB,CAAA;AACrD,OAAO,EAAC,eAAe,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAC,MAAM,gCAAgC,CAAA;AAC9F,OAAO,EAAC,OAAO,EAAC,MAAM,gCAAgC,CAAA;AACtD,OAAO,EAAC,UAAU,EAAE,eAAe,EAAC,MAAM,+BAA+B,CAAA;AACzE,OAAO,EAAC,YAAY,EAAC,MAAM,sCAAsC,CAAA;AACjE,OAAO,EAAC,QAAQ,EAAE,wBAAwB,EAAC,MAAM,4BAA4B,CAAA;AAC7E,OAAO,EAAC,UAAU,EAAC,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,SAAS,IAAI,eAAe,EAAC,MAAM,iBAAiB,CAAA;AAC5D,OAAO,WAAW,MAAM,cAAc,CAAA;AAOtC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAgB,EAAE,QAAgB,SAAS,CAAC,EAAE,CAAC;IAC7E,MAAM,IAAI,GAAG,IAAI,CAAA;IACjB,MAAM,IAAI,GAAG,WAAW,CAAA;IACxB,MAAM,WAAW,GAAG,UAAU,IAAI,IAAI,IAAI,EAAE,CAAA;IAC5C,MAAM,IAAI,GAAG,MAAM,YAAY,EAAE,CAAA;IACjC,MAAM,gBAAgB,GAAG,QAAQ,EAAE,CAAA;IAEnC,MAAM,mCAAmC,CAAC,IAAI,CAAC,CAAA;IAE/C,IAAI,GAAG,GAAG,UAAU,IAAI,kBAAkB,CAAA;IAE1C,MAAM,EAAC,YAAY,EAAE,aAAa,EAAC,GAAG,2BAA2B,EAAE,CAAA;IAEnE,MAAM,MAAM,GAAG;QACb,SAAS,EAAE,gBAAgB;QAC3B,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACvB,YAAY,EAAE,WAAW;QACzB,KAAK;QACL,aAAa,EAAE,MAAM;QACrB,qBAAqB,EAAE,MAAM;QAC7B,cAAc,EAAE,aAAa;KAC9B,CAAA;IAED,UAAU,CAAC,oDAAoD,CAAC,CAAA;IAChE,UAAU,CAAC,yDAAyD,CAAC,CAAA;IACrE,MAAM,QAAQ,EAAE,CAAA;IAEhB,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAA;IACxD,MAAM,OAAO,CAAC,GAAG,CAAC,CAAA;IAElB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../../../src/private/node/session/authorize.ts"],"names":[],"mappings":"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","sourcesContent":["import {clientId} from './identity.js'\nimport {listenRedirect} from './redirect-listener.js'\nimport {base64URLEncode, randomBytes, randomHex, sha256} from '../../../public/node/crypto.js'\nimport {openURL} from '../../../public/node/system.js'\nimport {AbortError, CancelExecution} from '../../../public/node/error.js'\nimport {identityFqdn} from '../../../public/node/context/fqdn.js'\nimport {keypress, renderConfirmationPrompt} from '../../../public/node/ui.js'\nimport {outputInfo} from '../../../public/node/output.js'\nimport {runWithTimer} from '../../../public/node/metadata.js'\nimport {checkPort as isPortAvailable} from 'get-port-please'\nimport findProcess from 'find-process'\n\nexport interface CodeAuthResult {\n code: string\n codeVerifier: string\n}\n\nexport async function authorize(scopes: string[], state: string = randomHex(30)): Promise<CodeAuthResult> {\n const port = 3456\n const host = '127.0.0.1'\n const redirectUri = `http://${host}:${port}`\n const fqdn = await identityFqdn()\n const identityClientId = clientId()\n\n await validateRedirectionPortAvailability(port)\n\n let url = `http://${fqdn}/oauth/authorize`\n\n const {codeVerifier, codeChallenge} = generateRandomChallengePair()\n\n const params = {\n client_id: identityClientId,\n scope: scopes.join(' '),\n redirect_uri: redirectUri,\n state,\n response_type: 'code',\n code_challenge_method: 'S256',\n code_challenge: codeChallenge,\n }\n\n outputInfo('\\nTo run this command, log in to Shopify Partners.')\n outputInfo('👉 Press any key to open the login page on your browser')\n await keypress()\n\n url = `${url}?${new URLSearchParams(params).toString()}`\n await openURL(url)\n\n return runWithTimer('cmd_all_timing_prompts_ms')(async () => {\n const result = await listenRedirect(host, port, url)\n\n if (result.state !== state) {\n throw new AbortError(\n \"The state received from the authentication doesn't match the one that initiated the authentication process.\",\n )\n }\n\n return {code: result.code, codeVerifier}\n })\n}\n\nfunction generateRandomChallengePair() {\n const codeVerifier = base64URLEncode(randomBytes(32))\n const codeChallenge = base64URLEncode(sha256(codeVerifier))\n return {codeVerifier, codeChallenge}\n}\n\nasync function validateRedirectionPortAvailability(port: number) {\n const {killPortProcess} = await import('kill-port-process')\n\n if (await isPortAvailable(port)) {\n return\n }\n\n if (await terminateBlockingPortProcessPrompt(port, 'Authentication')) {\n await killPortProcess(port)\n } else {\n throw new CancelExecution()\n }\n}\n\nasync function terminateBlockingPortProcessPrompt(port: number, stepDescription: string): Promise<boolean> {\n const processInfo = await findProcess('port', port)\n const formattedProcessName =\n processInfo && processInfo.length > 0 && processInfo[0]?.name ? ` (${processInfo[0].name})` : ''\n\n return renderConfirmationPrompt({\n message: `${stepDescription} requires a port ${port} that's unavailable because it's running another process${formattedProcessName}. Terminate that process?`,\n confirmationMessage: 'Yes, terminate process in order to log in now',\n cancellationMessage: `No, cancel command and try later`,\n })\n}\n"]}
|
|
@@ -10,14 +10,16 @@ declare class Stderr extends EventEmitter {
|
|
|
10
10
|
}
|
|
11
11
|
export declare class Stdin extends EventEmitter {
|
|
12
12
|
isTTY: boolean;
|
|
13
|
+
data: string | null;
|
|
13
14
|
constructor(options?: {
|
|
14
15
|
isTTY?: boolean;
|
|
15
16
|
});
|
|
16
17
|
write: (data: string) => void;
|
|
17
18
|
setEncoding(): void;
|
|
18
19
|
setRawMode(): void;
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
ref(): void;
|
|
21
|
+
unref(): void;
|
|
22
|
+
read: () => string | null;
|
|
21
23
|
}
|
|
22
24
|
interface Instance {
|
|
23
25
|
rerender: (tree: ReactElement) => void;
|
|
@@ -16,15 +16,22 @@ class Stderr extends EventEmitter {
|
|
|
16
16
|
export class Stdin extends EventEmitter {
|
|
17
17
|
constructor(options = {}) {
|
|
18
18
|
super();
|
|
19
|
+
this.data = null;
|
|
19
20
|
this.write = (data) => {
|
|
20
|
-
this.
|
|
21
|
+
this.data = data;
|
|
22
|
+
this.emit('readable');
|
|
23
|
+
};
|
|
24
|
+
this.read = () => {
|
|
25
|
+
const data = this.data;
|
|
26
|
+
this.data = null;
|
|
27
|
+
return data;
|
|
21
28
|
};
|
|
22
29
|
this.isTTY = options.isTTY ?? true;
|
|
23
30
|
}
|
|
24
31
|
setEncoding() { }
|
|
25
32
|
setRawMode() { }
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
ref() { }
|
|
34
|
+
unref() { }
|
|
28
35
|
}
|
|
29
36
|
export const render = (tree, options = {}) => {
|
|
30
37
|
const stdout = new Stdout({ columns: 100 });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../../src/private/node/testing/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import {isTruthy} from '../../../public/node/context/utilities.js'\nimport {Stdout} from '../ui.js'\nimport {ReactElement} from 'react'\nimport {render as inkRender} from 'ink'\nimport {EventEmitter} from 'events'\n\nclass Stderr extends EventEmitter {\n readonly frames: string[] = []\n private _lastFrame?: string\n\n write = (frame: string) => {\n this.frames.push(frame)\n this._lastFrame = frame\n }\n\n lastFrame = () => this._lastFrame\n}\n\nexport class Stdin extends EventEmitter {\n isTTY: boolean\n\n constructor(options: {isTTY?: boolean} = {}) {\n super()\n this.isTTY = options.isTTY ?? true\n }\n\n write = (data: string) => {\n this.emit('data', data)\n }\n\n setEncoding() {}\n setRawMode() {}\n resume() {}\n pause() {}\n}\n\ninterface Instance {\n rerender: (tree: ReactElement) => void\n unmount: () => void\n cleanup: () => void\n stdout: Stdout\n stderr: Stderr\n stdin: Stdin\n frames: string[]\n lastFrame: () => string | undefined\n waitUntilExit: () => TrackedPromise<void>\n}\n\ninterface RenderOptions {\n stdout?: EventEmitter\n stderr?: EventEmitter\n stdin?: EventEmitter\n}\n\nexport const render = (tree: ReactElement, options: RenderOptions = {}): Instance => {\n const stdout = new Stdout({columns: 100})\n const stderr = new Stderr()\n const stdin = new Stdin()\n\n const instance = inkRender(tree, {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stdout: options.stdout ?? (stdout as any),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stderr: options.stderr ?? (stderr as any),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stdin: options.stdin ?? (stdin as any),\n debug: true,\n exitOnCtrlC: false,\n patchConsole: false,\n })\n\n return {\n rerender: instance.rerender,\n unmount: instance.unmount,\n cleanup: instance.cleanup,\n waitUntilExit: () => trackPromise(instance.waitUntilExit()),\n stdout,\n stderr,\n stdin,\n frames: stdout.frames,\n lastFrame: stdout.lastFrame,\n }\n}\n\n/**\n * Wait for the component to be ready to accept input.\n */\nexport function waitForInputsToBeReady() {\n return new Promise((resolve) => setTimeout(resolve, 100))\n}\n\n/**\n * Wait for the last frame to change to anything.\n */\nexport function waitForChange(func: () => void, getChangingValue: () => string | number | undefined) {\n return new Promise<void>((resolve) => {\n const initialValue = getChangingValue()\n\n func()\n\n const interval = setInterval(() => {\n if (getChangingValue() !== initialValue) {\n clearInterval(interval)\n resolve()\n }\n }, 10)\n })\n}\n\nexport function waitFor(func: () => void, condition: () => boolean) {\n return new Promise<void>((resolve) => {\n func()\n\n const interval = setInterval(() => {\n if (condition()) {\n clearInterval(interval)\n resolve()\n }\n }, 10)\n })\n}\n\n/**\n * Wait for the last frame to contain specific text.\n */\nexport function waitForContent(\n renderInstance: ReturnType<typeof render>,\n content: string,\n func: () => void = () => {},\n) {\n return waitFor(\n () => func(),\n () => renderInstance.lastFrame()!.includes(content),\n )\n}\n\n/**\n * Send input and wait for the last frame to change.\n *\n * Useful when you want to send some input and wait for anything to change in the interface.\n * If you need to wait for a specific change instead, you can use sendInputAndWaitForContent.\n */\nexport async function sendInputAndWaitForChange(renderInstance: ReturnType<typeof render>, ...inputs: string[]) {\n await waitForChange(() => inputs.forEach((input) => renderInstance.stdin.write(input)), renderInstance.lastFrame)\n // wait for another tick so we give time to react to update caches\n await new Promise((resolve) => setTimeout(resolve, 0))\n}\n\n/** Send input and wait a number of milliseconds.\n *\n * Useful if you know some what will happen after input will take a certain amount of time\n * and it will not cause any visible change so you can't use sendInputAndWaitForChange.\n * This function can also be used if you want to test that nothing changes after some input has been sent.\n */\nexport async function sendInputAndWait(\n renderInstance: ReturnType<typeof render>,\n waitTime: number,\n ...inputs: string[]\n) {\n inputs.forEach((input) => renderInstance.stdin.write(input))\n await new Promise((resolve) => setTimeout(resolve, waitTime))\n}\n\n/**\n * Send input and wait for the last frame to contain specific text.\n *\n * Useful when you want to send some input and wait for a specific change to happen.\n * If you need to wait for any change instead, you can use sendInputAndWaitForChange.\n */\nexport async function sendInputAndWaitForContent(\n renderInstance: ReturnType<typeof render>,\n content: string,\n ...inputs: string[]\n) {\n await waitForContent(renderInstance, content, () => inputs.forEach((input) => renderInstance.stdin.write(input)))\n}\n\n/** Function that is useful when you want to check the last frame of a component that unmounted.\n *\n * The reason this function exists is that in CI Ink will clear the last frame on unmount.\n */\nexport function getLastFrameAfterUnmount(renderInstance: ReturnType<typeof render>) {\n return isTruthy(process.env.CI) ? renderInstance.frames[renderInstance.frames.length - 2] : renderInstance.lastFrame()\n}\n\ntype TrackedPromise<T> = Promise<T> & {\n isFulfilled: () => boolean\n isPending: () => boolean\n isRejected: () => boolean\n}\n\nfunction trackPromise<T>(promise: Promise<T>): TrackedPromise<T> {\n let isPending = true\n let isRejected = false\n let isFulfilled = false\n\n const trackedPromise = promise.then(\n function (result) {\n isFulfilled = true\n isPending = false\n return result\n },\n function (error) {\n isRejected = true\n isPending = false\n throw error\n },\n ) as TrackedPromise<T>\n\n trackedPromise.isFulfilled = function () {\n return isFulfilled\n }\n trackedPromise.isPending = function () {\n return isPending\n }\n trackedPromise.isRejected = function () {\n return isRejected\n }\n\n return trackedPromise\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../../src/private/node/testing/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import {isTruthy} from '../../../public/node/context/utilities.js'\nimport {Stdout} from '../ui.js'\nimport {ReactElement} from 'react'\nimport {render as inkRender} from 'ink'\nimport {EventEmitter} from 'events'\n\nclass Stderr extends EventEmitter {\n readonly frames: string[] = []\n private _lastFrame?: string\n\n write = (frame: string) => {\n this.frames.push(frame)\n this._lastFrame = frame\n }\n\n lastFrame = () => this._lastFrame\n}\n\nexport class Stdin extends EventEmitter {\n isTTY: boolean\n data: string | null = null\n\n constructor(options: {isTTY?: boolean} = {}) {\n super()\n this.isTTY = options.isTTY ?? true\n }\n\n write = (data: string) => {\n this.data = data\n this.emit('readable')\n }\n\n setEncoding() {}\n setRawMode() {}\n ref() {}\n unref() {}\n read: () => string | null = () => {\n const data = this.data\n\n this.data = null\n\n return data\n }\n}\n\ninterface Instance {\n rerender: (tree: ReactElement) => void\n unmount: () => void\n cleanup: () => void\n stdout: Stdout\n stderr: Stderr\n stdin: Stdin\n frames: string[]\n lastFrame: () => string | undefined\n waitUntilExit: () => TrackedPromise<void>\n}\n\ninterface RenderOptions {\n stdout?: EventEmitter\n stderr?: EventEmitter\n stdin?: EventEmitter\n}\n\nexport const render = (tree: ReactElement, options: RenderOptions = {}): Instance => {\n const stdout = new Stdout({columns: 100})\n const stderr = new Stderr()\n const stdin = new Stdin()\n\n const instance = inkRender(tree, {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stdout: options.stdout ?? (stdout as any),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stderr: options.stderr ?? (stderr as any),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stdin: options.stdin ?? (stdin as any),\n debug: true,\n exitOnCtrlC: false,\n patchConsole: false,\n })\n\n return {\n rerender: instance.rerender,\n unmount: instance.unmount,\n cleanup: instance.cleanup,\n waitUntilExit: () => trackPromise(instance.waitUntilExit()),\n stdout,\n stderr,\n stdin,\n frames: stdout.frames,\n lastFrame: stdout.lastFrame,\n }\n}\n\n/**\n * Wait for the component to be ready to accept input.\n */\nexport function waitForInputsToBeReady() {\n return new Promise((resolve) => setTimeout(resolve, 100))\n}\n\n/**\n * Wait for the last frame to change to anything.\n */\nexport function waitForChange(func: () => void, getChangingValue: () => string | number | undefined) {\n return new Promise<void>((resolve) => {\n const initialValue = getChangingValue()\n\n func()\n\n const interval = setInterval(() => {\n if (getChangingValue() !== initialValue) {\n clearInterval(interval)\n resolve()\n }\n }, 10)\n })\n}\n\nexport function waitFor(func: () => void, condition: () => boolean) {\n return new Promise<void>((resolve) => {\n func()\n\n const interval = setInterval(() => {\n if (condition()) {\n clearInterval(interval)\n resolve()\n }\n }, 10)\n })\n}\n\n/**\n * Wait for the last frame to contain specific text.\n */\nexport function waitForContent(\n renderInstance: ReturnType<typeof render>,\n content: string,\n func: () => void = () => {},\n) {\n return waitFor(\n () => func(),\n () => renderInstance.lastFrame()!.includes(content),\n )\n}\n\n/**\n * Send input and wait for the last frame to change.\n *\n * Useful when you want to send some input and wait for anything to change in the interface.\n * If you need to wait for a specific change instead, you can use sendInputAndWaitForContent.\n */\nexport async function sendInputAndWaitForChange(renderInstance: ReturnType<typeof render>, ...inputs: string[]) {\n await waitForChange(() => inputs.forEach((input) => renderInstance.stdin.write(input)), renderInstance.lastFrame)\n // wait for another tick so we give time to react to update caches\n await new Promise((resolve) => setTimeout(resolve, 0))\n}\n\n/** Send input and wait a number of milliseconds.\n *\n * Useful if you know some what will happen after input will take a certain amount of time\n * and it will not cause any visible change so you can't use sendInputAndWaitForChange.\n * This function can also be used if you want to test that nothing changes after some input has been sent.\n */\nexport async function sendInputAndWait(\n renderInstance: ReturnType<typeof render>,\n waitTime: number,\n ...inputs: string[]\n) {\n inputs.forEach((input) => renderInstance.stdin.write(input))\n await new Promise((resolve) => setTimeout(resolve, waitTime))\n}\n\n/**\n * Send input and wait for the last frame to contain specific text.\n *\n * Useful when you want to send some input and wait for a specific change to happen.\n * If you need to wait for any change instead, you can use sendInputAndWaitForChange.\n */\nexport async function sendInputAndWaitForContent(\n renderInstance: ReturnType<typeof render>,\n content: string,\n ...inputs: string[]\n) {\n await waitForContent(renderInstance, content, () => inputs.forEach((input) => renderInstance.stdin.write(input)))\n}\n\n/** Function that is useful when you want to check the last frame of a component that unmounted.\n *\n * The reason this function exists is that in CI Ink will clear the last frame on unmount.\n */\nexport function getLastFrameAfterUnmount(renderInstance: ReturnType<typeof render>) {\n return isTruthy(process.env.CI) ? renderInstance.frames[renderInstance.frames.length - 2] : renderInstance.lastFrame()\n}\n\ntype TrackedPromise<T> = Promise<T> & {\n isFulfilled: () => boolean\n isPending: () => boolean\n isRejected: () => boolean\n}\n\nfunction trackPromise<T>(promise: Promise<T>): TrackedPromise<T> {\n let isPending = true\n let isRejected = false\n let isFulfilled = false\n\n const trackedPromise = promise.then(\n function (result) {\n isFulfilled = true\n isPending = false\n return result\n },\n function (error) {\n isRejected = true\n isPending = false\n throw error\n },\n ) as TrackedPromise<T>\n\n trackedPromise.isFulfilled = function () {\n return isFulfilled\n }\n trackedPromise.isPending = function () {\n return isPending\n }\n trackedPromise.isRejected = function () {\n return isRejected\n }\n\n return trackedPromise\n}\n"]}
|
|
@@ -3,7 +3,7 @@ import { TextInput } from './TextInput.js';
|
|
|
3
3
|
import { PromptLayout } from './Prompts/PromptLayout.js';
|
|
4
4
|
import { debounce } from '../../../../public/common/function.js';
|
|
5
5
|
import usePrompt, { PromptState } from '../hooks/use-prompt.js';
|
|
6
|
-
import React, { useCallback, useRef, useState } from 'react';
|
|
6
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
7
7
|
import { Box, useApp } from 'ink';
|
|
8
8
|
const MIN_NUMBER_OF_ITEMS_FOR_SEARCH = 5;
|
|
9
9
|
// eslint-disable-next-line react/function-component-definition
|
|
@@ -24,11 +24,15 @@ function AutocompletePrompt({ message, choices, infoTable, onSubmit, search, has
|
|
|
24
24
|
if (promptState === PromptState.Idle) {
|
|
25
25
|
setAnswer(answer);
|
|
26
26
|
setPromptState(PromptState.Submitted);
|
|
27
|
+
}
|
|
28
|
+
}, [promptState, setAnswer, setPromptState]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (promptState === PromptState.Submitted && answer) {
|
|
27
31
|
setSearchTerm('');
|
|
28
32
|
unmountInk();
|
|
29
33
|
onSubmit(answer.value);
|
|
30
34
|
}
|
|
31
|
-
}, [
|
|
35
|
+
}, [answer, onSubmit, promptState, unmountInk]);
|
|
32
36
|
const setLoadingWhenSlow = useRef();
|
|
33
37
|
// we want to set it each time so that searchTermRef always tracks searchTerm,
|
|
34
38
|
// this is NOT the same as writing useRef(searchTerm)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutocompletePrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/AutocompletePrompt.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAuC,MAAM,kBAAkB,CAAA;AAElF,OAAO,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAA;AAGxC,OAAO,EAAU,YAAY,EAAC,MAAM,2BAA2B,CAAA;AAC/D,OAAO,EAAC,QAAQ,EAAC,MAAM,uCAAuC,CAAA;AAE9D,OAAO,SAAS,EAAE,EAAC,WAAW,EAAC,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,EAAe,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAC,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"AutocompletePrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/AutocompletePrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["import {SelectInput, SelectInputProps, Item as SelectItem} from './SelectInput.js'\nimport {InfoTableProps} from './Prompts/InfoTable.js'\nimport {TextInput} from './TextInput.js'\nimport {InfoMessageProps} from './Prompts/InfoMessage.js'\nimport {GitDiffProps} from './Prompts/GitDiff.js'\nimport {Message, PromptLayout} from './Prompts/PromptLayout.js'\nimport {debounce} from '../../../../public/common/function.js'\nimport {AbortSignal} from '../../../../public/node/abort.js'\nimport usePrompt, {PromptState} from '../hooks/use-prompt.js'\nimport React, {ReactElement, useCallback, useEffect, useRef, useState} from 'react'\nimport {Box, useApp} from 'ink'\n\nexport interface SearchResults<T> {\n data: SelectItem<T>[]\n meta?: {\n hasNextPage: boolean\n }\n}\n\nexport interface AutocompletePromptProps<T> {\n message: Message\n choices: SelectInputProps<T>['items']\n onSubmit: (value: T) => void\n infoTable?: InfoTableProps['table']\n hasMorePages?: boolean\n search: (term: string) => Promise<SearchResults<T>>\n abortSignal?: AbortSignal\n infoMessage?: InfoMessageProps['message']\n gitDiff?: GitDiffProps['gitDiff']\n}\n\nconst MIN_NUMBER_OF_ITEMS_FOR_SEARCH = 5\n\n// eslint-disable-next-line react/function-component-definition\nfunction AutocompletePrompt<T>({\n message,\n choices,\n infoTable,\n onSubmit,\n search,\n hasMorePages: initialHasMorePages = false,\n abortSignal,\n infoMessage,\n gitDiff,\n}: React.PropsWithChildren<AutocompletePromptProps<T>>): ReactElement | null {\n const {exit: unmountInk} = useApp()\n const [searchTerm, setSearchTerm] = useState('')\n const [searchResults, setSearchResults] = useState<SelectItem<T>[]>(choices)\n const canSearch = choices.length > MIN_NUMBER_OF_ITEMS_FOR_SEARCH\n const [hasMorePages, setHasMorePages] = useState(initialHasMorePages)\n const {promptState, setPromptState, answer, setAnswer} = usePrompt<SelectItem<T> | undefined>({\n initialAnswer: undefined,\n })\n\n const paginatedSearch = useCallback(\n async (term: string) => {\n const results = await search(term)\n return results\n },\n [search],\n )\n\n const submitAnswer = useCallback(\n (answer: SelectItem<T>) => {\n if (promptState === PromptState.Idle) {\n setAnswer(answer)\n setPromptState(PromptState.Submitted)\n }\n },\n [promptState, setAnswer, setPromptState],\n )\n\n useEffect(() => {\n if (promptState === PromptState.Submitted && answer) {\n setSearchTerm('')\n unmountInk()\n onSubmit(answer.value)\n }\n }, [answer, onSubmit, promptState, unmountInk])\n\n const setLoadingWhenSlow = useRef<NodeJS.Timeout>()\n\n // we want to set it each time so that searchTermRef always tracks searchTerm,\n // this is NOT the same as writing useRef(searchTerm)\n const searchTermRef = useRef('')\n searchTermRef.current = searchTerm\n\n // disable exhaustive-deps because we want to memoize the debounce function itself\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const debounceSearch = useCallback(\n debounce((term: string) => {\n setLoadingWhenSlow.current = setTimeout(() => {\n setPromptState(PromptState.Loading)\n }, 100)\n paginatedSearch(term)\n .then((result) => {\n // while we were waiting for the promise to resolve, the user\n // has emptied the search term, so we want to show the default\n // choices instead\n if (searchTermRef.current.length === 0) {\n setSearchResults(choices)\n setHasMorePages(initialHasMorePages)\n } else {\n setSearchResults(result.data)\n setHasMorePages(result.meta?.hasNextPage ?? false)\n }\n\n setPromptState(PromptState.Idle)\n })\n .catch(() => {\n setPromptState(PromptState.Error)\n })\n .finally(() => {\n clearTimeout(setLoadingWhenSlow.current)\n })\n }, 300),\n [initialHasMorePages, choices, paginatedSearch, searchResults],\n )\n\n return (\n <PromptLayout\n message={message}\n state={promptState}\n infoTable={infoTable}\n infoMessage={infoMessage}\n gitDiff={gitDiff}\n abortSignal={abortSignal}\n header={\n promptState !== PromptState.Submitted && canSearch ? (\n <Box marginLeft={3}>\n <TextInput\n value={searchTerm}\n onChange={(term) => {\n setSearchTerm(term)\n\n if (term.length > 0) {\n debounceSearch(term)\n } else {\n debounceSearch.cancel()\n setPromptState(PromptState.Idle)\n setSearchResults(choices)\n }\n }}\n placeholder=\"Type to search...\"\n />\n </Box>\n ) : null\n }\n submittedAnswerLabel={answer?.label}\n input={\n <SelectInput\n items={searchResults}\n initialItems={choices}\n enableShortcuts={false}\n emptyMessage=\"No results found.\"\n highlightedTerm={searchTerm}\n loading={promptState === PromptState.Loading}\n errorMessage={\n promptState === PromptState.Error\n ? 'There has been an error while searching. Please try again later.'\n : undefined\n }\n hasMorePages={hasMorePages}\n morePagesMessage=\"Find what you're looking for by typing its name.\"\n onSubmit={submitAnswer}\n />\n }\n />\n )\n}\n\nexport {AutocompletePrompt}\n"]}
|