@templatical/template-tools 0.51.1 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,10 @@
1
1
  import Ajv from "ajv";
2
2
  import { getContrastRatio, isOpaqueHex, lintAccessibility, lintHtmlFragment, lintTemplate } from "@templatical/quality";
3
- import { createCustomBlock, createDefaultTemplateContent, createParagraphBlock } from "@templatical/types";
3
+ import { createCustomBlock, createCustomBlockLiquid, createDefaultTemplateContent, createParagraphBlock } from "@templatical/types";
4
4
  import { Parser } from "htmlparser2";
5
- import { ForTag, Liquid, Output, Tag } from "liquidjs";
5
+ import * as liquidjs from "liquidjs";
6
6
  import { escapeHtml, renderToMjml } from "@templatical/renderer";
7
- //#endregion
8
- //#region src/validate.ts
7
+ // src/validate.ts
9
8
  /** The generated JSON Schema for `TemplateContent`, as a plain object. */
10
9
  const schema = {
11
10
  $id: "https://templatical.com/schema/template-content.json",
@@ -1403,8 +1402,7 @@ function runQualityLint(data) {
1403
1402
  };
1404
1403
  }
1405
1404
  }
1406
- //#endregion
1407
- //#region src/custom-block/safety.ts
1405
+ // src/custom-block/safety.ts
1408
1406
  /** Style-attribute declarations reused by the custom-block accessibility checks. */
1409
1407
  function declarations(style) {
1410
1408
  const out = [];
@@ -1503,8 +1501,7 @@ function checkEmailSafety(def, rendered) {
1503
1501
  return true;
1504
1502
  });
1505
1503
  }
1506
- //#endregion
1507
- //#region src/custom-block/a11y.ts
1504
+ // src/custom-block/a11y.ts
1508
1505
  const HEX = /#[0-9a-f]{3,8}\b/i;
1509
1506
  const PX = /^\s*(\d+(?:\.\d+)?)px/i;
1510
1507
  /** The colours and font size an element sets inline, over its parent's. */
@@ -1591,8 +1588,7 @@ function checkAccessibility(rendered) {
1591
1588
  return true;
1592
1589
  });
1593
1590
  }
1594
- //#endregion
1595
- //#region src/custom-block/definition.ts
1591
+ // src/custom-block/definition.ts
1596
1592
  const validateDefinition = new Ajv({
1597
1593
  allErrors: true,
1598
1594
  strict: false
@@ -2033,10 +2029,13 @@ function validateCustomBlockDefinition(data) {
2033
2029
  issues
2034
2030
  };
2035
2031
  }
2036
- //#endregion
2037
- //#region src/custom-block/liquid.ts
2032
+ // src/custom-block/liquid.ts
2038
2033
  function createLiquid() {
2039
- return new Liquid({
2034
+ return createCustomBlockLiquid(liquidjs);
2035
+ }
2036
+ /** Request URLs and JSON bodies need their original, non-HTML escaping. */
2037
+ function createRecipeLiquid() {
2038
+ return new liquidjs.Liquid({
2040
2039
  strictVariables: false,
2041
2040
  strictFilters: false,
2042
2041
  templates: {}
@@ -2063,16 +2062,16 @@ function allTemplates(templates) {
2063
2062
  }
2064
2063
  /** Every `for` tag in the tree, nested ones included. */
2065
2064
  function forTags(templates) {
2066
- return allTemplates(templates).filter((t) => t instanceof ForTag);
2065
+ return allTemplates(templates).filter((t) => t instanceof liquidjs.ForTag);
2067
2066
  }
2068
2067
  /** Names of the file-loading tags in the tree, each once, in source order. */
2069
2068
  function fileTags(templates) {
2070
- const names = allTemplates(templates).filter((t) => t instanceof Tag && FILE_TAGS.has(t.name)).map((t) => t.name);
2069
+ const names = allTemplates(templates).filter((t) => t instanceof liquidjs.Tag && FILE_TAGS.has(t.name)).map((t) => t.name);
2071
2070
  return [...new Set(names)];
2072
2071
  }
2073
2072
  /** How many `{{ }}` outputs in the tree reach the text without a `json` filter. */
2074
2073
  function outputsWithoutJson(templates) {
2075
- return allTemplates(templates).filter((t) => t instanceof Output && !t.value.filters.some((f) => f.name === "json")).length;
2074
+ return allTemplates(templates).filter((t) => t instanceof liquidjs.Output && !t.value.filters.some((f) => f.name === "json")).length;
2076
2075
  }
2077
2076
  /** The message for a template or recipe that uses a file-loading tag. */
2078
2077
  function fileTagMessage(name, where) {
@@ -2159,8 +2158,7 @@ function checkLiquid(def) {
2159
2158
  }
2160
2159
  return issues;
2161
2160
  }
2162
- //#endregion
2163
- //#region src/custom-block/recipe.ts
2161
+ // src/custom-block/recipe.ts
2164
2162
  const ENV_REF = /\$\{env:([A-Za-z_][A-Za-z0-9_]*)\}/g;
2165
2163
  const LIQUID = /\{\{|\{%/;
2166
2164
  const UNSAFE_KEYS = /* @__PURE__ */ new Set([
@@ -2222,7 +2220,7 @@ function checkRecipe(def) {
2222
2220
  });
2223
2221
  const err = add("error");
2224
2222
  const warn = add("warning");
2225
- const engine = createLiquid();
2223
+ const engine = createRecipeLiquid();
2226
2224
  for (const [part, text] of [["/request/url", r.request.url], ["/request/body", r.request.body]]) {
2227
2225
  if (text === void 0) continue;
2228
2226
  if (new RegExp(ENV_REF.source).test(text)) err("recipe.env-outside-headers", part, "`${env:…}` is resolved only in header values; keep secrets out of the URL and body.");
@@ -2256,7 +2254,7 @@ async function runRecipe(recipe, fieldValues, { env = process.env, fetch: doFetc
2256
2254
  ok: false,
2257
2255
  error: HOST_TEMPLATE_ERROR
2258
2256
  };
2259
- const engine = createLiquid();
2257
+ const engine = createRecipeLiquid();
2260
2258
  const encoded = Object.fromEntries(Object.entries(fieldValues).map(([k, v]) => [k, encodeValue(v)]));
2261
2259
  let url;
2262
2260
  let body;
@@ -2381,8 +2379,7 @@ function replaceLiquid(text) {
2381
2379
  i = close + 2;
2382
2380
  }
2383
2381
  }
2384
- //#endregion
2385
- //#region src/custom-block/specimen.ts
2382
+ // src/custom-block/specimen.ts
2386
2383
  const SPECIMEN_STATES = [
2387
2384
  "defaults",
2388
2385
  "empty",
@@ -2508,8 +2505,7 @@ function buildSpecimenTemplate(def, host, only) {
2508
2505
  }
2509
2506
  return content;
2510
2507
  }
2511
- //#endregion
2512
- //#region src/custom-block/render.ts
2508
+ // src/custom-block/render.ts
2513
2509
  async function renderStates(def, instances = buildSpecimen(def)) {
2514
2510
  const liquid = createLiquid();
2515
2511
  return Promise.all(instances.map(async (i) => ({
@@ -2518,14 +2514,9 @@ async function renderStates(def, instances = buildSpecimen(def)) {
2518
2514
  })));
2519
2515
  }
2520
2516
  async function renderSpecimenMjml(def, template = buildSpecimenTemplate(def)) {
2521
- const liquid = createLiquid();
2522
- return renderToMjml(template, {
2523
- renderCustomBlock: async (block) => String(await liquid.parseAndRender(def.template, block.fieldValues)),
2524
- getCustomBlockStylesheet: (customType) => customType === def.type ? def.stylesheet : void 0
2525
- });
2517
+ return renderToMjml(template, { customBlocks: [def] });
2526
2518
  }
2527
- //#endregion
2528
- //#region src/custom-block/check.ts
2519
+ // src/custom-block/check.ts
2529
2520
  const finish = (issues) => ({
2530
2521
  valid: !issues.some((i) => i.severity === "error"),
2531
2522
  issues
@@ -2604,7 +2595,6 @@ async function checkCustomBlock(data, { compileMjml } = {}) {
2604
2595
  }
2605
2596
  return finish(issues);
2606
2597
  }
2607
- //#endregion
2608
2598
  export { buildSpecimenTemplate as a, runRecipe as c, schema as d, validateTemplate as f, buildSpecimen as i, validateCustomBlockDefinition as l, renderSpecimenMjml as n, fetchedSpecimenBlocks as o, SPECIMEN_STATES as r, checkRecipe as s, checkCustomBlock as t, runQualityLint as u };
2609
2599
 
2610
- //# sourceMappingURL=custom-block-B2hUl-PC.js.map
2600
+ //# sourceMappingURL=custom-block-BgJCmUbD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-block-BgJCmUbD.js","names":["rawSchema","rawSchema"],"sources":["../schema.json","../src/validate.ts","../src/custom-block/safety.ts","../src/custom-block/a11y.ts","../custom-block-schema.json","../src/custom-block/definition.ts","../src/custom-block/liquid.ts","../src/custom-block/recipe.ts","../src/custom-block/specimen.ts","../src/custom-block/render.ts","../src/custom-block/check.ts"],"sourcesContent":["","// Structural validation of a Templatical template against the generated JSON\n// Schema, plus the @templatical/quality lint layered on top.\n//\n// Structural validation is discriminator-aware: each block is checked against\n// the subschema for its declared `type` (e.g. a block with `\"type\": \"button\"`\n// is validated as a ButtonBlock), so errors are precise (\"blocks[2] (button)\n// must have required property 'url'\") instead of the raw anyOf's \"must match\n// exactly one schema in anyOf\".\n\nimport Ajv, { type ValidateFunction, type ErrorObject } from \"ajv\";\nimport { lintTemplate, type LintIssue } from \"@templatical/quality\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport rawSchema from \"../schema.json\";\n\ninterface SchemaDefinition {\n properties?: { type?: { const?: unknown } } & Record<string, unknown>;\n [key: string]: unknown;\n}\n\ninterface SchemaDocument {\n definitions: Record<string, SchemaDefinition>;\n [key: string]: unknown;\n}\n\n/** The generated JSON Schema for `TemplateContent`, as a plain object. */\nexport const schema = rawSchema as unknown as SchemaDocument;\n\nconst ajv = new Ajv({ allErrors: true, strict: false });\n\n// Map a block's `type` discriminator (e.g. \"button\", \"social\") to its schema\n// definition name (e.g. \"ButtonBlock\", \"SocialIconsBlock\"), derived from the\n// schema itself so it never drifts from the generated defs.\nconst typeToDef: Record<string, string> = {};\nfor (const [name, def] of Object.entries(schema.definitions)) {\n const constType = def?.properties?.type?.const;\n if (typeof constType === \"string\") {\n typeToDef[constType] = name;\n }\n}\n\nconst validatorCache = new Map<string, ValidateFunction>();\n\nfunction validatorFor(defName: string): ValidateFunction {\n const cached = validatorCache.get(defName);\n if (cached) return cached;\n\n const definitions = structuredClone(schema.definitions);\n // A section's `children` holds nested blocks (Block[][]); we recurse into\n // them separately for precise paths, so stub the deep check here.\n if (defName === \"SectionBlock\") {\n const section = definitions.SectionBlock;\n if (section?.properties) {\n section.properties.children = { type: \"array\" };\n }\n }\n const compiled = ajv.compile({\n $ref: `#/definitions/${defName}`,\n definitions,\n });\n validatorCache.set(defName, compiled);\n return compiled;\n}\n\nconst settingsValidator = ajv.compile({\n $ref: \"#/definitions/TemplateSettings\",\n definitions: structuredClone(schema.definitions),\n});\n\ninterface FlatBlock {\n path: string;\n block: Record<string, unknown> | null;\n}\n\nfunction flattenBlocks(\n blocks: unknown,\n basePath: string,\n out: FlatBlock[],\n): void {\n if (!Array.isArray(blocks)) return;\n blocks.forEach((block, i) => {\n const path = `${basePath}[${i}]`;\n out.push({ path, block: block ?? null });\n if (block?.type === \"section\" && Array.isArray(block.children)) {\n block.children.forEach((column: unknown, ci: number) => {\n flattenBlocks(column, `${path}.children[${ci}]`, out);\n });\n }\n });\n}\n\nfunction formatAjvErrors(\n errors: ErrorObject[] | null | undefined,\n prefix: string,\n): string[] {\n return (errors ?? []).map((e) => {\n const where = `${prefix}${e.instancePath}`;\n const extra =\n e.keyword === \"additionalProperties\"\n ? ` (${(e.params as { additionalProperty?: string }).additionalProperty})`\n : \"\";\n return `${where} ${e.message}${extra}`;\n });\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: string[];\n}\n\n/**\n * Structural validation. Synchronous, depends only on ajv + the committed\n * schema.json (no build of the workspace packages required).\n */\nexport function validateTemplate(data: unknown): ValidationResult {\n const errors: string[] = [];\n\n if (data === null || typeof data !== \"object\" || Array.isArray(data)) {\n return { valid: false, errors: [\"(root) must be an object\"] };\n }\n\n const doc = data as Record<string, unknown>;\n\n if (!Array.isArray(doc.blocks)) {\n errors.push(\"blocks must be an array\");\n }\n if (doc.settings === null || typeof doc.settings !== \"object\") {\n errors.push(\"settings must be an object\");\n } else if (!settingsValidator(doc.settings)) {\n errors.push(...formatAjvErrors(settingsValidator.errors, \"settings\"));\n }\n\n const flat: FlatBlock[] = [];\n flattenBlocks(doc.blocks, \"blocks\", flat);\n for (const { path, block } of flat) {\n const type = block?.type;\n const defName = typeof type === \"string\" ? typeToDef[type] : undefined;\n if (!defName) {\n const known = Object.keys(typeToDef).join(\", \");\n errors.push(\n `${path} has unknown or missing block type ${JSON.stringify(type)} (expected one of: ${known})`,\n );\n continue;\n }\n const validate = validatorFor(defName);\n if (!validate(block)) {\n errors.push(...formatAjvErrors(validate.errors, `${path} (${type})`));\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n\nexport interface QualityLintResult {\n issues: LintIssue[];\n /** Set when the linter itself threw — the template is structurally suspect. */\n error?: string;\n}\n\n/**\n * Quality layer — accessibility / structure / link linting.\n *\n * Assumes structurally-valid input, so callers should run `validateTemplate`\n * first; the try/catch is a guard against a malformed template crashing the\n * linter rather than a substitute for that ordering.\n */\nexport function runQualityLint(data: unknown): QualityLintResult {\n try {\n return { issues: lintTemplate(data as TemplateContent) ?? [] };\n } catch (err) {\n return { issues: [], error: (err as Error).message };\n }\n}\n","// Authoring checks over custom block HTML and CSS. Client support facts live\n// in @templatical/quality and are applied by checkCustomBlock.\nimport { Parser } from \"htmlparser2\";\nimport type { RenderedState } from \"./render\";\nimport type { CustomBlockIssue, CustomBlockWorkingFile } from \"./types\";\n\n/** Style-attribute declarations reused by the custom-block accessibility checks. */\nexport function declarations(style: string): [string, string][] {\n const out: [string, string][] = [];\n for (const part of style.split(\";\")) {\n const colon = part.indexOf(\":\");\n if (colon > 0)\n out.push([\n part.slice(0, colon).trim().toLowerCase(),\n part.slice(colon + 1),\n ]);\n }\n return out;\n}\n\n/** Drops `/* … *\\/` comments in one linear pass; an unterminated comment drops the rest. */\nexport function stripCssComments(css: string): string {\n let out = \"\";\n let i = 0;\n for (;;) {\n const open = css.indexOf(\"/*\", i);\n if (open === -1) return out + css.slice(i);\n out += css.slice(i, open);\n const close = css.indexOf(\"*/\", open + 2);\n if (close === -1) return out;\n i = close + 2;\n }\n}\n\n/**\n * Remove `url(...)` and quoted strings so the class scan can't read `.png` or\n * `\"x.y\"` as a selector. Linear: each opener's next position is searched for\n * again only once the cursor has passed it, so an opener that never closes\n * costs one scan, not one per occurrence. An unterminated `url(` or quote\n * drops the rest, as a CSS parser would.\n */\nexport function stripCssUrlsAndStrings(css: string): string {\n const lower = css.toLowerCase();\n const next = (needle: string, from: number) => {\n const at = lower.indexOf(needle, from);\n return at === -1 ? Infinity : at;\n };\n const closers: Record<string, string> = { \"url(\": \")\", '\"': '\"', \"'\": \"'\" };\n const pos: Record<string, number> = { \"url(\": 0, '\"': 0, \"'\": 0 };\n for (const k of Object.keys(pos)) pos[k] = next(k, 0);\n let out = \"\";\n let i = 0;\n for (;;) {\n let opener = \"\";\n let open = Infinity;\n for (const k of Object.keys(pos)) {\n if (pos[k] < i) pos[k] = next(k, i);\n if (pos[k] < open) {\n open = pos[k];\n opener = k;\n }\n }\n if (open === Infinity) return out + css.slice(i);\n out += css.slice(i, open);\n const close = lower.indexOf(closers[opener], open + opener.length);\n if (close === -1) return out;\n i = close + 1;\n }\n}\n\nexport function checkEmailSafety(\n def: CustomBlockWorkingFile,\n rendered: RenderedState[],\n): CustomBlockIssue[] {\n const issues: CustomBlockIssue[] = [];\n const add = (\n ruleId: string,\n message: string,\n suggestion: string,\n path?: string,\n ) => {\n issues.push({\n ruleId,\n severity: \"warning\",\n message,\n suggestion,\n reference: \"https://docs.templatical.com/guide/custom-blocks\",\n ...(path ? { path } : {}),\n });\n };\n for (const { state, html } of rendered) {\n const parser = new Parser(\n {\n onopentag(name, attrs) {\n if (name === \"style\")\n add(\n \"safety.style-tag\",\n \"A <style> element is inside the custom block HTML.\",\n \"Move its rules into the definition's stylesheet, which the renderer places in <mj-head>.\",\n );\n if (name !== \"img\") return;\n if (!(\"alt\" in attrs))\n add(\n \"safety.img-alt\",\n \"An <img> element has no alt attribute.\",\n 'Add alt text, or alt=\"\" for a decorative image.',\n );\n if (!(\"width\" in attrs))\n add(\n \"safety.img-width\",\n \"An <img> element has no width attribute.\",\n \"Add a width attribute for Outlook.\",\n );\n if ((attrs.src ?? \"\") === \"\")\n add(\n \"safety.empty-img-src\",\n `An <img> element has an empty src in the \"${state}\" state.`,\n \"Wrap the image in a Liquid condition that checks the source against blank.\",\n );\n },\n },\n { decodeEntities: true },\n );\n parser.end(html);\n }\n if (def.stylesheet) {\n const stripped = stripCssUrlsAndStrings(stripCssComments(def.stylesheet));\n const prefix = `tplc-${def.type}-`;\n const unscoped = [\n ...new Set(\n [...stripped.matchAll(/\\.(-?[_a-zA-Z][\\w-]*)/g)].map(\n (match) => match[1],\n ),\n ),\n ].filter((name) => !name.startsWith(prefix));\n if (unscoped.length > 0)\n add(\n \"safety.unscoped-class\",\n `Classes ${unscoped.map((name) => `.${name}`).join(\", \")} aren't prefixed \\`${prefix}\\`; the SDK doesn't scope them, so they can collide with other blocks.`,\n `Prefix each class with ${prefix}.`,\n \"/stylesheet\",\n );\n }\n const seen = new Set<string>();\n return issues.filter((issue) => {\n const key = `${issue.ruleId}\\0${issue.path ?? \"\"}\\0${issue.message}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n","// Accessibility over a custom block's rendered HTML, on every specimen state.\n// Built-in blocks get @templatical/quality's rules from structured fields; a\n// custom block is opaque HTML, so these read the markup itself. All are\n// warnings: they read inline markup only, and a block's colours or table use\n// can come from context they can't see.\n\nimport { Parser } from \"htmlparser2\";\nimport {\n getContrastRatio,\n isOpaqueHex,\n lintAccessibility,\n} from \"@templatical/quality\";\nimport {\n createDefaultTemplateContent,\n createParagraphBlock,\n} from \"@templatical/types\";\nimport type { RenderedState } from \"./render\";\nimport { declarations, stripCssUrlsAndStrings } from \"./safety\";\nimport type { CustomBlockIssue } from \"./types\";\n\nconst HEX = /#[0-9a-f]{3,8}\\b/i;\nconst PX = /^\\s*(\\d+(?:\\.\\d+)?)px/i;\n\ninterface Frame {\n color?: string;\n background?: string;\n fontSize?: number;\n}\n\n/** The colours and font size an element sets inline, over its parent's. */\nfunction frameFor(attrs: Record<string, string>, parent: Frame): Frame {\n const frame: Frame = { ...parent };\n if (attrs.bgcolor && isOpaqueHex(attrs.bgcolor))\n frame.background = attrs.bgcolor;\n for (const [prop, raw] of declarations(attrs.style ?? \"\")) {\n const value = raw.replace(/!important/i, \"\").trim();\n if (prop === \"color\" && isOpaqueHex(value)) frame.color = value;\n if (prop === \"background-color\" && isOpaqueHex(value))\n frame.background = value;\n if (prop === \"background\") {\n const hex = stripCssUrlsAndStrings(value).match(HEX)?.[0];\n if (hex && isOpaqueHex(hex)) frame.background = hex;\n }\n if (prop === \"font-size\") {\n const px = value.match(PX)?.[1];\n if (px) frame.fontSize = Number(px);\n }\n }\n return frame;\n}\n\n/** quality's own vague-link rule, run over the HTML as if it were a paragraph. */\nfunction vagueLinkMessages(html: string): string[] {\n const content = createDefaultTemplateContent();\n content.blocks = [createParagraphBlock({ content: html })];\n return lintAccessibility(content)\n .filter((i) => i.ruleId === \"a11y.link-vague-text\")\n .map((i) => i.message);\n}\n\nexport function checkAccessibility(\n rendered: RenderedState[],\n): CustomBlockIssue[] {\n const issues: CustomBlockIssue[] = [];\n const add = (ruleId: string, message: string) =>\n issues.push({ ruleId, severity: \"warning\", message });\n\n for (const { state, html } of rendered) {\n for (const message of vagueLinkMessages(html)) {\n add(\"a11y.link-vague-text\", message);\n }\n\n const frames: Frame[] = [{}];\n const tables: { presentation: boolean; hasHeader: boolean }[] = [];\n let link: { named: boolean } | null = null;\n\n const parser = new Parser(\n {\n onopentag(name, attrs) {\n frames.push(frameFor(attrs, frames[frames.length - 1]));\n if (name === \"table\") {\n const role = (attrs.role ?? \"\").toLowerCase();\n tables.push({\n presentation: role === \"presentation\" || role === \"none\",\n hasHeader: false,\n });\n }\n if (name === \"th\" && tables.length > 0) {\n tables[tables.length - 1].hasHeader = true;\n }\n if (name === \"a\") {\n link = {\n named:\n (attrs[\"aria-label\"] ?? \"\").trim() !== \"\" ||\n (attrs.title ?? \"\").trim() !== \"\",\n };\n }\n if (name === \"img\" && link && (attrs.alt ?? \"\").trim() !== \"\") {\n link.named = true;\n }\n },\n ontext(text) {\n const trimmed = text.trim();\n if (trimmed === \"\") return;\n if (link) link.named = true;\n const { color, background, fontSize } = frames[frames.length - 1];\n if (!color || !background) return;\n const required = (fontSize ?? 0) >= 24 ? 3 : 4.5;\n const ratio = getContrastRatio(color, background);\n if (Number.isNaN(ratio) || ratio >= required) return;\n add(\n \"a11y.contrast\",\n `Text in ${color} on ${background} has ${ratio.toFixed(2)}:1 contrast; it needs ${required}:1.`,\n );\n },\n onclosetag(name) {\n frames.pop();\n if (name === \"table\") {\n const table = tables.pop();\n if (table && !table.presentation && !table.hasHeader) {\n add(\n \"a11y.table-role\",\n 'A layout `<table>` needs `role=\"presentation\"`, or screen readers read it as a data table, cell by cell. A data table needs `<th>` headers instead.',\n );\n }\n }\n if (name === \"a\" && link) {\n if (!link.named) {\n add(\n \"a11y.link-no-name\",\n `A link has no text, \\`aria-label\\` or image \\`alt\\` in the \"${state}\" state, so a screen reader announces only \"link\".`,\n );\n }\n link = null;\n }\n },\n },\n { decodeEntities: true },\n );\n parser.write(html);\n parser.end();\n }\n\n const seen = new Set<string>();\n return issues.filter((i) => {\n const key = `${i.ruleId}\\0${i.message}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n","","// Structural validation of a custom block definition: the generated\n// CustomBlockDefinition schema (dataSource excluded, see generate-schema.mjs),\n// the hand-written dataSourcePreview recipe schema, and the field-model checks\n// a JSON Schema can't express (duplicate keys, bounds that are ordered,\n// defaults within bounds). It takes a JSON working file or the object a\n// consumer's own module exports, `dataSource` function included, so the same\n// check can run in their CI over the definitions they ship.\n\nimport Ajv, { type ErrorObject } from \"ajv\";\nimport rawSchema from \"../../custom-block-schema.json\";\nimport type { CustomBlockCheckResult, CustomBlockIssue } from \"./types\";\n\nconst ajv = new Ajv({ allErrors: true, strict: false });\nconst validateDefinition = ajv.compile(rawSchema as object);\n\n// Owned by this package: the recipe is a template-tools concept, not part of\n// the SDK's block model, so it has no generated schema to drift from.\n// Reports the first problem only: a recipe is small, and one pointed message\n// reads better than a cascade of missing-property errors.\nconst validateRecipe = new Ajv({ allErrors: false, strict: false }).compile({\n type: \"object\",\n additionalProperties: false,\n required: [\"label\", \"request\", \"map\"],\n properties: {\n label: { type: \"string\", minLength: 1 },\n request: {\n type: \"object\",\n additionalProperties: false,\n required: [\"url\"],\n properties: {\n method: { enum: [\"GET\", \"POST\"] },\n url: { type: \"string\", minLength: 1 },\n headers: { type: \"object\", additionalProperties: { type: \"string\" } },\n body: { type: \"string\" },\n },\n },\n map: {\n type: \"object\",\n additionalProperties: { type: \"string\", minLength: 1 },\n },\n },\n});\n\nfunction schemaIssues(\n errors: ErrorObject[] | null | undefined,\n base = \"\",\n): CustomBlockIssue[] {\n return (errors ?? []).map((e) => ({\n ruleId: \"schema\",\n severity: \"error\",\n path: `${base}${e.instancePath}` || \"/\",\n message: `${base}${e.instancePath || \"\"} ${e.message ?? \"is invalid\"}${\n e.keyword === \"additionalProperties\"\n ? ` (\\`${(e.params as { additionalProperty: string }).additionalProperty}\\`)`\n : \"\"\n }`.trim(),\n }));\n}\n\ninterface FieldLike {\n key: string;\n type: string;\n default?: unknown;\n min?: number;\n max?: number;\n minItems?: number;\n maxItems?: number;\n options?: { value: string }[];\n fields?: FieldLike[];\n}\n\nconst isCount = (n: number | undefined) =>\n n === undefined || (Number.isInteger(n) && n >= 0);\n\nfunction fieldIssues(fields: FieldLike[], base: string): CustomBlockIssue[] {\n const issues: CustomBlockIssue[] = [];\n const seen = new Set<string>();\n fields.forEach((field, i) => {\n const path = `${base}/${i}`;\n if (seen.has(field.key)) {\n issues.push({\n ruleId: \"field.duplicate-key\",\n severity: \"error\",\n path,\n message: `Field key \\`${field.key}\\` is used twice; the second would overwrite the first's value.`,\n });\n }\n seen.add(field.key);\n if (\n field.type === \"number\" &&\n field.min !== undefined &&\n field.max !== undefined &&\n field.min > field.max\n ) {\n issues.push({\n ruleId: \"field.range\",\n severity: \"error\",\n path,\n message: `\\`${field.key}\\` has min ${field.min} above max ${field.max}, so no value is allowed.`,\n });\n }\n if (field.type === \"number\" && typeof field.default === \"number\") {\n const below = field.min !== undefined && field.default < field.min;\n const above = field.max !== undefined && field.default > field.max;\n if (below || above) {\n issues.push({\n ruleId: \"field.default-range\",\n severity: \"error\",\n path: `${path}/default`,\n message: `\\`${field.key}\\` defaults to ${field.default}, outside ${field.min ?? \"-∞\"}–${field.max ?? \"∞\"}.`,\n });\n }\n }\n if (\n field.type === \"select\" &&\n field.default !== undefined &&\n !(field.options ?? []).some((o) => o.value === field.default)\n ) {\n issues.push({\n ruleId: \"field.default-option\",\n severity: \"error\",\n path: `${path}/default`,\n message: `\\`${field.key}\\` defaults to \"${String(field.default)}\", which is not one of its options.`,\n });\n }\n if (field.type === \"repeatable\") {\n const { minItems, maxItems } = field;\n if (!isCount(minItems) || !isCount(maxItems)) {\n issues.push({\n ruleId: \"field.items-range\",\n severity: \"error\",\n path,\n message: `\\`${field.key}\\`'s minItems and maxItems must be whole numbers of 0 or more.`,\n });\n } else if (\n minItems !== undefined &&\n maxItems !== undefined &&\n minItems > maxItems\n ) {\n issues.push({\n ruleId: \"field.items-range\",\n severity: \"error\",\n path,\n message: `\\`${field.key}\\` has minItems ${minItems} above maxItems ${maxItems}, so no list length is allowed.`,\n });\n } else if (Array.isArray(field.default)) {\n const n = field.default.length;\n if (\n (minItems !== undefined && n < minItems) ||\n (maxItems !== undefined && n > maxItems)\n ) {\n issues.push({\n ruleId: \"field.default-items\",\n severity: \"error\",\n path: `${path}/default`,\n message: `\\`${field.key}\\` defaults to ${n} item(s), outside ${minItems ?? 0}–${maxItems ?? \"∞\"}; the palette would insert a list the editor won't allow.`,\n });\n }\n }\n if (field.fields) {\n issues.push(...fieldIssues(field.fields, `${path}/fields`));\n }\n }\n });\n return issues;\n}\n\n/**\n * A module's `dataSource` is `{ label, onFetch }` with a function, which the\n * schema can't describe (the generator hides functions). A JSON working file\n * can't hold a function at all, so there it is always an error that points at\n * `dataSourcePreview`.\n */\nfunction dataSourceIssues(dataSource: unknown): CustomBlockIssue[] {\n const ds = dataSource as { label?: unknown; onFetch?: unknown } | null;\n const issue = (message: string): CustomBlockIssue[] => [\n { ruleId: \"schema\", severity: \"error\", path: \"/dataSource\", message },\n ];\n if (ds === null || typeof ds !== \"object\" || Array.isArray(ds)) {\n return issue(\"`dataSource` must be an object with `label` and `onFetch`.\");\n }\n if (typeof ds.onFetch !== \"function\") {\n return issue(\n \"`dataSource.onFetch` must be a function. A JSON working file can't hold one: preview with `dataSourcePreview`, and write `dataSource` in the module at handoff.\",\n );\n }\n if (typeof ds.label !== \"string\" || ds.label === \"\") {\n return issue(\"`dataSource.label` must be a non-empty string.\");\n }\n return [];\n}\n\nexport function validateCustomBlockDefinition(\n data: unknown,\n): CustomBlockCheckResult {\n if (data === null || typeof data !== \"object\" || Array.isArray(data)) {\n return {\n valid: false,\n issues: [\n {\n ruleId: \"schema\",\n severity: \"error\",\n path: \"/\",\n message: \"A custom block definition must be a JSON object.\",\n },\n ],\n };\n }\n const { dataSourcePreview, dataSource, ...definition } = data as Record<\n string,\n unknown\n >;\n const issues: CustomBlockIssue[] = [];\n if (dataSource !== undefined) issues.push(...dataSourceIssues(dataSource));\n if (!validateDefinition(definition)) {\n issues.push(...schemaIssues(validateDefinition.errors));\n }\n if (dataSourcePreview !== undefined && !validateRecipe(dataSourcePreview)) {\n issues.push(...schemaIssues(validateRecipe.errors, \"/dataSourcePreview\"));\n }\n if (issues.length === 0) {\n issues.push(\n ...fieldIssues((definition as { fields: FieldLike[] }).fields, \"/fields\"),\n );\n }\n return { valid: !issues.some((i) => i.severity === \"error\"), issues };\n}\n","// Static analysis of a custom block's Liquid template against its field\n// model. Block output uses the shared @templatical/types Liquid factory,\n// so checks, CLI rendering, the editor and headless callers share one engine.\n// Recipes use their separate, unescaped factory below.\n\nimport * as liquidjs from \"liquidjs\";\nimport { createCustomBlockLiquid } from \"@templatical/types\";\nimport type { CustomBlockIssue, CustomBlockWorkingFile } from \"./types\";\n\nexport function createLiquid(): liquidjs.Liquid {\n return createCustomBlockLiquid(liquidjs);\n}\n\n/** Request URLs and JSON bodies need their original, non-HTML escaping. */\nexport function createRecipeLiquid(): liquidjs.Liquid {\n return new liquidjs.Liquid({\n strictVariables: false,\n strictFilters: false,\n // Resolve file tags against an empty map, never the disk: a recipe\n // body must not be able to carry a local file to the API host.\n templates: {},\n });\n}\n\n// `forloop` is Liquid's own loop object, never a field.\nconst LIQUID_GLOBALS = new Set([\"forloop\"]);\n\n// Tags that load another template file. A custom block's template and a\n// recipe are each one string, so nothing exists for them to load.\nconst FILE_TAGS = new Set([\"include\", \"render\", \"layout\"]);\n\n// Static analysis follows include/render into the partial by default, which\n// the engine refuses to load, so every analysis call skips partials. The\n// file-tag rule reports those tags instead.\nexport const NO_PARTIALS = { partials: false } as const;\n\n/** A tag's child templates, from liquidjs's static-analysis generator. */\nfunction childTemplates(template: liquidjs.Template): liquidjs.Template[] {\n if (!template.children) return [];\n const gen = template.children(false, true);\n let step = gen.next();\n while (!step.done) step = gen.next();\n return step.value ?? [];\n}\n\n/** Every template in the tree, depth-first, nested tags included. */\nfunction allTemplates(templates: liquidjs.Template[]): liquidjs.Template[] {\n return templates.flatMap((t) => [t, ...allTemplates(childTemplates(t))]);\n}\n\n/** Every `for` tag in the tree, nested ones included. */\nfunction forTags(templates: liquidjs.Template[]): liquidjs.ForTag[] {\n return allTemplates(templates).filter(\n (t): t is liquidjs.ForTag => t instanceof liquidjs.ForTag,\n );\n}\n\n/** Names of the file-loading tags in the tree, each once, in source order. */\nexport function fileTags(templates: liquidjs.Template[]): string[] {\n const names = allTemplates(templates)\n .filter(\n (t): t is liquidjs.Tag =>\n t instanceof liquidjs.Tag && FILE_TAGS.has(t.name),\n )\n .map((t) => t.name);\n return [...new Set(names)];\n}\n\n/** How many `{{ }}` outputs in the tree reach the text without a `json` filter. */\nexport function outputsWithoutJson(templates: liquidjs.Template[]): number {\n return allTemplates(templates).filter(\n (t) =>\n t instanceof liquidjs.Output &&\n !t.value.filters.some((f) => f.name === \"json\"),\n ).length;\n}\n\n/** The message for a template or recipe that uses a file-loading tag. */\nexport function fileTagMessage(name: string, where: string): string {\n return `\\`{% ${name} %}\\` loads another template file, and ${where} has none to load. Write its content inline instead.`;\n}\n\n/**\n * The field a loop iterates, when its collection is a bare identifier.\n * `for x in a.b` iterates a property of `a`, not `a`, so it has none.\n */\nfunction bareCollection(tag: liquidjs.ForTag): string | null {\n const c = tag.collection as {\n variable?: unknown;\n props?: Array<{ content?: unknown }>;\n };\n if (c.variable !== undefined || c.props?.length !== 1) return null;\n const name = c.props[0].content;\n return typeof name === \"string\" ? name : null;\n}\n\nexport function checkLiquid(def: CustomBlockWorkingFile): CustomBlockIssue[] {\n const engine = createLiquid();\n let parsed: liquidjs.Template[];\n try {\n parsed = engine.parse(def.template);\n } catch (err) {\n return [\n {\n ruleId: \"liquid.parse\",\n severity: \"error\",\n path: \"/template\",\n message: `The template does not parse: ${(err as Error).message}`,\n },\n ];\n }\n\n const issues: CustomBlockIssue[] = fileTags(parsed).map((name) => ({\n ruleId: \"liquid.file-tag\",\n severity: \"error\",\n path: \"/template\",\n message: fileTagMessage(name, \"a custom block\"),\n }));\n const fields = new Map(\n def.fields.map((f, i) => [f.key, { field: f, index: i }]),\n );\n\n const globals = new Set(\n engine\n .globalVariableSegmentsSync(def.template, NO_PARTIALS)\n .map((segments) => String(segments[0]))\n .filter((name) => !LIQUID_GLOBALS.has(name)),\n );\n for (const name of globals) {\n if (!fields.has(name)) {\n issues.push({\n ruleId: \"liquid.undefined-variable\",\n severity: \"error\",\n path: \"/template\",\n message: `The template reads \\`${name}\\`, which no field defines.`,\n });\n }\n }\n // A field only the data-source recipe reads (the product id it fetches by)\n // is in use; recipe Liquid that fails to parse is reported by checkRecipe.\n const recipeReads = new Set<string>();\n const request = def.dataSourcePreview?.request;\n for (const text of [request?.url, request?.body]) {\n if (text === undefined) continue;\n try {\n for (const [name] of engine.globalVariableSegmentsSync(\n text,\n NO_PARTIALS,\n )) {\n recipeReads.add(String(name));\n }\n } catch {\n /* reported by checkRecipe */\n }\n }\n for (const [key, { index }] of fields) {\n if (!globals.has(key) && !recipeReads.has(key)) {\n issues.push({\n ruleId: \"liquid.unused-field\",\n severity: \"warning\",\n path: `/fields/${index}`,\n message: `Field \\`${key}\\` is never read by the template or the data-source recipe.`,\n });\n }\n }\n\n // Each loop's body is checked against that loop's own collection, so two\n // loops may reuse one local name over different repeatables.\n for (const loop of forTags(parsed)) {\n const local = loop.variable;\n const collection = bareCollection(loop);\n if (collection === null) continue;\n const target = fields.get(collection)?.field;\n if (!target) continue;\n if (target.type !== \"repeatable\") {\n issues.push({\n ruleId: \"liquid.loop-not-repeatable\",\n severity: \"error\",\n path: \"/template\",\n message: `\\`{% for ${local} in ${collection} %}\\` iterates \\`${collection}\\`, which is a ${target.type} field, not a repeatable.`,\n });\n continue;\n }\n for (const [head, sub] of engine.variableSegmentsSync(\n loop.templates,\n NO_PARTIALS,\n )) {\n if (String(head) !== local || typeof sub !== \"string\") continue;\n if (!target.fields.some((f) => f.key === sub)) {\n issues.push({\n ruleId: \"liquid.unknown-item-key\",\n severity: \"error\",\n path: \"/template\",\n message: `\\`${local}.${sub}\\` reads \\`${sub}\\`, which \\`${collection}\\`'s items don't define.`,\n });\n }\n }\n }\n return issues;\n}\n","// The preview-only data-source recipe. The live bridge runs it from Node so\n// the page never sees a secret and no CORS applies; the agent generates the\n// shipped onFetch from the same recipe, so preview and production agree.\n//\n// These rules keep secrets contained, and each is load-bearing:\n// - `${env:NAME}` resolves only in header values;\n// - field values interpolate only into `url` and `body`;\n// - the Liquid engine loads no files, so `{% include %}` can't put a local\n// file (`.env`) into the request;\n// - the URL's scheme and host are literal, every field value is URL-encoded\n// (non-scalars as JSON), and the rendered URL must keep the recipe's\n// origin, so no field value can send the headers to another host.\n// A field value can never pull an env var into the request, and a secret can\n// never be moved into the URL or off to a host the author didn't write.\n\nimport {\n createRecipeLiquid,\n fileTagMessage,\n fileTags,\n NO_PARTIALS,\n outputsWithoutJson,\n} from \"./liquid\";\nimport type {\n CustomBlockIssue,\n CustomBlockWorkingFile,\n DataSourcePreview,\n} from \"./types\";\n\nconst ENV_REF = /\\$\\{env:([A-Za-z_][A-Za-z0-9_]*)\\}/g;\nconst LIQUID = /\\{\\{|\\{%/;\nconst UNSAFE_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n// A header-name word that names a credential. Whole words of the name split\n// on - and _, so X-Session-Locale and Accept-Keys don't match while X-API-Key,\n// X-Auth-Token and X-AccessToken do. `session` counts only before `id`.\nconst CREDENTIAL_WORD =\n /^(?:authorization|auth|cookie|key|apikey|password|credentials?|sessionid)$|(?:token|secret)$/;\n\nfunction namesCredential(header: string): boolean {\n const words = header.toLowerCase().split(/[-_]/);\n return words.some(\n (w, i) =>\n CREDENTIAL_WORD.test(w) || (w === \"session\" && words[i + 1] === \"id\"),\n );\n}\n\nconst HOST_TEMPLATE_ERROR =\n \"The request URL's scheme and host must be literal; Liquid may only follow the first `/` after the host.\";\n\n/**\n * Where the URL's scheme and authority end: the first `/`, `?` or `#` after\n * `://`, or the whole string when there is no `://`. Anything before this\n * index decides the origin, so it must hold no Liquid.\n */\nfunction authorityEnd(url: string): number {\n const scheme = url.indexOf(\"://\");\n if (scheme < 0) return url.length;\n const rest = url.slice(scheme + 3).search(/[/?#]/);\n return rest < 0 ? url.length : scheme + 3 + rest;\n}\n\n/** The recipe URL's origin, or null when Liquid can reach it. */\nfunction literalOrigin(url: string): string | null {\n const prefix = url.slice(0, authorityEnd(url));\n if (LIQUID.test(prefix)) return null;\n try {\n return new URL(prefix).origin;\n } catch {\n return null;\n }\n}\n\nfunction encodeValue(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"string\" || typeof value === \"number\") {\n return encodeURIComponent(String(value));\n }\n return encodeURIComponent(JSON.stringify(value));\n}\n\nexport function getPath(value: unknown, path: string): unknown {\n const parts = path\n .replace(/\\[(\\d+)\\]/g, \".$1\")\n .split(\".\")\n .filter(Boolean);\n let cur: unknown = value;\n for (const part of parts) {\n if (cur === null || typeof cur !== \"object\") return undefined;\n cur = (cur as Record<string, unknown>)[part];\n }\n return cur;\n}\n\nexport function checkRecipe(def: CustomBlockWorkingFile): CustomBlockIssue[] {\n const r = def.dataSourcePreview;\n if (!r) return [];\n const issues: CustomBlockIssue[] = [];\n const keys = new Set(def.fields.map((f) => f.key));\n const add =\n (severity: CustomBlockIssue[\"severity\"]) =>\n (ruleId: string, path: string, message: string) =>\n issues.push({\n ruleId,\n severity,\n path: `/dataSourcePreview${path}`,\n message,\n });\n const err = add(\"error\");\n const warn = add(\"warning\");\n const engine = createRecipeLiquid();\n\n for (const [part, text] of [\n [\"/request/url\", r.request.url],\n [\"/request/body\", r.request.body],\n ] as const) {\n if (text === undefined) continue;\n if (new RegExp(ENV_REF.source).test(text)) {\n err(\n \"recipe.env-outside-headers\",\n part,\n \"`${env:…}` is resolved only in header values; keep secrets out of the URL and body.\",\n );\n }\n try {\n for (const [name] of engine.globalVariableSegmentsSync(\n text,\n NO_PARTIALS,\n )) {\n if (!keys.has(String(name)))\n err(\n \"recipe.undefined-variable\",\n part,\n `The recipe reads \\`${String(name)}\\`, which no field defines.`,\n );\n }\n // The engine can't read files, so such a recipe would fail at fetch\n // time; refusing it here keeps the bridge from running it at all.\n const parsed = engine.parse(text);\n for (const name of fileTags(parsed)) {\n err(\"recipe.file-tag\", part, fileTagMessage(name, \"a recipe\"));\n }\n // Body values go in raw, so a quote typed into a field ends the string\n // early. `| json` quotes and escapes the value.\n if (\n part === \"/request/body\" &&\n /^\\s*[[{]/.test(text) &&\n outputsWithoutJson(parsed) > 0\n ) {\n warn(\n \"recipe.body-unescaped\",\n part,\n \"A JSON body interpolates a value without `| json`, so a quote in it breaks the JSON. Write `{{ key | json }}`, which adds the quotes.\",\n );\n }\n } catch (e) {\n err(\n \"recipe.undefined-variable\",\n part,\n `Does not parse: ${(e as Error).message}`,\n );\n }\n }\n // Without `://` the URL is not absolute, which recipe.url below reports.\n if (\n r.request.url.includes(\"://\") &&\n LIQUID.test(r.request.url.slice(0, authorityEnd(r.request.url)))\n ) {\n err(\"recipe.url-host-template\", \"/request/url\", HOST_TEMPLATE_ERROR);\n }\n const probeUrl = replaceLiquid(r.request.url);\n let parses = true;\n try {\n new URL(probeUrl);\n } catch {\n parses = false;\n }\n if (!/^https?:\\/\\//i.test(probeUrl) || !parses) {\n err(\n \"recipe.url\",\n \"/request/url\",\n \"The request URL must be an absolute http(s) URL.\",\n );\n }\n if (r.request.body !== undefined && (r.request.method ?? \"GET\") !== \"POST\") {\n err(\n \"recipe.body-without-post\",\n \"/request/body\",\n 'A `body` is sent only with `\"method\": \"POST\"`; a GET request can\\'t carry one.',\n );\n }\n for (const [name, value] of Object.entries(r.request.headers ?? {})) {\n if (LIQUID.test(value)) {\n err(\n \"recipe.field-in-header\",\n `/request/headers/${name}`,\n `Header \\`${name}\\` interpolates a field; headers take only \\`\\${env:NAME}\\`.`,\n );\n } else if (\n namesCredential(name) &&\n !new RegExp(ENV_REF.source).test(value)\n ) {\n err(\n \"recipe.literal-secret\",\n `/request/headers/${name}`,\n `Header \\`${name}\\` holds a literal credential. Use \\`\\${env:NAME}\\` and export the variable instead.`,\n );\n }\n }\n for (const field of Object.keys(r.map)) {\n if (!keys.has(field))\n err(\n \"recipe.unknown-map-key\",\n `/map/${field}`,\n `\\`map\\` writes \\`${field}\\`, which no field defines.`,\n );\n }\n return issues;\n}\n\nexport interface RecipeOptions {\n env?: Record<string, string | undefined>;\n fetch?: typeof fetch;\n timeoutMs?: number;\n}\nexport type RecipeResult =\n | {\n ok: true;\n status: number;\n values: Record<string, unknown>;\n unmapped: string[];\n }\n | { ok: false; status?: number; error: string };\n\nexport async function runRecipe(\n recipe: DataSourcePreview,\n fieldValues: Record<string, unknown>,\n {\n env = process.env,\n fetch: doFetch = fetch,\n timeoutMs = 10_000,\n }: RecipeOptions = {},\n): Promise<RecipeResult> {\n const expectedOrigin = literalOrigin(recipe.request.url);\n if (expectedOrigin === null) return { ok: false, error: HOST_TEMPLATE_ERROR };\n const engine = createRecipeLiquid();\n const encoded = Object.fromEntries(\n Object.entries(fieldValues).map(([k, v]) => [k, encodeValue(v)]),\n );\n let url: string;\n let body: string | undefined;\n try {\n url = String(await engine.parseAndRender(recipe.request.url, encoded));\n body =\n recipe.request.body === undefined\n ? undefined\n : String(await engine.parseAndRender(recipe.request.body, fieldValues));\n } catch (e) {\n return {\n ok: false,\n error: `The request template does not render: ${(e as Error).message}`,\n };\n }\n\n let renderedOrigin: string | null = null;\n try {\n renderedOrigin = new URL(url).origin;\n } catch {\n /* reported below */\n }\n // Unreachable while the URL authority refuses Liquid and every value is\n // encoded; it stops the proxy being repointed if either guard regresses.\n /* v8 ignore start */\n if (renderedOrigin !== expectedOrigin) {\n return {\n ok: false,\n error: `The rendered URL's origin (${renderedOrigin ?? \"unparseable\"}) differs from the recipe's (${expectedOrigin}); no request was made.`,\n };\n }\n /* v8 ignore stop */\n\n const secrets: string[] = [];\n const headers: Record<string, string> = {};\n for (const [name, template] of Object.entries(recipe.request.headers ?? {})) {\n let missing: string | undefined;\n headers[name] = template.replace(ENV_REF, (_, variable: string) => {\n const value = env[variable];\n if (value === undefined || value === \"\") {\n missing ??= variable;\n return \"\";\n }\n secrets.push(value);\n return value;\n });\n if (missing) {\n return {\n ok: false,\n error: `Environment variable ${missing} is not set. Export it in the shell that runs the live server.`,\n };\n }\n }\n const redact = (s: string) =>\n secrets.reduce((acc, secret) => acc.split(secret).join(\"***\"), s);\n const redactDeep = (v: unknown): unknown => {\n if (typeof v === \"string\") return redact(v);\n if (Array.isArray(v)) return v.map(redactDeep);\n if (v !== null && typeof v === \"object\") {\n return Object.fromEntries(\n Object.entries(v).map(([k, x]) => [k, redactDeep(x)]),\n );\n }\n return v;\n };\n const method = recipe.request.method ?? \"GET\";\n\n try {\n const res = await doFetch(url, {\n method,\n headers,\n body,\n redirect: \"manual\",\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (res.status >= 300 && res.status < 400) {\n const location = res.headers.get(\"location\") ?? \"(none)\";\n return {\n ok: false,\n status: res.status,\n error: redact(\n `${method} ${url} redirected (${res.status}) to ${location}. Redirects are not followed; point the recipe at the final URL.`,\n ),\n };\n }\n const text = await res.text();\n if (!res.ok) {\n return {\n ok: false,\n status: res.status,\n error: `${redact(`${method} ${url} returned ${res.status}: `)}${redact(text).slice(0, 200)}`,\n };\n }\n let json: unknown;\n try {\n json = JSON.parse(text);\n } catch {\n return {\n ok: false,\n status: res.status,\n error: redact(`${method} ${url} did not return JSON.`),\n };\n }\n const values: Record<string, unknown> = {};\n const unmapped: string[] = [];\n for (const [field, path] of Object.entries(recipe.map)) {\n if (UNSAFE_KEYS.has(field)) continue;\n const v = getPath(json, path);\n if (v === undefined) unmapped.push(field);\n else values[field] = redactDeep(v);\n }\n return { ok: true, status: res.status, values, unmapped };\n } catch (e) {\n return {\n ok: false,\n error: redact(`${method} ${url} failed: ${(e as Error).message}`),\n };\n }\n}\n\n/** Replaces each `{{…}}` / `{%…%}` span with \"x\" in one linear pass; an unterminated opener swallows the rest. */\nexport function replaceLiquid(text: string): string {\n const MISSING = Infinity;\n const next = (needle: string, from: number) => {\n const at = text.indexOf(needle, from);\n return at === -1 ? MISSING : at;\n };\n let out = \"\";\n let i = 0;\n // Each opener is searched for again only once the cursor passes its cached\n // position, so an opener that never recurs is scanned for once, not per span.\n let a = next(\"{{\", 0);\n let b = next(\"{%\", 0);\n for (;;) {\n if (a < i) a = next(\"{{\", i);\n if (b < i) b = next(\"{%\", i);\n const open = Math.min(a, b);\n if (open === MISSING) return out + text.slice(i);\n out += text.slice(i, open);\n const close = text.indexOf(open === a ? \"}}\" : \"%}\", open + 2);\n if (close === -1) return out + \"x\";\n out += \"x\";\n i = close + 2;\n }\n}\n","// The specimen sheet: one instance of the block per state that commonly\n// breaks a custom block — defaults, every optional cleared, long copy,\n// repeatables at their bounds, booleans inverted — so the live preview shows\n// the edge cases without the user thinking to try them.\n\nimport { escapeHtml } from \"@templatical/renderer\";\nimport {\n createCustomBlock,\n createDefaultTemplateContent,\n createParagraphBlock,\n type Block,\n type CustomBlockDefinition,\n type CustomBlockField,\n type TemplateContent,\n} from \"@templatical/types\";\nimport type { CustomBlockWorkingFile } from \"./types\";\n\nexport type SpecimenState =\n \"defaults\" | \"empty\" | \"long\" | \"min-items\" | \"max-items\" | \"flipped\";\nexport const SPECIMEN_STATES: readonly SpecimenState[] = [\n \"defaults\",\n \"empty\",\n \"long\",\n \"min-items\",\n \"max-items\",\n \"flipped\",\n];\nexport interface SpecimenInstance {\n state: SpecimenState;\n fieldValues: Record<string, unknown>;\n}\nexport const DEFAULT_MAX_ITEMS = 6;\n\ntype ItemField = Exclude<CustomBlockField, { type: \"repeatable\" }>;\ntype Repeatable = Extract<CustomBlockField, { type: \"repeatable\" }>;\n\n// Same fallbacks as @templatical/types' createCustomBlock, which keeps its own\n// copy private; the \"defaults\" test holds the two equal.\nfunction fieldDefault(f: CustomBlockField): unknown {\n if (f.default !== undefined) return structuredClone(f.default);\n if (f.type === \"repeatable\") return [];\n if (f.type === \"boolean\") return false;\n if (f.type === \"number\") return 0;\n return \"\";\n}\n\nfunction itemFrom(fields: ItemField[]): Record<string, unknown> {\n return Object.fromEntries(fields.map((f) => [f.key, fieldDefault(f)]));\n}\n\nfunction items(f: Repeatable, n: number): Record<string, unknown>[] {\n const seeds =\n Array.isArray(f.default) && f.default.length > 0\n ? f.default\n : [itemFrom(f.fields)];\n return Array.from({ length: n }, (_, i) =>\n structuredClone(seeds[i % seeds.length]),\n );\n}\n\nfunction lengthen(f: CustomBlockField, v: unknown): unknown {\n if (f.type === \"text\" || f.type === \"textarea\") {\n const base = (typeof v === \"string\" && v) || f.placeholder || f.label;\n return [base, base, base].join(\" \");\n }\n if (f.type === \"number\") return f.max ?? v;\n if (f.type === \"repeatable\" && Array.isArray(v)) {\n return v.map((item: Record<string, unknown>) =>\n Object.fromEntries(\n f.fields.map((s) => [s.key, lengthen(s, item[s.key])]),\n ),\n );\n }\n return v;\n}\n\nfunction clear(f: CustomBlockField, v: unknown): unknown {\n if (f.required) return v;\n if (f.type === \"boolean\") return false;\n if (f.type === \"number\") return null;\n if (f.type === \"repeatable\") return items(f, f.minItems ?? 0);\n return \"\";\n}\n\nexport function buildSpecimen(def: CustomBlockWorkingFile): SpecimenInstance[] {\n const defaults = createCustomBlock(def as CustomBlockDefinition).fieldValues;\n const map = (fn: (f: CustomBlockField, v: unknown) => unknown) =>\n Object.fromEntries(\n def.fields.map((f) => [f.key, fn(f, structuredClone(defaults[f.key]))]),\n );\n\n const out: SpecimenInstance[] = [\n { state: \"defaults\", fieldValues: structuredClone(defaults) },\n { state: \"empty\", fieldValues: map(clear) },\n { state: \"long\", fieldValues: map(lengthen) },\n ];\n const repeatables = def.fields.filter(\n (f): f is Repeatable => f.type === \"repeatable\",\n );\n if (repeatables.length > 0) {\n const sized = (pick: (f: Repeatable) => number) => ({\n ...structuredClone(defaults),\n ...Object.fromEntries(repeatables.map((f) => [f.key, items(f, pick(f))])),\n });\n out.push({\n state: \"min-items\",\n fieldValues: sized((f) => f.minItems ?? 0),\n });\n // Never below minItems: without a maxItems, the fallback can sit under\n // the minimum, and the editor never lets a list get that short.\n out.push({\n state: \"max-items\",\n fieldValues: sized((f) =>\n Math.max(f.minItems ?? 0, f.maxItems ?? DEFAULT_MAX_ITEMS),\n ),\n });\n }\n const booleans = def.fields.filter((f) => f.type === \"boolean\");\n if (booleans.length > 0) {\n out.push({\n state: \"flipped\",\n fieldValues: {\n ...structuredClone(defaults),\n ...Object.fromEntries(booleans.map((f) => [f.key, !defaults[f.key]])),\n },\n });\n }\n return out;\n}\n\n/** What running the data-source recipe for the live preview produced. */\nexport type FetchedSpecimen =\n | { ok: true; fieldValues: Record<string, unknown> }\n | { ok: false; error: string };\n\n/**\n * The live preview's \"fetched\" state: the block filled from the real\n * endpoint and marked fetched, so read-only fields lock as they do in\n * production. A failed request puts its error on the label instead.\n */\nexport function fetchedSpecimenBlocks(\n def: CustomBlockWorkingFile,\n fetched: FetchedSpecimen,\n): Block[] {\n const id = \"specimen-fetched\";\n if (!fetched.ok) {\n return [\n createParagraphBlock({\n id: `${id}-label`,\n content: `<p><strong>Specimen:</strong> fetched. The request failed: ${escapeHtml(fetched.error)}</p>`,\n }),\n ];\n }\n return [\n createParagraphBlock({\n id: `${id}-label`,\n content: \"<p><strong>Specimen:</strong> fetched</p>\",\n }),\n {\n ...createCustomBlock(def as CustomBlockDefinition),\n id,\n fieldValues: fetched.fieldValues,\n dataSourceFetched: true,\n },\n ];\n}\n\n/** The id each state's custom block gets in the specimen sheet; its label is `${id}-label`. */\nexport function specimenBlockId(state: SpecimenState): string {\n return `specimen-${state}`;\n}\n\nexport function buildSpecimenTemplate(\n def: CustomBlockWorkingFile,\n host?: TemplateContent,\n only?: SpecimenState,\n): TemplateContent {\n const content = host ? structuredClone(host) : createDefaultTemplateContent();\n for (const instance of buildSpecimen(def)) {\n if (only && instance.state !== only) continue;\n // Fixed ids, not fresh ones: the sheet is rebuilt on every reload and\n // page load, and a note left on a block keys on its id. A random id\n // would orphan every note at the next reload.\n const id = specimenBlockId(instance.state);\n content.blocks.push(\n createParagraphBlock({\n id: `${id}-label`,\n content: `<p><strong>Specimen:</strong> ${instance.state}</p>`,\n }),\n {\n ...createCustomBlock(def as CustomBlockDefinition),\n id,\n fieldValues: instance.fieldValues,\n },\n );\n }\n return content;\n}\n","// Specimen states use the shared Liquid engine; MJML rendering passes the\n// definition to the renderer, which resolves field values and stylesheet.\n\nimport { renderToMjml } from \"@templatical/renderer\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport { createLiquid } from \"./liquid\";\nimport {\n buildSpecimen,\n buildSpecimenTemplate,\n type SpecimenInstance,\n type SpecimenState,\n} from \"./specimen\";\nimport type { CustomBlockWorkingFile } from \"./types\";\n\nexport interface RenderedState {\n state: SpecimenState;\n html: string;\n}\n\nexport async function renderStates(\n def: CustomBlockWorkingFile,\n instances: SpecimenInstance[] = buildSpecimen(def),\n): Promise<RenderedState[]> {\n const liquid = createLiquid();\n return Promise.all(\n instances.map(async (i) => ({\n state: i.state,\n html: String(await liquid.parseAndRender(def.template, i.fieldValues)),\n })),\n );\n}\n\nexport async function renderSpecimenMjml(\n def: CustomBlockWorkingFile,\n template: TemplateContent = buildSpecimenTemplate(def),\n): Promise<string> {\n return renderToMjml(template, { customBlocks: [def] });\n}\n","// The full `custom-block validate` pipeline, as a library function. Stages\n// short-circuit where a later stage would only report noise: no Liquid or\n// render checks on a structurally broken definition, no render on a template\n// that doesn't parse or loads another file, and no lint on one that fails to\n// render.\n\nimport { checkAccessibility } from \"./a11y\";\nimport { validateCustomBlockDefinition } from \"./definition\";\nimport { checkLiquid } from \"./liquid\";\nimport { checkRecipe } from \"./recipe\";\nimport { renderSpecimenMjml, renderStates, type RenderedState } from \"./render\";\nimport { checkEmailSafety } from \"./safety\";\nimport { lintHtmlFragment } from \"@templatical/quality\";\nimport type {\n CustomBlockCheckResult,\n CustomBlockIssue,\n CustomBlockWorkingFile,\n} from \"./types\";\n\nexport interface CheckCustomBlockOptions {\n compileMjml?: (mjml: string) => Promise<{ errors: unknown[] }>;\n}\n\nconst finish = (issues: CustomBlockIssue[]): CustomBlockCheckResult => ({\n valid: !issues.some((i) => i.severity === \"error\"),\n issues,\n});\n\nexport async function checkCustomBlock(\n data: unknown,\n { compileMjml }: CheckCustomBlockOptions = {},\n): Promise<CustomBlockCheckResult> {\n const structural = validateCustomBlockDefinition(data);\n if (!structural.valid) return structural;\n const def = data as CustomBlockWorkingFile;\n\n const issues = [...structural.issues, ...checkRecipe(def)];\n const liquid = checkLiquid(def);\n issues.push(...liquid);\n if (\n liquid.some(\n (i) => i.ruleId === \"liquid.parse\" || i.ruleId === \"liquid.file-tag\",\n )\n )\n return finish(issues);\n\n // A template can parse and still throw while rendering; that is a finding\n // about the definition, not a crash of the check.\n let rendered: RenderedState[];\n try {\n rendered = await renderStates(def);\n } catch (err) {\n issues.push({\n ruleId: \"liquid.render\",\n severity: \"error\",\n path: \"/template\",\n message: `The template does not render: ${(err as Error).message}`,\n });\n return finish(issues);\n }\n issues.push(...checkEmailSafety(def, rendered));\n const strict = [\n \"compat.css-display-flex\",\n \"compat.css-display-grid\",\n \"compat.css-position\",\n \"compat.html-svg\",\n \"compat.html-script\",\n \"compat.css-at-import\",\n \"compat.html-link\",\n ];\n const compatibility = {\n rules: {\n ...Object.fromEntries(strict.map((id) => [id, \"error\"] as const)),\n \"compat.html-style\": \"off\",\n },\n } as const;\n const seenCompatibility = new Set<string>();\n const addCompatibility = (\n html: string,\n path: string,\n stylesheet?: string,\n ) => {\n for (const issue of lintHtmlFragment(html, { stylesheet, compatibility })) {\n const key = `${path}\\0${issue.ruleId}\\0${issue.excerpt ?? \"\"}`;\n if (seenCompatibility.has(key)) continue;\n seenCompatibility.add(key);\n issues.push({\n ruleId: issue.ruleId,\n severity: issue.severity,\n path,\n message: issue.message,\n suggestion: issue.suggestion,\n reference: issue.reference,\n clients: issue.clients,\n excerpt: issue.excerpt,\n });\n }\n };\n for (const { html } of rendered) addCompatibility(html, \"/template\");\n if (def.stylesheet) addCompatibility(\"\", \"/stylesheet\", def.stylesheet);\n issues.push(...checkAccessibility(rendered));\n const mjml = await renderSpecimenMjml(def);\n // The renderer trims the stylesheet before emitting it.\n const stylesheet = def.stylesheet?.trim();\n if (stylesheet && !mjml.includes(stylesheet)) {\n issues.push({\n ruleId: \"render.stylesheet-missing\",\n severity: \"error\",\n message: \"The stylesheet did not reach <mj-head> in the rendered MJML.\",\n });\n }\n if (compileMjml) {\n for (const e of (await compileMjml(mjml)).errors) {\n const o = e as { formattedMessage?: string; message?: string };\n issues.push({\n ruleId: \"render.mjml\",\n severity: \"warning\",\n message: o.formattedMessage ?? o.message ?? String(e),\n });\n }\n }\n return finish(issues);\n}\n"],"mappings":";;;;;;;;ACyBA,MAAa,SAASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { LintIssue } from "@templatical/quality";
2
2
  import { CustomBlockDefinition, OperationError, applyOperations, getColumnCount } from "@templatical/types";
3
- import { Liquid } from "liquidjs";
4
- //#region src/validate.d.ts
3
+ import "liquidjs";
5
4
  interface SchemaDefinition {
6
5
  properties?: {
7
6
  type?: {
@@ -38,8 +37,6 @@ interface QualityLintResult {
38
37
  * linter rather than a substitute for that ordering.
39
38
  */
40
39
  export declare function runQualityLint(data: unknown): QualityLintResult;
41
- //#endregion
42
- //#region src/custom-block/types.d.ts
43
40
  type CustomBlockIssueSeverity = "error" | "warning" | "info";
44
41
  interface CustomBlockIssue {
45
42
  ruleId: string;
@@ -73,21 +70,14 @@ interface CustomBlockCheckResult {
73
70
  valid: boolean;
74
71
  issues: CustomBlockIssue[];
75
72
  }
76
- //#endregion
77
- //#region src/custom-block/check.d.ts
78
73
  interface CheckCustomBlockOptions {
79
74
  compileMjml?: (mjml: string) => Promise<{
80
75
  errors: unknown[];
81
76
  }>;
82
77
  }
83
78
  export declare function checkCustomBlock(data: unknown, { compileMjml }?: CheckCustomBlockOptions): Promise<CustomBlockCheckResult>;
84
- //#endregion
85
- //#region src/custom-block/definition.d.ts
86
79
  export declare function validateCustomBlockDefinition(data: unknown): CustomBlockCheckResult;
87
- //#endregion
88
- //#region src/custom-block/specimen.d.ts
89
80
  type SpecimenState = "defaults" | "empty" | "long" | "min-items" | "max-items" | "flipped";
90
81
  export declare const SPECIMEN_STATES: readonly SpecimenState[];
91
- //#endregion
92
82
  export { type CheckCustomBlockOptions, type CustomBlockCheckResult, type CustomBlockIssue, type CustomBlockIssueSeverity, type CustomBlockWorkingFile, type DataSourcePreview, OperationError, type QualityLintResult, type SpecimenState, type ValidationResult, applyOperations, getColumnCount };
93
83
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { d as schema, f as validateTemplate, l as validateCustomBlockDefinition, r as SPECIMEN_STATES, t as checkCustomBlock, u as runQualityLint } from "./custom-block-B2hUl-PC.js";
1
+ import { d as schema, f as validateTemplate, l as validateCustomBlockDefinition, r as SPECIMEN_STATES, t as checkCustomBlock, u as runQualityLint } from "./custom-block-BgJCmUbD.js";
2
2
  import { OperationError, applyOperations, getColumnCount } from "@templatical/types";
3
3
  export { OperationError, SPECIMEN_STATES, applyOperations, checkCustomBlock, getColumnCount, runQualityLint, schema, validateCustomBlockDefinition, validateTemplate };
@@ -1,6 +1,5 @@
1
1
  import { Server } from "node:http";
2
- //#region src/live/index.d.ts
3
- export declare const EDITOR_VERSION = "0.51.1";
2
+ export declare const EDITOR_VERSION = "0.52.0";
4
3
  export declare const DEFAULT_PORT = 4747;
5
4
  export declare const WORKING_DIR = ".templatical";
6
5
  export declare const PID_FILE: string;
@@ -132,5 +131,4 @@ export interface PreferringHandle extends BridgeHandle {
132
131
  export declare function startBridgePreferring({ preferredPort, ...opts }?: StartBridgeOptions & {
133
132
  preferredPort?: number;
134
133
  }): Promise<PreferringHandle>;
135
- //#endregion
136
134
  //# sourceMappingURL=index.d.ts.map
@@ -1,13 +1,13 @@
1
- import { a as buildSpecimenTemplate, c as runRecipe, f as validateTemplate, i as buildSpecimen, l as validateCustomBlockDefinition, o as fetchedSpecimenBlocks, s as checkRecipe } from "../custom-block-B2hUl-PC.js";
1
+ import { a as buildSpecimenTemplate, c as runRecipe, f as validateTemplate, i as buildSpecimen, l as validateCustomBlockDefinition, o as fetchedSpecimenBlocks, s as checkRecipe } from "../custom-block-BgJCmUbD.js";
2
2
  import { createDefaultTemplateContent } from "@templatical/types";
3
3
  import { createServer } from "node:http";
4
4
  import { spawn } from "node:child_process";
5
5
  import { existsSync, readFileSync, readdirSync } from "node:fs";
6
6
  import { dirname, isAbsolute, join, resolve } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
- //#region src/live/index.ts
8
+ // src/live/index.ts
9
9
  const here = dirname(fileURLToPath(import.meta.url));
10
- const EDITOR_VERSION = "0.51.1";
10
+ const EDITOR_VERSION = "0.52.0";
11
11
  const DEFAULT_PORT = 4747;
12
12
  const WORKING_DIR = ".templatical";
13
13
  const PID_FILE = join(WORKING_DIR, "live-server.pid");
@@ -553,7 +553,6 @@ async function startBridgePreferring({ preferredPort = DEFAULT_PORT, ...opts } =
553
553
  };
554
554
  }
555
555
  }
556
- //#endregion
557
556
  export { DEFAULT_PORT, EDITOR_VERSION, PID_FILE, WORKING_DIR, deepEqual, listWorkingFiles, openBrowser, pidfilePath, processAlive, readPidfile, readWorkingFile, startBridge, startBridgePreferring };
558
557
 
559
558
  //# sourceMappingURL=index.js.map