@pipelex/mthds-form 0.3.0 → 0.5.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.
Files changed (81) hide show
  1. package/README.md +21 -7
  2. package/dist/chunk-25I6JDIK.js +28 -0
  3. package/dist/chunk-25I6JDIK.js.map +1 -0
  4. package/dist/chunk-5Q2W6HBA.js +201 -0
  5. package/dist/chunk-5Q2W6HBA.js.map +1 -0
  6. package/dist/chunk-GCXV76WU.js +213 -0
  7. package/dist/chunk-GCXV76WU.js.map +1 -0
  8. package/dist/chunk-GKV77RAS.js +46 -0
  9. package/dist/chunk-GKV77RAS.js.map +1 -0
  10. package/dist/chunk-IIJRJGZQ.js +128 -0
  11. package/dist/chunk-IIJRJGZQ.js.map +1 -0
  12. package/dist/chunk-IM2AHWBR.js +99 -0
  13. package/dist/chunk-IM2AHWBR.js.map +1 -0
  14. package/dist/chunk-LBFZXYCY.js +44 -0
  15. package/dist/chunk-LBFZXYCY.js.map +1 -0
  16. package/dist/chunk-OD3VYXUY.js +156 -0
  17. package/dist/chunk-OD3VYXUY.js.map +1 -0
  18. package/dist/chunk-PM55AHRO.js +52 -0
  19. package/dist/chunk-PM55AHRO.js.map +1 -0
  20. package/dist/chunk-RPA7DNSK.js +41 -0
  21. package/dist/chunk-RPA7DNSK.js.map +1 -0
  22. package/dist/chunk-V3OV2XQT.js +12 -0
  23. package/dist/chunk-V3OV2XQT.js.map +1 -0
  24. package/dist/chunk-VTPUDBSJ.js +61 -0
  25. package/dist/chunk-VTPUDBSJ.js.map +1 -0
  26. package/dist/chunk-XYQ7NXQZ.js +31 -0
  27. package/dist/chunk-XYQ7NXQZ.js.map +1 -0
  28. package/dist/chunk-YEVVR6FE.js +66 -0
  29. package/dist/chunk-YEVVR6FE.js.map +1 -0
  30. package/dist/core/contracts.d.ts +114 -0
  31. package/dist/core/contracts.js +4 -0
  32. package/dist/core/contracts.js.map +1 -0
  33. package/dist/core/date-format.d.ts +87 -0
  34. package/dist/core/date-format.js +3 -0
  35. package/dist/core/date-format.js.map +1 -0
  36. package/dist/core/derive.d.ts +55 -0
  37. package/dist/core/derive.js +6 -0
  38. package/dist/core/derive.js.map +1 -0
  39. package/dist/core/descriptor.d.ts +172 -0
  40. package/dist/core/descriptor.js +3 -0
  41. package/dist/core/descriptor.js.map +1 -0
  42. package/dist/core/gate-validator.d.ts +27 -0
  43. package/dist/core/gate-validator.js +4 -0
  44. package/dist/core/gate-validator.js.map +1 -0
  45. package/dist/core/gate.d.ts +144 -0
  46. package/dist/core/gate.js +12 -0
  47. package/dist/core/gate.js.map +1 -0
  48. package/dist/core/gating-fields.d.ts +38 -0
  49. package/dist/core/gating-fields.js +6 -0
  50. package/dist/core/gating-fields.js.map +1 -0
  51. package/dist/core/index.d.ts +13 -623
  52. package/dist/core/index.js +14 -1
  53. package/dist/core/normalize-schema.d.ts +30 -0
  54. package/dist/core/normalize-schema.js +4 -0
  55. package/dist/core/normalize-schema.js.map +1 -0
  56. package/dist/core/own-property.d.ts +46 -0
  57. package/dist/core/own-property.js +3 -0
  58. package/dist/core/own-property.js.map +1 -0
  59. package/dist/core/readiness.d.ts +91 -0
  60. package/dist/core/readiness.js +4 -0
  61. package/dist/core/readiness.js.map +1 -0
  62. package/dist/core/schema-utils.d.ts +86 -0
  63. package/dist/core/schema-utils.js +3 -0
  64. package/dist/core/schema-utils.js.map +1 -0
  65. package/dist/core/validation-message.d.ts +39 -0
  66. package/dist/core/validation-message.js +4 -0
  67. package/dist/core/validation-message.js.map +1 -0
  68. package/dist/core/values.d.ts +98 -0
  69. package/dist/core/values.js +10 -0
  70. package/dist/core/values.js.map +1 -0
  71. package/dist/core/wire-format.d.ts +95 -0
  72. package/dist/core/wire-format.js +6 -0
  73. package/dist/core/wire-format.js.map +1 -0
  74. package/dist/react/index.d.ts +43 -2
  75. package/dist/react/index.js +166 -64
  76. package/dist/react/index.js.map +1 -1
  77. package/dist/styles.css +1 -1
  78. package/package.json +7 -1
  79. package/dist/chunk-S6KOTTGD.js +0 -990
  80. package/dist/chunk-S6KOTTGD.js.map +0 -1
  81. package/dist/descriptor-DPrrfAjL.d.ts +0 -101
@@ -0,0 +1,30 @@
1
+ export { hoistDefsToRoot } from './schema-utils.js';
2
+
3
+ /**
4
+ * Normalizes a JSON Schema for RJSF compatibility.
5
+ *
6
+ * Pydantic v2 generates `anyOf: [{type: "string"}, {type: "null"}]` for optional
7
+ * fields (`str | None`). RJSF renders this as a dropdown selector asking the user
8
+ * to pick a sub-schema, which is terrible UX. The normalizer flattens those
9
+ * patterns on every node (see `flattenAnyOf` in `schema-utils.ts` for the exact
10
+ * rules) and hoists nested `$defs` to the root so composed sub-schemas keep
11
+ * their refs resolvable.
12
+ *
13
+ * The collapse and hoist engines live in `schema-utils.ts` - the
14
+ * consolidated home for the kernel's JSON-Schema plumbing; this module owns the
15
+ * recursive application over a whole schema tree.
16
+ */
17
+
18
+ /**
19
+ * Recursively normalize a JSON Schema for RJSF.
20
+ * Walks the entire schema tree and flattens anyOf patterns on every node.
21
+ */
22
+ declare function normalizeSchemaForRjsf(schema: Record<string, unknown>): Record<string, unknown>;
23
+ /**
24
+ * Prepare a schema for RJSF: hoist all nested `$defs` to the root, then normalize
25
+ * anyOf patterns. Call this on any schema that composes multiple sub-schemas
26
+ * (each of which may ship its own `$defs`).
27
+ */
28
+ declare function prepareSchemaForRjsf(schema: Record<string, unknown>): Record<string, unknown>;
29
+
30
+ export { normalizeSchemaForRjsf, prepareSchemaForRjsf };
@@ -0,0 +1,4 @@
1
+ export { normalizeSchemaForRjsf, prepareSchemaForRjsf } from '../chunk-LBFZXYCY.js';
2
+ export { hoistDefsToRoot } from '../chunk-OD3VYXUY.js';
3
+ //# sourceMappingURL=normalize-schema.js.map
4
+ //# sourceMappingURL=normalize-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"normalize-schema.js"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Read a property a record OWNS, never one it merely inherits.
3
+ *
4
+ * Every name this package indexes a record by - an input variable name, a
5
+ * structure's property name, a pipe code, a concept's content key - is chosen
6
+ * by the METHOD AUTHOR, and the records they index are plain objects the host
7
+ * built (`{}` from a form's state, `JSON.parse` of a request body, a map keyed
8
+ * by pipe ref). A plain object inherits `Object.prototype`, so a name that
9
+ * collides with a prototype member - `constructor`, `toString`, `valueOf`,
10
+ * `hasOwnProperty`, … - does NOT read as `undefined`: it reads as the inherited
11
+ * function, which is truthy.
12
+ *
13
+ * That is a verdict bug wherever a bare read decides one. An input named
14
+ * `constructor` with nothing entered used to resolve to `Object`, which
15
+ * `isFilled` had no branch for and therefore called filled - a live Run button
16
+ * over an empty required input. `getPipeIOContract` returned the same function
17
+ * for a prototype-named pipe code, which sailed past the `if (!contract)` guard
18
+ * hosts are shown writing.
19
+ *
20
+ * What it does NOT close, stated so nobody reads a fixed read as a fixed class:
21
+ * the WRITE twin. `out[name] = value` on a plain object invokes the inherited
22
+ * `__proto__` setter for that one name, so an input actually called `__proto__`
23
+ * is dropped from a payload rather than added to it. That is fail-loud (the
24
+ * runtime rejects a run whose input is missing) where the read bug was
25
+ * fail-open, and `__proto__` is not a name an author writes, so the writes are
26
+ * left plain and legible.
27
+ *
28
+ * Spelled with `Object.prototype.hasOwnProperty.call` rather than `Object.hasOwn`
29
+ * deliberately. `hasOwn` is an ES2022 runtime API and `tsconfig.json` compiles
30
+ * to **ES2020**, so a browser inside the declared target has no such function
31
+ * and every read here would throw before a form could render a single field -
32
+ * a whole-package failure in service of a shorter spelling. TypeScript cannot
33
+ * catch that: `lib` is what types a runtime API, `target` is what states the
34
+ * baseline, and the two are allowed to disagree.
35
+ *
36
+ * Deliberately module-private: this is a spelling of `record[key]`, not a
37
+ * concept a consumer needs. Reachability is low - only a method author picks
38
+ * these names - but the cost of being wrong is a form that says a field is
39
+ * filled when it is empty, so the package spells the read one way everywhere
40
+ * rather than guarding the sites someone happened to notice.
41
+ */
42
+ declare function ownProp<T>(record: Record<string, T> | null | undefined, key: string): T | undefined;
43
+ /** `key in record` with the prototype chain left out. */
44
+ declare function hasOwnProp(record: object | null | undefined, key: string): boolean;
45
+
46
+ export { hasOwnProp, ownProp };
@@ -0,0 +1,3 @@
1
+ export { hasOwnProp, ownProp } from '../chunk-V3OV2XQT.js';
2
+ //# sourceMappingURL=own-property.js.map
3
+ //# sourceMappingURL=own-property.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"own-property.js"}
@@ -0,0 +1,91 @@
1
+ import { RunField } from './descriptor.js';
2
+ import './contracts.js';
3
+ import 'mthds/protocol';
4
+
5
+ /**
6
+ * Value readiness - the helpers the runner uses to decide whether the Run
7
+ * button may fire and which inputs are still missing.
8
+ */
9
+
10
+ /** True when a value is meaningfully filled (not empty / blank / empty container). */
11
+ declare function isFilled(value: unknown): boolean;
12
+ /**
13
+ * True when a slot holds something AND every required field inside it has a
14
+ * value.
15
+ *
16
+ * The two halves are both load-bearing, and the first one is the one that is
17
+ * easy to leave out. A structure is satisfied only if there is something in it:
18
+ * a struct whose concept declares no required child used to be *vacuously*
19
+ * satisfied by an absent value - every child passed the `!f.required` test, so
20
+ * `every` returned true over a value that was not there. Readiness reported
21
+ * nothing missing and the Run button lit up, while the value bridge omitted the
22
+ * untouched structure and the gate then rejected the run on the combined
23
+ * schema's `required` list, naming an input the form had just declared ready.
24
+ *
25
+ * `isFilled` is the SAME predicate `toRjsf` uses to decide whether a structure
26
+ * collapses to an absence (`values.ts`), which is what makes the two halves
27
+ * agree by construction rather than by parallel edits: readiness calls an input
28
+ * present exactly when the bridge keeps it. Absence is what a singular slot
29
+ * expresses, so a required one has to be touched - the button stays dark until
30
+ * the user puts a value somewhere inside, and then the whole shell travels,
31
+ * empty children and all, so a required child left blank still fails loudly.
32
+ *
33
+ * Its own recursion follows the DESCRIPTOR, not the value, so it needs no depth
34
+ * cap of its own: a `RunField` tree only exists because `buildRunFields`
35
+ * finished walking a contract's schema, which bounds it. `isFilled` is where
36
+ * the caller's data is walked, and where the cap lives.
37
+ */
38
+ declare function fieldFilled(field: RunField, value: unknown): boolean;
39
+ interface Readiness {
40
+ total: number;
41
+ ready: number;
42
+ missing: string[];
43
+ }
44
+ /**
45
+ * True when the user must put a value in this field before the run can start.
46
+ *
47
+ * Distinct from `required`, which also drives layout (a required field always
48
+ * shows; an optional one may collapse). A **variable-plural** field stays
49
+ * required - it is a first-class input and keeps its place in the form - but it
50
+ * never gates: a plural slot is never "absent" in MTHDS, its empty form IS the
51
+ * empty list, and no method can declare "at least one item". A **fixed-count**
52
+ * list (`[N]`) is the exception the wire now states, and it gates like any other
53
+ * slot - there the method HAS ruled the empty form out.
54
+ *
55
+ * **For a field this package built, the answer comes from `inputMustBeFilled`**
56
+ * (`contracts.ts`) - the SAME predicate the method viewer's Run button, its ajv
57
+ * `required` list and its missing-inputs toast all read. `buildRunFields`
58
+ * stamps it onto `gating`. This used to be re-derived here as
59
+ * `required && kind !== 'list'`: the same rule expressed against the MAPPED
60
+ * field instead of the contract, and the two could disagree - `kind` is `'list'`
61
+ * for a `conceptRef` ending in `[]` OR an array schema, while the contract's
62
+ * plural test is the array schema alone, so a plural input carrying a non-array
63
+ * schema gated on one surface and not the other.
64
+ *
65
+ * The shape heuristic survives only as the fallback for a **hand-authored**
66
+ * `RunField` - story fixtures and unit tests, which have no contract to consult
67
+ * and therefore nothing to disagree with. Every field a host renders comes from
68
+ * `buildRunFields` and carries `gating`, so a host is on the contract's answer
69
+ * alone. (The fallback cannot see a fixed count either - one more thing only the
70
+ * contract knows.)
71
+ */
72
+ declare function mustBeFilled(field: RunField): boolean;
73
+ /**
74
+ * How many top-level inputs stand between the current values and a run.
75
+ *
76
+ * Two kinds count, and the second is easy to miss. An input the method DEMANDS
77
+ * counts always (`mustBeFilled`). An input the method left optional counts once
78
+ * the user has put something in it - because a structure that has been touched
79
+ * owes its concept every field the concept declares, and the gate enforces
80
+ * exactly that. Left out, an optional struct opened and half-filled was ignored
81
+ * here and refused there: the button stayed live and the run came back rejected
82
+ * on a required child, which is the same disagreement the demanded case had.
83
+ *
84
+ * So an optional input moves INTO the denominator the moment it is touched, and
85
+ * out of `missing` as soon as it is complete: 3 of 3 while it is untouched, 3
86
+ * of 4 once it is started, 4 of 4 once it is done. `total` answers "how many
87
+ * things stand between me and Run", which is the number a host is displaying.
88
+ */
89
+ declare function computeReadiness(fields: RunField[], values: Record<string, unknown>): Readiness;
90
+
91
+ export { type Readiness, computeReadiness, fieldFilled, isFilled, mustBeFilled };
@@ -0,0 +1,4 @@
1
+ export { computeReadiness, fieldFilled, isFilled, mustBeFilled } from '../chunk-PM55AHRO.js';
2
+ import '../chunk-V3OV2XQT.js';
3
+ //# sourceMappingURL=readiness.js.map
4
+ //# sourceMappingURL=readiness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"readiness.js"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The ONE place JSON-Schema plumbing lives for the run form kernel: nullable
3
+ * `anyOf` collapsing, `$defs` collection/hoisting, `$ref` resolution, and type
4
+ * extraction. Consolidated from three drifted private copies (`field-model.ts`,
5
+ * `normalize-schema.ts`, `input-format.ts`) during the form-kernel extraction.
6
+ *
7
+ * Two collapse engines are exported ON PURPOSE - their semantics genuinely
8
+ * differ, and each caller's observable behavior is characterized and preserved
9
+ * (see `schema-utils-characterization.test.ts`):
10
+ *
11
+ * - `collapseNullable` (the field mapper's rule): collapse ONLY when exactly one
12
+ * non-null branch remains, dropping the null. A multi-branch union stays an
13
+ * `anyOf` and maps to the raw-JSON escape hatch.
14
+ * - `flattenAnyOf` (the RJSF/ajv rule): a union of simple primitives becomes a
15
+ * `type` ARRAY (null preserved as a type entry - ajv validates `type:
16
+ * ["number","null"]` natively); a 2-branch nullable non-primitive drops the
17
+ * null branch.
18
+ *
19
+ * Whether these converge (and which semantics win) is an M1 question, decided
20
+ * when the descriptor derivation moves server-side - not silently here.
21
+ */
22
+ type JsonSchema = Record<string, unknown>;
23
+ /** Pydantic emits `anyOf: [T, {type:'null'}]` for Optional[T]; collapse to T.
24
+ * Collapses ONLY when exactly one non-null branch remains; the branch merges
25
+ * OVER the outer schema (its constraints win). */
26
+ declare function collapseNullable(schema: JsonSchema): JsonSchema;
27
+ /**
28
+ * The RJSF/ajv collapse: a union of simple primitives flattens to a `type`
29
+ * array (`anyOf: [{string},{null}]` → `type: ["string","null"]`, which RJSF
30
+ * renders as a plain input instead of a sub-schema picker); an exactly-two
31
+ * `[T, {null}]` union of anything else drops the null branch and merges T over
32
+ * the outer schema. Anything else passes through unchanged.
33
+ */
34
+ declare function flattenAnyOf(schema: JsonSchema): JsonSchema;
35
+ /** The schema's non-null type: a bare `type` string, or the first non-null
36
+ * entry of a `type` array (`["number","null"]` → `"number"`). */
37
+ declare function schemaTypeOf(schema: JsonSchema): string | undefined;
38
+ /** Resolve `{ $ref: "#/$defs/Foo" }` against a collected `$defs` map. Sibling
39
+ * keys on the referencing schema override the definition's. */
40
+ declare function derefSchema(schema: JsonSchema, defs: Record<string, JsonSchema>): JsonSchema;
41
+ /**
42
+ * Resolve the indirection layers pydantic puts in front of a value schema - a
43
+ * `#/$defs/...` ref, a nullable `anyOf: [T, {null}]` wrapper, or a stack of
44
+ * both - until a plain schema remains. This is the resolver `healStringWrappers`
45
+ * and `pruneEmptyOptionals` apply before reading `type`. Unlike `derefSchema`
46
+ * it REPLACES the schema with the definition (no sibling merge) - exactly what
47
+ * the callers did inline.
48
+ */
49
+ declare function resolveSchemaIndirection(schema: JsonSchema, defs: Record<string, JsonSchema>): JsonSchema;
50
+ /**
51
+ * Resolve one schema node to its fixpoint under `derefSchema` +
52
+ * `collapseNullable`, so a nullable REFERENCE - pydantic's `anyOf: [{$ref},
53
+ * {type:'null'}]` for an Optional concept-typed field - resolves the same way
54
+ * a bare `$ref` or an inline nullable does. One pass of either helper cannot:
55
+ * collapsing the nullable surfaces a `$ref` that was inside a branch, and
56
+ * dereferencing can surface a nullable the definition holds. Sibling keys
57
+ * still merge over the definition (`derefSchema`'s semantics - unlike
58
+ * `resolveSchemaIndirection`, which replaces). Identity is the fixpoint test:
59
+ * both helpers return the schema unchanged when they have nothing to do.
60
+ */
61
+ declare function resolveSchemaNode(schema: JsonSchema, defs: Record<string, JsonSchema>): JsonSchema;
62
+ interface CollectDefsOptions {
63
+ /**
64
+ * Whether the walk descends into JSON arrays (`anyOf`/`allOf` branches,
65
+ * tuple `items`). The field mapper always has (so a def declared inside an
66
+ * `anyOf` branch resolves); the wire-format heal path historically has NOT -
67
+ * both behaviors are characterized, and each caller states its mode
68
+ * explicitly until M1 retires the difference.
69
+ */
70
+ traverseArrays: boolean;
71
+ }
72
+ /**
73
+ * Collect every nested `$defs` entry into one flat map so `#/$defs/...` refs
74
+ * resolve from anywhere. First definition of a name wins. The input schema is
75
+ * NOT modified (see `hoistDefsToRoot` for the stripping variant).
76
+ */
77
+ declare function collectSchemaDefs(schema: JsonSchema, into: Record<string, JsonSchema>, options: CollectDefsOptions): Record<string, JsonSchema>;
78
+ /**
79
+ * Hoist every nested `$defs` to the schema root, so a wrapper schema composed
80
+ * from several pydantic sub-schemas keeps its `#/$defs/...` refs resolvable.
81
+ * (Pydantic emits one `$defs` per model; composing models as properties of a
82
+ * wrapper object leaves those maps below the root the refs point to.)
83
+ */
84
+ declare function hoistDefsToRoot(schema: JsonSchema): JsonSchema;
85
+
86
+ export { type CollectDefsOptions, type JsonSchema, collapseNullable, collectSchemaDefs, derefSchema, flattenAnyOf, hoistDefsToRoot, resolveSchemaIndirection, resolveSchemaNode, schemaTypeOf };
@@ -0,0 +1,3 @@
1
+ export { collapseNullable, collectSchemaDefs, derefSchema, flattenAnyOf, hoistDefsToRoot, resolveSchemaIndirection, resolveSchemaNode, schemaTypeOf } from '../chunk-OD3VYXUY.js';
2
+ //# sourceMappingURL=schema-utils.js.map
3
+ //# sourceMappingURL=schema-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"schema-utils.js"}
@@ -0,0 +1,39 @@
1
+ import { RunInputError } from './gate-validator.js';
2
+
3
+ /** The message keys this module renders, relative to the caller's namespace
4
+ * (a host passes its own translator for that namespace). */
5
+ type ValidationMessageKey = 'inputPanel.aDateField' | 'inputPanel.pickValidDate' | 'inputPanel.pickValidDateEmpty' | 'inputPanel.dateCarriesTime' | 'inputPanel.invalidValue' | 'inputPanel.invalidValueWithData';
6
+ /**
7
+ * The translate function the caller injects. Typed on the EXACT keys this
8
+ * module uses - not a loose `(key: string) => string` - for two reasons: a
9
+ * typo'd key still fails the build here, and next-intl's translator (whose
10
+ * `key` parameter is the namespace's real key union, a SUPERSET of these)
11
+ * remains assignable by parameter contravariance. Kernel-owned: this module
12
+ * must not import next-intl's types.
13
+ */
14
+ type Translate = (key: ValidationMessageKey, values?: Record<string, string>) => string;
15
+ /**
16
+ * The value the validator actually rejected, read back out of the submitted data.
17
+ *
18
+ * RJSF builds `property` from ajv's `instancePath` by swapping `/` for `.`
19
+ * (`.quote_date.date`), so the segments index straight into the form data.
20
+ * Array indices arrive as digit strings and index arrays natively.
21
+ */
22
+ declare function valueAtProperty(data: unknown, property: string | undefined): unknown;
23
+ /**
24
+ * The rejected value rendered for a toast, or `null` when there is nothing
25
+ * worth showing (an absent or blank value - "you left it empty" is what the
26
+ * message says on its own, and `“”` adds noise).
27
+ */
28
+ declare function displayValue(value: unknown): string | null;
29
+ /**
30
+ * One validation error as a human line naming the offending value.
31
+ *
32
+ * The date branch is special-cased because ajv's own complaint (`'Date' must
33
+ * match format "date-time"`) is opaque, and because the carries-a-time case has
34
+ * a specific fix the user cannot guess: move the time into the sibling `time`
35
+ * field, which is exactly what the runtime's own error says.
36
+ */
37
+ declare function describeValidationError(error: RunInputError, t: Translate, formData: unknown): string;
38
+
39
+ export { type Translate, type ValidationMessageKey, describeValidationError, displayValue, valueAtProperty };
@@ -0,0 +1,4 @@
1
+ export { describeValidationError, displayValue, valueAtProperty } from '../chunk-RPA7DNSK.js';
2
+ import '../chunk-GKV77RAS.js';
3
+ //# sourceMappingURL=validation-message.js.map
4
+ //# sourceMappingURL=validation-message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"validation-message.js"}
@@ -0,0 +1,98 @@
1
+ import { PipeInputContract, PipeIOContract, PipeInputFormDescriptor } from 'mthds/protocol';
2
+ import { RunField } from './descriptor.js';
3
+ import './contracts.js';
4
+
5
+ /**
6
+ * Bridges the `MethodRunner`'s field values to the rest of the host.
7
+ *
8
+ * The method-store keeps inputs in the *simplified* format and runs send the
9
+ * *full* RJSF form shape - both already handled by `inflate/deflateAllInputs`.
10
+ * The only gap is that a few field controls use a friendlier value than RJSF: a
11
+ * scalar is the bare scalar where the full shape is the content model its
12
+ * concept declares (`{ text }`, `{ number }`, `{ yes_no }`), and a file is
13
+ * `{ url, filename }`. `fromRjsf`/`toRjsf` translate just those, so store
14
+ * reads/writes and run payloads stay byte-for-byte compatible with the proven
15
+ * playground path.
16
+ *
17
+ * Which property a scalar wraps into is NOT decided here - it arrives on the
18
+ * descriptor as `contentKey`, derived from the contract by `buildRunFields`.
19
+ *
20
+ * Pure module - no React, unit-tested.
21
+ */
22
+
23
+ type Dict = Record<string, unknown>;
24
+ type InputSchemas = Record<string, PipeInputContract>;
25
+ /** Seed the runner's form values from the method-store's simplified inputData. */
26
+ declare function runValuesFromStore(inputData: Dict | undefined, fields: RunField[], inputSchemas: InputSchemas): Dict;
27
+ /**
28
+ * The runner's form values in **schema shape** - the same shape an RJSF panel
29
+ * holds, and therefore the shape `gate.ts` validates against the per-input
30
+ * JSON Schemas.
31
+ *
32
+ * Deliberately stops short of `deflateAllInputs`: deflation is the STORE's
33
+ * compact form, and ajv validates the full one.
34
+ */
35
+ declare function rjsfDataFromRunValues(values: Dict, fields: RunField[]): Dict;
36
+ /** Convert the runner's form values back to the store's simplified inputData. */
37
+ declare function storeInputDataFromRunValues(values: Dict, fields: RunField[], inputSchemas: InputSchemas): Dict;
38
+ /**
39
+ * Build the `{ concept, content }` map a run expects from the form values.
40
+ *
41
+ * The two ways a method says "this may be nothing" land differently on the wire,
42
+ * and both matter:
43
+ * - an **optional (`?`)** input left blank is OMITTED, so the runtime records a
44
+ * real absence the method can branch on - sending `{ text: "" }` would look
45
+ * like the caller supplied an empty string;
46
+ * - a **plural (`[]`)** input keeps its key, because a plural slot is never
47
+ * absent - its empty form IS the empty list, and the runtime requires the key.
48
+ * When that list is EMPTY it is sent BARE, without the envelope: the envelope
49
+ * is the runtime's "explicit form", which bypasses the top-down shaper and
50
+ * hands the value to the bottom-up factory, and that factory types a list from
51
+ * its first item - so an empty list there raises "Cannot create Stuff from
52
+ * empty list in content". The bare form keeps the shaper, which reads the
53
+ * DECLARED concept and builds the empty list correctly. (pipelex/#1096 makes
54
+ * the envelope agree; the bare form runs today and stays correct after.)
55
+ */
56
+ declare function apiInputsFromRunValues(values: Dict, fields: RunField[], inputSchemas: InputSchemas): Record<string, unknown>;
57
+ /** One named output pulled out of a run's working memory, ready to render. */
58
+ interface OutputEntry {
59
+ /** The output variable key (mono). */
60
+ key: string;
61
+ conceptRef?: string;
62
+ value: unknown;
63
+ }
64
+ /**
65
+ * Split a run's pipe output into the main result and any intermediate outputs,
66
+ * excluding the inputs. The main output's identity comes from the working
67
+ * memory's `main_stuff` alias (the authoritative pointer to the output variable)
68
+ * - so its label is the REAL stuff name, not a positional guess. Falls back to
69
+ * the last produced stuff only when the alias is absent.
70
+ */
71
+ declare function outputsFromPipeOutput(pipeOutput: unknown, inputKeys: string[]): {
72
+ main: OutputEntry;
73
+ intermediates: OutputEntry[];
74
+ } | null;
75
+ /**
76
+ * Recover the inputs a run was executed with from its working memory. The run's
77
+ * `working_memory.root` holds every named stuff - inputs AND outputs - keyed by
78
+ * variable name; we pull the entries whose keys are declared inputs and deflate
79
+ * their `content` (the full form shape) back into the store's simplified
80
+ * `inputData`. Lets the runner repopulate the form for a past/shared run - the
81
+ * inputs that produced the result. Returns `null` when no inputs are present.
82
+ */
83
+ declare function inputDataFromWorkingMemory(workingMemory: unknown, inputSchemas: InputSchemas): Dict | null;
84
+ /**
85
+ * Field descriptors for a method's main pipe - the wire descriptor mapped over
86
+ * the contract, or `[]` while either artifact has not arrived yet. The
87
+ * descriptor comes back from `validate` beside `pipe_io_contracts` when the
88
+ * request opts in with `views: ["input_form"]`; `getPipeInputForm` is the
89
+ * lookup that matches `getPipeIOContract`.
90
+ */
91
+ declare function fieldsForContract(contract: PipeIOContract | undefined, descriptor: PipeInputFormDescriptor | undefined): RunField[];
92
+ /**
93
+ * Immutably set a value at a dotted/indexed path (`a.0.b`). Used to write an
94
+ * uploaded file's URL back to a field anywhere in the (possibly nested) tree.
95
+ */
96
+ declare function setValueAtPath(root: Dict, path: string[], value: unknown): Dict;
97
+
98
+ export { type OutputEntry, apiInputsFromRunValues, fieldsForContract, inputDataFromWorkingMemory, outputsFromPipeOutput, rjsfDataFromRunValues, runValuesFromStore, setValueAtPath, storeInputDataFromRunValues };
@@ -0,0 +1,10 @@
1
+ export { apiInputsFromRunValues, fieldsForContract, inputDataFromWorkingMemory, outputsFromPipeOutput, rjsfDataFromRunValues, runValuesFromStore, setValueAtPath, storeInputDataFromRunValues } from '../chunk-5Q2W6HBA.js';
2
+ import '../chunk-IIJRJGZQ.js';
3
+ import '../chunk-GCXV76WU.js';
4
+ import '../chunk-PM55AHRO.js';
5
+ import '../chunk-GKV77RAS.js';
6
+ import '../chunk-OD3VYXUY.js';
7
+ import '../chunk-XYQ7NXQZ.js';
8
+ import '../chunk-V3OV2XQT.js';
9
+ //# sourceMappingURL=values.js.map
10
+ //# sourceMappingURL=values.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"values.js"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Converts between the simplified inputs.json format and the full RJSF form format.
3
+ *
4
+ * The inputs.json file uses a SIMPLIFIED format for users:
5
+ * - Text: "my text" (instead of { text: "my text" })
6
+ * - Document: "file.pdf" or { url: "..." } (instead of { url: "...", ... })
7
+ * - Image: same as Document
8
+ * - Text[]: ["t1", "t2"]
9
+ * - Document[]: ["f1.pdf"] or [{ url: "..." }]
10
+ * - Custom: { concept, content } (no simplification)
11
+ *
12
+ * The RJSF form uses the FULL concept structure from the json_schema.
13
+ *
14
+ * Two directions:
15
+ * - deflate: form (full) → inputs.json (simplified) - for writing inputs.json / API calls
16
+ * - inflate: inputs.json (simplified) → form (full) - for populating the form from inputs.json
17
+ */
18
+ /**
19
+ * Convert a single form value to the simplified inputs.json format.
20
+ * Used when writing inputs.json and when calling the run API.
21
+ *
22
+ * Rules:
23
+ * - Text { text: "hello" } → "hello"
24
+ * - Document { url: "file.pdf", ... } → "file.pdf" (if only url) or { url, ... }
25
+ * - Image: same as Document
26
+ * - Page: same as Document
27
+ * - List types → array of deflated items
28
+ * - Custom → { concept, content }
29
+ */
30
+ declare function deflateInput(value: unknown, conceptCode: string): unknown;
31
+ /**
32
+ * Convert a simplified inputs.json value to the full form structure.
33
+ * Used when populating the RJSF form from inputs.json.
34
+ *
35
+ * Rules:
36
+ * - "hello" → { text: "hello" } (for Text concepts)
37
+ * - "file.pdf" → { url: "file.pdf" } (for Document/Image/Page concepts)
38
+ * - { url: "..." } → passthrough (already full)
39
+ * - ["t1", "t2"] → inflated array of items
40
+ * - { concept, content } → content (for custom concepts)
41
+ */
42
+ declare function inflateInput(value: unknown, conceptCode: string): unknown;
43
+ interface InputSchema {
44
+ concept_ref: string;
45
+ json_schema?: Record<string, unknown>;
46
+ }
47
+ /**
48
+ * The validation API returns concept_ref WITHOUT the [] suffix for list inputs.
49
+ * The array-ness is only encoded in json_schema.type === "array".
50
+ * This helper resolves the correct concept ref by checking the schema.
51
+ */
52
+ declare function resolveConceptCode(schema: InputSchema): string;
53
+ /**
54
+ * Deflate all inputs from form format to simplified inputs.json format.
55
+ * Used by InputConfigPanel when preparing data for the run API.
56
+ */
57
+ declare function deflateAllInputs(formData: Record<string, unknown>, inputSchemas: Record<string, InputSchema>): Record<string, unknown>;
58
+ /**
59
+ * Heal values against their json_schema: the run-form bridge used to
60
+ * double-wrap plain string properties as `{ text: "..." }` (fixed in
61
+ * run-values.ts, but the corrupted shape survives in persisted inputData -
62
+ * store and server copies). Wherever the schema expects a string but the
63
+ * value is a lone `{ text: string }` wrapper, unwrap it. Walks nested
64
+ * objects and arrays, resolving `#/$defs/...` refs against the root schema;
65
+ * anything already valid passes through untouched.
66
+ */
67
+ declare function healStringWrappers(value: unknown, rawSchema: Record<string, unknown> | undefined, defs?: Record<string, Record<string, unknown>>): unknown;
68
+ /**
69
+ * Drop optional properties the user never filled, so an untouched field cannot
70
+ * fail validation or reach the API as an empty string.
71
+ *
72
+ * RJSF fills EVERY unset string property with `""` on mount. That is harmless
73
+ * for a plain string, but `prepareSchemaForRjsf` collapses pydantic's
74
+ * `anyOf: [T, null]` by dropping the null branch, so an optional *constrained*
75
+ * field - `DateContent.time`, declared `datetime.time | None` and rendered
76
+ * `{ type: "string", format: "time" }` - is left demanding a well-formed time.
77
+ * `""` is not one, and the run was blocked on a field the user never opened.
78
+ *
79
+ * Pruning is the semantically correct repair, not a validation dodge: for
80
+ * pipelex an absent optional field IS `None`, and `""` would fail pydantic at
81
+ * the runner just as it fails ajv here. Only properties absent from their
82
+ * parent's `required` are dropped - an empty REQUIRED field must still fail, so
83
+ * the user is told about it.
84
+ *
85
+ * What counts as empty is `isEmptyAfterPruning`, and it includes an optional
86
+ * STRUCTURE the user never opened, on exactly the same grounds.
87
+ */
88
+ declare function pruneEmptyOptionals(value: unknown, rawSchema: Record<string, unknown> | undefined, defs?: Record<string, Record<string, unknown>>): unknown;
89
+ /**
90
+ * Inflate all inputs from simplified inputs.json format to form format.
91
+ * Used by onInputUpdate when receiving inputs.json changes from the sandbox.
92
+ */
93
+ declare function inflateAllInputs(simplifiedData: Record<string, unknown>, inputSchemas: Record<string, InputSchema>): Record<string, unknown>;
94
+
95
+ export { deflateAllInputs, deflateInput, healStringWrappers, inflateAllInputs, inflateInput, pruneEmptyOptionals, resolveConceptCode };
@@ -0,0 +1,6 @@
1
+ export { deflateAllInputs, deflateInput, healStringWrappers, inflateAllInputs, inflateInput, pruneEmptyOptionals, resolveConceptCode } from '../chunk-GCXV76WU.js';
2
+ import '../chunk-GKV77RAS.js';
3
+ import '../chunk-OD3VYXUY.js';
4
+ import '../chunk-V3OV2XQT.js';
5
+ //# sourceMappingURL=wire-format.js.map
6
+ //# sourceMappingURL=wire-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"wire-format.js"}
@@ -1,6 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { R as RunField, B as BooleanRunField, D as DateRunField, E as EnumRunField, F as FileRunField, L as ListRunField, N as NumberRunField, O as ObjectRunField, P as ProseRunField, T as TextRunField, U as UnknownRunField, C as ConceptCategory } from '../descriptor-DPrrfAjL.js';
3
+ import { RunField, BooleanRunField, DateRunField, EnumRunField, FileRunField, ListRunField, NumberRunField, ObjectRunField, ProseRunField, TextRunField, UnknownRunField, ConceptCategory } from '../core/descriptor.js';
4
+ import '../core/contracts.js';
5
+ import 'mthds/protocol';
4
6
 
5
7
  /**
6
8
  * Ambient state that any field in the tree may need, threaded unchanged through
@@ -114,6 +116,25 @@ interface ListFieldProps {
114
116
  * own remove control; "Add" appends a fresh entry. The element renders through
115
117
  * the same `FieldRenderer`, so a list of documents gets dropzones and a list of
116
118
  * structured concepts gets nested cards - no special-casing per element type.
119
+ *
120
+ * **A row is a thing, not a slot.** Its React key is generated when the row
121
+ * appears and travels with it, so removing an earlier row moves the later ones
122
+ * instead of renumbering them into each other - which is what used to drop a
123
+ * half-typed value or a pending preview onto the neighbouring row. The row's
124
+ * field ID stays POSITIONAL (`cvs.1`), because that ID is a PATH: a host writes
125
+ * an upload back with `setValueAtPath(values, id.split('.'), value)`, and
126
+ * `setValueAtPath` is a pure function of the value tree. Handing out an opaque
127
+ * token instead would mean the kernel keeping a live token-to-index registry for
128
+ * that function to consult, which is exactly the kind of hidden state the
129
+ * headless core does not have.
130
+ *
131
+ * The positional ID is why removal is BLOCKED while a file is arriving anywhere
132
+ * in this list - see `listIsBusy`. Together those two are the whole fix: a
133
+ * generated key cannot rescue a write-back either, because the keys live in this
134
+ * control's state and not in the value, so a host that replaces `values`
135
+ * wholesale reconciles by length just as positions do. What a stable identity
136
+ * buys over a position is exactly the reorderings the KERNEL performs, and it
137
+ * performs one.
117
138
  */
118
139
  declare function ListField({ field, value, onChange, id, error, env }: ListFieldProps): react.JSX.Element;
119
140
 
@@ -249,6 +270,8 @@ interface FieldStrings {
249
270
  addItem: string;
250
271
  removeItemAria: (index: number) => string;
251
272
  itemsCount: (count: number) => string;
273
+ /** The same badge for a list the method gave an exact count (`Concept[N]`). */
274
+ itemsCountOf: (count: number, total: number) => string;
252
275
  uploading: string;
253
276
  dropToUpload: string;
254
277
  dropOrBrowse: string;
@@ -273,6 +296,24 @@ declare function FieldStringsProvider({ strings, children, }: {
273
296
  /** The strings the nearest provider supplies (English defaults when none does). */
274
297
  declare function useFieldStrings(): FieldStrings;
275
298
 
299
+ declare function FieldDomIdProvider({ prefix, children, }: {
300
+ /**
301
+ * Omit to mint one from `useId`; `''` writes the path unprefixed. An explicit
302
+ * prefix must be unique in the document - one provider, one form.
303
+ */
304
+ prefix?: string;
305
+ children: ReactNode;
306
+ }): react.JSX.Element;
307
+ /**
308
+ * The DOM id for a field path - the ONE place a path becomes an attribute.
309
+ *
310
+ * Called once per control so the `<label for>` and the control's `id` are the
311
+ * same string by construction: the pairing cannot drift, because there is only
312
+ * one call. `useId` runs unconditionally (hooks must) and is simply unused when
313
+ * a provider supplies the prefix.
314
+ */
315
+ declare function useFieldDomId(path: string): string;
316
+
276
317
  /**
277
318
  * How a field's LABEL chrome should read. The control itself never changes.
278
319
  *
@@ -303,4 +344,4 @@ declare function useFieldPresentation(): FieldPresentation;
303
344
  */
304
345
  declare function humanizeFieldName(name: string): string;
305
346
 
306
- export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EnumField, type FieldEnv, type FieldPresentation, FieldPresentationProvider, FieldRenderer, type FieldRendererProps, FieldShell, type FieldStrings, FieldStringsProvider, type FileValue, ImageField, ListField, NumberField, ObjectField, OptionalToggle, ProseField, TextField, UnknownField, fieldControlClass, humanizeFieldName, useFieldPresentation, useFieldStrings };
347
+ export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EnumField, FieldDomIdProvider, type FieldEnv, type FieldPresentation, FieldPresentationProvider, FieldRenderer, type FieldRendererProps, FieldShell, type FieldStrings, FieldStringsProvider, type FileValue, ImageField, ListField, NumberField, ObjectField, OptionalToggle, ProseField, TextField, UnknownField, fieldControlClass, humanizeFieldName, useFieldDomId, useFieldPresentation, useFieldStrings };