@wix/mcp 1.0.81 → 1.0.83
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/build/bin-standalone.js +847 -4
- package/build/cjs/index.cjs +900 -20
- package/build/cjs/toolkit.cjs +844 -4
- package/build/dts/bin.d.ts +1 -1
- package/build/dts/bin.js +13 -1
- package/build/dts/config/default-config.d.ts +1 -0
- package/build/dts/config/default-config.js +33 -2
- package/build/dts/config/param-descriptions.d.ts +21 -0
- package/build/dts/config/param-descriptions.js +21 -0
- package/build/dts/graphql/constants.d.ts +8 -0
- package/build/dts/graphql/constants.js +11 -0
- package/build/dts/graphql/index.d.ts +30 -0
- package/build/dts/graphql/index.js +259 -0
- package/build/dts/graphql/introspection.d.ts +106 -0
- package/build/dts/graphql/introspection.js +282 -0
- package/build/dts/graphql/search.d.ts +25 -0
- package/build/dts/graphql/search.js +277 -0
- package/build/dts/import-claude-design-tool/index.js +2 -2
- package/build/dts/index.d.ts +2 -1
- package/build/dts/index.js +3 -1
- package/build/dts/site-widget-tools/constants.d.ts +12 -5
- package/build/dts/site-widget-tools/constants.js +20 -11
- package/build/dts/site-widget-tools/site-builder-tool/index.js +1 -1
- package/build/dts/site-widget-tools/site-creation-tool/index.js +3 -2
- package/build/dts/site-widget-tools/suggested-domain-tool/index.js +25 -5
- package/build/dts/toolkit-entry.d.cts +12 -0
- package/build/dts/toolkit-entry.d.ts +12 -0
- package/build/dts/toolkit-entry.js +4 -0
- package/build/dts/toolkit.js +14 -0
- package/build/dts/wix-mcp-server.d.ts +2 -0
- package/build/dts/wix-mcp-server.js +5 -0
- package/build/esm/index.js +892 -20
- package/build/esm/toolkit.js +839 -4
- package/package.json +2 -2
package/build/dts/bin.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import './infra/sentry.js';
|
|
2
2
|
declare const PUBLIC_TOOLS: readonly ["WDS", "REST", "SDK", "BUILD_APPS", "WIX_HEADLESS", "VELO", "BUSINESS_SOLUTIONS", "CLI", "OVERVIEW"];
|
|
3
|
-
declare const EXPERIMENTAL_TOOLS: readonly ["WIX_API", "CLI_COMMAND", "GET_TO_KNOW_WIX", "CREATE_WIX_BUSINESS_GUIDE", "VELO_README", "WIX_API_THROUGH_FS"];
|
|
3
|
+
declare const EXPERIMENTAL_TOOLS: readonly ["WIX_API", "CLI_COMMAND", "GET_TO_KNOW_WIX", "CREATE_WIX_BUSINESS_GUIDE", "VELO_README", "WIX_API_THROUGH_FS", "GRAPHQL"];
|
|
4
4
|
type Tool = (typeof PUBLIC_TOOLS)[number] | (typeof EXPERIMENTAL_TOOLS)[number];
|
|
5
5
|
export declare const DEFAULT_TOOLS: Tool[];
|
|
6
6
|
export {};
|
package/build/dts/bin.js
CHANGED
|
@@ -15,6 +15,7 @@ import { addVeloReadmeTools } from './docs/velo-readme.js';
|
|
|
15
15
|
import { getCliAuthToken } from './cli-tools/utils.js';
|
|
16
16
|
import { TokenStore } from './cli-tools/account-token-store.js';
|
|
17
17
|
import { createAccountFileAuthStrategy } from './cli-tools/account-auth-strategy.js';
|
|
18
|
+
import { addGraphqlTools } from './graphql/index.js';
|
|
18
19
|
const PUBLIC_TOOLS = [...VALID_DOCS_TOOLS];
|
|
19
20
|
const EXPERIMENTAL_TOOLS = [
|
|
20
21
|
'WIX_API',
|
|
@@ -22,7 +23,8 @@ const EXPERIMENTAL_TOOLS = [
|
|
|
22
23
|
'GET_TO_KNOW_WIX',
|
|
23
24
|
'CREATE_WIX_BUSINESS_GUIDE',
|
|
24
25
|
'VELO_README',
|
|
25
|
-
'WIX_API_THROUGH_FS'
|
|
26
|
+
'WIX_API_THROUGH_FS',
|
|
27
|
+
'GRAPHQL'
|
|
26
28
|
];
|
|
27
29
|
export const DEFAULT_TOOLS = [...VALID_DOCS_TOOLS];
|
|
28
30
|
const parsedArgs = minimist(process.argv.slice(2));
|
|
@@ -139,6 +141,7 @@ if (cliTools.length > 0) {
|
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
143
|
}
|
|
144
|
+
let cliAccountAuthStrategy;
|
|
142
145
|
// When `--wixCliAuth` is passed, register the auth-requiring tools using the
|
|
143
146
|
// OAuth token written by `npx @wix/cli login` at ~/.wix/auth/account.json.
|
|
144
147
|
// Refreshes the access token transparently when it nears expiry.
|
|
@@ -152,6 +155,15 @@ if (isWixCliAuth) {
|
|
|
152
155
|
const tokens = new TokenStore({ accountFile });
|
|
153
156
|
const accountAuthStrategy = createAccountFileAuthStrategy({ tokens });
|
|
154
157
|
addApiCallTool(server, accountAuthStrategy);
|
|
158
|
+
cliAccountAuthStrategy = accountAuthStrategy;
|
|
159
|
+
}
|
|
160
|
+
// Experimental: Wix GraphQL tools. Schema search/inspection need no auth;
|
|
161
|
+
// ExecuteWixGraphQL registers only when --wixCliAuth supplies credentials.
|
|
162
|
+
if (activeTools.includes('GRAPHQL')) {
|
|
163
|
+
logger.log('Adding GraphQL tools', {
|
|
164
|
+
execute: Boolean(cliAccountAuthStrategy)
|
|
165
|
+
});
|
|
166
|
+
addGraphqlTools(server, { authStrategy: cliAccountAuthStrategy });
|
|
155
167
|
}
|
|
156
168
|
try {
|
|
157
169
|
const portals = parsedArgs['portals']?.split(',') || [];
|
|
@@ -19,6 +19,7 @@ export interface ModeDefaultConfig {
|
|
|
19
19
|
partnerContextEnabled?: boolean;
|
|
20
20
|
usePlatformizedSearch?: boolean;
|
|
21
21
|
skillsPrOverrideEnabled?: boolean;
|
|
22
|
+
graphqlEnabled?: boolean;
|
|
22
23
|
}
|
|
23
24
|
export declare const SYSTEM_REMINDER_GUIDELINES: string;
|
|
24
25
|
export declare function buildSystemReminder(includeReadme: boolean, includeImageUploadGuidance?: boolean): string;
|
|
@@ -80,6 +80,8 @@ export const SITE_CREATION_COPY = {
|
|
|
80
80
|
const SITE_GUIDANCE = dedent `
|
|
81
81
|
A new-site request has two things to settle: what the site is for, and how to build it. The how is AI generation vs. starting from a template, so naming templates, a template id or Wix Studio (Wix Studio is also the template side) already settles it.
|
|
82
82
|
|
|
83
|
+
One case skips all of this: the user ALREADY HAS the finished site as files — hand-written HTML/CSS/JS, a zip, or the export of an AI builder. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool. Send them to https://www.wix.com/headless/drop?utm_campaign=mcp — they drag the files in and get a live Wix-hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
|
|
84
|
+
|
|
83
85
|
Settle what the site is for first: its purpose, topic, business type, audience, or main visitor action. Naming a method is not a description of the site, and neither is the business or industry on its own.
|
|
84
86
|
If you can't tell yet, ask the preferred question below and nothing else — one question, no creation-method question in the same reply, and no internal tool names.
|
|
85
87
|
Once the purpose is clear, unless the user already named a method, ${SITE_CREATION_COPY.unstatedMethod.nonWidget}. A method they named is settled — never re-open it. Never default to AI or to templates, and never ask the user to choose between Harmony and Wix Studio; infer that from their words.
|
|
@@ -412,7 +414,8 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
|
|
|
412
414
|
After calling, you MUST speak the message provided in the tool result to the user as your reply — do not stay silent and do not replace it with a one-line pointer to the widget. Write out ALL of it, word for word: its opening line (the "Ready to set up your … site?" question INCLUDING "Choose your path in the widget above:"), BOTH of its option bullets, and its "Next up:" line, each on its own line and in that order. Do not summarize, shorten, drop any line, invent your own opener, rename the options, or add an intro or outro.
|
|
413
415
|
`,
|
|
414
416
|
'import-claude-design-from-url': dedent `
|
|
415
|
-
Import a design into Wix
|
|
417
|
+
Import a design created in Claude's design tool into Wix. Takes the signed claudeusercontent.com URL Claude Design produces for its self-contained HTML bundle (all images, fonts, and styles inlined); any other host is rejected. Creates a live Wix-hosted site and returns its URL.
|
|
418
|
+
NOT for site files the user provides themselves (their own HTML/CSS/JS, a zip, another builder's export) — for those, don't call this tool: send the user to https://www.wix.com/headless/drop?utm_campaign=mcp, where they upload the files and get a live hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
|
|
416
419
|
`,
|
|
417
420
|
RunWixCliCommand: dedent `
|
|
418
421
|
Executes Wix CLI commands for Wix app development and management.
|
|
@@ -809,10 +812,37 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
|
|
|
809
812
|
|
|
810
813
|
Call this when the user provides a specific template by \`metaSiteId\` or \`templateId\`. Use this only when the user gives a valid \`metaSiteId\` or \`templateId\` directly. To browse or choose a template first, call \`SearchSiteTemplates\`.
|
|
811
814
|
|
|
815
|
+
`,
|
|
816
|
+
SearchWixGraphQLOperations: dedent `
|
|
817
|
+
Discover Wix GraphQL operations by intent (ranked keyword search over the root fields). Wix exposes ONE GraphQL schema over all Wix public APIs (Developer Preview) at POST https://www.wixapis.com/graphql/alpha: about 100 query roots and 580 mutation roots named <app><Service><Version><Verb/Entity>, e.g. \`storesProductsV3V3Products\`, \`crmContactsV4Contacts\`, \`crmContactsV4CreateContact\`, \`ecomOrdersV1SearchOrders\`.
|
|
818
|
+
|
|
819
|
+
GraphQL flow (3 steps, keep to it): SearchWixGraphQLOperations (find the operation) -> InspectWixGraphQLSchema (read its exact input and return types) -> ExecuteWixGraphQL (run it). Never write an operation or field name you did not see in one of these results.
|
|
820
|
+
|
|
821
|
+
When to prefer GraphQL over the REST tools:
|
|
822
|
+
- Reading several related entities in ONE round-trip: related entities are resolved fields (product -> inventoryItems / collections, order -> fulfillments, booking -> contact), and independent root fields (products + contacts + orders) can share one request.
|
|
823
|
+
- Picking exactly the fields you need, so responses stay small.
|
|
824
|
+
- Answering "how many / list / show" questions: a query root plus \`pageInfo\` (\`hasNext\`, and \`total\` where the API provides it).
|
|
825
|
+
Prefer the REST tools when a recipe or code example already gives you the exact REST call, for uploads/media, or when the GraphQL schema has no matching operation.
|
|
826
|
+
|
|
827
|
+
Conventions: query roots take \`queryInput\`; list roots (plural names) take \`queryInput: { query: { paging | cursorPaging, filter, sort } }\` and return \`{ items, pageInfo }\`; singular roots take the entity id. \`filter\`/\`sort\` are a JSON scalar in REST filter syntax (e.g. {"info.name.first": {"$eq": "Ada"}}) — pass them as variables. Mutations take \`input\`; a few read operations (search/list) are exposed as mutations, so search kind "all" unless you are sure.
|
|
828
|
+
Results list \`kind name(arg: InputType): ReturnType\` plus a one-line description; pass the chosen name to InspectWixGraphQLSchema next.
|
|
829
|
+
`,
|
|
830
|
+
InspectWixGraphQLSchema: dedent `
|
|
831
|
+
Read the exact Wix GraphQL schema for an operation and its types, as compact SDL with field descriptions. Pass \`operation\` (a root query/mutation name from SearchWixGraphQLOperations) to get its arguments, input type and return type expanded \`depth\` levels (default 2). Types that were referenced but not expanded are listed at the end — pass the ones you need in \`typeNames\` (optionally together with \`operation\`) instead of raising depth for everything.
|
|
832
|
+
Use it before every ExecuteWixGraphQL call that introduces a new operation or new fields: read the INPUT type before writing a mutation or filter, and the RETURN type before writing the selection set. GraphQL rejects unknown fields and arguments, and every object-typed field needs its own selection set. \`JSON\` scalar fields (filter, sort, free-form data) accept any JSON — pass them as variables. Never guess a type or field name; if a validation error names a field, come back here.
|
|
833
|
+
`,
|
|
834
|
+
ExecuteWixGraphQL: dedent `
|
|
835
|
+
Execute a GraphQL query or mutation against the Wix GraphQL API (POST https://www.wixapis.com/graphql/alpha) with the user's credentials. Authentication is injected — never add Authorization or site/account headers.
|
|
836
|
+
Site targeting: site-level operations (anything that reads or writes inside a site: products, inventory, orders, contacts, bookings, CMS data, blog, members ...) require \`siteId\`; resolve it with GetSiteContext or ListWixSites first. Use \`scope: "account"\` only for account-level operations.
|
|
837
|
+
Grounding: every root field, argument and selected field must come from SearchWixGraphQLOperations / InspectWixGraphQLSchema results in this conversation — never from memory. If the response says "Cannot query field" or "Unknown argument", re-inspect the schema and fix the name; do not retry with another guess.
|
|
838
|
+
Compose: put independent reads in one document as several root fields, and use related-entity fields (e.g. \`inventoryItems\`, \`collections\`, \`fulfillments\`) instead of extra round-trips. Select only the fields you need; every object field needs a selection set. Variables: use them for \`JSON\` scalars (filter, sort) and plain scalars (String, Int, ID), declared in the document (e.g. \`query Q($f: JSON) { ... filter: $f ... }\`). Write mutation inputs and any object that contains an enum INLINE as GraphQL literals with enum values unquoted (e.g. \`input: { product: { name: "Mug", productType: PHYSICAL } }\`) — this endpoint fails with 503 when an enum value arrives inside an object-typed variable. For lists page with \`cursorPaging\` or \`paging\` exactly as the input type declares (never the other one), and request \`pageInfo { hasNext total }\`; \`total\` is only populated with offset \`paging\`, so for a cursor-paged list count by walking pages or use a count operation.
|
|
839
|
+
Results: each root field fails independently — \`errors[].path\` names the failing field and \`errors[].extensions.message\` the cause (e.g. PERMISSION_DENIED); \`data\` still holds the fields that succeeded. Mutations return the created/updated entity — read the IDs from it and do not re-query to verify. Large results are truncated: narrow the selection or page size instead of re-running.
|
|
840
|
+
Set \`hasMutations: true\` for any document containing a mutation root field.
|
|
812
841
|
`,
|
|
813
842
|
CreateWixBusinessGuide: dedent `
|
|
814
843
|
Provides comprehensive documentation for creating a new Wix business (site, app, etc.).
|
|
815
844
|
Call this ONLY when the user explicitly asks for a headless site, a Wix Classic Editor site, or manual/advanced account/API-based site creation — these don't need the site's purpose first. For normal site-creation ${SITE_CREATION_COPY.normalCreationRoute.nonWidget}; for a specific template id, call \`CreateSiteFromTemplate\`.
|
|
845
|
+
Exception, even when the user says "headless": if they ALREADY HAVE the finished site as files (their own HTML/CSS/JS, a zip, an AI-builder export) and want those files hosted, don't call this tool — send them to https://www.wix.com/headless/drop?utm_campaign=mcp to upload the files and get a live hosted site (give that URL exactly, keeping the utm_campaign=mcp parameter). This guide's CLI flow is for BUILDING a headless frontend, not for hosting ready-made files.
|
|
816
846
|
|
|
817
847
|
## What This Tool Contains
|
|
818
848
|
|
|
@@ -945,7 +975,8 @@ export function mergeConfig(base, overrides) {
|
|
|
945
975
|
dynamicContextEnabled: overrides.dynamicContextEnabled ?? base.dynamicContextEnabled,
|
|
946
976
|
partnerContextEnabled: overrides.partnerContextEnabled ?? base.partnerContextEnabled,
|
|
947
977
|
usePlatformizedSearch: overrides.usePlatformizedSearch ?? base.usePlatformizedSearch,
|
|
948
|
-
skillsPrOverrideEnabled: overrides.skillsPrOverrideEnabled ?? base.skillsPrOverrideEnabled
|
|
978
|
+
skillsPrOverrideEnabled: overrides.skillsPrOverrideEnabled ?? base.skillsPrOverrideEnabled,
|
|
979
|
+
graphqlEnabled: overrides.graphqlEnabled ?? base.graphqlEnabled
|
|
949
980
|
};
|
|
950
981
|
}
|
|
951
982
|
/**
|
|
@@ -90,6 +90,27 @@ export declare const paramDescriptions: {
|
|
|
90
90
|
mimeType: string;
|
|
91
91
|
displayName: string;
|
|
92
92
|
};
|
|
93
|
+
SearchWixGraphQLOperations: {
|
|
94
|
+
query: string;
|
|
95
|
+
kind: string;
|
|
96
|
+
maxResults: string;
|
|
97
|
+
reason: string;
|
|
98
|
+
};
|
|
99
|
+
InspectWixGraphQLSchema: {
|
|
100
|
+
operation: string;
|
|
101
|
+
typeNames: string;
|
|
102
|
+
depth: string;
|
|
103
|
+
reason: string;
|
|
104
|
+
};
|
|
105
|
+
ExecuteWixGraphQL: {
|
|
106
|
+
query: string;
|
|
107
|
+
variables: string;
|
|
108
|
+
operationName: string;
|
|
109
|
+
siteId: string;
|
|
110
|
+
scope: string;
|
|
111
|
+
hasMutations: string;
|
|
112
|
+
reason: string;
|
|
113
|
+
};
|
|
93
114
|
RunWixCliCommand: {
|
|
94
115
|
appPath: string;
|
|
95
116
|
extensionType: string;
|
|
@@ -127,6 +127,27 @@ export const paramDescriptions = {
|
|
|
127
127
|
mimeType: 'MIME type of the image (e.g. "image/jpeg", "image/png", "image/webp"). Required when imageBase64 does not include a data URL prefix.',
|
|
128
128
|
displayName: 'Optional display name for the file in Media Manager. If not provided, uses the original filename.'
|
|
129
129
|
},
|
|
130
|
+
SearchWixGraphQLOperations: {
|
|
131
|
+
query: 'Free-text intent or entity, e.g. "products with inventory", "create contact", "orders", "blog posts", "booking services". Operation names encode app + service + version + verb/entity (e.g. storesProductsV3V3Products, crmContactsV4CreateContact), so a noun plus a verb (query/get/create/update/delete) ranks best. You may also pass an exact operation name.',
|
|
132
|
+
kind: 'Restrict to "query" (reads), "mutation" (writes — note a few reads such as search/list calls are exposed as mutations), or "all" (default).',
|
|
133
|
+
maxResults: 'Maximum number of operations to return (default 10, max 50).',
|
|
134
|
+
reason: 'One sentence describing the original user request and why you are looking for a GraphQL operation.'
|
|
135
|
+
},
|
|
136
|
+
InspectWixGraphQLSchema: {
|
|
137
|
+
operation: 'Exact root query or mutation name from SearchWixGraphQLOperations (e.g. "crmContactsV4Contacts"). Returns its arguments, input type and return type as compact SDL, expanded `depth` levels.',
|
|
138
|
+
typeNames: 'Type names to expand, e.g. ones listed as "Not expanded" in a previous result, or a nested input/object/enum type you need before writing the request. Can be combined with `operation`.',
|
|
139
|
+
depth: 'How many levels of referenced types to expand (1-4, default 2). Level 1 is the given types, level 2 the types their fields reference, and so on. Keep it small and expand specific types with typeNames instead of dumping everything.',
|
|
140
|
+
reason: 'One sentence describing the original user request and why you need this part of the GraphQL schema.'
|
|
141
|
+
},
|
|
142
|
+
ExecuteWixGraphQL: {
|
|
143
|
+
query: 'The GraphQL document (a query or a mutation). Every root field, argument and selected field MUST come from SearchWixGraphQLOperations / InspectWixGraphQLSchema results in this conversation, never from memory. Declare variables for filters, sort and large inputs, e.g. `query Q($f: JSON) { crmContactsV4Contacts(queryInput: { query: { filter: $f, paging: { limit: 50 } } }) { items { id } pageInfo { hasNext } } }`. Object-typed fields need a selection set. You can put several independent root fields in one document.',
|
|
144
|
+
variables: 'Variables object for the document, as a JSON object (not a string). Use it for JSON-scalar filters/sort (REST filter syntax, e.g. {"info.name.first": {"$eq": "Ada"}}) and plain scalars. Do NOT pass object inputs that contain enum values (product types, statuses, registration types ...) through variables — write those inline in the document with the enum unquoted; the endpoint returns 503 otherwise.',
|
|
145
|
+
operationName: 'Operation to run when the document defines more than one named operation.',
|
|
146
|
+
siteId: 'The Wix site to run against. Required for site-level operations (anything reading or writing inside a site — products, orders, contacts, bookings, CMS, blog, members ...). Resolve it with GetSiteContext or ListWixSites; never pass a guessed value.',
|
|
147
|
+
scope: '"site" (default when siteId is given) runs with a site-scoped credential; "account" runs account-level operations (sites, account settings) and ignores siteId.',
|
|
148
|
+
hasMutations: 'Whether this document creates, updates, deletes or otherwise mutates Wix data. True for any mutation root field (in any operation of the document) even when the intent is inspection; false only for pure queries. This flag alone decides whether a transient server error may be retried, so never mark a document that writes as false.',
|
|
149
|
+
reason: 'One sentence explaining the original user request and why you are running this GraphQL operation to complete it.'
|
|
150
|
+
},
|
|
130
151
|
RunWixCliCommand: {
|
|
131
152
|
appPath: 'The full path to the Wix CLI app to run the command on.',
|
|
132
153
|
extensionType: 'The type of extension to generate. You MUST provide this for commands that generate extensions.',
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Wix "Auto GraphQL": one schema generated over all Wix public APIs. */
|
|
2
|
+
export declare const WIX_GRAPHQL_ENDPOINT = "https://www.wixapis.com/graphql/alpha";
|
|
3
|
+
export declare const SEARCH_GRAPHQL_TOOL_NAME = "SearchWixGraphQLOperations";
|
|
4
|
+
export declare const INSPECT_GRAPHQL_TOOL_NAME = "InspectWixGraphQLSchema";
|
|
5
|
+
export declare const EXECUTE_GRAPHQL_TOOL_NAME = "ExecuteWixGraphQL";
|
|
6
|
+
export declare const GRAPHQL_TOOL_NAMES: readonly ["SearchWixGraphQLOperations", "InspectWixGraphQLSchema", "ExecuteWixGraphQL"];
|
|
7
|
+
export type GraphqlToolName = (typeof GRAPHQL_TOOL_NAMES)[number];
|
|
8
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Wix "Auto GraphQL": one schema generated over all Wix public APIs. */
|
|
2
|
+
export const WIX_GRAPHQL_ENDPOINT = 'https://www.wixapis.com/graphql/alpha';
|
|
3
|
+
export const SEARCH_GRAPHQL_TOOL_NAME = 'SearchWixGraphQLOperations';
|
|
4
|
+
export const INSPECT_GRAPHQL_TOOL_NAME = 'InspectWixGraphQLSchema';
|
|
5
|
+
export const EXECUTE_GRAPHQL_TOOL_NAME = 'ExecuteWixGraphQL';
|
|
6
|
+
export const GRAPHQL_TOOL_NAMES = [
|
|
7
|
+
SEARCH_GRAPHQL_TOOL_NAME,
|
|
8
|
+
INSPECT_GRAPHQL_TOOL_NAME,
|
|
9
|
+
EXECUTE_GRAPHQL_TOOL_NAME
|
|
10
|
+
];
|
|
11
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { WixMcpServer } from '../wix-mcp-server.js';
|
|
2
|
+
import { type McpAuthenticationStrategy } from '../auth/index.js';
|
|
3
|
+
import { type FetchLike } from './introspection.js';
|
|
4
|
+
export { EXECUTE_GRAPHQL_TOOL_NAME, GRAPHQL_TOOL_NAMES, INSPECT_GRAPHQL_TOOL_NAME, SEARCH_GRAPHQL_TOOL_NAME, WIX_GRAPHQL_ENDPOINT, type GraphqlToolName } from './constants.js';
|
|
5
|
+
export { resetGraphqlSchemaCache } from './introspection.js';
|
|
6
|
+
export interface GraphqlToolsOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Auth for ExecuteWixGraphQL. Without it only the two schema tools
|
|
9
|
+
* (introspection needs no auth) are registered.
|
|
10
|
+
*/
|
|
11
|
+
authStrategy?: McpAuthenticationStrategy;
|
|
12
|
+
disableTools?: string[];
|
|
13
|
+
toolDescriptions?: Record<string, string>;
|
|
14
|
+
/** Max response size in chars before truncation (default: 24,000). */
|
|
15
|
+
maxResponseSize?: number;
|
|
16
|
+
/** Override the GraphQL endpoint (tests / other environments). */
|
|
17
|
+
endpoint?: string;
|
|
18
|
+
/** Override fetch (tests). */
|
|
19
|
+
fetchImpl?: FetchLike;
|
|
20
|
+
/** Backoff schedule for transient 5xx from the endpoint (tests pass []). */
|
|
21
|
+
retryDelaysMs?: number[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Register the Wix GraphQL tools. Progressive disclosure over one ~4MB schema:
|
|
25
|
+
* SearchWixGraphQLOperations (rank root operations for an intent)
|
|
26
|
+
* → InspectWixGraphQLSchema (exact input/return types, expanded on demand)
|
|
27
|
+
* → ExecuteWixGraphQL (run the query/mutation with the caller's auth).
|
|
28
|
+
*/
|
|
29
|
+
export declare function addGraphqlTools(server: WixMcpServer, options?: GraphqlToolsOptions): void;
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { logger } from '../infra/logger.js';
|
|
3
|
+
import { defaultToolDescriptions } from '../config/default-config.js';
|
|
4
|
+
import { desc } from '../config/param-descriptions.js';
|
|
5
|
+
import { truncateResponse } from '../code-mode/truncate-response.js';
|
|
6
|
+
import { rethrowIfUnauthorized } from '../auth/index.js';
|
|
7
|
+
import { EXECUTE_GRAPHQL_TOOL_NAME, INSPECT_GRAPHQL_TOOL_NAME, SEARCH_GRAPHQL_TOOL_NAME, WIX_GRAPHQL_ENDPOINT } from './constants.js';
|
|
8
|
+
import { expandTypes, fetchWithRetry, getRootOperations, namedType, renderOperationSignature } from './introspection.js';
|
|
9
|
+
import { searchOperations } from './search.js';
|
|
10
|
+
export { EXECUTE_GRAPHQL_TOOL_NAME, GRAPHQL_TOOL_NAMES, INSPECT_GRAPHQL_TOOL_NAME, SEARCH_GRAPHQL_TOOL_NAME, WIX_GRAPHQL_ENDPOINT } from './constants.js';
|
|
11
|
+
export { resetGraphqlSchemaCache } from './introspection.js';
|
|
12
|
+
const textResult = (text) => ({
|
|
13
|
+
content: [{ type: 'text', text }]
|
|
14
|
+
});
|
|
15
|
+
const errorResult = (text) => ({
|
|
16
|
+
isError: true,
|
|
17
|
+
content: [{ type: 'text', text }]
|
|
18
|
+
});
|
|
19
|
+
const SCHEMA_MAX_RESPONSE_CHARS = 48_000;
|
|
20
|
+
/**
|
|
21
|
+
* Register the Wix GraphQL tools. Progressive disclosure over one ~4MB schema:
|
|
22
|
+
* SearchWixGraphQLOperations (rank root operations for an intent)
|
|
23
|
+
* → InspectWixGraphQLSchema (exact input/return types, expanded on demand)
|
|
24
|
+
* → ExecuteWixGraphQL (run the query/mutation with the caller's auth).
|
|
25
|
+
*/
|
|
26
|
+
export function addGraphqlTools(server, options = {}) {
|
|
27
|
+
const { authStrategy, disableTools = [], toolDescriptions } = options;
|
|
28
|
+
const getDescription = (name) => toolDescriptions?.[name] ?? defaultToolDescriptions[name];
|
|
29
|
+
if (!disableTools.includes(SEARCH_GRAPHQL_TOOL_NAME)) {
|
|
30
|
+
addSearchGraphqlTool(server, getDescription(SEARCH_GRAPHQL_TOOL_NAME), options);
|
|
31
|
+
}
|
|
32
|
+
if (!disableTools.includes(INSPECT_GRAPHQL_TOOL_NAME)) {
|
|
33
|
+
addInspectGraphqlTool(server, getDescription(INSPECT_GRAPHQL_TOOL_NAME), options);
|
|
34
|
+
}
|
|
35
|
+
if (authStrategy && !disableTools.includes(EXECUTE_GRAPHQL_TOOL_NAME)) {
|
|
36
|
+
addExecuteGraphqlTool(server, getDescription(EXECUTE_GRAPHQL_TOOL_NAME), authStrategy, options);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function introspectionOptions(options) {
|
|
40
|
+
return {
|
|
41
|
+
endpoint: options.endpoint,
|
|
42
|
+
fetchImpl: options.fetchImpl,
|
|
43
|
+
retryDelaysMs: options.retryDelaysMs
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function addSearchGraphqlTool(server, description, options) {
|
|
47
|
+
const d = desc('SearchWixGraphQLOperations');
|
|
48
|
+
server.tool(SEARCH_GRAPHQL_TOOL_NAME, description, {
|
|
49
|
+
query: z.string().describe(d.query),
|
|
50
|
+
kind: z.enum(['query', 'mutation', 'all']).optional().describe(d.kind),
|
|
51
|
+
maxResults: z
|
|
52
|
+
.number()
|
|
53
|
+
.int()
|
|
54
|
+
.min(1)
|
|
55
|
+
.max(50)
|
|
56
|
+
.optional()
|
|
57
|
+
.describe(d.maxResults),
|
|
58
|
+
reason: z.string().describe(d.reason)
|
|
59
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ query, kind, maxResults, reason }) => {
|
|
60
|
+
logger.log(`[${SEARCH_GRAPHQL_TOOL_NAME}] query="${query}" kind=${kind ?? 'all'} reason="${reason}"`);
|
|
61
|
+
try {
|
|
62
|
+
const ops = await getRootOperations(introspectionOptions(options));
|
|
63
|
+
const matches = searchOperations(ops, query, {
|
|
64
|
+
kind: kind ?? 'all',
|
|
65
|
+
maxResults: maxResults ?? 10
|
|
66
|
+
});
|
|
67
|
+
if (matches.length === 0) {
|
|
68
|
+
return textResult(`No GraphQL operations matched "${query}". Try other words for the entity (e.g. "contact" instead of "customer", "product" instead of "item") or the verb (query/get/create/update/delete), or search a single noun.`);
|
|
69
|
+
}
|
|
70
|
+
const lines = matches.map((m, i) => `${i + 1}. ${m.kind} ${m.signature}\n ${m.description || '(no description)'}`);
|
|
71
|
+
const text = [
|
|
72
|
+
`Top ${matches.length} of ${ops.length} Wix GraphQL root operations for "${query}":`,
|
|
73
|
+
'',
|
|
74
|
+
...lines,
|
|
75
|
+
'',
|
|
76
|
+
`Next: call ${INSPECT_GRAPHQL_TOOL_NAME} with \`operation\` set to the chosen name to read its exact input type and return type before writing the request.`
|
|
77
|
+
].join('\n');
|
|
78
|
+
return textResult(text);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
82
|
+
logger.log(`[${SEARCH_GRAPHQL_TOOL_NAME}] Error: ${message}`);
|
|
83
|
+
return errorResult(`GraphQL schema search error: ${message}`);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function addInspectGraphqlTool(server, description, options) {
|
|
88
|
+
const d = desc('InspectWixGraphQLSchema');
|
|
89
|
+
const maxChars = options.maxResponseSize ?? SCHEMA_MAX_RESPONSE_CHARS;
|
|
90
|
+
server.tool(INSPECT_GRAPHQL_TOOL_NAME, description, {
|
|
91
|
+
operation: z.string().optional().describe(d.operation),
|
|
92
|
+
typeNames: z.array(z.string()).optional().describe(d.typeNames),
|
|
93
|
+
depth: z.number().int().min(1).max(4).optional().describe(d.depth),
|
|
94
|
+
reason: z.string().describe(d.reason)
|
|
95
|
+
}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ operation, typeNames, depth, reason }) => {
|
|
96
|
+
logger.log(`[${INSPECT_GRAPHQL_TOOL_NAME}] operation=${operation ?? '-'} typeNames=${JSON.stringify(typeNames ?? [])} depth=${depth ?? 2} reason="${reason}"`);
|
|
97
|
+
if (!operation && !(typeNames && typeNames.length > 0)) {
|
|
98
|
+
return errorResult('Pass `operation` (a root query/mutation name from SearchWixGraphQLOperations) and/or `typeNames` (type names to expand).');
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const io = introspectionOptions(options);
|
|
102
|
+
const sections = [];
|
|
103
|
+
const roots = [];
|
|
104
|
+
if (operation) {
|
|
105
|
+
const ops = await getRootOperations(io);
|
|
106
|
+
const op = ops.find((o) => o.name === operation);
|
|
107
|
+
if (!op) {
|
|
108
|
+
const suggestions = searchOperations(ops, operation, {
|
|
109
|
+
maxResults: 5
|
|
110
|
+
});
|
|
111
|
+
return errorResult(`No root operation named "${operation}". ${suggestions.length
|
|
112
|
+
? `Did you mean: ${suggestions.map((s) => `${s.kind} ${s.operation}`).join(', ')}? Use SearchWixGraphQLOperations to discover the exact name.`
|
|
113
|
+
: 'Use SearchWixGraphQLOperations to discover the exact name.'}`);
|
|
114
|
+
}
|
|
115
|
+
sections.push(`# ${renderOperationSignature(op)}`, op.description
|
|
116
|
+
? op.description
|
|
117
|
+
.trim()
|
|
118
|
+
.split('\n')
|
|
119
|
+
.map((l) => `# ${l}`)
|
|
120
|
+
.join('\n')
|
|
121
|
+
: '# (no description)');
|
|
122
|
+
op.args.forEach((a) => {
|
|
123
|
+
const n = namedType(a.type);
|
|
124
|
+
if (n)
|
|
125
|
+
roots.push(n);
|
|
126
|
+
});
|
|
127
|
+
const ret = namedType(op.type);
|
|
128
|
+
if (ret)
|
|
129
|
+
roots.push(ret);
|
|
130
|
+
}
|
|
131
|
+
if (typeNames?.length)
|
|
132
|
+
roots.push(...typeNames);
|
|
133
|
+
const { rendered, unexpanded, unknown } = await expandTypes(roots, {
|
|
134
|
+
...io,
|
|
135
|
+
depth: depth ?? 2
|
|
136
|
+
});
|
|
137
|
+
sections.push(...rendered);
|
|
138
|
+
if (unknown.length) {
|
|
139
|
+
sections.push(`# Unknown type names (not in the schema): ${unknown.join(', ')}`);
|
|
140
|
+
}
|
|
141
|
+
if (unexpanded.length) {
|
|
142
|
+
sections.push(`# Not expanded (pass any of these in \`typeNames\` to read them): ${unexpanded.join(', ')}`);
|
|
143
|
+
}
|
|
144
|
+
sections.push('# Notes: `JSON` scalar fields (filter, sort, free-form data) take any JSON — pass them as variables. Every object-typed field in a selection needs its own { ... } selection set. Only fields listed above exist. Paging: use exactly the paging style the input type declares — `paging` (offset) or `cursorPaging` (cursor) — never the other one. `pageInfo.total` is only populated with offset paging; with cursor paging, count by walking pages (`pageInfo.hasNext` + cursor) or use a dedicated count operation if one exists.');
|
|
145
|
+
return textResult(truncateResponse(sections.join('\n\n'), maxChars));
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149
|
+
logger.log(`[${INSPECT_GRAPHQL_TOOL_NAME}] Error: ${message}`);
|
|
150
|
+
return errorResult(`GraphQL schema inspection error: ${message}`);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function addExecuteGraphqlTool(server, description, authStrategy, options) {
|
|
155
|
+
const d = desc('ExecuteWixGraphQL');
|
|
156
|
+
const endpoint = options.endpoint ?? WIX_GRAPHQL_ENDPOINT;
|
|
157
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
158
|
+
server.tool(EXECUTE_GRAPHQL_TOOL_NAME, description, {
|
|
159
|
+
query: z.string().describe(d.query),
|
|
160
|
+
variables: z.record(z.unknown()).optional().describe(d.variables),
|
|
161
|
+
operationName: z.string().optional().describe(d.operationName),
|
|
162
|
+
siteId: z.string().optional().describe(d.siteId),
|
|
163
|
+
scope: z.enum(['site', 'account']).optional().describe(d.scope),
|
|
164
|
+
hasMutations: z.boolean().describe(d.hasMutations),
|
|
165
|
+
reason: z.string().describe(d.reason)
|
|
166
|
+
}, { readOnlyHint: false, destructiveHint: true, openWorldHint: false }, async ({ query, variables, operationName, siteId, scope, hasMutations, reason }, extra) => {
|
|
167
|
+
const effectiveScope = scope ?? (siteId ? 'site' : 'account');
|
|
168
|
+
logger.log(`[${EXECUTE_GRAPHQL_TOOL_NAME}] scope=${effectiveScope} siteId=${siteId ?? 'none'} hasMutations=${hasMutations} operationName=${operationName ?? '-'} reason="${reason}"`);
|
|
169
|
+
if (effectiveScope === 'site' && !siteId) {
|
|
170
|
+
return errorResult('A `siteId` is required for site-level GraphQL operations (anything that reads or writes inside a site). Resolve it with GetSiteContext or ListWixSites, or set scope: "account" for account-level operations.');
|
|
171
|
+
}
|
|
172
|
+
if (!query.trim()) {
|
|
173
|
+
return errorResult('`query` must be a non-empty GraphQL document.');
|
|
174
|
+
}
|
|
175
|
+
// Resolve auth before the try so an UnauthorizedError propagates to
|
|
176
|
+
// wrapToolCallback (which attaches the re-auth metadata).
|
|
177
|
+
const authHeaders = effectiveScope === 'site'
|
|
178
|
+
? await authStrategy.getSiteAuthHeaders(siteId)
|
|
179
|
+
: await authStrategy.getAccountAuthHeaders();
|
|
180
|
+
try {
|
|
181
|
+
const init = {
|
|
182
|
+
method: 'POST',
|
|
183
|
+
headers: {
|
|
184
|
+
'Content-Type': 'application/json',
|
|
185
|
+
Accept: 'application/json',
|
|
186
|
+
...authHeaders
|
|
187
|
+
},
|
|
188
|
+
body: JSON.stringify({
|
|
189
|
+
query,
|
|
190
|
+
...(variables ? { variables } : {}),
|
|
191
|
+
...(operationName ? { operationName } : {})
|
|
192
|
+
})
|
|
193
|
+
};
|
|
194
|
+
// Transient 503s are retried only when the agent declared the document
|
|
195
|
+
// read-only (hasMutations: false) — the same contract as ExecuteWixAPI,
|
|
196
|
+
// with no inference from the document text. A mutation might have been
|
|
197
|
+
// applied before the edge failed the response, so it is never resent.
|
|
198
|
+
const resp = hasMutations
|
|
199
|
+
? await fetchImpl(endpoint, init)
|
|
200
|
+
: await fetchWithRetry(fetchImpl, endpoint, init, options.retryDelaysMs);
|
|
201
|
+
const requestId = resp.headers?.get('x-wix-request-id') ?? undefined;
|
|
202
|
+
extra?.setToolBiParams?.({ siteId, requestId });
|
|
203
|
+
const text = await resp.text();
|
|
204
|
+
if (!resp.ok) {
|
|
205
|
+
await authStrategy.enforceValidWixToken?.({
|
|
206
|
+
status: resp.status,
|
|
207
|
+
response: { status: resp.status }
|
|
208
|
+
});
|
|
209
|
+
// The edge serves a branded HTML page on 5xx; keep the result short.
|
|
210
|
+
const body = /^\s*<(!doctype|html)/i.test(text)
|
|
211
|
+
? '(HTML error page)'
|
|
212
|
+
: text.slice(0, 2000);
|
|
213
|
+
const hasObjectVariables = !!variables &&
|
|
214
|
+
Object.values(variables).some((v) => v !== null && typeof v === 'object');
|
|
215
|
+
const hint = resp.status >= 500
|
|
216
|
+
? hasObjectVariables
|
|
217
|
+
? ` The Wix GraphQL endpoint (Developer Preview) returns 503 when an object-typed variable carries an enum value. Rewrite the document with the input INLINE as a GraphQL literal (enum values unquoted, e.g. productType: PHYSICAL), keep variables only for JSON scalars and plain scalars, and retry once. If it still fails and REST tools (ExecuteWixAPI / CallWixSiteAPI) are available, use the equivalent REST method.`
|
|
218
|
+
: ` The Wix GraphQL endpoint (Developer Preview) failed for this operation. Do not retry the same document more than once. If REST tools (ExecuteWixAPI / CallWixSiteAPI) are available, complete the task through the equivalent REST method instead; otherwise report the failure with the request id.`
|
|
219
|
+
: '';
|
|
220
|
+
return errorResult(`Wix GraphQL request failed: ${resp.status} ${resp.statusText}.${requestId ? ` request id: ${requestId}.` : ''}${hint}\n${body}`);
|
|
221
|
+
}
|
|
222
|
+
let json;
|
|
223
|
+
try {
|
|
224
|
+
json = JSON.parse(text);
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
return errorResult(`Wix GraphQL returned a non-JSON body:\n${text.slice(0, 2000)}`);
|
|
228
|
+
}
|
|
229
|
+
const hasData = json.data != null &&
|
|
230
|
+
Object.values(json.data).some((v) => v !== null && v !== undefined);
|
|
231
|
+
const errors = json.errors ?? [];
|
|
232
|
+
if (!hasData && errors.length > 0) {
|
|
233
|
+
const permissionDenied = errors.some((e) => e.extensions?.message === 'PERMISSION_DENIED');
|
|
234
|
+
return errorResult([
|
|
235
|
+
`Wix GraphQL returned errors and no data${requestId ? ` (request id: ${requestId})` : ''}:`,
|
|
236
|
+
JSON.stringify(errors, null, 2).slice(0, 4000),
|
|
237
|
+
permissionDenied
|
|
238
|
+
? 'PERMISSION_DENIED usually means the operation is site-level and was called without the right `siteId`, the app behind it is not installed on the site, or the credential lacks that permission scope.'
|
|
239
|
+
: 'Validation errors ("Cannot query field ...", "Unknown argument ...") mean a name was guessed — re-read the exact fields with InspectWixGraphQLSchema and retry.'
|
|
240
|
+
].join('\n'));
|
|
241
|
+
}
|
|
242
|
+
const body = { data: json.data };
|
|
243
|
+
if (errors.length > 0) {
|
|
244
|
+
body.errors = errors;
|
|
245
|
+
body.note =
|
|
246
|
+
'Partial result: some root fields failed (see errors[].path). Fields present in data succeeded.';
|
|
247
|
+
}
|
|
248
|
+
return textResult(truncateResponse(body, options.maxResponseSize));
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
rethrowIfUnauthorized(err);
|
|
252
|
+
await authStrategy.enforceValidWixToken?.(err);
|
|
253
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
254
|
+
logger.log(`[${EXECUTE_GRAPHQL_TOOL_NAME}] Error: ${message}`);
|
|
255
|
+
return errorResult(`Wix GraphQL execution error: ${message}`);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export interface TypeRef {
|
|
2
|
+
kind: string;
|
|
3
|
+
name: string | null;
|
|
4
|
+
ofType?: TypeRef | null;
|
|
5
|
+
}
|
|
6
|
+
export interface RootOperation {
|
|
7
|
+
/** 'query' | 'mutation' */
|
|
8
|
+
kind: 'query' | 'mutation';
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
args: Array<{
|
|
12
|
+
name: string;
|
|
13
|
+
type: TypeRef;
|
|
14
|
+
}>;
|
|
15
|
+
type: TypeRef;
|
|
16
|
+
}
|
|
17
|
+
export interface TypeField {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string | null;
|
|
20
|
+
type: TypeRef;
|
|
21
|
+
args?: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
type: TypeRef;
|
|
24
|
+
}> | null;
|
|
25
|
+
defaultValue?: string | null;
|
|
26
|
+
}
|
|
27
|
+
export interface TypeInfo {
|
|
28
|
+
kind: string;
|
|
29
|
+
name: string;
|
|
30
|
+
description: string | null;
|
|
31
|
+
fields: TypeField[] | null;
|
|
32
|
+
inputFields: TypeField[] | null;
|
|
33
|
+
enumValues: Array<{
|
|
34
|
+
name: string;
|
|
35
|
+
description: string | null;
|
|
36
|
+
}> | null;
|
|
37
|
+
possibleTypes: Array<{
|
|
38
|
+
name: string;
|
|
39
|
+
}> | null;
|
|
40
|
+
}
|
|
41
|
+
export type FetchLike = (input: string, init?: {
|
|
42
|
+
method?: string;
|
|
43
|
+
headers?: Record<string, string>;
|
|
44
|
+
body?: string;
|
|
45
|
+
}) => Promise<{
|
|
46
|
+
ok: boolean;
|
|
47
|
+
status: number;
|
|
48
|
+
statusText: string;
|
|
49
|
+
headers?: {
|
|
50
|
+
get(name: string): string | null;
|
|
51
|
+
};
|
|
52
|
+
text(): Promise<string>;
|
|
53
|
+
}>;
|
|
54
|
+
/** Test hook: forget every cached introspection result. */
|
|
55
|
+
export declare function resetGraphqlSchemaCache(): void;
|
|
56
|
+
export interface IntrospectionOptions {
|
|
57
|
+
endpoint?: string;
|
|
58
|
+
fetchImpl?: FetchLike;
|
|
59
|
+
/** Backoff schedule for transient 5xx (tests pass [] to disable). */
|
|
60
|
+
retryDelaysMs?: number[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The endpoint answers bursts with transient 503s (an HTML page). Retry a few
|
|
64
|
+
* times with backoff; introspection is idempotent so this is always safe.
|
|
65
|
+
*/
|
|
66
|
+
export declare const RETRY_DELAYS_MS: number[];
|
|
67
|
+
export declare function fetchWithRetry(fetchImpl: FetchLike, url: string, init: Parameters<FetchLike>[1], delays?: number[]): Promise<Awaited<ReturnType<FetchLike>>>;
|
|
68
|
+
/** All root query + mutation fields, cached in-process. */
|
|
69
|
+
export declare function getRootOperations(options?: IntrospectionOptions): Promise<RootOperation[]>;
|
|
70
|
+
/**
|
|
71
|
+
* Fetch several named types in one round-trip (aliased `__type` lookups).
|
|
72
|
+
* Unknown names resolve to null. Results are cached per name.
|
|
73
|
+
*/
|
|
74
|
+
export declare function getTypes(names: string[], options?: IntrospectionOptions): Promise<Map<string, TypeInfo | null>>;
|
|
75
|
+
/** `[Foo!]!` style rendering of a (possibly wrapped) type ref. */
|
|
76
|
+
export declare function renderTypeRef(ref: TypeRef | null | undefined): string;
|
|
77
|
+
/** The named type at the bottom of LIST/NON_NULL wrappers. */
|
|
78
|
+
export declare function namedType(ref: TypeRef | null | undefined): string | null;
|
|
79
|
+
export declare function isBuiltinScalar(name: string | null): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Compact SDL for one type. Returns the text and the named types it references
|
|
82
|
+
* (so callers can expand the next level).
|
|
83
|
+
*/
|
|
84
|
+
export declare function renderType(type: TypeInfo): {
|
|
85
|
+
text: string;
|
|
86
|
+
references: string[];
|
|
87
|
+
};
|
|
88
|
+
/** One-line signature of a root operation. */
|
|
89
|
+
export declare function renderOperationSignature(op: RootOperation): string;
|
|
90
|
+
export interface ExpandOptions extends IntrospectionOptions {
|
|
91
|
+
/** How many levels of referenced types to expand (1 = only the given types). */
|
|
92
|
+
depth?: number;
|
|
93
|
+
/** Safety cap on the number of types rendered in one call. */
|
|
94
|
+
maxTypes?: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Breadth-first expansion of `rootTypeNames`. Level 1 is the given names,
|
|
98
|
+
* level 2 the types they reference, and so on up to `depth`. Anything
|
|
99
|
+
* referenced but not expanded is returned as `unexpanded` for a follow-up.
|
|
100
|
+
*/
|
|
101
|
+
export declare function expandTypes(rootTypeNames: string[], options?: ExpandOptions): Promise<{
|
|
102
|
+
rendered: string[];
|
|
103
|
+
unexpanded: string[];
|
|
104
|
+
unknown: string[];
|
|
105
|
+
}>;
|
|
106
|
+
//# sourceMappingURL=introspection.d.ts.map
|