@pipelex/mthds-form 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +21 -7
  2. package/dist/chunk-25I6JDIK.js +28 -0
  3. package/dist/chunk-25I6JDIK.js.map +1 -0
  4. package/dist/chunk-5Q2W6HBA.js +201 -0
  5. package/dist/chunk-5Q2W6HBA.js.map +1 -0
  6. package/dist/chunk-GCXV76WU.js +213 -0
  7. package/dist/chunk-GCXV76WU.js.map +1 -0
  8. package/dist/chunk-GKV77RAS.js +46 -0
  9. package/dist/chunk-GKV77RAS.js.map +1 -0
  10. package/dist/chunk-IIJRJGZQ.js +128 -0
  11. package/dist/chunk-IIJRJGZQ.js.map +1 -0
  12. package/dist/chunk-IM2AHWBR.js +99 -0
  13. package/dist/chunk-IM2AHWBR.js.map +1 -0
  14. package/dist/chunk-LBFZXYCY.js +44 -0
  15. package/dist/chunk-LBFZXYCY.js.map +1 -0
  16. package/dist/chunk-OD3VYXUY.js +156 -0
  17. package/dist/chunk-OD3VYXUY.js.map +1 -0
  18. package/dist/chunk-PM55AHRO.js +52 -0
  19. package/dist/chunk-PM55AHRO.js.map +1 -0
  20. package/dist/chunk-RPA7DNSK.js +41 -0
  21. package/dist/chunk-RPA7DNSK.js.map +1 -0
  22. package/dist/chunk-V3OV2XQT.js +12 -0
  23. package/dist/chunk-V3OV2XQT.js.map +1 -0
  24. package/dist/chunk-VTPUDBSJ.js +61 -0
  25. package/dist/chunk-VTPUDBSJ.js.map +1 -0
  26. package/dist/chunk-XYQ7NXQZ.js +31 -0
  27. package/dist/chunk-XYQ7NXQZ.js.map +1 -0
  28. package/dist/chunk-YEVVR6FE.js +66 -0
  29. package/dist/chunk-YEVVR6FE.js.map +1 -0
  30. package/dist/core/contracts.d.ts +114 -0
  31. package/dist/core/contracts.js +4 -0
  32. package/dist/core/contracts.js.map +1 -0
  33. package/dist/core/date-format.d.ts +87 -0
  34. package/dist/core/date-format.js +3 -0
  35. package/dist/core/date-format.js.map +1 -0
  36. package/dist/core/derive.d.ts +55 -0
  37. package/dist/core/derive.js +6 -0
  38. package/dist/core/derive.js.map +1 -0
  39. package/dist/core/descriptor.d.ts +172 -0
  40. package/dist/core/descriptor.js +3 -0
  41. package/dist/core/descriptor.js.map +1 -0
  42. package/dist/core/gate-validator.d.ts +27 -0
  43. package/dist/core/gate-validator.js +4 -0
  44. package/dist/core/gate-validator.js.map +1 -0
  45. package/dist/core/gate.d.ts +144 -0
  46. package/dist/core/gate.js +12 -0
  47. package/dist/core/gate.js.map +1 -0
  48. package/dist/core/gating-fields.d.ts +38 -0
  49. package/dist/core/gating-fields.js +6 -0
  50. package/dist/core/gating-fields.js.map +1 -0
  51. package/dist/core/index.d.ts +13 -623
  52. package/dist/core/index.js +14 -1
  53. package/dist/core/normalize-schema.d.ts +30 -0
  54. package/dist/core/normalize-schema.js +4 -0
  55. package/dist/core/normalize-schema.js.map +1 -0
  56. package/dist/core/own-property.d.ts +46 -0
  57. package/dist/core/own-property.js +3 -0
  58. package/dist/core/own-property.js.map +1 -0
  59. package/dist/core/readiness.d.ts +91 -0
  60. package/dist/core/readiness.js +4 -0
  61. package/dist/core/readiness.js.map +1 -0
  62. package/dist/core/schema-utils.d.ts +86 -0
  63. package/dist/core/schema-utils.js +3 -0
  64. package/dist/core/schema-utils.js.map +1 -0
  65. package/dist/core/validation-message.d.ts +39 -0
  66. package/dist/core/validation-message.js +4 -0
  67. package/dist/core/validation-message.js.map +1 -0
  68. package/dist/core/values.d.ts +98 -0
  69. package/dist/core/values.js +10 -0
  70. package/dist/core/values.js.map +1 -0
  71. package/dist/core/wire-format.d.ts +95 -0
  72. package/dist/core/wire-format.js +6 -0
  73. package/dist/core/wire-format.js.map +1 -0
  74. package/dist/react/index.d.ts +43 -2
  75. package/dist/react/index.js +166 -64
  76. package/dist/react/index.js.map +1 -1
  77. package/dist/styles.css +1 -1
  78. package/package.json +7 -1
  79. package/dist/chunk-S6KOTTGD.js +0 -990
  80. package/dist/chunk-S6KOTTGD.js.map +0 -1
  81. package/dist/descriptor-DPrrfAjL.d.ts +0 -101
@@ -0,0 +1,156 @@
1
+ // src/core/schema-utils.ts
2
+ function collapseNullable(schema) {
3
+ const anyOf = schema.anyOf;
4
+ if (!Array.isArray(anyOf)) return schema;
5
+ const branches = anyOf;
6
+ const nonNull = branches.filter((b) => b?.type !== "null");
7
+ if (nonNull.length === 1) return { ...stripAnyOf(schema), ...nonNull[0] };
8
+ return schema;
9
+ }
10
+ var PRIMITIVE_TYPES = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean", "null"]);
11
+ function isSimplePrimitive(schema) {
12
+ const type = schema.type;
13
+ if (typeof type !== "string" || !PRIMITIVE_TYPES.has(type)) return false;
14
+ const allowedKeys = /* @__PURE__ */ new Set(["type", "default", "description", "title"]);
15
+ return Object.keys(schema).every((key) => allowedKeys.has(key));
16
+ }
17
+ function flattenAnyOf(schema) {
18
+ const anyOf = schema.anyOf;
19
+ if (!Array.isArray(anyOf)) return schema;
20
+ const branches = anyOf;
21
+ if (branches.every(
22
+ (branch) => typeof branch === "object" && branch !== null && isSimplePrimitive(branch)
23
+ )) {
24
+ const types = [];
25
+ let mergedDefault = void 0;
26
+ for (const branch of branches) {
27
+ types.push(branch.type);
28
+ if (branch.default !== void 0) mergedDefault = branch.default;
29
+ }
30
+ const { anyOf: _, ...rest } = schema;
31
+ const result = { ...rest, type: types };
32
+ if (mergedDefault !== void 0 && result.default === void 0) {
33
+ result.default = mergedDefault;
34
+ }
35
+ return result;
36
+ }
37
+ if (branches.length === 2) {
38
+ const nullBranch = branches.find(
39
+ (b) => typeof b === "object" && b !== null && b.type === "null"
40
+ );
41
+ const otherBranch = branches.find((b) => b !== nullBranch);
42
+ if (nullBranch && otherBranch && typeof otherBranch === "object") {
43
+ const { anyOf: _, ...rest } = schema;
44
+ return { ...rest, ...otherBranch };
45
+ }
46
+ }
47
+ return schema;
48
+ }
49
+ function stripAnyOf(schema) {
50
+ const { anyOf: _drop, ...rest } = schema;
51
+ return rest;
52
+ }
53
+ function schemaTypeOf(schema) {
54
+ const t = schema.type;
55
+ if (typeof t === "string") return t;
56
+ if (Array.isArray(t)) return t.find((x) => x !== "null");
57
+ return void 0;
58
+ }
59
+ function derefSchema(schema, defs) {
60
+ const ref = schema.$ref;
61
+ if (typeof ref === "string" && ref.startsWith("#/$defs/")) {
62
+ const resolved = defs[ref.slice("#/$defs/".length)];
63
+ if (resolved) return { ...resolved, ...stripRef(schema) };
64
+ }
65
+ return schema;
66
+ }
67
+ function stripRef(schema) {
68
+ const { $ref: _drop, ...rest } = schema;
69
+ return rest;
70
+ }
71
+ function resolveSchemaIndirection(schema, defs) {
72
+ let current = schema;
73
+ for (; ; ) {
74
+ const ref = current.$ref;
75
+ if (typeof ref === "string" && ref.startsWith("#/$defs/")) {
76
+ const resolved = defs[ref.slice("#/$defs/".length)];
77
+ if (resolved) {
78
+ current = resolved;
79
+ continue;
80
+ }
81
+ }
82
+ if (Array.isArray(current.anyOf)) {
83
+ const nonNull = current.anyOf.filter((b) => b?.type !== "null");
84
+ if (nonNull.length === 1) {
85
+ current = nonNull[0];
86
+ continue;
87
+ }
88
+ }
89
+ return current;
90
+ }
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
+ }
102
+ function collectSchemaDefs(schema, into, options) {
103
+ for (const [key, value] of Object.entries(schema)) {
104
+ if (key === "$defs" && value && typeof value === "object" && !Array.isArray(value)) {
105
+ for (const [name, def] of Object.entries(value)) {
106
+ if (!(name in into) && def && typeof def === "object") {
107
+ into[name] = def;
108
+ collectSchemaDefs(def, into, options);
109
+ }
110
+ }
111
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
112
+ collectSchemaDefs(value, into, options);
113
+ } else if (options.traverseArrays && Array.isArray(value)) {
114
+ for (const item of value) {
115
+ if (item && typeof item === "object") {
116
+ collectSchemaDefs(item, into, options);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ return into;
122
+ }
123
+ function walkAndCollectDefs(schema, collected) {
124
+ const out = {};
125
+ for (const [key, value] of Object.entries(schema)) {
126
+ if (key === "$defs" && value && typeof value === "object" && !Array.isArray(value)) {
127
+ for (const [defName, defValue] of Object.entries(value)) {
128
+ if (!(defName in collected) && defValue && typeof defValue === "object") {
129
+ collected[defName] = {};
130
+ collected[defName] = walkAndCollectDefs(defValue, collected);
131
+ }
132
+ }
133
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
134
+ out[key] = walkAndCollectDefs(value, collected);
135
+ } else if (Array.isArray(value)) {
136
+ out[key] = value.map(
137
+ (item) => item && typeof item === "object" && !Array.isArray(item) ? walkAndCollectDefs(item, collected) : item
138
+ );
139
+ } else {
140
+ out[key] = value;
141
+ }
142
+ }
143
+ return out;
144
+ }
145
+ function hoistDefsToRoot(schema) {
146
+ const collected = {};
147
+ const stripped = walkAndCollectDefs(schema, collected);
148
+ if (Object.keys(collected).length > 0) {
149
+ stripped.$defs = collected;
150
+ }
151
+ return stripped;
152
+ }
153
+
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"]}
@@ -0,0 +1,52 @@
1
+ import { hasOwnProp, ownProp } from './chunk-V3OV2XQT.js';
2
+
3
+ // src/core/readiness.ts
4
+ var MAX_FILLED_DEPTH = 64;
5
+ function isFilled(value) {
6
+ return walkFilled(value, 0, void 0);
7
+ }
8
+ function walkFilled(value, depth, seen) {
9
+ if (value == null) return false;
10
+ if (typeof value === "string") return value.trim() !== "";
11
+ if (typeof value === "function") return false;
12
+ if (typeof value !== "object") return true;
13
+ if (depth >= MAX_FILLED_DEPTH) return false;
14
+ const visited = seen ?? /* @__PURE__ */ new Map();
15
+ const judgedAt = visited.get(value);
16
+ if (judgedAt !== void 0 && judgedAt <= depth) return false;
17
+ visited.set(value, depth);
18
+ if (Array.isArray(value)) return value.some((item) => walkFilled(item, depth + 1, visited));
19
+ const obj = value;
20
+ if (hasOwnProp(obj, "url")) return walkFilled(obj.url, depth + 1, visited);
21
+ return Object.values(obj).some((child) => walkFilled(child, depth + 1, visited));
22
+ }
23
+ function fieldFilled(field, value) {
24
+ if (field.kind === "object") {
25
+ if (!isFilled(value)) return false;
26
+ const obj = value ?? {};
27
+ return field.fields.every((f) => !f.required || fieldFilled(f, ownProp(obj, f.name)));
28
+ }
29
+ if (field.kind === "list") {
30
+ if (field.maxItemCount !== void 0 && Array.isArray(value) && value.length > field.maxItemCount)
31
+ return false;
32
+ if (field.itemCount !== void 0) {
33
+ if (!Array.isArray(value) || value.length < field.itemCount) return false;
34
+ return value.every((item) => fieldFilled(field.item, item));
35
+ }
36
+ if (Array.isArray(value))
37
+ return value.every((item) => !isFilled(item) || fieldFilled(field.item, item)) && isFilled(value);
38
+ }
39
+ return isFilled(value);
40
+ }
41
+ function mustBeFilled(field) {
42
+ return field.gating ?? (field.required && field.kind !== "list");
43
+ }
44
+ function computeReadiness(fields, values) {
45
+ const gating = fields.filter((f) => mustBeFilled(f) || isFilled(ownProp(values, f.name)));
46
+ const missing = gating.filter((f) => !fieldFilled(f, ownProp(values, f.name))).map((f) => f.title ?? f.name);
47
+ return { total: gating.length, ready: gating.length - missing.length, missing };
48
+ }
49
+
50
+ export { computeReadiness, fieldFilled, isFilled, mustBeFilled };
51
+ //# sourceMappingURL=chunk-PM55AHRO.js.map
52
+ //# sourceMappingURL=chunk-PM55AHRO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/readiness.ts"],"names":[],"mappings":"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","file":"chunk-PM55AHRO.js","sourcesContent":["/**\n * Value readiness - the helpers the runner uses to decide whether the Run\n * button may fire and which inputs are still missing.\n */\n\nimport type { RunField } from './descriptor';\nimport { hasOwnProp, ownProp } from './own-property';\n\n/**\n * How deep `isFilled` walks a value before it stops looking, and the answer it\n * gives when it does.\n *\n * The walk is over CALLER-SUPPLIED data - a request body on a public run\n * endpoint, or a form's state - so its depth is whatever the caller sent, and\n * the schema is no bound on it: `pruneEmptyOptionals` copies a property the\n * schema does not declare straight through, so a key ajv never walks reaches\n * this function unwalked. Unbounded, a value a few thousand levels deep threw\n * `RangeError` out of the middle of a host's server gate - a gate whose whole\n * contract is that it returns a verdict instead of throwing.\n *\n * **The cap answers `false`, and that direction is the deliberate half.** Past\n * the cap this function cannot tell whether there is anything down there, and\n * the kernel's rule throughout is that an unanswerable absence fails closed.\n * The cost of the other choice is a run the method cannot use, started (and\n * billed) on a payload no schema validated. The cost of this one is nearly\n * nothing: `isFilled` combines branches with `some`, so a refused over-deep\n * branch only loses its own vote - a real value anywhere beside it still reads\n * filled - and no concept structure declares nesting anywhere near this deep,\n * so a value that reaches the cap is not one the method had a slot for.\n */\nconst MAX_FILLED_DEPTH = 64;\n\n/** True when a value is meaningfully filled (not empty / blank / empty container). */\nexport function isFilled(value: unknown): boolean {\n return walkFilled(value, 0, undefined);\n}\n\n/**\n * `isFilled`'s walk, carrying the depth and the objects already judged.\n *\n * `seen` is a cycle guard first - a value that references itself would\n * otherwise recurse to the cap on every branch - and a memo second. Both uses\n * rest on the same fact: an object reached twice within ONE call answered\n * `false` the first time, because a `true` short-circuits every `some` above it\n * and the walk never gets back down here. So the map can only ever hold empty\n * subtrees, which is what makes it safe to keep entries after unwinding -\n * without that, a value shaped like a diamond chain costs exponential time.\n *\n * **It records the DEPTH each object was judged at, and that is not a detail.**\n * A `false` here has two causes - nothing was down there, or the cap stopped\n * the walk - and only the first travels. An object whose children sit one level\n * past the cap answers `false` at depth 63 and is perfectly filled at depth 5;\n * memoizing identity alone then reported a genuinely filled input as missing\n * the moment a shallower path reached the same object second. So a recorded\n * answer is reused only for a revisit at the SAME depth or deeper, where the\n * walk could see no more than the first one did, and a strictly shallower\n * arrival walks again. That keeps the cycle guard exact (depth rises along\n * every path, so a cycle always revisits deeper) and keeps the memo bounded:\n * an object's recorded depth strictly falls each time it is re-walked, over a\n * range the cap fixes, so the diamond stays linear in all but a constant.\n */\nfunction walkFilled(value: unknown, depth: number, seen: Map<object, number> | undefined): boolean {\n if (value == null) return false;\n // Whitespace is not a value. The Run button lit up over a required text input\n // holding three spaces, ajv passed it (a content model carries no\n // `minLength`), and the run reached the runtime with nothing in it. The same\n // test is what makes a blank OPTIONAL input a real absence on the wire rather\n // than a supplied empty string - see `apiInputsFromSchemaData`.\n if (typeof value === 'string') return value.trim() !== '';\n // No concept in the taxonomy holds a function, so one here arrived by a route\n // that is already wrong - most plausibly an inherited `Object.prototype`\n // member read off a plain object by a colliding input name. `ownProp` closes\n // that route at every lookup; this branch is the backstop for the ones that\n // arrive by another.\n if (typeof value === 'function') return false;\n if (typeof value !== 'object') return true;\n\n if (depth >= MAX_FILLED_DEPTH) return false;\n const visited = seen ?? new Map<object, number>();\n const judgedAt = visited.get(value);\n if (judgedAt !== undefined && judgedAt <= depth) return false;\n visited.set(value, depth);\n\n if (Array.isArray(value)) return value.some((item) => walkFilled(item, depth + 1, visited));\n const obj = value as Record<string, unknown>;\n // A file value is { url } - filled when the url is set.\n if (hasOwnProp(obj, 'url')) return walkFilled(obj.url, depth + 1, visited);\n return Object.values(obj).some((child) => walkFilled(child, depth + 1, visited));\n}\n\n/**\n * True when a slot holds something AND every required field inside it has a\n * value.\n *\n * The two halves are both load-bearing, and the first one is the one that is\n * easy to leave out. A structure is satisfied only if there is something in it:\n * a struct whose concept declares no required child used to be *vacuously*\n * satisfied by an absent value - every child passed the `!f.required` test, so\n * `every` returned true over a value that was not there. Readiness reported\n * nothing missing and the Run button lit up, while the value bridge omitted the\n * untouched structure and the gate then rejected the run on the combined\n * schema's `required` list, naming an input the form had just declared ready.\n *\n * `isFilled` is the SAME predicate `toRjsf` uses to decide whether a structure\n * collapses to an absence (`values.ts`), which is what makes the two halves\n * agree by construction rather than by parallel edits: readiness calls an input\n * present exactly when the bridge keeps it. Absence is what a singular slot\n * expresses, so a required one has to be touched - the button stays dark until\n * the user puts a value somewhere inside, and then the whole shell travels,\n * empty children and all, so a required child left blank still fails loudly.\n *\n * Its own recursion follows the DESCRIPTOR, not the value, so it needs no depth\n * cap of its own: a `RunField` tree only exists because `buildRunFields`\n * finished walking a contract's schema, which bounds it. `isFilled` is where\n * the caller's data is walked, and where the cap lives.\n */\nexport function fieldFilled(field: RunField, value: unknown): boolean {\n if (field.kind === 'object') {\n if (!isFilled(value)) return false;\n const obj = (value ?? {}) as Record<string, unknown>;\n return field.fields.every((f) => !f.required || fieldFilled(f, ownProp(obj, f.name)));\n }\n if (field.kind === 'list') {\n // Too many items is not filled, and this is asked of EVERY list, not only\n // one carrying a lower bound. `ListField` will not add past the bound, so\n // it answers the list that arrived from somewhere else - a restored draft,\n // a host seeding values, a bespoke surface calling these predicates over\n // data it did not render. Asking it only inside the `itemCount` branch\n // meant a model stating `maxItems` alone published its ceiling on the\n // descriptor and enforced it nowhere: the button stayed live on a list ajv\n // was about to refuse. Naming it in `missing` is a small stretch of that\n // word, and the alternative is the disagreement.\n if (\n field.maxItemCount !== undefined &&\n Array.isArray(value) &&\n value.length > field.maxItemCount\n )\n return false;\n // A list the method gave a count to (`Concept[N]`) is satisfied only by that\n // many items, each of them filled. Answering it by `isFilled` alone - \"is\n // there anything in the array?\" - left the button live on a `[3]` slot\n // holding two, and the gate then refused it on the very count the method\n // declared. `itemCount` comes off the same `minItems` ajv reads, so the two\n // halves phrase one rule.\n if (field.itemCount !== undefined) {\n if (!Array.isArray(value) || value.length < field.itemCount) return false;\n return value.every((item) => fieldFilled(field.item, item));\n }\n // A VARIABLE list never gates on being empty (see `mustBeFilled`) - a plural\n // slot's empty form IS the empty list. But a row the user STARTED owes its\n // concept every field the concept declares, which is the same rule a touched\n // optional input follows one level up, and `isFilled` alone does not say it:\n // a row holding `{name: 'x', other: ''}` reads filled by `some`, satisfies\n // ajv's `required` (a present empty string is present), and went out as an\n // item the runtime cannot use. An untouched row stays an absence and still\n // blocks nothing.\n if (Array.isArray(value))\n return (\n value.every((item) => !isFilled(item) || fieldFilled(field.item, item)) && isFilled(value)\n );\n }\n return isFilled(value);\n}\n\nexport interface Readiness {\n total: number;\n ready: number;\n missing: string[];\n}\n\n/**\n * True when the user must put a value in this field before the run can start.\n *\n * Distinct from `required`, which also drives layout (a required field always\n * shows; an optional one may collapse). A **variable-plural** field stays\n * required - it is a first-class input and keeps its place in the form - but it\n * never gates: a plural slot is never \"absent\" in MTHDS, its empty form IS the\n * empty list, and no method can declare \"at least one item\". A **fixed-count**\n * list (`[N]`) is the exception the wire now states, and it gates like any other\n * slot - there the method HAS ruled the empty form out.\n *\n * **For a field this package built, the answer comes from `inputMustBeFilled`**\n * (`contracts.ts`) - the SAME predicate the method viewer's Run button, its ajv\n * `required` list and its missing-inputs toast all read. `buildRunFields`\n * stamps it onto `gating`. This used to be re-derived here as\n * `required && kind !== 'list'`: the same rule expressed against the MAPPED\n * field instead of the contract, and the two could disagree - `kind` is `'list'`\n * for a `conceptRef` ending in `[]` OR an array schema, while the contract's\n * plural test is the array schema alone, so a plural input carrying a non-array\n * schema gated on one surface and not the other.\n *\n * The shape heuristic survives only as the fallback for a **hand-authored**\n * `RunField` - story fixtures and unit tests, which have no contract to consult\n * and therefore nothing to disagree with. Every field a host renders comes from\n * `buildRunFields` and carries `gating`, so a host is on the contract's answer\n * alone. (The fallback cannot see a fixed count either - one more thing only the\n * contract knows.)\n */\nexport function mustBeFilled(field: RunField): boolean {\n return field.gating ?? (field.required && field.kind !== 'list');\n}\n\n/**\n * How many top-level inputs stand between the current values and a run.\n *\n * Two kinds count, and the second is easy to miss. An input the method DEMANDS\n * counts always (`mustBeFilled`). An input the method left optional counts once\n * the user has put something in it - because a structure that has been touched\n * owes its concept every field the concept declares, and the gate enforces\n * exactly that. Left out, an optional struct opened and half-filled was ignored\n * here and refused there: the button stayed live and the run came back rejected\n * on a required child, which is the same disagreement the demanded case had.\n *\n * So an optional input moves INTO the denominator the moment it is touched, and\n * out of `missing` as soon as it is complete: 3 of 3 while it is untouched, 3\n * of 4 once it is started, 4 of 4 once it is done. `total` answers \"how many\n * things stand between me and Run\", which is the number a host is displaying.\n */\nexport function computeReadiness(fields: RunField[], values: Record<string, unknown>): Readiness {\n const gating = fields.filter((f) => mustBeFilled(f) || isFilled(ownProp(values, f.name)));\n const missing = gating\n .filter((f) => !fieldFilled(f, ownProp(values, f.name)))\n .map((f) => f.title ?? f.name);\n return { total: gating.length, ready: gating.length - missing.length, missing };\n}\n"]}
@@ -0,0 +1,41 @@
1
+ import { dayWithinDateTime } from './chunk-GKV77RAS.js';
2
+
3
+ // src/core/validation-message.ts
4
+ var MAX_SHOWN_VALUE_CHARS = 120;
5
+ function valueAtProperty(data, property) {
6
+ if (!property) return void 0;
7
+ let current = data;
8
+ for (const segment of property.split(".").filter(Boolean)) {
9
+ if (typeof current !== "object" || current === null) return void 0;
10
+ current = current[segment];
11
+ }
12
+ return current;
13
+ }
14
+ function displayValue(value) {
15
+ if (value === void 0 || value === null) return null;
16
+ if (typeof value === "string" && value.trim() === "") return null;
17
+ const text = typeof value === "string" ? value : JSON.stringify(value);
18
+ if (text === void 0) return null;
19
+ return text.length > MAX_SHOWN_VALUE_CHARS ? `${text.slice(0, MAX_SHOWN_VALUE_CHARS)}\u2026` : text;
20
+ }
21
+ function labelOf(error, t) {
22
+ return error.stack?.match(/'([^']+)'/)?.[1] ?? t("inputPanel.aDateField");
23
+ }
24
+ function describeValidationError(error, t, formData) {
25
+ const format = error.params?.format;
26
+ const value = valueAtProperty(formData, error.property);
27
+ const shown = displayValue(value);
28
+ if (error.name === "format" && (format === "date" || format === "date-time")) {
29
+ const label = labelOf(error, t);
30
+ if (shown === null) return t("inputPanel.pickValidDateEmpty", { label });
31
+ const day = format === "date" && typeof value === "string" ? dayWithinDateTime(value) : null;
32
+ if (day !== null) return t("inputPanel.dateCarriesTime", { label, value: shown, day });
33
+ return t("inputPanel.pickValidDate", { label, value: shown });
34
+ }
35
+ const stack = error.stack ?? error.message ?? t("inputPanel.invalidValue");
36
+ return shown === null ? stack : t("inputPanel.invalidValueWithData", { stack, value: shown });
37
+ }
38
+
39
+ export { describeValidationError, displayValue, valueAtProperty };
40
+ //# sourceMappingURL=chunk-RPA7DNSK.js.map
41
+ //# sourceMappingURL=chunk-RPA7DNSK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/validation-message.ts"],"names":[],"mappings":";;;AA4BA,IAAM,qBAAA,GAAwB,GAAA;AA6BvB,SAAS,eAAA,CAAgB,MAAe,QAAA,EAAuC;AACpF,EAAA,IAAI,CAAC,UAAU,OAAO,MAAA;AACtB,EAAA,IAAI,OAAA,GAAmB,IAAA;AACvB,EAAA,KAAA,MAAW,WAAW,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,EAAG;AACzD,IAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,OAAA,KAAY,MAAM,OAAO,MAAA;AAC5D,IAAA,OAAA,GAAW,QAAoC,OAAO,CAAA;AAAA,EACxD;AACA,EAAA,OAAO,OAAA;AACT;AAOO,SAAS,aAAa,KAAA,EAA+B;AAC1D,EAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM,OAAO,IAAA;AAClD,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,MAAM,IAAA,EAAK,KAAM,IAAI,OAAO,IAAA;AAC7D,EAAA,MAAM,OAAO,OAAO,KAAA,KAAU,WAAW,KAAA,GAAQ,IAAA,CAAK,UAAU,KAAK,CAAA;AACrE,EAAA,IAAI,IAAA,KAAS,QAAW,OAAO,IAAA;AAC/B,EAAA,OAAO,IAAA,CAAK,SAAS,qBAAA,GAAwB,CAAA,EAAG,KAAK,KAAA,CAAM,CAAA,EAAG,qBAAqB,CAAC,CAAA,MAAA,CAAA,GAAM,IAAA;AAC5F;AAGA,SAAS,OAAA,CAAQ,OAAsB,CAAA,EAAsB;AAC3D,EAAA,OAAO,KAAA,CAAM,OAAO,KAAA,CAAM,WAAW,IAAI,CAAC,CAAA,IAAK,EAAE,uBAAuB,CAAA;AAC1E;AAUO,SAAS,uBAAA,CACd,KAAA,EACA,CAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,MAAA,GAAU,MAAM,MAAA,EAA4C,MAAA;AAClE,EAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,QAAA,EAAU,KAAA,CAAM,QAAQ,CAAA;AACtD,EAAA,MAAM,KAAA,GAAQ,aAAa,KAAK,CAAA;AAEhC,EAAA,IAAI,MAAM,IAAA,KAAS,QAAA,KAAa,MAAA,KAAW,MAAA,IAAU,WAAW,WAAA,CAAA,EAAc;AAC5E,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,EAAO,CAAC,CAAA;AAC9B,IAAA,IAAI,UAAU,IAAA,EAAM,OAAO,EAAE,+BAAA,EAAiC,EAAE,OAAO,CAAA;AAEvE,IAAA,MAAM,GAAA,GAAM,WAAW,MAAA,IAAU,OAAO,UAAU,QAAA,GAAW,iBAAA,CAAkB,KAAK,CAAA,GAAI,IAAA;AACxF,IAAA,IAAI,GAAA,KAAQ,IAAA,EAAM,OAAO,CAAA,CAAE,4BAAA,EAA8B,EAAE,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,GAAA,EAAK,CAAA;AACrF,IAAA,OAAO,EAAE,0BAAA,EAA4B,EAAE,KAAA,EAAO,KAAA,EAAO,OAAO,CAAA;AAAA,EAC9D;AAEA,EAAA,MAAM,QAAQ,KAAA,CAAM,KAAA,IAAS,KAAA,CAAM,OAAA,IAAW,EAAE,yBAAyB,CAAA;AACzE,EAAA,OAAO,KAAA,KAAU,OAAO,KAAA,GAAQ,CAAA,CAAE,mCAAmC,EAAE,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,CAAA;AAC9F","file":"chunk-RPA7DNSK.js","sourcesContent":["/**\n * Turning one ajv/RJSF validation error into a line a human can act on.\n *\n * ## Why this isn't just `error.stack`\n *\n * A blocked run used to say \"Date: pick a valid date\" while the date picker\n * showed a perfectly good 06/07/2026. Both were telling the truth about\n * different values: the stored value was `2026-07-06T14:30:00+02:00` (a real\n * time - correctly rejected for a `format: date` field), but\n * `toDateInputValue` renders `.slice(0, 10)` of it, so the FIELD showed the day\n * while the VALIDATOR judged the timestamp. The user cannot reconcile that from\n * the message, because the message named neither value.\n *\n * So: every line here quotes what is ACTUALLY in the data. An error the user\n * can see the input of is one they can fix; an instruction to \"pick a valid\n * date\" for a date that looks valid is not.\n *\n * Kept React-free (the caller passes its `t`) so the wording is unit-testable\n * without mounting the panel.\n */\nimport { dayWithinDateTime } from './date-format';\nimport type { RunInputError } from './gate-validator';\n\n/**\n * A rejected value can be an entire base64 document, and this lands in a toast.\n * Past this many characters the tail is elided - a date never comes close, and\n * a megabyte of base64 in a toast helps nobody understand anything.\n */\nconst MAX_SHOWN_VALUE_CHARS = 120;\n\n/** The message keys this module renders, relative to the caller's namespace\n * (a host passes its own translator for that namespace). */\nexport type ValidationMessageKey =\n | 'inputPanel.aDateField'\n | 'inputPanel.pickValidDate'\n | 'inputPanel.pickValidDateEmpty'\n | 'inputPanel.dateCarriesTime'\n | 'inputPanel.invalidValue'\n | 'inputPanel.invalidValueWithData';\n\n/**\n * The translate function the caller injects. Typed on the EXACT keys this\n * module uses - not a loose `(key: string) => string` - for two reasons: a\n * typo'd key still fails the build here, and next-intl's translator (whose\n * `key` parameter is the namespace's real key union, a SUPERSET of these)\n * remains assignable by parameter contravariance. Kernel-owned: this module\n * must not import next-intl's types.\n */\nexport type Translate = (key: ValidationMessageKey, values?: Record<string, string>) => string;\n\n/**\n * The value the validator actually rejected, read back out of the submitted data.\n *\n * RJSF builds `property` from ajv's `instancePath` by swapping `/` for `.`\n * (`.quote_date.date`), so the segments index straight into the form data.\n * Array indices arrive as digit strings and index arrays natively.\n */\nexport function valueAtProperty(data: unknown, property: string | undefined): unknown {\n if (!property) return undefined;\n let current: unknown = data;\n for (const segment of property.split('.').filter(Boolean)) {\n if (typeof current !== 'object' || current === null) return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\n/**\n * The rejected value rendered for a toast, or `null` when there is nothing\n * worth showing (an absent or blank value - \"you left it empty\" is what the\n * message says on its own, and `“”` adds noise).\n */\nexport function displayValue(value: unknown): string | null {\n if (value === undefined || value === null) return null;\n if (typeof value === 'string' && value.trim() === '') return null;\n const text = typeof value === 'string' ? value : JSON.stringify(value);\n if (text === undefined) return null;\n return text.length > MAX_SHOWN_VALUE_CHARS ? `${text.slice(0, MAX_SHOWN_VALUE_CHARS)}…` : text;\n}\n\n/** The field name ajv quoted in its stack, or a generic stand-in. */\nfunction labelOf(error: RunInputError, t: Translate): string {\n return error.stack?.match(/'([^']+)'/)?.[1] ?? t('inputPanel.aDateField');\n}\n\n/**\n * One validation error as a human line naming the offending value.\n *\n * The date branch is special-cased because ajv's own complaint (`'Date' must\n * match format \"date-time\"`) is opaque, and because the carries-a-time case has\n * a specific fix the user cannot guess: move the time into the sibling `time`\n * field, which is exactly what the runtime's own error says.\n */\nexport function describeValidationError(\n error: RunInputError,\n t: Translate,\n formData: unknown,\n): string {\n const format = (error.params as { format?: string } | undefined)?.format;\n const value = valueAtProperty(formData, error.property);\n const shown = displayValue(value);\n\n if (error.name === 'format' && (format === 'date' || format === 'date-time')) {\n const label = labelOf(error, t);\n if (shown === null) return t('inputPanel.pickValidDateEmpty', { label });\n // Only a `format: date` field rejects a real time; date-time takes it.\n const day = format === 'date' && typeof value === 'string' ? dayWithinDateTime(value) : null;\n if (day !== null) return t('inputPanel.dateCarriesTime', { label, value: shown, day });\n return t('inputPanel.pickValidDate', { label, value: shown });\n }\n\n const stack = error.stack ?? error.message ?? t('inputPanel.invalidValue');\n return shown === null ? stack : t('inputPanel.invalidValueWithData', { stack, value: shown });\n}\n"]}
@@ -0,0 +1,12 @@
1
+ // src/core/own-property.ts
2
+ function ownProp(record, key) {
3
+ if (record == null) return void 0;
4
+ return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : void 0;
5
+ }
6
+ function hasOwnProp(record, key) {
7
+ return record != null && Object.prototype.hasOwnProperty.call(record, key);
8
+ }
9
+
10
+ export { hasOwnProp, ownProp };
11
+ //# sourceMappingURL=chunk-V3OV2XQT.js.map
12
+ //# sourceMappingURL=chunk-V3OV2XQT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/own-property.ts"],"names":[],"mappings":";AAyCO,SAAS,OAAA,CACd,QACA,GAAA,EACe;AACf,EAAA,IAAI,MAAA,IAAU,MAAM,OAAO,MAAA;AAC3B,EAAA,OAAO,MAAA,CAAO,UAAU,cAAA,CAAe,IAAA,CAAK,QAAQ,GAAG,CAAA,GAAI,MAAA,CAAO,GAAG,CAAA,GAAI,MAAA;AAC3E;AAGO,SAAS,UAAA,CAAW,QAAmC,GAAA,EAAsB;AAClF,EAAA,OAAO,UAAU,IAAA,IAAQ,MAAA,CAAO,UAAU,cAAA,CAAe,IAAA,CAAK,QAAQ,GAAG,CAAA;AAC3E","file":"chunk-V3OV2XQT.js","sourcesContent":["/**\n * Read a property a record OWNS, never one it merely inherits.\n *\n * Every name this package indexes a record by - an input variable name, a\n * structure's property name, a pipe code, a concept's content key - is chosen\n * by the METHOD AUTHOR, and the records they index are plain objects the host\n * built (`{}` from a form's state, `JSON.parse` of a request body, a map keyed\n * by pipe ref). A plain object inherits `Object.prototype`, so a name that\n * collides with a prototype member - `constructor`, `toString`, `valueOf`,\n * `hasOwnProperty`, … - does NOT read as `undefined`: it reads as the inherited\n * function, which is truthy.\n *\n * That is a verdict bug wherever a bare read decides one. An input named\n * `constructor` with nothing entered used to resolve to `Object`, which\n * `isFilled` had no branch for and therefore called filled - a live Run button\n * over an empty required input. `getPipeIOContract` returned the same function\n * for a prototype-named pipe code, which sailed past the `if (!contract)` guard\n * hosts are shown writing.\n *\n * What it does NOT close, stated so nobody reads a fixed read as a fixed class:\n * the WRITE twin. `out[name] = value` on a plain object invokes the inherited\n * `__proto__` setter for that one name, so an input actually called `__proto__`\n * is dropped from a payload rather than added to it. That is fail-loud (the\n * runtime rejects a run whose input is missing) where the read bug was\n * fail-open, and `__proto__` is not a name an author writes, so the writes are\n * left plain and legible.\n *\n * Spelled with `Object.prototype.hasOwnProperty.call` rather than `Object.hasOwn`\n * deliberately. `hasOwn` is an ES2022 runtime API and `tsconfig.json` compiles\n * to **ES2020**, so a browser inside the declared target has no such function\n * and every read here would throw before a form could render a single field -\n * a whole-package failure in service of a shorter spelling. TypeScript cannot\n * catch that: `lib` is what types a runtime API, `target` is what states the\n * baseline, and the two are allowed to disagree.\n *\n * Deliberately module-private: this is a spelling of `record[key]`, not a\n * concept a consumer needs. Reachability is low - only a method author picks\n * these names - but the cost of being wrong is a form that says a field is\n * filled when it is empty, so the package spells the read one way everywhere\n * rather than guarding the sites someone happened to notice.\n */\nexport function ownProp<T>(\n record: Record<string, T> | null | undefined,\n key: string,\n): T | undefined {\n if (record == null) return undefined;\n return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : undefined;\n}\n\n/** `key in record` with the prototype chain left out. */\nexport function hasOwnProp(record: object | null | undefined, key: string): boolean {\n return record != null && Object.prototype.hasOwnProperty.call(record, key);\n}\n"]}
@@ -0,0 +1,61 @@
1
+ import { isAcceptableDate, isAcceptableDateTime } from './chunk-GKV77RAS.js';
2
+ import Ajv from 'ajv';
3
+ import addFormats from 'ajv-formats';
4
+
5
+ var ajv = new Ajv({
6
+ allErrors: true,
7
+ multipleOfPrecision: 8,
8
+ strict: false,
9
+ verbose: true,
10
+ discriminator: false,
11
+ coerceTypes: true
12
+ });
13
+ addFormats(ajv);
14
+ ajv.addFormat("data-url", /^data:([a-z]+\/[a-z0-9-+.]+)?;(?:name=(.*);)?base64,(.*)$/);
15
+ ajv.addFormat(
16
+ "color",
17
+ /^(#?([0-9A-Fa-f]{3}){1,2}\b|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|(rgb\(\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*\))|(rgb\(\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*\)))$/
18
+ );
19
+ ajv.addFormat("date", isAcceptableDate);
20
+ ajv.addFormat("date-time", isAcceptableDateTime);
21
+ function validateRunInputsSchema(data, schema) {
22
+ let validate;
23
+ try {
24
+ validate = ajv.compile(schema);
25
+ } catch (err) {
26
+ return [{ stack: err instanceof Error ? err.message : String(err) }];
27
+ }
28
+ validate(data);
29
+ const errors = (validate.errors ?? []).map(toRunInputError);
30
+ validate.errors = null;
31
+ return errors;
32
+ }
33
+ function toRunInputError(e) {
34
+ const { instancePath, keyword, schemaPath, parentSchema } = e;
35
+ const params = e.params;
36
+ const message = e.message ?? "";
37
+ let property = instancePath.replace(/\//g, ".");
38
+ let stack = `${property} ${message}`.trim();
39
+ const namesAProperty = [
40
+ ...typeof params.deps === "string" ? params.deps.split(", ") : [],
41
+ params.missingProperty,
42
+ params.property
43
+ ].some((item) => typeof item === "string" && item.length > 0);
44
+ if (namesAProperty) {
45
+ stack = message;
46
+ } else {
47
+ const parentTitle = parentSchema?.title;
48
+ if (typeof parentTitle === "string" && parentTitle) {
49
+ stack = `'${parentTitle}' ${message}`.trim();
50
+ }
51
+ }
52
+ if ("missingProperty" in params) {
53
+ const missing = String(params.missingProperty);
54
+ property = property ? `${property}.${missing}` : missing;
55
+ }
56
+ return { name: keyword, property, message, params, stack, schemaPath };
57
+ }
58
+
59
+ export { validateRunInputsSchema };
60
+ //# sourceMappingURL=chunk-VTPUDBSJ.js.map
61
+ //# sourceMappingURL=chunk-VTPUDBSJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/gate-validator.ts"],"names":[],"mappings":";;;;AAoDA,IAAM,GAAA,GAAM,IAAI,GAAA,CAAI;AAAA,EAClB,SAAA,EAAW,IAAA;AAAA,EACX,mBAAA,EAAqB,CAAA;AAAA,EACrB,MAAA,EAAQ,KAAA;AAAA,EACR,OAAA,EAAS,IAAA;AAAA,EACT,aAAA,EAAe,KAAA;AAAA,EACf,WAAA,EAAa;AACf,CAAC,CAAA;AACD,UAAA,CAAW,GAAG,CAAA;AACd,GAAA,CAAI,SAAA,CAAU,YAAY,2DAA2D,CAAA;AACrF,GAAA,CAAI,SAAA;AAAA,EACF,OAAA;AAAA,EACA;AACF,CAAA;AAKA,GAAA,CAAI,SAAA,CAAU,QAAQ,gBAAgB,CAAA;AACtC,GAAA,CAAI,SAAA,CAAU,aAAa,oBAAoB,CAAA;AAOxC,SAAS,uBAAA,CAAwB,MAAe,MAAA,EAAiC;AACtF,EAAA,IAAI,QAAA;AACJ,EAAA,IAAI;AACF,IAAA,QAAA,GAAW,GAAA,CAAI,QAAQ,MAAM,CAAA;AAAA,EAC/B,SAAS,GAAA,EAAK;AAGZ,IAAA,OAAO,CAAC,EAAE,KAAA,EAAO,GAAA,YAAe,KAAA,GAAQ,IAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAA,EAAG,CAAA;AAAA,EACrE;AACA,EAAA,QAAA,CAAS,IAAI,CAAA;AACb,EAAA,MAAM,UAAU,QAAA,CAAS,MAAA,IAAU,EAAC,EAAG,IAAI,eAAe,CAAA;AAC1D,EAAA,QAAA,CAAS,MAAA,GAAS,IAAA;AAClB,EAAA,OAAO,MAAA;AACT;AAIA,SAAS,gBAAgB,CAAA,EAA+B;AACtD,EAAA,MAAM,EAAE,YAAA,EAAc,OAAA,EAAS,UAAA,EAAY,cAAa,GAAI,CAAA;AAC5D,EAAA,MAAM,SAAS,CAAA,CAAE,MAAA;AACjB,EAAA,MAAM,OAAA,GAAU,EAAE,OAAA,IAAW,EAAA;AAC7B,EAAA,IAAI,QAAA,GAAW,YAAA,CAAa,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAC9C,EAAA,IAAI,QAAQ,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,OAAO,GAAG,IAAA,EAAK;AAE1C,EAAA,MAAM,cAAA,GAAiB;AAAA,IACrB,GAAI,OAAO,MAAA,CAAO,IAAA,KAAS,QAAA,GAAW,OAAO,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,GAAI,EAAC;AAAA,IACjE,MAAA,CAAO,eAAA;AAAA,IACP,MAAA,CAAO;AAAA,GACT,CAAE,KAAK,CAAC,IAAA,KAAS,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,CAAK,MAAA,GAAS,CAAC,CAAA;AAE5D,EAAA,IAAI,cAAA,EAAgB;AASlB,IAAA,KAAA,GAAQ,OAAA;AAAA,EACV,CAAA,MAAO;AACL,IAAA,MAAM,cAAe,YAAA,EAAkD,KAAA;AACvE,IAAA,IAAI,OAAO,WAAA,KAAgB,QAAA,IAAY,WAAA,EAAa;AAClD,MAAA,KAAA,GAAQ,CAAA,CAAA,EAAI,WAAW,CAAA,EAAA,EAAK,OAAO,GAAG,IAAA,EAAK;AAAA,IAC7C;AAAA,EACF;AAEA,EAAA,IAAI,qBAAqB,MAAA,EAAQ;AAC/B,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,MAAA,CAAO,eAAe,CAAA;AAC7C,IAAA,QAAA,GAAW,QAAA,GAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,GAAK,OAAA;AAAA,EACnD;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,UAAU,OAAA,EAAS,MAAA,EAAQ,OAAO,UAAA,EAAW;AACvE","file":"chunk-VTPUDBSJ.js","sourcesContent":["/**\n * The run gate's OWN validator - ajv direct, no RJSF.\n *\n * Until the form-kernel extraction the gate validated through\n * `@rjsf/validator-ajv8` (`rjsf-validator.ts`), which is an RJSF `ValidatorType`\n * wrapper around exactly this: an ajv instance plus an error-shape transform.\n * The kernel must not depend on RJSF, so this module owns both halves directly,\n * configured to reproduce its output but for one deliberate divergence:\n *\n * - the ajv OPTIONS are `@rjsf/validator-ajv8`'s `AJV_CONFIG` plus the same\n * `coerceTypes: true` override this gate has always run with (pydantic runs in\n * lax mode - `\"20\"` is a number, `\"5\"` an int - and rejecting client-side what\n * the runner accepts blocks valid work; see `rjsf-validator.ts`);\n * - the `date` / `date-time` formats are the pydantic-parity predicates from\n * `date-format.ts`, same as before;\n * - `toRunInputError` reproduces RJSF's `transformRJSFValidationErrors` for the\n * no-uiSchema case the gate is: `property` is the dotted instance path (plus\n * the missing property name for `required` errors), and `stack` quotes the\n * parent schema's `title` when the error does not itself name a property.\n *\n * It diverges from RJSF in exactly one place, and deliberately: an error that\n * NAMES a property keeps the property's own name rather than swapping in that\n * property's schema `title`. RJSF substitutes because the title is the label its\n * forms render; this package labels a field by its identifier instead, so the\n * substitution named something the user could not find. See `toRunInputError`.\n */\nimport Ajv, { type ErrorObject } from 'ajv';\nimport addFormats from 'ajv-formats';\nimport { isAcceptableDate, isAcceptableDateTime } from './date-format';\n\n/**\n * One validation error, in the shape hosts read (`property`, `message`,\n * `name`, `stack`, `params.format`). Structurally a superset-compatible twin of\n * RJSF's `RJSFValidationError`, so a host surface still holding RJSF form errors\n * (an RJSF-based inputs panel) can hand them to the same message renderer.\n */\nexport interface RunInputError {\n /** The ajv keyword that failed (`required`, `format`, `type`, …). */\n name?: string;\n /** Dotted path into the form data (`.quote_date.date`); for a `required`\n * error the missing property name is appended (top-level: no leading dot). */\n property?: string;\n message?: string;\n params?: Record<string, unknown>;\n /** The human line: `property message`, or `'title' message` when the parent\n * schema titles the field. */\n stack: string;\n schemaPath?: string;\n}\n\n// The exact instance `@rjsf/validator-ajv8` builds (its AJV_CONFIG + the\n// standard ajv-formats + its two custom formats), plus our two overrides.\nconst ajv = new Ajv({\n allErrors: true,\n multipleOfPrecision: 8,\n strict: false,\n verbose: true,\n discriminator: false,\n coerceTypes: true,\n});\naddFormats(ajv);\najv.addFormat('data-url', /^data:([a-z]+\\/[a-z0-9-+.]+)?;(?:name=(.*);)?base64,(.*)$/);\najv.addFormat(\n 'color',\n /^(#?([0-9A-Fa-f]{3}){1,2}\\b|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|(rgb\\(\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*,\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*,\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*\\))|(rgb\\(\\s*(\\d?\\d%|100%)+\\s*,\\s*(\\d?\\d%|100%)+\\s*,\\s*(\\d?\\d%|100%)+\\s*\\)))$/,\n);\n// Pydantic-parity leniency, replacing ajv-formats' spec-strict versions: a\n// midnight-padded timestamp IS a date, a bare day IS a date-time. Measured\n// against pydantic in `date-format.ts` - keep in step with the runtime, not\n// with the JSON Schema spec.\najv.addFormat('date', isAcceptableDate);\najv.addFormat('date-time', isAcceptableDateTime);\n\n/**\n * Validate `data` against `schema`, returning mapped errors (empty = valid).\n * MUTATES `data` where ajv coerces (`\"20\"` → `20`) - deliberate and relied on:\n * the string the user typed is stored as a number going forward.\n */\nexport function validateRunInputsSchema(data: unknown, schema: object): RunInputError[] {\n let validate;\n try {\n validate = ajv.compile(schema);\n } catch (err) {\n // A malformed schema produces a verdict-less failure, same as RJSF's\n // `validationError` path: one bare-stack error, nothing to pin on a field.\n return [{ stack: err instanceof Error ? err.message : String(err) }];\n }\n validate(data);\n const errors = (validate.errors ?? []).map(toRunInputError);\n validate.errors = null;\n return errors;\n}\n\n/** RJSF's `transformRJSFValidationErrors`, minus the uiSchema lookups the gate\n * never had (it passes no uiSchema). */\nfunction toRunInputError(e: ErrorObject): RunInputError {\n const { instancePath, keyword, schemaPath, parentSchema } = e;\n const params = e.params as Record<string, unknown>;\n const message = e.message ?? '';\n let property = instancePath.replace(/\\//g, '.');\n let stack = `${property} ${message}`.trim();\n\n const namesAProperty = [\n ...(typeof params.deps === 'string' ? params.deps.split(', ') : []),\n params.missingProperty,\n params.property,\n ].some((item) => typeof item === 'string' && item.length > 0);\n\n if (namesAProperty) {\n // ajv already quotes the property NAME, and that is the name kept - the ONE\n // deliberate divergence from RJSF, which substitutes the parent schema's\n // `properties[name].title` here. That substitution is right in an RJSF form,\n // where the title IS the rendered label; it is wrong for this package, whose\n // controls label a field by its identifier on purpose (see `mapSchema`). A\n // pydantic contract titles `audience` as `Audience`, so the blocked run read\n // `must have required property 'Audience'` and sent the user hunting for a\n // field their bundle does not contain.\n stack = message;\n } else {\n const parentTitle = (parentSchema as { title?: unknown } | undefined)?.title;\n if (typeof parentTitle === 'string' && parentTitle) {\n stack = `'${parentTitle}' ${message}`.trim();\n }\n }\n\n if ('missingProperty' in params) {\n const missing = String(params.missingProperty);\n property = property ? `${property}.${missing}` : missing;\n }\n\n return { name: keyword, property, message, params, stack, schemaPath };\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import { ownProp } from './chunk-V3OV2XQT.js';
2
+
3
+ // src/core/contracts.ts
4
+ function buildPipeRef(domain, pipeCode) {
5
+ return `${domain}.${pipeCode}`;
6
+ }
7
+ function getPipeIOContract(pipeIoContracts, domain, pipeCode) {
8
+ if (!pipeIoContracts || !pipeCode) return void 0;
9
+ if (domain) {
10
+ const byRef = ownProp(pipeIoContracts, buildPipeRef(domain, pipeCode));
11
+ if (byRef) return byRef;
12
+ }
13
+ return ownProp(pipeIoContracts, pipeCode);
14
+ }
15
+ function isOptionalInput(input) {
16
+ return input.presence === "optional";
17
+ }
18
+ function isPluralInput(input) {
19
+ return input.json_schema?.type === "array";
20
+ }
21
+ function isFixedCountInput(input) {
22
+ return input.multiplicity === "fixed";
23
+ }
24
+ function inputMustBeFilled(input) {
25
+ if (isOptionalInput(input)) return false;
26
+ return !isPluralInput(input) || isFixedCountInput(input);
27
+ }
28
+
29
+ export { buildPipeRef, getPipeIOContract, inputMustBeFilled, isFixedCountInput, isOptionalInput, isPluralInput };
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"]}