From fb04ea170f08eec27396a2b551e7385c4bc95729 Mon Sep 17 00:00:00 2001 From: Gonzalo Riestra Date: Mon, 6 Apr 2026 13:30:34 +0200 Subject: [PATCH 1/2] Fix eslint config to check all required files --- packages/cli/project.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/project.json b/packages/cli/project.json index 2ec9c58bfdc..624a373411c 100644 --- a/packages/cli/project.json +++ b/packages/cli/project.json @@ -37,14 +37,14 @@ "lint": { "executor": "nx:run-commands", "options": { - "command": "pnpm eslint \"src/**/*.ts\" 'bin/*.js' ", + "command": "pnpm eslint src 'bin/*.js'", "cwd": "packages/cli" } }, "lint:fix": { "executor": "nx:run-commands", "options": { - "command": "pnpm eslint 'src/**/*.ts' 'bin/*.js' --fix", + "command": "pnpm eslint src 'bin/*.js' --fix", "cwd": "packages/cli" } }, From 31092f27f7b992fb1af4e45cd27029395314c395 Mon Sep 17 00:00:00 2001 From: Gonzalo Riestra Date: Mon, 6 Apr 2026 13:37:31 +0200 Subject: [PATCH 2/2] Fix lint issues --- .../cli/src/cli/commands/docs/generate.ts | 3 +- .../cli/src/cli/commands/store/auth.test.ts | 6 +-- packages/cli/src/cli/commands/store/auth.ts | 4 +- .../src/cli/commands/store/execute.test.ts | 15 ++----- .../cli/src/cli/commands/store/execute.ts | 4 +- .../src/cli/services/kitchen-sink/static.ts | 2 +- .../cli/services/store/auth/callback.test.ts | 16 +++---- .../src/cli/services/store/auth/callback.ts | 34 ++++++-------- .../store/auth/existing-scopes.test.ts | 26 ++++++----- .../services/store/auth/existing-scopes.ts | 15 ++++--- .../src/cli/services/store/auth/index.test.ts | 10 +---- .../cli/src/cli/services/store/auth/index.ts | 10 ++--- .../src/cli/services/store/auth/pkce.test.ts | 8 ++-- .../cli/src/cli/services/store/auth/pkce.ts | 2 +- .../src/cli/services/store/auth/recovery.ts | 9 ++-- .../cli/services/store/auth/result.test.ts | 6 ++- .../cli/src/cli/services/store/auth/result.ts | 6 ++- .../cli/services/store/auth/scopes.test.ts | 2 +- .../store/auth/session-lifecycle.test.ts | 17 +++---- .../services/store/auth/session-lifecycle.ts | 31 +++++++------ .../services/store/auth/session-store.test.ts | 4 +- .../cli/services/store/auth/session-store.ts | 16 ++++--- .../services/store/auth/token-client.test.ts | 45 +++++++++---------- .../cli/services/store/auth/token-client.ts | 7 +-- .../store/execute/admin-context.test.ts | 11 ++--- .../services/store/execute/admin-context.ts | 8 ++-- .../store/execute/admin-transport.test.ts | 13 +++--- .../services/store/execute/admin-transport.ts | 8 ++-- .../cli/services/store/execute/index.test.ts | 5 +-- .../src/cli/services/store/execute/index.ts | 6 +-- .../services/store/execute/request.test.ts | 8 +--- .../src/cli/services/store/execute/request.ts | 1 - .../cli/services/store/execute/result.test.ts | 3 +- .../services/store/execute/targets.test.ts | 10 ++--- .../src/cli/services/store/execute/targets.ts | 4 +- 35 files changed, 174 insertions(+), 201 deletions(-) diff --git a/packages/cli/src/cli/commands/docs/generate.ts b/packages/cli/src/cli/commands/docs/generate.ts index 8e57e7782b3..5d8633c6d15 100644 --- a/packages/cli/src/cli/commands/docs/generate.ts +++ b/packages/cli/src/cli/commands/docs/generate.ts @@ -90,7 +90,6 @@ export async function writeCommandDocumentation( const previewDescription = command.summary ?? description ?? '' const cleanPreview = previewDescription.replace(/`/g, '\\`').replace(/https:\/\/shopify\.dev/g, '') - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const category = hasTopic && !generalTopics.includes(topic!) ? topic : 'general commands' const docString = `// This is an autogenerated file. Don't edit this file manually. @@ -139,7 +138,7 @@ export async function writeCommandFlagInterface( const flag = command.flags[flagName] if (!flag) return if (flag.hidden) return - const flagDescription = flag.description || '' + const flagDescription = flag.description ?? '' const char = flag.char ? `-${flag.char}, ` : '' const type = flag.type === 'option' ? 'string' : "''" const value = flag.type === 'option' ? ' ' : '' diff --git a/packages/cli/src/cli/commands/store/auth.test.ts b/packages/cli/src/cli/commands/store/auth.test.ts index d3b5276b1ba..684bce8876a 100644 --- a/packages/cli/src/cli/commands/store/auth.test.ts +++ b/packages/cli/src/cli/commands/store/auth.test.ts @@ -1,7 +1,7 @@ -import {beforeEach, describe, expect, test, vi} from 'vitest' import StoreAuth from './auth.js' import {authenticateStoreWithApp} from '../../services/store/auth/index.js' import {createStoreAuthPresenter} from '../../services/store/auth/result.js' +import {describe, expect, test, vi} from 'vitest' vi.mock('../../services/store/auth/index.js') vi.mock('../../services/store/auth/result.js', () => ({ @@ -9,10 +9,6 @@ vi.mock('../../services/store/auth/result.js', () => ({ })) describe('store auth command', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - test('passes parsed flags through to the auth service', async () => { await StoreAuth.run(['--store', 'shop.myshopify.com', '--scopes', 'read_products,write_products']) diff --git a/packages/cli/src/cli/commands/store/auth.ts b/packages/cli/src/cli/commands/store/auth.ts index a42d4dc327e..34ce68ff902 100644 --- a/packages/cli/src/cli/commands/store/auth.ts +++ b/packages/cli/src/cli/commands/store/auth.ts @@ -1,9 +1,9 @@ +import {authenticateStoreWithApp} from '../../services/store/auth/index.js' +import {createStoreAuthPresenter} from '../../services/store/auth/result.js' import Command from '@shopify/cli-kit/node/base-command' import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli' import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn' import {Flags} from '@oclif/core' -import {authenticateStoreWithApp} from '../../services/store/auth/index.js' -import {createStoreAuthPresenter} from '../../services/store/auth/result.js' export default class StoreAuth extends Command { static summary = 'Authenticate an app against a store for store commands.' diff --git a/packages/cli/src/cli/commands/store/execute.test.ts b/packages/cli/src/cli/commands/store/execute.test.ts index ffd339ac323..d4b7b8b333b 100644 --- a/packages/cli/src/cli/commands/store/execute.test.ts +++ b/packages/cli/src/cli/commands/store/execute.test.ts @@ -1,14 +1,13 @@ -import {beforeEach, describe, expect, test, vi} from 'vitest' import StoreExecute from './execute.js' import {executeStoreOperation} from '../../services/store/execute/index.js' import {writeOrOutputStoreExecuteResult} from '../../services/store/execute/result.js' +import {beforeEach, describe, expect, test, vi} from 'vitest' vi.mock('../../services/store/execute/index.js') vi.mock('../../services/store/execute/result.js') describe('store execute command', () => { beforeEach(() => { - vi.clearAllMocks() vi.mocked(executeStoreOperation).mockResolvedValue({data: {shop: {name: 'Test shop'}}}) }) @@ -24,11 +23,7 @@ describe('store execute command', () => { version: undefined, allowMutations: false, }) - expect(writeOrOutputStoreExecuteResult).toHaveBeenCalledWith( - {data: {shop: {name: 'Test shop'}}}, - undefined, - 'text', - ) + expect(writeOrOutputStoreExecuteResult).toHaveBeenCalledWith({data: {shop: {name: 'Test shop'}}}, undefined, 'text') }) test('passes the query file through to the service', async () => { @@ -46,11 +41,7 @@ describe('store execute command', () => { test('writes json output when --json is provided', async () => { await StoreExecute.run(['--store', 'shop.myshopify.com', '--query', 'query { shop { name } }', '--json']) - expect(writeOrOutputStoreExecuteResult).toHaveBeenCalledWith( - {data: {shop: {name: 'Test shop'}}}, - undefined, - 'json', - ) + expect(writeOrOutputStoreExecuteResult).toHaveBeenCalledWith({data: {shop: {name: 'Test shop'}}}, undefined, 'json') }) test('defines the expected flags', () => { diff --git a/packages/cli/src/cli/commands/store/execute.ts b/packages/cli/src/cli/commands/store/execute.ts index a184d6d3d8a..ddff009cf86 100644 --- a/packages/cli/src/cli/commands/store/execute.ts +++ b/packages/cli/src/cli/commands/store/execute.ts @@ -1,10 +1,10 @@ +import {executeStoreOperation} from '../../services/store/execute/index.js' +import {writeOrOutputStoreExecuteResult} from '../../services/store/execute/result.js' import Command from '@shopify/cli-kit/node/base-command' import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli' import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn' import {resolvePath} from '@shopify/cli-kit/node/path' import {Flags} from '@oclif/core' -import {executeStoreOperation} from '../../services/store/execute/index.js' -import {writeOrOutputStoreExecuteResult} from '../../services/store/execute/result.js' export default class StoreExecute extends Command { static summary = 'Execute GraphQL queries and mutations on a store.' diff --git a/packages/cli/src/cli/services/kitchen-sink/static.ts b/packages/cli/src/cli/services/kitchen-sink/static.ts index 32a01e8c1e6..9aee696cd37 100644 --- a/packages/cli/src/cli/services/kitchen-sink/static.ts +++ b/packages/cli/src/cli/services/kitchen-sink/static.ts @@ -35,7 +35,7 @@ export async function staticService() { }) renderInfo({ - headline: 'About your app', + headline: 'About your app.', customSections: [ { body: { diff --git a/packages/cli/src/cli/services/store/auth/callback.test.ts b/packages/cli/src/cli/services/store/auth/callback.test.ts index 2cdb74427c7..dc392774848 100644 --- a/packages/cli/src/cli/services/store/auth/callback.test.ts +++ b/packages/cli/src/cli/services/store/auth/callback.test.ts @@ -1,9 +1,9 @@ -import {createServer} from 'http' -import {describe, expect, test} from 'vitest' import {waitForStoreAuthCode} from './callback.js' +import {describe, expect, test} from 'vitest' +import {createServer} from 'http' async function getAvailablePort(): Promise { - return await new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const server = createServer() server.on('error', reject) @@ -26,12 +26,7 @@ async function getAvailablePort(): Promise { }) } -function callbackParams(options?: { - code?: string - shop?: string - state?: string - error?: string -}): URLSearchParams { +function callbackParams(options?: {code?: string; shop?: string; state?: string; error?: string}): URLSearchParams { const params = new URLSearchParams() params.set('shop', options?.shop ?? 'shop.myshopify.com') params.set('state', options?.state ?? 'state-123') @@ -101,7 +96,8 @@ describe('store auth callback server', () => { }), ).rejects.toMatchObject({ message: 'OAuth callback store does not match the requested store.', - tryMessage: 'Shopify returned other-shop.myshopify.com during authentication. Re-run using the permanent store domain:', + tryMessage: + 'Shopify returned other-shop.myshopify.com during authentication. Re-run using the permanent store domain:', nextSteps: [[{command: 'shopify store auth --store other-shop.myshopify.com --scopes '}]], }) }) diff --git a/packages/cli/src/cli/services/store/auth/callback.ts b/packages/cli/src/cli/services/store/auth/callback.ts index 6cf394c5952..4e3735da457 100644 --- a/packages/cli/src/cli/services/store/auth/callback.ts +++ b/packages/cli/src/cli/services/store/auth/callback.ts @@ -1,10 +1,10 @@ +import {STORE_AUTH_CALLBACK_PATH, maskToken} from './config.js' +import {retryStoreAuthWithPermanentDomainError} from './recovery.js' import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn' import {AbortError} from '@shopify/cli-kit/node/error' import {outputContent, outputDebug, outputToken} from '@shopify/cli-kit/node/output' import {timingSafeEqual} from 'crypto' import {createServer} from 'http' -import {STORE_AUTH_CALLBACK_PATH, maskToken} from './config.js' -import {retryStoreAuthWithPermanentDomainError} from './recovery.js' export interface WaitForAuthCodeOptions { store: string @@ -15,16 +15,8 @@ export interface WaitForAuthCodeOptions { } function renderAuthCallbackPage(title: string, message: string): string { - const safeTitle = title - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - const safeMessage = message - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') + const safeTitle = title.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') + const safeMessage = message.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') return ` @@ -120,7 +112,9 @@ export async function waitForStoreAuthCode({ res.setHeader('Content-Type', 'text/html') res.setHeader('Connection', 'close') res.once('finish', () => settle(() => resolve(code))) - res.end(renderAuthCallbackPage('Authentication succeeded', 'You can close this window and return to the terminal.')) + res.end( + renderAuthCallbackPage('Authentication succeeded', 'You can close this window and return to the terminal.'), + ) }) const settle = (callback: () => void) => { @@ -162,7 +156,7 @@ export async function waitForStoreAuthCode({ settleWithError(error) }) - server.listen(port, '127.0.0.1', async () => { + server.listen(port, '127.0.0.1', () => { isListening = true outputDebug( outputContent`PKCE callback server listening on http://127.0.0.1:${outputToken.raw(String(port))}${outputToken.raw(STORE_AUTH_CALLBACK_PATH)}`, @@ -170,16 +164,14 @@ export async function waitForStoreAuthCode({ if (!onListening) return - try { - await onListening() - } catch (error) { + Promise.resolve(onListening()).catch((error: unknown) => { settleWithError(error instanceof Error ? error : new Error(String(error))) - } + }) }) }) } -function constantTimeEqual(a: string, b: string): boolean { - if (a.length !== b.length) return false - return timingSafeEqual(Buffer.from(a, 'utf8'), Buffer.from(b, 'utf8')) +function constantTimeEqual(left: string, right: string): boolean { + if (left.length !== right.length) return false + return timingSafeEqual(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8')) } diff --git a/packages/cli/src/cli/services/store/auth/existing-scopes.test.ts b/packages/cli/src/cli/services/store/auth/existing-scopes.test.ts index ac53c16f13f..2ea04aab008 100644 --- a/packages/cli/src/cli/services/store/auth/existing-scopes.test.ts +++ b/packages/cli/src/cli/services/store/auth/existing-scopes.test.ts @@ -1,17 +1,19 @@ -import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest' -import {adminUrl} from '@shopify/cli-kit/node/api/admin' -import {graphqlRequest} from '@shopify/cli-kit/node/api/graphql' -import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output' import {STORE_AUTH_APP_CLIENT_ID} from './config.js' import {resolveExistingStoreAuthScopes} from './existing-scopes.js' import {loadStoredStoreSession} from './session-lifecycle.js' import {getCurrentStoredStoreAppSession} from './session-store.js' +import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest' +import {adminUrl} from '@shopify/cli-kit/node/api/admin' +import {graphqlRequest} from '@shopify/cli-kit/node/api/graphql' +import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output' vi.mock('./session-store.js') vi.mock('./session-lifecycle.js', () => ({loadStoredStoreSession: vi.fn()})) vi.mock('@shopify/cli-kit/node/api/graphql') vi.mock('@shopify/cli-kit/node/api/admin', async () => { - const actual = await vi.importActual('@shopify/cli-kit/node/api/admin') + const actual = await vi.importActual( + '@shopify/cli-kit/node/api/admin', + ) return { ...actual, adminUrl: vi.fn(), @@ -20,19 +22,20 @@ vi.mock('@shopify/cli-kit/node/api/admin', async () => { describe('resolveExistingStoreAuthScopes', () => { beforeEach(() => { - vi.clearAllMocks() vi.mocked(adminUrl).mockReturnValue('https://shop.myshopify.com/admin/api/unstable/graphql.json') }) afterEach(() => { - vi.restoreAllMocks() mockAndCaptureOutput().clear() }) test('returns no scopes when no stored auth exists', async () => { vi.mocked(getCurrentStoredStoreAppSession).mockReturnValue(undefined) - await expect(resolveExistingStoreAuthScopes('shop.myshopify.com')).resolves.toEqual({scopes: [], authoritative: true}) + await expect(resolveExistingStoreAuthScopes('shop.myshopify.com')).resolves.toEqual({ + scopes: [], + authoritative: true, + }) expect(loadStoredStoreSession).not.toHaveBeenCalled() expect(graphqlRequest).not.toHaveBeenCalled() }) @@ -120,7 +123,8 @@ describe('resolveExistingStoreAuthScopes', () => { errors: '[API] Invalid API key or access token (unrecognized login or wrong password)', }, request: { - query: '#graphql query CurrentAppInstallationAccessScopes { currentAppInstallation { accessScopes { handle } } }', + query: + '#graphql query CurrentAppInstallationAccessScopes { currentAppInstallation { accessScopes { handle } } }', }, }) vi.mocked(graphqlRequest).mockRejectedValue(scopeLookupError) @@ -129,7 +133,9 @@ describe('resolveExistingStoreAuthScopes', () => { scopes: ['read_orders'], authoritative: false, }) - expect(output.debug()).toContain('after remote scope lookup failed: HTTP 401: [API] Invalid API key or access token') + expect(output.debug()).toContain( + 'after remote scope lookup failed: HTTP 401: [API] Invalid API key or access token', + ) expect(output.debug()).not.toContain('CurrentAppInstallationAccessScopes') }) diff --git a/packages/cli/src/cli/services/store/auth/existing-scopes.ts b/packages/cli/src/cli/services/store/auth/existing-scopes.ts index 0dcc398e1be..4eaeb13ef2a 100644 --- a/packages/cli/src/cli/services/store/auth/existing-scopes.ts +++ b/packages/cli/src/cli/services/store/auth/existing-scopes.ts @@ -1,8 +1,8 @@ -import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn' -import {outputContent, outputDebug, outputToken} from '@shopify/cli-kit/node/output' import {getCurrentStoredStoreAppSession} from './session-store.js' import {loadStoredStoreSession} from './session-lifecycle.js' import {fetchCurrentStoreAuthScopes} from './token-client.js' +import {outputContent, outputDebug, outputToken} from '@shopify/cli-kit/node/output' +import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn' export interface ResolvedStoreAuthScopes { scopes: string[] @@ -46,9 +46,12 @@ export async function resolveExistingStoreAuthScopes(store: string): Promise ({openURL: vi.fn().mockResolvedValue(true)})) vi.mock('@shopify/cli-kit/node/crypto', () => ({randomUUID: vi.fn().mockReturnValue('state-123')})) describe('store auth service', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - afterEach(() => { - vi.restoreAllMocks() - }) - test('authenticateStoreWithApp opens the browser, stores the session, and returns auth result', async () => { const openURL = vi.fn().mockResolvedValue(true) const presenter = { diff --git a/packages/cli/src/cli/services/store/auth/index.ts b/packages/cli/src/cli/services/store/auth/index.ts index eabd3bf3b37..7fa23187af0 100644 --- a/packages/cli/src/cli/services/store/auth/index.ts +++ b/packages/cli/src/cli/services/store/auth/index.ts @@ -1,7 +1,3 @@ -import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn' -import {AbortError} from '@shopify/cli-kit/node/error' -import {outputContent, outputDebug, outputToken} from '@shopify/cli-kit/node/output' -import {openURL} from '@shopify/cli-kit/node/system' import {STORE_AUTH_APP_CLIENT_ID} from './config.js' import {setStoredStoreAppSession} from './session-store.js' import {exchangeStoreAuthCodeForToken} from './token-client.js' @@ -10,6 +6,10 @@ import {createPkceBootstrap} from './pkce.js' import {mergeRequestedAndStoredScopes, parseStoreAuthScopes, resolveGrantedScopes} from './scopes.js' import {resolveExistingStoreAuthScopes, type ResolvedStoreAuthScopes} from './existing-scopes.js' import {createStoreAuthPresenter, type StoreAuthPresenter, type StoreAuthResult} from './result.js' +import {openURL} from '@shopify/cli-kit/node/system' +import {outputContent, outputDebug, outputToken} from '@shopify/cli-kit/node/output' +import {AbortError} from '@shopify/cli-kit/node/error' +import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn' interface StoreAuthInput { store: string @@ -86,7 +86,7 @@ export async function authenticateStoreWithApp( refreshTokenExpiresAt: tokenResponse.refresh_token_expires_in ? new Date(now + tokenResponse.refresh_token_expires_in * 1000).toISOString() : undefined, - hasRefreshToken: !!tokenResponse.refresh_token, + hasRefreshToken: Boolean(tokenResponse.refresh_token), associatedUser: tokenResponse.associated_user ? { id: tokenResponse.associated_user.id, diff --git a/packages/cli/src/cli/services/store/auth/pkce.test.ts b/packages/cli/src/cli/services/store/auth/pkce.test.ts index d096446c94d..d46c844b3a2 100644 --- a/packages/cli/src/cli/services/store/auth/pkce.test.ts +++ b/packages/cli/src/cli/services/store/auth/pkce.test.ts @@ -1,6 +1,6 @@ -import {describe, expect, test} from 'vitest' import {STORE_AUTH_APP_CLIENT_ID} from './config.js' import {buildStoreAuthUrl, computeCodeChallenge, generateCodeVerifier} from './pkce.js' +import {describe, expect, test} from 'vitest' describe('store auth PKCE helpers', () => { test('generateCodeVerifier produces a base64url string of 43 chars', () => { @@ -9,9 +9,9 @@ describe('store auth PKCE helpers', () => { }) test('generateCodeVerifier produces unique values', () => { - const a = generateCodeVerifier() - const b = generateCodeVerifier() - expect(a).not.toBe(b) + const first = generateCodeVerifier() + const second = generateCodeVerifier() + expect(first).not.toBe(second) }) test('computeCodeChallenge produces a deterministic S256 hash', () => { diff --git a/packages/cli/src/cli/services/store/auth/pkce.ts b/packages/cli/src/cli/services/store/auth/pkce.ts index c9944eee30f..e3ba9fd9562 100644 --- a/packages/cli/src/cli/services/store/auth/pkce.ts +++ b/packages/cli/src/cli/services/store/auth/pkce.ts @@ -1,7 +1,7 @@ +import {DEFAULT_STORE_AUTH_PORT, STORE_AUTH_APP_CLIENT_ID, storeAuthRedirectUri} from './config.js' import {randomUUID} from '@shopify/cli-kit/node/crypto' import {outputContent, outputDebug, outputToken} from '@shopify/cli-kit/node/output' import {createHash, randomBytes} from 'crypto' -import {DEFAULT_STORE_AUTH_PORT, STORE_AUTH_APP_CLIENT_ID, storeAuthRedirectUri} from './config.js' import type {StoreTokenResponse} from './token-client.js' import type {WaitForAuthCodeOptions} from './callback.js' diff --git a/packages/cli/src/cli/services/store/auth/recovery.ts b/packages/cli/src/cli/services/store/auth/recovery.ts index 2308f3faead..8b328701eb0 100644 --- a/packages/cli/src/cli/services/store/auth/recovery.ts +++ b/packages/cli/src/cli/services/store/auth/recovery.ts @@ -8,19 +8,20 @@ function storeAuthCommandNextSteps(store: string, scopes: string) { return [[storeAuthCommand(store, scopes)]] } -export function createStoredStoreAuthError(store: string): AbortError { - return new AbortError( +export function throwStoredStoreAuthError(store: string): never { + throw new AbortError( `No stored app authentication found for ${store}.`, 'To create stored auth for this store, run:', storeAuthCommandNextSteps(store, ''), ) } -export function reauthenticateStoreAuthError(message: string, store: string, scopes: string): AbortError { - return new AbortError(message, 'To re-authenticate, run:', storeAuthCommandNextSteps(store, scopes)) +export function throwReauthenticateStoreAuthError(message: string, store: string, scopes: string): never { + throw new AbortError(message, 'To re-authenticate, run:', storeAuthCommandNextSteps(store, scopes)) } export function retryStoreAuthWithPermanentDomainError(returnedStore: string): AbortError { + // eslint-disable-next-line @shopify/cli/no-error-factory-functions return new AbortError( 'OAuth callback store does not match the requested store.', `Shopify returned ${returnedStore} during authentication. Re-run using the permanent store domain:`, diff --git a/packages/cli/src/cli/services/store/auth/result.test.ts b/packages/cli/src/cli/services/store/auth/result.test.ts index 83c1e9e9280..29e11516d71 100644 --- a/packages/cli/src/cli/services/store/auth/result.test.ts +++ b/packages/cli/src/cli/services/store/auth/result.test.ts @@ -1,6 +1,6 @@ +import {createStoreAuthPresenter} from './result.js' import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest' import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output' -import {createStoreAuthPresenter} from './result.js' function captureStandardStreams() { const stdout: string[] = [] @@ -51,7 +51,9 @@ describe('store auth presenter', () => { expect(output.completed()).toContain('Logged in.') expect(output.completed()).toContain('Authenticated as merchant@example.com against shop.myshopify.com.') - expect(output.info()).toContain("shopify store execute --store shop.myshopify.com --query 'query { shop { name id } }'") + expect(output.info()).toContain( + "shopify store execute --store shop.myshopify.com --query 'query { shop { name id } }'", + ) expect(output.output()).not.toContain('"store": "shop.myshopify.com"') }) diff --git a/packages/cli/src/cli/services/store/auth/result.ts b/packages/cli/src/cli/services/store/auth/result.ts index 4b7eec807e2..58098a7c4f4 100644 --- a/packages/cli/src/cli/services/store/auth/result.ts +++ b/packages/cli/src/cli/services/store/auth/result.ts @@ -34,7 +34,11 @@ function buildStoreAuthSuccessText(result: StoreAuthResult): {completed: string[ return { completed: ['Logged in.', `Authenticated${displayName} against ${result.store}.`], - info: ['', 'To verify that authentication worked, run:', `shopify store execute --store ${result.store} --query 'query { shop { name id } }'`], + info: [ + '', + 'To verify that authentication worked, run:', + `shopify store execute --store ${result.store} --query 'query { shop { name id } }'`, + ], } } diff --git a/packages/cli/src/cli/services/store/auth/scopes.test.ts b/packages/cli/src/cli/services/store/auth/scopes.test.ts index 6df7fa07512..5dbb0dba04f 100644 --- a/packages/cli/src/cli/services/store/auth/scopes.test.ts +++ b/packages/cli/src/cli/services/store/auth/scopes.test.ts @@ -1,5 +1,5 @@ -import {describe, expect, test} from 'vitest' import {mergeRequestedAndStoredScopes, parseStoreAuthScopes, resolveGrantedScopes} from './scopes.js' +import {describe, expect, test} from 'vitest' describe('store auth scope helpers', () => { test('parseStoreAuthScopes splits and deduplicates scopes', () => { diff --git a/packages/cli/src/cli/services/store/auth/session-lifecycle.test.ts b/packages/cli/src/cli/services/store/auth/session-lifecycle.test.ts index c1d3c652870..2b0b0ee7ee5 100644 --- a/packages/cli/src/cli/services/store/auth/session-lifecycle.test.ts +++ b/packages/cli/src/cli/services/store/auth/session-lifecycle.test.ts @@ -1,9 +1,4 @@ -import {beforeEach, describe, expect, test, vi} from 'vitest' -import {AbortError} from '@shopify/cli-kit/node/error' -import { - isSessionExpired, - loadStoredStoreSession, -} from './session-lifecycle.js' +import {isSessionExpired, loadStoredStoreSession} from './session-lifecycle.js' import { clearStoredStoreAppSession, getCurrentStoredStoreAppSession, @@ -12,6 +7,8 @@ import { } from './session-store.js' import {refreshStoreAccessToken} from './token-client.js' import {STORE_AUTH_APP_CLIENT_ID} from './config.js' +import {AbortError} from '@shopify/cli-kit/node/error' +import {describe, expect, test, vi} from 'vitest' vi.mock('./session-store.js') vi.mock('./token-client.js') @@ -61,10 +58,6 @@ describe('isSessionExpired', () => { }) describe('loadStoredStoreSession', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - test('throws when no stored auth exists', async () => { vi.mocked(getCurrentStoredStoreAppSession).mockReturnValue(undefined) @@ -157,7 +150,9 @@ describe('loadStoredStoreSession', () => { test('clears only the current stored auth and throws on malformed refresh JSON', async () => { const session = buildSession({expiresAt: new Date(Date.now() - 60 * 1000).toISOString()}) vi.mocked(getCurrentStoredStoreAppSession).mockReturnValue(session) - vi.mocked(refreshStoreAccessToken).mockRejectedValue(new AbortError('Received an invalid refresh response from Shopify.')) + vi.mocked(refreshStoreAccessToken).mockRejectedValue( + new AbortError('Received an invalid refresh response from Shopify.'), + ) await expect(loadStoredStoreSession('shop.myshopify.com')).rejects.toThrow('Received an invalid refresh response') expect(clearStoredStoreAppSession).toHaveBeenCalledWith('shop.myshopify.com', '42') diff --git a/packages/cli/src/cli/services/store/auth/session-lifecycle.ts b/packages/cli/src/cli/services/store/auth/session-lifecycle.ts index 309e6db71a2..bdefbfd54e4 100644 --- a/packages/cli/src/cli/services/store/auth/session-lifecycle.ts +++ b/packages/cli/src/cli/services/store/auth/session-lifecycle.ts @@ -1,14 +1,10 @@ -import {AbortError} from '@shopify/cli-kit/node/error' -import {outputContent, outputDebug, outputToken} from '@shopify/cli-kit/node/output' import {maskToken} from './config.js' -import {createStoredStoreAuthError, reauthenticateStoreAuthError} from './recovery.js' -import { - clearStoredStoreAppSession, - getCurrentStoredStoreAppSession, - setStoredStoreAppSession, -} from './session-store.js' -import type {StoredStoreAppSession} from './session-store.js' +import {throwStoredStoreAuthError, throwReauthenticateStoreAuthError} from './recovery.js' +import {clearStoredStoreAppSession, getCurrentStoredStoreAppSession, setStoredStoreAppSession} from './session-store.js' import {refreshStoreAccessToken} from './token-client.js' +import {outputContent, outputDebug, outputToken} from '@shopify/cli-kit/node/output' +import {AbortError} from '@shopify/cli-kit/node/error' +import type {StoredStoreAppSession} from './session-store.js' const EXPIRY_MARGIN_MS = 4 * 60 * 1000 @@ -49,7 +45,7 @@ export async function loadStoredStoreSession(store: string): Promise() diff --git a/packages/cli/src/cli/services/store/auth/session-store.ts b/packages/cli/src/cli/services/store/auth/session-store.ts index 868db8487cc..8e10f730e06 100644 --- a/packages/cli/src/cli/services/store/auth/session-store.ts +++ b/packages/cli/src/cli/services/store/auth/session-store.ts @@ -1,5 +1,5 @@ -import {LocalStorage} from '@shopify/cli-kit/node/local-storage' import {storeAuthSessionKey} from './config.js' +import {LocalStorage} from '@shopify/cli-kit/node/local-storage' export interface StoredStoreAppSession { store: string @@ -81,7 +81,9 @@ function sanitizeStoredStoreAppSession(value: unknown): StoredStoreAppSession | ...(isString(session.refreshToken) ? {refreshToken: session.refreshToken} : {}), ...(isString(session.expiresAt) ? {expiresAt: session.expiresAt} : {}), ...(isString(session.refreshTokenExpiresAt) ? {refreshTokenExpiresAt: session.refreshTokenExpiresAt} : {}), - ...(sanitizeAssociatedUser(session.associatedUser) ? {associatedUser: sanitizeAssociatedUser(session.associatedUser)} : {}), + ...(sanitizeAssociatedUser(session.associatedUser) + ? {associatedUser: sanitizeAssociatedUser(session.associatedUser)} + : {}), } } @@ -94,7 +96,12 @@ function readStoredStoreAppSessionBucket( if (!storedBucket || typeof storedBucket !== 'object') return undefined const {sessionsByUserId, currentUserId} = storedBucket as Partial - if (!sessionsByUserId || typeof sessionsByUserId !== 'object' || Array.isArray(sessionsByUserId) || typeof currentUserId !== 'string') { + if ( + !sessionsByUserId || + typeof sessionsByUserId !== 'object' || + Array.isArray(sessionsByUserId) || + typeof currentUserId !== 'string' + ) { storage.delete(key) return undefined } @@ -164,8 +171,7 @@ export function clearStoredStoreAppSession( maybeStorage?: LocalStorage, ): void { const userId = typeof userIdOrStorage === 'string' ? userIdOrStorage : undefined - const storage = - (typeof userIdOrStorage === 'string' ? maybeStorage : userIdOrStorage) ?? storeSessionStorage() + const storage = (typeof userIdOrStorage === 'string' ? maybeStorage : userIdOrStorage) ?? storeSessionStorage() const key = storeAuthSessionKey(store) diff --git a/packages/cli/src/cli/services/store/auth/token-client.test.ts b/packages/cli/src/cli/services/store/auth/token-client.test.ts index eab22b6abd0..600ea9022b2 100644 --- a/packages/cli/src/cli/services/store/auth/token-client.test.ts +++ b/packages/cli/src/cli/services/store/auth/token-client.test.ts @@ -1,19 +1,16 @@ +import {exchangeStoreAuthCodeForToken, fetchCurrentStoreAuthScopes, refreshStoreAccessToken} from './token-client.js' +import {STORE_AUTH_APP_CLIENT_ID} from './config.js' import {beforeEach, describe, expect, test, vi} from 'vitest' import {adminUrl} from '@shopify/cli-kit/node/api/admin' import {graphqlRequest} from '@shopify/cli-kit/node/api/graphql' import {fetch} from '@shopify/cli-kit/node/http' -import {outputDebug} from '@shopify/cli-kit/node/output' -import { - exchangeStoreAuthCodeForToken, - fetchCurrentStoreAuthScopes, - refreshStoreAccessToken, -} from './token-client.js' -import {STORE_AUTH_APP_CLIENT_ID} from './config.js' vi.mock('@shopify/cli-kit/node/http') vi.mock('@shopify/cli-kit/node/api/graphql') vi.mock('@shopify/cli-kit/node/api/admin', async () => { - const actual = await vi.importActual('@shopify/cli-kit/node/api/admin') + const actual = await vi.importActual( + '@shopify/cli-kit/node/api/admin', + ) return { ...actual, adminUrl: vi.fn(), @@ -29,7 +26,6 @@ vi.mock('@shopify/cli-kit/node/output', async () => { describe('token client', () => { beforeEach(() => { - vi.clearAllMocks() vi.mocked(adminUrl).mockReturnValue('https://shop.myshopify.com/admin/api/unstable/graphql.json') }) @@ -112,9 +108,9 @@ describe('token client', () => { text: vi.fn().mockResolvedValue('not-json'), } as any) - await expect( - refreshStoreAccessToken({store: 'shop.myshopify.com', refreshToken: 'refresh-token'}), - ).rejects.toThrow('Received an invalid refresh response from Shopify.') + await expect(refreshStoreAccessToken({store: 'shop.myshopify.com', refreshToken: 'refresh-token'})).rejects.toThrow( + 'Received an invalid refresh response from Shopify.', + ) }) test('refreshStoreAccessToken throws when access token is missing', async () => { @@ -123,9 +119,9 @@ describe('token client', () => { text: vi.fn().mockResolvedValue(JSON.stringify({refresh_token: 'fresh-refresh-token'})), } as any) - await expect( - refreshStoreAccessToken({store: 'shop.myshopify.com', refreshToken: 'refresh-token'}), - ).rejects.toThrow('Token refresh returned an invalid response for shop.myshopify.com.') + await expect(refreshStoreAccessToken({store: 'shop.myshopify.com', refreshToken: 'refresh-token'})).rejects.toThrow( + 'Token refresh returned an invalid response for shop.myshopify.com.', + ) }) test('fetchCurrentStoreAuthScopes returns current scope handles', async () => { @@ -133,9 +129,10 @@ describe('token client', () => { currentAppInstallation: {accessScopes: [{handle: 'read_products'}, {handle: 'read_orders'}]}, } as any) - await expect( - fetchCurrentStoreAuthScopes({store: 'shop.myshopify.com', accessToken: 'token'}), - ).resolves.toEqual(['read_products', 'read_orders']) + await expect(fetchCurrentStoreAuthScopes({store: 'shop.myshopify.com', accessToken: 'token'})).resolves.toEqual([ + 'read_products', + 'read_orders', + ]) expect(adminUrl).toHaveBeenCalledWith('shop.myshopify.com', 'unstable') expect(graphqlRequest).toHaveBeenCalledWith({ @@ -150,9 +147,9 @@ describe('token client', () => { test('fetchCurrentStoreAuthScopes throws on GraphQL lookup failure', async () => { vi.mocked(graphqlRequest).mockRejectedValue(new Error('shopify exploded')) - await expect( - fetchCurrentStoreAuthScopes({store: 'shop.myshopify.com', accessToken: 'token'}), - ).rejects.toThrow('shopify exploded') + await expect(fetchCurrentStoreAuthScopes({store: 'shop.myshopify.com', accessToken: 'token'})).rejects.toThrow( + 'shopify exploded', + ) }) test('fetchCurrentStoreAuthScopes throws on invalid response shape', async () => { @@ -160,8 +157,8 @@ describe('token client', () => { currentAppInstallation: undefined, } as any) - await expect( - fetchCurrentStoreAuthScopes({store: 'shop.myshopify.com', accessToken: 'token'}), - ).rejects.toThrow('Shopify did not return currentAppInstallation.accessScopes.') + await expect(fetchCurrentStoreAuthScopes({store: 'shop.myshopify.com', accessToken: 'token'})).rejects.toThrow( + 'Shopify did not return currentAppInstallation.accessScopes.', + ) }) }) diff --git a/packages/cli/src/cli/services/store/auth/token-client.ts b/packages/cli/src/cli/services/store/auth/token-client.ts index 5931e4de9ae..6aa6d02c96d 100644 --- a/packages/cli/src/cli/services/store/auth/token-client.ts +++ b/packages/cli/src/cli/services/store/auth/token-client.ts @@ -1,9 +1,9 @@ +import {maskToken, STORE_AUTH_APP_CLIENT_ID} from './config.js' import {adminUrl} from '@shopify/cli-kit/node/api/admin' import {graphqlRequest} from '@shopify/cli-kit/node/api/graphql' import {AbortError} from '@shopify/cli-kit/node/error' import {fetch} from '@shopify/cli-kit/node/http' import {outputContent, outputDebug, outputToken} from '@shopify/cli-kit/node/output' -import {maskToken, STORE_AUTH_APP_CLIENT_ID} from './config.js' export interface StoreTokenResponse { access_token: string @@ -145,10 +145,7 @@ const CurrentAppInstallationAccessScopesQuery = `#graphql } ` -export async function fetchCurrentStoreAuthScopes(options: { - store: string - accessToken: string -}): Promise { +export async function fetchCurrentStoreAuthScopes(options: {store: string; accessToken: string}): Promise { outputDebug( outputContent`Fetching current app installation scopes for ${outputToken.raw(options.store)} using token ${outputToken.raw(maskToken(options.accessToken))}`, ) diff --git a/packages/cli/src/cli/services/store/execute/admin-context.test.ts b/packages/cli/src/cli/services/store/execute/admin-context.test.ts index d82ed61de96..f19fafb461a 100644 --- a/packages/cli/src/cli/services/store/execute/admin-context.test.ts +++ b/packages/cli/src/cli/services/store/execute/admin-context.test.ts @@ -1,15 +1,17 @@ -import {beforeEach, describe, expect, test, vi} from 'vitest' -import {fetchApiVersions} from '@shopify/cli-kit/node/api/admin' -import {AbortError} from '@shopify/cli-kit/node/error' import {prepareAdminStoreGraphQLContext} from './admin-context.js' import {clearStoredStoreAppSession} from '../auth/session-store.js' import {loadStoredStoreSession} from '../auth/session-lifecycle.js' import {STORE_AUTH_APP_CLIENT_ID} from '../auth/config.js' +import {AbortError} from '@shopify/cli-kit/node/error' +import {fetchApiVersions} from '@shopify/cli-kit/node/api/admin' +import {beforeEach, describe, expect, test, vi} from 'vitest' vi.mock('../auth/session-store.js') vi.mock('../auth/session-lifecycle.js', () => ({loadStoredStoreSession: vi.fn()})) vi.mock('@shopify/cli-kit/node/api/admin', async () => { - const actual = await vi.importActual('@shopify/cli-kit/node/api/admin') + const actual = await vi.importActual( + '@shopify/cli-kit/node/api/admin', + ) return { ...actual, fetchApiVersions: vi.fn(), @@ -29,7 +31,6 @@ describe('prepareAdminStoreGraphQLContext', () => { } beforeEach(() => { - vi.clearAllMocks() vi.mocked(loadStoredStoreSession).mockResolvedValue(storedSession) vi.mocked(fetchApiVersions).mockResolvedValue([ {handle: '2025-10', supported: true}, diff --git a/packages/cli/src/cli/services/store/execute/admin-context.ts b/packages/cli/src/cli/services/store/execute/admin-context.ts index dfabaa2eb6f..8347db49d08 100644 --- a/packages/cli/src/cli/services/store/execute/admin-context.ts +++ b/packages/cli/src/cli/services/store/execute/admin-context.ts @@ -1,10 +1,10 @@ +import {throwReauthenticateStoreAuthError} from '../auth/recovery.js' +import {clearStoredStoreAppSession} from '../auth/session-store.js' +import {loadStoredStoreSession} from '../auth/session-lifecycle.js' import {fetchApiVersions} from '@shopify/cli-kit/node/api/admin' import {AbortError} from '@shopify/cli-kit/node/error' import type {AdminSession} from '@shopify/cli-kit/node/session' -import {reauthenticateStoreAuthError} from '../auth/recovery.js' -import {clearStoredStoreAppSession} from '../auth/session-store.js' import type {StoredStoreAppSession} from '../auth/session-store.js' -import {loadStoredStoreSession} from '../auth/session-lifecycle.js' export interface AdminStoreGraphQLContext { adminSession: AdminSession @@ -31,7 +31,7 @@ async function resolveApiVersion(options: { /\b(?:401|404)\b/.test(error.message) ) { clearStoredStoreAppSession(session.store, session.userId) - throw reauthenticateStoreAuthError( + throwReauthenticateStoreAuthError( `Stored app authentication for ${session.store} is no longer valid.`, session.store, session.scopes.join(','), diff --git a/packages/cli/src/cli/services/store/execute/admin-transport.test.ts b/packages/cli/src/cli/services/store/execute/admin-transport.test.ts index 0d39950d7aa..b0bec8f5a92 100644 --- a/packages/cli/src/cli/services/store/execute/admin-transport.test.ts +++ b/packages/cli/src/cli/services/store/execute/admin-transport.test.ts @@ -1,17 +1,19 @@ +import {prepareStoreExecuteRequest} from './request.js' +import {runAdminStoreGraphQLOperation} from './admin-transport.js' +import {clearStoredStoreAppSession} from '../auth/session-store.js' +import {STORE_AUTH_APP_CLIENT_ID} from '../auth/config.js' import {beforeEach, describe, expect, test, vi} from 'vitest' import {adminUrl} from '@shopify/cli-kit/node/api/admin' import {graphqlRequest} from '@shopify/cli-kit/node/api/graphql' import {renderSingleTask} from '@shopify/cli-kit/node/ui' -import {clearStoredStoreAppSession} from '../auth/session-store.js' -import {prepareStoreExecuteRequest} from './request.js' -import {runAdminStoreGraphQLOperation} from './admin-transport.js' -import {STORE_AUTH_APP_CLIENT_ID} from '../auth/config.js' vi.mock('../auth/session-store.js') vi.mock('@shopify/cli-kit/node/api/graphql') vi.mock('@shopify/cli-kit/node/ui') vi.mock('@shopify/cli-kit/node/api/admin', async () => { - const actual = await vi.importActual('@shopify/cli-kit/node/api/admin') + const actual = await vi.importActual( + '@shopify/cli-kit/node/api/admin', + ) return { ...actual, adminUrl: vi.fn(), @@ -34,7 +36,6 @@ describe('runAdminStoreGraphQLOperation', () => { } beforeEach(() => { - vi.clearAllMocks() vi.mocked(adminUrl).mockReturnValue('https://shop.myshopify.com/admin/api/2025-10/graphql.json') vi.mocked(renderSingleTask).mockImplementation(async ({task}) => task(() => {})) }) diff --git a/packages/cli/src/cli/services/store/execute/admin-transport.ts b/packages/cli/src/cli/services/store/execute/admin-transport.ts index 9ba1dced9c1..d780fc1f17c 100644 --- a/packages/cli/src/cli/services/store/execute/admin-transport.ts +++ b/packages/cli/src/cli/services/store/execute/admin-transport.ts @@ -1,17 +1,17 @@ +import {throwReauthenticateStoreAuthError} from '../auth/recovery.js' +import {clearStoredStoreAppSession} from '../auth/session-store.js' import {adminUrl} from '@shopify/cli-kit/node/api/admin' import {graphqlRequest} from '@shopify/cli-kit/node/api/graphql' import {AbortError} from '@shopify/cli-kit/node/error' import {outputContent} from '@shopify/cli-kit/node/output' import {renderSingleTask} from '@shopify/cli-kit/node/ui' -import {reauthenticateStoreAuthError} from '../auth/recovery.js' -import {clearStoredStoreAppSession} from '../auth/session-store.js' import type {PreparedStoreExecuteRequest} from './request.js' import type {AdminStoreGraphQLContext} from './admin-context.js' function isGraphQLClientError(error: unknown): error is {response: {errors?: unknown; status?: number}} { if (!error || typeof error !== 'object' || !('response' in error)) return false const response = (error as {response?: unknown}).response - return !!response && typeof response === 'object' + return Boolean(response) && typeof response === 'object' } export async function runAdminStoreGraphQLOperation(input: { @@ -36,7 +36,7 @@ export async function runAdminStoreGraphQLOperation(input: { } catch (error) { if (isGraphQLClientError(error) && error.response.status === 401) { clearStoredStoreAppSession(input.context.session.store, input.context.session.userId) - throw reauthenticateStoreAuthError( + throwReauthenticateStoreAuthError( `Stored app authentication for ${input.context.session.store} is no longer valid.`, input.context.session.store, input.context.session.scopes.join(','), diff --git a/packages/cli/src/cli/services/store/execute/index.test.ts b/packages/cli/src/cli/services/store/execute/index.test.ts index f29b1aed02a..ad4782f47da 100644 --- a/packages/cli/src/cli/services/store/execute/index.test.ts +++ b/packages/cli/src/cli/services/store/execute/index.test.ts @@ -1,8 +1,8 @@ -import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest' -import {renderSingleTask} from '@shopify/cli-kit/node/ui' import {executeStoreOperation} from './index.js' import {prepareStoreExecuteRequest} from './request.js' import {getStoreGraphQLTarget} from './targets.js' +import {renderSingleTask} from '@shopify/cli-kit/node/ui' +import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest' import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output' vi.mock('./request.js') @@ -24,7 +24,6 @@ describe('executeStoreOperation', () => { } beforeEach(() => { - vi.clearAllMocks() vi.mocked(prepareStoreExecuteRequest).mockResolvedValue(request) vi.mocked(getStoreGraphQLTarget).mockReturnValue(target as any) target.prepareContext.mockResolvedValue(context) diff --git a/packages/cli/src/cli/services/store/execute/index.ts b/packages/cli/src/cli/services/store/execute/index.ts index 1394a37d262..309cfda88d5 100644 --- a/packages/cli/src/cli/services/store/execute/index.ts +++ b/packages/cli/src/cli/services/store/execute/index.ts @@ -1,7 +1,7 @@ -import {renderSingleTask} from '@shopify/cli-kit/node/ui' -import {outputContent} from '@shopify/cli-kit/node/output' import {prepareStoreExecuteRequest} from './request.js' import {getStoreGraphQLTarget, type StoreGraphQLApi} from './targets.js' +import {renderSingleTask} from '@shopify/cli-kit/node/ui' +import {outputContent} from '@shopify/cli-kit/node/output' interface ExecuteStoreOperationInput { store: string @@ -32,5 +32,5 @@ export async function executeStoreOperation(input: ExecuteStoreOperationInput): renderOptions: {stdout: process.stderr}, }) - return await target.execute({context, request}) + return target.execute({context, request}) } diff --git a/packages/cli/src/cli/services/store/execute/request.test.ts b/packages/cli/src/cli/services/store/execute/request.test.ts index ce995fa9919..5f6b98fe031 100644 --- a/packages/cli/src/cli/services/store/execute/request.test.ts +++ b/packages/cli/src/cli/services/store/execute/request.test.ts @@ -1,14 +1,10 @@ -import {beforeEach, describe, expect, test, vi} from 'vitest' -import {fileExists, readFile} from '@shopify/cli-kit/node/fs' import {prepareStoreExecuteRequest} from './request.js' +import {describe, expect, test, vi} from 'vitest' +import {fileExists, readFile} from '@shopify/cli-kit/node/fs' vi.mock('@shopify/cli-kit/node/fs') describe('prepareStoreExecuteRequest', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - test('returns a prepared request for an inline query', async () => { const request = await prepareStoreExecuteRequest({ query: 'query { shop { name } }', diff --git a/packages/cli/src/cli/services/store/execute/request.ts b/packages/cli/src/cli/services/store/execute/request.ts index 2e605392682..71289bdaba3 100644 --- a/packages/cli/src/cli/services/store/execute/request.ts +++ b/packages/cli/src/cli/services/store/execute/request.ts @@ -146,4 +146,3 @@ export async function prepareStoreExecuteRequest(input: { requestedVersion: input.version, } } - diff --git a/packages/cli/src/cli/services/store/execute/result.test.ts b/packages/cli/src/cli/services/store/execute/result.test.ts index 083e5329019..b38b224d666 100644 --- a/packages/cli/src/cli/services/store/execute/result.test.ts +++ b/packages/cli/src/cli/services/store/execute/result.test.ts @@ -1,8 +1,8 @@ +import {writeOrOutputStoreExecuteResult} from './result.js' import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest' import {writeFile} from '@shopify/cli-kit/node/fs' import {renderSuccess} from '@shopify/cli-kit/node/ui' import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output' -import {writeOrOutputStoreExecuteResult} from './result.js' vi.mock('@shopify/cli-kit/node/fs') vi.mock('@shopify/cli-kit/node/ui') @@ -34,7 +34,6 @@ describe('writeOrOutputStoreExecuteResult', () => { const originalUnitTestEnv = process.env.SHOPIFY_UNIT_TEST beforeEach(() => { - vi.clearAllMocks() mockAndCaptureOutput().clear() }) diff --git a/packages/cli/src/cli/services/store/execute/targets.test.ts b/packages/cli/src/cli/services/store/execute/targets.test.ts index 28fc67d8b92..a333bb946d6 100644 --- a/packages/cli/src/cli/services/store/execute/targets.test.ts +++ b/packages/cli/src/cli/services/store/execute/targets.test.ts @@ -1,18 +1,14 @@ -import {beforeEach, describe, expect, test, vi} from 'vitest' import {prepareStoreExecuteRequest} from './request.js' import {prepareAdminStoreGraphQLContext} from './admin-context.js' import {runAdminStoreGraphQLOperation} from './admin-transport.js' import {getStoreGraphQLTarget} from './targets.js' import {STORE_AUTH_APP_CLIENT_ID} from '../auth/config.js' +import {describe, expect, test, vi} from 'vitest' vi.mock('./admin-context.js') vi.mock('./admin-transport.js') describe('getStoreGraphQLTarget', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - test('returns the admin target adapter', async () => { const target = getStoreGraphQLTarget('admin') const request = await prepareStoreExecuteRequest({query: 'query { shop { name } }'}) @@ -32,7 +28,9 @@ describe('getStoreGraphQLTarget', () => { vi.mocked(prepareAdminStoreGraphQLContext).mockResolvedValue(context) vi.mocked(runAdminStoreGraphQLOperation).mockResolvedValue({data: {shop: {name: 'Test shop'}}}) - await expect(target.prepareContext({store: 'shop.myshopify.com', requestedVersion: '2025-10'})).resolves.toEqual(context) + await expect(target.prepareContext({store: 'shop.myshopify.com', requestedVersion: '2025-10'})).resolves.toEqual( + context, + ) await expect(target.execute({context, request})).resolves.toEqual({data: {shop: {name: 'Test shop'}}}) expect(prepareAdminStoreGraphQLContext).toHaveBeenCalledWith({ diff --git a/packages/cli/src/cli/services/store/execute/targets.ts b/packages/cli/src/cli/services/store/execute/targets.ts index 125a86410b1..0e82fb1cb8d 100644 --- a/packages/cli/src/cli/services/store/execute/targets.ts +++ b/packages/cli/src/cli/services/store/execute/targets.ts @@ -1,7 +1,7 @@ -import {BugError} from '@shopify/cli-kit/node/error' -import type {PreparedStoreExecuteRequest} from './request.js' import {prepareAdminStoreGraphQLContext, type AdminStoreGraphQLContext} from './admin-context.js' import {runAdminStoreGraphQLOperation} from './admin-transport.js' +import {BugError} from '@shopify/cli-kit/node/error' +import type {PreparedStoreExecuteRequest} from './request.js' export type StoreGraphQLApi = 'admin'