@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.
- package/README.md +15 -6
- package/dist/{chunk-6XD6UZSU.js → chunk-25I6JDIK.js} +2 -2
- package/dist/chunk-25I6JDIK.js.map +1 -0
- package/dist/{chunk-WXBSM2CE.js → chunk-5Q2W6HBA.js} +8 -8
- package/dist/chunk-5Q2W6HBA.js.map +1 -0
- package/dist/{chunk-7BMOPXDY.js → chunk-GCXV76WU.js} +17 -9
- package/dist/chunk-GCXV76WU.js.map +1 -0
- package/dist/chunk-IIJRJGZQ.js +128 -0
- package/dist/chunk-IIJRJGZQ.js.map +1 -0
- package/dist/{chunk-XEPD5VJQ.js → chunk-IM2AHWBR.js} +8 -8
- package/dist/chunk-IM2AHWBR.js.map +1 -0
- package/dist/{chunk-KFZ5MZOM.js → chunk-LBFZXYCY.js} +3 -3
- package/dist/{chunk-KFZ5MZOM.js.map → chunk-LBFZXYCY.js.map} +1 -1
- package/dist/{chunk-O4O3QNVC.js → chunk-OD3VYXUY.js} +13 -3
- package/dist/chunk-OD3VYXUY.js.map +1 -0
- package/dist/{chunk-HARYEWNG.js → chunk-XYQ7NXQZ.js} +2 -2
- package/dist/chunk-XYQ7NXQZ.js.map +1 -0
- package/dist/chunk-YEVVR6FE.js +66 -0
- package/dist/chunk-YEVVR6FE.js.map +1 -0
- package/dist/core/contracts.d.ts +27 -56
- package/dist/core/contracts.js +1 -1
- package/dist/core/derive.d.ts +46 -11
- package/dist/core/derive.js +3 -4
- package/dist/core/descriptor.d.ts +70 -26
- package/dist/core/descriptor.js +1 -1
- package/dist/core/gate.d.ts +1 -1
- package/dist/core/gate.js +7 -8
- package/dist/core/gating-fields.d.ts +38 -0
- package/dist/core/gating-fields.js +6 -0
- package/dist/core/gating-fields.js.map +1 -0
- package/dist/core/index.d.ts +4 -3
- package/dist/core/index.js +10 -10
- package/dist/core/normalize-schema.js +2 -2
- package/dist/core/readiness.d.ts +2 -0
- package/dist/core/schema-utils.d.ts +13 -1
- package/dist/core/schema-utils.js +1 -1
- package/dist/core/values.d.ts +10 -3
- package/dist/core/values.js +6 -7
- package/dist/core/wire-format.js +3 -4
- package/dist/react/index.d.ts +21 -1
- package/dist/react/index.js +41 -19
- package/dist/react/index.js.map +1 -1
- package/package.json +3 -1
- package/dist/chunk-6XD6UZSU.js.map +0 -1
- package/dist/chunk-7BMOPXDY.js.map +0 -1
- package/dist/chunk-HARYEWNG.js.map +0 -1
- package/dist/chunk-NMT7BRLO.js +0 -40
- package/dist/chunk-NMT7BRLO.js.map +0 -1
- package/dist/chunk-O4O3QNVC.js.map +0 -1
- package/dist/chunk-VMLXO5JS.js +0 -132
- package/dist/chunk-VMLXO5JS.js.map +0 -1
- package/dist/chunk-WXBSM2CE.js.map +0 -1
- package/dist/chunk-XEPD5VJQ.js.map +0 -1
- package/dist/core/native-concepts.d.ts +0 -78
- package/dist/core/native-concepts.js +0 -3
- 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-
|
|
146
|
-
//# sourceMappingURL=chunk-
|
|
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-
|
|
31
|
-
//# sourceMappingURL=chunk-
|
|
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"]}
|
package/dist/core/contracts.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
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
|
|
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 -
|
|
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
|
|
114
|
+
export { type InputPresence, buildPipeRef, getPipeIOContract, inputMustBeFilled, isFixedCountInput, isOptionalInput, isPluralInput };
|
package/dist/core/contracts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { buildPipeRef, getPipeIOContract, inputMustBeFilled, isFixedCountInput, isOptionalInput, isPluralInput } from '../chunk-
|
|
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
|
package/dist/core/derive.d.ts
CHANGED
|
@@ -1,20 +1,55 @@
|
|
|
1
|
-
import { PipeInputContract } from '
|
|
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:
|
|
6
|
+
* The ONE derivation function: wire input-form descriptor → `RunField[]`.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the
|
|
11
|
-
*
|
|
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
|
-
*
|
|
16
|
-
*
|
|
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 };
|
package/dist/core/derive.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
export { buildRunFields } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
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
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* (in `derive.ts`)
|
|
8
|
-
* descriptor
|
|
9
|
-
* components never touch
|
|
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
|
-
|
|
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
|
-
*
|
|
60
|
-
*
|
|
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
|
|
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
|
|
94
|
-
*
|
|
138
|
+
* The FEWEST items the slot accepts. Absent for a variable `Concept[]` list,
|
|
139
|
+
* which demands none.
|
|
95
140
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
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
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
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 };
|
package/dist/core/descriptor.js
CHANGED
package/dist/core/gate.d.ts
CHANGED
package/dist/core/gate.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
export { apiInputsFromSchemaData, buildRunInputsSchema, gateRunInputs, prepareRunInputs, validateRunInputs } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
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-
|
|
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 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"gating-fields.js"}
|