@solumflow-app/crm-mcp-server 0.1.0 → 0.3.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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/placement-tools.ts","../src/tool-answers.ts","../src/server-config.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createClient } from '@solumflow-app/crm-client';\n\nimport { registerPlacementTools } from './placement-tools';\nimport { readServerConfig } from './server-config';\n\n/**\n * An MCP server over stdio that lets an agent place an account's forms,\n * booking pages, chat and tracking on a website. It holds no rules of its own:\n * every tool is one call of the public API through the published client.\n *\n * Errors go to stderr; stdout belongs to the protocol.\n */\nasync function main() {\n const config = readServerConfig(process.env);\n\n if (!config.ok) {\n console.error(`crm-mcp-server: ${config.error}`);\n process.exit(1);\n }\n\n const server = new McpServer({\n name: 'crm-mcp-server',\n version: '0.1.0',\n });\n\n registerPlacementTools(\n server,\n createClient({ apiKey: config.apiKey, baseUrl: config.baseUrl }),\n );\n\n await server.connect(new StdioServerTransport());\n}\n\nmain().catch((error: unknown) => {\n console.error('crm-mcp-server:', error);\n process.exit(1);\n});\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type {\n CrmClient,\n PlacementBookingPage,\n PlacementChatWidget,\n PlacementForm,\n PlacementSiteTracking,\n} from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport { answer, codeBlock, refusal } from './tool-answers';\n\n/** The part of the client these tools call; a test hands in a fake of exactly this. */\nexport type PlacementClient = Pick<\n CrmClient,\n | 'getForms'\n | 'getChatWidgets'\n | 'upsertChatWidget'\n | 'getSiteTracking'\n | 'updateSiteTracking'\n | 'getBookingPages'\n>;\n\nconst REACT_HINT =\n 'On a React or Next.js site, prefer the components in ' +\n '`@solumflow-app/crm-client/react` (CrmForm, CrmBooking, CrmChatWidget, ' +\n 'CrmSiteTracking) over pasting HTML.';\n\nconst page = {\n cursor: z\n .string()\n .optional()\n .describe('The nextCursor of the previous page; leave out for the first.'),\n limit: z.number().int().min(1).max(100).optional(),\n};\n\nconst origin = z\n .string()\n .regex(/^https?:\\/\\/[a-zA-Z0-9.-]+(:[0-9]{1,5})?$/)\n .describe('Scheme, host and optional port; no path: https://www.example.com');\n\n/**\n * One tool per route of the public API, nothing in between: every rule about\n * what may be read or changed is the API's, and this only says it in a way an\n * agent can follow. The annotations describe; the key's scope is what\n * enforces.\n */\nexport function registerPlacementTools(\n server: McpServer,\n client: PlacementClient,\n) {\n server.registerTool(\n 'list_forms',\n {\n title: 'List published forms',\n description:\n 'The published forms of the account, each with its own link and the ' +\n 'HTML snippet that places it on a page. Forms are made and published ' +\n 'by a person in the app’s form builder; this cannot create one. If the ' +\n 'form you need is missing, ask the user to build and publish it first. ' +\n REACT_HINT,\n inputSchema: page,\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async (input) => {\n try {\n const list = await client.getForms(input);\n\n return answer(\n list.data.length === 0\n ? 'No published forms. Ask the user to build and publish one in the app.'\n : [...list.data.map(describeForm), more(list.nextCursor)].join(\n '\\n\\n',\n ),\n list,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n\n server.registerTool(\n 'list_booking_pages',\n {\n title: 'List booking pages',\n description:\n 'Every page a visitor can book on: the services overview, each ' +\n 'category and service, and each appointment calendar. Each has a link; ' +\n 'services, categories and the overview also have an embed snippet ' +\n '(an iframe plus a sizing script). A calendar has no embed: link to it. ' +\n REACT_HINT,\n inputSchema: {},\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n try {\n const pages = await client.getBookingPages();\n\n return answer(\n pages.length === 0\n ? 'Nothing can be booked online on this account yet.'\n : pages.map(describeBookingPage).join('\\n\\n'),\n pages,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n\n server.registerTool(\n 'list_chat_widgets',\n {\n title: 'List chat widgets',\n description:\n 'The chat widgets of the account, with the sites each may run on and, ' +\n 'for a published one, the script tag that places it. An empty list of ' +\n 'allowed origins means any site. ' +\n REACT_HINT,\n inputSchema: page,\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async (input) => {\n try {\n const list = await client.getChatWidgets(input);\n\n return answer(\n list.data.length === 0\n ? 'No chat widgets yet. upsert_chat_widget can make one.'\n : [\n ...list.data.map(describeChatWidget),\n more(list.nextCursor),\n ].join('\\n\\n'),\n list,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n\n server.registerTool(\n 'upsert_chat_widget',\n {\n title: 'Make or change a chat widget',\n description:\n 'Makes the chat widget at this address when there is none, or changes ' +\n 'it. Sets its name, whether it is live, and which sites it may run on. ' +\n 'How it looks and what it says is done in the app’s builder. Sites are ' +\n 'added and removed, never replaced. Careful: an empty list means ANY ' +\n 'site, so adding the first origin narrows the widget to that site only, ' +\n 'and removing an origin takes the chat off a site that may be live.',\n inputSchema: {\n slug: z\n .string()\n .regex(/^[a-z][a-z0-9-]*$/)\n .max(100)\n .describe(\n 'The widget’s address on this account, such as \"website-chat\".',\n ),\n name: z\n .string()\n .min(1)\n .max(200)\n .optional()\n .describe('Required when the widget does not exist yet.'),\n status: z\n .enum(['draft', 'published'])\n .optional()\n .describe('Only a published widget answers on a site.'),\n addAllowedOrigins: z.array(origin).max(20).optional(),\n removeAllowedOrigins: z.array(origin).max(20).optional(),\n },\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: true,\n },\n },\n async ({ slug, ...input }) => {\n try {\n const widget = await client.upsertChatWidget(slug, input);\n\n return answer(\n `${widget.created ? 'Made' : 'Updated'} the widget.\\n\\n${describeChatWidget(widget)}`,\n widget,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n\n server.registerTool(\n 'get_site_tracking',\n {\n title: 'Get the tracking script',\n description:\n 'The website tracking script of the account: its tag, the hosts it ' +\n 'counts visits from, and the consent it waits for. Answers that ' +\n 'tracking is not set up when it never was; update_site_tracking sets ' +\n 'it up. ' +\n REACT_HINT,\n inputSchema: {},\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n try {\n const site = await client.getSiteTracking();\n\n return answer(\n site\n ? describeSiteTracking(site)\n : 'Website tracking is not set up yet. update_site_tracking with the site’s host sets it up.',\n site,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n\n server.registerTool(\n 'update_site_tracking',\n {\n title: 'Set up or change the tracking script',\n description:\n 'Sets website tracking up when it is not there, adds or removes the ' +\n 'hosts it counts visits from, and changes the consent it waits for. An ' +\n 'empty list of hosts counts nothing, so add the host of every site the ' +\n 'script is placed on. Hosts are added and removed, never replaced.',\n inputSchema: {\n addAllowedHosts: z\n .array(z.string())\n .max(20)\n .optional()\n .describe('Hosts such as \"www.example.com\" or \"*.example.com\".'),\n removeAllowedHosts: z.array(z.string()).max(20).optional(),\n consentSignal: z\n .enum([\n 'ad_storage',\n 'analytics_storage',\n 'personalization_storage',\n 'api_only',\n ])\n .optional()\n .describe(\n 'The consent the script waits for. api_only: it waits until the page tells it.',\n ),\n },\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: true,\n },\n },\n async (input) => {\n try {\n const site = await client.updateSiteTracking(input);\n\n return answer(\n `${site.created ? 'Set tracking up.' : 'Updated tracking.'}\\n\\n${describeSiteTracking(site)}`,\n site,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n}\n\nfunction describeForm(form: PlacementForm) {\n return [\n `**${form.name}** (id ${form.id})`,\n `Link: ${form.url}`,\n codeBlock(form.embed),\n ].join('\\n');\n}\n\nfunction describeBookingPage(page: PlacementBookingPage) {\n return [\n `**${page.name}** (${page.kind}${page.id ? `, id ${page.id}` : ''})`,\n `Link: ${page.url}`,\n page.embed ? codeBlock(page.embed) : 'No embed: link to it.',\n ].join('\\n');\n}\n\nfunction describeChatWidget(widget: PlacementChatWidget) {\n return [\n `**${widget.name}** (${widget.slug}, ${widget.status}, id ${widget.id})`,\n `Allowed sites: ${\n widget.allowedOrigins.length > 0\n ? widget.allowedOrigins.join(', ')\n : 'any site'\n }`,\n widget.embed\n ? codeBlock(widget.embed)\n : 'No snippet until the widget is published.',\n ].join('\\n');\n}\n\nfunction describeSiteTracking(site: PlacementSiteTracking) {\n return [\n `Hosts: ${\n site.allowedHosts.length > 0\n ? site.allowedHosts.join(', ')\n : 'none — no visit is counted until a host is added'\n }`,\n `Waits for consent: ${site.consentSignal}`,\n `Last visit received: ${site.lastReceivedAt ?? 'never'}`,\n codeBlock(site.embed),\n ].join('\\n');\n}\n\nfunction more(nextCursor: string | null) {\n return nextCursor ? `More on the next page: cursor ${nextCursor}` : '';\n}\n","import { CrmApiError } from '@solumflow-app/crm-client';\n\n/**\n * A tool's answer: a short text for the model to read, and the API's own JSON\n * beside it for a client that wants the fields.\n */\nexport function answer(text: string, data: unknown) {\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: { result: data },\n };\n}\n\n/**\n * A refusal from the API, said in a way an agent can act on. The code is\n * kept, so the agent can tell \"the module is off\" from \"the key cannot do\n * this\"; the fields name what in the input was wrong.\n */\nexport function refusal(error: unknown) {\n if (!(error instanceof CrmApiError)) {\n throw error;\n }\n\n const lines = [`${error.code}: ${error.message}`];\n const next = NEXT_STEP[error.code];\n\n if (next) {\n lines.push(next);\n }\n\n for (const field of error.fields) {\n lines.push(`- ${field.path}: ${field.message}`);\n }\n\n return {\n isError: true,\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n}\n\nconst NEXT_STEP: Partial<Record<CrmApiError['code'], string>> = {\n unauthorized:\n 'The key is not recognised or was revoked. Ask the account owner for a new secret key.',\n forbidden:\n 'The key may not do this. It needs to be a secret key with the scope \"website:manage\".',\n feature_unavailable:\n 'The module this needs is switched off for the account. Ask somebody to switch it on in the app.',\n rate_limited: 'Too many requests. Wait a moment and try again.',\n internal_error: 'Something went wrong in the app. Trying again is safe.',\n};\n\n/** A snippet as a fenced block, so a model copies it whole. */\nexport function codeBlock(snippet: string) {\n return `\\`\\`\\`html\\n${snippet}\\n\\`\\`\\``;\n}\n","/**\n * Where the server finds its account: a secret key and the app's address,\n * both from the environment. Every tool needs `website:manage`, which only a\n * secret (`crms_`) key can hold, so a publishable key is refused here rather\n * than on the first call.\n *\n * The address is required, not defaulted. The same server talks to a\n * production app, a reseller's own domain or a local one, and guessing the\n * wrong one sends the key somewhere it was never meant to go.\n */\nexport function readServerConfig(env: Record<string, string | undefined>) {\n const apiKey = env.SOLUMFLOW_API_KEY?.trim() ?? '';\n const baseUrl = env.SOLUMFLOW_API_URL?.trim().replace(/\\/+$/, '') ?? '';\n\n if (!apiKey) {\n return {\n ok: false as const,\n error:\n 'SOLUMFLOW_API_KEY is not set. Make a secret key with the scope ' +\n '\"website:manage\" in the app (Settings → API keys) and pass it in the ' +\n 'environment.',\n };\n }\n\n if (!apiKey.startsWith('crms_')) {\n return {\n ok: false as const,\n error:\n 'SOLUMFLOW_API_KEY is not a secret key. Placing things on a website ' +\n 'needs a `crms_` key with the scope \"website:manage\"; a `crmp_` key ' +\n 'can only read the catalogue.',\n };\n }\n\n if (!/^https?:\\/\\/[^/]+$/.test(baseUrl)) {\n return {\n ok: false as const,\n error:\n 'SOLUMFLOW_API_URL is not set to the app’s address, such as ' +\n '\"https://app.example.com\" (an origin, no path).',\n };\n }\n\n return { ok: true as const, apiKey, baseUrl };\n}\n"],"mappings":"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","names":["page"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/catalog-tools.ts","../src/tool-answers.ts","../src/configurator-tools.ts","../../features/product-configurator/src/shared/configurator-limits.ts","../../features/product-configurator/src/shared/configurator-issue.ts","../../features/product-configurator/src/shared/evaluate-formula.ts","../../features/product-configurator/src/shared/geometry-document.schema.ts","../../features/product-configurator/src/shared/geometry-document-problems.ts","../../features/product-configurator/src/shared/configurator-structure.schema.ts","../../features/product-configurator/src/shared/configurator-value.schema.ts","../../features/product-configurator/src/shared/resolve-configuration.ts","../../features/product-configurator/src/shared/expand-geometry.ts","../src/placement-tools.ts","../src/server-config.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createClient } from '@solumflow-app/crm-client';\n\nimport { registerCatalogTools } from './catalog-tools';\nimport { registerConfiguratorTools } from './configurator-tools';\nimport { registerPlacementTools } from './placement-tools';\nimport { readServerConfig } from './server-config';\n\n/**\n * An MCP server over stdio that lets an agent place an account's forms,\n * booking pages, chat and tracking on a website, and read the catalogue's\n * fields and products to build their pages. It holds no rules of its own:\n * every tool is one call of the public API through the published client.\n *\n * Errors go to stderr; stdout belongs to the protocol.\n */\nasync function main() {\n const config = readServerConfig(process.env);\n\n if (!config.ok) {\n console.error(`crm-mcp-server: ${config.error}`);\n process.exit(1);\n }\n\n const server = new McpServer({\n name: 'crm-mcp-server',\n version: '0.3.0',\n });\n\n const client = createClient({\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n });\n\n registerPlacementTools(server, client);\n registerCatalogTools(server, client);\n registerConfiguratorTools(server, client);\n\n await server.connect(new StdioServerTransport());\n}\n\nmain().catch((error: unknown) => {\n console.error('crm-mcp-server:', error);\n process.exit(1);\n});\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CatalogField, CrmClient } from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport { answer, refusal } from './tool-answers';\n\n/** The part of the client these tools call; a test hands in a fake of exactly this. */\nexport type CatalogClient = Pick<CrmClient, 'getFields' | 'getProduct'>;\n\n/**\n * What an agent needs to know about the content blocks of a product, without\n * reading documentation. Said once, in both tool descriptions: the field list\n * tells how each block is built, the product carries what is in it.\n */\nconst CONTENT_BLOCKS =\n 'Besides plain values (text, number, choice), a product can carry content ' +\n 'blocks, three field types: \"content_text\" is { html, links? } with ' +\n 'already-sanitised html; \"cta\" is { heading, body, primary: {label,url}, ' +\n 'secondary? }, a call to action; \"repeater\" is { items: [{ id, ...one ' +\n 'value per item field }] } and covers lists, accordions (FAQ), downloads ' +\n 'and testimonials. A repeater’s field says how to draw it: \"display\" is ' +\n '\"list\" or \"accordion\", \"preset\" names where it started (list, faq, ' +\n 'downloads, testimonials), \"itemFields\" is the structure of one item ' +\n '(key, label, type: text, richtext, url {label,url}, icon {set,name}, ' +\n 'image or file {url,name,size,contentType}) and \"titleKey\" is the item ' +\n 'field that names an item, such as the question of a FAQ. A \"configurator\" ' +\n 'is a 3D product configurator: a listing carries only { configurator: true }, ' +\n 'a product\\u2019s own page the whole document (see read_configurator). ' +\n 'A block that is ' +\n 'public but empty is null; a block that is not public is not there at all.';\n\nconst slugOrId = z\n .string()\n .min(1)\n .max(200)\n .describe('The address (slug) or the id of the product.');\n\nexport function registerCatalogTools(server: McpServer, client: CatalogClient) {\n server.registerTool(\n 'list_catalog_fields',\n {\n title: 'List the public fields of the catalogue',\n description:\n 'The custom fields the catalogue publishes on its products, with their ' +\n 'type, choices and, for a content block, how it is built. Read this ' +\n 'once before building a product page: it says which blocks exist and ' +\n 'what each one looks like. ' +\n CONTENT_BLOCKS,\n inputSchema: {},\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n try {\n const fields = await client.getFields();\n\n return answer(\n fields.length === 0\n ? 'The catalogue publishes no custom fields.'\n : fields.map(describeField).join('\\n'),\n fields,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n\n server.registerTool(\n 'get_product',\n {\n title: 'Read one product',\n description:\n 'One product as its page needs it: name, prices, pictures, ' +\n 'categories, variants and `fields`, keyed by field slug. May be a ' +\n 'minute old. Answers that it is not found for an unknown product. ' +\n CONTENT_BLOCKS,\n inputSchema: { slugOrId },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ slugOrId: wanted }) => {\n try {\n const product = await client.getProduct(wanted);\n\n if (!product) {\n return answer(`No product \"${wanted}\" on this account.`, null);\n }\n\n const slugs = Object.keys(product.fields);\n\n return answer(\n `${product.name} (${product.slug}), ${slugs.length} public fields: ${slugs.join(', ') || 'none'}.`,\n product,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n}\n\nfunction describeField(field: CatalogField) {\n if (field.type !== 'repeater') {\n return `- ${field.slug}: ${field.label}, ${field.type}`;\n }\n\n const items = (field.itemFields ?? [])\n .map(({ key, type }) => `${key}:${type}`)\n .join(', ');\n const preset = field.preset ? `, ${field.preset}` : '';\n const title = field.titleKey ? `; titled by ${field.titleKey}` : '';\n\n return `- ${field.slug}: ${field.label}, repeater (${field.display ?? 'list'}${preset}; items: ${items}${title})`;\n}\n","import { CrmApiError } from '@solumflow-app/crm-client';\n\n/**\n * A tool's answer: a short text for the model to read, and the API's own JSON\n * beside it for a client that wants the fields.\n */\nexport function answer(text: string, data: unknown) {\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: { result: data },\n };\n}\n\n/**\n * A refusal from the API, said in a way an agent can act on. The code is\n * kept, so the agent can tell \"the module is off\" from \"the key cannot do\n * this\"; the fields name what in the input was wrong.\n */\nexport function refusal(error: unknown) {\n if (!(error instanceof CrmApiError)) {\n throw error;\n }\n\n const lines = [`${error.code}: ${error.message}`];\n const next = NEXT_STEP[error.code];\n\n if (next) {\n lines.push(next);\n }\n\n for (const field of error.fields) {\n lines.push(`- ${field.path}: ${field.message}`);\n }\n\n return {\n isError: true,\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n}\n\nconst NEXT_STEP: Partial<Record<CrmApiError['code'], string>> = {\n unauthorized:\n 'The key is not recognised or was revoked. Ask the account owner for a new secret key.',\n forbidden:\n 'The key may not do this. It needs to be a secret key with the scope \"website:manage\".',\n feature_unavailable:\n 'The module this needs is switched off for the account. Ask somebody to switch it on in the app.',\n rate_limited: 'Too many requests. Wait a moment and try again.',\n internal_error: 'Something went wrong in the app. Trying again is safe.',\n};\n\n/** A snippet as a fenced block, so a model copies it whole. */\nexport function codeBlock(snippet: string) {\n return `\\`\\`\\`html\\n${snippet}\\n\\`\\`\\``;\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nimport {\n type ConfiguratorIssue,\n ConfiguratorStructureSchema,\n emptyConfiguratorValue,\n expandGeometry,\n formatIssuePath,\n GeometryDocumentSchema,\n geometryDocumentProblems,\n resolveConfiguration,\n} from '@kit/product-configurator/shared';\n\nimport type { CatalogClient } from './catalog-tools';\nimport { answer, refusal } from './tool-answers';\n\n/**\n * What an agent needs to write or read a 3D configurator, said once.\n *\n * The shape is data: a tree of building blocks whose measures are formulas\n * over the shape's inputs and constants. An agent drafts that document and\n * checks it here; it never saves one, because this server works with the\n * customer's own key and the database refuses a shape from any writer\n * without a reseller's right behind it. The reseller uploads the checked\n * document in the object builder.\n */\nconst CONFIGURATOR =\n 'A \"configurator\" field holds a 3D product configurator. Its structure has ' +\n '\"geometry\" (the shape document: \"inputs\" bound to parameters, \"constants\", ' +\n 'material \"slots\", a \"root\" tree of building blocks and \"annotations\" for ' +\n 'dimension lines), \"parameters\", \"choiceLists\", \"materials\", \"priceGroups\", ' +\n '\"amounts\", optional \"presets\", \"rules\" and \"priceRules\". Building blocks: ' +\n 'box, rounded_top_box (params.radius), stadium_prism, arc_panel ' +\n '(params.thickness, params.mirror), led_strip, niche_panel (params.niche_x, ' +\n 'niche_y, niche_width, niche_height) and repeat (count, children, index i ' +\n 'or j). Every measure is millimetres; \"at\" is the minimum corner, x left to ' +\n 'right, y bottom to top, z from the wall into the room; \"size\" is width, ' +\n 'height, depth. A measure is a number or a formula with + - * / ( ) and ' +\n 'min, max, floor, ceil, round, abs over the shape’s inputs, constants ' +\n 'and the repeat index. This server can check such a document but never ' +\n 'save it: a reseller uploads it in the object builder.';\n\nconst documentInput = z\n .string()\n .min(2)\n .max(200_000)\n .describe(\n 'The JSON text of a whole configurator structure (it has \"schemaVersion\"), or of a shape document alone (it has \"root\").',\n );\n\nexport function registerConfiguratorTools(\n server: McpServer,\n client: CatalogClient,\n) {\n server.registerTool(\n 'check_configurator_shape',\n {\n title: 'Check a configurator structure or shape document',\n description:\n 'Checks a configurator structure or a shape document exactly as the ' +\n 'object builder will when a reseller uploads it: the outline, every ' +\n 'name a formula reads, bindings, cycles in the rules and the limits. ' +\n 'A whole structure is also expanded at its smallest measures, so a ' +\n 'part that would come out negative is named. Answers with every ' +\n 'problem and where it is, or that the document is sound. ' +\n CONFIGURATOR,\n inputSchema: { document: documentInput },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n ({ document }) => {\n const problems = checkDocument(document);\n\n return answer(\n problems.length === 0\n ? 'The document is sound. A reseller can upload it in the object builder.'\n : [\n `${problems.length} problem(s):`,\n ...problems.map(describeIssue),\n ].join('\\n'),\n { problems },\n );\n },\n );\n\n server.registerTool(\n 'read_configurator',\n {\n title: 'Read the configurator of a product',\n description:\n 'The configurator field of one product as its page receives it: the ' +\n 'structure (texture paths already URLs), the model’s value ' +\n '(ranges, offered options, amounts, presets), the tax a quote uses and ' +\n 'the two hashes a request must send back. Useful to draft a new shape ' +\n 'from an existing one. ' +\n CONFIGURATOR,\n inputSchema: {\n slugOrId: z\n .string()\n .min(1)\n .max(200)\n .describe('The address (slug) or the id of the product.'),\n field: z\n .string()\n .min(1)\n .max(64)\n .describe('The slug of the configurator field.'),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ slugOrId, field }) => {\n try {\n const product = await client.getProduct(slugOrId);\n const value = product?.fields[field];\n\n if (!product) {\n return answer(`No product \"${slugOrId}\" on this account.`, null);\n }\n\n if (!value || typeof value !== 'object' || !('structure' in value)) {\n return answer(\n `The product has no public configurator under \"${field}\".`,\n null,\n );\n }\n\n return answer(`The configurator \"${field}\" of ${product.name}.`, value);\n } catch (error) {\n return refusal(error);\n }\n },\n );\n}\n\n/** Every problem with a structure or a shape document, each with its path. */\nexport function checkDocument(text: string): ConfiguratorIssue[] {\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(text);\n } catch {\n return [{ path: [], code: 'configurator.notJson' }];\n }\n\n if (isRecord(parsed) && 'schemaVersion' in parsed) {\n const structure = ConfiguratorStructureSchema.safeParse(parsed);\n\n if (!structure.success) {\n return structure.error.issues.map(toIssue);\n }\n\n // At the structure's own smallest measures: the shape must hold there too.\n const { configuration } = resolveConfiguration({\n structure: structure.data,\n value: emptyConfiguratorValue(),\n selection: { values: {} },\n });\n const expanded = expandGeometry(\n structure.data.geometry,\n configuration.inputs,\n );\n\n return expanded.ok\n ? []\n : expanded.issues.map((problem) => ({\n ...problem,\n path: ['geometry', ...problem.path],\n }));\n }\n\n const geometry = GeometryDocumentSchema.safeParse(parsed);\n\n if (!geometry.success) {\n return geometry.error.issues.map(toIssue);\n }\n\n return geometryDocumentProblems(geometry.data);\n}\n\n/** A zod issue as a configurator issue; a structure check carries its params. */\nfunction toIssue(problem: z.core.$ZodIssue): ConfiguratorIssue {\n const params = (problem as { params?: Record<string, string | number> })\n .params;\n\n return {\n path: problem.path.map((part) =>\n typeof part === 'number' ? part : String(part),\n ),\n code: problem.message,\n ...(params ? { params } : {}),\n };\n}\n\nfunction describeIssue(problem: ConfiguratorIssue) {\n const where = formatIssuePath(problem.path) || '(the document)';\n const params = problem.params\n ? ` (${Object.entries(problem.params)\n .map(([key, value]) => `${key}: ${value}`)\n .join(', ')})`\n : '';\n\n return `- ${where}: ${problem.code}${params}`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","/**\n * How large a configurator may get. The byte limits are repeated in SQL\n * (`kit.crm_content_value_problem`), so the browser, the app and the database\n * refuse the same document; change both or neither.\n *\n * The counts exist so that one structure cannot make every product page, every\n * request and every order slow: a resolver walks every parameter and rule on\n * each keystroke, and the 3D scene draws every part.\n */\nexport const CONFIGURATOR_LIMITS = {\n structureBytes: 64 * 1024,\n valueBytes: 32 * 1024,\n parameters: 60,\n tabs: 12,\n choiceLists: 40,\n optionsPerList: 60,\n materials: 120,\n priceGroups: 20,\n amounts: 20,\n rules: 80,\n priceRules: 20,\n presets: 60,\n formulaChars: 200,\n formulaNodes: 64,\n formulaDepth: 8,\n geometryInputs: 60,\n geometryConstants: 60,\n geometrySlots: 20,\n geometryNodes: 200,\n expandedParts: 200,\n repeatCount: 12,\n repeatDepth: 2,\n annotations: 30,\n} as const;\n\n/**\n * The size of a document the way the database measures it:\n * `octet_length(value::text)` of a jsonb, which prints `\": \"` after every key\n * and `\", \"` between elements where `JSON.stringify` prints no space. Counting\n * those spaces here is what lets the browser, the app and the database refuse\n * at the same byte rather than the database refusing a document the screen\n * just accepted.\n */\nexport function jsonByteLength(value: unknown) {\n return (\n new TextEncoder().encode(JSON.stringify(value)).length + jsonbSpacing(value)\n );\n}\n\nfunction jsonbSpacing(value: unknown): number {\n if (Array.isArray(value)) {\n return (\n Math.max(0, value.length - 1) +\n value.reduce<number>((sum, item) => sum + jsonbSpacing(item), 0)\n );\n }\n\n if (value !== null && typeof value === 'object') {\n const entries = Object.values(value as Record<string, unknown>).filter(\n (item) => item !== undefined,\n );\n\n return (\n entries.length +\n Math.max(0, entries.length - 1) +\n entries.reduce<number>((sum, item) => sum + jsonbSpacing(item), 0)\n );\n }\n\n return 0;\n}\n","/**\n * One thing wrong with a structure, a value or a selection.\n *\n * `code` is a translation key under `configurator.*` and `params` fills in\n * its blanks; `path` points at the field, in the document's own keys\n * (`['geometry', 'root', 'onderkast', 'size', 1]`), so a screen can name it\n * and an API can return it as is. The package itself never writes a\n * sentence: the app speaks Dutch and English, a customer's site its own.\n */\nexport interface ConfiguratorIssue {\n path: (string | number)[];\n code: string;\n params?: Record<string, string | number>;\n}\n\nexport function issue(\n path: (string | number)[],\n code: string,\n params?: Record<string, string | number>,\n): ConfiguratorIssue {\n return params ? { path, code, params } : { path, code };\n}\n\n/** `root.onderkast.size[1]`, the way an issue's path reads in a message. */\nexport function formatIssuePath(path: readonly (string | number)[]) {\n return path.reduce<string>((text, part) => {\n if (typeof part === 'number') {\n return `${text}[${part}]`;\n }\n\n return text ? `${text}.${part}` : part;\n }, '');\n}\n","import { CONFIGURATOR_LIMITS } from './configurator-limits';\n\n/**\n * The arithmetic a configurator may contain: numbers, names, `+ - * /`\n * (`×` and `÷` read the same), parentheses, unary minus, and the functions\n * `min`, `max`, `floor`, `ceil`, `round` and `abs`.\n *\n * Parsed by hand and never with `eval` or `Function`: a structure is written\n * by a reseller and evaluated in every visitor's browser and on our server, so\n * a formula must not be able to do anything but count.\n *\n * A formula is either a number or a string. Its names are whatever the place\n * it stands in offers -- the inputs and constants of a shape, the number\n * parameters and amounts of a price rule -- and the caller checks them with\n * `formulaNames` before anything is evaluated.\n */\nexport type Formula = number | string;\n\ntype Node =\n | { kind: 'number'; value: number }\n | { kind: 'name'; name: string }\n | { kind: 'negate'; operand: Node }\n | { kind: 'binary'; operator: '+' | '-' | '*' | '/'; left: Node; right: Node }\n | { kind: 'call'; fn: FormulaFunction; args: Node[] };\n\nconst FUNCTIONS = ['min', 'max', 'floor', 'ceil', 'round', 'abs'] as const;\ntype FormulaFunction = (typeof FUNCTIONS)[number];\n\nexport interface ParsedFormula {\n readonly source: Formula;\n readonly root: Node;\n /** Every name the formula reads, in order of first appearance. */\n readonly names: readonly string[];\n}\n\nexport type FormulaErrorCode =\n | 'formula_too_long'\n | 'formula_syntax'\n | 'formula_too_many_nodes'\n | 'formula_too_deep'\n | 'formula_unknown_function';\n\nexport type FormulaParseResult =\n | { ok: true; formula: ParsedFormula }\n | { ok: false; code: FormulaErrorCode; detail?: string };\n\nexport type FormulaEvaluation =\n | { ok: true; value: number }\n | { ok: false; code: 'formula_unknown_name'; name: string }\n | { ok: false; code: 'formula_not_finite' };\n\ntype Token =\n | { type: 'number'; value: number }\n | { type: 'name'; value: string }\n | { type: 'op'; value: '+' | '-' | '*' | '/' | '(' | ')' | ',' };\n\nconst NAME_START = /[a-z_]/;\nconst NAME_PART = /[a-z0-9_]/;\n\nfunction tokenize(source: string): Token[] | null {\n const tokens: Token[] = [];\n let index = 0;\n\n while (index < source.length) {\n const char = source.charAt(index);\n\n if (char === ' ' || char === '\\t' || char === '\\n') {\n index += 1;\n continue;\n }\n\n if (/[0-9.]/.test(char)) {\n let end = index;\n\n while (end < source.length && /[0-9.]/.test(source.charAt(end))) {\n end += 1;\n }\n\n const text = source.slice(index, end);\n\n if (!/^(\\d+(\\.\\d+)?|\\.\\d+)$/.test(text)) {\n return null;\n }\n\n tokens.push({ type: 'number', value: Number(text) });\n index = end;\n continue;\n }\n\n if (NAME_START.test(char)) {\n let end = index;\n\n while (end < source.length && NAME_PART.test(source.charAt(end))) {\n end += 1;\n }\n\n tokens.push({ type: 'name', value: source.slice(index, end) });\n index = end;\n continue;\n }\n\n const operator =\n char === '×' ? '*' : char === '÷' ? '/' : char === '−' ? '-' : char;\n\n if (\n operator === '+' ||\n operator === '-' ||\n operator === '*' ||\n operator === '/' ||\n operator === '(' ||\n operator === ')' ||\n operator === ','\n ) {\n tokens.push({ type: 'op', value: operator });\n index += 1;\n continue;\n }\n\n return null;\n }\n\n return tokens;\n}\n\nclass FormulaParser {\n private position = 0;\n private nodes = 0;\n private depth = 0;\n readonly names: string[] = [];\n\n constructor(private readonly tokens: Token[]) {}\n\n parse(): Node {\n const node = this.expression();\n\n if (this.position !== this.tokens.length) {\n throw new FormulaSyntaxError('formula_syntax');\n }\n\n return node;\n }\n\n private count<T extends Node>(node: T): T {\n this.nodes += 1;\n\n if (this.nodes > CONFIGURATOR_LIMITS.formulaNodes) {\n throw new FormulaSyntaxError('formula_too_many_nodes');\n }\n\n return node;\n }\n\n private peek() {\n return this.tokens[this.position];\n }\n\n private takeOperator(value: string) {\n const token = this.peek();\n\n if (token?.type === 'op' && token.value === value) {\n this.position += 1;\n return true;\n }\n\n return false;\n }\n\n private expression(): Node {\n let left = this.term();\n\n for (;;) {\n if (this.takeOperator('+')) {\n left = this.count({\n kind: 'binary',\n operator: '+',\n left,\n right: this.term(),\n });\n } else if (this.takeOperator('-')) {\n left = this.count({\n kind: 'binary',\n operator: '-',\n left,\n right: this.term(),\n });\n } else {\n return left;\n }\n }\n }\n\n private term(): Node {\n let left = this.unary();\n\n for (;;) {\n if (this.takeOperator('*')) {\n left = this.count({\n kind: 'binary',\n operator: '*',\n left,\n right: this.unary(),\n });\n } else if (this.takeOperator('/')) {\n left = this.count({\n kind: 'binary',\n operator: '/',\n left,\n right: this.unary(),\n });\n } else {\n return left;\n }\n }\n }\n\n private unary(): Node {\n if (this.takeOperator('-')) {\n return this.count({ kind: 'negate', operand: this.unary() });\n }\n\n if (this.takeOperator('+')) {\n return this.unary();\n }\n\n return this.primary();\n }\n\n private nested<T>(read: () => T): T {\n this.depth += 1;\n\n if (this.depth > CONFIGURATOR_LIMITS.formulaDepth) {\n throw new FormulaSyntaxError('formula_too_deep');\n }\n\n const result = read();\n this.depth -= 1;\n return result;\n }\n\n private primary(): Node {\n const token = this.peek();\n\n if (!token) {\n throw new FormulaSyntaxError('formula_syntax');\n }\n\n if (token.type === 'number') {\n this.position += 1;\n return this.count({ kind: 'number', value: token.value });\n }\n\n if (token.type === 'name') {\n this.position += 1;\n\n if (this.takeOperator('(')) {\n if (!(FUNCTIONS as readonly string[]).includes(token.value)) {\n throw new FormulaSyntaxError('formula_unknown_function', token.value);\n }\n\n const fn = token.value as FormulaFunction;\n\n return this.nested(() => {\n const args: Node[] = [this.expression()];\n\n while (this.takeOperator(',')) {\n args.push(this.expression());\n }\n\n if (!this.takeOperator(')')) {\n throw new FormulaSyntaxError('formula_syntax');\n }\n\n const variadic = fn === 'min' || fn === 'max';\n\n if (!variadic && args.length !== 1) {\n throw new FormulaSyntaxError('formula_syntax', fn);\n }\n\n return this.count({ kind: 'call', fn, args });\n });\n }\n\n if (!this.names.includes(token.value)) {\n this.names.push(token.value);\n }\n\n return this.count({ kind: 'name', name: token.value });\n }\n\n if (token.value === '(') {\n this.position += 1;\n\n return this.nested(() => {\n const inner = this.expression();\n\n if (!this.takeOperator(')')) {\n throw new FormulaSyntaxError('formula_syntax');\n }\n\n return inner;\n });\n }\n\n throw new FormulaSyntaxError('formula_syntax');\n }\n}\n\nclass FormulaSyntaxError extends Error {\n constructor(\n readonly code: FormulaErrorCode,\n readonly detail?: string,\n ) {\n super(code);\n }\n}\n\nconst parsedCache = new Map<string, FormulaParseResult>();\n\nexport function parseFormula(source: Formula): FormulaParseResult {\n if (typeof source === 'number') {\n if (!Number.isFinite(source)) {\n return { ok: false, code: 'formula_syntax' };\n }\n\n return {\n ok: true,\n formula: { source, root: { kind: 'number', value: source }, names: [] },\n };\n }\n\n const cached = parsedCache.get(source);\n\n if (cached) {\n return cached;\n }\n\n const result = parseUncached(source);\n\n // Formulas repeat across every keystroke of a visitor; a small cache keeps\n // the resolver from re-tokenising the same few hundred strings each time.\n if (parsedCache.size > 2000) {\n parsedCache.clear();\n }\n\n parsedCache.set(source, result);\n return result;\n}\n\nfunction parseUncached(source: string): FormulaParseResult {\n if (source.length > CONFIGURATOR_LIMITS.formulaChars) {\n return { ok: false, code: 'formula_too_long' };\n }\n\n const tokens = tokenize(source);\n\n if (!tokens || tokens.length === 0) {\n return { ok: false, code: 'formula_syntax' };\n }\n\n const parser = new FormulaParser(tokens);\n\n try {\n const root = parser.parse();\n return { ok: true, formula: { source, root, names: parser.names } };\n } catch (error) {\n if (error instanceof FormulaSyntaxError) {\n return { ok: false, code: error.code, detail: error.detail };\n }\n\n throw error;\n }\n}\n\nexport function evaluateFormula(\n formula: ParsedFormula,\n scope: Readonly<Record<string, number>>,\n): FormulaEvaluation {\n for (const name of formula.names) {\n if (!Object.prototype.hasOwnProperty.call(scope, name)) {\n return { ok: false, code: 'formula_unknown_name', name };\n }\n }\n\n const value = evaluateNode(formula.root, scope);\n\n if (!Number.isFinite(value)) {\n return { ok: false, code: 'formula_not_finite' };\n }\n\n return { ok: true, value };\n}\n\nfunction evaluateNode(\n node: Node,\n scope: Readonly<Record<string, number>>,\n): number {\n switch (node.kind) {\n case 'number':\n return node.value;\n case 'name':\n return scope[node.name] ?? Number.NaN;\n case 'negate':\n return -evaluateNode(node.operand, scope);\n case 'binary':\n return applyOperator(\n node.operator,\n evaluateNode(node.left, scope),\n evaluateNode(node.right, scope),\n );\n case 'call':\n return applyFunction(\n node.fn,\n node.args.map((arg) => evaluateNode(arg, scope)),\n );\n }\n}\n\nfunction applyOperator(\n operator: '+' | '-' | '*' | '/',\n left: number,\n right: number,\n) {\n switch (operator) {\n case '+':\n return left + right;\n case '-':\n return left - right;\n case '*':\n return left * right;\n case '/':\n return left / right;\n }\n}\n\nfunction applyFunction(fn: FormulaFunction, args: number[]) {\n const first = args[0] ?? Number.NaN;\n\n switch (fn) {\n case 'min':\n return Math.min(...args);\n case 'max':\n return Math.max(...args);\n case 'floor':\n return Math.floor(first);\n case 'ceil':\n return Math.ceil(first);\n case 'round':\n return Math.round(first);\n case 'abs':\n return Math.abs(first);\n }\n}\n\n/** The names a formula reads, or the reason it does not parse. */\nexport function formulaNames(\n source: Formula,\n):\n | { ok: true; names: readonly string[] }\n | { ok: false; code: FormulaErrorCode } {\n const parsed = parseFormula(source);\n return parsed.ok ? { ok: true, names: parsed.formula.names } : parsed;\n}\n\n/** Parses and evaluates in one go, for the places that hold a raw formula. */\nexport function evaluateFormulaSource(\n source: Formula,\n scope: Readonly<Record<string, number>>,\n): FormulaEvaluation | { ok: false; code: FormulaErrorCode } {\n const parsed = parseFormula(source);\n return parsed.ok ? evaluateFormula(parsed.formula, scope) : parsed;\n}\n","import { z } from 'zod';\n\nimport { CONFIGURATOR_LIMITS } from './configurator-limits';\nimport type { Formula } from './evaluate-formula';\n\n/**\n * A shape document: what a piece of furniture is made of, as data.\n *\n * The motor (expansion, the 3D renderer, the dimension sketch) knows a fixed\n * set of building blocks; a document says which blocks, how large, where and\n * in which material slot, with every measure a formula over the document's\n * inputs and constants. A new customer or a new kind of furniture is a new\n * document, never a release -- and a new kind of block is a small change to\n * the motor that every customer gets.\n *\n * Coordinates are millimetres. `at` is a block's minimum corner: x runs left\n * to right, y bottom to top, z from the wall (0) towards the room. `size` is\n * width, height and depth along those axes.\n */\n\nexport const CONFIGURATOR_NAME_PATTERN = /^[a-z][a-z0-9_]{0,39}$/;\nexport const CONFIGURATOR_CODE_PATTERN = /^[a-z0-9][a-z0-9_-]{0,39}$/;\n\nexport const ConfiguratorNameSchema = z\n .string()\n .regex(CONFIGURATOR_NAME_PATTERN, 'configurator.invalidName');\n\nexport const ConfiguratorCodeSchema = z\n .string()\n .regex(CONFIGURATOR_CODE_PATTERN, 'configurator.invalidCode');\n\nexport const FormulaSchema = z.union([\n z.number().finite(),\n z\n .string()\n .trim()\n .min(1)\n .max(CONFIGURATOR_LIMITS.formulaChars, 'configurator.formulaTooLong'),\n]);\n\nconst Vector3Schema = z.tuple([FormulaSchema, FormulaSchema, FormulaSchema]);\n\nexport const PRIMITIVE_TYPES = [\n 'box',\n 'rounded_top_box',\n 'stadium_prism',\n 'arc_panel',\n 'led_strip',\n 'niche_panel',\n] as const;\n\nexport type PrimitiveType = (typeof PRIMITIVE_TYPES)[number];\n\n/**\n * The formulas a block of each type reads beyond its size and position, and\n * whether it must have them. A key a type does not know is refused, so a typo\n * in a document is an error at upload and not a block that silently ignores\n * what the document said.\n */\nexport const PRIMITIVE_PARAMS: Record<\n PrimitiveType,\n { required: readonly string[]; optional: readonly string[] }\n> = {\n box: { required: [], optional: [] },\n // The front top edge, rounded over its whole width.\n rounded_top_box: { required: ['radius'], optional: [] },\n // A stadium (a rectangle with half-round ends) seen from the front,\n // extruded over the depth.\n stadium_prism: { required: [], optional: [] },\n // A quarter of an elliptical shell in plan view, filling the block's width\n // and depth; `mirror` 0 bends it towards the right, 1 towards the left.\n arc_panel: { required: ['thickness'], optional: ['mirror'] },\n // A thin strip that gives light.\n led_strip: { required: [], optional: [] },\n // A wall with a rectangular opening, measured from the block's own corner.\n niche_panel: {\n required: ['niche_x', 'niche_y', 'niche_width', 'niche_height'],\n optional: [],\n },\n};\n\nexport interface GeometryPrimitiveNode {\n id: string;\n type: PrimitiveType;\n slot: string;\n size: [Formula, Formula, Formula];\n at: [Formula, Formula, Formula];\n visible?: Formula;\n params?: Record<string, Formula>;\n}\n\nexport interface GeometryRepeatNode {\n id: string;\n type: 'repeat';\n count: Formula;\n /** The name of the running index inside; `i` unless an inner repeat needs `j`. */\n index?: 'i' | 'j';\n visible?: Formula;\n children: GeometryNode[];\n}\n\nexport type GeometryNode = GeometryPrimitiveNode | GeometryRepeatNode;\n\nconst PrimitiveNodeSchema = z.strictObject({\n id: ConfiguratorNameSchema,\n type: z.enum(PRIMITIVE_TYPES),\n slot: ConfiguratorNameSchema,\n size: Vector3Schema,\n at: Vector3Schema,\n visible: FormulaSchema.optional(),\n params: z.record(ConfiguratorNameSchema, FormulaSchema).optional(),\n});\n\nexport const GeometryNodeSchema: z.ZodType<GeometryNode> = z.lazy(() =>\n z.union([\n PrimitiveNodeSchema,\n z.strictObject({\n id: ConfiguratorNameSchema,\n type: z.literal('repeat'),\n count: FormulaSchema,\n index: z.enum(['i', 'j']).optional(),\n visible: FormulaSchema.optional(),\n children: z.array(GeometryNodeSchema).min(1),\n }),\n ]),\n);\n\n/**\n * How a shape input gets its number: from a parameter. A number parameter\n * gives its value, a switch gives 1 or 0, and a choice gives the number its\n * `map` names for the chosen code -- every code of the list needs one.\n */\nexport const GeometryBindingSchema = z.strictObject({\n param: ConfiguratorNameSchema,\n map: z.record(ConfiguratorCodeSchema, z.number().finite()).optional(),\n});\n\nexport const GeometryInputSchema = z.strictObject({\n name: ConfiguratorNameSchema,\n binding: GeometryBindingSchema,\n});\n\n/**\n * Where a block's material comes from: a material parameter (the visitor\n * chooses), one material of the library (always the same), or a plain colour.\n */\nexport const GeometrySlotSchema = z.strictObject({\n name: ConfiguratorNameSchema,\n param: ConfiguratorNameSchema.optional(),\n material: ConfiguratorCodeSchema.optional(),\n color: z\n .string()\n .regex(/^#[0-9a-f]{6}$/i, 'configurator.invalidColor')\n .optional(),\n});\n\n/** A dimension line in the front view, from `from` to `to` along `axis`, drawn at `at`. */\nexport const GeometryAnnotationSchema = z.strictObject({\n id: ConfiguratorNameSchema,\n axis: z.enum(['x', 'y']),\n from: FormulaSchema,\n to: FormulaSchema,\n at: FormulaSchema,\n label: z.string().trim().min(1).max(80).optional(),\n});\n\nexport const GeometryDocumentSchema = z.strictObject({\n /** Rises with every new upload, so a product page can say which one it shows. */\n version: z.number().int().min(1),\n /** The starter document this one was copied from, for information only. */\n startedFrom: z.string().trim().max(80).optional(),\n inputs: z.array(GeometryInputSchema).max(CONFIGURATOR_LIMITS.geometryInputs),\n constants: z.record(ConfiguratorNameSchema, z.number().finite()),\n slots: z\n .array(GeometrySlotSchema)\n .min(1)\n .max(CONFIGURATOR_LIMITS.geometrySlots),\n root: z.array(GeometryNodeSchema).min(1),\n camera: z\n .strictObject({\n /** Degrees around the vertical axis, 0 is straight on. */\n azimuth: z.number().min(-90).max(90),\n /** Degrees above the horizon. */\n elevation: z.number().min(-10).max(80),\n })\n .optional(),\n annotations: z\n .array(GeometryAnnotationSchema)\n .max(CONFIGURATOR_LIMITS.annotations),\n});\n\nexport type GeometryDocument = z.infer<typeof GeometryDocumentSchema>;\nexport type GeometrySlot = z.infer<typeof GeometrySlotSchema>;\nexport type GeometryAnnotation = z.infer<typeof GeometryAnnotationSchema>;\n","import { type ConfiguratorIssue, issue } from './configurator-issue';\nimport { CONFIGURATOR_LIMITS } from './configurator-limits';\nimport { type Formula, formulaNames } from './evaluate-formula';\nimport {\n type GeometryDocument,\n type GeometryNode,\n PRIMITIVE_PARAMS,\n} from './geometry-document.schema';\n\n/**\n * What can be said about a shape document without any numbers: every name a\n * formula reads exists where it stands, every block names a slot, every input\n * is bound to a parameter of the right kind, ids are unique, and repeats stay\n * within their limits. What only numbers can show -- a negative width, a\n * repeat of 30 -- is `expandGeometry`'s to report.\n */\nexport interface GeometryParameterInfo {\n kind: 'number' | 'choice' | 'material' | 'toggle';\n /** For a choice parameter, every code its list holds. */\n codes?: readonly string[];\n}\n\nexport function geometryDocumentProblems(\n document: GeometryDocument,\n context: {\n /** Absent when a document is checked on its own, before it meets a structure. */\n parameters?: ReadonlyMap<string, GeometryParameterInfo>;\n materialCodes?: ReadonlySet<string>;\n path?: (string | number)[];\n } = {},\n): ConfiguratorIssue[] {\n const base = context.path ?? [];\n const problems: ConfiguratorIssue[] = [];\n const constantNames = Object.keys(document.constants);\n\n if (constantNames.length > CONFIGURATOR_LIMITS.geometryConstants) {\n problems.push(\n issue([...base, 'constants'], 'configurator.tooMany', {\n max: CONFIGURATOR_LIMITS.geometryConstants,\n }),\n );\n }\n\n const inputNames = new Set<string>();\n\n document.inputs.forEach((input, index) => {\n const path = [...base, 'inputs', index];\n\n if (inputNames.has(input.name) || constantNames.includes(input.name)) {\n problems.push(\n issue([...path, 'name'], 'configurator.duplicateName', {\n name: input.name,\n }),\n );\n }\n\n if (input.name === 'i' || input.name === 'j') {\n problems.push(\n issue([...path, 'name'], 'configurator.reservedName', {\n name: input.name,\n }),\n );\n }\n\n inputNames.add(input.name);\n\n if (!context.parameters) {\n return;\n }\n\n const parameter = context.parameters.get(input.binding.param);\n\n if (!parameter) {\n problems.push(\n issue([...path, 'binding', 'param'], 'configurator.inputUnbound', {\n name: input.name,\n param: input.binding.param,\n }),\n );\n return;\n }\n\n if (parameter.kind === 'material') {\n problems.push(\n issue(\n [...path, 'binding', 'param'],\n 'configurator.inputCannotBindMaterial',\n {\n name: input.name,\n },\n ),\n );\n }\n\n if (parameter.kind === 'choice') {\n const missing = (parameter.codes ?? []).filter(\n (code) => input.binding.map?.[code] === undefined,\n );\n\n if (missing.length > 0) {\n problems.push(\n issue(\n [...path, 'binding', 'map'],\n 'configurator.choiceMapIncomplete',\n {\n name: input.name,\n codes: missing.join(', '),\n },\n ),\n );\n }\n } else if (input.binding.map) {\n problems.push(\n issue([...path, 'binding', 'map'], 'configurator.mapOnlyForChoice', {\n name: input.name,\n }),\n );\n }\n });\n\n const slotNames = new Set<string>();\n\n document.slots.forEach((slot, index) => {\n const path = [...base, 'slots', index];\n\n if (slotNames.has(slot.name)) {\n problems.push(\n issue([...path, 'name'], 'configurator.duplicateName', {\n name: slot.name,\n }),\n );\n }\n\n slotNames.add(slot.name);\n\n const sources = [slot.param, slot.material, slot.color].filter(\n (source) => source !== undefined,\n );\n\n if (sources.length !== 1) {\n problems.push(\n issue(path, 'configurator.slotNeedsOneSource', { name: slot.name }),\n );\n }\n\n if (slot.param && context.parameters) {\n const parameter = context.parameters.get(slot.param);\n\n if (parameter?.kind !== 'material') {\n problems.push(\n issue([...path, 'param'], 'configurator.slotParamNotMaterial', {\n param: slot.param,\n }),\n );\n }\n }\n\n if (\n slot.material &&\n context.materialCodes &&\n !context.materialCodes.has(slot.material)\n ) {\n problems.push(\n issue([...path, 'material'], 'configurator.unknownMaterial', {\n code: slot.material,\n }),\n );\n }\n });\n\n const shapeNames = new Set([...inputNames, ...constantNames]);\n const ids = new Set<string>();\n let declared = 0;\n\n const checkFormula = (\n formula: Formula,\n path: (string | number)[],\n scope: ReadonlySet<string>,\n ) => {\n const names = formulaNames(formula);\n\n if (!names.ok) {\n problems.push(issue(path, `configurator.${names.code}`));\n return;\n }\n\n for (const name of names.names) {\n if (!scope.has(name)) {\n problems.push(issue(path, 'configurator.unknownName', { name }));\n }\n }\n };\n\n const walk = (\n nodes: readonly GeometryNode[],\n path: (string | number)[],\n scope: ReadonlySet<string>,\n repeatDepth: number,\n ) => {\n for (const node of nodes) {\n const nodePath = [...path, node.id];\n declared += 1;\n\n if (ids.has(node.id)) {\n problems.push(\n issue([...nodePath, 'id'], 'configurator.duplicateId', {\n id: node.id,\n }),\n );\n }\n\n ids.add(node.id);\n\n if (node.visible !== undefined) {\n checkFormula(node.visible, [...nodePath, 'visible'], scope);\n }\n\n if (node.type === 'repeat') {\n checkFormula(node.count, [...nodePath, 'count'], scope);\n\n if (repeatDepth + 1 > CONFIGURATOR_LIMITS.repeatDepth) {\n problems.push(\n issue(nodePath, 'configurator.repeatTooDeep', {\n max: CONFIGURATOR_LIMITS.repeatDepth,\n }),\n );\n continue;\n }\n\n const index = node.index ?? 'i';\n\n if (scope.has(index)) {\n problems.push(\n issue([...nodePath, 'index'], 'configurator.indexInUse', { index }),\n );\n }\n\n walk(\n node.children,\n nodePath,\n new Set([...scope, index]),\n repeatDepth + 1,\n );\n continue;\n }\n\n if (!slotNames.has(node.slot)) {\n problems.push(\n issue([...nodePath, 'slot'], 'configurator.unknownSlot', {\n slot: node.slot,\n }),\n );\n }\n\n node.size.forEach((formula, axis) =>\n checkFormula(formula, [...nodePath, 'size', axis], scope),\n );\n node.at.forEach((formula, axis) =>\n checkFormula(formula, [...nodePath, 'at', axis], scope),\n );\n\n const allowed = PRIMITIVE_PARAMS[node.type];\n const given = node.params ?? {};\n\n for (const required of allowed.required) {\n if (given[required] === undefined) {\n problems.push(\n issue(\n [...nodePath, 'params', required],\n 'configurator.missingBlockParam',\n {\n type: node.type,\n param: required,\n },\n ),\n );\n }\n }\n\n for (const [key, formula] of Object.entries(given)) {\n if (\n !allowed.required.includes(key) &&\n !allowed.optional.includes(key)\n ) {\n problems.push(\n issue(\n [...nodePath, 'params', key],\n 'configurator.unknownBlockParam',\n {\n type: node.type,\n param: key,\n },\n ),\n );\n continue;\n }\n\n checkFormula(formula, [...nodePath, 'params', key], scope);\n }\n }\n };\n\n walk(document.root, [...base, 'root'], shapeNames, 0);\n\n if (declared > CONFIGURATOR_LIMITS.geometryNodes) {\n problems.push(\n issue([...base, 'root'], 'configurator.tooMany', {\n max: CONFIGURATOR_LIMITS.geometryNodes,\n }),\n );\n }\n\n const annotationIds = new Set<string>();\n\n document.annotations.forEach((annotation, index) => {\n const path = [...base, 'annotations', index];\n\n if (annotationIds.has(annotation.id)) {\n problems.push(\n issue([...path, 'id'], 'configurator.duplicateId', {\n id: annotation.id,\n }),\n );\n }\n\n annotationIds.add(annotation.id);\n checkFormula(annotation.from, [...path, 'from'], shapeNames);\n checkFormula(annotation.to, [...path, 'to'], shapeNames);\n checkFormula(annotation.at, [...path, 'at'], shapeNames);\n });\n\n return problems;\n}\n","import { z } from 'zod';\n\nimport { type ConfiguratorIssue, issue } from './configurator-issue';\nimport { CONFIGURATOR_LIMITS, jsonByteLength } from './configurator-limits';\nimport { type Formula, formulaNames } from './evaluate-formula';\nimport {\n type GeometryParameterInfo,\n geometryDocumentProblems,\n} from './geometry-document-problems';\nimport {\n ConfiguratorCodeSchema,\n ConfiguratorNameSchema,\n FormulaSchema,\n GeometryDocumentSchema,\n} from './geometry-document.schema';\n\n/**\n * The structure of a configurator: the setting of one `configurator` field on\n * a product object, built once in the object builder for, say, every\n * \"Haardmeubels\" product. It says what can be chosen and how a choice moves\n * the shape and the price. It holds no amounts: those differ per model and\n * live in each product's value (`configurator-value.schema.ts`).\n */\n\nconst LabelSchema = z.string().trim().min(1).max(80);\n\n/**\n * `{account}/{attribute}/{uuid}.webp|jpg|jpeg` in the configurator's own\n * bucket. The database checks that the first two are the row's own account\n * and attribute; here only the shape is known. The URL is put together by the\n * server, never stored, so a structure cannot point a browser elsewhere.\n */\nexport const CONFIGURATOR_TEXTURE_PATH_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\.(webp|jpg|jpeg)$/;\n\nconst TexturePathSchema = z\n .string()\n .regex(CONFIGURATOR_TEXTURE_PATH_PATTERN, 'configurator.invalidTexturePath');\n\nexport function texturePathBelongsTo(\n path: string,\n accountId: string,\n attributeId: string,\n) {\n return (\n CONFIGURATOR_TEXTURE_PATH_PATTERN.test(path) &&\n path.startsWith(`${accountId.toLowerCase()}/${attributeId.toLowerCase()}/`)\n );\n}\n\nconst ParameterBase = {\n key: ConfiguratorNameSchema,\n label: LabelSchema,\n tab: ConfiguratorNameSchema,\n};\n\nexport const ConfiguratorParameterSchema = z.discriminatedUnion('kind', [\n z.strictObject({\n ...ParameterBase,\n kind: z.literal('number'),\n /** `mm` is a measure, `count` a number of things (compartments). */\n unit: z.enum(['mm', 'count']),\n step: z.number().positive().finite(),\n /** The widest range any model may offer; a model narrows it in its value. */\n min: z.number().finite(),\n max: z.number().finite(),\n }),\n z.strictObject({\n ...ParameterBase,\n kind: z.literal('choice'),\n list: ConfiguratorNameSchema,\n }),\n z.strictObject({ ...ParameterBase, kind: z.literal('material') }),\n z.strictObject({ ...ParameterBase, kind: z.literal('toggle') }),\n]);\n\nexport const ConfiguratorChoiceListSchema = z.strictObject({\n key: ConfiguratorNameSchema,\n label: LabelSchema,\n options: z\n .array(\n z.strictObject({\n code: ConfiguratorCodeSchema,\n label: LabelSchema,\n image: TexturePathSchema.optional(),\n }),\n )\n .min(1)\n .max(CONFIGURATOR_LIMITS.optionsPerList),\n});\n\nexport const ConfiguratorMaterialSchema = z.strictObject({\n code: ConfiguratorCodeSchema,\n label: LabelSchema,\n color: z.string().regex(/^#[0-9a-f]{6}$/i, 'configurator.invalidColor'),\n texture: TexturePathSchema.optional(),\n /** Millimetres one repeat of the texture covers. */\n textureSize: z.number().positive().max(10_000).optional(),\n roughness: z.number().min(0).max(1).optional(),\n metalness: z.number().min(0).max(1).optional(),\n /** How much of what is behind it shows through: glass in front of a flame. Absent is solid. */\n opacity: z.number().min(0.05).max(1).optional(),\n /** Gives light rather than reflecting it: a flame, a LED. */\n emissive: z.boolean().optional(),\n priceGroup: ConfiguratorCodeSchema.optional(),\n});\n\nconst ConditionValueSchema = z.union([\n z.number().finite(),\n z.boolean(),\n ConfiguratorCodeSchema,\n z\n .array(ConfiguratorCodeSchema)\n .min(1)\n .max(CONFIGURATOR_LIMITS.optionsPerList),\n]);\n\nexport const ConfiguratorConditionSchema = z.strictObject({\n param: ConfiguratorNameSchema,\n op: z.enum(['eq', 'neq', 'in', 'gte', 'lte']),\n value: ConditionValueSchema,\n});\n\nexport const ConfiguratorRuleSchema = z.discriminatedUnion('kind', [\n /** Show `param` only while `when` holds. */\n z.strictObject({\n kind: z.literal('visible'),\n param: ConfiguratorNameSchema,\n when: ConfiguratorConditionSchema,\n }),\n /** Narrow a number parameter: `max cabinet_height = total_height - 300`. */\n z.strictObject({\n kind: z.literal('limit'),\n param: ConfiguratorNameSchema,\n bound: z.enum(['min', 'max']),\n formula: FormulaSchema,\n }),\n /** While `when` holds, these codes of a choice or material are not offered. */\n z.strictObject({\n kind: z.literal('exclude'),\n param: ConfiguratorNameSchema,\n codes: z\n .array(ConfiguratorCodeSchema)\n .min(1)\n .max(CONFIGURATOR_LIMITS.optionsPerList),\n when: ConfiguratorConditionSchema,\n }),\n]);\n\nconst PriceRuleBase = {\n key: ConfiguratorNameSchema,\n label: LabelSchema,\n /** How many times the amount counts, e.g. `compartments`. Absent is once. */\n multiplier: FormulaSchema.optional(),\n};\n\n/**\n * How a choice or a measure changes the price. The amounts themselves are in\n * each product's value; a rule only says where to find them.\n */\nexport const ConfiguratorPriceRuleSchema = z.discriminatedUnion('kind', [\n /** The surcharge the model gives the chosen option. */\n z.strictObject({\n ...PriceRuleBase,\n kind: z.literal('choice'),\n param: ConfiguratorNameSchema,\n }),\n /** The surcharge of the chosen material's price group. */\n z.strictObject({\n ...PriceRuleBase,\n kind: z.literal('material'),\n param: ConfiguratorNameSchema,\n }),\n /** An amount, counted while a switch is on. */\n z.strictObject({\n ...PriceRuleBase,\n kind: z.literal('toggle'),\n param: ConfiguratorNameSchema,\n amount: ConfiguratorNameSchema,\n }),\n /**\n * A price on measure: `max(0, total_width - 1800) / 1000 * meter_price`.\n * Reads number parameters (in their unit) and amounts (in cents).\n */\n z.strictObject({\n key: ConfiguratorNameSchema,\n label: LabelSchema,\n kind: z.literal('formula'),\n formula: FormulaSchema,\n }),\n]);\n\nexport const ConfiguratorStructureObjectSchema = z.strictObject({\n schemaVersion: z.literal(1),\n geometry: GeometryDocumentSchema,\n tabs: z\n .array(z.strictObject({ key: ConfiguratorNameSchema, label: LabelSchema }))\n .min(1)\n .max(CONFIGURATOR_LIMITS.tabs),\n parameters: z\n .array(ConfiguratorParameterSchema)\n .max(CONFIGURATOR_LIMITS.parameters),\n choiceLists: z\n .array(ConfiguratorChoiceListSchema)\n .max(CONFIGURATOR_LIMITS.choiceLists),\n materials: z\n .array(ConfiguratorMaterialSchema)\n .max(CONFIGURATOR_LIMITS.materials),\n priceGroups: z\n .array(z.strictObject({ code: ConfiguratorCodeSchema, label: LabelSchema }))\n .max(CONFIGURATOR_LIMITS.priceGroups),\n /** Named amounts a model fills in and price rules read (`meter_price`). */\n amounts: z\n .array(z.strictObject({ key: ConfiguratorNameSchema, label: LabelSchema }))\n .max(CONFIGURATOR_LIMITS.amounts),\n /**\n * A list of fixed combinations a model offers, each setting some number\n * parameters at once: a fireplace and the niche it needs.\n */\n presets: z\n .strictObject({\n label: LabelSchema,\n tab: ConfiguratorNameSchema,\n sets: z\n .array(ConfiguratorNameSchema)\n .min(1)\n .max(CONFIGURATOR_LIMITS.parameters),\n required: z.boolean(),\n })\n .optional(),\n rules: z.array(ConfiguratorRuleSchema).max(CONFIGURATOR_LIMITS.rules),\n priceRules: z\n .array(ConfiguratorPriceRuleSchema)\n .max(CONFIGURATOR_LIMITS.priceRules),\n});\n\nexport type ConfiguratorStructure = z.infer<\n typeof ConfiguratorStructureObjectSchema\n>;\nexport type ConfiguratorParameter = z.infer<typeof ConfiguratorParameterSchema>;\nexport type ConfiguratorMaterial = z.infer<typeof ConfiguratorMaterialSchema>;\nexport type ConfiguratorCondition = z.infer<typeof ConfiguratorConditionSchema>;\nexport type ConfiguratorRule = z.infer<typeof ConfiguratorRuleSchema>;\nexport type ConfiguratorPriceRule = z.infer<typeof ConfiguratorPriceRuleSchema>;\n\n/**\n * The structure as the object builder, the database's mirror and the public\n * API read it: the shape above plus everything that relates one part to\n * another. A structure that passes can be resolved for any value and any\n * selection without a crash; what is left is about numbers.\n */\nexport const ConfiguratorStructureSchema =\n ConfiguratorStructureObjectSchema.superRefine((structure, ctx) => {\n for (const problem of configuratorStructureProblems(structure)) {\n ctx.addIssue({\n code: 'custom',\n path: problem.path,\n message: problem.code,\n params: problem.params,\n });\n }\n });\n\nexport function configuratorStructureProblems(\n structure: ConfiguratorStructure,\n): ConfiguratorIssue[] {\n const problems: ConfiguratorIssue[] = [];\n\n if (jsonByteLength(structure) > CONFIGURATOR_LIMITS.structureBytes) {\n problems.push(\n issue([], 'configurator.structureTooLarge', {\n max: CONFIGURATOR_LIMITS.structureBytes,\n }),\n );\n }\n\n const tabs = uniqueKeys(\n structure.tabs,\n (tab) => tab.key,\n ['tabs'],\n 'key',\n problems,\n );\n const lists = new Map(\n structure.choiceLists.map((list) => [\n list.key,\n list.options.map((option) => option.code),\n ]),\n );\n uniqueKeys(\n structure.choiceLists,\n (list) => list.key,\n ['choiceLists'],\n 'key',\n problems,\n );\n\n structure.choiceLists.forEach((list, index) =>\n uniqueKeys(\n list.options,\n (option) => option.code,\n ['choiceLists', index, 'options'],\n 'code',\n problems,\n ),\n );\n\n const materials = uniqueKeys(\n structure.materials,\n (material) => material.code,\n ['materials'],\n 'code',\n problems,\n );\n const groups = uniqueKeys(\n structure.priceGroups,\n (group) => group.code,\n ['priceGroups'],\n 'code',\n problems,\n );\n const amounts = uniqueKeys(\n structure.amounts,\n (amount) => amount.key,\n ['amounts'],\n 'key',\n problems,\n );\n\n structure.materials.forEach((material, index) => {\n if (material.priceGroup && !groups.has(material.priceGroup)) {\n problems.push(\n issue(\n ['materials', index, 'priceGroup'],\n 'configurator.unknownPriceGroup',\n {\n code: material.priceGroup,\n },\n ),\n );\n }\n });\n\n const parameters = new Map(\n structure.parameters.map((parameter) => [parameter.key, parameter]),\n );\n uniqueKeys(\n structure.parameters,\n (parameter) => parameter.key,\n ['parameters'],\n 'key',\n problems,\n );\n\n structure.parameters.forEach((parameter, index) => {\n const path = ['parameters', index];\n\n if (!tabs.has(parameter.tab)) {\n problems.push(\n issue([...path, 'tab'], 'configurator.unknownTab', {\n tab: parameter.tab,\n }),\n );\n }\n\n if (parameter.kind === 'number' && parameter.min > parameter.max) {\n problems.push(\n issue([...path, 'min'], 'configurator.minAboveMax', {\n key: parameter.key,\n }),\n );\n }\n\n if (parameter.kind === 'choice' && !lists.has(parameter.list)) {\n problems.push(\n issue([...path, 'list'], 'configurator.unknownList', {\n list: parameter.list,\n }),\n );\n }\n });\n\n const numberKeys = new Set(\n structure.parameters\n .filter((parameter) => parameter.kind === 'number')\n .map((parameter) => parameter.key),\n );\n const toggleKeys = new Set(\n structure.parameters\n .filter((parameter) => parameter.kind === 'toggle')\n .map((parameter) => parameter.key),\n );\n // What a limit or a multiplier may read: measures, counts and switches (1 or 0).\n const numericScope = new Set([...numberKeys, ...toggleKeys]);\n\n if (structure.presets) {\n if (!tabs.has(structure.presets.tab)) {\n problems.push(\n issue(['presets', 'tab'], 'configurator.unknownTab', {\n tab: structure.presets.tab,\n }),\n );\n }\n\n structure.presets.sets.forEach((key, index) => {\n if (!numberKeys.has(key)) {\n problems.push(\n issue(['presets', 'sets', index], 'configurator.notANumberParam', {\n key,\n }),\n );\n }\n });\n }\n\n // Edges for the cycle check: a parameter depends on every parameter its\n // visibility or its limits read.\n const dependsOn = new Map<string, Set<string>>();\n const addDependency = (from: string, on: string) => {\n const set = dependsOn.get(from) ?? new Set<string>();\n set.add(on);\n dependsOn.set(from, set);\n };\n\n structure.rules.forEach((rule, index) => {\n const path = ['rules', index];\n const target = parameters.get(rule.param);\n\n if (!target) {\n problems.push(\n issue([...path, 'param'], 'configurator.unknownParam', {\n key: rule.param,\n }),\n );\n return;\n }\n\n if (rule.kind === 'limit') {\n if (target.kind !== 'number') {\n problems.push(\n issue([...path, 'param'], 'configurator.notANumberParam', {\n key: rule.param,\n }),\n );\n }\n\n for (const name of checkFormula(\n rule.formula,\n [...path, 'formula'],\n numericScope,\n problems,\n )) {\n addDependency(rule.param, name);\n }\n\n return;\n }\n\n if (rule.kind === 'exclude') {\n const codes = codesOf(target, lists, materials);\n\n if (!codes) {\n problems.push(\n issue([...path, 'param'], 'configurator.notAChoiceParam', {\n key: rule.param,\n }),\n );\n } else {\n rule.codes.forEach((code, codeIndex) => {\n if (!codes.has(code)) {\n problems.push(\n issue([...path, 'codes', codeIndex], 'configurator.unknownCode', {\n code,\n }),\n );\n }\n });\n }\n }\n\n const condition = rule.when;\n const conditionParam = parameters.get(condition.param);\n\n if (!conditionParam) {\n problems.push(\n issue([...path, 'when', 'param'], 'configurator.unknownParam', {\n key: condition.param,\n }),\n );\n return;\n }\n\n addDependency(rule.param, condition.param);\n\n const valueProblem = conditionValueProblem(\n condition.op,\n condition.value,\n conditionParam,\n lists,\n materials,\n );\n\n if (valueProblem) {\n problems.push(\n issue([...path, 'when', 'value'], valueProblem, {\n key: condition.param,\n }),\n );\n }\n });\n\n const cycle = findCycle(dependsOn);\n\n if (cycle) {\n problems.push(\n issue(['rules'], 'configurator.ruleCycle', { cycle: cycle.join(' → ') }),\n );\n }\n\n const priceRuleKeys = new Set<string>();\n\n structure.priceRules.forEach((rule, index) => {\n const path = ['priceRules', index];\n\n if (priceRuleKeys.has(rule.key)) {\n problems.push(\n issue([...path, 'key'], 'configurator.duplicateName', {\n name: rule.key,\n }),\n );\n }\n\n priceRuleKeys.add(rule.key);\n\n if (rule.kind === 'formula') {\n checkFormula(\n rule.formula,\n [...path, 'formula'],\n new Set([...numericScope, ...amounts]),\n problems,\n );\n return;\n }\n\n if (rule.multiplier !== undefined) {\n checkFormula(\n rule.multiplier,\n [...path, 'multiplier'],\n numericScope,\n problems,\n );\n }\n\n const target = parameters.get(rule.param);\n\n if (!target || target.kind !== rule.kind) {\n problems.push(\n issue([...path, 'param'], 'configurator.priceRuleParamKind', {\n key: rule.param,\n kind: rule.kind,\n }),\n );\n }\n\n if (rule.kind === 'toggle' && !amounts.has(rule.amount)) {\n problems.push(\n issue([...path, 'amount'], 'configurator.unknownAmount', {\n key: rule.amount,\n }),\n );\n }\n });\n\n const geometryParameters = new Map<string, GeometryParameterInfo>(\n structure.parameters.map((parameter) => [\n parameter.key,\n parameter.kind === 'choice'\n ? { kind: 'choice', codes: lists.get(parameter.list) ?? [] }\n : { kind: parameter.kind },\n ]),\n );\n\n problems.push(\n ...geometryDocumentProblems(structure.geometry, {\n parameters: geometryParameters,\n materialCodes: materials,\n path: ['geometry'],\n }),\n );\n\n return problems;\n}\n\nfunction uniqueKeys<T>(\n items: readonly T[],\n keyOf: (item: T) => string,\n path: (string | number)[],\n field: string,\n problems: ConfiguratorIssue[],\n) {\n const seen = new Set<string>();\n\n items.forEach((item, index) => {\n const key = keyOf(item);\n\n if (seen.has(key)) {\n problems.push(\n issue([...path, index, field], 'configurator.duplicateName', {\n name: key,\n }),\n );\n }\n\n seen.add(key);\n });\n\n return seen;\n}\n\nfunction checkFormula(\n formula: Formula,\n path: (string | number)[],\n scope: ReadonlySet<string>,\n problems: ConfiguratorIssue[],\n) {\n const names = formulaNames(formula);\n\n if (!names.ok) {\n problems.push(issue(path, `configurator.${names.code}`));\n return [];\n }\n\n for (const name of names.names) {\n if (!scope.has(name)) {\n problems.push(issue(path, 'configurator.unknownName', { name }));\n }\n }\n\n return names.names;\n}\n\nfunction codesOf(\n parameter: ConfiguratorParameter,\n lists: ReadonlyMap<string, readonly string[]>,\n materials: ReadonlySet<string>,\n) {\n if (parameter.kind === 'choice') {\n return new Set(lists.get(parameter.list) ?? []);\n }\n\n if (parameter.kind === 'material') {\n return materials;\n }\n\n return null;\n}\n\nfunction conditionValueProblem(\n op: ConfiguratorCondition['op'],\n value: ConfiguratorCondition['value'],\n parameter: ConfiguratorParameter,\n lists: ReadonlyMap<string, readonly string[]>,\n materials: ReadonlySet<string>,\n) {\n if (parameter.kind === 'number') {\n return typeof value === 'number' && op !== 'in'\n ? null\n : 'configurator.conditionNeedsNumber';\n }\n\n if (parameter.kind === 'toggle') {\n return typeof value === 'boolean' && (op === 'eq' || op === 'neq')\n ? null\n : 'configurator.conditionNeedsBoolean';\n }\n\n const codes = codesOf(parameter, lists, materials) ?? new Set<string>();\n\n if (op === 'in') {\n return Array.isArray(value) && value.every((code) => codes.has(code))\n ? null\n : 'configurator.conditionUnknownCode';\n }\n\n return (op === 'eq' || op === 'neq') &&\n typeof value === 'string' &&\n codes.has(value)\n ? null\n : 'configurator.conditionUnknownCode';\n}\n\n/** A cycle in the dependency graph as the names along it, or null. */\nfunction findCycle(graph: ReadonlyMap<string, ReadonlySet<string>>) {\n const state = new Map<string, 'visiting' | 'done'>();\n const stack: string[] = [];\n\n const visit = (node: string): string[] | null => {\n const current = state.get(node);\n\n if (current === 'done') {\n return null;\n }\n\n if (current === 'visiting') {\n return [...stack.slice(stack.indexOf(node)), node];\n }\n\n state.set(node, 'visiting');\n stack.push(node);\n\n for (const next of graph.get(node) ?? []) {\n const cycle = visit(next);\n\n if (cycle) {\n return cycle;\n }\n }\n\n stack.pop();\n state.set(node, 'done');\n return null;\n };\n\n for (const node of graph.keys()) {\n const cycle = visit(node);\n\n if (cycle) {\n return cycle;\n }\n }\n\n return null;\n}\n\n/**\n * The order parameters are resolved in: everything a parameter's rules read\n * comes before it. Only call on a structure that passed\n * `configuratorStructureProblems`, which refuses a cycle.\n */\nexport function parameterResolutionOrder(structure: ConfiguratorStructure) {\n const dependsOn = new Map<string, Set<string>>();\n\n for (const rule of structure.rules) {\n const set = dependsOn.get(rule.param) ?? new Set<string>();\n\n if (rule.kind === 'limit') {\n const names = formulaNames(rule.formula);\n\n for (const name of names.ok ? names.names : []) {\n set.add(name);\n }\n } else {\n set.add(rule.when.param);\n }\n\n dependsOn.set(rule.param, set);\n }\n\n const order: string[] = [];\n const placed = new Set<string>();\n\n const place = (key: string, trail: Set<string>) => {\n if (placed.has(key) || trail.has(key)) {\n return;\n }\n\n trail.add(key);\n\n for (const dependency of dependsOn.get(key) ?? []) {\n place(dependency, trail);\n }\n\n placed.add(key);\n order.push(key);\n };\n\n for (const parameter of structure.parameters) {\n place(parameter.key, new Set());\n }\n\n return order.filter((key) =>\n structure.parameters.some((parameter) => parameter.key === key),\n );\n}\n","import { z } from 'zod';\n\nimport { CONFIGURATOR_LIMITS, jsonByteLength } from './configurator-limits';\nimport type { ConfiguratorStructure } from './configurator-structure.schema';\nimport {\n ConfiguratorCodeSchema,\n ConfiguratorNameSchema,\n} from './geometry-document.schema';\n\n/**\n * The value of a configurator field on one product: what this model offers\n * within the structure, and what it costs. \"Fiona 3-vaks\" and \"Arles\" share a\n * structure and differ here.\n *\n * Amounts are whole cents, entered the way the account enters every product\n * price (`invoicing_settings.prices_include_tax`). `null` is \"price on\n * request\": the option is offered, but choosing it turns the quote partial.\n *\n * A key the structure no longer knows is left alone rather than refused: a\n * reseller who removes an option must not make every product that offered it\n * unsaveable. `orphanedReferences` counts them so a screen can say so, and\n * the resolver never reads them.\n */\nconst CentsSchema = z.number().int().min(0).max(100_000_000);\nconst PriceSchema = CentsSchema.nullable();\n\nexport const ConfiguratorValueObjectSchema = z.strictObject({\n /** Null: the model has no price of its own, every quote is \"open\". */\n basePriceCents: PriceSchema,\n numbers: z\n .record(\n ConfiguratorNameSchema,\n z.strictObject({\n min: z.number().finite(),\n max: z.number().finite(),\n default: z.number().finite(),\n }),\n )\n .default({}),\n /** Per choice list: the options this model offers, with their surcharge. */\n choices: z\n .record(\n ConfiguratorNameSchema,\n z.strictObject({\n options: z.record(\n ConfiguratorCodeSchema,\n z.strictObject({ priceCents: PriceSchema }),\n ),\n default: ConfiguratorCodeSchema.optional(),\n }),\n )\n .default({}),\n /** Per material parameter: the materials this model offers. */\n materials: z\n .record(\n ConfiguratorNameSchema,\n z.strictObject({\n offered: z\n .array(ConfiguratorCodeSchema)\n .max(CONFIGURATOR_LIMITS.materials),\n default: ConfiguratorCodeSchema.optional(),\n }),\n )\n .default({}),\n /** Per switch: whether this model offers it, and how it starts. Absent is offered, off. */\n toggles: z\n .record(\n ConfiguratorNameSchema,\n z.strictObject({ offered: z.boolean(), default: z.boolean() }),\n )\n .default({}),\n /** The surcharge of each material price group. */\n priceGroups: z.record(ConfiguratorCodeSchema, PriceSchema).default({}),\n /** The amounts the structure names, such as a price per metre. */\n amounts: z.record(ConfiguratorNameSchema, PriceSchema).default({}),\n presets: z\n .array(\n z.strictObject({\n code: ConfiguratorCodeSchema,\n label: z.string().trim().min(1).max(80),\n /** A product of the same account this preset stands for, e.g. the fireplace. */\n productId: z.string().uuid().nullable(),\n values: z.record(ConfiguratorNameSchema, z.number().finite()),\n }),\n )\n .max(CONFIGURATOR_LIMITS.presets)\n .default([]),\n});\n\nexport type ConfiguratorValue = z.infer<typeof ConfiguratorValueObjectSchema>;\nexport type ConfiguratorValueInput = z.input<\n typeof ConfiguratorValueObjectSchema\n>;\n\n/**\n * The value read through a structure, like `repeaterValueSchema(config)`:\n * the shape above, the size limit, and -- for every key the structure does\n * know -- ranges that fit inside the structure's own and a default inside\n * the model's range.\n */\nexport function configuratorValueSchema(\n structure: ConfiguratorStructure | undefined,\n) {\n return ConfiguratorValueObjectSchema.superRefine((value, ctx) => {\n if (jsonByteLength(value) > CONFIGURATOR_LIMITS.valueBytes) {\n ctx.addIssue({\n code: 'custom',\n path: [],\n message: 'configurator.valueTooLarge',\n params: { max: CONFIGURATOR_LIMITS.valueBytes },\n });\n }\n\n const codes = new Set<string>();\n\n value.presets.forEach((preset, index) => {\n if (codes.has(preset.code)) {\n ctx.addIssue({\n code: 'custom',\n path: ['presets', index, 'code'],\n message: 'configurator.duplicateName',\n params: { name: preset.code },\n });\n }\n\n codes.add(preset.code);\n });\n\n for (const [key, range] of Object.entries(value.numbers)) {\n const parameter = structure?.parameters.find(\n (candidate) => candidate.key === key && candidate.kind === 'number',\n );\n const outer =\n parameter?.kind === 'number'\n ? { min: parameter.min, max: parameter.max }\n : null;\n\n if (range.min > range.max) {\n ctx.addIssue({\n code: 'custom',\n path: ['numbers', key, 'min'],\n message: 'configurator.minAboveMax',\n params: { key },\n });\n } else if (range.default < range.min || range.default > range.max) {\n ctx.addIssue({\n code: 'custom',\n path: ['numbers', key, 'default'],\n message: 'configurator.defaultOutsideRange',\n params: { key, min: range.min, max: range.max },\n });\n }\n\n if (outer && (range.min < outer.min || range.max > outer.max)) {\n ctx.addIssue({\n code: 'custom',\n path: ['numbers', key],\n message: 'configurator.rangeOutsideStructure',\n params: { key, min: outer.min, max: outer.max },\n });\n }\n }\n });\n}\n\nexport function emptyConfiguratorValue(): ConfiguratorValue {\n return ConfiguratorValueObjectSchema.parse({ basePriceCents: null });\n}\n","import { type ConfiguratorIssue, issue } from './configurator-issue';\nimport type { ConfiguratorSelection } from './configurator-selection.schema';\nimport {\n type ConfiguratorCondition,\n type ConfiguratorMaterial,\n type ConfiguratorParameter,\n type ConfiguratorStructure,\n parameterResolutionOrder,\n} from './configurator-structure.schema';\nimport type { ConfiguratorValue } from './configurator-value.schema';\nimport { evaluateFormulaSource } from './evaluate-formula';\n\n/**\n * A structure, a model's value and a visitor's selection, turned into what\n * each parameter is now: visible or not, its range after the rules, the\n * options still offered, and the value that counts. Then the numbers the\n * shape asks for, and the material in each slot.\n *\n * Always returns a configuration, also for a selection that is wrong: the\n * panel needs something to draw while somebody is still typing. `issues` says\n * what was wrong; a quote is only made from a configuration without any, and\n * the server refuses a request that had one.\n */\nexport interface ResolvedOption {\n code: string;\n label: string;\n /** The model's surcharge; null is \"price on request\". */\n priceCents: number | null;\n /** For a material: what to draw it with. */\n material?: ConfiguratorMaterial;\n}\n\nexport interface ResolvedParameter {\n key: string;\n kind: ConfiguratorParameter['kind'];\n label: string;\n tab: string;\n visible: boolean;\n value: number | string | boolean | null;\n /** Number parameters: the range after the model and the rules. */\n min?: number;\n max?: number;\n step?: number;\n unit?: 'mm' | 'count';\n /** Set by the chosen preset; the visitor cannot move it. */\n fixed?: boolean;\n /** Choice and material parameters: what may be chosen now. */\n options?: ResolvedOption[];\n}\n\nexport interface ResolvedPreset {\n code: string;\n label: string;\n productId: string | null;\n}\n\nexport interface ResolvedSlot {\n color: string;\n material: ConfiguratorMaterial | null;\n}\n\nexport interface ResolvedConfiguration {\n parameters: ResolvedParameter[];\n byKey: Readonly<Record<string, ResolvedParameter>>;\n presets: ResolvedPreset[];\n preset: ResolvedPreset | null;\n /** The numbers the shape document asks for, by input name. */\n inputs: Record<string, number>;\n slots: Record<string, ResolvedSlot>;\n /** The selection as it counts: every visible parameter, nothing else. */\n selection: ConfiguratorSelection;\n}\n\n/** The grey a slot is drawn in when nothing says otherwise. */\nexport const NEUTRAL_SLOT_COLOR = '#b8b4ad';\n\nconst STEP_TOLERANCE = 1e-6;\n\nexport function resolveConfiguration(params: {\n structure: ConfiguratorStructure;\n value: ConfiguratorValue;\n selection: ConfiguratorSelection;\n}): { configuration: ResolvedConfiguration; issues: ConfiguratorIssue[] } {\n const { structure, value, selection } = params;\n const issues: ConfiguratorIssue[] = [];\n const parametersByKey = new Map(\n structure.parameters.map((parameter) => [parameter.key, parameter]),\n );\n const materialsByCode = new Map(\n structure.materials.map((material) => [material.code, material]),\n );\n\n for (const key of Object.keys(selection.values)) {\n if (!parametersByKey.has(key)) {\n issues.push(issue(['values', key], 'configurator.unknownParam', { key }));\n }\n }\n\n const presets: ResolvedPreset[] = structure.presets\n ? value.presets.map((preset) => ({\n code: preset.code,\n label: preset.label,\n productId: preset.productId,\n }))\n : [];\n\n let preset: ResolvedPreset | null = null;\n\n if (selection.preset !== undefined) {\n preset =\n presets.find((candidate) => candidate.code === selection.preset) ?? null;\n\n if (!preset) {\n issues.push(\n issue(['preset'], 'configurator.presetNotOffered', {\n code: selection.preset,\n }),\n );\n }\n }\n\n if (!preset && structure.presets?.required) {\n preset = presets[0] ?? null;\n }\n\n const presetValues = preset\n ? (value.presets.find((candidate) => candidate.code === preset?.code)\n ?.values ?? {})\n : {};\n const presetSets = new Set(structure.presets?.sets ?? []);\n\n const resolved = new Map<string, ResolvedParameter>();\n // Numbers (and switches as 1 or 0) as far as resolved, for limits to read.\n const numericScope: Record<string, number> = {};\n\n const conditionHolds = (condition: ConfiguratorCondition) => {\n const current = resolved.get(condition.param);\n const actual = current?.visible ? current.value : null;\n\n switch (condition.op) {\n case 'eq':\n return actual === condition.value;\n case 'neq':\n return actual !== condition.value;\n case 'in':\n return (\n Array.isArray(condition.value) &&\n condition.value.includes(actual as string)\n );\n case 'gte':\n return (\n typeof actual === 'number' && actual >= (condition.value as number)\n );\n case 'lte':\n return (\n typeof actual === 'number' && actual <= (condition.value as number)\n );\n }\n };\n\n for (const key of parameterResolutionOrder(structure)) {\n const parameter = parametersByKey.get(key);\n\n if (!parameter) {\n continue;\n }\n\n const rules = structure.rules.filter((rule) => rule.param === key);\n let visible = rules\n .filter((rule) => rule.kind === 'visible')\n .every((rule) => conditionHolds(rule.when));\n const chosen = selection.values[key];\n const path = ['values', key];\n const base = {\n key,\n kind: parameter.kind,\n label: parameter.label,\n tab: parameter.tab,\n };\n\n if (parameter.kind === 'number') {\n const range = value.numbers[key] ?? {\n min: parameter.min,\n max: parameter.max,\n default: parameter.min,\n };\n let min = range.min;\n let max = range.max;\n\n for (const rule of rules) {\n if (rule.kind !== 'limit') {\n continue;\n }\n\n const bound = evaluateFormulaSource(rule.formula, numericScope);\n\n if (!bound.ok) {\n issues.push(\n issue(\n ['rules', structure.rules.indexOf(rule)],\n `configurator.${bound.code}`,\n ),\n );\n continue;\n }\n\n if (rule.bound === 'min') {\n min = Math.max(min, bound.value);\n } else {\n max = Math.min(max, bound.value);\n }\n }\n\n if (min > max) {\n issues.push(issue(path, 'configurator.emptyRange', { key, min, max }));\n max = min;\n }\n\n const fixedValue = presetSets.has(key) ? presetValues[key] : undefined;\n let current = clamp(range.default, min, max);\n\n if (fixedValue !== undefined) {\n current = fixedValue;\n } else if (visible && chosen !== undefined) {\n if (typeof chosen !== 'number') {\n issues.push(issue(path, 'configurator.expectedNumber', { key }));\n } else if (chosen < min || chosen > max) {\n issues.push(\n issue(path, 'configurator.outOfRange', { key, min, max }),\n );\n current = clamp(chosen, min, max);\n } else if (!onStep(chosen, parameter.step)) {\n issues.push(\n issue(path, 'configurator.offStep', { key, step: parameter.step }),\n );\n } else {\n current = chosen;\n }\n }\n\n numericScope[key] = current;\n resolved.set(key, {\n ...base,\n visible,\n value: current,\n min,\n max,\n step: parameter.step,\n unit: parameter.unit,\n fixed: fixedValue !== undefined,\n });\n continue;\n }\n\n if (parameter.kind === 'toggle') {\n const setting = value.toggles[key] ?? { offered: true, default: false };\n visible = visible && setting.offered;\n let current = setting.default;\n\n if (visible && chosen !== undefined) {\n if (typeof chosen !== 'boolean') {\n issues.push(issue(path, 'configurator.expectedBoolean', { key }));\n } else {\n current = chosen;\n }\n }\n\n const on = visible && current;\n numericScope[key] = on ? 1 : 0;\n resolved.set(key, { ...base, visible, value: on });\n continue;\n }\n\n const excluded = new Set(\n rules\n .filter((rule) => rule.kind === 'exclude' && conditionHolds(rule.when))\n .flatMap((rule) => (rule.kind === 'exclude' ? rule.codes : [])),\n );\n\n let options: ResolvedOption[];\n let preferred: string | undefined;\n\n if (parameter.kind === 'choice') {\n const list = structure.choiceLists.find(\n (candidate) => candidate.key === parameter.list,\n );\n const offer = value.choices[parameter.list];\n\n options = (list?.options ?? [])\n .filter(\n (option) =>\n offer?.options[option.code] !== undefined &&\n !excluded.has(option.code),\n )\n .map((option) => ({\n code: option.code,\n label: option.label,\n priceCents: offer?.options[option.code]?.priceCents ?? null,\n }));\n preferred = offer?.default;\n } else {\n const offer = value.materials[key];\n\n options = (offer?.offered ?? [])\n .map((code) => materialsByCode.get(code))\n .filter(\n (material): material is ConfiguratorMaterial =>\n material !== undefined,\n )\n .filter((material) => !excluded.has(material.code))\n .map((material) => ({\n code: material.code,\n label: material.label,\n priceCents: material.priceGroup\n ? (value.priceGroups[material.priceGroup] ?? null)\n : 0,\n material,\n }));\n preferred = offer?.default;\n }\n\n visible = visible && options.length > 0;\n let current: string | null =\n options.find((option) => option.code === preferred)?.code ??\n options[0]?.code ??\n null;\n\n if (visible && chosen !== undefined) {\n if (\n typeof chosen !== 'string' ||\n !options.some((option) => option.code === chosen)\n ) {\n issues.push(\n issue(path, 'configurator.optionNotOffered', {\n key,\n code: String(chosen),\n }),\n );\n } else {\n current = chosen;\n }\n }\n\n resolved.set(key, {\n ...base,\n visible,\n value: visible ? current : null,\n options,\n });\n }\n\n const parameters = structure.parameters\n .map((parameter) => resolved.get(parameter.key))\n .filter(\n (parameter): parameter is ResolvedParameter => parameter !== undefined,\n );\n\n const inputs: Record<string, number> = {};\n\n for (const input of structure.geometry.inputs) {\n const parameter = resolved.get(input.binding.param);\n\n if (!parameter) {\n continue;\n }\n\n if (parameter.kind === 'number') {\n inputs[input.name] = parameter.value as number;\n } else if (parameter.kind === 'toggle') {\n inputs[input.name] = parameter.value === true ? 1 : 0;\n } else if (parameter.kind === 'choice') {\n inputs[input.name] =\n typeof parameter.value === 'string'\n ? (input.binding.map?.[parameter.value] ?? 0)\n : 0;\n }\n }\n\n const slots: Record<string, ResolvedSlot> = {};\n\n for (const slot of structure.geometry.slots) {\n let material: ConfiguratorMaterial | null = null;\n\n if (slot.param) {\n const parameter = resolved.get(slot.param);\n material =\n parameter?.options?.find((option) => option.code === parameter.value)\n ?.material ?? null;\n } else if (slot.material) {\n material = materialsByCode.get(slot.material) ?? null;\n }\n\n slots[slot.name] = {\n color: material?.color ?? slot.color ?? NEUTRAL_SLOT_COLOR,\n material,\n };\n }\n\n const normalised: ConfiguratorSelection = { values: {} };\n\n for (const parameter of parameters) {\n if (parameter.visible && !parameter.fixed && parameter.value !== null) {\n normalised.values[parameter.key] = parameter.value;\n }\n }\n\n if (preset) {\n normalised.preset = preset.code;\n }\n\n return {\n configuration: {\n parameters,\n byKey: Object.fromEntries(resolved),\n presets,\n preset,\n inputs,\n slots,\n selection: normalised,\n },\n issues,\n };\n}\n\nfunction clamp(value: number, min: number, max: number) {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction onStep(value: number, step: number) {\n const ratio = value / step;\n return Math.abs(ratio - Math.round(ratio)) < STEP_TOLERANCE;\n}\n","import { type ConfiguratorIssue, issue } from './configurator-issue';\nimport { CONFIGURATOR_LIMITS } from './configurator-limits';\nimport { type Formula, evaluateFormulaSource } from './evaluate-formula';\nimport type {\n GeometryDocument,\n GeometryNode,\n PrimitiveType,\n} from './geometry-document.schema';\n\n/**\n * A shape document and its input numbers, turned into a flat list of blocks\n * with their measures in millimetres. No three.js here: the 3D renderer, the\n * 2D dimension sketch and the tests all read this one list, so a test can\n * check the size of every part without drawing anything, and the three can\n * never disagree about where a part is.\n */\nexport interface GeometryPart {\n /** Stable across re-expansion: `front` or `front[2]` inside a repeat. */\n key: string;\n id: string;\n type: PrimitiveType;\n slot: string;\n /** Width, height, depth. */\n size: [number, number, number];\n /** Minimum corner: x left-right, y bottom-top, z wall-room. */\n at: [number, number, number];\n params: Record<string, number>;\n}\n\nexport interface GeometryDimension {\n id: string;\n axis: 'x' | 'y';\n from: number;\n to: number;\n at: number;\n label: string | null;\n length: number;\n}\n\nexport interface ExpandedGeometry {\n parts: GeometryPart[];\n dimensions: GeometryDimension[];\n bounds: { min: [number, number, number]; max: [number, number, number] };\n}\n\nexport type ExpandGeometryResult =\n | { ok: true; geometry: ExpandedGeometry }\n | { ok: false; issues: ConfiguratorIssue[] };\n\nexport function expandGeometry(\n document: GeometryDocument,\n inputs: Readonly<Record<string, number>>,\n): ExpandGeometryResult {\n const issues: ConfiguratorIssue[] = [];\n const parts: GeometryPart[] = [];\n const baseScope: Record<string, number> = { ...document.constants };\n\n for (const input of document.inputs) {\n const number = inputs[input.name];\n\n if (number === undefined || !Number.isFinite(number)) {\n issues.push(\n issue(['inputs', input.name], 'configurator.inputMissing', {\n name: input.name,\n }),\n );\n continue;\n }\n\n baseScope[input.name] = number;\n }\n\n if (issues.length > 0) {\n return { ok: false, issues };\n }\n\n const evaluate = (\n formula: Formula,\n scope: Record<string, number>,\n path: (string | number)[],\n ) => {\n const result = evaluateFormulaSource(formula, scope);\n\n if (!result.ok) {\n issues.push(\n result.code === 'formula_unknown_name'\n ? issue(path, 'configurator.unknownName', { name: result.name })\n : issue(path, `configurator.${result.code}`),\n );\n return null;\n }\n\n return result.value;\n };\n\n const walk = (\n nodes: readonly GeometryNode[],\n scope: Record<string, number>,\n path: (string | number)[],\n keySuffix: string,\n depth: number,\n ) => {\n for (const node of nodes) {\n if (issues.length > 0) {\n return;\n }\n\n const nodePath = [...path, node.id];\n\n if (node.visible !== undefined) {\n const visible = evaluate(node.visible, scope, [...nodePath, 'visible']);\n\n if (visible === null || visible <= 0) {\n continue;\n }\n }\n\n if (node.type === 'repeat') {\n if (depth + 1 > CONFIGURATOR_LIMITS.repeatDepth) {\n issues.push(\n issue(nodePath, 'configurator.repeatTooDeep', {\n max: CONFIGURATOR_LIMITS.repeatDepth,\n }),\n );\n return;\n }\n\n const count = evaluate(node.count, scope, [...nodePath, 'count']);\n\n if (count === null) {\n return;\n }\n\n if (\n !Number.isInteger(count) ||\n count < 0 ||\n count > CONFIGURATOR_LIMITS.repeatCount\n ) {\n issues.push(\n issue([...nodePath, 'count'], 'configurator.repeatCountInvalid', {\n count,\n max: CONFIGURATOR_LIMITS.repeatCount,\n }),\n );\n return;\n }\n\n const index = node.index ?? 'i';\n\n for (let step = 0; step < count; step += 1) {\n walk(\n node.children,\n { ...scope, [index]: step },\n [...nodePath, step],\n `${keySuffix}[${step}]`,\n depth + 1,\n );\n }\n\n continue;\n }\n\n const size = node.size.map((formula, axis) =>\n evaluate(formula, scope, [...nodePath, 'size', axis]),\n );\n const at = node.at.map((formula, axis) =>\n evaluate(formula, scope, [...nodePath, 'at', axis]),\n );\n\n if (size.includes(null) || at.includes(null)) {\n return;\n }\n\n const negative = (size as number[]).findIndex((measure) => measure < 0);\n\n if (negative >= 0) {\n issues.push(\n issue(\n [...nodePath, 'size', negative],\n 'configurator.negativeMeasure',\n {\n value: size[negative] as number,\n },\n ),\n );\n return;\n }\n\n const params: Record<string, number> = {};\n\n for (const [key, formula] of Object.entries(node.params ?? {})) {\n const number = evaluate(formula, scope, [...nodePath, 'params', key]);\n\n if (number === null) {\n return;\n }\n\n params[key] = number;\n }\n\n parts.push({\n key: `${node.id}${keySuffix}`,\n id: node.id,\n type: node.type,\n slot: node.slot,\n size: size as [number, number, number],\n at: at as [number, number, number],\n params,\n });\n\n if (parts.length > CONFIGURATOR_LIMITS.expandedParts) {\n issues.push(\n issue(['root'], 'configurator.tooManyParts', {\n max: CONFIGURATOR_LIMITS.expandedParts,\n }),\n );\n return;\n }\n }\n };\n\n walk(document.root, baseScope, ['root'], '', 0);\n\n const dimensions: GeometryDimension[] = [];\n\n for (const annotation of document.annotations) {\n const path = ['annotations', annotation.id];\n const from = evaluate(annotation.from, baseScope, [...path, 'from']);\n const to = evaluate(annotation.to, baseScope, [...path, 'to']);\n const at = evaluate(annotation.at, baseScope, [...path, 'at']);\n\n if (from === null || to === null || at === null) {\n continue;\n }\n\n dimensions.push({\n id: annotation.id,\n axis: annotation.axis,\n from,\n to,\n at,\n label: annotation.label ?? null,\n length: Math.abs(to - from),\n });\n }\n\n if (issues.length > 0) {\n return { ok: false, issues };\n }\n\n return { ok: true, geometry: { parts, dimensions, bounds: boundsOf(parts) } };\n}\n\nfunction boundsOf(parts: readonly GeometryPart[]): ExpandedGeometry['bounds'] {\n if (parts.length === 0) {\n return { min: [0, 0, 0], max: [0, 0, 0] };\n }\n\n const min: [number, number, number] = [Infinity, Infinity, Infinity];\n const max: [number, number, number] = [-Infinity, -Infinity, -Infinity];\n\n for (const part of parts) {\n for (let axis = 0; axis < 3; axis += 1) {\n min[axis] = Math.min(min[axis] as number, part.at[axis] as number);\n max[axis] = Math.max(\n max[axis] as number,\n (part.at[axis] as number) + (part.size[axis] as number),\n );\n }\n }\n\n return { min, max };\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type {\n CrmClient,\n PlacementBookingPage,\n PlacementChatWidget,\n PlacementForm,\n PlacementSiteTracking,\n} from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport { answer, codeBlock, refusal } from './tool-answers';\n\n/** The part of the client these tools call; a test hands in a fake of exactly this. */\nexport type PlacementClient = Pick<\n CrmClient,\n | 'getForms'\n | 'getChatWidgets'\n | 'upsertChatWidget'\n | 'getSiteTracking'\n | 'updateSiteTracking'\n | 'getBookingPages'\n>;\n\nconst REACT_HINT =\n 'On a React or Next.js site, prefer the components in ' +\n '`@solumflow-app/crm-client/react` (CrmForm, CrmBooking, CrmChatWidget, ' +\n 'CrmSiteTracking) over pasting HTML.';\n\nconst page = {\n cursor: z\n .string()\n .optional()\n .describe('The nextCursor of the previous page; leave out for the first.'),\n limit: z.number().int().min(1).max(100).optional(),\n};\n\nconst origin = z\n .string()\n .regex(/^https?:\\/\\/[a-zA-Z0-9.-]+(:[0-9]{1,5})?$/)\n .describe('Scheme, host and optional port; no path: https://www.example.com');\n\n/**\n * One tool per route of the public API, nothing in between: every rule about\n * what may be read or changed is the API's, and this only says it in a way an\n * agent can follow. The annotations describe; the key's scope is what\n * enforces.\n */\nexport function registerPlacementTools(\n server: McpServer,\n client: PlacementClient,\n) {\n server.registerTool(\n 'list_forms',\n {\n title: 'List published forms',\n description:\n 'The published forms of the account, each with its own link and the ' +\n 'HTML snippet that places it on a page. Forms are made and published ' +\n 'by a person in the app’s form builder; this cannot create one. If the ' +\n 'form you need is missing, ask the user to build and publish it first. ' +\n REACT_HINT,\n inputSchema: page,\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async (input) => {\n try {\n const list = await client.getForms(input);\n\n return answer(\n list.data.length === 0\n ? 'No published forms. Ask the user to build and publish one in the app.'\n : [...list.data.map(describeForm), more(list.nextCursor)].join(\n '\\n\\n',\n ),\n list,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n\n server.registerTool(\n 'list_booking_pages',\n {\n title: 'List booking pages',\n description:\n 'Every page a visitor can book on: the services overview, each ' +\n 'category and service, and each appointment calendar. Each has a link ' +\n 'and an embed snippet (an iframe plus a sizing script); a paid ' +\n 'calendar embedded this way opens its payment in a tab of its own. ' +\n REACT_HINT,\n inputSchema: {},\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n try {\n const pages = await client.getBookingPages();\n\n return answer(\n pages.length === 0\n ? 'Nothing can be booked online on this account yet.'\n : pages.map(describeBookingPage).join('\\n\\n'),\n pages,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n\n server.registerTool(\n 'list_chat_widgets',\n {\n title: 'List chat widgets',\n description:\n 'The chat widgets of the account, with the sites each may run on and, ' +\n 'for a published one, the script tag that places it. An empty list of ' +\n 'allowed origins means any site. ' +\n REACT_HINT,\n inputSchema: page,\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async (input) => {\n try {\n const list = await client.getChatWidgets(input);\n\n return answer(\n list.data.length === 0\n ? 'No chat widgets yet. upsert_chat_widget can make one.'\n : [\n ...list.data.map(describeChatWidget),\n more(list.nextCursor),\n ].join('\\n\\n'),\n list,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n\n server.registerTool(\n 'upsert_chat_widget',\n {\n title: 'Make or change a chat widget',\n description:\n 'Makes the chat widget at this address when there is none, or changes ' +\n 'it. Sets its name, whether it is live, and which sites it may run on. ' +\n 'How it looks and what it says is done in the app’s builder. Sites are ' +\n 'added and removed, never replaced. Careful: an empty list means ANY ' +\n 'site, so adding the first origin narrows the widget to that site only, ' +\n 'and removing an origin takes the chat off a site that may be live.',\n inputSchema: {\n slug: z\n .string()\n .regex(/^[a-z][a-z0-9-]*$/)\n .max(100)\n .describe(\n 'The widget’s address on this account, such as \"website-chat\".',\n ),\n name: z\n .string()\n .min(1)\n .max(200)\n .optional()\n .describe('Required when the widget does not exist yet.'),\n status: z\n .enum(['draft', 'published'])\n .optional()\n .describe('Only a published widget answers on a site.'),\n addAllowedOrigins: z.array(origin).max(20).optional(),\n removeAllowedOrigins: z.array(origin).max(20).optional(),\n },\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: true,\n },\n },\n async ({ slug, ...input }) => {\n try {\n const widget = await client.upsertChatWidget(slug, input);\n\n return answer(\n `${widget.created ? 'Made' : 'Updated'} the widget.\\n\\n${describeChatWidget(widget)}`,\n widget,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n\n server.registerTool(\n 'get_site_tracking',\n {\n title: 'Get the tracking script',\n description:\n 'The website tracking script of the account: its tag, the hosts it ' +\n 'counts visits from, and the consent it waits for. Answers that ' +\n 'tracking is not set up when it never was; update_site_tracking sets ' +\n 'it up. ' +\n REACT_HINT,\n inputSchema: {},\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n try {\n const site = await client.getSiteTracking();\n\n return answer(\n site\n ? describeSiteTracking(site)\n : 'Website tracking is not set up yet. update_site_tracking with the site’s host sets it up.',\n site,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n\n server.registerTool(\n 'update_site_tracking',\n {\n title: 'Set up or change the tracking script',\n description:\n 'Sets website tracking up when it is not there, adds or removes the ' +\n 'hosts it counts visits from, and changes the consent it waits for. An ' +\n 'empty list of hosts counts nothing, so add the host of every site the ' +\n 'script is placed on. Hosts are added and removed, never replaced.',\n inputSchema: {\n addAllowedHosts: z\n .array(z.string())\n .max(20)\n .optional()\n .describe('Hosts such as \"www.example.com\" or \"*.example.com\".'),\n removeAllowedHosts: z.array(z.string()).max(20).optional(),\n consentSignal: z\n .enum([\n 'ad_storage',\n 'analytics_storage',\n 'personalization_storage',\n 'api_only',\n ])\n .optional()\n .describe(\n 'The consent the script waits for. api_only: it waits until the page tells it.',\n ),\n },\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: true,\n },\n },\n async (input) => {\n try {\n const site = await client.updateSiteTracking(input);\n\n return answer(\n `${site.created ? 'Set tracking up.' : 'Updated tracking.'}\\n\\n${describeSiteTracking(site)}`,\n site,\n );\n } catch (error) {\n return refusal(error);\n }\n },\n );\n}\n\nfunction describeForm(form: PlacementForm) {\n return [\n `**${form.name}** (id ${form.id})`,\n `Link: ${form.url}`,\n codeBlock(form.embed),\n ].join('\\n');\n}\n\nfunction describeBookingPage(page: PlacementBookingPage) {\n return [\n `**${page.name}** (${page.kind}${page.id ? `, id ${page.id}` : ''})`,\n `Link: ${page.url}`,\n codeBlock(page.embed),\n ].join('\\n');\n}\n\nfunction describeChatWidget(widget: PlacementChatWidget) {\n return [\n `**${widget.name}** (${widget.slug}, ${widget.status}, id ${widget.id})`,\n `Allowed sites: ${\n widget.allowedOrigins.length > 0\n ? widget.allowedOrigins.join(', ')\n : 'any site'\n }`,\n widget.embed\n ? codeBlock(widget.embed)\n : 'No snippet until the widget is published.',\n ].join('\\n');\n}\n\nfunction describeSiteTracking(site: PlacementSiteTracking) {\n return [\n `Hosts: ${\n site.allowedHosts.length > 0\n ? site.allowedHosts.join(', ')\n : 'none — no visit is counted until a host is added'\n }`,\n `Waits for consent: ${site.consentSignal}`,\n `Last visit received: ${site.lastReceivedAt ?? 'never'}`,\n codeBlock(site.embed),\n ].join('\\n');\n}\n\nfunction more(nextCursor: string | null) {\n return nextCursor ? `More on the next page: cursor ${nextCursor}` : '';\n}\n","/**\n * Where the server finds its account: a secret key and the app's address,\n * both from the environment. Every tool needs `website:manage`, which only a\n * secret (`crms_`) key can hold, so a publishable key is refused here rather\n * than on the first call.\n *\n * The address is required, not defaulted. The same server talks to a\n * production app, a reseller's own domain or a local one, and guessing the\n * wrong one sends the key somewhere it was never meant to go.\n */\nexport function readServerConfig(env: Record<string, string | undefined>) {\n const apiKey = env.SOLUMFLOW_API_KEY?.trim() ?? '';\n const baseUrl = env.SOLUMFLOW_API_URL?.trim().replace(/\\/+$/, '') ?? '';\n\n if (!apiKey) {\n return {\n ok: false as const,\n error:\n 'SOLUMFLOW_API_KEY is not set. Make a secret key with the scope ' +\n '\"website:manage\" in the app (Settings → API keys) and pass it in the ' +\n 'environment.',\n };\n }\n\n if (!apiKey.startsWith('crms_')) {\n return {\n ok: false as const,\n error:\n 'SOLUMFLOW_API_KEY is not a secret key. Placing things on a website ' +\n 'needs a `crms_` key with the scope \"website:manage\"; a `crmp_` key ' +\n 'can only read the catalogue.',\n };\n }\n\n if (!/^https?:\\/\\/[^/]+$/.test(baseUrl)) {\n return {\n ok: false as const,\n error:\n 'SOLUMFLOW_API_URL is not set to the app’s address, such as ' +\n '\"https://app.example.com\" (an origin, no path).',\n };\n }\n\n return { ok: true as const, apiKey, baseUrl };\n}\n"],"mappings":"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","names":["z","z","z","checkFormula","z","z","z","z","z","preset","current","z","slugOrId","z","z","page"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@solumflow-app/crm-mcp-server",
3
- "version": "0.1.0",
4
- "description": "Let an AI agent place an account's forms, booking pages, chat and tracking on a website, through the public API.",
3
+ "version": "0.3.0",
4
+ "description": "Let an AI agent place an account's forms, booking pages, chat and tracking on a website and read its catalogue, through the public API.",
5
5
  "license": "MIT",
6
6
  "bin": {
7
7
  "crm-mcp-server": "./dist/index.js"
@@ -15,10 +15,11 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@modelcontextprotocol/sdk": "1.30.0",
18
- "@solumflow-app/crm-client": "0.2.0",
18
+ "@solumflow-app/crm-client": "0.6.0",
19
19
  "zod": "4.5.4"
20
20
  },
21
21
  "devDependencies": {
22
+ "@kit/product-configurator": "0.1.0",
22
23
  "@kit/tsconfig": "0.1.0",
23
24
  "@types/node": "26.4.1",
24
25
  "tsup": "8.5.1",