import * as _angular_core from '@angular/core'; import { Provider, EnvironmentProviders, InjectionToken, Signal, OnInit, OnDestroy, PipeTransform, Type, ElementRef } from '@angular/core'; import * as ngx_t_forms_types from 'ngx-t-forms-types'; import { NgxTFormsConfig, ElementTypes, FormInterface, APIDataFetchingConfigurationInterface, IFormActions, FormSlideInterface, InClassFormUtilsInterface, IWorkflowDocumentPicker, IBasicFormInput, IFileUploadInput, IDateRangePickerInput, ITextareaProperties, IToggleInput, IMatrixInput, IMultiple, IGetTreeResponse, IRunPipelineResponse, IPipelineGenerationResponse, InputDataTypes, FormInputBasicOptionInterface, LocalFormStateSelectorInterface, FormsStoreSelectorsInterface, FormListSection, ITowerFormSteps, WorkflowFunctionTypes, FileData, FormSubmissionStatus, IFormValidationOverride, FormColumnInputs, IFormChangeHistory, IStepOption, FormBuilderFunctions, ElementEditorTypes, ElementEditorInnerSectionElementInterface, ITowerStepColumn, ScoaInnerInput, IWorkflowOption, BlurHandleTypes, TreeNode, FlatNode, IArrayFunction, DocumentLitsLabelConfigInterfaceValueType, InputPipeTypes, EncryptedData, ImportProgress, ImportIdentity, ImportRowState, ValidationError, DialogConfig, PropertyPath, DeepPropertyValue, FileUploadInputValueInterface, FormInterfaceMigration, ConfigurationValidTestInterface } from 'ngx-t-forms-types'; export { BlurHandleTypes, ConfigurationValidTestInterface, DeepPropertyValue, DialogConfig, DocumentLitsLabelConfigInterfaceValueType, ElementEditorInnerSectionElementInterface, ElementEditorTypes, EncryptedData, FileUploadInputValueInterface, FormBuilderFunctions, FormColumnInputs, FormInputBasicOptionInterface, FormInterface, FormInterfaceMigration, FormListSection, FormSubmissionStatus, IArrayFunction, IFormActions, IFormChangeHistory, IFormTour, IFormValidationOverride, IGetTreeResponse, IScoaAccount, ITowerStepColumn, IWorkflowOption, ImportIdentity, ImportProgress, ImportRowState, ImportRowStatus, InClassFormUtilsInterface, InputDataTypes, InputPipeTypes, LocalFormStateSelectorInterface, NgxTFormsConfig, OLD_FormInterface, PropertyPath, TreeNode, ValidationError, WorkflowFunctionTypes } from 'ngx-t-forms-types'; import * as rxjs from 'rxjs'; import { Observable, Subject } from 'rxjs'; import { PipelineStage } from 'mongoose'; import { s } from '@hashbrownai/core'; import { AvailableApiEndpoint } from 'ngx-t-forms-types/skillet'; import { Router, ActivatedRoute } from '@angular/router'; import { MatStepper } from '@angular/material/stepper'; import { StepperSelectionEvent, StepState } from '@angular/cdk/stepper'; import { HttpClient } from '@angular/common/http'; import { FormGroup, FormControl } from '@angular/forms'; import * as ngx_ui_tour_core from 'ngx-ui-tour-core'; import { IStepOption as IStepOption$1 } from 'ngx-ui-tour-md-menu'; import { FlatTreeControl } from '@angular/cdk/tree'; import { MatTreeFlattener, MatTreeFlatDataSource } from '@angular/material/tree'; import { MatDialogRef } from '@angular/material/dialog'; /** @internal */ declare const enum NgxTFormsFeatureKind { SilencedConsole = 0, FormBuilder = 1, ImportTower = 2, HttpPipeline = 3, InputSecret = 4, RouterFormId = 5, FormSuggestions = 6, AiFormBuilder = 7, AiEndpointCatalog = 8, AiMscoaGrounding = 9, SpreadsheetImport = 10 } interface NgxTFormsFeature { readonly kind: NgxTFormsFeatureKind; readonly providers: Provider[]; } /** * Registers ngx-t-forms with the application injector. * * @param config Runtime configuration (form actions, builder, etc.). * @param features Optional with*() extension features. * @returns EnvironmentProviders ready to drop into `bootstrapApplication`. * * @example * bootstrapApplication(AppComponent, { * providers: [ * provideNgxTForms(myConfig, withSilencedConsole(), withFormBuilder()), * ], * }); */ declare function provideNgxTForms(config: NgxTFormsConfig, ...features: NgxTFormsFeature[]): EnvironmentProviders; /** * @public * * Binds the default HTTP-backed {@link HttpPipelineRepository} to * {@link PIPELINE_REPOSITORY}. Required when the consumer uses any * pipeline-running feature (e.g. the pipeline-generator UI) and wants the * built-in HTTP transport. * * Consumers that supply their own {@link PipelineRepository} implementation * should bind it directly to {@link PIPELINE_REPOSITORY} instead of (or after) * calling this — provider order resolution applies. * * @example * bootstrapApplication(AppComponent, { * providers: [ * provideHttpClient(), * provideNgxTForms(config, withHttpPipeline()), * ], * }); */ declare function withHttpPipeline(): NgxTFormsFeature; /** * Registers the HMAC secret used to derive per-input encryption keys for the * signature input. Required only when the signature input is in use — if the * token is unbound at the point of encryption/decryption the library throws a * clear error rather than silently falling back to a baked-in default. * * @param secret High-entropy string supplied by the consumer (e.g. read from * their own `environment.ts` or a runtime config endpoint). * * @example * provideNgxTForms( * myConfig, * withInputSecret(environment.ngxTFormsInputSecret), * ) */ declare function withInputSecret(secret: string): NgxTFormsFeature; /** * Feeds {@link FormBuilderComponent} its `formId` from the activated route, * preserving the historical route-driven form loading. Shipped by default in * `provideNgxTForms`, so existing consumers see identical behaviour (D-B2 / §5.5); * a router-free build resolves {@link FORM_ROUTE_SOURCE} to `EMPTY` and the * builder loads from its `formId` input only. * * @example * provideNgxTForms(config, withRouterFormId()) * * @public */ declare function withRouterFormId(): NgxTFormsFeature; /** * @public * * Enables the **AI edit-suggestion reporting layer**. Binds the default * {@link FormSuggestionRegistryService} to {@link FORM_SUGGESTION_REGISTRY}, so * every live form tower reports its editable schema / values / validation and * external systems can propose optional edits through the token. * * Opt-in and tree-shakeable: without this feature the token is unbound and form * towers (which inject it `{ optional: true }`) do no suggestion work at all. * * Consumers that supply their own {@link FormSuggestionRegistry} implementation * should bind it directly to {@link FORM_SUGGESTION_REGISTRY} instead of (or * after) calling this — provider order resolution applies. * * @example * bootstrapApplication(AppComponent, { * providers: [ * provideNgxTForms(config, withFormSuggestions()), * ], * }); */ declare function withFormSuggestions(): NgxTFormsFeature; /** * @file Domain models for the AI form-builder drafting pipeline. * * Inner-ring module: its only import is a **type-only** one from * `ngx-t-forms-types`, erased at compile time. No runtime Angular concretions, * no library internals, no `@hashbrownai/*`. * Mirrors the constraint documented on `domain/form-suggestion.model.ts`. * * These types describe **pipeline artifacts** (the conversation, the plan, the * session, the findings). They deliberately do *not* describe the form model: * `ngx-t-forms-types/skillet` is the single generation contract for that, and * this library never re-derives or forks it (AI_FORM_BUILDER_DESIGN.md §7). */ /** * A specialised role in the drafting pipeline. * * Phase 1 exercises `analyst`, `architect` and `generator`; the remaining roles * are reserved so that per-role model overrides written today keep working when * the grounded specialists and the critic land in Phases 2–3. */ type AiFormBuilderRole = 'analyst' | 'architect' | 'generator' | 'dataSource' | 'mscoa' | 'logic' | 'critic'; /** * Lifecycle stage of a {@link DraftSession}. * * The pipeline advances strictly forward except for its two loops: `repairing` * is re-entered from `verifying` while the repair budget lasts, and * `critiquing` returns to `planning` for each critique revision round. */ type DraftStage = /** No draft in progress. */ 'idle' /** Conversational intake with the Analyst. */ | 'intake' /** The Architect is planning slides and field stubs. */ | 'planning' /** The Field Generator is producing columns. */ | 'generating' /** Deterministic assembly and draft expansion. */ | 'assembling' /** Deterministic verification (Joi + reference integrity). */ | 'verifying' /** A routed repair turn is in flight. */ | 'repairing' /** * The Critic is reviewing a form that has already cleared every * deterministic gate. * * **Added after this union first shipped**, and therefore a widening a * consumer may have to answer for: an exhaustive `switch` or a * `Record` written against the narrower union stops * compiling until this member is handled. That cost was accepted * deliberately. The Critic previously ran under `planning`, so a host had no * way to tell a reviewer's turn from a planner's and could only show * "planning" while nothing was being planned — and a stage that misreports * what the pipeline is doing is worse than a compile error the consumer can * see and fix in one place. * * Covers the Critic's own turn and nothing else. The revision that a * blocking critique triggers is a re-plan by the Architect, so the session * goes back to `planning` for it. */ | 'critiquing' /** A valid form is staged and awaiting human review. */ | 'staged' /** The pipeline stopped without producing a staged form. */ | 'failed'; /** Author of a message in the intake conversation. */ type DraftMessageAuthor = 'user' | 'assistant'; /** One turn of the conversational intake with the Analyst. */ interface DraftMessage { /** Who produced this turn. */ readonly author: DraftMessageAuthor; /** Plain-text content of the turn. */ readonly content: string; } /** * One numbered business rule captured by the Analyst. * * The ledger of rules doubles as the Critic's coverage checklist in Phase 3, so * ids must be stable and unique within a {@link FormBrief}. */ interface FormBriefRule { /** Stable identifier, unique within the brief (e.g. `R1`). */ readonly id: string; /** The rule as stated, in the user's own terms. */ readonly statement: string; } /** A field the Analyst inferred from the brief, before any layout decision. */ interface FormBriefField { /** Human-readable name of the field. */ readonly name: string; /** What the field is for, in one sentence. */ readonly purpose: string; /** Whether the user must supply a value. */ readonly required: boolean; } /** * Structured intake produced by the Analyst — the conversational front door of * the pipeline and the input to the Architect. */ interface FormBrief { /** Proposed title for the form. */ readonly formTitle: string; /** One-paragraph statement of what the form is for. */ readonly summary: string; /** Field intents, before slides or element types are chosen. */ readonly fields: readonly FormBriefField[]; /** Numbered business-rules ledger. */ readonly rules: readonly FormBriefRule[]; /** Questions the Analyst wants the user to answer. */ readonly openQuestions: readonly string[]; } /** * A planned field stub. The Architect chooses the element and data type; the * Field Generator later expands the stub into a full form column. */ interface BlueprintField { /** Control name the generated column will carry. */ readonly formControlName: string; /** Visible label. */ readonly label: string; /** Element type to generate. Constrained to the configured element union. */ readonly element: ElementTypes; /** Whether the user must supply a value. */ readonly required: boolean; /** Ids of the {@link FormBriefRule}s this field is meant to satisfy. */ readonly ruleTags: readonly string[]; /** * Whether this field reads its value, or the options it offers, from * somewhere other than the user — so the Data-Source specialist must author * its `matOptions` against the endpoint catalog. * * Offered to the Architect **only** while a catalog is bound * (`withAiEndpointCatalog()`). With none bound there is nothing to point at, * and a flag the pipeline cannot honour is worse than no flag at all. */ readonly needsDataSource?: boolean; /** * Whether this field asks the user to pick a municipal SCOA account — so the * MSCOA specialist must author its `mscoaConfig`. * * Offered to the Architect **only** while chart-of-accounts grounding is * bound (`withAiMscoaGrounding()`), for the same reason as * {@link needsDataSource}. */ readonly needsMscoa?: boolean; /** * Whether this field carries a rule beyond being filled in — a custom * validator, a visibility condition, or a value computed from other fields. * * The Logic specialist runs as **one batched turn over every field flagged * this way**, because all three members share one expression DSL and one * sibling-field roster; splitting them would yield three agents needing the * same context (AI_FORM_BUILDER_DESIGN.md §2). */ readonly needsLogic?: boolean; /** * The rule to encode, in one sentence and in the brief's own terms. Read only * when {@link needsLogic} is set; with nothing stated the Logic specialist * works from {@link ruleTags} and the rules ledger alone. */ readonly logicIntent?: string; } /** A planned slide — a titled group of field stubs. */ interface BlueprintSlide { /** Slide heading shown to the end user. */ readonly label: string; /** Field stubs belonging to this slide. */ readonly fields: readonly BlueprintField[]; } /** * The Architect's plan: slides, field stubs and rule trace tags, produced before * the expensive per-column generation step so it is cheap to re-run. */ interface FormBlueprint { /** Title carried through to the generated form. */ readonly formTitle: string; /** Planned slides, in presentation order. */ readonly slides: readonly BlueprintSlide[]; /** * Ids of the catalog endpoints the completed form is submitted to, becoming * `submissionHandle.submissionAPI` once expansion resolves each id to its * stored configuration. * * Offered to the Architect **only** while a bound catalog declares at least * one endpoint for the `submission` slot — a form-level decision, made once by * the agent that can see the whole brief, rather than by a per-field * specialist that cannot. */ readonly submissionEndpointIds?: readonly string[]; /** * Ids of the catalog endpoints that **gate** submission, becoming * `submissionHandle.canSubmitAPI` once expansion resolves each id to its * stored configuration. * * Offered to the Architect on exactly the terms {@link submissionEndpointIds} * is, and absent for the same reason when the bound catalog declares no * `canSubmit` endpoint: an id nothing can resolve is worse than no gate. * * Not interchangeable with {@link submissionEndpointIds}. A gate is *asked* * before the form is sent and may refuse it, so an endpoint that is a valid * submission target is not thereby a valid gate. Only the catalog knows which * is which, and this pipeline never infers it. */ readonly canSubmitEndpointIds?: readonly string[]; /** * Control names of **baseline** fields this plan removes on purpose. * * Offered to the Architect **only** in update mode — when the session carries * a {@link DraftSession.baseline} — and read by the baseline-preservation * gate: a baseline field that is neither in the plan nor named here is a * blocking finding routed back to the Architect, because silently dropping a * saved field is the regression an update must never make. Each name here * should be justified by a brief rule. */ readonly removedFields?: readonly string[]; } /** What a {@link DraftFinding} is about. */ type DraftFindingKind = /** Joi rejected the assembled form. */ 'validation' /** A generated reference does not resolve within the form. */ | 'integrity' /** A brief rule is not mapped to any field. */ | 'coverage' /** A UX-heuristic observation. */ | 'ux'; /** Whether a finding stops the pipeline or is merely reported. */ type DraftFindingSeverity = 'blocking' | 'advisory'; /** * A typed, machine-routable observation about a draft. Findings gate the repair * loop and are surfaced to the user when a budget is exhausted. */ interface DraftFinding { /** What the finding is about. */ readonly kind: DraftFindingKind; /** Whether it blocks acceptance. */ readonly severity: DraftFindingSeverity; /** Dotted path or rule id the finding refers to. */ readonly target: string; /** Human-readable description; also used verbatim as the repair prompt. */ readonly message: string; /** Optional concrete correction. */ readonly suggestedFix?: string; } /** * What one drafting **session** has cost, in units the library can actually * count. * * Deliberately narrow. A **call** is one `DraftTransport.complete()` * invocation, and that is the only unit the orchestrator observes. Tokens, * money and wall-clock time are the transport's business: the library never * sees a usage report, so any member claiming to hold one would be an * estimate dressed as a measurement. A host that needs spend meters its own * adapter, where the provider's real numbers are. * * ## Scope: the session, not one run * * Every count accumulates over the whole session — the intake conversation * plus **every** `generate()` run made on it — and is cleared only when the * session itself is, by `FormDraftOrchestrator.reset()`. Two runs on one * session therefore report their combined cost, not the second run's. * * It is scoped that way because one of the things it counts could not be * attributed to a run at all: intake is a conversation, not a run, and the * Analyst turns it spends happen *before* the first `generate()` call. A * per-run counter would have nowhere to put them, which would make * `intake` — named in {@link callsByStage} as a stage this record carries — * permanently unreachable. Counting the session keeps every call the * orchestrator issued in exactly one place. * * A host that wants one run's figures subtracts two snapshots: read the counts * before calling `generate()` and again when its observable completes. Nothing * here ever decreases, so that difference is always meaningful. * * Loop counters are deliberately **not** repeated here — rounds live on * {@link DraftSession.repairTurnsUsed} and * {@link DraftSession.critiqueRoundsUsed}. Carrying them in two places would * make one fact into two numbers that can drift apart; read them from the * session alongside this. Mind that those two are scoped to the run in flight * and are cleared when the next one starts, while these counts carry across. * * Every count is cumulative over the session and never decreases within it. */ interface DraftTelemetry { /** Total `DraftTransport.complete()` calls issued so far this session. */ readonly totalCalls: number; /** * Calls issued per pipeline role, over the whole session. * * A missing key means zero: a role that never ran — an unbound specialist, * or the Critic on a draft it was never asked about — carries no entry. */ readonly callsByRole: Partial>; /** * Calls issued per lifecycle stage — the stage current when each call was * made, which is where the run's effort actually went. * * Not a restatement of {@link callsByRole}: a repair turn re-invokes the * Generator and the specialists, so their role counts fold first-pass and * repair work into one number while `repairing` isolates what the inner * loop cost. The outer loop splits the same way — `critiquing` is what the * Critic's review turns cost, while the re-plans those reviews trigger are * counted under `planning` alongside the first plan, because a revision is * the Architect planning again. Stages that issue no calls — `idle`, * `assembling`, `verifying`, `staged`, `failed` — carry no key, as does any * stage the session never entered. */ readonly callsByStage: Partial>; } /** * The most recent partial structured output of a turn that is still in flight. * * **This is a progress signal for the panel, and nothing else.** It is * emphatically *not* a form filling in on the canvas: only a Joi-validated form * ever reaches the canvas (AI_FORM_BUILDER_DESIGN.md §5.1 rule 4), and a * half-written plan or a half-written column is not one. Nothing in the * pipeline reads a partial — no verification, no repair routing, no staging * decision — so a run that never receives one is identical to a run that * receives many. * * Absent whenever nothing is streaming: no transport that streams is bound, the * turn in flight is not one of the two that stream, or the run has finished. */ interface DraftPartial { /** Which role's turn produced it. */ readonly role: AiFormBuilderRole; /** * The value so far. * * `unknown` for the same reason the schema that describes it is: it is the * shape of that schema with any subset of its members still missing or * half-written. Narrow it defensively and never treat one as final. */ readonly value: unknown; /** * Which slide is being generated, present only on a Field Generator turn — * slides are generated in parallel, so without it a panel cannot tell two * concurrent streams apart. */ readonly slideIndex?: number; } /** * The diagnostic record of the **thrown** error that ended a run — kept whole, * so a failure can be debugged rather than only read. * * Present on a `failed` session only when the run stopped on an exception: a * transport error, a model answer the pipeline could not use even after * repair, or a defect in the pipeline itself. A run that stopped by its own * rules — no brief to build from, a spent repair budget — is an outcome, not a * crash, and carries {@link DraftSession.error} alone. * * The same error object is also handed to Angular's `ErrorHandler`, so with the * default handler it reaches the console with its stack, and a host that has * replaced the handler with an error reporter receives it there. This record * is what lets the *panel* show the same thing, in place, without a console. */ interface DraftFailure { /** The error's class name — `TypeError`, `DraftOutputError`, … */ readonly name: string; /** The error's message; the same text as {@link DraftSession.error}. */ readonly message: string; /** The stack trace as the runtime produced it, when it produced one. */ readonly stack?: string; /** The lifecycle stage the error interrupted. */ readonly stage: DraftStage; /** * The role whose completion the error came out of, when it came out of one. * Absent for an error thrown between completions — by assembly, say. */ readonly role?: AiFormBuilderRole; /** A description of the error's own `cause`, when it carried one. */ readonly cause?: string; } /** * Where one round of repair work was sent. * * Every member is present so a host can render the route without probing: an * empty list means nothing of that kind was involved in the round. */ interface DraftRepairRoute { /** Whether the Architect was asked to correct the plan this round. */ readonly architect: boolean; /** Slide indexes the Field Generator re-ran. */ readonly slides: readonly number[]; /** Specialist turns re-run, each with the control names it was asked about. */ readonly specialists: readonly { readonly role: AiFormBuilderRole; readonly fields: readonly string[]; }[]; /** * Members withdrawn deterministically rather than re-asked, as * `.` — the escalation taken when a specialist had * failed to correct the same member twice. The column keeps working without * it; the author finishes it on the canvas. */ readonly retracted: readonly string[]; /** * Findings escalated from the Field Generator to the Architect, by target — * taken when a column could not be generated validly twice in a row, so the * plan for that field, not the column, is what gets changed. */ readonly escalated: readonly string[]; } /** What kind of turn a {@link DraftRepairTurn} records. */ type DraftRepairTurnKind = /** The Architect correcting its own plan before any column was generated. */ 'plan' /** A routed repair of an assembled form that failed a deterministic gate. */ | 'repair' /** A deterministic withdrawal of members no repair could fix; no model call. */ | 'retraction' /** A re-plan answering the Critic's blocking findings. */ | 'revision'; /** * One entry of the run's **repair log** — the record a host renders so a * failed or repaired run explains itself: what was wrong, who was asked to fix * it, and whether it worked. * * Entries are appended in order and never rewritten, except that * {@link outcome} is filled in once the verification that follows the turn has * run. A run that stages on the first pass carries an empty log. */ interface DraftRepairTurn { /** 1-based position in the run's log. */ readonly sequence: number; /** What kind of turn this was. */ readonly kind: DraftRepairTurnKind; /** * The blocking findings the turn set out to correct. For a `retraction` these * are the findings whose members were withdrawn; for a `revision` they are * the Critic's blocking findings. */ readonly findings: readonly DraftFinding[]; /** Where the work was sent. */ readonly route: DraftRepairRoute; /** * What the next verification found, relative to {@link findings}: how many * of them were gone, how many came back unchanged, and how many new blocking * findings appeared. Absent until that verification has run, and on the last * turn of a run that was cancelled. */ readonly outcome?: { readonly resolved: number; readonly persisted: number; readonly introduced: number; }; } /** * Options for one intake turn. See {@link FormDraftOrchestrator.describe}. */ interface DescribeOptions { /** * A saved form the conversation is about **changing**, rather than a new * one being described from nothing. * * Setting it puts the session in **update mode** for every run until * `reset()`: the Analyst records the requested changes as ledger rules, the * Architect starts from the plan the baseline already embodies, only the * fields the plan changes are regenerated, every other column comes back * byte-for-byte — identifiers included, so a staged result is the same form * with the requested edits rather than a re-keyed twin — and a baseline * field dropped or altered without a rule to justify it is a blocking * finding. Hand the orchestrator the form exactly as the canvas holds it. */ readonly baseline?: FormInterface; } /** * Immutable snapshot of a drafting session. The orchestrator republishes a new * snapshot on every stage transition; the panel renders straight from it. */ interface DraftSession { /** Stable id for this session. */ readonly id: string; /** Current lifecycle stage. */ readonly stage: DraftStage; /** Intake conversation so far. */ readonly conversation: readonly DraftMessage[]; /** Latest brief, once the Analyst has produced one. */ readonly brief?: FormBrief; /** Latest plan, once the Architect has produced one. */ readonly blueprint?: FormBlueprint; /** * The assembled, expanded and **Joi-validated** form: present only in the * `staged` stage, and only ever holding a form that cleared verification with * no blocking finding left standing. * * This is the one member a host may load onto the canvas. Because it has * passed exactly the validation a *stored* form passes, staging it cannot put * anything through the load path that the canvas could not already receive. * Never persisted by the library. Contrast {@link candidateForm}. */ readonly form?: FormInterface; /** * The best-effort form the pipeline actually built, published when a run ends * in `failed` because its repair budget ran out. * * **It did not pass validation.** Every blocking entry in {@link findings} is * a defect still standing in it, so it is not a form — it is the wreckage of * one, kept so a human can read, diff or salvage the run's work instead of * losing it. It is never a stand-in for {@link form}: loading it onto the * canvas would push a form through the load path that could never have been * stored, which is the guarantee staging rests on. A host that offers it must * present it as rejected output, behind the findings that rejected it. * * Unset on every other stage, and cleared at the start of each run. */ readonly candidateForm?: FormInterface; /** Outstanding findings, including any left over when a budget ran out. */ readonly findings: readonly DraftFinding[]; /** Repair turns consumed so far. */ readonly repairTurnsUsed: number; /** * Critique rounds consumed so far — the outer loop's counterpart to * {@link repairTurnsUsed}, bounded by * {@link AiFormBuilderBudgets.critiqueRounds}. * * Absent rather than `0` on a run that never reached the Critic, so a host * can tell *the outer loop ran and converged on the first pass* (`0`) from * *the outer loop never ran at all* (`undefined`) — the Critic is skipped * whenever verification never produced a form worth critiquing. Read it as * `critiqueRoundsUsed ?? 0` only where that difference genuinely does not * matter. */ readonly critiqueRoundsUsed?: number; /** * Whether this session reached `staged` carrying critique findings the outer * loop never cleared. * * The two loops end differently, and this is the member that records it. A * run whose *repair* budget runs out has not passed Joi, so it fails and * stages nothing — its work goes to {@link candidateForm}. A run whose * *critique* budget runs out has already passed every deterministic gate: * the form is valid and the Critic simply still has coverage or UX * observations about it. That run stages, and its leftover findings are * surfaced as `advisory` in {@link findings}. {@link form}'s guarantee is * untouched — no blocking finding is ever left standing on a staged form. * * Not derivable from {@link findings}. A staged session can carry advisory * findings with no Critic having run at all, because deterministic * verification emits advisories of its own, so *there are advisories* does * not mean *the outer loop gave up*. Only this flag says the Critic ran and * did not converge — the difference between a clean result and one a human * should read before accepting. * * Absent means no: either the outer loop converged, or it never ran. */ readonly stagedWithOutstandingFindings?: boolean; /** * What the run has cost so far, in calls the orchestrator actually counted. * * Republished with every stage transition, so a panel can meter a run while * it is still in flight rather than only once it ends. Absent on a session * no run has touched. */ readonly telemetry?: DraftTelemetry; /** * The latest partial output of a streaming turn, for a progress display. * * Cleared when a run starts and again when it ends, so a partial is present * only while something is genuinely in flight. See {@link DraftPartial} for * why it never reaches the canvas. */ readonly partial?: DraftPartial; /** * The run's repair log, in order — see {@link DraftRepairTurn}. * * The transparency record: for every turn the pipeline spent correcting the * draft, which findings it addressed, who it asked, what it withdrew or * escalated, and whether the next verification bore that out. Present from * the first repair turn of a run, cleared when the next run starts; absent on * a session no run has repaired. */ readonly repairLog?: readonly DraftRepairTurn[]; /** * The saved form this session is editing, when it is editing one — see * {@link DescribeOptions.baseline}. Absent on a session drafting a new form. */ readonly baseline?: FormInterface; /** Message describing why the session reached `failed`. */ readonly error?: string; /** * The thrown error that ended the run, whole — see {@link DraftFailure}. * * Set alongside {@link error} when the run crashed; absent when it stopped by * its own rules. Cleared at the start of the next run and by the next * `describe()` turn, both of which start clean from a failed session. */ readonly failure?: DraftFailure; } /** * Loop budgets. * * The two budgets exhaust to opposite outcomes, and the difference is not * incidental — it follows from what each loop is waiting for: * * - {@link repairTurns} guards the deterministic loop, whose draft has NOT * passed Joi. Exhaustion fails the run: every finding is surfaced and the * rejected draft is published on {@link DraftSession.candidateForm}, but an * unverified form is never staged. * - {@link critiqueRounds} guards the Critic loop, which only ever sees a form * that already passed Joi. Exhaustion **stages** it, with the outstanding * findings downgraded to advisory and * {@link DraftSession.stagedWithOutstandingFindings} set — the human is the * intended judge of a form that is valid but imperfect. */ interface AiFormBuilderBudgets { /** Deterministic-failure repair turns per generation. Default 3. */ readonly repairTurns?: number; /** * Critic revision rounds. Default 2. Reserved for Phase 3. * * Consumption is reported on {@link DraftSession.critiqueRoundsUsed}, and * exhausting it stages the form with its leftover findings rather than * failing the run — see {@link DraftSession.stagedWithOutstandingFindings}. */ readonly critiqueRounds?: number; } /** Model selection per tier, with optional per-role overrides. */ interface AiFormBuilderModels { /** Model for judgment-heavy roles (analyst, architect, critic). */ readonly planning: string; /** Model for schema-fill roles. Defaults to {@link planning}. */ readonly worker?: string; /** Per-role override, taking precedence over the tier default. */ readonly overrides?: Partial>; } /** * One hook over the `fetch` options of every request this feature sends. * * Declared here as a plain function over the platform `RequestInit` rather * than as Hashbrown's own `Chat.Middleware`, for the reason every other member * of this ring avoids `@hashbrownai/*`: naming that type would drag the * optional peer into the domain ring, where it is reachable from every * consumer of these models rather than only from the code that installs * `withAiFormBuilder()`. The two are structurally identical, so a consumer * can hand the same functions to both this feature and its own Hashbrown * resources. * * Runs in order, on the transport, immediately before the request leaves the * browser. Return the options to send; throw to refuse the request, which * fails the turn the way a transport error does. */ type DraftRequestMiddleware = (fetchInit: RequestInit) => RequestInit | Promise; /** * Consumer-supplied configuration for the AI form-builder feature. * * The library never names a vendor or a model: `models.planning` is an opaque * identifier forwarded to the consumer's own Hashbrown adapter. */ interface AiFormBuilderConfig { /** Model per tier. */ readonly models: AiFormBuilderModels; /** Loop budgets. Defaults applied by the orchestrator. */ readonly budgets?: AiFormBuilderBudgets; /** * Element union offered to the model. Defaults to the skillet * `COMMON_ELEMENTS` set, which keeps the generated JSON Schema roughly a * third the size of the full union. */ readonly elements?: readonly ElementTypes[]; /** `MultipleInput` unroll depth passed to the skillet schema. Default 1. */ readonly nestingDepth?: number; /** * Overrides the Hashbrown API base URL for this feature's requests only. * When omitted the adapter's own default applies. */ readonly apiUrl?: string; /** * Hooks applied to the `fetch` options of every request this feature sends, * in order, before it leaves the browser. * * The transport calls `fetch` directly, so nothing else your app configures * — an HTTP interceptor chain, a global Hashbrown provider — ever sees these * requests. This is the one place to attach what your adapter endpoint * needs to accept them: `credentials: 'include'` for a cookie-authenticated * endpoint on another origin, an authorisation header, a request-size guard. * Scoped to this feature's requests only, exactly as {@link apiUrl} is. * * When omitted the request goes out with `fetch`'s defaults, which send no * cookies cross-origin. */ readonly middleware?: readonly DraftRequestMiddleware[]; /** * Structured-output mode forwarded to Hashbrown. Use `'json'` when the * provider rejects the schema as too large for schema-constrained decoding. * Default `'strict'`. */ readonly structuredOutputMode?: 'strict' | 'json' | 'tool'; } /** * @public * * Enables the **AI form-builder** layer. Binds the supplied configuration to * {@link AI_FORM_BUILDER_CONFIG} and the default * {@link HashbrownFormDraftOrchestratorService} to {@link FORM_DRAFT_ORCHESTRATOR}, * so a natural-language brief can be turned into a validated form definition and * staged on the form-builder canvas for human review. * * The library never persists a generated form and never holds an API key: the * consumer's own Hashbrown adapter owns the transport, and the human saves the * staged draft through the ordinary form-builder save path. * * Opt-in and tree-shakeable: without this feature both tokens are unbound and * none of the drafting code — including the optional `@hashbrownai/core` peer — * is reachable from `provideNgxTForms()`. * * Consumers that supply their own {@link FormDraftOrchestrator} implementation * should bind it directly to {@link FORM_DRAFT_ORCHESTRATOR} instead of (or * after) calling this — provider order resolution applies. * * @example * bootstrapApplication(AppComponent, { * providers: [ * provideNgxTForms( * config, * withAiFormBuilder({ * models: { planning: 'my-planning-model', worker: 'my-worker-model' }, * }), * ), * ], * }); */ declare function withAiFormBuilder(config: AiFormBuilderConfig): NgxTFormsFeature; /** * @file The **endpoint catalog** — the grounding that lets the drafting * pipeline bind a generated form to endpoints that actually exist. * * Inner-ring module: every import is **type-only** and erased at compile time, * and {@link toAvailableApiEndpoints} is a pure function over those types with * no runtime dependency of its own. The constraint documented on * `domain/form-suggestion.model.ts` therefore still holds — no Angular * concretions, no library internals, no `@hashbrownai/*`. * * ## Why a catalog exists at all * * `createMatOptionsSchema` and `createFormDraftSchema` in * `ngx-t-forms-types/skillet` do not let a model author an endpoint: they take * the endpoints the caller has and emit an *enumeration over their ids*. Asked * for a URL, a model produces `https://api.example.com/departments` — plausible, * confidently wrong, and invisible until the form is used. So the model picks * from a list, and this is the list. * * Without a catalog bound, the `api` data-source branch and the submission * endpoint are simply not offered, and a draft comes back with no endpoint * selected. That is the safe default, not a degraded one. * * ## Why an entry carries the whole stored configuration * * {@link AiEndpointCatalogEntry.config} is a complete, already-persisted * `APIDataFetchingConfigurationInterface` — not a URL, not a partial. Expansion * of a draft is then a lookup on {@link AiEndpointCatalogEntry.id}, a structural * clone and an overlay: pure, synchronous, and incapable of inventing a request. * Anything less makes expansion guess at `httpMethod`, `postFormData`, * `backEndConfig` and the header/query templates, which is the same * hallucination one layer down. * * ## Why there is no workflow / `mongoPipeline` branch * * `MongoDbPipeLineConfigSchema` requires both `valueAccessRules` and a * `responseBody` key that exists on no TypeScript interface, and `pipeline` is a * raw aggregation that cannot be derived from a workflow id. A mongo branch * therefore cannot be deterministically expanded into anything that passes its * own Joi schema, so the pipeline never passes `workflows` to the skillet * factories. API endpoints only. */ /** * @public * * Where in a generated form an endpoint may legitimately be used. * * - `value` — supplies a control's value (`matOptions.fetch.value`). * - `options` — supplies a control's option list (`matOptions.fetch.options`). * - `submission` — receives the completed form (`submissionHandle.submissionAPI`). * - `canSubmit` — gates submission (`submissionHandle.canSubmitAPI`). * * An entry declares every slot it is valid for; an endpoint is offered to the * model for one slot only when it names that slot. A search endpoint that * happens to answer a GET is not a submission target, and nothing but the * consumer knows that. */ type AiEndpointSlot = 'value' | 'options' | 'submission' | 'canSubmit'; /** * @public * * One endpoint the consumer makes available to the drafting pipeline. */ interface AiEndpointCatalogEntry { /** * Stable identifier the model selects by. It MUST equal `config._id` — * expansion is a lookup on this id and then a clone of {@link config}, so a * mismatch silently produces a form pointing at the wrong request. */ readonly id: string; /** * Human name. This is what the model actually chooses on: it is rendered into * the schema's enumeration prose, so it should say what the endpoint is for * rather than repeat its path. */ readonly name: string; /** * What the endpoint returns, in one sentence. Optional, and worth writing: * it sharpens the choice considerably when two endpoints have similar names. */ readonly description?: string; /** Slots this endpoint may be selected for. An entry with none is never offered. */ readonly slots: readonly AiEndpointSlot[]; /** * The complete stored configuration, exactly as it is persisted on a form * today. Cloned into the generated form during expansion; never synthesised, * never partially filled in. */ readonly config: APIDataFetchingConfigurationInterface; } /** * @public * * A function the library calls to obtain the catalog. * * This is the shape {@link AI_ENDPOINT_CATALOG} is bound to: `withAiEndpointCatalog()` * normalises a plain array into one of these, so a reader has exactly one way * to ask for the entries. Mirrors `MSCOA_TREE_PROVIDER`, which has the same * "the consumer knows how to fetch it, the library does not" shape. * * The function is called lazily, when a draft first needs grounding — not at * bootstrap — so a remote catalog costs nothing to a session that never drafts. */ type AiEndpointCatalogProvider = () => Observable; /** * @public * * What a consumer may hand {@link withAiEndpointCatalog}: either the entries * outright, or a function that fetches them. * * The function form is the realistic one. There is no endpoint list sitting in * consumer code today — endpoints are read out of a stored Postman collection * over HTTP — so a catalog is normally resolved, not declared. The array form * exists for the case where the consumer already holds the entries (tests, * a static deployment) and should not be forced to wrap them. */ type AiEndpointCatalogSource = readonly AiEndpointCatalogEntry[] | AiEndpointCatalogProvider; /** * @public * * Grounds the AI form-builder on **endpoints that exist**. Binds the supplied * catalog to {@link AI_ENDPOINT_CATALOG}, which is what lets the pipeline offer * the `api` data source and a submission target: the model picks an endpoint id * out of the catalog, and expansion clones the stored configuration behind it. * * Without this feature the token stays unbound, neither branch is offered, and * a generated form comes back with no endpoint selected — the safe default, * because the only alternative to a real endpoint is an invented URL that looks * entirely plausible. * * Pass either the entries or a function that fetches them. The function form is * the usual one — endpoints normally live in a stored Postman collection read * over HTTP — and it is called lazily, when a draft first needs grounding, so a * session that never drafts pays nothing. An array is normalised into a * provider, so the token always holds one shape. * * Composes with {@link withAiFormBuilder} and {@link withAiMscoaGrounding}; * order is irrelevant, and this feature binds nothing but its own token. * * @param source - The catalog entries, or a function returning them. * @returns The feature, for `provideNgxTForms(...)`. * * @example * // Resolved at first use — the realistic shape. * bootstrapApplication(AppComponent, { * providers: [ * provideNgxTForms( * config, * withAiFormBuilder({ models: { planning: 'my-planning-model' } }), * withAiEndpointCatalog(() => inject(EndpointCatalogService).load()), * ), * ], * }); * * @example * // Already held in hand — a static deployment, or a test. * withAiEndpointCatalog([ * { id: storedConfig._id, name: 'Departments', slots: ['options'], config: storedConfig }, * ]); */ declare function withAiEndpointCatalog(source: AiEndpointCatalogSource): NgxTFormsFeature; /** * @file **MSCOA grounding** — the municipal chart-of-accounts facts a model * cannot infer from a prompt, supplied by the consumer so the drafting pipeline * can author an `mscoaSelection` control that names segments which exist. * * Inner-ring module: every import is **type-only** and erased at compile time. * No Angular concretions, no library internals, no `@hashbrownai/*`. * * ## What is actually missing without this * * `mscoaConfigSchema` (`ngx-t-forms-types/skillet`) already covers most of an * `IScoaInputConfig`: it enumerates `accountValueLabel` from the live option * pool, states the dual-cash semantics, and describes every segment flag. One * member it cannot constrain is `segment` itself, and its own description says * why — *"The real list comes from the loaded account tree, so use the key the * municipality actually publishes rather than inventing a plausible one."* * A model asked for segments produces ITEM, FUNCTION, FUND: correct for most * municipalities, silently wrong for one that publishes a different set, and * indistinguishable from correct on inspection. * * So the consumer names the segments. That is the whole job of this type. * * ## What it deliberately does not carry * * Not the account tree. The tree is large, per-tenant and already reachable at * runtime through `MSCOA_TREE_PROVIDER` (and, on the host, `getScoaTree()`); * duplicating it into a config object would put a second copy on a bootstrap * path that mostly never drafts a form. Nor account validation: that stays with * the host's `validateMscoaSelection`, which runs against a filled-in form, not * against a definition being authored. * * This is authoring-time metadata — a short, hand-written list — and nothing * here is read from `IStoreFunctions`. That interface opens with an * `[x: string]: any` index signature, so anything crossing that seam has to be * narrowed at the read site (the precedent is `t-form-import-controller.ts` * narrowing `getSCOAAccountByKey` to `Observable<{ account?: IScoaAccount }>`). * Keeping the seam out of this file is what guarantees no `any` reaches the * public surface, and it is why the shared `IStoreFunctions` needs no widening. */ /** * @public * * Which side (or sides) of the books an `mscoaSelection` control captures. * * Declared as a string-literal union rather than re-exporting the * `AccountingBasis` enum, so a consumer can write `'dual'` without importing an * enum to say it. The two are pinned together below: an enum member is * assignable to this union, and drift fails the build. */ type AiMscoaAccountingBasis = 'accrual' | 'cash' | 'dual'; /** * @public * * One segment of the chart of accounts this deployment publishes. */ interface AiMscoaSegmentDescriptor { /** * The segment key exactly as the chart of accounts spells it, upper case — * `ITEM`, `FUNCTION`, `FUND`, `PROJECT`, `REGION`. This is the value written * to `IIncludedSegmentConfig.segment`, and it must match a key the loaded * account tree actually carries: a segment nothing publishes renders nothing. */ readonly segment: string; /** * Heading shown above this segment's row, in title case. When omitted the * model writes one from the segment key and the form's own subject matter. */ readonly label?: string; /** * What this segment classifies, in one sentence. Optional, and the member * that most improves the result: it is what lets a model choose FUNCTION over * PROJECT on meaning rather than on the order of the list. */ readonly description?: string; /** * Whether the model may put this segment on a generated control. Defaults to * `true`. Set `false` for a segment that exists in the tree but is derived, * deprecated or reserved — it stays documented here without being offered. */ readonly selectable?: boolean; } /** * @public * * Chart-of-accounts grounding for the MSCOA specialist, installed with * {@link withAiMscoaGrounding}. * * Bound to {@link AI_MSCOA_GROUNDING}. Unbound, the specialist never activates * and the pipeline does not offer an `mscoaSelection` element — graceful * degradation, not a failure. */ interface AiMscoaGrounding { /** * The segments this deployment publishes, in the order they should normally * appear on a control. Required, and the reason the type exists: an empty * list grounds nothing, so the specialist treats it exactly as an unbound * token. */ readonly segments: readonly AiMscoaSegmentDescriptor[]; /** * The accounting basis a generated control should capture unless the brief * says otherwise. Written to `IScoaInputConfig.accountingBasis`. When omitted * the model decides from the brief, which is right for a form that states its * basis and a coin toss for one that does not. */ readonly defaultAccountingBasis?: AiMscoaAccountingBasis; /** * Which field of an account identifies it to a user here — one of the values * in `MSCOA_ACCOUNT_VALUE_LABEL_OPTIONS` (`'SCOAid'`, `'AccountNumber'`, * `'AccountNumberShortened'`, `'ShortDescription'`, …), written to * `IScoaInputConfig.accountValueLabel`. * * Typed `string` because that pool is a runtime option list with no literal * type to narrow to; the schema still enumerates it, so a wrong value is * caught at generation. Worth setting: which one an administrator reads back * is a house convention, not something recoverable from the field names. */ readonly defaultAccountValueLabel?: string; } /** * @public * * Grounds the AI form-builder's MSCOA specialist on **the chart of accounts * this deployment actually publishes**. Binds the supplied metadata to * {@link AI_MSCOA_GROUNDING}. * * The generation schema already constrains everything else about an * `mscoaSelection` control; the one fact it cannot supply is which segment keys * exist. Asked without grounding, a model writes `ITEM`, `FUNCTION`, `FUND` — * right for most municipalities, quietly wrong for one that publishes a * different set, and impossible to spot by reading the output. * * Without this feature the token stays unbound, the specialist never activates, * and no `mscoaSelection` element is offered at all. Graceful degradation, not * a failure. * * This is hand-written authoring-time metadata. It carries no account tree — * that stays behind `MSCOA_TREE_PROVIDER` at runtime — and reads nothing from * `IStoreFunctions`, which is why the shared typings interface needs no * widening to support the feature. * * Composes with {@link withAiFormBuilder} and {@link withAiEndpointCatalog}; * order is irrelevant, and this feature binds nothing but its own token. * * @param grounding - The published segments, plus the deployment's defaults. * @returns The feature, for `provideNgxTForms(...)`. * * @example * bootstrapApplication(AppComponent, { * providers: [ * provideNgxTForms( * config, * withAiFormBuilder({ models: { planning: 'my-planning-model' } }), * withAiMscoaGrounding({ * segments: [ * { segment: 'ITEM', label: 'Item', description: 'What is being bought or earned.' }, * { segment: 'FUNCTION', label: 'Function' }, * { segment: 'PROJECT', label: 'Project', selectable: false }, * ], * defaultAccountingBasis: 'dual', * defaultAccountValueLabel: 'AccountNumberShortened', * }), * ), * ], * }); */ declare function withAiMscoaGrounding(grounding: AiMscoaGrounding): NgxTFormsFeature; /** * @file The contract a host fulfils to let the library read — and write — * spreadsheets. * * The library never parses or serialises a workbook itself. Both are * environmental (LIB-05): they need a spreadsheet library (`exceljs`, `xlsx`, …) * that the host already owns and already has opinions about — how dates are * normalised, what a formula cell yields, which sheets matter. The host binds a * parser (and, optionally, a writer) through `withSpreadsheetImport()`; the * library hands the parser the picked file's bytes and receives header-keyed * rows back — the same shape `TFormImportController` consumes for bulk imports, * so a sheet that works for one works for the other — and hands the writer the * import template it built for a field, receiving a file to offer for download. * * Inner-ring file: no imports at all. `ArrayBuffer` is an ECMAScript type, so * the contract stays free of DOM types. */ /** One worksheet of a parsed workbook, as header-keyed rows. */ interface SpreadsheetSheet { /** The worksheet's name as it appears on its tab. */ readonly name: string; /** * One record per data row, keyed by the header-row cell text. Cells are * whatever the parser produced — typically strings and numbers; `Date`, * `boolean` and `null` are tolerated too. */ readonly rows: readonly Record[]; } /** * A spreadsheet file in memory. The library hands the parser the file the user * picked, and receives from the writer the template it then offers for * download. */ interface SpreadsheetSource { /** The file name, extension included (e.g. `lines.xlsx`). */ readonly fileName: string; /** The file's MIME type — as the browser reported it, or `''` when it reported none. */ readonly mimeType: string; /** The file's bytes. */ readonly data: ArrayBuffer; } /** * Turns a spreadsheet's bytes into header-keyed rows, one entry per sheet. * * Reject (or throw) with an `Error` whose message can be shown to the user — * the library surfaces it verbatim. * * @example * // exceljs-backed parser, bound once at bootstrap via withSpreadsheetImport() * const parseWithExcelJs: SpreadsheetParser = async ({ data }) => { * const workbook = new ExcelJS.Workbook(); * await workbook.xlsx.load(data); * return workbook.worksheets.map(sheet => ({ name: sheet.name, rows: sheetToRows(sheet) })); * }; */ type SpreadsheetParser = (source: SpreadsheetSource) => Promise; /** One sheet of a template the library asks the host to write. */ interface SpreadsheetTemplateSheet { /** * The name for the sheet's tab. Hosts sanitise it as their format requires * (Excel caps names at 31 characters and forbids `[ ] * / \ ? :`). */ readonly name: string; /** Row 1, left to right. */ readonly headers: readonly string[]; /** * The rows below the headers, keyed by header. A key with no entry leaves * the cell empty; a data sheet that is only headers has none. */ readonly rows: readonly Record[]; } /** * A workbook the library wants written: the import template for one * multiple-input field. * * The first sheet is the data sheet — named after the field's * `formControlName`, holding the headers the import matches and no rows. A * `Guide` sheet follows with one row per column: its key, label, whether it is * required and what to enter. Importing the template back (rows filled in) * picks the data sheet automatically. */ interface SpreadsheetTemplate { /** Suggested file name without its extension, e.g. `Template-lines`. */ readonly baseName: string; /** The sheets, in tab order; the data sheet leads. */ readonly sheets: readonly SpreadsheetTemplateSheet[]; } /** * Serialises a {@link SpreadsheetTemplate} into a file the library offers for * download. The host chooses the format: the extension goes on `fileName` * (`` `${baseName}.xlsx` ``) and the matching type on `mimeType`. * * Reject (or throw) with an `Error` whose message can be shown to the user. * * @example * const writeWithExcelJs: SpreadsheetWriter = async ({ baseName, sheets }) => { * const workbook = new ExcelJS.Workbook(); * for (const { name, headers, rows } of sheets) { * const sheet = workbook.addWorksheet(name); * sheet.columns = headers.map(header => ({ header, key: header })); * for (const row of rows) sheet.addRow(row); * } * return { fileName: `${baseName}.xlsx`, mimeType: XLSX_MIME, data: await workbook.xlsx.writeBuffer() }; * }; */ type SpreadsheetWriter = (template: SpreadsheetTemplate) => Promise; /** * Lets users prepopulate a multiple-input table from a spreadsheet. * * Binds the supplied parser to {@link SPREADSHEET_PARSER}. With it bound, every * multiple-input field that allows adding rows gains a chevron on its add-row * button whose menu offers "Import from spreadsheet". The user picks a file; * each row is validated by the same per-row tower the bulk * `TFormImportController` uses for that field's sub-rows — calculations, * API-fetched options, mSCOA lookups and custom validators included — and the * rows that pass are appended to the table. Rows that fail are shown against * their column headers and are never added. * * Pass a `writer` as well and the menu also offers "Download import template": * a workbook the library builds for the field — a data sheet carrying exactly * the headers the import matches, plus a `Guide` sheet that says what goes in * each column — which the writer serialises ({@link SPREADSHEET_WRITER}). The * import dialog then offers the same download in place of its column list. * * The library does not read or write workbooks itself: both functions are the * host's, so the app decides which spreadsheet library it carries and how cells * are normalised (dates, formula results, rich text). Omit the feature and * nothing changes — the affordances stay hidden and no import code path is * reachable. * * Expected sheet layout: row 1 is the header row; a header matches a child * input by its `formControlName` or, failing that, its label (both * case-insensitively). A workbook with several sheets uses the one named after * the field's `formControlName` when present, otherwise the first — so the * detail sheet of a bulk-import template, or the template downloaded from the * menu, can be reused as-is. An `id` column is ignored: imported rows always * receive fresh identities. * * @param parser Reads a picked file's bytes into header-keyed rows per sheet. * @param options.writer Serialises the import template the library builds for * a field. Optional; without it no template is offered. * * @example * provideNgxTForms( * config, * withSpreadsheetImport( * async ({ data }) => { * const workbook = new ExcelJS.Workbook(); * await workbook.xlsx.load(data); * return workbook.worksheets.map(sheet => ({ name: sheet.name, rows: sheetToRows(sheet) })); * }, * { * writer: async ({ baseName, sheets }) => ({ * fileName: `${baseName}.xlsx`, * mimeType: XLSX_MIME, * data: await writeWorkbook(sheets), * }), * }, * ), * ) */ declare function withSpreadsheetImport(parser: SpreadsheetParser, options?: { readonly writer?: SpreadsheetWriter; }): NgxTFormsFeature; /** * Provides the action handlers (submit, save, cancel, etc.) that the forms * runtime invokes when the user interacts with form controls. Bound from the * `formActions` field of `NgxTFormsConfig` when the consumer calls * `provideNgxTForms`. * * @see provideNgxTForms */ declare const FORM_ACTIONS_TOKEN: InjectionToken; /** * Provides the active `FormInterface` definition rendered by the forms * runtime. Components downstream of the form host inject this token to read * the form configuration without coupling to the store. * * @see provideNgxTForms */ declare const FORM_CONFIG_TOKEN: InjectionToken; /** * Provides the active `FormSlideInterface` (the slide/step currently being * rendered). Scoped per slide by the form host so individual inputs can read * their slide context. * * @see provideNgxTForms */ declare const FORM_SLIDES_TOKEN: InjectionToken; /** * Provides the in-class utility object (`InClassFormUtilsInterface`) that * exposes shared helper functions to form input components — formatting, * validation helpers, and cross-input lookups. * * @see provideNgxTForms */ declare const UTILS_OBJECT_TOKEN: InjectionToken; /** * Provides the merged form-input descriptor injected into each individual * input element. The descriptor is an intersection of every supported input * shape (`IWorkflowDocumentPicker & IBasicFormInput & IFileUploadInput & * IDateRangePickerInput & ITextareaProperties & IToggleInput & IMatrixInput * & IMultiple`) so a single token can describe any rendered input. * * @see provideNgxTForms */ declare const FORM_INPUTS_TOKEN: InjectionToken; /** * Provides the `IMultiple` configuration to "multiple"-type inputs (inputs * that produce an array of values). Scoped per input instance. * * @see provideNgxTForms */ declare const MULTIPLE_FORM_INPUT_TOKEN: InjectionToken; /** * Provides a factory function that returns an `Observable` * representing the MSCOA chart-of-accounts tree. Consumers bind this token * to their backend lookup so MSCOA-aware inputs can lazily fetch the tree * without depending on a concrete HTTP client. * * @see provideNgxTForms */ declare const MSCOA_TREE_PROVIDER: InjectionToken<() => Observable>; /** * Provides the top-level `NgxTFormsConfig` supplied by the consumer when * calling `provideNgxTForms`. This is the root configuration token from * which `formActions`, `formBuilder`, and other runtime concerns are read. * * @see provideNgxTForms */ declare const NGX_T_FORMS_CONFIG_TOKEN: InjectionToken; /** * @public * * Abstract repository for MongoDB-aggregation-pipeline operations used by the * library's pipeline-generator UI. The default HTTP-backed implementation is * {@link HttpPipelineRepository}, bound by {@link withHttpPipeline}. * * Consumers may bind a custom implementation against * {@link PIPELINE_REPOSITORY} (e.g. for offline / mocked environments) by * providing an alternative `useClass` against the same token instead of (or * after) `withHttpPipeline()`. * * Only the methods that cross the HTTP boundary are abstracted here; pipeline * orchestration state (`pipeline$`, `chatHistory$`, etc.) remains inside the * adapter / generator UI per LP §9 (the repository owns I/O, not UI state). */ declare abstract class PipelineRepository { /** * Execute the given aggregation pipeline against the configured backend for * the supplied workflow. * * @param pipeline - Validated MongoDB aggregation stages to run. * @param workflowId - Identifier of the workflow whose schema the pipeline * targets. * @returns An observable that emits exactly one {@link IRunPipelineResponse} * then completes. Errors surface as a typed Error. */ abstract runPipeline(pipeline: PipelineStage[], workflowId: string): Observable; /** * Ask the configured backend to generate (or refine) an aggregation pipeline * from a natural-language query plus the workflow's document schema. * * @param query - The user's prompt. * @param schema - Document schema the generator should target. * @param threadId - Optional generator-session id (continues an in-flight * chat). * @param existingPipeline - The pipeline-so-far, for incremental refinement. * @param previousError - The last execution error to feed back into the * model, when applicable. */ abstract generatePipeline(query: string, schema: object, threadId: string | undefined, existingPipeline: PipelineStage[], previousError: string): Observable; /** * Fetch the document schema for the supplied workflow. * * @param workflowId - Identifier of the workflow whose schema to load. */ abstract getSchema(workflowId: string): Observable<{ schema: object; }>; } /** * @public * * Token binding the {@link PipelineRepository} abstraction. The default * HTTP-backed implementation is {@link HttpPipelineRepository}, registered by * {@link withHttpPipeline}. Consumers may substitute their own implementation * by binding `useClass` (or `useValue` for a mock) against this token. */ declare const PIPELINE_REPOSITORY: InjectionToken; /** * Secret used to derive per-input HMAC keys for client-side encryption of * sensitive form values (currently: signature input payloads). * * Consumers MUST configure this via {@link withInputSecret} when the * signature input is in use. The library does not ship a default — hardcoding * a secret inside a published library would render it public. * * Treat the value as opaque, high-entropy, and never derived from anything * the browser can read (e.g. `environment.ts` baked into the bundle is * acceptable for low-sensitivity rotation; HSM-backed values are better). * * @see withInputSecret */ declare const INPUT_SECRET_TOKEN: InjectionToken; /** * Optional source of the form id to load into {@link FormBuilderComponent}. * * Bound by `withRouterFormId()` (shipped by default in `provideNgxTForms`) to the * activated route's `formId` param. When unbound — e.g. a router-free build — the * builder loads from its `formId` input only. This decouples the builder from a * direct `ActivatedRoute` dependency (plan D-B2 / §5.5). * * @public */ declare const FORM_ROUTE_SOURCE: InjectionToken>; /** * @file Pure domain types for the **AI edit-suggestion reporting layer**. * * These types describe the read-only, real-time view of a live form that an * external AI system observes (the *editable schema* + current values + * validation state) and the shape of the *optional* value edits it may propose * for a user to preview and accept/reject. They are the wire contract between a * running form tower and the {@link FormSuggestionRegistry}. * * **Inner-ring rule (CLAUDE.md §0.3):** this module imports only `@angular/core` * (the `Signal` *type*) and type-only symbols from `ngx-t-forms-types` — all * erased at compile time. No runtime Angular concretions, no library internals. * * @see FormSuggestionRegistry — the injection-token port external AI consumes. */ /** * A single validation message currently surfaced for an editable field. * * Mirrors the engine's overridable-validation model: a `canOverride === true` * error does not block submission outright (the user may motivate a bypass), * whereas a blocking error (standard validators, business rules) must be * resolved. An external system can use this to avoid proposing values that * would re-trip a known constraint. */ interface EditableFieldError { /** Human-readable validation message. */ readonly message: string; /** `true` when the form may still submit with this error if motivated. */ readonly canOverride: boolean; /** The custom validator's id, when the error originates from one. */ readonly validatorId?: string; } /** * Validation constraints and live error state for one editable field. The * constraint fields are the static rules authored on the column; `errors` is the * live set failing right now (recomputed as values change). */ interface EditableFieldValidation { /** Whether a non-empty value is required. */ readonly required: boolean; /** Regex pattern the value must match, when authored. */ readonly pattern?: string; /** Minimum numeric/date constraint, when authored as a plain value. */ readonly min?: number | string; /** Maximum numeric/date constraint, when authored as a plain value. */ readonly max?: number | string; /** Minimum string length, when authored. */ readonly minLength?: number | string; /** Maximum string length, when authored. */ readonly maxLength?: number | string; /** Live validation messages currently failing on the field. */ readonly errors: readonly EditableFieldError[]; } /** * How a field's value must be expressed in a suggestion — the contract that * tells an external system whether it may send a raw value or a structured * intent the engine resolves: * - `scalar` — a plain value (string/number/boolean/date). * - `choice` — one of the field's {@link EditableFieldSchema.options} values. * - `multipleRow` — an item list; suggest rows via {@link MultipleRowSuggestionValue}. * - `mscoa` — an account selection; suggest an intent via {@link MscoaSuggestionValue} * (never a raw value — the engine resolves accounts via the host's search). */ type EditableFieldKind = 'scalar' | 'choice' | 'multipleRow' | 'mscoa'; /** One selectable segment of an MSCOA field (per accounting basis). */ interface MscoaSegmentDescriptor { /** The segment key a suggestion references (the input's `customSegment` || `segment`). */ readonly segment: string; /** Display label. */ readonly label: string; /** Which accounting basis this segment belongs to. */ readonly basis: 'accrual' | 'cash'; /** When `true`, one account fills both debit + credit (suggest `accountType: 'single'`). */ readonly singleSelect: boolean; /** Whether VAT selection is active for this segment. */ readonly vat: boolean; } /** One custom inner input of an MSCOA field (segment-scoped or account-level). */ interface MscoaInnerInputDescriptor { /** The input key a suggestion references (its `formControlName`). */ readonly key: string; /** Display label. */ readonly label: string; /** The element kind, as a string. */ readonly element: string; /** The segment id this input is scoped to, or absent for an account-level input. */ readonly segmentId?: string; } /** One currently-selected account on an MSCOA field, by account code, for delta proposals. */ interface MscoaCurrentSelection { readonly basis: 'accrual' | 'cash'; readonly segment: string; /** Currently-selected debit account code (`SCOAAccount`), if any. */ readonly debit?: string; /** Currently-selected credit account code (`SCOAAccount`), if any. */ readonly credit?: string; } /** * The capability block describing an MSCOA field — everything an external * system needs to propose a valid account selection without hand-building the * (un-authorable) `IScoaAccount` value objects. */ interface MscoaFieldCapability { /** `'accrual'`, `'cash'`, or `'dual'` (both). */ readonly accountingBasis: 'accrual' | 'cash' | 'dual'; /** Selectable segments across the active basis(es). */ readonly segments: readonly MscoaSegmentDescriptor[]; /** Custom inner inputs that can also carry suggested values. */ readonly inputs: readonly MscoaInnerInputDescriptor[]; /** Currently-selected accounts (by code) per basis/segment. */ readonly currentSelections: readonly MscoaCurrentSelection[]; } /** * The AI-visible projection of one **editable** form field. Excludes * structural, system, calculated, read-only/disabled, view-only and * tower-derived inputs — only fields a user (and therefore a suggestion) could * legitimately change. */ interface EditableFieldSchema { /** The field's stable id (the suggestion target — see {@link FieldSuggestion.fieldId}). */ readonly id: string; /** The field's `formControlName` (its key in the submitted payload). */ readonly formControlName: string; /** Id of the section the field belongs to. */ readonly sectionId: string; /** Display label. */ readonly label: string; /** The element kind (e.g. `'input'`, `'select'`, `'textarea'`), as a string. */ readonly element: string; /** The field's data type, for value coercion/formatting by the consumer. */ readonly dataType: InputDataTypes; /** * The value contract for suggesting to this field — tells the proposer whether * to send a raw value, a choice, an item-list of rows, or an MSCOA intent. */ readonly kind: EditableFieldKind; /** The field's current live value. */ readonly currentValue: unknown; /** * Selectable options (static or resolved from an API), when the field is a * choice input. Absent for free-text/numeric fields. */ readonly options?: readonly FormInputBasicOptionInterface[]; /** * The per-row child-field schema, present only for `kind: 'multipleRow'`. A * proposed row is an object keyed by these fields' `formControlName`s. */ readonly rowFields?: readonly EditableFieldSchema[]; /** The account-selection contract, present only for `kind: 'mscoa'`. */ readonly mscoa?: MscoaFieldCapability; /** Validation constraints + live error state. */ readonly validation: EditableFieldValidation; } /** * A point-in-time snapshot of a form's editable surface, as observed by an * external suggestion provider. Recomputed reactively as the tower adapts to * user input and async data loads. */ interface FormSuggestionSnapshot { /** The form's id, when the definition carries one. */ readonly formId: string | undefined; /** Every currently-editable field. */ readonly fields: readonly EditableFieldSchema[]; /** Whether the form is currently submittable (no blocking/unmotivated errors). */ readonly canSubmit: boolean; /** Whether any async work (calculation/fetch/submission) is in flight. */ readonly isBusy: boolean; } /** * A single proposed value edit for one editable field. Purely advisory — it is * staged for user review and never mutates the live form on its own. * * The shape of `value` is dictated by the target field's * {@link EditableFieldSchema.kind}: * - `scalar` / `choice` → the raw value (a `choice` must be an allowed option value). * - `multipleRow` → a {@link MultipleRowSuggestionValue} (or a bare rows array → appended). * - `mscoa` → a {@link MscoaSuggestionValue} intent (the engine resolves accounts). * A value that does not satisfy the field's kind is rejected on accept — it is * never written to the live form. */ interface FieldSuggestion { /** Target field id (matches {@link EditableFieldSchema.id}). */ readonly fieldId: string; /** The proposed value (shape per the field's {@link EditableFieldSchema.kind}). */ readonly value: unknown; /** Optional natural-language explanation, shown to the user during review. */ readonly rationale?: string; /** Optional model confidence in `[0, 1]`, shown to the user during review. */ readonly confidence?: number; } /** One account-selection intent for an MSCOA field (resolved by the engine). */ interface MscoaAccountSelection { /** Which accounting basis to write. */ readonly basis: 'accrual' | 'cash'; /** The target segment key (see {@link MscoaSegmentDescriptor.segment}). */ readonly segment: string; /** * Which leg to set. `'single'` (or omitted on a single-select segment) fills * both debit and credit with the resolved account. */ readonly accountType?: 'debit' | 'credit' | 'single'; /** Account code / number to resolve via the host account search (e.g. `SCOAAccount`). */ readonly query: string; } /** * The intent payload for an `mscoa` field suggestion. The engine resolves each * selection's `query` to a full account via the host's account search, assembles * the canonical MSCOA value, validates it, and only then commits. */ interface MscoaSuggestionValue { /** Per-(basis, segment) account selections to apply. */ readonly selections: readonly MscoaAccountSelection[]; /** Optional custom inner-input values to apply alongside the account selections. */ readonly inputs?: readonly { readonly basis?: 'accrual' | 'cash'; readonly segment?: string; readonly key: string; readonly value: unknown; }[]; } /** * The payload for a `multipleRow` field suggestion: rows to add to (or replace) * the item list. Each row is keyed by the child fields' `formControlName`s * (see {@link EditableFieldSchema.rowFields}). */ interface MultipleRowSuggestionValue { /** `'append'` (default) adds rows to the existing list; `'replace'` overwrites it. */ readonly mode?: 'append' | 'replace'; /** The proposed rows, each keyed by child `formControlName`. */ readonly rows: readonly Record[]; } /** * A batch of proposed edits from one external provider, addressed to a specific * form by id. */ interface SuggestionBatch { /** The form the batch targets (matches {@link FormSuggestionSnapshot.formId}). */ readonly formId: string | undefined; /** Identifier of the proposing system (for audit/attribution). */ readonly source: string; /** The proposed edits. */ readonly suggestions: readonly FieldSuggestion[]; } /** * A staged suggestion the user can preview and act on. Carries the value that * was current at propose time (`previousValue`) so a preview diff can be shown * and a reject can be reasoned about. Status moves `pending → accepted | rejected`. */ interface StagedSuggestion extends FieldSuggestion { /** Stage id, unique within the tower. */ readonly id: string; /** Identifier of the proposing system (carried from {@link SuggestionBatch.source}). */ readonly source: string; /** The field's value at the moment the suggestion was staged. */ readonly previousValue: unknown; /** * Lifecycle status: `pending` → user review; `resolving` → an async commit is * in flight (MSCOA account resolution); `accepted` → committed to the live * form; `rejected` → dismissed by the user; `invalid` → could not be applied * (see {@link StagedSuggestion.invalidReason}), never written. */ readonly status: 'pending' | 'resolving' | 'accepted' | 'rejected' | 'invalid'; /** Why the suggestion was rejected as invalid (present only when `status === 'invalid'`). */ readonly invalidReason?: string; } /** * The handle a live form tower hands the {@link FormSuggestionRegistry} when it * registers, exposing its reactive snapshot for AI subscribers and a sink that * routes proposed batches back into the tower's staging area. */ interface FormSuggestionChannel { /** The registering form's id (the registry's lookup key). */ readonly formId: string | undefined; /** Reactive editable-surface snapshot of the registering form. */ readonly snapshot: Signal; /** Routes a proposed batch into the tower's staging area for user review. */ readonly propose: (batch: SuggestionBatch) => void; } /** * @file The abstract **suggestion registry** port — the injection-token surface * an external AI system uses to observe live forms and propose optional edits. * * It mirrors the library's established port pattern (see * {@link PipelineRepository}): an abstract class in `domain/`, bound to a token * in `tokens/`, and wired to a concrete implementation by a `with*()` feature in * `config/`. Consumers inject the {@link FORM_SUGGESTION_REGISTRY} token, never a * concrete class. * * The registry is a pure **mediator**: it holds no form logic. Live form towers * register a {@link FormSuggestionChannel} when they initialize and deregister on * teardown; AI subscribers read snapshots and route proposals through it. */ /** * @public * * Port for external AI systems to observe live, editable forms and propose * optional value edits. Bound to {@link FORM_SUGGESTION_REGISTRY} by * {@link withFormSuggestions}. * * Addressing is by `formId`: several form towers can be live at once * (a user form, a builder preview, …), so subscribers target a specific form by * id and proposals carry the id they apply to. Transient import-row towers do * not register. * * @example * // In an AI integration service: * readonly #registry = inject(FORM_SUGGESTION_REGISTRY); * * suggest(formId: string): void { * this.#registry.snapshot(formId) * .pipe(takeUntilDestroyed(this.#destroyRef)) * .subscribe(async snapshot => { * if (!snapshot) return; * const suggestions = await this.model.propose(snapshot.fields); * this.#registry.propose({ formId, source: 'my-model', suggestions }); * }); * } */ declare abstract class FormSuggestionRegistry { /** * Reactive editable-surface snapshot for one form, by id. Emits `undefined` * while no form with that id is registered, then the live snapshot once one * registers (and again whenever its schema/values/validation change). Lazy — * the underlying schema is only computed while subscribed. * * @param formId - The target form's id. * @returns An observable of the form's snapshot, or `undefined` when absent. */ abstract snapshot(formId: string): Observable; /** * Reactive snapshots of every currently-registered form. Emits a new array * whenever a form registers/deregisters or any registered form's snapshot * changes. * * @returns An observable of all live form snapshots. */ abstract snapshots(): Observable; /** * The ids of the currently-registered live forms (forms with an `undefined` * id are omitted). Reactive — recomputes as towers register/deregister. * * @returns A signal of the active form ids. */ abstract activeFormIds(): Signal; /** * Route a batch of proposed edits to the registered form matching * `batch.formId`. The proposals are staged for user review on that form (the * live form is not mutated). No-op when no matching form is registered. * * @param batch - The proposed edits and their target form id. */ abstract propose(batch: SuggestionBatch): void; /** * Register a live form's channel. Called by the form tower on initialize. * * @internal * @param channel - The tower's snapshot + proposal sink. * @returns A handle used to {@link FormSuggestionRegistry.deregister} this exact registration. */ abstract register(channel: FormSuggestionChannel): object; /** * Deregister a previously-registered channel by its handle. Called by the * form tower on teardown / re-initialization. * * @internal * @param handle - The handle returned by {@link FormSuggestionRegistry.register}. */ abstract deregister(handle: object): void; } /** * @public * * Token binding the {@link FormSuggestionRegistry} port — the surface external * AI systems use to observe live editable forms and propose optional edits. * Bound to the default {@link FormSuggestionRegistryService} by * {@link withFormSuggestions}; consumers may substitute their own implementation * by binding `useClass` (or `useValue` for a mock) against this token instead. * * Unbound by default — the suggestion layer is opt-in and fully tree-shakeable. * Form towers inject it `{ optional: true }`, so when the feature is not * installed they simply do not report (zero cost). */ declare const FORM_SUGGESTION_REGISTRY: InjectionToken; /** * @file The abstract **form-draft orchestrator** port — the injection-token * surface a consumer (or the shipped panel) uses to drive the AI drafting * pipeline and read back a staged form. * * It mirrors the library's established port pattern (see * {@link FormSuggestionRegistry} and `PipelineRepository`): an abstract class in * `domain/`, bound to a token in `tokens/`, and wired to a concrete * implementation by a `with*()` feature in `config/`. Consumers inject the * {@link FORM_DRAFT_ORCHESTRATOR} token, never a concrete class. * * The orchestrator is a **staging** surface, not a persistence one: it produces * a `FormInterface` on its session state and stops. Only the human saves, via * the existing form-builder save path (AI_FORM_BUILDER_DESIGN.md §5, guarantee 1). */ /** * @public * * Port driving the Understand → Plan → Generate → Assemble → Verify pipeline * that turns a natural-language brief into a validated `FormInterface`. * Bound to {@link FORM_DRAFT_ORCHESTRATOR} by {@link withAiFormBuilder}. * * One instance owns at most one live session. Call {@link reset} to discard the * current session and release the underlying model resources. * * @example * // In a consumer component hosting the drafting UI: * readonly #orchestrator = inject(FORM_DRAFT_ORCHESTRATOR); * protected readonly session = this.#orchestrator.session; * * describe(text: string): void { * this.#orchestrator.describe(text); * } * * build(): void { * this.#orchestrator.generate() * .pipe(takeUntilDestroyed(this.#destroyRef)) * .subscribe(session => { * if (session.form) this.stageOnCanvas(session.form); * }); * } */ declare abstract class FormDraftOrchestrator { /** * Reactive snapshot of the current drafting session. Republished on every * stage transition, so a template can render progress without subscribing. */ abstract readonly session: Signal; /** * Sends one turn of natural-language intake to the Analyst. * * Safe to call repeatedly: each call appends to the conversation and yields a * refreshed `brief` on the session once the Analyst responds. Answering the * Analyst's `openQuestions` is done with this same method. * * Pass `options.baseline` — the form on the canvas — to put the session in * **update mode**, where the conversation describes changes to that form * rather than a new form. The baseline stays on the session until `reset()`; * later turns need not repeat it. See {@link DescribeOptions}. * * @param message - What the user typed. * @param options - Optional: the saved form this turn is about changing. */ abstract describe(message: string, options?: DescribeOptions): void; /** * Runs the plan → generate → assemble → verify pipeline against the current * brief, including any routed repair turns the budget allows. * * The returned observable is cold: nothing runs until it is subscribed, and * unsubscribing cancels the run. It emits the terminal session — `staged` * when a form passed verification, `failed` otherwise — and completes. * Budget exhaustion is **not** an error: the session is emitted with the * outstanding {@link DraftFinding}s attached so a human can decide. * * @returns The terminal session for this run. */ abstract generate(): Observable; /** * Discards the current session, releases the underlying model resources, and * returns the orchestrator to the `idle` stage. */ abstract reset(): void; } /** * @public * * Token binding the {@link FormDraftOrchestrator} port — the surface external * systems use to drive the AI form-builder draft panel and orchestrate form * creation workflows. Bound to the default {@link HashbrownFormDraftOrchestratorService} by * {@link withAiFormBuilder}; consumers may substitute their own implementation * by binding `useClass` (or `useValue` for a mock) against this token instead. * * Unbound by default — the AI form-builder layer is opt-in and fully tree-shakeable. * Nothing reachable from `provideNgxTForms()` references the orchestrator, so a * consumer who never installs the feature pays nothing for it. The shipped * `` is presentational and injects nothing; the host reads * this token and wires the panel's inputs and outputs to it. */ declare const FORM_DRAFT_ORCHESTRATOR: InjectionToken; /** * @public * * Token binding the resolved configuration for the AI form-builder feature. * Supplied as the argument to {@link withAiFormBuilder}; the feature function * binds this token to the config object passed by the consumer. * * Unbound by default — available only when the AI form-builder feature is * installed via `withAiFormBuilder(config)`. */ declare const AI_FORM_BUILDER_CONFIG: InjectionToken; /** * @public * * Token binding the endpoint catalog the AI form-builder grounds itself on. * * Always bound to a provider function, never to a raw array: * {@link withAiEndpointCatalog} normalises the array form into one, so every * reader asks for entries the same way. Mirrors {@link MSCOA_TREE_PROVIDER}. * * Unbound by default — grounding is opt-in. With this token unbound the * pipeline never offers the `api` data-source branch or a submission endpoint, * and a draft simply comes back with none selected. That is the safe state: the * alternative to a real endpoint is an invented one. */ declare const AI_ENDPOINT_CATALOG: InjectionToken; /** * @public * * Token binding the municipal chart-of-accounts grounding for the AI * form-builder's MSCOA specialist. Supplied as the argument to * {@link withAiMscoaGrounding}. * * Unbound by default — grounding is opt-in, and the orchestrator injects it * `{ optional: true }`. With this token unbound the MSCOA specialist never * activates and no `mscoaSelection` element is offered, rather than one being * authored against segment keys the chart of accounts does not publish. */ declare const AI_MSCOA_GROUNDING: InjectionToken; /** * The host's spreadsheet parser — see {@link SpreadsheetParser}. * * Bound by `withSpreadsheetImport()`. Unbound by default: the library injects it * optionally and keeps every "import from spreadsheet" affordance hidden until a * parser exists, so an app that never opts in ships no import UI and carries no * spreadsheet dependency on the library's account. * * @see provideNgxTForms */ declare const SPREADSHEET_PARSER: InjectionToken; /** * The host's spreadsheet writer — see {@link SpreadsheetWriter}. * * Bound by `withSpreadsheetImport(parser, { writer })`. Unbound by default: the * library injects it optionally, and every "download the import template" * affordance stays hidden until a writer exists. A host that binds only a * parser gets the import without the template. * * @see SPREADSHEET_PARSER */ declare const SPREADSHEET_WRITER: InjectionToken; /** * @public * * Default HTTP-backed implementation of {@link PipelineRepository}. Bound by * {@link withHttpPipeline}; consumers needing a different transport should * provide their own implementation against {@link PIPELINE_REPOSITORY}. * * The endpoint host (`http://localhost:5000`) is preserved from the pre-Phase-2 * `PipelineService` for I-1 parity. Configurable endpoints are tracked for a * later phase — do not widen this surface ad-hoc. */ declare class HttpPipelineRepository implements PipelineRepository { #private; runPipeline(pipeline: PipelineStage[], workflowId: string): Observable; generatePipeline(query: string, schema: object, threadId: string | undefined, existingPipeline: PipelineStage[], previousError: string): Observable; getSchema(workflowId: string): Observable<{ schema: object; }>; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵprov: _angular_core.ɵɵInjectableDeclaration; } /** * @file Default {@link FormSuggestionRegistry} implementation — a root-level * mediator between per-component form towers and external AI subscribers. * * Holds a reactive set of registered {@link FormSuggestionChannel}s. Towers * register on initialize / deregister on teardown; subscribers read snapshots * and route proposals. The service owns **no form logic** — `propose` simply * forwards a batch to the channel whose `formId` matches. * * Provided **only** via {@link withFormSuggestions} (no `providedIn` — it has no * config but is opt-in, so it must not auto-register at root; LP §8 / LIB-02). */ declare class FormSuggestionRegistryService extends FormSuggestionRegistry { #private; /** @internal */ register(channel: FormSuggestionChannel): object; /** @internal */ deregister(handle: object): void; snapshots(): Observable; snapshot(formId: string): Observable; activeFormIds(): Signal; propose(batch: SuggestionBatch): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵprov: _angular_core.ɵɵInjectableDeclaration; } /** * @public * * Default {@link FormDraftOrchestrator}, driving the * Understand → Plan → Generate → Specialise → Assemble → Verify pipeline over a * {@link DraftTransport}. * * Bound to {@link FORM_DRAFT_ORCHESTRATOR} by `withAiFormBuilder()`. The class is * exported so it can be referenced in a custom provider; consumers inject the * token, never this class. */ declare class HashbrownFormDraftOrchestratorService extends FormDraftOrchestrator { #private; /** Reactive snapshot of the current drafting session. */ readonly session: _angular_core.Signal; constructor(); /** * Sends one turn of natural-language intake to the Analyst. * * With `options.baseline` the session enters update mode — the form is kept * on the session and every later turn and run is about changing it — until * `reset()`. */ describe(message: string, options?: DescribeOptions): void; /** Runs plan → generate → specialise → assemble → verify, including routed repair turns. */ generate(): Observable; /** Discards the session and releases every live model resource. */ reset(): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵprov: _angular_core.ɵɵInjectableDeclaration; } /** * @file Builds a Hashbrown **Skillet** schema (`@hashbrownai/core`) from a live * {@link FormSuggestionSnapshot}, so an LLM's structured output is constrained to * the form's *actual* dynamic shape — correct per-field types, enumerated * options, item-list row objects, and MSCOA account-selection intent. * * The schema regenerates from each snapshot, so it adapts as the tower adapts * (new fields, resolved options, changed validation). Each builder takes a human * description first, which the model reads as guidance. * * `@hashbrownai/core` is a **peer dependency** — the consuming app owns and * installs it; importing this builder is what pulls it in. * * @see toSuggestionBatch — converts the LLM's output back into a SuggestionBatch. */ /** * Builds a Skillet schema describing the suggestions an LLM may propose for a * form, keyed by field id: each value is the field's correctly-typed schema or * `null` (leave unchanged). Feed the result to a Hashbrown structured-output API * (e.g. `structuredCompletionResource({ schema })`), then convert the model's * output with {@link toSuggestionBatch} and route it to `FORM_SUGGESTION_REGISTRY`. * * @param snapshot - A live editable-form snapshot (from `FORM_SUGGESTION_REGISTRY.snapshot()`). * @param options - `streaming: true` builds a streamable schema (`s.streaming.object`). * @returns A Skillet schema (`s.object` / `s.streaming.object`) for the suggestion payload. * * @example * const schema = buildSuggestionSchema(snapshot); * const result = structuredCompletionResource({ model, input, schema }); * registry.propose(toSuggestionBatch(snapshot.formId, 'my-model', result.value() ?? {})); */ declare function buildSuggestionSchema(snapshot: FormSuggestionSnapshot, options?: { streaming?: boolean; }): s.HashbrownType; /** * @file Converts the keyed output of an LLM structured completion (driven by * {@link buildSuggestionSchema}) back into a {@link SuggestionBatch} ready for * `FORM_SUGGESTION_REGISTRY.propose()`. * * The schema is keyed by field id with `null` meaning "leave unchanged", so this * simply drops `null`/`undefined` entries and forwards each remaining value * verbatim — the engine's per-kind resolvers interpret it (raw scalar/choice, * a `MultipleRowSuggestionValue` rows array, or an `MscoaSuggestionValue`). */ /** * Builds a {@link SuggestionBatch} from a Skillet structured-output result. * * @param formId - The form the suggestions target (use the snapshot's `formId`). * @param source - Identifier of the proposing system (for audit/attribution). * @param output - The LLM output keyed by field id (values, or `null` to skip). * @returns A batch containing one {@link FieldSuggestion} per non-null field. */ declare function toSuggestionBatch(formId: string | undefined, source: string, output: Record): SuggestionBatch; /** * @file Builds a Hashbrown **Skillet** schema (`@hashbrownai/core`) describing a * {@link FormBrief} — the Analyst's structured intake — so the intake turn's * structured output is constrained to the pipeline's own conversation/planning * shape. * * This schema describes a **pipeline artifact**, not the form model: it never * carries an element type, a validator, or anything else that belongs to * `ngx-t-forms-types/skillet`, which remains the single generation contract for * the form itself (AI_FORM_BUILDER_DESIGN.md §7). * * `@hashbrownai/core` is a **peer dependency** — the consuming app owns and * installs it; importing this builder is what pulls it in. */ /** * Builds a Skillet schema describing a {@link FormBrief} — the Analyst's * structured intake (proposed title, summary, inferred field intents, a * numbered business-rules ledger, and open questions for the user). Feed the * result to a Hashbrown structured-output API for the intake turn; the pipeline * never uses this schema to describe the generated form itself — that is * `ngx-t-forms-types/skillet`'s job alone. * * @param options - `streaming: true` builds a streamable schema * (`s.streaming.object` at the root, `s.streaming.array` for `fields`, `rules` * and `openQuestions`) so the panel can render the brief as it arrives. * Otherwise every node uses the non-streaming builders. * @returns A Skillet schema (`s.object` / `s.streaming.object`) for a {@link FormBrief}. * * @example * const schema = buildBriefSchema({ streaming: true }); * const result = structuredCompletionResource({ model, input, schema }); * // result.value() narrows towards a FormBrief shape as tokens stream in. */ declare function buildBriefSchema(options?: { streaming?: boolean; }): s.HashbrownType; /** * @file Builds a Hashbrown **Skillet** schema (`@hashbrownai/core`) describing a * {@link FormBlueprint} — the Architect's slide/field-stub plan — so the * planning turn's structured output is constrained to the pipeline's own plan * shape and to a caller-supplied element union. * * This schema describes a **pipeline artifact**, not the form model: a field * stub carries only an {@link ElementTypes} pick, rule-tag references and the * specialist flags below — never a validator, an endpoint or anything else that * belongs to `ngx-t-forms-types/skillet`, which remains the single generation * contract for the form itself (AI_FORM_BUILDER_DESIGN.md §7). * * ## Why the specialist flags are conditional * * Phase 2 adds three grounded specialists, and the Architect is the agent that * decides which fields need one. Each flag is therefore offered **only when the * pipeline can honour it**: `needsDataSource` needs an endpoint catalog to point * at, `needsMscoa` needs chart-of-accounts grounding. Showing a model a choice * the library cannot act on is the same failure as asking it for a URL — it * answers confidently, and the answer goes nowhere. * * With every option left off, this builder emits exactly the Phase 1 schema. * * `@hashbrownai/core` is a **peer dependency** — the consuming app owns and * installs it; importing this builder is what pulls it in. */ /** * Which specialist flags the field-stub schema offers, which endpoints the form * may be submitted to, and whether the schema streams. Declared inline on * {@link buildBlueprintSchema} rather than as an exported interface, so the * public surface gains no name that consumers do not need. */ interface BlueprintSchemaOptions { readonly streaming?: boolean; readonly dataSourceGrounding?: boolean; readonly mscoaGrounding?: boolean; readonly logic?: boolean; readonly submissionEndpoints?: readonly AvailableApiEndpoint[]; readonly canSubmitEndpoints?: readonly AvailableApiEndpoint[]; /** * Update mode: the plan revises a saved form, so the schema offers * `removedFields` — the one thing a revision must be able to say that a plan * from nothing never needs to. */ readonly update?: boolean; } /** * Builds a Skillet schema describing a {@link FormBlueprint} — the plan of * slides and field stubs produced before the expensive per-column generation * step, so it is cheap to re-run. Each field stub's `element` is constrained to * `elements`, so the model can only pick a legal element. * * @param elements - The element union this pipeline is configured to offer. * Rendered as an `s.enumeration` so the model cannot pick outside the set the * consumer's Field Generator is prepared to expand. * @param options - `streaming: true` builds a streamable schema * (`s.streaming.object` at the root, `s.streaming.array` for `slides`) so the * panel can render the plan as it arrives. Otherwise every node uses the * non-streaming builders. * * `dataSourceGrounding`, `mscoaGrounding` and `logic` each add one specialist * flag to the field stub, and are set by the orchestrator only when the * corresponding specialist can actually run. `submissionEndpoints` and * `canSubmitEndpoints` add the two form-level endpoint choices, each set only * when a bound catalog declares an endpoint for that slot. All five default to * off, which reproduces the Phase 1 schema exactly. * @returns A Skillet schema (`s.object` / `s.streaming.object`) for a {@link FormBlueprint}. * * @example * const schema = buildBlueprintSchema(config.elements ?? COMMON_ELEMENTS, { streaming: true }); * const result = structuredCompletionResource({ model, input, schema }); * // result.value() narrows towards a FormBlueprint shape as tokens stream in. */ declare function buildBlueprintSchema(elements: readonly ElementTypes[], options?: BlueprintSchemaOptions): s.HashbrownType; /** * Renders the list of forms a user can open, edit, or build, with search, * grouping (system vs custom), and section switching (active / archived / * deleted) wired through the consumer-supplied `formBuilder` config. * * @remarks * The component reads its data from `FormsStoreService` (provided locally) * and reads the consumer's `NgxTFormsConfig` from {@link NGX_T_FORMS_CONFIG_TOKEN} * to discover the close-button template and form CRUD callbacks * (`editForm`, `addNewForm`). * * Inputs: * - _None._ The component is driven entirely by the activated route and * the injected forms store. * * Outputs: * - _None._ User intents (archive / unarchive / delete / edit / add) are * delegated to the store and to the consumer's `formBuilder` config. */ declare class FormsComponent implements OnInit { #private; selectFormsList$: Observable; selectGroupedForms$: Observable>; searchQuery$: Observable; selectHasActiveSearch$: Observable; selectFilteredGroupedForms$: Observable>; selectLoadingForms$: Observable; selectErrors$: FormsStoreSelectorsInterface['selectErrors$']; selectFormListSection$: Observable; NGX_T_FORMS_CONFIG: ngx_t_forms_types.NgxTFormsConfig; closeButton: ngx_t_forms_types.ActionButton; get formListSections(): Array<{ value: FormListSection; label: string; }>; viewportWidth: number; ngOnInit(): void; labels: { label: string; formControlName: string; }[]; activeForm: string | null; getCols(): number; getFormAvatar(formTitle: string): string; archive(form: LocalFormStateSelectorInterface, event: Event): Promise; unarchive(form: LocalFormStateSelectorInterface, event: Event): Promise; delete(form: LocalFormStateSelectorInterface, event: Event): Promise; loadForms(): void; router: Router; activatedRoute: ActivatedRoute; setSection(value: FormListSection): void; editForm(form: LocalFormStateSelectorInterface): void; addNewForm(): void; clearSearch(): void; get searchQuery(): string; set searchQuery(v: string); static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** * Editor surface for authoring a form definition — sections, steps, and * the individual inputs in each step. Hosts the section stepper, input * editor, builder menu, and missing-config diagnostics. * * @remarks * The component reads its state and effects from a locally provided * `FormsStoreService` and pulls the consumer-supplied close-button template * from {@link NGX_T_FORMS_CONFIG_TOKEN}. Form loading is driven either by * the `formId` input or by the activated route's `formId` param. * * Inputs: * - `formId: string` (optional) — when set, triggers `loadForm$` on the store. * * Outputs: * - _None._ All save/refresh intents are dispatched through the local store. * * @public * @deprecated Migrating to the strict library mandates. Public surface is * preserved verbatim; internal authoring is being refactored. */ declare class FormBuilderComponent { #private; /** Form-load spinner. */ readonly selectLoadingForm: _angular_core.Signal; /** Builder busy (saving / mutating). */ readonly selectFormBuilderIsBusy: _angular_core.Signal; /** "Updated N days ago" label, or null. */ readonly selectFormUpdated: _angular_core.Signal; /** Whether the current form passes validation and can be saved. */ readonly canSaveForm: _angular_core.Signal; /** Whether the form in edit is unsaved (no formId yet). */ readonly isNewForm: _angular_core.Signal; /** Optional form identifier. When set, triggers `loadForm$` on the local store. */ readonly formId: _angular_core.InputSignal; /** Whether a form id is present in the store. */ readonly selectHasFormId: _angular_core.Signal; /** Whether the form has missing/invalid configuration. */ readonly hasMissingConfigs: _angular_core.Signal; /** Whether an input editor panel is currently open. */ readonly elementEditorOpen: _angular_core.Signal; pendingExternalRefresh: _angular_core.Signal<{ form: FormInterface; timestamp: Date; } | null>; refreshCountdown: _angular_core.WritableSignal; constructor(); /** * Loads an already-validated form definition into this canvas as an unsaved * working form. * * The staging half of the AI form-builder contract: the drafting pipeline * produces a `FormInterface`, and a human saves it — nothing is persisted * here. The draft enters through the same store path a database-loaded form * takes, so once it lands it is indistinguishable from an opened unsaved * form: same selectors, same editor, same save flow. * * This component knows nothing about the AI layer — the argument is an * ordinary `FormInterface`, so nothing here imports, or pulls in, the * drafting code. A host that never drafts pays nothing for this method. * * By default any `formId` on the incoming form is discarded: a staged draft * is a new form, so saving it creates rather than overwrites. With * `options.mode: 'update'` the draft is applied to the form that is open * instead — it keeps that form's identity, so Save updates it — which is how * a result of the AI form builder's update mode (a draft built from the open * form as its baseline, see {@link currentForm}) is put back. When nothing * saved is open, `'update'` behaves as `'replace'`. * * @param form - The definition to stage. Expected to have passed the same Joi * validation a stored form passes; the canvas is not a validator. * @param options - `mode: 'update'` to apply the draft to the open form. * * @example * readonly #canvas = viewChild.required(FormBuilderComponent); * stage(form: FormInterface): void { * this.#canvas().stageGeneratedDraft(form); * } */ stageGeneratedDraft(form: FormInterface, options?: { readonly mode?: 'replace' | 'update'; }): void; /** * The form as the canvas currently holds it — saved or not — or `undefined` * when none is loaded. * * The host hands this to the AI form builder as the **baseline** of an * update (`FormDraftOrchestrator.describe(text, { baseline })`), and hands * the staged result back through {@link stageGeneratedDraft} with * `mode: 'update'`. A snapshot, not a live reference: editing the returned * object changes nothing on the canvas. * * @returns A structural copy of the form in edit, or `undefined`. */ currentForm(): FormInterface | undefined; triggerRefreshNow(): void; readonly NGX_T_FORMS_CONFIG: ngx_t_forms_types.NgxTFormsConfig; readonly closeButton: ngx_t_forms_types.ActionButton; saveForm: () => void; /** Editable form title — seeded from the store, overridable by local edits. */ protected readonly titleValue: _angular_core.WritableSignal; /** Pushes a title edit to the store (schedule-only refresh, matching `setFormTitle`). */ protected setTitleValue(value: string): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** * Angular-aware view of {@link ITowerFormSteps}. The shared `ngx-t-forms-types` * package keeps `sectionForm` opaque (`unknown`) so it carries no `@angular/forms` * dependency. Inside this library the value is always a reactive `FormGroup`, so * we re-narrow it here for template binding and call-site type safety. A * `TowerFormStep` remains assignable to `ITowerFormSteps`. */ interface TowerFormStep extends Omit { sectionForm: FormGroup | null; } /** * Workflow function-type enum, upstreamed to `ngx-t-forms-types` as * {@link WorkflowFunctionTypes} per DECISIONS.md D-016. Re-exported here under * its historical name `FunctionTypes` to preserve every existing local import * path (including the immutable `form-tower-controller.service.ts` per D-007). * * @public */ declare const FunctionTypes: typeof WorkflowFunctionTypes; type FunctionTypes = WorkflowFunctionTypes; declare enum HintType { WARNING = "warning", ERROR = "error", SUCCESS = "success", LOADING = "loading", INFO = "info", QUESTION = "question", CRITICAL = "critical", PENDING = "pending", BLOCKED = "blocked", COMPLETED = "completed", CANCELLED = "cancelled", DEFAULT = "default", QUERY_SUCCESS = "query_success", DB_LOAD = "db_load", CALCULATED = "calculated", TAG = "tag", DOCUMENT = "document", HISTORY = "history" } /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare function textIconsForUserHints(hint: string, type: HintType): string; /** * @file Pure helpers for the **overridable validation** submission gate. * * A custom validator declared with `canOverride === true` does not block * submission outright. The gate distinguishes two error classes across the * settled form: * * - **Blocking** — every error that is NOT an overridable custom-validator * error: standard validators (`required`, `email`, `minlength`, …), MSCOA * business-rule errors, and custom validators with `canOverride === false`. * These MUST be resolved before the form can be submitted. * - **Overridable** — a custom-validator error whose value object carries * `canOverride === true`. The form may be submitted with these still failing * **provided each one is motivated** with a non-empty comment. * * The error-record shape these helpers read is produced verbatim by the * {@link FunctionTypes.ManualValueValidation} builder * (`build-manual-validation.ts`): `{ [validatorId]: { message, canOverride } }`, * merged onto `control.errors` by the W11 projection adapter. A validator id is * a uuid unique per `(input, validator)`, so it uniquely keys an override * reason. * * All functions here are pure reads over the projected `FormGroup` tree and the * tower's `allFormInputs()` index — no signal writes, no Angular runtime * primitives created. They are safe to call from template getters during change * detection (mirrors `getSubmissionStatus.ts`). */ /** * A still-failing overridable custom validation surfaced to the override UI. */ interface OverridableError { /** The custom validator's id — the `control.errors` key and the reason key. */ validatorId: string; /** The owning input's id (its control name within the section group). */ inputId: string; /** The owning input's `formControlName`, used to build the submission entry. */ formControlName: string; /** The input's display label, for the override-reason prompt. */ label: string; /** The validator's human-readable message. */ message: string; } /** * A single captured override motivation, keyed in the engine by `validatorId`. */ interface OverrideReason { /** User-supplied motivation. Must be non-empty (trimmed) to unlock submission. */ comment: string; /** Optional supporting file, carried as base64 {@link FileData}. */ attachment?: FileData; } /** Map of `validatorId → captured reason`. The engine's override-reason state. */ type OverrideReasons = Record; /** * Core orchestrator for a runtime form (the "tower") — abstract because every * concrete consumer (`TFormEngine`, the import row instance) extends it and * Angular's DI provides {@link HttpClient} + {@link NGX_T_FORMS_CONFIG_TOKEN}. * * ### Lifecycle (Phase 1) * 1. **{@link FormTowerControllerService.initialize}** — prepPopulate + * loadSystemInputs + formGenerator + setFirstStepAsActive, then builds the * per-input `#derived` signal graph (in injection context) and wires the * FormGroup-projection + persist effects. There is NO `valueChanges` * subscription and NO dependency-graph build. * 2. **{@link FormTowerControllerService.initializeFormValues}** — writes * user/import values into `#model` (tower-owned derived inputs are skipped * structurally) and forces a liveness lead-read so lazy resources start. * 3. User interaction / `updateValue` → writes `#model` (or a sourced * `linkedSignal.set`) → derived signals + resources recompute automatically. * 4. **{@link FormTowerControllerService.waitUntilSettled}** — used by headless * consumers (import) to wait for all in-flight async work to drain. * 5. **{@link FormTowerControllerService.ngOnDestroy}** — explicitly destroys * every resource, the projection/persist effects, the `isBusy$` * subscription, and clears hint/error timers. * * ### Why an abstract `@Directive()`? * Angular requires the `@Directive()` decorator on classes that participate in * DI inheritance + `OnDestroy`. Subclasses can be plain `@Injectable()` (see * {@link TFormEngine}). */ declare abstract class FormTowerControllerService implements OnDestroy { #private; private _http; /** Resolved tower config (HTTP functions, file upload, getFinacialCycles, …). */ readonly NGX_T_FORMS_CONFIG: ngx_t_forms_types.NgxTFormsConfig; /** Convenience pass-through to the financial-cycles fetcher from config. */ readonly getFinacialCycles: () => Observable; constructor(_http: HttpClient); /** Emits exactly once on tower destruction. `takeUntil` anchor for long-lived subscriptions. */ protected _destroyed$: Subject; /** Live root `FormGroup` — the Material carrier (Blueprint §2.7). Built by `formGenerator`. */ protected _mainForm: FormGroup | undefined; /** The values record last passed to {@link FormTowerControllerService.initializeFormValues}. */ protected _initialFormValue: Record; /** Last reported submission state — drives the toolbar/spinner UI. */ submittingStatus: FormSubmissionStatus | undefined; /** True while a form submission is in flight. Counts toward {@link FormTowerControllerService.isBusy}. */ get submittingForm(): boolean; set submittingForm(value: boolean); /** * The still-failing overridable (`canOverride === true`) validations on the * settled form, one per failing validator, each carrying the input's * `formControlName` / `label` and the validator message — the data the * override-reason UI renders a motivation prompt from. * * Reads the projected control tree, so it reflects the latest validity. When * there are blocking errors as well, those still prevent submission — see * {@link FormTowerControllerService.canSubmit}. */ overridableErrors: () => OverridableError[]; /** * `true` when at least one blocking (non-overridable) error remains on the * form. While this holds, submission is impossible regardless of motivations. */ hasBlockingErrors: () => boolean; /** * Whether the form may be submitted: no blocking errors AND every overridable * error has a non-empty motivation. Replaces the blunt `mainForm.invalid` * submit gate — an overridable error with a reason no longer blocks. */ canSubmit: () => boolean; /** The currently captured override motivations, keyed by validator id. */ get overrideReasons(): OverrideReasons; /** * Records (or updates) the motivation comment for one overridable validation. * * @param validatorId - The validator's id (the `control.errors` key). * @param comment - The user's motivation; an empty/whitespace value * re-locks submission for that error. */ setOverrideComment: (validatorId: string, comment: string) => void; /** * Attaches (or clears) a supporting file for one overridable validation. * * @param validatorId - The validator's id (the `control.errors` key). * @param attachment - The base64 {@link FileData}, or `undefined` to clear. */ setOverrideAttachment: (validatorId: string, attachment: FileData | undefined) => void; /** * Drops the captured reasons for the given validator ids. Called once a * MultipleInput sub-row is saved — its motivations move from the live * reason map into {@link FormTowerControllerService.#rowOverrides}, and the * transient controls (and thus the live overridable errors) are removed. * * @param validatorIds - The validator ids whose live reasons to drop. */ clearOverrideReasons: (validatorIds: readonly string[]) => void; /** * Records (or clears) the captured override entries for one MultipleInput * sub-row. Replacing on every save keeps a re-edited row's motivations * current; passing an empty list removes the row's entry. * * @param inputId - The MultipleInput's id. * @param rowId - The sub-row's id (`${inputId}.id` value). * @param entries - The override entries captured for the row. */ setRowOverrides: (inputId: string, rowId: string, entries: IFormValidationOverride[]) => void; /** * Builds the override entries for the currently-open inner inputs of a * MultipleInput sub-row, pairing each overridable error with its captured * motivation. Returns the entries plus the validator ids consumed (so the * caller can move them out of the live reason map after the row is saved). * * @param inputId - The MultipleInput's id. * @param innerInputs - The sub-row's inner inputs (composite-id form inputs). * @returns The sub-row's override state: motivated `entries` ready to persist, * the `validatorIds` consumed, and the inner input ids that are blocking or * still unmotivated (for save-gate messaging). */ collectRowOverrideState: (inputId: string, innerInputs: FormColumnInputs[]) => { entries: IFormValidationOverride[]; validatorIds: string[]; blockingInputIds: string[]; unmotivatedInputIds: string[]; }; /** The current form definition (post-{@link prepPopulateForm}). */ get form(): FormInterface | undefined; set form(value: FormInterface | undefined); /** System inputs loaded by `loadSystemInputs`, prepended to {@link FormTowerControllerService.allFormInputs}. */ get systemInputs(): FormColumnInputs[]; set systemInputs(value: FormColumnInputs[]); /** Live root `FormGroup` — `undefined` before {@link FormTowerControllerService.initialize}. */ get mainForm(): FormGroup | undefined; /** Append-only change history; used by {@link FormTowerControllerService.revertBackHistory}. */ get changeHistory(): IFormChangeHistory[]; set changeHistory(value: IFormChangeHistory[]); /** * Whether a form input is currently revealed by its conditional-display rules. * * Reads the input's live visibility signal, so calling this from a template or * a `computed` makes that consumer re-evaluate whenever the rule's observed * inputs change. An input that declares no `conditionalInputConfig` is always * visible and costs nothing to ask about. * * @param inputId - The input id. For MultipleInput sub-row fields and mSCOA * inner inputs this is the composite `${columnId}.${childId}`. * @returns `true` when the input is revealed, `false` when it is hidden. * * @example * ```ts * const visibleColumns = computed(() => * step.columns.filter((col) => tower.isInputVisible(col.id)), * ); * ``` */ isInputVisible: (inputId: string) => boolean; /** * Whether a section still has anything to show. * * A section whose every column is hidden by a conditional rule is not an empty * step to page through — it is a section that does not apply. It is reported * hidden so the stepper can drop it entirely. * * A section that was authored with **no columns at all** stays visible: that is * an empty section the author made, not one this feature emptied, and hiding it * would change the behaviour of forms that declare no rules. * * Reads the visibility signals of the section's columns, so a consumer calling * this from a template or `computed` re-evaluates when a rule flips. * * @param sectionId - The slide's `sectionId`. * @returns `true` when the section should be rendered. * * @example * ```ts * const steps = computed(() => * tower.selectFormSteps().filter((s) => tower.isSectionVisible(s.sectionId)), * ); * ``` */ isSectionVisible: (sectionId: string) => boolean; /** * Busy iff a submission is in flight OR any value/options resource is * `'loading'`. Reading every resource's `status()` here is what ACTIVATES the * lazy resources — workers MUST NOT short-circuit before iterating * (resource-activation contract, Blueprint §2.4 / HAZARD 2). */ protected readonly isBusyComputed: Signal; /** Strict complement of {@link FormTowerControllerService.isBusyComputed}. */ protected readonly settled: Signal; /** Synchronous snapshot of the busy state. */ get isBusy(): boolean; /** * Reactive stream of the tower's busy state. Emits `true` while any * calculation/API/validation is in flight or the form is submitting, `false` * once everything settles. Backed by `toObservable(isBusy)`. */ get isBusy$(): Observable; /** * Resolves once the tower has finished all in-flight async work (calculations, * API fetches, sourced values). Used by headless consumers (import). * * **Contract (Blueprint §5 / §6, never changes):** * - Performs a one-tick liveness lead-read FIRST (reads `value()` + every * resource `status()` + flushes effects) so lazy resources start even when * no template reads them (HAZARD 2). * - Waits for the first `settled() === true` emission. * - On `timeoutMs` it resolves with `undefined` — it NEVER rejects and NEVER * hangs (`timeout` + `defaultIfEmpty`-equivalent via `catchError`). * * @param debounceMs - Accepted for signature parity; the signal graph needs no * debounce, so it is treated as a no-op lead time. * @param timeoutMs - Hard timeout before resolving with `undefined` (default 30s). * @returns A promise that always resolves (with `void`). */ waitUntilSettled(debounceMs?: number, timeoutMs?: number): Promise; /** * Tears the tower down. Fires `_destroyed$`, runs * {@link FormTowerControllerService.clearFormState} (which destroys resources * + effects), completes the `isBusy$` subscription, and clears pending * hint/error timers (HAZARD 7 / Blueprint §2.8). */ ngOnDestroy(): void; /** * Boots the tower against a form definition. * * Performs, in order: * 1. {@link FormTowerControllerService.clearFormState} — wipe prior state + * destroy any prior resources/effects. * 2. {@link prepPopulateForm} — merge `_initialFormValue` into the columns. * 3. {@link loadSystemInputs} — load financial cycle, current user, etc. * 4. {@link formGenerator} — build the nested `FormGroup` carrier. * 5. Re-open any `MultipleInput` that was open before re-init. * 6. Build the `#derived` signal graph + wire the projection/persist effects * — ALL inside `runInInjectionContext(this.#injector, …)` (HAZARD 1). * * @param form - The form definition to drive. * @param options - Optional behaviour flags. * @param options.skipInitialFunctions - **Documented no-op (parity).** The * old engine used this to suppress a premature change-monitor cycle in the * headless import path. The signal graph has no such race (resources fire * lazily once read; the liveness lead-read in * {@link FormTowerControllerService.initializeFormValues} + * {@link FormTowerControllerService.waitUntilSettled} activates them), so * the flag is accepted for signature parity and otherwise ignored. */ initialize: (form: FormInterface, options?: { skipInitialFunctions?: boolean; }) => Promise; /** * Marks the form's first slide as active. Called during * {@link FormTowerControllerService.initialize}. */ setFirstStepAsActive: () => void; /** * Returns the form's settled values keyed by **formControlName** (the * canonical external shape). Reads {@link FormTowerControllerService.value}. */ getFormValueNames: () => Record; /** * All form inputs that participate in the form, including system inputs. * Returns a fresh outer array each call (preserves the prior contract that * callers may mutate the returned array), but the expensive flatten beneath it * is memoized by {@link FormTowerControllerService.#allInputsMemo}. */ readonly allFormInputs: () => FormColumnInputs[]; /** @deprecated unused field, kept for backwards compatibility. */ prevValue: any; /** * Mark a logical operation in flight for DOM badge rendering. Phase 1: no * longer affects {@link FormTowerControllerService.isBusy} (busy is * resource-derived) — forwards to the DOM-status helper only. */ setInputStatus: (inputId: string, statusId: string, status: string) => void; /** Companion to {@link FormTowerControllerService.setInputStatus} — clears the DOM badge. */ removeInputStatus: (inputId: string, statusId: string) => void; /** * Programmatic write to a form input. * * - **Sourced inputs** (InputSourcedValue) → routes to the entry's writable * `linkedSignal.set(value)` (transient override; reset on next source change). * EXCEPT a merge-mode input ({@link mergesDerivedValue}): that entry is the * TOWER's channel, and the fold reads it to know what the tower says. Writing * the user's completion into it would make the merge diff the user's own * value against itself and lose the completion on the next source change — * so those go to `#model`, where the fold expects to find them. * - **All other inputs** → writes `#model[inputId]`. * * A **MultipleInput** row array is passed through * {@link FormTowerControllerService.#withRowIds} first, so no row can enter the * tower without an id (see {@link ensureMultipleInputRowIds}). This is the * choke point for every non-derived ingress: prefill/import * ({@link FormTowerControllerService.initializeFormValues}), accepted * suggestions, row deletes from the table view, and direct consumer writes. * * Appends the change to history IMPERATIVELY (HAZARD 4). The projection effect * patches the FormControl from the signal graph — this does NOT touch the * FormGroup directly (no `valueChanges` feedback loop). * * @param inputId - The input to write. * @param value - The new value. */ updateValue: (inputId: string, value: any) => void; /** * Apply an external values record onto the form (user prefill / import row). * * Per input: * - **Tower-owned non-MultipleInput** (calc/API/sourced) → SKIP structurally * (it never enters `#model`; the derived signal/resource owns the value). * - **MultipleInput** → ALWAYS apply the row array (its rows are user-authored * source data). The array-remap branch is UNCHANGED (Phase 2 fence). * - **Everything else** → write `#model` from the dotted-path or top-level value. * * Ends with a liveness lead-read so lazy resources start even in the headless * import path (no template reader) — HAZARD 2 / Blueprint §2.7. * * @param initialValues - The external values record (formControlName-keyed). */ initializeFormValues: (initialValues: Record) => void; /** * Snapshot of the inputId-keyed flat form value. Re-points to the merged * {@link FormTowerControllerService.value} computed (was the old `_formValue`). * Passed to legacy helpers (refresh) that expect a `() => Record` accessor. */ getFormValue: () => Record; /** * Resets all internal state — definition, FormGroup, signal graph (resources + * effects destroyed), busy/model/history — so the tower can be re-initialized. * Called at the start of {@link FormTowerControllerService.initialize} and from * {@link FormTowerControllerService.ngOnDestroy}. * * Explicitly `.destroy()`s every `ResourceRef` and the projection/persist * effects (HAZARD 7 / Blueprint §2.8 — per-row import towers are torn down by * manual `ngOnDestroy`, not injector destruction). */ clearFormState: () => void; _hintTimeoutIds: Record; setTemporaryHint: (inputId: string, hint: string, type: HintType) => void; _onFormInputConfigChange?: (inputId: string, config: FormColumnInputs) => void; registerFormInputConfigChangeFn: (fn: (inputId: string, config: FormColumnInputs) => void) => (inputId: string, config: FormColumnInputs) => void; httpPostDataFunction: (url: string, data: any, options?: any) => Observable; httpGetDataFunction: (url: string, options?: any) => Observable; httpPutDataFunction: (url: string, data: any, options?: any) => Observable; _errorTimeoutIds: Record; setInputTemporaryError: (inputId: string, message: string) => void; setInputError: (err: Record, inputId: string) => void; updateFormInputConfig: (inputId: string, config: FormColumnInputs) => void; handleStepChange: (event: StepperSelectionEvent) => void; selectFormSteps: () => TowerFormStep[]; selectAllFormTours: () => ngx_t_forms_types.IFormTour[]; setSectionAsSeen: (sectionId: string) => void; setSectionAsActive: (sectionId: string) => void; setInputAsTouchedAndDirty: (inputId: string) => void; formProgress: () => number; formSubmissionMessage: () => string; setSubmissionStatusValue: (status: FormSubmissionStatus) => FormSubmissionStatus; /** * Legacy-shaped per-input function registry, reconstructed from `#derived` * (Decision D3). Each present `(inputId, functionType)` maps to an * `Observable` that, on subscription, drives the underlying primitive: * - resource/options entries → `.reload()` then complete when status leaves * `'loading'`; * - value/sourced/validation entries → a synchronous read then complete. * * Kept so `refreshInputWithDependencies` / `refreshInput` / * `runMultipleInputPrepopulationFunctions` / `getTopologicalExecutionOrder` * compile + behave unchanged. The return TYPE is unchanged (api:check parity). */ get inputFunctionsCollection(): Record>>>; /** * Submits the form to the configured backend pipeline. Builds a payload by * un-flattening the settled values, delegates to {@link formHttpSubmissions}, * and reports status. Errors surface as `FormSubmissionStatus.FAILED` — they * do not throw to the caller. * * @param passData - Extra context handed through to the submission pipeline. */ submitForm: (passData: Record) => void; /** Toggle a MultipleInput element + its associated form controls. */ toggleMultipleInput: (inputId: string, open: boolean, rowInEditId?: string) => boolean; /** Persist the open MultipleInput sub-form back into the parent control's array value. */ saveMultipleInputForm: (inputId: string) => boolean; /** Open the MultipleInput sub-form prepopulated with `rowId`'s values. */ multipleInputEditRow: (inputId: string, rowId: string) => boolean; /** Duplicate `rowId` and open the sub-form on the new row. */ multipleInputDuplicateRow: (inputId: string, rowId: string) => boolean; /** * Bridges live edits of an OPEN MultipleInput sub-row into the signal graph. * * Subscribes every sub-row control (`${inputId}.${childId}`) — except the * `${inputId}.id` row-bookkeeping control — to `updateValue(controlName, …)`, * so a sub-row field behaves exactly like a primary-form input: dependent * calculations / value-fetches / validators (whether they live in the same * row or in the primary form) re-fire the moment a dependency changes. * * Idempotent per `inputId` (re-binding tears down the prior subscription * first). Each inner subscription is `takeUntil(this._destroyed$)`-anchored so * a re-init also drops it. Called by `toggleMultipleInput` on open; mirrored * by {@link FormTowerControllerService.unbindMultipleInputRowControls} on close. * * @param inputId - The MultipleInput whose open sub-row controls to bridge. */ bindMultipleInputRowControls: (inputId: string) => void; /** * Tears down the live sub-row `valueChanges` bridge wired by * {@link FormTowerControllerService.bindMultipleInputRowControls}. Called by * `toggleMultipleInput` on close (and implicitly on re-open / re-init). * * @param inputId - The MultipleInput whose sub-row bridge to drop. */ unbindMultipleInputRowControls: (inputId: string) => void; /** * Bridges live edits of mSCOA inner inputs — the custom form inputs configured * inside an `MscoaSelection` column (e.g. `budgetValue`, `department`) and * rendered inline by the MSCOA chart via a `ComponentPortal` — into the signal * graph. * * Each inner input is a `${mscoaColId}.${innerId}` control sitting in the mSCOA * column's section group. The engine already lists it in `allFormInputs`, so * its derived calc / value-API / options graph is built at init (the OUTPUT * half). But a `ComponentPortal` forwards inputs only and never subscribes the * host's `valueChange` output, so the user's typed value never reached `#model` * — leaving dependent calculations / value-fetches / options blind to it. This * subscribes each such control's `valueChanges` → `updateValue(controlName, …)`, * restoring the INPUT half so an mSCOA custom input behaves exactly like a * primary-form input. Direct mirror of * {@link FormTowerControllerService.bindMultipleInputRowControls}. * * Idempotent (tears down the prior bridge first) and anchored to `_destroyed$`, * so the re-init `clearFormState` drops every inner subscription. */ bindScoaInnerInputControls: () => void; /** * Bridges live edits of **DateRangePicker half controls** into the signal * graph. * * A date-range column is flattened into two synthetic inputs whose ids (and * control names) are `${columnId}.${startKey}` / `${columnId}.${endKey}`. * The range element writes those controls directly (`control.setValue`), and * the generic value bridge in `t-form-input` deliberately no-ops for date * ranges — so before this bridge existed a user-picked range never reached * `#model`, and every dependent value/options POST keyed on the range stayed * permanently `idle` (owner decision 2026-08-18: fixed, with the builders' * name-keyed snapshot fix). Direct mirror of * {@link FormTowerControllerService.bindScoaInnerInputControls}. * * Idempotent (tears down the prior bridge first) and anchored to * `_destroyed$`, so re-init/destroy drops every half subscription. */ bindDateRangeHalfControls: () => void; /** * Re-runs every refreshable function for inputs in `sectionId` (resource * `.reload()` + dependency order). Returns an Observable that completes when * the section's refresh chain drains. * * @param sectionId - The section to refresh. * @returns An Observable completing when the section is refreshed. */ refreshSection: (sectionId: string) => Observable; /** * Fire-and-forget refresh of whichever slide is currently active. Returns * `void` (parity — the consumer-map row claiming `Observable` is wrong). */ refreshActiveSection: () => void; /** * Refresh a single input plus everything it transitively depends on. * * @param inputId - The input to refresh. * @returns An Observable completing when the refresh drains. */ refreshInput: (inputId: string) => Observable; /** True iff the input has at least one refreshable `#derived` entry. */ canRefreshFn: (inputId: string) => boolean; /** * True iff any input in the section is refreshable. The HTML refresh-button * gate (`user-form-stepper.component.html`) depends on this exact truth value * (Blueprint §4 / W13). */ canRefreshSection: (sectionId: string) => boolean; /** * Pop the last change-history entry and re-apply the PRIOR snapshot via * {@link FormTowerControllerService.updateValue} (restores to second-to-last, * matching the old semantics exactly). Decorates each restored input with a * transient hint. History append stays imperative (HAZARD 4). */ revertBackHistory: () => void; /** * The staged AI suggestions for this form (the preview/accept/reject queue). * A `pending` entry overlays {@link FormTowerControllerService.previewFormValue} * but never the live form until {@link FormTowerControllerService.acceptSuggestion}. */ readonly stagedSuggestions: Signal; /** * Real-time, read-only snapshot of the form's EDITABLE surface — each field's * current value, selectable options, and validation constraints + live errors * — plus the form's submittable / busy state. Excludes structural, system, * calculated, read-only / disabled, view-only and tower-derived inputs (a * suggestion to any of those is un-committable). * * Lazy and non-impacting: a pure `computed` that costs nothing until read * (e.g. once an AI subscriber observes it through {@link FORM_SUGGESTION_REGISTRY}). * It recomputes as values, resolved options and override-reasons change. */ readonly editableFormSchema: Signal; /** * The form value with every PENDING staged suggestion overlaid — the * non-destructive preview a review UI renders. A pure overlay over * {@link FormTowerControllerService.getFormValue}; it is NEVER projected to the * live `FormGroup` (only `acceptSuggestion` writes the live form), so previewing * a suggestion can never disturb form state. */ readonly previewFormValue: Signal>; /** * Stage a batch of externally-proposed edits for user review. Each suggestion * targeting a currently-editable field is recorded as `pending` with a snapshot * of the field's value at propose time; suggestions to unknown / non-editable * fields are dropped. Does NOT mutate the live form. * * @param batch - The proposed edits (typically routed via {@link FORM_SUGGESTION_REGISTRY}). */ proposeSuggestions: (batch: SuggestionBatch) => void; /** * Accept one pending staged suggestion, applying it through the path its field * **kind** requires: * - `scalar` → `updateValue`. * - `choice` → only if the value is an allowed option (else `'invalid'`). * - `multipleRow` → validate + remap rows, then append/replace (else `'invalid'`). * - `mscoa` → resolve the account intent asynchronously via the host account * search (status `'resolving'`), then commit or mark `'invalid'`. * * A value that does not satisfy its field's contract is parked as `'invalid'` * and never written to the live form (this is the apply-guard). No-op when the * id is unknown or already resolved. * * @param stageId - The staged suggestion's id. */ acceptSuggestion: (stageId: string) => void; /** * Reject one pending staged suggestion: mark it rejected and drop it from the * preview overlay. The live form is left untouched. No-op when the id is * unknown or already resolved. * * @param stageId - The staged suggestion's id. */ rejectSuggestion: (stageId: string) => void; /** * Accept every pending staged suggestion. Each is routed through * {@link FormTowerControllerService.acceptSuggestion}, so per-kind validation / * remap / async MSCOA resolution apply (an invalid one parks as `'invalid'` * rather than blocking the rest). */ acceptAllSuggestions: () => void; /** Reject every pending staged suggestion (leaves the live form untouched). */ rejectAllSuggestions: () => void; /** * Hard reset: tear down current state and re-initialize from the same form * definition + originally supplied values. * * **D4 (deliberate, signed-off):** AWAITS `initialize(form)` BEFORE calling * `initializeFormValues(...)`, fixing the old unawaited mis-ordering where * `initializeFormValues` could run before `_mainForm` was rebuilt. The public * signature is unchanged. */ reset(): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Per-component runtime form engine. * * `TFormEngine` is the **composition seam** introduced by SIGNAL_FORMS migration * Phase 0 (see `docs/migration/SIGNAL_FORMS_MIGRATION_PLAN.md` §5.1/§6.0 and * DECISIONS.md D-023/D-025). Consumers previously **extended** * {@link FormTowerControllerService} directly; they now **hold** a `TFormEngine` * instance via `inject(TFormEngine)` (provided per-component, not * `providedIn: 'root'`) and reach the tower's surface through it. * * Phase 0 moves **no logic**: this façade is a thin subclass of the existing * tower so the full member surface is preserved verbatim and consumer behaviour * stays under the I-1 Parity invariant. Phase 1 will rewrite the tower internals * behind this same façade without touching the three consumers again. * * Internal to the library — not part of the public API. Provide it on the * component that needs it: * * @example * @Component({ * // … * providers: [TFormEngine], * }) * export class UserFormStepperComponent { * protected readonly engine = inject(TFormEngine); * } */ declare class TFormEngine extends FormTowerControllerService { constructor(http: HttpClient); static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵprov: _angular_core.ɵɵInjectableDeclaration; } declare class TourManagerService { private readonly tourService; userProfile: this; constructor(); startTourByOption(steps: IStepOption[]): { stepShow$: rxjs.Subject>; stepHide$: rxjs.Subject>; initialize$: rxjs.Subject; start$: rxjs.Subject; end$: rxjs.Subject; pause$: rxjs.Subject; resume$: rxjs.Subject; }; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵprov: _angular_core.ɵɵInjectableDeclaration; } /** * End-user surface for filling out a form. Extends `FormTowerControllerService` * to drive a `MatStepper` of form sections and renders each section's inputs * via `TFormInputComponent`. Loads the form definition through the consumer's * `formBuilder.getForm` hook on `NgxTFormsConfig`, supports undo (Ctrl+Z), * initial values, and per-instance global system inputs. * * Inputs: * - `formId: string | undefined` — when set, fetches the form * definition and initialises the tower controller. * - `initialValues: Record | undefined` — seed the form * with previously captured values; only re-initialises when the value * actually changes. * - `globalSystemInputs: FormColumnInputs[]` — system-level inputs * merged into every step of the form; re-initialises the form when set. * - `passParamsOnSubmit: Record | undefined` — extra params * passed through to the consumer's submit handler. * - `showAiSuggestions: boolean` — when `true` (and `withFormSuggestions()` is * installed), renders the built-in review panel for pending AI-proposed edits * so the user can accept/reject them. Off by default. * * Outputs: * - _None._ Submission and lifecycle events are dispatched through the * inherited `FormTowerControllerService` and the consumer's * `formActions` / `formBuilder` callbacks. */ declare class UserFormStepperComponent { #private; /** * Runtime form engine, held by composition (SIGNAL_FORMS Phase 0, D-025). * Provided per-component instance; the component reaches the former * inherited tower surface through this façade. */ protected readonly engine: TFormEngine; /** Form definition id; when set, the component fetches and initialises the form. */ readonly formId: _angular_core.InputSignal; /** * Seed values applied to the form once it is initialised. * * A keyed record of previously captured values (raw API payloads, partial * form values, nested objects). The downstream `initializeFormValues` * consumes the entries defensively; callers narrow specific value shapes. */ readonly initialValues: _angular_core.InputSignal | undefined>; /** System-level inputs merged into every step of the form. */ readonly globalSystemInputs: _angular_core.InputSignal; /** * Extra params forwarded to the consumer's submit handler. * * A keyed record merged into the submission payload; only meaningful to the * consumer's own submit handler, which narrows the individual values. */ readonly passParamsOnSubmit: _angular_core.InputSignal | undefined>; /** * Shows the built-in AI edit-suggestion review panel above the form when an * external system has proposed pending edits (requires `withFormSuggestions()` * at bootstrap). Off by default — opt in to render the turnkey panel; leave it * off to drive the suggestion stream / your own review UI via * `FORM_SUGGESTION_REGISTRY` instead. */ readonly showAiSuggestions: _angular_core.InputSignal; /** Underlying Material stepper instance (may be undefined before first CD pass). */ readonly stepper: _angular_core.Signal; /** True when the engine holds at least one pending (unreviewed) AI suggestion. */ protected readonly hasPendingSuggestions: _angular_core.Signal; /** * The form's steps with every conditionally-hidden input removed — what this * stepper renders. * * The filter lives HERE rather than in `selectFormSteps` because that selector * is shared with the form builder's own section stepper. Filtering centrally * would make a conditional field invisible, undraggable and un-editable to the * author the moment its condition happened to be false. The builder must see * every column; only the person filling the form in should not. * * A hidden MultipleInput sub-row field is dropped by replacing the column's * `formInputs` with a filtered copy. That array flows down through * `[inputConfig]` → `t-form-input` → `*ngComponentOutlet` → * `multiple-input-table-edit.innerInputs()`, so the sub-row editor, the * saved-rows table and `formInvalid()` all drop the field with no edits of * their own. * * Reading `engine.isInputVisible` makes this computed a consumer of the * visibility signals, so a rule flipping re-renders the affected columns and * nothing else. */ protected readonly visibleSteps: _angular_core.Signal; /** Whether the form is editable / can be navigated freely. */ isEditable: boolean; /** True while the form definition is being fetched. */ readonly loading: _angular_core.WritableSignal; protected readonly tourManagerService: TourManagerService; protected readonly formIdBridge: _angular_core.EffectRef; protected readonly initialValuesBridge: _angular_core.EffectRef; protected readonly globalSystemInputsBridge: _angular_core.EffectRef; /** * Handles the key down event and reverts the form's last history entry on Ctrl+Z. * @param event The keyboard event object. */ onKeyDown(event: KeyboardEvent): void; loadForm(formId: string | undefined): void; /** * Builder-function shim passed to inputs that need callbacks (e.g. * MultipleInput, MSCOA). The user form surface only implements the subset * relevant to runtime input editing; builder-only members (`editInput`, * `deleteInput`, `addFunction`, `reorderItems`, `multipleInputToggleLabel`) * are stubbed because they have no meaning outside the form builder. */ get formBuilderFunctions(): FormBuilderFunctions; getStepState(step: ITowerFormSteps, index: number): StepState; getPreviousStepLabel(): string | undefined; getNextStepLabel(): string | undefined; canProceedToNextStep(step: ITowerFormSteps): boolean; get getSubmissionStatus(): string; handleSubmit(): void; /** Still-failing overridable validations the user may motivate to submit. */ get overridableErrors(): OverridableError[]; /** True while a blocking (non-overridable) error keeps submission impossible. */ get hasBlockingErrors(): boolean; /** True once every blocking error is resolved and every override is motivated. */ get canSubmit(): boolean; /** * Overridable validations that still lack a non-empty motivation — the count * shown on the "Provide reason" button and what keeps submission gated. */ get pendingOverrideCount(): number; /** * Opens the override-reasons dialog so the user can add or edit a motivation * (and optional supporting file) for each overridable validation. The dialog * writes back through the engine, so the submit gate updates live on close. */ openOverrideDialog(): void; get hasTour(): boolean; /** * Initiates and configures a dynamic, multi-step tutorial tour for the form. * The tour guides the user through the form's title, sections, and individual fields. */ startTour(): void; refreshASection(sectionId: string): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** * An insertable binding surfaced by the `$`-trigger menu. The editor stays * agnostic of any token syntax: it inserts `insert` verbatim and filters by * `searchText` (falling back to `label`). */ interface EditorBinding { /** Human label shown (prominently) in the menu — never the raw id. */ readonly label: string; /** The exact text inserted at the caret when chosen (e.g. `"{{contractType}}"`). */ readonly insert: string; /** Text matched against the user's `$`-filter; defaults to {@link label}. */ readonly searchText?: string; /** Optional Material icon name shown beside the label. */ readonly icon?: string; /** Optional muted secondary line (e.g. field type) that disambiguates same-named fields. */ readonly detail?: string; /** * Other token contents that should also be recognised as *this* field (e.g. the * raw id when {@link insert} carries a friendly label). A `{{token}}` whose * trimmed content matches {@link insert}'s content or any alias renders as a * valid (clickable) pill; anything else is flagged as unrecognised. */ readonly aliases?: readonly string[]; } interface IConfigElementError { key: string; message: string; } /** * Builder-side editor for a single configurable property on a form input — * dispatches to the correct editor (selection options, REST API setup, * value-access rules, pipeline, validators, MSCOA segment config, slider, * chip list, data source picker, document list label config, workflow * picker, EditorJS, record list manager, etc.) based on the supplied * `ElementEditorInnerSectionElementInterface`. * * @remarks * Emits debounced value changes (and a separate `blur` event when the * editor is a free-text expression) so the form builder can persist edits * without re-reading the underlying form on every keystroke. * * Inputs: * - `editorConfig: ElementEditorInnerSectionElementInterface | undefined` * — descriptor of the editor to render. * - `formInputs: FormColumnInputs[]` — the set of inputs the * editor can reference (used by expression / value-access editors). * - `data: unknown` — current value being edited; resets the value * pipeline when changed by reference. * - `validationErrors: IConfigElementError[] | null` — validation errors * surfaced by the parent for display. * * Outputs: * - `valueChange` — fires when the edited value changes (debounced). * - `blur` — fires when an inner editor loses focus * (used for expression-style editors). */ declare class TDynamicDataEditComponent { #private; protected readonly elementEditorTypes: typeof ElementEditorTypes; /** Descriptor of the editor to render. */ readonly editorConfig: _angular_core.InputSignal; /** The set of inputs the editor can reference (used by expression / value-access editors). */ readonly formInputs: _angular_core.InputSignal; /** * Bindings for the `$`-trigger picker on * {@link ElementEditorTypes.TokenTextInput} and * {@link ElementEditorTypes.RichTextEditor}. * * TWO SOURCES, config first. The workflow step editor passes * `formInputs: []` and resolves every picker's option pool onto the element * config instead (see `stepEditorSections$` in `ngx-t-workflows`) — for those * hosts `editorConfig().bindings` is the ONLY channel, and deriving from the * empty `formInputs` here is exactly the bug that made the `$` menu open with * nothing. The form builder does pass real `formInputs`, so when no config * bindings are present they are derived the same way the payload and header * template editors derive theirs (`_shared/field-bindings.ts`), keeping the * label + inserted spelling identical everywhere. */ protected readonly tokenBindings: _angular_core.Signal; /** * Column config for the rich-text editor. * * `QuillInputComponent.inputConfig` is `input.required` and * it reads `placeholder`, `readonly` and `disabled` off it to build its * options. The previous binding passed `$any(data())` — the object being * edited — so the editor got a shape with none of those fields and came up * empty. Synthesising a real column here keeps the cast out of the template * and gives Quill the three values it actually uses. */ protected readonly richTextInputConfig: _angular_core.Signal; /** * Current value being edited; resets the value pipeline when changed by reference. * @remarks Heterogeneous across consumers (ITowerStepColumn, plain records, etc.), * so it is typed as a keyed record; the editor pipeline reads individual fields * via `deepBind` paths and callers narrow specific value shapes. */ readonly data: _angular_core.InputSignal; /** Validation errors surfaced by the parent for display. */ readonly validationErrors: _angular_core.InputSignal; /** * Builder-only: creates or updates an account-level MSCOA custom input (a peer * of the segments) from the segment-config quick editor. Forwarded to that * editor; absent outside the form builder, which hides the related affordance. */ readonly mscoaSaveCustomInput: _angular_core.InputSignal<((sectionId: string, scoaInputId: string, config: Pick & { id?: string; }) => void) | undefined>; /** Builder-only: opens an MSCOA custom input in the input editor for advanced configuration. */ readonly mscoaEditInput: _angular_core.InputSignal<((input: FormColumnInputs) => void) | undefined>; /** Builder-only: removes an MSCOA custom input from the config. */ readonly mscoaDeleteInput: _angular_core.InputSignal<((input: FormColumnInputs) => void) | undefined>; /** Fires when the edited value changes (debounced). */ readonly valueChange: _angular_core.OutputEmitterRef; /** Fires when an inner editor loses focus (used for expression-style editors). */ readonly blur: _angular_core.OutputEmitterRef; protected readonly editorConfigBridge: _angular_core.EffectRef; protected readonly dataBridge: _angular_core.EffectRef; protected readonly editorConfigValue$: Observable; protected readonly dataValue$: Observable | undefined>; protected get getMscoaTree$(): Observable; protected get getWorkflowOptions$(): Observable; protected get value$(): Observable; protected get dataOptions$(): Observable; protected get disabled$(): Observable; inputChange(event: unknown): void; valueChanged(value: unknown): void; manualValueChange(event: { value: unknown; deepBind: Array; }): void; blurOff: boolean; elementBlur(event: unknown): void; protected get inputHasBlurFunction$(): Observable; blurFunctionTooltip(blurHandle: BlurHandleTypes | undefined): string; /** Slider label formatter — Mat slider's `displayWith` accepts `string | number`. */ formatLabel: (value: number) => string | number; protected vm$: Observable<{ editorConfigValue: ElementEditorInnerSectionElementInterface | undefined; isFormFieldControl: boolean; value: any; dataOptions: any; disabled: boolean | undefined; inputHasBlurFunction: BlurHandleTypes | undefined; dataValue: Record | undefined; inputConfig: Partial; }>; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } declare class TreeComponent { activePath: TreeNode[]; pathInEdit: TreeNode | null; get activeLevel(): number; private _transformer; setupPath(node: TreeNode): void; updateStateFromPath(path: TreeNode[]): void; selectNode(node: FlatNode): TreeNode[] | undefined; saveNodeInEdit(): { children: undefined; value: undefined; key: string; name: string; inEdit?: boolean; path: string[]; keyIsArrayIndex?: boolean; functions: IArrayFunction[]; }[] | undefined; resetNode(): void; removeFunction(id: string): void; closeEdit(): void; nodeIsExpandable(node: TreeNode): boolean; showNode(node: FlatNode): boolean; hasANodeInEdit(): boolean; nodeIsChecked(node: FlatNode): boolean; treeControl: FlatTreeControl; treeFlattener: MatTreeFlattener; dataSource: MatTreeFlatDataSource; private dynamicObject; constructor(); computeTreeData(dynamicObject: unknown): void; assignDataSourceData(tree: TreeNode[]): void; hasChild: (_: number, node: FlatNode) => boolean; getValue(): unknown; addNewFunction(): void; functionChanged(value: string): void; } /** * Read-only tree view for an arbitrary JSON-shaped value. Converts the input * into a flat tree (via `TreeComponent`) and renders each leaf with a typed * style class (`value-string`, `value-number`, `value-boolean`, `value-null`). */ declare class TDynamicDataViewComponent { /** Arbitrary JSON-shaped value to render. Recomputes the tree on structural change. */ readonly data: _angular_core.InputSignal; protected readonly treeClass: TreeComponent; constructor(); protected formatKey(key: string): string; protected getDisplayValue(value: unknown): string; protected getValueClass(value: unknown): string; protected getLevelArray(node: unknown): readonly number[]; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** * Renders the queue of pending AI-proposed edits for a form and lets the user * accept or reject each one (or all at once). It is **presentational only** — it * holds no engine reference and mutates nothing; the host wires its inputs to the * tower's reporting signals and its outputs back to the tower's accept/reject * methods, so a per-component form engine is never pulled into this component's * injector. * * @example * */ declare class TSuggestionReviewComponent { /** Staged suggestions to review (typically `engine.stagedSuggestions()`). */ readonly suggestions: _angular_core.InputSignal; /** The form's editable schema (typically `engine.editableFormSchema()`), used for field labels. */ readonly schema: _angular_core.InputSignal; /** The form value with pending suggestions overlaid (typically `engine.previewFormValue()`). */ readonly previewValue: _angular_core.InputSignal>; /** Emits the stage id of a suggestion the user accepted. */ readonly accept: _angular_core.OutputEmitterRef; /** Emits the stage id of a suggestion the user rejected. */ readonly reject: _angular_core.OutputEmitterRef; /** Emits when the user accepts every pending suggestion. */ readonly acceptAll: _angular_core.OutputEmitterRef; /** Emits when the user rejects every pending suggestion. */ readonly rejectAll: _angular_core.OutputEmitterRef; /** The pending suggestions — the only ones offered for review. */ protected readonly pending: _angular_core.Signal; /** Suggestions that could not be applied (shown informationally with a reason). */ protected readonly invalid: _angular_core.Signal; /** Whether there is nothing to show (no pending and no invalid). */ protected readonly isEmpty: _angular_core.Signal; /** `fieldId → schema` index for label / kind lookup. */ protected readonly schemaById: _angular_core.Signal>; /** * Resolves a field's display label, falling back to its id. * * @param fieldId - The target field's id. * @returns The field's label, or the id when the field is unknown. */ protected labelFor(fieldId: string): string; /** The kind of the suggestion's target field, when known. */ protected kindOf(fieldId: string): EditableFieldKind | undefined; /** Structured fields (mscoa / multipleRow) show an action summary, not a raw diff. */ protected isStructured(s: StagedSuggestion): boolean; /** A short human description of what a structured suggestion will do. */ protected describeAction(s: StagedSuggestion): string; /** Renders an arbitrary value as a short string for the from → to diff. */ protected display(value: unknown): string; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** One repair-log entry, reduced to the lines the panel draws. */ interface RepairTurnView { readonly sequence: number; readonly kind: DraftRepairTurnKind; readonly kindLabel: string; /** Where the work went, as one sentence. */ readonly route: string; /** The targets the turn addressed. */ readonly findings: readonly string[]; /** What the next verification found, or `undefined` while it has not run. */ readonly outcome?: string; } /** One planned field stub, reduced to what the panel draws. */ interface PlannedFieldView { /** Visible label, or a positional stand-in while none has streamed in. */ readonly label: string; /** Element type, or `''` while none has streamed in. */ readonly element: string; } /** One planned slide, reduced to what the panel draws. */ interface PlannedSlideView { /** Slide heading, or a positional stand-in while none has streamed in. */ readonly label: string; /** Field stubs planned for the slide. */ readonly fields: readonly PlannedFieldView[]; /** Whether this slide's columns are the ones streaming right now. */ readonly generating: boolean; } /** * Renders one AI form-builder drafting session — the intake conversation, the * Analyst's brief, the Architect's blueprint as it streams in, rule-coverage * traceability, verification findings, the loop counters and what the run cost * — and lets the user describe the form, run generation, stage the result on * the canvas, or discard the session. It is **presentational only** — it holds * no orchestrator reference and mutates nothing beyond its own composer text; * the host wires its inputs to the orchestrator's session signal and its * outputs back to the orchestrator's `describe()` / `generate()` / `reset()` * methods, so a per-component pipeline is never pulled into this component's * injector. * * Every region below is a rendering of data already on {@link DraftSession}: * nothing here derives state the orchestrator does not publish, and a member * the session omits renders as an absent region rather than as a zero. * * The panel's four outputs are named for the user's intent (describe, build, * stage, discard); the port they map onto is `describe()`, `generate()`, * the host's own staging call, and `reset()`. * * `busy` is derived by the host, not read off the session: `stage` stays * `'intake'` after the Analyst answers, so it cannot drive a spinner on its own. * * @example * readonly #orchestrator = inject(FORM_DRAFT_ORCHESTRATOR); * readonly #canvas = viewChild.required(FormBuilderComponent); * protected readonly session = this.#orchestrator.session; * protected readonly building = signal(false); * protected readonly busy = computed(() => { * const s = this.session(); * return this.building() || * (s.stage === 'intake' && s.conversation.at(-1)?.author === 'user'); * }); * * protected build(): void { * this.building.set(true); * this.#orchestrator.generate() * .pipe(takeUntilDestroyed(this.#destroyRef)) * .subscribe(() => this.building.set(false)); * } * * */ declare class TFormDraftPanelComponent { #private; /** The drafting session to render (typically `orchestrator.session()`). */ readonly session: _angular_core.InputSignal; /** Whether a pipeline turn is in flight; disables the composer and Build. */ readonly busy: _angular_core.InputSignal; /** Emits the natural-language message the user submitted from the composer. */ readonly describe: _angular_core.OutputEmitterRef; /** Emitted when the user asks the pipeline to run generation. */ readonly build: _angular_core.OutputEmitterRef; /** Emits the staged {@link FormInterface} when the user accepts it for the canvas. */ readonly stage: _angular_core.OutputEmitterRef; /** Emitted when the user discards the current session. */ readonly discard: _angular_core.OutputEmitterRef; /** The session's current lifecycle stage; also reflected on the host as `data-stage`. */ protected readonly stageName: _angular_core.Signal; /** Intake conversation so far. */ protected readonly conversation: _angular_core.Signal; /** The Analyst's latest brief, once produced. */ protected readonly brief: _angular_core.Signal; /** The Architect's latest blueprint, once produced. */ protected readonly blueprint: _angular_core.Signal; /** * The partial output of the turn in flight, when one is streaming. * * The panel's only progress signal, and read for nothing else: a partial is * never staged, never verified and never counted. */ protected readonly partial: _angular_core.Signal; /** All outstanding findings for the session. */ protected readonly findings: _angular_core.Signal; /** * Whether the ledger below is the verifier's verdict rather than the panel's. * * Rendered as provenance, because the two are not equally authoritative: the * verifier reads the assembled form, while the fallback reads the plan that * was *meant* to produce it and can therefore call a rule covered on the * strength of a tag whose field never survived generation. * * Two independent pieces of evidence, because neither is sufficient alone. * * A reported coverage gap is DIRECT evidence: only the verifier emits * `kind: 'coverage'`. But keying off gaps alone said "verification has not * reported on this draft yet" on exactly the runs where every rule was * covered — telling the user the opposite of the truth at the one moment the * draft was clean. So a post-verification stage counts too, which is what * covers the clean run that has nothing to report. */ protected readonly coverageFromVerifier: _angular_core.Signal; /** * Traceability ledger: each brief rule paired with whether the draft covers * it, and with what the verifier's finding adds when it reported the gap. * * Prefers the verifier's `coverage` findings whenever the session carries * any, and falls back to matching brief rules against the blueprint's * `ruleTags` when it carries none — which is every moment before verification * has run, when the plan is the only evidence there is. * * `detail` is the finding's `suggestedFix` where it has one, and only falls * back to its `message`. The row already prints the rule id, the statement * and the verdict, so the message's own sentence — "Rule R9 is not covered … * Rule as stated: …" — is that row read back to the user. What the finding * knows and the ledger does not is what to *do* about the gap, so that is * what the row carries. See {@link listedFindings} for why the finding itself * is then not rendered a second time. * * An uncovered rule is advisory, never an error: it says the draft did not * trace to something the brief asked for, which is a thing for a human to * look at, not a thing that failed. */ protected readonly coverage: _angular_core.Signal; /** * The findings the general list renders. * * **The coverage ledger owns coverage.** A `coverage` finding and a ledger * row are one fact — *this rule was not traced to* — so rendering both put * the same uncovered rule on screen twice: once as an amber row, once as an * advisory finding a few sections below it. The ledger keeps it, because it * is the only surface that shows covered and uncovered together and can * therefore answer the question the finding cannot: how much of the brief the * draft did reach. The finding's own contribution, its remedy, moves onto the * row as that row's `detail` (see {@link coverage}), so nothing is lost by * withdrawing it from here. * * Only findings the ledger genuinely renders are withdrawn. A coverage * finding whose target is not a rule in the brief — an invented rule tag — * has no row to move to, so it stays in this list rather than vanishing from * the panel altogether. * * Do not restore coverage findings to this list: the duplicate is what this * filter exists to remove, and a coverage finding reappearing beneath the * ledger is the defect, not the fix. */ protected readonly listedFindings: _angular_core.Signal; /** Listed findings that block acceptance — shown first, ahead of advisory ones. */ protected readonly blockingFindings: _angular_core.Signal; /** Listed findings that are informational only. */ protected readonly advisoryFindings: _angular_core.Signal; /** * Every listed finding in reading order — blocking first — each paired with * the display name of its kind, so one loop renders both dimensions. */ protected readonly orderedFindings: _angular_core.Signal; /** * Listed findings tallied by kind, in reading order, skipping kinds this * session carries none of. * * Counts what the list below it shows, never the raw session: a tally reading * `Coverage — 2 advisory` above a list holding neither of them would be the * duplicate {@link listedFindings} removes, put back in summary form. * * Both counts travel together so the template can obey SC-06 (never two * differently-coloured semantic chips on one element): where a kind has * blocking entries they take the chip and the advisory count is plain text. */ protected readonly findingTally: _angular_core.Signal; /** * Whether the plan on screen is still being written. * * Keyed off the live partial, and deliberately **not** off * `stage === 'planning' && blueprint !== undefined`: that pair is * unobservable. The orchestrator assigns the blueprint and moves to * `generating` in two consecutive statements with nothing awaited between * them, so the earliest snapshot any subscriber can sample already says * `generating` — the predicate was false at every instant a panel could read * it. `partial` is the one member that is true for the whole of the * Architect's turn and cleared outside it. */ protected readonly blueprintStreaming: _angular_core.Signal; /** * Whether there is a plan region to render at all. * * True from the first architect partial, before any slide has streamed in, so * the "still being written" caption can appear where the plan is about to — * rather than the region popping into existence once it is half-built. */ protected readonly hasPlan: _angular_core.Signal; /** * The slide whose columns are streaming right now, or `undefined`. * * Slides are generated in parallel and the session carries one partial at a * time, so this marks the slide the latest partial names — not every slide in * flight. Without `slideIndex` the two concurrent streams would be * indistinguishable and the mark would have to go on all of them or none. */ protected readonly generatingSlideIndex: _angular_core.Signal; /** * The planned slides, rendered defensively so a half-written stream reads. * * Every member is read through the streaming guards above, because a partial * carries no guarantee that a sibling's presence implies its own: a slide can * arrive with fields and no label, with a `fields` array that is present and * empty, or with a field carrying nothing but a `formControlName`. * * Positional fallbacks (`Slide 2`, `Field 3`) stand in for members that have * not streamed in yet, so a row that is still arriving keeps its place in the * list instead of collapsing to blank space. */ protected readonly plannedSlides: _angular_core.Signal; /** * The run's repair log, one row per turn, in order. * * This is the region that makes a repaired or failed run explain itself: * what each turn set out to fix, who was asked, what was withdrawn or * escalated, and whether the next verification bore it out. Empty for a run * that staged on the first pass, and for a session no run has repaired. */ protected readonly repairLog: _angular_core.Signal; /** * The title of the saved form this session is editing, when it is editing * one — the one line that tells the reader a build will change that form * rather than draft a new one. */ protected readonly baselineTitle: _angular_core.Signal; /** Repair turns the inner loop has consumed. */ protected readonly repairTurnsUsed: _angular_core.Signal; /** * Critique rounds consumed, or `undefined` when the Critic never ran. * * Deliberately not collapsed with `?? 0`: here the difference is the whole * point of showing the number, so `undefined` renders as no row at all rather * than as a zero that would claim the outer loop ran and converged at once. */ protected readonly critiqueRoundsUsed: _angular_core.Signal; /** * Whether the run staged a valid form the Critic still had observations * about. A normal, successful outcome — the form cleared every deterministic * gate — so it is rendered as something to read, never as a failure. */ protected readonly stagedWithOutstandingFindings: _angular_core.Signal; /** Whether either loop has anything to report yet. */ protected readonly showLoopLedger: _angular_core.Signal; /** What the run has cost so far, once the orchestrator has counted anything. */ protected readonly telemetry: _angular_core.Signal; /** * Calls per role, in pipeline order. A missing key means zero, so a role that * never ran carries no row. */ protected readonly callsByRole: _angular_core.Signal; /** * Calls per lifecycle stage, in lifecycle order — not a restatement of * {@link callsByRole}: a repair turn re-invokes the Generator and the * specialists, so `repairing` is what isolates the inner loop's cost, and * `critiquing` what isolates the outer loop's reviews from the re-plans they * trigger. */ protected readonly callsByStage: _angular_core.Signal; /** Whether the Build action is available: a brief exists and the pipeline is idle. */ protected readonly canBuild: _angular_core.Signal; /** Whether a verified form is staged and ready to accept onto the canvas. */ protected readonly isStaged: _angular_core.Signal; /** * The diagnostic record of the crash that ended the run, when one did. * * Present only when the run stopped on a *thrown* error. A run that stopped * by its own rules — no brief, a spent repair budget — carries `error` alone, * and is rendered as something to act on rather than something that broke. */ protected readonly failure: _angular_core.Signal; /** * Why the session stopped, on a `failed` session; `undefined` on any other. * * Falls back to a fixed line when `error` is empty, because a failed stage * with no reason on screen is the state this region exists to prevent. */ protected readonly stoppedReason: _angular_core.Signal; /** * Where a crash happened, as one line — `TypeError during planning · * Architect` — so the reader knows which stage and which model turn to look * at before opening the trace. */ protected readonly failureOrigin: _angular_core.Signal; /** Text currently typed into the composer, not yet submitted. */ protected readonly draftText: _angular_core.WritableSignal; /** Tracks composer input as the user types. */ protected onComposerInput(event: Event): void; /** Submits the composer text as a `describe` message and clears the composer. */ protected submitMessage(): void; /** Emits `build`. No-op when {@link canBuild} is false. */ protected emitBuild(): void; /** Emits the session's staged form for the canvas. No-op when nothing is staged. */ protected acceptStaged(): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } declare class FormatDataPipe implements PipeTransform { /** * Formats an arbitrary input value for display according to `dataType`. * * `value` is typed `unknown` (a pipe can be handed any binding) and each * branch narrows it to the shape it needs. The return is the set of * display-primitive types (`string | number | null | undefined`) so it * binds cleanly to interpolation and `matTooltip`; "passthrough" branches * return the original value (asserted to the display-primitive union — its * runtime value is unchanged and Angular string-coerces it for display). * * @param value - The raw value to format. * @param dataType - Which formatting branch to apply. * @param pipeConfig - Optional pipe configuration (currency code, nested pipe type). * @returns The formatted representation, or the original value when no formatting applies. */ transform(value: unknown, dataType: DocumentLitsLabelConfigInterfaceValueType | InputDataTypes | undefined | InputPipeTypes, pipeConfig?: FormColumnInputs['pipe'] | undefined): string | number | null | undefined; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵpipe: _angular_core.ɵɵPipeDeclaration; } /** * Writes the user's pick for an ambiguous text back into a raw import row: * the cell becomes `value` for a single select; for a multi-select cell every * entry equal to `text` becomes `value` and the rest stand. Re-running the row * then resolves the pick by stored value, so it can never tie again. */ declare function applyOptionChoice(row: Record, formControlName: string, text: string, value: unknown, multi: boolean): Record; declare function decrypt(encrypted: EncryptedData, secret: string): Promise; /** * Derives a deterministic 32-char HMAC key for a given form input, salted by * the consumer-supplied secret. The secret must be configured via * `withInputSecret()` and read from `INPUT_SECRET_TOKEN` at the callsite — * this function does not (and cannot, as a free helper) inject it itself. * * @param input Input descriptor; its identity fields seed the primary digest. * @param secret Consumer-supplied HMAC secret. Throws if missing. */ declare const generateSecretKey: (input: FormColumnInputs, secret: string) => Promise; /** * @public * @deprecated Reachable for one minor cycle only as part of the Phase 1 public-surface * lockdown. Target removal: v3.0.0. No replacement — this is the empty * `ng generate library` stub and will be deleted in Phase 2. */ declare class NgxTFormsService { constructor(); static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵprov: _angular_core.ɵɵInjectableDeclaration; } /** * @public * @deprecated Reachable for one minor cycle only as part of the Phase 1 public-surface * lockdown. Target removal: v3.0.0. No replacement — this is the empty * `ng generate library` stub and will be deleted in Phase 2. */ declare class NgxTFormsComponent { static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** * Public entry-point for bulk imports. * * Typical usage: * ```ts * const importController = inject(TFormImportController); * importController.progress$.subscribe(p => render(p)); // optional reactive UI * const results = await importController.runImport(form, rows); * const validRows = results.filter(r => r.isValid); * ``` * * Singleton (`providedIn: 'root'`) because all import sessions share the same * caches/injector wiring. Call {@link TFormImportController.reset} between * runs only if you want to force consumers of `progress$` to see an empty * baseline; `runImport` already clears caches on entry and exit. * * @public * @deprecated Reachable for one minor cycle only as part of the Phase 1 public-surface * lockdown. Target removal: v3.0.0. Consumers should switch to wiring an import flow * through the `withImportTower()` feature; the controller will move under that * feature tree in Phase 2. */ declare class TFormImportController { private readonly _injector; private readonly _rows$; /** * Per-session HTTP caches. * Key: serialized request identity → shared Observable (shareReplay). * Populated at the start of runImport and cleared when it finishes, * so every import session starts cold but identical in-flight requests * across concurrent rows reuse a single HTTP call. */ private _getCache; private _postCache; /** * Registry of pre-processors keyed by ElementType. * Add an entry here to handle a new input type in _preProcessRow — no other * changes needed. Each processor receives the column definition and the raw * row and must return `{ value?, errors[] }`. * `value` is written to processedRow[formControlName] when defined; * `errors` accumulates in colErrors[formControlName]. */ private readonly _inputPreProcessors; /** * Shared financial-cycles observable for the current import session. * loadSystemInputs calls getFinacialCycles() directly via NGX_T_FORMS_CONFIG * (not through utils), so HTTP-function patching alone cannot deduplicate it. * We intercept it at the config level by providing a wrapped token in each * tower's child injector that returns this shared observable instead. */ private _financialCycles$; /** * The field of the multiple-input session most recently started by * {@link runMultipleInputImport}, so {@link rerunMultipleInputRow} can * re-check one of its rows against the same per-field context. Cleared by * a bulk run and by {@link reset}. */ private _multipleInputSession; /** * The form of the bulk session most recently started by {@link runImport}, * so {@link rerunImportRow} can re-check one of its rows the same way. * Cleared by a multiple-input run and by {@link reset}. */ private _bulkSession; /** * Duplicate detection for the current session, or `null` when the run was * given no {@link ImportIdentity}. Set by both run methods; cleared by * {@link reset}. */ private _identity; /** * Each processed row's own verdict, keyed by row index. Reporting a row as a * duplicate overlays its status; this is what a release restores. */ private readonly _verdicts; /** * Reactive progress stream — subscribe to track each row in real time. */ readonly progress$: Observable; /** * Snapshot of the current progress (non-reactive). */ get currentProgress(): ImportProgress; /** * Runs an import session. * * For each row a fresh tower instance is spun up via a child injector, * prepopulated, and left to settle (calculations + API fetches) before * recording the final value and validation status. * All rows are processed concurrently. * * Usage: * ```ts * const results = await importController.runImport(form, rows); * const valid = results.filter(r => r.isValid); * ``` * * With an `identity`, a row that repeats an earlier row on the named fields * is held as its duplicate — `status: 'duplicate'`, `duplicateOf` naming * the earlier row — and, under the default `skip` policy, never gets a * tower when the sheet already shows the repeat. A repeat that only shows * once labels and codes have resolved is recognised after the row settles. * Rejects, before any state changes, when a key is not a `formControlName` * of the form. * * @param form The form definition every row is validated against. * @param rows Raw data objects — one per import row. * @param identity Which fields identify a record, and what to do with a * duplicate. Absent, or with no keys: every row stands on its own. * * @example * const results = await importController.runImport(form, rows, { distinctKeys: ['supplierRef', 'invoiceNo'] }); * const repeats = results.filter(r => r.status === 'duplicate'); // r.duplicateOf names the row each repeats */ runImport(form: FormInterface, rows: Record[], identity?: ImportIdentity): Promise; /** * Validates rows for ONE multiple-input field, each as its own * {@link ImportRowState}. * * Every row runs through the same per-row tower the bulk path uses for a * MultipleInput's sub-rows ({@link _processMultipleFormInput}): the field's * children are hoisted into a one-slide form, pre-processors resolve mSCOA * codes and multi-selects, calculations and API fetches settle, and the * settled errors are partitioned into blocking / overridable. What differs is * the reporting — a bulk import folds sub-row errors onto the parent row as * `"[i].field.error"`; here each row is reported on its own, with * `settledValue` keyed by child `formControlName` (plus `id`) and * `colErrors` keyed the same way, so a spreadsheet import can show errors * against the user's own column headers. `validationErrors` / * `overridableErrors` stay keyed by input id, as for a bulk row. * * A choice cell (select, autocomplete, paginated selection table) may hold * the option as the user saw it — its label, or one of a table row's column * values — instead of the value the form stores; once the options have * loaded, the cell is swapped for the stored value. A text that fits several * options is reported in {@link ImportRowState.optionChoices} for the user * to settle through {@link rerunMultipleInputRow}. * * Drives the same `progress$` stream and per-session HTTP caches as * {@link runImport}; the two are not meant to run concurrently. * * Duplicate rows are recognised exactly as in {@link runImport}, with * `identity.distinctKeys` naming the children's `formControlName`s. * * @param input - The MultipleInput column whose `formInputs` define a row. * @param rows - Raw rows keyed by child `formControlName` — one object per row. * @param identity - Which children identify a row, and what to do with a * duplicate. Absent, or with no keys: every row stands on its own. * * @example * const states = await importCtrl.runMultipleInputImport(linesColumn, rows, { distinctKeys: ['item'] }); * const ready = states.filter(s => s.status === 'valid').map(s => s.settledValue); */ runMultipleInputImport(input: FormColumnInputs, rows: Record[], identity?: ImportIdentity): Promise; /** * Re-checks ONE row of the bulk session the last {@link runImport} started — * after the user settled an ambiguous option reported in * {@link ImportRowState.optionChoices}, say — and reports it in place: the * row's `originalData` becomes `row`, its state passes through `processing` * to its new outcome on `progress$`, and every other row stands. The * per-session HTTP caches are cold by then, so the row's own fetches run * again. * * When the session was given an {@link ImportIdentity}, the duplicate * relation is a fact about the session, not the row, and is re-evaluated: * the row may now repeat an earlier row (and is held as its duplicate — * without a tower, under `skip` — when the repeat shows in the raw data), * and rows that repeated it may be released back to their own verdicts. * A released row that was never processed is processed now, before this * resolves. Only rows after `rowIndex` can be affected. * * Rejects when no bulk session holds `rowIndex` (none was run, a * multiple-input run or {@link reset} has since cleared it, or the index is * out of range). * * @param rowIndex - The row's position in the session (`ImportRowState.rowIndex`). * @param row - The row to check, keyed by `formControlName` like the original. * @returns The row's new state. * * @example * // The user picked `pick` for the text `choice.text` of column `key`. * const fixed = applyOptionChoice(state.originalData, key, choice.text, pick.value, false); * const next = await importCtrl.rerunImportRow(state.rowIndex, fixed); */ rerunImportRow(rowIndex: number, row: Record): Promise; /** * Re-checks ONE row of the multiple-input session the last * {@link runMultipleInputImport} started — after the user settled an * ambiguous option reported in {@link ImportRowState.optionChoices}, say — * and reports it in place: the row's `originalData` becomes `row`, its state * passes through `processing` to its new outcome on `progress$`, and every * other row stands. * * Duplicates are re-evaluated exactly as {@link rerunImportRow} describes. * * Rejects when no multiple-input session holds `rowIndex` (none was run, a * bulk run or {@link reset} has since cleared it, or the index is out of * range). * * @param rowIndex - The row's position in the session (`ImportRowState.rowIndex`). * @param row - The row to check, keyed by child `formControlName` like the original. * @returns The row's new state. * * @example * // The user picked candidate `pick` for the text `choice.text` of column `key`. * const fixed = { ...state.originalData, [key]: pick.value }; * const next = await importCtrl.rerunMultipleInputRow(state.rowIndex, fixed); */ rerunMultipleInputRow(rowIndex: number, row: Record): Promise; /** Clears all row state. */ reset(): void; /** The patch that queues a row again with `row` as its data and every earlier outcome cleared. */ private _freshRowPatch; /** * Creates an isolated tower instance through a short-lived child injector. * * The child injector delegates all token resolution to the parent, so * HttpClient, NGX_T_FORMS_CONFIG_TOKEN, and any other deps the tower needs * are found automatically — no manual wiring required here. * * After creation the tower's HTTP functions are replaced with cache-aware * wrappers so that identical requests fired by different rows share one * in-flight Observable instead of each issuing their own HTTP call. */ private _createTower; /** * Returns a shared Observable for a GET request. * On cache miss the original function is called once and its result is * multicasted via shareReplay(1) so every concurrent subscriber (row) that * asks for the same URL+options receives the same response without an * additional HTTP call. */ private _cachedGet; /** * Returns a shared Observable for a POST request. * Keyed by URL + serialised body + serialised options so only truly * identical requests are deduplicated. */ private _cachedPost; /** * End-to-end pipeline for a single import row. Runs concurrently with every * other row in the session. * * Steps (any thrown error short-circuits to `status: 'error'`): * 1. Mark the row `processing`. * 2. Spin up an isolated tower via {@link TFormImportController._createTower}. * 3. `initialize(form, { skipInitialFunctions: true })` — see the flag's * docs; this avoids the timing race between the tower's initial async * work and our synchronous `initializeFormValues`. * 4. `_preProcessRow` resolves nested formats (SCOA codes, multi-select * strings, MultipleInput sub-rows) into the shape `initializeFormValues` * expects. * 5. Apply the resolved row → tower's changeMonitor cycle fires dependent * functions → busy=true. * 6. `waitUntilSettled()` blocks until every async op drains. * 7. `markAllAsTouched()` so required-field validators surface even on * untouched controls. * 8. {@link TFormImportController._recomputeAllValidationErrors} re-runs * sync validators across the whole tree (matches what * `getInputErrorMessage` reports in the interactive UI). * 9. Collect settled values + per-input + per-column errors and update the * row's state. * * `finally` always destroys the tower so child injectors / valueChanges * subscriptions don't leak across rows. */ private _processRow; /** * Runs one row of a multiple-input session through * {@link _processMultipleInputItem} and reports its outcome; a tower failure * fails this row alone. */ private _processMultipleInputRow; /** * Dispatches each column that has a registered pre-processor, collects * resolved values and errors, then returns the enriched row. * * To support a new input type add an entry to `_inputPreProcessors` — * nothing else needs to change here. */ private _preProcessRow; /** * Pre-processor for MscoaSelection inputs. * * Resolves flat account codes into full IScoaAccount objects. * Column naming convention expected in the raw row: * {formControlName}.{basisKey}.{SEGMENT}.debit e.g. "glAccount.accrual.FUND.debit" * {formControlName}.{basisKey}.{SEGMENT}.credit e.g. "glAccount.cash.COSTING.credit" * * Returns the nested structure that `initializeFormValues` / the mscoa * component store expect: * { accrual: { FUND: { debit: IScoaAccount, credit: IScoaAccount } }, cash: { ... } } */ private _processMscoaInput; /** * Pre-processor for MultipleInput inputs. * * Expects `row[input.formControlName]` to be an array of objects where each * object's keys are the formControlNames declared in `input.formInputs`. * * For each item the full tower lifecycle is run against a one-slide mock form * (the same sequence as _processRow on the main form), so Angular validators, * async validators, API calls, and calculated fields all fire and settle before * errors are collected. Pre-processors (e.g. MscoaSelection) are applied first * so resolved objects are in place when the tower initializes. * * Errors are prefixed with the item index: "[0].subField.errorCode". */ private _processMultipleFormInput; /** * Builds the per-field context the sub-row pipeline runs against: the * one-slide mock form (children hoisted to top-level columns with their * dependency references localised) and whether any child needs * pre-processing. Built once per field and reused for every row. */ private _multipleInputContext; /** * Runs ONE multiple-input row through the full tower lifecycle — the same * sequence as {@link _processRow} on the main form — and returns its settled * value and partitioned errors. Shared by the bulk path * ({@link _processMultipleFormInput}, which index-prefixes the errors onto the * parent row) and {@link runMultipleInputImport} (which reports each row on * its own). * * Throws when the tower itself fails; callers decide whether that fails the * parent row (bulk) or just this row (single-field import). The tower is * always destroyed. * * @param depth - Nesting depth of the OWNING field; nested MultipleInputs in * the row are pre-processed at `depth + 1`. */ private _processMultipleInputItem; /** * Projects a settled row outcome onto the {@link ImportRowState} fields with * the status rules of {@link _processRow}: any blocking or pre-processing * error → `invalid`; otherwise any overridable error → `overridable`; * otherwise `valid`. Error maps carry every failing validator key (blocking * and overridable), as the bulk collectors do. */ private _toMultipleInputRowState; /** * Rewrites a MultipleInput sub-input's cross-field dependency references from * the live form's COMPOUND id scheme back to the SIMPLE id the per-row mock * form uses. * * In the real form, {@link allFormInputs} flattens a MultipleInput's children * and rewrites each child's id to `${multipleInputId}.${childId}`. Every * dependent field's `mapTo.inputId` (value/options-API `minimumInputRequired`) * is authored against that compound id — so `readDep(compoundId)` resolves and * the dependent fetch fires. The mock form built in * {@link TFormImportController._processMultipleFormInput} hoists those children * to top-level columns, so they keep their SIMPLE ids; the baked-in compound * `mapTo.inputId` references then resolve to nothing, `validateMinInput` fails, * the value-API `params` stay `undefined`, and the resource never fetches — * leaving every derived sub-field null (and dropped by `getFormValueNames`). * * Deep-clones `node` (never mutates the shared form definition) and strips the * `${parentId}.` prefix from any `inputId` that targets a sibling in THIS * MultipleInput, so the sub-tower's signal graph resolves dependencies exactly * as the live form does. * * @param node - The sub-input config (or any nested fragment) to rewrite. * @param parentId - The owning MultipleInput's id (the compound prefix). */ private _localizeSubInputDeps; /** * Parses a string MultipleInput cell into an array of row objects. * * Spreadsheet cells are primitives, so a MultipleInput column arrives as a * JSON-stringified array. Returns the parsed array on success; returns the * raw value unchanged on failure (empty string, plain text, malformed JSON, * or a non-array JSON value) so the caller's `Array.isArray` check treats it * as "no rows" rather than throwing. */ private _tryParseJsonArray; /** * Resolves a single account code to an IScoaAccount. * Routed through `_cachedGet` so identical codes across concurrent rows share * one in-flight Observable (shareReplay) — the same mechanism used for all * other HTTP calls in this class. */ private _lookupScoaAccount; /** * Pre-processor for `Select` and `PaginatedSelectionTable` inputs. * * Single-select inputs pass through unchanged — their cell is matched against * the loaded options once the row's tower has settled * ({@link _resolveOptionValues}). * * Multi-select inputs accept the value in any of: * - `string[]` — taken as-is, each entry trimmed. * - `'["a","b"]'` — JSON-stringified array. * - `'a, b, c'` — comma-separated string. * - `'[a, b]'` (malformed JSON) — fallback: strip brackets, split, strip quotes. * * If the input declares custom (in-config) options, every parsed value is * matched against them the way the settled pass does: a stored value stands * (in the option's own type and case), a text naming exactly one option by * label becomes that option's value, and a tie is left in place for the * settled pass to report as a choice. Texts that name nothing are returned as * errors of the form `'invalidOption:'` and `value` is omitted (so the * parent form control keeps its original/empty state and the row is flagged * invalid). API-fetched options are not known here; the settled pass matches * those. */ private _processSelectInput; /** * Swaps what the user typed into a row's choice cells for what the form * stores. * * Runs after the row's first settle, when the tower has loaded every option * list the row's values reach. Each choice input (select, autocomplete, * paginated selection table) whose cell the tower holds as written — a * tower-owned value, from an API or a calculation, is left alone — is * matched against its options through {@link resolveOptionCell}: a stored * value stands, a unique label (or table column value) becomes the stored * value, a tie is kept for the user, an unmatched text on a strict field is * an error. Nothing is judged while an option list is still empty. A swap * can change what a dependent option list fetches, so after any swap the * tower settles again and the pass repeats, until a pass changes nothing or * {@link MAX_OPTION_PASSES} is reached. * * @param tower - The row's settled tower. * @param row - The pre-processed row the tower was initialised from. * @param rejected - `formControlName`s a pre-processor already reported an * error for; their cells are not judged again (the pre-processor's verdict * stands, and the cell it refused still reaches the tower as raw text). * @returns Blocking column errors (`ambiguousOption:`, * `invalidOption:`; several joined with `.`) and the choices left * for the user, both keyed by `formControlName`. */ private _resolveOptionValues; /** * Whether the tower holds `cell` for an input — i.e. the imported cell was * applied rather than displaced by a tower-owned value. Compared as text so * a numeric id written from a text cell still counts. */ private _holdsCell; /** Merges two `formControlName → 'a.b'` error maps, joining keys that occur in both. */ private _mergeColErrors; /** * Re-runs sync validation across the WHOLE form tree against the settled values. * * **Why this is still required after the signal-forms migration (Phase 1.5 finding; * kept until Phase 3):** it is NOT a settle-timing workaround — Phase 1 already made * `waitUntilSettled` deterministic. It compensates for two reactive-forms semantics * the signal-graph engine does not change: * * 1. **Cross-field validators.** Custom validators (commonFormOperations * `createCustomValidator`) read OTHER fields via `getFormValue()`. Angular only * re-runs a control's validators when ITS OWN value changes, and the engine's * projection effect likewise only `setValue`s controls whose value actually * changed — so a control whose value is unchanged keeps STALE cross-field errors * until a whole-tree `updateValueAndValidity` forces re-evaluation. * 2. **Disabled controls.** Angular never validates disabled controls (formGenerator * disables them from `input.disabled`); import temporarily enables the tree, * validates, then restores the disabled flags. * * Phase 3 (`@angular/forms/signals` `validate()`/`validateAsync()`) makes validation * reactive over observed fields and disabled-aware — at which point this method, its * enable/disable dance, and this call site can be deleted. */ private _recomputeAllValidationErrors; /** * Walks `{ [sectionId]: FormGroup { [inputId]: FormControl } }` and * collects every control that has validation errors (from `control.errors`). */ private _collectValidationErrors; /** * Groups the flat overridable-error list from {@link partitionFormErrors} into * an `inputId → messages[]` map for {@link ImportRowState.overridableErrors}. */ private _groupOverridableByInput; /** * Builds a formControlName-keyed error map from the settled form. * Multiple error keys on one control are joined with '.'. */ private _collectColErrors; /** * Collects the MultipleInput columns whose value is tower-derived — i.e. they * carry a value-API fetch (`matOptions.fetch.value.source === 'api'`) or a * value-producing calculation. These are the columns whose import rows are * overridden by the tower's own value graph and therefore need the * tower-favored deep merge applied in {@link TFormImportController._processRow}. */ private _valueDerivedMultipleInputs; /** True iff the column derives its value from a value-API fetch or a calculation. */ private _isValueDerivedColumn; /** * Deep-merges two values with the **override** (tower-derived) value favored * and the **base** (import) value filling every gap it leaves. * * Rules (mirrors the import-merge contract): * - `override` wins for scalars and for any key it defines. * - Keys present only on `base` are preserved (import fills gaps). * - Arrays merge **element-wise by index** (each element deep-merged), so a * tower row that omits an import-only field keeps that field. * - An **empty** `override` array (or `null`/`undefined`) falls back entirely * to `base` — a tower value that resolves empty never erases the import rows. */ private _deepMergePreferTower; /** * Processes every queued row — `pending`, and so not held as a duplicate — * lowest index first, until none is left. A row's outcome can release rows * queued behind it (see {@link _reconcile}); they are picked up in turn. * * Rows are processed SEQUENTIALLY, not concurrently. Each row spins up its * own tower whose settle (`waitUntilSettled`) and FormGroup projection rely * on `ApplicationRef.tick()`-driven effect flushes over the application's * single shared `ApplicationRef`. Running every row concurrently makes those * ticks interleave across towers, so `waitUntilSettled` resolves on a * spurious `settled === true` (value resources still `idle`, not yet * `loading`) before a row's derived values have drained and projected — * leaving most rows with empty API-derived fields (objectives/nKPA/etc.). * The per-session HTTP caches (`_getCache`/`_postCache`/`_financialCycles$`) * still dedupe identical requests across rows, so sequential processing * reuses the same network calls without the race. * * @param process - Settles the row at an index from its raw data; must leave * it in a terminal status. */ private _runQueued; /** The lowest index of a row that is `pending` and has data to process, or -1. */ private _nextQueued; /** * The duplicate detection a session runs with, or `null` when `identity` * names no keys. Throws when a key is not one of `fields`: a key that * matched nothing would leave every row blank there and make each a * duplicate of the first, so the failure has to be loud. */ private _identitySession; /** The `formControlName`s of a form's top-level columns. */ private _formControlNames; /** The `formControlName`s of a MultipleInput's children. */ private _childControlNames; /** * Records a row's own outcome and reports it — as a duplicate, if the * session says so — in one emission, so no subscriber sees a verdict that * is about to be overlaid. */ private _settleRow; /** * Queues a row again with `row` as its data, its earlier outcome cleared, * and re-evaluates which rows repeat which: the row may now repeat an * earlier one, and rows that repeated it may be released. */ private _requeueRow; /** Reports the current rows through {@link _reconcile}, emitting only when something changed. */ private _reconcileDuplicates; /** * Applies the session's duplicate relation to `rows` and returns them as * they are to be reported — `rows` itself when nothing changes. * * Each row's identities — raw, from `originalData`; settled, from * `settledValue` once it has one — go through {@link assignDuplicates}, * first wins. A row that repeats an earlier one is reported as its * duplicate: under `skip` with status `duplicate` and `isValid: false`, * whatever it settled to kept; under `flag` with `duplicateOf` alone. A row * that no longer repeats anything gets its own verdict back — or, never * having been processed, returns to the queue as `pending`. */ private _reconcile; /** One row as it is to be reported, given the row it repeats (`original`), if any. */ private _reportRow; /** * Immutable row update — clones the rows array and the targeted row before * pushing through `_rows$` so subscribers downstream of `progress$` always * receive a fresh reference (cheap change detection in components). */ private _patchRow; /** * Compute the {@link ImportProgress} aggregate from the row array. Pure — * driven by `progress$` and `currentProgress`. */ private _toProgress; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵprov: _angular_core.ɵɵInjectableDeclaration; } /** * Provides a unique identifier for each tower instance created during an * import run — one id per imported row. The form import controller uses this * to disambiguate concurrent tower contexts when materialising rows into * form submissions. * * @see provideNgxTForms * * @public * @deprecated Internal token used by `TFormImportController`. Will be relocated * under the `withImportTower()` feature tree in Phase 2; the token (or its * renamed/improved successor) will remain callable. See DECISIONS.md D-014. */ declare const IMPORT_TOWER_INSTANCE_ID: InjectionToken; /** * Generates a hierarchical form with nested FormGroups based on section IDs. * Parent form listens to all descendant changes, with validation cascading upward. * * @param inputs Array of form inputs with their configurations * @param getFormValue Record of form values used for custom validation * @returns A FormGroup with nested FormGroups for each section * * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare function formGenerator(inputs: Array, getFormValue: () => Record): FormGroup; /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare function getSectionElements(elements: ElementEditorInnerSectionElementInterface[], inputInEdit: (FormColumnInputs) | undefined, NGX_T_FORMS_CONFIG: NgxTFormsConfig, utils: InClassFormUtilsInterface, errors?: ValidationError[]): ElementEditorInnerSectionElementInterface[]; /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare const returnMappedPathValue: (pathMap: TreeNode[], obj: unknown) => unknown; /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare const getValueFromValueAccessor: (valueAccessRules: FormInputBasicOptionInterface | TreeNode[] | Record, data: unknown) => unknown; /** * One entry in the polymorphic-input registry. Maps an `ElementTypes` value to * a lazily-loaded renderer plus the metadata the host needs to wire it. * * Each `load()` is a dynamic `import()`, so every renderer is code-split out of * the host bundle — a form with only text fields never downloads the editor, * camera, signature pad, MSCOA charts, etc. The dynamic boundary also breaks * the otherwise-circular import between the host and `ImageCapture`. */ interface ElementEntry { /** Lazy loader for the concrete renderer component. */ readonly load: () => Promise>; /** * `true` when the renderer also accepts `editorMode` and * `formBuilderFunctions` inputs. The host only forwards those two inputs to * builder-aware entries, so `NgComponentOutlet` never tries to set an input * the target component does not declare. */ readonly builderAware?: boolean; } type TFormInputFormGroup = FormGroup<{ [sectionId: string]: FormGroup<{ [inputId: string]: FormControl; }>; }> | undefined | null; /** * Polymorphic input host — given an `ITowerStepColumn` descriptor, selects * and renders the correct concrete input component (text, select, date, * file upload, signature, geo-location, MSCOA, document picker, workflow * adjudication, etc.) and wires it to the parent reactive `FormGroup`. * * @remarks * Accepts portal-style prop injection via the optional `'COMPONENT_PROPS'` * token so it can be rendered through `ComponentPortal` with inputs supplied * dynamically. * * Inputs: * - `inputConfig: ITowerStepColumn | undefined` — descriptor of the input * to render; the `element` field selects the concrete renderer. * - `formGroup: FormGroup<{...}> | undefined | null` — the parent reactive * form group keyed by section id then input id. * - `editorMode: boolean` — default `false`; when `true`, renders the * builder-side variant. * - `formBuilderFunctions: FormBuilderFunctions | undefined` — builder * hooks passed through to inputs that need them (e.g., MSCOA). * * Outputs: * - `valueChange: unknown` — emits the input's new value whenever the user * edits the bound `FormControl`. Concrete child inputs are rendered through * `NgComponentOutlet` (which forwards inputs only, never outputs) and write * their value through the shared `FormGroup` via `ControlValueAccessor`, so * this host bridges those control edits into a single output the tower wires * to `engine.updateValue(col.id, $event)`. Without this bridge user edits * reach the projected `FormGroup` (so submission works) but never reach the * engine's `#model`, leaving the signal graph blind to user input — no * dependent calculation, value-fetch, or options-fetch ever re-fires. * * @public */ declare class TFormInputComponent { #private; /** * Descriptor of the input to render. `element` selects the concrete renderer. * * Modeled as a `model()` (not `input()`) so that portal-style injection via * `'COMPONENT_PROPS'` can write into the signal at construction time — signal * inputs are read-only at runtime, but model signals are writable via `.set()`. */ readonly inputConfig: _angular_core.ModelSignal; /** Parent reactive `FormGroup` keyed by section id then input id. */ readonly formGroup: _angular_core.ModelSignal; /** When `true`, renders the builder-side variant. */ readonly editorMode: _angular_core.ModelSignal; /** Builder hooks passed through to inputs that need them (e.g., MSCOA). */ readonly formBuilderFunctions: _angular_core.ModelSignal; /** * Emits the input's new value on every user-originated edit of the bound * control. The tower binds this to `engine.updateValue(col.id, $event)`, * which is the ONLY path user input takes into the engine's `#model` (and * therefore the signal-graph dependency reactivity). The projection effect * patches controls with `{ emitEvent: false }`, so signal→control writes do * NOT echo back here — only genuine user edits emit. */ readonly valueChange: _angular_core.OutputEmitterRef; protected readonly elementTypes: typeof ElementTypes; /** * Polymorphic-host control flag — true when the bound `FormGroup` already * has a `FormControl` (or, for `DateRangePicker`, both start/end controls) * matching the descriptor's `id`. */ protected readonly hasFormControlInstance: _angular_core.Signal; /** * Registry entry for the current descriptor's element type. Resolves to the * basic-input fallback for unknown types, mirroring the former `@default` * switch branch. `undefined` only while no descriptor is bound. */ protected readonly elementEntry: _angular_core.Signal; /** * Lazily-loaded renderer component for the current element type, unwrapped in * the template via the `async` pipe. The promise is re-created only when the * resolved entry changes, so editing other descriptor fields does not reload * the component. */ protected readonly elementComponent: _angular_core.Signal> | null>; /** * Inputs forwarded to the resolved renderer. Builder-aware renderers also * receive `editorMode` and `formBuilderFunctions`; every other renderer gets * only `inputConfig` and `formGroup`, so `NgComponentOutlet` never sets an * input the target component does not declare. */ protected readonly elementInputs: _angular_core.Signal>; constructor(props: Record | null); static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** * Renders an MSCOA (Municipal Standard Chart of Accounts) form input — a * segmented reactive form field that lets the user pick an account by * walking the MSCOA tree. Surfaces validation status via * `TFormInputStatusComponent`. * * Inputs: * - `inputConfig: ITowerStepColumn` — required, descriptor for this MSCOA input. * - `editorMode: boolean` — default `false`; when `true`, renders in * builder-editor mode rather than user-facing mode. * - `formGroup: FormGroup` — required, the parent reactive form group that * owns this input's controls. * - `formBuilderFunctions: FormBuilderFunctions | undefined` — optional * builder hooks supplied when used inside the form builder. * * Outputs: * - `reload: void` — fires when the input requests its * container reload the underlying data (e.g., after a tree refresh). */ declare class MscoaFormInputComponent { /** Descriptor for this MSCOA input. */ readonly inputConfig: _angular_core.InputSignal; /** When `true`, renders in builder-editor mode rather than user-facing mode. */ readonly editorMode: _angular_core.InputSignal; /** Parent reactive form group that owns this input's controls. */ readonly formGroup: _angular_core.InputSignal>; /** Optional builder hooks supplied when used inside the form builder. */ readonly formBuilderFunctions: _angular_core.InputSignal; /** Fires when the input requests its container reload the underlying data. */ readonly reload: _angular_core.OutputEmitterRef; protected get errorMessage(): string | string[] | undefined; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** * Form input that captures a hand-drawn signature via `SignaturePadInputComponent` * and writes the resulting image to the bound `FormGroup` control. Surfaces * validation state through `TFormInputStatusComponent`. * * Inputs: * - `inputConfig: ITowerStepColumn` — required, descriptor for this input. * - `formGroup: FormGroup` — required, the parent reactive form group. * * Outputs: * - `reload: void` — fires when the input requests its container reload * (e.g., after clearing the pad). * * @public */ declare class SignatureInputElementComponent { readonly inputConfig: _angular_core.InputSignal; readonly formGroup: _angular_core.InputSignal>; readonly reload: _angular_core.OutputEmitterRef; protected readonly errorMessage: _angular_core.Signal; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** * @file Round-trip bridge between the guided condition builder in * `lib-validators-config` and a custom validator's `expression` **string** plus * its `inputsObservedForChanges` dependency list — both part of the frozen * {@link FormControlCustomValidatorsInterface} contract. * * The validator engine evaluates `expression` with the shared predicate DSL * ({@link evaluatePredicate}) where **`true` means the field is INVALID**. The * scope it evaluates against is `{ [variable]: }` for * every entry in `inputsObservedForChanges` (see `build-manual-validation.ts`). * * Because the grammar is identical to the array-access predicate builder, this * module reuses {@link PREDICATE_OPERATORS} so the operator labels stay in lock * step with `lib-data-tree`. The model differs in one validator-specific way: * the right-hand side of a comparison may be **another field** (e.g. an * "end date is before start date" rule), not only a typed literal — so the * serializer must emit a bare variable token rather than a quoted string in * that case. * * {@link parseConditions} best-effort parses an expression into the structured * model; when it cannot (parentheses, mixed `&&`/`||`, unary `!`, an unknown * operator), it returns `null` and the UI falls back to the raw editor — power * users and legacy expressions are never locked out. * * @internal */ /** A single guided comparison: `field operator (value | field)`. */ interface ValidatorCondition { /** Left operand — always a referenced field's variable name. */ field: string; /** One of the {@link PREDICATE_OPERATORS} values (`===`, `>`, `includes`, …). */ operator: string; /** Whether the right operand is a typed literal or another field. */ compareTo: 'value' | 'field'; /** Literal text, or the variable name of the compared field. */ value: string; } /** A flat list of conditions joined by a single connector. */ interface ValidatorConditionGroup { connector: '&&' | '||'; conditions: ValidatorCondition[]; } /** A field the guided builder can reference, with a friendly label. */ interface ExpressionFieldOption { readonly variable: string; readonly label: string; /** The step the field belongs to, used to group the dropdown (`null` = ungrouped). */ readonly stepName: string | null; } /** A set of field options sharing a step, rendered as one ``. */ interface ExpressionFieldGroup { /** The `` label, or `null` to render the options without a group. */ readonly label: string | null; readonly options: readonly ExpressionFieldOption[]; } /** * Builder-side editor for authoring a boolean expression (used for guarded * navigation, conditional validation, and API path-selection rules). Validates * the expression's syntax in real time and exposes the typed tree of value- * access references the expression depends on. * * Inputs: * - `hint: string` — helper text shown beneath the expression field. * - `label: string` — label for the expression field (default * `'Validation Expression'`). * - `formInputs: FormColumnInputs[]` — inputs the expression may reference. * - `data: unknown` — sample data used to test the expression. * - `expression: string` — initial expression string written into the editor * control. * - `valueAccessRules: Record` — current value-access * tree for variables referenced by the expression. * * Outputs: * - `expressionChange` — fires when either the expression text or its derived * value-access tree changes. * * @public * @deprecated Internal builder UI re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare class ValidationExpressioCreatorComponent { #private; /** Helper text shown beneath the expression field. */ readonly hint: _angular_core.InputSignal; /** Label for the expression field. */ readonly label: _angular_core.InputSignal; /** Inputs the expression may reference. */ readonly formInputs: _angular_core.InputSignal; /** Sample data used to test the expression. Heterogeneous shape from parent template binding. */ readonly data: _angular_core.InputSignal; /** Initial expression string written into the editor control. */ readonly expression: _angular_core.InputSignal; /** Current value-access tree for variables referenced by the expression. */ readonly valueAccessRules: _angular_core.InputSignal>; /** Fires when either the expression text or its derived value-access tree changes. */ readonly expressionChange: _angular_core.OutputEmitterRef<{ expression: string | null; valueAccessRules: Record; }>; protected readonly textareaElementRef: _angular_core.Signal | undefined>; protected readonly expressionControl: FormControl; protected readonly showSuggestions: _angular_core.WritableSignal; protected readonly optionsSearch: _angular_core.WritableSignal; protected readonly variableInEdit: _angular_core.WritableSignal; /** Operator choices, shared with the validators builder for label parity. */ protected readonly operators: readonly { value: string; label: string; }[]; protected readonly mode: _angular_core.Signal<"guided" | "advanced">; protected readonly group: _angular_core.Signal; protected readonly expressionText: _angular_core.Signal; /** Fields the guided builder can reference, as friendly options. */ protected readonly fieldOptions: _angular_core.Signal; /** * Field options grouped by their `stepName` for the dropdowns, preserving * first-seen order. Fields without a step collapse into a single ungrouped * bucket (rendered without an ``), so forms that don't carry step * names degrade to a plain flat list. */ protected readonly fieldOptionGroups: _angular_core.Signal; /** Whether the current expression can be represented by the guided builder. */ protected readonly canUseGuided: _angular_core.Signal; protected readonly methods: { label: string; hint: string; value: string; }[]; /** * Bridge the `expression` input into the FormControl and seed the guided * builder. Reparsing is `untracked` so this only re-runs when the `expression` * input changes — never when the user edits inside either mode. * * A host that echoes `expressionChange` back into `expression` (the intended * controlled-component usage) makes that input change on every keystroke. Those * echoes are this component's own output coming home, not a new expression to * seed from, so they are skipped entirely. */ protected readonly expressionBridge: _angular_core.EffectRef; /** Mirror the `valueAccessRules` input into the local mutable copy. */ protected readonly valueAccessRulesBridge: _angular_core.EffectRef; protected readonly valueAccessOptions: _angular_core.Signal; protected readonly allInputs: _angular_core.Signal; constructor(); protected getMethods(): { label: string; hint: string; value: string; }[]; protected get errorMessage(): string | null; protected getValueAccessRule(key: string): TreeNode[]; protected onTextChange(expression: string): Promise; protected cleanValueAccessRules(): void; protected selectSuggestionInput(suggestion: string): void; protected onFocus(): void; /** Read the value-event target as a string. */ protected inputValue(event: Event): string; /** Switch the All/Any connector joining the conditions. */ protected setConnector(connector: '&&' | '||'): void; /** Append a fresh condition defaulting its field to the first available one. */ protected addCondition(): void; /** Apply a partial change to the condition at `index`. */ protected updateCondition(index: number, patch: Partial): void; /** Flip a condition between comparing to a typed value and another field. */ protected toggleCompareTo(index: number): void; /** Remove the condition at `index`. */ protected removeCondition(index: number): void; /** Switch to the raw expression editor (the escape hatch for power users). */ protected useAdvanced(): void; /** Switch back to the guided builder, reparsing the current expression. */ protected useGuided(): void; protected toggleVariableInEdit(variable: string): void; protected activeVariableChanged(value: TreeNode[] | string | { [key: string]: TreeNode[] | string; }): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** * Generic Material dialog shell used by the forms runtime to surface * confirmation prompts and short-lived informational dialogs. Reads its * configuration (title, body, button labels) from `MAT_DIALOG_DATA` as a * `DialogConfig` and resolves its `MatDialogRef` with `true` when the user * proceeds. * * Inputs: * - _None._ Configuration arrives through `MAT_DIALOG_DATA` (`DialogConfig`). * * Outputs: * - _None._ Result is delivered via `MatDialogRef.close(true)`. * * @public * @deprecated Internal Material dialog template re-exported via the `_deprecated/` * compatibility barrel; remains functional. A renamed/improved replacement may be * introduced in a future minor; the original symbol stays callable. See * DECISIONS.md D-014. */ declare class DialogTemplateComponent { readonly dialogRef: MatDialogRef; readonly data: DialogConfig; proceed(): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** * Custom error class for property access errors in deep object traversal. * * @class PropertyAccessError * @extends Error * * @property {PropertyPath} path - The path segment where the error occurred. * @property {string | number | symbol} segment - The specific path segment that caused the error. * * @description * This error is thrown when there's an issue accessing a property during deep object traversal. * It provides detailed information about where in the path the error occurred and what caused it. * * @example * try { * const result = returnDeepProperty(obj, ['a', 'b', 'c']); * } catch (error) { * if (error instanceof PropertyAccessError) { * console.log(`Error at path: ${error.path.join('.')}`); * console.log(`Problem segment: ${String(error.segment)}`); * console.log(`Error message: ${error.message}`); * } * } * * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare class PropertyAccessError extends Error { path: PropertyPath; segment: string | number | IArrayFunction[]; constructor(message: string, path: PropertyPath, segment: string | number | IArrayFunction[]); } /** * Retrieves a deeply nested property from an object using a path array. * * @function returnDeepProperty * * @param {unknown} item - The object to traverse. * @param {(string|number|symbol)[]} path - An array representing the path to the desired property. * Each element in the array can be a string (for object properties), * a number (for array indices), or a symbol. * * @returns {DeepPropertyValue} The value of the deep property if it exists, otherwise undefined. * * @throws {Error} If the input item is null or undefined, or if the path is not a non-empty array. * * @example * const obj = { * a: { * b: [ * { c: 1 }, * { c: 2 } * ] * } * }; * * returnDeepProperty(obj, ['a', 'b', 1, 'c']); // Returns 2 * returnDeepProperty(obj, ['a', 'x']); // Returns undefined * * @description * This function allows for safe traversal of nested objects and arrays. * It can handle various types of keys including strings, numbers, and symbols. * The function is optimized for performance and can handle very deep object structures. * * Key features: * - Iterative approach for better performance with deep structures * - Handles array indices (both as numbers and strings) * - Supports symbol keys * - Safely returns undefined for non-existent paths without throwing errors * - Type-safe with TypeScript * * Performance considerations: * - Efficient for very deep object structures (tested up to 1000 levels deep) * - Faster than naive recursive approaches for deep traversal * * Error handling: * - Returns undefined for non-existent paths or when trying to access properties of non-objects * - Throws an error if the input item is null/undefined or if the path is invalid * * @see safeReturnDeepProperty for a version that never throws and returns an object with value and error properties * * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare const returnDeepProperty: (item: unknown, path: PropertyPath) => DeepPropertyValue; /** * Safely retrieves a deeply nested property from an object using a path array. * * @function safeReturnDeepProperty * * @param {unknown} item - The object to traverse. * @param {(string|number|symbol)[]} path - An array representing the path to the desired property. * Each element in the array can be a string (for object properties), * a number (for array indices), or a symbol. * * @returns {{value: DeepPropertyValue, error: string|null}} An object containing: * - value: The value of the deep property if found, otherwise undefined. * - error: A string describing the error if one occurred, otherwise null. * * @example * const obj = { * a: { * b: [ * { c: 1 }, * { c: 2 } * ] * } * }; * * safeReturnDeepProperty(obj, ['a', 'b', 1, 'c']); * // Returns { value: 2, error: null } * * safeReturnDeepProperty(obj, ['a', 'x']); * // Returns { value: undefined, error: "Property 'x' does not exist" } * * safeReturnDeepProperty(null, ['a']); * // Returns { value: undefined, error: "Item cannot be null or undefined" } * * @description * This function is a wrapper around returnDeepProperty that never throws exceptions. * Instead, it returns an object with both the retrieved value and any error message. * This makes it suitable for use in situations where you want to handle all possible * errors without try/catch blocks. * * Key features: * - Never throws exceptions * - Provides both the retrieved value and error information * - Handles the same range of inputs as returnDeepProperty * * Use this function when you need to: * - Safely traverse objects without worrying about exceptions * - Get detailed error information along with the result * - Implement error handling in a functional programming style * * @see returnDeepProperty for the core implementation that this function wraps * * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare const safeReturnDeepProperty: (item: unknown, path: PropertyPath) => { value: DeepPropertyValue; error: string | null; }; /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare function assignDeepPropertyToObject(item: any, path: Array, value: any): any; /** * Creates a file upload data object from a Base64 string. * * @param fileName The desired name for the file (e.g., 'my-image.png'). * @param contentType The MIME type of the file (e.g., 'image/png'). * @param base64Content The raw Base64 encoded content of the file. * @returns A populated FileUploadInputValueInterface object. * * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare const createFileFromBase64: (fileName: string, contentType: string, base64Content: string) => FileUploadInputValueInterface; /** * Creates a sample value for a given form input configuration. * It determines the value based on the input's element type, specific input type (e.g., email, number), * or data type, with a fallback to a default value. * * @param {FormColumnInputs} input - The configuration object for the form input. * @returns {any} A sample value appropriate for the input type. * * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare function getSampleValueForInput(input: FormColumnInputs): any; /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare function getPipedValueFromDataType(dataType: InputDataTypes | undefined | DocumentLitsLabelConfigInterfaceValueType, value: any, pipe: FormColumnInputs['pipe'] | undefined): any; /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare const initFormConfigToV2: (formConfig: FormInterfaceMigration) => FormInterface; /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare function getUrl(url: string, variables: NgxTFormsConfig): string; /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare const sheetConsolodation: (allSheets: Record[]>) => Record[]; /** * Validates an object against a validation expression. * * @param {string} expression - The validation expression to evaluate. * @param {object} object - The object to validate. * @returns {boolean} - A boolean indicating whether the object is valid according to the expression. * * Backed by the shared predicate evaluator ({@link evaluatePredicate}), a * paren-aware, backward-compatible superset of the original space-split DSL. It * supports `===`/`==`/`!==`/`!=`/`>`/`<`/`>=`/`<=`, `&&`/`||` with correct * precedence and grouping, unary `!`, deep-path operands, multi-word quoted * strings, the keyword operators `includes`/`in`/`startsWith`/`endsWith`/`matches`, * and bare-operand truthiness. */ /** * Public callers pass either a plain object (e.g. `{ age: 1 }`) or an array * value (`item as T[]` from {@link evaluateArrayAccessRules}). Both are valid * index containers for the DSL (arrays support `length`, numeric indices, and * the `in` operator on string keys), so accept `object` and narrow internally. */ type ValidationTarget = object; /** * Validates an object against a predicate expression. Delegates to the shared, * paren-aware predicate evaluator (a backward-compatible superset of the * original space-split DSL). Throws on a structurally invalid expression. */ declare const validateObjectAgainstString: (expression: string, object: ValidationTarget) => boolean; /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare function validateExpressionSyntax(expression: string): string | null; /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. */ declare function testAgainstItem(test: ConfigurationValidTestInterface, item: unknown): boolean; /** * @public * @deprecated Internal helper re-exported via the `_deprecated/` compatibility * barrel; remains functional. A renamed/improved replacement may be introduced * in a future minor; the original symbol stays callable. See DECISIONS.md D-014. * * **Breaking (v2.x):** now requires `secret` as a third argument — the * hardcoded library secret was removed (LIB-05). Read it from * `INPUT_SECRET_TOKEN` in your component / service before calling. */ declare const getSignatureImage: (inputConfig: FormColumnInputs, value: EncryptedData | undefined, secret: string) => Promise; export { AI_ENDPOINT_CATALOG, AI_FORM_BUILDER_CONFIG, AI_MSCOA_GROUNDING, DialogTemplateComponent, FORM_ACTIONS_TOKEN, FORM_CONFIG_TOKEN, FORM_DRAFT_ORCHESTRATOR, FORM_INPUTS_TOKEN, FORM_ROUTE_SOURCE, FORM_SLIDES_TOKEN, FORM_SUGGESTION_REGISTRY, FormBuilderComponent, FormDraftOrchestrator, FormSuggestionRegistry, FormSuggestionRegistryService, FormTowerControllerService, FormatDataPipe, FormsComponent, HashbrownFormDraftOrchestratorService, HttpPipelineRepository, IMPORT_TOWER_INSTANCE_ID, INPUT_SECRET_TOKEN, MSCOA_TREE_PROVIDER, MULTIPLE_FORM_INPUT_TOKEN, MscoaFormInputComponent, NGX_T_FORMS_CONFIG_TOKEN, NgxTFormsComponent, NgxTFormsService, PIPELINE_REPOSITORY, PipelineRepository, PropertyAccessError, SPREADSHEET_PARSER, SPREADSHEET_WRITER, SignatureInputElementComponent, TDynamicDataEditComponent, TDynamicDataViewComponent, TFormDraftPanelComponent, TFormImportController, TFormInputComponent, TSuggestionReviewComponent, UTILS_OBJECT_TOKEN, UserFormStepperComponent, ValidationExpressioCreatorComponent, applyOptionChoice, assignDeepPropertyToObject, buildBlueprintSchema, buildBriefSchema, buildSuggestionSchema, createFileFromBase64, decrypt as decryptSecuredInputValue, formGenerator, generateSecretKey as generateInputSecretKey, getPipedValueFromDataType, getSampleValueForInput, getSectionElements, getSignatureImage, getUrl, getValueFromValueAccessor, initFormConfigToV2, provideNgxTForms, returnDeepProperty, returnMappedPathValue, safeReturnDeepProperty, sheetConsolodation, testAgainstItem, textIconsForUserHints, toSuggestionBatch, validateExpressionSyntax, validateObjectAgainstString, withAiEndpointCatalog, withAiFormBuilder, withAiMscoaGrounding, withFormSuggestions, withHttpPipeline, withInputSecret, withRouterFormId, withSpreadsheetImport }; export type { AiEndpointCatalogEntry, AiEndpointCatalogProvider, AiEndpointCatalogSource, AiEndpointSlot, AiFormBuilderBudgets, AiFormBuilderConfig, AiFormBuilderModels, AiFormBuilderRole, AiMscoaAccountingBasis, AiMscoaGrounding, AiMscoaSegmentDescriptor, BlueprintField, BlueprintSlide, DescribeOptions, DraftFailure, DraftFinding, DraftFindingKind, DraftFindingSeverity, DraftMessage, DraftMessageAuthor, DraftPartial, DraftRepairRoute, DraftRepairTurn, DraftRepairTurnKind, DraftRequestMiddleware, DraftSession, DraftStage, DraftTelemetry, EditableFieldError, EditableFieldKind, EditableFieldSchema, EditableFieldValidation, FieldSuggestion, FormBlueprint, FormBrief, FormBriefField, FormBriefRule, FormSuggestionChannel, FormSuggestionSnapshot, MscoaAccountSelection, MscoaCurrentSelection, MscoaFieldCapability, MscoaInnerInputDescriptor, MscoaSegmentDescriptor, MscoaSuggestionValue, MultipleRowSuggestionValue, SpreadsheetParser, SpreadsheetSheet, SpreadsheetSource, SpreadsheetTemplate, SpreadsheetTemplateSheet, SpreadsheetWriter, StagedSuggestion, SuggestionBatch };