@salesforce/vite-plugin-lwc-ui-bundle 1.134.5 → 2.0.0
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 +46 -27
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/plugins/proxy.d.ts +1 -1
- package/dist/providers/index.d.ts +1 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +0 -3
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/lds/index.d.ts +72 -10
- package/dist/providers/lds/index.d.ts.map +1 -1
- package/dist/providers/lds/index.js +58 -12
- package/dist/providers/lds/index.js.map +1 -1
- package/dist/providers/lds/runtime.js +177 -17
- package/dist/providers/lds/runtime.js.map +1 -1
- package/docs/consumer-guide.md +44 -114
- package/package.json +9 -13
- package/skills/setup-lwc-vite-plugin/SKILL.md +168 -27
- package/skills/setup-lwc-vite-plugin/references/bootstrap-js-patterns.md +1 -1
- package/skills/setup-lwc-vite-plugin/references/known-pitfalls.md +153 -45
- package/dist/providers/lightning-graphql/index.d.ts +0 -10
- package/dist/providers/lightning-graphql/index.d.ts.map +0 -1
- package/dist/providers/lightning-graphql/index.js +0 -24
- package/dist/providers/lightning-graphql/index.js.map +0 -1
- package/dist/providers/lightning-graphql/runtime.js +0 -103
- package/dist/providers/lightning-graphql/runtime.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sources":["../../../src/providers/shared/normalize-mcp-response.ts","../../../src/providers/lds/runtime.ts"],"sourcesContent":["/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\n\n/**\n * Unwraps the MCP tool transport envelope and returns the tool's payload as-is.\n *\n * Handles the three surface shapes `sdk.callTool()` can resolve with:\n * - MCP Apps surface: `{ structuredContent, content }`\n * - OpenAI surface: `{ result: \"<JSON string>\" }`, where the JSON may itself be\n * an MCP content array (`[{ type: 'text', text: \"<JSON string>\" }, ...]`)\n * - Fallback: the raw value returned by `callTool`\n *\n * The shape of the unwrapped payload is the tool's responsibility — this helper\n * does not project out `data` / `error` / `errors`. Callers read whichever keys\n * their tool contract defines.\n */\nexport function normalizeMcpResponse(raw: unknown): unknown {\n\tif (raw && typeof raw === \"object\" && \"structuredContent\" in raw) {\n\t\treturn (raw as { structuredContent: unknown }).structuredContent;\n\t}\n\n\tif (raw && typeof raw === \"object\" && typeof (raw as { result?: unknown }).result === \"string\") {\n\t\tconst parsed = JSON.parse((raw as { result: string }).result);\n\t\tif (Array.isArray(parsed)) {\n\t\t\tconst textBlock = parsed.find(\n\t\t\t\t(b: { type?: string; text?: string }) =>\n\t\t\t\t\tb && b.type === \"text\" && typeof b.text === \"string\",\n\t\t\t);\n\t\t\tconst text = textBlock ? textBlock.text : null;\n\t\t\tif (text) {\n\t\t\t\treturn JSON.parse(text);\n\t\t\t}\n\t\t\treturn {};\n\t\t}\n\t\treturn parsed;\n\t}\n\n\treturn raw ?? {};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { ResultCommand, SubscribableResultCommand } from \"@conduit-client/bindings-utils/v1\";\nimport type { assertIsValid, JSONSchema } from \"@conduit-client/jsonschema-validate\";\nimport {\n\tbuildDefaultImperativeBindingsServiceDescriptor,\n\tbuildLegacyImperativeBindingsServiceDescriptor,\n\tbuildQueryImperativeBindingsServiceDescriptor,\n\tbuildSubscribableImperativeBindingsServiceDescriptor,\n} from \"@conduit-client/service-bindings-imperative/v1\";\nimport { buildLWCWireBindingsServiceDescriptor } from \"@conduit-client/service-bindings-lwc/v1\";\nimport {\n\tbuildSubscribableResult,\n\terr,\n\tok,\n\ttoError,\n\ttype Callback,\n\ttype Result,\n\ttype Unsubscribe,\n} from \"@conduit-client/utils\";\nimport { getChatSDK } from \"@salesforce/sdk-chat\";\nimport type { ReadInvokerShape } from \"./types\";\nimport { normalizeMcpResponse } from \"../shared/normalize-mcp-response\";\n\ninterface WireAdapterConfig {\n\ttoolName: string;\n\tconfigJsonSchema: JSONSchema;\n}\n\ninterface MutationAdapterConfig {\n\ttoolName: string;\n\tconfigJsonSchema: JSONSchema;\n}\n\ninterface ReadAdapterConfig {\n\tinvokerShape: ReadInvokerShape;\n\ttoolName: string;\n\tconfigJsonSchema: JSONSchema;\n}\n\n/**\n * Optional envelope splitter. Converts the normalized MCP payload into a\n * `Result<Data, Error>` — used by the wire adapter so tools that return\n * `{ data, error }` land on OneStore's Err branch when `error` is set.\n * Imperative callers leave this unset and get the raw payload wrapped in\n * `ok(...)`.\n */\ntype UnwrapEnvelope<Data> = (payload: unknown) => Result<Data, Error>;\n\n/**\n * Pulls a human-readable message out of an MCP error response\n * (`{ isError: true, content: [{ type: 'text', text: ... }, ...] }`).\n * Returns an empty string if no text content is present so the caller can\n * supply a fallback.\n */\nfunction extractToolErrorText(raw: unknown): string {\n\tconst content = (raw as { content?: unknown }).content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.map((c) => {\n\t\t\tif (c && typeof c === \"object\" && (c as { type?: string }).type === \"text\") {\n\t\t\t\treturn (c as { text?: string }).text ?? \"\";\n\t\t\t}\n\t\t\treturn \"\";\n\t\t})\n\t\t.join(\" \")\n\t\t.trim();\n}\n\n/**\n * `Command` implementation that calls the configured MCP tool. One class,\n * reused across imperative and wire adapters.\n *\n * `execute()` returns a `Result<Data, unknown>`:\n * - `ok(data)` when the tool resolves — `data` is the unwrapped payload from\n * `normalizeMcpResponse` (or the result of the optional `unwrap` hook).\n * - `err(error)` when the tool rejects, when `sdk.callTool` is unavailable,\n * or when the `unwrap` hook returns `err(...)`.\n *\n * OneStore's `DefaultImperativeBindingsService` wraps this: on `ok(v)` it\n * deep-freezes and returns `v`; on `err(e)` it re-throws via `toError`. Sync\n * throws from `assertIsValid` in the `getCommand` factory are funneled through\n * `throwUserlandError`. `LWCWireBindingsService` wraps the same command\n * differently — it emits `{ data, error }` to the wire callback and attaches\n * freeze / race-guard / incomplete-config behavior.\n */\nclass McpToolCommand<Data> implements ResultCommand<Data, Error> {\n\tconstructor(\n\t\tprivate readonly adapterName: string,\n\t\tprivate readonly toolName: string,\n\t\tprivate readonly params: unknown,\n\t\tprivate readonly unwrap?: UnwrapEnvelope<Data>,\n\t) {}\n\n\tasync execute(): Promise<Result<Data, Error>> {\n\t\ttry {\n\t\t\tconst app = await getChatSDK();\n\t\t\tif (!app.callTool) {\n\t\t\t\treturn err(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t`[${this.adapterName}] sdk.callTool is not available on this surface. ` +\n\t\t\t\t\t\t\t\"Make sure window.openai is configured or the component is running \" +\n\t\t\t\t\t\t\t\"in an MCP Apps / OpenAI chat context.\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst raw = await app.callTool({\n\t\t\t\ttoolName: this.toolName,\n\t\t\t\tparams: (this.params as Record<string, unknown>) ?? undefined,\n\t\t\t});\n\t\t\t// The MCP SDK converts handler throws into a resolved tool response\n\t\t\t// with `{ isError: true, content: [...] }` — not a transport\n\t\t\t// rejection. Surface it as an Err here so the default invoker\n\t\t\t// re-throws and the wire/legacy adapters route it through their\n\t\t\t// error channel.\n\t\t\tif (raw && typeof raw === \"object\" && (raw as { isError?: boolean }).isError) {\n\t\t\t\treturn err(new Error(extractToolErrorText(raw) || \"MCP tool error\"));\n\t\t\t}\n\t\t\tconst payload = normalizeMcpResponse(raw);\n\t\t\treturn this.unwrap ? this.unwrap(payload) : ok(payload as Data);\n\t\t} catch (e) {\n\t\t\treturn err(e instanceof Error ? e : new Error(String(e)));\n\t\t}\n\t}\n}\n\n// Off-platform has no store to observe. `subscribe` never fires the callback;\n// the returned unsubscribe is a safe no-op. Module-level so `refresh()`\n// doesn't re-allocate.\nconst noopUnsubscribe: Unsubscribe = () => {\n\t// no store to unsubscribe from off-platform\n};\nconst noopSubscribe = (_cb: Callback<Result<unknown, unknown>>): Unsubscribe => noopUnsubscribe;\n\n/**\n * Adapts `McpToolCommand` into a `SubscribableResultCommand`. Wraps the\n * command's `Result` with `buildSubscribableResult`, supplying:\n * - `subscribe`: no-op — off-platform has no store, so the callback never\n * fires. Returned unsubscribe is idempotent.\n * - `refresh`: builds a fresh `McpToolCommand` and re-executes it so each\n * refresh issues a new MCP tool call. Returns `Result<void, Error>`\n * (ok → undefined; err → the error from the fresh execute).\n *\n * This shim is what lets OneStore's Subscribable and Legacy services run\n * verbatim against MCP: their invokers gate on `isSubscribableResult`, which\n * requires the command's `execute()` to resolve a `SubscribableResult` (not\n * a plain `Result`). The deliberate \"subscribe that doesn't subscribe\"\n * preserves the OneStore API surface so ported code keeps its call sites.\n */\nclass McpToolSubscribableCommand<Data> implements SubscribableResultCommand<Data, Error> {\n\tconstructor(\n\t\tprivate readonly adapterName: string,\n\t\tprivate readonly toolName: string,\n\t\tprivate readonly params: unknown,\n\t\tprivate readonly unwrap?: UnwrapEnvelope<Data>,\n\t) {}\n\n\tasync execute() {\n\t\tconst base = new McpToolCommand<Data>(\n\t\t\tthis.adapterName,\n\t\t\tthis.toolName,\n\t\t\tthis.params,\n\t\t\tthis.unwrap,\n\t\t);\n\t\tconst result = await base.execute();\n\t\tconst refresh = async () => {\n\t\t\tconst next = await new McpToolCommand<Data>(\n\t\t\t\tthis.adapterName,\n\t\t\t\tthis.toolName,\n\t\t\t\tthis.params,\n\t\t\t\tthis.unwrap,\n\t\t\t).execute();\n\t\t\treturn next.isOk() ? ok<void, Error>(undefined) : err(next.error);\n\t\t};\n\t\treturn buildSubscribableResult(result, noopSubscribe, refresh);\n\t}\n}\n\nconst defaultImperativeService = buildDefaultImperativeBindingsServiceDescriptor().service;\nconst queryImperativeService = buildQueryImperativeBindingsServiceDescriptor().service;\nconst subscribableImperativeService =\n\tbuildSubscribableImperativeBindingsServiceDescriptor().service;\nconst legacyImperativeService = buildLegacyImperativeBindingsServiceDescriptor().service;\n\n/**\n * Mutation-shape factory. Builds an async `(config) => Promise<Data>` via\n * OneStore's `DefaultImperativeBindingsService`. Throws on validation error\n * (`throwUserlandError`) or tool error (`toError`). This is the only shape a\n * mutation ever takes on platform — mutation adapters do not carry an\n * `invokerShape` at all.\n */\nexport function createMutationAdapter(name: string, cfg: MutationAdapterConfig) {\n\tconst getCommand = (options: { params: unknown[]; assertIsValid: typeof assertIsValid }) => {\n\t\toptions.assertIsValid(options.params[0], cfg.configJsonSchema);\n\t\treturn new McpToolCommand<unknown>(name, cfg.toolName, options.params[0]);\n\t};\n\tconst invoker = defaultImperativeService.bind<unknown[], unknown>(getCommand);\n\treturn (...params: unknown[]) => Promise.resolve(invoker(...params));\n}\n\n/**\n * Read-shape factory. Dispatches on `cfg.invokerShape`:\n *\n * - `legacy` — `{ invoke, subscribe }` callback surface with `{ data, error }`.\n * `subscribe` fires the callback once from the initial execute's `data`,\n * returns a no-op unsubscribe, and never fires again off-platform.\n * - `query` — `(config) => Promise<{ data }>`; one-shot read.\n * - `subscribable` — `(config) => Promise<{ data, subscribe }>`. Callback\n * passed to `subscribe` never fires; unsubscribe is a no-op.\n * - `subscribable-refreshable` — `(config) => Promise<{ data, subscribe, refresh }>`.\n * `refresh()` re-executes the MCP tool; `subscribe` is still a no-op.\n *\n * All four delegate to an OneStore service (Query / Subscribable / Legacy)\n * over `McpToolSubscribableCommand`, which preserves OneStore's deep-freeze\n * and error-funnel semantics end-to-end.\n */\nexport function createReadAdapter(name: string, cfg: ReadAdapterConfig) {\n\tconst getSubscribableCommand = (options: {\n\t\tparams: unknown[];\n\t\tassertIsValid: typeof assertIsValid;\n\t}) => {\n\t\toptions.assertIsValid(options.params[0], cfg.configJsonSchema);\n\t\treturn new McpToolSubscribableCommand<unknown>(name, cfg.toolName, options.params[0]);\n\t};\n\n\tswitch (cfg.invokerShape) {\n\t\tcase \"query\": {\n\t\t\tconst getCommand = (options: { params: unknown[]; assertIsValid: typeof assertIsValid }) => {\n\t\t\t\toptions.assertIsValid(options.params[0], cfg.configJsonSchema);\n\t\t\t\treturn new McpToolCommand<unknown>(name, cfg.toolName, options.params[0]);\n\t\t\t};\n\t\t\tconst invoker = queryImperativeService.bind<unknown[], unknown>(getCommand);\n\t\t\treturn (...params: unknown[]) => Promise.resolve(invoker(...params));\n\t\t}\n\t\tcase \"subscribable\": {\n\t\t\tconst invoker = subscribableImperativeService.bind<unknown[], unknown>(\n\t\t\t\tgetSubscribableCommand,\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\treturn (...params: unknown[]) => Promise.resolve(invoker(...params));\n\t\t}\n\t\tcase \"subscribable-refreshable\": {\n\t\t\tconst invoker = subscribableImperativeService.bind<unknown[], unknown>(\n\t\t\t\tgetSubscribableCommand,\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\treturn (...params: unknown[]) => Promise.resolve(invoker(...params));\n\t\t}\n\t\tcase \"legacy\": {\n\t\t\t// Legacy service passes `{ config, assertIsValid }`, not `{ params, ... }`.\n\t\t\tconst getLegacyCommand = (options: {\n\t\t\t\tconfig: unknown;\n\t\t\t\tassertIsValid: typeof assertIsValid;\n\t\t\t}) => {\n\t\t\t\toptions.assertIsValid(options.config, cfg.configJsonSchema);\n\t\t\t\treturn new McpToolSubscribableCommand<unknown>(name, cfg.toolName, options.config);\n\t\t\t};\n\t\t\treturn legacyImperativeService.bind<unknown, unknown>(getLegacyCommand);\n\t\t}\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t`[${name}] unsupported invokerShape: ${String((cfg as { invokerShape: unknown }).invokerShape)}. ` +\n\t\t\t\t\t`Expected 'legacy' | 'query' | 'subscribable' | 'subscribable-refreshable'.`,\n\t\t\t);\n\t}\n}\n\nconst lwcWireBindingsService = buildLWCWireBindingsServiceDescriptor().service;\n\n/**\n * Splits the MCP tool's `{ data, error }` envelope into a `Result`. Used as\n * the `unwrap` hook on `McpToolCommand` so OneStore's wire invoker sees the\n * error branch directly instead of receiving an envelope wrapped in `ok(...)`.\n *\n * Tools that don't emit the envelope (e.g. imperative) never go through this\n * path — the default imperative invoker doesn't pass `unwrap`.\n */\nfunction unwrapWireEnvelope<Data>(payload: unknown): Result<Data, Error> {\n\tconst envelope = payload as { data?: unknown; error?: unknown } | null | undefined;\n\tif (envelope && envelope.error !== undefined && envelope.error !== null) {\n\t\treturn err(toError(envelope.error));\n\t}\n\treturn ok(envelope?.data as Data);\n}\n\n/**\n * Creates an LWC wire adapter class that calls an MCP tool. Delegates to\n * OneStore's `LWCWireBindingsService`, so the returned class inherits the\n * full `CommandWireAdapterConstructor` contract: initial empty emit, config\n * `sanitize()`, `MissingRequiredPropertyError` → wait-for-next-config gating,\n * deep-freeze on success, race guard on stale resolves, and unsubscriber\n * management on disconnect.\n *\n * The `McpToolCommand` class is reused verbatim — the wire call-site only\n * supplies an `unwrap` hook so the tool's `{ data, error }` envelope lands\n * on OneStore's Err branch rather than being leaked through as an `ok(...)`\n * envelope.\n *\n * @param name - Wire adapter export name, used in MCP error messages.\n * @param cfg - MCP tool name and JSON Schema for valid wire configs.\n */\nexport function createWireAdapter(name: string, cfg: WireAdapterConfig) {\n\treturn lwcWireBindingsService.bind<unknown>(\n\t\t(config) => new McpToolCommand(name, cfg.toolName, config, unwrapWireEnvelope),\n\t\tcfg.configJsonSchema,\n\t);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime.js","sources":["../../../src/providers/shared/normalize-mcp-response.ts","../../../src/providers/lds/runtime.ts"],"sourcesContent":["/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\n\n/**\n * Unwraps the MCP tool transport envelope and returns the tool's payload as-is.\n *\n * Handles the three surface shapes `sdk.callTool()` can resolve with:\n * - MCP Apps surface: `{ structuredContent, content }`\n * - OpenAI surface: `{ result: \"<JSON string>\" }`, where the JSON may itself be\n * an MCP content array (`[{ type: 'text', text: \"<JSON string>\" }, ...]`)\n * - Fallback: the raw value returned by `callTool`\n *\n * The shape of the unwrapped payload is the tool's responsibility — this helper\n * does not project out `data` / `error` / `errors`. Callers read whichever keys\n * their tool contract defines.\n */\nexport function normalizeMcpResponse(raw: unknown): unknown {\n\tif (raw && typeof raw === \"object\" && \"structuredContent\" in raw) {\n\t\treturn (raw as { structuredContent: unknown }).structuredContent;\n\t}\n\n\tif (raw && typeof raw === \"object\" && typeof (raw as { result?: unknown }).result === \"string\") {\n\t\tconst parsed = JSON.parse((raw as { result: string }).result);\n\t\tif (Array.isArray(parsed)) {\n\t\t\tconst textBlock = parsed.find(\n\t\t\t\t(b: { type?: string; text?: string }) =>\n\t\t\t\t\tb && b.type === \"text\" && typeof b.text === \"string\",\n\t\t\t);\n\t\t\tconst text = textBlock ? textBlock.text : null;\n\t\t\tif (text) {\n\t\t\t\treturn JSON.parse(text);\n\t\t\t}\n\t\t\treturn {};\n\t\t}\n\t\treturn parsed;\n\t}\n\n\treturn raw ?? {};\n}\n","/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nimport type { ResultCommand, SubscribableResultCommand } from \"@conduit-client/bindings-utils/v1\";\nimport type { assertIsValid, JSONSchema } from \"@conduit-client/jsonschema-validate\";\nimport {\n\tbuildDefaultImperativeBindingsServiceDescriptor,\n\tbuildLegacyImperativeBindingsServiceDescriptor,\n\tbuildQueryImperativeBindingsServiceDescriptor,\n\tbuildSubscribableImperativeBindingsServiceDescriptor,\n} from \"@conduit-client/service-bindings-imperative/v1\";\nimport { buildLWCWireBindingsServiceDescriptor } from \"@conduit-client/service-bindings-lwc/v1\";\nimport {\n\tbuildSubscribableResult,\n\terr,\n\tok,\n\ttoError,\n\ttype Callback,\n\ttype Result,\n\ttype Unsubscribe,\n} from \"@conduit-client/utils\";\nimport { getChatSDK } from \"@salesforce/platform-sdk-chat\";\nimport type { ReadInvokerShape } from \"./types\";\nimport { normalizeMcpResponse } from \"../shared/normalize-mcp-response\";\n\n/**\n * Runtime mirror of the public `LdsAdapterConfigBase` — adapters dispatch\n * through `cfg.mcp?.toolName`. Kept nominal-free so the virtual module can\n * cheaply deserialize the JSON-stringified config written by the load hook.\n */\ninterface McpBackedAdapterConfig {\n\tmcp?: { toolName: string };\n}\n\n/**\n * LDS adapter configs whose input is validated against a JSON Schema at\n * invoke time. Wire, mutation, and read factories all consume this shape;\n * graphql factories skip the schema layer entirely.\n */\ninterface SchemaValidatedAdapterConfig extends McpBackedAdapterConfig {\n\tconfigJsonSchema: JSONSchema;\n}\n\ntype WireAdapterConfig = SchemaValidatedAdapterConfig;\n\ntype MutationAdapterConfig = SchemaValidatedAdapterConfig;\n\ninterface ReadAdapterConfig extends SchemaValidatedAdapterConfig {\n\tinvokerShape: ReadInvokerShape;\n}\n\n/**\n * Resolves the MCP tool name for an adapter config, or throws if none of the\n * supported backings are set. Today only `mcp` is wired up; future backings\n * (e.g. `http`) will be resolved in the same choke point.\n */\nfunction resolveMcpToolName(adapterName: string, cfg: McpBackedAdapterConfig): string {\n\tconst toolName = cfg.mcp?.toolName;\n\tif (!toolName) {\n\t\tthrow new Error(`[${adapterName}] no dispatch backing configured — expected \\`mcp.toolName\\`.`);\n\t}\n\treturn toolName;\n}\n\n/**\n * Optional envelope splitter. Converts the normalized MCP payload into a\n * `Result<Data, Error>` — used by the wire adapter so tools that return\n * `{ data, error }` land on OneStore's Err branch when `error` is set.\n * Imperative callers leave this unset and get the raw payload wrapped in\n * `ok(...)`.\n */\ntype UnwrapEnvelope<Data> = (payload: unknown) => Result<Data, Error>;\n\n/**\n * Resolves the MCP chat SDK and returns its `callTool` function, or throws a\n * consistent \"[adapter] sdk.callTool is not available\" error. Shared by the\n * LDS `McpToolCommand` and the graphql `runGraphqlQuery` — the check and\n * message are identical on both paths.\n */\nasync function getCallTool(\n\tadapterName: string,\n): Promise<(args: { toolName: string; params?: Record<string, unknown> }) => Promise<unknown>> {\n\tconst sdk = await getChatSDK();\n\tif (typeof sdk.callTool !== \"function\") {\n\t\tthrow new Error(\n\t\t\t`[${adapterName}] sdk.callTool is not available on this surface. ` +\n\t\t\t\t\"Make sure window.openai is configured or the component is running \" +\n\t\t\t\t\"in an MCP Apps / OpenAI chat context.\",\n\t\t);\n\t}\n\treturn sdk.callTool.bind(sdk);\n}\n\n/**\n * Pulls a human-readable message out of an MCP error response\n * (`{ isError: true, content: [{ type: 'text', text: ... }, ...] }`).\n * Returns an empty string if no text content is present so the caller can\n * supply a fallback.\n */\nfunction extractToolErrorText(raw: unknown): string {\n\tconst content = (raw as { content?: unknown }).content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.map((c) => {\n\t\t\tif (c && typeof c === \"object\" && (c as { type?: string }).type === \"text\") {\n\t\t\t\treturn (c as { text?: string }).text ?? \"\";\n\t\t\t}\n\t\t\treturn \"\";\n\t\t})\n\t\t.join(\" \")\n\t\t.trim();\n}\n\n/**\n * `Command` implementation that calls the configured MCP tool. One class,\n * reused across imperative and wire adapters.\n *\n * `execute()` returns a `Result<Data, unknown>`:\n * - `ok(data)` when the tool resolves — `data` is the unwrapped payload from\n * `normalizeMcpResponse` (or the result of the optional `unwrap` hook).\n * - `err(error)` when the tool rejects, when `sdk.callTool` is unavailable,\n * or when the `unwrap` hook returns `err(...)`.\n *\n * OneStore's `DefaultImperativeBindingsService` wraps this: on `ok(v)` it\n * deep-freezes and returns `v`; on `err(e)` it re-throws via `toError`. Sync\n * throws from `assertIsValid` in the `getCommand` factory are funneled through\n * `throwUserlandError`. `LWCWireBindingsService` wraps the same command\n * differently — it emits `{ data, error }` to the wire callback and attaches\n * freeze / race-guard / incomplete-config behavior.\n */\nclass McpToolCommand<Data> implements ResultCommand<Data, Error> {\n\tconstructor(\n\t\tprivate readonly adapterName: string,\n\t\tprivate readonly toolName: string,\n\t\tprivate readonly params: unknown,\n\t\tprivate readonly unwrap?: UnwrapEnvelope<Data>,\n\t) {}\n\n\tasync execute(): Promise<Result<Data, Error>> {\n\t\ttry {\n\t\t\tconst callTool = await getCallTool(this.adapterName);\n\t\t\tconst raw = await callTool({\n\t\t\t\ttoolName: this.toolName,\n\t\t\t\tparams: (this.params as Record<string, unknown>) ?? undefined,\n\t\t\t});\n\t\t\t// The MCP SDK converts handler throws into a resolved tool response\n\t\t\t// with `{ isError: true, content: [...] }` — not a transport\n\t\t\t// rejection. Surface it as an Err here so the default invoker\n\t\t\t// re-throws and the wire/legacy adapters route it through their\n\t\t\t// error channel.\n\t\t\tif (raw && typeof raw === \"object\" && (raw as { isError?: boolean }).isError) {\n\t\t\t\treturn err(new Error(extractToolErrorText(raw) || \"MCP tool error\"));\n\t\t\t}\n\t\t\tconst payload = normalizeMcpResponse(raw);\n\t\t\treturn this.unwrap ? this.unwrap(payload) : ok(payload as Data);\n\t\t} catch (e) {\n\t\t\treturn err(e instanceof Error ? e : new Error(String(e)));\n\t\t}\n\t}\n}\n\n// Off-platform has no store to observe. For shapes that have no `refresh`,\n// nothing can ever trigger a post-initial update, so `subscribe` is a\n// deliberate no-op: the callback never fires and the returned unsubscribe is\n// idempotent. Shapes that *do* expose `refresh` wire subscribers into the\n// refresh path instead (see `McpToolSubscribableCommand` and the\n// `query-refreshable` graphql shape).\nconst noopUnsubscribe: Unsubscribe = () => {\n\t// no store to unsubscribe from off-platform\n};\nconst noopSubscribe = (_cb: unknown): Unsubscribe => noopUnsubscribe;\n\n/**\n * Adapts `McpToolCommand` into a `SubscribableResultCommand`. Wraps the\n * command's `Result` with `buildSubscribableResult`, supplying:\n * - `subscribe`: per-execution subscriber set. Each `execute()` call has\n * its own set so sibling invocations don't cross-pollinate. Returned\n * unsubscribe removes the callback and is idempotent.\n * - `refresh`: builds a fresh `McpToolCommand`, re-executes it, and\n * broadcasts the fresh `Result` to every registered subscriber before\n * resolving. Returns `Result<void, Error>` (ok → undefined; err → the\n * error from the fresh execute).\n *\n * Wiring subscribers into refresh mirrors on-platform behavior, where the\n * reactive store fans `refresh` results out to every listener. Off-platform\n * there is no store, so the subscriber set lives on the command itself. This\n * shim is what lets OneStore's Subscribable and Legacy services run verbatim\n * against MCP: their invokers gate on `isSubscribableResult`, which requires\n * the command's `execute()` to resolve a `SubscribableResult` (not a plain\n * `Result`).\n */\nclass McpToolSubscribableCommand<Data> implements SubscribableResultCommand<Data, Error> {\n\tconstructor(\n\t\tprivate readonly adapterName: string,\n\t\tprivate readonly toolName: string,\n\t\tprivate readonly params: unknown,\n\t\tprivate readonly unwrap?: UnwrapEnvelope<Data>,\n\t) {}\n\n\tasync execute() {\n\t\tconst base = new McpToolCommand<Data>(\n\t\t\tthis.adapterName,\n\t\t\tthis.toolName,\n\t\t\tthis.params,\n\t\t\tthis.unwrap,\n\t\t);\n\t\tconst result = await base.execute();\n\t\tconst subscribers = new Set<Callback<Result<Data, Error>>>();\n\t\tconst subscribe = (cb: Callback<Result<Data, Error>>): Unsubscribe => {\n\t\t\tsubscribers.add(cb);\n\t\t\treturn () => {\n\t\t\t\tsubscribers.delete(cb);\n\t\t\t};\n\t\t};\n\t\tconst refresh = async () => {\n\t\t\tconst next = await new McpToolCommand<Data>(\n\t\t\t\tthis.adapterName,\n\t\t\t\tthis.toolName,\n\t\t\t\tthis.params,\n\t\t\t\tthis.unwrap,\n\t\t\t).execute();\n\t\t\tsubscribers.forEach((cb) => cb(next));\n\t\t\treturn next.isOk() ? ok<void, Error>(undefined) : err(next.error);\n\t\t};\n\t\treturn buildSubscribableResult(result, subscribe, refresh);\n\t}\n}\n\nconst defaultImperativeService = buildDefaultImperativeBindingsServiceDescriptor().service;\nconst queryImperativeService = buildQueryImperativeBindingsServiceDescriptor().service;\nconst subscribableImperativeService =\n\tbuildSubscribableImperativeBindingsServiceDescriptor().service;\nconst legacyImperativeService = buildLegacyImperativeBindingsServiceDescriptor().service;\n\n/**\n * Mutation-shape factory. Builds an async `(config) => Promise<Data>` via\n * OneStore's `DefaultImperativeBindingsService`. Throws on validation error\n * (`throwUserlandError`) or tool error (`toError`). This is the only shape a\n * mutation ever takes on platform — mutation adapters do not carry an\n * `invokerShape` at all.\n */\nexport function createMutationAdapter(name: string, cfg: MutationAdapterConfig) {\n\tconst toolName = resolveMcpToolName(name, cfg);\n\tconst getCommand = (options: { params: unknown[]; assertIsValid: typeof assertIsValid }) => {\n\t\toptions.assertIsValid(options.params[0], cfg.configJsonSchema);\n\t\treturn new McpToolCommand<unknown>(name, toolName, options.params[0]);\n\t};\n\tconst invoker = defaultImperativeService.bind<unknown[], unknown>(getCommand);\n\treturn (...params: unknown[]) => Promise.resolve(invoker(...params));\n}\n\n/**\n * Read-shape factory. Dispatches on `cfg.invokerShape`:\n *\n * - `legacy` — `{ invoke, subscribe }` callback surface with `{ data, error }`.\n * `subscribe` fires the callback once from the initial execute's `data`,\n * returns a no-op unsubscribe, and never fires again off-platform.\n * - `query` — `(config) => Promise<{ data }>`; one-shot read.\n * - `subscribable` — `(config) => Promise<{ data, subscribe }>`. Callback\n * passed to `subscribe` never fires; unsubscribe is a no-op.\n * - `subscribable-refreshable` — `(config) => Promise<{ data, subscribe, refresh }>`.\n * `refresh()` re-executes the MCP tool; `subscribe` is still a no-op.\n *\n * All four delegate to an OneStore service (Query / Subscribable / Legacy)\n * over `McpToolSubscribableCommand`, which preserves OneStore's deep-freeze\n * and error-funnel semantics end-to-end.\n */\nexport function createReadAdapter(name: string, cfg: ReadAdapterConfig) {\n\tconst toolName = resolveMcpToolName(name, cfg);\n\tconst getSubscribableCommand = (options: {\n\t\tparams: unknown[];\n\t\tassertIsValid: typeof assertIsValid;\n\t}) => {\n\t\toptions.assertIsValid(options.params[0], cfg.configJsonSchema);\n\t\treturn new McpToolSubscribableCommand<unknown>(name, toolName, options.params[0]);\n\t};\n\n\tswitch (cfg.invokerShape) {\n\t\tcase \"query\": {\n\t\t\tconst getCommand = (options: { params: unknown[]; assertIsValid: typeof assertIsValid }) => {\n\t\t\t\toptions.assertIsValid(options.params[0], cfg.configJsonSchema);\n\t\t\t\treturn new McpToolCommand<unknown>(name, toolName, options.params[0]);\n\t\t\t};\n\t\t\tconst invoker = queryImperativeService.bind<unknown[], unknown>(getCommand);\n\t\t\treturn (...params: unknown[]) => Promise.resolve(invoker(...params));\n\t\t}\n\t\tcase \"subscribable\": {\n\t\t\tconst invoker = subscribableImperativeService.bind<unknown[], unknown>(\n\t\t\t\tgetSubscribableCommand,\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\treturn (...params: unknown[]) => Promise.resolve(invoker(...params));\n\t\t}\n\t\tcase \"subscribable-refreshable\": {\n\t\t\tconst invoker = subscribableImperativeService.bind<unknown[], unknown>(\n\t\t\t\tgetSubscribableCommand,\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\treturn (...params: unknown[]) => Promise.resolve(invoker(...params));\n\t\t}\n\t\tcase \"legacy\": {\n\t\t\t// Legacy service passes `{ config, assertIsValid }`, not `{ params, ... }`.\n\t\t\tconst getLegacyCommand = (options: {\n\t\t\t\tconfig: unknown;\n\t\t\t\tassertIsValid: typeof assertIsValid;\n\t\t\t}) => {\n\t\t\t\toptions.assertIsValid(options.config, cfg.configJsonSchema);\n\t\t\t\treturn new McpToolSubscribableCommand<unknown>(name, toolName, options.config);\n\t\t\t};\n\t\t\treturn legacyImperativeService.bind<unknown, unknown>(getLegacyCommand);\n\t\t}\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t`[${name}] unsupported invokerShape: ${String((cfg as { invokerShape: unknown }).invokerShape)}. ` +\n\t\t\t\t\t`Expected 'legacy' | 'query' | 'subscribable' | 'subscribable-refreshable'.`,\n\t\t\t);\n\t}\n}\n\nconst lwcWireBindingsService = buildLWCWireBindingsServiceDescriptor().service;\n\n/**\n * Splits the MCP tool's `{ data, error }` envelope into a `Result`. Used as\n * the `unwrap` hook on `McpToolCommand` so OneStore's wire invoker sees the\n * error branch directly instead of receiving an envelope wrapped in `ok(...)`.\n *\n * Tools that don't emit the envelope (e.g. imperative) never go through this\n * path — the default imperative invoker doesn't pass `unwrap`.\n */\nfunction unwrapWireEnvelope<Data>(payload: unknown): Result<Data, Error> {\n\tconst envelope = payload as { data?: unknown; error?: unknown } | null | undefined;\n\tif (envelope && envelope.error !== undefined && envelope.error !== null) {\n\t\treturn err(toError(envelope.error));\n\t}\n\treturn ok(envelope?.data as Data);\n}\n\n/**\n * Creates an LWC wire adapter class that calls an MCP tool. Delegates to\n * OneStore's `LWCWireBindingsService`, so the returned class inherits the\n * full `CommandWireAdapterConstructor` contract: initial empty emit, config\n * `sanitize()`, `MissingRequiredPropertyError` → wait-for-next-config gating,\n * deep-freeze on success, race guard on stale resolves, and unsubscriber\n * management on disconnect.\n *\n * The `McpToolCommand` class is reused verbatim — the wire call-site only\n * supplies an `unwrap` hook so the tool's `{ data, error }` envelope lands\n * on OneStore's Err branch rather than being leaked through as an `ok(...)`\n * envelope.\n *\n * @param name - Wire adapter export name, used in MCP error messages.\n * @param cfg - MCP tool name and JSON Schema for valid wire configs.\n */\nexport function createWireAdapter(name: string, cfg: WireAdapterConfig) {\n\tconst toolName = resolveMcpToolName(name, cfg);\n\treturn lwcWireBindingsService.bind<unknown>(\n\t\t(config) => new McpToolCommand(name, toolName, config, unwrapWireEnvelope),\n\t\tcfg.configJsonSchema,\n\t);\n}\n\n// ─── GraphQL adapters ─────────────────────────────────────────────────────────\n\n/**\n * GraphQL adapters diverge from the base wire / mutation shapes in two ways:\n *\n * 1. Envelope is `{ data, errors[] }` instead of `{ data, error }`. Errors\n * are collected; an empty array collapses to `undefined`.\n * 2. The `_fetch` path routes thrown errors **into** the `errors[]`\n * envelope on the wire callback (never throws to the LWC host). Mutation\n * likewise resolves with `{ data, errors }` rather than rejecting.\n *\n * Dispatch is MCP-only: `getChatSDK().callTool({ toolName, params })` with\n * `normalizeMcpResponse` unwrapping. No `globalThis.__sfdc_sdk__` shortcut —\n * the on-platform behaviour is already covered by real `lightning/graphql`,\n * and every off-platform caller routes through an MCP tool like every other\n * LDS adapter.\n */\ntype GraphqlAdapterConfig = McpBackedAdapterConfig;\n\ninterface GraphqlConfig {\n\tquery?: string;\n\tvariables?: Record<string, unknown>;\n}\n\ninterface GraphqlResult {\n\tdata?: unknown;\n\terrors?: { message: string }[];\n}\n\ntype GraphqlWireCallback = (result: {\n\tdata: unknown;\n\terrors: { message: string }[] | undefined;\n\trefresh: () => Promise<void>;\n}) => void;\n\n/**\n * Template-literal tag that stitches the string into a plain `query` string.\n * Deliberately minimal — no AST parsing, no fragment substitution, no opaque\n * doc emulation. Matches the shape used by today's off-platform `gql`.\n */\nexport function gql(strings: TemplateStringsArray, ...values: unknown[]): string {\n\tlet result = \"\";\n\tstrings.forEach((string, i) => {\n\t\tresult += string;\n\t\tif (i < values.length) result += String(values[i]);\n\t});\n\treturn result.trim();\n}\n\nasync function runGraphqlQuery(\n\tadapterName: string,\n\ttoolName: string,\n\tconfig: GraphqlConfig,\n): Promise<GraphqlResult> {\n\tconst { query, variables } = config;\n\tif (!query) return { data: undefined, errors: undefined };\n\n\tconst callTool = await getCallTool(adapterName);\n\tconst raw = await callTool({\n\t\ttoolName,\n\t\tparams: { query, variables: variables ?? {} },\n\t});\n\n\treturn (normalizeMcpResponse(raw) as GraphqlResult) ?? {};\n}\n\n/** Wraps a thrown Error into the graphql `{ data, errors[] }` envelope. */\nfunction toGraphqlErrorResult(error: unknown): GraphqlResult {\n\treturn { data: undefined, errors: [{ message: (error as Error).message }] };\n}\n\n/**\n * `runGraphqlQuery` wrapped in try/catch so thrown errors land on `errors[]`\n * instead of rejecting. Used by every graphql shape that routes errors\n * in-band (wire, mutation, imperative-read query/query-refreshable, legacy).\n */\nasync function runGraphqlQuerySafe(\n\tadapterName: string,\n\ttoolName: string,\n\tconfig: GraphqlConfig,\n): Promise<GraphqlResult> {\n\ttry {\n\t\treturn await runGraphqlQuery(adapterName, toolName, config);\n\t} catch (error) {\n\t\treturn toGraphqlErrorResult(error);\n\t}\n}\n\n/**\n * GraphQL wire adapter factory. Emits `{ data, errors, refresh }` to the wire\n * callback. Mirrors the `constructor(callback)` → `connect` / `disconnect` /\n * `update(config)` / `refresh()` contract used by the LWC wire service.\n * Thrown fetch errors are routed into `errors[0].message`.\n *\n * `_fetch` is gated on `_connected` so that the common wire lifecycle\n * (`constructor` → `update(config)` → `connect`) doesn't fire two requests:\n * the initial `update(config)` arrives before `connect()`, stores the config,\n * and waits. `connect()` does the one fetch; later `update(config)` while\n * connected re-fetches. This matches luvio's `LWCLuvioWireAdapter`, which\n * gates on the same flag to prevent duplicate endpoint calls.\n */\nexport function createGraphQLWireAdapter(name: string, cfg: GraphqlAdapterConfig) {\n\tconst toolName = resolveMcpToolName(name, cfg);\n\treturn class {\n\t\t_dataCallback: GraphqlWireCallback;\n\t\t_config: GraphqlConfig | undefined;\n\t\t_connected = false;\n\n\t\tconstructor(dataCallback: GraphqlWireCallback) {\n\t\t\tthis._dataCallback = dataCallback;\n\t\t}\n\n\t\tconnect() {\n\t\t\tthis._connected = true;\n\t\t\tvoid this._fetch();\n\t\t}\n\n\t\tdisconnect() {\n\t\t\tthis._connected = false;\n\t\t}\n\n\t\tupdate(config: GraphqlConfig | undefined) {\n\t\t\tthis._config = config;\n\n\t\t\tif (!this._connected || this._config === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvoid this._fetch();\n\t\t}\n\n\t\trefresh() {\n\t\t\treturn this._fetch();\n\t\t}\n\n\t\tasync _fetch() {\n\t\t\tthis._emit(await runGraphqlQuerySafe(name, toolName, this._config ?? {}));\n\t\t}\n\n\t\t_emit({ data, errors }: GraphqlResult) {\n\t\t\tthis._dataCallback({\n\t\t\t\tdata,\n\t\t\t\terrors: errors?.length ? errors : undefined,\n\t\t\t\trefresh: () => this.refresh(),\n\t\t\t});\n\t\t}\n\t};\n}\n\n/**\n * GraphQL mutation factory. Returns `async (config) => Promise<{ data, errors }>`.\n * Empty-query guard short-circuits with\n * `{ data: undefined, errors: [{ message: \"No query provided\" }] }`.\n * All other errors are routed in-band to the `errors[]` envelope — the\n * function never rejects.\n */\nexport function createGraphQLMutationAdapter(name: string, cfg: GraphqlAdapterConfig) {\n\tconst toolName = resolveMcpToolName(name, cfg);\n\treturn async (config: GraphqlConfig): Promise<GraphqlResult> => {\n\t\tif (!config?.query) return { data: undefined, errors: [{ message: \"No query provided\" }] };\n\t\treturn runGraphqlQuerySafe(name, toolName, config);\n\t};\n}\n\ntype GraphqlImperativeReadInvokerShape = \"query\" | \"query-refreshable\" | \"legacy\";\n\ninterface GraphqlImperativeReadAdapterConfig extends GraphqlAdapterConfig {\n\tinvokerShape: GraphqlImperativeReadInvokerShape;\n}\n\ntype GraphqlImperativeResult = GraphqlResult & {\n\tsubscribe?: (cb: (result: GraphqlResult) => void) => Unsubscribe;\n\trefresh?: () => Promise<void>;\n};\n\ntype LegacyGraphqlCallback = (result: GraphqlResult) => void;\n\n/**\n * Imperative GraphQL read factory — single entry point for all imperative\n * graphql read shapes, mirroring the LDS `createReadAdapter` pattern. The\n * `invokerShape` selects the return surface:\n *\n * - `query` — `async (config) => Promise<{ data, errors, subscribe }>`.\n * Mirrors on-platform `GraphQLImperativeBindingsService` without\n * `exposeRefresh`. Errors are routed in-band; the function never throws.\n * - `query-refreshable` — same as `query` plus `refresh()` that re-runs the\n * underlying MCP tool. Mirrors the same service with `exposeRefresh: true`.\n * - `legacy` — `{ invoke(config, context, callback),\n * subscribe(config, context, callback): Unsubscribe }`. The callback\n * always receives `{ data, errors }`; neither method throws. `context` is\n * accepted but ignored.\n *\n * In every shape `subscribe` is a deliberate no-op off-platform (no reactive\n * store); returned unsubscribes are idempotent.\n */\nexport function createGraphQLImperativeReadAdapter(\n\tname: string,\n\tcfg: GraphqlImperativeReadAdapterConfig,\n) {\n\tconst toolName = resolveMcpToolName(name, cfg);\n\tif (cfg.invokerShape === \"legacy\") {\n\t\tasync function run(\n\t\t\tconfig: GraphqlConfig | undefined,\n\t\t\tcallback: LegacyGraphqlCallback,\n\t\t): Promise<void> {\n\t\t\tif (!config?.query) {\n\t\t\t\tcallback({ data: undefined, errors: undefined });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst result = await runGraphqlQuerySafe(name, toolName, config);\n\t\t\tcallback({\n\t\t\t\tdata: result.data,\n\t\t\t\terrors: result.errors?.length ? result.errors : undefined,\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tinvoke(\n\t\t\t\tconfig: GraphqlConfig | undefined,\n\t\t\t\t_context: unknown,\n\t\t\t\tcallback: LegacyGraphqlCallback,\n\t\t\t): void {\n\t\t\t\tvoid run(config, callback);\n\t\t\t},\n\t\t\tsubscribe(\n\t\t\t\tconfig: GraphqlConfig | undefined,\n\t\t\t\t_context: unknown,\n\t\t\t\tcallback: LegacyGraphqlCallback,\n\t\t\t): Unsubscribe {\n\t\t\t\tvoid run(config, callback);\n\t\t\t\treturn noopUnsubscribe;\n\t\t\t},\n\t\t};\n\t}\n\n\tif (cfg.invokerShape === \"query-refreshable\") {\n\t\t// Per-invocation subscriber set: each call to the returned function\n\t\t// gets its own adapter instance with an isolated subscriber list.\n\t\t// `refresh()` re-runs the MCP tool and broadcasts the fresh\n\t\t// `{ data, errors }` to every registered callback, which mirrors the\n\t\t// on-platform contract where `refresh` causes store updates that fan\n\t\t// out through the `subscribe` fan-in. Without this wiring, `refresh`\n\t\t// would re-run the query off-platform but nothing could observe the\n\t\t// new payload (no store, noop subscribe).\n\t\treturn async (config: GraphqlConfig): Promise<GraphqlImperativeResult> => {\n\t\t\tconst result = await runGraphqlQuerySafe(name, toolName, config);\n\t\t\tif (result.errors?.length) {\n\t\t\t\treturn { data: undefined, errors: result.errors };\n\t\t\t}\n\t\t\tconst subscribers = new Set<(result: GraphqlResult) => void>();\n\t\t\treturn {\n\t\t\t\tdata: result.data,\n\t\t\t\terrors: undefined,\n\t\t\t\tsubscribe: (cb) => {\n\t\t\t\t\tsubscribers.add(cb);\n\t\t\t\t\treturn () => {\n\t\t\t\t\t\tsubscribers.delete(cb);\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t\trefresh: async () => {\n\t\t\t\t\tconst fresh = await runGraphqlQuerySafe(name, toolName, config);\n\t\t\t\t\tconst payload: GraphqlResult = {\n\t\t\t\t\t\tdata: fresh.data,\n\t\t\t\t\t\terrors: fresh.errors?.length ? fresh.errors : undefined,\n\t\t\t\t\t};\n\t\t\t\t\tsubscribers.forEach((cb) => cb(payload));\n\t\t\t\t},\n\t\t\t};\n\t\t};\n\t}\n\n\t// `query` shape: no refresh → nothing can trigger a post-initial update\n\t// off-platform, so `subscribe` stays a noop. The on-platform contract\n\t// allows subscribe callbacks to fire from unrelated store writes; off-\n\t// platform there is no store, so the callback is correctly unreachable.\n\treturn async (config: GraphqlConfig): Promise<GraphqlImperativeResult> => {\n\t\tconst result = await runGraphqlQuerySafe(name, toolName, config);\n\t\tif (result.errors?.length) {\n\t\t\treturn { data: undefined, errors: result.errors };\n\t\t}\n\t\treturn {\n\t\t\tdata: result.data,\n\t\t\terrors: undefined,\n\t\t\tsubscribe: noopSubscribe,\n\t\t};\n\t};\n}\n"],"names":[],"mappings":";;;;AAmBO,SAAS,qBAAqB,KAAuB;AAC3D,MAAI,OAAO,OAAO,QAAQ,YAAY,uBAAuB,KAAK;AACjE,WAAQ,IAAuC;AAAA,EAChD;AAEA,MAAI,OAAO,OAAO,QAAQ,YAAY,OAAQ,IAA6B,WAAW,UAAU;AAC/F,UAAM,SAAS,KAAK,MAAO,IAA2B,MAAM;AAC5D,QAAI,MAAM,QAAQ,MAAM,GAAG;AAC1B,YAAM,YAAY,OAAO;AAAA,QACxB,CAAC,MACA,KAAK,EAAE,SAAS,UAAU,OAAO,EAAE,SAAS;AAAA,MAAA;AAE9C,YAAM,OAAO,YAAY,UAAU,OAAO;AAC1C,UAAI,MAAM;AACT,eAAO,KAAK,MAAM,IAAI;AAAA,MACvB;AACA,aAAO,CAAA;AAAA,IACR;AACA,WAAO;AAAA,EACR;AAEA,SAAO,OAAO,CAAA;AACf;ACiBA,SAAS,mBAAmB,aAAqB,KAAqC;AACrF,QAAM,WAAW,IAAI,KAAK;AAC1B,MAAI,CAAC,UAAU;AACd,UAAM,IAAI,MAAM,IAAI,WAAW,+DAA+D;AAAA,EAC/F;AACA,SAAO;AACR;AAiBA,eAAe,YACd,aAC8F;AAC9F,QAAM,MAAM,MAAM,WAAA;AAClB,MAAI,OAAO,IAAI,aAAa,YAAY;AACvC,UAAM,IAAI;AAAA,MACT,IAAI,WAAW;AAAA,IAAA;AAAA,EAIjB;AACA,SAAO,IAAI,SAAS,KAAK,GAAG;AAC7B;AAQA,SAAS,qBAAqB,KAAsB;AACnD,QAAM,UAAW,IAA8B;AAC/C,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,SAAO,QACL,IAAI,CAAC,MAAM;AACX,QAAI,KAAK,OAAO,MAAM,YAAa,EAAwB,SAAS,QAAQ;AAC3E,aAAQ,EAAwB,QAAQ;AAAA,IACzC;AACA,WAAO;AAAA,EACR,CAAC,EACA,KAAK,GAAG,EACR,KAAA;AACH;AAmBA,MAAM,eAA2D;AAAA,EAChE,YACkB,aACA,UACA,QACA,QAChB;AAJgB,SAAA,cAAA;AACA,SAAA,WAAA;AACA,SAAA,SAAA;AACA,SAAA,SAAA;AAAA,EACf;AAAA,EAEH,MAAM,UAAwC;AAC7C,QAAI;AACH,YAAM,WAAW,MAAM,YAAY,KAAK,WAAW;AACnD,YAAM,MAAM,MAAM,SAAS;AAAA,QAC1B,UAAU,KAAK;AAAA,QACf,QAAS,KAAK,UAAsC;AAAA,MAAA,CACpD;AAMD,UAAI,OAAO,OAAO,QAAQ,YAAa,IAA8B,SAAS;AAC7E,eAAO,IAAI,IAAI,MAAM,qBAAqB,GAAG,KAAK,gBAAgB,CAAC;AAAA,MACpE;AACA,YAAM,UAAU,qBAAqB,GAAG;AACxC,aAAO,KAAK,SAAS,KAAK,OAAO,OAAO,IAAI,GAAG,OAAe;AAAA,IAC/D,SAAS,GAAG;AACX,aAAO,IAAI,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,IACzD;AAAA,EACD;AACD;AAQA,MAAM,kBAA+B,MAAM;AAE3C;AACA,MAAM,gBAAgB,CAAC,QAA8B;AAqBrD,MAAM,2BAAmF;AAAA,EACxF,YACkB,aACA,UACA,QACA,QAChB;AAJgB,SAAA,cAAA;AACA,SAAA,WAAA;AACA,SAAA,SAAA;AACA,SAAA,SAAA;AAAA,EACf;AAAA,EAEH,MAAM,UAAU;AACf,UAAM,OAAO,IAAI;AAAA,MAChB,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IAAA;AAEN,UAAM,SAAS,MAAM,KAAK,QAAA;AAC1B,UAAM,kCAAkB,IAAA;AACxB,UAAM,YAAY,CAAC,OAAmD;AACrE,kBAAY,IAAI,EAAE;AAClB,aAAO,MAAM;AACZ,oBAAY,OAAO,EAAE;AAAA,MACtB;AAAA,IACD;AACA,UAAM,UAAU,YAAY;AAC3B,YAAM,OAAO,MAAM,IAAI;AAAA,QACtB,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MAAA,EACJ,QAAA;AACF,kBAAY,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;AACpC,aAAO,KAAK,SAAS,GAAgB,MAAS,IAAI,IAAI,KAAK,KAAK;AAAA,IACjE;AACA,WAAO,wBAAwB,QAAQ,WAAW,OAAO;AAAA,EAC1D;AACD;AAEA,MAAM,2BAA2B,kDAAkD;AACnF,MAAM,yBAAyB,gDAAgD;AAC/E,MAAM,gCACL,uDAAuD;AACxD,MAAM,0BAA0B,iDAAiD;AAS1E,SAAS,sBAAsB,MAAc,KAA4B;AAC/E,QAAM,WAAW,mBAAmB,MAAM,GAAG;AAC7C,QAAM,aAAa,CAAC,YAAwE;AAC3F,YAAQ,cAAc,QAAQ,OAAO,CAAC,GAAG,IAAI,gBAAgB;AAC7D,WAAO,IAAI,eAAwB,MAAM,UAAU,QAAQ,OAAO,CAAC,CAAC;AAAA,EACrE;AACA,QAAM,UAAU,yBAAyB,KAAyB,UAAU;AAC5E,SAAO,IAAI,WAAsB,QAAQ,QAAQ,QAAQ,GAAG,MAAM,CAAC;AACpE;AAkBO,SAAS,kBAAkB,MAAc,KAAwB;AACvE,QAAM,WAAW,mBAAmB,MAAM,GAAG;AAC7C,QAAM,yBAAyB,CAAC,YAG1B;AACL,YAAQ,cAAc,QAAQ,OAAO,CAAC,GAAG,IAAI,gBAAgB;AAC7D,WAAO,IAAI,2BAAoC,MAAM,UAAU,QAAQ,OAAO,CAAC,CAAC;AAAA,EACjF;AAEA,UAAQ,IAAI,cAAA;AAAA,IACX,KAAK,SAAS;AACb,YAAM,aAAa,CAAC,YAAwE;AAC3F,gBAAQ,cAAc,QAAQ,OAAO,CAAC,GAAG,IAAI,gBAAgB;AAC7D,eAAO,IAAI,eAAwB,MAAM,UAAU,QAAQ,OAAO,CAAC,CAAC;AAAA,MACrE;AACA,YAAM,UAAU,uBAAuB,KAAyB,UAAU;AAC1E,aAAO,IAAI,WAAsB,QAAQ,QAAQ,QAAQ,GAAG,MAAM,CAAC;AAAA,IACpE;AAAA,IACA,KAAK,gBAAgB;AACpB,YAAM,UAAU,8BAA8B;AAAA,QAC7C;AAAA,QACA;AAAA,MAAA;AAED,aAAO,IAAI,WAAsB,QAAQ,QAAQ,QAAQ,GAAG,MAAM,CAAC;AAAA,IACpE;AAAA,IACA,KAAK,4BAA4B;AAChC,YAAM,UAAU,8BAA8B;AAAA,QAC7C;AAAA,QACA;AAAA,MAAA;AAED,aAAO,IAAI,WAAsB,QAAQ,QAAQ,QAAQ,GAAG,MAAM,CAAC;AAAA,IACpE;AAAA,IACA,KAAK,UAAU;AAEd,YAAM,mBAAmB,CAAC,YAGpB;AACL,gBAAQ,cAAc,QAAQ,QAAQ,IAAI,gBAAgB;AAC1D,eAAO,IAAI,2BAAoC,MAAM,UAAU,QAAQ,MAAM;AAAA,MAC9E;AACA,aAAO,wBAAwB,KAAuB,gBAAgB;AAAA,IACvE;AAAA,IACA;AACC,YAAM,IAAI;AAAA,QACT,IAAI,IAAI,+BAA+B,OAAQ,IAAkC,YAAY,CAAC;AAAA,MAAA;AAAA,EAE/F;AAEH;AAEA,MAAM,yBAAyB,wCAAwC;AAUvE,SAAS,mBAAyB,SAAuC;AACxE,QAAM,WAAW;AACjB,MAAI,YAAY,SAAS,UAAU,UAAa,SAAS,UAAU,MAAM;AACxE,WAAO,IAAI,QAAQ,SAAS,KAAK,CAAC;AAAA,EACnC;AACA,SAAO,GAAG,UAAU,IAAY;AACjC;AAkBO,SAAS,kBAAkB,MAAc,KAAwB;AACvE,QAAM,WAAW,mBAAmB,MAAM,GAAG;AAC7C,SAAO,uBAAuB;AAAA,IAC7B,CAAC,WAAW,IAAI,eAAe,MAAM,UAAU,QAAQ,kBAAkB;AAAA,IACzE,IAAI;AAAA,EAAA;AAEN;AA0CO,SAAS,IAAI,YAAkC,QAA2B;AAChF,MAAI,SAAS;AACb,UAAQ,QAAQ,CAAC,QAAQ,MAAM;AAC9B,cAAU;AACV,QAAI,IAAI,OAAO,kBAAkB,OAAO,OAAO,CAAC,CAAC;AAAA,EAClD,CAAC;AACD,SAAO,OAAO,KAAA;AACf;AAEA,eAAe,gBACd,aACA,UACA,QACyB;AACzB,QAAM,EAAE,OAAO,UAAA,IAAc;AAC7B,MAAI,CAAC,MAAO,QAAO,EAAE,MAAM,QAAW,QAAQ,OAAA;AAE9C,QAAM,WAAW,MAAM,YAAY,WAAW;AAC9C,QAAM,MAAM,MAAM,SAAS;AAAA,IAC1B;AAAA,IACA,QAAQ,EAAE,OAAO,WAAW,aAAa,CAAA,EAAC;AAAA,EAAE,CAC5C;AAED,SAAQ,qBAAqB,GAAG,KAAuB,CAAA;AACxD;AAGA,SAAS,qBAAqB,OAA+B;AAC5D,SAAO,EAAE,MAAM,QAAW,QAAQ,CAAC,EAAE,SAAU,MAAgB,QAAA,CAAS,EAAA;AACzE;AAOA,eAAe,oBACd,aACA,UACA,QACyB;AACzB,MAAI;AACH,WAAO,MAAM,gBAAgB,aAAa,UAAU,MAAM;AAAA,EAC3D,SAAS,OAAO;AACf,WAAO,qBAAqB,KAAK;AAAA,EAClC;AACD;AAeO,SAAS,yBAAyB,MAAc,KAA2B;AACjF,QAAM,WAAW,mBAAmB,MAAM,GAAG;AAC7C,SAAO,MAAM;AAAA,IACZ;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IAEb,YAAY,cAAmC;AAC9C,WAAK,gBAAgB;AAAA,IACtB;AAAA,IAEA,UAAU;AACT,WAAK,aAAa;AAClB,WAAK,KAAK,OAAA;AAAA,IACX;AAAA,IAEA,aAAa;AACZ,WAAK,aAAa;AAAA,IACnB;AAAA,IAEA,OAAO,QAAmC;AACzC,WAAK,UAAU;AAEf,UAAI,CAAC,KAAK,cAAc,KAAK,YAAY,QAAW;AACnD;AAAA,MACD;AAEA,WAAK,KAAK,OAAA;AAAA,IACX;AAAA,IAEA,UAAU;AACT,aAAO,KAAK,OAAA;AAAA,IACb;AAAA,IAEA,MAAM,SAAS;AACd,WAAK,MAAM,MAAM,oBAAoB,MAAM,UAAU,KAAK,WAAW,CAAA,CAAE,CAAC;AAAA,IACzE;AAAA,IAEA,MAAM,EAAE,MAAM,UAAyB;AACtC,WAAK,cAAc;AAAA,QAClB;AAAA,QACA,QAAQ,QAAQ,SAAS,SAAS;AAAA,QAClC,SAAS,MAAM,KAAK,QAAA;AAAA,MAAQ,CAC5B;AAAA,IACF;AAAA,EAAA;AAEF;AASO,SAAS,6BAA6B,MAAc,KAA2B;AACrF,QAAM,WAAW,mBAAmB,MAAM,GAAG;AAC7C,SAAO,OAAO,WAAkD;AAC/D,QAAI,CAAC,QAAQ,MAAO,QAAO,EAAE,MAAM,QAAW,QAAQ,CAAC,EAAE,SAAS,oBAAA,CAAqB,EAAA;AACvF,WAAO,oBAAoB,MAAM,UAAU,MAAM;AAAA,EAClD;AACD;AAiCO,SAAS,mCACf,MACA,KACC;AACD,QAAM,WAAW,mBAAmB,MAAM,GAAG;AAC7C,MAAI,IAAI,iBAAiB,UAAU;AAClC,mBAAe,IACd,QACA,UACgB;AAChB,UAAI,CAAC,QAAQ,OAAO;AACnB,iBAAS,EAAE,MAAM,QAAW,QAAQ,QAAW;AAC/C;AAAA,MACD;AACA,YAAM,SAAS,MAAM,oBAAoB,MAAM,UAAU,MAAM;AAC/D,eAAS;AAAA,QACR,MAAM,OAAO;AAAA,QACb,QAAQ,OAAO,QAAQ,SAAS,OAAO,SAAS;AAAA,MAAA,CAChD;AAAA,IACF;AAEA,WAAO;AAAA,MACN,OACC,QACA,UACA,UACO;AACP,aAAK,IAAI,QAAQ,QAAQ;AAAA,MAC1B;AAAA,MACA,UACC,QACA,UACA,UACc;AACd,aAAK,IAAI,QAAQ,QAAQ;AACzB,eAAO;AAAA,MACR;AAAA,IAAA;AAAA,EAEF;AAEA,MAAI,IAAI,iBAAiB,qBAAqB;AAS7C,WAAO,OAAO,WAA4D;AACzE,YAAM,SAAS,MAAM,oBAAoB,MAAM,UAAU,MAAM;AAC/D,UAAI,OAAO,QAAQ,QAAQ;AAC1B,eAAO,EAAE,MAAM,QAAW,QAAQ,OAAO,OAAA;AAAA,MAC1C;AACA,YAAM,kCAAkB,IAAA;AACxB,aAAO;AAAA,QACN,MAAM,OAAO;AAAA,QACb,QAAQ;AAAA,QACR,WAAW,CAAC,OAAO;AAClB,sBAAY,IAAI,EAAE;AAClB,iBAAO,MAAM;AACZ,wBAAY,OAAO,EAAE;AAAA,UACtB;AAAA,QACD;AAAA,QACA,SAAS,YAAY;AACpB,gBAAM,QAAQ,MAAM,oBAAoB,MAAM,UAAU,MAAM;AAC9D,gBAAM,UAAyB;AAAA,YAC9B,MAAM,MAAM;AAAA,YACZ,QAAQ,MAAM,QAAQ,SAAS,MAAM,SAAS;AAAA,UAAA;AAE/C,sBAAY,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC;AAAA,QACxC;AAAA,MAAA;AAAA,IAEF;AAAA,EACD;AAMA,SAAO,OAAO,WAA4D;AACzE,UAAM,SAAS,MAAM,oBAAoB,MAAM,UAAU,MAAM;AAC/D,QAAI,OAAO,QAAQ,QAAQ;AAC1B,aAAO,EAAE,MAAM,QAAW,QAAQ,OAAO,OAAA;AAAA,IAC1C;AACA,WAAO;AAAA,MACN,MAAM,OAAO;AAAA,MACb,QAAQ;AAAA,MACR,WAAW;AAAA,IAAA;AAAA,EAEb;AACD;"}
|
package/docs/consumer-guide.md
CHANGED
|
@@ -10,25 +10,16 @@ existing LWC project so you can compile your components into a single self-conta
|
|
|
10
10
|
|
|
11
11
|
- **Node.js** >= 20.0.0
|
|
12
12
|
- **LWC components** in a project directory (standard SFDX project or custom layout)
|
|
13
|
-
- **Salesforce CLI** (`sf`) — only needed if connecting to a real org (Tier 2)
|
|
14
13
|
|
|
15
14
|
---
|
|
16
15
|
|
|
17
16
|
## Overview
|
|
18
17
|
|
|
19
|
-
The plugin
|
|
20
|
-
|
|
21
|
-
| Tier | What You Get | Org Required? |
|
|
22
|
-
| ------------------------------- | ------------------------------------------ | ------------------ |
|
|
23
|
-
| **Tier 1: Off-Platform Build** | Static bundle with mock data, labels, i18n | No |
|
|
24
|
-
| **Tier 2: Live Org Connection** | Real GraphQL queries, live Salesforce data | Yes (via `sf` CLI) |
|
|
25
|
-
|
|
26
|
-
Start with Tier 1. Add Tier 2 later if your components use `lightning/graphql` or
|
|
27
|
-
need live data.
|
|
18
|
+
The plugin compiles your LWC components into a single self-contained `dist/index.html` that runs in a real MCP host (ChatGPT, MCP Apps, local demo MCP) — same bundle, no conditional wrapping. For **local development without a real host**, install a `window.openai.callTool` mock in your entry script; the guarded install is skipped automatically when a real host provides `window.openai`. `lightning/graphql` and `lightning/uiRecordApi` both route through MCP (`callTool("graphqlQuery", ...)` and `callTool("getRecordMcpTool", ...)`) via `builtins.lds()`.
|
|
28
19
|
|
|
29
20
|
---
|
|
30
21
|
|
|
31
|
-
##
|
|
22
|
+
## Off-Platform Build
|
|
32
23
|
|
|
33
24
|
### Project Structure
|
|
34
25
|
|
|
@@ -231,20 +222,55 @@ Always include `builtins.gate()` and `builtins.accessCheck()` when using
|
|
|
231
222
|
|
|
232
223
|
### Step 4: Create `bootstrap.js`
|
|
233
224
|
|
|
225
|
+
The compiled bundle targets real MCP hosts (ChatGPT, MCP Apps, local demo MCP) — they provide `window.openai.callTool` before the bundle loads. For **local development without a real host**, install a guarded mock at the top of `bootstrap.js` so `@wire(graphql)`, `@wire(getRecord)`, and `executeMutation` calls return data. The guard (`if (!window.openai?.callTool)`) makes the mock a no-op when a real host is present, so the same compiled bundle works in both environments.
|
|
226
|
+
|
|
234
227
|
```js
|
|
235
|
-
|
|
236
|
-
import
|
|
237
|
-
|
|
228
|
+
// Mock window.openai.callTool for local dev. Must run before any LWC / sdk-core
|
|
229
|
+
// import, because sdk-core detects the surface at module-load time by reading
|
|
230
|
+
// window.openai. Guard with `if (!window.openai?.callTool)` so a real MCP host
|
|
231
|
+
// skips this block entirely.
|
|
232
|
+
if (!window.openai?.callTool) {
|
|
233
|
+
window.openai = {
|
|
234
|
+
...window.openai,
|
|
235
|
+
callTool: async (name, args) => {
|
|
236
|
+
await new Promise((r) => setTimeout(r, 300)); // simulate latency
|
|
237
|
+
|
|
238
|
+
if (name === "getRecordMcpTool") {
|
|
239
|
+
// lightning/uiRecordApi → @wire(getRecord)
|
|
240
|
+
return {
|
|
241
|
+
structuredContent: {
|
|
242
|
+
data: {
|
|
243
|
+
fields: { Name: { value: "Mock Record" } },
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (name === "graphqlQuery") {
|
|
249
|
+
// lightning/graphql → @wire(graphql) or executeMutation(...)
|
|
250
|
+
// Branch on args.query to return different envelopes.
|
|
251
|
+
return { result: JSON.stringify({ data: {} }) };
|
|
252
|
+
}
|
|
253
|
+
return { result: JSON.stringify({ data: {} }) };
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
238
257
|
|
|
239
|
-
//
|
|
240
|
-
import
|
|
258
|
+
// Dynamic imports so the mock above is in place before sdk-core loads.
|
|
259
|
+
await import("@lwc/synthetic-shadow");
|
|
260
|
+
await import("@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.css");
|
|
261
|
+
const { createElement } = await import("lwc");
|
|
262
|
+
const { default: App } = await import("c/myApp");
|
|
241
263
|
|
|
242
|
-
// Mount it — tag name is the kebab-case version of the specifier
|
|
243
264
|
const el = createElement("c-my-app", { is: App });
|
|
244
265
|
document.getElementById("app").appendChild(el);
|
|
245
266
|
```
|
|
246
267
|
|
|
247
|
-
Replace `c/myApp` and `c-my-app` with your actual root component name.
|
|
268
|
+
Replace `c/myApp` and `c-my-app` with your actual root component name. If your components use only labels, i18n, and base components (no LDS / graphql), the `callTool` mock block can be omitted — `window.openai` only matters when MCP-backed adapters are in play.
|
|
269
|
+
|
|
270
|
+
For complete working examples, see:
|
|
271
|
+
|
|
272
|
+
- [`lwc-simple/src/main.js`](../../../examples/lwc-axl/lwc-simple/src/main.js) — `@wire(getRecord)` + `@wire(graphql)` (read paths)
|
|
273
|
+
- [`lwc-records/src/app/bootstrap.js`](../../../examples/lwc-axl/lwc-records/src/app/bootstrap.js) — `executeMutation` with a `ContactUpdate` branch
|
|
248
274
|
|
|
249
275
|
### Step 5: Build and Test
|
|
250
276
|
|
|
@@ -266,102 +292,6 @@ npm run preview
|
|
|
266
292
|
|
|
267
293
|
---
|
|
268
294
|
|
|
269
|
-
## Tier 2: Live Org Connection (lwcProxy)
|
|
270
|
-
|
|
271
|
-
If your components use `lightning/graphql` or need live Salesforce data, add
|
|
272
|
-
`lwcProxy()` to connect to a real org during development.
|
|
273
|
-
|
|
274
|
-
### Prerequisites
|
|
275
|
-
|
|
276
|
-
- Salesforce CLI installed and an org connected: `sf org display`
|
|
277
|
-
- Additional packages: `@salesforce/sdk-data` and `@salesforce/ui-bundle`
|
|
278
|
-
|
|
279
|
-
### Step 1: Install Additional Dependencies
|
|
280
|
-
|
|
281
|
-
```bash
|
|
282
|
-
npm install @salesforce/sdk-data @salesforce/ui-bundle
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
### Step 2: Update `vite.config.js`
|
|
286
|
-
|
|
287
|
-
Add `lwcProxy()` before `lwcVitePlugin()` and add `builtins.lightningGraphql()`
|
|
288
|
-
to providers:
|
|
289
|
-
|
|
290
|
-
```js
|
|
291
|
-
import { defineConfig } from "vite";
|
|
292
|
-
import lwcVitePlugin, { builtins, lwcProxy } from "@salesforce/vite-plugin-lwc-ui-bundle";
|
|
293
|
-
import { viteSingleFile } from "vite-plugin-singlefile";
|
|
294
|
-
|
|
295
|
-
export default defineConfig({
|
|
296
|
-
build: {
|
|
297
|
-
target: "esnext",
|
|
298
|
-
minify: false,
|
|
299
|
-
},
|
|
300
|
-
plugins: [
|
|
301
|
-
lwcProxy({ debug: true }),
|
|
302
|
-
// lwcProxy({ orgAlias: "my-org", debug: true }), // explicit org
|
|
303
|
-
lwcVitePlugin({
|
|
304
|
-
modules: {
|
|
305
|
-
dirs: [{ path: "force-app/main/default/lwc", namespace: "c" }],
|
|
306
|
-
npm: ["lightning-base-components"],
|
|
307
|
-
},
|
|
308
|
-
providers: [
|
|
309
|
-
builtins.label(),
|
|
310
|
-
builtins.i18n(),
|
|
311
|
-
builtins.client(),
|
|
312
|
-
builtins.gate(),
|
|
313
|
-
builtins.accessCheck(),
|
|
314
|
-
builtins.lightningGraphql(),
|
|
315
|
-
],
|
|
316
|
-
}),
|
|
317
|
-
viteSingleFile(),
|
|
318
|
-
],
|
|
319
|
-
});
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
### Step 3: Update `bootstrap.js`
|
|
323
|
-
|
|
324
|
-
Initialize the Data SDK before mounting your app. This requires top-level `await`
|
|
325
|
-
(hence `target: "esnext"` in the vite config):
|
|
326
|
-
|
|
327
|
-
```js
|
|
328
|
-
import "@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.css";
|
|
329
|
-
import "@lwc/synthetic-shadow";
|
|
330
|
-
import { createElement } from "lwc";
|
|
331
|
-
|
|
332
|
-
// Initialize SDK — lwcProxy() handles /services/* in the Vite dev server
|
|
333
|
-
import { createDataSDK } from "@salesforce/sdk-data";
|
|
334
|
-
try {
|
|
335
|
-
globalThis.__sfdc_sdk__ = await createDataSDK({ uiBundle: { basePath: "/" } });
|
|
336
|
-
} catch (_err) {
|
|
337
|
-
globalThis.__sfdc_sdk__ = {};
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Import your root component
|
|
341
|
-
import App from "c/myApp";
|
|
342
|
-
|
|
343
|
-
const el = createElement("c-my-app", { is: App });
|
|
344
|
-
document.getElementById("app").appendChild(el);
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
### Step 4: Run Dev Server
|
|
348
|
-
|
|
349
|
-
```bash
|
|
350
|
-
npm run dev
|
|
351
|
-
# Terminal shows: [lwc-proxy] Connected to https://your-org.my.salesforce.com
|
|
352
|
-
# Open http://localhost:5173
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
Components using `@wire(graphql, ...)` will now execute real GraphQL queries
|
|
356
|
-
against your connected org. The proxy intercepts `/services/*` calls inside
|
|
357
|
-
the Vite dev server — no separate proxy process needed.
|
|
358
|
-
|
|
359
|
-
> **Note:** `lwcProxy()` works with `npm run dev` only. The production build
|
|
360
|
-
> (`dist/index.html`) does not include the proxy — it's a static file. For
|
|
361
|
-
> production use with live data, you need a separate API proxy or MCP server.
|
|
362
|
-
|
|
363
|
-
---
|
|
364
|
-
|
|
365
295
|
## Common Errors
|
|
366
296
|
|
|
367
297
|
### `Cannot read properties of undefined (reading 'isOpen')`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/vite-plugin-lwc-ui-bundle",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Vite plugin for compiling LWC components into static bundles for off-platform and MCP use",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"author": "Salesforce",
|
|
@@ -34,10 +34,6 @@
|
|
|
34
34
|
"types": "./dist/providers/lds/index.d.ts",
|
|
35
35
|
"import": "./dist/providers/lds/index.js"
|
|
36
36
|
},
|
|
37
|
-
"./providers/lightning-graphql": {
|
|
38
|
-
"types": "./dist/providers/lightning-graphql/index.d.ts",
|
|
39
|
-
"import": "./dist/providers/lightning-graphql/index.js"
|
|
40
|
-
},
|
|
41
37
|
"./package.json": "./package.json"
|
|
42
38
|
},
|
|
43
39
|
"main": "./dist/index.js",
|
|
@@ -49,7 +45,7 @@
|
|
|
49
45
|
"skills"
|
|
50
46
|
],
|
|
51
47
|
"scripts": {
|
|
52
|
-
"build": "vite build && vite build --mode runtime-lds
|
|
48
|
+
"build": "vite build && vite build --mode runtime-lds",
|
|
53
49
|
"clean": "rm -rf dist",
|
|
54
50
|
"dev": "vite build --watch",
|
|
55
51
|
"test": "vitest run",
|
|
@@ -58,8 +54,8 @@
|
|
|
58
54
|
},
|
|
59
55
|
"peerDependencies": {
|
|
60
56
|
"@lwc/rollup-plugin": "^9.0.0",
|
|
61
|
-
"@salesforce/sdk-chat": "^
|
|
62
|
-
"@salesforce/ui-bundle": "^
|
|
57
|
+
"@salesforce/platform-sdk-chat": "^2.0.0",
|
|
58
|
+
"@salesforce/ui-bundle": "^2.0.0",
|
|
63
59
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
|
64
60
|
"zod": "^3.23.8"
|
|
65
61
|
},
|
|
@@ -69,17 +65,17 @@
|
|
|
69
65
|
}
|
|
70
66
|
},
|
|
71
67
|
"dependencies": {
|
|
68
|
+
"@conduit-client/jsonschema-validate": "3.19.3",
|
|
69
|
+
"@conduit-client/service-bindings-imperative": "3.19.3",
|
|
70
|
+
"@conduit-client/service-bindings-lwc": "3.19.3",
|
|
71
|
+
"@conduit-client/utils": "3.19.3",
|
|
72
72
|
"es-module-lexer": "^1.7.0",
|
|
73
73
|
"magic-string": "^0.30.17"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@conduit-client/bindings-utils": "3.19.3",
|
|
77
77
|
"@conduit-client/command-base": "3.19.3",
|
|
78
|
-
"@
|
|
79
|
-
"@conduit-client/service-bindings-imperative": "3.19.3",
|
|
80
|
-
"@conduit-client/service-bindings-lwc": "3.19.3",
|
|
81
|
-
"@conduit-client/utils": "3.19.3",
|
|
82
|
-
"@salesforce/sdk-chat": "^1.134.5",
|
|
78
|
+
"@salesforce/platform-sdk-chat": "^2.0.0",
|
|
83
79
|
"typescript": "^5.9.3",
|
|
84
80
|
"vite": "^7.0.0",
|
|
85
81
|
"vite-plugin-dts": "^4.5.4",
|
|
@@ -75,8 +75,18 @@ Surface this early:
|
|
|
75
75
|
> A) Upgrade Node (recommended — `nvm install 20.19` or `22`)
|
|
76
76
|
> B) Pin `vite@^7` and a compatible `@lwc/rollup-plugin` major"
|
|
77
77
|
|
|
78
|
+
**Stop here.** Report the detected layout and the Node decision, then
|
|
79
|
+
wait for the user to answer. Do **not** bundle Step 2's chat-wrapper
|
|
80
|
+
question into the same turn — a misdetection in Step 1 cascades into
|
|
81
|
+
every later decision, so the user needs a clean moment to confirm or
|
|
82
|
+
correct before anything else lands.
|
|
83
|
+
|
|
78
84
|
### Step 2: Ask about chat wrappers
|
|
79
85
|
|
|
86
|
+
Only start this step **after** Step 1 has been answered and resolved.
|
|
87
|
+
Ask the chat-wrapper question in its own turn — don't stack it on top
|
|
88
|
+
of Step 1's findings report.
|
|
89
|
+
|
|
80
90
|
If the project will run inside a ChatGPT/MCP host (tool output drives
|
|
81
91
|
the UI), components need a thin chat-aware wrapper + mapper. The wrapper
|
|
82
92
|
reads tool output from the host, the mapper normalizes it into the
|
|
@@ -162,10 +172,63 @@ Report what you found — plainly, so the user can correct misdetections:
|
|
|
162
172
|
> - 3 label imports: `c.appTitle`, `c.greeting`, `c.save`
|
|
163
173
|
> - Uses `lightning-card` → needs lightning-base-components + the
|
|
164
174
|
> gate/accessCheck/primitiveUtils providers
|
|
165
|
-
> - Uses `lightning/graphql` → needs `builtins.
|
|
166
|
-
>
|
|
167
|
-
>
|
|
168
|
-
>
|
|
175
|
+
> - Uses `lightning/graphql` → needs `builtins.lds()` provider (graphql
|
|
176
|
+
> specifiers are registered by default) + the `@conduit-client/*` LDS
|
|
177
|
+
> runtime peers (verified after install in Step 8.1) + a `graphqlQuery`
|
|
178
|
+
> mock branch in `bootstrap.js`
|
|
179
|
+
> - Uses `lightning/uiRecordApi` → needs `builtins.lds()` provider + the
|
|
180
|
+
> `@conduit-client/*` LDS runtime peers + a `getRecordMcpTool` mock
|
|
181
|
+
> branch"
|
|
182
|
+
|
|
183
|
+
### Step 4.5: Verify existing `vite.config.js` against findings
|
|
184
|
+
|
|
185
|
+
If `vite.config.js` already exists (Step 1 noted whether it does), open it
|
|
186
|
+
now and cross-check against what Step 4 found. This catches the silent-
|
|
187
|
+
failure case where a project builds successfully but wire adapters and
|
|
188
|
+
scoped imports return `undefined` at runtime because the `providers` array
|
|
189
|
+
is missing or incomplete.
|
|
190
|
+
|
|
191
|
+
Check in this order:
|
|
192
|
+
|
|
193
|
+
1. **Does `lwcVitePlugin({...})` include a `providers` array at all?**
|
|
194
|
+
If not, flag it. Without the array, every `@salesforce/*` import returns
|
|
195
|
+
`undefined` — labels, gates, access checks, i18n, client — even though
|
|
196
|
+
the build succeeds. See `references/known-pitfalls.md#11`.
|
|
197
|
+
|
|
198
|
+
2. **For each finding from Step 4, verify the matching provider is present:**
|
|
199
|
+
- Step 4 found `@salesforce/label/*` usage → `builtins.label({...})` must be in `providers`
|
|
200
|
+
- Step 4 found `@wire(graphql)`, `executeMutation`, or any `lightning/graphql` import →
|
|
201
|
+
`builtins.lds()` must be present (graphql specifiers are in the default registry)
|
|
202
|
+
- Step 4 found `@wire(getRecord)` from `lightning/uiRecordApi` →
|
|
203
|
+
`builtins.lds()` must be present (one `builtins.lds()` covers both)
|
|
204
|
+
- Step 4 found any `@salesforce/i18n/*` → `builtins.i18n()` must be
|
|
205
|
+
present
|
|
206
|
+
- Step 4 found any `@salesforce/client/*` → `builtins.client()` must
|
|
207
|
+
be present
|
|
208
|
+
- `modules.npm` includes `lightning-base-components` (or
|
|
209
|
+
`lwc-components-lightning`) → `builtins.gate()`,
|
|
210
|
+
`builtins.accessCheck()`, `builtins.primitiveUtils()` must all be
|
|
211
|
+
present. Base components use these modules internally even if user
|
|
212
|
+
code doesn't.
|
|
213
|
+
|
|
214
|
+
3. **Report the gaps explicitly** before proceeding to later steps:
|
|
215
|
+
|
|
216
|
+
> "Your `vite.config.js` exists but its `providers` array is missing:
|
|
217
|
+
>
|
|
218
|
+
> - `builtins.label({...})` — Step 4 found 3 `@salesforce/label/*` imports
|
|
219
|
+
> - `builtins.lds()` — Step 4 found `@wire(graphql)` / `executeMutation` in `c/recordDetail`
|
|
220
|
+
> - `builtins.gate()`, `builtins.accessCheck()`, `builtins.primitiveUtils()` — `lightning-base-components` is in `modules.npm` and needs these
|
|
221
|
+
>
|
|
222
|
+
> I'll add these in Step 7. The build may currently succeed even with
|
|
223
|
+
> these missing, but wire adapters and labels will fail silently at
|
|
224
|
+
> runtime."
|
|
225
|
+
|
|
226
|
+
4. **If the existing `providers` array is complete**, say so and move on.
|
|
227
|
+
|
|
228
|
+
Do not skip this verification. A build that compiles fine but has no
|
|
229
|
+
providers registered is the single most common failure mode for projects
|
|
230
|
+
that already have a partial config — the compiler doesn't care, but the
|
|
231
|
+
runtime silently returns `undefined` for everything.
|
|
169
232
|
|
|
170
233
|
### Step 5: Decide the runtime mode
|
|
171
234
|
|
|
@@ -297,21 +360,30 @@ See "Step 2" in the consumer guide for the template.
|
|
|
297
360
|
|
|
298
361
|
**Import providers from the plugin, do not reimplement them.** All
|
|
299
362
|
provider names (`label`, `i18n`, `client`, `gate`, `accessCheck`,
|
|
300
|
-
`primitiveUtils`, `
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
`references/known-pitfalls.md#3`).
|
|
363
|
+
`primitiveUtils`, `lds`) must come from the `builtins` export of
|
|
364
|
+
`@salesforce/vite-plugin-lwc-ui-bundle`. Calling them invokes the
|
|
365
|
+
plugin's real runtime — which includes the `lightning/graphql` wire
|
|
366
|
+
adapter, `normalizeMcpResponse` envelope parsing, LDS wire/read/mutation
|
|
367
|
+
adapters, and more. Hand-rolling any of them bypasses all that and
|
|
368
|
+
breaks host integration (see `references/known-pitfalls.md#3`).
|
|
307
369
|
|
|
308
370
|
```js
|
|
309
371
|
import lwcVitePlugin, { builtins } from "@salesforce/vite-plugin-lwc-ui-bundle";
|
|
310
372
|
|
|
311
373
|
// inside plugins -> lwcVitePlugin({ providers: [...] })
|
|
312
|
-
builtins.
|
|
313
|
-
|
|
314
|
-
|
|
374
|
+
builtins.lds(), // default registry:
|
|
375
|
+
// lightning/uiRecordApi, uiObjectInfoApi,
|
|
376
|
+
// and lightning/graphql (tool: "graphqlQuery")
|
|
377
|
+
|
|
378
|
+
// Add to / override the default registry. The argument is deep-merged onto
|
|
379
|
+
// the defaults per specifier, so you only declare what you are changing —
|
|
380
|
+
// unmentioned entries (e.g. `getRecord`, `createRecord`) are preserved.
|
|
381
|
+
builtins.lds({
|
|
382
|
+
"lightning/graphql": {
|
|
383
|
+
graphql: { type: "graphql-wire", mcp: { toolName: "myGraphqlTool" } },
|
|
384
|
+
executeMutation: { type: "graphql-mutation", mcp: { toolName: "myGraphqlTool" } },
|
|
385
|
+
},
|
|
386
|
+
}),
|
|
315
387
|
```
|
|
316
388
|
|
|
317
389
|
Adapt based on earlier findings:
|
|
@@ -319,21 +391,21 @@ Adapt based on earlier findings:
|
|
|
319
391
|
- Set `dirs` for the detected project structure (SFDX vs namespaced).
|
|
320
392
|
- Populate `builtins.label({...})` with values from Step 6.
|
|
321
393
|
- Include `builtins.primitiveUtils()` if using `lightning-base-components`.
|
|
322
|
-
- Include `builtins.
|
|
323
|
-
|
|
394
|
+
- Include `builtins.lds()` if any component uses `lightning/uiRecordApi`,
|
|
395
|
+
`lightning/uiObjectInfoApi`, or `lightning/graphql`.
|
|
324
396
|
- Include `viteSingleFile()` — without it, `vite build` emits multi-file
|
|
325
397
|
output and the MCP host can't inline the bundle. See
|
|
326
|
-
`known-pitfalls.md#
|
|
398
|
+
`known-pitfalls.md#6` for the full config block.
|
|
327
399
|
- Remove `npm: ["lightning-base-components"]` if no base components used.
|
|
328
400
|
|
|
329
401
|
**Tool names** (canonical list — referenced elsewhere):
|
|
330
402
|
|
|
331
403
|
- `builtins.lds()` default for `lightning/uiRecordApi.getRecord`:
|
|
332
404
|
`getRecordMcpTool`. Override via
|
|
333
|
-
`builtins.lds({ "lightning/uiRecordApi": { getRecord: { toolName: "..." } } })`.
|
|
334
|
-
- `builtins.
|
|
335
|
-
|
|
336
|
-
|
|
405
|
+
`builtins.lds({ "lightning/uiRecordApi": { getRecord: { type: "wire", mcp: { toolName: "..." } } } })`.
|
|
406
|
+
- `builtins.lds()` default for `lightning/graphql` (both `graphql` wire
|
|
407
|
+
and `executeMutation`): `graphqlQuery`. Override by registering the
|
|
408
|
+
specifier explicitly — see example above.
|
|
337
409
|
|
|
338
410
|
Whatever tool names you settle on here **must match** the branches in
|
|
339
411
|
`bootstrap.js` mocks (Step 8).
|
|
@@ -377,12 +449,79 @@ Only add these if the component tree inspection reveals a need:
|
|
|
377
449
|
system. Useful when an npm package has its own label definitions that
|
|
378
450
|
shouldn't be intercepted.
|
|
379
451
|
- **`ignorePatterns`**: Specifier prefixes that providers should never
|
|
380
|
-
intercept. Defaults include `@salesforce/sdk
|
|
452
|
+
intercept. Defaults include `@salesforce/sdk-*`, `@salesforce/platform-sdk-*`, and `@salesforce/core`.
|
|
381
453
|
|
|
382
454
|
### Step 8: Install and build
|
|
383
455
|
|
|
384
456
|
```bash
|
|
385
457
|
npm install
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
#### Step 8.1: Verify LDS runtime peers (only if LDS is in use)
|
|
461
|
+
|
|
462
|
+
Run this check **only** if Step 4 found actual usage of any of:
|
|
463
|
+
|
|
464
|
+
- `lightning/graphql` with real `@wire(graphql)`, `executeMutation`, or
|
|
465
|
+
graphql-imperative calls
|
|
466
|
+
- `lightning/uiRecordApi` with real `@wire` usage of `getRecord` /
|
|
467
|
+
`createRecord` / `updateRecord`
|
|
468
|
+
- `lightning/uiObjectInfoApi` with real usage
|
|
469
|
+
|
|
470
|
+
If none of the above are in the component tree, skip this sub-step
|
|
471
|
+
entirely — the LDS runtime is never loaded and the peers don't matter.
|
|
472
|
+
|
|
473
|
+
**Why this check exists:** the plugin's `dist/providers/lds/runtime.js`
|
|
474
|
+
statically imports three `@conduit-client/*` packages at build time.
|
|
475
|
+
Depending on the plugin version, these may ship as the plugin's own
|
|
476
|
+
`dependencies`/`optionalDependencies`, or may be left undeclared (as of
|
|
477
|
+
`@salesforce/vite-plugin-lwc-ui-bundle@1.135.0`). The skill does not
|
|
478
|
+
need to know which — it just verifies they are resolvable in the
|
|
479
|
+
consumer's `node_modules` after install, regardless of how they got
|
|
480
|
+
there.
|
|
481
|
+
|
|
482
|
+
**Check:**
|
|
483
|
+
|
|
484
|
+
```bash
|
|
485
|
+
node -e "
|
|
486
|
+
const peers = [
|
|
487
|
+
'@conduit-client/service-bindings-imperative',
|
|
488
|
+
'@conduit-client/service-bindings-lwc',
|
|
489
|
+
'@conduit-client/utils'
|
|
490
|
+
];
|
|
491
|
+
const missing = peers.filter(p => {
|
|
492
|
+
try { require.resolve(p); return false; }
|
|
493
|
+
catch { return true; }
|
|
494
|
+
});
|
|
495
|
+
if (missing.length) {
|
|
496
|
+
console.error('Missing LDS runtime peers:', missing.join(', '));
|
|
497
|
+
process.exit(1);
|
|
498
|
+
} else {
|
|
499
|
+
console.log('LDS runtime peers OK');
|
|
500
|
+
}
|
|
501
|
+
"
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**If any are missing**, install the set as consumer `dependencies` (not
|
|
505
|
+
`@salesforce/ui-bundle` — that package does not pull these three
|
|
506
|
+
transitively and installing it does not resolve the error):
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
npm install --save \
|
|
510
|
+
@conduit-client/service-bindings-imperative@^3 \
|
|
511
|
+
@conduit-client/service-bindings-lwc@^3 \
|
|
512
|
+
@conduit-client/utils@^3
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Re-run the check. See `references/known-pitfalls.md#13` for the full
|
|
516
|
+
background.
|
|
517
|
+
|
|
518
|
+
**If all three are already present** (e.g., a future plugin version
|
|
519
|
+
ships them as its own deps, or a sibling package already pulled them
|
|
520
|
+
in), do nothing — no lockfile churn, no duplicate entries.
|
|
521
|
+
|
|
522
|
+
#### Step 8.2: Build
|
|
523
|
+
|
|
524
|
+
```bash
|
|
386
525
|
npm run build
|
|
387
526
|
```
|
|
388
527
|
|
|
@@ -393,15 +532,17 @@ open dist/index.html
|
|
|
393
532
|
```
|
|
394
533
|
|
|
395
534
|
A correctly built `dist/index.html` is typically 100 KB+. A 1-2 KB file
|
|
396
|
-
is the un-inlined stub (see `known-pitfalls.md#
|
|
535
|
+
is the un-inlined stub (see `known-pitfalls.md#6`).
|
|
397
536
|
|
|
398
537
|
If the build fails, diagnose via `references/known-pitfalls.md`. The most
|
|
399
538
|
frequent issues have symptoms that map directly to specific entries:
|
|
400
539
|
|
|
401
540
|
- "Cannot find native binding" inside rolldown → pitfall #1 (Node)
|
|
402
541
|
- "No matching version found for \<pkg\>" → pitfall #2 (stale dep pins)
|
|
403
|
-
- Missing `isOpen` / missing `lightning/button` → pitfalls #
|
|
404
|
-
- `force/someModule` not resolved → pitfall #
|
|
542
|
+
- Missing `isOpen` / missing `lightning/button` → pitfalls #8 / #6
|
|
543
|
+
- `force/someModule` not resolved → pitfall #9
|
|
544
|
+
- `Rollup failed to resolve import "@conduit-client/..."` → pitfall #13
|
|
545
|
+
(LDS runtime peers missing; Step 8.1 was skipped or failed silently)
|
|
405
546
|
- Component renders but unstyled → missing SLDS CSS import in
|
|
406
547
|
`bootstrap.js`
|
|
407
548
|
|
|
@@ -424,8 +565,8 @@ Open `http://localhost:5173` and confirm:
|
|
|
424
565
|
|
|
425
566
|
If something renders wrong, check `references/known-pitfalls.md`. The
|
|
426
567
|
most common dev-time issues are graphql wire adapters returning empty
|
|
427
|
-
data (pitfalls #3, #4
|
|
428
|
-
payloads (#
|
|
568
|
+
data (pitfalls #3, #4) and mapper returning `null` on Avro-wrapped
|
|
569
|
+
payloads (#5).
|
|
429
570
|
|
|
430
571
|
The same compiled `dist/index.html` runs in ChatGPT — the guard in
|
|
431
572
|
`bootstrap.js` ensures local mocks are skipped when the host provides
|