Add shopify store info --store <domain> command#7660
Draft
amcaplan wants to merge 21 commits into
Draft
Conversation
Contributor
Author
This stack of pull requests is managed by Graphite. Learn more about stacking. |
This was referenced May 28, 2026
dmerand
reviewed
May 28, 2026
|
|
||
| static descriptionWithMarkdown = `Reads metadata for a store from the Business Platform Destinations and Organizations APIs. | ||
|
|
||
| Tier 1 and Tier 2 fields work without \`store auth\`. Tier 3 fields (shop owner, timezone, features, setup required) require \`store auth\` and are only included when \`--verbose\` is set. |
Contributor
There was a problem hiding this comment.
Nit: This reads like harness session language and could be clarified.
amcaplan
commented
May 28, 2026
| -j, --json [env: SHOPIFY_FLAG_JSON] Output the result as JSON. Automatically disables color output. | ||
| -s, --store=<value> (required) [env: SHOPIFY_FLAG_STORE] The myshopify.com domain of the store to authenticate | ||
| against. | ||
| -s, --store=<value> (required) [env: SHOPIFY_FLAG_STORE] The myshopify.com domain of the store. |
Contributor
Author
There was a problem hiding this comment.
We are losing some info here + below. I am debating whether it's better to have a shared flag entirely, or there's value in overriding the description.
shopify store info <store> commandshopify store info --store <domain> command
d67e8d3 to
c9531e3
Compare
38490df to
979bd9a
Compare
Adds a new read-only command that surfaces shop metadata from BP Destinations and BP Organizations APIs, with optional Admin API enrichment via `--verbose`. Supports `--json` output and per-field graceful degradation via a `_field_errors` envelope. Tier 1 + Tier 2 fields work without `store auth` for the shop; Tier 3 fields (shop_owner, timezone, features, setup_required) require `store auth` and are opt-in via `--verbose`. Fixes #22724 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces repeated `if (value) result.field = value` chains with a single `compact()` pass from `@shopify/cli-kit/common/object`, flattening buildResult, applyVerboseFields, buildPlan, and mapAdminShop. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aligns with the rest of the store: namespace (e.g. store execute) which takes the shop domain via `-s, --store` rather than a positional. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hoists the `-s, --store` flag definition into packages/store/src/cli/flags.ts
following the themeFlags/appFlags precedent. `store auth`, `store execute`,
and `store info` now share the same flag declaration.
Side effect: the per-command verbal description ("…to authenticate against",
"…to execute against", "…to inspect") collapses into "The myshopify.com
domain of the store." — visible only in --help. The verb is redundant with
the command name.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
BP's Destinations + Organizations APIs return primaryDomain/webUrl as
full URLs with scheme ("https://shop.myshopify.com"), not bare hosts.
Strict string equality against the requested store FQDN missed every
real shop. They also return `handle: null` and a non-subdomain
`shortName` (e.g. "ACT"), so admin_url construction had nothing to work
with.
- Extract host from URL fields before comparing (new info/host.ts).
- Derive the canonical myshopify subdomain from primaryDomain/webUrl
and overwrite destination.handle with it so admin_url is built from
the actual shop subdomain.
- Use primaryDomain (storefront URL) for primary_url instead of
webUrl/url (both are admin URLs ending in /admin).
- Drop the misleading `url` field from OrganizationShopFields and the
Org GraphQL selection — it's the admin URL, not the storefront.
- Correct features.branding type from boolean to string (it's an enum
like "SHOPIFY", not a flag).
Verified live against ariel-caplan-test: Tier 1, 2, and 3 all populate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Keys render as "Store Type" instead of "store_type", and GraphQL enum strings like APP_DEVELOPMENT render as "App Development". Currency codes like USD pass through unchanged.
CLI commands take --store as a domain, so BP/Core shop IDs and the owning-org id have no follow-on use for users of this command. is_main_shop is a billing-contract internal that almost no user cares about. Removed from both text and JSON output; org id is kept as an internal-only type to drive the BP Organizations request.
Local-time output without a timezone is ambiguous when sharing output across regions; rendering in UTC makes timestamps unambiguous regardless of where the user is.
Drop references to "Tier", "Identity", and "Business Platform" from the command description, section titles, and error reasons — those are internal taxonomy that users don't recognize.
--verbose is a global "show your work" flag (more diagnostic output) across the CLI; using it to mean "include more result fields" is a mismatched concept. --full reads naturally as "give me the full set of fields".
Drop the features sub-object: storefront is uniformly true, branding is cosmetic, harmonizedSystemCode is niche cross-border tooling. Lift shopifyPlus to a top-level `plus` boolean — it overlaps with plan.name but is relevant enough on its own to deserve a clean signal rather than requiring callers to regex-match plan names. --full now adds exactly: shop_owner, timezone, setup_required, plus.
…uthed The flag was redundant: we already know whether the store is authenticated by the time we compose the result, so gating the Admin fetch behind an opt-in flag just added friction without unlocking anything. Now `store info` quietly includes shop_owner/timezone/ setup_required/plus when `store auth` exists for the store, and omits them otherwise. The `auth_status: not authenticated` field already serves as the discoverability hint, so we don't pollute output with field errors for fields the user never asked for.
Replace data-source-based section names (Overview / Plan & lifecycle / Admin details) with categories users actually think in: Store, Access, Plan, Activity. Timezone is a store property, so it sits in Store rather than Activity.
The --verbose flag was removed when Admin-sourced fields became automatic for authed shops; the test still asserted on it.
knip flagged a handful of types and option interfaces that were exported but only referenced inside their own file. Also drop the stale --full and Tier-1/2 comments from the result envelope.
Both @Shopify/organizations and the store info plumbing were decoding base64-encoded organization GIDs. Pull the helper into the organizations package's models module and export it, then have store info import it. Returns string | undefined so each caller can decide what to do on failure: fetchOrganizations still aborts (the id is required to display the org); store info silently omits the id (the BP Organizations request would have failed with a garbage id anyway).
The base64↔gid logic isn't organization-specific, so a helper named decodeOrganizationGid in @Shopify/organizations was misleading. Add three generic primitives in @shopify/cli-kit/common/gid: - numericIdFromGid: extract /<digits> from a plain gid://... - numericIdFromEncodedGid: same, but for base64-encoded gids - encodeGid: base64-encode a plain gid Thread these through every site that does the same work: - @Shopify/organizations fetch.ts - @shopify/store store info destinations - app-management-client.ts (numberFromGid/idFromEncodedGid wrappers keep their domain semantics — auto-detect numeric strings, fail-fast on bad gids — but delegate the regex/base64 work) Domain-validated extractors like extractBulkOperationId (@shopify/app bulk operations) stay; they require a specific gid prefix that the generic helper doesn't enforce. Drops the store→organizations dependency that existed only for this helper.
979bd9a to
c1e7a69
Compare
Neither helper was specific to store info — extractHost is a generic URL→host parser, and extractMyshopifyHandle is Shopify-domain logic that already has neighbors in cli-kit. Move both into @shopify/cli-kit/common/url alongside isValidURL and safeParseURL, add direct tests, and delete the store/info/host.ts module. extractHost now uses safeParseURL to avoid a bare try/catch.
Follow the repo convention used by other packages: queries live in .graphql files under src/cli/api/graphql/<api>/queries/, with typed documents generated alongside. Replaces hand-written inline query strings and hand-typed response interfaces with the businessPlatform- RequestDoc / businessPlatformOrganizationsRequestDoc / adminRequestDoc typed-document variants. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The schema files are sourced from external repos at codegen time (mirroring how packages/app and packages/organizations work) and shouldn't be tracked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationspackages/cli-kit/dist/public/common/gid.d.ts/**
* Extracts the trailing numeric id from a plain GraphQL global id like
* `gid://shopify/Product/123`.
*
* @param gid - A plain GraphQL global id string.
* @returns The trailing numeric id, or undefined when the string does not end with `/<digits>`.
*/
export declare function numericIdFromGid(gid: string): string | undefined;
/**
* Decodes a base64-encoded GraphQL global id (for example, the form
* Business Platform APIs return) and returns the trailing numeric id.
*
* @param gid - A base64-encoded GraphQL global id.
* @returns The trailing numeric id, or undefined when the decoded string does not end with `/<digits>`.
*/
export declare function numericIdFromEncodedGid(gid: string): string | undefined;
/**
* Encodes a plain GraphQL global id (`gid://...`) as base64, which is the
* form some Business Platform endpoints require.
*
* @param gid - A plain GraphQL global id string to encode.
* @returns The base64-encoded gid.
*/
export declare function encodeGid(gid: string): string;
Existing type declarationspackages/cli-kit/dist/public/common/url.d.ts@@ -12,4 +12,20 @@ export declare function isValidURL(url: string): boolean;
* @param url - The string to parse into a URL.
* @returns A URL object if the parsing is successful, undefined otherwise.
*/
-export declare function safeParseURL(url: string): URL | undefined;
\ No newline at end of file
+export declare function safeParseURL(url: string): URL | undefined;
+/**
+ * Extracts the lowercased hostname from a URL-shaped string. Tolerates
+ * bare hosts (without a scheme) and inputs that come back from APIs as
+ * either or .
+ *
+ * @param value - A URL or bare host string, possibly null/undefined.
+ * @returns The lowercased hostname, or undefined when the input is empty.
+ */
+export declare function extractHost(value: string | null | undefined): string | undefined;
+/**
+ * Extracts the subdomain handle from a URL or host.
+ *
+ * @param value - A URL or host string, possibly null/undefined.
+ * @returns The myshopify subdomain handle, or undefined when the input isn't a URL.
+ */
+export declare function extractMyshopifyHandle(value: string | null | undefined): string | undefined;
\ No newline at end of file
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
shopify store info --store <domain>command that surfaces shop metadata from BP Destinations + BP Organizations APIs.store authfor the shop.store auth, the Admin API is queried in parallel and additional fields (shop_owner,timezone,setup_required,plus) are folded into the same output — no extra flag needed._field_errorswhen an individual backend fails.auth_statusalready tells callers why Admin-sourced fields are absent for unauthed shops, so those are silently omitted instead of cluttering_field_errors.--jsonfor machine consumption.Implementation notes
destinations(search:)→currentUserAccount.organizationForDestination(destinationPublicId:). Avoids the name-collision risk of resolving owning_org viaparentDestinationName.prepareAdminStoreGraphQLContextfromstore executefor the Admin API path.YYYY-MM-DD HH:MM:SS UTCin text.is_main_shop) are intentionally not surfaced — the CLI addresses stores by domain.@shopify/storeand@shopify/organizations.Quality gates
pnpm vitest run(packages/store) — 168 tests passingpnpm type-checkpnpm lintpnpm refresh-manifests,pnpm refresh-readme,pnpm build-dev-docs— generated artifacts committedTest plan
shopify store info --store <unauthed-store>.myshopify.comreturns baseline fields withoutstore authshopify store info --store <authed-store>.myshopify.comincludesshop_owner,timezone,setup_required,plus--jsonoutput matches the snake_case envelope in the issue--storesurfaces a clearAbortErrorAbortError_field_errorsOut of scope (follow-up issue)
--storeis omitted — deferred untilLocalStorage.keys()lands in cli-kit (would unlockstore list --authedtoo).Closes shop/issues-develop#22724
🤖 Generated with Claude Code