diff --git a/.changeset/store-list-command.md b/.changeset/store-list-command.md new file mode 100644 index 0000000000..94ce763ad3 --- /dev/null +++ b/.changeset/store-list-command.md @@ -0,0 +1,9 @@ +--- +'@shopify/store': minor +'@shopify/cli-kit': patch +'@shopify/cli': minor +--- + +Add `shopify store list`, which prints every locally stored store-auth session — both standard PKCE-authenticated sessions (`shopify store auth`) and preview-store sessions (`shopify store create preview`) — as a table with `Store`, `Kind`, and `User` columns. Supports `--kind standard|preview` for filtering and `--json` for machine-readable output (intended for AI agent consumption alongside the M1 [Preview Store for AI Agent Surfaces](https://vault.shopify.io/gsd/proposals/60T12R) work). + +Internally adds a `LocalStorage#entries()` enumerator to `@shopify/cli-kit` and a `listStoredStoreAppSessions()` helper to `@shopify/store` so the new command can resolve sessions across every stored shop without knowing the keys in advance. diff --git a/packages/cli-kit/src/public/node/local-storage.test.ts b/packages/cli-kit/src/public/node/local-storage.test.ts index cc1ab84232..d12f215107 100644 --- a/packages/cli-kit/src/public/node/local-storage.test.ts +++ b/packages/cli-kit/src/public/node/local-storage.test.ts @@ -62,6 +62,30 @@ describe('storage', () => { expect(got2).toEqual(undefined) }) }) + + test('entries returns every stored key/value pair', async () => { + await inTemporaryDirectory((cwd) => { + const storage = new LocalStorage({cwd}) + + storage.set('testValue', 'first') + storage.set('anotherKey' as keyof TestSchema, 'second' as TestSchema[keyof TestSchema]) + + const entries = storage.entries() + + expect(entries).toHaveLength(2) + expect(Object.fromEntries(entries)).toEqual({ + testValue: 'first', + anotherKey: 'second', + }) + }) + }) + + test('entries returns an empty array when nothing has been stored', async () => { + await inTemporaryDirectory((cwd) => { + const storage = new LocalStorage({cwd}) + expect(storage.entries()).toEqual([]) + }) + }) }) describe('error handling', () => { diff --git a/packages/cli-kit/src/public/node/local-storage.ts b/packages/cli-kit/src/public/node/local-storage.ts index 9759267aab..aec9a58b5c 100644 --- a/packages/cli-kit/src/public/node/local-storage.ts +++ b/packages/cli-kit/src/public/node/local-storage.ts @@ -66,6 +66,27 @@ export class LocalStorage> { } } + /** + * Get every `[key, value]` pair currently held in the local storage. + * + * Useful for callers that need to enumerate all stored values without knowing the + * full set of keys in advance (for example, a `list` command iterating over every + * stored session). The `conf` package stores its entire state as a single JSON + * object, so this is just a typed wrapper around that object. + * + * @returns An array of `[key, value]` tuples. + * @throws AbortError if a permission error occurs. + * @throws BugError if an unexpected error occurs. + */ + entries(): [keyof T, T[keyof T]][] { + try { + return Object.entries(this.config.store) as [keyof T, T[keyof T]][] + // eslint-disable-next-line no-catch-all/no-catch-all + } catch (error) { + this.handleError(error, 'entries') + } + } + /** * Clear the local storage (delete all values). * diff --git a/packages/cli/README.md b/packages/cli/README.md index 72159c4712..02c1f635d0 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -79,6 +79,7 @@ * [`shopify store auth`](#shopify-store-auth) * [`shopify store create preview`](#shopify-store-create-preview) * [`shopify store execute`](#shopify-store-execute) +* [`shopify store list`](#shopify-store-list) * [`shopify theme check`](#shopify-theme-check) * [`shopify theme console`](#shopify-theme-console) * [`shopify theme delete`](#shopify-theme-delete) @@ -2224,6 +2225,37 @@ EXAMPLES $ shopify store execute --store shop.myshopify.com --query "query { shop { name } }" --json ``` +## `shopify store list` + +List stored store-auth sessions. + +``` +USAGE + $ shopify store list [-j] [--kind standard|preview] [--no-color] [--verbose] + +FLAGS + -j, --json [env: SHOPIFY_FLAG_JSON] Output the result as JSON. Automatically disables color output. + --kind=