From a41813abf7b80ecd1b8d33f3a02e3d49a7e3b25b Mon Sep 17 00:00:00 2001 From: Ariel Caplan Date: Wed, 24 Jun 2026 20:33:06 +0300 Subject: [PATCH] Prompt for store create dev flags in TTY, require them in non-TTY Make --name, --organization-id, and --plan required only in non-interactive environments via failMissingNonTTYFlags. In TTY environments, prompt for any missing value: text prompt for name, existing org selector (selectOrg), and a list selector for plan. Regenerated oclif.manifest.json to reflect the flags no longer being unconditionally required. Assisted-By: devx/ae84811a-733c-4815-8d7d-604504428a4b --- packages/cli/oclif.manifest.json | 2 - .../src/cli/commands/store/create/dev.test.ts | 112 +++++++++++++----- .../src/cli/commands/store/create/dev.ts | 16 ++- packages/store/src/cli/prompts/store.test.ts | 37 ++++++ packages/store/src/cli/prompts/store.ts | 26 ++++ .../src/cli/services/store/create/dev.test.ts | 80 ++++++------- .../src/cli/services/store/create/dev.ts | 18 +-- 7 files changed, 201 insertions(+), 90 deletions(-) create mode 100644 packages/store/src/cli/prompts/store.test.ts create mode 100644 packages/store/src/cli/prompts/store.ts diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 515b56507bb..b1a03f7b2ec 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -5965,7 +5965,6 @@ "hasDynamicHelp": false, "multiple": false, "name": "name", - "required": true, "type": "option" }, "no-color": { @@ -5996,7 +5995,6 @@ "advanced", "plus" ], - "required": true, "type": "option" }, "verbose": { diff --git a/packages/store/src/cli/commands/store/create/dev.test.ts b/packages/store/src/cli/commands/store/create/dev.test.ts index 405c9f85b0b..28916e4c998 100644 --- a/packages/store/src/cli/commands/store/create/dev.test.ts +++ b/packages/store/src/cli/commands/store/create/dev.test.ts @@ -1,10 +1,19 @@ import StoreCreateDev from './dev.js' import {createDevStore} from '../../../services/store/create/dev.js' +import {storeNamePrompt, storePlanPrompt} from '../../../prompts/store.js' +import {selectOrg} from '@shopify/organizations' import {AbortError} from '@shopify/cli-kit/node/error' import {outputResult} from '@shopify/cli-kit/node/output' -import {describe, expect, test, vi} from 'vitest' +import {terminalSupportsPrompting} from '@shopify/cli-kit/node/system' +import {describe, expect, test, vi, beforeEach} from 'vitest' vi.mock('../../../services/store/create/dev.js') +vi.mock('../../../prompts/store.js') +vi.mock('@shopify/cli-kit/node/system') + +vi.mock('@shopify/organizations', () => ({ + selectOrg: vi.fn(), +})) vi.mock('@shopify/cli-kit/node/output', async (importOriginal) => { const actual: Record = await importOriginal() @@ -14,26 +23,21 @@ vi.mock('@shopify/cli-kit/node/output', async (importOriginal) => { } }) -describe('store create dev command', () => { - test('passes parsed flags through to the service', async () => { - await StoreCreateDev.run(['--name', 'my-test-store', '--plan', 'plus']) +const defaultOrg = {id: '12345', businessName: 'Test Org'} - expect(createDevStore).toHaveBeenCalledWith({ - name: 'my-test-store', - organizationId: undefined, - plan: 'plus', - featurePreview: undefined, - withDemoData: false, - json: false, - }) - }) +beforeEach(() => { + vi.mocked(selectOrg).mockResolvedValue(defaultOrg) + vi.mocked(terminalSupportsPrompting).mockReturnValue(true) +}) - test('passes organization-id flag through to the service', async () => { - await StoreCreateDev.run(['--name', 'my-test-store', '--organization-id', '12345', '--plan', 'plus']) +describe('store create dev command', () => { + test('resolves the organization and passes parsed flags through to the service', async () => { + await StoreCreateDev.run(['--name', 'my-test-store', '--plan', 'plus', '--organization-id', '12345']) + expect(selectOrg).toHaveBeenCalledWith('12345') expect(createDevStore).toHaveBeenCalledWith({ name: 'my-test-store', - organizationId: 12345, + organization: defaultOrg, plan: 'plus', featurePreview: undefined, withDemoData: false, @@ -42,11 +46,11 @@ describe('store create dev command', () => { }) test('passes json flag through to the service', async () => { - await StoreCreateDev.run(['--name', 'my-test-store', '--json', '--plan', 'plus']) + await StoreCreateDev.run(['--name', 'my-test-store', '--json', '--plan', 'plus', '--organization-id', '12345']) expect(createDevStore).toHaveBeenCalledWith({ name: 'my-test-store', - organizationId: undefined, + organization: defaultOrg, plan: 'plus', featurePreview: undefined, withDemoData: false, @@ -60,6 +64,8 @@ describe('store create dev command', () => { 'my-test-store', '--plan', 'basic', + '--organization-id', + '12345', '--feature-preview', 'extended_variants', '--with-demo-data', @@ -67,7 +73,7 @@ describe('store create dev command', () => { expect(createDevStore).toHaveBeenCalledWith({ name: 'my-test-store', - organizationId: undefined, + organization: defaultOrg, plan: 'basic', featurePreview: 'extended_variants', withDemoData: true, @@ -75,16 +81,60 @@ describe('store create dev command', () => { }) }) - test('rejects an invalid plan value without calling the service', async () => { - await expect(StoreCreateDev.run(['--name', 'my-test-store', '--plan', 'enterprise'])).rejects.toThrow() - expect(createDevStore).not.toHaveBeenCalled() + test('prompts for the name when --name is omitted in an interactive environment', async () => { + vi.mocked(storeNamePrompt).mockResolvedValue('prompted-store') + + await StoreCreateDev.run(['--organization-id', '12345', '--plan', 'plus']) + + expect(storeNamePrompt).toHaveBeenCalled() + expect(createDevStore).toHaveBeenCalledWith(expect.objectContaining({name: 'prompted-store'})) + }) + + test('prompts for the plan when --plan is omitted in an interactive environment', async () => { + vi.mocked(storePlanPrompt).mockResolvedValue('advanced') + + await StoreCreateDev.run(['--organization-id', '12345', '--name', 'my-test-store']) + + expect(storePlanPrompt).toHaveBeenCalled() + expect(createDevStore).toHaveBeenCalledWith(expect.objectContaining({plan: 'advanced'})) + }) + + test('does not prompt when all flags are provided', async () => { + await StoreCreateDev.run(['--name', 'my-test-store', '--plan', 'plus', '--organization-id', '12345']) + + expect(storeNamePrompt).not.toHaveBeenCalled() + expect(storePlanPrompt).not.toHaveBeenCalled() }) - test('requires the plan flag', async () => { - await expect(StoreCreateDev.run(['--name', 'my-test-store'])).rejects.toThrow() + test('rejects an invalid plan value without calling the service', async () => { + await expect( + StoreCreateDev.run(['--name', 'my-test-store', '--plan', 'enterprise', '--organization-id', '12345']), + ).rejects.toThrow() expect(createDevStore).not.toHaveBeenCalled() }) + test.each(['name', 'organization-id', 'plan'])( + 'fails in a non-interactive environment when --%s is missing', + async (missingFlag) => { + vi.mocked(terminalSupportsPrompting).mockReturnValue(false) + const argv = ['--name', 'my-test-store', '--organization-id', '12345', '--plan', 'plus'].filter( + (value, index, all) => { + // Drop the missing flag and its value. + if (value === `--${missingFlag}`) return false + return all[index - 1] !== `--${missingFlag}` + }, + ) + const mockExit = vi.spyOn(process, 'exit').mockImplementation((() => { + throw new Error('process.exit') + }) as never) + + await expect(StoreCreateDev.run(argv)).rejects.toThrow() + expect(createDevStore).not.toHaveBeenCalled() + + mockExit.mockRestore() + }, + ) + test('defines the expected flags', () => { expect(StoreCreateDev.flags.name).toBeDefined() expect(StoreCreateDev.flags['organization-id']).toBeDefined() @@ -100,9 +150,9 @@ describe('store create dev command', () => { throw new Error('process.exit') }) as never) - await expect(StoreCreateDev.run(['--name', 'my-test-store', '--plan', 'plus', '--json'])).rejects.toThrow( - 'process.exit', - ) + await expect( + StoreCreateDev.run(['--name', 'my-test-store', '--plan', 'plus', '--organization-id', '12345', '--json']), + ).rejects.toThrow('process.exit') const call = vi.mocked(outputResult).mock.calls[0]![0] as string const parsed = JSON.parse(call) @@ -120,14 +170,18 @@ describe('store create dev command', () => { test('does not output JSON for non-AbortError even when --json is active', async () => { vi.mocked(createDevStore).mockRejectedValueOnce(new Error('unexpected')) - await expect(StoreCreateDev.run(['--name', 'my-test-store', '--plan', 'plus', '--json'])).rejects.toThrow() + await expect( + StoreCreateDev.run(['--name', 'my-test-store', '--plan', 'plus', '--organization-id', '12345', '--json']), + ).rejects.toThrow() expect(vi.mocked(outputResult)).not.toHaveBeenCalled() }) test('does not output JSON for AbortError when --json is not active', async () => { vi.mocked(createDevStore).mockRejectedValueOnce(new AbortError('Something went wrong')) - await expect(StoreCreateDev.run(['--name', 'my-test-store', '--plan', 'plus'])).rejects.toThrow() + await expect( + StoreCreateDev.run(['--name', 'my-test-store', '--plan', 'plus', '--organization-id', '12345']), + ).rejects.toThrow() expect(vi.mocked(outputResult)).not.toHaveBeenCalled() }) }) diff --git a/packages/store/src/cli/commands/store/create/dev.ts b/packages/store/src/cli/commands/store/create/dev.ts index 5fade41803f..3ba6dfbda3e 100644 --- a/packages/store/src/cli/commands/store/create/dev.ts +++ b/packages/store/src/cli/commands/store/create/dev.ts @@ -1,6 +1,8 @@ import {createDevStore} from '../../../services/store/create/dev.js' import {devStorePlanHandles, DevStorePlan} from '../../../services/store/constants.js' +import {storeNamePrompt, storePlanPrompt} from '../../../prompts/store.js' import {storeFlags} from '../../../flags.js' +import {selectOrg} from '@shopify/organizations' import Command from '@shopify/cli-kit/node/base-command' import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli' import {AbortError} from '@shopify/cli-kit/node/error' @@ -21,14 +23,12 @@ export default class StoreCreateDev extends Command { ...jsonFlag, name: Flags.string({ description: 'Name for the new development store.', - required: true, env: 'SHOPIFY_FLAG_STORE_NAME', }), 'organization-id': storeFlags['organization-id'], plan: Flags.string({ description: 'The Shopify plan to use for the new development store.', options: devStorePlanHandles, - required: true, env: 'SHOPIFY_FLAG_STORE_PLAN', }), 'feature-preview': Flags.string({ @@ -44,11 +44,17 @@ export default class StoreCreateDev extends Command { async run(): Promise { const {flags} = await this.parse(StoreCreateDev) + this.failMissingNonTTYFlags(flags, ['name', 'organization-id', 'plan']) + + const organization = await selectOrg(flags['organization-id']?.toString()) + const name = flags.name ?? (await storeNamePrompt()) + const plan = (flags.plan as DevStorePlan | undefined) ?? (await storePlanPrompt()) + try { await createDevStore({ - name: flags.name, - organizationId: flags['organization-id'], - plan: flags.plan as DevStorePlan, + name, + organization, + plan, featurePreview: flags['feature-preview'], withDemoData: flags['with-demo-data'], json: flags.json, diff --git a/packages/store/src/cli/prompts/store.test.ts b/packages/store/src/cli/prompts/store.test.ts new file mode 100644 index 00000000000..f9dcf5c6485 --- /dev/null +++ b/packages/store/src/cli/prompts/store.test.ts @@ -0,0 +1,37 @@ +import {storeNamePrompt, storePlanPrompt} from './store.js' +import {describe, expect, test, vi} from 'vitest' +import {renderSelectPrompt, renderTextPrompt} from '@shopify/cli-kit/node/ui' + +vi.mock('@shopify/cli-kit/node/ui') + +describe('storeNamePrompt', () => { + test('asks for the store name and returns the entered value', async () => { + vi.mocked(renderTextPrompt).mockResolvedValue('my-store') + + const result = await storeNamePrompt() + + expect(result).toBe('my-store') + expect(renderTextPrompt).toHaveBeenCalledWith( + expect.objectContaining({message: 'Name for the new development store'}), + ) + }) +}) + +describe('storePlanPrompt', () => { + test('offers every plan handle and returns the selected value', async () => { + vi.mocked(renderSelectPrompt).mockResolvedValue('advanced') + + const result = await storePlanPrompt() + + expect(result).toBe('advanced') + expect(renderSelectPrompt).toHaveBeenCalledWith({ + message: 'Which Shopify plan do you want to use?', + choices: [ + {label: 'Basic', value: 'basic'}, + {label: 'Grow', value: 'grow'}, + {label: 'Advanced', value: 'advanced'}, + {label: 'Plus', value: 'plus'}, + ], + }) + }) +}) diff --git a/packages/store/src/cli/prompts/store.ts b/packages/store/src/cli/prompts/store.ts new file mode 100644 index 00000000000..f184c793c4d --- /dev/null +++ b/packages/store/src/cli/prompts/store.ts @@ -0,0 +1,26 @@ +import {DevStorePlan, devStorePlanHandles} from '../services/store/constants.js' +import {renderSelectPrompt, renderTextPrompt} from '@shopify/cli-kit/node/ui' + +/** Human-readable labels for each `--plan` handle, shown in the interactive plan selector. */ +const PLAN_LABELS: {[plan in DevStorePlan]: string} = { + basic: 'Basic', + grow: 'Grow', + advanced: 'Advanced', + plus: 'Plus', +} + +export async function storeNamePrompt(): Promise { + return renderTextPrompt({ + message: 'Name for the new development store', + }) +} + +export async function storePlanPrompt(): Promise { + return renderSelectPrompt({ + message: 'Which Shopify plan do you want to use?', + choices: devStorePlanHandles.map((handle) => ({ + label: PLAN_LABELS[handle], + value: handle, + })), + }) +} diff --git a/packages/store/src/cli/services/store/create/dev.test.ts b/packages/store/src/cli/services/store/create/dev.test.ts index 724cb4e0ca9..c94971468dd 100644 --- a/packages/store/src/cli/services/store/create/dev.test.ts +++ b/packages/store/src/cli/services/store/create/dev.test.ts @@ -1,17 +1,12 @@ import {createDevStore} from './dev.js' import {describe, expect, test, vi, beforeEach} from 'vitest' -import {selectOrg} from '@shopify/organizations' import {businessPlatformOrganizationsRequestDoc} from '@shopify/cli-kit/node/api/business-platform' import {ensureAuthenticatedBusinessPlatform} from '@shopify/cli-kit/node/session' import {renderSingleTask, renderSuccess} from '@shopify/cli-kit/node/ui' import {outputResult} from '@shopify/cli-kit/node/output' import {sleep} from '@shopify/cli-kit/node/system' -vi.mock('@shopify/organizations', () => ({ - selectOrg: vi.fn(), -})) - vi.mock('@shopify/cli-kit/node/api/business-platform', () => ({ businessPlatformOrganizationsRequestDoc: vi.fn(), })) @@ -47,7 +42,6 @@ const defaultMutationResult = { } beforeEach(() => { - vi.mocked(selectOrg).mockResolvedValue(defaultOrg) vi.mocked(ensureAuthenticatedBusinessPlatform).mockResolvedValue('test-token') vi.mocked(businessPlatformOrganizationsRequestDoc).mockResolvedValue(defaultMutationResult) vi.mocked(renderSingleTask).mockImplementation(async ({task}) => { @@ -64,9 +58,8 @@ describe('createDevStore', () => { organization: {id: '123', storeCreation: {status: 'COMPLETE'}}, }) - await createDevStore({name: 'test-store', plan: 'plus', json: false}) + await createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', json: false}) - expect(selectOrg).toHaveBeenCalledWith(undefined) expect(ensureAuthenticatedBusinessPlatform).toHaveBeenCalled() expect(businessPlatformOrganizationsRequestDoc).toHaveBeenCalledWith( expect.objectContaining({ @@ -100,7 +93,7 @@ describe('createDevStore', () => { organization: {id: '123', storeCreation: {status: 'COMPLETE'}}, }) - await createDevStore({name: 'test-store', plan, json: false}) + await createDevStore({name: 'test-store', organization: defaultOrg, plan, json: false}) expect(businessPlatformOrganizationsRequestDoc).toHaveBeenCalledWith( expect.objectContaining({ @@ -116,7 +109,13 @@ describe('createDevStore', () => { organization: {id: '123', storeCreation: {status: 'COMPLETE'}}, }) - await createDevStore({name: 'test-store', plan: 'plus', featurePreview: 'extended_variants', json: false}) + await createDevStore({ + name: 'test-store', + organization: defaultOrg, + plan: 'plus', + featurePreview: 'extended_variants', + json: false, + }) expect(businessPlatformOrganizationsRequestDoc).toHaveBeenCalledWith( expect.objectContaining({ @@ -132,7 +131,7 @@ describe('createDevStore', () => { organization: {id: '123', storeCreation: {status: 'COMPLETE'}}, }) - await createDevStore({name: 'test-store', plan: 'plus', withDemoData: true, json: false}) + await createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', withDemoData: true, json: false}) expect(businessPlatformOrganizationsRequestDoc).toHaveBeenCalledWith( expect.objectContaining({ @@ -150,6 +149,7 @@ describe('createDevStore', () => { await createDevStore({ name: 'test-store', + organization: defaultOrg, plan: 'basic', featurePreview: 'extended_variants', withDemoData: true, @@ -168,7 +168,7 @@ describe('createDevStore', () => { organization: {id: '123', storeCreation: {status: 'COMPLETE'}}, }) - await createDevStore({name: 'test-store', plan: 'plus', json: true}) + await createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', json: true}) expect(outputResult).toHaveBeenCalledWith(expect.stringContaining('"domain": "test-store.myshopify.com"')) expect(renderSuccess).not.toHaveBeenCalled() @@ -179,9 +179,9 @@ describe('createDevStore', () => { createAppDevelopmentStore: null, }) - await expect(createDevStore({name: 'test-store', plan: 'plus', json: false})).rejects.toThrow( - 'unexpected empty response', - ) + await expect( + createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', json: false}), + ).rejects.toThrow('unexpected empty response') }) test('throws AbortError when mutation returns userErrors', async () => { @@ -193,7 +193,9 @@ describe('createDevStore', () => { }, }) - await expect(createDevStore({name: 'test-store', plan: 'plus', json: false})).rejects.toThrow('Name is taken') + await expect( + createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', json: false}), + ).rejects.toThrow('Name is taken') }) test('throws AbortError when mutation returns no shopDomain', async () => { @@ -205,9 +207,9 @@ describe('createDevStore', () => { }, }) - await expect(createDevStore({name: 'test-store', plan: 'plus', json: false})).rejects.toThrow( - 'no shop domain was returned', - ) + await expect( + createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', json: false}), + ).rejects.toThrow('no shop domain was returned') }) test('throws AbortError when polling returns FAILED status', async () => { @@ -217,9 +219,9 @@ describe('createDevStore', () => { organization: {id: '123', storeCreation: {status: 'FAILED'}}, }) - await expect(createDevStore({name: 'test-store', plan: 'plus', json: false})).rejects.toThrow( - 'Store creation failed with status: FAILED', - ) + await expect( + createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', json: false}), + ).rejects.toThrow('Store creation failed with status: FAILED') }) test('throws AbortError when polling returns TIMED_OUT status', async () => { @@ -229,9 +231,9 @@ describe('createDevStore', () => { organization: {id: '123', storeCreation: {status: 'TIMED_OUT'}}, }) - await expect(createDevStore({name: 'test-store', plan: 'plus', json: false})).rejects.toThrow( - 'Store creation failed with status: TIMED_OUT', - ) + await expect( + createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', json: false}), + ).rejects.toThrow('Store creation failed with status: TIMED_OUT') }) test('throws AbortError when polling returns USER_ERROR status', async () => { @@ -241,9 +243,9 @@ describe('createDevStore', () => { organization: {id: '123', storeCreation: {status: 'USER_ERROR'}}, }) - await expect(createDevStore({name: 'test-store', plan: 'plus', json: false})).rejects.toThrow( - 'Store creation failed with status: USER_ERROR', - ) + await expect( + createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', json: false}), + ).rejects.toThrow('Store creation failed with status: USER_ERROR') }) test('throws AbortError when polling times out after 5 minutes', async () => { @@ -257,21 +259,9 @@ describe('createDevStore', () => { vi.mocked(businessPlatformOrganizationsRequestDoc).mockResolvedValueOnce(defaultMutationResult) - await expect(createDevStore({name: 'test-store', plan: 'plus', json: false})).rejects.toThrow( - 'Store creation timed out after 5 minutes', - ) - }) - - test('passes organization-id flag to selectOrg as a string', async () => { - vi.mocked(businessPlatformOrganizationsRequestDoc) - .mockResolvedValueOnce(defaultMutationResult) - .mockResolvedValueOnce({ - organization: {id: '123', storeCreation: {status: 'COMPLETE'}}, - }) - - await createDevStore({name: 'test-store', plan: 'plus', organizationId: 456, json: false}) - - expect(selectOrg).toHaveBeenCalledWith('456') + await expect( + createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', json: false}), + ).rejects.toThrow('Store creation timed out after 5 minutes') }) test('calls sleep with 2 seconds between polls', async () => { @@ -284,7 +274,7 @@ describe('createDevStore', () => { organization: {id: '123', storeCreation: {status: 'COMPLETE'}}, }) - await createDevStore({name: 'test-store', plan: 'plus', json: false}) + await createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', json: false}) expect(sleep).toHaveBeenCalledWith(2) }) @@ -296,7 +286,7 @@ describe('createDevStore', () => { organization: {id: '123', storeCreation: {status: 'COMPLETE'}}, }) - await createDevStore({name: 'test-store', plan: 'plus', json: false}) + await createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', json: false}) expect(renderSingleTask).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/packages/store/src/cli/services/store/create/dev.ts b/packages/store/src/cli/services/store/create/dev.ts index 7147179891a..9929f0fcd9e 100644 --- a/packages/store/src/cli/services/store/create/dev.ts +++ b/packages/store/src/cli/services/store/create/dev.ts @@ -5,7 +5,7 @@ import { PollStoreCreation, PollStoreCreationQuery, } from '../../../api/graphql/business-platform-organizations/generated/poll_store_creation.js' -import {selectOrg} from '@shopify/organizations' +import {Organization} from '@shopify/organizations' import {businessPlatformOrganizationsRequestDoc} from '@shopify/cli-kit/node/api/business-platform' import {ensureAuthenticatedBusinessPlatform} from '@shopify/cli-kit/node/session' import {renderSingleTask, renderSuccess} from '@shopify/cli-kit/node/ui' @@ -19,7 +19,7 @@ const POLL_TIMEOUT_MS = 5 * 60 * 1000 interface CreateDevStoreOptions { name: string plan: DevStorePlan - organizationId?: number + organization: Organization featurePreview?: string withDemoData?: boolean json: boolean @@ -51,7 +51,7 @@ function friendlyStatus(status: StoreCreationStatus): string { } export async function createDevStore(options: CreateDevStoreOptions): Promise { - const org = await selectOrg(options.organizationId?.toString()) + const {organization: org, name, plan} = options const token = await ensureAuthenticatedBusinessPlatform() const unauthorizedHandler = businessPlatformTokenRefreshHandler() @@ -60,8 +60,8 @@ export async function createDevStore(options: CreateDevStoreOptions): Promise