@clipform/mcp-server 2.2.1 → 2.2.3

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.
@@ -0,0 +1,27 @@
1
+ declare function setMcpVersion(version: string): void;
2
+ interface ErrorReport {
3
+ error_type: string;
4
+ error_message: string;
5
+ tool_name?: string;
6
+ status_code?: number;
7
+ api_path?: string;
8
+ }
9
+ type ErrorReporter = (report: ErrorReport) => void;
10
+ declare function setErrorReporter(reporter: ErrorReporter | null): void;
11
+ declare function reportError(report: ErrorReport): void;
12
+ declare function isServerFault(status: number): boolean;
13
+ /**
14
+ * Genuine auth failures - unlike 404/422 these are never ordinary user/agent
15
+ * error, so they're worth reporting even though they're 4xx (#2599). A 401
16
+ * has no other legitimate cause in this API, so it always reports. A 403,
17
+ * though, is NOT always an auth fault here: PLAN_LIMIT and other
18
+ * business-logic denials (feature gating) also respond 403, and those are
19
+ * ordinary user error - exactly the noise this stays silent for. Only the
20
+ * API's own `code` on the JSON error body (from `{ code, error }`, e.g.
21
+ * `create-form.ts`'s ApiError) tells them apart, so a 403 only counts as an
22
+ * auth fault when the code says so; an unrecognized/missing code on a 403
23
+ * stays silent rather than risk misclassifying a new business-logic denial.
24
+ */
25
+ declare function isAuthFault(status: number, code?: string): boolean;
26
+
27
+ export { type ErrorReport, type ErrorReporter, isAuthFault, isServerFault, reportError, setErrorReporter, setMcpVersion };
@@ -0,0 +1,16 @@
1
+ import {
2
+ isAuthFault,
3
+ isServerFault,
4
+ reportError,
5
+ setErrorReporter,
6
+ setMcpVersion
7
+ } from "./chunk-PBQ5BQWD.js";
8
+ import "./chunk-G3PMV62Z.js";
9
+ export {
10
+ isAuthFault,
11
+ isServerFault,
12
+ reportError,
13
+ setErrorReporter,
14
+ setMcpVersion
15
+ };
16
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipform/mcp-server",
3
- "version": "2.2.1",
3
+ "version": "2.2.3",
4
4
  "mcpName": "io.github.Clipform/mcp-server",
5
5
  "description": "MCP server for building and managing Clipform video forms",
6
6
  "license": "MIT",
@@ -24,6 +24,10 @@
24
24
  "types": "./dist/auth-context.d.ts",
25
25
  "default": "./dist/auth-context.js"
26
26
  },
27
+ "./telemetry": {
28
+ "types": "./dist/telemetry.d.ts",
29
+ "default": "./dist/telemetry.js"
30
+ },
27
31
  "./prompts": {
28
32
  "types": "./dist/prompts.d.ts",
29
33
  "default": "./dist/prompts.js"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/guides.ts","../src/resources.ts"],"sourcesContent":["import { callApi } from \"./api-client.js\";\n\n/**\n * Craft guide bodies live server-side (#470) - the npm package is a shim\n * that fetches them at runtime instead of bundling them in the tarball.\n * One fetch per process (all ~30KB of guides in a single response), cached;\n * a failed fetch is NOT cached so the next read retries.\n */\n\nexport interface RemoteGuide {\n type: string;\n variant: string | null;\n uri: string;\n mimeType: string;\n text: string;\n}\n\nlet guidesPromise: Promise<Map<string, RemoteGuide>> | null = null;\n\nasync function loadGuides(): Promise<Map<string, RemoteGuide>> {\n const result = await callApi(\"/internal/mcp/guides\", { method: \"GET\" });\n if (!result.ok) {\n throw new Error(`Failed to load guides: ${result.error}`);\n }\n const guides = (result.data as { guides?: RemoteGuide[] }).guides ?? [];\n return new Map(guides.map((g) => [g.uri, g]));\n}\n\n/** Fetch a guide body by its clipform:// URI. Returns null when unavailable. */\nexport async function fetchGuideText(uri: string): Promise<string | null> {\n if (!guidesPromise) {\n guidesPromise = loadGuides();\n }\n try {\n const guides = await guidesPromise;\n return guides.get(uri)?.text ?? null;\n } catch (err) {\n guidesPromise = null; // don't cache failures - retry on the next read\n // Loud, not silent: a misconfigured API_URL/key otherwise degrades every\n // guide to the stub with nothing in the logs.\n console.error(`[guides] Failed to fetch ${uri}: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n}\n\n/** Shown in place of a guide when the API is unreachable or unauthenticated. */\nexport function guideFallbackText(uri: string): string {\n return [\n `# Guide unavailable`,\n ``,\n `The craft guide (${uri}) could not be loaded from the Clipform API.`,\n `Guides are served at runtime and need a reachable API with a valid key -`,\n `check API_URL and CLIPFORM_API_KEY, then try again.`,\n ``,\n `General principles in the meantime: write narration for the ear (short,`,\n `conversational), never reveal answers in narration or media, and keep`,\n `forms tight - every question must earn its place.`,\n ].join(\"\\n\");\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSessionContext } from \"./lib/session-context.js\";\nimport { FORM_TYPE_KEYS, FORM_TYPES, ALL_VARIANTS } from \"@vid-master/config\";\n\nexport const GUIDE_TYPES = FORM_TYPE_KEYS as readonly string[] as readonly [string, ...string[]];\nexport type GuideType = (typeof FORM_TYPE_KEYS)[number];\n\nexport const QUIZ_VARIANTS = ALL_VARIANTS as readonly string[] as readonly [string, ...string[]];\nexport type QuizVariant = (typeof ALL_VARIANTS)[number];\n// Guide BODIES are not bundled here (#470): the npm tarball ships only this\n// registration metadata. Text is fetched from the API at runtime - see\n// lib/guides.ts (cached per process, stub fallback when unreachable).\nimport { fetchGuideText, guideFallbackText } from \"./lib/guides.js\";\n\nconst GUIDE_DESCRIPTIONS: Record<GuideType, string> = {\n \"quiz\": \"Craft knowledge for writing engaging quizzes - difficulty curves, question psychology, narration style, scoring\",\n \"survey\": \"Craft knowledge for feedback surveys, NPS, and research forms - brevity, rating scales, respondent fatigue\",\n \"interview\": \"Craft knowledge for building interview forms - warm-up pacing, open questions, consent, video responses\",\n \"funnel\": \"Craft knowledge for lead qualification funnels - planned feature, conditional routing coming soon\",\n \"testimonial\": \"Craft knowledge for collecting testimonials and customer stories on video - storytelling prompts, comfort techniques, consent\",\n \"application\": \"Craft knowledge for application and evaluation forms - multi-section structure, video responses for behavioural questions, screening\",\n \"booking\": \"Craft knowledge for event registration and booking forms - minimal friction, video welcome, confirmation flow\",\n};\n\nconst QUIZ_VARIANT_DESCRIPTIONS: Record<QuizVariant, string> = {\n \"personality\": \"Addendum for personality quizzes - category design, option weighting, outcome writing, no right/wrong answers\",\n \"comprehension\": \"Addendum for YouTube comprehension quizzes - extracting questions from transcripts, distractor design, audience adaptation\",\n \"composition\": \"Addendum for composition quizzes - guess formats built from rendered compositions: mechanic selection, the clue clip + answer feedback default (with an optional reveal payoff node), difficulty design\",\n};\n\nexport function getGuideUri(type: GuideType, variant?: QuizVariant): string {\n if (type === \"quiz\" && variant) {\n return `clipform://guides/quiz/${variant}`;\n }\n return `clipform://guides/${type}`;\n}\n\nasync function readGuide(uri: string): Promise<string> {\n return (await fetchGuideText(uri)) ?? guideFallbackText(uri);\n}\n\nexport function registerResources(server: McpServer) {\n for (const type of GUIDE_TYPES) {\n server.registerResource(\n `guide-${type}`,\n getGuideUri(type),\n {\n description: GUIDE_DESCRIPTIONS[type],\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\" as const], priority: 0.8 },\n },\n async () => ({\n contents: [{\n uri: getGuideUri(type),\n mimeType: \"text/markdown\",\n text: await readGuide(getGuideUri(type)),\n }],\n }),\n );\n }\n\n for (const variant of QUIZ_VARIANTS) {\n server.registerResource(\n `guide-quiz-${variant}`,\n getGuideUri(\"quiz\", variant),\n {\n description: QUIZ_VARIANT_DESCRIPTIONS[variant],\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\" as const], priority: 0.8 },\n },\n async () => ({\n contents: [{\n uri: getGuideUri(\"quiz\", variant),\n mimeType: \"text/markdown\",\n text: await readGuide(getGuideUri(\"quiz\", variant)),\n }],\n }),\n );\n }\n\n server.registerResource(\n \"context-session\",\n \"clipform://context/session\",\n {\n description:\n \"Current session info: auth mode, workspace, plan tier, node limits, feature flags. Read this before planning content to know your constraints.\",\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\"], priority: 1.0 },\n },\n async () => {\n const text = await getSessionContext();\n return {\n contents: [\n {\n uri: \"clipform://context/session\",\n mimeType: \"text/markdown\",\n text: text || \"Session context unavailable - API may not be reachable.\",\n },\n ],\n };\n }\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../config/features.js","../../config/node-types.js","../../config/form-types.js","../../config/help-links.js","../../config/index.js","../src/lib/telemetry.ts","../src/lib/api-client.ts","../src/lib/session-context.ts"],"sourcesContent":["/**\n * Feature registry - single source of truth for what Clipform can and cannot do.\n *\n * enabled: runtime gate - does the dashboard/viewer expose this feature?\n * status:\n * 'live' - shipped and working in production\n * 'beta' - available but not fully polished\n * 'planned' - on the roadmap, not yet usable\n *\n * Rules:\n * - Marketing copy, blog posts, and landing pages must only claim features with status 'live'\n * - 'planned' features can be mentioned as \"coming soon\" but never as current capability\n * - When a feature ships: set enabled to true, status to 'live', and stamp wentLiveAt\n * - Everything else derives from this file (PRODUCT.features, FEATURE_FLAGS, content claims)\n *\n * wentLiveAt (optional, 'YYYY-MM-DD'): the date a feature flipped planned -> live. It is\n * the dated capability event the What's New changelog renders (getWhatsNew) and the SEO/GTM\n * skills key off (\"live-but-uncovered -> content backlog\"). Best-effort: features that were\n * already live at this registry's baseline have no known date and stay undated - they're\n * simply absent from the dated timeline until backfilled, never stamped with a wrong date.\n *\n * @type {Record<string, { name: string, description: string, enabled: boolean, status: 'live' | 'beta' | 'planned', category: string, wentLiveAt?: string }>}\n */\nexport const FEATURES = {\n // -- Builder --\n visual_form_builder: {\n name: 'Visual form builder',\n description: 'Drag-and-drop node-based form builder powered by React Flow',\n enabled: true,\n status: 'live',\n category: 'builder',\n },\n form_themes: {\n name: 'Form themes',\n description: 'Customise primary colour, background colour, and font',\n enabled: true,\n status: 'live',\n category: 'builder',\n },\n media_prompts: {\n name: 'Media prompts',\n description: 'Attach video, audio, or images to any question node',\n enabled: true,\n status: 'live',\n category: 'builder',\n },\n node_logic: {\n name: 'Node routing logic',\n description: 'Define the order nodes appear in - currently supports linear paths (A to B to C)',\n enabled: true,\n status: 'live',\n category: 'builder',\n },\n branching: {\n name: 'Conditional branching',\n description: 'Choice nodes route to different destinations based on which option is picked',\n enabled: true,\n status: 'live',\n wentLiveAt: '2026-06-25',\n category: 'builder',\n },\n funnel: {\n name: 'Lead qualification funnels',\n description: 'Multi-step funnels with conditional routing built on branching',\n enabled: false,\n status: 'planned',\n category: 'builder',\n },\n custom_scores: {\n name: 'Custom scoring',\n description: 'Assign scores to choice options for quizzes and assessments',\n enabled: false,\n status: 'planned',\n category: 'builder',\n },\n text_response: {\n name: 'Text response option',\n description: 'Allow free-text input alongside choice options',\n enabled: false,\n status: 'planned',\n category: 'builder',\n },\n multi_select: {\n name: 'Multi-select choices',\n description: 'Pick-all-that-apply choice questions (checkbox-style selection)',\n enabled: false,\n status: 'planned',\n category: 'builder',\n },\n custom_fonts: {\n name: 'Custom fonts',\n description: 'Upload and use custom fonts in forms',\n enabled: false,\n status: 'planned',\n category: 'builder',\n },\n brand_name: {\n name: 'Custom brand name',\n description: 'Replace Clipform branding with your own brand name',\n enabled: false,\n status: 'planned',\n category: 'builder',\n },\n allow_resume: {\n name: 'Session resume',\n description: 'Respondents can continue a form where they left off',\n enabled: true,\n status: 'live',\n category: 'builder',\n },\n\n // -- Node types --\n node_start: {\n name: 'Start node',\n description: 'Entry point for every form',\n enabled: true,\n status: 'live',\n category: 'node-types',\n },\n node_choice: {\n name: 'Multiple choice',\n description: 'Single-select questions with configurable options (multi-select is gated by the multi_select feature)',\n enabled: true,\n status: 'live',\n category: 'node-types',\n },\n node_open: {\n name: 'Open-ended questions',\n description: 'Free-form responses via text, audio, or video',\n enabled: true,\n status: 'live',\n category: 'node-types',\n },\n node_details: {\n name: 'Details',\n description: 'Structured fields (name, email, phone, company) with consent checkboxes',\n enabled: true,\n status: 'live',\n category: 'node-types',\n },\n node_button: {\n name: 'Button',\n description: 'Simple navigation or statement node',\n enabled: true,\n status: 'live',\n category: 'node-types',\n },\n node_redirect: {\n name: 'Redirect',\n description: 'Terminal node that sends respondents to an external URL',\n enabled: true,\n status: 'live',\n category: 'node-types',\n },\n node_end_screen: {\n name: 'End screen',\n description: 'Configurable thank-you screen with messaging, CTAs, and share button',\n enabled: true,\n status: 'live',\n category: 'node-types',\n },\n node_scale: {\n name: 'Scale / rating',\n description: 'Numerical rating questions (1-10, stars, etc.)',\n enabled: false,\n status: 'planned',\n category: 'node-types',\n },\n node_file_upload: {\n name: 'File upload',\n description: 'Collect files from respondents',\n enabled: false,\n status: 'planned',\n category: 'node-types',\n },\n node_booking: {\n name: 'Booking',\n description: 'Calendly or Google Meet scheduling embedded in the form',\n enabled: false,\n status: 'planned',\n category: 'node-types',\n },\n node_draw: {\n name: 'Drawing',\n description: 'Drawing canvas; respondents draw and it is saved as a PNG image',\n // Went GA 2026-06-27 (#1012), pulled back behind the allowlist 2026-07-20\n // (#2289) while pen options / match-to-shape mature. Prod usage at\n // pull-back verified = Clipform Public only, zero external customers.\n // Existing nodes still render (creation-gated only, camera precedent).\n enabled: false,\n status: 'beta',\n wentLiveAt: '2026-06-27',\n category: 'node-types',\n },\n node_camera: {\n name: 'Camera',\n description: 'Capture a photo from the device camera',\n // Scaffolded disabled (#2097): code lands ready, but node.type's DB check\n // constraint doesn't accept 'camera' yet - that's a separate parked\n // holds-migration PR. Flip once the migration has merged (= applied to\n // prod) and the apps have deployed.\n enabled: false,\n status: 'planned',\n category: 'node-types',\n },\n node_payment: {\n name: 'Payment',\n description: 'Collect payments via Stripe or Paddle inline',\n enabled: false,\n status: 'planned',\n category: 'node-types',\n },\n node_binary: {\n name: 'Binary choice',\n description: 'Two-option choice node (yes/no, this/that)',\n enabled: false,\n status: 'planned',\n category: 'node-types',\n },\n node_link_list: {\n name: 'Link list',\n description: 'Terminal node showing a list of links to choose from',\n enabled: false,\n status: 'planned',\n category: 'node-types',\n },\n node_file_download: {\n name: 'File download',\n description: 'Provide files for respondents to download',\n enabled: true,\n status: 'live',\n wentLiveAt: '2026-07-12',\n category: 'node-types',\n },\n node_shopping: {\n name: 'Shopping',\n description: 'Product recommendations with direct checkout',\n enabled: false,\n status: 'planned',\n category: 'node-types',\n },\n\n // -- Response formats --\n text_responses: {\n name: 'Text responses',\n description: 'Respondents type free-form text answers',\n enabled: true,\n status: 'live',\n category: 'responses',\n },\n audio_responses: {\n name: 'Audio responses',\n description: 'Respondents record audio directly in the browser',\n enabled: true,\n status: 'live',\n category: 'responses',\n },\n video_responses: {\n name: 'Video responses',\n description: 'Respondents record video directly in the browser',\n enabled: true,\n status: 'live',\n category: 'responses',\n },\n transcription: {\n name: 'Auto-transcription',\n description: 'Whisper-powered transcription for audio and video responses',\n enabled: true,\n status: 'live',\n category: 'responses',\n },\n\n // -- Analytics --\n analytics_overview: {\n name: 'Analytics dashboard',\n description: 'Views, starts, completions with daily time series',\n enabled: true,\n status: 'live',\n category: 'analytics',\n },\n analytics_demographics: {\n name: 'Demographic breakdown',\n description: 'Country, browser, device, and referrer source analytics',\n enabled: true,\n status: 'live',\n category: 'analytics',\n },\n utm_tracking: {\n name: 'UTM parameter tracking',\n description: 'Track utm_source, utm_medium, utm_campaign on form views',\n enabled: false,\n status: 'planned',\n category: 'analytics',\n },\n csv_export: {\n name: 'CSV export',\n description: 'Export response data to CSV, including transcriptions and media links',\n enabled: true,\n status: 'live',\n wentLiveAt: '2026-06-07',\n category: 'analytics',\n },\n tracked_links: {\n name: 'Tracked links',\n description: 'URL tracking and click-through analytics for shared form links',\n enabled: false,\n status: 'planned',\n category: 'analytics',\n },\n\n // -- Sharing & embedding --\n public_share_links: {\n name: 'Public share links',\n description: 'Share forms via a public URL',\n enabled: true,\n status: 'live',\n category: 'sharing',\n },\n share_node: {\n name: 'Share individual nodes',\n description: 'Share a direct link to a specific node within a form',\n enabled: true,\n status: 'live',\n wentLiveAt: '2026-07-17',\n category: 'sharing',\n },\n public_results: {\n name: 'Public results portal',\n description: 'Share a live, read-only results page with anyone via a link',\n // Ships dark (#2129): code lands ready, gated server-side (apps/api) on\n // this flag OR the FEATURE_COMPANY_ALLOWLIST.public_results company\n // allowlist below (companyHasPublicResults) for per-company dogfood ahead\n // of the global flip - same flag-or-allowlist pattern as node_camera /\n // companyHasNodeType. Flip enabled/status together for GA - the API\n // re-checks this flag on every enable/rotate/read, so flipping it on/off\n // (or editing the allowlist) takes effect immediately, no redeploy needed.\n enabled: false,\n status: 'planned',\n category: 'sharing',\n },\n embed_inline: {\n name: 'Inline embed',\n description: 'Embed forms on external websites via script tag or JavaScript API',\n enabled: true,\n status: 'live',\n wentLiveAt: '2026-05-27',\n category: 'sharing',\n },\n embed_widget: {\n name: 'Embed widget',\n description: 'Floating button that opens a form in a modal overlay',\n enabled: false,\n status: 'planned',\n category: 'sharing',\n },\n embed_slots: {\n name: 'Embed slots',\n description: 'Named slots that point an embed at a form, so content can be swapped from the dashboard without changing the embed code',\n enabled: false,\n status: 'beta',\n category: 'sharing',\n },\n remove_branding: {\n name: 'Remove branding',\n description: 'Hide Clipform watermark (Pro plan)',\n enabled: true,\n status: 'live',\n category: 'sharing',\n },\n\n // -- AI --\n ai_form_generation: {\n name: 'AI form generation',\n description: 'Generate complete forms from a text prompt via MCP or dashboard',\n enabled: true,\n status: 'live',\n category: 'ai',\n },\n ai_tts: {\n name: 'AI text-to-speech',\n description: 'Generate voiceover audio for form prompts',\n enabled: true,\n status: 'live',\n category: 'ai',\n },\n\n // -- API --\n public_api: {\n name: 'Public API',\n description: 'REST API for form creation, node management, and response collection',\n enabled: true,\n status: 'live',\n category: 'api',\n },\n api_keys: {\n name: 'API key management',\n description: 'Self-service API key creation in the dashboard',\n enabled: true,\n status: 'live',\n wentLiveAt: '2026-06-22',\n category: 'api',\n },\n\n // -- Integrations --\n webhooks: {\n name: 'Webhooks',\n description: 'Send response data to external URLs on form completion',\n enabled: true,\n status: 'live',\n wentLiveAt: '2026-06-24',\n category: 'integrations',\n },\n // Per-integration lifecycle (split from the old coarse `integrations` flag so\n // each can ship independently). integration-catalog.js carries the metadata\n // and derives is_active from these. None are live yet.\n integration_stripe: {\n name: 'Stripe',\n description: 'Accept payments directly in your forms',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n integration_paddle: {\n name: 'Paddle',\n description: 'Accept payments with Paddle',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n integration_square: {\n name: 'Square',\n description: 'Accept payments with Square',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n integration_calendly: {\n name: 'Calendly',\n description: 'Schedule meetings directly in your forms',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n integration_calcom: {\n name: 'Cal.com',\n description: 'Open-source scheduling platform for booking meetings',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n integration_docusign: {\n name: 'DocuSign',\n description: 'Collect signatures in your forms',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n integration_zapier: {\n name: 'Zapier',\n description: 'Connect to 5,000+ apps and automate workflows',\n enabled: true,\n status: 'live',\n wentLiveAt: '2026-06-21',\n category: 'integrations',\n },\n integration_make: {\n name: 'Make',\n description: 'Advanced automation and integrations',\n enabled: true,\n status: 'live',\n wentLiveAt: '2026-06-22',\n category: 'integrations',\n },\n integration_n8n: {\n name: 'n8n',\n description: 'Open-source workflow automation - trigger flows on form responses',\n enabled: true,\n status: 'live',\n wentLiveAt: '2026-06-26',\n category: 'integrations',\n },\n integration_hubspot: {\n name: 'HubSpot',\n description: 'Sync responses to HubSpot CRM',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n integration_salesforce: {\n name: 'Salesforce',\n description: 'Sync responses to Salesforce CRM',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n integration_slack: {\n name: 'Slack',\n description: 'Send notifications to Slack channels',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n integration_shopify: {\n name: 'Shopify',\n description: 'Product recommendations from quiz results with direct checkout',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n integration_google_sheets: {\n name: 'Google Sheets',\n description: 'Sync form responses to a Google Sheet, one row per submission',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n notifications: {\n name: 'Response notifications',\n description: 'Email or webhook alerts when new responses arrive',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n import_typeform: {\n name: 'Typeform importer',\n description: 'Import forms from Typeform into Clipform',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n import_tally: {\n name: 'Tally importer',\n description: 'Import forms from Tally into Clipform',\n enabled: false,\n status: 'planned',\n category: 'integrations',\n },\n};\n\n// `enabled` (runtime gate) and `status` (lifecycle) are two independent axes -\n// but two pairings are incoherent and almost always a dual-write slip:\n// - status 'live' with enabled:false -> shipped-but-hidden (marketing claims it, no one can use it)\n// - status 'planned' with enabled:true -> advertised as \"coming soon\" but actually exposed\n// 'beta' is intentionally free: enabled:true is a public beta, enabled:false a\n// dark beta (e.g. embed_slots). Catch the two bad pairings at module load so a\n// typo can't ship.\nfor (const [key, f] of Object.entries(FEATURES)) {\n if (f.status === 'live' && !f.enabled) {\n throw new Error(`FEATURES.${key}: status 'live' requires enabled:true (shipped features must be exposed)`);\n }\n if (f.status === 'planned' && f.enabled) {\n throw new Error(`FEATURES.${key}: status 'planned' requires enabled:false (planned features must not be exposed)`);\n }\n // wentLiveAt is the dated go-live event (see header). Only a shipped feature has\n // one, and it must be a real ISO date - a free-text slip would silently corrupt\n // the changelog sort order, so fail at load instead.\n if (f.wentLiveAt !== undefined) {\n if (f.status === 'planned') {\n throw new Error(`FEATURES.${key}: wentLiveAt set but status is 'planned' (only shipped features have a go-live date)`);\n }\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(f.wentLiveAt)) {\n throw new Error(`FEATURES.${key}: wentLiveAt must be 'YYYY-MM-DD' (got ${JSON.stringify(f.wentLiveAt)})`);\n }\n }\n}\n\n/**\n * Backwards-compatible FEATURE_FLAGS object.\n * Maps the old flag names to FEATURES[key].enabled so existing dashboard code\n * keeps working without changes. Migrate consumers to FEATURES.x.enabled over time.\n */\nexport const FEATURE_FLAGS = {\n get ENABLE_BRANCHING() { return FEATURES.branching.enabled; },\n get ENABLE_CUSTOM_SCORES() { return FEATURES.custom_scores.enabled; },\n get ENABLE_TEXT_RESPONSE() { return FEATURES.text_response.enabled; },\n get ENABLE_MULTI_SELECT() { return FEATURES.multi_select.enabled; },\n get INLINE_EMBED() { return FEATURES.embed_inline.enabled; },\n get WIDGET_EMBED() { return FEATURES.embed_widget.enabled; },\n get WEBHOOKS() { return FEATURES.webhooks.enabled; },\n get INTEGRATIONS() { return Object.entries(FEATURES).some(([k, f]) => k.startsWith('integration_') && f.enabled); },\n get CUSTOM_FONTS() { return FEATURES.custom_fonts.enabled; },\n get TRACKED_LINKS() { return FEATURES.tracked_links.enabled; },\n get SHARE_NODE() { return FEATURES.share_node.enabled; },\n get BRAND_NAME() { return FEATURES.brand_name.enabled; },\n get CSV_EXPORT() { return FEATURES.csv_export.enabled; },\n get ALLOW_RESUME() { return FEATURES.allow_resume.enabled; },\n get UTM_ANALYTICS() { return FEATURES.utm_tracking.enabled; },\n get API_KEYS() { return FEATURES.api_keys.enabled; },\n get NOTIFICATIONS() { return FEATURES.notifications.enabled; },\n get IMPORT_TYPEFORM() { return FEATURES.import_typeform.enabled; },\n get IMPORT_TALLY() { return FEATURES.import_tally.enabled; },\n};\n\n/**\n * Per-integration limited-rollout allowlists - one entry per gated integration.\n * While an\n * integration's FEATURES flag is off, these companies get early access to test\n * it in live. Each integration switches on independently: flip its flag for\n * global GA, or allowlist a company to dogfood ahead of that. Dev + prod company\n * ids differ (separate Supabase projects).\n */\nexport const INTEGRATION_COMPANY_ALLOWLIST = {\n // integration_zapier + integration_make + integration_n8n removed - all\n // globally live (FEATURES.integration_*.enabled), so companyHasIntegration\n // short-circuits true for everyone; no allowlist needed. (n8n went live with\n // the published Clipform Trigger node, #976.)\n // Stripe payments dogfood (#node_payment): connect a real Stripe account in\n // live ahead of the global flip. Pairs with NODE_COMPANY_ALLOWLIST.node_payment\n // so the same companies can both build the node and wire up an account.\n integration_stripe: [\n '51aef9d0-b26d-4f17-bf6f-583dde728ac2', // prod - Andy's Company\n '35c1dccb-22b8-43a6-9181-28987e63ae48', // dev - andy@clipform.io's Company\n '00000000-0000-4000-b000-000000000001', // Clipform Public (system company; same id dev + prod)\n ],\n};\n\n/**\n * Per-node-type limited-rollout allowlist - same pattern as\n * INTEGRATION_COMPANY_ALLOWLIST, one entry per gated node type (keyed by the\n * FEATURES key, e.g. 'node_payment'). While a node type's FEATURES flag is off,\n * these companies can build with it in live to dogfood ahead of the global flip.\n * Dev + prod company ids differ (separate Supabase projects).\n */\nexport const NODE_COMPANY_ALLOWLIST = {\n node_payment: [\n '51aef9d0-b26d-4f17-bf6f-583dde728ac2', // prod - Andy's Company\n '35c1dccb-22b8-43a6-9181-28987e63ae48', // dev - andy@clipform.io's Company\n '00000000-0000-4000-b000-000000000001', // Clipform Public (system company; same id dev + prod)\n ],\n // Camera node dogfood (#2097): build with it in live ahead of the global flip.\n // Creating a camera node also needs the node_type_check constraint (parked\n // migration PR #2103) - applied on dev already, prod at the next deploy.\n node_camera: [\n '51aef9d0-b26d-4f17-bf6f-583dde728ac2', // prod - Andy's Company\n '35c1dccb-22b8-43a6-9181-28987e63ae48', // dev - andy@clipform.io's Company\n '00000000-0000-4000-b000-000000000001', // Clipform Public (system company; same id dev + prod)\n ],\n // Draw node pull-back (#2289): went GA 2026-06-27 (#1012), pulled back behind\n // the allowlist while pen options / match-to-shape mature. Zero external\n // customers were using it at pull-back (Clipform Public only), so this list\n // also carries the e2e auth user and a demo company so existing specs/forms\n // keep working.\n node_draw: [\n '51aef9d0-b26d-4f17-bf6f-583dde728ac2', // prod - Andy's Company\n '35c1dccb-22b8-43a6-9181-28987e63ae48', // dev - andy@clipform.io's Company\n '00000000-0000-4000-b000-000000000001', // Clipform Public (system company; same id dev + prod)\n 'd9808c42-05ee-4830-9634-f0b1496bf484', // dev - andycsmith313@gmail.com's Company (e2e auth user; draw e2e specs create draw nodes)\n '9dcb32a1-b52f-4873-a099-ed44abd4959f', // dev - Clipform Zapier (demo company with a draw demo form)\n ],\n};\n\n/**\n * General-purpose limited-rollout allowlist for a FEATURES-gated capability\n * that isn't a node type or an integration (same pattern as\n * NODE_COMPANY_ALLOWLIST / INTEGRATION_COMPANY_ALLOWLIST, keyed by the\n * FEATURES key). Dev + prod company ids differ (separate Supabase projects).\n */\nexport const FEATURE_COMPANY_ALLOWLIST = {\n // Public results portal dogfood (#2129): build with it in live ahead of the\n // global flip. Server-side gate only (apps/api) - see companyHasPublicResults.\n public_results: [\n '51aef9d0-b26d-4f17-bf6f-583dde728ac2', // prod - Andy's Company\n '35c1dccb-22b8-43a6-9181-28987e63ae48', // dev - andy@clipform.io's Company\n '00000000-0000-4000-b000-000000000001', // Clipform Public (system company; same id dev + prod)\n ],\n};\n\n/**\n * Shared resolver for the flag-or-allowlist rollout pattern: a capability is\n * available once its global FEATURES flag is on, or while the company sits on\n * the capability's limited-rollout allowlist. Single source of truth for\n * companyHasIntegration / companyHasNodeType.\n */\nfunction gatedByFlagOrAllowlist(enabled, companyId, allowlist) {\n return enabled || (!!companyId && (allowlist || []).includes(companyId));\n}\n\n/**\n * Whether a company can use an integration: true once its FEATURES flag is\n * globally live, or while the company sits on that integration's allowlist\n * above. Keyed by the FEATURES key (e.g. 'integration_zapier').\n */\nexport function companyHasIntegration(companyId, integrationKey) {\n return gatedByFlagOrAllowlist(\n !!FEATURES[integrationKey]?.enabled,\n companyId,\n INTEGRATION_COMPANY_ALLOWLIST[integrationKey],\n );\n}\n\n/**\n * Whether a company can see the Integrations surface at all: true if ANY\n * integration is available to it (globally live or allowlisted). Gates the\n * Integrations nav tab - the bare\n * FEATURE_FLAGS.INTEGRATIONS getter ignores the per-integration allowlists, so\n * allowlisted companies would never see the tab while every flag is off.\n */\nexport function companyHasAnyIntegration(companyId) {\n return Object.keys(FEATURES)\n .filter((k) => k.startsWith('integration_'))\n .some((k) => companyHasIntegration(companyId, k));\n}\n\n/**\n * Whether a company can build with a node type: true once its FEATURES flag is\n * globally live, or while the company sits on that node type's allowlist above.\n * Takes the node type key (e.g. 'payment'); maps to the FEATURES key\n * `node_<type>`, the same mapping node-types.js uses to derive is_active.\n */\nexport function companyHasNodeType(companyId, nodeType) {\n const featureKey = `node_${nodeType}`;\n return gatedByFlagOrAllowlist(\n !!FEATURES[featureKey]?.enabled,\n companyId,\n NODE_COMPANY_ALLOWLIST[featureKey],\n );\n}\n\n/**\n * Whether a company can turn on the public results portal: true once the\n * FEATURES flag is globally live, or while the company sits on the\n * public_results allowlist above. Server-side gate only - see apps/api's\n * update-form / rotate-results-share / get-public-results routes.\n */\nexport function companyHasPublicResults(companyId) {\n return gatedByFlagOrAllowlist(\n !!FEATURES.public_results?.enabled,\n companyId,\n FEATURE_COMPANY_ALLOWLIST.public_results,\n );\n}\n\nexport function getLiveFeatures() {\n return Object.values(FEATURES).filter(f => f.status === 'live');\n}\n\nexport function getPlannedFeatures() {\n return Object.values(FEATURES).filter(f => f.status === 'planned');\n}\n\nexport function getFeaturesByCategory(category) {\n return Object.values(FEATURES).filter(f => f.category === category);\n}\n\n/**\n * The What's New timeline: live features with a known go-live date, newest first.\n * The single machine-readable source for the user-facing changelog and the SEO/GTM\n * \"what shipped recently\" signal. Returns [key, feature] entries so consumers can\n * derive links/anchors from the key. Features live since the registry baseline have\n * no wentLiveAt and are intentionally excluded - a dated changelog never invents a date.\n */\nexport function getWhatsNew() {\n return Object.entries(FEATURES)\n .filter(([, f]) => f.status === 'live' && f.wentLiveAt)\n .sort(([, a], [, b]) => b.wentLiveAt.localeCompare(a.wentLiveAt));\n}\n","// @vid-master/config/node-types\n// Static node type definitions — single source of truth for all apps.\n// New node types require code changes (new component, renderer, etc.),\n// so this data is version-controlled rather than stored in the database.\n\nimport { FEATURES, companyHasNodeType } from './features.js';\n\n// Canonical answer-format and consent enums (single source of truth). The node\n// schemas below reference these, and all apps import them, so they cannot drift.\nexport const RESPONSE_FORMATS = ['text', 'audio', 'video'];\nexport const CONSENT_TYPES = ['consent', 'opt_in'];\nexport const CONTACT_FIELD_TYPES = ['text', 'textarea', 'email', 'tel', 'url', 'date', 'number'];\n\n// Opinionated default tier bands for the draw node's optional match-to-shape\n// game (#2148) - a builder-authored target scores the respondent's drawing on\n// submit. Shipped as the config default (`config_schema.properties.game.\n// properties.tiers.default` below) so a node with a target but no custom tiers\n// still gets a sensible 5-band scale; the viewer's scorer (apps/viewer/lib/\n// matchShape.ts) falls back to this same list when `config.game.tiers` is\n// absent. No tier-editing UI in v1 (see the issue's Future section).\nexport const DRAW_GAME_DEFAULT_TIERS = [\n { min_score: 0, label: 'Scribble' },\n { min_score: 35, label: 'Warming up' },\n { min_score: 55, label: 'Nice try' },\n { min_score: 75, label: 'Great match' },\n { min_score: 92, label: 'Perfect' },\n];\n\nexport const NODE_TYPES = {\n start: {\n label: 'Start',\n shorthand: 'Start',\n icon: 'ArrowRight',\n color: '#D1FAE5',\n description: 'Entry point for the form - connects to the first node',\n category: 'start',\n sort_order: 0,\n has_options: false,\n is_terminal: false,\n show_nav_bar: false,\n loading: 'eager',\n supports_prompt: false,\n supports_media: false,\n is_system: true,\n show_in_results: false,\n default_config: null,\n config_schema: null,\n response_schema: null,\n output_schema: null,\n },\n choice: {\n label: 'Multiple Choice',\n shorthand: 'Multi',\n icon: 'CheckSquare',\n color: '#DBEAFE',\n description: 'Single or multiple choice node with predefined options',\n category: 'input',\n sort_order: 1,\n has_options: true,\n min_options: 1,\n max_options: 6,\n max_option_length: 36,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'eager',\n supports_prompt: true,\n supports_media: true,\n is_system: false,\n show_in_results: true,\n default_config: {\n selection_mode: 'single',\n choice: { enable_branching: false, record_scores: false },\n randomise_options: false,\n show_option_count: false,\n option_display: 'list',\n options: [\n { content: 'Option 1' },\n { content: 'Option 2' },\n ],\n },\n config_schema: {\n type: 'object',\n properties: {\n choice: {\n type: 'object',\n properties: {\n enable_branching: {\n type: 'boolean',\n label: 'Enable branching logic',\n default: true,\n description: 'Allow each option to have its own logic path. When disabled, all options share a single jump action.',\n },\n show_answer_feedback: {\n type: 'boolean',\n label: 'Show answer feedback',\n default: false,\n description: 'Show correct/incorrect feedback after selection (requires scored options).',\n },\n record_scores: {\n type: 'boolean',\n label: 'Mark correct answer',\n default: false,\n description: 'Select which option is the correct answer.',\n },\n },\n },\n selection_mode: {\n enum: ['single', 'multiple'],\n type: 'string',\n label: 'Selection mode',\n default: 'single',\n description: 'Allow single or multiple selections. \"multiple\" is gated by the multi_select feature - while it is off, the API coerces it to \"single\" (the viewer renders single-select only).',\n },\n allow_text_response: {\n type: 'boolean',\n label: 'Allow text response',\n default: false,\n description: 'Allow free-text response in addition to options (single choice only). Gated by the text_response feature - while it is off, the API coerces it to false (the viewer renders no free-text field yet).',\n },\n randomise_options: {\n type: 'boolean',\n label: 'Randomise options',\n default: false,\n description: 'Show options in random order',\n },\n show_option_count: {\n type: 'boolean',\n label: 'Show option count',\n default: false,\n description: 'Display number of options',\n },\n option_display: {\n enum: ['list', 'letters'],\n type: 'string',\n label: 'Option display',\n default: 'list',\n description: 'How options render: full text rows (list), or compact A/B/C/D chips (letters) when the media already shows the labelled options - e.g. a Grid background',\n },\n content_media_type: {\n enum: ['upload', 'recorded'],\n type: 'string',\n label: 'Content media type',\n description: 'How the node media was provided',\n },\n },\n },\n response_schema: {\n type: 'string',\n description: 'Selected option ID',\n },\n output_schema: {\n type: 'object',\n required: ['node_type', 'label', 'option_id'],\n properties: {\n node_type: { type: 'string', const: 'choice' },\n label: { type: 'string', description: 'Human-readable option label' },\n option_id: { type: 'string', format: 'uuid', description: 'Selected option UUID' },\n },\n },\n },\n open: {\n label: 'Open Ended',\n shorthand: 'Open',\n icon: 'Type',\n color: '#DBEAFE',\n description: 'Free-form text responses from users',\n category: 'input',\n sort_order: 2,\n has_options: false,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'eager',\n supports_prompt: true,\n supports_media: true,\n is_system: false,\n show_in_results: true,\n default_config: {\n formats: [\n { order: 0, format: 'text' },\n { order: 1, format: 'audio' },\n { order: 2, format: 'video' },\n ],\n },\n config_schema: {\n type: 'object',\n properties: {\n formats: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n format: { enum: RESPONSE_FORMATS, type: 'string' },\n order: { type: 'number' },\n },\n },\n label: 'Allowed response formats',\n description: 'Which formats the user can respond with, in display order',\n },\n content_media_type: {\n enum: ['upload', 'recorded'],\n type: 'string',\n label: 'Content media type',\n description: 'How the node media was provided',\n },\n },\n },\n response_schema: {\n type: 'object',\n required: ['response_type'],\n properties: {\n text: {\n type: 'string',\n description: 'Text response (when response_type is text) or transcribed text from audio/video',\n },\n media: {\n type: 'object',\n properties: {\n type: { enum: ['audio', 'video'], type: 'string' },\n storage_path: { type: 'string' },\n facing_mode: { enum: ['user', 'environment'], type: 'string' },\n preview_crop: {\n type: 'object',\n properties: {\n stream_width: { type: 'number' },\n stream_height: { type: 'number' },\n preview_width: { type: 'number' },\n preview_height: { type: 'number' },\n },\n },\n },\n description: 'Media response (when response_type is audio or video)',\n },\n response_type: {\n enum: RESPONSE_FORMATS,\n type: 'string',\n },\n transcription: {\n type: 'object',\n properties: {\n text: { type: 'string' },\n language: { type: 'string' },\n duration: { type: 'number' },\n words: { type: 'array' },\n },\n description: 'Whisper transcription data for audio/video responses',\n },\n },\n },\n output_schema: {\n type: 'object',\n required: ['node_type'],\n properties: {\n node_type: { type: 'string', const: 'open' },\n text: { type: 'string', description: 'Text response or transcription' },\n media: {\n type: 'object',\n properties: {\n type: { enum: ['audio', 'video'], type: 'string' },\n content_type: { type: 'string', description: 'MIME type of the recording (e.g. audio/webm, video/mp4)' },\n filename: { type: 'string', description: 'Suggested download filename' },\n // Direct, time-limited signed URL to the recording (~48h, supports\n // HTTP range); minted per delivery, never the internal storage path.\n url: { type: 'string', format: 'uri' },\n download_url: { type: 'string', format: 'uri', description: 'Same link with a forced download (Content-Disposition: attachment)' },\n expires_at: { type: 'string', format: 'date-time', description: 'When url/download_url stop working' },\n },\n },\n transcription: {\n type: 'object',\n properties: {\n text: { type: 'string' },\n language: { type: 'string' },\n segments: {\n type: 'array',\n description: 'Timestamped segments (seconds) for captions/seeking.',\n items: {\n type: 'object',\n properties: {\n start: { type: 'number' },\n end: { type: 'number' },\n text: { type: 'string' },\n },\n },\n },\n },\n },\n },\n },\n },\n scale: {\n label: 'Scale',\n shorthand: 'Scale',\n icon: 'BarChart3',\n color: '#DBEAFE',\n description: 'Numerical rating or scale node (1-10, etc.)',\n category: 'input',\n sort_order: 3,\n has_options: false,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'eager',\n supports_prompt: true,\n supports_media: true,\n is_system: false,\n show_in_results: true,\n default_config: null,\n config_schema: {\n type: 'object',\n properties: {\n max: { type: 'number', label: 'Maximum value', default: 10, required: true },\n min: { type: 'number', label: 'Minimum value', default: 1, required: true },\n step: { min: 0.1, type: 'number', label: 'Step increment', default: 1 },\n left_label: { type: 'string', label: 'Left label', placeholder: 'Not likely' },\n right_label: { type: 'string', label: 'Right label', placeholder: 'Very likely' },\n show_numbers: { type: 'boolean', label: 'Show numbers', default: true },\n },\n },\n response_schema: {\n type: 'number',\n description: 'Selected scale value',\n },\n output_schema: null,\n },\n booking: {\n label: 'Booking',\n shorthand: 'Booking',\n icon: 'Calendar',\n color: '#E9D5FF',\n description: null,\n category: 'action',\n sort_order: 4,\n has_options: false,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'lazy',\n supports_prompt: false,\n supports_media: false,\n is_system: false,\n show_in_results: true,\n default_config: null,\n config_schema: {\n type: 'object',\n oneOf: [\n {\n required: ['calendly'],\n properties: {\n calendly: {\n type: 'object',\n properties: {\n event_name: { type: 'string', label: 'Event name', description: 'Display name for the event' },\n event_type_uri: { type: 'string', label: 'Event type URI', format: 'uri', description: 'Your Calendly event type URI', placeholder: 'https://calendly.com/your-link' },\n },\n },\n },\n },\n {\n required: ['google_meet'],\n properties: {\n google_meet: {\n type: 'object',\n properties: {\n duration: { type: 'number', label: 'Duration (minutes)', default: 30, description: 'Meeting duration in minutes' },\n calendar_id: { type: 'string', label: 'Calendar', description: 'Google Calendar to use for bookings' },\n event_title: { type: 'string', label: 'Event title', default: 'Meeting', description: 'Default title for scheduled meetings' },\n calendar_name: { type: 'string', label: 'Calendar name', description: 'Display name for the selected calendar' },\n },\n },\n },\n },\n ],\n description: 'Booking provider configuration (one provider per node)',\n },\n response_schema: {\n type: 'object',\n oneOf: [\n {\n required: ['calendly'],\n properties: {\n calendly: {\n type: 'object',\n required: ['event_uri', 'scheduled_time'],\n properties: {\n event_uri: { type: 'string', format: 'uri' },\n event_type: { type: 'string' },\n invitee_uri: { type: 'string', format: 'uri' },\n reschedule_url: { type: 'string', format: 'uri' },\n scheduled_time: { type: 'string', format: 'date-time' },\n cancellation_url: { type: 'string', format: 'uri' },\n },\n },\n },\n },\n ],\n description: 'Booking response with provider-specific data',\n },\n output_schema: null,\n },\n details: {\n label: 'Details',\n shorthand: 'Details',\n icon: 'User',\n color: '#CFFAFE',\n description: 'Collect several fields on one screen - name, email, phone, address, date, and more',\n category: 'input',\n sort_order: 5,\n has_options: false,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'lazy',\n supports_prompt: false,\n supports_media: false,\n is_system: false,\n show_in_results: false,\n // Excluded from the aggregate Results charts (a list of emails can't be\n // charted) but DOES show in the per-response Responses table, where each\n // lead's contact fields are actionable (#746).\n show_in_responses: true,\n default_config: {\n fields: [\n { id: 'first_name', type: 'first_name', label: 'First Name', enabled: true, required: true },\n { id: 'email', type: 'email', label: 'Email', enabled: true, required: true },\n ],\n // No agreement out of the box - the builder adds one via \"Add agreement\"\n // when they actually need consent, so a fresh node ships no orphan checkbox. (#1378)\n consent_items: [],\n },\n config_schema: {\n type: 'object',\n required: ['fields'],\n properties: {\n title: { type: 'string' },\n fields: {\n type: 'array',\n minItems: 1,\n items: {\n type: 'object',\n required: ['id', 'required'],\n properties: {\n id: { type: 'string' },\n type: { enum: CONTACT_FIELD_TYPES, type: 'string' },\n label: { type: 'string' },\n order: { type: 'number' },\n required: { type: 'boolean', default: true },\n is_custom: { type: 'boolean', default: false },\n },\n },\n },\n description: { type: 'string' },\n consent_items: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n name: { type: 'string' },\n label: { type: 'string' },\n order: { type: 'number' },\n type: { type: 'string', enum: CONSENT_TYPES },\n },\n },\n },\n },\n },\n response_schema: {\n type: 'object',\n required: ['fields'],\n properties: {\n fields: {\n type: 'array',\n items: {\n type: 'object',\n required: ['id', 'type', 'label', 'value'],\n properties: {\n id: { type: 'string' },\n type: { type: 'string' },\n label: { type: 'string' },\n value: { type: 'string' },\n },\n },\n description: 'Contact form field entries with metadata',\n },\n consent: {\n type: 'array',\n items: {\n type: 'object',\n required: ['id', 'name', 'label', 'accepted', 'type'],\n properties: {\n id: { type: 'string' },\n name: { type: 'string' },\n label: { type: 'string' },\n accepted: { type: 'boolean' },\n type: { type: 'string', enum: CONSENT_TYPES },\n },\n },\n description: 'Consent checkbox entries',\n },\n },\n },\n output_schema: {\n type: 'object',\n required: ['node_type', 'fields'],\n properties: {\n node_type: { type: 'string', const: 'details' },\n fields: {\n type: 'array',\n items: {\n type: 'object',\n required: ['type', 'value'],\n properties: {\n type: { type: 'string' },\n value: { type: 'string' },\n },\n },\n },\n },\n },\n },\n file_upload: {\n label: 'File Upload',\n shorthand: 'Upload',\n icon: 'Upload',\n color: '#CFFAFE',\n description: 'Collect file uploads from respondents',\n category: 'input',\n sort_order: 6,\n has_options: false,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'lazy',\n supports_prompt: false,\n supports_media: false,\n is_system: false,\n show_in_results: true,\n default_config: null,\n config_schema: {\n type: 'object',\n properties: {\n max_files: { max: 20, min: 1, type: 'number', label: 'Max files', default: 5, description: 'Maximum number of files respondents can upload' },\n max_file_size_mb: { max: 500, min: 1, type: 'number', label: 'Max file size (MB)', default: 50, description: 'Maximum size per file (1-500 MB)' },\n allowed_media_types: {\n type: 'array',\n items: { enum: ['images', 'videos', 'documents', 'all'], type: 'string' },\n label: 'Allowed media types',\n default: ['images', 'documents'],\n enumLabels: {\n all: 'All file types',\n images: 'Images (JPEG, PNG, GIF, WebP, HEIC)',\n videos: 'Videos (MP4, MOV, WebM, AVI)',\n documents: 'Documents (PDF, DOC, DOCX, XLS, XLSX, TXT, CSV)',\n },\n description: 'Select which types of files respondents can upload',\n },\n },\n },\n response_schema: {\n type: 'object',\n required: ['files'],\n properties: {\n files: {\n type: 'array',\n items: {\n type: 'object',\n required: ['id', 'name', 'storage_path', 'url', 'mime_type', 'size', 'uploaded_at'],\n properties: {\n id: { type: 'string', format: 'uuid', description: 'Unique file identifier' },\n url: { type: 'string', format: 'uri', description: 'Signed Supabase Storage URL for download' },\n name: { type: 'string', description: 'Original filename' },\n size: { type: 'integer', minimum: 1, description: 'File size in bytes' },\n mime_type: { type: 'string', description: 'File MIME type' },\n uploaded_at: { type: 'string', format: 'date-time', description: 'ISO timestamp of upload completion' },\n storage_path: { type: 'string', description: 'Full storage path: workspace_id/form_id/node_id/uuid.ext' },\n },\n },\n minItems: 1,\n description: 'Array of uploaded file metadata',\n },\n },\n },\n output_schema: {\n type: 'object',\n required: ['node_type'],\n properties: {\n node_type: { type: 'string', const: 'file_upload' },\n files: {\n type: 'array',\n description: 'The uploaded files, each a canonical media object with a fresh signed link.',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Unique file identifier' },\n filename: { type: 'string', description: 'Original filename' },\n content_type: { type: 'string', description: 'File MIME type' },\n size: { type: 'integer', description: 'File size in bytes' },\n uploaded_at: { type: 'string', format: 'date-time', description: 'ISO timestamp of upload completion' },\n url: { type: 'string', format: 'uri', description: 'Direct, time-limited signed link (~48h, supports HTTP range)' },\n download_url: { type: 'string', format: 'uri', description: 'Same link with a forced download (Content-Disposition: attachment)' },\n expires_at: { type: 'string', format: 'date-time', description: 'When url/download_url stop working' },\n },\n },\n },\n },\n },\n },\n draw: {\n label: 'Drawing',\n shorthand: 'Draw',\n icon: 'Pencil',\n color: '#FEF3C7',\n description: 'Drawing canvas; respondents draw and it is saved as a PNG image',\n category: 'input',\n sort_order: 6.5,\n has_options: false,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'lazy',\n supports_prompt: false,\n supports_media: true,\n is_system: false,\n show_in_results: true,\n default_config: {\n paper_color: '#FAF9F6',\n stroke_colors: ['#1F2937', '#EF4444', '#3B82F6', '#22C55E', '#FACC15'],\n stroke_widths: [3, 6, 12],\n background: { fit: 'contain', opacity: 1 },\n },\n config_schema: {\n type: 'object',\n properties: {\n paper_color: { type: 'string', label: 'Paper colour', default: '#FAF9F6', description: 'Canvas background colour (off-white paper by default)' },\n stroke_colors: {\n type: 'array',\n items: { type: 'string' },\n minItems: 1,\n maxItems: 8,\n label: 'Pen colours',\n default: ['#1F2937', '#EF4444', '#3B82F6', '#22C55E', '#FACC15'],\n description: 'Colour swatches the respondent can draw with. The first entry is the default selection; a single entry locks the pen to that colour and hides the picker.',\n },\n stroke_widths: {\n type: 'array',\n items: { type: 'number', minimum: 1, maximum: 60 },\n minItems: 1,\n maxItems: 5,\n label: 'Pen thicknesses',\n default: [3, 6, 12],\n description: 'Stroke widths (px) the respondent can choose. The middle entry is the default selection; a single entry locks the pen to that width and hides the picker.',\n },\n // The reference image itself is the node's attached media (media_id,\n // via the same media_asset/create-media.ts pipeline every other\n // supports_media node uses) - not a config field, to avoid a\n // duplicated asset ref. This only tunes its display/bake.\n background: {\n type: 'object',\n label: 'Background image',\n description: 'Display settings for the optional reference image behind the drawing canvas',\n properties: {\n fit: { type: 'string', enum: ['cover', 'contain'], default: 'contain', label: 'Fit', description: 'contain letterboxes the image, cover crops it to fill the canvas' },\n opacity: { type: 'number', minimum: 0, maximum: 1, default: 1, label: 'Opacity', description: 'Background image opacity, from 0 (invisible) to 1 (fully opaque)' },\n },\n },\n // Optional match-to-shape game (#2148). Absent entirely by default\n // (not in default_config) - a node with no `game.target` behaves\n // exactly as it did before this key existed. `target.points` is\n // authored once in the builder (a simplified sketch modal, see\n // apps/dashboard) and stored pre-normalized to a [0,1] unit box so the\n // viewer's scorer (apps/viewer/lib/matchShape.ts) and the reveal\n // overlay share the same coordinate frame without re-deriving it.\n game: {\n type: 'object',\n label: 'Match-to-shape game',\n description: 'Optional target shape the drawing is scored against on submit',\n properties: {\n target: {\n type: 'object',\n label: 'Target shape',\n description: 'The outline the respondent is scored against, drawn once by the form builder',\n properties: {\n points: {\n type: 'array',\n minItems: 2,\n maxItems: 500,\n items: {\n type: 'object',\n properties: {\n x: { type: 'number', minimum: 0, maximum: 1 },\n y: { type: 'number', minimum: 0, maximum: 1 },\n },\n },\n description: 'Closed polyline outline, normalized to a [0,1] unit box (aspect-preserving)',\n },\n },\n },\n tiers: {\n type: 'array',\n label: 'Score tiers',\n minItems: 1,\n maxItems: 10,\n items: {\n type: 'object',\n properties: {\n min_score: { type: 'number', minimum: 0, maximum: 100 },\n label: { type: 'string' },\n },\n },\n default: DRAW_GAME_DEFAULT_TIERS,\n description: 'Score bands shown in the reveal, ascending by min_score - defaults to a 5-band Scribble to Perfect scale',\n },\n },\n },\n },\n },\n response_schema: {\n // Mirrors the live `open` node's media response shape (the only complete\n // media path): the sketch image is uploaded to the `media-responses`\n // bucket via the same upload-client pipeline and referenced by\n // storage_path. Exported as PNG (crisp line art); the off-white paper\n // background is composited in before export so it is baked into the file.\n type: 'object',\n required: ['media'],\n properties: {\n media: {\n type: 'object',\n required: ['storage_path', 'type'],\n properties: {\n type: { enum: ['image'], type: 'string', description: 'Always \"image\" for a sketch' },\n storage_path: { type: 'string', description: 'Path in the media-responses bucket: bucket/workspace_id/.../uuid.png' },\n mime_type: { type: 'string', description: 'Image MIME type (image/png)' },\n },\n description: 'The exported sketch, stored as a single image in media-responses',\n },\n // Present only when the node has a configured game.target - absent for\n // every draw node published before #2148 and for one with no target.\n game: {\n type: 'object',\n description: 'Present when the node has a configured target shape - the respondent\\'s match-to-shape score',\n properties: {\n score: { type: 'number', minimum: 0, maximum: 100, description: '0-100 match score against the target outline' },\n avg_deviation: { type: 'number', minimum: 0, description: 'Average normalized deviation from the target outline (lower is better)' },\n tier: { type: 'string', description: 'The configured tier label the score falls into' },\n },\n },\n },\n },\n output_schema: {\n type: 'object',\n required: ['node_type'],\n properties: {\n node_type: { type: 'string', const: 'draw' },\n media: {\n type: 'object',\n properties: {\n type: { enum: ['image'], type: 'string' },\n content_type: { type: 'string', description: 'Image MIME type (e.g. image/png)' },\n filename: { type: 'string', description: 'Suggested download filename' },\n url: { type: 'string', format: 'uri', description: 'Direct, time-limited signed link to the drawing (~48h, supports HTTP range)' },\n download_url: { type: 'string', format: 'uri', description: 'Same link with a forced download (Content-Disposition: attachment)' },\n expires_at: { type: 'string', format: 'date-time', description: 'When url/download_url stop working' },\n },\n },\n // Mirrors response_schema.game (piece 1 of #2148) - transformResponse\n // passes it through unchanged, so this describes the FINAL emitted shape.\n game: {\n type: 'object',\n description: 'Present when the node has a configured target shape - the respondent\\'s match-to-shape score',\n properties: {\n score: { type: 'number', minimum: 0, maximum: 100, description: '0-100 match score against the target outline' },\n avg_deviation: { type: 'number', minimum: 0, description: 'Average normalized deviation from the target outline (lower is better)' },\n tier: { type: 'string', description: 'The configured tier label the score falls into' },\n },\n },\n },\n },\n },\n camera: {\n label: 'Camera',\n shorthand: 'Camera',\n icon: 'Camera',\n color: '#FCE7F3',\n description: 'Capture a photo from the device camera',\n category: 'input',\n sort_order: 6.6,\n has_options: false,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'lazy',\n supports_prompt: false,\n supports_media: false,\n is_system: false,\n show_in_results: true,\n default_config: null,\n config_schema: {\n type: 'object',\n properties: {\n camera_facing: {\n type: 'string',\n enum: ['user', 'environment'],\n default: 'user',\n label: 'Default camera',\n description: 'Which camera the device opens by default (a hint only - the respondent can still switch)',\n enumLabels: { user: 'Front (selfie)', environment: 'Rear' },\n },\n allow_library_upload: {\n type: 'boolean',\n default: true,\n label: 'Allow choosing from photo library',\n description: 'Show a secondary option to pick an existing photo instead of taking a new one',\n },\n },\n },\n // Reuses file_upload's response shape verbatim (single-item array) so\n // Results/exports/webhooks treat it the same way (scaffold skill: shape reuse).\n response_schema: {\n type: 'object',\n required: ['files'],\n properties: {\n files: {\n type: 'array',\n items: {\n type: 'object',\n required: ['id', 'name', 'storage_path', 'url', 'mime_type', 'size', 'uploaded_at'],\n properties: {\n id: { type: 'string', format: 'uuid', description: 'Unique file identifier' },\n url: { type: 'string', format: 'uri', description: 'Signed Supabase Storage URL for download' },\n name: { type: 'string', description: 'Original filename' },\n size: { type: 'integer', minimum: 1, description: 'File size in bytes' },\n mime_type: { type: 'string', description: 'File MIME type' },\n uploaded_at: { type: 'string', format: 'date-time', description: 'ISO timestamp of upload completion' },\n storage_path: { type: 'string', description: 'Full storage path: workspace_id/form_id/node_id/uuid.ext' },\n },\n },\n minItems: 1,\n maxItems: 1,\n description: 'The captured photo (single-item array, mirroring file_upload)',\n },\n },\n },\n output_schema: {\n type: 'object',\n required: ['node_type'],\n properties: {\n node_type: { type: 'string', const: 'camera' },\n files: {\n type: 'array',\n description: 'The captured photo, as a canonical media object with a fresh signed link.',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Unique file identifier' },\n filename: { type: 'string', description: 'Original filename' },\n content_type: { type: 'string', description: 'File MIME type' },\n size: { type: 'integer', description: 'File size in bytes' },\n uploaded_at: { type: 'string', format: 'date-time', description: 'ISO timestamp of upload completion' },\n url: { type: 'string', format: 'uri', description: 'Direct, time-limited signed link (~48h, supports HTTP range)' },\n download_url: { type: 'string', format: 'uri', description: 'Same link with a forced download (Content-Disposition: attachment)' },\n expires_at: { type: 'string', format: 'date-time', description: 'When url/download_url stop working' },\n },\n },\n },\n },\n },\n },\n payment: {\n label: 'Payment',\n shorthand: 'Payment',\n icon: 'DollarSign',\n color: '#E9D5FF',\n description: null,\n category: 'action',\n sort_order: 7,\n has_options: false,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'lazy',\n supports_prompt: false,\n supports_media: false,\n is_system: false,\n show_in_results: true,\n // Config keys that reference a workspace-scoped connected account (Stripe/etc\n // via workspace_integrations). The SoT for save_form to strip on cross-workspace\n // copy (#2064 part 2 - migration, not yet wired). Standing convention for any\n // future OAuth/connected-account node (booking, shopify), not a payment special\n // case - the publish gate's own showcase waiver (graph-validation.js) still\n // names workspace_integration_id directly since it's the only per-type check today.\n connection_config_keys: ['workspace_integration_id'],\n default_config: { amount: null, currency: 'usd', provider: 'stripe' },\n config_schema: {\n type: 'object',\n required: ['amount', 'currency', 'provider'],\n properties: {\n amount: { type: 'number', label: 'Amount (in cents)', minimum: 50, required: true, description: 'Payment amount in cents (e.g., 5000 = $50.00)' },\n currency: { enum: ['usd', 'eur', 'gbp', 'cad', 'aud'], type: 'string', label: 'Currency', default: 'usd', required: true, description: 'Three-letter ISO currency code' },\n provider: { enum: ['stripe', 'paddle'], type: 'string', label: 'Payment Provider', default: 'stripe', required: true, description: 'Payment provider to use' },\n workspace_integration_id: { type: 'string', label: 'Workspace Integration ID', description: 'UUID of the workspace_integrations record for the connected payment account' },\n },\n description: 'Payment configuration with provider-agnostic flat structure',\n },\n response_schema: {\n type: 'object',\n oneOf: [\n {\n required: ['stripe'],\n properties: {\n stripe: {\n type: 'object',\n required: ['payment_intent_id', 'status', 'amount', 'currency'],\n properties: {\n name: { type: 'string', description: 'Customer name from Stripe billing details' },\n email: { type: 'string', format: 'email', description: 'Customer email from Stripe billing details' },\n amount: { type: 'number', description: 'Payment amount in cents' },\n status: { enum: ['pending', 'succeeded', 'failed'], type: 'string', description: 'Payment status' },\n currency: { type: 'string', description: 'Three-letter ISO currency code (e.g., usd, eur)' },\n payment_intent_id: { type: 'string', description: 'Stripe payment intent ID' },\n },\n },\n },\n },\n {\n required: ['square'],\n properties: {\n square: {\n type: 'object',\n properties: {\n amount: { type: 'number' },\n status: { type: 'string' },\n order_id: { type: 'string' },\n payment_id: { type: 'string' },\n },\n },\n },\n },\n {\n required: ['paddle'],\n properties: {\n paddle: {\n type: 'object',\n properties: {\n amount: { type: 'number' },\n status: { type: 'string' },\n order_id: { type: 'string' },\n checkout_id: { type: 'string' },\n },\n },\n },\n },\n ],\n description: 'Payment response with provider-specific data',\n },\n output_schema: null,\n },\n binary: {\n label: 'Binary',\n shorthand: 'A/B',\n icon: 'CircleCheck',\n color: '#DBEAFE',\n description: 'Two-option choice - yes/no, true/false, or this vs that',\n category: 'input',\n sort_order: 1.5,\n has_options: true,\n min_options: 2,\n max_options: 2,\n max_option_length: 12,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'eager',\n supports_prompt: true,\n supports_media: true,\n is_system: false,\n show_in_results: true,\n default_config: {\n choice: { enable_branching: false },\n options: [\n { content: 'Yes' },\n { content: 'No' },\n ],\n },\n config_schema: {\n type: 'object',\n properties: {\n choice: {\n type: 'object',\n properties: {\n enable_branching: {\n type: 'boolean',\n label: 'Enable branching logic',\n default: true,\n description: 'Allow each option to have its own logic path.',\n },\n },\n },\n content_media_type: {\n enum: ['upload', 'recorded'],\n type: 'string',\n label: 'Content media type',\n description: 'How the node media was provided',\n },\n },\n },\n response_schema: {\n type: 'string',\n description: 'Selected option ID',\n },\n output_schema: {\n type: 'object',\n required: ['node_type', 'label', 'option_id'],\n properties: {\n node_type: { type: 'string', const: 'binary' },\n label: { type: 'string', description: 'Human-readable option label' },\n option_id: { type: 'string', format: 'uuid', description: 'Selected option UUID' },\n },\n },\n },\n button: {\n label: 'Button',\n shorthand: 'Button',\n icon: 'RectangleHorizontal',\n color: '#DBEAFE',\n description: 'Simple button for acknowledgment or navigation',\n category: 'input',\n sort_order: 3,\n has_options: true,\n min_options: 1,\n max_options: 1,\n max_option_length: 28,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'eager',\n supports_prompt: true,\n supports_media: true,\n is_system: false,\n show_in_results: false,\n default_config: { options: [{ content: 'Continue' }] },\n config_schema: {\n type: 'object',\n properties: {\n button_text: { type: 'string', label: 'Button text', default: 'Continue' },\n button_style: { enum: ['primary', 'secondary', 'outline'], type: 'string', label: 'Button style', default: 'primary' },\n },\n },\n response_schema: {\n type: 'string',\n description: 'Selected option ID',\n },\n output_schema: {\n type: 'object',\n required: ['node_type', 'label', 'option_id'],\n properties: {\n node_type: { type: 'string', const: 'button' },\n label: { type: 'string', description: 'Human-readable option label' },\n option_id: { type: 'string', format: 'uuid', description: 'Selected option UUID' },\n },\n },\n },\n redirect: {\n label: 'Redirect',\n shorthand: 'Redirect',\n icon: 'ExternalLink',\n color: '#FFEDD5',\n description: 'Redirect users to an external URL',\n category: 'end',\n sort_order: 997,\n has_options: false,\n is_terminal: true,\n show_nav_bar: false,\n loading: 'lazy',\n supports_prompt: false,\n supports_media: false,\n is_system: false,\n show_in_results: false,\n default_config: { url: '', auto_redirect: true },\n config_schema: {\n type: 'object',\n properties: {\n url: { type: 'string', label: 'Redirect URL', placeholder: 'https://example.com' },\n auto_redirect: { type: 'boolean', label: 'Auto-redirect', default: true, description: 'Automatically redirect to the URL instead of showing a button' },\n },\n },\n response_schema: {\n type: 'string',\n description: 'URL the user was redirected to',\n },\n output_schema: null,\n },\n link_list: {\n label: 'Link List',\n shorthand: 'Links',\n icon: 'ExternalLink',\n color: '#FFEDD5',\n description: 'Display a list of links for users to choose from',\n category: 'end',\n sort_order: 998,\n has_options: false,\n is_terminal: true,\n show_nav_bar: false,\n loading: 'lazy',\n supports_prompt: false,\n supports_media: false,\n is_system: false,\n show_in_results: false,\n default_config: { links: [{ id: 'default', url: '', title: '', description: '', order: 0 }], auto_redirect: false },\n config_schema: {\n type: 'object',\n properties: {\n links: {\n type: 'array',\n items: {\n type: 'object',\n required: ['id', 'url'],\n properties: {\n id: { type: 'string' },\n url: { type: 'string', label: 'URL', placeholder: 'https://example.com' },\n title: { type: 'string', label: 'Heading' },\n description: { type: 'string', label: 'Description' },\n order: { type: 'number', label: 'Sort order' },\n },\n },\n label: 'Links',\n minItems: 1,\n },\n auto_redirect: { type: 'boolean', label: 'Auto-redirect', default: false, description: 'Automatically redirect to the URL instead of showing a button' },\n },\n },\n response_schema: {\n type: 'string',\n description: 'URL of the link that was clicked',\n },\n output_schema: null,\n },\n file_download: {\n label: 'File Download',\n shorthand: 'Download',\n icon: 'Download',\n color: '#E9D5FF',\n description: 'Provide a file for respondents to download',\n category: 'action',\n sort_order: 9,\n has_options: false,\n is_terminal: false,\n show_nav_bar: true,\n loading: 'lazy',\n supports_prompt: false,\n supports_media: false,\n is_system: false,\n show_in_results: true,\n default_config: null,\n config_schema: {\n type: 'object',\n properties: {\n files: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n file_name: { type: 'string', label: 'Original file name', required: true },\n display_name: { type: 'string', label: 'Friendly name shown to respondents' },\n file_path: { type: 'string', label: 'File path in storage', required: true },\n file_size: { type: 'number', label: 'File size in bytes' },\n mime_type: { type: 'string', label: 'MIME type' },\n },\n },\n label: 'Downloadable files',\n },\n button_text: { type: 'string', label: 'Continue button text', default: 'Continue' },\n description: { type: 'string', label: 'Description text' },\n },\n },\n response_schema: {\n type: 'object',\n required: ['files'],\n properties: {\n files: {\n type: 'array',\n items: {\n type: 'object',\n required: ['file_path', 'file_name', 'downloaded_at'],\n properties: {\n file_name: { type: 'string', description: 'Display name of the file' },\n file_path: { type: 'string', description: 'Storage path of the downloaded file' },\n downloaded_at: { type: 'string', format: 'date-time', description: 'Timestamp when download was initiated' },\n },\n },\n description: 'List of files downloaded by respondent',\n },\n },\n },\n output_schema: {\n type: 'object',\n required: ['node_type'],\n properties: {\n node_type: { type: 'string', const: 'file_download' },\n files: {\n type: 'array',\n description: 'The files the respondent downloaded, each a canonical media object with a fresh signed link.',\n items: {\n type: 'object',\n properties: {\n filename: { type: 'string', description: 'Original filename' },\n content_type: { type: 'string', description: 'File MIME type' },\n downloaded_at: { type: 'string', format: 'date-time', description: 'Timestamp when download was initiated' },\n url: { type: 'string', format: 'uri', description: 'Direct, time-limited signed link (~48h, supports HTTP range)' },\n download_url: { type: 'string', format: 'uri', description: 'Same link with a forced download (Content-Disposition: attachment)' },\n expires_at: { type: 'string', format: 'date-time', description: 'When url/download_url stop working' },\n },\n },\n },\n },\n },\n },\n shopping: {\n label: 'Shopping',\n shorthand: 'Shop',\n icon: 'ShoppingBag',\n color: '#E9D5FF',\n description: 'Product recommendations with direct checkout',\n category: 'action',\n sort_order: 8,\n has_options: false,\n is_terminal: true,\n show_nav_bar: true,\n loading: 'lazy',\n supports_prompt: false,\n supports_media: false,\n is_system: false,\n show_in_results: false,\n default_config: { provider: 'shopify', cta_mode: 'checkout', source_mode: 'manual' },\n config_schema: {\n type: 'object',\n required: ['provider', 'cta_mode'],\n properties: {\n title: { type: 'string', label: 'Heading', description: 'Heading shown above the product list' },\n description: { type: 'string', label: 'Description', description: 'Body text shown below the heading' },\n provider: { enum: ['shopify'], type: 'string', label: 'Provider', default: 'shopify', description: 'Ecommerce provider' },\n workspace_integration_id: { type: 'string', label: 'Workspace Integration ID', description: 'UUID of the workspace_integrations record for the connected store' },\n cta_mode: { enum: ['checkout', 'add_to_cart'], type: 'string', label: 'CTA Mode', default: 'checkout', description: 'Whether to go to checkout or add to cart' },\n source_mode: { enum: ['manual', 'collection'], type: 'string', label: 'Product source', default: 'manual', description: 'How products are selected: manually picked or from a collection' },\n collection_id: { type: 'string', label: 'Collection ID', description: 'Shopify collection GID (when source_mode is collection)' },\n collection_title: { type: 'string', label: 'Collection title', description: 'Display name of the selected collection' },\n products: {\n type: 'array',\n label: 'Selected Products',\n description: 'Products to show (static mode)',\n items: {\n type: 'object',\n properties: {\n product_id: { type: 'string', description: 'Shopify product GID' },\n variant_id: { type: 'string', description: 'Shopify variant GID' },\n title: { type: 'string' },\n handle: { type: 'string' },\n image_url: { type: 'string' },\n price: { type: 'string' },\n currency: { type: 'string' },\n recommended: { type: 'boolean', default: true, description: 'Pre-ticked in viewer' },\n },\n },\n },\n product_mappings: {\n type: 'array',\n label: 'Score-based product mappings',\n description: 'Map score ranges to different product sets. First matching range wins.',\n items: {\n type: 'object',\n required: ['min', 'max'],\n properties: {\n min: { type: 'integer', label: 'Minimum score (inclusive)' },\n max: { type: 'integer', label: 'Maximum score (inclusive)' },\n message: { type: 'string', label: 'Message' },\n products: { type: 'array', description: 'Products for this score range' },\n },\n },\n },\n },\n description: 'Shopping configuration with provider-agnostic structure',\n },\n response_schema: null,\n output_schema: null,\n },\n end_screen: {\n label: 'Ending',\n shorthand: 'End',\n icon: 'Flag',\n color: '#FFEDD5',\n description: 'Final screen shown when form is completed',\n category: 'end',\n sort_order: 999,\n has_options: false,\n is_terminal: true,\n show_nav_bar: false,\n loading: 'eager',\n supports_prompt: false,\n supports_media: false,\n is_system: false,\n show_in_results: false,\n default_config: null,\n config_schema: {\n type: 'object',\n properties: {\n title: { type: 'string', label: 'Title', default: 'Thank you!', description: 'Heading shown on completion' },\n message: { type: 'string', label: 'Message', default: 'Your response has been submitted.', description: 'Message shown on completion' },\n show_score: { type: 'boolean', label: 'Show score', default: false, description: 'Display score on the end screen (e.g. \"You scored 4 out of 5\")' },\n icon: { type: 'string', label: 'Icon', enum: ['tick', 'trophy', 'star', 'crown', 'party', 'none'], default: 'tick', description: 'Icon shown above the title' },\n show_share_button: { type: 'boolean', label: 'Show share button', default: false, description: 'Show a share button above the CTA' },\n cta_type: { type: 'string', label: 'CTA type', enum: ['none', 'restart', 'external_link'], default: 'none', description: 'Primary call-to-action button type' },\n cta_text: { type: 'string', label: 'CTA button text', default: 'Continue', description: 'Button label for the CTA' },\n cta_url: { type: 'string', label: 'CTA URL', format: 'uri', description: 'URL to open (only for external_link CTA type)', placeholder: 'https://example.com' },\n score_ranges: {\n type: 'array',\n label: 'Score-based content',\n description: 'Show different content based on cumulative score. First matching range wins.',\n items: {\n type: 'object',\n required: ['min', 'max', 'title'],\n properties: {\n min: { type: 'integer', label: 'Minimum score (inclusive)' },\n max: { type: 'integer', label: 'Maximum score (inclusive)' },\n title: { type: 'string', label: 'Title' },\n message: { type: 'string', label: 'Message' },\n },\n },\n },\n scoring_results: {\n type: 'array',\n label: 'Category-based results',\n description: 'Results keyed by scoring category. The winning category (highest non-knocked-out score) determines which result is shown.',\n items: {\n type: 'object',\n required: ['category', 'title'],\n properties: {\n category: { type: 'string', label: 'Category key (must match keys used in option scores)' },\n title: { type: 'string', label: 'Title' },\n message: { type: 'string', label: 'Message' },\n cta_url: { type: 'string', label: 'CTA URL', format: 'uri' },\n cta_text: { type: 'string', label: 'CTA button text' },\n },\n },\n },\n },\n },\n response_schema: null,\n output_schema: null,\n },\n};\n\n// Availability is derived from the FEATURES registry (single source of truth).\n// Each node type `x` maps to feature `node_x`; the throw below makes a missing\n// or renamed feature fail loudly at import, and a vitest guards the mapping.\nfor (const [type, def] of Object.entries(NODE_TYPES)) {\n const feature = FEATURES[`node_${type}`];\n if (!feature) {\n throw new Error(`NODE_TYPES.${type}: no matching FEATURES[\"node_${type}\"] entry`);\n }\n def.is_active = feature.enabled;\n}\n\n// Derived exports\n\n/** Sorted list of all node types with their type key included */\nexport const NODE_TYPES_LIST = Object.entries(NODE_TYPES)\n .map(([type, def]) => ({ type, ...def }))\n .sort((a, b) => a.sort_order - b.sort_order);\n\n/**\n * Node types the dashboard treats as active, as full definitions - the single\n * owner of the derivation previously inlined in NodeTypesProvider. A type is\n * active when it is globally active OR the company is allowlisted to dogfood it\n * ahead of the global flip. Mirrors the old dashboard filter exactly: system\n * nodes (e.g. `start`, which is is_active) stay in, so getNodeType('start')\n * still resolves its colour - the node picker filters `is_system` separately.\n * Pass no companyId for the plain globally-active set.\n */\nexport function getActiveNodeTypes(companyId) {\n return NODE_TYPES_LIST.filter(\n t => t.is_active || companyHasNodeType(companyId, t.type)\n );\n}\n\n/**\n * Active, non-system node type KEYS - the user-addable set consumed by the API\n * and MCP server. Derived from Object.entries(NODE_TYPES) (definition order) so\n * the list is byte-identical to the derivations it replaces, including the order\n * that surfaces in z.enum / validation error messages.\n */\nexport const ACTIVE_NODE_TYPES = Object.entries(NODE_TYPES)\n .filter(([, def]) => def.is_active && !def.is_system)\n .map(([type]) => type);\n\n/** All valid node type keys */\nexport const NODE_TYPE_KEYS = Object.keys(NODE_TYPES);\n\n/**\n * Node types -> their connection_config_keys, for types that declare any.\n * The SoT for save_form (cross-workspace copy, #2064 part 2) to strip\n * workspace-scoped connected-account refs from a copied node's config -\n * derived like ACTIVE_NODE_TYPES so no consumer hand-maintains a duplicate list.\n */\nexport const CONNECTION_CONFIG_KEYS = Object.fromEntries(\n Object.entries(NODE_TYPES)\n .filter(([, def]) => Array.isArray(def.connection_config_keys) && def.connection_config_keys.length > 0)\n .map(([type, def]) => [type, def.connection_config_keys])\n);\n\n/** Response schemas keyed by node type */\nexport const RESPONSE_SCHEMAS = Object.fromEntries(\n Object.entries(NODE_TYPES).map(([k, v]) => [k, v.response_schema])\n);\n\n/** Config schemas keyed by node type */\nexport const CONFIG_SCHEMAS = Object.fromEntries(\n Object.entries(NODE_TYPES).map(([k, v]) => [k, v.config_schema])\n);\n\n/** Output schemas keyed by node type */\nexport const OUTPUT_SCHEMAS = Object.fromEntries(\n Object.entries(NODE_TYPES).map(([k, v]) => [k, v.output_schema])\n);\n\n/** Metadata subset used by viewer for layout decisions */\nexport const NODE_TYPE_METADATA = Object.fromEntries(\n Object.entries(NODE_TYPES).map(([k, v]) => [k, {\n supports_prompt: v.supports_prompt,\n supports_media: v.supports_media,\n is_terminal: v.is_terminal,\n show_nav_bar: v.show_nav_bar,\n category: v.category,\n loading: v.loading,\n }])\n);\n\n/** Terminal node type keys (is_terminal === true) */\nexport const TERMINAL_TYPES = Object.entries(NODE_TYPES)\n .filter(([, v]) => v.is_terminal)\n .map(([k]) => k);\n\n/** Node types excluded from counts (system + terminal) */\nexport const NON_COUNTABLE_TYPES = Object.entries(NODE_TYPES)\n .filter(([, v]) => v.is_system || v.is_terminal)\n .map(([k]) => k);\n\n/** Supabase .not() filter string for non-countable types */\nexport const NON_COUNTABLE_FILTER = `(${NON_COUNTABLE_TYPES.map(t => `\"${t}\"`).join(',')})`;\n\n/** Node types excluded from the aggregate Results charts */\nexport const RESULTS_EXCLUDED_TYPES = Object.entries(NODE_TYPES)\n .filter(([, v]) => !v.show_in_results)\n .map(([k]) => k);\n\n/**\n * Node types excluded from the PUBLIC results portal on top of\n * `RESULTS_EXCLUDED_TYPES` (#2129): contact details, file uploads, and payment\n * data never leave the owner-only Results tab, even when a form's results are\n * shared publicly. The `get_public_form_results` RPC already excludes these in\n * SQL - this list is the belt-and-braces filter the API route applies again on\n * the returned rows.\n */\nexport const PUBLIC_RESULTS_EXCLUDED_TYPES = ['details', 'file_upload', 'payment'];\n\n/**\n * Node types that count as a step in the respondent-facing step counter (#488).\n * Only answer-collecting nodes count - the respondent thinks in questions\n * (\"3 of 5\"), not screens. Pure transitions (`button`) and terminals/start are\n * nav-bar or non-nav nodes that advance neither the current step nor the total.\n */\nconst NON_STEP_TYPES = new Set(['button']);\nexport const STEP_COUNTED_TYPES = Object.entries(NODE_TYPES)\n .filter(([k, v]) => v.show_nav_bar && !NON_STEP_TYPES.has(k))\n .map(([k]) => k);\n\n/**\n * Node types excluded from the per-response Responses table.\n * Defaults to the Results exclusions; a type can opt back IN for responses\n * with `show_in_responses: true` (e.g. contact - aggregate-meaningless but\n * actionable per-lead, #746).\n */\nexport const RESPONSES_EXCLUDED_TYPES = Object.entries(NODE_TYPES)\n .filter(([, v]) => !(v.show_in_responses ?? v.show_in_results))\n .map(([k]) => k);\n\n/** Check if a node type is terminal */\nexport function isTerminalType(type) {\n return NODE_TYPE_METADATA[type]?.is_terminal === true;\n}\n\n/** Check if a node type counts as a step in the respondent step counter (#488) */\nexport function countsAsStep(type) {\n return STEP_COUNTED_TYPES.includes(type);\n}\n\n","// @vid-master/config/form-types\n// Static form type definitions — single source of truth for all apps.\n// Workflow prose (step-by-step instructions) and craft guides remain in the\n// MCP server. This registry defines structural metadata only.\n\nimport { FEATURES } from './features.js';\nimport { NODE_TYPES } from './node-types.js';\n\nexport const FORM_TYPES = {\n quiz: {\n label: 'Quiz',\n description: 'Scored knowledge quiz with narrated video questions',\n sort_order: 1,\n category: 'assessment',\n aliases: ['trivia', 'test', 'exam'],\n variants: ['personality', 'comprehension', 'composition'],\n required_features: [],\n required_node_types: ['choice', 'end_screen'],\n default_media_style: 'video',\n guide_uri: 'clipform://guides/quiz',\n guide_description: 'Craft knowledge for writing engaging quizzes - difficulty curves, question psychology, narration style, scoring',\n discovery: [\n { key: 'topic', question: 'What topic or subject area?', fallback: 'General trivia', mode: 'all' },\n { key: 'question_count', question: 'How many questions?', fallback: '8', mode: 'all' },\n { key: 'media_style', question: 'Video with narration (recommended), images, or text only?', fallback: 'Video with narration', mode: 'full' },\n ],\n variant_discovery: {\n personality: [\n { key: 'topic', question: \"What's the theme? (Which X are you?)\", fallback: 'General personality', mode: 'all' },\n { key: 'categories', question: 'What outcome categories? (3-5 results)', fallback: 'Infer 4 categories from the theme', mode: 'full' },\n { key: 'question_count', question: 'How many questions?', fallback: '8', mode: 'all' },\n ],\n comprehension: [\n { key: 'youtube_url', question: \"What's the YouTube URL?\", fallback: 'REQUIRED - cannot proceed without it', mode: 'all' },\n { key: 'question_count', question: 'How many questions?', fallback: '8', mode: 'all' },\n { key: 'audience', question: \"Who's the audience? (kids, teens, adults)\", fallback: 'General adults', mode: 'full' },\n ],\n composition: [\n { key: 'topic', question: 'What topic? (flags, movies, geography, music...)', fallback: 'General trivia', mode: 'all' },\n { key: 'question_count', question: 'How many questions? (composition quizzes use ~1 node per question, plus one more for any optional reveal beat - 5-6 is the sweet spot)', fallback: '5', mode: 'all' },\n ],\n },\n variant_guide_uris: {\n personality: 'clipform://guides/quiz/personality',\n comprehension: 'clipform://guides/quiz/comprehension',\n composition: 'clipform://guides/quiz/composition',\n },\n variant_descriptions: {\n personality: 'Addendum for personality quizzes - category design, option weighting, outcome writing, no right/wrong answers',\n comprehension: 'Addendum for YouTube comprehension quizzes - extracting questions from transcripts, distractor design, audience adaptation',\n composition: 'Addendum for composition quizzes - guess formats built from rendered compositions: mechanic selection, the clue clip + answer feedback default (with an optional reveal payoff node), difficulty design',\n },\n prompt: { name: 'create-quiz', title: 'Create a Quiz', description: 'Build a scored knowledge quiz with narrated video questions' },\n },\n\n survey: {\n label: 'Survey',\n description: 'Feedback survey, NPS form, or research questionnaire',\n sort_order: 2,\n category: 'feedback',\n aliases: ['feedback', 'poll', 'nps', 'questionnaire'],\n variants: [],\n required_features: [],\n required_node_types: ['choice', 'open', 'end_screen'],\n default_media_style: 'text',\n guide_uri: 'clipform://guides/survey',\n guide_description: 'Craft knowledge for feedback surveys, NPS, and research forms - brevity, rating scales, respondent fatigue',\n discovery: [\n { key: 'topic', question: 'What are you measuring? (satisfaction, NPS, feedback on what?)', fallback: 'General satisfaction', mode: 'all' },\n { key: 'anonymous', question: 'Anonymous or identified responses?', fallback: 'Anonymous', mode: 'full' },\n ],\n variant_discovery: {},\n variant_guide_uris: {},\n variant_descriptions: {},\n prompt: { name: 'create-survey', title: 'Create a Survey', description: 'Build a feedback survey, NPS form, or research questionnaire' },\n },\n\n interview: {\n label: 'Interview',\n description: 'Async video interview, case study, or expert input collection',\n sort_order: 3,\n category: 'collection',\n aliases: ['case-study', 'callout'],\n variants: [],\n required_features: [],\n required_node_types: ['open', 'details', 'end_screen'],\n default_media_style: 'video',\n guide_uri: 'clipform://guides/interview',\n guide_description: 'Craft knowledge for building interview forms - warm-up pacing, open questions, consent, video responses',\n discovery: [\n { key: 'purpose', question: 'What are you collecting? (case studies, feedback, expert input)', fallback: 'General responses', mode: 'all' },\n { key: 'response_format', question: 'How should people respond? (video, audio, text, all)', fallback: 'All formats', mode: 'full' },\n { key: 'needs_consent', question: 'Need a consent statement?', fallback: 'Yes', mode: 'full' },\n ],\n variant_discovery: {},\n variant_guide_uris: {},\n variant_descriptions: {},\n prompt: { name: 'create-interview', title: 'Create an Interview', description: 'Build an async video interview for collecting case studies, feedback, or expert input' },\n },\n\n funnel: {\n label: 'Funnel',\n description: 'Lead qualification funnel with conditional routing',\n sort_order: 4,\n category: 'qualification',\n aliases: ['lead-gen', 'qualification', 'lead-magnet'],\n variants: [],\n required_features: ['branching', 'funnel'],\n required_node_types: ['choice', 'details', 'end_screen'],\n default_media_style: 'text',\n guide_uri: 'clipform://guides/funnel',\n guide_description: 'Craft knowledge for lead qualification funnels - planned feature, conditional routing coming soon',\n discovery: [],\n variant_discovery: {},\n variant_guide_uris: {},\n variant_descriptions: {},\n prompt: { name: 'create-funnel', title: 'Create a Funnel', description: 'Lead qualification funnels with branching logic are planned but not yet available' },\n },\n\n testimonial: {\n label: 'Testimonial',\n description: 'Video testimonial and story collection form',\n sort_order: 5,\n category: 'collection',\n aliases: ['story', 'review'],\n variants: [],\n required_features: [],\n required_node_types: ['open', 'details', 'end_screen'],\n default_media_style: 'video',\n guide_uri: 'clipform://guides/testimonial',\n guide_description: 'Craft knowledge for collecting testimonials and customer stories on video - storytelling prompts, comfort techniques, consent',\n discovery: [\n { key: 'use_case', question: 'What stories are you collecting? (customer success, employee, partner)', fallback: 'Customer success stories', mode: 'all' },\n { key: 'media_style', question: 'Video responses (recommended), audio, or text?', fallback: 'Video', mode: 'full' },\n ],\n variant_discovery: {},\n variant_guide_uris: {},\n variant_descriptions: {},\n prompt: { name: 'create-testimonial', title: 'Collect Testimonials', description: 'Build a video testimonial collection form - capture authentic customer stories on camera' },\n },\n\n application: {\n label: 'Application',\n description: 'Structured application form for jobs, programmes, or grants',\n sort_order: 6,\n category: 'collection',\n aliases: ['job-application', 'admission', 'enrollment', 'grant'],\n variants: [],\n required_features: [],\n required_node_types: ['open', 'choice', 'details', 'end_screen'],\n default_media_style: 'video',\n guide_uri: 'clipform://guides/application',\n guide_description: 'Craft knowledge for application and evaluation forms - multi-section structure, video responses for behavioural questions, screening',\n discovery: [\n { key: 'role', question: 'What role or position?', fallback: 'General application', mode: 'all' },\n ],\n variant_discovery: {},\n variant_guide_uris: {},\n variant_descriptions: {},\n prompt: { name: 'create-application', title: 'Create an Application Form', description: 'Build a structured application form with video responses for behavioural questions' },\n },\n\n booking: {\n label: 'Booking',\n description: 'Event registration, course signup, or booking form',\n sort_order: 7,\n category: 'registration',\n aliases: ['registration', 'signup', 'event', 'rsvp', 'workshop'],\n variants: [],\n required_features: [],\n required_node_types: ['details', 'choice', 'end_screen'],\n default_media_style: 'text',\n guide_uri: 'clipform://guides/booking',\n guide_description: 'Craft knowledge for event registration and booking forms - minimal friction, video welcome, confirmation flow',\n discovery: [\n { key: 'event_name', question: \"What's the event name?\", fallback: 'Event registration', mode: 'all' },\n { key: 'event_type', question: 'What type? (workshop, webinar, meetup, conference)', fallback: 'Event', mode: 'full' },\n ],\n variant_discovery: {},\n variant_guide_uris: {},\n variant_descriptions: {},\n prompt: { name: 'create-booking', title: 'Create a Booking Form', description: 'Build an event registration or booking form with a personal video welcome' },\n },\n};\n\n// ---------------------------------------------------------------------------\n// Derived exports\n// ---------------------------------------------------------------------------\n\n// Availability is derived from dependencies (single source of truth): a form type\n// is active iff every required feature is enabled and every required node type is\n// active. No hand-set flag to drift - e.g. funnel auto-hides while branching is planned.\nfor (const def of Object.values(FORM_TYPES)) {\n const featuresLive = def.required_features.every((k) => FEATURES[k]?.enabled === true);\n const nodesLive = def.required_node_types.every((t) => NODE_TYPES[t]?.is_active === true);\n def.is_active = featuresLive && nodesLive;\n}\n\nexport const FORM_TYPES_LIST = Object.entries(FORM_TYPES)\n .map(([key, def]) => ({ key, ...def }))\n .sort((a, b) => a.sort_order - b.sort_order);\n\nexport const FORM_TYPE_KEYS = Object.keys(FORM_TYPES);\n\nexport const WORKFLOW_TYPES = FORM_TYPE_KEYS.filter(\n key => FORM_TYPES[key].is_active\n);\n\nexport const FORM_TYPE_ALIASES = Object.fromEntries(\n Object.entries(FORM_TYPES).flatMap(([key, def]) =>\n def.aliases.map(alias => [alias, key])\n )\n);\n\nexport const ALL_VARIANTS = Object.entries(FORM_TYPES).flatMap(\n ([, def]) => def.variants\n);\n\nexport function resolveFormType(input) {\n const normalised = input.toLowerCase().trim();\n if (FORM_TYPES[normalised]) return normalised;\n return FORM_TYPE_ALIASES[normalised] ?? null;\n}\n\nexport function getDiscoveryParams(formType, variant, mode = 'full') {\n const def = FORM_TYPES[formType];\n if (!def) return [];\n\n let params;\n if (variant && def.variant_discovery?.[variant]) {\n params = def.variant_discovery[variant];\n } else {\n params = def.discovery;\n }\n\n if (mode === 'demo') {\n return params.filter(p => p.mode === 'all');\n }\n return params;\n}\n\nexport function isFormTypeAvailable(formType, features) {\n const def = FORM_TYPES[formType];\n if (!def || !def.is_active) return false;\n return def.required_features.every(\n featureKey => features[featureKey]?.enabled === true\n );\n}\n\nexport function getAvailableFormTypes(features) {\n return FORM_TYPE_KEYS.filter(key => isFormTypeAvailable(key, features));\n}\n","// @vid-master/config/help-links\n// Single source of truth for every in-product \"Learn more\" deep-link (#394).\n// In-product copy stays a short, actionable summary; the full explanation\n// lives in docs and we link to it, so the two can't drift.\n//\n// HELP_TOPICS is pure data (docs path + anchor, no base URL) so the docs\n// link-check script (apps/docs/scripts/check-help-links.mjs) can verify every\n// anchor resolves to a real heading without touching env. Consumers use\n// HELP_LINKS from the package root, which composes full URLs off\n// BUSINESS.urls.docs.\n\n/**\n * Keyed by concept. `path` is relative to the docs site root (which already\n * includes the /docs basePath); `anchor` must match a heading slug in the\n * target page (custom [#anchor] syntax or the auto-generated slug).\n */\nexport const HELP_TOPICS = {\n goLive: { path: '/help/sharing', anchor: 'go-live' },\n cantGoLive: { path: '/help/sharing', anchor: 'cant-go-live' },\n liveAndDraft: { path: '/help/managing-forms', anchor: 'live-and-draft' },\n publishingChanges: { path: '/help/managing-forms', anchor: 'publishing-changes' },\n embedding: { path: '/help/embed', anchor: null },\n publicResults: { path: '/help/public-results', anchor: null },\n};\n\n/** Compose full HELP_LINKS URLs from a docs base URL (no trailing slash). */\nexport function buildHelpLinks(docsBaseUrl) {\n const links = {};\n for (const [key, { path, anchor }] of Object.entries(HELP_TOPICS)) {\n links[key] = `${docsBaseUrl}${path}${anchor ? `#${anchor}` : ''}`;\n }\n return links;\n}\n","import { FEATURES, getLiveFeatures } from './features.js';\nimport { HELP_TOPICS, buildHelpLinks } from './help-links.js';\n\n// @vid-master/config - Essential shared configuration\n\n/**\n * Environment Detection\n * Automatically detects dev/localhost vs production\n */\nconst isServer = typeof window === 'undefined';\nconst isBrowser = typeof window !== 'undefined';\n\n// Detect localhost/development environment\nconst isLocalhost = isBrowser && (\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname.includes('local')\n);\n\nconst isDevelopment = isServer\n ? process.env.NODE_ENV !== 'production'\n : isLocalhost;\n\nexport const ENV = {\n isDevelopment,\n isProduction: !isDevelopment,\n isLocalhost: isDevelopment, // alias for clarity\n};\n\n/**\n * Cookie domain for cross-subdomain auth & tracking\n * undefined in dev (browsers reject explicit domain on localhost)\n * .clipform.io in production (covers www., app., and apex)\n */\nexport const COOKIE_DOMAIN = ENV.isDevelopment ? undefined : '.clipform.io';\n\n/**\n * Browser is accessing the app via the Cloudflare dev tunnel\n * (e.g. dashboard-dev.smith-forge.com → routes to local Next.js).\n * In that case browser fetches must use the matching tunnel hostnames,\n * because the browser cannot reach localhost on the dev's machine.\n */\nconst isTunnelBrowser = isBrowser &&\n window.location.hostname.endsWith('.smith-forge.com');\n\n/**\n * Production origins - the single source of truth for Clipform's public URLs.\n * getUrls() returns these in prod (dev/tunnel branches override). Embed adapters\n * that run on third-party platforms and can't import this package (apps/embeds/*,\n * apps/shopify) hardcode these origins; they're pinned to this value by\n * apps/api/src/__tests__/lib/embed-origin-parity.test.ts so a domain change\n * can't silently skip an adapter.\n */\nexport const PROD_URLS = {\n marketing: 'https://www.clipform.io',\n // Dedicated CDN host for the embed SDK, decoupled from the marketing deploy\n // (ships in seconds via packages/embed-sdk `deploy:cf`). www.clipform.io/embed.js\n // stays as the legacy bridge until all installs migrate here.\n embed: 'https://js.clipform.io',\n dashboard: 'https://app.clipform.io',\n viewer: 'https://clipform.io',\n api: 'https://api.clipform.io',\n // Dedicated subdomain for the remote MCP server. Same Render service\n // as `api`, just routed by Host header. Token audience is bound here.\n mcp: 'https://mcp.clipform.io',\n docs: 'https://clipform.io/docs',\n};\n\nexport function getUrls() {\n if (isTunnelBrowser) {\n return {\n marketing: 'https://marketing-dev.smith-forge.com',\n // Dev/tunnel: the marketing dev server 308-redirects /embed.js to js.clipform.io\n // (public/embed.js was retired in #1288), so embeds load the live SDK.\n embed: 'https://marketing-dev.smith-forge.com',\n dashboard: 'https://dashboard-dev.smith-forge.com',\n viewer: 'https://viewer-dev.smith-forge.com',\n api: 'https://api-dev.smith-forge.com',\n mcp: 'https://api-dev.smith-forge.com/mcp',\n docs: 'https://clipform.io/docs',\n };\n }\n\n if (ENV.isDevelopment) {\n // Per-worktree overrides: a parallel worktree can run its own full stack on\n // a distinct port block by setting NEXT_PUBLIC_*_URL in its .env.local (plus\n // a matching `-p` / PORT). Without these the apps resolve each other at the\n // base ports below and would cross-wire to another worktree's servers.\n // Direct `process.env.NEXT_PUBLIC_*` reads (not destructured) so Next inlines\n // them into client bundles - every app that imports this package client-side\n // transpiles it (see each app's next.config transpilePackages).\n const api = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3003';\n return {\n marketing: process.env.NEXT_PUBLIC_MARKETING_URL || 'http://localhost:3002',\n // Local: the marketing dev server redirects /embed.js to js.clipform.io\n // (public/embed.js was retired in #1288), so embeds load the live SDK.\n embed: process.env.NEXT_PUBLIC_MARKETING_URL || 'http://localhost:3002',\n dashboard: process.env.NEXT_PUBLIC_DASHBOARD_URL || 'http://localhost:3000',\n viewer: process.env.NEXT_PUBLIC_VIEWER_URL || 'http://localhost:3001',\n api,\n // No separate MCP host in dev - tools live on the api subpath.\n mcp: api + '/mcp',\n // The docs app serves under basePath /docs (matching prod\n // clipform.io/docs), so the dev URL must include it too.\n docs: process.env.NEXT_PUBLIC_DOCS_URL || 'http://localhost:3004/docs',\n };\n }\n\n // Fresh copy so callers can't mutate the shared PROD_URLS source of truth.\n return { ...PROD_URLS };\n}\n\n/**\n * Core business information - UPDATE THESE AS NEEDED\n */\nexport const BUSINESS = {\n name: \"Clipform\",\n tagline: \"Video-Driven Form Builder\",\n description:\n \"Create engaging forms that combine video content with interactive questions. Capture authentic responses with text, audio, or video recordings.\",\n shortDescription:\n \"Interactive video forms that capture authentic responses. Build engaging forms in minutes.\",\n domain: \"clipform.io\",\n email: {\n support: \"support@clipform.io\",\n sales: \"sales@clipform.io\",\n hello: \"hello@clipform.io\",\n founder: \"andy@clipform.io\",\n },\n urls: getUrls(),\n};\n\n/**\n * Two-tier video delivery threshold (#1015): a video at/above this size goes to\n * Mux (adaptive HLS); below it (and short) it's stored direct in Supabase. The\n * API gate (`apps/api/src/lib/video-delivery.ts`) and the uploader client both\n * read this so the client knows whether to send bytes through the API (direct)\n * or straight to Mux (large). Single source - never hardcode the threshold.\n *\n * Set to 20MB from real data (#1495): every uploaded video is short (n=718, p95\n * ~24s, MAX 28s - there's a ~30s recording cap), so by Mux's own \"~2 min\"\n * guidance they all belong on progressive/Supabase. The old 10MB cap sent ~half\n * of them to Mux purely for being big, not long. A max-length (28s) clip\n * compresses to ~12MB at the 3.5Mbps target (#1512), so 20MB fits every\n * compressed clip with headroom - keeping short clips on cheap direct storage\n * and making Mux a rare fallback.\n */\nexport const MEDIA_DIRECT_MAX_BYTES = 20 * 1024 * 1024; // 20 MB\n\n/**\n * On-device video compression settings (#1512, desktop parity #2329) - single\n * source of truth for both the phone upload page (`apps/api/src/routes/v1/media/\n * phone-upload-page.ts`, inline script) and the desktop builder helper\n * (`apps/dashboard/lib/compress-video.ts`). Both re-encode via mediabunny/\n * WebCodecs to these exact settings before the direct-vs-Mux size handshake, so\n * a clip compresses identically regardless of upload surface.\n *\n * VIDEO_COMPRESS_FLOOR_BYTES is a \"won't shrink, skip the generation loss + CPU\"\n * floor - below this a clip can't meaningfully benefit from re-encoding at the\n * bitrate below, and the keep-only-if-smaller guard in both callers already\n * blocks any accidental bloat. Locked at 1 MB (#2329).\n */\nexport const VIDEO_COMPRESS_BITRATE = 3_500_000;\nexport const VIDEO_COMPRESS_HEIGHT = 1080;\nexport const VIDEO_COMPRESS_FPS = 30;\nexport const VIDEO_COMPRESS_FLOOR_BYTES = 1 * 1024 * 1024; // 1 MB\n\n/**\n * In-product \"Learn more\" deep-links (#394) - single source of truth, keyed\n * by concept. Anchors are CI-checked against real docs headings\n * (apps/docs/scripts/check-help-links.mjs). Never hardcode docs URLs in UI.\n */\nexport const HELP_LINKS = buildHelpLinks(BUSINESS.urls.docs);\nexport { HELP_TOPICS };\n\n/** URL builders — single source of truth for public-facing links */\nexport function viewerUrl(shareId) {\n return `${BUSINESS.urls.viewer}/${shareId}`;\n}\nexport function formUrl(shareId) {\n return `${BUSINESS.urls.dashboard}/save/${shareId}`;\n}\n\n/**\n * Dashboard auth routes - single source of truth so a login/signup route rename\n * is one edit here (plus renaming the app/<route> folder). Internal dashboard\n * redirects use AUTH_ROUTES.*; cross-app links (marketing) use the *Url builders.\n */\nexport const AUTH_ROUTES = { login: '/login', signup: '/signup' };\nexport function loginUrl() {\n return `${BUSINESS.urls.dashboard}${AUTH_ROUTES.login}`;\n}\nexport function signupUrl() {\n return `${BUSINESS.urls.dashboard}${AUTH_ROUTES.signup}`;\n}\n\n/**\n * Embeddable viewer iframe — single source of truth for embedding a form on a\n * third-party page. Consumed directly by anything inside our bundler (e.g. the\n * oEmbed endpoint in `apps/api`). Two artifacts run OUTSIDE the bundler and so\n * cannot import this — the static SDK (`apps/marketing/public/embed.js`) and\n * the standalone Framer component (`apps/embeds/framer/Clipform.tsx`) — and\n * carry a verbatim copy of `iframeAllow`. A parity test\n * (`apps/api/src/__tests__/lib/embed-allow-parity.test.ts`) fails if either\n * copy drifts from this value, so this stays the source of truth.\n */\nexport const EMBED = {\n // Permissions delegated to the cross-origin viewer iframe: recording\n // (camera/microphone), video (autoplay/fullscreen), Stripe wallets\n // (payment), and the end-screen share button (web-share + clipboard-write).\n iframeAllow: 'camera; microphone; autoplay; fullscreen; payment; web-share; clipboard-write',\n // Default 9:16 portrait box — matches embed.js DEFAULT_MAX_WIDTH (460px).\n defaultWidth: 460,\n defaultHeight: 818, // round(460 * 16 / 9)\n};\n\n/** Embeddable viewer URL for a form (the viewer's `display=embed` mode). */\nexport function embedViewerUrl(shareId) {\n return `${BUSINESS.urls.viewer}/${shareId}?display=embed`;\n}\n\n/**\n * Brand assets and visual identity\n */\nexport const BRAND = {\n logo: {\n primary: \"/logo.svg\",\n withText: \"/logo_text.svg\",\n icon: \"/favicon.ico\",\n iconSvg: \"/icon.svg\",\n appleTouchIcon: \"/apple-touch-icon.png\",\n minWidth: \"120px\",\n maxWidth: \"200px\",\n spacing: \"16px\",\n },\n colors: {\n primary: \"#171717\",\n secondary: \"#404040\",\n accent: \"#3182CE\",\n background: \"#FAFAFA\",\n surface: \"#FFFFFF\",\n border: \"#E5E5E5\",\n muted: \"#737373\",\n destructive: \"#DC2626\",\n success: \"#16A34A\",\n },\n fonts: {\n body: \"Inter, sans-serif\",\n headings: \"'Satoshi', Inter, sans-serif\",\n logo: \"'Plus Jakarta Sans', sans-serif\",\n satoshiImport: \"https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700,900&display=swap\",\n },\n /** HSL design tokens for CSS variable overrides */\n hsl: {\n background: \"0 0% 98%\",\n foreground: \"0 0% 25%\",\n muted: \"0 0% 96%\",\n mutedForeground: \"0 0% 45%\",\n popover: \"0 0% 100%\",\n popoverForeground: \"0 0% 9%\",\n card: \"0 0% 100%\",\n cardForeground: \"0 0% 9%\",\n border: \"0 0% 90%\",\n primary: \"0 0% 9%\",\n primaryForeground: \"0 0% 98%\",\n secondary: \"0 0% 96%\",\n secondaryForeground: \"0 0% 9%\",\n accent: \"0 0% 96%\",\n accentForeground: \"0 0% 9%\",\n ring: \"0 0% 64%\",\n darkBackground: \"0 0% 6%\",\n darkForeground: \"0 0% 92%\",\n darkMuted: \"0 0% 12%\",\n darkMutedForeground: \"0 0% 64%\",\n darkPopover: \"0 0% 8%\",\n darkPopoverForeground: \"0 0% 92%\",\n darkCard: \"0 0% 8%\",\n darkCardForeground: \"0 0% 98%\",\n darkBorder: \"0 0% 16%\",\n darkPrimary: \"0 0% 98%\",\n darkPrimaryForeground: \"0 0% 9%\",\n darkSecondary: \"0 0% 14%\",\n darkSecondaryForeground: \"0 0% 98%\",\n darkAccent: \"0 0% 16%\",\n darkAccentForeground: \"0 0% 92%\",\n darkRing: \"0 0% 30%\",\n },\n};\n\n/**\n * Plan limits — shared between API (enforcement), dashboard (UI gating), and viewer (branding)\n *\n * Keyed by plan_version string (e.g. 'free_v1', 'pro_v1') so we can\n * grandfather old limits when pricing changes. New signups get the version\n * from CURRENT_PLAN_VERSIONS; existing companies keep theirs until\n * explicitly migrated.\n *\n * Per-version billing identity is provider-agnostic (#1027): `price_cents` is\n * the canonical machine-readable price (the `price` string is display-only);\n * `stripe_price_id` and `shopify_plan_handle` map the version to each biller.\n * `shopify_plan_handle` is null until the matching Shopify App Pricing plan is\n * created in the Partner Dashboard.\n */\nexport const PLAN_LIMITS = {\n free_v1: {\n tier: 0,\n name: 'Free',\n price: '$0',\n period: 'forever',\n price_cents: 0,\n stripe_price_id: { test: null, live: null },\n // Shopify App Pricing plan NAME (must match the Partner Dashboard plan\n // exactly - the app_subscriptions/update payload carries this name). Free\n // generates no subscription, so this is only used by the drift check.\n shopify_plan_handle: 'Free',\n node_limit: null,\n form_limit: 5,\n workspace_limit: 1,\n show_branding: true,\n custom_theme: false,\n response_limit: 100,\n features: [\n 'Up to 5 forms',\n 'Unlimited questions per form',\n '100 responses per month',\n 'Full AI tooling (TTS, media, video)',\n 'Clipform branding',\n ],\n },\n pro_v1: {\n tier: 1,\n name: 'Pro',\n price: '$25',\n period: '/month',\n price_cents: 2500,\n stripe_price_id: { test: 'price_1TZnmhCWdEPD0KoNWWJTc3B6', live: 'price_1TZnrZEJOD9ik3LhhFwGZ6ut' },\n // Must match the Partner Dashboard plan name exactly (drives webhook plan\n // mapping + the price drift check). Keep in step with price_cents above.\n shopify_plan_handle: 'Pro',\n node_limit: null,\n form_limit: null,\n workspace_limit: null,\n show_branding: false,\n custom_theme: true,\n response_limit: null,\n features: [\n 'Unlimited forms',\n 'Unlimited questions',\n 'Unlimited responses',\n 'Custom themes & colors',\n 'Remove Clipform branding',\n ],\n },\n};\n\n/** Maps tier integer → version string assigned to new signups */\nexport const CURRENT_PLAN_VERSIONS = { 0: 'free_v1', 1: 'pro_v1' };\n\n/**\n * Product information and features\n */\nexport const PRODUCT = {\n features: {\n get core() {\n return getLiveFeatures()\n .filter(f => ['node-types', 'responses', 'builder', 'analytics', 'ai'].includes(f.category))\n .map(f => f.name);\n },\n get premium() {\n return getLiveFeatures()\n .filter(f => ['sharing', 'api'].includes(f.category))\n .map(f => f.name);\n },\n },\n // Derived from PLAN_LIMITS (source of truth)\n get pricing() {\n const free = PLAN_LIMITS[CURRENT_PLAN_VERSIONS[0]];\n const pro = PLAN_LIMITS[CURRENT_PLAN_VERSIONS[1]];\n return {\n free: { name: free.name, price: free.price, period: free.period, features: free.features },\n pro: { name: pro.name, price: pro.price, period: pro.period, features: pro.features },\n };\n },\n};\n\n/**\n * Social media links\n */\nexport const SOCIAL = {\n twitter: \"@clipform\",\n linkedin: \"company/clipform\",\n github: \"clipform\",\n urls: {\n twitter: \"https://twitter.com/clipform\",\n linkedin: \"https://linkedin.com/company/clipform\",\n github: \"https://github.com/clipform\",\n },\n};\n\n/**\n * Legal and compliance\n */\n/**\n * Legal facts - single source of truth for /terms and /privacy (#459).\n * Pages derive entity names, contacts, dates, and numbers from here;\n * never hardcode them in page copy.\n *\n * Drafting principle: minimum legally-required disclosure only. No voluntary\n * commitments, no named vendors in public docs (categories of recipients\n * satisfy UK GDPR Art 13(1)(e)), no published DPA until a customer requests\n * one. Shortest legally-sufficient wording everywhere.\n */\nexport const LEGAL = {\n company: \"Reco Ventures Ltd.\",\n companyCountry: \"United Kingdom\",\n governingLaw: \"England and Wales\",\n copyright: \"© 2026 Clipform. All rights reserved.\",\n privacy: \"/privacy\",\n terms: \"/terms\",\n privacyEmail: \"hello@clipform.io\", // GDPR contact - no separate DPO mailbox\n termsUpdated: \"2026-06-30\",\n privacyUpdated: \"2026-06-30\",\n minAge: 18, // contract capacity; no parental-consent handling\n billing: {\n processor: \"Stripe\", // live\n model: \"monthly-recurring\", // auto-renews; cancel effective end of period\n refunds: \"none\", // except at our sole discretion\n },\n // Total liability capped at fees paid in the prior 12 months (free = zero),\n // plus standard as-is / no indirect damages wording.\n liabilityCapMonths: 12,\n // Account/data deletion completed within this window (incl. Mux assets +\n // Supabase storage; backups may persist briefly beyond).\n deletionDays: 30,\n // Aggregated/anonymized data: rights reserved via quiet drafting - folded\n // into the content licence (terms s5) and lawful-bases (privacy s3) rather\n // than a standalone billboard section. Covers product improvement,\n // benchmarks, published insights; never re-identified.\n //\n // Internal only - NOT rendered anywhere. Kept current so a DPA can be\n // produced the day a customer requests one. Public docs disclose\n // categories of recipients only.\n subprocessors: [\n { name: \"Supabase\", purpose: \"Database and file storage\" },\n { name: \"Mux\", purpose: \"Video hosting and streaming\" },\n { name: \"Vercel\", purpose: \"Web hosting\" },\n { name: \"Render\", purpose: \"API hosting\" },\n { name: \"AWS\", purpose: \"Video rendering (Lambda)\" },\n { name: \"PostHog\", purpose: \"Product analytics and error tracking\" },\n { name: \"Cloudflare\", purpose: \"DNS, CDN, email routing\" },\n { name: \"Stripe\", purpose: \"Payment processing\" },\n { name: \"Anthropic\", purpose: \"AI form generation\" },\n { name: \"OpenAI\", purpose: \"AI form generation\" },\n ],\n};\n\n/**\n * Standard contact form field definitions\n * Source of truth for both dashboard (field picker) and viewer (form rendering)\n */\nexport const CONTACT_FIELDS = [\n { id: 'first_name', label: 'First Name', type: 'text', placeholder: 'Enter first name', order: 1 },\n { id: 'last_name', label: 'Last Name', type: 'text', placeholder: 'Enter last name', order: 2 },\n { id: 'email', label: 'Email Address', type: 'email', placeholder: 'you@example.com', order: 3 },\n { id: 'phone', label: 'Phone Number', type: 'tel', placeholder: '(555) 123-4567', order: 4 },\n];\n\nexport const CONTACT_FIELDS_MAP = Object.fromEntries(\n CONTACT_FIELDS.map(f => [f.id, f])\n);\n\n/**\n * The public \"Templates\" workspace. Curated template forms live here (promoted\n * via scripts/promote-form.ts); the template gallery (GET /v1/forms/templates)\n * is scoped to this workspace so a form flagged is_template in ANY other\n * workspace can't leak into it (#1370). Re-exported from\n * apps/api/src/lib/constants.ts so api and scripts/promote-form.ts share one\n * definition.\n */\nexport const CLIPFORM_TEMPLATES_WORKSPACE_ID = \"00000000-0000-4000-b000-000000000100\";\n\n/**\n * Node type definitions — re-exported from node-types.js\n */\nexport {\n NODE_TYPES,\n NODE_TYPES_LIST,\n getActiveNodeTypes,\n ACTIVE_NODE_TYPES,\n NODE_TYPE_KEYS,\n CONNECTION_CONFIG_KEYS,\n RESPONSE_FORMATS,\n CONSENT_TYPES,\n CONTACT_FIELD_TYPES,\n DRAW_GAME_DEFAULT_TIERS,\n NODE_TYPE_METADATA,\n RESPONSE_SCHEMAS,\n CONFIG_SCHEMAS,\n OUTPUT_SCHEMAS,\n TERMINAL_TYPES,\n NON_COUNTABLE_TYPES,\n NON_COUNTABLE_FILTER,\n RESULTS_EXCLUDED_TYPES,\n PUBLIC_RESULTS_EXCLUDED_TYPES,\n RESPONSES_EXCLUDED_TYPES,\n STEP_COUNTED_TYPES,\n isTerminalType,\n countsAsStep,\n} from './node-types.js';\n\nexport {\n validateAgainstSchema,\n validateConfig,\n validateOptions,\n validateResponse,\n validateOutput,\n} from './validate.js';\n\nexport { FEATURE_FLAGS, FEATURES, getLiveFeatures, getPlannedFeatures, getFeaturesByCategory, getWhatsNew, INTEGRATION_COMPANY_ALLOWLIST, companyHasIntegration, companyHasAnyIntegration, NODE_COMPANY_ALLOWLIST, companyHasNodeType, FEATURE_COMPANY_ALLOWLIST, companyHasPublicResults } from './features.js';\n\nexport { ANALYTICS_METRICS, ANALYTICS_INVARIANTS, ANALYTICS_BREAKDOWNS, ANALYTICS_FUNNEL_ORDER, getMetricLabel } from './analytics.js';\n\n/**\n * Form type definitions — re-exported from form-types.js\n */\nexport {\n FORM_TYPES,\n FORM_TYPES_LIST,\n FORM_TYPE_KEYS,\n WORKFLOW_TYPES,\n FORM_TYPE_ALIASES,\n ALL_VARIANTS,\n resolveFormType,\n getDiscoveryParams,\n isFormTypeAvailable,\n getAvailableFormTypes,\n} from './form-types.js';\n\nexport { validateFormForPublish, getNodeContentError, buildAdjacencyMap, getReachableIds, resolveRedirectUrl, resolveLinkListConfig } from './graph-validation.js';\n\n/**\n * Video template layer (#1736) — re-exported from video-templates.js. Data\n * + a pure expander that fills a curated control surface into Scene props;\n * video templates render through the existing Scene composition, never a\n * new primitive.\n */\nexport { VIDEO_TEMPLATE_REGISTRY, VIDEO_TEMPLATE_NAMES, buildVideoTemplate, exposedVideoTemplateNames } from './video-templates.js';\n\n/**\n * Form update accept-list — re-exported from form-update-fields.js\n * (source of truth for the fields PATCH /v1/forms/:id accepts, #1219)\n */\nexport { FORM_UPDATE_FIELDS, FORM_UPDATE_FIELD_KEYS } from './form-update-fields.js';\n\n/**\n * Placement tag vocabulary (#2017) — re-exported from placement-tags.js. The\n * closed `for:*`/`usecase:*` namespace mirrored against active `/for/*` and\n * `/use-case/*` marketing pages; write-time enforcement lives in\n * apps/api's set-form-tags route.\n */\nexport {\n PLACEMENT_FOR_ROLES,\n PLACEMENT_USECASE_WORKFLOWS,\n PLACEMENT_TAGS,\n isPlacementNamespace,\n invalidPlacementTags,\n} from './placement-tags.js';\n\n/**\n * Node logic grammar constants — re-exported from logic-types.js\n */\nexport {\n LOGIC_ACTION_TYPES,\n LOGIC_CONDITION_OPS,\n LOGIC_VAR_TYPES,\n LOGIC_TARGET_TYPES,\n isBranchingActive,\n isOptionEdge,\n isInactiveOptionEdge,\n actionReferenceErrors,\n} from './logic-types.js';\n\n/**\n * Pure, branch-aware logic-chain graph helpers — re-exported from\n * logic-chain.js (#1058). apps/api's DB-backed chain ops (getOrderedNodes,\n * insertAfter, removeFromChain, findLastBeforeTerminal) call these.\n */\nexport {\n outgoingTargets,\n isBranched,\n defaultTarget,\n bfsOrder,\n computeRemovalRewrites,\n} from './logic-chain.js';\n\n/**\n * Integration catalog definitions — re-exported from integration-catalog.js\n */\nexport {\n INTEGRATION_CATALOG,\n INTEGRATION_CATALOG_LIST,\n INTEGRATION_SLUGS,\n getIntegrationsByCategory,\n getIntegrationsByTags,\n} from './integration-catalog.js';\n\n/**\n * API key prefixes — shared between API (validation) and dashboard (generation)\n */\nexport const API_KEY_PREFIXES = ['cf_', 'cf_live_', 'cf_test_'];\nexport const API_KEY_PREFIX = 'cf_';\n\n/**\n * Input validation limits — shared between API routes and MCP server\n */\nexport const VALIDATION_LIMITS = {\n form: {\n title: { max: 1000 },\n nodes: { max: 100 },\n },\n node: {\n prompt: { max: 5000, media_max: 60 },\n options: { max: 50 },\n },\n workspace: {\n name: { max: 100 },\n },\n};\n\n/**\n * Theme defaults and colour validation — single source of truth\n * All colours must be 6-digit hex (#RRGGBB). No rgba, no shorthand.\n */\nexport const HEX_COLOR_REGEX = /^#[0-9A-Fa-f]{6}$/;\n\nexport const THEME_DEFAULTS = {\n primary_color: '#374151',\n background_color: '#F3F4F6',\n font_family: 'Inter',\n};\n\n/**\n * AI-protected form fields — fields the AI must NOT set unless the user\n * explicitly requests it. Everything else is AI-mutable (set freely).\n *\n * Used by MCP tools to guard descriptions and omit defaults.\n */\nexport const AI_PROTECTED_FIELDS = {\n font_family: {\n reason: 'Brand/design decision — do not set unless the user explicitly requests a different font.',\n },\n author: {\n reason: 'Brand identity — do not invent. Only set when the user provides a value.',\n },\n brand_name: {\n reason: 'Brand identity — do not invent. Only set when the user provides a value.',\n },\n logo_url: {\n reason: 'Brand identity — do not invent. Only set when the user provides a value.',\n },\n is_live: {\n reason: 'Going live is a deliberate action. Never auto-publish unless the user asks.',\n },\n};\n\n/**\n * Ken Burns / Slideshow constants — single source of truth.\n * Used by Remotion compositions, API slideshow route, and MCP tools.\n */\nexport const KEN_BURNS_EFFECTS = [\n 'pan-left',\n 'pan-right',\n 'pan-up',\n 'pan-down',\n 'zoom-in-pan-left',\n 'zoom-in-pan-right',\n 'zoom-in',\n 'zoom-out',\n 'static',\n];\n\nexport const KEN_BURNS_PRESETS = [\n 'cinematic',\n 'dramatic',\n 'calm',\n 'documentary',\n 'dreamy',\n 'moody',\n 'energetic',\n];\n\nexport const KEN_BURNS_EASINGS = ['ease-in-out', 'ease-in', 'ease-out', 'linear'];\n\nexport const KEN_BURNS_FIT_MODES = ['cover', 'blur-pad', 'auto'];\n\nexport const SLIDESHOW_TRANSITIONS = [\n 'fade',\n 'fadeblack',\n 'fadewhite',\n 'slideleft',\n 'slideright',\n 'circlecrop',\n 'circleopen',\n 'circleclose',\n 'dissolve',\n 'pixelize',\n 'radial',\n 'smoothleft',\n 'smoothright',\n 'wipeleft',\n 'wiperight',\n 'diagtl',\n 'diagbr',\n 'hblur',\n];\n\n/**\n * Composition IDs that require a Mapbox access token.\n * Map is the primitive (#453); GlobeToCity/CityToGlobe/MapPin are its\n * presets/aliases.\n */\nexport const MAP_COMPOSITIONS = ['Map', 'GlobeToCity', 'CityToGlobe', 'MapPin'];\n\n/**\n * Composition registry - single source of truth for which compositions the MCP\n * server exposes and at what access tier, plus their kind (for docs/tooling).\n *\n * - `tier`: 'public' (everyone) | 'labs' (non-prod, power users/dogfooding) |\n * 'social' (non-prod, marketing templates). The MCP PUBLIC/LABS/SOCIAL\n * lists derive from this (see packages/mcp-server/src/lib/config.ts).\n * - `kind`: 'bed' (full-bleed Scene layer 0) | 'overlay' (Scene asset layer) |\n * 'container' (Scene itself) | 'video' (whole standalone pre-baked video).\n * - `scene_layer`: usable as a Scene layer (#652). SCENE_LAYER_IDS in\n * packages/remotion derives from this - never hand-list layer ids there.\n * `kind` says where a comp sits when layered; `scene_layer` says whether it\n * may be (most beds render standalone only).\n *\n * Variant presets (e.g. a 'neon' Text) are NOT separate entries - they\n * are the base composition + a `preset` prop. Render-only/dev compositions\n * (test cards, Map preset-aliases, StatOverlay) are intentionally absent: they\n * are registered in Remotion but not advertised on the MCP. The GridList/\n * FourImageGrid/OddOneOutReveal Grid layout-aliases that used to live here\n * were deleted outright (#1615) - a list is just Grid with\n * `layout: { columns: 1 }`, not its own primitive. FlagReveal, ColorCards and\n * TitleCard were folded the same way (#1736) - they are VIDEO_TEMPLATE_REGISTRY\n * entries (video-templates.js) that expand onto Slideshow/Grid/Text through Scene,\n * not COMPOSITION_REGISTRY primitives.\n */\nexport const COMPOSITION_REGISTRY = [\n // Beds (full-bleed backgrounds)\n { id: 'Slideshow', tier: 'public', kind: 'bed', scene_layer: true },\n { id: 'Map', tier: 'public', kind: 'bed', scene_layer: true },\n { id: 'Grid', tier: 'public', kind: 'bed', scene_layer: true },\n { id: 'Timeline', tier: 'labs', kind: 'bed' },\n { id: 'Silhouette', tier: 'labs', kind: 'bed', scene_layer: true },\n // Overlays (assets layered on a bed)\n { id: 'Text', tier: 'labs', kind: 'overlay', scene_layer: true },\n { id: 'Rebus', tier: 'labs', kind: 'overlay', scene_layer: true },\n // Container\n { id: 'Scene', tier: 'labs', kind: 'container' },\n // Social marketing templates (gated)\n { id: 'ShortFormQuiz', tier: 'social', kind: 'video' },\n { id: 'ScorecardQuiz', tier: 'social', kind: 'video' },\n { id: 'ScorecardGK', tier: 'social', kind: 'video' },\n { id: 'CountrySilhouetteQuiz', tier: 'social', kind: 'video' },\n];\n\nconst compositionIdsByTier = (tier) =>\n COMPOSITION_REGISTRY.filter((c) => c.tier === tier).map((c) => c.id);\n\nexport const PUBLIC_COMPOSITION_IDS = compositionIdsByTier('public');\nexport const LABS_COMPOSITION_IDS = compositionIdsByTier('labs');\nexport const SOCIAL_COMPOSITION_IDS = compositionIdsByTier('social');\n\n/**\n * The composition surface exposed for a given environment (#1674, #1789).\n * `labs`/`social` are a pure environment gate - not a per-workspace/billing\n * concept - so this takes a single boolean rather than reading `process.env`\n * itself (this package stays dependency-free; callers resolve NODE_ENV at\n * their own edge). Single derivation shared by the MCP server\n * (packages/mcp-server/src/lib/config.ts) and the API's render gate\n * (apps/api/src/routes/internal/render.ts) - never hand-roll a second copy\n * of this list.\n */\nexport function exposedCompositionIds(includeNonPublic) {\n return [\n ...PUBLIC_COMPOSITION_IDS,\n ...(includeNonPublic ? LABS_COMPOSITION_IDS : []),\n ...(includeNonPublic ? SOCIAL_COMPOSITION_IDS : []),\n ];\n}\n\n/** Compositions usable as Scene layers (#652) - the single source for\n * SCENE_LAYER_IDS in packages/remotion and (via sync-schemas) the API's\n * per-layer validation allowlist. */\nexport const SCENE_LAYER_COMPOSITION_IDS = COMPOSITION_REGISTRY\n .filter((c) => c.scene_layer)\n .map((c) => c.id);\n\n/**\n * Minimum plan tier for an MCP tool to appear in tools/list on the remote\n * (OAuth) MCP server (#471). Tools not listed default to 0 (every plan).\n * Presentation-level only - real enforcement (rate limits, plan checks)\n * stays in the API routes. stdio/API-key sessions always see all tools.\n *\n * To gate a tool, add e.g. `clipform_render_composition: 1`.\n */\nexport const MCP_TOOL_TIERS = {};\n\n/**\n * Documentation RAG embeddings (the `docs.chunks` vector store powering the\n * dashboard docs chatbot). SINGLE SOURCE OF TRUTH for the OpenAI embedding\n * model + dimensions.\n *\n * BOTH sides must use these or retrieval silently breaks: the ingest side\n * (`scripts/embed-docs.mjs`, which writes the chunk vectors) and the query\n * side (`apps/api/src/routes/v1/docs/chat.ts`, which embeds the user's\n * question). If they drift, questions get embedded in a different vector\n * space than the stored chunks and `match_chunks` returns garbage.\n *\n * Pinned to 1536 dims (not text-embedding-3-large's native 3072) so the\n * vectors fit the `docs.chunks vector(1536)` column AND stay under pgvector's\n * 2000-dim HNSW index limit - no schema migration needed. Changing either\n * value requires a full re-embed (`FULL_REEMBED=1 node scripts/embed-docs.mjs`):\n * a model/dim swap doesn't change content hashes, so the incremental path\n * would skip every chunk.\n */\nexport const DOCS_EMBEDDING_MODEL = 'text-embedding-3-large';\nexport const DOCS_EMBEDDING_DIMENSIONS = 1536;\n","const telemetryEnabled =\n process.env.DO_NOT_TRACK !== \"1\" &&\n process.env.CLIPFORM_TELEMETRY !== \"off\";\n\nlet mcpVersion = \"unknown\";\n\nexport function setMcpVersion(version: string) {\n mcpVersion = version;\n}\n\nfunction getApiBaseUrl(): string | null {\n return process.env.API_URL || null;\n}\n\nexport interface ErrorReport {\n error_type: string;\n error_message: string;\n tool_name?: string;\n status_code?: number;\n api_path?: string;\n}\n\nexport function reportError(report: ErrorReport) {\n if (!telemetryEnabled) return;\n const base = getApiBaseUrl();\n if (!base) return;\n\n const payload = {\n ...report,\n error_message: report.error_message.slice(0, 500),\n mcp_version: mcpVersion,\n runtime: detectRuntime(),\n timestamp: new Date().toISOString(),\n };\n\n fetch(`${base}/v1/telemetry/errors`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(2_000),\n }).catch(() => {});\n}\n\nexport function isServerFault(status: number): boolean {\n return status >= 500;\n}\n\nfunction detectRuntime(): string {\n if (process.env.CLAUDE_CODE) return \"claude-code\";\n if (process.env.CURSOR_SESSION_ID) return \"cursor\";\n return \"stdio\";\n}\n","import { getMcpAuth } from \"./auth-context.js\";\nimport { reportError, isServerFault } from \"./telemetry.js\";\n\nfunction getApiBaseUrl(): string {\n const url = process.env.API_URL;\n if (!url) {\n throw new Error(\n \"API_URL must be set (e.g. http://localhost:3003 or https://api.clipform.io)\"\n );\n }\n return url;\n}\n\n\nfunction getAuthHeaders(): Record<string, string> {\n const authCtx = getMcpAuth();\n const apiKey = authCtx?.api_key || process.env.CLIPFORM_API_KEY;\n if (!apiKey) {\n throw new Error(\"CLIPFORM_API_KEY must be set.\");\n }\n\n return { \"Authorization\": `Bearer ${apiKey}` };\n}\n\ninterface ApiOptions {\n method?: string;\n body?: Record<string, unknown>;\n params?: Record<string, string>;\n timeoutMs?: number;\n}\n\ntype ApiResult =\n | { ok: true; data: Record<string, unknown> }\n | { ok: false; status: number; error: string };\n\nexport async function callApi(\n path: string,\n options: ApiOptions = {}\n): Promise<ApiResult> {\n const { body, params, timeoutMs = 30_000 } = options;\n const method = options.method || (body ? \"POST\" : \"GET\");\n\n const base = getApiBaseUrl();\n const prefix = path.startsWith(\"/internal/\") ? \"\" : \"/v1\";\n let url = `${base}${prefix}${path}`;\n if (params) {\n const searchParams = new URLSearchParams(params);\n url += `?${searchParams.toString()}`;\n }\n\n // Creative endpoints (/internal/*) authenticate with the same Bearer API\n // key as everything else. The X-Service-Secret header the MCP used to send\n // is webhook-trigger auth only and was dropped here (#412 F7).\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...getAuthHeaders(),\n };\n\n const fetchOptions: RequestInit = {\n method,\n headers,\n signal: AbortSignal.timeout(timeoutMs),\n };\n\n if (body && method !== \"GET\") {\n fetchOptions.body = JSON.stringify(body);\n }\n\n let response: Response;\n try {\n response = await fetch(url, fetchOptions);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n reportError({\n error_type: \"connection_error\",\n error_message: message,\n tool_name: getMcpAuth()?.tool_name,\n api_path: `${prefix}${path}`,\n });\n return {\n ok: false,\n status: 0,\n error: `Failed to connect to API at ${url}.\\n\\nError: ${message}`,\n };\n }\n\n if (response.status === 204) {\n return { ok: true, data: {} };\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n\n if (!response.ok) {\n const hint = recoveryHint(response.status, path);\n const apiMsg = (data.error as string) || `API error (${response.status})`;\n const errorMsg = hint ? `${apiMsg} - ${hint}` : apiMsg;\n if (isServerFault(response.status)) {\n reportError({\n error_type: `http_${response.status}`,\n error_message: errorMsg,\n tool_name: getMcpAuth()?.tool_name,\n status_code: response.status,\n api_path: `${prefix}${path}`,\n });\n }\n return {\n ok: false,\n status: response.status,\n error: errorMsg,\n };\n }\n\n return { ok: true, data };\n}\n\n/** Next-step hint by status + path so an agent can recover instead of\n * retrying blind (#651). The raw API message stays first, hint second. */\nexport function recoveryHint(status: number, path: string): string | undefined {\n if (status === 401 || status === 403) {\n return \"Check credentials: the key comes from CLIPFORM_API_KEY or the OAuth session - read clipform://context/session for the active workspace and auth mode.\";\n }\n if (status === 404) {\n if (/^\\/forms\\/[^/]+\\/nodes\\//.test(path)) {\n return \"Call clipform_get_form to list this form's nodes and their IDs.\";\n }\n if (/^\\/forms\\/[^/]+/.test(path)) {\n return \"Call clipform_list_forms to find valid form IDs (UUIDs, not the share_id from a form URL).\";\n }\n }\n return undefined;\n}\n\nexport function errorResult(message: string) {\n return {\n content: [{ type: \"text\" as const, text: message }],\n isError: true,\n };\n}\n\nexport function textResult(text: string) {\n return {\n content: [{ type: \"text\" as const, text }],\n };\n}\n\n/** Like textResult, but also carries a machine-readable `structuredContent`.\n * A tool that declares an `outputSchema` MUST return `structuredContent` on\n * every non-error result (the MCP SDK validates it and throws otherwise) - use\n * this for every success/empty path, and `errorResult` (exempt) for failures.\n * Project to the schema's explicit field set here - never pass raw API rows\n * (response-hygiene invariant, #797). */\nexport function structuredResult(\n text: string,\n structuredContent: Record<string, unknown>\n) {\n return {\n content: [{ type: \"text\" as const, text }],\n structuredContent,\n };\n}\n","import { BUSINESS } from \"@vid-master/config\";\nimport { callApi } from \"./api-client.js\";\n\nexport type AuthMode = \"authenticated\" | \"anonymous\";\n\nexport interface SessionContextResult {\n text: string;\n authMode: AuthMode;\n}\n\nexport async function getSessionContextWithAuth(): Promise<SessionContextResult> {\n let result;\n try {\n result = await callApi(\"/me\", { method: \"GET\" });\n } catch {\n return { text: \"\", authMode: \"anonymous\" };\n }\n if (!result.ok) return { text: \"\", authMode: \"anonymous\" };\n\n const me = result.data as any;\n // The API's auth_mode is \"api_key\" | \"session\" | \"anonymous\". Anything\n // credentialed counts as authenticated - the old `=== \"oauth\"` check\n // matched nothing the API returns, so API-key sessions were labelled\n // anonymous with a \"sign in to unlock\" nudge right next to their real\n // plan (#412 F6).\n const apiAuthMode: string = (me.auth_mode as string) ?? \"anonymous\";\n const authMode: AuthMode = apiAuthMode === \"anonymous\" ? \"anonymous\" : \"authenticated\";\n const plan = me.plan;\n const lines: string[] = [];\n\n lines.push(\"## Your Session\");\n if (authMode === \"authenticated\") {\n lines.push(\n apiAuthMode === \"api_key\"\n ? \"Auth: API key (workspace-scoped)\"\n : \"Auth: signed in (connected to a Clipform account)\"\n );\n lines.push(`Workspace: ${me.workspace?.name ?? \"Unknown\"} (${me.workspace?.id ?? \"?\"})`);\n if (me.user_id) lines.push(`User: ${me.user_id}`);\n if (me.company_id) lines.push(`Company: ${me.company_id}`);\n } else {\n lines.push(\"Auth: anonymous (not connected to a Clipform account)\");\n if (me.workspace) {\n lines.push(`Workspace: ${me.workspace.name} (${me.workspace.id})`);\n }\n }\n lines.push(`Plan: ${plan.name} (tier ${plan.tier})`);\n if (plan.node_limit !== null) {\n lines.push(`Question limit: ${plan.node_limit} per form`);\n } else {\n lines.push(\"Questions: unlimited\");\n }\n if (plan.form_limit !== null) {\n lines.push(`Form limit: ${plan.form_limit} forms`);\n } else {\n lines.push(\"Forms: unlimited\");\n }\n if (plan.custom_theme === false) {\n lines.push(\"Custom themes: not available on this plan\");\n }\n if (authMode === \"anonymous\") {\n lines.push(`\\nTo unlock your full plan: sign in to your Clipform account.`);\n }\n\n return { text: lines.join(\"\\n\"), authMode };\n}\n\nexport async function getSessionContext(): Promise<string> {\n const { text } = await getSessionContextWithAuth();\n return text;\n}\n"],"mappings":"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","names":["getApiBaseUrl"]}