From 60d6ad606e41acd1ec3d91ff726a74a93dfbd257 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 25 Jun 2026 18:43:54 +0000 Subject: [PATCH] Mirror from ai-toolkit-source to Shopify-AI-Toolkit Synced from https://github.com/Shopify/ai-toolkit-source/commit/fa4db24bb68b8bd1a9815923a4e715bcdef461c4 --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- .cursor-plugin/marketplace.json | 2 +- .cursor-plugin/plugin.json | 2 +- .hermes-plugin/plugin.yaml | 2 +- CHANGELOG.md | 6 + gemini-extension.json | 2 +- package.json | 2 +- plugin.json | 2 +- skills/shopify-admin/SKILL.md | 2 +- skills/shopify-admin/scripts/search_docs.mjs | 6 +- skills/shopify-admin/scripts/validate.mjs | 850 ++++++++--------- skills/shopify-app-store-review/SKILL.md | 2 +- .../scripts/log_skill_use.mjs | 4 +- skills/shopify-custom-data/SKILL.md | 2 +- .../scripts/log_skill_use.mjs | 4 +- skills/shopify-customer/SKILL.md | 2 +- .../shopify-customer/scripts/search_docs.mjs | 6 +- skills/shopify-customer/scripts/validate.mjs | 850 ++++++++--------- skills/shopify-dev/SKILL.md | 2 +- skills/shopify-dev/scripts/log_skill_use.mjs | 4 +- skills/shopify-dev/scripts/search_docs.mjs | 6 +- skills/shopify-functions/SKILL.md | 6 +- .../shopify-functions/scripts/search_docs.mjs | 6 +- skills/shopify-functions/scripts/validate.mjs | 854 +++++++++--------- skills/shopify-hydrogen/SKILL.md | 2 +- .../shopify-hydrogen/scripts/search_docs.mjs | 6 +- skills/shopify-hydrogen/scripts/validate.mjs | 6 +- skills/shopify-liquid/SKILL.md | 2 +- skills/shopify-liquid/scripts/search_docs.mjs | 6 +- skills/shopify-liquid/scripts/validate.mjs | 6 +- skills/shopify-onboarding-dev/SKILL.md | 2 +- .../scripts/log_skill_use.mjs | 4 +- skills/shopify-onboarding-merchant/SKILL.md | 2 +- .../scripts/log_skill_use.mjs | 4 +- skills/shopify-partner/SKILL.md | 2 +- .../shopify-partner/scripts/search_docs.mjs | 6 +- skills/shopify-partner/scripts/validate.mjs | 850 ++++++++--------- skills/shopify-payments-apps/SKILL.md | 2 +- .../scripts/search_docs.mjs | 6 +- .../scripts/validate.mjs | 850 ++++++++--------- .../shopify-polaris-admin-extensions/SKILL.md | 2 +- .../scripts/search_docs.mjs | 6 +- .../scripts/validate.mjs | 6 +- skills/shopify-polaris-app-home/SKILL.md | 2 +- .../scripts/search_docs.mjs | 6 +- .../scripts/validate.mjs | 6 +- .../SKILL.md | 2 +- .../scripts/search_docs.mjs | 6 +- .../scripts/validate.mjs | 6 +- .../SKILL.md | 2 +- .../scripts/search_docs.mjs | 6 +- .../scripts/validate.mjs | 6 +- skills/shopify-pos-ui/SKILL.md | 2 +- skills/shopify-pos-ui/scripts/search_docs.mjs | 6 +- skills/shopify-pos-ui/scripts/validate.mjs | 6 +- skills/shopify-storefront-graphql/SKILL.md | 2 +- .../scripts/search_docs.mjs | 6 +- .../scripts/validate.mjs | 850 ++++++++--------- skills/shopify-use-shopify-cli/SKILL.md | 2 +- .../scripts/log_skill_use.mjs | 4 +- skills/ucp/SKILL.md | 2 +- skills/ucp/scripts/log_skill_use.mjs | 4 +- 64 files changed, 2696 insertions(+), 2630 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 893db87..67e05d0 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,7 +11,7 @@ "name": "shopify-plugin", "source": "./", "description": "Search Shopify docs, generate and validate GraphQL, Liquid, and UI extension code. Skill scripts send usage telemetry (queries, code, model/client identifiers) to shopify.dev by default; set OPT_OUT_INSTRUMENTATION=true to disable.", - "version": "1.5.0", + "version": "1.5.1", "author": { "name": "Shopify" }, "license": "MIT", "homepage": "https://github.com/Shopify/Shopify-AI-Toolkit", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 4417930..2220832 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "shopify-plugin", "description": "Shopify developer tools for Claude Code — search Shopify docs, generate and validate GraphQL, Liquid, and UI extension code. Skill scripts send usage telemetry (queries, code, model/client identifiers) to shopify.dev by default; set OPT_OUT_INSTRUMENTATION=true to disable.", - "version": "1.5.0", + "version": "1.5.1", "author": { "name": "Shopify" }, "license": "MIT", "keywords": ["shopify", "mcp", "graphql", "liquid", "storefront", "admin-api"] diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 9f350af..11029cf 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "shopify-plugin", - "version": "1.5.0", + "version": "1.5.1", "description": "Shopify developer tools for OpenAI Codex — search Shopify docs, generate and validate GraphQL, Liquid, and UI extension code. Skill scripts send usage telemetry (queries, code, model/client identifiers) to shopify.dev by default; set OPT_OUT_INSTRUMENTATION=true to disable.", "author": { "name": "Shopify" }, "homepage": "https://shopify.dev/docs/apps/build/devmcp", diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 4327e5e..2dfc84f 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -11,7 +11,7 @@ "name": "shopify-plugin", "source": "./", "description": "Search Shopify docs, generate and validate GraphQL, Liquid, and UI extension code. Skill scripts send usage telemetry (queries, code, model/client identifiers) to shopify.dev by default; set OPT_OUT_INSTRUMENTATION=true to disable.", - "version": "1.5.0", + "version": "1.5.1", "author": { "name": "Shopify" }, "license": "MIT", "homepage": "https://shopify.dev/docs/apps/build/devmcp", diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index 75ed03a..83380c3 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "shopify-plugin", "description": "Shopify developer tools for Cursor — search Shopify docs, generate and validate GraphQL, Liquid, and UI extension code. Skill scripts send usage telemetry (queries, code, model/client identifiers) to shopify.dev by default; set OPT_OUT_INSTRUMENTATION=true to disable.", - "version": "1.5.0", + "version": "1.5.1", "author": { "name": "Shopify" }, "logo": "../assets/shopify_glyph.svg", "homepage": "https://shopify.dev/docs/apps/build/devmcp", diff --git a/.hermes-plugin/plugin.yaml b/.hermes-plugin/plugin.yaml index ef644d6..970e081 100644 --- a/.hermes-plugin/plugin.yaml +++ b/.hermes-plugin/plugin.yaml @@ -1,5 +1,5 @@ name: shopify-plugin -version: 1.5.0 +version: 1.5.1 description: > Shopify developer tools for Hermes — search Shopify docs, generate and validate GraphQL, Liquid, and UI extension code, plus a `hermes shopify` diff --git a/CHANGELOG.md b/CHANGELOG.md index 3554793..d1a3e4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # shopify-plugin +## 1.5.1 + +### Patch Changes + +- b1a40b9: Use `log!` + `process::abort()` in the Functions `main.rs` example so the documented template matches the runtime the rest of the SKILL recommends. + ## 1.5.0 ### Minor Changes diff --git a/gemini-extension.json b/gemini-extension.json index 49dcd14..97dc1cb 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -1,4 +1,4 @@ { "name": "shopify-plugin", - "version": "1.5.0" + "version": "1.5.1" } diff --git a/package.json b/package.json index c220cdf..2d3f16d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "shopify-plugin", - "version": "1.5.0", + "version": "1.5.1", "private": true, "description": "AI agent plugin manifests for the Shopify Dev MCP server", "author": "Shopify", diff --git a/plugin.json b/plugin.json index 73e5854..bcfd005 100644 --- a/plugin.json +++ b/plugin.json @@ -1,7 +1,7 @@ { "name": "shopify-plugin", "description": "Shopify developer tools for GitHub Copilot — search Shopify docs, generate and validate GraphQL, Liquid, and UI extension code. Skill scripts send usage telemetry (queries, code, model/client identifiers) to shopify.dev by default; set OPT_OUT_INSTRUMENTATION=true to disable.", - "version": "1.5.0", + "version": "1.5.1", "author": { "name": "Shopify" }, "license": "MIT", "keywords": [ diff --git a/skills/shopify-admin/SKILL.md b/skills/shopify-admin/SKILL.md index 18172b2..1e494b0 100644 --- a/skills/shopify-admin/SKILL.md +++ b/skills/shopify-admin/SKILL.md @@ -4,7 +4,7 @@ description: "Write or explain **Admin GraphQL** queries and mutations for apps compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-admin/scripts/search_docs.mjs b/skills/shopify-admin/scripts/search_docs.mjs index edd056b..bb8a9ee 100755 --- a/skills/shopify-admin/scripts/search_docs.mjs +++ b/skills/shopify-admin/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-admin", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-admin/scripts/validate.mjs b/skills/shopify-admin/scripts/validate.mjs index db197fe..e3d7f7a 100755 --- a/skills/shopify-admin/scripts/validate.mjs +++ b/skills/shopify-admin/scripts/validate.mjs @@ -17303,14 +17303,6 @@ import { fileURLToPath as fileURLToPath2 } from "url"; import path2 from "path"; import { parseArgs } from "util"; -// src/validation/index.ts -var import_graphql2 = __toESM(require_graphql2(), 1); - -// src/schemaOperations/loadAPISchemas.ts -import { readFileSync } from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - // src/types/api-types.ts var Visibility = { PUBLIC: "public", @@ -17734,400 +17726,116 @@ var SHOPIFY_APIS = defineApis({ } }); -// src/schemaOperations/loadAPISchemas.ts -function getDataDirectory() { - const currentDir = path.dirname(fileURLToPath(import.meta.url)); - if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { - return path.join(currentDir, "data"); - } - if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { - const distIndex = currentDir.lastIndexOf(path.sep + "dist"); - if (distIndex !== -1) { - const distRoot = currentDir.substring(0, distIndex + 5); - return path.join(distRoot, "data"); - } else { - return path.join(currentDir, "data"); +// src/data/supported-versions-schema.json +var supported_versions_schema_default = { + admin: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return path.resolve(currentDir, "../data"); -} -var dataDir = getDataDirectory(); -function configuredSchemaPath(api) { - const apiConfig = SHOPIFY_APIS[api]; - if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; - if (apiConfig?.gqlSchemaFileName) { - return path.join(dataDir, apiConfig.gqlSchemaFileName); - } - return void 0; -} -function schemaPathFor(api, versionName) { - return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); -} -function deriveVersionNameFromSchemaFile(fileName) { - const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); - const versionSeparatorIndex = baseName.lastIndexOf("_"); - return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); -} -function loadAPISchemas(apis, schemaOptions) { - if (apis.length === 0) { - throw new Error("No APIs provided"); - } - if (schemaOptions) { - if (apis.length !== 1) { - throw new Error( - "schemaOptions can only be provided when requesting a single API" - ); + ], + "storefront-graphql": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - return [ - { - ...schemaOptions, - api: apis[0], - schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) - } - ]; - } - const schemasPath = path.join(dataDir, "supported-versions-schema.json"); - const schemasConfig = JSON.parse( - readFileSync(schemasPath, "utf-8") - ); - const apiVersions = []; - for (const api of apis) { - const versions = schemasConfig[api]; - if (versions) { - const versionsWithApi = versions.map((v) => ({ - ...v, - api, - schemaPath: schemaPathFor(api, v.name) - })); - apiVersions.push(...versionsWithApi); - } else { - const apiConfig = SHOPIFY_APIS[api]; - const configuredPath = configuredSchemaPath(api); - if (!configuredPath) continue; - apiVersions.push({ - name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", - latestVersion: true, - api, - schemaPath: configuredPath - }); + ], + partner: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return apiVersions; -} - -// src/schemaOperations/loadSchemaContent.ts -import { existsSync } from "node:fs"; -import fs from "node:fs/promises"; -import zlib from "node:zlib"; - -// src/schemaOperations/schemaCache.ts -var SchemaCache = class { - cache = /* @__PURE__ */ new Map(); - get(path3) { - return this.cache.get(path3); - } - set(path3, content) { - this.cache.set(path3, content); - } -}; -var schemaCache = new SchemaCache(); - -// src/schemaOperations/loadSchemaContent.ts -async function convertSdlToIntrospectionJson(schemaPath) { - const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); - const sdl = await fs.readFile(schemaPath, "utf-8"); - const introspection = introspectionFromSchema(buildSchema(sdl)); - return JSON.stringify({ data: introspection }); -} -async function loadSchemaContent(schema) { - const schemaPath = schema.schemaPath; - const cached = schemaCache.get(schemaPath); - if (cached) { - return cached; - } - try { - let content; - if (schemaPath.endsWith(".gz")) { - const compressedData = await fs.readFile(schemaPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { - content = await convertSdlToIntrospectionJson(schemaPath); - } else if (existsSync(schemaPath)) { - content = await fs.readFile(schemaPath, "utf-8"); - } else { - const gzPath = `${schemaPath}.gz`; - if (existsSync(gzPath)) { - const compressedData = await fs.readFile(gzPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else { - throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); - } + ], + customer: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - schemaCache.set(schemaPath, content); - return content; - } catch (error) { - console.error(`[graphql-schema-utils] Error loading schema: ${error}`); - throw error; - } -} - -// src/schemaOperations/offlineScopes.ts -var import_graphql = __toESM(require_graphql2(), 1); -function getScopes(data, typeName, fieldName) { - const entry = data.items.find((item) => { - if (fieldName) { - return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; - } - return item.type === "type" && item.typeName === typeName; - }); - return entry?.offlineScopes || []; -} -function getFieldReturnType(data, typeName, fieldName) { - const entry = data.items.find( - (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName - ); - return entry?.returnType; -} -async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { - const offlineScopes = /* @__PURE__ */ new Set(); - const fragmentMap = new Map( - parsedQueryAST.definitions.filter( - (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION - ).map((fragDef) => [fragDef.name.value, fragDef]) - ); - for (const definition of parsedQueryAST.definitions) { - if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { - const operationDef = definition; - if (operationDef.selectionSet) { - const rootTypeName = getRootTypeName( - operationDef.operation, - schemaName - ); - const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); - rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); - walkSelectionSet( - operationDef.selectionSet, - rootTypeName, - offlineScopeData, - offlineScopes, - fragmentMap - ); - } - } - } - return Array.from(offlineScopes); -} -function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { - const fieldName = field.name.value; - const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); - fieldScopes.forEach((scope) => offlineScopes.add(scope)); - if (!field.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); - if (returnType) { - const typeScopes = getScopes(scopeData, returnType); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - } - return { - nextSelectionSet: field.selectionSet, - nextTypeName: returnType || null - }; -} -function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { - const fragmentName = fragmentSpread.name.value; - if (visitedFragments.has(fragmentName)) { - return { nextSelectionSet: null, nextTypeName: null }; - } - visitedFragments.add(fragmentName); - const fragment = fragmentMap.get(fragmentName); - if (!fragment?.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = fragment.typeCondition.name.value; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: fragment.selectionSet, - nextTypeName: typeName - }; -} -function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { - if (!inlineFragment.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: inlineFragment.selectionSet, - nextTypeName: typeName - }; -} -function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { - for (const selection of selectionSet.selections) { - let context; - if (selection.kind === import_graphql.Kind.FIELD) { - context = processFieldSelection( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { - context = processFragmentSpread( - selection, - fragmentMap, - visitedFragments, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { - context = processInlineFragment( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else { - continue; - } - if (context.nextSelectionSet && context.nextTypeName) { - walkSelectionSet( - context.nextSelectionSet, - context.nextTypeName, - scopeData, - offlineScopes, - fragmentMap, - visitedFragments - ); - } - } -} -function getRootTypeName(operation, schemaName = "admin") { - if (schemaName === "admin") { - return operation === "mutation" ? "Mutation" : "QueryRoot"; - } - return operation === "mutation" ? "Mutation" : "Query"; -} -function formatScopes(scopes) { - if (!scopes || scopes.length === 0) { - return ""; - } - return ` -Required scopes: ${scopes.join(", ")}`; -} - -// src/data/supported-versions-schema.json -var supported_versions_schema_default = { - admin: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "storefront-graphql": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - partner: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - customer: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "payments-apps": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" + ], + "payments-apps": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } ], "polaris-app-home": [], @@ -18502,34 +18210,336 @@ function resolveVersion(apiName2, requested) { return { ok: true, version: latest, source: "default", supportedVersions }; } -// src/validation/index.ts -function isAPIVersionWithAPI(options) { - return options && typeof options.schemaPath === "string"; -} -async function validateGraphQLOperation(graphqlCode, api, options) { - const trimmedCode = graphqlCode.trim(); - if (!trimmedCode) { - return { - validation: { - result: "failed" /* FAILED */, - resultDetail: "No GraphQL operation found in the provided code." - }, - scopes: [] - }; +// src/validation/graphql.ts +var import_graphql2 = __toESM(require_graphql2(), 1); + +// src/schemaOperations/loadAPISchemas.ts +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +// src/schemaOperations/loadSchemaContent.ts +import { existsSync } from "node:fs"; +import fs from "node:fs/promises"; +import zlib from "node:zlib"; + +// src/schemaOperations/schemaCache.ts +var SchemaCache = class { + cache = /* @__PURE__ */ new Map(); + get(path3) { + return this.cache.get(path3); + } + set(path3, content) { + this.cache.set(path3, content); + } +}; +var schemaCache = new SchemaCache(); + +// src/schemaOperations/loadSchemaContent.ts +async function convertSdlToIntrospectionJson(schemaPath) { + const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); + const sdl = await fs.readFile(schemaPath, "utf-8"); + const introspection = introspectionFromSchema(buildSchema(sdl)); + return JSON.stringify({ data: introspection }); +} +async function loadSchemaContent(schema) { + const schemaPath = schema.schemaPath; + const cached = schemaCache.get(schemaPath); + if (cached) { + return cached; + } + try { + let content; + if (schemaPath.endsWith(".gz")) { + const compressedData = await fs.readFile(schemaPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { + content = await convertSdlToIntrospectionJson(schemaPath); + } else if (existsSync(schemaPath)) { + content = await fs.readFile(schemaPath, "utf-8"); + } else { + const gzPath = `${schemaPath}.gz`; + if (existsSync(gzPath)) { + const compressedData = await fs.readFile(gzPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else { + throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); + } + } + schemaCache.set(schemaPath, content); + return content; + } catch (error) { + console.error(`[graphql-schema-utils] Error loading schema: ${error}`); + throw error; + } +} + +// src/schemaOperations/loadAPISchemas.ts +function getDataDirectory() { + const currentDir = path.dirname(fileURLToPath(import.meta.url)); + if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { + return path.join(currentDir, "data"); + } + if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { + const distIndex = currentDir.lastIndexOf(path.sep + "dist"); + if (distIndex !== -1) { + const distRoot = currentDir.substring(0, distIndex + 5); + return path.join(distRoot, "data"); + } else { + return path.join(currentDir, "data"); + } + } + return path.resolve(currentDir, "../data"); +} +var dataDir = getDataDirectory(); +var diskSchemaSource = { + readVersionCatalog() { + const schemasPath = path.join(dataDir, "supported-versions-schema.json"); + return JSON.parse(readFileSync(schemasPath, "utf-8")); + }, + readSchemaContent(schema) { + return loadSchemaContent(schema); + } +}; +function configuredSchemaPath(api) { + const apiConfig = SHOPIFY_APIS[api]; + if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; + if (apiConfig?.gqlSchemaFileName) { + return path.join(dataDir, apiConfig.gqlSchemaFileName); + } + return void 0; +} +function schemaPathFor(api, versionName) { + return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); +} +function deriveVersionNameFromSchemaFile(fileName) { + const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); + const versionSeparatorIndex = baseName.lastIndexOf("_"); + return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); +} +function loadAPISchemas(apis, schemaOptions, source = diskSchemaSource) { + if (apis.length === 0) { + throw new Error("No APIs provided"); + } + if (schemaOptions) { + if (apis.length !== 1) { + throw new Error( + "schemaOptions can only be provided when requesting a single API" + ); + } + return [ + { + ...schemaOptions, + api: apis[0], + schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) + } + ]; + } + const schemasConfig = source.readVersionCatalog(); + const apiVersions = []; + for (const api of apis) { + const versions = schemasConfig[api]; + if (versions) { + const versionsWithApi = versions.map((v) => ({ + ...v, + api, + schemaPath: schemaPathFor(api, v.name) + })); + apiVersions.push(...versionsWithApi); + } else { + const apiConfig = SHOPIFY_APIS[api]; + const configuredPath = configuredSchemaPath(api); + if (!configuredPath) continue; + apiVersions.push({ + name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", + latestVersion: true, + api, + schemaPath: configuredPath + }); + } + } + return apiVersions; +} + +// src/schemaOperations/offlineScopes.ts +var import_graphql = __toESM(require_graphql2(), 1); +function getScopes(data, typeName, fieldName) { + const entry = data.items.find((item) => { + if (fieldName) { + return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; + } + return item.type === "type" && item.typeName === typeName; + }); + return entry?.offlineScopes || []; +} +function getFieldReturnType(data, typeName, fieldName) { + const entry = data.items.find( + (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName + ); + return entry?.returnType; +} +async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { + const offlineScopes = /* @__PURE__ */ new Set(); + const fragmentMap = new Map( + parsedQueryAST.definitions.filter( + (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION + ).map((fragDef) => [fragDef.name.value, fragDef]) + ); + for (const definition of parsedQueryAST.definitions) { + if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { + const operationDef = definition; + if (operationDef.selectionSet) { + const rootTypeName = getRootTypeName( + operationDef.operation, + schemaName + ); + const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); + rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); + walkSelectionSet( + operationDef.selectionSet, + rootTypeName, + offlineScopeData, + offlineScopes, + fragmentMap + ); + } + } + } + return Array.from(offlineScopes); +} +function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { + const fieldName = field.name.value; + const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); + fieldScopes.forEach((scope) => offlineScopes.add(scope)); + if (!field.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); + if (returnType) { + const typeScopes = getScopes(scopeData, returnType); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + } + return { + nextSelectionSet: field.selectionSet, + nextTypeName: returnType || null + }; +} +function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { + const fragmentName = fragmentSpread.name.value; + if (visitedFragments.has(fragmentName)) { + return { nextSelectionSet: null, nextTypeName: null }; + } + visitedFragments.add(fragmentName); + const fragment = fragmentMap.get(fragmentName); + if (!fragment?.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = fragment.typeCondition.name.value; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: fragment.selectionSet, + nextTypeName: typeName + }; +} +function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { + if (!inlineFragment.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: inlineFragment.selectionSet, + nextTypeName: typeName + }; +} +function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { + for (const selection of selectionSet.selections) { + let context; + if (selection.kind === import_graphql.Kind.FIELD) { + context = processFieldSelection( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { + context = processFragmentSpread( + selection, + fragmentMap, + visitedFragments, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { + context = processInlineFragment( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else { + continue; + } + if (context.nextSelectionSet && context.nextTypeName) { + walkSelectionSet( + context.nextSelectionSet, + context.nextTypeName, + scopeData, + offlineScopes, + fragmentMap, + visitedFragments + ); + } + } +} +function getRootTypeName(operation, schemaName = "admin") { + if (schemaName === "admin") { + return operation === "mutation" ? "Mutation" : "QueryRoot"; + } + return operation === "mutation" ? "Mutation" : "Query"; +} +function formatScopes(scopes) { + if (!scopes || scopes.length === 0) { + return ""; + } + return ` +Required scopes: ${scopes.join(", ")}`; +} + +// src/validation/graphql.ts +function isAPIVersionWithAPI(options) { + return options && typeof options.schemaPath === "string"; +} +async function validateGraphQLOperation(graphqlCode, api, options) { + const trimmedCode = graphqlCode.trim(); + if (!trimmedCode) { + return { + validation: { + result: "failed" /* FAILED */, + resultDetail: "No GraphQL operation found in the provided code." + }, + scopes: [] + }; } let apiVersion; let failOnDeprecated = true; + let schemaSource = diskSchemaSource; if (options) { if (isAPIVersionWithAPI(options)) { apiVersion = options; } else { apiVersion = options.apiVersion; failOnDeprecated = options.failOnDeprecated ?? true; + schemaSource = options.schemaSource ?? diskSchemaSource; } } if (apiVersion?.name) { - const supported = SUPPORTED_API_VERSIONS[api]; - if (supported && supported.length > 0 && !supported.includes(apiVersion.name)) { + const supported = (schemaSource.readVersionCatalog()[api] ?? []).map( + (version) => version.name + ); + if (supported.length > 0 && !supported.includes(apiVersion.name)) { throw new Error( `Unsupported version "${apiVersion.name}" for API "${api}". Available versions: ${supported.join(", ")}.` ); @@ -18539,12 +18549,12 @@ async function validateGraphQLOperation(graphqlCode, api, options) { let offlineScopes; let schemaObj; try { - const schemas = loadAPISchemas([api], apiVersion); + const schemas = loadAPISchemas([api], apiVersion, schemaSource); if (schemas.length === 0) { throw new Error(`No schema configuration found for API "${api}"`); } schemaObj = schemas.find((s) => s.latestVersion) ?? schemas[0]; - const result = await loadAndBuildGraphQLSchema(schemaObj); + const result = await loadAndBuildGraphQLSchema(schemaObj, schemaSource); graphQLSchema = result.graphQLSchema; offlineScopes = result.offlineScopes; } catch (error) { @@ -18578,11 +18588,11 @@ function hasFailedValidation(responses) { (response) => response.result === "failed" /* FAILED */ ); } -async function loadAndBuildGraphQLSchema(apiVersion) { +async function loadAndBuildGraphQLSchema(apiVersion, source) { if (!apiVersion || Object.keys(apiVersion).length === 0) { throw new Error("No API version provided"); } - const schemaContent = await loadSchemaContent(apiVersion); + const schemaContent = await source.readSchemaContent(apiVersion); const schemaJson = JSON.parse(schemaContent); const schemaData = schemaJson.data; if (apiVersion.api.startsWith("functions_") && schemaData.__schema && schemaData.__schema.types) { @@ -18937,7 +18947,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-admin", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -18953,7 +18963,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-app-store-review/SKILL.md b/skills/shopify-app-store-review/SKILL.md index f6c0b19..adf59d7 100644 --- a/skills/shopify-app-store-review/SKILL.md +++ b/skills/shopify-app-store-review/SKILL.md @@ -4,7 +4,7 @@ description: "Run a pre-submission compliance check against your Shopify app's c compatibility: Claude Code, Claude Desktop, Cursor metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-app-store-review/scripts/log_skill_use.mjs b/skills/shopify-app-store-review/scripts/log_skill_use.mjs index 3d1b367..f1d01bd 100755 --- a/skills/shopify-app-store-review/scripts/log_skill_use.mjs +++ b/skills/shopify-app-store-review/scripts/log_skill_use.mjs @@ -154,7 +154,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-app-store-review", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -170,7 +170,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-custom-data/SKILL.md b/skills/shopify-custom-data/SKILL.md index c5e7e84..4700a9e 100644 --- a/skills/shopify-custom-data/SKILL.md +++ b/skills/shopify-custom-data/SKILL.md @@ -4,7 +4,7 @@ description: "MUST be used first when prompts mention Metafields or Metaobjects. compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-custom-data/scripts/log_skill_use.mjs b/skills/shopify-custom-data/scripts/log_skill_use.mjs index 838f157..834b4b1 100755 --- a/skills/shopify-custom-data/scripts/log_skill_use.mjs +++ b/skills/shopify-custom-data/scripts/log_skill_use.mjs @@ -154,7 +154,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-custom-data", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -170,7 +170,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-customer/SKILL.md b/skills/shopify-customer/SKILL.md index 5a9e723..9074f09 100644 --- a/skills/shopify-customer/SKILL.md +++ b/skills/shopify-customer/SKILL.md @@ -4,7 +4,7 @@ description: "The Customer Account API allows customers to access their own data compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-customer/scripts/search_docs.mjs b/skills/shopify-customer/scripts/search_docs.mjs index 6508d80..b06fb20 100755 --- a/skills/shopify-customer/scripts/search_docs.mjs +++ b/skills/shopify-customer/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-customer", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-customer/scripts/validate.mjs b/skills/shopify-customer/scripts/validate.mjs index c76fe35..aa66226 100755 --- a/skills/shopify-customer/scripts/validate.mjs +++ b/skills/shopify-customer/scripts/validate.mjs @@ -17303,14 +17303,6 @@ import { fileURLToPath as fileURLToPath2 } from "url"; import path2 from "path"; import { parseArgs } from "util"; -// src/validation/index.ts -var import_graphql2 = __toESM(require_graphql2(), 1); - -// src/schemaOperations/loadAPISchemas.ts -import { readFileSync } from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - // src/types/api-types.ts var Visibility = { PUBLIC: "public", @@ -17734,400 +17726,116 @@ var SHOPIFY_APIS = defineApis({ } }); -// src/schemaOperations/loadAPISchemas.ts -function getDataDirectory() { - const currentDir = path.dirname(fileURLToPath(import.meta.url)); - if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { - return path.join(currentDir, "data"); - } - if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { - const distIndex = currentDir.lastIndexOf(path.sep + "dist"); - if (distIndex !== -1) { - const distRoot = currentDir.substring(0, distIndex + 5); - return path.join(distRoot, "data"); - } else { - return path.join(currentDir, "data"); +// src/data/supported-versions-schema.json +var supported_versions_schema_default = { + admin: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return path.resolve(currentDir, "../data"); -} -var dataDir = getDataDirectory(); -function configuredSchemaPath(api) { - const apiConfig = SHOPIFY_APIS[api]; - if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; - if (apiConfig?.gqlSchemaFileName) { - return path.join(dataDir, apiConfig.gqlSchemaFileName); - } - return void 0; -} -function schemaPathFor(api, versionName) { - return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); -} -function deriveVersionNameFromSchemaFile(fileName) { - const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); - const versionSeparatorIndex = baseName.lastIndexOf("_"); - return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); -} -function loadAPISchemas(apis, schemaOptions) { - if (apis.length === 0) { - throw new Error("No APIs provided"); - } - if (schemaOptions) { - if (apis.length !== 1) { - throw new Error( - "schemaOptions can only be provided when requesting a single API" - ); + ], + "storefront-graphql": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - return [ - { - ...schemaOptions, - api: apis[0], - schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) - } - ]; - } - const schemasPath = path.join(dataDir, "supported-versions-schema.json"); - const schemasConfig = JSON.parse( - readFileSync(schemasPath, "utf-8") - ); - const apiVersions = []; - for (const api of apis) { - const versions = schemasConfig[api]; - if (versions) { - const versionsWithApi = versions.map((v) => ({ - ...v, - api, - schemaPath: schemaPathFor(api, v.name) - })); - apiVersions.push(...versionsWithApi); - } else { - const apiConfig = SHOPIFY_APIS[api]; - const configuredPath = configuredSchemaPath(api); - if (!configuredPath) continue; - apiVersions.push({ - name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", - latestVersion: true, - api, - schemaPath: configuredPath - }); + ], + partner: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return apiVersions; -} - -// src/schemaOperations/loadSchemaContent.ts -import { existsSync } from "node:fs"; -import fs from "node:fs/promises"; -import zlib from "node:zlib"; - -// src/schemaOperations/schemaCache.ts -var SchemaCache = class { - cache = /* @__PURE__ */ new Map(); - get(path3) { - return this.cache.get(path3); - } - set(path3, content) { - this.cache.set(path3, content); - } -}; -var schemaCache = new SchemaCache(); - -// src/schemaOperations/loadSchemaContent.ts -async function convertSdlToIntrospectionJson(schemaPath) { - const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); - const sdl = await fs.readFile(schemaPath, "utf-8"); - const introspection = introspectionFromSchema(buildSchema(sdl)); - return JSON.stringify({ data: introspection }); -} -async function loadSchemaContent(schema) { - const schemaPath = schema.schemaPath; - const cached = schemaCache.get(schemaPath); - if (cached) { - return cached; - } - try { - let content; - if (schemaPath.endsWith(".gz")) { - const compressedData = await fs.readFile(schemaPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { - content = await convertSdlToIntrospectionJson(schemaPath); - } else if (existsSync(schemaPath)) { - content = await fs.readFile(schemaPath, "utf-8"); - } else { - const gzPath = `${schemaPath}.gz`; - if (existsSync(gzPath)) { - const compressedData = await fs.readFile(gzPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else { - throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); - } + ], + customer: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - schemaCache.set(schemaPath, content); - return content; - } catch (error) { - console.error(`[graphql-schema-utils] Error loading schema: ${error}`); - throw error; - } -} - -// src/schemaOperations/offlineScopes.ts -var import_graphql = __toESM(require_graphql2(), 1); -function getScopes(data, typeName, fieldName) { - const entry = data.items.find((item) => { - if (fieldName) { - return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; - } - return item.type === "type" && item.typeName === typeName; - }); - return entry?.offlineScopes || []; -} -function getFieldReturnType(data, typeName, fieldName) { - const entry = data.items.find( - (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName - ); - return entry?.returnType; -} -async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { - const offlineScopes = /* @__PURE__ */ new Set(); - const fragmentMap = new Map( - parsedQueryAST.definitions.filter( - (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION - ).map((fragDef) => [fragDef.name.value, fragDef]) - ); - for (const definition of parsedQueryAST.definitions) { - if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { - const operationDef = definition; - if (operationDef.selectionSet) { - const rootTypeName = getRootTypeName( - operationDef.operation, - schemaName - ); - const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); - rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); - walkSelectionSet( - operationDef.selectionSet, - rootTypeName, - offlineScopeData, - offlineScopes, - fragmentMap - ); - } - } - } - return Array.from(offlineScopes); -} -function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { - const fieldName = field.name.value; - const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); - fieldScopes.forEach((scope) => offlineScopes.add(scope)); - if (!field.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); - if (returnType) { - const typeScopes = getScopes(scopeData, returnType); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - } - return { - nextSelectionSet: field.selectionSet, - nextTypeName: returnType || null - }; -} -function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { - const fragmentName = fragmentSpread.name.value; - if (visitedFragments.has(fragmentName)) { - return { nextSelectionSet: null, nextTypeName: null }; - } - visitedFragments.add(fragmentName); - const fragment = fragmentMap.get(fragmentName); - if (!fragment?.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = fragment.typeCondition.name.value; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: fragment.selectionSet, - nextTypeName: typeName - }; -} -function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { - if (!inlineFragment.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: inlineFragment.selectionSet, - nextTypeName: typeName - }; -} -function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { - for (const selection of selectionSet.selections) { - let context; - if (selection.kind === import_graphql.Kind.FIELD) { - context = processFieldSelection( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { - context = processFragmentSpread( - selection, - fragmentMap, - visitedFragments, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { - context = processInlineFragment( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else { - continue; - } - if (context.nextSelectionSet && context.nextTypeName) { - walkSelectionSet( - context.nextSelectionSet, - context.nextTypeName, - scopeData, - offlineScopes, - fragmentMap, - visitedFragments - ); - } - } -} -function getRootTypeName(operation, schemaName = "admin") { - if (schemaName === "admin") { - return operation === "mutation" ? "Mutation" : "QueryRoot"; - } - return operation === "mutation" ? "Mutation" : "Query"; -} -function formatScopes(scopes) { - if (!scopes || scopes.length === 0) { - return ""; - } - return ` -Required scopes: ${scopes.join(", ")}`; -} - -// src/data/supported-versions-schema.json -var supported_versions_schema_default = { - admin: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "storefront-graphql": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - partner: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - customer: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "payments-apps": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" + ], + "payments-apps": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } ], "polaris-app-home": [], @@ -18502,34 +18210,336 @@ function resolveVersion(apiName2, requested) { return { ok: true, version: latest, source: "default", supportedVersions }; } -// src/validation/index.ts -function isAPIVersionWithAPI(options) { - return options && typeof options.schemaPath === "string"; -} -async function validateGraphQLOperation(graphqlCode, api, options) { - const trimmedCode = graphqlCode.trim(); - if (!trimmedCode) { - return { - validation: { - result: "failed" /* FAILED */, - resultDetail: "No GraphQL operation found in the provided code." - }, - scopes: [] - }; +// src/validation/graphql.ts +var import_graphql2 = __toESM(require_graphql2(), 1); + +// src/schemaOperations/loadAPISchemas.ts +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +// src/schemaOperations/loadSchemaContent.ts +import { existsSync } from "node:fs"; +import fs from "node:fs/promises"; +import zlib from "node:zlib"; + +// src/schemaOperations/schemaCache.ts +var SchemaCache = class { + cache = /* @__PURE__ */ new Map(); + get(path3) { + return this.cache.get(path3); + } + set(path3, content) { + this.cache.set(path3, content); + } +}; +var schemaCache = new SchemaCache(); + +// src/schemaOperations/loadSchemaContent.ts +async function convertSdlToIntrospectionJson(schemaPath) { + const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); + const sdl = await fs.readFile(schemaPath, "utf-8"); + const introspection = introspectionFromSchema(buildSchema(sdl)); + return JSON.stringify({ data: introspection }); +} +async function loadSchemaContent(schema) { + const schemaPath = schema.schemaPath; + const cached = schemaCache.get(schemaPath); + if (cached) { + return cached; + } + try { + let content; + if (schemaPath.endsWith(".gz")) { + const compressedData = await fs.readFile(schemaPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { + content = await convertSdlToIntrospectionJson(schemaPath); + } else if (existsSync(schemaPath)) { + content = await fs.readFile(schemaPath, "utf-8"); + } else { + const gzPath = `${schemaPath}.gz`; + if (existsSync(gzPath)) { + const compressedData = await fs.readFile(gzPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else { + throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); + } + } + schemaCache.set(schemaPath, content); + return content; + } catch (error) { + console.error(`[graphql-schema-utils] Error loading schema: ${error}`); + throw error; + } +} + +// src/schemaOperations/loadAPISchemas.ts +function getDataDirectory() { + const currentDir = path.dirname(fileURLToPath(import.meta.url)); + if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { + return path.join(currentDir, "data"); + } + if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { + const distIndex = currentDir.lastIndexOf(path.sep + "dist"); + if (distIndex !== -1) { + const distRoot = currentDir.substring(0, distIndex + 5); + return path.join(distRoot, "data"); + } else { + return path.join(currentDir, "data"); + } + } + return path.resolve(currentDir, "../data"); +} +var dataDir = getDataDirectory(); +var diskSchemaSource = { + readVersionCatalog() { + const schemasPath = path.join(dataDir, "supported-versions-schema.json"); + return JSON.parse(readFileSync(schemasPath, "utf-8")); + }, + readSchemaContent(schema) { + return loadSchemaContent(schema); + } +}; +function configuredSchemaPath(api) { + const apiConfig = SHOPIFY_APIS[api]; + if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; + if (apiConfig?.gqlSchemaFileName) { + return path.join(dataDir, apiConfig.gqlSchemaFileName); + } + return void 0; +} +function schemaPathFor(api, versionName) { + return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); +} +function deriveVersionNameFromSchemaFile(fileName) { + const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); + const versionSeparatorIndex = baseName.lastIndexOf("_"); + return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); +} +function loadAPISchemas(apis, schemaOptions, source = diskSchemaSource) { + if (apis.length === 0) { + throw new Error("No APIs provided"); + } + if (schemaOptions) { + if (apis.length !== 1) { + throw new Error( + "schemaOptions can only be provided when requesting a single API" + ); + } + return [ + { + ...schemaOptions, + api: apis[0], + schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) + } + ]; + } + const schemasConfig = source.readVersionCatalog(); + const apiVersions = []; + for (const api of apis) { + const versions = schemasConfig[api]; + if (versions) { + const versionsWithApi = versions.map((v) => ({ + ...v, + api, + schemaPath: schemaPathFor(api, v.name) + })); + apiVersions.push(...versionsWithApi); + } else { + const apiConfig = SHOPIFY_APIS[api]; + const configuredPath = configuredSchemaPath(api); + if (!configuredPath) continue; + apiVersions.push({ + name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", + latestVersion: true, + api, + schemaPath: configuredPath + }); + } + } + return apiVersions; +} + +// src/schemaOperations/offlineScopes.ts +var import_graphql = __toESM(require_graphql2(), 1); +function getScopes(data, typeName, fieldName) { + const entry = data.items.find((item) => { + if (fieldName) { + return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; + } + return item.type === "type" && item.typeName === typeName; + }); + return entry?.offlineScopes || []; +} +function getFieldReturnType(data, typeName, fieldName) { + const entry = data.items.find( + (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName + ); + return entry?.returnType; +} +async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { + const offlineScopes = /* @__PURE__ */ new Set(); + const fragmentMap = new Map( + parsedQueryAST.definitions.filter( + (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION + ).map((fragDef) => [fragDef.name.value, fragDef]) + ); + for (const definition of parsedQueryAST.definitions) { + if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { + const operationDef = definition; + if (operationDef.selectionSet) { + const rootTypeName = getRootTypeName( + operationDef.operation, + schemaName + ); + const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); + rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); + walkSelectionSet( + operationDef.selectionSet, + rootTypeName, + offlineScopeData, + offlineScopes, + fragmentMap + ); + } + } + } + return Array.from(offlineScopes); +} +function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { + const fieldName = field.name.value; + const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); + fieldScopes.forEach((scope) => offlineScopes.add(scope)); + if (!field.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); + if (returnType) { + const typeScopes = getScopes(scopeData, returnType); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + } + return { + nextSelectionSet: field.selectionSet, + nextTypeName: returnType || null + }; +} +function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { + const fragmentName = fragmentSpread.name.value; + if (visitedFragments.has(fragmentName)) { + return { nextSelectionSet: null, nextTypeName: null }; + } + visitedFragments.add(fragmentName); + const fragment = fragmentMap.get(fragmentName); + if (!fragment?.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = fragment.typeCondition.name.value; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: fragment.selectionSet, + nextTypeName: typeName + }; +} +function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { + if (!inlineFragment.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: inlineFragment.selectionSet, + nextTypeName: typeName + }; +} +function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { + for (const selection of selectionSet.selections) { + let context; + if (selection.kind === import_graphql.Kind.FIELD) { + context = processFieldSelection( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { + context = processFragmentSpread( + selection, + fragmentMap, + visitedFragments, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { + context = processInlineFragment( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else { + continue; + } + if (context.nextSelectionSet && context.nextTypeName) { + walkSelectionSet( + context.nextSelectionSet, + context.nextTypeName, + scopeData, + offlineScopes, + fragmentMap, + visitedFragments + ); + } + } +} +function getRootTypeName(operation, schemaName = "admin") { + if (schemaName === "admin") { + return operation === "mutation" ? "Mutation" : "QueryRoot"; + } + return operation === "mutation" ? "Mutation" : "Query"; +} +function formatScopes(scopes) { + if (!scopes || scopes.length === 0) { + return ""; + } + return ` +Required scopes: ${scopes.join(", ")}`; +} + +// src/validation/graphql.ts +function isAPIVersionWithAPI(options) { + return options && typeof options.schemaPath === "string"; +} +async function validateGraphQLOperation(graphqlCode, api, options) { + const trimmedCode = graphqlCode.trim(); + if (!trimmedCode) { + return { + validation: { + result: "failed" /* FAILED */, + resultDetail: "No GraphQL operation found in the provided code." + }, + scopes: [] + }; } let apiVersion; let failOnDeprecated = true; + let schemaSource = diskSchemaSource; if (options) { if (isAPIVersionWithAPI(options)) { apiVersion = options; } else { apiVersion = options.apiVersion; failOnDeprecated = options.failOnDeprecated ?? true; + schemaSource = options.schemaSource ?? diskSchemaSource; } } if (apiVersion?.name) { - const supported = SUPPORTED_API_VERSIONS[api]; - if (supported && supported.length > 0 && !supported.includes(apiVersion.name)) { + const supported = (schemaSource.readVersionCatalog()[api] ?? []).map( + (version) => version.name + ); + if (supported.length > 0 && !supported.includes(apiVersion.name)) { throw new Error( `Unsupported version "${apiVersion.name}" for API "${api}". Available versions: ${supported.join(", ")}.` ); @@ -18539,12 +18549,12 @@ async function validateGraphQLOperation(graphqlCode, api, options) { let offlineScopes; let schemaObj; try { - const schemas = loadAPISchemas([api], apiVersion); + const schemas = loadAPISchemas([api], apiVersion, schemaSource); if (schemas.length === 0) { throw new Error(`No schema configuration found for API "${api}"`); } schemaObj = schemas.find((s) => s.latestVersion) ?? schemas[0]; - const result = await loadAndBuildGraphQLSchema(schemaObj); + const result = await loadAndBuildGraphQLSchema(schemaObj, schemaSource); graphQLSchema = result.graphQLSchema; offlineScopes = result.offlineScopes; } catch (error) { @@ -18578,11 +18588,11 @@ function hasFailedValidation(responses) { (response) => response.result === "failed" /* FAILED */ ); } -async function loadAndBuildGraphQLSchema(apiVersion) { +async function loadAndBuildGraphQLSchema(apiVersion, source) { if (!apiVersion || Object.keys(apiVersion).length === 0) { throw new Error("No API version provided"); } - const schemaContent = await loadSchemaContent(apiVersion); + const schemaContent = await source.readSchemaContent(apiVersion); const schemaJson = JSON.parse(schemaContent); const schemaData = schemaJson.data; if (apiVersion.api.startsWith("functions_") && schemaData.__schema && schemaData.__schema.types) { @@ -18937,7 +18947,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-customer", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -18953,7 +18963,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-dev/SKILL.md b/skills/shopify-dev/SKILL.md index b26bc80..de4631d 100644 --- a/skills/shopify-dev/SKILL.md +++ b/skills/shopify-dev/SKILL.md @@ -4,7 +4,7 @@ description: "Search Shopify developer documentation across all APIs. Use only w compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-dev/scripts/log_skill_use.mjs b/skills/shopify-dev/scripts/log_skill_use.mjs index d60e837..e1bb28d 100755 --- a/skills/shopify-dev/scripts/log_skill_use.mjs +++ b/skills/shopify-dev/scripts/log_skill_use.mjs @@ -154,7 +154,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-dev", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -170,7 +170,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-dev/scripts/search_docs.mjs b/skills/shopify-dev/scripts/search_docs.mjs index f89e57f..ebddb71 100755 --- a/skills/shopify-dev/scripts/search_docs.mjs +++ b/skills/shopify-dev/scripts/search_docs.mjs @@ -145,7 +145,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-dev", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -161,7 +161,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -209,7 +209,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-functions/SKILL.md b/skills/shopify-functions/SKILL.md index dd6c8ed..e873c2e 100644 --- a/skills/shopify-functions/SKILL.md +++ b/skills/shopify-functions/SKILL.md @@ -4,7 +4,7 @@ description: "Shopify Functions allow developers to customize the backend logic compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill @@ -340,8 +340,8 @@ pub mod schema { } fn main() { - eprintln!("Please invoke a named export."); - process::exit(1); + log!("Please invoke a named export."); + process::abort(); } ``` diff --git a/skills/shopify-functions/scripts/search_docs.mjs b/skills/shopify-functions/scripts/search_docs.mjs index e1d58b1..ce1cb05 100755 --- a/skills/shopify-functions/scripts/search_docs.mjs +++ b/skills/shopify-functions/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-functions", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-functions/scripts/validate.mjs b/skills/shopify-functions/scripts/validate.mjs index 6a8e88b..84b7ead 100755 --- a/skills/shopify-functions/scripts/validate.mjs +++ b/skills/shopify-functions/scripts/validate.mjs @@ -17303,14 +17303,6 @@ import { fileURLToPath as fileURLToPath2 } from "url"; import path2 from "path"; import { parseArgs } from "util"; -// src/validation/index.ts -var import_graphql2 = __toESM(require_graphql2(), 1); - -// src/schemaOperations/loadAPISchemas.ts -import { readFileSync } from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - // src/types/api-types.ts var Visibility = { PUBLIC: "public", @@ -17734,400 +17726,116 @@ var SHOPIFY_APIS = defineApis({ } }); -// src/schemaOperations/loadAPISchemas.ts -function getDataDirectory() { - const currentDir = path.dirname(fileURLToPath(import.meta.url)); - if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { - return path.join(currentDir, "data"); - } - if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { - const distIndex = currentDir.lastIndexOf(path.sep + "dist"); - if (distIndex !== -1) { - const distRoot = currentDir.substring(0, distIndex + 5); - return path.join(distRoot, "data"); - } else { - return path.join(currentDir, "data"); +// src/data/supported-versions-schema.json +var supported_versions_schema_default = { + admin: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return path.resolve(currentDir, "../data"); -} -var dataDir = getDataDirectory(); -function configuredSchemaPath(api) { - const apiConfig = SHOPIFY_APIS[api]; - if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; - if (apiConfig?.gqlSchemaFileName) { - return path.join(dataDir, apiConfig.gqlSchemaFileName); - } - return void 0; -} -function schemaPathFor(api, versionName) { - return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); -} -function deriveVersionNameFromSchemaFile(fileName) { - const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); - const versionSeparatorIndex = baseName.lastIndexOf("_"); - return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); -} -function loadAPISchemas(apis, schemaOptions) { - if (apis.length === 0) { - throw new Error("No APIs provided"); - } - if (schemaOptions) { - if (apis.length !== 1) { - throw new Error( - "schemaOptions can only be provided when requesting a single API" - ); + ], + "storefront-graphql": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - return [ - { - ...schemaOptions, - api: apis[0], - schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) - } - ]; - } - const schemasPath = path.join(dataDir, "supported-versions-schema.json"); - const schemasConfig = JSON.parse( - readFileSync(schemasPath, "utf-8") - ); - const apiVersions = []; - for (const api of apis) { - const versions = schemasConfig[api]; - if (versions) { - const versionsWithApi = versions.map((v) => ({ - ...v, - api, - schemaPath: schemaPathFor(api, v.name) - })); - apiVersions.push(...versionsWithApi); - } else { - const apiConfig = SHOPIFY_APIS[api]; - const configuredPath = configuredSchemaPath(api); - if (!configuredPath) continue; - apiVersions.push({ - name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", - latestVersion: true, - api, - schemaPath: configuredPath - }); + ], + partner: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return apiVersions; -} -function loadAPISchema(api, schemaOptions) { - const schemas = loadAPISchemas([api], schemaOptions); - if (schemas.length === 0) { - throw new Error(`No schema found for API: ${api}`); - } - return schemas.find((s) => s.latestVersion) ?? schemas[0]; -} - -// src/schemaOperations/loadSchemaContent.ts -import { existsSync } from "node:fs"; -import fs from "node:fs/promises"; -import zlib from "node:zlib"; - -// src/schemaOperations/schemaCache.ts -var SchemaCache = class { - cache = /* @__PURE__ */ new Map(); - get(path3) { - return this.cache.get(path3); - } - set(path3, content) { - this.cache.set(path3, content); - } -}; -var schemaCache = new SchemaCache(); - -// src/schemaOperations/loadSchemaContent.ts -async function convertSdlToIntrospectionJson(schemaPath) { - const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); - const sdl = await fs.readFile(schemaPath, "utf-8"); - const introspection = introspectionFromSchema(buildSchema(sdl)); - return JSON.stringify({ data: introspection }); -} -async function loadSchemaContent(schema) { - const schemaPath = schema.schemaPath; - const cached = schemaCache.get(schemaPath); - if (cached) { - return cached; - } - try { - let content; - if (schemaPath.endsWith(".gz")) { - const compressedData = await fs.readFile(schemaPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { - content = await convertSdlToIntrospectionJson(schemaPath); - } else if (existsSync(schemaPath)) { - content = await fs.readFile(schemaPath, "utf-8"); - } else { - const gzPath = `${schemaPath}.gz`; - if (existsSync(gzPath)) { - const compressedData = await fs.readFile(gzPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else { - throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); - } + ], + customer: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - schemaCache.set(schemaPath, content); - return content; - } catch (error) { - console.error(`[graphql-schema-utils] Error loading schema: ${error}`); - throw error; - } -} - -// src/schemaOperations/offlineScopes.ts -var import_graphql = __toESM(require_graphql2(), 1); -function getScopes(data, typeName, fieldName) { - const entry = data.items.find((item) => { - if (fieldName) { - return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; - } - return item.type === "type" && item.typeName === typeName; - }); - return entry?.offlineScopes || []; -} -function getFieldReturnType(data, typeName, fieldName) { - const entry = data.items.find( - (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName - ); - return entry?.returnType; -} -async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { - const offlineScopes = /* @__PURE__ */ new Set(); - const fragmentMap = new Map( - parsedQueryAST.definitions.filter( - (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION - ).map((fragDef) => [fragDef.name.value, fragDef]) - ); - for (const definition of parsedQueryAST.definitions) { - if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { - const operationDef = definition; - if (operationDef.selectionSet) { - const rootTypeName = getRootTypeName( - operationDef.operation, - schemaName - ); - const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); - rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); - walkSelectionSet( - operationDef.selectionSet, - rootTypeName, - offlineScopeData, - offlineScopes, - fragmentMap - ); - } - } - } - return Array.from(offlineScopes); -} -function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { - const fieldName = field.name.value; - const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); - fieldScopes.forEach((scope) => offlineScopes.add(scope)); - if (!field.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); - if (returnType) { - const typeScopes = getScopes(scopeData, returnType); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - } - return { - nextSelectionSet: field.selectionSet, - nextTypeName: returnType || null - }; -} -function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { - const fragmentName = fragmentSpread.name.value; - if (visitedFragments.has(fragmentName)) { - return { nextSelectionSet: null, nextTypeName: null }; - } - visitedFragments.add(fragmentName); - const fragment = fragmentMap.get(fragmentName); - if (!fragment?.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = fragment.typeCondition.name.value; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: fragment.selectionSet, - nextTypeName: typeName - }; -} -function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { - if (!inlineFragment.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: inlineFragment.selectionSet, - nextTypeName: typeName - }; -} -function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { - for (const selection of selectionSet.selections) { - let context; - if (selection.kind === import_graphql.Kind.FIELD) { - context = processFieldSelection( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { - context = processFragmentSpread( - selection, - fragmentMap, - visitedFragments, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { - context = processInlineFragment( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else { - continue; - } - if (context.nextSelectionSet && context.nextTypeName) { - walkSelectionSet( - context.nextSelectionSet, - context.nextTypeName, - scopeData, - offlineScopes, - fragmentMap, - visitedFragments - ); - } - } -} -function getRootTypeName(operation, schemaName = "admin") { - if (schemaName === "admin") { - return operation === "mutation" ? "Mutation" : "QueryRoot"; - } - return operation === "mutation" ? "Mutation" : "Query"; -} - -// src/data/supported-versions-schema.json -var supported_versions_schema_default = { - admin: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "storefront-graphql": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - partner: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - customer: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "payments-apps": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" + ], + "payments-apps": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } ], "polaris-app-home": [], @@ -18502,34 +18210,336 @@ function resolveVersion(apiName2, requested) { return { ok: true, version: latest, source: "default", supportedVersions }; } -// src/validation/index.ts -function isAPIVersionWithAPI(options) { - return options && typeof options.schemaPath === "string"; -} -async function validateGraphQLOperation(graphqlCode, api, options) { - const trimmedCode = graphqlCode.trim(); - if (!trimmedCode) { - return { - validation: { - result: "failed" /* FAILED */, - resultDetail: "No GraphQL operation found in the provided code." - }, - scopes: [] - }; - } - let apiVersion; +// src/validation/graphql.ts +var import_graphql2 = __toESM(require_graphql2(), 1); + +// src/schemaOperations/loadAPISchemas.ts +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +// src/schemaOperations/loadSchemaContent.ts +import { existsSync } from "node:fs"; +import fs from "node:fs/promises"; +import zlib from "node:zlib"; + +// src/schemaOperations/schemaCache.ts +var SchemaCache = class { + cache = /* @__PURE__ */ new Map(); + get(path3) { + return this.cache.get(path3); + } + set(path3, content) { + this.cache.set(path3, content); + } +}; +var schemaCache = new SchemaCache(); + +// src/schemaOperations/loadSchemaContent.ts +async function convertSdlToIntrospectionJson(schemaPath) { + const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); + const sdl = await fs.readFile(schemaPath, "utf-8"); + const introspection = introspectionFromSchema(buildSchema(sdl)); + return JSON.stringify({ data: introspection }); +} +async function loadSchemaContent(schema) { + const schemaPath = schema.schemaPath; + const cached = schemaCache.get(schemaPath); + if (cached) { + return cached; + } + try { + let content; + if (schemaPath.endsWith(".gz")) { + const compressedData = await fs.readFile(schemaPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { + content = await convertSdlToIntrospectionJson(schemaPath); + } else if (existsSync(schemaPath)) { + content = await fs.readFile(schemaPath, "utf-8"); + } else { + const gzPath = `${schemaPath}.gz`; + if (existsSync(gzPath)) { + const compressedData = await fs.readFile(gzPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else { + throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); + } + } + schemaCache.set(schemaPath, content); + return content; + } catch (error) { + console.error(`[graphql-schema-utils] Error loading schema: ${error}`); + throw error; + } +} + +// src/schemaOperations/loadAPISchemas.ts +function getDataDirectory() { + const currentDir = path.dirname(fileURLToPath(import.meta.url)); + if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { + return path.join(currentDir, "data"); + } + if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { + const distIndex = currentDir.lastIndexOf(path.sep + "dist"); + if (distIndex !== -1) { + const distRoot = currentDir.substring(0, distIndex + 5); + return path.join(distRoot, "data"); + } else { + return path.join(currentDir, "data"); + } + } + return path.resolve(currentDir, "../data"); +} +var dataDir = getDataDirectory(); +var diskSchemaSource = { + readVersionCatalog() { + const schemasPath = path.join(dataDir, "supported-versions-schema.json"); + return JSON.parse(readFileSync(schemasPath, "utf-8")); + }, + readSchemaContent(schema) { + return loadSchemaContent(schema); + } +}; +function configuredSchemaPath(api) { + const apiConfig = SHOPIFY_APIS[api]; + if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; + if (apiConfig?.gqlSchemaFileName) { + return path.join(dataDir, apiConfig.gqlSchemaFileName); + } + return void 0; +} +function schemaPathFor(api, versionName) { + return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); +} +function deriveVersionNameFromSchemaFile(fileName) { + const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); + const versionSeparatorIndex = baseName.lastIndexOf("_"); + return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); +} +function loadAPISchemas(apis, schemaOptions, source = diskSchemaSource) { + if (apis.length === 0) { + throw new Error("No APIs provided"); + } + if (schemaOptions) { + if (apis.length !== 1) { + throw new Error( + "schemaOptions can only be provided when requesting a single API" + ); + } + return [ + { + ...schemaOptions, + api: apis[0], + schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) + } + ]; + } + const schemasConfig = source.readVersionCatalog(); + const apiVersions = []; + for (const api of apis) { + const versions = schemasConfig[api]; + if (versions) { + const versionsWithApi = versions.map((v) => ({ + ...v, + api, + schemaPath: schemaPathFor(api, v.name) + })); + apiVersions.push(...versionsWithApi); + } else { + const apiConfig = SHOPIFY_APIS[api]; + const configuredPath = configuredSchemaPath(api); + if (!configuredPath) continue; + apiVersions.push({ + name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", + latestVersion: true, + api, + schemaPath: configuredPath + }); + } + } + return apiVersions; +} +function loadAPISchema(api, schemaOptions, source = diskSchemaSource) { + const schemas = loadAPISchemas([api], schemaOptions, source); + if (schemas.length === 0) { + throw new Error(`No schema found for API: ${api}`); + } + return schemas.find((s) => s.latestVersion) ?? schemas[0]; +} + +// src/schemaOperations/offlineScopes.ts +var import_graphql = __toESM(require_graphql2(), 1); +function getScopes(data, typeName, fieldName) { + const entry = data.items.find((item) => { + if (fieldName) { + return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; + } + return item.type === "type" && item.typeName === typeName; + }); + return entry?.offlineScopes || []; +} +function getFieldReturnType(data, typeName, fieldName) { + const entry = data.items.find( + (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName + ); + return entry?.returnType; +} +async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { + const offlineScopes = /* @__PURE__ */ new Set(); + const fragmentMap = new Map( + parsedQueryAST.definitions.filter( + (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION + ).map((fragDef) => [fragDef.name.value, fragDef]) + ); + for (const definition of parsedQueryAST.definitions) { + if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { + const operationDef = definition; + if (operationDef.selectionSet) { + const rootTypeName = getRootTypeName( + operationDef.operation, + schemaName + ); + const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); + rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); + walkSelectionSet( + operationDef.selectionSet, + rootTypeName, + offlineScopeData, + offlineScopes, + fragmentMap + ); + } + } + } + return Array.from(offlineScopes); +} +function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { + const fieldName = field.name.value; + const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); + fieldScopes.forEach((scope) => offlineScopes.add(scope)); + if (!field.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); + if (returnType) { + const typeScopes = getScopes(scopeData, returnType); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + } + return { + nextSelectionSet: field.selectionSet, + nextTypeName: returnType || null + }; +} +function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { + const fragmentName = fragmentSpread.name.value; + if (visitedFragments.has(fragmentName)) { + return { nextSelectionSet: null, nextTypeName: null }; + } + visitedFragments.add(fragmentName); + const fragment = fragmentMap.get(fragmentName); + if (!fragment?.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = fragment.typeCondition.name.value; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: fragment.selectionSet, + nextTypeName: typeName + }; +} +function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { + if (!inlineFragment.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: inlineFragment.selectionSet, + nextTypeName: typeName + }; +} +function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { + for (const selection of selectionSet.selections) { + let context; + if (selection.kind === import_graphql.Kind.FIELD) { + context = processFieldSelection( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { + context = processFragmentSpread( + selection, + fragmentMap, + visitedFragments, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { + context = processInlineFragment( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else { + continue; + } + if (context.nextSelectionSet && context.nextTypeName) { + walkSelectionSet( + context.nextSelectionSet, + context.nextTypeName, + scopeData, + offlineScopes, + fragmentMap, + visitedFragments + ); + } + } +} +function getRootTypeName(operation, schemaName = "admin") { + if (schemaName === "admin") { + return operation === "mutation" ? "Mutation" : "QueryRoot"; + } + return operation === "mutation" ? "Mutation" : "Query"; +} + +// src/validation/graphql.ts +function isAPIVersionWithAPI(options) { + return options && typeof options.schemaPath === "string"; +} +async function validateGraphQLOperation(graphqlCode, api, options) { + const trimmedCode = graphqlCode.trim(); + if (!trimmedCode) { + return { + validation: { + result: "failed" /* FAILED */, + resultDetail: "No GraphQL operation found in the provided code." + }, + scopes: [] + }; + } + let apiVersion; let failOnDeprecated = true; + let schemaSource = diskSchemaSource; if (options) { if (isAPIVersionWithAPI(options)) { apiVersion = options; } else { apiVersion = options.apiVersion; failOnDeprecated = options.failOnDeprecated ?? true; + schemaSource = options.schemaSource ?? diskSchemaSource; } } if (apiVersion?.name) { - const supported = SUPPORTED_API_VERSIONS[api]; - if (supported && supported.length > 0 && !supported.includes(apiVersion.name)) { + const supported = (schemaSource.readVersionCatalog()[api] ?? []).map( + (version) => version.name + ); + if (supported.length > 0 && !supported.includes(apiVersion.name)) { throw new Error( `Unsupported version "${apiVersion.name}" for API "${api}". Available versions: ${supported.join(", ")}.` ); @@ -18539,12 +18549,12 @@ async function validateGraphQLOperation(graphqlCode, api, options) { let offlineScopes; let schemaObj; try { - const schemas = loadAPISchemas([api], apiVersion); + const schemas = loadAPISchemas([api], apiVersion, schemaSource); if (schemas.length === 0) { throw new Error(`No schema configuration found for API "${api}"`); } schemaObj = schemas.find((s) => s.latestVersion) ?? schemas[0]; - const result = await loadAndBuildGraphQLSchema(schemaObj); + const result = await loadAndBuildGraphQLSchema(schemaObj, schemaSource); graphQLSchema = result.graphQLSchema; offlineScopes = result.offlineScopes; } catch (error) { @@ -18578,11 +18588,11 @@ function hasFailedValidation(responses) { (response) => response.result === "failed" /* FAILED */ ); } -async function loadAndBuildGraphQLSchema(apiVersion) { +async function loadAndBuildGraphQLSchema(apiVersion, source) { if (!apiVersion || Object.keys(apiVersion).length === 0) { throw new Error("No API version provided"); } - const schemaContent = await loadSchemaContent(apiVersion); + const schemaContent = await source.readSchemaContent(apiVersion); const schemaJson = JSON.parse(schemaContent); const schemaData = schemaJson.data; if (apiVersion.api.startsWith("functions_") && schemaData.__schema && schemaData.__schema.types) { @@ -18937,7 +18947,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-functions", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -18953,7 +18963,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-hydrogen/SKILL.md b/skills/shopify-hydrogen/SKILL.md index b96282d..b75673c 100644 --- a/skills/shopify-hydrogen/SKILL.md +++ b/skills/shopify-hydrogen/SKILL.md @@ -4,7 +4,7 @@ description: "Hydrogen storefront implementation cookbooks. Some of the availabl compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-hydrogen/scripts/search_docs.mjs b/skills/shopify-hydrogen/scripts/search_docs.mjs index 10ccf52..341b55d 100755 --- a/skills/shopify-hydrogen/scripts/search_docs.mjs +++ b/skills/shopify-hydrogen/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-hydrogen", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-hydrogen/scripts/validate.mjs b/skills/shopify-hydrogen/scripts/validate.mjs index 06fab5c..000f331 100755 --- a/skills/shopify-hydrogen/scripts/validate.mjs +++ b/skills/shopify-hydrogen/scripts/validate.mjs @@ -2463,7 +2463,7 @@ ${installCmd}` // src/validation/format.ts import { randomUUID } from "crypto"; -// src/validation/index.ts +// src/validation/graphql.ts function hasFailedValidation(responses) { return responses.some( (response) => response.result === "failed" /* FAILED */ @@ -2694,7 +2694,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-hydrogen", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -2710,7 +2710,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-liquid/SKILL.md b/skills/shopify-liquid/SKILL.md index 3b46518..78f560a 100644 --- a/skills/shopify-liquid/SKILL.md +++ b/skills/shopify-liquid/SKILL.md @@ -4,7 +4,7 @@ description: "Liquid is an open-source templating language created by Shopify. I compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-liquid/scripts/search_docs.mjs b/skills/shopify-liquid/scripts/search_docs.mjs index d001cea..9fb03f3 100755 --- a/skills/shopify-liquid/scripts/search_docs.mjs +++ b/skills/shopify-liquid/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-liquid", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-liquid/scripts/validate.mjs b/skills/shopify-liquid/scripts/validate.mjs index 85ad59f..13c5f40 100755 --- a/skills/shopify-liquid/scripts/validate.mjs +++ b/skills/shopify-liquid/scripts/validate.mjs @@ -21,7 +21,7 @@ import { themeCheckRun } from "@shopify/theme-check-node"; // src/validation/format.ts import { randomUUID } from "crypto"; -// src/validation/index.ts +// src/validation/graphql.ts function hasFailedValidation(responses) { return responses.some( (response) => response.result === "failed" /* FAILED */ @@ -252,7 +252,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-liquid", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -268,7 +268,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-onboarding-dev/SKILL.md b/skills/shopify-onboarding-dev/SKILL.md index 0c96aee..01d0309 100644 --- a/skills/shopify-onboarding-dev/SKILL.md +++ b/skills/shopify-onboarding-dev/SKILL.md @@ -4,7 +4,7 @@ description: "Get started building on Shopify. Use when a developer asks to buil compatibility: Claude Code, Claude Desktop, Cursor metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-onboarding-dev/scripts/log_skill_use.mjs b/skills/shopify-onboarding-dev/scripts/log_skill_use.mjs index 325eb65..749941d 100755 --- a/skills/shopify-onboarding-dev/scripts/log_skill_use.mjs +++ b/skills/shopify-onboarding-dev/scripts/log_skill_use.mjs @@ -154,7 +154,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-onboarding-dev", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -170,7 +170,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-onboarding-merchant/SKILL.md b/skills/shopify-onboarding-merchant/SKILL.md index 7fa0c98..2584950 100644 --- a/skills/shopify-onboarding-merchant/SKILL.md +++ b/skills/shopify-onboarding-merchant/SKILL.md @@ -6,7 +6,7 @@ context: fork maintainer: Shopify metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-onboarding-merchant/scripts/log_skill_use.mjs b/skills/shopify-onboarding-merchant/scripts/log_skill_use.mjs index 2da23d8..1a185a3 100755 --- a/skills/shopify-onboarding-merchant/scripts/log_skill_use.mjs +++ b/skills/shopify-onboarding-merchant/scripts/log_skill_use.mjs @@ -154,7 +154,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-onboarding-merchant", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -170,7 +170,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-partner/SKILL.md b/skills/shopify-partner/SKILL.md index 5715436..6cb37ed 100644 --- a/skills/shopify-partner/SKILL.md +++ b/skills/shopify-partner/SKILL.md @@ -4,7 +4,7 @@ description: "The Partner API lets you programmatically access data about your P compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-partner/scripts/search_docs.mjs b/skills/shopify-partner/scripts/search_docs.mjs index a3d9bda..7cb3f2d 100755 --- a/skills/shopify-partner/scripts/search_docs.mjs +++ b/skills/shopify-partner/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-partner", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-partner/scripts/validate.mjs b/skills/shopify-partner/scripts/validate.mjs index 2f50199..c751bd4 100755 --- a/skills/shopify-partner/scripts/validate.mjs +++ b/skills/shopify-partner/scripts/validate.mjs @@ -17303,14 +17303,6 @@ import { fileURLToPath as fileURLToPath2 } from "url"; import path2 from "path"; import { parseArgs } from "util"; -// src/validation/index.ts -var import_graphql2 = __toESM(require_graphql2(), 1); - -// src/schemaOperations/loadAPISchemas.ts -import { readFileSync } from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - // src/types/api-types.ts var Visibility = { PUBLIC: "public", @@ -17734,400 +17726,116 @@ var SHOPIFY_APIS = defineApis({ } }); -// src/schemaOperations/loadAPISchemas.ts -function getDataDirectory() { - const currentDir = path.dirname(fileURLToPath(import.meta.url)); - if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { - return path.join(currentDir, "data"); - } - if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { - const distIndex = currentDir.lastIndexOf(path.sep + "dist"); - if (distIndex !== -1) { - const distRoot = currentDir.substring(0, distIndex + 5); - return path.join(distRoot, "data"); - } else { - return path.join(currentDir, "data"); +// src/data/supported-versions-schema.json +var supported_versions_schema_default = { + admin: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return path.resolve(currentDir, "../data"); -} -var dataDir = getDataDirectory(); -function configuredSchemaPath(api) { - const apiConfig = SHOPIFY_APIS[api]; - if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; - if (apiConfig?.gqlSchemaFileName) { - return path.join(dataDir, apiConfig.gqlSchemaFileName); - } - return void 0; -} -function schemaPathFor(api, versionName) { - return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); -} -function deriveVersionNameFromSchemaFile(fileName) { - const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); - const versionSeparatorIndex = baseName.lastIndexOf("_"); - return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); -} -function loadAPISchemas(apis, schemaOptions) { - if (apis.length === 0) { - throw new Error("No APIs provided"); - } - if (schemaOptions) { - if (apis.length !== 1) { - throw new Error( - "schemaOptions can only be provided when requesting a single API" - ); + ], + "storefront-graphql": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - return [ - { - ...schemaOptions, - api: apis[0], - schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) - } - ]; - } - const schemasPath = path.join(dataDir, "supported-versions-schema.json"); - const schemasConfig = JSON.parse( - readFileSync(schemasPath, "utf-8") - ); - const apiVersions = []; - for (const api of apis) { - const versions = schemasConfig[api]; - if (versions) { - const versionsWithApi = versions.map((v) => ({ - ...v, - api, - schemaPath: schemaPathFor(api, v.name) - })); - apiVersions.push(...versionsWithApi); - } else { - const apiConfig = SHOPIFY_APIS[api]; - const configuredPath = configuredSchemaPath(api); - if (!configuredPath) continue; - apiVersions.push({ - name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", - latestVersion: true, - api, - schemaPath: configuredPath - }); + ], + partner: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return apiVersions; -} - -// src/schemaOperations/loadSchemaContent.ts -import { existsSync } from "node:fs"; -import fs from "node:fs/promises"; -import zlib from "node:zlib"; - -// src/schemaOperations/schemaCache.ts -var SchemaCache = class { - cache = /* @__PURE__ */ new Map(); - get(path3) { - return this.cache.get(path3); - } - set(path3, content) { - this.cache.set(path3, content); - } -}; -var schemaCache = new SchemaCache(); - -// src/schemaOperations/loadSchemaContent.ts -async function convertSdlToIntrospectionJson(schemaPath) { - const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); - const sdl = await fs.readFile(schemaPath, "utf-8"); - const introspection = introspectionFromSchema(buildSchema(sdl)); - return JSON.stringify({ data: introspection }); -} -async function loadSchemaContent(schema) { - const schemaPath = schema.schemaPath; - const cached = schemaCache.get(schemaPath); - if (cached) { - return cached; - } - try { - let content; - if (schemaPath.endsWith(".gz")) { - const compressedData = await fs.readFile(schemaPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { - content = await convertSdlToIntrospectionJson(schemaPath); - } else if (existsSync(schemaPath)) { - content = await fs.readFile(schemaPath, "utf-8"); - } else { - const gzPath = `${schemaPath}.gz`; - if (existsSync(gzPath)) { - const compressedData = await fs.readFile(gzPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else { - throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); - } + ], + customer: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - schemaCache.set(schemaPath, content); - return content; - } catch (error) { - console.error(`[graphql-schema-utils] Error loading schema: ${error}`); - throw error; - } -} - -// src/schemaOperations/offlineScopes.ts -var import_graphql = __toESM(require_graphql2(), 1); -function getScopes(data, typeName, fieldName) { - const entry = data.items.find((item) => { - if (fieldName) { - return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; - } - return item.type === "type" && item.typeName === typeName; - }); - return entry?.offlineScopes || []; -} -function getFieldReturnType(data, typeName, fieldName) { - const entry = data.items.find( - (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName - ); - return entry?.returnType; -} -async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { - const offlineScopes = /* @__PURE__ */ new Set(); - const fragmentMap = new Map( - parsedQueryAST.definitions.filter( - (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION - ).map((fragDef) => [fragDef.name.value, fragDef]) - ); - for (const definition of parsedQueryAST.definitions) { - if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { - const operationDef = definition; - if (operationDef.selectionSet) { - const rootTypeName = getRootTypeName( - operationDef.operation, - schemaName - ); - const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); - rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); - walkSelectionSet( - operationDef.selectionSet, - rootTypeName, - offlineScopeData, - offlineScopes, - fragmentMap - ); - } - } - } - return Array.from(offlineScopes); -} -function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { - const fieldName = field.name.value; - const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); - fieldScopes.forEach((scope) => offlineScopes.add(scope)); - if (!field.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); - if (returnType) { - const typeScopes = getScopes(scopeData, returnType); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - } - return { - nextSelectionSet: field.selectionSet, - nextTypeName: returnType || null - }; -} -function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { - const fragmentName = fragmentSpread.name.value; - if (visitedFragments.has(fragmentName)) { - return { nextSelectionSet: null, nextTypeName: null }; - } - visitedFragments.add(fragmentName); - const fragment = fragmentMap.get(fragmentName); - if (!fragment?.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = fragment.typeCondition.name.value; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: fragment.selectionSet, - nextTypeName: typeName - }; -} -function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { - if (!inlineFragment.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: inlineFragment.selectionSet, - nextTypeName: typeName - }; -} -function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { - for (const selection of selectionSet.selections) { - let context; - if (selection.kind === import_graphql.Kind.FIELD) { - context = processFieldSelection( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { - context = processFragmentSpread( - selection, - fragmentMap, - visitedFragments, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { - context = processInlineFragment( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else { - continue; - } - if (context.nextSelectionSet && context.nextTypeName) { - walkSelectionSet( - context.nextSelectionSet, - context.nextTypeName, - scopeData, - offlineScopes, - fragmentMap, - visitedFragments - ); - } - } -} -function getRootTypeName(operation, schemaName = "admin") { - if (schemaName === "admin") { - return operation === "mutation" ? "Mutation" : "QueryRoot"; - } - return operation === "mutation" ? "Mutation" : "Query"; -} -function formatScopes(scopes) { - if (!scopes || scopes.length === 0) { - return ""; - } - return ` -Required scopes: ${scopes.join(", ")}`; -} - -// src/data/supported-versions-schema.json -var supported_versions_schema_default = { - admin: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "storefront-graphql": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - partner: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - customer: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "payments-apps": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" + ], + "payments-apps": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } ], "polaris-app-home": [], @@ -18502,34 +18210,336 @@ function resolveVersion(apiName2, requested) { return { ok: true, version: latest, source: "default", supportedVersions }; } -// src/validation/index.ts -function isAPIVersionWithAPI(options) { - return options && typeof options.schemaPath === "string"; -} -async function validateGraphQLOperation(graphqlCode, api, options) { - const trimmedCode = graphqlCode.trim(); - if (!trimmedCode) { - return { - validation: { - result: "failed" /* FAILED */, - resultDetail: "No GraphQL operation found in the provided code." - }, - scopes: [] - }; +// src/validation/graphql.ts +var import_graphql2 = __toESM(require_graphql2(), 1); + +// src/schemaOperations/loadAPISchemas.ts +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +// src/schemaOperations/loadSchemaContent.ts +import { existsSync } from "node:fs"; +import fs from "node:fs/promises"; +import zlib from "node:zlib"; + +// src/schemaOperations/schemaCache.ts +var SchemaCache = class { + cache = /* @__PURE__ */ new Map(); + get(path3) { + return this.cache.get(path3); + } + set(path3, content) { + this.cache.set(path3, content); + } +}; +var schemaCache = new SchemaCache(); + +// src/schemaOperations/loadSchemaContent.ts +async function convertSdlToIntrospectionJson(schemaPath) { + const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); + const sdl = await fs.readFile(schemaPath, "utf-8"); + const introspection = introspectionFromSchema(buildSchema(sdl)); + return JSON.stringify({ data: introspection }); +} +async function loadSchemaContent(schema) { + const schemaPath = schema.schemaPath; + const cached = schemaCache.get(schemaPath); + if (cached) { + return cached; + } + try { + let content; + if (schemaPath.endsWith(".gz")) { + const compressedData = await fs.readFile(schemaPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { + content = await convertSdlToIntrospectionJson(schemaPath); + } else if (existsSync(schemaPath)) { + content = await fs.readFile(schemaPath, "utf-8"); + } else { + const gzPath = `${schemaPath}.gz`; + if (existsSync(gzPath)) { + const compressedData = await fs.readFile(gzPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else { + throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); + } + } + schemaCache.set(schemaPath, content); + return content; + } catch (error) { + console.error(`[graphql-schema-utils] Error loading schema: ${error}`); + throw error; + } +} + +// src/schemaOperations/loadAPISchemas.ts +function getDataDirectory() { + const currentDir = path.dirname(fileURLToPath(import.meta.url)); + if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { + return path.join(currentDir, "data"); + } + if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { + const distIndex = currentDir.lastIndexOf(path.sep + "dist"); + if (distIndex !== -1) { + const distRoot = currentDir.substring(0, distIndex + 5); + return path.join(distRoot, "data"); + } else { + return path.join(currentDir, "data"); + } + } + return path.resolve(currentDir, "../data"); +} +var dataDir = getDataDirectory(); +var diskSchemaSource = { + readVersionCatalog() { + const schemasPath = path.join(dataDir, "supported-versions-schema.json"); + return JSON.parse(readFileSync(schemasPath, "utf-8")); + }, + readSchemaContent(schema) { + return loadSchemaContent(schema); + } +}; +function configuredSchemaPath(api) { + const apiConfig = SHOPIFY_APIS[api]; + if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; + if (apiConfig?.gqlSchemaFileName) { + return path.join(dataDir, apiConfig.gqlSchemaFileName); + } + return void 0; +} +function schemaPathFor(api, versionName) { + return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); +} +function deriveVersionNameFromSchemaFile(fileName) { + const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); + const versionSeparatorIndex = baseName.lastIndexOf("_"); + return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); +} +function loadAPISchemas(apis, schemaOptions, source = diskSchemaSource) { + if (apis.length === 0) { + throw new Error("No APIs provided"); + } + if (schemaOptions) { + if (apis.length !== 1) { + throw new Error( + "schemaOptions can only be provided when requesting a single API" + ); + } + return [ + { + ...schemaOptions, + api: apis[0], + schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) + } + ]; + } + const schemasConfig = source.readVersionCatalog(); + const apiVersions = []; + for (const api of apis) { + const versions = schemasConfig[api]; + if (versions) { + const versionsWithApi = versions.map((v) => ({ + ...v, + api, + schemaPath: schemaPathFor(api, v.name) + })); + apiVersions.push(...versionsWithApi); + } else { + const apiConfig = SHOPIFY_APIS[api]; + const configuredPath = configuredSchemaPath(api); + if (!configuredPath) continue; + apiVersions.push({ + name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", + latestVersion: true, + api, + schemaPath: configuredPath + }); + } + } + return apiVersions; +} + +// src/schemaOperations/offlineScopes.ts +var import_graphql = __toESM(require_graphql2(), 1); +function getScopes(data, typeName, fieldName) { + const entry = data.items.find((item) => { + if (fieldName) { + return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; + } + return item.type === "type" && item.typeName === typeName; + }); + return entry?.offlineScopes || []; +} +function getFieldReturnType(data, typeName, fieldName) { + const entry = data.items.find( + (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName + ); + return entry?.returnType; +} +async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { + const offlineScopes = /* @__PURE__ */ new Set(); + const fragmentMap = new Map( + parsedQueryAST.definitions.filter( + (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION + ).map((fragDef) => [fragDef.name.value, fragDef]) + ); + for (const definition of parsedQueryAST.definitions) { + if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { + const operationDef = definition; + if (operationDef.selectionSet) { + const rootTypeName = getRootTypeName( + operationDef.operation, + schemaName + ); + const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); + rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); + walkSelectionSet( + operationDef.selectionSet, + rootTypeName, + offlineScopeData, + offlineScopes, + fragmentMap + ); + } + } + } + return Array.from(offlineScopes); +} +function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { + const fieldName = field.name.value; + const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); + fieldScopes.forEach((scope) => offlineScopes.add(scope)); + if (!field.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); + if (returnType) { + const typeScopes = getScopes(scopeData, returnType); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + } + return { + nextSelectionSet: field.selectionSet, + nextTypeName: returnType || null + }; +} +function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { + const fragmentName = fragmentSpread.name.value; + if (visitedFragments.has(fragmentName)) { + return { nextSelectionSet: null, nextTypeName: null }; + } + visitedFragments.add(fragmentName); + const fragment = fragmentMap.get(fragmentName); + if (!fragment?.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = fragment.typeCondition.name.value; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: fragment.selectionSet, + nextTypeName: typeName + }; +} +function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { + if (!inlineFragment.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: inlineFragment.selectionSet, + nextTypeName: typeName + }; +} +function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { + for (const selection of selectionSet.selections) { + let context; + if (selection.kind === import_graphql.Kind.FIELD) { + context = processFieldSelection( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { + context = processFragmentSpread( + selection, + fragmentMap, + visitedFragments, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { + context = processInlineFragment( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else { + continue; + } + if (context.nextSelectionSet && context.nextTypeName) { + walkSelectionSet( + context.nextSelectionSet, + context.nextTypeName, + scopeData, + offlineScopes, + fragmentMap, + visitedFragments + ); + } + } +} +function getRootTypeName(operation, schemaName = "admin") { + if (schemaName === "admin") { + return operation === "mutation" ? "Mutation" : "QueryRoot"; + } + return operation === "mutation" ? "Mutation" : "Query"; +} +function formatScopes(scopes) { + if (!scopes || scopes.length === 0) { + return ""; + } + return ` +Required scopes: ${scopes.join(", ")}`; +} + +// src/validation/graphql.ts +function isAPIVersionWithAPI(options) { + return options && typeof options.schemaPath === "string"; +} +async function validateGraphQLOperation(graphqlCode, api, options) { + const trimmedCode = graphqlCode.trim(); + if (!trimmedCode) { + return { + validation: { + result: "failed" /* FAILED */, + resultDetail: "No GraphQL operation found in the provided code." + }, + scopes: [] + }; } let apiVersion; let failOnDeprecated = true; + let schemaSource = diskSchemaSource; if (options) { if (isAPIVersionWithAPI(options)) { apiVersion = options; } else { apiVersion = options.apiVersion; failOnDeprecated = options.failOnDeprecated ?? true; + schemaSource = options.schemaSource ?? diskSchemaSource; } } if (apiVersion?.name) { - const supported = SUPPORTED_API_VERSIONS[api]; - if (supported && supported.length > 0 && !supported.includes(apiVersion.name)) { + const supported = (schemaSource.readVersionCatalog()[api] ?? []).map( + (version) => version.name + ); + if (supported.length > 0 && !supported.includes(apiVersion.name)) { throw new Error( `Unsupported version "${apiVersion.name}" for API "${api}". Available versions: ${supported.join(", ")}.` ); @@ -18539,12 +18549,12 @@ async function validateGraphQLOperation(graphqlCode, api, options) { let offlineScopes; let schemaObj; try { - const schemas = loadAPISchemas([api], apiVersion); + const schemas = loadAPISchemas([api], apiVersion, schemaSource); if (schemas.length === 0) { throw new Error(`No schema configuration found for API "${api}"`); } schemaObj = schemas.find((s) => s.latestVersion) ?? schemas[0]; - const result = await loadAndBuildGraphQLSchema(schemaObj); + const result = await loadAndBuildGraphQLSchema(schemaObj, schemaSource); graphQLSchema = result.graphQLSchema; offlineScopes = result.offlineScopes; } catch (error) { @@ -18578,11 +18588,11 @@ function hasFailedValidation(responses) { (response) => response.result === "failed" /* FAILED */ ); } -async function loadAndBuildGraphQLSchema(apiVersion) { +async function loadAndBuildGraphQLSchema(apiVersion, source) { if (!apiVersion || Object.keys(apiVersion).length === 0) { throw new Error("No API version provided"); } - const schemaContent = await loadSchemaContent(apiVersion); + const schemaContent = await source.readSchemaContent(apiVersion); const schemaJson = JSON.parse(schemaContent); const schemaData = schemaJson.data; if (apiVersion.api.startsWith("functions_") && schemaData.__schema && schemaData.__schema.types) { @@ -18937,7 +18947,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-partner", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -18953,7 +18963,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-payments-apps/SKILL.md b/skills/shopify-payments-apps/SKILL.md index f0ca524..37931a7 100644 --- a/skills/shopify-payments-apps/SKILL.md +++ b/skills/shopify-payments-apps/SKILL.md @@ -4,7 +4,7 @@ description: "The Payments Apps API enables payment providers to integrate their compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-payments-apps/scripts/search_docs.mjs b/skills/shopify-payments-apps/scripts/search_docs.mjs index d1219f2..a5f7360 100755 --- a/skills/shopify-payments-apps/scripts/search_docs.mjs +++ b/skills/shopify-payments-apps/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-payments-apps", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-payments-apps/scripts/validate.mjs b/skills/shopify-payments-apps/scripts/validate.mjs index ceda52c..a2e6130 100755 --- a/skills/shopify-payments-apps/scripts/validate.mjs +++ b/skills/shopify-payments-apps/scripts/validate.mjs @@ -17303,14 +17303,6 @@ import { fileURLToPath as fileURLToPath2 } from "url"; import path2 from "path"; import { parseArgs } from "util"; -// src/validation/index.ts -var import_graphql2 = __toESM(require_graphql2(), 1); - -// src/schemaOperations/loadAPISchemas.ts -import { readFileSync } from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - // src/types/api-types.ts var Visibility = { PUBLIC: "public", @@ -17734,400 +17726,116 @@ var SHOPIFY_APIS = defineApis({ } }); -// src/schemaOperations/loadAPISchemas.ts -function getDataDirectory() { - const currentDir = path.dirname(fileURLToPath(import.meta.url)); - if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { - return path.join(currentDir, "data"); - } - if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { - const distIndex = currentDir.lastIndexOf(path.sep + "dist"); - if (distIndex !== -1) { - const distRoot = currentDir.substring(0, distIndex + 5); - return path.join(distRoot, "data"); - } else { - return path.join(currentDir, "data"); +// src/data/supported-versions-schema.json +var supported_versions_schema_default = { + admin: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return path.resolve(currentDir, "../data"); -} -var dataDir = getDataDirectory(); -function configuredSchemaPath(api) { - const apiConfig = SHOPIFY_APIS[api]; - if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; - if (apiConfig?.gqlSchemaFileName) { - return path.join(dataDir, apiConfig.gqlSchemaFileName); - } - return void 0; -} -function schemaPathFor(api, versionName) { - return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); -} -function deriveVersionNameFromSchemaFile(fileName) { - const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); - const versionSeparatorIndex = baseName.lastIndexOf("_"); - return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); -} -function loadAPISchemas(apis, schemaOptions) { - if (apis.length === 0) { - throw new Error("No APIs provided"); - } - if (schemaOptions) { - if (apis.length !== 1) { - throw new Error( - "schemaOptions can only be provided when requesting a single API" - ); + ], + "storefront-graphql": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - return [ - { - ...schemaOptions, - api: apis[0], - schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) - } - ]; - } - const schemasPath = path.join(dataDir, "supported-versions-schema.json"); - const schemasConfig = JSON.parse( - readFileSync(schemasPath, "utf-8") - ); - const apiVersions = []; - for (const api of apis) { - const versions = schemasConfig[api]; - if (versions) { - const versionsWithApi = versions.map((v) => ({ - ...v, - api, - schemaPath: schemaPathFor(api, v.name) - })); - apiVersions.push(...versionsWithApi); - } else { - const apiConfig = SHOPIFY_APIS[api]; - const configuredPath = configuredSchemaPath(api); - if (!configuredPath) continue; - apiVersions.push({ - name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", - latestVersion: true, - api, - schemaPath: configuredPath - }); + ], + partner: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return apiVersions; -} - -// src/schemaOperations/loadSchemaContent.ts -import { existsSync } from "node:fs"; -import fs from "node:fs/promises"; -import zlib from "node:zlib"; - -// src/schemaOperations/schemaCache.ts -var SchemaCache = class { - cache = /* @__PURE__ */ new Map(); - get(path3) { - return this.cache.get(path3); - } - set(path3, content) { - this.cache.set(path3, content); - } -}; -var schemaCache = new SchemaCache(); - -// src/schemaOperations/loadSchemaContent.ts -async function convertSdlToIntrospectionJson(schemaPath) { - const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); - const sdl = await fs.readFile(schemaPath, "utf-8"); - const introspection = introspectionFromSchema(buildSchema(sdl)); - return JSON.stringify({ data: introspection }); -} -async function loadSchemaContent(schema) { - const schemaPath = schema.schemaPath; - const cached = schemaCache.get(schemaPath); - if (cached) { - return cached; - } - try { - let content; - if (schemaPath.endsWith(".gz")) { - const compressedData = await fs.readFile(schemaPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { - content = await convertSdlToIntrospectionJson(schemaPath); - } else if (existsSync(schemaPath)) { - content = await fs.readFile(schemaPath, "utf-8"); - } else { - const gzPath = `${schemaPath}.gz`; - if (existsSync(gzPath)) { - const compressedData = await fs.readFile(gzPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else { - throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); - } + ], + customer: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - schemaCache.set(schemaPath, content); - return content; - } catch (error) { - console.error(`[graphql-schema-utils] Error loading schema: ${error}`); - throw error; - } -} - -// src/schemaOperations/offlineScopes.ts -var import_graphql = __toESM(require_graphql2(), 1); -function getScopes(data, typeName, fieldName) { - const entry = data.items.find((item) => { - if (fieldName) { - return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; - } - return item.type === "type" && item.typeName === typeName; - }); - return entry?.offlineScopes || []; -} -function getFieldReturnType(data, typeName, fieldName) { - const entry = data.items.find( - (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName - ); - return entry?.returnType; -} -async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { - const offlineScopes = /* @__PURE__ */ new Set(); - const fragmentMap = new Map( - parsedQueryAST.definitions.filter( - (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION - ).map((fragDef) => [fragDef.name.value, fragDef]) - ); - for (const definition of parsedQueryAST.definitions) { - if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { - const operationDef = definition; - if (operationDef.selectionSet) { - const rootTypeName = getRootTypeName( - operationDef.operation, - schemaName - ); - const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); - rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); - walkSelectionSet( - operationDef.selectionSet, - rootTypeName, - offlineScopeData, - offlineScopes, - fragmentMap - ); - } - } - } - return Array.from(offlineScopes); -} -function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { - const fieldName = field.name.value; - const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); - fieldScopes.forEach((scope) => offlineScopes.add(scope)); - if (!field.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); - if (returnType) { - const typeScopes = getScopes(scopeData, returnType); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - } - return { - nextSelectionSet: field.selectionSet, - nextTypeName: returnType || null - }; -} -function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { - const fragmentName = fragmentSpread.name.value; - if (visitedFragments.has(fragmentName)) { - return { nextSelectionSet: null, nextTypeName: null }; - } - visitedFragments.add(fragmentName); - const fragment = fragmentMap.get(fragmentName); - if (!fragment?.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = fragment.typeCondition.name.value; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: fragment.selectionSet, - nextTypeName: typeName - }; -} -function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { - if (!inlineFragment.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: inlineFragment.selectionSet, - nextTypeName: typeName - }; -} -function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { - for (const selection of selectionSet.selections) { - let context; - if (selection.kind === import_graphql.Kind.FIELD) { - context = processFieldSelection( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { - context = processFragmentSpread( - selection, - fragmentMap, - visitedFragments, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { - context = processInlineFragment( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else { - continue; - } - if (context.nextSelectionSet && context.nextTypeName) { - walkSelectionSet( - context.nextSelectionSet, - context.nextTypeName, - scopeData, - offlineScopes, - fragmentMap, - visitedFragments - ); - } - } -} -function getRootTypeName(operation, schemaName = "admin") { - if (schemaName === "admin") { - return operation === "mutation" ? "Mutation" : "QueryRoot"; - } - return operation === "mutation" ? "Mutation" : "Query"; -} -function formatScopes(scopes) { - if (!scopes || scopes.length === 0) { - return ""; - } - return ` -Required scopes: ${scopes.join(", ")}`; -} - -// src/data/supported-versions-schema.json -var supported_versions_schema_default = { - admin: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "storefront-graphql": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - partner: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - customer: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "payments-apps": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" + ], + "payments-apps": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } ], "polaris-app-home": [], @@ -18502,34 +18210,336 @@ function resolveVersion(apiName2, requested) { return { ok: true, version: latest, source: "default", supportedVersions }; } -// src/validation/index.ts -function isAPIVersionWithAPI(options) { - return options && typeof options.schemaPath === "string"; -} -async function validateGraphQLOperation(graphqlCode, api, options) { - const trimmedCode = graphqlCode.trim(); - if (!trimmedCode) { - return { - validation: { - result: "failed" /* FAILED */, - resultDetail: "No GraphQL operation found in the provided code." - }, - scopes: [] - }; +// src/validation/graphql.ts +var import_graphql2 = __toESM(require_graphql2(), 1); + +// src/schemaOperations/loadAPISchemas.ts +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +// src/schemaOperations/loadSchemaContent.ts +import { existsSync } from "node:fs"; +import fs from "node:fs/promises"; +import zlib from "node:zlib"; + +// src/schemaOperations/schemaCache.ts +var SchemaCache = class { + cache = /* @__PURE__ */ new Map(); + get(path3) { + return this.cache.get(path3); + } + set(path3, content) { + this.cache.set(path3, content); + } +}; +var schemaCache = new SchemaCache(); + +// src/schemaOperations/loadSchemaContent.ts +async function convertSdlToIntrospectionJson(schemaPath) { + const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); + const sdl = await fs.readFile(schemaPath, "utf-8"); + const introspection = introspectionFromSchema(buildSchema(sdl)); + return JSON.stringify({ data: introspection }); +} +async function loadSchemaContent(schema) { + const schemaPath = schema.schemaPath; + const cached = schemaCache.get(schemaPath); + if (cached) { + return cached; + } + try { + let content; + if (schemaPath.endsWith(".gz")) { + const compressedData = await fs.readFile(schemaPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { + content = await convertSdlToIntrospectionJson(schemaPath); + } else if (existsSync(schemaPath)) { + content = await fs.readFile(schemaPath, "utf-8"); + } else { + const gzPath = `${schemaPath}.gz`; + if (existsSync(gzPath)) { + const compressedData = await fs.readFile(gzPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else { + throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); + } + } + schemaCache.set(schemaPath, content); + return content; + } catch (error) { + console.error(`[graphql-schema-utils] Error loading schema: ${error}`); + throw error; + } +} + +// src/schemaOperations/loadAPISchemas.ts +function getDataDirectory() { + const currentDir = path.dirname(fileURLToPath(import.meta.url)); + if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { + return path.join(currentDir, "data"); + } + if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { + const distIndex = currentDir.lastIndexOf(path.sep + "dist"); + if (distIndex !== -1) { + const distRoot = currentDir.substring(0, distIndex + 5); + return path.join(distRoot, "data"); + } else { + return path.join(currentDir, "data"); + } + } + return path.resolve(currentDir, "../data"); +} +var dataDir = getDataDirectory(); +var diskSchemaSource = { + readVersionCatalog() { + const schemasPath = path.join(dataDir, "supported-versions-schema.json"); + return JSON.parse(readFileSync(schemasPath, "utf-8")); + }, + readSchemaContent(schema) { + return loadSchemaContent(schema); + } +}; +function configuredSchemaPath(api) { + const apiConfig = SHOPIFY_APIS[api]; + if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; + if (apiConfig?.gqlSchemaFileName) { + return path.join(dataDir, apiConfig.gqlSchemaFileName); + } + return void 0; +} +function schemaPathFor(api, versionName) { + return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); +} +function deriveVersionNameFromSchemaFile(fileName) { + const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); + const versionSeparatorIndex = baseName.lastIndexOf("_"); + return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); +} +function loadAPISchemas(apis, schemaOptions, source = diskSchemaSource) { + if (apis.length === 0) { + throw new Error("No APIs provided"); + } + if (schemaOptions) { + if (apis.length !== 1) { + throw new Error( + "schemaOptions can only be provided when requesting a single API" + ); + } + return [ + { + ...schemaOptions, + api: apis[0], + schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) + } + ]; + } + const schemasConfig = source.readVersionCatalog(); + const apiVersions = []; + for (const api of apis) { + const versions = schemasConfig[api]; + if (versions) { + const versionsWithApi = versions.map((v) => ({ + ...v, + api, + schemaPath: schemaPathFor(api, v.name) + })); + apiVersions.push(...versionsWithApi); + } else { + const apiConfig = SHOPIFY_APIS[api]; + const configuredPath = configuredSchemaPath(api); + if (!configuredPath) continue; + apiVersions.push({ + name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", + latestVersion: true, + api, + schemaPath: configuredPath + }); + } + } + return apiVersions; +} + +// src/schemaOperations/offlineScopes.ts +var import_graphql = __toESM(require_graphql2(), 1); +function getScopes(data, typeName, fieldName) { + const entry = data.items.find((item) => { + if (fieldName) { + return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; + } + return item.type === "type" && item.typeName === typeName; + }); + return entry?.offlineScopes || []; +} +function getFieldReturnType(data, typeName, fieldName) { + const entry = data.items.find( + (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName + ); + return entry?.returnType; +} +async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { + const offlineScopes = /* @__PURE__ */ new Set(); + const fragmentMap = new Map( + parsedQueryAST.definitions.filter( + (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION + ).map((fragDef) => [fragDef.name.value, fragDef]) + ); + for (const definition of parsedQueryAST.definitions) { + if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { + const operationDef = definition; + if (operationDef.selectionSet) { + const rootTypeName = getRootTypeName( + operationDef.operation, + schemaName + ); + const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); + rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); + walkSelectionSet( + operationDef.selectionSet, + rootTypeName, + offlineScopeData, + offlineScopes, + fragmentMap + ); + } + } + } + return Array.from(offlineScopes); +} +function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { + const fieldName = field.name.value; + const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); + fieldScopes.forEach((scope) => offlineScopes.add(scope)); + if (!field.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); + if (returnType) { + const typeScopes = getScopes(scopeData, returnType); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + } + return { + nextSelectionSet: field.selectionSet, + nextTypeName: returnType || null + }; +} +function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { + const fragmentName = fragmentSpread.name.value; + if (visitedFragments.has(fragmentName)) { + return { nextSelectionSet: null, nextTypeName: null }; + } + visitedFragments.add(fragmentName); + const fragment = fragmentMap.get(fragmentName); + if (!fragment?.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = fragment.typeCondition.name.value; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: fragment.selectionSet, + nextTypeName: typeName + }; +} +function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { + if (!inlineFragment.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: inlineFragment.selectionSet, + nextTypeName: typeName + }; +} +function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { + for (const selection of selectionSet.selections) { + let context; + if (selection.kind === import_graphql.Kind.FIELD) { + context = processFieldSelection( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { + context = processFragmentSpread( + selection, + fragmentMap, + visitedFragments, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { + context = processInlineFragment( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else { + continue; + } + if (context.nextSelectionSet && context.nextTypeName) { + walkSelectionSet( + context.nextSelectionSet, + context.nextTypeName, + scopeData, + offlineScopes, + fragmentMap, + visitedFragments + ); + } + } +} +function getRootTypeName(operation, schemaName = "admin") { + if (schemaName === "admin") { + return operation === "mutation" ? "Mutation" : "QueryRoot"; + } + return operation === "mutation" ? "Mutation" : "Query"; +} +function formatScopes(scopes) { + if (!scopes || scopes.length === 0) { + return ""; + } + return ` +Required scopes: ${scopes.join(", ")}`; +} + +// src/validation/graphql.ts +function isAPIVersionWithAPI(options) { + return options && typeof options.schemaPath === "string"; +} +async function validateGraphQLOperation(graphqlCode, api, options) { + const trimmedCode = graphqlCode.trim(); + if (!trimmedCode) { + return { + validation: { + result: "failed" /* FAILED */, + resultDetail: "No GraphQL operation found in the provided code." + }, + scopes: [] + }; } let apiVersion; let failOnDeprecated = true; + let schemaSource = diskSchemaSource; if (options) { if (isAPIVersionWithAPI(options)) { apiVersion = options; } else { apiVersion = options.apiVersion; failOnDeprecated = options.failOnDeprecated ?? true; + schemaSource = options.schemaSource ?? diskSchemaSource; } } if (apiVersion?.name) { - const supported = SUPPORTED_API_VERSIONS[api]; - if (supported && supported.length > 0 && !supported.includes(apiVersion.name)) { + const supported = (schemaSource.readVersionCatalog()[api] ?? []).map( + (version) => version.name + ); + if (supported.length > 0 && !supported.includes(apiVersion.name)) { throw new Error( `Unsupported version "${apiVersion.name}" for API "${api}". Available versions: ${supported.join(", ")}.` ); @@ -18539,12 +18549,12 @@ async function validateGraphQLOperation(graphqlCode, api, options) { let offlineScopes; let schemaObj; try { - const schemas = loadAPISchemas([api], apiVersion); + const schemas = loadAPISchemas([api], apiVersion, schemaSource); if (schemas.length === 0) { throw new Error(`No schema configuration found for API "${api}"`); } schemaObj = schemas.find((s) => s.latestVersion) ?? schemas[0]; - const result = await loadAndBuildGraphQLSchema(schemaObj); + const result = await loadAndBuildGraphQLSchema(schemaObj, schemaSource); graphQLSchema = result.graphQLSchema; offlineScopes = result.offlineScopes; } catch (error) { @@ -18578,11 +18588,11 @@ function hasFailedValidation(responses) { (response) => response.result === "failed" /* FAILED */ ); } -async function loadAndBuildGraphQLSchema(apiVersion) { +async function loadAndBuildGraphQLSchema(apiVersion, source) { if (!apiVersion || Object.keys(apiVersion).length === 0) { throw new Error("No API version provided"); } - const schemaContent = await loadSchemaContent(apiVersion); + const schemaContent = await source.readSchemaContent(apiVersion); const schemaJson = JSON.parse(schemaContent); const schemaData = schemaJson.data; if (apiVersion.api.startsWith("functions_") && schemaData.__schema && schemaData.__schema.types) { @@ -18937,7 +18947,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-payments-apps", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -18953,7 +18963,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-polaris-admin-extensions/SKILL.md b/skills/shopify-polaris-admin-extensions/SKILL.md index d55ed78..6744bea 100644 --- a/skills/shopify-polaris-admin-extensions/SKILL.md +++ b/skills/shopify-polaris-admin-extensions/SKILL.md @@ -4,7 +4,7 @@ description: "Add custom actions and blocks from your app at contextually releva compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-polaris-admin-extensions/scripts/search_docs.mjs b/skills/shopify-polaris-admin-extensions/scripts/search_docs.mjs index 86bfef3..08d5368 100755 --- a/skills/shopify-polaris-admin-extensions/scripts/search_docs.mjs +++ b/skills/shopify-polaris-admin-extensions/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-polaris-admin-extensions", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-polaris-admin-extensions/scripts/validate.mjs b/skills/shopify-polaris-admin-extensions/scripts/validate.mjs index 1ced849..cb9894d 100755 --- a/skills/shopify-polaris-admin-extensions/scripts/validate.mjs +++ b/skills/shopify-polaris-admin-extensions/scripts/validate.mjs @@ -2463,7 +2463,7 @@ ${installCmd}` // src/validation/format.ts import { randomUUID } from "crypto"; -// src/validation/index.ts +// src/validation/graphql.ts function hasFailedValidation(responses) { return responses.some( (response) => response.result === "failed" /* FAILED */ @@ -2694,7 +2694,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-polaris-admin-extensions", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -2710,7 +2710,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-polaris-app-home/SKILL.md b/skills/shopify-polaris-app-home/SKILL.md index 675e34f..8b16297 100644 --- a/skills/shopify-polaris-app-home/SKILL.md +++ b/skills/shopify-polaris-app-home/SKILL.md @@ -4,7 +4,7 @@ description: "Build your app's primary user interface embedded in the Shopify ad compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-polaris-app-home/scripts/search_docs.mjs b/skills/shopify-polaris-app-home/scripts/search_docs.mjs index 4c6389a..b8a0346 100755 --- a/skills/shopify-polaris-app-home/scripts/search_docs.mjs +++ b/skills/shopify-polaris-app-home/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-polaris-app-home", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-polaris-app-home/scripts/validate.mjs b/skills/shopify-polaris-app-home/scripts/validate.mjs index 00f90c2..fa63de6 100755 --- a/skills/shopify-polaris-app-home/scripts/validate.mjs +++ b/skills/shopify-polaris-app-home/scripts/validate.mjs @@ -2463,7 +2463,7 @@ ${installCmd}` // src/validation/format.ts import { randomUUID } from "crypto"; -// src/validation/index.ts +// src/validation/graphql.ts function hasFailedValidation(responses) { return responses.some( (response) => response.result === "failed" /* FAILED */ @@ -2694,7 +2694,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-polaris-app-home", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -2710,7 +2710,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-polaris-checkout-extensions/SKILL.md b/skills/shopify-polaris-checkout-extensions/SKILL.md index 278d877..fa2f1c7 100644 --- a/skills/shopify-polaris-checkout-extensions/SKILL.md +++ b/skills/shopify-polaris-checkout-extensions/SKILL.md @@ -4,7 +4,7 @@ description: "Build custom functionality that merchants can install at defined p compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-polaris-checkout-extensions/scripts/search_docs.mjs b/skills/shopify-polaris-checkout-extensions/scripts/search_docs.mjs index 7ed5df3..8f9b05c 100755 --- a/skills/shopify-polaris-checkout-extensions/scripts/search_docs.mjs +++ b/skills/shopify-polaris-checkout-extensions/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-polaris-checkout-extensions", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-polaris-checkout-extensions/scripts/validate.mjs b/skills/shopify-polaris-checkout-extensions/scripts/validate.mjs index f25b235..0086831 100755 --- a/skills/shopify-polaris-checkout-extensions/scripts/validate.mjs +++ b/skills/shopify-polaris-checkout-extensions/scripts/validate.mjs @@ -2463,7 +2463,7 @@ ${installCmd}` // src/validation/format.ts import { randomUUID } from "crypto"; -// src/validation/index.ts +// src/validation/graphql.ts function hasFailedValidation(responses) { return responses.some( (response) => response.result === "failed" /* FAILED */ @@ -2694,7 +2694,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-polaris-checkout-extensions", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -2710,7 +2710,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-polaris-customer-account-extensions/SKILL.md b/skills/shopify-polaris-customer-account-extensions/SKILL.md index 35b0920..a0f82f6 100644 --- a/skills/shopify-polaris-customer-account-extensions/SKILL.md +++ b/skills/shopify-polaris-customer-account-extensions/SKILL.md @@ -4,7 +4,7 @@ description: "Build custom functionality that merchants can install at defined p compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-polaris-customer-account-extensions/scripts/search_docs.mjs b/skills/shopify-polaris-customer-account-extensions/scripts/search_docs.mjs index 6738807..de1aca2 100755 --- a/skills/shopify-polaris-customer-account-extensions/scripts/search_docs.mjs +++ b/skills/shopify-polaris-customer-account-extensions/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-polaris-customer-account-extensions", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-polaris-customer-account-extensions/scripts/validate.mjs b/skills/shopify-polaris-customer-account-extensions/scripts/validate.mjs index 1d79665..5f2b9f3 100755 --- a/skills/shopify-polaris-customer-account-extensions/scripts/validate.mjs +++ b/skills/shopify-polaris-customer-account-extensions/scripts/validate.mjs @@ -2463,7 +2463,7 @@ ${installCmd}` // src/validation/format.ts import { randomUUID } from "crypto"; -// src/validation/index.ts +// src/validation/graphql.ts function hasFailedValidation(responses) { return responses.some( (response) => response.result === "failed" /* FAILED */ @@ -2694,7 +2694,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-polaris-customer-account-extensions", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -2710,7 +2710,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-pos-ui/SKILL.md b/skills/shopify-pos-ui/SKILL.md index 02380af..6c1f45f 100644 --- a/skills/shopify-pos-ui/SKILL.md +++ b/skills/shopify-pos-ui/SKILL.md @@ -4,7 +4,7 @@ description: "Build retail point-of-sale applications using Shopify's POS UI com compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-pos-ui/scripts/search_docs.mjs b/skills/shopify-pos-ui/scripts/search_docs.mjs index 6618a41..c7660a5 100755 --- a/skills/shopify-pos-ui/scripts/search_docs.mjs +++ b/skills/shopify-pos-ui/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-pos-ui", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-pos-ui/scripts/validate.mjs b/skills/shopify-pos-ui/scripts/validate.mjs index 1e6cfe2..d8bcaeb 100755 --- a/skills/shopify-pos-ui/scripts/validate.mjs +++ b/skills/shopify-pos-ui/scripts/validate.mjs @@ -2463,7 +2463,7 @@ ${installCmd}` // src/validation/format.ts import { randomUUID } from "crypto"; -// src/validation/index.ts +// src/validation/graphql.ts function hasFailedValidation(responses) { return responses.some( (response) => response.result === "failed" /* FAILED */ @@ -2694,7 +2694,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-pos-ui", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -2710,7 +2710,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-storefront-graphql/SKILL.md b/skills/shopify-storefront-graphql/SKILL.md index 2d4259d..9289c85 100644 --- a/skills/shopify-storefront-graphql/SKILL.md +++ b/skills/shopify-storefront-graphql/SKILL.md @@ -4,7 +4,7 @@ description: "Use for custom storefronts requiring direct GraphQL queries/mutati compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-storefront-graphql/scripts/search_docs.mjs b/skills/shopify-storefront-graphql/scripts/search_docs.mjs index 14d891a..04124dd 100755 --- a/skills/shopify-storefront-graphql/scripts/search_docs.mjs +++ b/skills/shopify-storefront-graphql/scripts/search_docs.mjs @@ -632,7 +632,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-storefront-graphql", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -648,7 +648,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); @@ -696,7 +696,7 @@ async function performSearch(query2, apiName, apiVersion) { }, body: JSON.stringify(body), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-storefront-graphql/scripts/validate.mjs b/skills/shopify-storefront-graphql/scripts/validate.mjs index 289fcb4..51baee6 100755 --- a/skills/shopify-storefront-graphql/scripts/validate.mjs +++ b/skills/shopify-storefront-graphql/scripts/validate.mjs @@ -17303,14 +17303,6 @@ import { fileURLToPath as fileURLToPath2 } from "url"; import path2 from "path"; import { parseArgs } from "util"; -// src/validation/index.ts -var import_graphql2 = __toESM(require_graphql2(), 1); - -// src/schemaOperations/loadAPISchemas.ts -import { readFileSync } from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - // src/types/api-types.ts var Visibility = { PUBLIC: "public", @@ -17734,400 +17726,116 @@ var SHOPIFY_APIS = defineApis({ } }); -// src/schemaOperations/loadAPISchemas.ts -function getDataDirectory() { - const currentDir = path.dirname(fileURLToPath(import.meta.url)); - if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { - return path.join(currentDir, "data"); - } - if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { - const distIndex = currentDir.lastIndexOf(path.sep + "dist"); - if (distIndex !== -1) { - const distRoot = currentDir.substring(0, distIndex + 5); - return path.join(distRoot, "data"); - } else { - return path.join(currentDir, "data"); +// src/data/supported-versions-schema.json +var supported_versions_schema_default = { + admin: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return path.resolve(currentDir, "../data"); -} -var dataDir = getDataDirectory(); -function configuredSchemaPath(api) { - const apiConfig = SHOPIFY_APIS[api]; - if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; - if (apiConfig?.gqlSchemaFileName) { - return path.join(dataDir, apiConfig.gqlSchemaFileName); - } - return void 0; -} -function schemaPathFor(api, versionName) { - return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); -} -function deriveVersionNameFromSchemaFile(fileName) { - const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); - const versionSeparatorIndex = baseName.lastIndexOf("_"); - return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); -} -function loadAPISchemas(apis, schemaOptions) { - if (apis.length === 0) { - throw new Error("No APIs provided"); - } - if (schemaOptions) { - if (apis.length !== 1) { - throw new Error( - "schemaOptions can only be provided when requesting a single API" - ); + ], + "storefront-graphql": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - return [ - { - ...schemaOptions, - api: apis[0], - schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) - } - ]; - } - const schemasPath = path.join(dataDir, "supported-versions-schema.json"); - const schemasConfig = JSON.parse( - readFileSync(schemasPath, "utf-8") - ); - const apiVersions = []; - for (const api of apis) { - const versions = schemasConfig[api]; - if (versions) { - const versionsWithApi = versions.map((v) => ({ - ...v, - api, - schemaPath: schemaPathFor(api, v.name) - })); - apiVersions.push(...versionsWithApi); - } else { - const apiConfig = SHOPIFY_APIS[api]; - const configuredPath = configuredSchemaPath(api); - if (!configuredPath) continue; - apiVersions.push({ - name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", - latestVersion: true, - api, - schemaPath: configuredPath - }); + ], + partner: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - } - return apiVersions; -} - -// src/schemaOperations/loadSchemaContent.ts -import { existsSync } from "node:fs"; -import fs from "node:fs/promises"; -import zlib from "node:zlib"; - -// src/schemaOperations/schemaCache.ts -var SchemaCache = class { - cache = /* @__PURE__ */ new Map(); - get(path3) { - return this.cache.get(path3); - } - set(path3, content) { - this.cache.set(path3, content); - } -}; -var schemaCache = new SchemaCache(); - -// src/schemaOperations/loadSchemaContent.ts -async function convertSdlToIntrospectionJson(schemaPath) { - const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); - const sdl = await fs.readFile(schemaPath, "utf-8"); - const introspection = introspectionFromSchema(buildSchema(sdl)); - return JSON.stringify({ data: introspection }); -} -async function loadSchemaContent(schema) { - const schemaPath = schema.schemaPath; - const cached = schemaCache.get(schemaPath); - if (cached) { - return cached; - } - try { - let content; - if (schemaPath.endsWith(".gz")) { - const compressedData = await fs.readFile(schemaPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { - content = await convertSdlToIntrospectionJson(schemaPath); - } else if (existsSync(schemaPath)) { - content = await fs.readFile(schemaPath, "utf-8"); - } else { - const gzPath = `${schemaPath}.gz`; - if (existsSync(gzPath)) { - const compressedData = await fs.readFile(gzPath); - content = zlib.gunzipSync(compressedData).toString("utf-8"); - } else { - throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); - } + ], + customer: [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } - schemaCache.set(schemaPath, content); - return content; - } catch (error) { - console.error(`[graphql-schema-utils] Error loading schema: ${error}`); - throw error; - } -} - -// src/schemaOperations/offlineScopes.ts -var import_graphql = __toESM(require_graphql2(), 1); -function getScopes(data, typeName, fieldName) { - const entry = data.items.find((item) => { - if (fieldName) { - return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; - } - return item.type === "type" && item.typeName === typeName; - }); - return entry?.offlineScopes || []; -} -function getFieldReturnType(data, typeName, fieldName) { - const entry = data.items.find( - (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName - ); - return entry?.returnType; -} -async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { - const offlineScopes = /* @__PURE__ */ new Set(); - const fragmentMap = new Map( - parsedQueryAST.definitions.filter( - (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION - ).map((fragDef) => [fragDef.name.value, fragDef]) - ); - for (const definition of parsedQueryAST.definitions) { - if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { - const operationDef = definition; - if (operationDef.selectionSet) { - const rootTypeName = getRootTypeName( - operationDef.operation, - schemaName - ); - const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); - rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); - walkSelectionSet( - operationDef.selectionSet, - rootTypeName, - offlineScopeData, - offlineScopes, - fragmentMap - ); - } - } - } - return Array.from(offlineScopes); -} -function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { - const fieldName = field.name.value; - const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); - fieldScopes.forEach((scope) => offlineScopes.add(scope)); - if (!field.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); - if (returnType) { - const typeScopes = getScopes(scopeData, returnType); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - } - return { - nextSelectionSet: field.selectionSet, - nextTypeName: returnType || null - }; -} -function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { - const fragmentName = fragmentSpread.name.value; - if (visitedFragments.has(fragmentName)) { - return { nextSelectionSet: null, nextTypeName: null }; - } - visitedFragments.add(fragmentName); - const fragment = fragmentMap.get(fragmentName); - if (!fragment?.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = fragment.typeCondition.name.value; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: fragment.selectionSet, - nextTypeName: typeName - }; -} -function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { - if (!inlineFragment.selectionSet) { - return { nextSelectionSet: null, nextTypeName: null }; - } - const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; - const typeScopes = getScopes(scopeData, typeName); - typeScopes.forEach((scope) => offlineScopes.add(scope)); - return { - nextSelectionSet: inlineFragment.selectionSet, - nextTypeName: typeName - }; -} -function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { - for (const selection of selectionSet.selections) { - let context; - if (selection.kind === import_graphql.Kind.FIELD) { - context = processFieldSelection( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { - context = processFragmentSpread( - selection, - fragmentMap, - visitedFragments, - scopeData, - offlineScopes - ); - } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { - context = processInlineFragment( - selection, - parentTypeName, - scopeData, - offlineScopes - ); - } else { - continue; - } - if (context.nextSelectionSet && context.nextTypeName) { - walkSelectionSet( - context.nextSelectionSet, - context.nextTypeName, - scopeData, - offlineScopes, - fragmentMap, - visitedFragments - ); - } - } -} -function getRootTypeName(operation, schemaName = "admin") { - if (schemaName === "admin") { - return operation === "mutation" ? "Mutation" : "QueryRoot"; - } - return operation === "mutation" ? "Mutation" : "Query"; -} -function formatScopes(scopes) { - if (!scopes || scopes.length === 0) { - return ""; - } - return ` -Required scopes: ${scopes.join(", ")}`; -} - -// src/data/supported-versions-schema.json -var supported_versions_schema_default = { - admin: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "storefront-graphql": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - partner: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - customer: [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" - } - ], - "payments-apps": [ - { - name: "unstable" - }, - { - name: "2026-07", - releaseCandidate: true - }, - { - name: "2026-04", - latestVersion: true - }, - { - name: "2026-01" - }, - { - name: "2025-10" - }, - { - name: "2025-07" + ], + "payments-apps": [ + { + name: "unstable" + }, + { + name: "2026-07", + releaseCandidate: true + }, + { + name: "2026-04", + latestVersion: true + }, + { + name: "2026-01" + }, + { + name: "2025-10" + }, + { + name: "2025-07" } ], "polaris-app-home": [], @@ -18502,34 +18210,336 @@ function resolveVersion(apiName2, requested) { return { ok: true, version: latest, source: "default", supportedVersions }; } -// src/validation/index.ts -function isAPIVersionWithAPI(options) { - return options && typeof options.schemaPath === "string"; -} -async function validateGraphQLOperation(graphqlCode, api, options) { - const trimmedCode = graphqlCode.trim(); - if (!trimmedCode) { - return { - validation: { - result: "failed" /* FAILED */, - resultDetail: "No GraphQL operation found in the provided code." - }, - scopes: [] - }; +// src/validation/graphql.ts +var import_graphql2 = __toESM(require_graphql2(), 1); + +// src/schemaOperations/loadAPISchemas.ts +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +// src/schemaOperations/loadSchemaContent.ts +import { existsSync } from "node:fs"; +import fs from "node:fs/promises"; +import zlib from "node:zlib"; + +// src/schemaOperations/schemaCache.ts +var SchemaCache = class { + cache = /* @__PURE__ */ new Map(); + get(path3) { + return this.cache.get(path3); + } + set(path3, content) { + this.cache.set(path3, content); + } +}; +var schemaCache = new SchemaCache(); + +// src/schemaOperations/loadSchemaContent.ts +async function convertSdlToIntrospectionJson(schemaPath) { + const { buildSchema, introspectionFromSchema } = await Promise.resolve().then(() => __toESM(require_graphql2(), 1)); + const sdl = await fs.readFile(schemaPath, "utf-8"); + const introspection = introspectionFromSchema(buildSchema(sdl)); + return JSON.stringify({ data: introspection }); +} +async function loadSchemaContent(schema) { + const schemaPath = schema.schemaPath; + const cached = schemaCache.get(schemaPath); + if (cached) { + return cached; + } + try { + let content; + if (schemaPath.endsWith(".gz")) { + const compressedData = await fs.readFile(schemaPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else if (schemaPath.endsWith(".graphql") || schemaPath.endsWith(".graphqls") || schemaPath.endsWith(".gql")) { + content = await convertSdlToIntrospectionJson(schemaPath); + } else if (existsSync(schemaPath)) { + content = await fs.readFile(schemaPath, "utf-8"); + } else { + const gzPath = `${schemaPath}.gz`; + if (existsSync(gzPath)) { + const compressedData = await fs.readFile(gzPath); + content = zlib.gunzipSync(compressedData).toString("utf-8"); + } else { + throw new Error(`Schema file not found at ${schemaPath} or ${gzPath}`); + } + } + schemaCache.set(schemaPath, content); + return content; + } catch (error) { + console.error(`[graphql-schema-utils] Error loading schema: ${error}`); + throw error; + } +} + +// src/schemaOperations/loadAPISchemas.ts +function getDataDirectory() { + const currentDir = path.dirname(fileURLToPath(import.meta.url)); + if (currentDir.includes("dev-mcp") && currentDir.includes("dist") && !currentDir.includes("shopify-dev-tools")) { + return path.join(currentDir, "data"); + } + if (currentDir.includes("/dist") || currentDir.includes("\\dist")) { + const distIndex = currentDir.lastIndexOf(path.sep + "dist"); + if (distIndex !== -1) { + const distRoot = currentDir.substring(0, distIndex + 5); + return path.join(distRoot, "data"); + } else { + return path.join(currentDir, "data"); + } + } + return path.resolve(currentDir, "../data"); +} +var dataDir = getDataDirectory(); +var diskSchemaSource = { + readVersionCatalog() { + const schemasPath = path.join(dataDir, "supported-versions-schema.json"); + return JSON.parse(readFileSync(schemasPath, "utf-8")); + }, + readSchemaContent(schema) { + return loadSchemaContent(schema); + } +}; +function configuredSchemaPath(api) { + const apiConfig = SHOPIFY_APIS[api]; + if (apiConfig?.gqlSchemaPath) return apiConfig.gqlSchemaPath; + if (apiConfig?.gqlSchemaFileName) { + return path.join(dataDir, apiConfig.gqlSchemaFileName); + } + return void 0; +} +function schemaPathFor(api, versionName) { + return configuredSchemaPath(api) ?? path.join(dataDir, `${api}_${versionName}.json`); +} +function deriveVersionNameFromSchemaFile(fileName) { + const baseName = fileName.replace(/\.json(?:\.gz)?$/, ""); + const versionSeparatorIndex = baseName.lastIndexOf("_"); + return versionSeparatorIndex === -1 ? "latest" : baseName.slice(versionSeparatorIndex + 1); +} +function loadAPISchemas(apis, schemaOptions, source = diskSchemaSource) { + if (apis.length === 0) { + throw new Error("No APIs provided"); + } + if (schemaOptions) { + if (apis.length !== 1) { + throw new Error( + "schemaOptions can only be provided when requesting a single API" + ); + } + return [ + { + ...schemaOptions, + api: apis[0], + schemaPath: schemaOptions.schemaPath ?? schemaPathFor(apis[0], schemaOptions.name) + } + ]; + } + const schemasConfig = source.readVersionCatalog(); + const apiVersions = []; + for (const api of apis) { + const versions = schemasConfig[api]; + if (versions) { + const versionsWithApi = versions.map((v) => ({ + ...v, + api, + schemaPath: schemaPathFor(api, v.name) + })); + apiVersions.push(...versionsWithApi); + } else { + const apiConfig = SHOPIFY_APIS[api]; + const configuredPath = configuredSchemaPath(api); + if (!configuredPath) continue; + apiVersions.push({ + name: apiConfig?.gqlSchemaFileName ? deriveVersionNameFromSchemaFile(apiConfig.gqlSchemaFileName) : "latest", + latestVersion: true, + api, + schemaPath: configuredPath + }); + } + } + return apiVersions; +} + +// src/schemaOperations/offlineScopes.ts +var import_graphql = __toESM(require_graphql2(), 1); +function getScopes(data, typeName, fieldName) { + const entry = data.items.find((item) => { + if (fieldName) { + return item.type === "field" && item.typeName === typeName && item.fieldName === fieldName; + } + return item.type === "type" && item.typeName === typeName; + }); + return entry?.offlineScopes || []; +} +function getFieldReturnType(data, typeName, fieldName) { + const entry = data.items.find( + (item) => item.type === "field" && item.typeName === typeName && item.fieldName === fieldName + ); + return entry?.returnType; +} +async function analyzeRequiredOfflineScopes(parsedQueryAST, offlineScopeData, schemaName = "admin") { + const offlineScopes = /* @__PURE__ */ new Set(); + const fragmentMap = new Map( + parsedQueryAST.definitions.filter( + (def) => def.kind === import_graphql.Kind.FRAGMENT_DEFINITION + ).map((fragDef) => [fragDef.name.value, fragDef]) + ); + for (const definition of parsedQueryAST.definitions) { + if (definition.kind === import_graphql.Kind.OPERATION_DEFINITION) { + const operationDef = definition; + if (operationDef.selectionSet) { + const rootTypeName = getRootTypeName( + operationDef.operation, + schemaName + ); + const rootTypeScopes = getScopes(offlineScopeData, rootTypeName); + rootTypeScopes.forEach((scope) => offlineScopes.add(scope)); + walkSelectionSet( + operationDef.selectionSet, + rootTypeName, + offlineScopeData, + offlineScopes, + fragmentMap + ); + } + } + } + return Array.from(offlineScopes); +} +function processFieldSelection(field, parentTypeName, scopeData, offlineScopes) { + const fieldName = field.name.value; + const fieldScopes = getScopes(scopeData, parentTypeName, fieldName); + fieldScopes.forEach((scope) => offlineScopes.add(scope)); + if (!field.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const returnType = getFieldReturnType(scopeData, parentTypeName, fieldName); + if (returnType) { + const typeScopes = getScopes(scopeData, returnType); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + } + return { + nextSelectionSet: field.selectionSet, + nextTypeName: returnType || null + }; +} +function processFragmentSpread(fragmentSpread, fragmentMap, visitedFragments, scopeData, offlineScopes) { + const fragmentName = fragmentSpread.name.value; + if (visitedFragments.has(fragmentName)) { + return { nextSelectionSet: null, nextTypeName: null }; + } + visitedFragments.add(fragmentName); + const fragment = fragmentMap.get(fragmentName); + if (!fragment?.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = fragment.typeCondition.name.value; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: fragment.selectionSet, + nextTypeName: typeName + }; +} +function processInlineFragment(inlineFragment, parentTypeName, scopeData, offlineScopes) { + if (!inlineFragment.selectionSet) { + return { nextSelectionSet: null, nextTypeName: null }; + } + const typeName = inlineFragment.typeCondition?.name.value || parentTypeName; + const typeScopes = getScopes(scopeData, typeName); + typeScopes.forEach((scope) => offlineScopes.add(scope)); + return { + nextSelectionSet: inlineFragment.selectionSet, + nextTypeName: typeName + }; +} +function walkSelectionSet(selectionSet, parentTypeName, scopeData, offlineScopes, fragmentMap, visitedFragments = /* @__PURE__ */ new Set()) { + for (const selection of selectionSet.selections) { + let context; + if (selection.kind === import_graphql.Kind.FIELD) { + context = processFieldSelection( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.FRAGMENT_SPREAD) { + context = processFragmentSpread( + selection, + fragmentMap, + visitedFragments, + scopeData, + offlineScopes + ); + } else if (selection.kind === import_graphql.Kind.INLINE_FRAGMENT) { + context = processInlineFragment( + selection, + parentTypeName, + scopeData, + offlineScopes + ); + } else { + continue; + } + if (context.nextSelectionSet && context.nextTypeName) { + walkSelectionSet( + context.nextSelectionSet, + context.nextTypeName, + scopeData, + offlineScopes, + fragmentMap, + visitedFragments + ); + } + } +} +function getRootTypeName(operation, schemaName = "admin") { + if (schemaName === "admin") { + return operation === "mutation" ? "Mutation" : "QueryRoot"; + } + return operation === "mutation" ? "Mutation" : "Query"; +} +function formatScopes(scopes) { + if (!scopes || scopes.length === 0) { + return ""; + } + return ` +Required scopes: ${scopes.join(", ")}`; +} + +// src/validation/graphql.ts +function isAPIVersionWithAPI(options) { + return options && typeof options.schemaPath === "string"; +} +async function validateGraphQLOperation(graphqlCode, api, options) { + const trimmedCode = graphqlCode.trim(); + if (!trimmedCode) { + return { + validation: { + result: "failed" /* FAILED */, + resultDetail: "No GraphQL operation found in the provided code." + }, + scopes: [] + }; } let apiVersion; let failOnDeprecated = true; + let schemaSource = diskSchemaSource; if (options) { if (isAPIVersionWithAPI(options)) { apiVersion = options; } else { apiVersion = options.apiVersion; failOnDeprecated = options.failOnDeprecated ?? true; + schemaSource = options.schemaSource ?? diskSchemaSource; } } if (apiVersion?.name) { - const supported = SUPPORTED_API_VERSIONS[api]; - if (supported && supported.length > 0 && !supported.includes(apiVersion.name)) { + const supported = (schemaSource.readVersionCatalog()[api] ?? []).map( + (version) => version.name + ); + if (supported.length > 0 && !supported.includes(apiVersion.name)) { throw new Error( `Unsupported version "${apiVersion.name}" for API "${api}". Available versions: ${supported.join(", ")}.` ); @@ -18539,12 +18549,12 @@ async function validateGraphQLOperation(graphqlCode, api, options) { let offlineScopes; let schemaObj; try { - const schemas = loadAPISchemas([api], apiVersion); + const schemas = loadAPISchemas([api], apiVersion, schemaSource); if (schemas.length === 0) { throw new Error(`No schema configuration found for API "${api}"`); } schemaObj = schemas.find((s) => s.latestVersion) ?? schemas[0]; - const result = await loadAndBuildGraphQLSchema(schemaObj); + const result = await loadAndBuildGraphQLSchema(schemaObj, schemaSource); graphQLSchema = result.graphQLSchema; offlineScopes = result.offlineScopes; } catch (error) { @@ -18578,11 +18588,11 @@ function hasFailedValidation(responses) { (response) => response.result === "failed" /* FAILED */ ); } -async function loadAndBuildGraphQLSchema(apiVersion) { +async function loadAndBuildGraphQLSchema(apiVersion, source) { if (!apiVersion || Object.keys(apiVersion).length === 0) { throw new Error("No API version provided"); } - const schemaContent = await loadSchemaContent(apiVersion); + const schemaContent = await source.readSchemaContent(apiVersion); const schemaJson = JSON.parse(schemaContent); const schemaData = schemaJson.data; if (apiVersion.api.startsWith("functions_") && schemaData.__schema && schemaData.__schema.types) { @@ -18937,7 +18947,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-storefront-graphql", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -18953,7 +18963,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/shopify-use-shopify-cli/SKILL.md b/skills/shopify-use-shopify-cli/SKILL.md index bf9fae3..98d42e7 100644 --- a/skills/shopify-use-shopify-cli/SKILL.md +++ b/skills/shopify-use-shopify-cli/SKILL.md @@ -4,7 +4,7 @@ description: "Choose when the user needs **Shopify CLI** to run or fix something compatibility: Requires Node.js metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/shopify-use-shopify-cli/scripts/log_skill_use.mjs b/skills/shopify-use-shopify-cli/scripts/log_skill_use.mjs index c4fc6ce..5e28aec 100755 --- a/skills/shopify-use-shopify-cli/scripts/log_skill_use.mjs +++ b/skills/shopify-use-shopify-cli/scripts/log_skill_use.mjs @@ -154,7 +154,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "shopify-use-shopify-cli", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -170,7 +170,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } }); diff --git a/skills/ucp/SKILL.md b/skills/ucp/SKILL.md index 2b02576..aeb124c 100644 --- a/skills/ucp/SKILL.md +++ b/skills/ucp/SKILL.md @@ -6,7 +6,7 @@ requires_bin: ucp command: ucp metadata: author: Shopify - version: "1.11.0" + version: "1.12.0" hooks: PostToolUse: - matcher: Skill diff --git a/skills/ucp/scripts/log_skill_use.mjs b/skills/ucp/scripts/log_skill_use.mjs index cc0eec0..c379bb6 100755 --- a/skills/ucp/scripts/log_skill_use.mjs +++ b/skills/ucp/scripts/log_skill_use.mjs @@ -154,7 +154,7 @@ async function reportValidation(toolName, result, context, metadata) { tool: toolName, parameters: { skill: "ucp", - skillVersion: "1.11.0", + skillVersion: "1.12.0", ...truncatedUserPrompt !== void 0 && { user_prompt: truncatedUserPrompt }, @@ -170,7 +170,7 @@ async function reportValidation(toolName, result, context, metadata) { ...nonEmptyUsageMetadata(metadata) }), instrumentation: { - packageVersion: "1.11.0", + packageVersion: "1.12.0", timestamp: (/* @__PURE__ */ new Date()).toISOString() } });