@pipelex/mthds-form 0.4.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 (56) hide show
  1. package/README.md +15 -6
  2. package/dist/{chunk-6XD6UZSU.js → chunk-25I6JDIK.js} +2 -2
  3. package/dist/chunk-25I6JDIK.js.map +1 -0
  4. package/dist/{chunk-WXBSM2CE.js → chunk-5Q2W6HBA.js} +8 -8
  5. package/dist/chunk-5Q2W6HBA.js.map +1 -0
  6. package/dist/{chunk-7BMOPXDY.js → chunk-GCXV76WU.js} +17 -9
  7. package/dist/chunk-GCXV76WU.js.map +1 -0
  8. package/dist/chunk-IIJRJGZQ.js +128 -0
  9. package/dist/chunk-IIJRJGZQ.js.map +1 -0
  10. package/dist/{chunk-XEPD5VJQ.js → chunk-IM2AHWBR.js} +8 -8
  11. package/dist/chunk-IM2AHWBR.js.map +1 -0
  12. package/dist/{chunk-KFZ5MZOM.js → chunk-LBFZXYCY.js} +3 -3
  13. package/dist/{chunk-KFZ5MZOM.js.map → chunk-LBFZXYCY.js.map} +1 -1
  14. package/dist/{chunk-O4O3QNVC.js → chunk-OD3VYXUY.js} +13 -3
  15. package/dist/chunk-OD3VYXUY.js.map +1 -0
  16. package/dist/{chunk-HARYEWNG.js → chunk-XYQ7NXQZ.js} +2 -2
  17. package/dist/chunk-XYQ7NXQZ.js.map +1 -0
  18. package/dist/chunk-YEVVR6FE.js +66 -0
  19. package/dist/chunk-YEVVR6FE.js.map +1 -0
  20. package/dist/core/contracts.d.ts +27 -56
  21. package/dist/core/contracts.js +1 -1
  22. package/dist/core/derive.d.ts +46 -11
  23. package/dist/core/derive.js +3 -4
  24. package/dist/core/descriptor.d.ts +70 -26
  25. package/dist/core/descriptor.js +1 -1
  26. package/dist/core/gate.d.ts +1 -1
  27. package/dist/core/gate.js +7 -8
  28. package/dist/core/gating-fields.d.ts +38 -0
  29. package/dist/core/gating-fields.js +6 -0
  30. package/dist/core/gating-fields.js.map +1 -0
  31. package/dist/core/index.d.ts +4 -3
  32. package/dist/core/index.js +10 -10
  33. package/dist/core/normalize-schema.js +2 -2
  34. package/dist/core/readiness.d.ts +2 -0
  35. package/dist/core/schema-utils.d.ts +13 -1
  36. package/dist/core/schema-utils.js +1 -1
  37. package/dist/core/values.d.ts +10 -3
  38. package/dist/core/values.js +6 -7
  39. package/dist/core/wire-format.js +3 -4
  40. package/dist/react/index.d.ts +21 -1
  41. package/dist/react/index.js +41 -19
  42. package/dist/react/index.js.map +1 -1
  43. package/package.json +3 -1
  44. package/dist/chunk-6XD6UZSU.js.map +0 -1
  45. package/dist/chunk-7BMOPXDY.js.map +0 -1
  46. package/dist/chunk-HARYEWNG.js.map +0 -1
  47. package/dist/chunk-NMT7BRLO.js +0 -40
  48. package/dist/chunk-NMT7BRLO.js.map +0 -1
  49. package/dist/chunk-O4O3QNVC.js.map +0 -1
  50. package/dist/chunk-VMLXO5JS.js +0 -132
  51. package/dist/chunk-VMLXO5JS.js.map +0 -1
  52. package/dist/chunk-WXBSM2CE.js.map +0 -1
  53. package/dist/chunk-XEPD5VJQ.js.map +0 -1
  54. package/dist/core/native-concepts.d.ts +0 -78
  55. package/dist/core/native-concepts.js +0 -3
  56. package/dist/core/native-concepts.js.map +0 -1
@@ -89,6 +89,16 @@ function resolveSchemaIndirection(schema, defs) {
89
89
  return current;
90
90
  }
91
91
  }
92
+ var MAX_INDIRECTION_HOPS = 16;
93
+ function resolveSchemaNode(schema, defs) {
94
+ let current = schema;
95
+ for (let hop = 0; hop < MAX_INDIRECTION_HOPS; hop++) {
96
+ const next = collapseNullable(derefSchema(current, defs));
97
+ if (next === current) return current;
98
+ current = next;
99
+ }
100
+ return current;
101
+ }
92
102
  function collectSchemaDefs(schema, into, options) {
93
103
  for (const [key, value] of Object.entries(schema)) {
94
104
  if (key === "$defs" && value && typeof value === "object" && !Array.isArray(value)) {
@@ -141,6 +151,6 @@ function hoistDefsToRoot(schema) {
141
151
  return stripped;
142
152
  }
143
153
 
144
- export { collapseNullable, collectSchemaDefs, derefSchema, flattenAnyOf, hoistDefsToRoot, resolveSchemaIndirection, schemaTypeOf };
145
- //# sourceMappingURL=chunk-O4O3QNVC.js.map
146
- //# sourceMappingURL=chunk-O4O3QNVC.js.map
154
+ export { collapseNullable, collectSchemaDefs, derefSchema, flattenAnyOf, hoistDefsToRoot, resolveSchemaIndirection, resolveSchemaNode, schemaTypeOf };
155
+ //# sourceMappingURL=chunk-OD3VYXUY.js.map
156
+ //# sourceMappingURL=chunk-OD3VYXUY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/schema-utils.ts"],"names":[],"mappings":"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","file":"chunk-OD3VYXUY.js","sourcesContent":["/**\n * The ONE place JSON-Schema plumbing lives for the run form kernel: nullable\n * `anyOf` collapsing, `$defs` collection/hoisting, `$ref` resolution, and type\n * extraction. Consolidated from three drifted private copies (`field-model.ts`,\n * `normalize-schema.ts`, `input-format.ts`) during the form-kernel extraction.\n *\n * Two collapse engines are exported ON PURPOSE - their semantics genuinely\n * differ, and each caller's observable behavior is characterized and preserved\n * (see `schema-utils-characterization.test.ts`):\n *\n * - `collapseNullable` (the field mapper's rule): collapse ONLY when exactly one\n * non-null branch remains, dropping the null. A multi-branch union stays an\n * `anyOf` and maps to the raw-JSON escape hatch.\n * - `flattenAnyOf` (the RJSF/ajv rule): a union of simple primitives becomes a\n * `type` ARRAY (null preserved as a type entry - ajv validates `type:\n * [\"number\",\"null\"]` natively); a 2-branch nullable non-primitive drops the\n * null branch.\n *\n * Whether these converge (and which semantics win) is an M1 question, decided\n * when the descriptor derivation moves server-side - not silently here.\n */\n\nexport type JsonSchema = Record<string, unknown>;\n\n// ─── anyOf collapsing ────────────────────────────────────────────────────────\n\n/** Pydantic emits `anyOf: [T, {type:'null'}]` for Optional[T]; collapse to T.\n * Collapses ONLY when exactly one non-null branch remains; the branch merges\n * OVER the outer schema (its constraints win). */\nexport function collapseNullable(schema: JsonSchema): JsonSchema {\n const anyOf = schema.anyOf;\n if (!Array.isArray(anyOf)) return schema;\n const branches = anyOf as JsonSchema[];\n const nonNull = branches.filter((b) => b?.type !== 'null');\n if (nonNull.length === 1) return { ...stripAnyOf(schema), ...nonNull[0] };\n return schema;\n}\n\nconst PRIMITIVE_TYPES = new Set(['string', 'number', 'integer', 'boolean', 'null']);\n\n/**\n * An `anyOf` entry that is a simple primitive with no constraints:\n * `{type: \"string\"}` is simple, `{type: \"string\", maxLength: 10}` is not.\n */\nfunction isSimplePrimitive(schema: JsonSchema): boolean {\n const type = schema.type;\n if (typeof type !== 'string' || !PRIMITIVE_TYPES.has(type)) return false;\n const allowedKeys = new Set(['type', 'default', 'description', 'title']);\n return Object.keys(schema).every((key) => allowedKeys.has(key));\n}\n\n/**\n * The RJSF/ajv collapse: a union of simple primitives flattens to a `type`\n * array (`anyOf: [{string},{null}]` → `type: [\"string\",\"null\"]`, which RJSF\n * renders as a plain input instead of a sub-schema picker); an exactly-two\n * `[T, {null}]` union of anything else drops the null branch and merges T over\n * the outer schema. Anything else passes through unchanged.\n */\nexport function flattenAnyOf(schema: JsonSchema): JsonSchema {\n const anyOf = schema.anyOf;\n if (!Array.isArray(anyOf)) return schema;\n\n const branches = anyOf as JsonSchema[];\n\n // Case 1: all branches are simple primitives → type array.\n if (\n branches.every(\n (branch) => typeof branch === 'object' && branch !== null && isSimplePrimitive(branch),\n )\n ) {\n const types: string[] = [];\n let mergedDefault: unknown = undefined;\n for (const branch of branches) {\n types.push(branch.type as string);\n if (branch.default !== undefined) mergedDefault = branch.default;\n }\n const { anyOf: _, ...rest } = schema;\n const result: JsonSchema = { ...rest, type: types };\n if (mergedDefault !== undefined && result.default === undefined) {\n result.default = mergedDefault;\n }\n return result;\n }\n\n // Case 2: exactly [T, {type: \"null\"}] - nullable non-primitive → drop null.\n if (branches.length === 2) {\n const nullBranch = branches.find(\n (b) => typeof b === 'object' && b !== null && b.type === 'null',\n );\n const otherBranch = branches.find((b) => b !== nullBranch);\n if (nullBranch && otherBranch && typeof otherBranch === 'object') {\n const { anyOf: _, ...rest } = schema;\n return { ...rest, ...otherBranch };\n }\n }\n\n return schema;\n}\n\nfunction stripAnyOf(schema: JsonSchema): JsonSchema {\n const { anyOf: _drop, ...rest } = schema;\n return rest;\n}\n\n// ─── Type & $ref helpers ─────────────────────────────────────────────────────\n\n/** The schema's non-null type: a bare `type` string, or the first non-null\n * entry of a `type` array (`[\"number\",\"null\"]` → `\"number\"`). */\nexport function schemaTypeOf(schema: JsonSchema): string | undefined {\n const t = schema.type;\n if (typeof t === 'string') return t;\n if (Array.isArray(t)) return t.find((x) => x !== 'null') as string | undefined;\n return undefined;\n}\n\n/** Resolve `{ $ref: \"#/$defs/Foo\" }` against a collected `$defs` map. Sibling\n * keys on the referencing schema override the definition's. */\nexport function derefSchema(schema: JsonSchema, defs: Record<string, JsonSchema>): JsonSchema {\n const ref = schema.$ref;\n if (typeof ref === 'string' && ref.startsWith('#/$defs/')) {\n const resolved = defs[ref.slice('#/$defs/'.length)];\n if (resolved) return { ...resolved, ...stripRef(schema) };\n }\n return schema;\n}\n\nfunction stripRef(schema: JsonSchema): JsonSchema {\n const { $ref: _drop, ...rest } = schema;\n return rest;\n}\n\n/**\n * Resolve the indirection layers pydantic puts in front of a value schema - a\n * `#/$defs/...` ref, a nullable `anyOf: [T, {null}]` wrapper, or a stack of\n * both - until a plain schema remains. This is the resolver `healStringWrappers`\n * and `pruneEmptyOptionals` apply before reading `type`. Unlike `derefSchema`\n * it REPLACES the schema with the definition (no sibling merge) - exactly what\n * the callers did inline.\n */\nexport function resolveSchemaIndirection(\n schema: JsonSchema,\n defs: Record<string, JsonSchema>,\n): JsonSchema {\n let current = schema;\n for (;;) {\n const ref = current.$ref;\n if (typeof ref === 'string' && ref.startsWith('#/$defs/')) {\n const resolved = defs[ref.slice('#/$defs/'.length)];\n if (resolved) {\n current = resolved;\n continue;\n }\n }\n if (Array.isArray(current.anyOf)) {\n const nonNull = (current.anyOf as JsonSchema[]).filter((b) => b?.type !== 'null');\n if (nonNull.length === 1) {\n current = nonNull[0]!;\n continue;\n }\n }\n return current;\n }\n}\n\n/** How many indirection layers `resolveSchemaNode` unwraps before concluding\n * the chain is cyclic and returning what it has. Pydantic stacks at most a\n * handful (`$ref` inside a nullable `anyOf`); real chains end long before. */\nconst MAX_INDIRECTION_HOPS = 16;\n\n/**\n * Resolve one schema node to its fixpoint under `derefSchema` +\n * `collapseNullable`, so a nullable REFERENCE - pydantic's `anyOf: [{$ref},\n * {type:'null'}]` for an Optional concept-typed field - resolves the same way\n * a bare `$ref` or an inline nullable does. One pass of either helper cannot:\n * collapsing the nullable surfaces a `$ref` that was inside a branch, and\n * dereferencing can surface a nullable the definition holds. Sibling keys\n * still merge over the definition (`derefSchema`'s semantics - unlike\n * `resolveSchemaIndirection`, which replaces). Identity is the fixpoint test:\n * both helpers return the schema unchanged when they have nothing to do.\n */\nexport function resolveSchemaNode(\n schema: JsonSchema,\n defs: Record<string, JsonSchema>,\n): JsonSchema {\n let current = schema;\n for (let hop = 0; hop < MAX_INDIRECTION_HOPS; hop++) {\n const next = collapseNullable(derefSchema(current, defs));\n if (next === current) return current;\n current = next;\n }\n return current;\n}\n\n// ─── $defs collection ────────────────────────────────────────────────────────\n\nexport interface CollectDefsOptions {\n /**\n * Whether the walk descends into JSON arrays (`anyOf`/`allOf` branches,\n * tuple `items`). The field mapper always has (so a def declared inside an\n * `anyOf` branch resolves); the wire-format heal path historically has NOT -\n * both behaviors are characterized, and each caller states its mode\n * explicitly until M1 retires the difference.\n */\n traverseArrays: boolean;\n}\n\n/**\n * Collect every nested `$defs` entry into one flat map so `#/$defs/...` refs\n * resolve from anywhere. First definition of a name wins. The input schema is\n * NOT modified (see `hoistDefsToRoot` for the stripping variant).\n */\nexport function collectSchemaDefs(\n schema: JsonSchema,\n into: Record<string, JsonSchema>,\n options: CollectDefsOptions,\n): Record<string, JsonSchema> {\n for (const [key, value] of Object.entries(schema)) {\n if (key === '$defs' && value && typeof value === 'object' && !Array.isArray(value)) {\n for (const [name, def] of Object.entries(value as Record<string, JsonSchema>)) {\n if (!(name in into) && def && typeof def === 'object') {\n into[name] = def;\n collectSchemaDefs(def, into, options);\n }\n }\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n collectSchemaDefs(value as JsonSchema, into, options);\n } else if (options.traverseArrays && Array.isArray(value)) {\n // Any object item recurses - including a nested array, whose indices then\n // walk as keys (matches the historical field-model walker exactly).\n for (const item of value) {\n if (item && typeof item === 'object') {\n collectSchemaDefs(item as JsonSchema, into, options);\n }\n }\n }\n }\n return into;\n}\n\n// ─── $defs hoisting (the RJSF wrapper-schema case) ──────────────────────────\n\n/**\n * Walk a schema, collect every `$defs` entry into `collected`, and return the\n * schema with those nested `$defs` STRIPPED. Cycle-safe: a def name is claimed\n * (placeholder) before its body is walked.\n */\nfunction walkAndCollectDefs(schema: JsonSchema, collected: JsonSchema): JsonSchema {\n const out: JsonSchema = {};\n for (const [key, value] of Object.entries(schema)) {\n if (key === '$defs' && value && typeof value === 'object' && !Array.isArray(value)) {\n for (const [defName, defValue] of Object.entries(value as JsonSchema)) {\n if (!(defName in collected) && defValue && typeof defValue === 'object') {\n collected[defName] = {};\n collected[defName] = walkAndCollectDefs(defValue as JsonSchema, collected);\n }\n }\n // Don't copy `$defs` into out - it's hoisted to the root.\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n out[key] = walkAndCollectDefs(value as JsonSchema, collected);\n } else if (Array.isArray(value)) {\n out[key] = value.map((item) =>\n item && typeof item === 'object' && !Array.isArray(item)\n ? walkAndCollectDefs(item as JsonSchema, collected)\n : item,\n );\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\n/**\n * Hoist every nested `$defs` to the schema root, so a wrapper schema composed\n * from several pydantic sub-schemas keeps its `#/$defs/...` refs resolvable.\n * (Pydantic emits one `$defs` per model; composing models as properties of a\n * wrapper object leaves those maps below the root the refs point to.)\n */\nexport function hoistDefsToRoot(schema: JsonSchema): JsonSchema {\n const collected: JsonSchema = {};\n const stripped = walkAndCollectDefs(schema, collected);\n if (Object.keys(collected).length > 0) {\n stripped.$defs = collected;\n }\n return stripped;\n}\n"]}
@@ -27,5 +27,5 @@ function inputMustBeFilled(input) {
27
27
  }
28
28
 
29
29
  export { buildPipeRef, getPipeIOContract, inputMustBeFilled, isFixedCountInput, isOptionalInput, isPluralInput };
30
- //# sourceMappingURL=chunk-HARYEWNG.js.map
31
- //# sourceMappingURL=chunk-HARYEWNG.js.map
30
+ //# sourceMappingURL=chunk-XYQ7NXQZ.js.map
31
+ //# sourceMappingURL=chunk-XYQ7NXQZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/contracts.ts"],"names":[],"mappings":";;;AA4DO,SAAS,YAAA,CAAa,QAAgB,QAAA,EAA0B;AACrE,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA;AAC9B;AAkBO,SAAS,iBAAA,CACd,eAAA,EACA,MAAA,EACA,QAAA,EAC4B;AAC5B,EAAA,IAAI,CAAC,eAAA,IAAmB,CAAC,QAAA,EAAU,OAAO,MAAA;AAC1C,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAM,QAAQ,OAAA,CAAQ,eAAA,EAAiB,YAAA,CAAa,MAAA,EAAQ,QAAQ,CAAC,CAAA;AACrE,IAAA,IAAI,OAAO,OAAO,KAAA;AAAA,EACpB;AACA,EAAA,OAAO,OAAA,CAAQ,iBAAiB,QAAQ,CAAA;AAC1C;AAWO,SAAS,gBAAgB,KAAA,EAAmC;AACjE,EAAA,OAAO,MAAM,QAAA,KAAa,UAAA;AAC5B;AAcO,SAAS,cAAc,KAAA,EAAmC;AAC/D,EAAA,OAAQ,KAAA,CAAM,aAAgD,IAAA,KAAS,OAAA;AACzE;AASO,SAAS,kBAAkB,KAAA,EAAmC;AACnE,EAAA,OAAO,MAAM,YAAA,KAAiB,OAAA;AAChC;AAqBO,SAAS,kBAAkB,KAAA,EAAmC;AACnE,EAAA,IAAI,eAAA,CAAgB,KAAK,CAAA,EAAG,OAAO,KAAA;AACnC,EAAA,OAAO,CAAC,aAAA,CAAc,KAAK,CAAA,IAAK,kBAAkB,KAAK,CAAA;AACzD","file":"chunk-XYQ7NXQZ.js","sourcesContent":["/**\n * The `pipe_io_contracts` types and the kernel's gating predicates over them.\n *\n * The types are the STANDARD'S, imported from `mthds/protocol` and re-exported\n * under the names this package has always exported. `pipe_io_contracts` is an\n * MTHDS artifact - the standard specifies it (the \"Pipe I/O Contracts\" page)\n * and its TypeScript client declares it - so this module states no shape of its\n * own any more. What it keeps is the half that is genuinely the kernel's: the\n * predicates that turn a declared slot into a gating answer, and the lookup\n * that tolerates both key conventions a real payload arrives with.\n *\n * `mthds` is a TYPES-ONLY peer dependency: every import here is `import type`,\n * so nothing survives into `dist/` and no consumer ships the standard's CLI.\n * `scripts/assert-bundle.mjs` holds that on the built graph and lint holds it\n * on the source. See docs/dependency-budget.md.\n *\n * The map itself is keyed by namespaced pipe ref (`<domain>.<pipe_code>`),\n * NOT bare pipe code - use `getPipeIOContract` to look entries up by the bare\n * codes the UI tracks.\n */\n\nimport type {\n IOMultiplicity,\n PipeInputContract,\n PipeIOContract,\n PipeIOContracts,\n PipeOutputContract,\n PresenceMarker,\n} from 'mthds/protocol';\n\nimport { ownProp } from './own-property';\n\nexport type {\n IOMultiplicity,\n PipeInputContract,\n PipeIOContract,\n PipeIOContracts,\n PipeOutputContract,\n};\n\n/**\n * The authored presence marker on an input slot, verbatim - the standard's\n * `PresenceMarker` under the name this package exports.\n *\n * - `plain` - no marker: the caller must supply the input.\n * - `optional` (`?`) - the caller may omit it and the pipe handles the absence\n * itself.\n * - `force` (`!`) - a use-site assertion that the value IS present. It must be\n * supplied, exactly like `plain`; the distinction is the authored assertion,\n * which lint and graph surfaces read and a form does not.\n *\n * Kept as an alias rather than renamed because a consumer imports it by this\n * name; a host that would rather read the standard's spelling has `mthds`\n * installed through this package's peer and can import `PresenceMarker`\n * straight from `mthds/protocol`.\n */\nexport type InputPresence = PresenceMarker;\n\n/** Namespaced pipe ref (`<domain>.<pipe_code>`) - the identity convention the\n * validate artifacts key on. */\nexport function buildPipeRef(domain: string, pipeCode: string): string {\n return `${domain}.${pipeCode}`;\n}\n\n/**\n * Look up a pipe's IO contract by pipe code, tolerant of both key conventions.\n *\n * The map may be keyed by namespaced `pipe_ref` (`<domain>.<code>`, the\n * canonical convention) OR by bare pipe code - the current hosted `/validate`\n * keys `pipe_structures` by bare code, and methods persisted before the\n * pipe_ref re-keying also carry bare-code keys. Try the namespaced ref first\n * (when the caller supplies a domain), then fall back to the bare code.\n * Returns `undefined` (never crashes) when neither matches.\n *\n * Both lookups read OWN properties only. A bare index returned the inherited\n * `Object` constructor for a pipe code named `constructor` (or `toString`, …) -\n * a truthy non-contract that sailed straight through the `if (!contract)` guard\n * hosts are shown writing, so a pipe that does not exist rendered as one taking\n * no inputs instead of reaching the host's not-found path. See `ownProp`.\n */\nexport function getPipeIOContract(\n pipeIoContracts: PipeIOContracts | null | undefined,\n domain: string | null | undefined,\n pipeCode: string | null | undefined,\n): PipeIOContract | undefined {\n if (!pipeIoContracts || !pipeCode) return undefined;\n if (domain) {\n const byRef = ownProp(pipeIoContracts, buildPipeRef(domain, pipeCode));\n if (byRef) return byRef;\n }\n return ownProp(pipeIoContracts, pipeCode);\n}\n\n/**\n * True when the method declared the input omittable (`?`).\n *\n * The ONE reader of `presence` in the package: everything that asks \"may this\n * be absent?\" - the descriptor's `required`, the gate's `required` list, the\n * payload builders that omit an unfilled input - goes through here, so a\n * marker the wire adds later cannot mean one thing on one surface and another\n * elsewhere. `force` (`!`) reads as NOT optional, like `plain`.\n */\nexport function isOptionalInput(input: PipeInputContract): boolean {\n return input.presence === 'optional';\n}\n\n/**\n * True when the input carries a plural concept (`Image[]`, `Image[3]`): its\n * JSON Schema is an array wrapper.\n *\n * Deliberately a SCHEMA test rather than a read of the declared `multiplicity`,\n * even though the wire now states both. The schema is what `buildRunFields`\n * maps and therefore what the user is shown; a predicate reading the other\n * field could disagree with the rendered control, which is the exact failure\n * mode `mustBeFilled` (readiness.ts) exists to prevent. The two agree by\n * construction upstream - the engine wraps the schema in an array exactly when\n * it reports `variable` or `fixed`.\n */\nexport function isPluralInput(input: PipeInputContract): boolean {\n return (input.json_schema as { type?: unknown } | undefined)?.type === 'array';\n}\n\n/**\n * True when the slot declares an EXACT number of items (`Concept[N]`), whose\n * count is `item_count`.\n *\n * The distinction that matters to the gate: a variable list can legitimately be\n * empty, a fixed one cannot.\n */\nexport function isFixedCountInput(input: PipeInputContract): boolean {\n return input.multiplicity === 'fixed';\n}\n\n/**\n * True when the user MUST put a value in before a run can start.\n *\n * Two declarations relieve an input of that duty, for different reasons:\n * - **optional (`?`)** - the method itself states the caller may omit it;\n * - **variable-plural (`[]`)** - the empty list is a legitimate value and the\n * language cannot declare \"at least one\", so gating on it would invent a\n * constraint the method never made.\n *\n * Everything else gates: `plain`, `force` (`!`), and a **fixed-count** list\n * (`[N]`), which is a list whose empty form the method has explicitly ruled out.\n * This is the derivation the input-form descriptor spec states for `gating`.\n *\n * Emptiness is all this predicate answers for - WHETHER a slot gates, never how\n * much it needs. A fixed-count list that holds fewer items than it declares\n * gates here and is refused by `fieldFilled`, which reads the declared count off\n * the descriptor's `itemCount`, and by the gate's ajv pass on the schema's\n * `minItems`. All three read the one number the method stated.\n */\nexport function inputMustBeFilled(input: PipeInputContract): boolean {\n if (isOptionalInput(input)) return false;\n return !isPluralInput(input) || isFixedCountInput(input);\n}\n"]}
@@ -0,0 +1,66 @@
1
+ import { collectSchemaDefs, collapseNullable, derefSchema, schemaTypeOf } from './chunk-OD3VYXUY.js';
2
+ import { isOptionalInput, inputMustBeFilled } from './chunk-XYQ7NXQZ.js';
3
+
4
+ // src/core/gating-fields.ts
5
+ function numOrUndef(v) {
6
+ return typeof v === "number" ? v : void 0;
7
+ }
8
+ function gatingNode(name, rawSchema, required, defs, active) {
9
+ let schema = rawSchema;
10
+ let path = active;
11
+ for (; ; ) {
12
+ const ref = schema.$ref;
13
+ if (typeof ref === "string") {
14
+ if (path.has(ref)) return { name, required, kind: "unknown" };
15
+ path = new Set(path).add(ref);
16
+ }
17
+ const next = collapseNullable(derefSchema(schema, defs));
18
+ if (next === schema) break;
19
+ schema = next;
20
+ }
21
+ const type = schemaTypeOf(schema);
22
+ if (type === "array") {
23
+ const item = gatingNode(name, schema.items ?? {}, true, defs, path);
24
+ const itemCount = numOrUndef(schema.minItems);
25
+ const maxItemCount = numOrUndef(schema.maxItems);
26
+ return {
27
+ name,
28
+ required,
29
+ kind: "list",
30
+ item,
31
+ ...itemCount === void 0 ? {} : { itemCount },
32
+ ...maxItemCount === void 0 ? {} : { maxItemCount }
33
+ };
34
+ }
35
+ if (type === "object") {
36
+ const props = schema.properties ?? {};
37
+ const req = new Set(Array.isArray(schema.required) ? schema.required : []);
38
+ return {
39
+ name,
40
+ required,
41
+ kind: "object",
42
+ fields: Object.entries(props).map(
43
+ ([childName, childSchema]) => gatingNode(childName, childSchema ?? {}, req.has(childName), defs, path)
44
+ )
45
+ };
46
+ }
47
+ return { name, required, kind: "unknown" };
48
+ }
49
+ function gatingFieldsFromInputs(inputs) {
50
+ return Object.entries(inputs).map(([name, input]) => {
51
+ const defs = {};
52
+ collectSchemaDefs(input.json_schema, defs, { traverseArrays: true });
53
+ const node = gatingNode(
54
+ name,
55
+ input.json_schema ?? {},
56
+ !isOptionalInput(input),
57
+ defs,
58
+ /* @__PURE__ */ new Set()
59
+ );
60
+ return { ...node, gating: inputMustBeFilled(input) };
61
+ });
62
+ }
63
+
64
+ export { gatingFieldsFromInputs };
65
+ //# sourceMappingURL=chunk-YEVVR6FE.js.map
66
+ //# sourceMappingURL=chunk-YEVVR6FE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/gating-fields.ts"],"names":[],"mappings":"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","file":"chunk-YEVVR6FE.js","sourcesContent":["/**\n * The gate's own field tree - structural, schema-derived, and private.\n *\n * `gateRunInputs` re-applies the Run button's emptiness rule with\n * `mustBeFilled`/`fieldFilled`, which read a `RunField` tree. The RENDER tree\n * is built from the wire input-form descriptor - but the gate must not require\n * that artifact: the descriptor is a presentation view, and the workspace\n * doctrine is that a machine consumer never needs it to validate a payload -\n * `json_schema` keeps that job. A server holds contracts; it may never have\n * asked for the view.\n *\n * So the gate walks the contract's `json_schema` into the MINIMAL tree the\n * emptiness predicates read: `object` nodes with their required children,\n * `list` nodes with their declared bounds, and opaque leaves. This is not a\n * revival of the deleted heuristics - it reads exactly the keywords ajv itself\n * enforces (`type`, `properties`, `required`, `items`, `minItems`, `maxItems`)\n * and decides nothing about what a field IS. No kind here ever reaches a\n * renderer: the tree exists for one call inside `gateRunInputs` and is not\n * exported from the package.\n *\n * Agreement with the browser's readiness (which runs over the render tree) is\n * held by construction - both trees take required children and list bounds\n * from the same model the engine derived both artifacts from - and asserted in\n * `gate-agreement.test.ts`, which runs both sides over one table.\n */\n\nimport { inputMustBeFilled, isOptionalInput, type PipeInputContract } from './contracts';\nimport type { RunField } from './descriptor';\nimport {\n collapseNullable,\n collectSchemaDefs,\n derefSchema,\n schemaTypeOf,\n type JsonSchema,\n} from './schema-utils';\n\nfunction numOrUndef(v: unknown): number | undefined {\n return typeof v === 'number' ? v : undefined;\n}\n\nfunction gatingNode(\n name: string,\n rawSchema: JsonSchema,\n required: boolean,\n defs: Record<string, JsonSchema>,\n active: ReadonlySet<string>,\n): RunField {\n // Resolve `$ref` / nullable-`anyOf` layers to a fixpoint - one pass of\n // either helper cannot see through pydantic's Optional concept-typed shape,\n // `anyOf: [{$ref}, {type:'null'}]` - keeping the set of references OPEN ON\n // THIS PATH. A reference met again while still open is a self-referencing\n // model: expanding it has no floor, so the node degrades to the opaque leaf\n // instead of overflowing the stack - the gate returns a verdict, never a\n // throw. Open on this path, not seen anywhere: two siblings referencing one\n // definition are ordinary composition and both expand in full. The degrade\n // is OPEN, and deliberately so: ajv - which resolves recursion natively,\n // bounded by the data - has already enforced PRESENCE of required children\n // at every depth, but presence is all it enforces, so a required scalar\n // that is present-but-blank below the cycle point is caught by neither\n // side. That residual leniency is a recorded follow-up, not a safety\n // property; failing CLOSED here would refuse every legitimate recursive\n // payload, which is the worse defect.\n let schema = rawSchema;\n let path = active;\n for (;;) {\n const ref = schema.$ref;\n if (typeof ref === 'string') {\n if (path.has(ref)) return { name, required, kind: 'unknown' };\n path = new Set(path).add(ref);\n }\n const next = collapseNullable(derefSchema(schema, defs));\n if (next === schema) break;\n schema = next;\n }\n const type = schemaTypeOf(schema);\n\n if (type === 'array') {\n const item = gatingNode(name, (schema.items as JsonSchema) ?? {}, true, defs, path);\n const itemCount = numOrUndef(schema.minItems);\n const maxItemCount = numOrUndef(schema.maxItems);\n return {\n name,\n required,\n kind: 'list',\n item,\n ...(itemCount === undefined ? {} : { itemCount }),\n ...(maxItemCount === undefined ? {} : { maxItemCount }),\n };\n }\n if (type === 'object') {\n const props = (schema.properties as Record<string, JsonSchema>) ?? {};\n const req = new Set(Array.isArray(schema.required) ? (schema.required as string[]) : []);\n return {\n name,\n required,\n kind: 'object',\n fields: Object.entries(props).map(([childName, childSchema]) =>\n gatingNode(childName, childSchema ?? {}, req.has(childName), defs, path),\n ),\n };\n }\n // Everything else is a leaf to the emptiness rule: `fieldFilled` answers it\n // with `isFilled` alone, whatever kind it carries. `unknown` states that\n // honestly.\n return { name, required, kind: 'unknown' };\n}\n\n/**\n * The tree `gateRunInputs`' emptiness re-check runs over, one node per input,\n * with `gating` stamped from the contract - the same `inputMustBeFilled` the\n * Run button's `required` list is built from.\n */\nexport function gatingFieldsFromInputs(inputs: Record<string, PipeInputContract>): RunField[] {\n return Object.entries(inputs).map(([name, input]) => {\n const defs: Record<string, JsonSchema> = {};\n collectSchemaDefs(input.json_schema, defs, { traverseArrays: true });\n const node = gatingNode(\n name,\n (input.json_schema as JsonSchema) ?? {},\n !isOptionalInput(input),\n defs,\n new Set(),\n );\n return { ...node, gating: inputMustBeFilled(input) };\n });\n}\n"]}
@@ -1,16 +1,30 @@
1
+ import { PresenceMarker, PipeIOContracts, PipeIOContract, PipeInputContract } from 'mthds/protocol';
2
+ export { IOMultiplicity, PipeIOContract, PipeIOContracts, PipeInputContract, PipeOutputContract } from 'mthds/protocol';
3
+
1
4
  /**
2
- * The typed `pipe_io_contracts` mirror and its gating predicates.
5
+ * The `pipe_io_contracts` types and the kernel's gating predicates over them.
6
+ *
7
+ * The types are the STANDARD'S, imported from `mthds/protocol` and re-exported
8
+ * under the names this package has always exported. `pipe_io_contracts` is an
9
+ * MTHDS artifact - the standard specifies it (the "Pipe I/O Contracts" page)
10
+ * and its TypeScript client declares it - so this module states no shape of its
11
+ * own any more. What it keeps is the half that is genuinely the kernel's: the
12
+ * predicates that turn a declared slot into a gating answer, and the lookup
13
+ * that tolerates both key conventions a real payload arrives with.
3
14
  *
4
- * Mirrors pipelex's `PipeIOContract` (pipelex/pipeline/pipe_io_contracts.py).
5
- * The SDK deliberately keeps `pipe_io_contracts` opaque, so the kernel is the
6
- * canonical TS home for this shape until the D-track specs it.
15
+ * `mthds` is a TYPES-ONLY peer dependency: every import here is `import type`,
16
+ * so nothing survives into `dist/` and no consumer ships the standard's CLI.
17
+ * `scripts/assert-bundle.mjs` holds that on the built graph and lint holds it
18
+ * on the source. See docs/dependency-budget.md.
7
19
  *
8
20
  * The map itself is keyed by namespaced pipe ref (`<domain>.<pipe_code>`),
9
21
  * NOT bare pipe code - use `getPipeIOContract` to look entries up by the bare
10
22
  * codes the UI tracks.
11
23
  */
24
+
12
25
  /**
13
- * The authored presence marker on an input slot, verbatim.
26
+ * The authored presence marker on an input slot, verbatim - the standard's
27
+ * `PresenceMarker` under the name this package exports.
14
28
  *
15
29
  * - `plain` - no marker: the caller must supply the input.
16
30
  * - `optional` (`?`) - the caller may omit it and the pipe handles the absence
@@ -18,56 +32,13 @@
18
32
  * - `force` (`!`) - a use-site assertion that the value IS present. It must be
19
33
  * supplied, exactly like `plain`; the distinction is the authored assertion,
20
34
  * which lint and graph surfaces read and a form does not.
21
- */
22
- type InputPresence = 'plain' | 'optional' | 'force';
23
- /**
24
- * How many items a slot carries.
25
35
  *
26
- * - `single` - one item. `Concept[1]` is single too: no list framing.
27
- * - `variable` (`Concept[]`) - a list of any length, the empty list included.
28
- * - `fixed` (`Concept[N]`, N > 1) - a list of exactly `item_count` items.
36
+ * Kept as an alias rather than renamed because a consumer imports it by this
37
+ * name; a host that would rather read the standard's spelling has `mthds`
38
+ * installed through this package's peer and can import `PresenceMarker`
39
+ * straight from `mthds/protocol`.
29
40
  */
30
- type IOMultiplicity = 'single' | 'variable' | 'fixed';
31
- interface PipeInputContract {
32
- concept_ref: string;
33
- /** The declared presence marker - see `InputPresence`. */
34
- presence: InputPresence;
35
- /** The declared multiplicity - see `IOMultiplicity`. */
36
- multiplicity: IOMultiplicity;
37
- /**
38
- * The exact item count, non-null exactly when `multiplicity` is `fixed`. The
39
- * slot is always on the wire, `null` off the fixed arm.
40
- */
41
- item_count: number | null;
42
- /**
43
- * The input's JSON Schema. A plural slot (`variable` or `fixed`) is an array
44
- * wrapper around the item schema, and a `fixed` one additionally carries
45
- * `minItems` / `maxItems` set to `item_count` - which is what makes the gate's
46
- * ajv pass enforce the declared count without the kernel restating it.
47
- */
48
- json_schema: Record<string, unknown>;
49
- }
50
- interface PipeOutputContract {
51
- concept_ref: string;
52
- multiplicity: IOMultiplicity;
53
- /** As on the input: non-null exactly on the `fixed` arm, always on the wire. */
54
- item_count: number | null;
55
- /**
56
- * `true` when the output is declared optional (`?`) - the pipe may resolve it
57
- * as a recorded absence instead of a value.
58
- *
59
- * Deliberately still a boolean while the INPUT side carries three-valued
60
- * `presence`: output presence is genuinely two-valued, because `!` is
61
- * rejected on an output. The asymmetry is the wire's, not ours.
62
- */
63
- optional: boolean;
64
- }
65
- interface PipeIOContract {
66
- inputs: Record<string, PipeInputContract>;
67
- output: PipeOutputContract;
68
- }
69
- /** The `pipe_io_contracts` map: namespaced `pipe_ref` → IO contract. */
70
- type PipeIOContracts = Record<string, PipeIOContract>;
41
+ type InputPresence = PresenceMarker;
71
42
  /** Namespaced pipe ref (`<domain>.<pipe_code>`) - the identity convention the
72
43
  * validate artifacts key on. */
73
44
  declare function buildPipeRef(domain: string, pipeCode: string): string;
@@ -107,8 +78,8 @@ declare function isOptionalInput(input: PipeInputContract): boolean;
107
78
  * maps and therefore what the user is shown; a predicate reading the other
108
79
  * field could disagree with the rendered control, which is the exact failure
109
80
  * mode `mustBeFilled` (readiness.ts) exists to prevent. The two agree by
110
- * construction upstream - pipelex wraps the schema in an array exactly when it
111
- * reports `variable` or `fixed`.
81
+ * construction upstream - the engine wraps the schema in an array exactly when
82
+ * it reports `variable` or `fixed`.
112
83
  */
113
84
  declare function isPluralInput(input: PipeInputContract): boolean;
114
85
  /**
@@ -140,4 +111,4 @@ declare function isFixedCountInput(input: PipeInputContract): boolean;
140
111
  */
141
112
  declare function inputMustBeFilled(input: PipeInputContract): boolean;
142
113
 
143
- export { type IOMultiplicity, type InputPresence, type PipeIOContract, type PipeIOContracts, type PipeInputContract, type PipeOutputContract, buildPipeRef, getPipeIOContract, inputMustBeFilled, isFixedCountInput, isOptionalInput, isPluralInput };
114
+ export { type InputPresence, buildPipeRef, getPipeIOContract, inputMustBeFilled, isFixedCountInput, isOptionalInput, isPluralInput };
@@ -1,4 +1,4 @@
1
- export { buildPipeRef, getPipeIOContract, inputMustBeFilled, isFixedCountInput, isOptionalInput, isPluralInput } from '../chunk-HARYEWNG.js';
1
+ export { buildPipeRef, getPipeIOContract, inputMustBeFilled, isFixedCountInput, isOptionalInput, isPluralInput } from '../chunk-XYQ7NXQZ.js';
2
2
  import '../chunk-V3OV2XQT.js';
3
3
  //# sourceMappingURL=contracts.js.map
4
4
  //# sourceMappingURL=contracts.js.map
@@ -1,20 +1,55 @@
1
- import { PipeInputContract } from './contracts.js';
1
+ import { PipeInputFormDescriptor, PipeInputContract, InputForm } from 'mthds/protocol';
2
2
  import { RunField } from './descriptor.js';
3
+ import './contracts.js';
3
4
 
4
5
  /**
5
- * The ONE derivation function: `PipeInputContract` map → `RunField[]`.
6
+ * The ONE derivation function: wire input-form descriptor → `RunField[]`.
6
7
  *
7
- * Everything heuristic stays module-private behind `buildRunFields`: the
8
- * native-concept taxonomy, `isDocumentObject`, the depth-based prose/label
9
- * rule, the `accept` strings, list splitting. Nothing here is exported except
10
- * the function itself - so the M1 milestone can swap the source of the
11
- * descriptor (server-derived) without touching consumers.
8
+ * `buildRunFields` maps the standard's per-pipe descriptor STRUCTURALLY onto
9
+ * the kernel's `RunField` shape. It decides nothing: what a field IS - its
10
+ * kind, its constraints, its refinement chain, whether Run gates on it - is
11
+ * stated by the wire, derived by the engine from the resolved library. The
12
+ * heuristics that used to live here (native-concept sets, the url-bearing
13
+ * object test, the depth-based prose/label rule) are deleted, not bypassed;
14
+ * docs/derivation-swap.md is the record of that swap and of every behaviour
15
+ * that moved with it.
16
+ *
17
+ * The contract is still consulted, for exactly two facts the wire deliberately
18
+ * does not carry:
19
+ *
20
+ * - **`contentKey`** - the single property a native scalar's value sits inside
21
+ * on the wire (`TextContent {text}`, `NumberContent {number}`). A renderer
22
+ * holds the bare scalar; the value bridge wraps by this name. It is read off
23
+ * the contract's `json_schema` because it is a fact about the PAYLOAD shape,
24
+ * which is the schema's job - the descriptor describes the form, never the
25
+ * payload.
26
+ * - **Nested list bounds** - the wire puts `item_count` only on a top-level
27
+ * fixed `[N]` slot, but the model behind a structured concept may state
28
+ * `minItems`/`maxItems` on an array property of its own, and ajv enforces
29
+ * them. Reading them off the schema is what keeps readiness from being more
30
+ * permissive than the gate.
31
+ *
32
+ * Both are structural reads (a property count, two keywords ajv itself
33
+ * enforces), not judgements - the mapper stays honest about owning no taxonomy.
12
34
  */
13
35
 
14
36
  /**
15
- * Convert the API's per-input contracts into the runner's field descriptors.
16
- * The map's insertion order is preserved (required-first is the caller's job).
37
+ * Look up a pipe's input-form descriptor by pipe code, tolerant of both key
38
+ * conventions - the exact mirror of `getPipeIOContract`, because the standard
39
+ * keys `input_form` by the same `pipe_ref` set as `pipe_io_contracts` and a
40
+ * host tracks the same bare codes either way.
41
+ */
42
+ declare function getPipeInputForm(inputForm: InputForm | null | undefined, domain: string | null | undefined, pipeCode: string | null | undefined): PipeInputFormDescriptor | undefined;
43
+ /**
44
+ * Map a pipe's wire input-form descriptor onto the kernel's field descriptors,
45
+ * with the pipe's input contracts beside it for the two schema-derived facts.
46
+ *
47
+ * The order is the DESCRIPTOR's - authored input order, the fact the contract's
48
+ * `inputs` map deliberately does not carry. `required`, `presence` and `gating`
49
+ * are taken from the wire verbatim: the standard's top-level type ties
50
+ * `required` to the presence marker, and `gating` is the producer-derived
51
+ * answer `mustBeFilled` reads - the kernel re-derives none of them.
17
52
  */
18
- declare function buildRunFields(inputs: Record<string, PipeInputContract>): RunField[];
53
+ declare function buildRunFields(descriptor: PipeInputFormDescriptor, inputs: Record<string, PipeInputContract>): RunField[];
19
54
 
20
- export { buildRunFields };
55
+ export { buildRunFields, getPipeInputForm };
@@ -1,7 +1,6 @@
1
- export { buildRunFields } from '../chunk-VMLXO5JS.js';
2
- import '../chunk-O4O3QNVC.js';
3
- import '../chunk-HARYEWNG.js';
1
+ export { buildRunFields, getPipeInputForm } from '../chunk-IIJRJGZQ.js';
2
+ import '../chunk-OD3VYXUY.js';
3
+ import '../chunk-XYQ7NXQZ.js';
4
4
  import '../chunk-V3OV2XQT.js';
5
- import '../chunk-NMT7BRLO.js';
6
5
  //# sourceMappingURL=derive.js.map
7
6
  //# sourceMappingURL=derive.js.map
@@ -1,16 +1,20 @@
1
+ import { InputPresence } from './contracts.js';
2
+ import 'mthds/protocol';
3
+
1
4
  /**
2
5
  * The `RunField` descriptor - the consumer-facing currency of the form kernel.
3
6
  *
4
- * A method's inputs arrive from the API as a map of variable name →
5
- * `PipeInputContract` ({ concept_ref, json_schema }). That JSON Schema is rich
6
- * (Pydantic-generated) and not pleasant to render directly. `buildRunFields`
7
- * (in `derive.ts`) normalizes it once into a flat, recursive `RunField[]`
8
- * descriptor that the presentational field components consume - so the
9
- * components never touch raw JSON Schema, and stories can describe any input
10
- * shape declaratively.
7
+ * A method's inputs arrive from the API as two sibling artifacts: the wire
8
+ * input-form descriptor (`input_form`, the ordered presentation view the MTHDS
9
+ * standard specifies) and the `pipe_io_contracts` entry whose `json_schema`
10
+ * stays the validation contract. `buildRunFields` (in `derive.ts`) maps the
11
+ * wire descriptor structurally onto this `RunField[]` shape - so the
12
+ * presentational field components never touch either wire artifact directly,
13
+ * and stories can describe any input shape declaratively.
11
14
  *
12
15
  * Pure module: no React, no API calls. Trivially unit-testable.
13
16
  */
17
+
14
18
  type RunFieldKind = 'text' | 'prose' | 'date' | 'number' | 'boolean' | 'enum' | 'document' | 'image' | 'object' | 'list' | 'unknown';
15
19
  interface RunFieldCommon {
16
20
  /** The variable identifier as written in the method (rendered in mono). */
@@ -19,10 +23,38 @@ interface RunFieldCommon {
19
23
  title?: string;
20
24
  /** The concept this input carries, e.g. `native.Document`, `demo.Invoice`. */
21
25
  conceptRef?: string;
26
+ /**
27
+ * The concept's refinement chain, immediate parent first, walked to its end
28
+ * (`['legal.BaseClause', 'native.Text']`). Straight off the wire descriptor;
29
+ * absent when the concept refines nothing. "Does this refine `native.X`?" is
30
+ * a membership test on this list, never shape sniffing.
31
+ */
32
+ refines?: string[];
22
33
  /** One-line helper text shown under the label. */
23
34
  description?: string;
24
35
  /** Required inputs always show; optional ones can collapse. */
25
36
  required: boolean;
37
+ /**
38
+ * The authored presence marker of the pipe's input slot, verbatim - set on
39
+ * TOP-LEVEL fields only, like `gating`. `required` is its two-valued
40
+ * projection (`presence !== 'optional'`); the marker itself is carried so a
41
+ * renderer can distinguish the authored `!` assertion from a plain slot.
42
+ */
43
+ presence?: InputPresence;
44
+ /**
45
+ * The value applied when the caller omits the field - present only when a
46
+ * default was AUTHORED, never the `null` a schema projection attaches to an
47
+ * optional field. A defaulted field always arrives `required: false`.
48
+ */
49
+ defaultValue?: unknown;
50
+ /** Example values for the field, when the wire carries any. */
51
+ examples?: unknown[];
52
+ /**
53
+ * The node's effective intent hints - the flat `string → string` merge the
54
+ * language defines, carried verbatim. Non-normative: a renderer that ignores
55
+ * them stays correct, and nothing in the kernel's gating reads them.
56
+ */
57
+ hints?: Record<string, string>;
26
58
  /**
27
59
  * Whether Run waits for this input - set on TOP-LEVEL fields only, straight
28
60
  * from `inputMustBeFilled` (see `mustBeFilled` in `readiness.ts` for why this
@@ -45,21 +77,34 @@ interface RunFieldCommon {
45
77
  */
46
78
  contentKey?: string;
47
79
  }
48
- interface TextRunField extends RunFieldCommon {
80
+ /**
81
+ * The constraint slots the two text kinds share, mapped from the wire's
82
+ * `min_length` / `max_length` / `pattern` / `format`. `format` is the open
83
+ * string set carrying schema formats the `date` kind does not absorb
84
+ * (`'time'`, `'uri'`, …); a renderer may hint on it and ignore it safely.
85
+ */
86
+ interface TextConstraints {
87
+ minLength?: number;
88
+ maxLength?: number;
89
+ pattern?: string;
90
+ format?: string;
91
+ }
92
+ interface TextRunField extends RunFieldCommon, TextConstraints {
49
93
  kind: 'text';
50
94
  placeholder?: string;
51
95
  }
52
- interface ProseRunField extends RunFieldCommon {
96
+ interface ProseRunField extends RunFieldCommon, TextConstraints {
53
97
  kind: 'prose';
54
98
  placeholder?: string;
55
99
  }
56
100
  interface DateRunField extends RunFieldCommon {
57
101
  kind: 'date';
58
102
  /**
59
- * True when the backing schema is `format: date-time`: the stored value is a
60
- * full RFC 3339 timestamp (`2026-07-06T00:00:00Z`) so it passes the API's
103
+ * The wire's `datetime` flag - true when the value carries a time of day
104
+ * (the backing schema is `format: date-time`): the stored value is a full
105
+ * RFC 3339 timestamp (`2026-07-06T00:00:00Z`) so it passes the API's
61
106
  * `date-time` validation, even though the control only asks for a day. False
62
- * for a bare `format: date`, where the value stays `YYYY-MM-DD`.
107
+ * for a bare calendar date, where the value stays `YYYY-MM-DD`.
63
108
  */
64
109
  datetime: boolean;
65
110
  }
@@ -90,24 +135,23 @@ interface ListRunField extends RunFieldCommon {
90
135
  /** The element descriptor (its `name` is unused; the index labels items). */
91
136
  item: RunField;
92
137
  /**
93
- * The FEWEST items the slot accepts, when the schema states a lower bound.
94
- * Absent for a variable `Concept[]` list, which demands none.
138
+ * The FEWEST items the slot accepts. Absent for a variable `Concept[]` list,
139
+ * which demands none.
95
140
  *
96
- * Read off the array schema's `minItems` rather than off the contract's
97
- * `item_count`, which states the same number: this exists so `fieldFilled` can
98
- * answer the question **ajv** answers, and `minItems` is the keyword ajv
99
- * reads. pipelex emits `minItems`/`maxItems` = N for a `[N]` slot and neither
100
- * for `[]`, so on a top-level slot the two sources agree by construction.
141
+ * On a top-level slot this is the wire descriptor's `item_count` (present
142
+ * exactly on a fixed `[N]` slot); an engine emits `minItems`/`maxItems` = N
143
+ * on the same slot's schema, so the number `fieldFilled` gates on and the
144
+ * keyword ajv reads agree by construction.
101
145
  *
102
- * A schema may still state a lower bound alone - `mapSchema` recurses, so a
103
- * nested array inside a structured concept reaches here with whatever its
104
- * model declared and no MTHDS multiplicity behind it. Gating on that bound is
105
- * the same rule, and it is the direction that cannot leave readiness more
106
- * permissive than the gate.
146
+ * The wire never puts `item_count` on a NESTED list, but the model behind a
147
+ * structured concept may state `minItems` on an array property of its own -
148
+ * so on nested nodes this is read off the schema, which is the direction
149
+ * that cannot leave readiness more permissive than the gate.
107
150
  */
108
151
  itemCount?: number;
109
152
  /**
110
- * The MOST items the slot accepts, off the schema's `maxItems`.
153
+ * The MOST items the slot accepts, off the schema's `maxItems` (which a
154
+ * fixed `[N]` slot also states as N).
111
155
  *
112
156
  * A separate field because it is a separate fact, and conflating them is a
113
157
  * bug that hides: `itemCount` was read as a minimum by readiness and as a
@@ -125,4 +169,4 @@ type RunField = TextRunField | ProseRunField | DateRunField | NumberRunField | B
125
169
  type ConceptCategory = 'text' | 'date' | 'document' | 'image' | 'number' | 'boolean' | 'choice' | 'structured' | 'list';
126
170
  declare function conceptCategory(field: RunField): ConceptCategory;
127
171
 
128
- export { type BooleanRunField, type ConceptCategory, type DateRunField, type EnumRunField, type FileRunField, type ListRunField, type NumberRunField, type ObjectRunField, type ProseRunField, type RunField, type RunFieldCommon, type RunFieldKind, type TextRunField, type UnknownRunField, conceptCategory };
172
+ export { type BooleanRunField, type ConceptCategory, type DateRunField, type EnumRunField, type FileRunField, type ListRunField, type NumberRunField, type ObjectRunField, type ProseRunField, type RunField, type RunFieldCommon, type RunFieldKind, type TextConstraints, type TextRunField, type UnknownRunField, conceptCategory };
@@ -1,3 +1,3 @@
1
- export { conceptCategory } from '../chunk-6XD6UZSU.js';
1
+ export { conceptCategory } from '../chunk-25I6JDIK.js';
2
2
  //# sourceMappingURL=descriptor.js.map
3
3
  //# sourceMappingURL=descriptor.js.map
@@ -1,5 +1,5 @@
1
1
  import { RunInputError } from './gate-validator.js';
2
- import { PipeInputContract, PipeIOContract } from './contracts.js';
2
+ import { PipeInputContract, PipeIOContract } from 'mthds/protocol';
3
3
 
4
4
  type Dict = Record<string, unknown>;
5
5
  /**
package/dist/core/gate.js CHANGED
@@ -1,13 +1,12 @@
1
- export { apiInputsFromSchemaData, buildRunInputsSchema, gateRunInputs, prepareRunInputs, validateRunInputs } from '../chunk-XEPD5VJQ.js';
2
- import '../chunk-KFZ5MZOM.js';
3
- import '../chunk-7BMOPXDY.js';
1
+ export { apiInputsFromSchemaData, buildRunInputsSchema, gateRunInputs, prepareRunInputs, validateRunInputs } from '../chunk-IM2AHWBR.js';
2
+ import '../chunk-LBFZXYCY.js';
3
+ import '../chunk-GCXV76WU.js';
4
4
  import '../chunk-PM55AHRO.js';
5
- import '../chunk-VMLXO5JS.js';
6
- import '../chunk-O4O3QNVC.js';
7
- import '../chunk-HARYEWNG.js';
8
- import '../chunk-V3OV2XQT.js';
9
5
  import '../chunk-VTPUDBSJ.js';
10
6
  import '../chunk-GKV77RAS.js';
11
- import '../chunk-NMT7BRLO.js';
7
+ import '../chunk-YEVVR6FE.js';
8
+ import '../chunk-OD3VYXUY.js';
9
+ import '../chunk-XYQ7NXQZ.js';
10
+ import '../chunk-V3OV2XQT.js';
12
11
  //# sourceMappingURL=gate.js.map
13
12
  //# sourceMappingURL=gate.js.map
@@ -0,0 +1,38 @@
1
+ import { PipeInputContract } from 'mthds/protocol';
2
+ import { RunField } from './descriptor.js';
3
+ import './contracts.js';
4
+
5
+ /**
6
+ * The gate's own field tree - structural, schema-derived, and private.
7
+ *
8
+ * `gateRunInputs` re-applies the Run button's emptiness rule with
9
+ * `mustBeFilled`/`fieldFilled`, which read a `RunField` tree. The RENDER tree
10
+ * is built from the wire input-form descriptor - but the gate must not require
11
+ * that artifact: the descriptor is a presentation view, and the workspace
12
+ * doctrine is that a machine consumer never needs it to validate a payload -
13
+ * `json_schema` keeps that job. A server holds contracts; it may never have
14
+ * asked for the view.
15
+ *
16
+ * So the gate walks the contract's `json_schema` into the MINIMAL tree the
17
+ * emptiness predicates read: `object` nodes with their required children,
18
+ * `list` nodes with their declared bounds, and opaque leaves. This is not a
19
+ * revival of the deleted heuristics - it reads exactly the keywords ajv itself
20
+ * enforces (`type`, `properties`, `required`, `items`, `minItems`, `maxItems`)
21
+ * and decides nothing about what a field IS. No kind here ever reaches a
22
+ * renderer: the tree exists for one call inside `gateRunInputs` and is not
23
+ * exported from the package.
24
+ *
25
+ * Agreement with the browser's readiness (which runs over the render tree) is
26
+ * held by construction - both trees take required children and list bounds
27
+ * from the same model the engine derived both artifacts from - and asserted in
28
+ * `gate-agreement.test.ts`, which runs both sides over one table.
29
+ */
30
+
31
+ /**
32
+ * The tree `gateRunInputs`' emptiness re-check runs over, one node per input,
33
+ * with `gating` stamped from the contract - the same `inputMustBeFilled` the
34
+ * Run button's `required` list is built from.
35
+ */
36
+ declare function gatingFieldsFromInputs(inputs: Record<string, PipeInputContract>): RunField[];
37
+
38
+ export { gatingFieldsFromInputs };
@@ -0,0 +1,6 @@
1
+ export { gatingFieldsFromInputs } from '../chunk-YEVVR6FE.js';
2
+ import '../chunk-OD3VYXUY.js';
3
+ import '../chunk-XYQ7NXQZ.js';
4
+ import '../chunk-V3OV2XQT.js';
5
+ //# sourceMappingURL=gating-fields.js.map
6
+ //# sourceMappingURL=gating-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"gating-fields.js"}