Skip to content

Commit 9fd0733

Browse files
chore: extract GraphiQL server to @shopify/cli-kit
Move the GraphiQL HTTP proxy and its templates/assets out of @shopify/app and into @shopify/cli-kit so other packages (notably @shopify/store) can reuse it. This commit is purely mechanical: - Files moved to packages/cli-kit/src/public/node/graphiql/ and packages/cli-kit/assets/graphiql/. - Imports inside the moved server now use relative paths to other cli-kit modules instead of cross-package @shopify/cli-kit/* imports. - Asset resolution switched to @shopify/cli-kit/assets/graphiql/*. - @shopify/app consumers updated to import from @shopify/cli-kit/node/graphiql/server. - cli-kit package.json gains h3, @shopify/polaris, @shopify/polaris-icons, and react-dom (with @types/react-dom in devDependencies). Behavior is unchanged. Existing graphiql server.test.ts and utilities.test.ts move alongside the source.
1 parent 975bf1a commit 9fd0733

14 files changed

Lines changed: 60 additions & 42 deletions

File tree

packages/app/package.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@
5252
"@oclif/core": "4.5.3",
5353
"@shopify/cli-kit": "3.93.0",
5454
"@shopify/plugin-cloudflare": "3.93.0",
55-
"@shopify/polaris": "12.27.0",
56-
"@shopify/polaris-icons": "8.11.1",
5755
"@shopify/theme": "3.93.0",
5856
"@shopify/theme-check-node": "3.25.0",
5957
"@shopify/toml-patch": "0.3.0",
@@ -68,15 +66,13 @@
6866
"prettier": "3.8.1",
6967
"proper-lockfile": "4.1.2",
7068
"react": "19.2.4",
71-
"react-dom": "19.2.4",
7269
"which": "4.0.0",
7370
"ws": "8.18.0"
7471
},
7572
"devDependencies": {
7673
"@types/diff": "^5.0.3",
7774
"@types/proper-lockfile": "4.1.4",
7875
"@types/react": "^19.0.0",
79-
"@types/react-dom": "^19.0.0",
8076
"@types/which": "3.0.4",
8177
"@types/ws": "^8.5.13",
8278
"@vitest/coverage-istanbul": "^3.1.4"

packages/app/src/cli/services/dev/processes/graphiql.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {BaseProcess, DevProcessFunction} from './types.js'
2-
import {setupGraphiQLServer} from '../graphiql/server.js'
2+
import {setupGraphiQLServer} from '@shopify/cli-kit/node/graphiql/server'
33

44
interface GraphiQLServerProcessOptions {
55
appName: string

packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {pushUpdatesForDraftableExtensions} from './draftable-extension.js'
88
import {pushUpdatesForDevSession} from './dev-session/dev-session-process.js'
99
import {runThemeAppExtensionsServer} from './theme-app-extension.js'
1010
import {launchAppWatcher} from './app-watcher-process.js'
11-
import {resolveGraphiQLKey} from '../graphiql/server.js'
1211
import {
1312
testAppAccessConfigExtension,
1413
testAppConfigExtensions,
@@ -31,6 +30,7 @@ import {ensureDeploymentIdsPresence} from '../../context/identifiers.js'
3130
import {DeveloperPlatformClient} from '../../../utilities/developer-platform-client.js'
3231
import {AppEventWatcher} from '../app-events/app-event-watcher.js'
3332
import * as loader from '../../../models/app/loader.js'
33+
import {resolveGraphiQLKey} from '@shopify/cli-kit/node/graphiql/server'
3434
import {describe, test, expect, beforeEach, vi} from 'vitest'
3535
import {ensureAuthenticatedAdmin, ensureAuthenticatedStorefront} from '@shopify/cli-kit/node/session'
3636
import {Config} from '@oclif/core'

packages/app/src/cli/services/dev/processes/setup-dev-processes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {DevSessionProcess, setupDevSessionProcess} from './dev-session/dev-sessi
99
import {AppLogsSubscribeProcess, setupAppLogsPollingProcess} from './app-logs-polling.js'
1010
import {AppWatcherProcess, setupAppWatcherProcess} from './app-watcher-process.js'
1111
import {DevSessionStatusManager} from './dev-session/dev-session-status-manager.js'
12-
import {resolveGraphiQLKey} from '../graphiql/server.js'
1312
import {environmentVariableNames} from '../../../constants.js'
1413
import {AppLinkedInterface, getAppScopes, WebType} from '../../../models/app/app.js'
1514

@@ -21,6 +20,7 @@ import {ApplicationURLs} from '../urls.js'
2120
import {DeveloperPlatformClient} from '../../../utilities/developer-platform-client.js'
2221
import {AppEventWatcher} from '../app-events/app-event-watcher.js'
2322
import {reloadApp} from '../../../models/app/loader.js'
23+
import {resolveGraphiQLKey} from '@shopify/cli-kit/node/graphiql/server'
2424
import {getAvailableTCPPort} from '@shopify/cli-kit/node/tcp'
2525
import {isTruthy} from '@shopify/cli-kit/node/context/utilities'
2626
import {firstPartyDev} from '@shopify/cli-kit/node/context/local'
File renamed without changes.
File renamed without changes.

packages/cli-kit/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@
107107
"@graphql-typed-document-node/core": "3.2.0",
108108
"@iarna/toml": "2.2.5",
109109
"@oclif/core": "4.5.3",
110+
"@shopify/polaris": "12.27.0",
111+
"@shopify/polaris-icons": "8.11.1",
110112
"@shopify/toml-patch": "0.3.0",
111113
"@opentelemetry/api": "1.9.0",
112114
"@opentelemetry/core": "1.30.0",
@@ -134,6 +136,7 @@
134136
"gradient-string": "2.0.2",
135137
"graphql": "16.10.0",
136138
"graphql-request": "6.1.0",
139+
"h3": "1.15.9",
137140
"ignore": "6.0.2",
138141
"ink": "6.8.0",
139142
"is-executable": "2.0.1",
@@ -151,6 +154,7 @@
151154
"open": "8.4.2",
152155
"pathe": "1.1.2",
153156
"react": "19.2.4",
157+
"react-dom": "19.2.4",
154158
"semver": "7.6.3",
155159
"stacktracey": "2.1.8",
156160
"strip-ansi": "7.1.0",
@@ -165,6 +169,7 @@
165169
"@types/gradient-string": "^1.1.2",
166170
"@types/lodash": "4.17.19",
167171
"@types/react": "^19.0.0",
172+
"@types/react-dom": "^19.0.0",
168173
"@types/semver": "^7.5.2",
169174
"@types/which": "3.0.4",
170175
"@vitest/coverage-istanbul": "^3.1.4",

packages/app/src/cli/services/dev/graphiql/server.test.ts renamed to packages/cli-kit/src/public/node/graphiql/server.test.ts

File renamed without changes.

packages/app/src/cli/services/dev/graphiql/server.ts renamed to packages/cli-kit/src/public/node/graphiql/server.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import {defaultQuery, graphiqlTemplate} from './templates/graphiql.js'
22
import {unauthorizedTemplate} from './templates/unauthorized.js'
33
import {filterCustomHeaders} from './utilities.js'
4+
import {performActionWithRetryAfterRecovery} from '../../common/retry.js'
5+
import {CLI_KIT_VERSION} from '../../common/version.js'
6+
import {AbortError} from '../error.js'
7+
import {adminUrl, supportedApiVersions} from '../api/admin.js'
8+
import {fetch} from '../http.js'
9+
import {renderLiquidTemplate} from '../liquid.js'
10+
import {outputDebug} from '../output.js'
411
import {
512
createApp,
613
createRouter,
@@ -13,13 +20,6 @@ import {
1320
setResponseStatus,
1421
toNodeListener,
1522
} from 'h3'
16-
import {performActionWithRetryAfterRecovery} from '@shopify/cli-kit/common/retry'
17-
import {CLI_KIT_VERSION} from '@shopify/cli-kit/common/version'
18-
import {AbortError} from '@shopify/cli-kit/node/error'
19-
import {adminUrl, supportedApiVersions} from '@shopify/cli-kit/node/api/admin'
20-
import {fetch} from '@shopify/cli-kit/node/http'
21-
import {renderLiquidTemplate} from '@shopify/cli-kit/node/liquid'
22-
import {outputDebug} from '@shopify/cli-kit/node/output'
2323
import {createHmac} from 'crypto'
2424
import {createServer, Server} from 'http'
2525
import {readFileSync} from 'fs'
@@ -30,6 +30,10 @@ import {createRequire} from 'module'
3030
* Derives a deterministic GraphiQL authentication key from the app's API secret and store FQDN.
3131
* The key is stable across dev server restarts (so browser tabs survive restarts)
3232
* but is not guessable without the app secret.
33+
*
34+
* @param apiSecret - The Partners app's client secret used as the HMAC key.
35+
* @param storeFqdn - The myshopify.com domain the GraphiQL session targets.
36+
* @returns A 64-character hex string suitable for use as the `?key=` query param.
3337
*/
3438
export function deriveGraphiQLKey(apiSecret: string, storeFqdn: string): string {
3539
return createHmac('sha256', apiSecret).update(`graphiql:${storeFqdn}`).digest('hex')
@@ -38,6 +42,11 @@ export function deriveGraphiQLKey(apiSecret: string, storeFqdn: string): string
3842
/**
3943
* Resolves the GraphiQL authentication key. Uses the explicitly provided key
4044
* if non-empty, otherwise derives one deterministically from the app secret.
45+
*
46+
* @param providedKey - An explicit key supplied by the caller; takes precedence when non-empty.
47+
* @param apiSecret - The Partners app's client secret, used to derive a stable key as a fallback.
48+
* @param storeFqdn - The myshopify.com domain the GraphiQL session targets.
49+
* @returns The resolved key.
4150
*/
4251
export function resolveGraphiQLKey(providedKey: string | undefined, apiSecret: string, storeFqdn: string): string {
4352
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- intentional: empty string after trim should fall through to deriveGraphiQLKey
@@ -63,16 +72,18 @@ interface SetupGraphiQLServerOptions {
6372
storeFqdn: string
6473
}
6574

66-
export function setupGraphiQLServer({
67-
stdout,
68-
port,
69-
appName,
70-
appUrl,
71-
apiKey,
72-
apiSecret,
73-
key: providedKey,
74-
storeFqdn,
75-
}: SetupGraphiQLServerOptions): Server {
75+
/**
76+
* Starts a local HTTP server that hosts the GraphiQL UI and proxies requests to the
77+
* Admin API for the configured store. The server uses the OAuth `client_credentials`
78+
* grant with the supplied `apiKey` / `apiSecret` to mint and refresh access tokens
79+
* on the fly.
80+
*
81+
* @param options - Configuration for the server, including the target store, the
82+
* Partners app credentials, and the local port to bind to.
83+
* @returns The underlying Node `http.Server` instance, already listening on `options.port`.
84+
*/
85+
export function setupGraphiQLServer(options: SetupGraphiQLServerOptions): Server {
86+
const {stdout, port, appName, appUrl, apiKey, apiSecret, key: providedKey, storeFqdn} = options
7687
// Always require an authentication key. If not explicitly provided, derive one
7788
// deterministically from apiSecret + storeFqdn so the key is stable across restarts
7889
// (browser tabs survive dev server restarts) but not guessable without the app secret.
@@ -120,9 +131,9 @@ export function setupGraphiQLServer({
120131
)
121132
}
122133

123-
const faviconPath = require.resolve('@shopify/app/assets/graphiql/favicon.ico')
134+
const faviconPath = require.resolve('@shopify/cli-kit/assets/graphiql/favicon.ico')
124135
const faviconContent = readFileSync(faviconPath)
125-
const stylePath = require.resolve('@shopify/app/assets/graphiql/style.css')
136+
const stylePath = require.resolve('@shopify/cli-kit/assets/graphiql/style.css')
126137
const styleContent = readFileSync(stylePath, 'utf8')
127138

128139
app.use(

packages/app/src/cli/services/dev/graphiql/templates/graphiql.tsx renamed to packages/cli-kit/src/public/node/graphiql/templates/graphiql.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {platformAndArch} from '@shopify/cli-kit/node/os'
1+
import {platformAndArch} from '../../os.js'
22
import React from 'react'
33
import {renderToStaticMarkup} from 'react-dom/server'
44
import {AppProvider, Badge, Banner, BlockStack, Box, Grid, InlineStack, Link, Select, Text} from '@shopify/polaris'

0 commit comments

Comments
 (0)