{"version":3,"file":"ngx-t-forms-header-template-editor.component-Z5frEhho.mjs","sources":["../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/header-template-editor/flatten-header-template.ts","../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/header-template-editor/header-template-editor.component.ts","../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/header-template-editor/header-template-editor.component.html"],"sourcesContent":["/**\r\n * @file Flattening + validation for the request-header JSON editor.\r\n *\r\n * A {@link HeaderTemplate} is a flat `name → scalar` map, but the editor beneath\r\n * it is a general JSON editor: the author can type anything that parses. This\r\n * module is the narrow gate between the two — it accepts what a header map can\r\n * express and reports, per key, what it had to reject, so the editor can say\r\n * *which* header is wrong instead of silently dropping it.\r\n *\r\n * Kept separate from the component so it stays trivially unit-testable and free\r\n * of Angular.\r\n */\r\nimport type { HeaderTemplate, HeaderTemplateValue } from 'ngx-t-forms-types';\r\n\r\n/** The outcome of narrowing an edited JSON document to a header map. */\r\nexport interface HeaderTemplateNarrowing {\r\n  /** The header entries that are expressible; always safe to persist. */\r\n  readonly template: HeaderTemplate;\r\n  /**\r\n   * Human-readable complaints, one per rejected entry (nested object/array value,\r\n   * or a blank header name). Empty when the document is a clean header map.\r\n   */\r\n  readonly problems: readonly string[];\r\n}\r\n\r\n/** True for a value a header can actually carry. */\r\nfunction isScalar(value: unknown): value is HeaderTemplateValue {\r\n  return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';\r\n}\r\n\r\n/** The words a complaint uses for the thing being narrowed. */\r\nexport interface NarrowingNouns {\r\n  /** e.g. `'header'` / `'query parameter'`. */\r\n  readonly singular: string;\r\n  /** Sentence-initial plural, e.g. `'Headers'` / `'Query parameters'`. */\r\n  readonly plural: string;\r\n}\r\n\r\nconst HEADER_NOUNS: NarrowingNouns = { singular: 'header', plural: 'Headers' };\r\n\r\n/**\r\n * Narrows a parsed JSON document to a {@link HeaderTemplate} (or its structural\r\n * twin, a `QueryTemplate` — pass query nouns so complaints name the right thing).\r\n *\r\n * Rejects — rather than coerces — anything the flat shape cannot carry: an array\r\n * root, a nested object/array value, or a blank name. Coercing would produce an\r\n * entry the endpoint silently misreads (`\"[object Object]\"`), so the entry is\r\n * dropped and named in `problems` instead.\r\n *\r\n * @param value - The parsed editor document.\r\n * @param nouns - The words complaints use (defaults to header phrasing).\r\n * @returns The expressible entries plus a complaint per rejected one.\r\n */\r\nexport function narrowHeaderTemplate(\r\n  value: unknown,\r\n  nouns: NarrowingNouns = HEADER_NOUNS,\r\n): HeaderTemplateNarrowing {\r\n  const template: Record<string, HeaderTemplateValue> = {};\r\n  const problems: string[] = [];\r\n\r\n  if (Array.isArray(value) || value === null || typeof value !== 'object') {\r\n    return { template, problems: [`${nouns.plural} must be a JSON object of \"name\": \"value\" pairs.`] };\r\n  }\r\n\r\n  for (const [rawName, rawValue] of Object.entries(value as Record<string, unknown>)) {\r\n    const name = rawName.trim();\r\n    if (name === '') {\r\n      problems.push(`A ${nouns.singular} name cannot be blank.`);\r\n      continue;\r\n    }\r\n    if (rawValue === null || rawValue === undefined) {\r\n      problems.push(`\"${name}\" has no value.`);\r\n      continue;\r\n    }\r\n    if (!isScalar(rawValue)) {\r\n      problems.push(`\"${name}\" must be a single text value — objects and lists cannot be sent as a ${nouns.singular}.`);\r\n      continue;\r\n    }\r\n    template[name] = rawValue;\r\n  }\r\n\r\n  return { template, problems };\r\n}\r\n","import {\r\n  ChangeDetectionStrategy,\r\n  Component,\r\n  ViewEncapsulation,\r\n  computed,\r\n  input,\r\n  output,\r\n  signal,\r\n} from '@angular/core';\r\nimport { MatIconModule } from '@angular/material/icon';\r\n\r\nimport type { FormColumnInputs, HeaderTemplate } from 'ngx-t-forms-types';\r\n\r\nimport type { IConfigElementError } from '../../t-dynamic-data-edit.component';\r\nimport { bindableFields, fieldEditorBindings } from '../_shared/field-bindings';\r\nimport {\r\n  JsonEditorComponent,\r\n  type EditorBinding,\r\n  type JsonEditorChange,\r\n} from '../json-editor/json-editor.component';\r\nimport { buildTokenMaps, mapTemplateTokens } from '../payload-template-editor/token-transform';\r\nimport { narrowHeaderTemplate } from './flatten-header-template';\r\n\r\n/** Shown when nothing has been authored and the endpoint captured no headers. */\r\nconst EMPTY_SKELETON: HeaderTemplate = { 'Content-Type': 'application/json' };\r\n\r\n/**\r\n * Shown when nothing has been authored and the endpoint's URL captured no query\r\n * params. A blank literal is dropped at resolve time, so the skeleton teaches\r\n * the shape without ever sending anything.\r\n */\r\nconst QUERY_SKELETON: HeaderTemplate = { search: '' };\r\n\r\n/**\r\n * Editor for a value/options fetch's `headerTemplate` — and, via\r\n * `kind=\"query\"`, its `queryTemplate` (the exact structural twin: a flat\r\n * `name → scalar` map with the same `{{inputId}}` token grammar).\r\n *\r\n * Shares the {@link JsonEditorComponent} and the friendly-label token transform\r\n * with the payload-template editor, so binding a header to a live field is the\r\n * same gesture as binding a payload field: type `$`, pick the field, and the\r\n * editor inserts its `{{token}}`. Unlike a payload it enforces a **flat**\r\n * `name → text` shape — a header has no structure to nest into — and names any\r\n * entry it had to reject rather than dropping it silently.\r\n *\r\n * Applies to GET as well as POST; which slot it edits is decided upstream by the\r\n * element-editor descriptor, so this component owns no mode toggle.\r\n *\r\n * @example\r\n *   <lib-header-template-editor [value]=\"headers()\" [formInputs]=\"inputs()\"\r\n *     (valueChanged)=\"onHeaders($event)\" />\r\n * @example\r\n *   <lib-header-template-editor kind=\"query\" [value]=\"query()\"\r\n *     [formInputs]=\"inputs()\" (valueChanged)=\"onQuery($event)\" />\r\n */\r\n@Component({\r\n  selector: 'lib-header-template-editor',\r\n  templateUrl: './header-template-editor.component.html',\r\n  styleUrl: './header-template-editor.component.scss',\r\n  changeDetection: ChangeDetectionStrategy.OnPush,\r\n  encapsulation: ViewEncapsulation.Emulated,\r\n  host: { class: 'lib-header-template-editor' },\r\n  imports: [MatIconModule, JsonEditorComponent],\r\n})\r\nexport class HeaderTemplateEditorComponent {\r\n  /**\r\n   * What the flat map configures: request headers (default) or URL query\r\n   * parameters. Selects the empty skeleton, the hint copy, and the noun used\r\n   * in shape complaints — the editing behaviour is otherwise identical.\r\n   */\r\n  readonly kind = input<'headers' | 'query'>('headers');\r\n\r\n  /** Current header template (`undefined` when nothing has been authored yet). */\r\n  readonly value = input<HeaderTemplate | undefined>(undefined);\r\n\r\n  /**\r\n   * Default shown when nothing has been authored yet — the endpoint's captured\r\n   * headers, when the collection carried any. Falls back to a one-line skeleton\r\n   * so the editor never opens blank.\r\n   */\r\n  readonly defaultTemplate = input<HeaderTemplate | undefined>(undefined);\r\n\r\n  /** All form inputs, offered as insertable `{{inputId}}` bindings. */\r\n  readonly formInputs = input<readonly FormColumnInputs[]>([]);\r\n\r\n  /** Disables the editor. */\r\n  readonly disabled = input<boolean>(false);\r\n\r\n  /**\r\n   * Shows the built-in instruction line. A host that provides its own copy for\r\n   * this editor (the endpoint dialog's per-tab hint) passes `false` so the\r\n   * author reads ONE instruction, not two stacked ones.\r\n   */\r\n  readonly showHint = input<boolean>(true);\r\n\r\n  /** Validation errors surfaced by the parent editor. */\r\n  readonly errors = input<IConfigElementError[] | undefined>([]);\r\n\r\n  /** Optional id supplied by the parent editor. */\r\n  readonly id = input<string | undefined>(undefined);\r\n\r\n  /** Emits the narrowed header map on every valid edit. */\r\n  readonly valueChanged = output<HeaderTemplate>();\r\n\r\n  /** Per-entry complaints from the last edit (nested value, blank name, …). */\r\n  protected readonly shapeProblems = signal<readonly string[]>([]);\r\n\r\n  /** id⇄label token maps (safe + unique labels only; everything else stays a raw id). */\r\n  readonly #tokenMaps = computed(() => buildTokenMaps(bindableFields(this.formInputs())));\r\n\r\n  /** Field bindings for the JSON editor's `$`-trigger picker. */\r\n  protected readonly editorBindings = computed<readonly EditorBinding[]>(() =>\r\n    fieldEditorBindings(this.formInputs(), this.#tokenMaps().idToLabel),\r\n  );\r\n\r\n  /**\r\n   * The value the JSON editor renders — the authored headers, the endpoint's\r\n   * captured ones, or the skeleton — with stored `{{id}}` tokens rewritten to the\r\n   * friendly `{{Label}}` form for display.\r\n   */\r\n  protected readonly editorValue = computed<object | undefined>(() => {\r\n    const authored = this.value();\r\n    const skeleton = this.kind() === 'query' ? QUERY_SKELETON : EMPTY_SKELETON;\r\n    const source = authored ?? this.defaultTemplate() ?? skeleton;\r\n    return mapTemplateTokens(source, this.#tokenMaps().idToLabel) as object;\r\n  });\r\n\r\n  /**\r\n   * Commit a valid edit: narrow the document to a flat map, then rewrite the\r\n   * friendly `{{Label}}` tokens back to the stored `{{id}}` form before\r\n   * emitting. Invalid JSON is ignored (the last valid map stays in effect);\r\n   * entries the flat shape cannot carry are reported rather than persisted.\r\n   */\r\n  protected onEditorChange(change: JsonEditorChange): void {\r\n    if (!change.valid) return;\r\n    const nouns =\r\n      this.kind() === 'query'\r\n        ? ({ singular: 'query parameter', plural: 'Query parameters' } as const)\r\n        : undefined;\r\n    const { template, problems } = narrowHeaderTemplate(change.value, nouns);\r\n    this.shapeProblems.set(problems);\r\n    const stored = mapTemplateTokens(template, this.#tokenMaps().labelToId);\r\n    this.valueChanged.emit(stored);\r\n  }\r\n}\r\n","<div class=\"header-editor\">\r\n  @if (showHint()) {\r\n  <p class=\"hint-line\">\r\n    <mat-icon class=\"info-icon\">info</mat-icon>\r\n    @if (kind() === 'query') {\r\n    <span>Author the URL query parameters as JSON — one <code>\"name\": \"value\"</code> pair per\r\n      parameter. Type <code>$</code> to bind a value to a field; the request waits until every\r\n      bound field has a value.</span>\r\n    } @else {\r\n    <span>Author the request headers as JSON — one <code>\"name\": \"value\"</code> pair per header.\r\n      Type <code>$</code> to bind a value to a field; the request waits until every bound field\r\n      has a value.</span>\r\n    }\r\n  </p>\r\n  }\r\n\r\n  <lib-json-editor\r\n    height=\"260px\"\r\n    [value]=\"editorValue()\"\r\n    [bindings]=\"editorBindings()\"\r\n    [disabled]=\"disabled()\"\r\n    (valueChange)=\"onEditorChange($event)\" />\r\n\r\n  @for (problem of shapeProblems(); track $index) {\r\n    <p class=\"error-line\"><span>{{ problem }}</span></p>\r\n  }\r\n\r\n  @for (error of errors(); track $index) {\r\n    <p class=\"error-line\"><span>{{ error.message }}</span></p>\r\n  }\r\n</div>\r\n"],"names":[],"mappings":"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"}