{"version":3,"file":"ngx-t-forms-token-transform-D_wsFbNc.mjs","sources":["../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/_shared/field-bindings.ts","../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/payload-template-editor/token-transform.ts"],"sourcesContent":["/**\r\n * @file Field → `$`-picker binding derivation, shared by every token-binding\r\n * JSON editor (payload templates, header templates).\r\n *\r\n * Both editors offer the same gesture — type `$`, pick a form field, get its\r\n * `{{token}}` inserted — over the same {@link JsonEditorComponent}. The only\r\n * thing that differs is the document being edited, so the field list, the icon\r\n * and kind labels, and the friendly-label ⇄ id aliasing live here rather than\r\n * being copied per editor.\r\n *\r\n * Pure: no Angular DI, no signals. Callers wrap these in `computed()`.\r\n */\r\nimport { ElementTypes } from 'ngx-t-forms-types';\r\nimport type { FormColumnInputs } from 'ngx-t-forms-types';\r\n\r\nimport { getInputIcon } from '../../../../shared/functions/getInputIcon';\r\nimport type { EditorBinding } from '../json-editor/json-editor.component';\r\nimport type { TokenField } from '../payload-template-editor/token-transform';\r\n\r\n/**\r\n * Turns an element type into a short, friendly field-kind label — the muted\r\n * second line in the picker that disambiguates same-named fields.\r\n *\r\n * @param element - The input's element type.\r\n * @returns A spaced, capitalised label (e.g. `MultipleInput` → `Multiple Input`).\r\n */\r\nfunction humanizeElement(element: ElementTypes): string {\r\n  return String(element)\r\n    .replace(/([a-z])([A-Z])/g, '$1 $2')\r\n    .replace(/^./, (c) => c.toUpperCase());\r\n}\r\n\r\n/**\r\n * The inputs a token may bind to.\r\n *\r\n * Excludes what cannot carry a value at request time: a `SectionTitle` (layout\r\n * only) and a MultipleInput **sub-row** input (`multipleInputInEditId`), whose\r\n * control exists only while its row is open — a token bound to one would resolve\r\n * empty for every closed row and hold the fetch idle forever.\r\n *\r\n * @param formInputs - Every input on the form being configured.\r\n * @returns The bindable inputs, unfiltered by anything else.\r\n */\r\nexport function bindableInputs(\r\n  formInputs: readonly FormColumnInputs[],\r\n): readonly FormColumnInputs[] {\r\n  return formInputs.filter(\r\n    (input) => input.element !== ElementTypes.SectionTitle && !input.multipleInputInEditId && !!input.id,\r\n  );\r\n}\r\n\r\n/**\r\n * The bindable inputs as id/label pairs — the input to `buildTokenMaps`.\r\n *\r\n * @param formInputs - Every input on the form being configured.\r\n * @returns One {@link TokenField} per bindable input, labelled by its display\r\n *   label (falling back to the raw id when none is authored).\r\n */\r\nexport function bindableFields(formInputs: readonly FormColumnInputs[]): readonly TokenField[] {\r\n  return bindableInputs(formInputs).map((input) => ({\r\n    id: input.id,\r\n    label: input.label?.trim() || input.id,\r\n  }));\r\n}\r\n\r\n/**\r\n * The bindings for the JSON editor's `$`-trigger picker.\r\n *\r\n * The picker shows the friendly label + field kind (never the raw id); the\r\n * inserted token is the friendly `{{Label}}` when that label earned one, else the\r\n * raw `{{id}}`. Each binding also aliases the *other* form, so a pasted or legacy\r\n * `{{id}}` still renders as a valid (clickable) pill — and vice versa.\r\n *\r\n * @param formInputs - Every input on the form being configured.\r\n * @param idToLabel  - The `id → label` half of `buildTokenMaps`; only safe,\r\n *   uniquely-labelled fields appear in it, so anything absent keeps its raw id.\r\n * @returns One {@link EditorBinding} per bindable input.\r\n */\r\nexport function fieldEditorBindings(\r\n  formInputs: readonly FormColumnInputs[],\r\n  idToLabel: ReadonlyMap<string, string>,\r\n): readonly EditorBinding[] {\r\n  return bindableInputs(formInputs).map((input) => {\r\n    const label = input.label?.trim() || input.id;\r\n    const display = idToLabel.get(input.id);\r\n    return {\r\n      label,\r\n      insert: display ? `{{${display}}}` : `{{${input.id}}}`,\r\n      searchText: `${input.id} ${input.label ?? ''}`,\r\n      icon: getInputIcon(input.element),\r\n      detail: humanizeElement(input.element),\r\n      aliases: display ? [input.id] : [label],\r\n    } satisfies EditorBinding;\r\n  });\r\n}\r\n","/**\r\n * @file Friendly-label ⇄ id token transform for the payload-template editor.\r\n *\r\n * The stored config always uses `{{inputId}}` tokens, but the editor shows the\r\n * user-friendly field **label** inline (`{{Contract Type}}`). This module maps\r\n * between the two forms on a per-field basis.\r\n *\r\n * ### Robustness (why this is safe despite arbitrary labels)\r\n * The transform runs on the **parsed value** (objects/strings), never on raw\r\n * JSON text — so `JSON.stringify`/`parse` handle escaping and a label with a\r\n * quote can never corrupt the document. On top of that, a label only earns a\r\n * friendly token when it is BOTH:\r\n *  - **safe** — contains none of `{ } \" \\` (which would break the `{{…}}`\r\n *    delimiters or a raw picker-insertion), and\r\n *  - **unique** — no other field shares it (so `label → id` is unambiguous).\r\n *\r\n * Any field failing either test keeps its raw `{{id}}` inline. The friendly view\r\n * is therefore a lossless, best-effort layer that degrades to the id — it can\r\n * never produce invalid JSON or an ambiguous mapping.\r\n */\r\n\r\n/** A field that can back a token: its stable `id` and its display `label`. */\r\nexport interface TokenField {\r\n  readonly id: string;\r\n  readonly label: string;\r\n}\r\n\r\n/** Bidirectional token maps (only safe, uniquely-labelled fields are included). */\r\nexport interface TokenMaps {\r\n  /** `id → label` (drives the id→friendly display transform + picker insert). */\r\n  readonly idToLabel: ReadonlyMap<string, string>;\r\n  /** `label → id` (drives the friendly→id save transform). */\r\n  readonly labelToId: ReadonlyMap<string, string>;\r\n}\r\n\r\n/** Characters that make a label unusable as a `{{label}}` token. */\r\nconst UNSAFE_LABEL = /[{}\"\\\\]/;\r\n\r\n/** Matches a `{{token}}` occurrence inside a string (token captured, trimmed by the caller). */\r\nconst TOKEN_RE = /\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g;\r\n\r\n/**\r\n * Builds the id⇄label token maps for a set of fields, applying the safety and\r\n * uniqueness rules described in the file header.\r\n *\r\n * @param fields - The candidate fields (id + label).\r\n * @returns The bidirectional maps; a field absent from them keeps its raw id.\r\n */\r\nexport function buildTokenMaps(fields: readonly TokenField[]): TokenMaps {\r\n  const labelCounts = new Map<string, number>();\r\n  for (const field of fields) {\r\n    const label = field.label?.trim();\r\n    if (!label || UNSAFE_LABEL.test(label)) continue;\r\n    labelCounts.set(label, (labelCounts.get(label) ?? 0) + 1);\r\n  }\r\n\r\n  const idToLabel = new Map<string, string>();\r\n  const labelToId = new Map<string, string>();\r\n  for (const field of fields) {\r\n    const label = field.label?.trim();\r\n    if (!label || UNSAFE_LABEL.test(label)) continue;\r\n    if (labelCounts.get(label) !== 1) continue; // ambiguous → keep the raw id\r\n    if (label === field.id) continue; // transform would be a no-op\r\n    idToLabel.set(field.id, label);\r\n    labelToId.set(label, field.id);\r\n  }\r\n  return { idToLabel, labelToId };\r\n}\r\n\r\n/**\r\n * Deep-walks a parsed JSON value, rewriting every `{{token}}` whose trimmed token\r\n * is a key in `lookup` to `{{mappedValue}}`. Unmapped tokens are left untouched.\r\n * Non-string leaves (numbers/booleans/null) pass through unchanged.\r\n *\r\n * @typeParam T - The value shape (preserved).\r\n * @param value  - The parsed template value (object / array / primitive / undefined).\r\n * @param lookup - `idToLabel` for display, `labelToId` for save.\r\n * @returns A structurally-cloned value with tokens rewritten (or the original when `lookup` is empty).\r\n */\r\nexport function mapTemplateTokens<T>(value: T, lookup: ReadonlyMap<string, string>): T {\r\n  if (lookup.size === 0) return value;\r\n  return walk(value, lookup) as T;\r\n}\r\n\r\nfunction walk(node: unknown, lookup: ReadonlyMap<string, string>): unknown {\r\n  if (typeof node === 'string') {\r\n    return node.replace(TOKEN_RE, (match, rawToken: string) => {\r\n      const mapped = lookup.get(rawToken.trim());\r\n      return mapped !== undefined ? `{{${mapped}}}` : match;\r\n    });\r\n  }\r\n  if (Array.isArray(node)) {\r\n    return node.map((item) => walk(item, lookup));\r\n  }\r\n  if (node !== null && typeof node === 'object') {\r\n    const out: Record<string, unknown> = {};\r\n    for (const [key, val] of Object.entries(node as Record<string, unknown>)) {\r\n      out[key] = walk(val, lookup);\r\n    }\r\n    return out;\r\n  }\r\n  return node;\r\n}\r\n"],"names":[],"mappings":"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"}