@solumflow-app/crm-mcp-server 0.8.0 → 0.9.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","../package.json","../src/catalog-migration-tools.ts","../../features/crm-invoicing/src/schema/catalog-api.schema.ts","../../features/crm-invoicing/src/schema/amount-ceiling.ts","../src/catalog-tools.ts","../src/tool-answers.ts","../src/catalog-write-tools.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/event-tools.ts","../src/placement-tools.ts","../src/server-config.ts","../src/server-info-tool.ts","../src/tool-definitions-version.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 { version } from '../package.json';\nimport { registerCatalogMigrationTools } from './catalog-migration-tools';\nimport { registerCatalogTools } from './catalog-tools';\nimport { registerCatalogWriteTools } from './catalog-write-tools';\nimport { registerConfiguratorTools } from './configurator-tools';\nimport { registerEventTools } from './event-tools';\nimport { registerPlacementTools } from './placement-tools';\nimport { readServerConfig } from './server-config';\nimport { registerServerInfoTool } from './server-info-tool';\nimport { holdToolRegistrations } from './tool-definitions-version';\n\nimport { createRequire } from 'node:module';\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, read the catalogue and the\n * events to build their pages, and fill the catalogue. It holds no rules of\n * its own: every tool is one call of the public API through the published\n * client, and the key's scopes decide which of them answer.\n *\n * Errors go to stderr; stdout belongs to the protocol.\n */\n/**\n * The version of the client installed beside this server. Read at run time:\n * the client is a dependency that stays external, so a JSON import of it would\n * need an import attribute Node 20 asks for and a bundler cannot give.\n */\nfunction installedClientVersion() {\n return (\n createRequire(import.meta.url)(\n '@solumflow-app/crm-client/package.json',\n ) as {\n version: string;\n }\n ).version;\n}\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 // Read from the package, so the version an agent is told is the one\n // that was published.\n version,\n });\n\n const client = createClient({\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n });\n\n // The tools are held back until all are known, so each write tool can\n // carry the fingerprint of the whole set (see `holdToolRegistrations`).\n const held = holdToolRegistrations(server);\n\n registerServerInfoTool(held.server, {\n version,\n clientVersion: installedClientVersion(),\n baseUrl: config.baseUrl,\n definitionsVersion: held.currentHash,\n });\n registerPlacementTools(held.server, client);\n registerCatalogTools(held.server, client);\n registerCatalogWriteTools(held.server, client);\n registerCatalogMigrationTools(held.server, client);\n registerEventTools(held.server, client);\n registerConfiguratorTools(held.server, client);\n held.publish();\n\n await server.connect(new StdioServerTransport());\n\n // For a client that honours it. Claude Code is known to keep the\n // definitions of the session it started with; `server_info` and the\n // `schemaHash` of a write tool are what reach an agent that does not.\n try {\n server.sendToolListChanged();\n } catch (error) {\n // A notification that could not be sent changes nothing about the tools.\n console.error('crm-mcp-server: could not announce the tool list:', error);\n }\n}\n\nmain().catch((error: unknown) => {\n console.error('crm-mcp-server:', error);\n process.exit(1);\n});\n","{\n \"name\": \"@solumflow-app/crm-mcp-server\",\n \"version\": \"0.8.0\",\n \"description\": \"Let an AI agent place an account's forms, booking pages, chat and tracking on a website, read its catalogue and events, and fill its catalogue, through the public API.\",\n \"license\": \"MIT\",\n \"bin\": {\n \"crm-mcp-server\": \"./dist/index.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"type\": \"module\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"clean\": \"git clean -xdf .turbo node_modules dist\",\n \"build\": \"tsup\",\n \"typecheck\": \"tsc --noEmit\",\n \"test:unit\": \"vitest run\",\n \"prepublishOnly\": \"pnpm run typecheck && pnpm run test:unit && pnpm run build\"\n },\n \"dependencies\": {\n \"@modelcontextprotocol/sdk\": \"catalog:\",\n \"@solumflow-app/crm-client\": \"workspace:*\",\n \"zod\": \"catalog:\"\n },\n \"devDependencies\": {\n \"@kit/crm-invoicing\": \"workspace:*\",\n \"@kit/product-configurator\": \"workspace:*\",\n \"@kit/tsconfig\": \"workspace:*\",\n \"@types/node\": \"catalog:\",\n \"tsup\": \"catalog:\",\n \"typescript\": \"catalog:\",\n \"vitest\": \"catalog:\"\n },\n \"engines\": {\n \"node\": \">=20\"\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CrmClient } from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport {\n CatalogExternalRefSchema,\n CatalogFieldSlugSchema,\n CatalogProductUpsertSchema,\n CatalogSourceSchema,\n} from '@kit/crm-invoicing/schema/catalog-api';\n\nimport { objectInput } from './catalog-tools';\nimport { answer, fieldsNotOnPageNote, refusal } from './tool-answers';\n\nimport { readFile, stat } from 'node:fs/promises';\nimport { extname } from 'node:path';\n\n/** The part of the client these tools call; a test hands in a fake of exactly this. */\nexport type CatalogMigrationClient = Pick<\n CrmClient,\n | 'getProductByRef'\n | 'upsertProducts'\n | 'removeField'\n | 'restoreField'\n | 'getCategoryByRef'\n | 'uploadCatalogFile'\n>;\n\n/** At most this many products per call to upsert_products. */\nexport const BULK_LIMIT = 25;\n\n/**\n * What the API allows, said once in every tool that writes many things. The\n * figures are `API_BUDGETS` in the app; a long HTML text counts in the bytes.\n */\nexport const LIMITS =\n 'Limits: 120 requests a minute per key and 600 per account; 5 MB of ' +\n 'request bodies a day per key and 20 MB per account (a long HTML ' +\n 'description counts). A refusal for speed says how long to wait.';\n\n/** What a file on disk may be, by kind, checked before anything is read. */\nconst FILE_RULES = {\n image: {\n maxBytes: 2 * 1024 * 1024,\n types: {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.webp': 'image/webp',\n },\n },\n file: {\n maxBytes: 20 * 1024 * 1024,\n types: {\n '.pdf': 'application/pdf',\n '.docx':\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.xlsx':\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.pptx':\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n '.txt': 'text/plain',\n '.csv': 'text/csv',\n '.zip': 'application/zip',\n },\n },\n} as const;\n\nfunction target(object: string | undefined) {\n return object ? { object } : undefined;\n}\n\nfunction notDone(text: string) {\n return { isError: true, content: [{ type: 'text' as const, text }] };\n}\n\n/**\n * The tools a migration from another system needs beyond writing one\n * product: reading a product back by its reference, writing many, taking a\n * field away and back, finding a category by reference, and sending a file\n * from disk instead of an address.\n */\nexport function registerCatalogMigrationTools(\n server: McpServer,\n client: CatalogMigrationClient,\n) {\n server.registerTool(\n 'get_product_by_ref',\n {\n title: 'Read back a product you wrote',\n description:\n 'One product by the reference it was written under (`source` + ' +\n '`externalRef`), a draft too. `fields` holds every field the product ' +\n 'has a value for, public or not, as stored -- list item ids and file ' +\n 'paths included, which a write accepts back; `publicFields` is what a ' +\n 'visitor sees of them. `categories` lists every category it is filed ' +\n 'under, a concept one too, each with its `status`. Also `status` and ' +\n '`publishedAt`, its sku and shelf per variant, the shared-link texts ' +\n 'and picture, and `files` -- for every picture or file of the last ' +\n 'write whether it is `pending`, `stored` or `refused`, with a reason ' +\n 'code. Live, never cached: read this after upsert_product to see what ' +\n 'arrived.',\n inputSchema: {\n externalRef: CatalogExternalRefSchema,\n source: CatalogSourceSchema,\n ...objectInput,\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ externalRef, source, object }) => {\n try {\n const product = await client.getProductByRef(\n externalRef,\n source,\n target(object),\n );\n\n if (!product) {\n return answer(\n `No product with reference \"${externalRef}\" from \"${source}\".`,\n null,\n );\n }\n\n const refused = product.files.filter(\n (file) => file.status === 'refused',\n );\n const pending = product.files.filter(\n (file) => file.status === 'pending',\n );\n\n return answer(\n `${product.name} (${product.slug || 'no address'}), ${product.status} (${product.availability}); ` +\n `${Object.keys(product.fields).length} fields, ${product.categories.length} categories, ` +\n `${product.images.length} pictures; files: ${pending.length} pending, ` +\n `${refused.length} refused${refused.length ? ` (${refused.map((file) => `${file.ref}: ${file.reason}`).join('; ')})` : ''}.` +\n fieldsNotOnPageNote(product.fieldsNotOnPage),\n product,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'upsert_products',\n {\n title: 'Make or change many products',\n description:\n `Up to ${BULK_LIMIT} products, one after another, each exactly as ` +\n 'upsert_product takes it (`externalRef` beside its body). A refusal ' +\n 'is that product’s outcome and the others go on; a refusal for ' +\n 'speed is waited out and the product sent again; a key that is ' +\n 'refused stops the run and the rest is `skipped`. Answers per product ' +\n '`created`, `changed`, `refused` (with the fields) or `skipped`. ' +\n LIMITS,\n inputSchema: {\n products: z\n .array(\n z.object({\n externalRef: CatalogExternalRefSchema,\n product: CatalogProductUpsertSchema,\n }),\n )\n .min(1)\n .max(BULK_LIMIT),\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ products, object }) => {\n try {\n const outcomes = await client.upsertProducts(\n products.map((item) => ({\n externalRef: item.externalRef,\n input: item.product,\n })),\n target(object),\n );\n\n const count = (kind: string) =>\n outcomes.filter((outcome) => outcome.outcome === kind).length;\n // Across the run: one line, not one per product.\n const offPage = new Set(\n outcomes.flatMap((outcome) => outcome.product?.fieldsNotOnPage ?? []),\n );\n const refusals = outcomes\n .filter((outcome) => outcome.outcome === 'refused')\n .map(\n (outcome) =>\n `- ${outcome.externalRef}: ${outcome.error?.message ?? 'refused'}` +\n (outcome.error?.fields.length\n ? ` (${outcome.error.fields.map((field) => `${field.path}: ${field.message}`).join('; ')})`\n : ''),\n );\n\n return answer(\n [\n `${count('created')} made, ${count('changed')} changed, ` +\n `${count('refused')} refused, ${count('skipped')} skipped.` +\n (offPage.size\n ? ` Some products hold public fields that are not on their page (${[...offPage].join(', ')}); send them in publicFields to show them.`\n : ''),\n ...refusals,\n ].join('\\n'),\n outcomes,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'delete_field',\n {\n title: 'Take a catalogue field away',\n description:\n 'Moves a field to the trash, as the app does: for thirty days it can ' +\n 'come back with restore_field, and every product keeps its value ' +\n 'meanwhile. Use `permanent` only when the user explicitly asks to ' +\n 'delete for good, and first without `confirm`: that only counts the ' +\n 'products that would lose their value. Show the user that count; ' +\n 'send `confirm` (the slug again) only after they agree. A field of ' +\n 'the app’s own, or one a product varies along, is refused.',\n inputSchema: {\n slug: CatalogFieldSlugSchema,\n permanent: z.boolean().optional(),\n confirm: z.string().max(63).optional(),\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ slug, permanent, confirm, object }) => {\n try {\n const result = await client.removeField(slug, {\n ...target(object),\n ...(permanent ? { permanent } : {}),\n ...(confirm ? { confirm } : {}),\n });\n\n const text =\n result.state === 'trashed'\n ? `${slug} is in the trash until ${result.restorableUntil ?? 'thirty days from now'}; ${result.productsWithValue} products keep their value.`\n : result.state === 'would_delete'\n ? `Nothing changed. Deleting ${slug} for good would clear its value on ${result.productsWithValue} products. ${result.confirm}`\n : `${slug} is deleted; its value on ${result.productsWithValue} products is being cleared.`;\n\n return answer(text, result);\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'restore_field',\n {\n title: 'Bring a catalogue field back from the trash',\n description:\n 'A field moved to the trash in the last thirty days comes back under ' +\n 'its own id, with every value the products kept.',\n inputSchema: { slug: CatalogFieldSlugSchema, ...objectInput },\n annotations: { destructiveHint: false, idempotentHint: true },\n },\n async ({ slug, object }) => {\n try {\n const field = await client.restoreField(slug, target(object));\n\n return answer(`${field.label} (${field.slug}) is back.`, field);\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'get_category_by_ref',\n {\n title: 'Find a category by its reference',\n description:\n 'The category upsert_category recorded with this `source` and ' +\n '`externalRef` -- the id of a series in your own system -- also after ' +\n 'its address was changed in the app. A published category unless you ' +\n 'pass `status`: \"draft\" finds a concept or scheduled one, \"all\" any ' +\n '(needs a server key).',\n inputSchema: {\n source: CatalogSourceSchema,\n externalRef: CatalogExternalRefSchema,\n status: z\n .enum(['published', 'draft', 'all'])\n .optional()\n .describe(\n 'Which categories to look in; anything but published needs catalog:write.',\n ),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ source, externalRef, status }) => {\n try {\n const category = await client.getCategoryByRef(\n source,\n externalRef,\n status ? { status } : undefined,\n );\n\n return answer(\n category\n ? `${category.name} (${category.slug || 'no address yet'}), id ${category.id}${category.status ? `, ${category.status}` : ''}.`\n : `No ${status && status !== 'published' ? status : 'published'} category with reference \"${externalRef}\" from \"${source}\"${status ? '' : ' (a concept category needs status \"all\")'}.`,\n category,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'upload_catalog_file',\n {\n title: 'Upload a picture or a file from disk',\n description:\n 'Reads one file from the user’s disk and uploads it, for a ' +\n 'product write to name as { \"upload\": \"<uploadPath>\" } instead of a ' +\n 'url: a picture (png, jpeg, webp, at most 2 MB) for `images`, ' +\n '`socialImage` or a list item’s image, or a \"file\" (pdf, Office, ' +\n 'txt, csv, zip, at most 20 MB) for a list item’s download. An ' +\n 'upload is kept a day.',\n inputSchema: {\n kind: z.enum(['image', 'file']),\n filePath: z\n .string()\n .min(1)\n .max(1000)\n .describe('The absolute path of the file on this machine.'),\n },\n annotations: { destructiveHint: false, idempotentHint: false },\n },\n async ({ kind, filePath }) => {\n const rules = FILE_RULES[kind];\n const extension = extname(filePath).toLowerCase();\n const contentType = (rules.types as Record<string, string>)[extension];\n\n if (!contentType) {\n return notDone(\n `A ${kind} is a ${Object.keys(rules.types).join(', ')} file; this is \"${extension || 'no extension'}\".`,\n );\n }\n\n const info = await stat(filePath).catch(() => null);\n\n if (!info?.isFile()) {\n return notDone(`\"${filePath}\" is not a file on this machine.`);\n }\n\n if (info.size > rules.maxBytes) {\n return notDone(\n `The file is ${info.size} bytes; a ${kind} may be at most ${rules.maxBytes}.`,\n );\n }\n\n try {\n const result = await client.uploadCatalogFile({\n kind,\n bytes: new Uint8Array(await readFile(filePath)),\n contentType: contentType as Parameters<\n CatalogMigrationClient['uploadCatalogFile']\n >[0]['contentType'],\n });\n\n return answer(\n `Uploaded. Name it as { \"upload\": \"${result.uploadPath}\" }.`,\n result,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n}\n","import * as z from 'zod';\n\nimport { MAX_AMOUNT_CENTS } from './amount-ceiling';\n\n/**\n * What the public API accepts when a catalogue is written to it.\n *\n * ONE RULE FOR EVERY FIELD: absent is \"leave it as it is\", `null` is \"empty\n * it\". An import runs again and again, and somebody may have corrected a\n * description in the app since the last run; a body that only names what it\n * knows must never wipe what it does not. So nothing here has a default that\n * would write on its own -- the defaults that exist (a new product starts as\n * a concept, a price is sold once) apply only to a row being created.\n *\n * `.strict()` throughout. A field the API does not know is a typo or a field\n * from a later version, and in both cases the caller should hear about it\n * rather than believe it was stored.\n */\n\n/** The spelling `crm_products.slug` and `crm_product_categories.slug` insist on. */\nconst WEB_SLUG = /^[a-z0-9]+(-[a-z0-9]+)*$/;\n\n/** The spelling `crm_attributes.slug` insists on. */\nconst FIELD_SLUG = /^[a-z][a-z0-9_]*$/;\n\n/**\n * Which system the identifiers in a body belong to. Lower case, because\n * `crm_source_refs.provider` is, and two spellings of one system would be two\n * namespaces in which every product imports again as new.\n */\nexport const CatalogSourceSchema = z\n .string()\n .regex(/^[a-z0-9][a-z0-9-]{0,63}$/, {\n message:\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n 'A source is lower-case letters, digits and hyphens, at most 64 characters (for example \"coolflames-payload\").',\n });\n\nexport const CatalogExternalRefSchema = z.string().trim().min(1).max(255);\n\nexport const CatalogWebSlugSchema = z.string().min(2).max(64).regex(WEB_SLUG, {\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'An address is lower-case letters and digits joined by hyphens.',\n});\n\nexport const CatalogFieldSlugSchema = z.string().max(63).regex(FIELD_SLUG, {\n message:\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n 'A field slug starts with a letter and holds lower-case letters, digits and underscores.',\n});\n\nconst cents = z.number().int().min(0).max(MAX_AMOUNT_CENTS);\n\n/** A category a product is filed under, with its place on that page. */\nexport const CatalogCategoryPlacementSchema = z\n .object({\n slug: CatalogWebSlugSchema,\n /** Lowest first; equal places stand in id order. */\n position: z.number().int().min(-1_000_000).max(1_000_000).optional(),\n })\n .strict();\n\nconst text = (max: number) => z.string().trim().max(max).nullable().optional();\n\nexport const CatalogPriceSchema = z\n .object({\n /** The price's own id at the source, so a renamed price stays itself. */\n externalRef: CatalogExternalRefSchema.optional(),\n label: z.string().trim().min(1).max(120),\n /**\n * ISO 4217. Left out, the price is in the product's own currency. A\n * price in another currency is the fixed price for that currency: it\n * wins over a converted one and is never the default.\n */\n currency: z\n .string()\n .regex(/^[A-Z]{3}$/)\n .optional(),\n amountCents: cents,\n compareAtCents: cents.nullable().optional(),\n kind: z.enum(['one_off', 'recurring']).optional(),\n recurringInterval: z.enum(['day', 'week', 'month', 'year']).optional(),\n recurringIntervalCount: z.number().int().min(1).max(365).optional(),\n isDefault: z.boolean().optional(),\n active: z.boolean().optional(),\n })\n .strict()\n .superRefine((price, ctx) => {\n if ((price.kind ?? 'one_off') === 'recurring' && !price.recurringInterval) {\n ctx.addIssue({\n code: 'custom',\n path: ['recurringInterval'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A recurring price needs an interval.',\n });\n }\n });\n\nconst pricesSchema = z\n .array(CatalogPriceSchema)\n .min(1)\n .max(50)\n .superRefine((prices, ctx) => {\n if (prices.filter((price) => price.isDefault).length > 1) {\n ctx.addIssue({\n code: 'custom',\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'At most one price can be the default.',\n });\n }\n\n const refs = prices.flatMap((price) =>\n price.externalRef ? [price.externalRef] : [],\n );\n\n if (new Set(refs).size !== refs.length) {\n ctx.addIssue({\n code: 'custom',\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Two prices carry the same externalRef.',\n });\n }\n\n // A label is unique per currency: \"A3\" in euros and \"A3\" in dollars are\n // the same option sold in two currencies. A price without a currency is\n // in the product's own, which only the service knows, so the exact check\n // is repeated there once that currency is known.\n const labels = prices.map(\n (price) => `${price.currency ?? ''} ${price.label.toLowerCase()}`,\n );\n\n if (new Set(labels).size !== labels.length) {\n ctx.addIssue({\n code: 'custom',\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Two prices carry the same label in the same currency.',\n });\n }\n });\n\nconst HttpsUrlSchema = z\n .string()\n .max(2048)\n .url()\n .refine((url) => url.startsWith('https://'), {\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A file is fetched over https only.',\n });\n\n/** The `uploadPath` `POST /catalog-files` answered with. */\nconst UploadPathSchema = z.string().min(1).max(200);\n\n/**\n * Where a file comes from: an https address this server fetches, or an\n * upload of the caller's own (`POST /catalog-files`). Exactly one.\n */\nexport const CatalogFileRefSchema = z.union([\n z.object({ url: HttpsUrlSchema }).strict(),\n z.object({ upload: UploadPathSchema }).strict(),\n]);\n\nexport type CatalogFileRef = z.output<typeof CatalogFileRefSchema>;\n\n/** The spelling `crm_product_images.role` insists on. */\nconst IMAGE_ROLE = /^[a-z][a-z0-9_-]{0,31}$/;\n\n/**\n * A few plain facts about one picture, for a storefront to print beside it:\n * flat, values a string, number or boolean, at most 2 KiB as the database\n * counts it (`crm_product_images_details_flat`).\n */\nexport const CatalogImageDetailsSchema = z\n .record(\n z.string().min(1).max(64),\n z.union([z.string().max(500), z.number().finite(), z.boolean()]),\n )\n .refine((details) => JSON.stringify(details).length <= 1800, {\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Details are at most about 2 KB.',\n });\n\nexport const CatalogImageSchema = z\n .object({\n url: HttpsUrlSchema.optional(),\n upload: UploadPathSchema.optional(),\n alt: text(300),\n /**\n * What the picture is of, in the account's own words: `work`, `detail`,\n * `insitu`. Left out keeps the role a stored picture has; `null` clears it.\n */\n role: z\n .string()\n .regex(IMAGE_ROLE, {\n message:\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n 'A role starts with a letter and holds lower-case letters, digits, hyphens and underscores, at most 32.',\n })\n .nullable()\n .optional(),\n details: CatalogImageDetailsSchema.optional(),\n })\n .strict()\n .refine(\n (image) => (image.url === undefined) !== (image.upload === undefined),\n {\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A picture names either a url or an upload, not both.',\n },\n );\n\n/**\n * The shelf of one physical product or variant: a count, not a movement.\n *\n * Whatever `quantity` says is what the shelf holds afterwards. A sync that\n * sends it again tomorrow writes that number again, over any correction made\n * in the app meanwhile -- the same rule as every other field, so send `stock`\n * only when the source is where the count is kept. Sales move the count\n * through the checkout, never through this.\n *\n * `managed: false` stops counting; the threshold goes with it, because a\n * warning on a product nobody counts can never fire.\n */\nexport const CatalogStockSchema = z\n .object({\n managed: z.boolean(),\n /** Left out, the count stays as it is. */\n quantity: z.number().int().min(0).max(1_000_000_000).optional(),\n /** Below this a product counts as running low; `null` warns never. */\n lowThreshold: z\n .number()\n .int()\n .min(0)\n .max(1_000_000_000)\n .nullable()\n .optional(),\n /** Whether it can still be ordered at zero: `deny`, `allow`, or `allow_notify`. */\n backorders: z.enum(['deny', 'allow', 'allow_notify']).optional(),\n })\n .strict();\n\nexport const CatalogVariantAxisSchema = z\n .object({\n slug: CatalogFieldSlugSchema,\n label: z.string().trim().min(1).max(120).optional(),\n })\n .strict();\n\nexport const CatalogVariantSchema = z\n .object({\n externalRef: CatalogExternalRefSchema,\n /** The value per axis, keyed by the axis slug. */\n options: z.record(\n CatalogFieldSlugSchema,\n z.string().trim().min(1).max(120),\n ),\n sku: text(100),\n gtin: z\n .string()\n .regex(/^[0-9]{8,14}$/)\n .nullable()\n .optional(),\n prices: pricesSchema.optional(),\n /** The variant's own shelf; a physical product counts per variant. */\n stock: CatalogStockSchema.optional(),\n position: z.number().int().min(0).max(10_000).optional(),\n active: z.boolean().optional(),\n })\n .strict();\n\nexport const CATALOG_AVAILABILITIES = [\n 'draft',\n 'internal',\n 'scheduled',\n 'public',\n] as const;\n\nexport const CatalogProductUpsertSchema = z\n .object({\n source: CatalogSourceSchema,\n /** Required to create a product; optional to update one. */\n name: z.string().trim().min(1).max(500).optional(),\n slug: CatalogWebSlugSchema.nullable().optional(),\n description: text(5_000),\n /** HTML; cleaned against the same allow-list the app uses. */\n longDescription: text(100_000),\n sku: text(100),\n gtin: z\n .string()\n .regex(/^[0-9]{8,14}$/)\n .nullable()\n .optional(),\n productType: z.enum(['digital', 'physical']).optional(),\n /**\n * Where the product stands. A new product is a `draft` unless this says\n * otherwise -- an import should not put a hundred products on somebody's\n * website because a run finished.\n */\n availability: z.enum(CATALOG_AVAILABILITIES).optional(),\n /** The moment for `scheduled`; ignored for the other three. */\n publishedAt: z.string().datetime({ offset: true }).nullable().optional(),\n visibility: z.enum(['public', 'unlisted']).optional(),\n metaTitle: text(300),\n metaDescription: text(1_000),\n /**\n * What a shared link to the product shows (the og tags): its own title,\n * description and picture. Left out, a link falls back to the name, the\n * description and the first picture. `socialImage` is fetched after the\n * answer like `images`; `null` takes it away.\n */\n socialTitle: text(300),\n socialDescription: text(1_000),\n socialImage: CatalogFileRefSchema.nullable().optional(),\n /**\n * Replaces the product's categories, by slug, the first one leading. An\n * entry may be `{ slug, position }` instead, to also set where the\n * product stands on that category's page (`?sort=position`, lowest\n * first); a plain slug leaves a stored place as it is.\n */\n categories: z\n .array(z.union([CatalogWebSlugSchema, CatalogCategoryPlacementSchema]))\n .max(50)\n .optional(),\n /**\n * Make a category that does not exist yet instead of refusing. Off unless\n * asked for, so a typo cannot become a category on a customer's website.\n */\n createMissingCategories: z.boolean().optional(),\n /** Field values by field slug; `null` empties one field. */\n fields: z.record(CatalogFieldSlugSchema, z.unknown()).optional(),\n /** Which fields the product's own page shows, in that order. */\n publicFields: z.array(CatalogFieldSlugSchema).max(100).optional(),\n /**\n * Add a choice a select field does not offer yet instead of refusing.\n * Off unless asked for, for the same reason as categories.\n */\n addMissingOptions: z.boolean().optional(),\n /** Replaces the product's prices as a whole. */\n prices: pricesSchema.optional(),\n /**\n * The product's own shelf. Physical products only, and only one without\n * variants: a product with variants counts per variant.\n */\n stock: CatalogStockSchema.optional(),\n /** Replaces the product's pictures, in this order. */\n images: z.array(CatalogImageSchema).max(20).optional(),\n variantAxes: z.array(CatalogVariantAxisSchema).min(1).max(3).optional(),\n /** Replaces the product's variants; one missing here is archived. */\n variants: z.array(CatalogVariantSchema).max(100).optional(),\n /** `false` brings back a product the API archived earlier. */\n archived: z.literal(false).optional(),\n })\n .strict()\n .superRefine((body, ctx) => {\n if (body.variants !== undefined && body.variantAxes === undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['variantAxes'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Variants need the axes they differ along.',\n });\n }\n\n if (body.variants && body.variantAxes) {\n const axes = body.variantAxes.map((axis) => axis.slug);\n const seen = new Set<string>();\n const refs = new Set<string>();\n\n body.variants.forEach((variant, index) => {\n const named = Object.keys(variant.options);\n\n if (\n named.length !== axes.length ||\n !axes.every((axis) => named.includes(axis))\n ) {\n ctx.addIssue({\n code: 'custom',\n path: ['variants', index, 'options'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: `A variant names a value for exactly these axes: ${axes.join(', ')}.`,\n });\n }\n\n const combination = axes\n .map((axis) => variant.options[axis] ?? '')\n .join('\\u0000');\n\n if (seen.has(combination)) {\n ctx.addIssue({\n code: 'custom',\n path: ['variants', index, 'options'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Two variants stand for the same combination.',\n });\n }\n\n if (refs.has(variant.externalRef)) {\n ctx.addIssue({\n code: 'custom',\n path: ['variants', index, 'externalRef'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Two variants carry the same externalRef.',\n });\n }\n\n seen.add(combination);\n refs.add(variant.externalRef);\n });\n }\n\n if (body.variantAxes) {\n const slugs = body.variantAxes.map((axis) => axis.slug);\n\n if (new Set(slugs).size !== slugs.length) {\n ctx.addIssue({\n code: 'custom',\n path: ['variantAxes'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'An axis is named twice.',\n });\n }\n }\n\n if (body.stock !== undefined && body.variantAxes !== undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['stock'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A product with variants counts its stock per variant.',\n });\n }\n\n if (body.availability === 'scheduled' && !body.publishedAt) {\n ctx.addIssue({\n code: 'custom',\n path: ['publishedAt'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A scheduled product needs the moment it appears.',\n });\n }\n });\n\nexport type CatalogProductUpsert = z.output<typeof CatalogProductUpsertSchema>;\nexport type CatalogPrice = z.output<typeof CatalogPriceSchema>;\nexport type CatalogVariant = z.output<typeof CatalogVariantSchema>;\nexport type CatalogImage = z.output<typeof CatalogImageSchema>;\nexport type CatalogStock = z.output<typeof CatalogStockSchema>;\n\n/** `DELETE` carries only the source the reference belongs to. */\nexport const CatalogProductArchiveSchema = z\n .object({ source: CatalogSourceSchema })\n .strict();\n\nexport const CatalogCategoryUpsertSchema = z\n .object({\n /**\n * The category's id in the system it came from -- a series in a site's\n * own CMS -- with `source` naming whose id it is. With both, a category\n * that carries this reference is changed, and renamed to the address in\n * the path when somebody gave it another one in the app; without, the\n * address alone finds it, as before.\n */\n source: CatalogSourceSchema.optional(),\n externalRef: CatalogExternalRefSchema.optional(),\n name: z.string().trim().min(1).max(200).optional(),\n description: text(5_000),\n /** `null` puts the category at the top of the tree. */\n parentSlug: CatalogWebSlugSchema.nullable().optional(),\n availability: z.enum(CATALOG_AVAILABILITIES).optional(),\n publishedAt: z.string().datetime({ offset: true }).nullable().optional(),\n metaTitle: text(300),\n metaDescription: text(1_000),\n })\n .strict()\n .refine(\n (body) => (body.source === undefined) === (body.externalRef === undefined),\n {\n path: ['externalRef'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A category reference names both source and externalRef.',\n },\n );\n\nexport type CatalogCategoryUpsert = z.output<\n typeof CatalogCategoryUpsertSchema\n>;\n\n/**\n * The spelling `crm_objects.slug` insists on: what `?object=` names on every\n * catalogue route. Underscores, not hyphens -- an object slug is also the\n * first segment of a merge token.\n */\nexport const CatalogObjectSlugSchema = z.string().max(64).regex(FIELD_SLUG, {\n message:\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n 'A catalogue slug starts with a letter and holds lower-case letters, digits and underscores.',\n});\n\n/**\n * A product catalogue of its own -- \"Fireplaces\" beside \"Products\". Both\n * names are what the app's own dialog asks: one product, many products. A new\n * catalogue needs both; a rename names only what changes.\n */\nexport const CatalogCatalogueUpsertSchema = z\n .object({\n nameSingular: z.string().trim().min(1).max(120).optional(),\n namePlural: z.string().trim().min(1).max(120).optional(),\n /** A lucide icon name, as the object settings offer them. */\n icon: z.string().trim().max(64).nullable().optional(),\n })\n .strict();\n\nexport type CatalogCatalogueUpsert = z.output<\n typeof CatalogCatalogueUpsertSchema\n>;\n\n/**\n * The field types a catalogue may make through the API: the ones the object\n * builder offers a product object (`PRODUCT_ATTRIBUTE_TYPES` in\n * `@kit/crm-objects`: select, text, number, checkbox, colour -- `#rrggbb`,\n * stored lower case -- and the content blocks),\n * never more, plus `url` and `date`, which the API offered before that list\n * existed and a sync may still be making.\n *\n * The three content blocks take their settings in `config`: a text block has\n * none, a call to action may offer a second button, and a repeater names how\n * it is drawn and what one entry holds. A configurator is made the same way\n * but asks for its own right (`configurator:shape`). A grid of checkboxes is\n * never made here: it answers a person on a record page a product does not\n * have.\n */\nexport const CatalogFieldTypeSchema = z.enum([\n 'text',\n 'number',\n 'select',\n 'checkbox',\n 'color',\n 'url',\n 'date',\n 'content_text',\n 'repeater',\n 'cta',\n 'configurator',\n]);\n\nexport const CATALOG_FIELD_TYPES = CatalogFieldTypeSchema.options;\n\n/** The field types whose settings live in `config`. */\nexport const CATALOG_CONFIG_FIELD_TYPES = [\n 'number',\n 'content_text',\n 'repeater',\n 'cta',\n 'configurator',\n] as const satisfies readonly (typeof CATALOG_FIELD_TYPES)[number][];\n\nexport const CatalogFieldOptionSchema = z\n .object({\n value: z.string().trim().min(1).max(120),\n label: z.string().trim().min(1).max(120).optional(),\n })\n .strict();\n\nexport const CatalogAttributeUpsertSchema = z\n .object({\n label: z.string().trim().min(1).max(120).optional(),\n type: CatalogFieldTypeSchema.optional(),\n /** For a select: whether a product may carry more than one choice. */\n multiple: z.boolean().optional(),\n /** Choices to make sure exist; never removes one that is not listed. */\n options: z.array(CatalogFieldOptionSchema).max(500).optional(),\n /** Choices to take off the list, by value. */\n removeOptions: z\n .array(z.string().trim().min(1).max(120))\n .max(500)\n .optional(),\n /** Whether the public API may show this field at all. */\n public: z.boolean().optional(),\n /**\n * The field's settings, replaced as a whole. Checked against the same\n * schema the object builder saves with, per type: a repeater's `display`,\n * `preset`, `itemFields` and `titleKey`; a call to action's `secondary`;\n * a configurator's whole structure. An item field keeps its type once it\n * exists.\n */\n config: z.record(z.string(), z.unknown()).optional(),\n /**\n * Say what this body would change -- a new field, a new label, choices\n * added, choices taken away and how many products hold each -- and\n * write nothing.\n */\n dryRun: z.boolean().optional(),\n /**\n * Bring the field back from the trash under its own id, with every value\n * the products kept. Nothing else may be sent with it.\n */\n restore: z.literal(true).optional(),\n /**\n * For a new content field (`content_text`, `repeater`, `cta`): put its\n * block at the end of the product page. On by default; `false` makes the\n * field only. Never moves or restores a block of an existing field.\n */\n placeOnPage: z.boolean().optional(),\n })\n .strict();\n\nexport type CatalogAttributeUpsert = z.output<\n typeof CatalogAttributeUpsertSchema\n>;\n\n/**\n * A file for a configurator, uploaded in two steps: `start` asks for a signed\n * upload address, the caller PUTs the bytes there, and `finish` has the\n * server read them back and check them. A model (`.glb`) is shape and asks\n * for `configurator:shape`; a texture (`.webp` or `.jpg`) is a material, which\n * `catalog:write` may change, as in the app.\n */\nexport const CONFIGURATOR_FILE_KINDS = ['model', 'texture'] as const;\n\nexport const CONFIGURATOR_TEXTURE_CONTENT_TYPES = [\n 'image/webp',\n 'image/jpeg',\n] as const;\n\nexport const ConfiguratorFileUploadSchema = z.discriminatedUnion('step', [\n z\n .object({\n step: z.literal('start'),\n /** The slug of the configurator field the file is for. */\n field: CatalogFieldSlugSchema,\n kind: z.enum(CONFIGURATOR_FILE_KINDS),\n /** The bytes the caller is about to send, so a too-large file is refused before it travels. */\n size: z.number().int().min(1),\n /** For a texture: what kind of picture it is. A model is always glTF binary. */\n contentType: z.enum(CONFIGURATOR_TEXTURE_CONTENT_TYPES).optional(),\n })\n .strict(),\n z\n .object({\n step: z.literal('finish'),\n field: CatalogFieldSlugSchema,\n kind: z.enum(CONFIGURATOR_FILE_KINDS),\n /** The `uploadPath` `start` answered with. */\n uploadPath: z.string().max(200),\n })\n .strict(),\n]);\n\nexport type ConfiguratorFileUpload = z.output<\n typeof ConfiguratorFileUploadSchema\n>;\n\n/**\n * A picture or a download from an integration's own disk, in two steps:\n * `start` names what is coming and is answered with a signed upload address,\n * the caller PUTs the bytes there with that content type, and `finish` has\n * the server read them back and check them. The `uploadPath` it answers with\n * is what a product write names instead of a `url` (`{ \"upload\": ... }`).\n */\nexport const CATALOG_FILE_KINDS = ['image', 'file'] as const;\n\nexport const CATALOG_FILE_CONTENT_TYPES = [\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n 'application/pdf',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n 'text/plain',\n 'text/csv',\n 'application/zip',\n] as const;\n\nexport const CatalogFileUploadSchema = z.discriminatedUnion('step', [\n z\n .object({\n step: z.literal('start'),\n /** A picture (2 MB, png/jpeg/webp) or a download for a list item (20 MB). */\n kind: z.enum(CATALOG_FILE_KINDS),\n /** The bytes about to be sent, so a too-large file is refused before it travels. */\n size: z.number().int().min(1),\n contentType: z.enum(CATALOG_FILE_CONTENT_TYPES),\n })\n .strict(),\n z\n .object({\n step: z.literal('finish'),\n kind: z.enum(CATALOG_FILE_KINDS),\n /** The `uploadPath` `start` answered with. */\n uploadPath: z.string().max(200),\n })\n .strict(),\n]);\n\nexport type CatalogFileUpload = z.output<typeof CatalogFileUploadSchema>;\n","/**\n * Stripe refuses a `unit_amount` above 99,999,999 in the currency's smallest\n * unit. Below that ceiling the number is only as big as the tenant meant it to\n * be; above it, the save succeeds and the first checkout fails — which is the\n * worst possible place to learn about a typo.\n *\n * A file of its own, reachable without the schema barrel, because the ceiling\n * has to hold on every path that can write a price. The barrel pulls in the\n * proposal blocks and their editor dependencies, so a caller that only needs\n * one number would otherwise drag an email editor along with it — and the\n * cheapest way out of that is a second copy of the number, which is exactly\n * how a ceiling stops being one.\n */\nexport const MAX_AMOUNT_CENTS = 99_999_999;\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type {\n CatalogField,\n CategoryListItem,\n CrmClient,\n ProductListItem,\n} from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport { CatalogObjectSlugSchema } from '@kit/crm-invoicing/schema/catalog-api';\n\nimport { answer, money, more, page, refusal } from './tool-answers';\n\n/**\n * Which catalogue a catalogue tool is about. Most accounts have one; one that\n * keeps fireplaces apart from its other products has two, and list_catalogs\n * names them.\n */\nexport const objectInput = {\n object: CatalogObjectSlugSchema.optional().describe(\n 'The slug of the catalogue (see list_catalogs). Leave out for the account\\u2019s own catalogue.',\n ),\n};\n\n/** The part of the client these tools call; a test hands in a fake of exactly this. */\nexport type CatalogClient = Pick<\n CrmClient,\n | 'getFields'\n | 'getCatalogs'\n | 'getCurrencies'\n | 'getProduct'\n | 'getProducts'\n | 'getCategories'\n | 'getCategory'\n | 'getAvailability'\n>;\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 'A new block field stands at the end of the product page from the moment ' +\n 'upsert_field makes it (`placeOnPage: false` skips that).';\n\nconst currency = z\n .string()\n .regex(/^[A-Z]{3}$/)\n .optional()\n .describe(\n 'Every price in this currency (see list_currencies), each with its origin: base, fixed or converted. Left out, every price in its own currency.',\n );\n\nconst READ_STATUS_HINT =\n 'Left out, only published ones. \"draft\" is what is not on the website yet ' +\n '(concept, internal or scheduled), \"archived\" what was taken out, \"all\" ' +\n 'everything; each answers with a `status` and `publishedAt` per item. ' +\n 'Anything but published needs a key with catalog:write.';\n\nconst productStatus = z\n .enum(['published', 'draft', 'archived', 'all'])\n .optional()\n .describe(`Which products. ${READ_STATUS_HINT}`);\n\nconst categoryStatus = z\n .enum(['published', 'draft', 'all'])\n .optional()\n .describe(\n `Which categories. ${READ_STATUS_HINT.replace('\"archived\" what was taken out, ', '')} A category cannot be archived.`,\n );\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: { ...objectInput },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ object }) => {\n try {\n const fields = await client.getFields(object ? { object } : undefined);\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, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'list_catalogs',\n {\n title: 'List the product catalogues',\n description:\n 'The product catalogues this key can write to, the account\\u2019s own ' +\n 'first. A catalogue other than the account\\u2019s own is named by its ' +\n 'slug in `object` on every catalogue tool; upsert_catalog makes one.',\n inputSchema: {},\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n try {\n const catalogs = await client.getCatalogs();\n\n return answer(\n catalogs\n .map(\n (catalog) =>\n `${catalog.name} (${catalog.slug})${catalog.isDefault ? ', the account\\u2019s own' : ''}`,\n )\n .join('\\n') || 'This account has no product catalogue.',\n catalogs,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'list_currencies',\n {\n title: 'List the currencies the shop sells in',\n description:\n 'The account\\u2019s own currency first, then the ones it switched on, ' +\n 'with a name and symbol: what a currency menu on a website is built ' +\n 'from. Pass one as `currency` to get_product or list_products for ' +\n 'prices in it. Which currencies are offered, and at what rate, is set ' +\n 'in the app (Settings \\u2192 Verkoop \\u2192 Valuta), not through this tool.',\n inputSchema: {\n locale: z\n .string()\n .min(2)\n .max(35)\n .optional()\n .describe('The language names are written in, such as \"nl\".'),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ locale }) => {\n try {\n const currencies = await client.getCurrencies(\n locale ? { locale } : undefined,\n );\n\n return answer(\n currencies\n .map(\n (option) =>\n `${option.code} (${option.symbol}) ${option.name}${option.isDefault ? ', the account\\u2019s own' : ''}`,\n )\n .join('\\n'),\n currencies,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\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. Published ' +\n 'products only unless you pass `status` (needs a server key), and ' +\n 'a published one may be a minute old: to read back exactly what you ' +\n 'just wrote, use get_product_by_ref. Answers that it is not found ' +\n 'for an unknown product, or one not in the status asked. ' +\n CONTENT_BLOCKS,\n inputSchema: { slugOrId, currency, status: productStatus },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ slugOrId: wanted, currency: inCurrency, status }) => {\n try {\n const product = await client.getProduct(\n wanted,\n inCurrency || status\n ? {\n ...(inCurrency ? { currency: inCurrency } : {}),\n ...(status ? { status } : {}),\n }\n : undefined,\n );\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, 'catalog:read');\n }\n },\n );\n server.registerTool(\n 'list_products',\n {\n title: 'List the products',\n description:\n 'One page of the catalogue: name, address (slug), lowest price, ' +\n 'whether it is in stock, its first picture and the public fields a ' +\n 'listing shows. Narrow it to one category\\u2019s whole branch by its ' +\n 'id or address, or by the reference upsert_category recorded ' +\n '(`categorySource` + `categoryRef`), to one catalogue by the slug of ' +\n 'its object, or to the product carrying an article number (`sku`). ' +\n 'Read get_product for everything a product page needs. ' +\n 'Published products only, and may be a minute old; `status` lists ' +\n 'concepts, scheduled and archived ones too (needs a server key). ' +\n '`sort` orders the published ones: -publishedAt (newest first, the ' +\n 'default), publishedAt, name, or position -- the seller\\u2019s own ' +\n 'order on the page of `category`, which it needs. Each item carries ' +\n '`sku`, `publishedAt` and `stockQuantity` (null when not counted).',\n inputSchema: {\n ...page,\n category: z\n .string()\n .regex(\n /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[a-z0-9]+(-[a-z0-9]+)*)$/i,\n )\n .max(64)\n .optional()\n .describe(\n 'A category id or address (slug); includes its subcategories.',\n ),\n categorySource: z\n .string()\n .max(64)\n .optional()\n .describe('With categoryRef: whose id the category reference is.'),\n categoryRef: z\n .string()\n .max(255)\n .optional()\n .describe('A category by the id it has in your own system.'),\n sku: z\n .string()\n .min(1)\n .max(100)\n .optional()\n .describe(\n 'The product carrying this article number, or whose variant does.',\n ),\n object: z\n .string()\n .min(1)\n .max(63)\n .optional()\n .describe(\n 'One catalogue, by the slug of its object, such as \"product\".',\n ),\n currency,\n status: productStatus,\n sort: z\n .enum(['-publishedAt', 'publishedAt', 'name', 'position'])\n .optional()\n .describe(\n 'The order: newest first by default; position needs `category`.',\n ),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ categorySource, categoryRef, ...input }) => {\n try {\n const list = await client.getProducts({\n ...input,\n ...(categorySource && categoryRef\n ? {\n categoryRef: {\n source: categorySource,\n externalRef: categoryRef,\n },\n }\n : {}),\n });\n\n return answer(\n list.data.length === 0\n ? `No ${input.status && input.status !== 'published' ? input.status : 'published'} products match.`\n : [...list.data.map(describeProduct), more(list.nextCursor)]\n .filter(Boolean)\n .join('\\n'),\n list,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'list_categories',\n {\n title: 'List the categories',\n description:\n 'The categories of the catalogue, each with its path from the top ' +\n 'and how many products are in it. Left without `parent`, every ' +\n 'category comes back, which is what building a menu in one go ' +\n 'needs; \"root\" asks for the top level, a category id for its ' +\n 'children. Published categories only unless you pass `status` ' +\n '(draft or all, needs a server key).',\n inputSchema: {\n ...page,\n parent: z\n .union([z.literal('root'), z.string().uuid()])\n .optional()\n .describe('\"root\" or the id of a category.'),\n status: categoryStatus,\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async (input) => {\n try {\n const list = await client.getCategories(input);\n\n return answer(\n list.data.length === 0\n ? `No ${input.status && input.status !== 'published' ? input.status : 'published'} categories.`\n : [...list.data.map(describeCategory), more(list.nextCursor)]\n .filter(Boolean)\n .join('\\n'),\n list,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'get_category',\n {\n title: 'Read one category page',\n description:\n 'One category as its page needs it: name, description, the texts ' +\n 'for search engines and social media, and the first page of its ' +\n 'products. Pass the nextCursor back to page through the products. ' +\n 'A category that is not on the website yet needs `status` ' +\n '(draft or all, a server key); its products are the published ones.',\n inputSchema: {\n slugOrId: z\n .string()\n .min(1)\n .max(200)\n .describe('The address (slug) or the id of the category.'),\n ...page,\n status: categoryStatus,\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ slugOrId: wanted, ...listOptions }) => {\n try {\n const category = await client.getCategory(wanted, listOptions);\n\n if (!category) {\n return answer(`No category \"${wanted}\" on this account.`, null);\n }\n\n const { data, nextCursor } = category;\n\n return answer(\n [\n `${data.name} (${data.slug}), ${data.productCount} products.`,\n ...data.products.map(describeProduct),\n more(nextCursor),\n ]\n .filter(Boolean)\n .join('\\n'),\n category,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'get_product_availability',\n {\n title: 'Whether a product can be bought now',\n description:\n 'Live, not cached: whether a visitor can buy the product right now. ' +\n 'Never how many are left. For a stock badge that must be current; ' +\n 'the inStock of a listing may be a minute old.',\n inputSchema: { slugOrId },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ slugOrId: wanted }) => {\n try {\n const status = await client.getAvailability(wanted);\n\n if (!status) {\n return answer(`No product \"${wanted}\" on this account.`, null);\n }\n\n return answer(\n `${status.slug}: ${status.inStock ? 'in stock' : 'out of stock'}.`,\n status,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n}\n\nfunction describeProduct(product: ProductListItem) {\n const stock = `${product.inStock ? '' : ', out of stock'}${\n product.status ? `, ${product.status}` : ''\n }`;\n\n const from =\n product.priceFromCents === null\n ? `no price in ${product.currency}`\n : `from ${money(product.priceFromCents, product.currency)}`;\n\n return `- ${product.name} (${product.slug}): ${from}${stock}`;\n}\n\nfunction describeCategory(category: CategoryListItem) {\n const path = category.path.map((step) => step.name).join(' / ');\n\n const state = category.status ? `, ${category.status}` : '';\n\n return `- ${path || category.name} (${category.slug || 'no address yet'}, id ${category.id}${state}): ${category.productCount} products`;\n}\n\nfunction describeField(field: CatalogField) {\n if (field.type !== 'repeater') {\n return `- ${field.slug}: ${field.label}, ${field.type}${settingsOf(field)}`;\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\n/**\n * The settings a field of any other type shows, as `key value` after the\n * type: the unit beside a number, how an amount is written, how a choice is\n * drawn. The JSON beside the text has them whole.\n */\nconst SETTING_KEYS = [\n 'prefix',\n 'suffix',\n 'currency',\n 'symbolPosition',\n 'thousandsSeparator',\n 'decimalSeparator',\n 'decimals',\n 'display',\n 'layout',\n 'mode',\n 'secondary',\n] as const;\n\nfunction settingsOf(field: CatalogField) {\n const given = SETTING_KEYS.flatMap((key) =>\n field[key] === undefined ? [] : [`${key} ${JSON.stringify(field[key])}`],\n );\n\n if (field.rows) {\n given.push(`${field.rows.length} rows`);\n }\n\n if (field.columns) {\n given.push(`${field.columns.length} columns`);\n }\n\n return given.length > 0 ? ` (${given.join(', ')})` : '';\n}\n","import { CrmApiError } from '@solumflow-app/crm-client';\nimport { z } from 'zod';\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/** The scopes the tools of this server ask for; the API's names, verbatim. */\nexport type ToolScope =\n | 'website:manage'\n | 'catalog:read'\n | 'catalog:write'\n | 'events:read'\n | 'configurator:shape';\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. `scope` is the one the\n * tool's route asks for, because the API deliberately does not say which\n * scope was missing.\n */\nexport function refusal(error: unknown, scope: ToolScope) {\n if (!(error instanceof CrmApiError)) {\n throw error;\n }\n\n const lines = [`${error.code}: ${error.message}`];\n const next =\n error.code === 'forbidden'\n ? `The key may not do this. It needs to be a secret key with the scope \"${scope}\".`\n : 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 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:\n 'Too many requests: 120 a minute per key, 600 per account, and 5 MB of bodies a day per key. Wait the seconds the answer names 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/** The input every listing takes: the cursor of the page before, and a size. */\nexport const 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\n/** The line that tells an agent there is a next page, or nothing. */\nexport function more(nextCursor: string | null) {\n return nextCursor ? `More on the next page: cursor ${nextCursor}` : '';\n}\n\n/** An amount as a person reads it; the JSON beside it keeps the cents. */\nexport function money(cents: number, currency: string) {\n return `${(cents / 100).toFixed(2)} ${currency.toUpperCase()}`;\n}\n\n/**\n * A sentence about public fields that hold a value but stay off a product's\n * page, or nothing. Said out loud because the silent version -- an import\n * that never sent `publicFields` -- leaves every page with no fields and no\n * refusal anywhere.\n */\nexport function fieldsNotOnPageNote(slugs: readonly string[] | undefined) {\n if (!slugs?.length) {\n return '';\n }\n\n const count =\n slugs.length === 1\n ? '1 public field has'\n : `${slugs.length} public fields have`;\n\n return (\n ` ${count} a value but ${slugs.length === 1 ? 'is' : 'are'} not on this product's page: ` +\n `${slugs.join(', ')}; send them in publicFields to show them.`\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { CrmApiError, type CrmClient } from '@solumflow-app/crm-client';\n\nimport {\n CatalogAttributeUpsertSchema,\n CatalogCatalogueUpsertSchema,\n CatalogCategoryUpsertSchema,\n CatalogExternalRefSchema,\n CatalogFieldSlugSchema,\n CatalogObjectSlugSchema,\n CatalogProductArchiveSchema,\n CatalogProductUpsertSchema,\n CatalogWebSlugSchema,\n} from '@kit/crm-invoicing/schema/catalog-api';\n\nimport { objectInput } from './catalog-tools';\nimport { answer, fieldsNotOnPageNote, 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 CatalogWriteClient = Pick<\n CrmClient,\n | 'upsertProduct'\n | 'archiveProduct'\n | 'deleteProduct'\n | 'upsertCategory'\n | 'upsertField'\n | 'previewField'\n | 'upsertCatalog'\n>;\n\n/**\n * Where a refused delete says the product is used, in words: the API's\n * `details.blockers` (`{ kind, count }` pairs), or null for an answer that\n * carries none.\n */\nfunction usedIn(details: unknown) {\n const blockers = (details as { blockers?: unknown } | null)?.blockers;\n\n if (!Array.isArray(blockers) || blockers.length === 0) {\n return null;\n }\n\n return blockers\n .map(\n (entry: { kind?: unknown; count?: unknown }) =>\n `${String(entry.kind).replaceAll('_', ' ')} (${String(entry.count)})`,\n )\n .join(', ');\n}\n\n/** `object` for the client: only when the agent named another catalogue. */\nfunction target(object: string | undefined) {\n return object ? { object } : undefined;\n}\n\n/**\n * What each kind of field takes in `config`, said once: an agent cannot see\n * the app's builder, and a repeater without its item fields is refused.\n */\nconst FIELD_KINDS =\n 'Kinds: text, number, select, checkbox, color (a value \"#rrggbb\"; ' +\n 'upper case is stored lower case), url, date, and three content ' +\n 'blocks for a product page: \"content_text\" (formatted text, no config), ' +\n '\"cta\" (a call to action; config { secondary: true } offers a second ' +\n 'button) and \"repeater\" (a list, an accordion such as a FAQ, downloads or ' +\n 'testimonials; config { display: \"list\" | \"accordion\", preset?: \"list\" | ' +\n '\"faq\" | \"downloads\" | \"testimonials\", itemFields: [{ key, label, type: ' +\n 'text | richtext | url | icon | image | file | number | select }], ' +\n 'titleKey?: the item field that names an item }). A number item may name ' +\n '{ min?, max?, suffix? }, a select item names { options: [{ value, label }] } ' +\n '(at most 100); its value is one of those values. An image or file item ' +\n 'is filled with { url } or { upload } in upsert_product, fetched after ' +\n 'the answer. A number may take config { prefix?, suffix? }. ' +\n 'A configurator is made with upsert_configurator. The kind is fixed once ' +\n 'the field exists, and so is the type of a repeater\\u2019s item field.';\n\n/**\n * What an agent has to understand before it writes anything, said once and\n * repeated in each product tool: the reference is the identity, and a write\n * changes a customer's live catalogue.\n */\nconst IDENTITY =\n 'A product is known by `externalRef`, its id in the system it comes from, ' +\n 'together with `source`, the name of that system (for example ' +\n '\"acme-webshop\"). The same pair always means the same product: sending it ' +\n 'again changes it instead of making a second one, so use references that ' +\n 'stay the same, never a name that may change.';\n\n/**\n * Writing the catalogue through the API's own routes. The input schemas are\n * the routes' own, bundled in, so an agent is told the same limits the API\n * enforces; what a schema cannot say on one field alone (variants against\n * their axes, a scheduled product without a moment) the API answers with the\n * field it is about.\n */\nexport function registerCatalogWriteTools(\n server: McpServer,\n client: CatalogWriteClient,\n) {\n server.registerTool(\n 'upsert_product',\n {\n title: 'Make or change a product',\n description:\n IDENTITY +\n ' Every field but `source` may be left out, and left out means ' +\n '\"leave it as it is\"; null empties a field. A new product needs a ' +\n '`name` and is a draft unless `availability` says otherwise: do not ' +\n 'publish without the user asking. `prices`, `categories`, `images` ' +\n 'and `variants` each replace what the product had as a whole, and a ' +\n 'variant left out is archived. Categories are named by slug and a ' +\n 'missing one is refused unless `createMissingCategories` is true; a ' +\n 'choice a select field does not offer is refused unless ' +\n '`addMissingOptions` is true. Pictures are https URLs, fetched after ' +\n 'the answer (`images: \"pending\"`). Amounts are in cents. A price ' +\n \"without `currency` is in the product's own; one with another \" +\n '`currency` is the fixed price for that currency, may share its ' +\n 'label with the euro price, and can never be the default. ' +\n '`stock` (physical only; per variant when there are variants) is a ' +\n 'count, not a movement: a second run writes it again over a ' +\n 'correction made in the app, so send it only when the source keeps ' +\n 'the count. A picture is { url } or { upload } (upload_catalog_file), ' +\n 'with `alt`, a `role` in your own words (\"insitu\") and flat ' +\n '`details` ({ widthCm: 120 }); `socialImage` is the shared-link ' +\n 'picture beside `socialTitle` and `socialDescription`. Files arrive ' +\n 'after the answer: get_product_by_ref says per file whether it came. ' +\n '`description` is plain text (5,000 characters); `longDescription` ' +\n 'is HTML, cleaned to headings, paragraphs, lists, links, bold and ' +\n 'italic. A repeater is sent whole: `items` replaces the list. An item ' +\n 'may leave out its `id` -- SolumFlow makes one, and `fields` in the ' +\n 'answer holds it; send the id back to change that item, or the items ' +\n 'are replaced by new ones and their files fetched again. An image or ' +\n 'file item key is { url: \"https://...\" } (fetched after the answer) ' +\n 'or { upload }; a wrong one is refused with the path of the item. ' +\n 'To write many products use upsert_products. ' +\n 'Read list_catalog_fields first for the slugs `fields` takes.',\n inputSchema: {\n externalRef: CatalogExternalRefSchema.describe(\n 'The id of the product in the system named by `source`.',\n ),\n ...CatalogProductUpsertSchema.shape,\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ externalRef, object, ...input }) => {\n try {\n const product = await client.upsertProduct(\n externalRef,\n input,\n target(object),\n );\n const verb = product.created ? 'Made' : 'Changed';\n\n return answer(\n `${verb} ${product.name} (${product.slug ?? 'no address'}), ${product.availability}; ` +\n `${product.variants.length} variants; pictures ${product.images}.` +\n fieldsNotOnPageNote(product.fieldsNotOnPage),\n product,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'archive_product',\n {\n title: 'Take a product out of the catalogue',\n description:\n IDENTITY +\n ' Archiving takes the product and its variants off the website and ' +\n 'out of the shop at once. It stays in the database for every invoice ' +\n 'that names it, and upsert_product with `archived: false` brings it ' +\n 'back. Ask the user before archiving anything they did not name.',\n inputSchema: {\n externalRef: CatalogExternalRefSchema.describe(\n 'The id of the product in the system named by `source`.',\n ),\n ...CatalogProductArchiveSchema.shape,\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ externalRef, source, object }) => {\n try {\n const result = await client.archiveProduct(\n externalRef,\n source,\n target(object),\n );\n\n return answer(\n result.alreadyArchived\n ? 'That product was already archived.'\n : 'Archived.',\n result,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'delete_product',\n {\n title: 'Delete a product for good',\n description:\n IDENTITY +\n ' Deleting removes the product, its variants, prices and pictures ' +\n 'for good; it cannot be undone. It only works for a product that ' +\n 'nothing uses: one that is on an order, a payment page, a ' +\n 'subscription, a booking or a hosting service is refused, and the ' +\n 'answer says where. Then archive_product is the way out. Archive ' +\n 'when in doubt, and ask the user before deleting anything they did ' +\n 'not name.',\n inputSchema: {\n externalRef: CatalogExternalRefSchema.describe(\n 'The id of the product in the system named by `source`.',\n ),\n ...CatalogProductArchiveSchema.shape,\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ externalRef, source, object }) => {\n try {\n const result = await client.deleteProduct(\n externalRef,\n source,\n target(object),\n );\n\n return answer('Deleted for good.', result);\n } catch (error) {\n if (error instanceof CrmApiError && error.code === 'conflict') {\n const places = usedIn(error.details);\n\n if (places) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text:\n `conflict: ${error.message}\\n` +\n `Used in: ${places}.\\n` +\n 'Archive it with archive_product instead.',\n },\n ],\n };\n }\n }\n\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'upsert_category',\n {\n title: 'Make or change a category',\n description:\n 'A category, known by its address (slug): sending the same slug ' +\n 'again changes it. Left-out fields stay as they are; a new category ' +\n 'needs a `name`. `parentSlug` places it under another category, null ' +\n 'at the top. A new category is a draft unless `availability` says ' +\n 'otherwise. Categories belong to one catalogue: a product names only ' +\n 'categories of its own catalogue. Send `source` and `externalRef` -- ' +\n 'the id of the series in your own system -- and the category keeps ' +\n 'that identity: it is found by it even after its address was changed ' +\n 'in the app (and gets the address of this call back), and ' +\n 'list_products narrows by it. Content of a series (long texts, ' +\n 'galleries) stays in your own CMS.',\n inputSchema: {\n slug: CatalogWebSlugSchema.describe(\n 'The address of the category: lower case, digits and dashes.',\n ),\n ...CatalogCategoryUpsertSchema.shape,\n ...objectInput,\n },\n annotations: { destructiveHint: false, idempotentHint: true },\n },\n async ({ slug, object, ...input }) => {\n try {\n const category = await client.upsertCategory(\n slug,\n input,\n target(object),\n );\n\n return answer(\n `${category.created ? 'Made' : 'Changed'} ${category.name} (${category.slug}).`,\n category,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'upsert_field',\n {\n title: 'Make or change a catalogue field',\n description:\n 'A custom field on products, known by its slug: sending the same ' +\n 'slug again changes it. A new field needs a `label`. ' +\n FIELD_KINDS +\n ' `config` replaces the settings as a whole. `options` makes sure ' +\n 'choices exist and never removes one; `removeOptions` takes choices ' +\n 'off the list, which empties them on every product that had them. ' +\n '`public` decides whether the website may show the field at all. ' +\n 'A new content field (content_text, repeater, cta) gets its block at ' +\n 'the end of the product page in the same save, and the answer says ' +\n '`placedOnPage`; `placeOnPage: false` makes the field only. An ' +\n 'existing field is never placed again, so a block somebody took off ' +\n 'the page stays off. ' +\n '`dryRun: true` answers what would change -- and how many products ' +\n 'hold each choice that would go -- and writes nothing. A slug in the ' +\n 'trash is taken until restore_field or a permanent delete_field.',\n inputSchema: {\n slug: CatalogFieldSlugSchema.describe(\n 'The slug of the field: a lower-case letter, then lower case, digits and underscores.',\n ),\n ...CatalogAttributeUpsertSchema.shape,\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ slug, object, ...input }) => {\n try {\n if (input.dryRun) {\n const { dryRun: _dryRun, ...rest } = input;\n const preview = await client.previewField(slug, rest, target(object));\n\n return answer(`Nothing written. ${JSON.stringify(preview)}`, preview);\n }\n\n const field = await client.upsertField(slug, input, target(object));\n\n return answer(\n `${field.created ? 'Made' : 'Changed'} ${field.label} (${field.slug}), ${field.type}, ` +\n `${field.public ? 'public' : 'not public'}.` +\n (field.placedOnPage === undefined\n ? ''\n : field.placedOnPage\n ? ' Its block is at the end of the product page.'\n : ` Its block is not on the product page (${field.notPlacedBecause ?? 'not placed'}).`),\n field,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'upsert_catalog',\n {\n title: 'Make or rename a product catalogue',\n description:\n 'A product catalogue of its own beside the account\\u2019s products, ' +\n 'such as \"Fireplaces\", known by its slug: sending the same slug again ' +\n 'renames it. A new one needs both names, one product and many. Its ' +\n 'fields, categories and products are then written with `object` set ' +\n 'to this slug. Make one only when the user asks for a separate ' +\n 'catalogue; most products belong in the account\\u2019s own.',\n inputSchema: {\n slug: CatalogObjectSlugSchema.describe(\n 'The slug of the catalogue: a lower-case letter, then lower case, digits and underscores.',\n ),\n ...CatalogCatalogueUpsertSchema.shape,\n },\n annotations: { destructiveHint: false, idempotentHint: true },\n },\n async ({ slug, ...input }) => {\n try {\n const catalog = await client.upsertCatalog(slug, input);\n\n return answer(\n `${catalog.created ? 'Made' : 'Changed'} ${catalog.namePlural} (${catalog.slug}).`,\n catalog,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CrmClient } from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport {\n CatalogFieldSlugSchema,\n CONFIGURATOR_FILE_KINDS,\n} from '@kit/crm-invoicing/schema/catalog-api';\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 { objectInput } from './catalog-tools';\nimport { answer, refusal } from './tool-answers';\n\nimport { readFile, stat } from 'node:fs/promises';\nimport { extname } from 'node:path';\n\n/** The part of the client these tools call; a test hands in a fake of exactly this. */\nexport type ConfiguratorClient = Pick<\n CrmClient,\n 'getProduct' | 'upsertField' | 'uploadConfiguratorFile'\n>;\n\n/**\n * What a file on the user's disk may be, by kind: its extensions, its type\n * and the most bytes the API keeps. Checked here before anything is read, so\n * a folder, a wrong file or a huge one never leaves the machine.\n */\nconst FILE_RULES = {\n model: { extensions: ['.glb'], maxBytes: 10 * 1024 * 1024 },\n texture: {\n extensions: ['.webp', '.jpg', '.jpeg'],\n maxBytes: 2 * 1024 * 1024,\n },\n} as const;\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,\n * checks it here, and saves it with upsert_configurator -- which the API only\n * accepts from a key carrying `configurator:shape`, a right a reseller gives,\n * because the shape is the reseller's work.\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\", optional \"scene\" ({ \"lighting\": \"studio\" | ' +\n '\"warm\" | \"daylight\" }), \"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), model (a \"model\" path to a .glb ' +\n 'uploaded with upload_configurator_file, params.fit 0 contain or 1 stretch, ' +\n 'params.paint 1 to use the slot material) and repeat (count, children, ' +\n 'index i 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\\u2019s inputs, constants ' +\n 'and the repeat index. Texture paths in materials come from ' +\n 'upload_configurator_file too. Making a configurator, or changing its ' +\n '\"geometry\", needs a key with the configurator:shape scope; everything ' +\n 'around the shape needs only catalog:write.';\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: ConfiguratorClient,\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 'API will when it is saved: 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. upsert_configurator saves it.'\n : [\n `${problems.length} problem(s):`,\n ...problems.map(describeIssue),\n ].join('\\n'),\n { problems },\n );\n },\n );\n\n server.registerTool(\n 'upsert_configurator',\n {\n title: 'Save a configurator',\n description:\n 'Makes a configurator field, or changes one, with its whole ' +\n 'structure. The structure is checked here first, exactly as ' +\n 'check_configurator_shape does, and nothing is sent while it has a ' +\n 'problem. Making the field, or changing its \"geometry\", needs a key ' +\n 'with configurator:shape; changing parameters, lists, materials, ' +\n 'prices or rules of an existing one needs only catalog:write. A new ' +\n 'field needs a `label`. Upload models and textures first with ' +\n 'upload_configurator_file and put the paths it answers in the ' +\n 'structure. ' +\n CONFIGURATOR,\n inputSchema: {\n slug: CatalogFieldSlugSchema.describe(\n 'The slug of the configurator field.',\n ),\n label: z.string().trim().min(1).max(120).optional(),\n structure: z\n .string()\n .min(2)\n .max(200_000)\n .describe(\n 'The JSON text of the whole configurator structure (it has \"schemaVersion\").',\n ),\n public: z\n .boolean()\n .optional()\n .describe('Whether product pages may show the configurator.'),\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ slug, label, structure, public: isPublic, object }) => {\n const problems = checkDocument(structure);\n\n if (problems.length > 0 || !structure.includes('\"schemaVersion\"')) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: [\n problems.length > 0\n ? `Not saved. ${problems.length} problem(s):`\n : 'Not saved: this is a shape document alone; save a whole structure (it has \"schemaVersion\").',\n ...problems.map(describeIssue),\n ].join('\\n'),\n },\n ],\n };\n }\n\n try {\n const field = await client.upsertField(\n slug,\n {\n ...(label ? { label } : {}),\n type: 'configurator',\n config: JSON.parse(structure) as Record<string, unknown>,\n ...(isPublic === undefined ? {} : { public: isPublic }),\n },\n object ? { object } : undefined,\n );\n\n return answer(\n `${field.created ? 'Made' : 'Changed'} the configurator ${field.label} (${field.slug}).`,\n field,\n );\n } catch (error) {\n return refusal(error, 'configurator:shape');\n }\n },\n );\n\n server.registerTool(\n 'upload_configurator_file',\n {\n title: 'Upload a model or a texture for a configurator',\n description:\n 'Reads one file from the user\\u2019s disk and uploads it for a ' +\n 'configurator field: a \"model\" is a binary glTF (.glb, at most 10 MB, ' +\n 'everything inside the file, no Draco, no lights of its own, at most ' +\n '200,000 triangles) and part of the shape, so it needs ' +\n 'configurator:shape; a \"texture\" is a .webp or .jpg picture of at ' +\n 'most 2 MB. Answers with the path to put in the structure. The field ' +\n 'must exist first (upsert_configurator).',\n inputSchema: {\n fieldSlug: CatalogFieldSlugSchema.describe(\n 'The slug of the configurator field.',\n ),\n kind: z.enum(CONFIGURATOR_FILE_KINDS),\n filePath: z\n .string()\n .min(1)\n .max(1000)\n .describe('The absolute path of the file on this machine.'),\n ...objectInput,\n },\n annotations: { destructiveHint: false, idempotentHint: false },\n },\n async ({ fieldSlug, kind, filePath, object }) => {\n const rules = FILE_RULES[kind];\n const extension = extname(filePath).toLowerCase();\n\n if (!(rules.extensions as readonly string[]).includes(extension)) {\n return notUploaded(\n `A ${kind} is a ${rules.extensions.join(' or ')} file; this is \"${extension || 'no extension'}\".`,\n );\n }\n\n const info = await stat(filePath).catch(() => null);\n\n if (!info?.isFile()) {\n return notUploaded(`\"${filePath}\" is not a file on this machine.`);\n }\n\n if (info.size > rules.maxBytes) {\n return notUploaded(\n `The file is ${info.size} bytes; a ${kind} may be at most ${rules.maxBytes}.`,\n );\n }\n\n try {\n const bytes = new Uint8Array(await readFile(filePath));\n const result = await client.uploadConfiguratorFile(\n fieldSlug,\n {\n kind,\n bytes,\n ...(kind === 'texture'\n ? {\n contentType:\n extension === '.webp' ? 'image/webp' : 'image/jpeg',\n }\n : {}),\n },\n object ? { object } : undefined,\n );\n\n return answer(\n `Uploaded. Put this path in the structure: ${result.path}`,\n result,\n );\n } catch (error) {\n return refusal(\n error,\n kind === 'model' ? 'configurator:shape' : 'catalog:write',\n );\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, 'catalog:read');\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 notUploaded(text: string) {\n return {\n isError: true,\n content: [{ type: 'text' as const, text: `Not uploaded. ${text}` }],\n };\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 'model',\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 // An uploaded `.glb` placed in the block's frame. `fit` 0 scales it evenly\n // and centres it (the default), 1 stretches it to the frame: a handle that\n // grows with the door; 2 keeps the model's own size, standing on the\n // frame's floor in the middle and against its front: a fire insert, which\n // is as large as it is. `paint` 1 gives every mesh the slot's material, so\n // it follows the visitor's colour; 0 (the default) keeps the model's own.\n model: { required: [], optional: ['fit', 'paint'] },\n};\n\n/**\n * `{account}/{attribute}/{uuid}.glb` in the model bucket. Like a texture path,\n * the database checks the first two against the row; the URL is built by the\n * server, never stored. A model only ever reaches the bucket through the\n * upload route, which reads it first (`inspect-glb.ts`).\n */\nexport const CONFIGURATOR_MODEL_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}\\.glb$/;\n\n/**\n * What a model block that changes with a choice follows: the structure's\n * presets (the fireplace a model offers) or a choice parameter by its key.\n */\nexport const CONFIGURATOR_FOLLOWS_PRESET = 'preset';\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 /** Only on a `model` block: where its `.glb` is stored (a URL once served). */\n model?: string;\n /**\n * Only on a `model` block that shows one file per choice: `preset` or the\n * key of a choice parameter. Its files are in `models`.\n */\n follows?: string;\n /** One file per code of what the block follows; a code without one draws nothing. */\n models?: Record<string, string>;\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 PrimitiveNodeFields = {\n id: ConfiguratorNameSchema,\n slot: ConfiguratorNameSchema,\n size: Vector3Schema,\n at: Vector3Schema,\n visible: FormulaSchema.optional(),\n params: z.record(ConfiguratorNameSchema, FormulaSchema).optional(),\n};\n\nconst PrimitiveNodeSchema = z.strictObject({\n ...PrimitiveNodeFields,\n type: z.enum(PRIMITIVE_TYPES).exclude(['model']),\n});\n\n/**\n * A model's file as stored -- a path the database checks against the row --\n * and as a browser receives it: a URL the server built from that path.\n */\nexport const ModelPathSchema = z\n .string()\n .regex(CONFIGURATOR_MODEL_PATH_PATTERN, 'configurator.invalidModelPath');\n\nexport const ServedFileUrlSchema = z.url({ protocol: /^https?$/ });\n\n/**\n * The node tree, for one form of file: a model block names its file, every\n * other block is drawn from numbers alone. A model's slot is still required:\n * the frame is drawn in it while the model loads or where it cannot, and the\n * dimension sketch colours it.\n */\nfunction geometryNodeSchema(modelFile: z.ZodType<string>) {\n const ModelNodeSchema = z.strictObject({\n ...PrimitiveNodeFields,\n type: z.literal('model'),\n model: modelFile,\n });\n\n // A file per choice: the fireplace a visitor picks is the one drawn.\n const ModelPerChoiceNodeSchema = z.strictObject({\n ...PrimitiveNodeFields,\n type: z.literal('model'),\n follows: ConfiguratorNameSchema,\n models: z\n .record(ConfiguratorCodeSchema, modelFile)\n .refine(\n (models) => Object.keys(models).length <= CONFIGURATOR_LIMITS.presets,\n { message: 'configurator.tooMany' },\n ),\n });\n\n const node: z.ZodType<GeometryNode> = z.lazy(() =>\n z.union([\n PrimitiveNodeSchema,\n ModelNodeSchema,\n ModelPerChoiceNodeSchema,\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(node).min(1),\n }),\n ]),\n );\n\n return node;\n}\n\nexport const GeometryNodeSchema = geometryNodeSchema(ModelPathSchema);\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 * Which way the grain of a picture runs on this part: `horizontal` (the\n * default) as the picture is drawn, `vertical` turned a quarter. It belongs\n * to the part, not the material: the same oak lies on a cabinet and stands\n * on a tall wall panel.\n */\n grain: z.enum(['horizontal', 'vertical']).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\n/** A shape document whose model files are of the given form. */\nexport function geometryDocumentSchema(modelFile: z.ZodType<string>) {\n return 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\n .array(GeometryInputSchema)\n .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(modelFile)).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}\n\nexport const GeometryDocumentSchema = geometryDocumentSchema(ModelPathSchema);\n\nexport type GeometryDocument = z.infer<typeof GeometryDocumentSchema>;\nexport type GeometrySlot = z.infer<typeof GeometrySlotSchema>;\nexport type GeometryAnnotation = z.infer<typeof GeometryAnnotationSchema>;\n\n/**\n * Every model block that follows a choice, at any depth, with the name a\n * formula reads to know whether it is drawn now: `<id>_shown` is 1 while the\n * chosen code has a file and 0 otherwise. That is how a drawn stand-in -- the\n * flame and the glass of a fireplace without a model -- steps aside the\n * moment a real model takes its place (`visible: \"1 - haard_shown\"`).\n */\nexport function choiceModelBlocks(nodes: readonly GeometryNode[]) {\n const found: { name: string; node: GeometryPrimitiveNode }[] = [];\n const walk = (list: readonly GeometryNode[]) => {\n for (const node of list) {\n if (node.type === 'repeat') {\n walk(node.children);\n } else if (node.type === 'model' && node.follows !== undefined) {\n found.push({ name: `${node.id}_shown`, node });\n }\n }\n };\n\n walk(nodes);\n\n return found;\n}\n","import { type ConfiguratorIssue, issue } from './configurator-issue';\nimport { CONFIGURATOR_LIMITS } from './configurator-limits';\nimport { type Formula, formulaNames } from './evaluate-formula';\nimport {\n CONFIGURATOR_FOLLOWS_PRESET,\n choiceModelBlocks,\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 /** Whether the structure offers presets, which a model block may follow. */\n hasPresets?: boolean;\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 // A block that follows a choice gives its formulas `<id>_shown`; an input or\n // a constant of that name would be overwritten by it without a word.\n const shownNames = choiceModelBlocks(document.root).map(\n (block) => block.name,\n );\n\n for (const name of shownNames) {\n if (inputNames.has(name) || constantNames.includes(name)) {\n problems.push(\n issue([...base, 'root'], 'configurator.duplicateName', { name }),\n );\n }\n }\n\n const shapeNames = new Set([...inputNames, ...constantNames, ...shownNames]);\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 if (node.follows !== undefined && context.parameters) {\n problems.push(\n ...followProblems(node.follows, nodePath, {\n parameters: context.parameters,\n hasPresets: context.hasPresets ?? false,\n }),\n );\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\n/**\n * A model block that follows a choice must follow something that is there:\n * the presets, when the structure offers them, or a choice parameter. A file\n * for a code the list no longer has is not a problem: the owner may take an\n * option away without touching the shape, and that file is simply never\n * drawn.\n */\nfunction followProblems(\n follows: string,\n path: (string | number)[],\n context: {\n parameters: ReadonlyMap<string, GeometryParameterInfo>;\n hasPresets: boolean;\n },\n): ConfiguratorIssue[] {\n if (follows === CONFIGURATOR_FOLLOWS_PRESET) {\n return context.hasPresets\n ? []\n : [issue([...path, 'follows'], 'configurator.followsNoPresets')];\n }\n\n return context.parameters.get(follows)?.kind === 'choice'\n ? []\n : [\n issue([...path, 'follows'], 'configurator.followsNotAChoice', {\n param: follows,\n }),\n ];\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 ModelPathSchema,\n ServedFileUrlSchema,\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\nconst choiceListSchema = (picture: z.ZodType<string>) =>\n z.strictObject({\n key: ConfiguratorNameSchema,\n label: LabelSchema,\n options: z\n .array(\n z.strictObject({\n code: ConfiguratorCodeSchema,\n label: LabelSchema,\n image: picture.optional(),\n }),\n )\n .min(1)\n .max(CONFIGURATOR_LIMITS.optionsPerList),\n });\n\nconst materialSchema = (picture: z.ZodType<string>) =>\n z.strictObject({\n code: ConfiguratorCodeSchema,\n label: LabelSchema,\n color: z.string().regex(/^#[0-9a-f]{6}$/i, 'configurator.invalidColor'),\n texture: picture.optional(),\n /** Millimetres one repeat of the texture covers; the two maps below share it. */\n textureSize: z.number().positive().max(10_000).optional(),\n /** Relief that moves with the light: a tangent-space normal map. */\n normalMap: picture.optional(),\n /** How deep the relief reads; 1 is the map as drawn. */\n normalScale: z.number().min(0).max(3).optional(),\n /** Where the surface is matte and where it shines (the green channel), times `roughness`. */\n roughnessMap: picture.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\nexport const ConfiguratorChoiceListSchema = choiceListSchema(TexturePathSchema);\nexport const ConfiguratorMaterialSchema = materialSchema(TexturePathSchema);\n\n/**\n * Every key on a material that holds a storage path. `withTextureUrls` turns\n * each into a URL and the database's `kit.crm_configurator_texture_paths_problem`\n * checks each against the row's account and attribute; a test holds both to\n * this list, so a fourth map cannot be served without being checked.\n */\nexport const CONFIGURATOR_MATERIAL_PATH_KEYS = [\n 'texture',\n 'normalMap',\n 'roughnessMap',\n] as const satisfies readonly (keyof ConfiguratorMaterial)[];\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\n/**\n * How the scene is lit. Each is a whole look -- exposure, the room's\n * reflections and the colour of the main lamp -- and never a setting a site\n * passes in: a configurator looks the same in the object builder, on the\n * product page and on a customer's own site. A fireplace reads warmer in\n * `warm`, a cabinet truer in `studio`.\n */\nexport const CONFIGURATOR_SCENE_LIGHTINGS = [\n 'studio',\n 'warm',\n 'daylight',\n] as const;\n\nexport type ConfiguratorSceneLighting =\n (typeof CONFIGURATOR_SCENE_LIGHTINGS)[number];\n\n/**\n * How a block's edges are drawn: `soft` (the default) rounds every edge by a\n * few millimetres so it catches the light, `sharp` draws a plain box. Only\n * the picture changes; every measure stays the block's.\n */\nexport const CONFIGURATOR_SCENE_EDGES = ['soft', 'sharp'] as const;\n\nexport type ConfiguratorSceneEdges = (typeof CONFIGURATOR_SCENE_EDGES)[number];\n\nexport const ConfiguratorSceneSchema = z.strictObject({\n lighting: z.enum(CONFIGURATOR_SCENE_LIGHTINGS).optional(),\n edges: z.enum(CONFIGURATOR_SCENE_EDGES).optional(),\n});\n\n/**\n * The structure, for one form of file: as stored (storage paths the database\n * checks) or as a browser receives it (URLs the server built from them).\n */\nconst structureObjectSchema = (files: {\n picture: z.ZodType<string>;\n model: z.ZodType<string>;\n}) =>\n z.strictObject({\n schemaVersion: z.literal(1),\n geometry: geometryDocumentSchema(files.model),\n /**\n * How the scene is presented. Part of the structure and not of the shape\n * document, so the owner sets it under `crm.manage` like a material.\n */\n scene: ConfiguratorSceneSchema.optional(),\n tabs: z\n .array(\n z.strictObject({ key: ConfiguratorNameSchema, label: LabelSchema }),\n )\n .min(1)\n .max(CONFIGURATOR_LIMITS.tabs),\n parameters: z\n .array(ConfiguratorParameterSchema)\n .max(CONFIGURATOR_LIMITS.parameters),\n choiceLists: z\n .array(choiceListSchema(files.picture))\n .max(CONFIGURATOR_LIMITS.choiceLists),\n materials: z\n .array(materialSchema(files.picture))\n .max(CONFIGURATOR_LIMITS.materials),\n priceGroups: z\n .array(\n z.strictObject({ code: ConfiguratorCodeSchema, label: LabelSchema }),\n )\n .max(CONFIGURATOR_LIMITS.priceGroups),\n /** Named amounts a model fills in and price rules read (`meter_price`). */\n amounts: z\n .array(\n z.strictObject({ key: ConfiguratorNameSchema, label: LabelSchema }),\n )\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 const ConfiguratorStructureObjectSchema = structureObjectSchema({\n picture: TexturePathSchema,\n model: ModelPathSchema,\n});\n\n/**\n * The structure as `withTextureUrls` serves it: every file a URL. What a site\n * receiving it reads it with -- the stored form would refuse every URL as a\n * path that is not one.\n */\nexport const ServedConfiguratorStructureObjectSchema = structureObjectSchema({\n picture: ServedFileUrlSchema,\n model: ServedFileUrlSchema,\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 hasPresets: structure.presets !== undefined,\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';\nimport type { ChosenCodes } from './expand-geometry';\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 /**\n * Only one value is left: a single option, or a range with `min` equal to\n * `max`. It counts exactly like any other value -- in the picture, the price\n * and the selection -- but there is nothing to ask, so a panel leaves it out,\n * unless its one option costs extra or is priced on request: then the panel\n * shows it without a control, so the total can be traced.\n * This is how one product of a family pins what the shared structure offers.\n */\n settled?: 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 grain: 'horizontal' | 'vertical';\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 settled: fixedValue === undefined && min === max,\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 settled: options.length === 1,\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 grain: slot.grain === 'vertical' ? 'vertical' : 'horizontal',\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\n/**\n * The code each choice stands on, by parameter key, and the chosen preset's\n * code beside them: what a model block that follows a choice reads to know\n * which file to draw. A hidden parameter counts as not chosen.\n */\nexport function chosenCodes(configuration: ResolvedConfiguration): ChosenCodes {\n const choices: Record<string, string> = {};\n\n for (const parameter of configuration.parameters) {\n if (\n parameter.kind === 'choice' &&\n parameter.visible &&\n typeof parameter.value === 'string'\n ) {\n choices[parameter.key] = parameter.value;\n }\n }\n\n return { choices, preset: configuration.preset?.code };\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 {\n CONFIGURATOR_FOLLOWS_PRESET,\n type GeometryDocument,\n type GeometryNode,\n type PrimitiveType,\n choiceModelBlocks,\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 /** A `model` block's file: a storage path, or its URL once served. */\n model?: string;\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\n/**\n * What a model block that follows a choice reads to know which file to draw:\n * the code each choice parameter stands on, by its key, and the chosen\n * preset's code apart from them -- kept apart, so a choice whose key happens\n * to be `preset` is never taken for the fireplace.\n */\nexport interface ChosenCodes {\n choices: Readonly<Record<string, string>>;\n preset?: string;\n}\n\n/**\n * `chosen` says which code each thing a model block can follow stands on. A\n * block whose choice has no file -- \"no fireplace\", or nothing chosen -- is\n * left out.\n */\nexport function expandGeometry(\n document: GeometryDocument,\n inputs: Readonly<Record<string, number>>,\n chosen: ChosenCodes = { choices: {} },\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 for (const block of choiceModelBlocks(document.root)) {\n baseScope[block.name] = modelOf(block.node, chosen) === undefined ? 0 : 1;\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 model = modelOf(node, chosen);\n\n // Nothing to draw for this choice; the blocks after it still are.\n if (node.type === 'model' && model === undefined) {\n continue;\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 ...(model === undefined ? {} : { model }),\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\n/** The file a model block draws now: its one file, or the one for the chosen code. */\nfunction modelOf(\n node: Exclude<GeometryNode, { type: 'repeat' }>,\n chosen: ChosenCodes,\n) {\n if (node.follows === undefined) {\n return node.model;\n }\n\n const code =\n node.follows === CONFIGURATOR_FOLLOWS_PRESET\n ? chosen.preset\n : chosen.choices[node.follows];\n\n return code === undefined ? undefined : node.models?.[code];\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CrmClient, EventListItem } from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport { answer, money, more, page, 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 EventClient = Pick<\n CrmClient,\n 'getEvents' | 'getEvent' | 'getEventAvailability'\n>;\n\nconst moment = z\n .string()\n .datetime({ offset: true })\n .optional()\n .describe('An ISO timestamp with its offset, such as 2026-11-01T00:00:00Z.');\n\n/**\n * The account's ticketed events, for an agenda on a website. Read only: an\n * event is made and put on sale in the app, where its tickets and seats are.\n */\nexport function registerEventTools(server: McpServer, client: EventClient) {\n server.registerTool(\n 'list_events',\n {\n title: 'List upcoming events',\n description:\n 'The published events of the account, soonest first: title, when ' +\n 'and where, the lowest ticket price, whether it is sold out and the ' +\n 'link to its ticket page. Both ends of the window are optional and ' +\n 'inclusive. Events are made in the app; this cannot create one. ' +\n 'May be a minute old.',\n inputSchema: { ...page, from: moment, to: moment },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async (input) => {\n try {\n const list = await client.getEvents(input);\n\n return answer(\n list.events.length === 0\n ? 'No published events in this window.'\n : [...list.events.map(describeEvent), more(list.nextCursor)]\n .filter(Boolean)\n .join('\\n'),\n list,\n );\n } catch (error) {\n return refusal(error, 'events:read');\n }\n },\n );\n\n server.registerTool(\n 'get_event',\n {\n title: 'Read one event',\n description:\n 'One event as its page needs it: description, doors and sales ' +\n 'times, address, ticket types with their prices, and the texts for ' +\n 'search engines. May be a minute old; ask get_event_availability ' +\n 'for seats that must be current.',\n inputSchema: {\n idOrSlug: z\n .string()\n .min(1)\n .max(200)\n .describe('The id or the address (slug) of the event.'),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ idOrSlug }) => {\n try {\n const event = await client.getEvent(idOrSlug);\n\n if (!event) {\n return answer(`No event \"${idOrSlug}\" on this account.`, null);\n }\n\n return answer(\n `${event.title} (${event.slug}), ${event.status}, ${event.ticketTypes.length} ticket types.`,\n event,\n );\n } catch (error) {\n return refusal(error, 'events:read');\n }\n },\n );\n\n server.registerTool(\n 'get_event_availability',\n {\n title: 'Seats left for an event',\n description:\n 'Live, not cached: how many seats are left for the event and for ' +\n 'each ticket type, and whether it is sold out. A remaining of null ' +\n 'means there is no ceiling.',\n inputSchema: {\n eventId: z.string().uuid().describe('The id of the event.'),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ eventId }) => {\n try {\n const seats = await client.getEventAvailability(eventId);\n\n if (!seats) {\n return answer(`No event \"${eventId}\" on this account.`, null);\n }\n\n return answer(\n seats.soldOut\n ? 'Sold out.'\n : `${seats.remaining ?? 'No limit on'} seats left.`,\n seats,\n );\n } catch (error) {\n return refusal(error, 'events:read');\n }\n },\n );\n}\n\nfunction describeEvent(event: EventListItem) {\n const where = event.locationName ? `, ${event.locationName}` : '';\n const price =\n event.priceFromCents !== null && event.currency\n ? `, from ${money(event.priceFromCents, event.currency)}`\n : '';\n const sold = event.soldOut ? ', sold out' : '';\n\n return `- ${event.title} (id ${event.id}): ${event.startsAt}${where}${price}${sold} — ${event.url}`;\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, more, page, 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 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, 'website:manage');\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, 'website:manage');\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, 'website:manage');\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, 'website:manage');\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, 'website:manage');\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, 'website:manage');\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","/**\n * Where the server finds its account: a secret key and the app's address,\n * both from the environment. Placing things needs `website:manage` and\n * filling the catalogue `catalog:write`, which only a secret (`crms_`) key\n * can hold, so a publishable key is refused here rather than on the first\n * call. Which tools answer is then the key's scopes, asked by the API.\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 in the app ' +\n '(Settings → API keys) with the scopes the agent needs: ' +\n '\"website:manage\" to place things on a site, \"catalog:read\" and ' +\n '\"events:read\" to read, \"catalog:write\" to fill the catalogue, and ' +\n '\"configurator:shape\" -- which only a reseller can give -- to make a ' +\n '3D configurator and upload its models. Settings → Integrations → ' +\n 'MCP server makes such a key and the command to add this server in ' +\n 'one go. Pass it in the 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. This server needs a `crms_` ' +\n 'key: a `crmp_` key is meant for a browser and can never place ' +\n 'things on a website or write 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","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nimport { answer } from './tool-answers';\n\n/** What `server_info` reports; the hash is asked at the time, after every tool is registered. */\nexport interface ServerInfo {\n version: string;\n clientVersion: string;\n baseUrl: string;\n definitionsVersion: () => string;\n}\n\n/**\n * Which server this is. An agent keeps the tool definitions of the session it\n * started in; this is the way to see whether the server it is talking to is\n * newer than those: compare `definitionsVersion` with the one in the\n * description of a write tool.\n */\nexport function registerServerInfoTool(server: McpServer, info: ServerInfo) {\n server.registerTool(\n 'server_info',\n {\n title: 'Which version of this server is running',\n description:\n 'The version of this MCP server and of the API client it uses, the ' +\n 'address of the API it talks to, and `definitionsVersion`, a ' +\n 'fingerprint of every tool definition it serves. A write tool names ' +\n 'the same fingerprint in its description; when the two differ, your ' +\n 'session was started before an update of this server and works from ' +\n 'older tool definitions -- restart the session (in Claude Code ' +\n '`/mcp` and reconnect, or a new session). The server cannot do that ' +\n 'for you.',\n inputSchema: {},\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n const result = {\n version: info.version,\n clientVersion: info.clientVersion,\n baseUrl: info.baseUrl,\n definitionsVersion: info.definitionsVersion(),\n };\n\n return answer(\n `crm-mcp-server ${result.version} (client ${result.clientVersion}), ` +\n `API ${result.baseUrl}, tool definitions ${result.definitionsVersion}.`,\n result,\n );\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nimport { createHash } from 'node:crypto';\n\n/**\n * A tool as a register function hands it over. The input is a map of zod\n * schemas, the shape `McpServer.registerTool` takes.\n */\ninterface ToolConfig {\n title?: string;\n description?: string;\n inputSchema?: Record<string, z.ZodType>;\n annotations?: { readOnlyHint?: boolean } & Record<string, unknown>;\n}\n\n// i18n-exempt: nothing here is shown in the app; it is read by an agent\n// `any` is the SDK's own handler type erased: this wrapper passes the\n// arguments through untouched and never looks inside them.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ToolHandler = (...args: any[]) => unknown;\n\ninterface PendingTool {\n name: string;\n config: ToolConfig;\n handler: ToolHandler;\n}\n\n/**\n * A short fingerprint of what the tools say about themselves: names, titles,\n * descriptions, input schemas and annotations. It changes when any of them\n * does, and for nothing else, so two servers with the same tools agree on it\n * and an agent holding older definitions can be told so.\n */\nexport function toolDefinitionsHash(\n tools: readonly { name: string; config: ToolConfig }[],\n) {\n const definitions = [...tools]\n .sort((left, right) => left.name.localeCompare(right.name))\n .map(({ name, config }) => ({\n name,\n title: config.title ?? null,\n description: config.description ?? null,\n inputSchema: config.inputSchema\n ? z.toJSONSchema(z.object(config.inputSchema), {\n io: 'input',\n unrepresentable: 'any',\n })\n : null,\n annotations: config.annotations ?? null,\n }));\n\n return createHash('sha256')\n .update(JSON.stringify(definitions))\n .digest('hex')\n .slice(0, 12);\n}\n\n/** The tools that change something take a `schemaHash`; a read tool has no use for one. */\nfunction writesSomething(config: ToolConfig) {\n return config.annotations?.readOnlyHint !== true;\n}\n\n/**\n * Holds the registrations of every tool back until all are known, so that the\n * fingerprint of the whole set can be put in each write tool's description\n * and checked when one is called.\n *\n * Why it exists: an agent keeps the tool definitions it was handed when its\n * session began, and a session that outlives an update of this server calls\n * the new server with the old definitions. The server cannot make the agent\n * reload. What it can do is tell, on the next write, that the definitions the\n * agent is using are not the ones it is serving, and say what to do about it.\n * The warning never refuses the call.\n *\n * `server` is what the register functions are given: it takes `registerTool`\n * for later and passes everything else to the real server. `publish` registers\n * the held tools and returns the fingerprint.\n */\nexport function holdToolRegistrations(real: McpServer) {\n const pending: PendingTool[] = [];\n let hash: string | undefined;\n\n const server = new Proxy(real, {\n get(target, property) {\n if (property === 'registerTool') {\n return (name: string, config: ToolConfig, handler: ToolHandler) => {\n pending.push({ name, config, handler });\n\n // The register functions do not use what the SDK returns.\n return undefined;\n };\n }\n\n const value = Reflect.get(target, property, target) as unknown;\n\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n\n function currentHash() {\n hash ??= toolDefinitionsHash(pending);\n\n return hash;\n }\n\n function publish() {\n const version = currentHash();\n\n for (const { name, config, handler } of pending) {\n if (!writesSomething(config)) {\n real.registerTool(\n name,\n config as Parameters<McpServer['registerTool']>[1],\n handler as Parameters<McpServer['registerTool']>[2],\n );\n continue;\n }\n\n real.registerTool(\n name,\n {\n ...config,\n description: `${config.description ?? ''} Definitions version ${version}: send it as schemaHash and the answer tells you when these tool definitions are older than the server.`,\n inputSchema: {\n ...config.inputSchema,\n schemaHash: z\n .string()\n .max(64)\n .optional()\n .describe(\n `The definitions version named in this tool's description (${version}).`,\n ),\n },\n } as Parameters<McpServer['registerTool']>[1],\n (async (\n { schemaHash, ...input }: { schemaHash?: string },\n extra: unknown,\n ) => {\n const result = (await handler(input, extra)) as {\n content?: { type: 'text'; text: string }[];\n };\n\n return schemaHash !== undefined && schemaHash !== version\n ? {\n ...result,\n content: [\n ...(result.content ?? []),\n { type: 'text' as const, text: staleDefinitions(version) },\n ],\n }\n : result;\n }) as unknown as Parameters<McpServer['registerTool']>[2],\n );\n }\n\n return version;\n }\n\n return { server: server as McpServer, publish, currentHash };\n}\n\n/** What the answer adds when an agent's tool definitions are not the server's. */\nexport function staleDefinitions(version: string) {\n return (\n `Note: your tool definitions are older than this server (its version is ${version}). ` +\n 'The call was carried out as given. Restart the session, or reconnect ' +\n 'the server with /mcp in Claude Code, to load the current definitions; ' +\n 'server_info says which version is running.'\n );\n}\n"],"mappings":"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","names":["z","z","z","text","cents","currency","z","object","object","text","z","CrmApiError","target","object","CrmApiError","z","text","text","z","z","checkFormula","z","z","z","z","target","z","z","preset","current","number","readFile","stat","extname","FILE_RULES","z","object","slugOrId","text","z","z","z","z","page","z","target","version"]}
1
+ {"version":3,"sources":["../src/index.ts","../package.json","../src/catalog-migration-tools.ts","../../features/crm-invoicing/src/schema/catalog-api.schema.ts","../../features/crm-invoicing/src/schema/amount-ceiling.ts","../src/catalog-tools.ts","../src/tool-answers.ts","../src/catalog-write-tools.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/room-palette.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/event-tools.ts","../src/placement-tools.ts","../src/server-config.ts","../src/server-info-tool.ts","../src/shop-setup-tools.ts","../src/shop-test-order-tool.ts","../src/tool-definitions-version.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 { version } from '../package.json';\nimport { registerCatalogMigrationTools } from './catalog-migration-tools';\nimport { registerCatalogTools } from './catalog-tools';\nimport { registerCatalogWriteTools } from './catalog-write-tools';\nimport { registerConfiguratorTools } from './configurator-tools';\nimport { registerEventTools } from './event-tools';\nimport { registerPlacementTools } from './placement-tools';\nimport { readServerConfig } from './server-config';\nimport { registerServerInfoTool } from './server-info-tool';\nimport { registerShopSetupTools } from './shop-setup-tools';\nimport { registerShopTestOrderTool } from './shop-test-order-tool';\nimport { holdToolRegistrations } from './tool-definitions-version';\n\nimport { createRequire } from 'node:module';\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, read the catalogue and the\n * events to build their pages, and fill the catalogue. It holds no rules of\n * its own: every tool is one call of the public API through the published\n * client, and the key's scopes decide which of them answer.\n *\n * Errors go to stderr; stdout belongs to the protocol.\n */\n/**\n * The version of the client installed beside this server. Read at run time:\n * the client is a dependency that stays external, so a JSON import of it would\n * need an import attribute Node 20 asks for and a bundler cannot give.\n */\nfunction installedClientVersion() {\n return (\n createRequire(import.meta.url)(\n '@solumflow-app/crm-client/package.json',\n ) as {\n version: string;\n }\n ).version;\n}\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 // Read from the package, so the version an agent is told is the one\n // that was published.\n version,\n });\n\n const client = createClient({\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n });\n\n // The tools are held back until all are known, so each write tool can\n // carry the fingerprint of the whole set (see `holdToolRegistrations`).\n const held = holdToolRegistrations(server);\n\n registerServerInfoTool(held.server, {\n version,\n clientVersion: installedClientVersion(),\n baseUrl: config.baseUrl,\n definitionsVersion: held.currentHash,\n });\n registerPlacementTools(held.server, client);\n registerCatalogTools(held.server, client);\n registerCatalogWriteTools(held.server, client);\n registerCatalogMigrationTools(held.server, client);\n registerEventTools(held.server, client);\n registerConfiguratorTools(held.server, client);\n registerShopSetupTools(held.server, client);\n registerShopTestOrderTool(held.server, client);\n held.publish();\n\n await server.connect(new StdioServerTransport());\n\n // For a client that honours it. Claude Code is known to keep the\n // definitions of the session it started with; `server_info` and the\n // `schemaHash` of a write tool are what reach an agent that does not.\n try {\n server.sendToolListChanged();\n } catch (error) {\n // A notification that could not be sent changes nothing about the tools.\n console.error('crm-mcp-server: could not announce the tool list:', error);\n }\n}\n\nmain().catch((error: unknown) => {\n console.error('crm-mcp-server:', error);\n process.exit(1);\n});\n","{\n \"name\": \"@solumflow-app/crm-mcp-server\",\n \"version\": \"0.9.0\",\n \"description\": \"Let an AI agent place an account's forms, booking pages, chat and tracking on a website, read its catalogue and events, fill its catalogue, and set up and prove a shop, through the public API.\",\n \"license\": \"MIT\",\n \"bin\": {\n \"crm-mcp-server\": \"./dist/index.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"type\": \"module\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"clean\": \"git clean -xdf .turbo node_modules dist\",\n \"build\": \"tsup\",\n \"typecheck\": \"tsc --noEmit\",\n \"test:unit\": \"vitest run\",\n \"prepublishOnly\": \"pnpm run typecheck && pnpm run test:unit && pnpm run build\"\n },\n \"dependencies\": {\n \"@modelcontextprotocol/sdk\": \"catalog:\",\n \"@solumflow-app/crm-client\": \"workspace:*\",\n \"zod\": \"catalog:\"\n },\n \"devDependencies\": {\n \"@kit/crm-invoicing\": \"workspace:*\",\n \"@kit/product-configurator\": \"workspace:*\",\n \"@kit/tsconfig\": \"workspace:*\",\n \"@types/node\": \"catalog:\",\n \"tsup\": \"catalog:\",\n \"typescript\": \"catalog:\",\n \"vitest\": \"catalog:\"\n },\n \"engines\": {\n \"node\": \">=20\"\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CrmClient } from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport {\n CatalogExternalRefSchema,\n CatalogFieldSlugSchema,\n CatalogProductUpsertSchema,\n CatalogSourceSchema,\n} from '@kit/crm-invoicing/schema/catalog-api';\n\nimport { objectInput } from './catalog-tools';\nimport { answer, fieldsNotOnPageNote, refusal } from './tool-answers';\n\nimport { readFile, stat } from 'node:fs/promises';\nimport { extname } from 'node:path';\n\n/** The part of the client these tools call; a test hands in a fake of exactly this. */\nexport type CatalogMigrationClient = Pick<\n CrmClient,\n | 'getProductByRef'\n | 'upsertProducts'\n | 'removeField'\n | 'restoreField'\n | 'getCategoryByRef'\n | 'uploadCatalogFile'\n>;\n\n/** At most this many products per call to upsert_products. */\nexport const BULK_LIMIT = 25;\n\n/**\n * What the API allows, said once in every tool that writes many things. The\n * figures are `API_BUDGETS` in the app; a long HTML text counts in the bytes.\n */\nexport const LIMITS =\n 'Limits: 120 requests a minute per key and 600 per account; 5 MB of ' +\n 'request bodies a day per key and 20 MB per account (a long HTML ' +\n 'description counts). A refusal for speed says how long to wait.';\n\n/** What a file on disk may be, by kind, checked before anything is read. */\nconst FILE_RULES = {\n image: {\n maxBytes: 2 * 1024 * 1024,\n types: {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.webp': 'image/webp',\n },\n },\n file: {\n maxBytes: 20 * 1024 * 1024,\n types: {\n '.pdf': 'application/pdf',\n '.docx':\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.xlsx':\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.pptx':\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n '.txt': 'text/plain',\n '.csv': 'text/csv',\n '.zip': 'application/zip',\n },\n },\n} as const;\n\nfunction target(object: string | undefined) {\n return object ? { object } : undefined;\n}\n\nfunction notDone(text: string) {\n return { isError: true, content: [{ type: 'text' as const, text }] };\n}\n\n/**\n * The tools a migration from another system needs beyond writing one\n * product: reading a product back by its reference, writing many, taking a\n * field away and back, finding a category by reference, and sending a file\n * from disk instead of an address.\n */\nexport function registerCatalogMigrationTools(\n server: McpServer,\n client: CatalogMigrationClient,\n) {\n server.registerTool(\n 'get_product_by_ref',\n {\n title: 'Read back a product you wrote',\n description:\n 'One product by the reference it was written under (`source` + ' +\n '`externalRef`), a draft too. `fields` holds every field the product ' +\n 'has a value for, public or not, as stored -- list item ids and file ' +\n 'paths included, which a write accepts back; `publicFields` is what a ' +\n 'visitor sees of them. `categories` lists every category it is filed ' +\n 'under, a concept one too, each with its `status`. Also `status` and ' +\n '`publishedAt`, its sku and shelf per variant, the shared-link texts ' +\n 'and picture, and `files` -- for every picture or file of the last ' +\n 'write whether it is `pending`, `stored` or `refused`, with a reason ' +\n 'code. Live, never cached: read this after upsert_product to see what ' +\n 'arrived.',\n inputSchema: {\n externalRef: CatalogExternalRefSchema,\n source: CatalogSourceSchema,\n ...objectInput,\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ externalRef, source, object }) => {\n try {\n const product = await client.getProductByRef(\n externalRef,\n source,\n target(object),\n );\n\n if (!product) {\n return answer(\n `No product with reference \"${externalRef}\" from \"${source}\".`,\n null,\n );\n }\n\n const refused = product.files.filter(\n (file) => file.status === 'refused',\n );\n const pending = product.files.filter(\n (file) => file.status === 'pending',\n );\n\n return answer(\n `${product.name} (${product.slug || 'no address'}), ${product.status} (${product.availability}); ` +\n `${Object.keys(product.fields).length} fields, ${product.categories.length} categories, ` +\n `${product.images.length} pictures; files: ${pending.length} pending, ` +\n `${refused.length} refused${refused.length ? ` (${refused.map((file) => `${file.ref}: ${file.reason}`).join('; ')})` : ''}.` +\n fieldsNotOnPageNote(product.fieldsNotOnPage),\n product,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'upsert_products',\n {\n title: 'Make or change many products',\n description:\n `Up to ${BULK_LIMIT} products, one after another, each exactly as ` +\n 'upsert_product takes it (`externalRef` beside its body). A refusal ' +\n 'is that product’s outcome and the others go on; a refusal for ' +\n 'speed is waited out and the product sent again; a key that is ' +\n 'refused stops the run and the rest is `skipped`. Answers per product ' +\n '`created`, `changed`, `refused` (with the fields) or `skipped`. ' +\n LIMITS,\n inputSchema: {\n products: z\n .array(\n z.object({\n externalRef: CatalogExternalRefSchema,\n product: CatalogProductUpsertSchema,\n }),\n )\n .min(1)\n .max(BULK_LIMIT),\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ products, object }) => {\n try {\n const outcomes = await client.upsertProducts(\n products.map((item) => ({\n externalRef: item.externalRef,\n input: item.product,\n })),\n target(object),\n );\n\n const count = (kind: string) =>\n outcomes.filter((outcome) => outcome.outcome === kind).length;\n // Across the run: one line, not one per product.\n const offPage = new Set(\n outcomes.flatMap((outcome) => outcome.product?.fieldsNotOnPage ?? []),\n );\n const refusals = outcomes\n .filter((outcome) => outcome.outcome === 'refused')\n .map(\n (outcome) =>\n `- ${outcome.externalRef}: ${outcome.error?.message ?? 'refused'}` +\n (outcome.error?.fields.length\n ? ` (${outcome.error.fields.map((field) => `${field.path}: ${field.message}`).join('; ')})`\n : ''),\n );\n\n return answer(\n [\n `${count('created')} made, ${count('changed')} changed, ` +\n `${count('refused')} refused, ${count('skipped')} skipped.` +\n (offPage.size\n ? ` Some products hold public fields that are not on their page (${[...offPage].join(', ')}); send them in publicFields to show them.`\n : ''),\n ...refusals,\n ].join('\\n'),\n outcomes,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'delete_field',\n {\n title: 'Take a catalogue field away',\n description:\n 'Moves a field to the trash, as the app does: for thirty days it can ' +\n 'come back with restore_field, and every product keeps its value ' +\n 'meanwhile. Use `permanent` only when the user explicitly asks to ' +\n 'delete for good, and first without `confirm`: that only counts the ' +\n 'products that would lose their value. Show the user that count; ' +\n 'send `confirm` (the slug again) only after they agree. A field of ' +\n 'the app’s own, or one a product varies along, is refused.',\n inputSchema: {\n slug: CatalogFieldSlugSchema,\n permanent: z.boolean().optional(),\n confirm: z.string().max(63).optional(),\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ slug, permanent, confirm, object }) => {\n try {\n const result = await client.removeField(slug, {\n ...target(object),\n ...(permanent ? { permanent } : {}),\n ...(confirm ? { confirm } : {}),\n });\n\n const text =\n result.state === 'trashed'\n ? `${slug} is in the trash until ${result.restorableUntil ?? 'thirty days from now'}; ${result.productsWithValue} products keep their value.`\n : result.state === 'would_delete'\n ? `Nothing changed. Deleting ${slug} for good would clear its value on ${result.productsWithValue} products. ${result.confirm}`\n : `${slug} is deleted; its value on ${result.productsWithValue} products is being cleared.`;\n\n return answer(text, result);\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'restore_field',\n {\n title: 'Bring a catalogue field back from the trash',\n description:\n 'A field moved to the trash in the last thirty days comes back under ' +\n 'its own id, with every value the products kept.',\n inputSchema: { slug: CatalogFieldSlugSchema, ...objectInput },\n annotations: { destructiveHint: false, idempotentHint: true },\n },\n async ({ slug, object }) => {\n try {\n const field = await client.restoreField(slug, target(object));\n\n return answer(`${field.label} (${field.slug}) is back.`, field);\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'get_category_by_ref',\n {\n title: 'Find a category by its reference',\n description:\n 'The category upsert_category recorded with this `source` and ' +\n '`externalRef` -- the id of a series in your own system -- also after ' +\n 'its address was changed in the app. A published category unless you ' +\n 'pass `status`: \"draft\" finds a concept or scheduled one, \"all\" any ' +\n '(needs a server key).',\n inputSchema: {\n source: CatalogSourceSchema,\n externalRef: CatalogExternalRefSchema,\n status: z\n .enum(['published', 'draft', 'all'])\n .optional()\n .describe(\n 'Which categories to look in; anything but published needs catalog:write.',\n ),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ source, externalRef, status }) => {\n try {\n const category = await client.getCategoryByRef(\n source,\n externalRef,\n status ? { status } : undefined,\n );\n\n return answer(\n category\n ? `${category.name} (${category.slug || 'no address yet'}), id ${category.id}${category.status ? `, ${category.status}` : ''}.`\n : `No ${status && status !== 'published' ? status : 'published'} category with reference \"${externalRef}\" from \"${source}\"${status ? '' : ' (a concept category needs status \"all\")'}.`,\n category,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'upload_catalog_file',\n {\n title: 'Upload a picture or a file from disk',\n description:\n 'Reads one file from the user’s disk and uploads it, for a ' +\n 'product write to name as { \"upload\": \"<uploadPath>\" } instead of a ' +\n 'url: a picture (png, jpeg, webp, at most 2 MB) for `images`, ' +\n '`socialImage` or a list item’s image, or a \"file\" (pdf, Office, ' +\n 'txt, csv, zip, at most 20 MB) for a list item’s download. An ' +\n 'upload is kept a day.',\n inputSchema: {\n kind: z.enum(['image', 'file']),\n filePath: z\n .string()\n .min(1)\n .max(1000)\n .describe('The absolute path of the file on this machine.'),\n },\n annotations: { destructiveHint: false, idempotentHint: false },\n },\n async ({ kind, filePath }) => {\n const rules = FILE_RULES[kind];\n const extension = extname(filePath).toLowerCase();\n const contentType = (rules.types as Record<string, string>)[extension];\n\n if (!contentType) {\n return notDone(\n `A ${kind} is a ${Object.keys(rules.types).join(', ')} file; this is \"${extension || 'no extension'}\".`,\n );\n }\n\n const info = await stat(filePath).catch(() => null);\n\n if (!info?.isFile()) {\n return notDone(`\"${filePath}\" is not a file on this machine.`);\n }\n\n if (info.size > rules.maxBytes) {\n return notDone(\n `The file is ${info.size} bytes; a ${kind} may be at most ${rules.maxBytes}.`,\n );\n }\n\n try {\n const result = await client.uploadCatalogFile({\n kind,\n bytes: new Uint8Array(await readFile(filePath)),\n contentType: contentType as Parameters<\n CatalogMigrationClient['uploadCatalogFile']\n >[0]['contentType'],\n });\n\n return answer(\n `Uploaded. Name it as { \"upload\": \"${result.uploadPath}\" }.`,\n result,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n}\n","import * as z from 'zod';\n\nimport { MAX_AMOUNT_CENTS } from './amount-ceiling';\n\n/**\n * What the public API accepts when a catalogue is written to it.\n *\n * ONE RULE FOR EVERY FIELD: absent is \"leave it as it is\", `null` is \"empty\n * it\". An import runs again and again, and somebody may have corrected a\n * description in the app since the last run; a body that only names what it\n * knows must never wipe what it does not. So nothing here has a default that\n * would write on its own -- the defaults that exist (a new product starts as\n * a concept, a price is sold once) apply only to a row being created.\n *\n * `.strict()` throughout. A field the API does not know is a typo or a field\n * from a later version, and in both cases the caller should hear about it\n * rather than believe it was stored.\n */\n\n/** The spelling `crm_products.slug` and `crm_product_categories.slug` insist on. */\nconst WEB_SLUG = /^[a-z0-9]+(-[a-z0-9]+)*$/;\n\n/** The spelling `crm_attributes.slug` insists on. */\nconst FIELD_SLUG = /^[a-z][a-z0-9_]*$/;\n\n/**\n * Which system the identifiers in a body belong to. Lower case, because\n * `crm_source_refs.provider` is, and two spellings of one system would be two\n * namespaces in which every product imports again as new.\n */\nexport const CatalogSourceSchema = z\n .string()\n .regex(/^[a-z0-9][a-z0-9-]{0,63}$/, {\n message:\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n 'A source is lower-case letters, digits and hyphens, at most 64 characters (for example \"coolflames-payload\").',\n });\n\nexport const CatalogExternalRefSchema = z.string().trim().min(1).max(255);\n\nexport const CatalogWebSlugSchema = z.string().min(2).max(64).regex(WEB_SLUG, {\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'An address is lower-case letters and digits joined by hyphens.',\n});\n\nexport const CatalogFieldSlugSchema = z.string().max(63).regex(FIELD_SLUG, {\n message:\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n 'A field slug starts with a letter and holds lower-case letters, digits and underscores.',\n});\n\nconst cents = z.number().int().min(0).max(MAX_AMOUNT_CENTS);\n\n/** A category a product is filed under, with its place on that page. */\nexport const CatalogCategoryPlacementSchema = z\n .object({\n slug: CatalogWebSlugSchema,\n /** Lowest first; equal places stand in id order. */\n position: z.number().int().min(-1_000_000).max(1_000_000).optional(),\n })\n .strict();\n\nconst text = (max: number) => z.string().trim().max(max).nullable().optional();\n\nexport const CatalogPriceSchema = z\n .object({\n /** The price's own id at the source, so a renamed price stays itself. */\n externalRef: CatalogExternalRefSchema.optional(),\n label: z.string().trim().min(1).max(120),\n /**\n * ISO 4217. Left out, the price is in the product's own currency. A\n * price in another currency is the fixed price for that currency: it\n * wins over a converted one and is never the default.\n */\n currency: z\n .string()\n .regex(/^[A-Z]{3}$/)\n .optional(),\n amountCents: cents,\n compareAtCents: cents.nullable().optional(),\n kind: z.enum(['one_off', 'recurring']).optional(),\n recurringInterval: z.enum(['day', 'week', 'month', 'year']).optional(),\n recurringIntervalCount: z.number().int().min(1).max(365).optional(),\n isDefault: z.boolean().optional(),\n active: z.boolean().optional(),\n })\n .strict()\n .superRefine((price, ctx) => {\n if ((price.kind ?? 'one_off') === 'recurring' && !price.recurringInterval) {\n ctx.addIssue({\n code: 'custom',\n path: ['recurringInterval'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A recurring price needs an interval.',\n });\n }\n });\n\nconst pricesSchema = z\n .array(CatalogPriceSchema)\n .min(1)\n .max(50)\n .superRefine((prices, ctx) => {\n if (prices.filter((price) => price.isDefault).length > 1) {\n ctx.addIssue({\n code: 'custom',\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'At most one price can be the default.',\n });\n }\n\n const refs = prices.flatMap((price) =>\n price.externalRef ? [price.externalRef] : [],\n );\n\n if (new Set(refs).size !== refs.length) {\n ctx.addIssue({\n code: 'custom',\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Two prices carry the same externalRef.',\n });\n }\n\n // A label is unique per currency: \"A3\" in euros and \"A3\" in dollars are\n // the same option sold in two currencies. A price without a currency is\n // in the product's own, which only the service knows, so the exact check\n // is repeated there once that currency is known.\n const labels = prices.map(\n (price) => `${price.currency ?? ''} ${price.label.toLowerCase()}`,\n );\n\n if (new Set(labels).size !== labels.length) {\n ctx.addIssue({\n code: 'custom',\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Two prices carry the same label in the same currency.',\n });\n }\n });\n\nconst HttpsUrlSchema = z\n .string()\n .max(2048)\n .url()\n .refine((url) => url.startsWith('https://'), {\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A file is fetched over https only.',\n });\n\n/** The `uploadPath` `POST /catalog-files` answered with. */\nconst UploadPathSchema = z.string().min(1).max(200);\n\n/**\n * Where a file comes from: an https address this server fetches, or an\n * upload of the caller's own (`POST /catalog-files`). Exactly one.\n */\nexport const CatalogFileRefSchema = z.union([\n z.object({ url: HttpsUrlSchema }).strict(),\n z.object({ upload: UploadPathSchema }).strict(),\n]);\n\nexport type CatalogFileRef = z.output<typeof CatalogFileRefSchema>;\n\n/** The spelling `crm_product_images.role` insists on. */\nconst IMAGE_ROLE = /^[a-z][a-z0-9_-]{0,31}$/;\n\n/**\n * A few plain facts about one picture, for a storefront to print beside it:\n * flat, values a string, number or boolean, at most 2 KiB as the database\n * counts it (`crm_product_images_details_flat`).\n */\nexport const CatalogImageDetailsSchema = z\n .record(\n z.string().min(1).max(64),\n z.union([z.string().max(500), z.number().finite(), z.boolean()]),\n )\n .refine((details) => JSON.stringify(details).length <= 1800, {\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Details are at most about 2 KB.',\n });\n\nexport const CatalogImageSchema = z\n .object({\n url: HttpsUrlSchema.optional(),\n upload: UploadPathSchema.optional(),\n alt: text(300),\n /**\n * What the picture is of, in the account's own words: `work`, `detail`,\n * `insitu`. Left out keeps the role a stored picture has; `null` clears it.\n */\n role: z\n .string()\n .regex(IMAGE_ROLE, {\n message:\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n 'A role starts with a letter and holds lower-case letters, digits, hyphens and underscores, at most 32.',\n })\n .nullable()\n .optional(),\n details: CatalogImageDetailsSchema.optional(),\n })\n .strict()\n .refine(\n (image) => (image.url === undefined) !== (image.upload === undefined),\n {\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A picture names either a url or an upload, not both.',\n },\n );\n\n/**\n * The shelf of one physical product or variant: a count, not a movement.\n *\n * Whatever `quantity` says is what the shelf holds afterwards. A sync that\n * sends it again tomorrow writes that number again, over any correction made\n * in the app meanwhile -- the same rule as every other field, so send `stock`\n * only when the source is where the count is kept. Sales move the count\n * through the checkout, never through this.\n *\n * `managed: false` stops counting; the threshold goes with it, because a\n * warning on a product nobody counts can never fire.\n */\nexport const CatalogStockSchema = z\n .object({\n managed: z.boolean(),\n /** Left out, the count stays as it is. */\n quantity: z.number().int().min(0).max(1_000_000_000).optional(),\n /** Below this a product counts as running low; `null` warns never. */\n lowThreshold: z\n .number()\n .int()\n .min(0)\n .max(1_000_000_000)\n .nullable()\n .optional(),\n /** Whether it can still be ordered at zero: `deny`, `allow`, or `allow_notify`. */\n backorders: z.enum(['deny', 'allow', 'allow_notify']).optional(),\n })\n .strict();\n\nexport const CatalogVariantAxisSchema = z\n .object({\n slug: CatalogFieldSlugSchema,\n label: z.string().trim().min(1).max(120).optional(),\n })\n .strict();\n\nexport const CatalogVariantSchema = z\n .object({\n externalRef: CatalogExternalRefSchema,\n /** The value per axis, keyed by the axis slug. */\n options: z.record(\n CatalogFieldSlugSchema,\n z.string().trim().min(1).max(120),\n ),\n sku: text(100),\n gtin: z\n .string()\n .regex(/^[0-9]{8,14}$/)\n .nullable()\n .optional(),\n prices: pricesSchema.optional(),\n /** The variant's own shelf; a physical product counts per variant. */\n stock: CatalogStockSchema.optional(),\n position: z.number().int().min(0).max(10_000).optional(),\n active: z.boolean().optional(),\n })\n .strict();\n\nexport const CATALOG_AVAILABILITIES = [\n 'draft',\n 'internal',\n 'scheduled',\n 'public',\n] as const;\n\nexport const CatalogProductUpsertSchema = z\n .object({\n source: CatalogSourceSchema,\n /** Required to create a product; optional to update one. */\n name: z.string().trim().min(1).max(500).optional(),\n slug: CatalogWebSlugSchema.nullable().optional(),\n description: text(5_000),\n /** HTML; cleaned against the same allow-list the app uses. */\n longDescription: text(100_000),\n sku: text(100),\n gtin: z\n .string()\n .regex(/^[0-9]{8,14}$/)\n .nullable()\n .optional(),\n productType: z.enum(['digital', 'physical']).optional(),\n /**\n * Where the product stands. A new product is a `draft` unless this says\n * otherwise -- an import should not put a hundred products on somebody's\n * website because a run finished.\n */\n availability: z.enum(CATALOG_AVAILABILITIES).optional(),\n /** The moment for `scheduled`; ignored for the other three. */\n publishedAt: z.string().datetime({ offset: true }).nullable().optional(),\n visibility: z.enum(['public', 'unlisted']).optional(),\n metaTitle: text(300),\n metaDescription: text(1_000),\n /**\n * What a shared link to the product shows (the og tags): its own title,\n * description and picture. Left out, a link falls back to the name, the\n * description and the first picture. `socialImage` is fetched after the\n * answer like `images`; `null` takes it away.\n */\n socialTitle: text(300),\n socialDescription: text(1_000),\n socialImage: CatalogFileRefSchema.nullable().optional(),\n /**\n * Replaces the product's categories, by slug, the first one leading. An\n * entry may be `{ slug, position }` instead, to also set where the\n * product stands on that category's page (`?sort=position`, lowest\n * first); a plain slug leaves a stored place as it is.\n */\n categories: z\n .array(z.union([CatalogWebSlugSchema, CatalogCategoryPlacementSchema]))\n .max(50)\n .optional(),\n /**\n * Make a category that does not exist yet instead of refusing. Off unless\n * asked for, so a typo cannot become a category on a customer's website.\n */\n createMissingCategories: z.boolean().optional(),\n /** Field values by field slug; `null` empties one field. */\n fields: z.record(CatalogFieldSlugSchema, z.unknown()).optional(),\n /** Which fields the product's own page shows, in that order. */\n publicFields: z.array(CatalogFieldSlugSchema).max(100).optional(),\n /**\n * Add a choice a select field does not offer yet instead of refusing.\n * Off unless asked for, for the same reason as categories.\n */\n addMissingOptions: z.boolean().optional(),\n /** Replaces the product's prices as a whole. */\n prices: pricesSchema.optional(),\n /**\n * The product's own shelf. Physical products only, and only one without\n * variants: a product with variants counts per variant.\n */\n stock: CatalogStockSchema.optional(),\n /** Replaces the product's pictures, in this order. */\n images: z.array(CatalogImageSchema).max(20).optional(),\n variantAxes: z.array(CatalogVariantAxisSchema).min(1).max(3).optional(),\n /** Replaces the product's variants; one missing here is archived. */\n variants: z.array(CatalogVariantSchema).max(100).optional(),\n /** `false` brings back a product the API archived earlier. */\n archived: z.literal(false).optional(),\n })\n .strict()\n .superRefine((body, ctx) => {\n if (body.variants !== undefined && body.variantAxes === undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['variantAxes'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Variants need the axes they differ along.',\n });\n }\n\n if (body.variants && body.variantAxes) {\n const axes = body.variantAxes.map((axis) => axis.slug);\n const seen = new Set<string>();\n const refs = new Set<string>();\n\n body.variants.forEach((variant, index) => {\n const named = Object.keys(variant.options);\n\n if (\n named.length !== axes.length ||\n !axes.every((axis) => named.includes(axis))\n ) {\n ctx.addIssue({\n code: 'custom',\n path: ['variants', index, 'options'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: `A variant names a value for exactly these axes: ${axes.join(', ')}.`,\n });\n }\n\n const combination = axes\n .map((axis) => variant.options[axis] ?? '')\n .join('\\u0000');\n\n if (seen.has(combination)) {\n ctx.addIssue({\n code: 'custom',\n path: ['variants', index, 'options'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Two variants stand for the same combination.',\n });\n }\n\n if (refs.has(variant.externalRef)) {\n ctx.addIssue({\n code: 'custom',\n path: ['variants', index, 'externalRef'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'Two variants carry the same externalRef.',\n });\n }\n\n seen.add(combination);\n refs.add(variant.externalRef);\n });\n }\n\n if (body.variantAxes) {\n const slugs = body.variantAxes.map((axis) => axis.slug);\n\n if (new Set(slugs).size !== slugs.length) {\n ctx.addIssue({\n code: 'custom',\n path: ['variantAxes'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'An axis is named twice.',\n });\n }\n }\n\n if (body.stock !== undefined && body.variantAxes !== undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['stock'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A product with variants counts its stock per variant.',\n });\n }\n\n if (body.availability === 'scheduled' && !body.publishedAt) {\n ctx.addIssue({\n code: 'custom',\n path: ['publishedAt'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A scheduled product needs the moment it appears.',\n });\n }\n });\n\nexport type CatalogProductUpsert = z.output<typeof CatalogProductUpsertSchema>;\nexport type CatalogPrice = z.output<typeof CatalogPriceSchema>;\nexport type CatalogVariant = z.output<typeof CatalogVariantSchema>;\nexport type CatalogImage = z.output<typeof CatalogImageSchema>;\nexport type CatalogStock = z.output<typeof CatalogStockSchema>;\n\n/** `DELETE` carries only the source the reference belongs to. */\nexport const CatalogProductArchiveSchema = z\n .object({ source: CatalogSourceSchema })\n .strict();\n\nexport const CatalogCategoryUpsertSchema = z\n .object({\n /**\n * The category's id in the system it came from -- a series in a site's\n * own CMS -- with `source` naming whose id it is. With both, a category\n * that carries this reference is changed, and renamed to the address in\n * the path when somebody gave it another one in the app; without, the\n * address alone finds it, as before.\n */\n source: CatalogSourceSchema.optional(),\n externalRef: CatalogExternalRefSchema.optional(),\n name: z.string().trim().min(1).max(200).optional(),\n description: text(5_000),\n /** `null` puts the category at the top of the tree. */\n parentSlug: CatalogWebSlugSchema.nullable().optional(),\n availability: z.enum(CATALOG_AVAILABILITIES).optional(),\n publishedAt: z.string().datetime({ offset: true }).nullable().optional(),\n metaTitle: text(300),\n metaDescription: text(1_000),\n })\n .strict()\n .refine(\n (body) => (body.source === undefined) === (body.externalRef === undefined),\n {\n path: ['externalRef'],\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n message: 'A category reference names both source and externalRef.',\n },\n );\n\nexport type CatalogCategoryUpsert = z.output<\n typeof CatalogCategoryUpsertSchema\n>;\n\n/**\n * The spelling `crm_objects.slug` insists on: what `?object=` names on every\n * catalogue route. Underscores, not hyphens -- an object slug is also the\n * first segment of a merge token.\n */\nexport const CatalogObjectSlugSchema = z.string().max(64).regex(FIELD_SLUG, {\n message:\n // i18n-exempt: a public API's field error, returned verbatim to an integration's own code and never shown in this app\n 'A catalogue slug starts with a letter and holds lower-case letters, digits and underscores.',\n});\n\n/**\n * A product catalogue of its own -- \"Fireplaces\" beside \"Products\". Both\n * names are what the app's own dialog asks: one product, many products. A new\n * catalogue needs both; a rename names only what changes.\n */\nexport const CatalogCatalogueUpsertSchema = z\n .object({\n nameSingular: z.string().trim().min(1).max(120).optional(),\n namePlural: z.string().trim().min(1).max(120).optional(),\n /** A lucide icon name, as the object settings offer them. */\n icon: z.string().trim().max(64).nullable().optional(),\n })\n .strict();\n\nexport type CatalogCatalogueUpsert = z.output<\n typeof CatalogCatalogueUpsertSchema\n>;\n\n/**\n * The field types a catalogue may make through the API: the ones the object\n * builder offers a product object (`PRODUCT_ATTRIBUTE_TYPES` in\n * `@kit/crm-objects`: select, text, number, checkbox, colour -- `#rrggbb`,\n * stored lower case -- and the content blocks),\n * never more, plus `url` and `date`, which the API offered before that list\n * existed and a sync may still be making.\n *\n * The three content blocks take their settings in `config`: a text block has\n * none, a call to action may offer a second button, and a repeater names how\n * it is drawn and what one entry holds. A configurator is made the same way\n * but asks for its own right (`configurator:shape`). A grid of checkboxes is\n * never made here: it answers a person on a record page a product does not\n * have.\n */\nexport const CatalogFieldTypeSchema = z.enum([\n 'text',\n 'number',\n 'select',\n 'checkbox',\n 'color',\n 'url',\n 'date',\n 'content_text',\n 'repeater',\n 'cta',\n 'configurator',\n]);\n\nexport const CATALOG_FIELD_TYPES = CatalogFieldTypeSchema.options;\n\n/** The field types whose settings live in `config`. */\nexport const CATALOG_CONFIG_FIELD_TYPES = [\n 'number',\n 'content_text',\n 'repeater',\n 'cta',\n 'configurator',\n] as const satisfies readonly (typeof CATALOG_FIELD_TYPES)[number][];\n\nexport const CatalogFieldOptionSchema = z\n .object({\n value: z.string().trim().min(1).max(120),\n label: z.string().trim().min(1).max(120).optional(),\n })\n .strict();\n\nexport const CatalogAttributeUpsertSchema = z\n .object({\n label: z.string().trim().min(1).max(120).optional(),\n type: CatalogFieldTypeSchema.optional(),\n /** For a select: whether a product may carry more than one choice. */\n multiple: z.boolean().optional(),\n /** Choices to make sure exist; never removes one that is not listed. */\n options: z.array(CatalogFieldOptionSchema).max(500).optional(),\n /** Choices to take off the list, by value. */\n removeOptions: z\n .array(z.string().trim().min(1).max(120))\n .max(500)\n .optional(),\n /** Whether the public API may show this field at all. */\n public: z.boolean().optional(),\n /**\n * The field's settings, replaced as a whole. Checked against the same\n * schema the object builder saves with, per type: a repeater's `display`,\n * `preset`, `itemFields` and `titleKey`; a call to action's `secondary`;\n * a configurator's whole structure. An item field keeps its type once it\n * exists.\n */\n config: z.record(z.string(), z.unknown()).optional(),\n /**\n * Say what this body would change -- a new field, a new label, choices\n * added, choices taken away and how many products hold each -- and\n * write nothing.\n */\n dryRun: z.boolean().optional(),\n /**\n * Bring the field back from the trash under its own id, with every value\n * the products kept. Nothing else may be sent with it.\n */\n restore: z.literal(true).optional(),\n /**\n * For a new content field (`content_text`, `repeater`, `cta`): put its\n * block at the end of the product page. On by default; `false` makes the\n * field only. Never moves or restores a block of an existing field.\n */\n placeOnPage: z.boolean().optional(),\n })\n .strict();\n\nexport type CatalogAttributeUpsert = z.output<\n typeof CatalogAttributeUpsertSchema\n>;\n\n/**\n * A file for a configurator, uploaded in two steps: `start` asks for a signed\n * upload address, the caller PUTs the bytes there, and `finish` has the\n * server read them back and check them. A model (`.glb`) is shape and asks\n * for `configurator:shape`; a texture (`.webp` or `.jpg`) is a material, which\n * `catalog:write` may change, as in the app.\n */\nexport const CONFIGURATOR_FILE_KINDS = ['model', 'texture'] as const;\n\nexport const CONFIGURATOR_TEXTURE_CONTENT_TYPES = [\n 'image/webp',\n 'image/jpeg',\n] as const;\n\nexport const ConfiguratorFileUploadSchema = z.discriminatedUnion('step', [\n z\n .object({\n step: z.literal('start'),\n /** The slug of the configurator field the file is for. */\n field: CatalogFieldSlugSchema,\n kind: z.enum(CONFIGURATOR_FILE_KINDS),\n /** The bytes the caller is about to send, so a too-large file is refused before it travels. */\n size: z.number().int().min(1),\n /** For a texture: what kind of picture it is. A model is always glTF binary. */\n contentType: z.enum(CONFIGURATOR_TEXTURE_CONTENT_TYPES).optional(),\n })\n .strict(),\n z\n .object({\n step: z.literal('finish'),\n field: CatalogFieldSlugSchema,\n kind: z.enum(CONFIGURATOR_FILE_KINDS),\n /** The `uploadPath` `start` answered with. */\n uploadPath: z.string().max(200),\n })\n .strict(),\n]);\n\nexport type ConfiguratorFileUpload = z.output<\n typeof ConfiguratorFileUploadSchema\n>;\n\n/**\n * A picture or a download from an integration's own disk, in two steps:\n * `start` names what is coming and is answered with a signed upload address,\n * the caller PUTs the bytes there with that content type, and `finish` has\n * the server read them back and check them. The `uploadPath` it answers with\n * is what a product write names instead of a `url` (`{ \"upload\": ... }`).\n */\nexport const CATALOG_FILE_KINDS = ['image', 'file'] as const;\n\nexport const CATALOG_FILE_CONTENT_TYPES = [\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n 'application/pdf',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n 'text/plain',\n 'text/csv',\n 'application/zip',\n] as const;\n\nexport const CatalogFileUploadSchema = z.discriminatedUnion('step', [\n z\n .object({\n step: z.literal('start'),\n /** A picture (2 MB, png/jpeg/webp) or a download for a list item (20 MB). */\n kind: z.enum(CATALOG_FILE_KINDS),\n /** The bytes about to be sent, so a too-large file is refused before it travels. */\n size: z.number().int().min(1),\n contentType: z.enum(CATALOG_FILE_CONTENT_TYPES),\n })\n .strict(),\n z\n .object({\n step: z.literal('finish'),\n kind: z.enum(CATALOG_FILE_KINDS),\n /** The `uploadPath` `start` answered with. */\n uploadPath: z.string().max(200),\n })\n .strict(),\n]);\n\nexport type CatalogFileUpload = z.output<typeof CatalogFileUploadSchema>;\n","/**\n * Stripe refuses a `unit_amount` above 99,999,999 in the currency's smallest\n * unit. Below that ceiling the number is only as big as the tenant meant it to\n * be; above it, the save succeeds and the first checkout fails — which is the\n * worst possible place to learn about a typo.\n *\n * A file of its own, reachable without the schema barrel, because the ceiling\n * has to hold on every path that can write a price. The barrel pulls in the\n * proposal blocks and their editor dependencies, so a caller that only needs\n * one number would otherwise drag an email editor along with it — and the\n * cheapest way out of that is a second copy of the number, which is exactly\n * how a ceiling stops being one.\n */\nexport const MAX_AMOUNT_CENTS = 99_999_999;\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type {\n CatalogField,\n CategoryListItem,\n CrmClient,\n ProductListItem,\n} from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport { CatalogObjectSlugSchema } from '@kit/crm-invoicing/schema/catalog-api';\n\nimport { answer, money, more, page, refusal } from './tool-answers';\n\n/**\n * Which catalogue a catalogue tool is about. Most accounts have one; one that\n * keeps fireplaces apart from its other products has two, and list_catalogs\n * names them.\n */\nexport const objectInput = {\n object: CatalogObjectSlugSchema.optional().describe(\n 'The slug of the catalogue (see list_catalogs). Leave out for the account\\u2019s own catalogue.',\n ),\n};\n\n/** The part of the client these tools call; a test hands in a fake of exactly this. */\nexport type CatalogClient = Pick<\n CrmClient,\n | 'getFields'\n | 'getCatalogs'\n | 'getCurrencies'\n | 'getProduct'\n | 'getProducts'\n | 'getCategories'\n | 'getCategory'\n | 'getAvailability'\n>;\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 'A new block field stands at the end of the product page from the moment ' +\n 'upsert_field makes it (`placeOnPage: false` skips that).';\n\nconst currency = z\n .string()\n .regex(/^[A-Z]{3}$/)\n .optional()\n .describe(\n 'Every price in this currency (see list_currencies), each with its origin: base, fixed or converted. Left out, every price in its own currency.',\n );\n\nconst READ_STATUS_HINT =\n 'Left out, only published ones. \"draft\" is what is not on the website yet ' +\n '(concept, internal or scheduled), \"archived\" what was taken out, \"all\" ' +\n 'everything; each answers with a `status` and `publishedAt` per item. ' +\n 'Anything but published needs a key with catalog:write.';\n\nconst productStatus = z\n .enum(['published', 'draft', 'archived', 'all'])\n .optional()\n .describe(`Which products. ${READ_STATUS_HINT}`);\n\nconst categoryStatus = z\n .enum(['published', 'draft', 'all'])\n .optional()\n .describe(\n `Which categories. ${READ_STATUS_HINT.replace('\"archived\" what was taken out, ', '')} A category cannot be archived.`,\n );\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: { ...objectInput },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ object }) => {\n try {\n const fields = await client.getFields(object ? { object } : undefined);\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, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'list_catalogs',\n {\n title: 'List the product catalogues',\n description:\n 'The product catalogues this key can write to, the account\\u2019s own ' +\n 'first. A catalogue other than the account\\u2019s own is named by its ' +\n 'slug in `object` on every catalogue tool; upsert_catalog makes one.',\n inputSchema: {},\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n try {\n const catalogs = await client.getCatalogs();\n\n return answer(\n catalogs\n .map(\n (catalog) =>\n `${catalog.name} (${catalog.slug})${catalog.isDefault ? ', the account\\u2019s own' : ''}`,\n )\n .join('\\n') || 'This account has no product catalogue.',\n catalogs,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'list_currencies',\n {\n title: 'List the currencies the shop sells in',\n description:\n 'The account\\u2019s own currency first, then the ones it switched on, ' +\n 'with a name and symbol: what a currency menu on a website is built ' +\n 'from. Pass one as `currency` to get_product or list_products for ' +\n 'prices in it. Which currencies are offered, and at what rate, is set ' +\n 'in the app (Settings \\u2192 Verkoop \\u2192 Valuta), not through this tool.',\n inputSchema: {\n locale: z\n .string()\n .min(2)\n .max(35)\n .optional()\n .describe('The language names are written in, such as \"nl\".'),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ locale }) => {\n try {\n const currencies = await client.getCurrencies(\n locale ? { locale } : undefined,\n );\n\n return answer(\n currencies\n .map(\n (option) =>\n `${option.code} (${option.symbol}) ${option.name}${option.isDefault ? ', the account\\u2019s own' : ''}`,\n )\n .join('\\n'),\n currencies,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\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. Published ' +\n 'products only unless you pass `status` (needs a server key), and ' +\n 'a published one may be a minute old: to read back exactly what you ' +\n 'just wrote, use get_product_by_ref. Answers that it is not found ' +\n 'for an unknown product, or one not in the status asked. ' +\n CONTENT_BLOCKS,\n inputSchema: { slugOrId, currency, status: productStatus },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ slugOrId: wanted, currency: inCurrency, status }) => {\n try {\n const product = await client.getProduct(\n wanted,\n inCurrency || status\n ? {\n ...(inCurrency ? { currency: inCurrency } : {}),\n ...(status ? { status } : {}),\n }\n : undefined,\n );\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, 'catalog:read');\n }\n },\n );\n server.registerTool(\n 'list_products',\n {\n title: 'List the products',\n description:\n 'One page of the catalogue: name, address (slug), lowest price, ' +\n 'whether it is in stock, its first picture and the public fields a ' +\n 'listing shows. Narrow it to one category\\u2019s whole branch by its ' +\n 'id or address, or by the reference upsert_category recorded ' +\n '(`categorySource` + `categoryRef`), to one catalogue by the slug of ' +\n 'its object, or to the product carrying an article number (`sku`). ' +\n 'Read get_product for everything a product page needs. ' +\n 'Published products only, and may be a minute old; `status` lists ' +\n 'concepts, scheduled and archived ones too (needs a server key). ' +\n '`sort` orders the published ones: -publishedAt (newest first, the ' +\n 'default), publishedAt, name, or position -- the seller\\u2019s own ' +\n 'order on the page of `category`, which it needs. Each item carries ' +\n '`sku`, `publishedAt` and `stockQuantity` (null when not counted).',\n inputSchema: {\n ...page,\n category: z\n .string()\n .regex(\n /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[a-z0-9]+(-[a-z0-9]+)*)$/i,\n )\n .max(64)\n .optional()\n .describe(\n 'A category id or address (slug); includes its subcategories.',\n ),\n categorySource: z\n .string()\n .max(64)\n .optional()\n .describe('With categoryRef: whose id the category reference is.'),\n categoryRef: z\n .string()\n .max(255)\n .optional()\n .describe('A category by the id it has in your own system.'),\n sku: z\n .string()\n .min(1)\n .max(100)\n .optional()\n .describe(\n 'The product carrying this article number, or whose variant does.',\n ),\n object: z\n .string()\n .min(1)\n .max(63)\n .optional()\n .describe(\n 'One catalogue, by the slug of its object, such as \"product\".',\n ),\n currency,\n status: productStatus,\n sort: z\n .enum(['-publishedAt', 'publishedAt', 'name', 'position'])\n .optional()\n .describe(\n 'The order: newest first by default; position needs `category`.',\n ),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ categorySource, categoryRef, ...input }) => {\n try {\n const list = await client.getProducts({\n ...input,\n ...(categorySource && categoryRef\n ? {\n categoryRef: {\n source: categorySource,\n externalRef: categoryRef,\n },\n }\n : {}),\n });\n\n return answer(\n list.data.length === 0\n ? `No ${input.status && input.status !== 'published' ? input.status : 'published'} products match.`\n : [...list.data.map(describeProduct), more(list.nextCursor)]\n .filter(Boolean)\n .join('\\n'),\n list,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'list_categories',\n {\n title: 'List the categories',\n description:\n 'The categories of the catalogue, each with its path from the top ' +\n 'and how many products are in it. Left without `parent`, every ' +\n 'category comes back, which is what building a menu in one go ' +\n 'needs; \"root\" asks for the top level, a category id for its ' +\n 'children. Published categories only unless you pass `status` ' +\n '(draft or all, needs a server key).',\n inputSchema: {\n ...page,\n parent: z\n .union([z.literal('root'), z.string().uuid()])\n .optional()\n .describe('\"root\" or the id of a category.'),\n status: categoryStatus,\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async (input) => {\n try {\n const list = await client.getCategories(input);\n\n return answer(\n list.data.length === 0\n ? `No ${input.status && input.status !== 'published' ? input.status : 'published'} categories.`\n : [...list.data.map(describeCategory), more(list.nextCursor)]\n .filter(Boolean)\n .join('\\n'),\n list,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'get_category',\n {\n title: 'Read one category page',\n description:\n 'One category as its page needs it: name, description, the texts ' +\n 'for search engines and social media, and the first page of its ' +\n 'products. Pass the nextCursor back to page through the products. ' +\n 'A category that is not on the website yet needs `status` ' +\n '(draft or all, a server key); its products are the published ones.',\n inputSchema: {\n slugOrId: z\n .string()\n .min(1)\n .max(200)\n .describe('The address (slug) or the id of the category.'),\n ...page,\n status: categoryStatus,\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ slugOrId: wanted, ...listOptions }) => {\n try {\n const category = await client.getCategory(wanted, listOptions);\n\n if (!category) {\n return answer(`No category \"${wanted}\" on this account.`, null);\n }\n\n const { data, nextCursor } = category;\n\n return answer(\n [\n `${data.name} (${data.slug}), ${data.productCount} products.`,\n ...data.products.map(describeProduct),\n more(nextCursor),\n ]\n .filter(Boolean)\n .join('\\n'),\n category,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n\n server.registerTool(\n 'get_product_availability',\n {\n title: 'Whether a product can be bought now',\n description:\n 'Live, not cached: whether a visitor can buy the product right now. ' +\n 'Never how many are left. For a stock badge that must be current; ' +\n 'the inStock of a listing may be a minute old.',\n inputSchema: { slugOrId },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ slugOrId: wanted }) => {\n try {\n const status = await client.getAvailability(wanted);\n\n if (!status) {\n return answer(`No product \"${wanted}\" on this account.`, null);\n }\n\n return answer(\n `${status.slug}: ${status.inStock ? 'in stock' : 'out of stock'}.`,\n status,\n );\n } catch (error) {\n return refusal(error, 'catalog:read');\n }\n },\n );\n}\n\nfunction describeProduct(product: ProductListItem) {\n const stock = `${product.inStock ? '' : ', out of stock'}${\n product.status ? `, ${product.status}` : ''\n }`;\n\n const from =\n product.priceFromCents === null\n ? `no price in ${product.currency}`\n : `from ${money(product.priceFromCents, product.currency)}`;\n\n return `- ${product.name} (${product.slug}): ${from}${stock}`;\n}\n\nfunction describeCategory(category: CategoryListItem) {\n const path = category.path.map((step) => step.name).join(' / ');\n\n const state = category.status ? `, ${category.status}` : '';\n\n return `- ${path || category.name} (${category.slug || 'no address yet'}, id ${category.id}${state}): ${category.productCount} products`;\n}\n\nfunction describeField(field: CatalogField) {\n if (field.type !== 'repeater') {\n return `- ${field.slug}: ${field.label}, ${field.type}${settingsOf(field)}`;\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\n/**\n * The settings a field of any other type shows, as `key value` after the\n * type: the unit beside a number, how an amount is written, how a choice is\n * drawn. The JSON beside the text has them whole.\n */\nconst SETTING_KEYS = [\n 'prefix',\n 'suffix',\n 'currency',\n 'symbolPosition',\n 'thousandsSeparator',\n 'decimalSeparator',\n 'decimals',\n 'display',\n 'layout',\n 'mode',\n 'secondary',\n] as const;\n\nfunction settingsOf(field: CatalogField) {\n const given = SETTING_KEYS.flatMap((key) =>\n field[key] === undefined ? [] : [`${key} ${JSON.stringify(field[key])}`],\n );\n\n if (field.rows) {\n given.push(`${field.rows.length} rows`);\n }\n\n if (field.columns) {\n given.push(`${field.columns.length} columns`);\n }\n\n return given.length > 0 ? ` (${given.join(', ')})` : '';\n}\n","import { CrmApiError } from '@solumflow-app/crm-client';\nimport { z } from 'zod';\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/** The scopes the tools of this server ask for; the API's names, verbatim. */\nexport type ToolScope =\n | 'website:manage'\n | 'catalog:read'\n | 'catalog:write'\n | 'events:read'\n | 'configurator:shape'\n | 'shop:manage'\n | 'cart:write'\n | 'orders:write'\n | 'orders:read';\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. `scope` is the one the\n * tool's route asks for, because the API deliberately does not say which\n * scope was missing.\n */\nexport function refusal(error: unknown, scope: ToolScope) {\n if (!(error instanceof CrmApiError)) {\n throw error;\n }\n\n const lines = [`${error.code}: ${error.message}`];\n const next =\n error.code === 'forbidden'\n ? `The key may not do this. It needs to be a secret key with the scope \"${scope}\".`\n : 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 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:\n 'Too many requests: 120 a minute per key, 600 per account, and 5 MB of bodies a day per key. Wait the seconds the answer names 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/** The input every listing takes: the cursor of the page before, and a size. */\nexport const 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\n/** The line that tells an agent there is a next page, or nothing. */\nexport function more(nextCursor: string | null) {\n return nextCursor ? `More on the next page: cursor ${nextCursor}` : '';\n}\n\n/** An amount as a person reads it; the JSON beside it keeps the cents. */\nexport function money(cents: number, currency: string) {\n return `${(cents / 100).toFixed(2)} ${currency.toUpperCase()}`;\n}\n\n/**\n * A sentence about public fields that hold a value but stay off a product's\n * page, or nothing. Said out loud because the silent version -- an import\n * that never sent `publicFields` -- leaves every page with no fields and no\n * refusal anywhere.\n */\nexport function fieldsNotOnPageNote(slugs: readonly string[] | undefined) {\n if (!slugs?.length) {\n return '';\n }\n\n const count =\n slugs.length === 1\n ? '1 public field has'\n : `${slugs.length} public fields have`;\n\n return (\n ` ${count} a value but ${slugs.length === 1 ? 'is' : 'are'} not on this product's page: ` +\n `${slugs.join(', ')}; send them in publicFields to show them.`\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { CrmApiError, type CrmClient } from '@solumflow-app/crm-client';\n\nimport {\n CatalogAttributeUpsertSchema,\n CatalogCatalogueUpsertSchema,\n CatalogCategoryUpsertSchema,\n CatalogExternalRefSchema,\n CatalogFieldSlugSchema,\n CatalogObjectSlugSchema,\n CatalogProductArchiveSchema,\n CatalogProductUpsertSchema,\n CatalogWebSlugSchema,\n} from '@kit/crm-invoicing/schema/catalog-api';\n\nimport { objectInput } from './catalog-tools';\nimport { answer, fieldsNotOnPageNote, 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 CatalogWriteClient = Pick<\n CrmClient,\n | 'upsertProduct'\n | 'archiveProduct'\n | 'deleteProduct'\n | 'upsertCategory'\n | 'upsertField'\n | 'previewField'\n | 'upsertCatalog'\n>;\n\n/**\n * Where a refused delete says the product is used, in words: the API's\n * `details.blockers` (`{ kind, count }` pairs), or null for an answer that\n * carries none.\n */\nfunction usedIn(details: unknown) {\n const blockers = (details as { blockers?: unknown } | null)?.blockers;\n\n if (!Array.isArray(blockers) || blockers.length === 0) {\n return null;\n }\n\n return blockers\n .map(\n (entry: { kind?: unknown; count?: unknown }) =>\n `${String(entry.kind).replaceAll('_', ' ')} (${String(entry.count)})`,\n )\n .join(', ');\n}\n\n/** `object` for the client: only when the agent named another catalogue. */\nfunction target(object: string | undefined) {\n return object ? { object } : undefined;\n}\n\n/**\n * What each kind of field takes in `config`, said once: an agent cannot see\n * the app's builder, and a repeater without its item fields is refused.\n */\nconst FIELD_KINDS =\n 'Kinds: text, number, select, checkbox, color (a value \"#rrggbb\"; ' +\n 'upper case is stored lower case), url, date, and three content ' +\n 'blocks for a product page: \"content_text\" (formatted text, no config), ' +\n '\"cta\" (a call to action; config { secondary: true } offers a second ' +\n 'button) and \"repeater\" (a list, an accordion such as a FAQ, downloads or ' +\n 'testimonials; config { display: \"list\" | \"accordion\", preset?: \"list\" | ' +\n '\"faq\" | \"downloads\" | \"testimonials\", itemFields: [{ key, label, type: ' +\n 'text | richtext | url | icon | image | file | number | select }], ' +\n 'titleKey?: the item field that names an item }). A number item may name ' +\n '{ min?, max?, suffix? }, a select item names { options: [{ value, label }] } ' +\n '(at most 100); its value is one of those values. An image or file item ' +\n 'is filled with { url } or { upload } in upsert_product, fetched after ' +\n 'the answer. A number may take config { prefix?, suffix? }. ' +\n 'A configurator is made with upsert_configurator. The kind is fixed once ' +\n 'the field exists, and so is the type of a repeater\\u2019s item field.';\n\n/**\n * What an agent has to understand before it writes anything, said once and\n * repeated in each product tool: the reference is the identity, and a write\n * changes a customer's live catalogue.\n */\nconst IDENTITY =\n 'A product is known by `externalRef`, its id in the system it comes from, ' +\n 'together with `source`, the name of that system (for example ' +\n '\"acme-webshop\"). The same pair always means the same product: sending it ' +\n 'again changes it instead of making a second one, so use references that ' +\n 'stay the same, never a name that may change.';\n\n/**\n * Writing the catalogue through the API's own routes. The input schemas are\n * the routes' own, bundled in, so an agent is told the same limits the API\n * enforces; what a schema cannot say on one field alone (variants against\n * their axes, a scheduled product without a moment) the API answers with the\n * field it is about.\n */\nexport function registerCatalogWriteTools(\n server: McpServer,\n client: CatalogWriteClient,\n) {\n server.registerTool(\n 'upsert_product',\n {\n title: 'Make or change a product',\n description:\n IDENTITY +\n ' Every field but `source` may be left out, and left out means ' +\n '\"leave it as it is\"; null empties a field. A new product needs a ' +\n '`name` and is a draft unless `availability` says otherwise: do not ' +\n 'publish without the user asking. `prices`, `categories`, `images` ' +\n 'and `variants` each replace what the product had as a whole, and a ' +\n 'variant left out is archived. Categories are named by slug and a ' +\n 'missing one is refused unless `createMissingCategories` is true; a ' +\n 'choice a select field does not offer is refused unless ' +\n '`addMissingOptions` is true. Pictures are https URLs, fetched after ' +\n 'the answer (`images: \"pending\"`). Amounts are in cents. A price ' +\n \"without `currency` is in the product's own; one with another \" +\n '`currency` is the fixed price for that currency, may share its ' +\n 'label with the euro price, and can never be the default. ' +\n '`stock` (physical only; per variant when there are variants) is a ' +\n 'count, not a movement: a second run writes it again over a ' +\n 'correction made in the app, so send it only when the source keeps ' +\n 'the count. A picture is { url } or { upload } (upload_catalog_file), ' +\n 'with `alt`, a `role` in your own words (\"insitu\") and flat ' +\n '`details` ({ widthCm: 120 }); `socialImage` is the shared-link ' +\n 'picture beside `socialTitle` and `socialDescription`. Files arrive ' +\n 'after the answer: get_product_by_ref says per file whether it came. ' +\n '`description` is plain text (5,000 characters); `longDescription` ' +\n 'is HTML, cleaned to headings, paragraphs, lists, links, bold and ' +\n 'italic. A repeater is sent whole: `items` replaces the list. An item ' +\n 'may leave out its `id` -- SolumFlow makes one, and `fields` in the ' +\n 'answer holds it; send the id back to change that item, or the items ' +\n 'are replaced by new ones and their files fetched again. An image or ' +\n 'file item key is { url: \"https://...\" } (fetched after the answer) ' +\n 'or { upload }; a wrong one is refused with the path of the item. ' +\n 'To write many products use upsert_products. ' +\n 'Read list_catalog_fields first for the slugs `fields` takes.',\n inputSchema: {\n externalRef: CatalogExternalRefSchema.describe(\n 'The id of the product in the system named by `source`.',\n ),\n ...CatalogProductUpsertSchema.shape,\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ externalRef, object, ...input }) => {\n try {\n const product = await client.upsertProduct(\n externalRef,\n input,\n target(object),\n );\n const verb = product.created ? 'Made' : 'Changed';\n\n return answer(\n `${verb} ${product.name} (${product.slug ?? 'no address'}), ${product.availability}; ` +\n `${product.variants.length} variants; pictures ${product.images}.` +\n fieldsNotOnPageNote(product.fieldsNotOnPage),\n product,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'archive_product',\n {\n title: 'Take a product out of the catalogue',\n description:\n IDENTITY +\n ' Archiving takes the product and its variants off the website and ' +\n 'out of the shop at once. It stays in the database for every invoice ' +\n 'that names it, and upsert_product with `archived: false` brings it ' +\n 'back. Ask the user before archiving anything they did not name.',\n inputSchema: {\n externalRef: CatalogExternalRefSchema.describe(\n 'The id of the product in the system named by `source`.',\n ),\n ...CatalogProductArchiveSchema.shape,\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ externalRef, source, object }) => {\n try {\n const result = await client.archiveProduct(\n externalRef,\n source,\n target(object),\n );\n\n return answer(\n result.alreadyArchived\n ? 'That product was already archived.'\n : 'Archived.',\n result,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'delete_product',\n {\n title: 'Delete a product for good',\n description:\n IDENTITY +\n ' Deleting removes the product, its variants, prices and pictures ' +\n 'for good; it cannot be undone. It only works for a product that ' +\n 'nothing uses: one that is on an order, a payment page, a ' +\n 'subscription, a booking or a hosting service is refused, and the ' +\n 'answer says where. Then archive_product is the way out. Archive ' +\n 'when in doubt, and ask the user before deleting anything they did ' +\n 'not name.',\n inputSchema: {\n externalRef: CatalogExternalRefSchema.describe(\n 'The id of the product in the system named by `source`.',\n ),\n ...CatalogProductArchiveSchema.shape,\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ externalRef, source, object }) => {\n try {\n const result = await client.deleteProduct(\n externalRef,\n source,\n target(object),\n );\n\n return answer('Deleted for good.', result);\n } catch (error) {\n if (error instanceof CrmApiError && error.code === 'conflict') {\n const places = usedIn(error.details);\n\n if (places) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text:\n `conflict: ${error.message}\\n` +\n `Used in: ${places}.\\n` +\n 'Archive it with archive_product instead.',\n },\n ],\n };\n }\n }\n\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'upsert_category',\n {\n title: 'Make or change a category',\n description:\n 'A category, known by its address (slug): sending the same slug ' +\n 'again changes it. Left-out fields stay as they are; a new category ' +\n 'needs a `name`. `parentSlug` places it under another category, null ' +\n 'at the top. A new category is a draft unless `availability` says ' +\n 'otherwise. Categories belong to one catalogue: a product names only ' +\n 'categories of its own catalogue. Send `source` and `externalRef` -- ' +\n 'the id of the series in your own system -- and the category keeps ' +\n 'that identity: it is found by it even after its address was changed ' +\n 'in the app (and gets the address of this call back), and ' +\n 'list_products narrows by it. Content of a series (long texts, ' +\n 'galleries) stays in your own CMS.',\n inputSchema: {\n slug: CatalogWebSlugSchema.describe(\n 'The address of the category: lower case, digits and dashes.',\n ),\n ...CatalogCategoryUpsertSchema.shape,\n ...objectInput,\n },\n annotations: { destructiveHint: false, idempotentHint: true },\n },\n async ({ slug, object, ...input }) => {\n try {\n const category = await client.upsertCategory(\n slug,\n input,\n target(object),\n );\n\n return answer(\n `${category.created ? 'Made' : 'Changed'} ${category.name} (${category.slug}).`,\n category,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'upsert_field',\n {\n title: 'Make or change a catalogue field',\n description:\n 'A custom field on products, known by its slug: sending the same ' +\n 'slug again changes it. A new field needs a `label`. ' +\n FIELD_KINDS +\n ' `config` replaces the settings as a whole. `options` makes sure ' +\n 'choices exist and never removes one; `removeOptions` takes choices ' +\n 'off the list, which empties them on every product that had them. ' +\n '`public` decides whether the website may show the field at all. ' +\n 'A new content field (content_text, repeater, cta) gets its block at ' +\n 'the end of the product page in the same save, and the answer says ' +\n '`placedOnPage`; `placeOnPage: false` makes the field only. An ' +\n 'existing field is never placed again, so a block somebody took off ' +\n 'the page stays off. ' +\n '`dryRun: true` answers what would change -- and how many products ' +\n 'hold each choice that would go -- and writes nothing. A slug in the ' +\n 'trash is taken until restore_field or a permanent delete_field.',\n inputSchema: {\n slug: CatalogFieldSlugSchema.describe(\n 'The slug of the field: a lower-case letter, then lower case, digits and underscores.',\n ),\n ...CatalogAttributeUpsertSchema.shape,\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ slug, object, ...input }) => {\n try {\n if (input.dryRun) {\n const { dryRun: _dryRun, ...rest } = input;\n const preview = await client.previewField(slug, rest, target(object));\n\n return answer(`Nothing written. ${JSON.stringify(preview)}`, preview);\n }\n\n const field = await client.upsertField(slug, input, target(object));\n\n return answer(\n `${field.created ? 'Made' : 'Changed'} ${field.label} (${field.slug}), ${field.type}, ` +\n `${field.public ? 'public' : 'not public'}.` +\n (field.placedOnPage === undefined\n ? ''\n : field.placedOnPage\n ? ' Its block is at the end of the product page.'\n : ` Its block is not on the product page (${field.notPlacedBecause ?? 'not placed'}).`),\n field,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n\n server.registerTool(\n 'upsert_catalog',\n {\n title: 'Make or rename a product catalogue',\n description:\n 'A product catalogue of its own beside the account\\u2019s products, ' +\n 'such as \"Fireplaces\", known by its slug: sending the same slug again ' +\n 'renames it. A new one needs both names, one product and many. Its ' +\n 'fields, categories and products are then written with `object` set ' +\n 'to this slug. Make one only when the user asks for a separate ' +\n 'catalogue; most products belong in the account\\u2019s own.',\n inputSchema: {\n slug: CatalogObjectSlugSchema.describe(\n 'The slug of the catalogue: a lower-case letter, then lower case, digits and underscores.',\n ),\n ...CatalogCatalogueUpsertSchema.shape,\n },\n annotations: { destructiveHint: false, idempotentHint: true },\n },\n async ({ slug, ...input }) => {\n try {\n const catalog = await client.upsertCatalog(slug, input);\n\n return answer(\n `${catalog.created ? 'Made' : 'Changed'} ${catalog.namePlural} (${catalog.slug}).`,\n catalog,\n );\n } catch (error) {\n return refusal(error, 'catalog:write');\n }\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CrmClient } from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport {\n CatalogFieldSlugSchema,\n CONFIGURATOR_FILE_KINDS,\n} from '@kit/crm-invoicing/schema/catalog-api';\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 { objectInput } from './catalog-tools';\nimport { answer, refusal } from './tool-answers';\n\nimport { readFile, stat } from 'node:fs/promises';\nimport { extname } from 'node:path';\n\n/** The part of the client these tools call; a test hands in a fake of exactly this. */\nexport type ConfiguratorClient = Pick<\n CrmClient,\n 'getProduct' | 'upsertField' | 'uploadConfiguratorFile'\n>;\n\n/**\n * What a file on the user's disk may be, by kind: its extensions, its type\n * and the most bytes the API keeps. Checked here before anything is read, so\n * a folder, a wrong file or a huge one never leaves the machine.\n */\nconst FILE_RULES = {\n model: { extensions: ['.glb'], maxBytes: 10 * 1024 * 1024 },\n texture: {\n extensions: ['.webp', '.jpg', '.jpeg'],\n maxBytes: 2 * 1024 * 1024,\n },\n} as const;\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,\n * checks it here, and saves it with upsert_configurator -- which the API only\n * accepts from a key carrying `configurator:shape`, a right a reseller gives,\n * because the shape is the reseller's work.\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\", optional \"scene\" ({ \"lighting\": \"studio\" | ' +\n '\"warm\" | \"daylight\" }), \"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), model (a \"model\" path to a .glb ' +\n 'uploaded with upload_configurator_file, params.fit 0 contain or 1 stretch, ' +\n 'params.paint 1 to use the slot material) and repeat (count, children, ' +\n 'index i 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\\u2019s inputs, constants ' +\n 'and the repeat index. Texture paths in materials come from ' +\n 'upload_configurator_file too. Making a configurator, or changing its ' +\n '\"geometry\", needs a key with the configurator:shape scope; everything ' +\n 'around the shape needs only catalog:write.';\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: ConfiguratorClient,\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 'API will when it is saved: 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. upsert_configurator saves it.'\n : [\n `${problems.length} problem(s):`,\n ...problems.map(describeIssue),\n ].join('\\n'),\n { problems },\n );\n },\n );\n\n server.registerTool(\n 'upsert_configurator',\n {\n title: 'Save a configurator',\n description:\n 'Makes a configurator field, or changes one, with its whole ' +\n 'structure. The structure is checked here first, exactly as ' +\n 'check_configurator_shape does, and nothing is sent while it has a ' +\n 'problem. Making the field, or changing its \"geometry\", needs a key ' +\n 'with configurator:shape; changing parameters, lists, materials, ' +\n 'prices or rules of an existing one needs only catalog:write. A new ' +\n 'field needs a `label`. Upload models and textures first with ' +\n 'upload_configurator_file and put the paths it answers in the ' +\n 'structure. ' +\n CONFIGURATOR,\n inputSchema: {\n slug: CatalogFieldSlugSchema.describe(\n 'The slug of the configurator field.',\n ),\n label: z.string().trim().min(1).max(120).optional(),\n structure: z\n .string()\n .min(2)\n .max(200_000)\n .describe(\n 'The JSON text of the whole configurator structure (it has \"schemaVersion\").',\n ),\n public: z\n .boolean()\n .optional()\n .describe('Whether product pages may show the configurator.'),\n ...objectInput,\n },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async ({ slug, label, structure, public: isPublic, object }) => {\n const problems = checkDocument(structure);\n\n if (problems.length > 0 || !structure.includes('\"schemaVersion\"')) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: [\n problems.length > 0\n ? `Not saved. ${problems.length} problem(s):`\n : 'Not saved: this is a shape document alone; save a whole structure (it has \"schemaVersion\").',\n ...problems.map(describeIssue),\n ].join('\\n'),\n },\n ],\n };\n }\n\n try {\n const field = await client.upsertField(\n slug,\n {\n ...(label ? { label } : {}),\n type: 'configurator',\n config: JSON.parse(structure) as Record<string, unknown>,\n ...(isPublic === undefined ? {} : { public: isPublic }),\n },\n object ? { object } : undefined,\n );\n\n return answer(\n `${field.created ? 'Made' : 'Changed'} the configurator ${field.label} (${field.slug}).`,\n field,\n );\n } catch (error) {\n return refusal(error, 'configurator:shape');\n }\n },\n );\n\n server.registerTool(\n 'upload_configurator_file',\n {\n title: 'Upload a model or a texture for a configurator',\n description:\n 'Reads one file from the user\\u2019s disk and uploads it for a ' +\n 'configurator field: a \"model\" is a binary glTF (.glb, at most 10 MB, ' +\n 'everything inside the file, no Draco, no lights of its own, at most ' +\n '200,000 triangles) and part of the shape, so it needs ' +\n 'configurator:shape; a \"texture\" is a .webp or .jpg picture of at ' +\n 'most 2 MB. Answers with the path to put in the structure. The field ' +\n 'must exist first (upsert_configurator).',\n inputSchema: {\n fieldSlug: CatalogFieldSlugSchema.describe(\n 'The slug of the configurator field.',\n ),\n kind: z.enum(CONFIGURATOR_FILE_KINDS),\n filePath: z\n .string()\n .min(1)\n .max(1000)\n .describe('The absolute path of the file on this machine.'),\n ...objectInput,\n },\n annotations: { destructiveHint: false, idempotentHint: false },\n },\n async ({ fieldSlug, kind, filePath, object }) => {\n const rules = FILE_RULES[kind];\n const extension = extname(filePath).toLowerCase();\n\n if (!(rules.extensions as readonly string[]).includes(extension)) {\n return notUploaded(\n `A ${kind} is a ${rules.extensions.join(' or ')} file; this is \"${extension || 'no extension'}\".`,\n );\n }\n\n const info = await stat(filePath).catch(() => null);\n\n if (!info?.isFile()) {\n return notUploaded(`\"${filePath}\" is not a file on this machine.`);\n }\n\n if (info.size > rules.maxBytes) {\n return notUploaded(\n `The file is ${info.size} bytes; a ${kind} may be at most ${rules.maxBytes}.`,\n );\n }\n\n try {\n const bytes = new Uint8Array(await readFile(filePath));\n const result = await client.uploadConfiguratorFile(\n fieldSlug,\n {\n kind,\n bytes,\n ...(kind === 'texture'\n ? {\n contentType:\n extension === '.webp' ? 'image/webp' : 'image/jpeg',\n }\n : {}),\n },\n object ? { object } : undefined,\n );\n\n return answer(\n `Uploaded. Put this path in the structure: ${result.path}`,\n result,\n );\n } catch (error) {\n return refusal(\n error,\n kind === 'model' ? 'configurator:shape' : 'catalog:write',\n );\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, 'catalog:read');\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 notUploaded(text: string) {\n return {\n isError: true,\n content: [{ type: 'text' as const, text: `Not uploaded. ${text}` }],\n };\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 roomWallOptions: 12,\n roomFloorOptions: 8,\n focus: 60,\n focusNames: 20,\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 'model',\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. `facing` is where the light goes: 0 down\n // (the default: under a shelf, under a floating cabinet), 1 up, 2 forwards\n // into the room, 3 back against the wall.\n led_strip: { required: [], optional: ['facing'] },\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 // An uploaded `.glb` placed in the block's frame. `fit` 0 scales it evenly\n // and centres it (the default), 1 stretches it to the frame: a handle that\n // grows with the door; 2 keeps the model's own size, standing on the\n // frame's floor in the middle and against its front: a fire insert, which\n // is as large as it is. `paint` 1 gives every mesh the slot's material, so\n // it follows the visitor's colour; 0 (the default) keeps the model's own.\n model: { required: [], optional: ['fit', 'paint'] },\n};\n\n/**\n * `{account}/{attribute}/{uuid}.glb` in the model bucket. Like a texture path,\n * the database checks the first two against the row; the URL is built by the\n * server, never stored. A model only ever reaches the bucket through the\n * upload route, which reads it first (`inspect-glb.ts`).\n */\nexport const CONFIGURATOR_MODEL_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}\\.glb$/;\n\n/**\n * What a model block that changes with a choice follows: the structure's\n * presets (the fireplace a model offers) or a choice parameter by its key.\n */\nexport const CONFIGURATOR_FOLLOWS_PRESET = 'preset';\n\n/**\n * The name of a group of blocks that open together. Inside a repeat it may\n * hold `{i}` or `{j}`, which become the repeat's number counted from one:\n * `vak_{i}` names `vak_1`, `vak_2`, ... -- the compartments as a visitor\n * counts them, and as `front_1`, `front_2` name their fronts.\n */\nexport const MOTION_GROUP_PATTERN = /^[a-z](?:[a-z0-9_]|\\{[ij]\\}){0,39}$/;\n\nexport const MOTION_KINDS = ['hinge', 'slide'] as const;\nexport const MOTION_EDGES = ['bottom', 'top', 'left', 'right'] as const;\n\n/**\n * How a block opens. A `hinge` turns about one edge of its group, towards\n * the room: `bottom` is a flap that folds down, `top` one that lifts, `left`\n * and `right` a door. A `slide` comes straight out, as a drawer does. Every\n * block of a group -- a front, the drawer box behind it, a handle -- moves as\n * one, about the group's own edge.\n */\nexport interface GeometryMotion {\n group: string;\n kind: (typeof MOTION_KINDS)[number];\n /** A hinge's edge; `bottom` when absent. */\n edge?: (typeof MOTION_EDGES)[number];\n /** A hinge's opening in degrees; 90 when absent. */\n angle?: Formula;\n /** A slide's travel in millimetres; 0.8 × the group's depth when absent. */\n distance?: Formula;\n}\n\nexport const GeometryMotionSchema = z.strictObject({\n group: z.string().regex(MOTION_GROUP_PATTERN, 'configurator.invalidName'),\n kind: z.enum(MOTION_KINDS),\n edge: z.enum(MOTION_EDGES).optional(),\n angle: FormulaSchema.optional(),\n distance: FormulaSchema.optional(),\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 /** Only on a `model` block: where its `.glb` is stored (a URL once served). */\n model?: string;\n /**\n * Only on a `model` block that shows one file per choice: `preset` or the\n * key of a choice parameter. Its files are in `models`.\n */\n follows?: string;\n /** One file per code of what the block follows; a code without one draws nothing. */\n models?: Record<string, string>;\n /** That the block opens, and how; absent stands still. */\n motion?: GeometryMotion;\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 PrimitiveNodeFields = {\n id: ConfiguratorNameSchema,\n slot: ConfiguratorNameSchema,\n size: Vector3Schema,\n at: Vector3Schema,\n visible: FormulaSchema.optional(),\n params: z.record(ConfiguratorNameSchema, FormulaSchema).optional(),\n motion: GeometryMotionSchema.optional(),\n};\n\nconst PrimitiveNodeSchema = z.strictObject({\n ...PrimitiveNodeFields,\n type: z.enum(PRIMITIVE_TYPES).exclude(['model']),\n});\n\n/**\n * A model's file as stored -- a path the database checks against the row --\n * and as a browser receives it: a URL the server built from that path.\n */\nexport const ModelPathSchema = z\n .string()\n .regex(CONFIGURATOR_MODEL_PATH_PATTERN, 'configurator.invalidModelPath');\n\nexport const ServedFileUrlSchema = z.url({ protocol: /^https?$/ });\n\n/**\n * The node tree, for one form of file: a model block names its file, every\n * other block is drawn from numbers alone. A model's slot is still required:\n * the frame is drawn in it while the model loads or where it cannot, and the\n * dimension sketch colours it.\n */\nfunction geometryNodeSchema(modelFile: z.ZodType<string>) {\n const ModelNodeSchema = z.strictObject({\n ...PrimitiveNodeFields,\n type: z.literal('model'),\n model: modelFile,\n });\n\n // A file per choice: the fireplace a visitor picks is the one drawn.\n const ModelPerChoiceNodeSchema = z.strictObject({\n ...PrimitiveNodeFields,\n type: z.literal('model'),\n follows: ConfiguratorNameSchema,\n models: z\n .record(ConfiguratorCodeSchema, modelFile)\n .refine(\n (models) => Object.keys(models).length <= CONFIGURATOR_LIMITS.presets,\n { message: 'configurator.tooMany' },\n ),\n });\n\n const node: z.ZodType<GeometryNode> = z.lazy(() =>\n z.union([\n PrimitiveNodeSchema,\n ModelNodeSchema,\n ModelPerChoiceNodeSchema,\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(node).min(1),\n }),\n ]),\n );\n\n return node;\n}\n\nexport const GeometryNodeSchema = geometryNodeSchema(ModelPathSchema);\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 * Which way the grain of a picture runs on this part: `horizontal` (the\n * default) as the picture is drawn, `vertical` turned a quarter. It belongs\n * to the part, not the material: the same oak lies on a cabinet and stands\n * on a tall wall panel.\n */\n grain: z.enum(['horizontal', 'vertical']).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\n/** A shape document whose model files are of the given form. */\nexport function geometryDocumentSchema(modelFile: z.ZodType<string>) {\n return 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\n .array(GeometryInputSchema)\n .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(modelFile)).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}\n\nexport const GeometryDocumentSchema = geometryDocumentSchema(ModelPathSchema);\n\nexport type GeometryDocument = z.infer<typeof GeometryDocumentSchema>;\nexport type GeometrySlot = z.infer<typeof GeometrySlotSchema>;\nexport type GeometryAnnotation = z.infer<typeof GeometryAnnotationSchema>;\n\n/**\n * Every block's id and every motion group a document can produce, at any\n * depth: what a focus may open and frame. A group with `{i}` stands for all\n * of its numbers (`motionGroupMatches`).\n */\nexport function motionNames(nodes: readonly GeometryNode[]) {\n const ids = new Set<string>();\n const groups = new Set<string>();\n const walk = (list: readonly GeometryNode[]) => {\n for (const node of list) {\n ids.add(node.id);\n\n if (node.type === 'repeat') {\n walk(node.children);\n } else if (node.motion) {\n groups.add(node.motion.group);\n }\n }\n };\n\n walk(nodes);\n\n return { ids, groups };\n}\n\n/** Whether `name` is one of the groups `template` produces: `vak_2` of `vak_{i}`. */\nexport function motionGroupMatches(template: string, name: string) {\n // Names hold nothing a pattern reads as more than itself (`MOTION_GROUP_PATTERN`).\n const pattern = template.split(/\\{[ij]\\}/).join('[1-9][0-9]?');\n\n return new RegExp(`^${pattern}$`).test(name);\n}\n\n/**\n * Every model block that follows a choice, at any depth, with the name a\n * formula reads to know whether it is drawn now: `<id>_shown` is 1 while the\n * chosen code has a file and 0 otherwise. That is how a drawn stand-in -- the\n * flame and the glass of a fireplace without a model -- steps aside the\n * moment a real model takes its place (`visible: \"1 - haard_shown\"`).\n */\nexport function choiceModelBlocks(nodes: readonly GeometryNode[]) {\n const found: { name: string; node: GeometryPrimitiveNode }[] = [];\n const walk = (list: readonly GeometryNode[]) => {\n for (const node of list) {\n if (node.type === 'repeat') {\n walk(node.children);\n } else if (node.type === 'model' && node.follows !== undefined) {\n found.push({ name: `${node.id}_shown`, node });\n }\n }\n };\n\n walk(nodes);\n\n return found;\n}\n","import { type ConfiguratorIssue, issue } from './configurator-issue';\nimport { CONFIGURATOR_LIMITS } from './configurator-limits';\nimport { type Formula, formulaNames } from './evaluate-formula';\nimport {\n CONFIGURATOR_FOLLOWS_PRESET,\n choiceModelBlocks,\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 /** Whether the structure offers presets, which a model block may follow. */\n hasPresets?: boolean;\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 // A block that follows a choice gives its formulas `<id>_shown`; an input or\n // a constant of that name would be overwritten by it without a word.\n const shownNames = choiceModelBlocks(document.root).map(\n (block) => block.name,\n );\n\n for (const name of shownNames) {\n if (inputNames.has(name) || constantNames.includes(name)) {\n problems.push(\n issue([...base, 'root'], 'configurator.duplicateName', { name }),\n );\n }\n }\n\n const shapeNames = new Set([...inputNames, ...constantNames, ...shownNames]);\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 if (node.motion) {\n const motion = node.motion;\n const motionPath = [...nodePath, 'motion'];\n\n if (motion.angle !== undefined) {\n checkFormula(motion.angle, [...motionPath, 'angle'], scope);\n }\n\n if (motion.distance !== undefined) {\n checkFormula(motion.distance, [...motionPath, 'distance'], scope);\n }\n\n // `{i}` only where there is an `i` to count.\n for (const placeholder of motion.group.match(/\\{([ij])\\}/g) ?? []) {\n if (!scope.has(placeholder.slice(1, 2))) {\n problems.push(\n issue([...motionPath, 'group'], 'configurator.unknownName', {\n name: placeholder,\n }),\n );\n }\n }\n\n if (\n (motion.kind === 'hinge' && motion.distance !== undefined) ||\n (motion.kind === 'slide' &&\n (motion.edge !== undefined || motion.angle !== undefined))\n ) {\n problems.push(\n issue(motionPath, 'configurator.motionMixed', {\n kind: motion.kind,\n }),\n );\n }\n }\n\n if (node.follows !== undefined && context.parameters) {\n problems.push(\n ...followProblems(node.follows, nodePath, {\n parameters: context.parameters,\n hasPresets: context.hasPresets ?? false,\n }),\n );\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\n/**\n * A model block that follows a choice must follow something that is there:\n * the presets, when the structure offers them, or a choice parameter. A file\n * for a code the list no longer has is not a problem: the owner may take an\n * option away without touching the shape, and that file is simply never\n * drawn.\n */\nfunction followProblems(\n follows: string,\n path: (string | number)[],\n context: {\n parameters: ReadonlyMap<string, GeometryParameterInfo>;\n hasPresets: boolean;\n },\n): ConfiguratorIssue[] {\n if (follows === CONFIGURATOR_FOLLOWS_PRESET) {\n return context.hasPresets\n ? []\n : [issue([...path, 'follows'], 'configurator.followsNoPresets')];\n }\n\n return context.parameters.get(follows)?.kind === 'choice'\n ? []\n : [\n issue([...path, 'follows'], 'configurator.followsNotAChoice', {\n param: follows,\n }),\n ];\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 ModelPathSchema,\n ServedFileUrlSchema,\n geometryDocumentSchema,\n motionGroupMatches,\n motionNames,\n} from './geometry-document.schema';\nimport { ROOM_FLOOR_PALETTE, ROOM_WALL_PALETTE } from './room-palette';\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\nconst choiceListSchema = (picture: z.ZodType<string>) =>\n z.strictObject({\n key: ConfiguratorNameSchema,\n label: LabelSchema,\n options: z\n .array(\n z.strictObject({\n code: ConfiguratorCodeSchema,\n label: LabelSchema,\n image: picture.optional(),\n }),\n )\n .min(1)\n .max(CONFIGURATOR_LIMITS.optionsPerList),\n });\n\nconst materialSchema = (picture: z.ZodType<string>) =>\n z.strictObject({\n code: ConfiguratorCodeSchema,\n label: LabelSchema,\n color: z.string().regex(/^#[0-9a-f]{6}$/i, 'configurator.invalidColor'),\n texture: picture.optional(),\n /** Millimetres one repeat of the texture covers; the two maps below share it. */\n textureSize: z.number().positive().max(10_000).optional(),\n /** Relief that moves with the light: a tangent-space normal map. */\n normalMap: picture.optional(),\n /** How deep the relief reads; 1 is the map as drawn. */\n normalScale: z.number().min(0).max(3).optional(),\n /** Where the surface is matte and where it shines (the green channel), times `roughness`. */\n roughnessMap: picture.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 /**\n * The colour temperature of a light, in kelvin: a LED strip sold as\n * 3000 K glows and lights in that colour rather than in `color`, which\n * stays the swatch's fallback. A strip that can do several (2700-6500 K)\n * names the one to show: 4000 K.\n */\n kelvin: z.number().int().min(1500).max(10_000).optional(),\n priceGroup: ConfiguratorCodeSchema.optional(),\n });\n\nexport const ConfiguratorChoiceListSchema = choiceListSchema(TexturePathSchema);\nexport const ConfiguratorMaterialSchema = materialSchema(TexturePathSchema);\n\n/**\n * Every key on a material that holds a storage path. `withTextureUrls` turns\n * each into a URL and the database's `kit.crm_configurator_texture_paths_problem`\n * checks each against the row's account and attribute; a test holds both to\n * this list, so a fourth map cannot be served without being checked.\n */\nexport const CONFIGURATOR_MATERIAL_PATH_KEYS = [\n 'texture',\n 'normalMap',\n 'roughnessMap',\n] as const satisfies readonly (keyof ConfiguratorMaterial)[];\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\n/**\n * How the scene is lit. Each is a whole look -- exposure, the room's\n * reflections and the colour of the main lamp -- and never a setting a site\n * passes in: a configurator looks the same in the object builder, on the\n * product page and on a customer's own site. A fireplace reads warmer in\n * `warm`, a cabinet truer in `studio`.\n */\nexport const CONFIGURATOR_SCENE_LIGHTINGS = [\n 'studio',\n 'warm',\n 'daylight',\n] as const;\n\nexport type ConfiguratorSceneLighting =\n (typeof CONFIGURATOR_SCENE_LIGHTINGS)[number];\n\n/**\n * How a block's edges are drawn: `soft` (the default) rounds every edge by a\n * few millimetres so it catches the light, `sharp` draws a plain box. Only\n * the picture changes; every measure stays the block's.\n */\nexport const CONFIGURATOR_SCENE_EDGES = ['soft', 'sharp'] as const;\n\nexport type ConfiguratorSceneEdges = (typeof CONFIGURATOR_SCENE_EDGES)[number];\n\nconst RoomColorSchema = z.strictObject({\n code: ConfiguratorCodeSchema,\n label: LabelSchema,\n color: z.string().regex(/^#[0-9a-f]{6}$/i, 'configurator.invalidColor'),\n});\n\nconst roomSurfaceSchema = (max: number) =>\n z.strictObject({\n /** The code shown first; absent is the first option. */\n default: ConfiguratorCodeSchema.optional(),\n /** Absent offers the package's own palette (`room-palette.ts`). */\n options: z.array(RoomColorSchema).min(1).max(max).optional(),\n });\n\n/**\n * A wall behind the furniture and a floor under it, drawn by the\n * configurator itself and wider and deeper than any model, in a colour the\n * visitor picks. A view and never a choice: the colour of the room reaches\n * no price, no request and no order. Absent draws no room, as before.\n */\nexport const ConfiguratorRoomSchema = z.strictObject({\n wall: roomSurfaceSchema(CONFIGURATOR_LIMITS.roomWallOptions).optional(),\n floor: roomSurfaceSchema(CONFIGURATOR_LIMITS.roomFloorOptions).optional(),\n /** Millimetres of room beside the furniture on each side; 1500 by default. */\n margin: z.number().min(0).max(5000).optional(),\n /** Millimetres of floor in front of the wall; 3500 by default. */\n depth: z.number().min(1000).max(10_000).optional(),\n});\n\nexport const ConfiguratorSceneSchema = z.strictObject({\n lighting: z.enum(CONFIGURATOR_SCENE_LIGHTINGS).optional(),\n edges: z.enum(CONFIGURATOR_SCENE_EDGES).optional(),\n room: ConfiguratorRoomSchema.optional(),\n});\n\nexport type ConfiguratorRoom = z.infer<typeof ConfiguratorRoomSchema>;\n\nconst FocusNameSchema = z\n .string()\n .regex(/^[a-z][a-z0-9_]{0,39}$/, 'configurator.invalidName');\n\n/**\n * Where the picture goes when a choice changes: a change of `param` (while\n * `when` holds) opens the motion groups in `open` and turns the camera to\n * what `frame` names -- block ids or groups, the open groups when absent. A\n * moment later, or at the visitor's next move in the scene, the fronts close\n * and the camera goes back. `night` dims the room so a LED shows; it is on by\n * default when what is framed holds a LED strip.\n */\nexport const ConfiguratorFocusSchema = z.strictObject({\n param: ConfiguratorNameSchema,\n when: ConfiguratorConditionSchema.optional(),\n open: z.array(FocusNameSchema).max(CONFIGURATOR_LIMITS.focusNames).optional(),\n frame: z\n .array(FocusNameSchema)\n .min(1)\n .max(CONFIGURATOR_LIMITS.focusNames)\n .optional(),\n night: z.boolean().optional(),\n});\n\nexport type ConfiguratorFocus = z.infer<typeof ConfiguratorFocusSchema>;\n\n/**\n * The structure, for one form of file: as stored (storage paths the database\n * checks) or as a browser receives it (URLs the server built from them).\n */\nconst structureObjectSchema = (files: {\n picture: z.ZodType<string>;\n model: z.ZodType<string>;\n}) =>\n z.strictObject({\n schemaVersion: z.literal(1),\n geometry: geometryDocumentSchema(files.model),\n /**\n * How the scene is presented. Part of the structure and not of the shape\n * document, so the owner sets it under `crm.manage` like a material.\n */\n scene: ConfiguratorSceneSchema.optional(),\n tabs: z\n .array(\n z.strictObject({ key: ConfiguratorNameSchema, label: LabelSchema }),\n )\n .min(1)\n .max(CONFIGURATOR_LIMITS.tabs),\n parameters: z\n .array(ConfiguratorParameterSchema)\n .max(CONFIGURATOR_LIMITS.parameters),\n choiceLists: z\n .array(choiceListSchema(files.picture))\n .max(CONFIGURATOR_LIMITS.choiceLists),\n materials: z\n .array(materialSchema(files.picture))\n .max(CONFIGURATOR_LIMITS.materials),\n priceGroups: z\n .array(\n z.strictObject({ code: ConfiguratorCodeSchema, label: LabelSchema }),\n )\n .max(CONFIGURATOR_LIMITS.priceGroups),\n /** Named amounts a model fills in and price rules read (`meter_price`). */\n amounts: z\n .array(\n z.strictObject({ key: ConfiguratorNameSchema, label: LabelSchema }),\n )\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 /** What the picture opens and looks at when a choice changes. */\n focus: z\n .array(ConfiguratorFocusSchema)\n .max(CONFIGURATOR_LIMITS.focus)\n .optional(),\n priceRules: z\n .array(ConfiguratorPriceRuleSchema)\n .max(CONFIGURATOR_LIMITS.priceRules),\n });\n\nexport const ConfiguratorStructureObjectSchema = structureObjectSchema({\n picture: TexturePathSchema,\n model: ModelPathSchema,\n});\n\n/**\n * The structure as `withTextureUrls` serves it: every file a URL. What a site\n * receiving it reads it with -- the stored form would refuse every URL as a\n * path that is not one.\n */\nexport const ServedConfiguratorStructureObjectSchema = structureObjectSchema({\n picture: ServedFileUrlSchema,\n model: ServedFileUrlSchema,\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 shape = motionNames(structure.geometry.root);\n const isGroup = (name: string) =>\n [...shape.groups].some((template) => motionGroupMatches(template, name));\n\n (structure.focus ?? []).forEach((focus, index) => {\n const path = ['focus', index];\n\n if (!parameters.has(focus.param)) {\n problems.push(\n issue([...path, 'param'], 'configurator.unknownParam', {\n key: focus.param,\n }),\n );\n }\n\n if (focus.when) {\n const conditionParam = parameters.get(focus.when.param);\n const valueProblem = conditionParam\n ? conditionValueProblem(\n focus.when.op,\n focus.when.value,\n conditionParam,\n lists,\n materials,\n )\n : 'configurator.unknownParam';\n\n if (valueProblem) {\n problems.push(\n issue([...path, 'when'], valueProblem, { key: focus.when.param }),\n );\n }\n }\n\n (focus.open ?? []).forEach((name, nameIndex) => {\n if (!isGroup(name)) {\n problems.push(\n issue(\n [...path, 'open', nameIndex],\n 'configurator.unknownMotionGroup',\n {\n name,\n },\n ),\n );\n }\n });\n\n (focus.frame ?? []).forEach((name, nameIndex) => {\n if (!shape.ids.has(name) && !isGroup(name)) {\n problems.push(\n issue([...path, 'frame', nameIndex], 'configurator.unknownFrame', {\n name,\n }),\n );\n }\n });\n });\n\n const room = structure.scene?.room;\n\n for (const surface of ['wall', 'floor'] as const) {\n const own = room?.[surface];\n const path = ['scene', 'room', surface];\n const codes = own?.options\n ? uniqueKeys(\n own.options,\n (option) => option.code,\n [...path, 'options'],\n 'code',\n problems,\n )\n : new Set(\n (surface === 'wall' ? ROOM_WALL_PALETTE : ROOM_FLOOR_PALETTE).map(\n (option) => option.code,\n ),\n );\n\n if (own?.default !== undefined && !codes.has(own.default)) {\n problems.push(\n issue([...path, 'default'], 'configurator.unknownCode', {\n code: own.default,\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 hasPresets: structure.presets !== undefined,\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","/**\n * The colours a room offers when its structure names none of its own: six\n * walls and four floors, enough to answer \"does this suit my room\" without a\n * colour picker that clutters the panel and invites combinations nobody\n * would build. Plain colours: a floor picture would be one more file every\n * visitor downloads for a view that never reaches the order.\n */\nexport interface RoomColorOption {\n code: string;\n label: string;\n color: string;\n}\n\nexport const ROOM_WALL_PALETTE: readonly RoomColorOption[] = [\n // i18n-exempt: the Dutch name of a colour in a palette that is also served to customers' own sites, where there is no catalogue\n { code: 'gebroken-wit', label: 'Gebroken wit', color: '#ece8e1' },\n // i18n-exempt: the Dutch name of a colour in a palette that is also served to customers' own sites, where there is no catalogue\n { code: 'warm-grijs', label: 'Warm grijs', color: '#b9b2a8' },\n // i18n-exempt: the Dutch name of a colour in a palette that is also served to customers' own sites, where there is no catalogue\n { code: 'zand', label: 'Zand', color: '#d6c5a8' },\n // i18n-exempt: the Dutch name of a colour in a palette that is also served to customers' own sites, where there is no catalogue\n { code: 'saliegroen', label: 'Saliegroen', color: '#a7b29c' },\n // i18n-exempt: the Dutch name of a colour in a palette that is also served to customers' own sites, where there is no catalogue\n { code: 'nachtblauw', label: 'Nachtblauw', color: '#2f3a4c' },\n // i18n-exempt: the Dutch name of a colour in a palette that is also served to customers' own sites, where there is no catalogue\n { code: 'antraciet', label: 'Antraciet', color: '#3d3d3f' },\n];\n\nexport const ROOM_FLOOR_PALETTE: readonly RoomColorOption[] = [\n // i18n-exempt: the Dutch name of a colour in a palette that is also served to customers' own sites, where there is no catalogue\n { code: 'licht-eiken', label: 'Licht eiken', color: '#c8a77e' },\n // i18n-exempt: the Dutch name of a colour in a palette that is also served to customers' own sites, where there is no catalogue\n { code: 'donker-eiken', label: 'Donker eiken', color: '#6b4f38' },\n // i18n-exempt: the Dutch name of a colour in a palette that is also served to customers' own sites, where there is no catalogue\n { code: 'betongrijs', label: 'Betongrijs', color: '#9a9893' },\n // i18n-exempt: the Dutch name of a colour in a palette that is also served to customers' own sites, where there is no catalogue\n { code: 'warm-beige', label: 'Warm beige tegel', color: '#d9cbb7' },\n];\n\n/** Millimetres of room beside the furniture on each side, and of floor in front of the wall. */\nexport const ROOM_DEFAULT_MARGIN_MM = 1500;\nexport const ROOM_DEFAULT_DEPTH_MM = 3500;\n\n/** What a room looks like right now: the code of each surface. */\nexport interface RoomLook {\n wall: string;\n floor: string;\n}\n\n/**\n * The options of each surface (the structure's, or the palette above), the\n * look a visitor's choice comes to -- a code the room does not offer falls\n * back to its default, then to its first option -- and the room's measures.\n */\nexport function resolveRoom(\n room: {\n wall?: { default?: string; options?: RoomColorOption[] };\n floor?: { default?: string; options?: RoomColorOption[] };\n margin?: number;\n depth?: number;\n },\n chosen: Partial<RoomLook> = {},\n) {\n const surface = (\n own: { default?: string; options?: RoomColorOption[] } | undefined,\n palette: readonly RoomColorOption[],\n code: string | undefined,\n ) => {\n const options = own?.options ?? palette;\n const pick = (wanted: string | undefined) =>\n options.find((option) => option.code === wanted);\n const option = pick(code) ?? pick(own?.default) ?? options[0];\n\n return { options, option: option as RoomColorOption };\n };\n\n const wall = surface(room.wall, ROOM_WALL_PALETTE, chosen.wall);\n const floor = surface(room.floor, ROOM_FLOOR_PALETTE, chosen.floor);\n\n return {\n wall,\n floor,\n look: { wall: wall.option.code, floor: floor.option.code } as RoomLook,\n margin: room.margin ?? ROOM_DEFAULT_MARGIN_MM,\n depth: room.depth ?? ROOM_DEFAULT_DEPTH_MM,\n };\n}\n\nexport type ResolvedRoom = ReturnType<typeof resolveRoom>;\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';\nimport type { ChosenCodes } from './expand-geometry';\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 /**\n * Only one value is left: a single option, or a range with `min` equal to\n * `max`. It counts exactly like any other value -- in the picture, the price\n * and the selection -- but there is nothing to ask, so a panel leaves it out,\n * unless its one option costs extra or is priced on request: then the panel\n * shows it without a control, so the total can be traced.\n * This is how one product of a family pins what the shared structure offers.\n */\n settled?: 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 grain: 'horizontal' | 'vertical';\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 settled: fixedValue === undefined && min === max,\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 settled: options.length === 1,\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 grain: slot.grain === 'vertical' ? 'vertical' : 'horizontal',\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\n/**\n * The code each choice stands on, by parameter key, and the chosen preset's\n * code beside them: what a model block that follows a choice reads to know\n * which file to draw. A hidden parameter counts as not chosen.\n */\nexport function chosenCodes(configuration: ResolvedConfiguration): ChosenCodes {\n const choices: Record<string, string> = {};\n\n for (const parameter of configuration.parameters) {\n if (\n parameter.kind === 'choice' &&\n parameter.visible &&\n typeof parameter.value === 'string'\n ) {\n choices[parameter.key] = parameter.value;\n }\n }\n\n return { choices, preset: configuration.preset?.code };\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 {\n CONFIGURATOR_FOLLOWS_PRESET,\n type GeometryDocument,\n type GeometryNode,\n type PrimitiveType,\n choiceModelBlocks,\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 /** A `model` block's file: a storage path, or its URL once served. */\n model?: string;\n /** How it opens, with its group named for this repeat: `vak_{i}` became `vak_2`. */\n motion?: PartMotion;\n}\n\nexport interface PartMotion {\n group: string;\n kind: 'hinge' | 'slide';\n /** A hinge's edge. */\n edge: 'bottom' | 'top' | 'left' | 'right';\n /** A hinge's opening in degrees. */\n angle: number;\n /** A slide's travel in millimetres; null is 0.8 × the depth of the group. */\n distance: number | null;\n}\n\n/**\n * A motion group's name for one pass of its repeats: `{i}` and `{j}` become\n * the running index counted from one, as a visitor counts compartments.\n */\nexport function motionGroupName(\n template: string,\n scope: Readonly<Record<string, number>>,\n) {\n return template.replace(/\\{([ij])\\}/g, (_, index: string) =>\n String((scope[index] ?? 0) + 1),\n );\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\nconst DEFAULT_HINGE_ANGLE = 90;\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\n/**\n * What a model block that follows a choice reads to know which file to draw:\n * the code each choice parameter stands on, by its key, and the chosen\n * preset's code apart from them -- kept apart, so a choice whose key happens\n * to be `preset` is never taken for the fireplace.\n */\nexport interface ChosenCodes {\n choices: Readonly<Record<string, string>>;\n preset?: string;\n}\n\n/**\n * `chosen` says which code each thing a model block can follow stands on. A\n * block whose choice has no file -- \"no fireplace\", or nothing chosen -- is\n * left out.\n */\nexport function expandGeometry(\n document: GeometryDocument,\n inputs: Readonly<Record<string, number>>,\n chosen: ChosenCodes = { choices: {} },\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 for (const block of choiceModelBlocks(document.root)) {\n baseScope[block.name] = modelOf(block.node, chosen) === undefined ? 0 : 1;\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 model = modelOf(node, chosen);\n\n // Nothing to draw for this choice; the blocks after it still are.\n if (node.type === 'model' && model === undefined) {\n continue;\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 let motion: PartMotion | undefined;\n\n if (node.motion) {\n const angle =\n node.motion.angle === undefined\n ? DEFAULT_HINGE_ANGLE\n : evaluate(node.motion.angle, scope, [\n ...nodePath,\n 'motion',\n 'angle',\n ]);\n const distance =\n node.motion.distance === undefined\n ? null\n : evaluate(node.motion.distance, scope, [\n ...nodePath,\n 'motion',\n 'distance',\n ]);\n\n if (\n angle === null ||\n (node.motion.distance !== undefined && distance === null)\n ) {\n return;\n }\n\n motion = {\n group: motionGroupName(node.motion.group, scope),\n kind: node.motion.kind,\n edge: node.motion.edge ?? 'bottom',\n angle,\n distance,\n };\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 ...(model === undefined ? {} : { model }),\n ...(motion === undefined ? {} : { motion }),\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\n/** The file a model block draws now: its one file, or the one for the chosen code. */\nfunction modelOf(\n node: Exclude<GeometryNode, { type: 'repeat' }>,\n chosen: ChosenCodes,\n) {\n if (node.follows === undefined) {\n return node.model;\n }\n\n const code =\n node.follows === CONFIGURATOR_FOLLOWS_PRESET\n ? chosen.preset\n : chosen.choices[node.follows];\n\n return code === undefined ? undefined : node.models?.[code];\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CrmClient, EventListItem } from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport { answer, money, more, page, 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 EventClient = Pick<\n CrmClient,\n 'getEvents' | 'getEvent' | 'getEventAvailability'\n>;\n\nconst moment = z\n .string()\n .datetime({ offset: true })\n .optional()\n .describe('An ISO timestamp with its offset, such as 2026-11-01T00:00:00Z.');\n\n/**\n * The account's ticketed events, for an agenda on a website. Read only: an\n * event is made and put on sale in the app, where its tickets and seats are.\n */\nexport function registerEventTools(server: McpServer, client: EventClient) {\n server.registerTool(\n 'list_events',\n {\n title: 'List upcoming events',\n description:\n 'The published events of the account, soonest first: title, when ' +\n 'and where, the lowest ticket price, whether it is sold out and the ' +\n 'link to its ticket page. Both ends of the window are optional and ' +\n 'inclusive. Events are made in the app; this cannot create one. ' +\n 'May be a minute old.',\n inputSchema: { ...page, from: moment, to: moment },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async (input) => {\n try {\n const list = await client.getEvents(input);\n\n return answer(\n list.events.length === 0\n ? 'No published events in this window.'\n : [...list.events.map(describeEvent), more(list.nextCursor)]\n .filter(Boolean)\n .join('\\n'),\n list,\n );\n } catch (error) {\n return refusal(error, 'events:read');\n }\n },\n );\n\n server.registerTool(\n 'get_event',\n {\n title: 'Read one event',\n description:\n 'One event as its page needs it: description, doors and sales ' +\n 'times, address, ticket types with their prices, and the texts for ' +\n 'search engines. May be a minute old; ask get_event_availability ' +\n 'for seats that must be current.',\n inputSchema: {\n idOrSlug: z\n .string()\n .min(1)\n .max(200)\n .describe('The id or the address (slug) of the event.'),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ idOrSlug }) => {\n try {\n const event = await client.getEvent(idOrSlug);\n\n if (!event) {\n return answer(`No event \"${idOrSlug}\" on this account.`, null);\n }\n\n return answer(\n `${event.title} (${event.slug}), ${event.status}, ${event.ticketTypes.length} ticket types.`,\n event,\n );\n } catch (error) {\n return refusal(error, 'events:read');\n }\n },\n );\n\n server.registerTool(\n 'get_event_availability',\n {\n title: 'Seats left for an event',\n description:\n 'Live, not cached: how many seats are left for the event and for ' +\n 'each ticket type, and whether it is sold out. A remaining of null ' +\n 'means there is no ceiling.',\n inputSchema: {\n eventId: z.string().uuid().describe('The id of the event.'),\n },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ eventId }) => {\n try {\n const seats = await client.getEventAvailability(eventId);\n\n if (!seats) {\n return answer(`No event \"${eventId}\" on this account.`, null);\n }\n\n return answer(\n seats.soldOut\n ? 'Sold out.'\n : `${seats.remaining ?? 'No limit on'} seats left.`,\n seats,\n );\n } catch (error) {\n return refusal(error, 'events:read');\n }\n },\n );\n}\n\nfunction describeEvent(event: EventListItem) {\n const where = event.locationName ? `, ${event.locationName}` : '';\n const price =\n event.priceFromCents !== null && event.currency\n ? `, from ${money(event.priceFromCents, event.currency)}`\n : '';\n const sold = event.soldOut ? ', sold out' : '';\n\n return `- ${event.title} (id ${event.id}): ${event.startsAt}${where}${price}${sold} — ${event.url}`;\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, more, page, 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 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, 'website:manage');\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, 'website:manage');\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, 'website:manage');\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, 'website:manage');\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, 'website:manage');\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, 'website:manage');\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","/**\n * Where the server finds its account: a secret key and the app's address,\n * both from the environment. Placing things needs `website:manage` and\n * filling the catalogue `catalog:write`, which only a secret (`crms_`) key\n * can hold, so a publishable key is refused here rather than on the first\n * call. Which tools answer is then the key's scopes, asked by the API.\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 in the app ' +\n '(Settings → API keys) with the scopes the agent needs: ' +\n '\"website:manage\" to place things on a site, \"catalog:read\" and ' +\n '\"events:read\" to read, \"catalog:write\" to fill the catalogue, and ' +\n '\"configurator:shape\" -- which only a reseller can give -- to make a ' +\n '3D configurator and upload its models. Settings → Integrations → ' +\n 'MCP server makes such a key and the command to add this server in ' +\n 'one go. Pass it in the 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. This server needs a `crms_` ' +\n 'key: a `crmp_` key is meant for a browser and can never place ' +\n 'things on a website or write 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","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nimport { answer } from './tool-answers';\n\n/** What `server_info` reports; the hash is asked at the time, after every tool is registered. */\nexport interface ServerInfo {\n version: string;\n clientVersion: string;\n baseUrl: string;\n definitionsVersion: () => string;\n}\n\n/**\n * Which server this is. An agent keeps the tool definitions of the session it\n * started in; this is the way to see whether the server it is talking to is\n * newer than those: compare `definitionsVersion` with the one in the\n * description of a write tool.\n */\nexport function registerServerInfoTool(server: McpServer, info: ServerInfo) {\n server.registerTool(\n 'server_info',\n {\n title: 'Which version of this server is running',\n description:\n 'The version of this MCP server and of the API client it uses, the ' +\n 'address of the API it talks to, and `definitionsVersion`, a ' +\n 'fingerprint of every tool definition it serves. A write tool names ' +\n 'the same fingerprint in its description; when the two differ, your ' +\n 'session was started before an update of this server and works from ' +\n 'older tool definitions -- restart the session (in Claude Code ' +\n '`/mcp` and reconnect, or a new session). The server cannot do that ' +\n 'for you.',\n inputSchema: {},\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n const result = {\n version: info.version,\n clientVersion: info.clientVersion,\n baseUrl: info.baseUrl,\n definitionsVersion: info.definitionsVersion(),\n };\n\n return answer(\n `crm-mcp-server ${result.version} (client ${result.clientVersion}), ` +\n `API ${result.baseUrl}, tool definitions ${result.definitionsVersion}.`,\n result,\n );\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type {\n CrmClient,\n Discount,\n ShippingZoneSetup,\n ShopReadinessFinding,\n} from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport { answer, money, 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 ShopSetupClient = Pick<\n CrmClient,\n | 'getShopReadiness'\n | 'getShippingSetup'\n | 'upsertShippingZone'\n | 'upsertShippingMethod'\n | 'getDiscounts'\n | 'upsertDiscount'\n | 'archiveDiscount'\n | 'getPaymentStatus'\n | 'getApiWebhooks'\n | 'upsertApiWebhook'\n>;\n\nconst id = z.string().uuid();\nconst cents = z.number().int().min(0).max(99_999_999);\n\nconst region = z.object({\n countryCode: z\n .string()\n .regex(/^[A-Z]{2}$/)\n .describe('Two capital letters, ISO 3166 (NL, BE, DE).'),\n postcodeFrom: z\n .string()\n .max(20)\n .nullable()\n .default(null)\n .describe('With postcodeTo: only this range of the country.'),\n postcodeTo: z.string().max(20).nullable().default(null),\n});\n\nconst discount = z.object({\n name: z.string().min(1).max(120).describe('What the team sees it as.'),\n code: z\n .string()\n .max(40)\n .nullable()\n .describe(\n 'What a buyer types (upper-cased), or null for one that applies on its own.',\n ),\n effect: z\n .enum([\n 'tier_price',\n 'percent_off_lines',\n 'amount_off_lines',\n 'percent_off_order',\n 'amount_off_order',\n 'free_shipping',\n ])\n .describe(\n 'tier_price: a lower price from tierMinQuantity of one product; ' +\n '*_lines: off each matching line; *_order: once off the whole order; ' +\n 'free_shipping: no postage.',\n ),\n percent: z\n .number()\n .nullable()\n .describe('For a percentage: above 0, at most 100.'),\n amountCents: z\n .number()\n .int()\n .nullable()\n .describe('For an amount, in currency, as the account enters prices.'),\n currency: z\n .string()\n .regex(/^[A-Z]{3}$/)\n .nullable()\n .describe('Required beside amountCents (EUR).'),\n tierMinQuantity: z.number().int().nullable(),\n minSubtotalCents: z\n .number()\n .int()\n .nullable()\n .describe('Only from this basket value, before discount.'),\n productIds: z.array(id).max(500).describe('Empty: every product.'),\n categoryIds: z.array(id).max(100),\n objectIds: z.array(id).max(20),\n startsAt: z.string().datetime({ offset: true }).nullable(),\n endsAt: z.string().datetime({ offset: true }).nullable(),\n maxUses: z.number().int().positive().nullable(),\n maxUsesPerEmail: z.number().int().positive().nullable(),\n combinable: z\n .boolean()\n .describe(\n 'Whether it may count beside another discount on the same order.',\n ),\n});\n\n/**\n * Setting a shop up from an agent: postage, discounts, the payment\n * connection, the address that keeps the site's pages fresh, and a list of\n * what is still missing. Every tool is one call of the public API with the\n * `shop:manage` scope. What a key can never do -- connect Stripe, switch to\n * live -- stays a person's, in the app, and the tools say where.\n */\nexport function registerShopSetupTools(\n server: McpServer,\n client: ShopSetupClient,\n) {\n server.registerTool(\n 'check_shop_readiness',\n {\n title: 'What the shop still misses',\n description:\n 'Everything that stops the shop from selling (blocker) or costs it ' +\n 'sales (warning): no product for sale, products without a price, no ' +\n 'shipping method, Stripe not connected or still in test mode, no ' +\n 'revalidation webhook, no cookie banner, no visit tracking. Each ' +\n 'finding names the tool that fixes it, or the screen in the app. ' +\n 'Run it first, and again when you think you are done.',\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n try {\n const readiness = await client.getShopReadiness();\n\n return answer(\n [\n readiness.ready\n ? 'Nothing blocks a sale.'\n : 'The shop cannot sell yet.',\n ...readiness.findings.map(describeFinding),\n ].join('\\n'),\n readiness,\n );\n } catch (error) {\n return refusal(error, 'shop:manage');\n }\n },\n );\n\n server.registerTool(\n 'list_shipping_methods',\n {\n title: 'List shipping zones and methods',\n description:\n 'Every shipping zone with the countries it covers and the methods ' +\n 'under it (what each charges, whether it is on), and the shipping ' +\n 'classes a flat rate may charge per class. Amounts in cents, as the ' +\n 'account enters prices.',\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n try {\n const setup = await client.getShippingSetup();\n\n return answer(\n setup.zones.length === 0\n ? 'No shipping zones yet: make one with upsert_shipping_zone.'\n : setup.zones.map(describeZone).join('\\n'),\n setup,\n );\n } catch (error) {\n return refusal(error, 'shop:manage');\n }\n },\n );\n\n server.registerTool(\n 'upsert_shipping_zone',\n {\n title: 'Make or change a shipping zone',\n description:\n 'A zone is a name and the countries (or postcode ranges) it covers. ' +\n 'Without id a new zone; with one, that zone renamed and its places ' +\n 'replaced by these. Answers the zone id, for upsert_shipping_method.',\n inputSchema: {\n id: id\n .optional()\n .describe('The zone to change; leave out for a new one.'),\n name: z.string().min(1).max(80),\n regions: z.array(region).max(300).default([]),\n },\n annotations: { idempotentHint: true },\n },\n async (input) => {\n try {\n const zone = await client.upsertShippingZone(input);\n\n return answer(\n `${input.id ? 'Changed' : 'Made'} zone \"${input.name}\" (id ${zone.id}).`,\n zone,\n );\n } catch (error) {\n return refusal(error, 'shop:manage');\n }\n },\n );\n\n server.registerTool(\n 'upsert_shipping_method',\n {\n title: 'Make or change a shipping method',\n description:\n 'A way to post under a zone. flat_rate charges amountCents (and per ' +\n 'class through classRates, noClassAmountCents for a product without ' +\n 'one); free_shipping charges nothing, from freeThresholdCents of ' +\n 'goods if given; local_pickup charges nothing. Without id a new ' +\n 'method under zoneId; with one, that method changed -- its kind and ' +\n 'zone are fixed once made.',\n inputSchema: {\n id: id.optional(),\n zoneId: id,\n kind: z.enum(['flat_rate', 'free_shipping', 'local_pickup']),\n name: z.string().min(1).max(80).describe('What the buyer chooses.'),\n enabled: z.boolean().default(true),\n amountCents: cents.default(0),\n noClassAmountCents: cents.default(0),\n taxRateId: id.nullish(),\n freeThresholdCents: cents.nullish(),\n classRates: z\n .array(z.object({ classId: id, amountCents: cents }))\n .max(100)\n .default([]),\n },\n annotations: { idempotentHint: true },\n },\n async (input) => {\n try {\n const method = await client.upsertShippingMethod(input);\n\n return answer(\n `${input.id ? 'Changed' : 'Made'} method \"${input.name}\" (id ${method.id}).`,\n method,\n );\n } catch (error) {\n return refusal(error, 'shop:manage');\n }\n },\n );\n\n server.registerTool(\n 'list_discounts',\n {\n title: 'List discounts',\n description:\n 'The account discounts, newest first, at most 200: codes, the ones ' +\n 'that apply on their own, and tier prices, with every condition and ' +\n 'how many paid orders used each. Archived ones only with archived: true.',\n inputSchema: { archived: z.boolean().optional() },\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async ({ archived }) => {\n try {\n const discounts = await client.getDiscounts({ archived });\n\n return answer(\n discounts.length === 0\n ? 'No discounts.'\n : discounts.map(describeDiscount).join('\\n'),\n discounts,\n );\n } catch (error) {\n return refusal(error, 'shop:manage');\n }\n },\n );\n\n server.registerTool(\n 'upsert_discount',\n {\n title: 'Make or change a discount',\n description:\n 'A code, a discount that applies on its own (code null), or a tier ' +\n 'price. Every field is sent, null where it does not apply. Without ' +\n 'id a new discount; with one, that discount replaced field by field. ' +\n 'A code the account already has is refused (conflict). A discount is ' +\n 'counted against maxUses when an order with it is paid.',\n inputSchema: {\n id: id.optional(),\n discount,\n },\n annotations: { idempotentHint: true },\n },\n async (input) => {\n try {\n const made = await client.upsertDiscount(input);\n\n return answer(describeDiscount(made), made);\n } catch (error) {\n return refusal(error, 'shop:manage');\n }\n },\n );\n\n server.registerTool(\n 'archive_discount',\n {\n title: 'Archive a discount',\n description:\n 'Stops a discount from giving anything to a new cart or order; orders ' +\n 'that used it keep it. archived: false brings it back. Deleting is ' +\n 'left to a person in the app.',\n inputSchema: {\n id,\n archived: z.boolean().default(true),\n },\n annotations: { idempotentHint: true },\n },\n async ({ id: discountId, archived }) => {\n try {\n const result = await client.archiveDiscount(discountId, archived);\n\n return answer(\n archived ? 'Archived.' : 'Brought back from the archive.',\n result,\n );\n } catch (error) {\n return refusal(error, 'shop:manage');\n }\n },\n );\n\n server.registerTool(\n 'get_payment_status',\n {\n title: 'Can the shop take a payment',\n description:\n 'Whether a Stripe account is connected and ready for cards, whether ' +\n 'Stripe still asks for something, and whether the account is in test ' +\n 'mode. Read only: connecting Stripe and switching to live happen in ' +\n 'the app, at the screen named in connectIn -- tell the person.',\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n try {\n const status = await client.getPaymentStatus();\n\n const sentence = !status.connected\n ? `Stripe is not connected (${status.status}). A person connects it in the app: ${status.connectIn}.`\n : [\n 'Stripe is connected',\n status.testMode\n ? 'in test mode: orders are test orders and no money moves'\n : 'and live',\n status.requirementsDue ? 'Stripe still asks for details' : '',\n ]\n .filter(Boolean)\n .join(', ') + '.';\n\n return answer(sentence, status);\n } catch (error) {\n return refusal(error, 'shop:manage');\n }\n },\n );\n\n server.registerTool(\n 'list_api_webhooks',\n {\n title: 'List webhook addresses',\n description:\n 'The addresses the account tells about changes (products, categories, ' +\n 'events, orders), with whether each is on, paused after failures, and ' +\n 'how its last delivery went. The signing key is never shown again.',\n annotations: { readOnlyHint: true, idempotentHint: true },\n },\n async () => {\n try {\n const hooks = await client.getApiWebhooks();\n\n return answer(\n hooks.length === 0\n ? 'No webhook addresses.'\n : hooks\n .map(\n (hook) =>\n `- ${hook.name} (id ${hook.id}): ${hook.url}, ${hook.events.join(', ')}` +\n (hook.paused\n ? `, paused after ${hook.consecutiveFailures} failures`\n : hook.active\n ? ''\n : ', off'),\n )\n .join('\\n'),\n hooks,\n );\n } catch (error) {\n return refusal(error, 'shop:manage');\n }\n },\n );\n\n server.registerTool(\n 'upsert_api_webhook',\n {\n title: 'Make or change a webhook address',\n description:\n 'Point the account at the site revalidation route (with the SDK: ' +\n 'https://<site>/api/solumflow/revalidate) so its cached pages follow ' +\n 'a product the moment it changes. Without id a new address: the ' +\n 'answer carries its signing key once -- put it in the site ' +\n 'environment as SOLUMFLOW_WEBHOOK_KEY, it cannot be read back. ' +\n 'https only, never a private network.',\n inputSchema: {\n id: id.optional(),\n name: z.string().min(1).max(80),\n url: z.string().url().max(2048),\n events: z\n .array(z.string().min(1).max(60))\n .min(1)\n .max(20)\n .describe(\n 'For a shop: product.changed, product.deleted, category.changed.',\n ),\n },\n annotations: { idempotentHint: false },\n },\n async (input) => {\n try {\n const result = await client.upsertApiWebhook(input);\n\n return answer(\n result.signingSecret\n ? `Made address ${result.id}. Its signing key is in this answer and nowhere else: set it as SOLUMFLOW_WEBHOOK_KEY in the site environment now.`\n : `Changed address ${result.id}.`,\n result,\n );\n } catch (error) {\n return refusal(error, 'shop:manage');\n }\n },\n );\n}\n\nfunction describeFinding(finding: ShopReadinessFinding) {\n const examples = finding.examples?.length\n ? ` (${finding.examples.join(', ')})`\n : '';\n\n return `- ${finding.severity}: ${finding.message}${examples} Fixed by ${finding.fixedBy}.`;\n}\n\nfunction describeZone(zone: ShippingZoneSetup) {\n const where = zone.isFallback\n ? 'everywhere else'\n : zone.regions.map((place) => place.countryCode).join(', ') || 'no places';\n const methods = zone.methods.map(\n (method) =>\n ` - ${method.name} (${method.kind}, id ${method.id})${method.enabled ? '' : ', off'}: ` +\n (method.kind === 'flat_rate'\n ? `${(method.amountCents / 100).toFixed(2)}`\n : method.freeThresholdCents !== null\n ? `free from ${(method.freeThresholdCents / 100).toFixed(2)}`\n : 'free'),\n );\n\n return [`- ${zone.name} (id ${zone.id}): ${where}`, ...methods].join('\\n');\n}\n\nfunction describeDiscount(entry: Discount) {\n const what =\n entry.percent !== null\n ? `${entry.percent}%`\n : entry.amountCents !== null && entry.currency\n ? money(entry.amountCents, entry.currency)\n : '';\n\n return (\n `- ${entry.name} (id ${entry.id}): ${entry.code ?? 'no code'}, ` +\n `${entry.effect}${what ? ` ${what}` : ''}, used ${entry.timesUsed}x` +\n (entry.archived ? ', archived' : '')\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CrmClient, OrderStatus } from '@solumflow-app/crm-client';\nimport { z } from 'zod';\n\nimport { type ToolScope, answer, money, refusal } from './tool-answers';\n\n/** The part of the client the test order walks through; a test fakes exactly this. */\nexport type ShopTestOrderClient = Pick<\n CrmClient,\n | 'getPaymentStatus'\n | 'getProducts'\n | 'createCart'\n | 'addCartLine'\n | 'setCartShipping'\n | 'submitCartOrder'\n | 'payTestOrder'\n | 'getOrder'\n>;\n\n/** How long the tool waits for Stripe's webhook to mark the order paid. */\nconst PAID_POLLS = 10;\nconst PAID_POLL_MS = 1500;\n\n/**\n * One order through the shop the way a visitor's would go, with Stripe's\n * test card instead of a visitor: a cart, a line, postage, the order, the\n * payment, and the order read back once the webhook has settled it. Only in\n * test mode -- the API refuses a test payment for a live order, and this tool\n * stops before the cart when the account is live.\n *\n * It needs more than `shop:manage`: the cart (`cart:write`), the order\n * (`orders:write`) and reading it back (`orders:read`), which is what a\n * shop's own server key carries anyway.\n */\nexport function registerShopTestOrderTool(\n server: McpServer,\n client: ShopTestOrderClient,\n options: { wait?: (ms: number) => Promise<void> } = {},\n) {\n const wait =\n options.wait ??\n ((ms: number) => new Promise<void>((done) => setTimeout(done, ms)));\n\n server.registerTool(\n 'run_test_order',\n {\n title: 'Place and pay a test order',\n description:\n 'Proves the shop end to end in Stripe test mode: puts one product in ' +\n 'a cart, chooses postage, places the order, pays it with Stripe test ' +\n 'card 4242, and reads the order back until it is paid (the invoice ' +\n 'and the confirmation mail follow from there). Refused when the ' +\n 'account is live -- no real money can move through this tool. Needs ' +\n 'a key with shop:manage, cart:write, orders:write and orders:read.',\n inputSchema: {\n email: z\n .string()\n .email()\n .describe(\n 'Where the confirmation goes: the shop owner, never a customer.',\n ),\n productId: z\n .string()\n .uuid()\n .optional()\n .describe(\n 'The product to buy; leave out for the first one in stock.',\n ),\n countryCode: z\n .string()\n .regex(/^[A-Z]{2}$/)\n .default('NL')\n .describe('Where it is posted, for a product that is posted.'),\n postalCode: z.string().max(20).optional(),\n },\n annotations: { idempotentHint: false },\n },\n async (input) => {\n let scope: ToolScope = 'shop:manage';\n\n try {\n const status = await client.getPaymentStatus();\n\n if (!status.connected) {\n return answer(\n `Stripe is not connected, so there is nothing to pay with. A person connects it in the app: ${status.connectIn}.`,\n status,\n );\n }\n\n if (!status.testMode) {\n return answer(\n 'The account is live: a test order would be a real one. Switch the account to Stripe test mode in the app first ' +\n `(${status.connectIn}), or prove the shop with a real purchase by hand.`,\n status,\n );\n }\n\n scope = 'catalog:read';\n const productId = input.productId ?? (await firstProductForSale());\n\n if (!productId) {\n return answer(\n 'No product is for sale and in stock: there is nothing to order. check_shop_readiness says what is missing.',\n null,\n );\n }\n\n scope = 'cart:write';\n const handle = await client.createCart({ email: input.email });\n let cart = await client.addCartLine(handle.token, {\n productId,\n quantity: 1,\n });\n\n if (cart.shipping.required) {\n cart = await client.setCartShipping(handle.token, {\n countryCode: input.countryCode,\n postalCode: input.postalCode ?? null,\n });\n\n const method = cart.shipping.methods[0];\n\n if (!method) {\n return answer(\n `No shipping method delivers to ${input.countryCode}: add one with upsert_shipping_method.`,\n cart,\n );\n }\n\n cart = await client.setCartShipping(handle.token, {\n countryCode: input.countryCode,\n postalCode: input.postalCode ?? null,\n methodId: method.id,\n });\n }\n\n if (cart.problem) {\n return answer(\n `The cart cannot be ordered: ${cart.problem.message}`,\n cart,\n );\n }\n\n scope = 'orders:write';\n const order = await client.submitCartOrder({\n buyer: { email: input.email },\n cartToken: handle.token,\n ...(cart.shipping.required\n ? {\n shippingAddress: {\n countryCode: input.countryCode,\n ...(input.postalCode ? { postalCode: input.postalCode } : {}),\n },\n }\n : {}),\n });\n\n scope = 'shop:manage';\n const payment = await client.payTestOrder(order.id);\n\n scope = 'orders:read';\n const settled = await readUntilPaid(order.id);\n const total = money(order.totalCents, order.currency);\n\n return answer(\n settled?.status === 'paid'\n ? `Test order ${order.number} for ${total} is paid. The invoice and the confirmation mail to ${input.email} follow from here.`\n : `Test order ${order.number} for ${total}: Stripe says the payment ${payment.paymentStatus}, but the order still reads ${settled?.status ?? 'unknown'}. ` +\n 'The platform marks it paid when Stripe reaches its webhook; if that never happens, the webhook between Stripe and the platform is the place to look.',\n { order, payment, settled },\n );\n } catch (error) {\n return refusal(error, scope);\n }\n },\n );\n\n async function firstProductForSale() {\n const list = await client.getProducts({ limit: 50 });\n\n return list.data.find(\n (product) => product.inStock && product.priceFromCents !== null,\n )?.id;\n }\n\n async function readUntilPaid(orderId: string) {\n let order: OrderStatus | null = null;\n\n for (let poll = 0; poll < PAID_POLLS; poll += 1) {\n order = await client.getOrder(orderId);\n\n if (order?.status === 'paid') {\n return order;\n }\n\n await wait(PAID_POLL_MS);\n }\n\n return order;\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nimport { createHash } from 'node:crypto';\n\n/**\n * A tool as a register function hands it over. The input is a map of zod\n * schemas, the shape `McpServer.registerTool` takes.\n */\ninterface ToolConfig {\n title?: string;\n description?: string;\n inputSchema?: Record<string, z.ZodType>;\n annotations?: { readOnlyHint?: boolean } & Record<string, unknown>;\n}\n\n// i18n-exempt: nothing here is shown in the app; it is read by an agent\n// `any` is the SDK's own handler type erased: this wrapper passes the\n// arguments through untouched and never looks inside them.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ToolHandler = (...args: any[]) => unknown;\n\ninterface PendingTool {\n name: string;\n config: ToolConfig;\n handler: ToolHandler;\n}\n\n/**\n * A short fingerprint of what the tools say about themselves: names, titles,\n * descriptions, input schemas and annotations. It changes when any of them\n * does, and for nothing else, so two servers with the same tools agree on it\n * and an agent holding older definitions can be told so.\n */\nexport function toolDefinitionsHash(\n tools: readonly { name: string; config: ToolConfig }[],\n) {\n const definitions = [...tools]\n .sort((left, right) => left.name.localeCompare(right.name))\n .map(({ name, config }) => ({\n name,\n title: config.title ?? null,\n description: config.description ?? null,\n inputSchema: config.inputSchema\n ? z.toJSONSchema(z.object(config.inputSchema), {\n io: 'input',\n unrepresentable: 'any',\n })\n : null,\n annotations: config.annotations ?? null,\n }));\n\n return createHash('sha256')\n .update(JSON.stringify(definitions))\n .digest('hex')\n .slice(0, 12);\n}\n\n/** The tools that change something take a `schemaHash`; a read tool has no use for one. */\nfunction writesSomething(config: ToolConfig) {\n return config.annotations?.readOnlyHint !== true;\n}\n\n/**\n * Holds the registrations of every tool back until all are known, so that the\n * fingerprint of the whole set can be put in each write tool's description\n * and checked when one is called.\n *\n * Why it exists: an agent keeps the tool definitions it was handed when its\n * session began, and a session that outlives an update of this server calls\n * the new server with the old definitions. The server cannot make the agent\n * reload. What it can do is tell, on the next write, that the definitions the\n * agent is using are not the ones it is serving, and say what to do about it.\n * The warning never refuses the call.\n *\n * `server` is what the register functions are given: it takes `registerTool`\n * for later and passes everything else to the real server. `publish` registers\n * the held tools and returns the fingerprint.\n */\nexport function holdToolRegistrations(real: McpServer) {\n const pending: PendingTool[] = [];\n let hash: string | undefined;\n\n const server = new Proxy(real, {\n get(target, property) {\n if (property === 'registerTool') {\n return (name: string, config: ToolConfig, handler: ToolHandler) => {\n pending.push({ name, config, handler });\n\n // The register functions do not use what the SDK returns.\n return undefined;\n };\n }\n\n const value = Reflect.get(target, property, target) as unknown;\n\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n\n function currentHash() {\n hash ??= toolDefinitionsHash(pending);\n\n return hash;\n }\n\n function publish() {\n const version = currentHash();\n\n for (const { name, config, handler } of pending) {\n if (!writesSomething(config)) {\n real.registerTool(\n name,\n config as Parameters<McpServer['registerTool']>[1],\n handler as Parameters<McpServer['registerTool']>[2],\n );\n continue;\n }\n\n real.registerTool(\n name,\n {\n ...config,\n description: `${config.description ?? ''} Definitions version ${version}: send it as schemaHash and the answer tells you when these tool definitions are older than the server.`,\n inputSchema: {\n ...config.inputSchema,\n schemaHash: z\n .string()\n .max(64)\n .optional()\n .describe(\n `The definitions version named in this tool's description (${version}).`,\n ),\n },\n } as Parameters<McpServer['registerTool']>[1],\n (async (\n { schemaHash, ...input }: { schemaHash?: string },\n extra: unknown,\n ) => {\n const result = (await handler(input, extra)) as {\n content?: { type: 'text'; text: string }[];\n };\n\n return schemaHash !== undefined && schemaHash !== version\n ? {\n ...result,\n content: [\n ...(result.content ?? []),\n { type: 'text' as const, text: staleDefinitions(version) },\n ],\n }\n : result;\n }) as unknown as Parameters<McpServer['registerTool']>[2],\n );\n }\n\n return version;\n }\n\n return { server: server as McpServer, publish, currentHash };\n}\n\n/** What the answer adds when an agent's tool definitions are not the server's. */\nexport function staleDefinitions(version: string) {\n return (\n `Note: your tool definitions are older than this server (its version is ${version}). ` +\n 'The call was carried out as given. Restart the session, or reconnect ' +\n 'the server with /mcp in Claude Code, to load the current definitions; ' +\n 'server_info says which version is running.'\n );\n}\n"],"mappings":"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","names":["z","z","z","text","cents","currency","z","object","object","text","z","CrmApiError","target","object","CrmApiError","z","text","text","z","z","checkFormula","z","z","z","z","target","z","z","preset","current","number","readFile","stat","extname","FILE_RULES","z","object","slugOrId","text","z","z","z","z","page","z","z","cents","z","z","z","target","version"]}