jsonisch 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,612 @@
1
+ import { D as InternalFieldStore, F as ReadonlySignal, G as JsonSchema, I as Signal, K as Path, a as InternalFormStore, b as PluginKey, m as JsonischPlugin, t as FormRef, y as FieldSlotKey } from "./form-ref-D_xHSaqL.js";
2
+
3
+ //#region src/core/types/derivation.d.ts
4
+
5
+ /**
6
+ * The result of parsing a formula expression: the engine's opaque AST node,
7
+ * or a parse error. Shaped so a typical engine's parse result passes
8
+ * through unchanged — jsonisch itself never depends on the engine.
9
+ */
10
+ type CalcParseResult = {
11
+ readonly ok: true;
12
+ readonly node: unknown;
13
+ } | {
14
+ readonly ok: false;
15
+ readonly error: string;
16
+ };
17
+ /**
18
+ * The injected calc engine, supplied by the host. Method syntax on
19
+ * purpose: bivariant parameters let the engine's concrete AST type satisfy
20
+ * the `unknown` node without an adapter layer —
21
+ * `{ parse: parseFormula, evaluate: evaluateFormula, extractDependencies,
22
+ * extractPathRefs }` passes through unchanged.
23
+ */
24
+ interface CalcEngine {
25
+ /**
26
+ * Parses a formula expression into an opaque AST node. Called once per
27
+ * `x-formula` at store init.
28
+ */
29
+ parse(formula: string): CalcParseResult;
30
+ /**
31
+ * Evaluates a parsed node against a flat values scope. May throw (the
32
+ * `#ERROR` contract) — the derivation layer contains the throw to the
33
+ * field's own errors channel.
34
+ */
35
+ evaluate(node: unknown, scope: Record<string, unknown>): unknown;
36
+ /**
37
+ * Extracts the identifiers a formula reads: plain field refs plus
38
+ * collection names of aggregate refs (`SUM(assets[aiv])` → `assets`).
39
+ * Defines both the dep-graph edges and the eval-scope keys.
40
+ */
41
+ extractDependencies(node: unknown): string[];
42
+ /**
43
+ * Extracts `collection[field]` reference pairs. Optional: when present, a
44
+ * formula with at least one such reference is classified a rollup (the
45
+ * stricter-persistence set). A scalar root-record ref (`record[…]`) uses the
46
+ * same syntax and currently also flags — shape-aware refinement belongs
47
+ * to the persistence-rule slice.
48
+ */
49
+ extractPathRefs?(node: unknown): ReadonlyArray<{
50
+ readonly collection: string;
51
+ readonly field: string;
52
+ }>;
53
+ }
54
+ /**
55
+ * The mode of a formula field that accepts an estimate: `estimate` holds
56
+ * the provisional manually-entered value (the field's own input signal);
57
+ * `formula` computes. Estimate-first chronology — the formula supersedes
58
+ * the estimate, never "overrides" it. Wire-compatible with the legacy
59
+ * `<key>Source` companion's `manual` (→ `estimate`) / `formula` modes; the
60
+ * companion decode itself is a later slice.
61
+ */
62
+ type DerivationMode = "estimate" | "formula";
63
+ /**
64
+ * The output of a formula field's derived signal. `error` is the calc
65
+ * error message (`null` when the evaluation succeeded); it feeds the
66
+ * field's composed `errors` channel, where `#ERROR` rendering picks it up.
67
+ */
68
+ interface DerivedState {
69
+ readonly value: unknown;
70
+ readonly error: string | null;
71
+ }
72
+ //#endregion
73
+ //#region src/plugins/envelopes/types.d.ts
74
+ /**
75
+ * The persisted meta half of an estimate field. Wire `mode` uses the
76
+ * settled names (`estimate` / `formula`). `manualValue` preserves the
77
+ * typed estimate across a mode flip; `lastFlippedAt` is stamped only when
78
+ * the mode actually changes. Server recompute may write a PARTIAL meta
79
+ * (`{ mode: "formula" }` only) — every reader tolerates that.
80
+ */
81
+ interface EstimateMeta {
82
+ mode?: DerivationMode;
83
+ manualValue?: unknown;
84
+ lastFlippedAt?: string;
85
+ }
86
+ /**
87
+ * The persisted meta half of an amount-or-percent field. Wire `mode` is
88
+ * `amount` | `percent`; `basis` is the percent-of field key. The meta
89
+ * holds ONLY entry state — the value half is always the resolved dollar
90
+ * amount.
91
+ */
92
+ interface EntryMeta {
93
+ mode?: EntryMode;
94
+ basis?: string;
95
+ }
96
+ /**
97
+ * The entry mode of an amount-or-percent field: enter a dollar `amount`,
98
+ * or a `percent` of the percent basis.
99
+ */
100
+ type EntryMode = "amount" | "percent";
101
+ /**
102
+ * In-memory estimate envelope (PR #553 kind union). `mode` is omitted
103
+ * when nothing was ever persisted — encode must not fabricate a pin.
104
+ */
105
+ interface EstimateEnvelope {
106
+ readonly kind: "estimate";
107
+ readonly value?: unknown;
108
+ readonly mode?: DerivationMode;
109
+ readonly manualValue?: unknown;
110
+ readonly lastFlippedAt?: string;
111
+ }
112
+ /**
113
+ * In-memory amount-or-percent envelope. `mode` / `basis` are resolved
114
+ * (schema default applied) so dirty compare is `!==`, not re-defaulting.
115
+ */
116
+ interface AmountOrPercentEnvelope {
117
+ readonly kind: "amount-or-percent";
118
+ readonly value?: unknown;
119
+ readonly mode: EntryMode;
120
+ readonly basis?: string;
121
+ }
122
+ type Envelope = EstimateEnvelope | AmountOrPercentEnvelope;
123
+ /**
124
+ * The envelope slot of an estimate field (the `estimate` family). The mode
125
+ * signal is a computed over `envelope.mode` — write via `setMode` (the
126
+ * flip API), never by assigning `mode`.
127
+ */
128
+ interface EstimateSlot {
129
+ readonly family: "estimate";
130
+ /**
131
+ * Live envelope. Written only by `writeEnvelope`.
132
+ */
133
+ readonly envelope: Signal<EstimateEnvelope>;
134
+ /**
135
+ * Dirty baseline / reset target. Reassigned on rebase and on
136
+ * `reset({ initialInput })`.
137
+ */
138
+ readonly startEnvelope: Signal<EstimateEnvelope>;
139
+ /**
140
+ * Resolved estimate/formula mode. An unpinned empty field follows
141
+ * `x-estimate-default-mode`; a stored unpinned value stays `estimate`.
142
+ */
143
+ readonly mode: ReadonlySignal<DerivationMode>;
144
+ /**
145
+ * The estimate value preserved when the mode flipped to formula this
146
+ * session (wire `manualValue` carry).
147
+ */
148
+ readonly manualValue: ReadonlySignal<unknown>;
149
+ /**
150
+ * The flip timestamp stamped this session, or the decoded one.
151
+ */
152
+ readonly lastFlippedAt: ReadonlySignal<string | undefined>;
153
+ /**
154
+ * Whether the meta half must serialize: the mode changed, or the
155
+ * estimate value did (an estimate keystroke dirties the meta with it).
156
+ */
157
+ readonly isDirty: ReadonlySignal<boolean>;
158
+ /**
159
+ * The identity-stable react callbacks the plugin's `fieldSnapshot`
160
+ * contributes, created lazily on first snapshot — a fresh closure per
161
+ * snapshot would defeat the equality gate and re-render every
162
+ * notification. Bound to form + path, which is safe because stores are
163
+ * position-fixed (array ops move values, not stores).
164
+ */
165
+ callbacks?: {
166
+ readonly setMode: (mode: DerivationMode) => void;
167
+ };
168
+ }
169
+ /**
170
+ * The envelope slot of an amount-or-percent field (the `amount-or-percent` family).
171
+ * Value edits never dirty the meta — only entry-state changes do.
172
+ */
173
+ interface AmountOrPercentSlot {
174
+ readonly family: "amount-or-percent";
175
+ /**
176
+ * Live envelope. Written only by `writeEnvelope`.
177
+ */
178
+ readonly envelope: Signal<AmountOrPercentEnvelope>;
179
+ /**
180
+ * Dirty baseline / reset target. Reassigned on rebase and on
181
+ * `reset({ initialInput })`.
182
+ */
183
+ readonly startEnvelope: Signal<AmountOrPercentEnvelope>;
184
+ /**
185
+ * The current entry mode. An unpinned empty field follows
186
+ * `x-hybrid-default-mode`; a stored unpinned value stays `amount`.
187
+ */
188
+ readonly entryMode: ReadonlySignal<EntryMode>;
189
+ /**
190
+ * The current percent basis (a root-level field key), or `undefined` when the
191
+ * schema declares no default and none was stored.
192
+ */
193
+ readonly percentBasis: ReadonlySignal<string | undefined>;
194
+ /**
195
+ * Whether the meta half must serialize (entry mode or basis changed).
196
+ */
197
+ readonly isDirty: ReadonlySignal<boolean>;
198
+ /**
199
+ * The identity-stable react callbacks (see `EstimateSlot.callbacks`).
200
+ */
201
+ callbacks?: {
202
+ readonly setEntryMode: (mode: EntryMode) => void;
203
+ readonly setPercentBasis: (percentBasis: string) => void;
204
+ };
205
+ }
206
+ /**
207
+ * The envelope slot a value field may carry: meta state that is
208
+ * dirty-tracked and serialized by the envelopes plugin, never rendered as
209
+ * a field.
210
+ */
211
+ type EnvelopeSlot = EstimateSlot | AmountOrPercentSlot;
212
+ //#endregion
213
+ //#region src/core/form/validate-form-input.d.ts
214
+ /**
215
+ * Configuration for validating the form input.
216
+ */
217
+ interface ValidateFormInputConfig {
218
+ /**
219
+ * Whether to focus the first field with an error.
220
+ */
221
+ readonly shouldFocus?: boolean | undefined;
222
+ }
223
+ /**
224
+ * The result of validating the form input.
225
+ */
226
+ interface ValidationResult {
227
+ /**
228
+ * Whether the input passed validation.
229
+ */
230
+ readonly success: boolean;
231
+ /**
232
+ * The validated form input (the current tree input — AJV does not
233
+ * transform).
234
+ */
235
+ readonly output: unknown;
236
+ }
237
+ /**
238
+ * Validates the form input using the injected validator. Runs the validator
239
+ * against the current tree input, routes each issue's `instancePath` to its
240
+ * field store's `errors` signal (accumulating multiple issues per field,
241
+ * clearing every other field), and optionally focuses the first field with
242
+ * an error. A form without a validator always validates successfully.
243
+ *
244
+ * @param internalFormStore The form store to validate.
245
+ * @param config The validation configuration.
246
+ *
247
+ * @returns The validation result.
248
+ */
249
+ declare function validateFormInput(internalFormStore: InternalFormStore, config?: ValidateFormInputConfig): ValidationResult;
250
+ //#endregion
251
+ //#region src/plugins/envelopes/plugin.d.ts
252
+ /**
253
+ * The envelopes plugin's state: one slot per estimate/amount-or-percent
254
+ * value store, keyed by store identity.
255
+ */
256
+ type EnvelopeState = Map<InternalFieldStore, EnvelopeSlot>;
257
+ /**
258
+ * The envelopes plugin: owns the meta half of estimate and
259
+ * amount-or-percent fields — mode/entry state decoded from the kind
260
+ * envelope, dirty-tracked, and serialized by wrapping the field's own
261
+ * payload entry. No factory arguments: the envelope rides the field key,
262
+ * so every scope's raw value already carries the meta half (no envelope
263
+ * side-channel to decode).
264
+ */
265
+ declare function envelopes(): JsonischPlugin<EnvelopeState>;
266
+ declare module "../../react/types" {
267
+ interface FieldStoreSlots {
268
+ /**
269
+ * The estimate/formula mode of an estimate field, `undefined`
270
+ * otherwise.
271
+ */
272
+ readonly mode: DerivationMode | undefined;
273
+ /**
274
+ * Flips an estimate field's mode (the `setMode` method — seeds the
275
+ * estimate from the last formula result, stamps the meta half).
276
+ * Contributed only for estimate fields.
277
+ */
278
+ readonly setMode: (mode: DerivationMode) => void;
279
+ /**
280
+ * The entry mode of an amount-or-percent field, `undefined` otherwise.
281
+ */
282
+ readonly entryMode: EntryMode | undefined;
283
+ /**
284
+ * Sets an amount-or-percent field's entry mode (dirties the meta
285
+ * half). Contributed only for amount-or-percent fields.
286
+ */
287
+ readonly setEntryMode: (mode: EntryMode) => void;
288
+ /**
289
+ * The percent basis of an amount-or-percent field (a root-level field key).
290
+ */
291
+ readonly percentBasis: string | undefined;
292
+ /**
293
+ * Sets an amount-or-percent field's percent basis (dirties the meta
294
+ * half). Contributed only for amount-or-percent fields.
295
+ */
296
+ readonly setPercentBasis: (percentBasis: string) => void;
297
+ }
298
+ }
299
+ //#endregion
300
+ //#region src/plugins/derivation/key.d.ts
301
+ /**
302
+ * The derivation slot of a formula/estimate value store.
303
+ */
304
+ interface DerivationSlot {
305
+ /**
306
+ * The derived output dependents chain through and the field displays: a
307
+ * computed over the deps' input signals + `offFormValues`, resolved
308
+ * through the single scope path — mode-aware: an estimate pin holds the
309
+ * field's own input. NEVER written back into `input` — derived values
310
+ * are outputs, excluded from dirty and payload by construction.
311
+ */
312
+ readonly derived: ReadonlySignal<DerivedState>;
313
+ /**
314
+ * The always-computed formula result, IGNORING the estimate pin — the
315
+ * candidate value the estimate wrapper's nudge compares against and the
316
+ * seed for a formula→estimate flip. The same signal object as `derived`
317
+ * on a plain formula field.
318
+ */
319
+ readonly formulaValue: ReadonlySignal<DerivedState>;
320
+ /**
321
+ * Whether the formula references a collection (`SUM(assets[…])`) —
322
+ * classified statically at wire time. Marks the stricter-persistence
323
+ * set; carries no behavior in this slice.
324
+ */
325
+ readonly isRollup: boolean;
326
+ }
327
+ /**
328
+ * The derivation plugin's slot key. The scope resolvers and the react
329
+ * surface read derived state through this identity.
330
+ */
331
+ declare const derivationKey: FieldSlotKey<DerivationSlot>;
332
+ //#endregion
333
+ //#region src/plugins/derivation/plugin.d.ts
334
+ /**
335
+ * The derivation plugin's state: one slot per formula/estimate value
336
+ * store, keyed by store identity.
337
+ */
338
+ type DerivationState = Map<InternalFieldStore, DerivationSlot>;
339
+ /**
340
+ * The derivation plugin: parses every `x-formula` once per scope (the
341
+ * document root and each array row — it is scope, not machinery, that
342
+ * differs; LOS-596), breaks dependency cycles deterministically, and gives
343
+ * each formula field its `formulaValue`/`derived` slot plus the composed
344
+ * `errors` channel.
345
+ *
346
+ * Scope resolution lives HERE and only here (the LOS-514 closure — no
347
+ * other read path exists): per dependency, the form value wins (a formula
348
+ * dep resolves through its own derived signal, chaining fresh values),
349
+ * `offFormValues` fills only `undefined`, and when both sides are arrays
350
+ * the canonical rows merge with the live form rows (the collection
351
+ * overlay for aggregate refs).
352
+ *
353
+ * Declares `dependsOn: [envelopesKey]` — the estimate pin reads the mode
354
+ * signal the envelopes plugin creates; without it the pin would silently
355
+ * never engage and a manually pinned value would be overwritten by the
356
+ * next recompute (the D2 data-loss scenario), so a missing envelopes
357
+ * plugin is a startup error instead.
358
+ */
359
+ declare function derivation(engine: CalcEngine): JsonischPlugin<DerivationState>;
360
+ declare module "../../react/types" {
361
+ interface FieldStoreSlots {
362
+ /**
363
+ * The mode-aware derived output of a formula/estimate field (what the
364
+ * field displays; an estimate pin holds the input). `undefined` on
365
+ * non-derived fields or without the derivation plugin.
366
+ */
367
+ readonly derived: DerivedState | undefined;
368
+ /**
369
+ * The always-computed formula result of a formula/estimate field,
370
+ * ignoring the estimate pin — the nudge's candidate value.
371
+ */
372
+ readonly formulaValue: DerivedState | undefined;
373
+ }
374
+ }
375
+ //#endregion
376
+ //#region src/plugins/visibility/plugin.d.ts
377
+ /**
378
+ * The visibility plugin's key. It keeps no state (rules and computeds live
379
+ * on the field stores' base `visibleWhen`/`visible` members).
380
+ */
381
+ declare const visibilityKey: PluginKey<null>;
382
+ /**
383
+ * The visibility plugin: every field gated by an `allOf` `if/then/else`
384
+ * block on its OWN scope's schema gets a `visible` computed signal over the
385
+ * watched field's resolved value. Two scope kinds (LOS-722 extended
386
+ * visibility into rows — the spec's open question 3):
387
+ *
388
+ * - the root scope, whose rules come from the form schema's `allOf`;
389
+ * - an array-row scope, whose rules come from the ITEMS schema's `allOf`
390
+ * (`buildNestedFieldItems` carries the related dataset's conditionals),
391
+ * evaluated against THAT row's values — row A's trigger never gates
392
+ * row B's field.
393
+ *
394
+ * Visibility gates RENDERING only: a hidden field keeps its state, stays
395
+ * in the dirty diff, and rides the payload. Fields without a rule get no
396
+ * signal — the public store reads that as always visible.
397
+ *
398
+ * Register AFTER derivation (array order): a WHEN watching a formula field
399
+ * resolves through its derived slot. No hard `dependsOn` — a form without
400
+ * a calc engine legitimately omits derivation, and the watched-value read
401
+ * falls back to the field's input.
402
+ */
403
+ declare function visibility(): JsonischPlugin<null>;
404
+ //#endregion
405
+ //#region src/plugins/bagger/compute-scope.d.ts
406
+ interface BaggerOptions {
407
+ bag?: string;
408
+ /**
409
+ * Second classification source for row collections, union-ed with the
410
+ * walked schema's own relation nodes (`collectionKeys`). The host passes
411
+ * its DATASET schema here: which record keys hold rows is a property of
412
+ * the record, not of the stage being rendered.
413
+ */
414
+ collectionsSchema?: JsonSchema;
415
+ }
416
+ /**
417
+ * `computeBag` is store-less, so the alias key arrives as an option here;
418
+ * the plugin threads `form.rootRecordAlias` through (the one home — the
419
+ * plugin deliberately has NO alias option of its own, so the alias writer
420
+ * and the row-scope reader can never disagree).
421
+ */
422
+ interface ComputeBagOptions extends BaggerOptions {
423
+ rootRecordAlias?: string;
424
+ }
425
+ /**
426
+ * The record keys that hold relation ROWS, from both classification
427
+ * sources. The union is load-bearing in both directions:
428
+ *
429
+ * - `collectionsSchema` (the dataset schema) alone decides collections the
430
+ * walked schema does not configure. A stage that renders no assets tray
431
+ * still needs `assets` on the shelf as FLATTENED rows — left raw, every
432
+ * bag-resident key (`assignmentFee`) reads undefined per row and
433
+ * `SUM(assets[assignmentFee])` or a check silently computes 0.
434
+ * - The walked schema alone declares relations the dataset schema lacks:
435
+ * registry-role slugs and fallback defs are synthesized per stage
436
+ * (`buildStageSchemaFromConfig`), so replacing rather than union-ing
437
+ * would drop those collections off the shelf.
438
+ */
439
+ declare function collectionKeys(schema: JsonSchema, collectionsSchema?: JsonSchema): Set<string>;
440
+ /**
441
+ * Merges a source row's `x-column` fields with its bag column — bag wins,
442
+ * per the `x-column` save-routing authority (AI.md: read = row spread over
443
+ * its `data`). INTACT merge only, no envelope unwrapping: a later task
444
+ * seeds form rows from this output, and unwrapping here would hand the
445
+ * store already-flattened envelope values, reproducing the LOS-461
446
+ * regression.
447
+ */
448
+ declare function flattenSourceRow(row: Record<string, unknown>, bag: string): Record<string, unknown>;
449
+ /**
450
+ * Computes the off-form value bag for a canonical record: every declared
451
+ * scalar and relation collection, bag-merged and envelope-unwrapped, plus
452
+ * an alias entry holding the whole computed scope (so a formula or
453
+ * widget can address `record.termMonths` as readily as bare `termMonths`).
454
+ */
455
+ declare function computeBag(schema: JsonSchema, record: Record<string, unknown>, opts?: ComputeBagOptions): Record<string, unknown>;
456
+ //#endregion
457
+ //#region src/plugins/bagger/plugin.d.ts
458
+ /**
459
+ * The bagger plugin's state: the computed shelf plus an id-keyed row index
460
+ * per many:true relation field, used to seed a row's declared keys with
461
+ * their canonical baseline. Single-pick relations (`many: false`) hold a
462
+ * ref object, not a row collection, so they have no rows to index.
463
+ */
464
+ interface BaggerState {
465
+ readonly scope: Record<string, unknown>;
466
+ readonly rowsByField: Map<string, Map<string, Record<string, unknown>>>;
467
+ }
468
+ declare const baggerKey: PluginKey<BaggerState>;
469
+ /**
470
+ * The bagger plugin: computes the off-form shelf once (`computeBag`) and
471
+ * owns `offFormValues`, then seeds every relation row's declared keys from
472
+ * its canonical row as an unedited baseline. Registration order:
473
+ * `[envelopes(), bagger(record), derivation?, visibility()]` — envelopes
474
+ * first so its slot already exists on every estimate/amount-or-percent leaf by the
475
+ * time seeding runs (`dependsOn: [envelopesKey]` enforces this at
476
+ * `createFormStore`).
477
+ */
478
+ declare function bagger(record: Record<string, unknown>, opts?: BaggerOptions): JsonischPlugin<BaggerState>;
479
+ //#endregion
480
+ //#region src/plugins/checks/types.d.ts
481
+ type Severity = "info" | "warning" | "error";
482
+ type SeverityConfig = Severity | "off";
483
+ interface Finding {
484
+ /** Instance id — the bespoke.check row id. */
485
+ readonly id: string;
486
+ /** Definition id — "formula", … */
487
+ readonly checkId: string;
488
+ readonly messageId?: string;
489
+ /** Already interpolated. */
490
+ readonly message: string;
491
+ /** Display title (instance options.name, else the definition id). */
492
+ readonly name: string;
493
+ /** From config. A check cannot set this. */
494
+ readonly severity: Severity;
495
+ /** Field this finding attaches to; `[]` means form-level. */
496
+ readonly path: Path;
497
+ }
498
+ interface CheckScope {
499
+ /**
500
+ * One key through the derivation closure (LOS-514): when BOTH the
501
+ * canonical side and the live side are arrays, mergeCollectionRows;
502
+ * otherwise live wins and offForm / plugin collections fill only
503
+ * `undefined`. A root-record ref like `record` is a plain object — get
504
+ * returns that object, never an empty array.
505
+ */
506
+ get(key: string): unknown;
507
+ /**
508
+ * `get(collection)` when the value is an array, else `[]`. Never forces
509
+ * a non-array live value through the collection merge.
510
+ */
511
+ rows(collection: string): readonly Readonly<Record<string, unknown>>[];
512
+ /**
513
+ * The whole bag. Frozen; building it subscribes to every root key.
514
+ */
515
+ values(): Readonly<Record<string, unknown>>;
516
+ }
517
+ interface FindingDescriptor {
518
+ readonly messageId?: string;
519
+ readonly message?: string;
520
+ readonly data?: Readonly<Record<string, string | number>>;
521
+ /** Field(s) the finding attaches to. Empty/omitted → form-level. */
522
+ readonly paths?: readonly Path[];
523
+ }
524
+ interface CheckContext<Options = unknown> {
525
+ readonly id: string;
526
+ readonly checkId: string;
527
+ readonly options: Options;
528
+ readonly scope: CheckScope;
529
+ report(finding: FindingDescriptor): void;
530
+ }
531
+ interface CheckMeta<Options = unknown> {
532
+ readonly name: string;
533
+ readonly messages: Readonly<Record<string, string>>;
534
+ readonly optionsSchema?: Record<string, unknown> | false;
535
+ readonly defaultOptions?: Options;
536
+ }
537
+ interface CheckHandlers {
538
+ evaluate(): void;
539
+ }
540
+ interface CheckDefinition<Options = unknown> {
541
+ readonly meta: CheckMeta<Options>;
542
+ create(context: CheckContext<Options>): CheckHandlers;
543
+ }
544
+ interface CheckInstanceConfig<Options = unknown> {
545
+ readonly id: string;
546
+ readonly check: string;
547
+ readonly severity: SeverityConfig;
548
+ readonly options?: Options;
549
+ }
550
+ interface ChecksConfig {
551
+ readonly definitions: Readonly<Record<string, CheckDefinition<unknown>>>;
552
+ readonly instances: readonly CheckInstanceConfig[];
553
+ /**
554
+ * Canonical collection rows for CheckScope only (legacy host
555
+ * `evalCollections`). Must not be written onto the form's offForm shelf.
556
+ */
557
+ readonly collections?: Readonly<Record<string, ReadonlyArray<Record<string, unknown>>>>;
558
+ }
559
+ interface FormulaOptions {
560
+ readonly formula: string;
561
+ readonly message?: string;
562
+ readonly name?: string;
563
+ readonly targetFieldKeys?: readonly string[];
564
+ }
565
+ //#endregion
566
+ //#region src/plugins/checks/key.d.ts
567
+ interface ChecksState {
568
+ readonly findings: ReadonlySignal<readonly Finding[]>;
569
+ readonly hasBlockingFinding: ReadonlySignal<boolean>;
570
+ /**
571
+ * Per-instance finding computeds. `replaceCheckInstances` swaps this
572
+ * signal's value so live admin edits rebind without remounting the form.
573
+ */
574
+ readonly instances: Signal<readonly ReadonlySignal<Finding[]>[]>;
575
+ /**
576
+ * Canonical collection rows for CheckScope (legacy `evalCollections`).
577
+ * Reactive so replace can refresh them with the instances.
578
+ */
579
+ readonly collections: Signal<Readonly<Record<string, ReadonlyArray<Record<string, unknown>>>>>;
580
+ }
581
+ declare const checksKey: PluginKey<ChecksState>;
582
+ //#endregion
583
+ //#region src/plugins/checks/formula.d.ts
584
+ /** Shared messageId for parse/eval failures (plugin catch + host mapping). */
585
+ declare const UNEVALUABLE_MESSAGE_ID = "unevaluable";
586
+ /**
587
+ * The one built-in check definition: a `bespoke.check` row is an *instance*
588
+ * of this, not a definition of its own (ANALYSIS-eslint.md §2.6).
589
+ */
590
+ declare function formulaCheck(engine: CalcEngine): CheckDefinition<FormulaOptions>;
591
+ //#endregion
592
+ //#region src/plugins/checks/plugin.d.ts
593
+ /**
594
+ * Checks plugin (LOS-605 / spec D8): eslint's contract on a form store.
595
+ * Findings are a parallel channel — they never merge into `errors`.
596
+ *
597
+ * Register after derivation so a check that reads a formula field resolves
598
+ * through its derived slot. Array order, not a hard `dependsOn` — a form
599
+ * without a calc engine legitimately omits derivation.
600
+ *
601
+ * Always register (even with zero instances). Live instance edits go through
602
+ * `replaceCheckInstances` so the host does not remount the form.
603
+ */
604
+ declare function checks(config: ChecksConfig): JsonischPlugin<ChecksState>;
605
+ /**
606
+ * Re-registers check instances on a live form store (admin edits, late
607
+ * prop arrival) without remounting. Swaps the per-instance computeds the
608
+ * `findings` signal already reads.
609
+ */
610
+ declare function replaceCheckInstances(form: FormRef, config: ChecksConfig): void;
611
+ //#endregion
612
+ export { EnvelopeState as A, EnvelopeSlot as B, flattenSourceRow as C, derivation as D, DerivationState as E, AmountOrPercentEnvelope as F, CalcParseResult as G, EstimateMeta as H, AmountOrPercentSlot as I, DerivationMode as K, EntryMeta as L, ValidateFormInputConfig as M, ValidationResult as N, DerivationSlot as O, validateFormInput as P, EntryMode as R, computeBag as S, visibilityKey as T, EstimateSlot as U, EstimateEnvelope as V, CalcEngine as W, bagger as _, ChecksState as a, ComputeBagOptions as b, CheckDefinition as c, ChecksConfig as d, Finding as f, BaggerState as g, SeverityConfig as h, formulaCheck as i, envelopes as j, derivationKey as k, CheckInstanceConfig as l, Severity as m, replaceCheckInstances as n, checksKey as o, FormulaOptions as p, DerivedState as q, UNEVALUABLE_MESSAGE_ID as r, CheckContext as s, checks as t, CheckScope as u, baggerKey as v, visibility as w, collectionKeys as x, BaggerOptions as y, Envelope as z };