@pipelex/mthds-form 0.33.0 → 0.34.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/dist/brand/index.js +2 -2
- package/dist/{chunk-UKTYADY3.js → chunk-5RV3ASMO.js} +141 -69
- package/dist/chunk-5RV3ASMO.js.map +1 -0
- package/dist/{chunk-VH5CDQDR.js → chunk-AURDKAIC.js} +3 -3
- package/dist/{chunk-VH5CDQDR.js.map → chunk-AURDKAIC.js.map} +1 -1
- package/dist/{chunk-ZX52CQJ4.js → chunk-CFBK7NO7.js} +3 -3
- package/dist/{chunk-ZX52CQJ4.js.map → chunk-CFBK7NO7.js.map} +1 -1
- package/dist/{chunk-3GO65HWP.js → chunk-PNNWNRPG.js} +11 -7
- package/dist/chunk-PNNWNRPG.js.map +1 -0
- package/dist/{chunk-XO5OENNF.js → chunk-TF4RTKLH.js} +32 -5
- package/dist/chunk-TF4RTKLH.js.map +1 -0
- package/dist/{chunk-IBBAJXIO.js → chunk-XIWBKV7T.js} +3 -3
- package/dist/{chunk-IBBAJXIO.js.map → chunk-XIWBKV7T.js.map} +1 -1
- package/dist/{chunk-4SRSGZWM.js → chunk-XYS5KCXB.js} +158 -31
- package/dist/chunk-XYS5KCXB.js.map +1 -0
- package/dist/core/gate.js +2 -2
- package/dist/core/index.js +4 -4
- package/dist/core/readiness.d.ts +34 -1
- package/dist/core/readiness.js +1 -1
- package/dist/core/seed.js +2 -2
- package/dist/core/values.js +2 -2
- package/dist/generative/headless/index.d.ts +15 -9
- package/dist/generative/headless/index.js +3 -2
- package/dist/generative/index.d.ts +27 -4
- package/dist/generative/index.js +628 -240
- package/dist/generative/index.js.map +1 -1
- package/dist/react/index.d.ts +30 -3
- package/dist/react/index.js +4 -4
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/chunk-3GO65HWP.js.map +0 -1
- package/dist/chunk-4SRSGZWM.js.map +0 -1
- package/dist/chunk-UKTYADY3.js.map +0 -1
- package/dist/chunk-XO5OENNF.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isOptionalStructure, optionalStructureOpen } from './chunk-
|
|
1
|
+
import { isOptionalStructure, optionalStructureOpen } from './chunk-TF4RTKLH.js';
|
|
2
2
|
import { ownProp } from './chunk-V3OV2XQT.js';
|
|
3
3
|
|
|
4
4
|
// src/core/seed.ts
|
|
@@ -37,5 +37,5 @@ function closeOptionalStructures(fields, values) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export { closeOptionalStructures, openOptionalStructures, seedInputs, seedObjectValue };
|
|
40
|
-
//# sourceMappingURL=chunk-
|
|
41
|
-
//# sourceMappingURL=chunk-
|
|
40
|
+
//# sourceMappingURL=chunk-AURDKAIC.js.map
|
|
41
|
+
//# sourceMappingURL=chunk-AURDKAIC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/seed.ts"],"names":[],"mappings":";;;;AAWA,SAAS,SAAS,KAAA,EAAkD;AAClE,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;AAoBO,SAAS,WAAW,MAAA,EAAsD;AAC/E,EAAA,MAAM,OAAgC,EAAC;AACvC,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,KAAA,CAAM,iBAAiB,MAAA,EAAW;AACpC,MAAA,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,GAAI,KAAA,CAAM,YAAA;AAAA,IAC3B,CAAA,MAAA,IAAW,KAAA,CAAM,IAAA,KAAS,QAAA,IAAY,MAAM,QAAA,EAAU;AACpD,MAAA,MAAM,MAAA,GAAS,UAAA,CAAW,KAAA,CAAM,MAAM,CAAA;AACtC,MAAA,IAAI,MAAA,CAAO,KAAK,MAAM,CAAA,CAAE,SAAS,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,GAAI,MAAA;AAAA,IACzD;AAAA,EACF;AACA,EAAA,OAAO,IAAA;AACT;AAYO,SAAS,gBAAgB,KAAA,EAAgD;AAC9E,EAAA,IAAI,QAAA,CAAS,MAAM,YAAY,CAAA,SAAU,EAAE,GAAG,MAAM,YAAA,EAAa;AACjE,EAAA,OAAO,UAAA,CAAW,MAAM,MAAM,CAAA;AAChC;AAOO,SAAS,sBAAA,CACd,QACA,MAAA,EACyB;AACzB,EAAA,MAAM,GAAA,GAA+B,EAAE,GAAG,MAAA,EAAO;AACjD,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,mBAAA,CAAoB,KAAK,CAAA,IAAK,CAAC,qBAAA,CAAsB,QAAQ,MAAA,EAAQ,KAAA,CAAM,IAAI,CAAC,CAAA,EAAG;AACrF,MAAA,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA,GAAI,eAAA,CAAgB,KAAK,CAAA;AAAA,IACzC;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AAOO,SAAS,uBAAA,CACd,QACA,MAAA,EACyB;AACzB,EAAA,MAAM,GAAA,GAA+B,EAAE,GAAG,MAAA,EAAO;AACjD,EAAA,KAAA,MAAW,KAAA,IAAS,QAAQ,IAAI,mBAAA,CAAoB,KAAK,CAAA,EAAG,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA,GAAI,MAAA;AAC9E,EAAA,OAAO,GAAA;AACT","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/core/seed.ts"],"names":[],"mappings":";;;;AAWA,SAAS,SAAS,KAAA,EAAkD;AAClE,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;AAoBO,SAAS,WAAW,MAAA,EAAsD;AAC/E,EAAA,MAAM,OAAgC,EAAC;AACvC,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,KAAA,CAAM,iBAAiB,MAAA,EAAW;AACpC,MAAA,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,GAAI,KAAA,CAAM,YAAA;AAAA,IAC3B,CAAA,MAAA,IAAW,KAAA,CAAM,IAAA,KAAS,QAAA,IAAY,MAAM,QAAA,EAAU;AACpD,MAAA,MAAM,MAAA,GAAS,UAAA,CAAW,KAAA,CAAM,MAAM,CAAA;AACtC,MAAA,IAAI,MAAA,CAAO,KAAK,MAAM,CAAA,CAAE,SAAS,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,GAAI,MAAA;AAAA,IACzD;AAAA,EACF;AACA,EAAA,OAAO,IAAA;AACT;AAYO,SAAS,gBAAgB,KAAA,EAAgD;AAC9E,EAAA,IAAI,QAAA,CAAS,MAAM,YAAY,CAAA,SAAU,EAAE,GAAG,MAAM,YAAA,EAAa;AACjE,EAAA,OAAO,UAAA,CAAW,MAAM,MAAM,CAAA;AAChC;AAOO,SAAS,sBAAA,CACd,QACA,MAAA,EACyB;AACzB,EAAA,MAAM,GAAA,GAA+B,EAAE,GAAG,MAAA,EAAO;AACjD,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,mBAAA,CAAoB,KAAK,CAAA,IAAK,CAAC,qBAAA,CAAsB,QAAQ,MAAA,EAAQ,KAAA,CAAM,IAAI,CAAC,CAAA,EAAG;AACrF,MAAA,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA,GAAI,eAAA,CAAgB,KAAK,CAAA;AAAA,IACzC;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AAOO,SAAS,uBAAA,CACd,QACA,MAAA,EACyB;AACzB,EAAA,MAAM,GAAA,GAA+B,EAAE,GAAG,MAAA,EAAO;AACjD,EAAA,KAAA,MAAW,KAAA,IAAS,QAAQ,IAAI,mBAAA,CAAoB,KAAK,CAAA,EAAG,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA,GAAI,MAAA;AAC9E,EAAA,OAAO,GAAA;AACT","file":"chunk-AURDKAIC.js","sourcesContent":["/**\n * Seeding a form from the method's authored defaults.\n *\n * Pure module: no React, no DOM - a server building a payload and a control\n * opening an optional structure read the same answer from here.\n */\n\nimport type { ObjectRunField, RunField } from './descriptor';\nimport { ownProp } from './own-property';\nimport { isOptionalStructure, optionalStructureOpen } from './readiness';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * The `/inputs` seed: the authored defaults, and nothing else.\n *\n * A defaulted field carries `defaultValue` (never the `null` a schema\n * projection attaches to an optional field); a structure's defaults sit inside\n * it. What is NOT seeded is an empty string or a zero for an unfilled field -\n * the kernel's readiness treats an absent value as absent, and a seeded\n * placeholder would count as filled.\n *\n * **An optional structure is not descended into.** Its presence is a choice the\n * person makes with the \"+ N optional\" disclosure it folds behind (see\n * `isOptionalStructure`), so a seed that put its children's defaults inside it would make that choice\n * for them: the structure would arrive open and be sent, merely because its\n * concept declares defaults. It is seeded only when the structure itself\n * carries an authored default; otherwise it stays absent, and turning it on\n * seeds it then, through `seedObjectValue`. A host that wants it open from the\n * start puts `seedObjectValue(field)` at its path.\n */\nexport function seedInputs(fields: readonly RunField[]): Record<string, unknown> {\n const seed: Record<string, unknown> = {};\n for (const field of fields) {\n if (field.defaultValue !== undefined) {\n seed[field.name] = field.defaultValue;\n } else if (field.kind === 'object' && field.required) {\n const nested = seedInputs(field.fields);\n if (Object.keys(nested).length > 0) seed[field.name] = nested;\n }\n }\n return seed;\n}\n\n/**\n * The value a structured field holds the moment it is opened: the\n * structure's own authored default when it carries an object one, otherwise\n * an object seeded from its children's defaults by `seedInputs` - which is `{}`\n * when none of them declares one. Always a plain object, so the disclosure that\n * reads the value back (`optionalStructureOpen`) reads open.\n *\n * Optional structures beneath it stay absent, by `seedInputs`' own rule: each\n * opens with its own container's disclosure.\n */\nexport function seedObjectValue(field: ObjectRunField): Record<string, unknown> {\n if (isRecord(field.defaultValue)) return { ...field.defaultValue };\n return seedInputs(field.fields);\n}\n\n/**\n * What expanding a \"+ N optional\" disclosure writes: `values` with every\n * CLOSED optional structure among `fields` opened to `seedObjectValue(field)`.\n * Open ones and every other field are kept as they are. Returns a new object.\n */\nexport function openOptionalStructures(\n fields: readonly RunField[],\n values: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...values };\n for (const field of fields) {\n if (isOptionalStructure(field) && !optionalStructureOpen(ownProp(values, field.name))) {\n out[field.name] = seedObjectValue(field);\n }\n }\n return out;\n}\n\n/**\n * What collapsing a \"+ N optional\" disclosure writes: `values` with every\n * optional structure among `fields` closed to `undefined`, so each leaves the\n * payload. Every other field is kept as it is. Returns a new object.\n */\nexport function closeOptionalStructures(\n fields: readonly RunField[],\n values: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...values };\n for (const field of fields) if (isOptionalStructure(field)) out[field.name] = undefined;\n return out;\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildRunFields } from './chunk-ZB4RYUKV.js';
|
|
2
2
|
import { inflateAllInputs, deflateAllInputs, isWrappedForm } from './chunk-E6BIXJ6C.js';
|
|
3
|
-
import { isFilled, isOptionalStructure, optionalStructureOpen } from './chunk-
|
|
3
|
+
import { isFilled, isOptionalStructure, optionalStructureOpen } from './chunk-TF4RTKLH.js';
|
|
4
4
|
import { isOptionalInput, isPluralInput } from './chunk-WYH56SOZ.js';
|
|
5
5
|
import { ownProp } from './chunk-V3OV2XQT.js';
|
|
6
6
|
|
|
@@ -210,5 +210,5 @@ function setValueAtPath(root, path, value) {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
export { apiInputsFromRunValues, fieldsForContract, inputDataFromWorkingMemory, outputsFromPipeOutput, rjsfDataFromRunValues, runValuesFromInputs, runValuesFromStore, sameRunValues, setValueAtPath, storeInputDataFromRunValues };
|
|
213
|
-
//# sourceMappingURL=chunk-
|
|
214
|
-
//# sourceMappingURL=chunk-
|
|
213
|
+
//# sourceMappingURL=chunk-CFBK7NO7.js.map
|
|
214
|
+
//# sourceMappingURL=chunk-CFBK7NO7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/values.ts"],"names":[],"mappings":"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","file":"chunk-ZX52CQJ4.js","sourcesContent":["/**\n * Bridges the `MethodRunner`'s field values to the rest of the host.\n *\n * The method-store keeps inputs in the *simplified* format and runs send the\n * *full* RJSF form shape - both already handled by `inflate/deflateAllInputs`.\n * The only gap is that a few field controls use a friendlier value than RJSF: a\n * scalar is the bare scalar where the full shape is the content model its\n * concept declares (`{ text }`, `{ number }`, `{ yes_no }`), and a file is\n * `{ url, filename }`. `fromRjsf`/`toRjsf` translate just those, so store\n * reads/writes and run payloads stay byte-for-byte compatible with the proven\n * playground path.\n *\n * Which property a scalar wraps into is NOT decided here - it arrives on the\n * descriptor as `contentKey`, derived from the contract by `buildRunFields`.\n *\n * Pure module - no React, unit-tested.\n */\nimport type { PipeInputFormDescriptor } from 'mthds/protocol';\n\nimport type { RunField } from './descriptor';\nimport { buildRunFields } from './derive';\nimport { ownProp } from './own-property';\nimport { isFilled, isOptionalStructure, optionalStructureOpen } from './readiness';\nimport { isOptionalInput, isPluralInput } from './contracts';\nimport { deflateAllInputs, inflateAllInputs, isWrappedForm } from './wire-format';\nimport type { PipeIOContract, PipeInputContract } from './contracts';\n\ntype Dict = Record<string, unknown>;\ntype InputSchemas = Record<string, PipeInputContract>;\n\n// ─── RJSF full shape ↔ RunField value ──────────────────────────────────────\n\n/**\n * Put a scalar back inside the content model its concept declares:\n * `\"hi\"` → `{ text: \"hi\" }`, `2` → `{ number: 2 }`, `false` → `{ yes_no: false }`.\n *\n * The property name comes from the DESCRIPTOR (`contentKey`, stamped by\n * `buildRunFields` - the only reader of JSON Schema). This module holds no\n * taxonomy of its own, which is the point: the version that did held a list of\n * concepts that wrap, `native.Number` was missing from it, and every run\n * carrying a number was rejected by the gate against `NumberContent`.\n *\n * A field with no `contentKey` keeps its value plain. That is not an oversight\n * either - a child property of a structured concept, or a custom concept whose\n * schema is a bare string, IS its value, and wrapping it double-nests it\n * (`{ text: { text } }`) into a payload no schema accepts.\n */\nfunction wrapContent(field: RunField, value: unknown): unknown {\n return field.contentKey === undefined ? value : { [field.contentKey]: value };\n}\n\n/**\n * The inverse. A value that is already bare passes through untouched: persisted\n * `inputData` predates the number and yes/no wrappers, and an inputs.json\n * written by hand or by an agent says `2`, not `{number: 2}`.\n */\nfunction unwrapContent(field: RunField, value: unknown): unknown {\n const key = field.contentKey;\n if (key === undefined || !value || typeof value !== 'object' || Array.isArray(value))\n return value;\n return ownProp(value as Record<string, unknown>, key);\n}\n\n/** How deep a nested `{ text: … }` / `{ content: … }` chain we bother unwrapping. */\nconst MAX_UNWRAP_DEPTH = 8;\n\n/**\n * Pull a plain string out of whatever the store hands a text field.\n *\n * Persisted `inputData` carries historical corruptions - a double-wrapped\n * `{ text: { text } }`, a `{ concept, content }` envelope that was never\n * deflated - and the old fallback ran `String()` over them, which renders the\n * literal `\"[object Object]\"` into the textarea. That is worse than showing\n * nothing: it looks exactly like content the user typed, and one keystroke\n * later it is content the user typed. So unwrap the envelopes we know, and give\n * up with `undefined` (an empty field) rather than displaying garbage.\n *\n * Genuine primitives still stringify - a number or a boolean in a text slot is\n * a value, not a wrapper.\n *\n * The envelope names here are the HISTORICAL corruptions, not the wrapper key:\n * the declared wrapper is stripped by `unwrapContent` first, off the field's\n * `contentKey`, exactly as the number and boolean paths do. A text wrapper\n * whose property is not literally `text` would otherwise read back empty - and\n * one save later, BE empty.\n */\nfunction textFromStored(value: unknown, depth = 0): string | undefined {\n if (value == null) return undefined;\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean') return String(value);\n if (typeof value !== 'object' || depth >= MAX_UNWRAP_DEPTH) return undefined;\n const envelope = value as { text?: unknown; content?: unknown };\n if ('text' in envelope) return textFromStored(envelope.text, depth + 1);\n if ('content' in envelope) return textFromStored(envelope.content, depth + 1);\n return undefined;\n}\n\n/** RJSF full form value → the runner's field value. */\nfunction fromRjsf(field: RunField, value: unknown): unknown {\n switch (field.kind) {\n case 'text':\n case 'prose':\n return textFromStored(unwrapContent(field, value));\n case 'number': {\n // The control holds `number | undefined`. A numeric STRING is tolerated\n // because that is what a hand-written inputs.json tends to carry; anything\n // else empties the field rather than feeding NaN to a number input.\n const raw = unwrapContent(field, value);\n if (typeof raw === 'number') return Number.isFinite(raw) ? raw : undefined;\n if (typeof raw === 'string' && raw.trim() !== '') {\n const parsed = Number(raw);\n return Number.isFinite(parsed) ? parsed : undefined;\n }\n return undefined;\n }\n case 'boolean': {\n // Strictly a real boolean, deliberately: pipelex declares `YesNoContent.\n // yes_no` with `strict=True` precisely to refuse \"yes\"/1/\"true\", and\n // coercing here would smuggle back in what the runtime closed off.\n const raw = unwrapContent(field, value);\n return typeof raw === 'boolean' ? raw : undefined;\n }\n case 'unknown':\n // The escape hatch is a raw-JSON textarea, so a structured value belongs\n // there as JSON - `String(value)` would have shown \"[object Object]\" and\n // thrown the actual value away.\n if (typeof value === 'string') return value;\n if (value == null) return undefined;\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return undefined;\n }\n }\n return String(value);\n case 'document':\n case 'image': {\n if (value && typeof value === 'object') {\n const o = value as { url?: unknown; filename?: unknown };\n return o.url\n ? { url: String(o.url), filename: o.filename as string | undefined }\n : undefined;\n }\n return typeof value === 'string' && value ? { url: value } : undefined;\n }\n case 'object': {\n // An optional structure the store holds no object for - absent, or the\n // `null` a stored run writes for a structure it went without - stays\n // CLOSED. Materializing a shell of `undefined` children here reopened it:\n // `optionalStructureOpen` reads any plain object as open, so a run reloaded\n // from the store showed every structure it had left out as opened and\n // blank. A required structure has no closed state, and keeps its shell.\n if (isOptionalStructure(field) && !optionalStructureOpen(value)) return undefined;\n const obj = (value && typeof value === 'object' ? value : {}) as Dict;\n const out: Dict = {};\n for (const child of field.fields) out[child.name] = fromRjsf(child, ownProp(obj, child.name));\n return out;\n }\n case 'list': {\n const arr = Array.isArray(value) ? value : [];\n return arr.map((item) => fromRjsf(field.item, item));\n }\n default:\n return value;\n }\n}\n\n/**\n * The runner's field value → RJSF full form value (what runs/store expect).\n *\n * `collapseEmpty` is what a SINGULAR slot does and a LIST ITEM must not: an\n * untouched structure in a singular slot is absent, but an item exists only\n * because the user added it, so adding IS the touch. Only the structure case\n * reads the flag.\n */\nfunction toRjsf(field: RunField, value: unknown, collapseEmpty = true): unknown {\n switch (field.kind) {\n case 'text':\n case 'prose':\n return wrapContent(field, typeof value === 'string' ? value : '');\n case 'number':\n // An untouched number stays ABSENT rather than becoming an empty wrapper.\n // The gate then does the right thing on both counts: it prunes an absent\n // optional input, and it reports a required one as missing by name -\n // where `{number: undefined}` would read as a malformed value instead.\n return typeof value === 'number' && Number.isFinite(value)\n ? wrapContent(field, value)\n : undefined;\n case 'boolean':\n // `false` is a value, not an absence - hence the type test rather than a\n // truthiness one.\n return typeof value === 'boolean' ? wrapContent(field, value) : undefined;\n case 'unknown':\n return typeof value === 'string' ? value : '';\n case 'document':\n case 'image': {\n const url = value && typeof value === 'object' ? (value as { url?: unknown }).url : value;\n const filename =\n value && typeof value === 'object' ? (value as { filename?: unknown }).filename : undefined;\n return { url: url ? String(url) : '', ...(filename ? { filename } : {}) };\n }\n case 'object': {\n const obj = (value && typeof value === 'object' ? value : {}) as Dict;\n const out: Dict = {};\n for (const child of field.fields) out[child.name] = toRjsf(child, ownProp(obj, child.name));\n // A structure nobody put anything into stays ABSENT, exactly as an\n // untouched number does above - it does NOT become a shell of empty\n // children. Materializing that shell made an OPTIONAL structured input\n // unrunnable whenever its concept declared a required child: readiness\n // ignored the input (correctly - the method said it may be omitted), Run\n // lit up, and the gate then judged an object the kernel had invented\n // against the concept's full schema and rejected the run for a child the\n // user was never asked to fill.\n //\n // Emptiness is `isFilled`, the SAME predicate readiness and the wire\n // payload use, so the three cannot disagree about whether this input is\n // there. Which also says what the touch is NOT: opening the optional\n // disclosure is local view state the value never sees, so a section\n // opened and left blank collapses exactly like one never opened. What\n // keeps a structure is a VALUE in it - and then the whole shell survives,\n // empty children and all, so a required child left blank still fails, and\n // loudly.\n return collapseEmpty && !isFilled(out) ? undefined : out;\n }\n case 'list': {\n const arr = Array.isArray(value) ? value : [];\n // An item is NEVER absent: it is in the array because the user added it,\n // and `ListField`'s \"Add\" seeds an object item with `{}`. Letting it\n // collapse the way an untouched singular slot does put `undefined` in the\n // array, which ajv rejects as `must be object` - blocking an empty item\n // the item schema allowed, and turning a required-child complaint into a\n // type error that names nothing the user can act on.\n return arr.map((item) => toRjsf(field.item, item, false));\n }\n default:\n return value;\n }\n}\n\n// ─── Store ↔ RunField values ──────────────────────────────────────────────\n\n/** Seed the runner's form values from the method-store's simplified inputData. */\nexport function runValuesFromStore(\n inputData: Dict | undefined,\n fields: RunField[],\n inputSchemas: InputSchemas,\n): Dict {\n const full = inflateAllInputs(inputData ?? {}, inputSchemas);\n const out: Dict = {};\n for (const field of fields) out[field.name] = fromRjsf(field, ownProp(full, field.name));\n return out;\n}\n\n/**\n * The runner's form values in **schema shape** - the same shape an RJSF panel\n * holds, and therefore the shape `gate.ts` validates against the per-input\n * JSON Schemas.\n *\n * Deliberately stops short of `deflateAllInputs`: deflation is the STORE's\n * compact form, and ajv validates the full one.\n */\nexport function rjsfDataFromRunValues(values: Dict, fields: RunField[]): Dict {\n const full: Dict = {};\n for (const field of fields) full[field.name] = toRjsf(field, ownProp(values, field.name));\n return full;\n}\n\n/** Convert the runner's form values back to the store's simplified inputData. */\nexport function storeInputDataFromRunValues(\n values: Dict,\n fields: RunField[],\n inputSchemas: InputSchemas,\n): Dict {\n return deflateAllInputs(rjsfDataFromRunValues(values, fields), inputSchemas);\n}\n\n/**\n * Build the `{ concept, content }` map a run expects from the form values.\n *\n * The two ways a method says \"this may be nothing\" land differently on the wire,\n * and both matter:\n * - an **optional (`?`)** input left blank is OMITTED, so the runtime records a\n * real absence the method can branch on - sending `{ text: \"\" }` would look\n * like the caller supplied an empty string;\n * - a **plural (`[]`)** input keeps its key, because a plural slot is never\n * absent - its empty form IS the empty list, and the runtime requires the key.\n * When that list is EMPTY it is sent BARE, without the envelope: the envelope\n * is the runtime's \"explicit form\", which bypasses the top-down shaper and\n * hands the value to the bottom-up factory, and that factory types a list from\n * its first item - so an empty list there raises \"Cannot create Stuff from\n * empty list in content\". The bare form keeps the shaper, which reads the\n * DECLARED concept and builds the empty list correctly. (pipelex/#1096 makes\n * the envelope agree; the bare form runs today and stays correct after.)\n */\nexport function apiInputsFromRunValues(\n values: Dict,\n fields: RunField[],\n inputSchemas: InputSchemas,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const field of fields) {\n const schema = ownProp(inputSchemas, field.name);\n if (!schema) continue;\n const value = ownProp(values, field.name);\n if (isOptionalInput(schema) && !isFilled(value)) continue;\n if (isPluralInput(schema) && !isFilled(value)) {\n out[field.name] = [];\n continue;\n }\n out[field.name] = { concept: schema.concept_ref, content: toRjsf(field, value) };\n }\n return out;\n}\n\n/**\n * The form values a set of run inputs stands for: the inverse of\n * `apiInputsFromRunValues`, for inputs saved from a form (an example) and\n * handed back to one.\n *\n * Each input is read as a run sends it, `{ concept, content }`, or as its bare\n * content (the empty plural `[]`), and its content is turned back into the\n * control's own shape. Only an object naming its concept is read as that\n * envelope: a structure sent bare may well have a property called `content`. An input the inputs do not hold is left empty, and an\n * optional structure they do not hold stays closed: the inputs are the whole\n * run, so an input they left out was left out of the run too. An input no\n * field takes is ignored.\n */\nexport function runValuesFromInputs(inputs: Readonly<Dict> | undefined, fields: RunField[]): Dict {\n const out: Dict = {};\n for (const field of fields) {\n const entry = ownProp(inputs ?? {}, field.name);\n const content = isWrappedForm(entry) ? entry.content : entry;\n out[field.name] = fromRjsf(field, content);\n }\n return out;\n}\n\n/**\n * Whether two sets of form values send the same run.\n *\n * Compared in the shape a run takes, so the differences a run cannot see do\n * not count: a text typed and then erased is the blank it started as, and an\n * optional structure opened and left empty is the closed one it was. What\n * \"Fill with an example\" asks before replacing is the work a person did, and\n * a value that sends nothing is no work to lose.\n */\nexport function sameRunValues(a: Dict, b: Dict, fields: RunField[]): boolean {\n return (\n JSON.stringify(rjsfDataFromRunValues(a, fields)) ===\n JSON.stringify(rjsfDataFromRunValues(b, fields))\n );\n}\n\n// ─── Pipe output → result entries ────────────────────────────────────────────\n\n/** One named output pulled out of a run's working memory, ready to render. */\nexport interface OutputEntry {\n /** The output variable key (mono). */\n key: string;\n conceptRef?: string;\n value: unknown;\n}\n\n/**\n * The slice of a serialized pipe output this module actually reads. Structural\n * on purpose: the SDK's `DictPipeOutput` is the host's wire type, and the kernel\n * must not depend on `@pipelex/sdk` - the guard below verifies the shape at\n * runtime anyway.\n */\ninterface PipeOutputWithWorkingMemory {\n working_memory: {\n root: Record<string, unknown>;\n aliases?: Record<string, string>;\n };\n}\n\nfunction hasWorkingMemoryRoot(value: unknown): value is PipeOutputWithWorkingMemory {\n return (\n !!value &&\n typeof value === 'object' &&\n 'working_memory' in value &&\n !!(value as { working_memory?: { root?: unknown } }).working_memory &&\n typeof (value as { working_memory: { root?: unknown } }).working_memory.root === 'object'\n );\n}\n\n/** The concept code of a working-memory entry (`{concept}` may be obj/str/absent). */\nfunction entryConceptRef(data: unknown): string | undefined {\n if (!data || typeof data !== 'object') return undefined;\n const raw = (data as { concept?: unknown; concept_code?: unknown }).concept;\n if (raw && typeof raw === 'object')\n return (\n ((raw as { concept_code?: string }).concept_code ?? (raw as { code?: string }).code) ||\n undefined\n );\n if (typeof raw === 'string') return raw;\n return (data as { concept_code?: string }).concept_code || undefined;\n}\n\n/**\n * Split a run's pipe output into the main result and any intermediate outputs,\n * excluding the inputs. The main output's identity comes from the working\n * memory's `main_stuff` alias (the authoritative pointer to the output variable)\n * - so its label is the REAL stuff name, not a positional guess. Falls back to\n * the last produced stuff only when the alias is absent.\n */\nexport function outputsFromPipeOutput(\n pipeOutput: unknown,\n inputKeys: string[],\n): { main: OutputEntry; intermediates: OutputEntry[] } | null {\n if (!hasWorkingMemoryRoot(pipeOutput)) return null;\n const wm = pipeOutput.working_memory as {\n root: Record<string, unknown>;\n aliases?: Record<string, string>;\n };\n const entries = Object.entries(wm.root).filter(([key]) => !inputKeys.includes(key));\n if (entries.length === 0) return null;\n\n const toEntry = ([key, data]: [string, unknown]): OutputEntry => ({\n key,\n conceptRef: entryConceptRef(data),\n value: (data as { content?: unknown })?.content ?? data,\n });\n\n // The real main-output name: `aliases.main_stuff` → its root key.\n const mainKey = wm.aliases?.['main_stuff'];\n const mainEntry: [string, unknown] =\n mainKey && Object.prototype.hasOwnProperty.call(wm.root, mainKey)\n ? [mainKey, wm.root[mainKey]]\n : entries[entries.length - 1]!;\n const intermediates = entries.filter(([key]) => key !== mainEntry[0]).map(toEntry);\n\n return { main: toEntry(mainEntry), intermediates };\n}\n\n/**\n * Recover the inputs a run was executed with from its working memory. The run's\n * `working_memory.root` holds every named stuff - inputs AND outputs - keyed by\n * variable name; we pull the entries whose keys are declared inputs and deflate\n * their `content` (the full form shape) back into the store's simplified\n * `inputData`. Lets the runner repopulate the form for a past/shared run - the\n * inputs that produced the result. Returns `null` when no inputs are present.\n */\nexport function inputDataFromWorkingMemory(\n workingMemory: unknown,\n inputSchemas: InputSchemas,\n): Dict | null {\n const root = (workingMemory as { root?: Record<string, unknown> } | null | undefined)?.root;\n if (!root || typeof root !== 'object') return null;\n const full: Dict = {};\n let found = false;\n for (const key of Object.keys(inputSchemas)) {\n const entry = ownProp(root, key);\n if (entry && typeof entry === 'object' && 'content' in entry) {\n full[key] = (entry as { content: unknown }).content;\n found = true;\n }\n }\n if (!found) return null;\n return deflateAllInputs(full, inputSchemas);\n}\n\n/**\n * Field descriptors for a method's main pipe - the wire descriptor mapped over\n * the contract, or `[]` while either artifact has not arrived yet. The\n * descriptor comes back from `validate` beside `pipe_io_contracts` when the\n * request opts in with `views: [\"input_form\"]`; `getPipeInputForm` is the\n * lookup that matches `getPipeIOContract`.\n */\nexport function fieldsForContract(\n contract: PipeIOContract | undefined,\n descriptor: PipeInputFormDescriptor | undefined,\n): RunField[] {\n return contract?.inputs && descriptor ? buildRunFields(descriptor, contract.inputs) : [];\n}\n\n/**\n * Immutably set a value at a dotted/indexed path (`a.0.b`). Used to write an\n * uploaded file's URL back to a field anywhere in the (possibly nested) tree.\n */\nexport function setValueAtPath(root: Dict, path: string[], value: unknown): Dict {\n const head = path[0];\n if (head === undefined) return root;\n const rest = path.slice(1);\n if (rest.length === 0) return { ...root, [head]: value };\n\n const nextSeg = rest[0] as string;\n const child = root[head];\n if (/^\\d+$/.test(nextSeg)) {\n const arr = Array.isArray(child) ? child.slice() : [];\n const idx = Number(nextSeg);\n arr[idx] =\n rest.length === 1 ? value : setValueAtPath((arr[idx] as Dict) ?? {}, rest.slice(1), value);\n return { ...root, [head]: arr };\n }\n return { ...root, [head]: setValueAtPath((child as Dict) ?? {}, rest, value) };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/values.ts"],"names":[],"mappings":"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","file":"chunk-CFBK7NO7.js","sourcesContent":["/**\n * Bridges the `MethodRunner`'s field values to the rest of the host.\n *\n * The method-store keeps inputs in the *simplified* format and runs send the\n * *full* RJSF form shape - both already handled by `inflate/deflateAllInputs`.\n * The only gap is that a few field controls use a friendlier value than RJSF: a\n * scalar is the bare scalar where the full shape is the content model its\n * concept declares (`{ text }`, `{ number }`, `{ yes_no }`), and a file is\n * `{ url, filename }`. `fromRjsf`/`toRjsf` translate just those, so store\n * reads/writes and run payloads stay byte-for-byte compatible with the proven\n * playground path.\n *\n * Which property a scalar wraps into is NOT decided here - it arrives on the\n * descriptor as `contentKey`, derived from the contract by `buildRunFields`.\n *\n * Pure module - no React, unit-tested.\n */\nimport type { PipeInputFormDescriptor } from 'mthds/protocol';\n\nimport type { RunField } from './descriptor';\nimport { buildRunFields } from './derive';\nimport { ownProp } from './own-property';\nimport { isFilled, isOptionalStructure, optionalStructureOpen } from './readiness';\nimport { isOptionalInput, isPluralInput } from './contracts';\nimport { deflateAllInputs, inflateAllInputs, isWrappedForm } from './wire-format';\nimport type { PipeIOContract, PipeInputContract } from './contracts';\n\ntype Dict = Record<string, unknown>;\ntype InputSchemas = Record<string, PipeInputContract>;\n\n// ─── RJSF full shape ↔ RunField value ──────────────────────────────────────\n\n/**\n * Put a scalar back inside the content model its concept declares:\n * `\"hi\"` → `{ text: \"hi\" }`, `2` → `{ number: 2 }`, `false` → `{ yes_no: false }`.\n *\n * The property name comes from the DESCRIPTOR (`contentKey`, stamped by\n * `buildRunFields` - the only reader of JSON Schema). This module holds no\n * taxonomy of its own, which is the point: the version that did held a list of\n * concepts that wrap, `native.Number` was missing from it, and every run\n * carrying a number was rejected by the gate against `NumberContent`.\n *\n * A field with no `contentKey` keeps its value plain. That is not an oversight\n * either - a child property of a structured concept, or a custom concept whose\n * schema is a bare string, IS its value, and wrapping it double-nests it\n * (`{ text: { text } }`) into a payload no schema accepts.\n */\nfunction wrapContent(field: RunField, value: unknown): unknown {\n return field.contentKey === undefined ? value : { [field.contentKey]: value };\n}\n\n/**\n * The inverse. A value that is already bare passes through untouched: persisted\n * `inputData` predates the number and yes/no wrappers, and an inputs.json\n * written by hand or by an agent says `2`, not `{number: 2}`.\n */\nfunction unwrapContent(field: RunField, value: unknown): unknown {\n const key = field.contentKey;\n if (key === undefined || !value || typeof value !== 'object' || Array.isArray(value))\n return value;\n return ownProp(value as Record<string, unknown>, key);\n}\n\n/** How deep a nested `{ text: … }` / `{ content: … }` chain we bother unwrapping. */\nconst MAX_UNWRAP_DEPTH = 8;\n\n/**\n * Pull a plain string out of whatever the store hands a text field.\n *\n * Persisted `inputData` carries historical corruptions - a double-wrapped\n * `{ text: { text } }`, a `{ concept, content }` envelope that was never\n * deflated - and the old fallback ran `String()` over them, which renders the\n * literal `\"[object Object]\"` into the textarea. That is worse than showing\n * nothing: it looks exactly like content the user typed, and one keystroke\n * later it is content the user typed. So unwrap the envelopes we know, and give\n * up with `undefined` (an empty field) rather than displaying garbage.\n *\n * Genuine primitives still stringify - a number or a boolean in a text slot is\n * a value, not a wrapper.\n *\n * The envelope names here are the HISTORICAL corruptions, not the wrapper key:\n * the declared wrapper is stripped by `unwrapContent` first, off the field's\n * `contentKey`, exactly as the number and boolean paths do. A text wrapper\n * whose property is not literally `text` would otherwise read back empty - and\n * one save later, BE empty.\n */\nfunction textFromStored(value: unknown, depth = 0): string | undefined {\n if (value == null) return undefined;\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean') return String(value);\n if (typeof value !== 'object' || depth >= MAX_UNWRAP_DEPTH) return undefined;\n const envelope = value as { text?: unknown; content?: unknown };\n if ('text' in envelope) return textFromStored(envelope.text, depth + 1);\n if ('content' in envelope) return textFromStored(envelope.content, depth + 1);\n return undefined;\n}\n\n/** RJSF full form value → the runner's field value. */\nfunction fromRjsf(field: RunField, value: unknown): unknown {\n switch (field.kind) {\n case 'text':\n case 'prose':\n return textFromStored(unwrapContent(field, value));\n case 'number': {\n // The control holds `number | undefined`. A numeric STRING is tolerated\n // because that is what a hand-written inputs.json tends to carry; anything\n // else empties the field rather than feeding NaN to a number input.\n const raw = unwrapContent(field, value);\n if (typeof raw === 'number') return Number.isFinite(raw) ? raw : undefined;\n if (typeof raw === 'string' && raw.trim() !== '') {\n const parsed = Number(raw);\n return Number.isFinite(parsed) ? parsed : undefined;\n }\n return undefined;\n }\n case 'boolean': {\n // Strictly a real boolean, deliberately: pipelex declares `YesNoContent.\n // yes_no` with `strict=True` precisely to refuse \"yes\"/1/\"true\", and\n // coercing here would smuggle back in what the runtime closed off.\n const raw = unwrapContent(field, value);\n return typeof raw === 'boolean' ? raw : undefined;\n }\n case 'unknown':\n // The escape hatch is a raw-JSON textarea, so a structured value belongs\n // there as JSON - `String(value)` would have shown \"[object Object]\" and\n // thrown the actual value away.\n if (typeof value === 'string') return value;\n if (value == null) return undefined;\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return undefined;\n }\n }\n return String(value);\n case 'document':\n case 'image': {\n if (value && typeof value === 'object') {\n const o = value as { url?: unknown; filename?: unknown };\n return o.url\n ? { url: String(o.url), filename: o.filename as string | undefined }\n : undefined;\n }\n return typeof value === 'string' && value ? { url: value } : undefined;\n }\n case 'object': {\n // An optional structure the store holds no object for - absent, or the\n // `null` a stored run writes for a structure it went without - stays\n // CLOSED. Materializing a shell of `undefined` children here reopened it:\n // `optionalStructureOpen` reads any plain object as open, so a run reloaded\n // from the store showed every structure it had left out as opened and\n // blank. A required structure has no closed state, and keeps its shell.\n if (isOptionalStructure(field) && !optionalStructureOpen(value)) return undefined;\n const obj = (value && typeof value === 'object' ? value : {}) as Dict;\n const out: Dict = {};\n for (const child of field.fields) out[child.name] = fromRjsf(child, ownProp(obj, child.name));\n return out;\n }\n case 'list': {\n const arr = Array.isArray(value) ? value : [];\n return arr.map((item) => fromRjsf(field.item, item));\n }\n default:\n return value;\n }\n}\n\n/**\n * The runner's field value → RJSF full form value (what runs/store expect).\n *\n * `collapseEmpty` is what a SINGULAR slot does and a LIST ITEM must not: an\n * untouched structure in a singular slot is absent, but an item exists only\n * because the user added it, so adding IS the touch. Only the structure case\n * reads the flag.\n */\nfunction toRjsf(field: RunField, value: unknown, collapseEmpty = true): unknown {\n switch (field.kind) {\n case 'text':\n case 'prose':\n return wrapContent(field, typeof value === 'string' ? value : '');\n case 'number':\n // An untouched number stays ABSENT rather than becoming an empty wrapper.\n // The gate then does the right thing on both counts: it prunes an absent\n // optional input, and it reports a required one as missing by name -\n // where `{number: undefined}` would read as a malformed value instead.\n return typeof value === 'number' && Number.isFinite(value)\n ? wrapContent(field, value)\n : undefined;\n case 'boolean':\n // `false` is a value, not an absence - hence the type test rather than a\n // truthiness one.\n return typeof value === 'boolean' ? wrapContent(field, value) : undefined;\n case 'unknown':\n return typeof value === 'string' ? value : '';\n case 'document':\n case 'image': {\n const url = value && typeof value === 'object' ? (value as { url?: unknown }).url : value;\n const filename =\n value && typeof value === 'object' ? (value as { filename?: unknown }).filename : undefined;\n return { url: url ? String(url) : '', ...(filename ? { filename } : {}) };\n }\n case 'object': {\n const obj = (value && typeof value === 'object' ? value : {}) as Dict;\n const out: Dict = {};\n for (const child of field.fields) out[child.name] = toRjsf(child, ownProp(obj, child.name));\n // A structure nobody put anything into stays ABSENT, exactly as an\n // untouched number does above - it does NOT become a shell of empty\n // children. Materializing that shell made an OPTIONAL structured input\n // unrunnable whenever its concept declared a required child: readiness\n // ignored the input (correctly - the method said it may be omitted), Run\n // lit up, and the gate then judged an object the kernel had invented\n // against the concept's full schema and rejected the run for a child the\n // user was never asked to fill.\n //\n // Emptiness is `isFilled`, the SAME predicate readiness and the wire\n // payload use, so the three cannot disagree about whether this input is\n // there. Which also says what the touch is NOT: opening the optional\n // disclosure is local view state the value never sees, so a section\n // opened and left blank collapses exactly like one never opened. What\n // keeps a structure is a VALUE in it - and then the whole shell survives,\n // empty children and all, so a required child left blank still fails, and\n // loudly.\n return collapseEmpty && !isFilled(out) ? undefined : out;\n }\n case 'list': {\n const arr = Array.isArray(value) ? value : [];\n // An item is NEVER absent: it is in the array because the user added it,\n // and `ListField`'s \"Add\" seeds an object item with `{}`. Letting it\n // collapse the way an untouched singular slot does put `undefined` in the\n // array, which ajv rejects as `must be object` - blocking an empty item\n // the item schema allowed, and turning a required-child complaint into a\n // type error that names nothing the user can act on.\n return arr.map((item) => toRjsf(field.item, item, false));\n }\n default:\n return value;\n }\n}\n\n// ─── Store ↔ RunField values ──────────────────────────────────────────────\n\n/** Seed the runner's form values from the method-store's simplified inputData. */\nexport function runValuesFromStore(\n inputData: Dict | undefined,\n fields: RunField[],\n inputSchemas: InputSchemas,\n): Dict {\n const full = inflateAllInputs(inputData ?? {}, inputSchemas);\n const out: Dict = {};\n for (const field of fields) out[field.name] = fromRjsf(field, ownProp(full, field.name));\n return out;\n}\n\n/**\n * The runner's form values in **schema shape** - the same shape an RJSF panel\n * holds, and therefore the shape `gate.ts` validates against the per-input\n * JSON Schemas.\n *\n * Deliberately stops short of `deflateAllInputs`: deflation is the STORE's\n * compact form, and ajv validates the full one.\n */\nexport function rjsfDataFromRunValues(values: Dict, fields: RunField[]): Dict {\n const full: Dict = {};\n for (const field of fields) full[field.name] = toRjsf(field, ownProp(values, field.name));\n return full;\n}\n\n/** Convert the runner's form values back to the store's simplified inputData. */\nexport function storeInputDataFromRunValues(\n values: Dict,\n fields: RunField[],\n inputSchemas: InputSchemas,\n): Dict {\n return deflateAllInputs(rjsfDataFromRunValues(values, fields), inputSchemas);\n}\n\n/**\n * Build the `{ concept, content }` map a run expects from the form values.\n *\n * The two ways a method says \"this may be nothing\" land differently on the wire,\n * and both matter:\n * - an **optional (`?`)** input left blank is OMITTED, so the runtime records a\n * real absence the method can branch on - sending `{ text: \"\" }` would look\n * like the caller supplied an empty string;\n * - a **plural (`[]`)** input keeps its key, because a plural slot is never\n * absent - its empty form IS the empty list, and the runtime requires the key.\n * When that list is EMPTY it is sent BARE, without the envelope: the envelope\n * is the runtime's \"explicit form\", which bypasses the top-down shaper and\n * hands the value to the bottom-up factory, and that factory types a list from\n * its first item - so an empty list there raises \"Cannot create Stuff from\n * empty list in content\". The bare form keeps the shaper, which reads the\n * DECLARED concept and builds the empty list correctly. (pipelex/#1096 makes\n * the envelope agree; the bare form runs today and stays correct after.)\n */\nexport function apiInputsFromRunValues(\n values: Dict,\n fields: RunField[],\n inputSchemas: InputSchemas,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const field of fields) {\n const schema = ownProp(inputSchemas, field.name);\n if (!schema) continue;\n const value = ownProp(values, field.name);\n if (isOptionalInput(schema) && !isFilled(value)) continue;\n if (isPluralInput(schema) && !isFilled(value)) {\n out[field.name] = [];\n continue;\n }\n out[field.name] = { concept: schema.concept_ref, content: toRjsf(field, value) };\n }\n return out;\n}\n\n/**\n * The form values a set of run inputs stands for: the inverse of\n * `apiInputsFromRunValues`, for inputs saved from a form (an example) and\n * handed back to one.\n *\n * Each input is read as a run sends it, `{ concept, content }`, or as its bare\n * content (the empty plural `[]`), and its content is turned back into the\n * control's own shape. Only an object naming its concept is read as that\n * envelope: a structure sent bare may well have a property called `content`. An input the inputs do not hold is left empty, and an\n * optional structure they do not hold stays closed: the inputs are the whole\n * run, so an input they left out was left out of the run too. An input no\n * field takes is ignored.\n */\nexport function runValuesFromInputs(inputs: Readonly<Dict> | undefined, fields: RunField[]): Dict {\n const out: Dict = {};\n for (const field of fields) {\n const entry = ownProp(inputs ?? {}, field.name);\n const content = isWrappedForm(entry) ? entry.content : entry;\n out[field.name] = fromRjsf(field, content);\n }\n return out;\n}\n\n/**\n * Whether two sets of form values send the same run.\n *\n * Compared in the shape a run takes, so the differences a run cannot see do\n * not count: a text typed and then erased is the blank it started as, and an\n * optional structure opened and left empty is the closed one it was. What\n * \"Fill with an example\" asks before replacing is the work a person did, and\n * a value that sends nothing is no work to lose.\n */\nexport function sameRunValues(a: Dict, b: Dict, fields: RunField[]): boolean {\n return (\n JSON.stringify(rjsfDataFromRunValues(a, fields)) ===\n JSON.stringify(rjsfDataFromRunValues(b, fields))\n );\n}\n\n// ─── Pipe output → result entries ────────────────────────────────────────────\n\n/** One named output pulled out of a run's working memory, ready to render. */\nexport interface OutputEntry {\n /** The output variable key (mono). */\n key: string;\n conceptRef?: string;\n value: unknown;\n}\n\n/**\n * The slice of a serialized pipe output this module actually reads. Structural\n * on purpose: the SDK's `DictPipeOutput` is the host's wire type, and the kernel\n * must not depend on `@pipelex/sdk` - the guard below verifies the shape at\n * runtime anyway.\n */\ninterface PipeOutputWithWorkingMemory {\n working_memory: {\n root: Record<string, unknown>;\n aliases?: Record<string, string>;\n };\n}\n\nfunction hasWorkingMemoryRoot(value: unknown): value is PipeOutputWithWorkingMemory {\n return (\n !!value &&\n typeof value === 'object' &&\n 'working_memory' in value &&\n !!(value as { working_memory?: { root?: unknown } }).working_memory &&\n typeof (value as { working_memory: { root?: unknown } }).working_memory.root === 'object'\n );\n}\n\n/** The concept code of a working-memory entry (`{concept}` may be obj/str/absent). */\nfunction entryConceptRef(data: unknown): string | undefined {\n if (!data || typeof data !== 'object') return undefined;\n const raw = (data as { concept?: unknown; concept_code?: unknown }).concept;\n if (raw && typeof raw === 'object')\n return (\n ((raw as { concept_code?: string }).concept_code ?? (raw as { code?: string }).code) ||\n undefined\n );\n if (typeof raw === 'string') return raw;\n return (data as { concept_code?: string }).concept_code || undefined;\n}\n\n/**\n * Split a run's pipe output into the main result and any intermediate outputs,\n * excluding the inputs. The main output's identity comes from the working\n * memory's `main_stuff` alias (the authoritative pointer to the output variable)\n * - so its label is the REAL stuff name, not a positional guess. Falls back to\n * the last produced stuff only when the alias is absent.\n */\nexport function outputsFromPipeOutput(\n pipeOutput: unknown,\n inputKeys: string[],\n): { main: OutputEntry; intermediates: OutputEntry[] } | null {\n if (!hasWorkingMemoryRoot(pipeOutput)) return null;\n const wm = pipeOutput.working_memory as {\n root: Record<string, unknown>;\n aliases?: Record<string, string>;\n };\n const entries = Object.entries(wm.root).filter(([key]) => !inputKeys.includes(key));\n if (entries.length === 0) return null;\n\n const toEntry = ([key, data]: [string, unknown]): OutputEntry => ({\n key,\n conceptRef: entryConceptRef(data),\n value: (data as { content?: unknown })?.content ?? data,\n });\n\n // The real main-output name: `aliases.main_stuff` → its root key.\n const mainKey = wm.aliases?.['main_stuff'];\n const mainEntry: [string, unknown] =\n mainKey && Object.prototype.hasOwnProperty.call(wm.root, mainKey)\n ? [mainKey, wm.root[mainKey]]\n : entries[entries.length - 1]!;\n const intermediates = entries.filter(([key]) => key !== mainEntry[0]).map(toEntry);\n\n return { main: toEntry(mainEntry), intermediates };\n}\n\n/**\n * Recover the inputs a run was executed with from its working memory. The run's\n * `working_memory.root` holds every named stuff - inputs AND outputs - keyed by\n * variable name; we pull the entries whose keys are declared inputs and deflate\n * their `content` (the full form shape) back into the store's simplified\n * `inputData`. Lets the runner repopulate the form for a past/shared run - the\n * inputs that produced the result. Returns `null` when no inputs are present.\n */\nexport function inputDataFromWorkingMemory(\n workingMemory: unknown,\n inputSchemas: InputSchemas,\n): Dict | null {\n const root = (workingMemory as { root?: Record<string, unknown> } | null | undefined)?.root;\n if (!root || typeof root !== 'object') return null;\n const full: Dict = {};\n let found = false;\n for (const key of Object.keys(inputSchemas)) {\n const entry = ownProp(root, key);\n if (entry && typeof entry === 'object' && 'content' in entry) {\n full[key] = (entry as { content: unknown }).content;\n found = true;\n }\n }\n if (!found) return null;\n return deflateAllInputs(full, inputSchemas);\n}\n\n/**\n * Field descriptors for a method's main pipe - the wire descriptor mapped over\n * the contract, or `[]` while either artifact has not arrived yet. The\n * descriptor comes back from `validate` beside `pipe_io_contracts` when the\n * request opts in with `views: [\"input_form\"]`; `getPipeInputForm` is the\n * lookup that matches `getPipeIOContract`.\n */\nexport function fieldsForContract(\n contract: PipeIOContract | undefined,\n descriptor: PipeInputFormDescriptor | undefined,\n): RunField[] {\n return contract?.inputs && descriptor ? buildRunFields(descriptor, contract.inputs) : [];\n}\n\n/**\n * Immutably set a value at a dotted/indexed path (`a.0.b`). Used to write an\n * uploaded file's URL back to a field anywhere in the (possibly nested) tree.\n */\nexport function setValueAtPath(root: Dict, path: string[], value: unknown): Dict {\n const head = path[0];\n if (head === undefined) return root;\n const rest = path.slice(1);\n if (rest.length === 0) return { ...root, [head]: value };\n\n const nextSeg = rest[0] as string;\n const child = root[head];\n if (/^\\d+$/.test(nextSeg)) {\n const arr = Array.isArray(child) ? child.slice() : [];\n const idx = Number(nextSeg);\n arr[idx] =\n rest.length === 1 ? value : setValueAtPath((arr[idx] as Dict) ?? {}, rest.slice(1), value);\n return { ...root, [head]: arr };\n }\n return { ...root, [head]: setValueAtPath((child as Dict) ?? {}, rest, value) };\n}\n"]}
|
|
@@ -502,13 +502,15 @@ var DEFINITIONS = {
|
|
|
502
502
|
// On a dark page a shadow barely shows, so the panel is lifted by its
|
|
503
503
|
// fill instead, the card a little toward the foreground, with a faint
|
|
504
504
|
// highlight along its top edge where light would catch it, and its
|
|
505
|
-
// fields
|
|
505
|
+
// fields are lifted a step further, so each reads as a control on
|
|
506
|
+
// the panel. At the page's own background they read as wells cut
|
|
507
|
+
// into it.
|
|
506
508
|
dark: {
|
|
507
509
|
fill: role("card", { toward: { role: "foreground", share: 0.07 } }),
|
|
508
510
|
edge: null,
|
|
509
511
|
edgeWidth: 1,
|
|
510
512
|
shadow: "inset 0 1px 0 rgb(255 255 255 / 0.07), 0 2px 4px rgb(0 0 0 / 0.55), 0 24px 56px -12px rgb(0 0 0 / 0.95)",
|
|
511
|
-
field: role("
|
|
513
|
+
field: role("card", { toward: { role: "foreground", share: 0.12 } })
|
|
512
514
|
}
|
|
513
515
|
},
|
|
514
516
|
// A short shadow in the button colour on a light page, kept within the
|
|
@@ -563,8 +565,10 @@ var DEFINITIONS = {
|
|
|
563
565
|
radius: 6,
|
|
564
566
|
// One firm edge around a solid panel, softer on a dark page. The panel
|
|
565
567
|
// is the muted fill, which the host's fields all but match, so fields
|
|
566
|
-
// set into it take the card on a light page and
|
|
567
|
-
//
|
|
568
|
+
// set into it take the card on a light page, and on a dark one the
|
|
569
|
+
// muted fill lifted a little toward the foreground: a step above the
|
|
570
|
+
// panel, as the card is on a light page, where the page's background
|
|
571
|
+
// read as a well cut into it.
|
|
568
572
|
panel: {
|
|
569
573
|
light: {
|
|
570
574
|
fill: role("muted"),
|
|
@@ -578,7 +582,7 @@ var DEFINITIONS = {
|
|
|
578
582
|
edge: role("foreground", { alpha: 0.3 }),
|
|
579
583
|
edgeWidth: 2,
|
|
580
584
|
shadow: null,
|
|
581
|
-
field: role("
|
|
585
|
+
field: role("muted", { toward: { role: "foreground", share: 0.06 } })
|
|
582
586
|
}
|
|
583
587
|
},
|
|
584
588
|
actionShadow: { light: "none", dark: "none" },
|
|
@@ -689,5 +693,5 @@ function fromOklab([lightness, a, b]) {
|
|
|
689
693
|
}
|
|
690
694
|
|
|
691
695
|
export { APP_STYLES, APP_STYLE_IDS, BRAND_CONTRACT, COLOR_TOKEN_NAMES, CONTRAST_PAIRS, MIN_CONTRAST, MIN_CONTROL_CONTRAST, TYPEFACES, aliasTarget, asWritten, colorHex, colorIsHex, contractVariable, contrastRatio, deriveBrandRoles, deriveStyleRoles, isAccentAdjusted, isActionFaint, isAppStyle, isColorTokenName, isControlCharacter, isTypeface, modeValue, readableTextOn, resolveColor, resolveDarkSeeds, resolveRoleColor, srgbColorSchema, validateBrandTokens };
|
|
692
|
-
//# sourceMappingURL=chunk-
|
|
693
|
-
//# sourceMappingURL=chunk-
|
|
696
|
+
//# sourceMappingURL=chunk-PNNWNRPG.js.map
|
|
697
|
+
//# sourceMappingURL=chunk-PNNWNRPG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/brand/contract.ts","../src/brand/tokens.ts","../src/brand/seed-roles.ts","../src/brand/styles.ts"],"names":[],"mappings":"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","file":"chunk-PNNWNRPG.js","sourcesContent":["/**\n * The brand contract, as data: which token of a brand's `tokens.json` sets\n * which custom property, and what that property paints.\n *\n * It is the theme contract seen from the other side. `src/styles/theme.css`\n * states it as a working default and `docs/theming.md` in prose; this is the\n * same list as a table a build can read, and a brand is exactly these tokens\n * and no others - the validator refuses a file that adds one, because a token\n * nothing consumes is a value with no way to be looked at.\n *\n * The colour and radius rows are not a second list kept beside `theme.css`:\n * they are held to it, by `__tests__/contract.test.ts` over the source and by\n * `scripts/assert-bundle.mjs` over the build. The two typography rows are the\n * exception, and on purpose. `--font-sans` and `--font-mono` are the names of\n * Tailwind's own theme variables, which the prebuilt sheet already declares,\n * so `theme.css` does not define them: it is unlayered, and a value there\n * would override the typeface of every host that loads it. The stock\n * typeface is therefore the host's own, and only a brand's scope sets these\n * two. See docs/brand.md.\n */\n\nexport type ContractTokenType = 'color' | 'dimension' | 'fontFamily';\n\nexport interface ContractToken {\n /** The DTCG token id, `color.background`. */\n id: string;\n /** The custom property it sets, `--background`. */\n variable: string;\n type: ContractTokenType;\n /** What the property paints, in the words a producer is told. */\n paints: string;\n}\n\n/** The colour tokens, in the order `theme.css` states them. */\nexport const COLOR_TOKEN_NAMES = [\n 'background',\n 'foreground',\n 'card',\n 'card-foreground',\n 'popover',\n 'popover-foreground',\n 'primary',\n 'primary-foreground',\n 'secondary',\n 'secondary-foreground',\n 'muted',\n 'muted-foreground',\n 'accent',\n 'accent-foreground',\n 'destructive',\n 'destructive-foreground',\n 'border',\n 'input',\n 'ring',\n] as const;\n\nexport type ColorTokenName = (typeof COLOR_TOKEN_NAMES)[number];\n\nexport function isColorTokenName(name: string): name is ColorTokenName {\n return (COLOR_TOKEN_NAMES as readonly string[]).includes(name);\n}\n\nconst COLOR_PAINTS: Record<ColorTokenName, string> = {\n background: 'The page canvas.',\n foreground: 'Text on the canvas.',\n card: \"A raised surface on the canvas: the rail's panel, a list's rows. May be translucent.\",\n 'card-foreground': 'Text on a card.',\n popover: \"A floating surface: a select's menu, a tooltip. Opaque.\",\n 'popover-foreground': 'Text on a popover.',\n primary: 'The accent: the run button, a selected pill, a section number, a highlight.',\n 'primary-foreground': 'Text on the accent.',\n secondary: 'A quiet filled surface: a secondary button, an unselected segment.',\n 'secondary-foreground': 'Text on a secondary surface.',\n muted: \"A subdued fill: a table's header band, an inactive tab strip.\",\n 'muted-foreground':\n \"Secondary text: descriptions, hints, a summary row's label. Most of the text on the page that is not a heading or a value.\",\n accent: 'A hover fill: a menu item under the pointer.',\n 'accent-foreground': 'Text on an accent fill.',\n destructive: 'The error colour: an invalid mark, a remove button.',\n 'destructive-foreground': 'Text on the destructive colour.',\n border: 'The hairline: every border and divider.',\n input:\n \"The control surface, a field's own fill - a step off the canvas, so fields read as one family.\",\n ring: 'The focus ring.',\n};\n\nexport const BRAND_CONTRACT: readonly ContractToken[] = [\n ...COLOR_TOKEN_NAMES.map((name): ContractToken => ({\n id: `color.${name}`,\n variable: `--${name}`,\n type: 'color',\n paints: COLOR_PAINTS[name],\n })),\n {\n id: 'radius.base',\n variable: '--radius',\n type: 'dimension',\n paints: 'The corner radius of a control; cards take one and a half of it.',\n },\n {\n id: 'font.sans',\n variable: '--font-sans',\n type: 'fontFamily',\n paints: 'The typeface of everything on the page.',\n },\n {\n id: 'font.mono',\n variable: '--font-mono',\n type: 'fontFamily',\n paints: 'Tags, numbers, the receipt.',\n },\n];\n\n/** The minimum contrast ratio text must reach: WCAG 2 AA for normal text. */\nexport const MIN_CONTRAST = 4.5;\n\n/**\n * The contrast a control's outline or mark needs against what surrounds it:\n * WCAG 2's level for a user interface component (1.4.11). A button colour\n * below it against the page is a button whose shape is hard to see.\n */\nexport const MIN_CONTROL_CONTRAST = 3;\n\n/**\n * Two colours the contract holds to a contrast ratio in both modes, as they\n * render: the canvas opaque, a translucent surface laid over it, and the ink\n * blended over whatever is beneath it by its alpha.\n */\nexport interface ContrastPair {\n foreground: ColorTokenName;\n background: ColorTokenName;\n /**\n * The canvas beneath the pair's surface. When it is set, `background` names\n * a surface the contract lets be translucent, and that surface is laid over\n * this canvas before anything is measured on it. Absent, `background` is\n * the canvas itself and must be opaque, since what shows through a\n * translucent canvas is whatever the host paints beneath it.\n */\n over?: ColorTokenName;\n /** The ratio the pair must reach: `MIN_CONTRAST` for text, `MIN_CONTROL_CONTRAST` for a mark. */\n minimum: number;\n}\n\n/**\n * The pairs a brand must hold in both modes. The ink and the secondary text\n * on the canvas are text, at WCAG AA.\n *\n * The secondary text on the field fill is held to a control's 3:1. The kernel\n * draws marks in that colour on the fill, an off switch's track and a number\n * field's stepper icons, and those need 3:1 to be seen. It also sets text in\n * that colour on the fill, a boolean row's description, an unselected pill's\n * label and a placeholder, which AA would hold to 4.5:1, but the stock\n * palette's light mode measures 4.39:1 on its own field fill, the same grey\n * it measures on `--muted`, so the text bar there waits on the stock palette\n * reaching it. The field fill may be translucent, as a style's often is, so\n * it is laid over the canvas first.\n *\n * The error colour is held to the same 3:1, as a solid colour, on the canvas\n * and on a card laid over it, which is what a host's panel usually is,\n * because a framed section's error bar is drawn in that colour, solid, on\n * either. The outlines of a card and of a field in error are drawn at half\n * and seven tenths of it, so they read fainter than this pair measures, and\n * the pair does not hold them. The error captions are text, which AA would\n * hold to 4.5:1, but the stock palette's red measures 3.76:1 on white, so\n * that bar waits on the stock palette too.\n */\nexport const CONTRAST_PAIRS: readonly ContrastPair[] = [\n { foreground: 'foreground', background: 'background', minimum: MIN_CONTRAST },\n { foreground: 'muted-foreground', background: 'background', minimum: MIN_CONTRAST },\n {\n foreground: 'muted-foreground',\n background: 'input',\n over: 'background',\n minimum: MIN_CONTROL_CONTRAST,\n },\n { foreground: 'destructive', background: 'background', minimum: MIN_CONTROL_CONTRAST },\n {\n foreground: 'destructive',\n background: 'card',\n over: 'background',\n minimum: MIN_CONTROL_CONTRAST,\n },\n];\n\nconst byId = new Map(BRAND_CONTRACT.map((token) => [token.id, token]));\n\n/** The custom property a token sets, or a loud failure for a token the contract does not name. */\nexport function contractVariable(id: string): string {\n const token = byId.get(id);\n if (!token) throw new Error(`The brand contract has no token ${id}.`);\n return token.variable;\n}\n","import { z } from 'zod';\nimport {\n COLOR_TOKEN_NAMES,\n type ColorTokenName,\n CONTRAST_PAIRS,\n isColorTokenName,\n MIN_CONTRAST,\n} from './contract';\n\n/**\n * A brand's `tokens.json`, validated against the contract.\n *\n * The file is standard DTCG (the Design Tokens Community Group format, 2025.10),\n * and DTCG is looser than the contract in every direction that matters here: it\n * accepts any token, any colour space, a string colour, and a mode of any name.\n * Each of those is a way for a producer's file to be wrong and a page to be\n * painted anyway. So this holds the contract's own rules - exactly its tokens,\n * object colours in sRGB whose hex agrees with their components, aliases only\n * to contract tokens and never in a cycle, a description on every token, `dark`\n * the only mode and stated on every colour object (an alias may stand for both\n * modes), the contrast pairs in both modes - and a file it accepts is one the\n * compiler can only render. Every problem is named by its path, all at once,\n * so a producer's repair round is handed the whole list.\n */\n\nconst component = z.number().min(0).max(1);\n\nexport const srgbColorSchema = z.strictObject({\n colorSpace: z.literal('srgb'),\n components: z.tuple([component, component, component]),\n alpha: z.number().min(0).max(1),\n hex: z\n .string()\n .regex(/^#[0-9a-f]{6}$/i, 'a hex colour is #rrggbb')\n .optional(),\n});\n\nexport type SrgbColor = z.infer<typeof srgbColorSchema>;\n\nconst colorAliasSchema = z\n .string()\n .regex(/^\\{color\\.[a-z][a-z-]*\\}$/, 'an alias is \"{color.<name>}\"');\n\nconst colorValueSchema = z.union([srgbColorSchema, colorAliasSchema]);\n\nexport type ColorValue = z.infer<typeof colorValueSchema>;\n\nconst description = z.string().min(1, 'every token carries a $description');\n\n/**\n * A C0 control or DEL. Written as a test on the code point rather than a\n * character class, since that range is what a regex may not spell\n * (`no-control-regex`).\n */\nexport function isControlCharacter(char: string): boolean {\n const code = char.codePointAt(0)!;\n return code < 0x20 || code === 0x7f;\n}\n\nconst colorTokenSchema = z\n .strictObject({\n $value: colorValueSchema,\n $description: description,\n $extensions: z\n .strictObject({\n mode: z.strictObject({ dark: colorValueSchema }),\n })\n .optional(),\n })\n .refine((token) => token.$extensions !== undefined || typeof token.$value === 'string', {\n message: 'a colour carries $extensions.mode.dark; only an alias may stand for both modes',\n path: ['$extensions'],\n });\n\nexport type ColorToken = z.infer<typeof colorTokenSchema>;\n\nconst dimensionTokenSchema = z.strictObject({\n $value: z.strictObject({ value: z.number().min(0), unit: z.enum(['rem', 'px']) }),\n $description: description,\n});\n\nexport type DimensionToken = z.infer<typeof dimensionTokenSchema>;\n\nconst fontFamilyTokenSchema = z.strictObject({\n $value: z\n .array(\n z\n .string()\n .min(1)\n // A face's name never holds one, and a form feed ends a CSS string.\n .refine((name) => ![...name].some(isControlCharacter), {\n message: 'a family name holds no control characters',\n }),\n )\n .min(1),\n $description: description,\n});\n\nexport type FontFamilyToken = z.infer<typeof fontFamilyTokenSchema>;\n\n/** The token file, typed: the contract's groups and tokens, nothing else. */\nexport interface BrandTokens {\n color: { $type: 'color' } & Record<ColorTokenName, ColorToken>;\n radius: { $type: 'dimension'; base: DimensionToken };\n font: { $type: 'fontFamily'; sans: FontFamilyToken; mono: FontFamilyToken };\n}\n\nconst brandTokensSchema = z.strictObject({\n color: z.strictObject({\n $type: z.literal('color'),\n ...Object.fromEntries(COLOR_TOKEN_NAMES.map((name) => [name, colorTokenSchema])),\n }),\n radius: z.strictObject({ $type: z.literal('dimension'), base: dimensionTokenSchema }),\n font: z.strictObject({\n $type: z.literal('fontFamily'),\n sans: fontFamilyTokenSchema,\n mono: fontFamilyTokenSchema,\n }),\n});\n\nexport type TokensValidation =\n { ok: true; tokens: BrandTokens } | { ok: false; problems: string[] };\n\nexport type ColorMode = 'light' | 'dark';\n\n/** The name an alias points at, or null for a colour object. */\nexport function aliasTarget(value: ColorValue): string | null {\n return typeof value === 'string' ? value.slice('{color.'.length, -1) : null;\n}\n\n/** A token's dark value: what it states, or - for an alias that states none - the same alias. */\nexport function darkValue(token: ColorToken): ColorValue {\n return token.$extensions?.mode.dark ?? token.$value;\n}\n\n/** A token's value in a mode. */\nexport function modeValue(token: ColorToken, mode: ColorMode): ColorValue {\n return mode === 'light' ? token.$value : darkValue(token);\n}\n\n/** `#rrggbb` for a colour, its alpha dropped: each channel the byte the compiler writes. */\nexport function colorHex(color: SrgbColor): string {\n return `#${color.components\n .map((channel) =>\n Math.round(channel * 255)\n .toString(16)\n .padStart(2, '0'),\n )\n .join('')}`;\n}\n\n/**\n * Whether a colour compiles to the hex. The hex is what a brand ships for the\n * colour, so any slack here would let a stated accent ship as its neighbour.\n */\nexport function colorIsHex(color: SrgbColor, hex: string): boolean {\n return colorHex(color) === hex.toLowerCase();\n}\n\n/**\n * A colour as the compiler writes it: each channel the byte a hex or `rgb()`\n * carries, the alpha to the three decimals `rgb()` is given, and opaque once it\n * rounds to 1. A page renders this colour, not the components it was produced\n * as, so contrast is measured on it: a grey that passes at full precision can\n * round below the bar.\n */\nexport function asWritten(color: SrgbColor): SrgbColor {\n const alpha = Number(Math.min(color.alpha, 1).toFixed(3));\n return {\n colorSpace: 'srgb',\n components: color.components.map((channel) => Math.round(channel * 255) / 255) as [\n number,\n number,\n number,\n ],\n alpha,\n };\n}\n\nfunction checkHexAgreement(name: string, mode: ColorMode, value: ColorValue, problems: string[]) {\n if (typeof value === 'string' || !value.hex) return;\n if (!colorIsHex(value, value.hex)) {\n problems.push(\n `color.${name} (${mode}): hex ${value.hex} does not agree with components [${value.components.join(', ')}]`,\n );\n }\n}\n\nfunction checkAliases(tokens: BrandTokens, mode: ColorMode, problems: string[]) {\n for (const name of COLOR_TOKEN_NAMES) {\n const seen = new Set<string>([name]);\n let current: ColorTokenName = name;\n for (;;) {\n const target = aliasTarget(modeValue(tokens.color[current], mode));\n if (target === null) break;\n if (!isColorTokenName(target)) {\n problems.push(\n `color.${name} (${mode}): aliases {color.${target}}, which the contract has no token for`,\n );\n break;\n }\n if (seen.has(target)) {\n problems.push(`color.${name} (${mode}): alias cycle through {color.${target}}`);\n break;\n }\n seen.add(target);\n current = target;\n }\n }\n}\n\n/** The colour a token resolves to in a mode, through its aliases; null when a chain is broken. */\nexport function resolveColor(\n tokens: BrandTokens,\n name: ColorTokenName,\n mode: ColorMode,\n): SrgbColor | null {\n const seen = new Set<string>();\n let current: ColorTokenName = name;\n for (;;) {\n if (seen.has(current)) return null;\n seen.add(current);\n const value = modeValue(tokens.color[current], mode);\n const target = aliasTarget(value);\n if (target === null) return value as SrgbColor;\n if (!isColorTokenName(target)) return null;\n current = target;\n }\n}\n\n/**\n * An sRGB channel, from 0 to 1, as linear light: the transfer function both\n * WCAG's luminance and the Oklab mix in `./styles` start from.\n */\nexport function linearChannel(channel: number): number {\n return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;\n}\n\nfunction luminance(color: SrgbColor): number {\n const [r, g, b] = color.components.map(linearChannel) as [number, number, number];\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG 2 contrast ratio between two colours, taken as opaque. */\nexport function contrastRatio(a: SrgbColor, b: SrgbColor): number {\n const la = luminance(a);\n const lb = luminance(b);\n const [light, dark] = la > lb ? [la, lb] : [lb, la];\n return (light + 0.05) / (dark + 0.05);\n}\n\n/** A colour as it renders over an opaque one: the two blended by its alpha, as a browser does. */\nexport function composite(color: SrgbColor, over: SrgbColor): SrgbColor {\n const components = color.components.map(\n (channel, index) => channel * color.alpha + over.components[index]! * (1 - color.alpha),\n ) as [number, number, number];\n return { colorSpace: 'srgb', components, alpha: 1 };\n}\n\n/**\n * Each contrast pair as it renders. The canvas must be opaque, since a\n * translucent one shows whatever the host paints beneath it and its contrast is\n * nobody's to promise; a surface a pair lays `over` the canvas is blended onto\n * it first, so a translucent field fill is measured as the fill a reader sees;\n * and the ink is blended over that, so a translucent ink is measured at the\n * strength it actually has, and a transparent one fails. All are measured as\n * the compiler writes them, which is what renders.\n */\nfunction checkContrast(tokens: BrandTokens, mode: ColorMode, problems: string[]) {\n for (const pair of CONTRAST_PAIRS) {\n const canvasName = pair.over ?? pair.background;\n const foreground = resolveColor(tokens, pair.foreground, mode);\n const background = resolveColor(tokens, pair.background, mode);\n const canvasColor = resolveColor(tokens, canvasName, mode);\n if (!foreground || !background || !canvasColor) continue;\n if (canvasColor.alpha < 1) {\n // Several pairs stand on one canvas, and it is named once.\n const problem = `color.${canvasName} (${mode}): alpha ${canvasColor.alpha}, but text is measured against it, so it must be opaque (alpha 1)`;\n if (!problems.includes(problem)) problems.push(problem);\n continue;\n }\n const canvas = asWritten(canvasColor);\n const surface = pair.over ? composite(asWritten(background), canvas) : canvas;\n const ratio = contrastRatio(composite(asWritten(foreground), surface), surface);\n if (ratio < pair.minimum) {\n const on = pair.over\n ? `color.${pair.background} over color.${pair.over}`\n : `color.${pair.background}`;\n const level = pair.minimum >= MIN_CONTRAST ? 'WCAG AA' : 'WCAG AA for a control';\n problems.push(\n `color.${pair.foreground} on ${on} (${mode}): contrast ${ratio.toFixed(2)}, expected ${pair.minimum} (${level})`,\n );\n }\n }\n}\n\n/** Every problem with the file at once, so a producer's repair round sees the whole list. */\nexport function validateBrandTokens(doc: unknown): TokensValidation {\n const parsed = brandTokensSchema.safeParse(doc);\n if (!parsed.success) {\n return {\n ok: false,\n problems: parsed.error.issues.map((issue) => {\n const where = issue.path.map(String).join('.') || '(root)';\n return `${where}: ${issue.message}`;\n }),\n };\n }\n const tokens = parsed.data as unknown as BrandTokens;\n const problems: string[] = [];\n for (const name of COLOR_TOKEN_NAMES) {\n const token = tokens.color[name];\n checkHexAgreement(name, 'light', token.$value, problems);\n checkHexAgreement(name, 'dark', darkValue(token), problems);\n }\n checkAliases(tokens, 'light', problems);\n checkAliases(tokens, 'dark', problems);\n // Contrast is read through the aliases, so it is only meaningful once every\n // chain resolves.\n if (problems.length === 0) {\n checkContrast(tokens, 'light', problems);\n checkContrast(tokens, 'dark', problems);\n }\n return problems.length === 0 ? { ok: true, tokens } : { ok: false, problems };\n}\n","import { MIN_CONTRAST, MIN_CONTROL_CONTRAST } from './contract';\nimport { colorHex, contrastRatio, type SrgbColor } from './tokens';\n\n/**\n * Up to three seed colours in, the colour roles a page paints with out.\n *\n * A team brands its pages with at most three colours, each optional, each a\n * `#rrggbb` a person picked: the button colour (`action`), the accent\n * (`accent`) and the header band (`header`). A person picks a colour for how\n * it looks, never for how text reads on it, so nothing here assumes a seed is\n * readable. What this derives is everything a page needs around the seeds,\n * with every text role readable by construction:\n *\n * - **Fills are the seeds, exactly.** `action`, `accent` and `header` come\n * back as they were given, in lower case, so the button and the band are the\n * colours the person chose.\n * - **Text on a fill is pure black or pure white**, whichever contrasts more\n * (`onAction`, `onHeader`). One of the two reaches at least 4.58:1 on any\n * colour, so every label clears WCAG AA. Only the two extremes carry that\n * guarantee: a warm near-black or an off-white falls below 4.5:1 on some\n * mid-tone, so no other colour is ever returned for text.\n * - **The accent as text is ink** (`accentInk`): the accent with its hue\n * and saturation kept and its lightness moved just until it reads at 4.5:1\n * on the page, on the pale accent fill and on every other surface the host\n * names (a style's panel and its fields), the backgrounds a host draws\n * accent text on. The lightness moves toward the page's extreme, darker on\n * a light page and lighter on a dark one, and away from it only when a\n * surface on the far side of the page's lightness leaves no shade that way\n * reading on every background at once. A random search over accents and\n * surfaces on a white and on a black page found no case of it; on a\n * mid-tone page it happens: `#1e40af` on the grey `#767676`, which counts\n * as light, inks as `#000001` alone and as `#fefeff` beside a black panel.\n * An accent that already reads is its own ink.\n * - **The pale accent fill** (`accentFill`) is the accent laid over the page\n * at a low strength: a pale tint on a light page, a deep one on a dark page.\n *\n * **An unset seed produces no role.** The roles a seed paints are present\n * when it is set and absent when it is not, so a host keeps its own token for\n * whatever the team left unset: the team's colours where it chose some, and\n * the host's palette everywhere else. The one seed that reaches further is the\n * button colour, which the accent follows when the accent is unset, so a red\n * button never sits beside links in another hue. With no seed at all, there\n * is no colour role to return.\n *\n * The page colour is an argument because every text role is measured against\n * it: a host renders a light and a dark theme, and it calls this once per\n * theme with that theme's page colour and that theme's seeds. Which seeds a\n * theme uses is the host's to decide; `resolveDarkSeeds` is the rule for a\n * dark set that falls back, colour by colour, to the light one.\n *\n * It returns roles, never custom property names: each host maps the roles onto\n * its own tokens. See docs/brand.md § \"Three seed colours\".\n */\n\n/**\n * How strongly the accent is laid over the page to make the pale accent fill:\n * the same order as a control's `bg-primary/10` and `bg-primary/15` tints.\n */\nconst ACCENT_FILL_STRENGTH = 0.12;\n\n/**\n * How many halvings the searches below run. Thirty-two pins a lightness far\n * below the step a byte can express, so the result is the first colour that\n * reads, to the byte.\n */\nconst SEARCH_STEPS = 32;\n\n/**\n * Up to three seed colours, each `#rrggbb` in any case, or unset. `null` and\n * `undefined` both mean unset, so a host can pass a team's stored colours as\n * they arrive.\n */\nexport interface BrandSeeds {\n /** The button colour: the run button, every main button, the selected state of a pill. */\n action?: string | null;\n /** The accent: links, the active tab, the upload area on hover, progress. */\n accent?: string | null;\n /** The band across the top of the page, behind the logo. */\n header?: string | null;\n}\n\n/** Text on a fill: only the two colours that read on every fill. */\nexport type TextOnFill = '#000000' | '#ffffff';\n\n/** The button colour's roles, present when the button colour is set. */\ninterface ActionRoles {\n /** The button colour, as a fill. Never text. */\n action: string;\n /** Text and icons on the button colour. */\n onAction: TextOnFill;\n}\n\n/** The accent's roles, present when the accent or the button colour it follows is set. */\ninterface AccentRoles {\n /** The accent, as a fill: an active tab's underline, a progress bar. Never text. */\n accent: string;\n /** A pale accent fill (deep on a dark page), behind accent ink: a pill, a badge. */\n accentFill: string;\n /**\n * The accent as text, reading at 4.5:1 or more on the page, on `accentFill`\n * and on every other surface the host named (`deriveBrandRoles`' `surfaces`).\n */\n accentInk: string;\n}\n\n/** The header band's roles, present when the header is set. */\ninterface HeaderRoles {\n /** The header band, as a fill. */\n header: string;\n /** Text on the header band: the back link, a wordmark. */\n onHeader: TextOnFill;\n}\n\n/** A group of roles that is either all there or not there at all. */\ntype Absent<Roles> = { [Role in keyof Roles]?: never };\n\n/**\n * The colours a page paints with, each `#rrggbb` in lower case. The roles come\n * in groups, each present whole or absent whole: checking one role of a group\n * (`roles.action !== undefined`) narrows the rest of it.\n */\nexport type BrandRoles = (ActionRoles | Absent<ActionRoles>) &\n (AccentRoles | Absent<AccentRoles>) &\n (HeaderRoles | Absent<HeaderRoles>);\n\n/**\n * The roles a page paints with, from up to three seeds and the colour of the\n * page they render on. Throws when a seed or the page is not a `#rrggbb`\n * colour: a host validates what a person typed before it reaches here.\n *\n * `surfaces` are the other colours accent text sits on besides the page and\n * the pale fill, each `#rrggbb`: a style's panel and the fields inside it\n * (`resolveRoleColor` turns a style's role colour into one), or a canvas\n * behind the page. The ink is moved until it reads on all of them, and an\n * accent that already reads on every one is its own ink. When there is an\n * accent to derive and no shade of it reads on the page, the pale fill and\n * every surface at once, which a surface on the far side of the page's\n * lightness can cause, it throws, naming them. With no accent, no ink is\n * derived, and the surfaces are only parsed.\n */\nexport function deriveBrandRoles(\n seeds: BrandSeeds | null | undefined,\n page: string,\n surfaces: readonly string[] = [],\n): BrandRoles {\n const canvas = parseHex(page, 'The page colour');\n const extreme = textOn(canvas) === '#000000' ? BLACK : WHITE;\n const others = surfaces.map((surface, index) => parseHex(surface, `Surface ${index + 1}`));\n const action = seeds?.action == null ? null : parseHex(seeds.action, 'The action seed');\n // An unset accent follows the button colour, and is unset too when that is.\n const accent = seeds?.accent == null ? action : parseHex(seeds.accent, 'The accent seed');\n const header = seeds?.header == null ? null : parseHex(seeds.header, 'The header seed');\n\n const actionRoles: ActionRoles | Absent<ActionRoles> =\n action === null ? {} : { action: colorHex(action), onAction: textOn(action) };\n const accentRoles: AccentRoles | Absent<AccentRoles> =\n accent === null ? {} : accentRolesOf(accent, canvas, extreme, others);\n const headerRoles: HeaderRoles | Absent<HeaderRoles> =\n header === null ? {} : { header: colorHex(header), onHeader: textOn(header) };\n return { ...actionRoles, ...accentRoles, ...headerRoles };\n}\n\n/**\n * The accent as a fill, its pale fill on the page, and its ink on both and on\n * every other surface. The way accent text moves first is the way text on the\n * page moves, toward `extreme`: whichever of black and white reads better\n * there, which always reaches 4.58:1 on the page itself. The accent is\n * measured before any surface is judged, so an accent that already reads\n * everywhere is kept, and a surface is refused only when no shade of the\n * accent reads on it and on the others at once.\n */\nfunction accentRolesOf(\n accent: SrgbColor,\n canvas: SrgbColor,\n extreme: SrgbColor,\n surfaces: readonly SrgbColor[],\n): AccentRoles {\n const accentFill = paleFill(accent, canvas, extreme);\n const backgrounds = [canvas, accentFill, ...surfaces];\n const accentInk = inkFor(accent, backgrounds, extreme);\n if (accentInk === undefined) {\n throw new Error(\n `No accent ink can read on every surface at once: no shade of ${colorHex(accent)} reaches ${MIN_CONTRAST}:1 on the page ${colorHex(canvas)}, the pale fill ${colorHex(accentFill)} and ${surfaces.map(colorHex).join(', ')}.`,\n );\n }\n return {\n accent: colorHex(accent),\n accentFill: colorHex(accentFill),\n accentInk: colorHex(accentInk),\n };\n}\n\n/**\n * Whether the button colour sits below 3:1 against the page, so a button may\n * be hard to see in that theme. Only a colour the person set is judged: with\n * no button colour the button is the host's own, and the answer is `false`.\n */\nexport function isActionFaint(seeds: BrandSeeds | null | undefined, page: string): boolean {\n const canvas = parseHex(page, 'The page colour');\n if (seeds?.action == null) return false;\n return contrastRatio(parseHex(seeds.action, 'The action seed'), canvas) < MIN_CONTROL_CONTRAST;\n}\n\n/**\n * Whether the accent had to be darkened or lightened to become ink on the\n * page (and on the `surfaces`, as `deriveBrandRoles` takes them), so links read\n * in a shade of it rather than in the colour itself. Only\n * a colour the person set is judged, and the accent counts as set when it\n * follows a button colour the person set: with neither, the answer is `false`.\n */\nexport function isAccentAdjusted(\n seeds: BrandSeeds | null | undefined,\n page: string,\n surfaces: readonly string[] = [],\n): boolean {\n const roles = deriveBrandRoles(seeds, page, surfaces);\n return roles.accentInk !== undefined && roles.accentInk !== roles.accent;\n}\n\n/**\n * The seeds a dark theme uses: each dark colour where one is set, and the\n * light one where it is not. Colour by colour, before the accent follows the\n * button colour in `deriveBrandRoles`, so a light accent the person set\n * outranks a dark button colour the accent would otherwise follow. A colour\n * set in neither stays unset. Either set may be absent.\n */\nexport function resolveDarkSeeds(\n colors: BrandSeeds | null | undefined,\n darkColors: BrandSeeds | null | undefined,\n): BrandSeeds {\n return {\n action: darkColors?.action ?? colors?.action ?? null,\n accent: darkColors?.accent ?? colors?.accent ?? null,\n header: darkColors?.header ?? colors?.header ?? null,\n };\n}\n\n/** Pure black or pure white, whichever reads better on a `#rrggbb` fill. */\nexport function readableTextOn(fill: string): TextOnFill {\n return textOn(parseHex(fill, 'The fill'));\n}\n\nconst BLACK: SrgbColor = { colorSpace: 'srgb', components: [0, 0, 0], alpha: 1 };\nconst WHITE: SrgbColor = { colorSpace: 'srgb', components: [1, 1, 1], alpha: 1 };\n\n/** Ties go to black: on a colour where both measure the same, either clears 4.58:1. */\nfunction textOn(fill: SrgbColor): TextOnFill {\n return contrastRatio(BLACK, fill) >= contrastRatio(WHITE, fill) ? '#000000' : '#ffffff';\n}\n\n/** A `#rrggbb` colour, or a throw naming `what` it was meant to be. Module-internal: not on the entry. */\nexport function parseHex(value: string, what: string): SrgbColor {\n if (typeof value !== 'string' || !/^#[0-9a-f]{6}$/i.test(value)) {\n throw new Error(`${what} must be a #rrggbb colour, not ${JSON.stringify(value)}.`);\n }\n const channel = (at: number) => parseInt(value.slice(at, at + 2), 16) / 255;\n return { colorSpace: 'srgb', components: [channel(1), channel(3), channel(5)], alpha: 1 };\n}\n\n/**\n * A colour as a hex can carry it, each channel rounded to its byte. Every\n * contrast below is measured on this, the colour a page renders, never on the\n * unrounded value a search produced, so a colour that clears the bar by a\n * rounding error is never returned.\n */\nfunction snapped(components: readonly number[]): SrgbColor {\n const [r, g, b] = components.map(\n (channel) => Math.round(Math.min(1, Math.max(0, channel)) * 255) / 255,\n ) as [number, number, number];\n return { colorSpace: 'srgb', components: [r, g, b], alpha: 1 };\n}\n\n/**\n * The accent laid over the page at `ACCENT_FILL_STRENGTH`. On a page near the\n * middle of the lightness scale, a fill that strong can leave even black or\n * white below 4.5:1, and then no ink would read on it; the fill moves back\n * toward the page until the extreme reads again. On a page a person would\n * actually use, light or dark, that never happens.\n */\nfunction paleFill(accent: SrgbColor, page: SrgbColor, extreme: SrgbColor): SrgbColor {\n const at = (strength: number) =>\n snapped(\n page.components.map(\n (channel, index) => channel + (accent.components[index]! - channel) * strength,\n ),\n );\n const nominal = at(ACCENT_FILL_STRENGTH);\n if (contrastRatio(extreme, nominal) >= MIN_CONTRAST) return nominal;\n // At strength 0 the fill is the page, on which the extreme reads.\n let enough = 0;\n let short = ACCENT_FILL_STRENGTH;\n for (let step = 0; step < SEARCH_STEPS; step++) {\n const middle = (enough + short) / 2;\n if (contrastRatio(extreme, at(middle)) >= MIN_CONTRAST) enough = middle;\n else short = middle;\n }\n return at(enough);\n}\n\n/**\n * Every shade of one hue and saturation, as the lightness of a point inside\n * it, from black to white. Each channel is linear in the lightness on either\n * side of the middle (`fromHsl`), so the lightness at which it turns over to\n * its next byte is solved for rather than searched; between two neighbouring\n * turnovers every channel keeps its byte, and the hex is one shade. A walk in\n * fixed steps could not promise this: where two channels turn over close\n * together, a shade spans less lightness than any step, and the one shade\n * that reads may be it.\n */\nfunction shadeLightnesses(hue: number, saturation: number): number[] {\n const bounds = new Set<number>([0, 0.5, 1]);\n for (const [low, high] of [\n [0, 0.5],\n [0.5, 1],\n ] as const) {\n const from = fromHsl(hue, saturation, low);\n const to = fromHsl(hue, saturation, high);\n for (let channel = 0; channel < 3; channel++) {\n const start = from[channel]! * 255;\n const rise = to[channel]! * 255 - start;\n if (rise === 0) continue;\n // `snapped` rounds a channel, so its byte turns over at each half.\n for (let byte = 0; byte < 255; byte++) {\n const at = low + ((byte + 0.5 - start) / rise) * (high - low);\n if (at > low && at < high) bounds.add(at);\n }\n }\n }\n const sorted = [...bounds].sort((a, b) => a - b);\n return sorted.slice(1).map((bound, index) => (sorted[index]! + bound) / 2);\n}\n\n/**\n * The accent as text on every background given: the accent itself if it\n * reads, otherwise the accent with its lightness moved just until it does, or\n * `undefined` when no lightness reads on all of them. Hue and saturation are\n * kept, so a pale yellow becomes a gold rather than a grey.\n *\n * The lightness moves toward the extreme (black or white) first. When the\n * extreme reads on every background, which it does on the page by the choice\n * of direction and on the fill by `paleFill`'s construction, each step that\n * way only brings the ink closer to reading, and a search halves the\n * lightness between a value known to fall short and one known to read. It\n * only ever returns the one known to read, measured as a hex, so the result\n * reads whatever the rounding does.\n *\n * A surface on the far side of the page's lightness, a dark panel on a light\n * page, is one the extreme does not read on. The shades that read on every\n * background then lie between the two extremes, if there are any: a shade\n * dark enough for the light backgrounds and light enough for the dark ones,\n * and sometimes a single one. Every shade of the accent's hue and saturation\n * is tried (`shadeLightnesses`), nearest first, toward the extreme and then\n * away from it, and the first that reads is the ink; `undefined` means no\n * shade does.\n */\nfunction inkFor(\n accent: SrgbColor,\n backgrounds: readonly SrgbColor[],\n extreme: SrgbColor,\n): SrgbColor | undefined {\n const reads = (color: SrgbColor) =>\n backgrounds.every((background) => contrastRatio(color, background) >= MIN_CONTRAST);\n if (reads(accent)) return accent;\n const [hue, saturation, lightness] = toHsl(accent.components);\n const at = (value: number) => snapped(fromHsl(hue, saturation, value));\n /** The first lightness that reads between one known to fall short and one known to read. */\n const narrowed = (short: number, enough: number): SrgbColor => {\n for (let step = 0; step < SEARCH_STEPS; step++) {\n const middle = (short + enough) / 2;\n if (reads(at(middle))) enough = middle;\n else short = middle;\n }\n return at(enough);\n };\n const toward = extreme === BLACK ? 0 : 1;\n if (reads(extreme)) return narrowed(lightness, toward);\n const shades = shadeLightnesses(hue, saturation);\n const darker = shades.filter((value) => value < lightness).reverse();\n const lighter = shades.filter((value) => value > lightness);\n for (const side of toward === 0 ? [darker, lighter] : [lighter, darker]) {\n for (const value of side) {\n const shade = at(value);\n if (reads(shade)) return shade;\n }\n }\n return undefined;\n}\n\n/** sRGB components to hue (degrees), saturation and lightness, each of the last two in 0..1. */\nfunction toHsl(components: readonly number[]): [number, number, number] {\n const [r, g, b] = components as [number, number, number];\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const lightness = (max + min) / 2;\n const chroma = max - min;\n if (chroma === 0) return [0, 0, lightness];\n const saturation = chroma / (1 - Math.abs(2 * lightness - 1));\n const sector =\n max === r\n ? ((g - b) / chroma + 6) % 6\n : max === g\n ? (b - r) / chroma + 2\n : (r - g) / chroma + 4;\n return [sector * 60, Math.min(1, saturation), lightness];\n}\n\n/**\n * Hue, saturation and lightness back to sRGB components. Lightness 0 is black\n * and lightness 1 is white whatever the hue, which is what lets the ink's\n * search end on an extreme.\n */\nfunction fromHsl(hue: number, saturation: number, lightness: number): [number, number, number] {\n const chroma = (1 - Math.abs(2 * lightness - 1)) * saturation;\n const sector = hue / 60;\n const second = chroma * (1 - Math.abs((sector % 2) - 1));\n const [r, g, b]: [number, number, number] =\n sector < 1\n ? [chroma, second, 0]\n : sector < 2\n ? [second, chroma, 0]\n : sector < 3\n ? [0, chroma, second]\n : sector < 4\n ? [0, second, chroma]\n : sector < 5\n ? [second, 0, chroma]\n : [chroma, 0, second];\n const offset = lightness - chroma / 2;\n return [r + offset, g + offset, b + offset];\n}\n","import { isColorTokenName, type ColorTokenName } from './contract';\nimport { parseHex } from './seed-roles';\nimport { colorHex, linearChannel, type SrgbColor } from './tokens';\n\n/**\n * The app styles and the typefaces, as data, and the roles a team's choice of\n * the two sets.\n *\n * A style is a small set of design values a team picks from a closed list, on\n * top of its own colours and logo: how its headings, its panels, its corners,\n * its call to action and its rhythm look. A typeface is picked from a short\n * list beside it, and replaces the style's body face, and its heading face\n * wherever the style's headings follow the body. Neither adds a colour: every\n * fill, edge and ink a style uses is a role the theme contract already has,\n * named here by that role (`RoleColor`), or a shadow.\n *\n * `deriveStyleRoles` returns roles, never custom property names, the way\n * `deriveBrandRoles` does for the seed colours: each host maps them onto its\n * own tokens, and the kernel's controls and the generative layer read the few\n * optional tokens docs/theming.md lists, each with today's look as its\n * fallback. A role a choice does not set is not a key, so a host keeps its own\n * value there; `quiet` with no typeface sets nothing at all, which is what\n * makes \"no style\" and \"the default style\" one state.\n *\n * See docs/brand.md § \"App styles and typefaces\".\n */\n\n// ─── Typefaces ───────────────────────────────────────────────────────────────\n\n/** The faces of the closed list, all on Google Fonts under the SIL Open Font License. */\nexport type TypefaceId =\n | 'inter'\n | 'dm-sans'\n | 'plus-jakarta-sans'\n | 'manrope'\n | 'ibm-plex-sans'\n | 'source-sans-3'\n | 'work-sans'\n | 'space-grotesk'\n | 'newsreader';\n\n/** The faces a team may pick as its typeface: every face but a style's own heading face. */\nexport type BodyTypefaceId = Exclude<TypefaceId, 'newsreader'>;\n\nexport interface Typeface {\n id: TypefaceId;\n /** The family name, as a stylesheet and a font service name it. */\n family: string;\n category: 'sans-serif' | 'serif';\n /** The weights a host loads it in: from the host's own light titles (300) to the boldest a style sets (700). */\n weights: readonly number[];\n /** The generic stack that renders while the face loads, or when it never does. */\n fallback: string;\n /** The face first, then its fallback: a whole `font-family` value. */\n stack: string;\n /** Whether a team may pick it (`body`), or a style uses it for its headings only (`heading`). */\n use: 'body' | 'heading';\n}\n\nconst SANS_FALLBACK = 'ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif';\nconst SERIF_FALLBACK = 'ui-serif, Georgia, Cambria, \"Times New Roman\", serif';\nconst WEIGHTS: readonly number[] = [300, 400, 500, 600, 700];\n\nfunction face(\n id: TypefaceId,\n family: string,\n category: Typeface['category'],\n use: Typeface['use'],\n): Typeface {\n const fallback = category === 'serif' ? SERIF_FALLBACK : SANS_FALLBACK;\n return {\n id,\n family,\n category,\n weights: WEIGHTS,\n fallback,\n stack: `\"${family}\", ${fallback}`,\n use,\n };\n}\n\n/**\n * Every face a style or a team can set. The body faces are the list a team\n * picks from; Newsreader is the `editorial` style's heading face, listed so a\n * host loads it, and is not a team's choice. Frozen, like `APP_STYLES`: a\n * process serves many teams, so the catalogue one of them reads cannot be\n * one another has edited.\n */\nexport const TYPEFACES: readonly Typeface[] = deepFreeze([\n face('inter', 'Inter', 'sans-serif', 'body'),\n face('dm-sans', 'DM Sans', 'sans-serif', 'body'),\n face('plus-jakarta-sans', 'Plus Jakarta Sans', 'sans-serif', 'body'),\n face('manrope', 'Manrope', 'sans-serif', 'body'),\n face('ibm-plex-sans', 'IBM Plex Sans', 'sans-serif', 'body'),\n face('source-sans-3', 'Source Sans 3', 'sans-serif', 'body'),\n face('work-sans', 'Work Sans', 'sans-serif', 'body'),\n face('space-grotesk', 'Space Grotesk', 'sans-serif', 'body'),\n face('newsreader', 'Newsreader', 'serif', 'heading'),\n]);\n\nconst FACES = new Map(TYPEFACES.map((candidate) => [candidate.id as string, candidate]));\n\n/** Whether `value` names a face a team may pick: a body face of `TYPEFACES`. */\nexport function isTypeface(value: unknown): value is BodyTypefaceId {\n return typeof value === 'string' && FACES.get(value)?.use === 'body';\n}\n\nfunction faceOf(id: TypefaceId): Typeface {\n return FACES.get(id)!;\n}\n\n// ─── The roles a style paints with ──────────────────────────────────────────\n\n/**\n * A colour a style paints with, named by the role of the theme contract it\n * is: never a colour of its own, so a team's palette and the host's theme\n * stay the only sources of colour, and their readability guarantees hold.\n */\nexport interface RoleColor {\n role: ColorTokenName;\n /** Mixed toward another role, in Oklab, by `share` (0 to 1): the card a little toward the foreground. */\n toward?: { role: ColorTokenName; share: number };\n /** Its opacity over what lies behind it, 0 to 1; absent, opaque. */\n alpha?: number;\n}\n\n/** How a style draws a panel - a host's composer, a designed page's rail or card - in one theme. */\nexport interface PanelRoles {\n fill: RoleColor;\n /** The edge's colour, or `null` for no visible edge (the width is kept, so a panel's box does not move). */\n edge: RoleColor | null;\n /** The edge's width, in CSS pixels. */\n edgeWidth: number;\n /** A CSS `box-shadow` in a neutral ink, or `null` for none. */\n shadow: string | null;\n /**\n * The fill of the fields set into the panel, where the host's field fill\n * would sink into it; absent, the host's own.\n */\n field?: RoleColor;\n}\n\n/** The call to action's shadow: a CSS shadow's geometry, in pixels, tinted with a role. */\nexport interface ActionShadow {\n x: number;\n y: number;\n blur: number;\n spread: number;\n color: RoleColor;\n}\n\n/**\n * The roles a style and a typeface set. An absent role is not a key: a host\n * keeps its own value there.\n */\nexport interface StyleRoles {\n /** The face of everything on the page. */\n bodyFace?: Typeface;\n /** The face of the page's titles and its sections' and panels' headings. */\n headingFace?: Typeface;\n headingWeight?: number;\n /** The headings' letter spacing, in `em`. */\n headingTracking?: string;\n /** A control's corner radius, in CSS pixels; a panel takes one and a half of it. */\n radius?: number;\n /** The panel treatment in each theme: a shadow that reads on a light page is lost on a dark one. */\n panel?: { light: PanelRoles; dark: PanelRoles };\n /**\n * The call to action's shadow in each theme, or `none` for a plain fill: a\n * shadow in the button colour that lifts a button on a light page smears\n * under it on a dark one.\n */\n actionShadow?: { light: ActionShadow | 'none'; dark: ActionShadow | 'none' };\n /** The rhythm: a multiple of the host's own margins, paddings and gaps around the form. Never a control's size. */\n density?: number;\n}\n\n// ─── The styles ──────────────────────────────────────────────────────────────\n\nexport type AppStyleId = 'quiet' | 'soft' | 'editorial' | 'bold';\n\nexport interface AppStyle {\n id: AppStyleId;\n name: string;\n /** The direction, in one sentence, as a designer reads it. */\n direction: string;\n /** The roles the style sets when the team picks no typeface. */\n roles: StyleRoles;\n}\n\ninterface Definition {\n name: string;\n direction: string;\n /** The style's own body face, or none (the host's). */\n bodyFace: BodyTypefaceId | null;\n /** The heading face: the body's, or a face of the style's own that a team's typeface does not replace. */\n headingFace: 'body' | TypefaceId;\n roles: Omit<StyleRoles, 'bodyFace' | 'headingFace'>;\n}\n\nconst role = (name: ColorTokenName, more: Omit<RoleColor, 'role'> = {}): RoleColor => ({\n role: name,\n ...more,\n});\n\nconst DEFINITIONS: Readonly<Record<AppStyleId, Definition>> = {\n // The host's own look, named: it sets nothing, and a team's typeface is its\n // body and its headings.\n quiet: {\n name: 'Quiet',\n direction:\n 'The host’s own look, unstyled: its light titles and hairline panels, generous air, nothing decorated.',\n bodyFace: null,\n headingFace: 'body',\n roles: {},\n },\n soft: {\n name: 'Soft',\n direction:\n 'Friendly and rounded: generous corners, panels that float on a soft shadow with no outline, warm medium-weight titles.',\n bodyFace: 'dm-sans',\n headingFace: 'body',\n roles: {\n headingWeight: 500,\n headingTracking: '-0.015em',\n radius: 12,\n panel: {\n light: {\n fill: role('card'),\n edge: null,\n edgeWidth: 1,\n shadow: '0 1px 2px rgb(16 24 40 / 0.05), 0 12px 32px -10px rgb(16 24 40 / 0.16)',\n field: role('card'),\n },\n // On a dark page a shadow barely shows, so the panel is lifted by its\n // fill instead, the card a little toward the foreground, with a faint\n // highlight along its top edge where light would catch it, and its\n // fields are lifted a step further, so each reads as a control on\n // the panel. At the page's own background they read as wells cut\n // into it.\n dark: {\n fill: role('card', { toward: { role: 'foreground', share: 0.07 } }),\n edge: null,\n edgeWidth: 1,\n shadow:\n 'inset 0 1px 0 rgb(255 255 255 / 0.07), 0 2px 4px rgb(0 0 0 / 0.55), 0 24px 56px -12px rgb(0 0 0 / 0.95)',\n field: role('card', { toward: { role: 'foreground', share: 0.12 } }),\n },\n },\n // A short shadow in the button colour on a light page, kept within the\n // gap a host leaves under its button. On a dark page the panel is lifted\n // by its fill, and a glow in a light button colour reads as a smear.\n actionShadow: {\n light: { x: 0, y: 6, blur: 14, spread: -6, color: role('primary', { alpha: 0.5 }) },\n dark: 'none',\n },\n density: 1.08,\n },\n },\n editorial: {\n name: 'Editorial',\n direction:\n 'Considered and literary: serif display titles, fine hairlines, wide margins and a reading rhythm.',\n bodyFace: 'source-sans-3',\n headingFace: 'newsreader',\n roles: {\n headingWeight: 500,\n headingTracking: '-0.01em',\n radius: 2,\n // Hairlines all the way in: the fields inside the panel take its own\n // fill, so each reads as its fine edge rather than as a grey block.\n panel: {\n light: {\n fill: role('card'),\n edge: role('border'),\n edgeWidth: 1,\n shadow: null,\n field: role('card'),\n },\n dark: {\n fill: role('card'),\n edge: role('border'),\n edgeWidth: 1,\n shadow: null,\n field: role('card'),\n },\n },\n actionShadow: { light: 'none', dark: 'none' },\n density: 1.25,\n },\n },\n bold: {\n name: 'Bold',\n direction:\n 'Confident and graphic: heavy, tightly set titles, solid panels with firm edges, a compact rhythm.',\n bodyFace: 'space-grotesk',\n headingFace: 'body',\n roles: {\n headingWeight: 700,\n headingTracking: '-0.03em',\n radius: 6,\n // One firm edge around a solid panel, softer on a dark page. The panel\n // is the muted fill, which the host's fields all but match, so fields\n // set into it take the card on a light page, and on a dark one the\n // muted fill lifted a little toward the foreground: a step above the\n // panel, as the card is on a light page, where the page's background\n // read as a well cut into it.\n panel: {\n light: {\n fill: role('muted'),\n edge: role('foreground', { alpha: 0.62 }),\n edgeWidth: 2,\n shadow: null,\n field: role('card'),\n },\n dark: {\n fill: role('muted'),\n edge: role('foreground', { alpha: 0.3 }),\n edgeWidth: 2,\n shadow: null,\n field: role('muted', { toward: { role: 'foreground', share: 0.06 } }),\n },\n },\n actionShadow: { light: 'none', dark: 'none' },\n density: 0.85,\n },\n },\n};\n\nexport const APP_STYLE_IDS: readonly AppStyleId[] = Object.freeze([\n 'quiet',\n 'soft',\n 'editorial',\n 'bold',\n]);\n\n/** Whether `value` names one of the app styles. */\nexport function isAppStyle(value: unknown): value is AppStyleId {\n // Against the definitions themselves, which no caller can reach, rather\n // than the exported list.\n return typeof value === 'string' && Object.prototype.hasOwnProperty.call(DEFINITIONS, value);\n}\n\n/**\n * The roles a team's choice sets: its style and its typeface, each a stored\n * id or nothing. An unknown or absent style is `quiet`, which sets nothing;\n * an unknown typeface, or one a team may not pick, is no typeface, so the\n * style's own face (or the host's) stays. The team's typeface replaces the\n * style's body face, and its heading face wherever the style's headings\n * follow the body.\n */\nexport function deriveStyleRoles(\n style: string | null | undefined,\n typeface?: string | null,\n): StyleRoles {\n const definition = DEFINITIONS[isAppStyle(style) ? style : 'quiet'];\n const roles: StyleRoles = {};\n const body = isTypeface(typeface)\n ? faceOf(typeface)\n : definition.bodyFace === null\n ? undefined\n : faceOf(definition.bodyFace);\n if (body) roles.bodyFace = body;\n const heading = definition.headingFace === 'body' ? body : faceOf(definition.headingFace);\n if (heading) roles.headingFace = heading;\n // A copy, faces included, so a host that edits what it got changes nothing\n // for the next caller and nothing in the catalogue.\n return copyData({ ...roles, ...definition.roles });\n}\n\n/** The styles, each with its own roles: what it sets for a team that picked no typeface. */\nexport const APP_STYLES: Readonly<Record<AppStyleId, AppStyle>> = deepFreeze(\n Object.fromEntries(\n APP_STYLE_IDS.map((id) => [\n id,\n {\n id,\n name: DEFINITIONS[id].name,\n direction: DEFINITIONS[id].direction,\n roles: deriveStyleRoles(id),\n },\n ]),\n ) as Record<AppStyleId, AppStyle>,\n);\n\n/**\n * A deep copy of plain data - objects, arrays and primitives, which is all a\n * style's roles are. Spelled out rather than `structuredClone`, which is a\n * host API rather than the language's, and which this isomorphic entry would\n * otherwise call at import time through `APP_STYLES`.\n */\nfunction copyData<T>(value: T): T {\n if (Array.isArray(value)) return value.map(copyData) as T;\n if (value !== null && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value).map(([key, inner]) => [key, copyData(inner)]),\n ) as T;\n }\n return value;\n}\n\n/** Freezes plain data all the way down, and hands it back. */\nfunction deepFreeze<T>(value: T): T {\n if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {\n for (const inner of Object.values(value)) deepFreeze(inner);\n Object.freeze(value);\n }\n return value;\n}\n\n// ─── Measuring a role ────────────────────────────────────────────────────────\n\n/**\n * A role colour as a `#rrggbb` hex over the palette it names, to measure a\n * surface a style draws: the ink a team's accent needs on a style's panel\n * (`deriveBrandRoles`' `surfaces`). The mix is Oklab's, as CSS `color-mix()`\n * computes it. A translucent colour is laid over `behind`; without it, it\n * throws, since a translucent colour has no one value. Throws, too, for a\n * role the palette does not hold or a palette colour that is not a hex.\n */\nexport function resolveRoleColor(\n color: RoleColor,\n palette: Partial<Record<ColorTokenName, string>>,\n behind?: string,\n): string {\n const of = (name: ColorTokenName): SrgbColor => {\n const value = palette[name];\n if (!isColorTokenName(name) || value === undefined) {\n throw new Error(`The palette has no ${name} to resolve a style's colour against.`);\n }\n return parseHex(value, `The palette's ${name}`);\n };\n let rgb = of(color.role).components as [number, number, number];\n if (color.toward) {\n const share = clampUnit(color.toward.share);\n rgb = mixOklab(rgb, of(color.toward.role).components as [number, number, number], share);\n }\n if (color.alpha !== undefined && color.alpha < 1) {\n if (behind === undefined) {\n throw new Error(`A translucent ${color.role} has no one colour: say what it lies over.`);\n }\n const under = parseHex(behind, 'The colour behind').components;\n const alpha = clampUnit(color.alpha);\n rgb = rgb.map((channel, index) => under[index]! + (channel - under[index]!) * alpha) as [\n number,\n number,\n number,\n ];\n }\n return colorHex({ colorSpace: 'srgb', components: rgb, alpha: 1 });\n}\n\nfunction clampUnit(value: number): number {\n return Math.min(1, Math.max(0, value));\n}\n\ntype Rgb = [number, number, number];\n\n/** `a` moved toward `b` by `share` in Oklab, as `color-mix(in oklab, a, b share)` paints it. */\nfunction mixOklab(a: Rgb, b: Rgb, share: number): Rgb {\n const [l1, a1, b1] = toOklab(a);\n const [l2, a2, b2] = toOklab(b);\n const at = (from: number, to: number) => from + (to - from) * share;\n return fromOklab([at(l1, l2), at(a1, a2), at(b1, b2)]).map(clampUnit) as Rgb;\n}\n\nconst fromLinear = (c: number) => (c <= 0.0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055);\n\nfunction toOklab(rgb: Rgb): Rgb {\n const [r, g, b] = rgb.map(linearChannel) as Rgb;\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);\n return [\n 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n ];\n}\n\nfunction fromOklab([lightness, a, b]: Rgb): Rgb {\n const l = (lightness + 0.3963377774 * a + 0.2158037573 * b) ** 3;\n const m = (lightness - 0.1055613458 * a - 0.0638541728 * b) ** 3;\n const s = (lightness - 0.0894841775 * a - 1.291485548 * b) ** 3;\n return [\n 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,\n ].map((channel) => fromLinear(clampUnit(channel))) as Rgb;\n}\n"]}
|
|
@@ -45,10 +45,37 @@ function mustBeFilled(field) {
|
|
|
45
45
|
return field.gating ?? (field.required && field.kind !== "list");
|
|
46
46
|
}
|
|
47
47
|
function computeReadiness(fields, values) {
|
|
48
|
-
const gating = fields
|
|
49
|
-
const missing = gating
|
|
48
|
+
const gating = gatingFields(fields, values);
|
|
49
|
+
const missing = missingAmong(gating, values).map((f) => f.title ?? f.name);
|
|
50
50
|
return { total: gating.length, ready: gating.length - missing.length, missing };
|
|
51
51
|
}
|
|
52
|
+
function missingFields(fields, values) {
|
|
53
|
+
return missingAmong(gatingFields(fields, values), values);
|
|
54
|
+
}
|
|
55
|
+
function gateRefusal(field, value, mustFill) {
|
|
56
|
+
if (mustFill) return fieldFilled(field, value) ? void 0 : "required";
|
|
57
|
+
return isFilled(value) && !fieldFilled(field, value) ? "incomplete" : void 0;
|
|
58
|
+
}
|
|
59
|
+
function waitingFields(fields, values) {
|
|
60
|
+
return missingFields(fields, values).flatMap(
|
|
61
|
+
(field) => refusedWithin(field, ownProp(values, field.name), [field.name])
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function refusedWithin(field, value, path) {
|
|
65
|
+
if (field.kind !== "object") return [{ field, path }];
|
|
66
|
+
const members = optionalStructureOpen(value) ? value : void 0;
|
|
67
|
+
const refused = field.fields.flatMap((member) => {
|
|
68
|
+
const held = ownProp(members, member.name);
|
|
69
|
+
return gateRefusal(member, held, member.required) ? refusedWithin(member, held, [...path, member.name]) : [];
|
|
70
|
+
});
|
|
71
|
+
return refused.length > 0 ? refused : [{ field, path }];
|
|
72
|
+
}
|
|
73
|
+
function gatingFields(fields, values) {
|
|
74
|
+
return fields.filter((f) => mustBeFilled(f) || isFilled(ownProp(values, f.name)));
|
|
75
|
+
}
|
|
76
|
+
function missingAmong(gating, values) {
|
|
77
|
+
return gating.filter((f) => !fieldFilled(f, ownProp(values, f.name)));
|
|
78
|
+
}
|
|
52
79
|
function shownAsOptional(field) {
|
|
53
80
|
return !field.required && field.defaultValue === void 0;
|
|
54
81
|
}
|
|
@@ -71,6 +98,6 @@ function anyOptionalStructureOpen(fields, values) {
|
|
|
71
98
|
);
|
|
72
99
|
}
|
|
73
100
|
|
|
74
|
-
export { anyOptionalStructureOpen, computeReadiness, fieldFilled, foldsBehindOptionalDisclosure, isFilled, isFoldedOptional, isOptionalStructure, mustBeFilled, optionalStructureOpen, shownAsOptional };
|
|
75
|
-
//# sourceMappingURL=chunk-
|
|
76
|
-
//# sourceMappingURL=chunk-
|
|
101
|
+
export { anyOptionalStructureOpen, computeReadiness, fieldFilled, foldsBehindOptionalDisclosure, gateRefusal, isFilled, isFoldedOptional, isOptionalStructure, missingFields, mustBeFilled, optionalStructureOpen, shownAsOptional, waitingFields };
|
|
102
|
+
//# sourceMappingURL=chunk-TF4RTKLH.js.map
|
|
103
|
+
//# sourceMappingURL=chunk-TF4RTKLH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/readiness.ts"],"names":[],"mappings":"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","file":"chunk-TF4RTKLH.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 { ObjectRunField, 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 // A required child must be filled. An OPTIONAL child the value holds\n // something in owes its own concept the same - the rule a touched optional\n // input follows at the top level (`computeReadiness`) and a started list\n // row follows below, applied at every depth. Asking required children\n // alone let a half-filled optional structure INSIDE a structure through:\n // `{bank: {holder: 'x', iban: ''}}` satisfied ajv (a present empty string\n // is present, a content model carries no `minLength`) and readiness never\n // looked below `bank`, so the button stayed live and the run went out with\n // a required child blank. An optional child that holds nothing is absent,\n // and absence blocks nothing - which is what closing it writes.\n return field.fields.every((f) => {\n const child = ownProp(obj, f.name);\n return f.required ? fieldFilled(f, child) : !isFilled(child) || fieldFilled(f, child);\n });\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 = gatingFields(fields, values);\n const missing = missingAmong(gating, values).map((f) => f.title ?? f.name);\n return { total: gating.length, ready: gating.length - missing.length, missing };\n}\n\n/**\n * The top-level fields that stand between the current values and a run, as\n * fields rather than names, in the descriptor's order: the inputs\n * `computeReadiness` counts as missing. A surface that names them in its own\n * words - a humanised label, a run button's description - reads them here, so\n * it names exactly what the count says.\n */\nexport function missingFields(\n fields: readonly RunField[],\n values: Record<string, unknown> | undefined,\n): RunField[] {\n return missingAmong(gatingFields(fields, values), values);\n}\n\n/**\n * Why the run gate refuses a field, as the structure or the form holding it\n * holds it: a field that must be filled (`mustFill`) and is not is\n * `required`, and a field that need not be but holds something short of what\n * its concept demands is `incomplete`. This is `fieldFilled`'s own rule for a\n * structure's members, so a structure the gate refuses has at least one\n * member it refuses, unless it is a structure that must be touched and holds\n * nothing at all.\n */\nexport function gateRefusal(\n field: RunField,\n value: unknown,\n mustFill: boolean,\n): 'required' | 'incomplete' | undefined {\n if (mustFill) return fieldFilled(field, value) ? undefined : 'required';\n return isFilled(value) && !fieldFilled(field, value) ? 'incomplete' : undefined;\n}\n\n/**\n * The fields a person fills to let the run go, at the depth a form draws\n * them: each missing input (`missingFields`), and in place of a structure the\n * members it refuses (`gateRefusal`), at every depth, which is where a form\n * marks them and where a layout that spreads a structure's members over the\n * page puts them. A structure that refuses no member of its own - one that\n * must be touched and holds nothing, with no required member - is itself the\n * field to fill. A list is one field: its rows are the person's own.\n */\nexport function waitingFields(\n fields: readonly RunField[],\n values: Record<string, unknown> | undefined,\n): WaitingField[] {\n return missingFields(fields, values).flatMap((field) =>\n refusedWithin(field, ownProp(values, field.name), [field.name]),\n );\n}\n\n/** A field the run waits for, and the names that lead to it from the inputs, its own last. */\nexport interface WaitingField {\n field: RunField;\n path: readonly string[];\n}\n\nfunction refusedWithin(field: RunField, value: unknown, path: readonly string[]): WaitingField[] {\n if (field.kind !== 'object') return [{ field, path }];\n const members = optionalStructureOpen(value) ? (value as Record<string, unknown>) : undefined;\n const refused = field.fields.flatMap((member) => {\n const held = ownProp(members, member.name);\n return gateRefusal(member, held, member.required)\n ? refusedWithin(member, held, [...path, member.name])\n : [];\n });\n return refused.length > 0 ? refused : [{ field, path }];\n}\n\n/** The fields that gate: every one the method demands, and an optional one once it is touched. */\nfunction gatingFields(\n fields: readonly RunField[],\n values: Record<string, unknown> | undefined,\n): RunField[] {\n return fields.filter((f) => mustBeFilled(f) || isFilled(ownProp(values, f.name)));\n}\n\nfunction missingAmong(\n gating: readonly RunField[],\n values: Record<string, unknown> | undefined,\n): RunField[] {\n return gating.filter((f) => !fieldFilled(f, ownProp(values, f.name)));\n}\n\n/**\n * Whether a person should see this field as optional: not required AND carrying no authored\n * default. The standard marks a defaulted field `required: false` (it can never be missing), but it\n * is always filled, so presenting it as optional — the badge, the fold behind the optional toggle —\n * misreads it: a choice defaulted to one of its options is still a choice the user must see.\n * Presentation only; what gates a run is `mustBeFilled`.\n */\nexport function shownAsOptional(field: RunField): boolean {\n return !field.required && field.defaultValue === undefined;\n}\n\n/**\n * Whether a field is an optional structure: an OPTIONAL structured concept, at\n * any depth - a structure inside a structure, or an optional structured input\n * at the top level.\n *\n * Its value is either absent (closed) or an object (open), and closing it is\n * the one way a person says \"none of this\" once the structure's children carry\n * defaults - a half-filled optional structure otherwise reads as a choice\n * nobody made. It folds behind its container's \"+ N optional\" disclosure while\n * closed, and that disclosure opens and closes it\n * (`openOptionalStructures`, `closeOptionalStructures`).\n *\n * An item of a list is never one: a row is in the array because the person\n * added it.\n */\nexport function isOptionalStructure(field: RunField): field is ObjectRunField {\n return field.kind === 'object' && !field.required;\n}\n\n/**\n * Whether an optional structure reads open for this value: a plain object,\n * even `{}`. `undefined` and `null` read closed.\n *\n * Derived from the value, never held beside it, so a host that seeds the value\n * (`seedObjectValue`) has opened the structure, and one that clears it has\n * closed it. Open is presentation; what the run makes of the value is\n * `isFilled`'s answer as everywhere else - an open structure holding nothing\n * at all is still absent on the wire, exactly as an opened optional section\n * left blank always was, while one holding anything owes its concept every\n * required child (`fieldFilled`).\n */\nexport function optionalStructureOpen(value: unknown): boolean {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Whether a field takes part in its container's \"+ N optional\" disclosure:\n * shown as optional (`shownAsOptional`), or an optional structure\n * (`isOptionalStructure`) whatever its defaults.\n */\nexport function foldsBehindOptionalDisclosure(field: RunField): boolean {\n return shownAsOptional(field) || isOptionalStructure(field);\n}\n\n/**\n * Whether a field is folded behind the disclosure right now, for this value:\n * it takes part in the disclosure and is empty - for an optional structure,\n * closed (`optionalStructureOpen`); for any other field, not `isFilled`. The\n * disclosure's count, \"+ N optional\", is the number of fields this answers\n * `true` for while it is collapsed.\n */\nexport function isFoldedOptional(field: RunField, value: unknown): boolean {\n if (!foldsBehindOptionalDisclosure(field)) return false;\n return isOptionalStructure(field) ? !optionalStructureOpen(value) : !isFilled(value);\n}\n\n/** Whether any optional structure among `fields` is open in `values`. */\nexport function anyOptionalStructureOpen(\n fields: readonly RunField[],\n values: Record<string, unknown> | undefined,\n): boolean {\n return fields.some(\n (f) => isOptionalStructure(f) && optionalStructureOpen(ownProp(values, f.name)),\n );\n}\n"]}
|