@pipelex/mthds-form 0.9.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +3 -0
  2. package/dist/{chunk-S4CB423P.js → chunk-4JQAJT7C.js} +3 -3
  3. package/dist/{chunk-S4CB423P.js.map → chunk-4JQAJT7C.js.map} +1 -1
  4. package/dist/{chunk-YABRYTQ4.js → chunk-4KGQ65TQ.js} +3 -3
  5. package/dist/{chunk-YABRYTQ4.js.map → chunk-4KGQ65TQ.js.map} +1 -1
  6. package/dist/chunk-CAKQ5VFT.js +35 -0
  7. package/dist/chunk-CAKQ5VFT.js.map +1 -0
  8. package/dist/{chunk-25I6JDIK.js → chunk-FABY3DEY.js} +2 -2
  9. package/dist/chunk-FABY3DEY.js.map +1 -0
  10. package/dist/{chunk-AZZAAGNX.js → chunk-TID7ZN63.js} +7 -3
  11. package/dist/chunk-TID7ZN63.js.map +1 -0
  12. package/dist/{chunk-KG4IYFWI.js → chunk-W56WIIVC.js} +174 -51
  13. package/dist/chunk-W56WIIVC.js.map +1 -0
  14. package/dist/chunk-XCO6GTEC.js +38 -0
  15. package/dist/chunk-XCO6GTEC.js.map +1 -0
  16. package/dist/{chunk-KIEOLP2L.js → chunk-ZB4RYUKV.js} +4 -4
  17. package/dist/chunk-ZB4RYUKV.js.map +1 -0
  18. package/dist/core/derive.d.ts +1 -0
  19. package/dist/core/derive.js +2 -2
  20. package/dist/core/descriptor.d.ts +13 -2
  21. package/dist/core/descriptor.js +1 -1
  22. package/dist/core/file-formats.d.ts +34 -7
  23. package/dist/core/file-formats.js +1 -1
  24. package/dist/core/gating-fields.d.ts +1 -0
  25. package/dist/core/index.d.ts +2 -1
  26. package/dist/core/index.js +7 -6
  27. package/dist/core/narrow-file-formats.d.ts +50 -0
  28. package/dist/core/narrow-file-formats.js +4 -0
  29. package/dist/core/narrow-file-formats.js.map +1 -0
  30. package/dist/core/native-content.d.ts +20 -11
  31. package/dist/core/native-content.js +1 -1
  32. package/dist/core/readiness.d.ts +1 -0
  33. package/dist/core/stuff-files.d.ts +1 -0
  34. package/dist/core/stuff-files.js +2 -2
  35. package/dist/core/values.d.ts +1 -0
  36. package/dist/core/values.js +3 -3
  37. package/dist/generative/index.d.ts +105 -12
  38. package/dist/generative/index.js +79 -157
  39. package/dist/generative/index.js.map +1 -1
  40. package/dist/{ui-designer.mthds → layout-design.mthds} +177 -29
  41. package/dist/react/index.d.ts +66 -10
  42. package/dist/react/index.js +5 -5
  43. package/dist/styles.css +1 -1
  44. package/package.json +3 -3
  45. package/dist/chunk-25I6JDIK.js.map +0 -1
  46. package/dist/chunk-2JWRG53S.js +0 -33
  47. package/dist/chunk-2JWRG53S.js.map +0 -1
  48. package/dist/chunk-AZZAAGNX.js.map +0 -1
  49. package/dist/chunk-KG4IYFWI.js.map +0 -1
  50. package/dist/chunk-KIEOLP2L.js.map +0 -1
package/README.md CHANGED
@@ -56,6 +56,8 @@ The wire descriptor states what each field IS; `buildRunFields` maps it structur
56
56
 
57
57
  Submitting goes through the gate: `buildRunInputsSchema` → `prepareRunInputs` → `validateRunInputs` → `apiInputsFromSchemaData`. The verdict is structured (`RunInputError[]`), and `describeValidationError` renders it for a person.
58
58
 
59
+ A file field (`document` or `image`) carries `formats`, the formats its slot accepts, and the control's hint, file-picker filter and check all read that one list. A host whose upload path takes fewer types narrows the whole tree once with `narrowFileFormats(fields, mimeTypes)` and has its server check uploads against the same list. [docs/upload-seam.md](docs/upload-seam.md) is everything a file control and its host owe each other.
60
+
59
61
  ## Theming
60
62
 
61
63
  The controls carry Tailwind classes over the standard shadcn/ui semantic tokens (`--background`, `--input`, `--border`, `--primary`, …). A host that already defines those tokens gets controls that match its product with no configuration. A host with no Tailwind build loads the prebuilt `@pipelex/mthds-form/styles.css`. Both paths, and the exact token list, are in [docs/theming.md](docs/theming.md).
@@ -68,6 +70,7 @@ The controls carry Tailwind classes over the standard shadcn/ui semantic tokens
68
70
  | [docs/dependency-budget.md](docs/dependency-budget.md) | what each layer may depend on, and how that is enforced |
69
71
  | [docs/theming.md](docs/theming.md) | the token contract and host setup |
70
72
  | [docs/wire-correspondence.md](docs/wire-correspondence.md) | the name-for-name wire ↔ `RunField` mapping |
73
+ | [docs/upload-seam.md](docs/upload-seam.md) | what a file control and its host owe each other: accepted formats and narrowing them, write-back paths, previews, the file card |
71
74
  | [docs/derivation-swap.md](docs/derivation-swap.md) | the record of the swap to the wire descriptor — what changed, what survived |
72
75
 
73
76
  ## Development
@@ -1,4 +1,4 @@
1
- import { isNativeHtmlNode, readHtmlContent, readDocumentContent, readImageContent } from './chunk-AZZAAGNX.js';
1
+ import { isNativeHtmlNode, readHtmlContent, readDocumentContent, readImageContent } from './chunk-TID7ZN63.js';
2
2
  import { ownProp } from './chunk-V3OV2XQT.js';
3
3
 
4
4
  // src/core/stuff-files.ts
@@ -76,5 +76,5 @@ function unwrapContent(field, value) {
76
76
  }
77
77
 
78
78
  export { collectStuffFiles };
79
- //# sourceMappingURL=chunk-S4CB423P.js.map
80
- //# sourceMappingURL=chunk-S4CB423P.js.map
79
+ //# sourceMappingURL=chunk-4JQAJT7C.js.map
80
+ //# sourceMappingURL=chunk-4JQAJT7C.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/stuff-files.ts"],"names":[],"mappings":";;;;AAuDO,SAAS,iBAAA,CAAkB,OAAiB,KAAA,EAA6B;AAC9E,EAAA,MAAM,QAAqB,EAAC;AAC5B,EAAA,IAAA,CAAK,KAAA,EAAO,KAAA,EAAO,EAAC,EAAG,KAAK,CAAA;AAC5B,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,IAAA,CAAK,KAAA,EAAiB,GAAA,EAAc,IAAA,EAAgB,GAAA,EAAwB;AACnF,EAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,KAAA,EAAO,GAAG,CAAA;AACtC,EAAA,MAAM,OAAO,IAAA,CAAK,MAAA,GAAS,IAAI,IAAA,GAAO,CAAC,MAAM,IAAI,CAAA;AAYjD,EAAA,IAAI,gBAAA,CAAiB,KAAK,CAAA,EAAG;AAC3B,IAAA,MAAM,OAAA,GAAU,gBAAgB,KAAK,CAAA;AACrC,IAAA,IAAI,SAAS,SAAA,EAAW;AACtB,MAAA,GAAA,CAAI,IAAA,CAAK;AAAA,QACP,MAAM,OAAA,CAAQ,SAAA;AAAA,QACd,SAAA,EAAW,MAAA;AAAA,QACX,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA;AAAA,QACnB,IAAA,EAAM;AAAA,OACP,CAAA;AAAA,IACH;AACA,IAAA;AAAA,EACF;AAEA,EAAA,QAAQ,MAAM,IAAA;AAAM,IAClB,KAAK,OAAA,EAAS;AACZ,MAAA,MAAM,OAAA,GAAU,iBAAiB,KAAK,CAAA;AACtC,MAAA,IAAI,SAAS,GAAA,EAAK;AAChB,QAAA,GAAA,CAAI,IAAA,CAAK;AAAA,UACP,KAAK,OAAA,CAAQ,GAAA;AAAA,UACb,WAAW,OAAA,CAAQ,SAAA;AAAA,UACnB,UAAU,OAAA,CAAQ,QAAA;AAAA,UAClB,UAAU,OAAA,CAAQ,QAAA;AAAA,UAClB,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA;AAAA,UACnB,IAAA,EAAM;AAAA,SACP,CAAA;AAAA,MACH;AACA,MAAA;AAAA,IACF;AAAA,IACA,KAAK,UAAA,EAAY;AACf,MAAA,MAAM,OAAA,GAAU,oBAAoB,KAAK,CAAA;AACzC,MAAA,IAAI,SAAS,GAAA,EAAK;AAChB,QAAA,GAAA,CAAI,IAAA,CAAK;AAAA,UACP,KAAK,OAAA,CAAQ,GAAA;AAAA,UACb,WAAW,OAAA,CAAQ,SAAA;AAAA,UACnB,UAAU,OAAA,CAAQ,QAAA;AAAA,UAClB,UAAU,OAAA,CAAQ,QAAA;AAAA,UAClB,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA;AAAA,UACnB,IAAA,EAAM;AAAA,SACP,CAAA;AAAA,MACH;AACA,MAAA;AAAA,IACF;AAAA,IACA,KAAK,QAAA,EAAU;AACb,MAAA,IAAI,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACzE,MAAA,MAAM,MAAA,GAAS,KAAA;AACf,MAAA,KAAA,MAAW,KAAA,IAAS,MAAM,MAAA,EAAQ;AAChC,QAAA,IAAA,CAAK,KAAA,EAAO,OAAA,CAAQ,MAAA,EAAQ,KAAA,CAAM,IAAI,CAAA,EAAG,CAAC,GAAG,IAAA,EAAM,KAAA,CAAM,IAAI,CAAA,EAAG,GAAG,CAAA;AAAA,MACrE;AACA,MAAA;AAAA,IACF;AAAA,IACA,KAAK,MAAA,EAAQ;AACX,MAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AAI3B,MAAA,KAAA,CAAM,QAAQ,CAAC,KAAA,EAAO,KAAA,KAAU,IAAA,CAAK,MAAM,IAAA,EAAM,KAAA,EAAO,CAAC,GAAG,MAAM,MAAA,CAAO,KAAK,CAAC,CAAA,EAAG,GAAG,CAAC,CAAA;AACtF,MAAA;AAAA,IACF;AAAA,IACA;AAIE,MAAA;AAAA;AAEN;AAMA,SAAS,aAAA,CAAc,OAAiB,KAAA,EAAyB;AAC/D,EAAA,IAAI,CAAC,KAAA,CAAM,UAAA,EAAY,OAAO,KAAA;AAC9B,EAAA,IAAI,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG,OAAO,KAAA;AAChF,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,KAAA,EAAkC,KAAA,CAAM,UAAU,CAAA;AAC5E,EAAA,OAAO,SAAA,KAAc,SAAY,KAAA,GAAQ,SAAA;AAC3C","file":"chunk-S4CB423P.js","sourcesContent":["import { ownProp } from './own-property';\nimport {\n isNativeHtmlNode,\n readDocumentContent,\n readHtmlContent,\n readImageContent,\n} from './native-content';\nimport type { RunField } from './descriptor';\n\n/**\n * One file a stuff carries: what to fetch, and what to call it once saved.\n */\nexport interface StuffFile {\n /**\n * The durable reference or URL the value states, for a file held BY\n * REFERENCE. Absent for inline content, which needs no fetch.\n */\n url?: string;\n /**\n * The content itself, for a file the stuff carries INLINE — markup, today.\n * Exactly one of `url` and `text` is set.\n */\n text?: string;\n /** The extension to save inline content under, without the dot. */\n extension?: string;\n /**\n * The URL that works outside this session, when the value carries one. A host\n * whose display URL is session-scoped (an owner-checked proxy route) needs\n * this for anything that leaves the browser.\n */\n publicUrl?: string;\n /** The file's own name when it states one — otherwise the caller derives it. */\n filename?: string;\n mimeType?: string;\n /** Where it sat, dotted: `report.attachments.2.scan`. Names a saved file. */\n path: string;\n kind: 'image' | 'document' | 'markup';\n}\n\n/**\n * Every file inside a stuff, found by walking the DESCRIPTOR — never the value.\n *\n * The distinction is the whole point. A payload walk would have to decide what\n * a file is by looking at it: an object with a `url`, a string that ends in\n * `.pdf`. That is the guessing this package exists to remove, and it is wrong in\n * both directions — a method whose output legitimately has a `url` field of its\n * own is not a document, and a stored image whose reference carries no extension\n * is. The descriptor already states `kind: \"image\"` or `kind: \"document\"`, so\n * the walk reads the declaration and consults the value only for the members\n * that declaration promises.\n *\n * Ordering is the authored one, depth-first, which is the order the rendered\n * view puts them in — so a download of three images numbers them the way the\n * reader saw them.\n */\nexport function collectStuffFiles(field: RunField, value: unknown): StuffFile[] {\n const found: StuffFile[] = [];\n walk(field, value, [], found);\n return found;\n}\n\nfunction walk(field: RunField, raw: unknown, path: string[], out: StuffFile[]): void {\n const value = unwrapContent(field, raw);\n const here = path.length > 0 ? path : [field.name];\n\n // Markup, before the kind switch, exactly as the renderer takes it: a\n // `native.Html` node's KIND is `object` (its content model has two members),\n // so the switch below would walk into it looking for files and find none.\n //\n // It is a file even though nothing stores it as one. The stuff IS a page —\n // the reader is looking at a rendered report — and handing them a JSON\n // envelope with the page inside it as a string is the same mistake as handing\n // them a URL instead of a picture. `isNativeHtmlNode` asks the descriptor\n // whether the concept refines `native.Html`, which is the standard's own way\n // to ask; it never looks at the value to decide.\n if (isNativeHtmlNode(field)) {\n const content = readHtmlContent(value);\n if (content?.innerHtml) {\n out.push({\n text: content.innerHtml,\n extension: 'html',\n path: here.join('.'),\n kind: 'markup',\n });\n }\n return;\n }\n\n switch (field.kind) {\n case 'image': {\n const content = readImageContent(value);\n if (content?.url) {\n out.push({\n url: content.url,\n publicUrl: content.publicUrl,\n filename: content.filename,\n mimeType: content.mimeType,\n path: here.join('.'),\n kind: 'image',\n });\n }\n return;\n }\n case 'document': {\n const content = readDocumentContent(value);\n if (content?.url) {\n out.push({\n url: content.url,\n publicUrl: content.publicUrl,\n filename: content.filename,\n mimeType: content.mimeType,\n path: here.join('.'),\n kind: 'document',\n });\n }\n return;\n }\n case 'object': {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return;\n const record = value as Record<string, unknown>;\n for (const child of field.fields) {\n walk(child, ownProp(record, child.name), [...here, child.name], out);\n }\n return;\n }\n case 'list': {\n if (!Array.isArray(value)) return;\n // The element descriptor's own `name` is unused by the standard, so the\n // index is what identifies an entry — the same thing the rendered list\n // shows beside each row.\n value.forEach((entry, index) => walk(field.item, entry, [...here, String(index)], out));\n return;\n }\n default:\n // Every other kind is a scalar. It cannot contain a file, and a `native`\n // node with no declared members (an `unknown` kind — a composite, say)\n // states nothing a walk could follow, so there is nothing to look at.\n return;\n }\n}\n\n/**\n * The one unwrap path, matching the renderer's: a content model is opened by\n * the property the schema named, and by nothing else.\n */\nfunction unwrapContent(field: RunField, value: unknown): unknown {\n if (!field.contentKey) return value;\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return value;\n const unwrapped = ownProp(value as Record<string, unknown>, field.contentKey);\n return unwrapped === undefined ? value : unwrapped;\n}\n"]}
1
+ {"version":3,"sources":["../src/core/stuff-files.ts"],"names":[],"mappings":"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","file":"chunk-4JQAJT7C.js","sourcesContent":["import { ownProp } from './own-property';\nimport {\n isNativeHtmlNode,\n readDocumentContent,\n readHtmlContent,\n readImageContent,\n} from './native-content';\nimport type { RunField } from './descriptor';\n\n/**\n * One file a stuff carries: what to fetch, and what to call it once saved.\n */\nexport interface StuffFile {\n /**\n * The durable reference or URL the value states, for a file held BY\n * REFERENCE. Absent for inline content, which needs no fetch.\n */\n url?: string;\n /**\n * The content itself, for a file the stuff carries INLINE — markup, today.\n * Exactly one of `url` and `text` is set.\n */\n text?: string;\n /** The extension to save inline content under, without the dot. */\n extension?: string;\n /**\n * The URL that works outside this session, when the value carries one. A host\n * whose display URL is session-scoped (an owner-checked proxy route) needs\n * this for anything that leaves the browser.\n */\n publicUrl?: string;\n /** The file's own name when it states one — otherwise the caller derives it. */\n filename?: string;\n mimeType?: string;\n /** Where it sat, dotted: `report.attachments.2.scan`. Names a saved file. */\n path: string;\n kind: 'image' | 'document' | 'markup';\n}\n\n/**\n * Every file inside a stuff, found by walking the DESCRIPTOR — never the value.\n *\n * The distinction is the whole point. A payload walk would have to decide what\n * a file is by looking at it: an object with a `url`, a string that ends in\n * `.pdf`. That is the guessing this package exists to remove, and it is wrong in\n * both directions — a method whose output legitimately has a `url` field of its\n * own is not a document, and a stored image whose reference carries no extension\n * is. The descriptor already states `kind: \"image\"` or `kind: \"document\"`, so\n * the walk reads the declaration and consults the value only for the members\n * that declaration promises.\n *\n * Ordering is the authored one, depth-first, which is the order the rendered\n * view puts them in — so a download of three images numbers them the way the\n * reader saw them.\n */\nexport function collectStuffFiles(field: RunField, value: unknown): StuffFile[] {\n const found: StuffFile[] = [];\n walk(field, value, [], found);\n return found;\n}\n\nfunction walk(field: RunField, raw: unknown, path: string[], out: StuffFile[]): void {\n const value = unwrapContent(field, raw);\n const here = path.length > 0 ? path : [field.name];\n\n // Markup, before the kind switch, exactly as the renderer takes it: a\n // `native.Html` node's KIND is `object` (its content model has two members),\n // so the switch below would walk into it looking for files and find none.\n //\n // It is a file even though nothing stores it as one. The stuff IS a page —\n // the reader is looking at a rendered report — and handing them a JSON\n // envelope with the page inside it as a string is the same mistake as handing\n // them a URL instead of a picture. `isNativeHtmlNode` asks the descriptor\n // whether the concept refines `native.Html`, which is the standard's own way\n // to ask; it never looks at the value to decide.\n if (isNativeHtmlNode(field)) {\n const content = readHtmlContent(value);\n if (content?.innerHtml) {\n out.push({\n text: content.innerHtml,\n extension: 'html',\n path: here.join('.'),\n kind: 'markup',\n });\n }\n return;\n }\n\n switch (field.kind) {\n case 'image': {\n const content = readImageContent(value);\n if (content?.url) {\n out.push({\n url: content.url,\n publicUrl: content.publicUrl,\n filename: content.filename,\n mimeType: content.mimeType,\n path: here.join('.'),\n kind: 'image',\n });\n }\n return;\n }\n case 'document': {\n const content = readDocumentContent(value);\n if (content?.url) {\n out.push({\n url: content.url,\n publicUrl: content.publicUrl,\n filename: content.filename,\n mimeType: content.mimeType,\n path: here.join('.'),\n kind: 'document',\n });\n }\n return;\n }\n case 'object': {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return;\n const record = value as Record<string, unknown>;\n for (const child of field.fields) {\n walk(child, ownProp(record, child.name), [...here, child.name], out);\n }\n return;\n }\n case 'list': {\n if (!Array.isArray(value)) return;\n // The element descriptor's own `name` is unused by the standard, so the\n // index is what identifies an entry — the same thing the rendered list\n // shows beside each row.\n value.forEach((entry, index) => walk(field.item, entry, [...here, String(index)], out));\n return;\n }\n default:\n // Every other kind is a scalar. It cannot contain a file, and a `native`\n // node with no declared members (an `unknown` kind — a composite, say)\n // states nothing a walk could follow, so there is nothing to look at.\n return;\n }\n}\n\n/**\n * The one unwrap path, matching the renderer's: a content model is opened by\n * the property the schema named, and by nothing else.\n */\nfunction unwrapContent(field: RunField, value: unknown): unknown {\n if (!field.contentKey) return value;\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return value;\n const unwrapped = ownProp(value as Record<string, unknown>, field.contentKey);\n return unwrapped === undefined ? value : unwrapped;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { buildRunFields } from './chunk-KIEOLP2L.js';
1
+ import { buildRunFields } from './chunk-ZB4RYUKV.js';
2
2
  import { inflateAllInputs, deflateAllInputs } from './chunk-GCXV76WU.js';
3
3
  import { isFilled } from './chunk-PM55AHRO.js';
4
4
  import { isOptionalInput, isPluralInput } from './chunk-WYH56SOZ.js';
@@ -197,5 +197,5 @@ function setValueAtPath(root, path, value) {
197
197
  }
198
198
 
199
199
  export { apiInputsFromRunValues, fieldsForContract, inputDataFromWorkingMemory, outputsFromPipeOutput, rjsfDataFromRunValues, runValuesFromStore, setValueAtPath, storeInputDataFromRunValues };
200
- //# sourceMappingURL=chunk-YABRYTQ4.js.map
201
- //# sourceMappingURL=chunk-YABRYTQ4.js.map
200
+ //# sourceMappingURL=chunk-4KGQ65TQ.js.map
201
+ //# sourceMappingURL=chunk-4KGQ65TQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/values.ts"],"names":[],"mappings":"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","file":"chunk-YABRYTQ4.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 } from './readiness';\nimport { isOptionalInput, isPluralInput } from './contracts';\nimport { deflateAllInputs, inflateAllInputs } 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 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// ─── 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-4KGQ65TQ.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 } from './readiness';\nimport { isOptionalInput, isPluralInput } from './contracts';\nimport { deflateAllInputs, inflateAllInputs } 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 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// ─── 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"]}
@@ -0,0 +1,35 @@
1
+ import { normalizeMimeType, acceptLabel } from './chunk-XCO6GTEC.js';
2
+
3
+ // src/core/narrow-file-formats.ts
4
+ function narrowFileFormats(fields, mimeTypes) {
5
+ const allowed = new Set(mimeTypes.map(normalizeMimeType));
6
+ const named = mimeTypes.length > 0 ? mimeTypes.join(", ") : "no MIME type";
7
+ const narrow = (field, path) => {
8
+ switch (field.kind) {
9
+ case "document":
10
+ case "image": {
11
+ const formats = field.formats.filter((format) => allowed.has(format.mimeType));
12
+ if (formats.length === 0) {
13
+ throw new Error(
14
+ `narrowFileFormats: the ${field.kind} field "${path}" would accept no format. It takes ${acceptLabel(field.formats) || "nothing"}, and the list names ${named}.`
15
+ );
16
+ }
17
+ return { ...field, formats };
18
+ }
19
+ case "object":
20
+ return {
21
+ ...field,
22
+ fields: field.fields.map((child) => narrow(child, `${path}.${child.name}`))
23
+ };
24
+ case "list":
25
+ return { ...field, item: narrow(field.item, `${path}[]`) };
26
+ default:
27
+ return field;
28
+ }
29
+ };
30
+ return fields.map((field) => narrow(field, field.name));
31
+ }
32
+
33
+ export { narrowFileFormats };
34
+ //# sourceMappingURL=chunk-CAKQ5VFT.js.map
35
+ //# sourceMappingURL=chunk-CAKQ5VFT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/narrow-file-formats.ts"],"names":[],"mappings":";;;AA6CO,SAAS,iBAAA,CACd,QACA,SAAA,EACY;AACZ,EAAA,MAAM,UAAU,IAAI,GAAA,CAAI,SAAA,CAAU,GAAA,CAAI,iBAAiB,CAAC,CAAA;AACxD,EAAA,MAAM,QAAQ,SAAA,CAAU,MAAA,GAAS,IAAI,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GAAI,cAAA;AAE5D,EAAA,MAAM,MAAA,GAAS,CAAC,KAAA,EAAiB,IAAA,KAA2B;AAC1D,IAAA,QAAQ,MAAM,IAAA;AAAM,MAClB,KAAK,UAAA;AAAA,MACL,KAAK,OAAA,EAAS;AACZ,QAAA,MAAM,OAAA,GAAU,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,CAAC,WAAW,OAAA,CAAQ,GAAA,CAAI,MAAA,CAAO,QAAQ,CAAC,CAAA;AAC7E,QAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,uBAAA,EAA0B,KAAA,CAAM,IAAI,CAAA,QAAA,EAAW,IAAI,CAAA,mCAAA,EACrC,WAAA,CAAY,KAAA,CAAM,OAAO,CAAA,IAAK,SAAS,CAAA,qBAAA,EAAwB,KAAK,CAAA,CAAA;AAAA,WACpF;AAAA,QACF;AACA,QAAA,OAAO,EAAE,GAAG,KAAA,EAAO,OAAA,EAAQ;AAAA,MAC7B;AAAA,MACA,KAAK,QAAA;AACH,QAAA,OAAO;AAAA,UACL,GAAG,KAAA;AAAA,UACH,MAAA,EAAQ,KAAA,CAAM,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,KAAU,MAAA,CAAO,KAAA,EAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,KAAA,CAAM,IAAI,EAAE,CAAC;AAAA,SAC5E;AAAA,MACF,KAAK,MAAA;AAGH,QAAA,OAAO,EAAE,GAAG,KAAA,EAAO,IAAA,EAAM,MAAA,CAAO,MAAM,IAAA,EAAM,CAAA,EAAG,IAAI,CAAA,EAAA,CAAI,CAAA,EAAE;AAAA,MAC3D;AACE,QAAA,OAAO,KAAA;AAAA;AACX,EACF,CAAA;AAEA,EAAA,OAAO,MAAA,CAAO,IAAI,CAAC,KAAA,KAAU,OAAO,KAAA,EAAO,KAAA,CAAM,IAAI,CAAC,CAAA;AACxD","file":"chunk-CAKQ5VFT.js","sourcesContent":["/**\n * Narrowing what a form's file slots accept to what a host will take.\n *\n * `buildRunFields` gives every `document` and `image` field the whole of its\n * kind's table (`./file-formats`): everything the runtime can decode. A host's\n * own upload path can be narrower - a method that only ever reads photographs,\n * a server that stores only some types - and a dropzone that advertises more\n * than the server takes refuses the file only after the upload has been asked\n * for. So a host narrows the tree once, with the same list its server checks\n * uploads against, and the control reads the narrowed list for its hint, its\n * picker filter and its check.\n *\n * It reads `RunField`, the kernel's own output, and never a JSON Schema: which\n * bytes a runtime can decode is not a wire fact, so there is nothing in the\n * schema to read, and the two schema walks the kernel allows stay the only two.\n *\n * It lives in the core rather than on `FieldEnv` so that a host's server, which\n * renders no control, can hold the very list its form was narrowed with.\n *\n * Pure: no React, no ajv, and the input tree is never mutated.\n */\n\nimport type { RunField } from './descriptor';\nimport { acceptLabel, normalizeMimeType } from './file-formats';\n\n/**\n * The field tree with every `document` and `image` field narrowed to the\n * formats whose MIME type is in `mimeTypes`, walking nested structures and\n * list items.\n *\n * One list serves every slot: it is intersected with each slot's own formats,\n * so a list of PDF, PNG and JPEG leaves a document slot with all three and an\n * image slot with PNG and JPEG. Narrowing never widens a slot - a MIME type the\n * slot's kind cannot take is simply not in the intersection - and each slot\n * keeps its own table's order, whatever order the list is in. MIME types are\n * compared the way `isAcceptedFile` compares them: without parameters, and\n * case-insensitively.\n *\n * **Throws when a slot would be left accepting nothing**, naming the field. A\n * dropzone that refuses every file is a host's configuration error, and a form\n * that renders one has already failed the person holding the file.\n *\n * The input tree is never mutated: a file slot, a structure or a list comes\n * back as a copy, and every other field as the same object it was.\n */\nexport function narrowFileFormats(\n fields: readonly RunField[],\n mimeTypes: readonly string[],\n): RunField[] {\n const allowed = new Set(mimeTypes.map(normalizeMimeType));\n const named = mimeTypes.length > 0 ? mimeTypes.join(', ') : 'no MIME type';\n\n const narrow = (field: RunField, path: string): RunField => {\n switch (field.kind) {\n case 'document':\n case 'image': {\n const formats = field.formats.filter((format) => allowed.has(format.mimeType));\n if (formats.length === 0) {\n throw new Error(\n `narrowFileFormats: the ${field.kind} field \"${path}\" would accept no format. ` +\n `It takes ${acceptLabel(field.formats) || 'nothing'}, and the list names ${named}.`,\n );\n }\n return { ...field, formats };\n }\n case 'object':\n return {\n ...field,\n fields: field.fields.map((child) => narrow(child, `${path}.${child.name}`)),\n };\n case 'list':\n // The item carries the list's own name, so the path marks the step into\n // an item rather than repeating the name: `gallery[]`, `cvs[].resume`.\n return { ...field, item: narrow(field.item, `${path}[]`) };\n default:\n return field;\n }\n };\n\n return fields.map((field) => narrow(field, field.name));\n}\n"]}
@@ -24,5 +24,5 @@ function conceptCategory(field) {
24
24
  }
25
25
 
26
26
  export { conceptCategory };
27
- //# sourceMappingURL=chunk-25I6JDIK.js.map
28
- //# sourceMappingURL=chunk-25I6JDIK.js.map
27
+ //# sourceMappingURL=chunk-FABY3DEY.js.map
28
+ //# sourceMappingURL=chunk-FABY3DEY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/descriptor.ts"],"names":[],"mappings":";AAwNO,SAAS,gBAAgB,KAAA,EAAkC;AAChE,EAAA,QAAQ,MAAM,IAAA;AAAM,IAClB,KAAK,MAAA;AAAA,IACL,KAAK,OAAA;AACH,MAAA,OAAO,MAAA;AAAA,IACT,KAAK,MAAA;AACH,MAAA,OAAO,MAAA;AAAA,IACT,KAAK,UAAA;AACH,MAAA,OAAO,UAAA;AAAA,IACT,KAAK,OAAA;AACH,MAAA,OAAO,OAAA;AAAA,IACT,KAAK,QAAA;AACH,MAAA,OAAO,QAAA;AAAA,IACT,KAAK,SAAA;AACH,MAAA,OAAO,SAAA;AAAA,IACT,KAAK,MAAA;AACH,MAAA,OAAO,QAAA;AAAA,IACT,KAAK,MAAA;AACH,MAAA,OAAO,MAAA;AAAA,IACT;AACE,MAAA,OAAO,YAAA;AAAA;AAEb","file":"chunk-FABY3DEY.js","sourcesContent":["/**\n * The `RunField` descriptor - the consumer-facing currency of the form kernel.\n *\n * A method's inputs arrive from the API as two sibling artifacts: the wire\n * input-form descriptor (`input_form`, the ordered presentation view the MTHDS\n * standard specifies) and the `pipe_io_contracts` entry whose `json_schema`\n * stays the validation contract. `buildRunFields` (in `derive.ts`) maps the\n * wire descriptor structurally onto this `RunField[]` shape - so the\n * presentational field components never touch either wire artifact directly,\n * and stories can describe any input shape declaratively.\n *\n * Pure module: no React, no API calls. Trivially unit-testable.\n */\n\nimport type { InputPresence } from './contracts';\nimport type { FileFormat } from './file-formats';\n\nexport type RunFieldKind =\n | 'text' // short single-line string\n | 'prose' // long free-text (textarea)\n | 'date' // a calendar date (schema `format: date` or `date-time`)\n | 'number' // integer or float\n | 'boolean' // true/false\n | 'enum' // pick one of a fixed set\n | 'document' // a file (PDF, docx…) by upload or URL\n | 'image' // a file rendered with a preview\n | 'object' // a structured concept with nested fields\n | 'list' // an array of one element type\n | 'unknown'; // anything we can't map → raw JSON escape hatch\n\nexport interface RunFieldCommon {\n /** The variable identifier as written in the method (rendered in mono). */\n name: string;\n /** Human label; falls back to `name` when absent. */\n title?: string;\n /** The concept this input carries, e.g. `native.Document`, `demo.Invoice`. */\n conceptRef?: string;\n /**\n * The concept's refinement chain, immediate parent first, walked to its end\n * (`['legal.BaseClause', 'native.Text']`). Straight off the wire descriptor;\n * absent when the concept refines nothing. \"Does this refine `native.X`?\" is\n * a membership test on this list, never shape sniffing.\n */\n refines?: string[];\n /** One-line helper text shown under the label. */\n description?: string;\n /** Required inputs always show; optional ones can collapse. */\n required: boolean;\n /**\n * The authored presence marker of the pipe's input slot, verbatim - set on\n * TOP-LEVEL fields only, like `gating`. `required` is its two-valued\n * projection (`presence !== 'optional'`); the marker itself is carried so a\n * renderer can distinguish the authored `!` assertion from a plain slot.\n */\n presence?: InputPresence;\n /**\n * The value applied when the caller omits the field - present only when a\n * default was AUTHORED, never the `null` a schema projection attaches to an\n * optional field. A defaulted field always arrives `required: false`.\n */\n defaultValue?: unknown;\n /** Example values for the field, when the wire carries any. */\n examples?: unknown[];\n /**\n * The node's effective intent hints - the flat `string → string` merge the\n * language defines, carried verbatim. Non-normative: a renderer that ignores\n * them stays correct, and nothing in the kernel's gating reads them.\n */\n hints?: Record<string, string>;\n /**\n * Whether Run waits for this input - set on TOP-LEVEL fields only, straight\n * from `inputMustBeFilled` (see `mustBeFilled` in `readiness.ts` for why this\n * is not the same question as `required`). Nested fields leave it undefined;\n * they are gated through their parent's `fieldFilled`.\n */\n gating?: boolean;\n /**\n * The single property this field's value sits inside on the wire, when its\n * concept declares a scalar content model: `native.Text` is `TextContent\n * {text}`, `native.Number` is `NumberContent {number}`, `native.YesNo` is\n * `YesNoContent {yes_no}`. The control still holds the plain scalar; the\n * value bridge wraps into this property on the way out and unwraps on the way\n * back in.\n *\n * A property NAME, never a schema - the descriptor stays the only currency\n * and `buildRunFields` stays the only reader of JSON Schema. Absent whenever\n * the value is already the whole content (a structured concept, a file, a\n * child property of a structure): wrapping those would double-nest them.\n */\n contentKey?: string;\n}\n\n/**\n * The constraint slots the two text kinds share, mapped from the wire's\n * `min_length` / `max_length` / `pattern` / `format`. `format` is the open\n * string set carrying schema formats the `date` kind does not absorb\n * (`'time'`, `'uri'`, …); a renderer may hint on it and ignore it safely.\n */\nexport interface TextConstraints {\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n format?: string;\n}\n\nexport interface TextRunField extends RunFieldCommon, TextConstraints {\n kind: 'text';\n placeholder?: string;\n}\n\nexport interface ProseRunField extends RunFieldCommon, TextConstraints {\n kind: 'prose';\n placeholder?: string;\n}\n\nexport interface DateRunField extends RunFieldCommon {\n kind: 'date';\n /**\n * The wire's `datetime` flag - true when the value carries a time of day\n * (the backing schema is `format: date-time`): the stored value is a full\n * RFC 3339 timestamp (`2026-07-06T00:00:00Z`) so it passes the API's\n * `date-time` validation, even though the control only asks for a day. False\n * for a bare calendar date, where the value stays `YYYY-MM-DD`.\n */\n datetime: boolean;\n}\n\nexport interface NumberRunField extends RunFieldCommon {\n kind: 'number';\n integer: boolean;\n min?: number;\n max?: number;\n}\n\nexport interface BooleanRunField extends RunFieldCommon {\n kind: 'boolean';\n}\n\nexport interface EnumRunField extends RunFieldCommon {\n kind: 'enum';\n options: string[];\n}\n\nexport interface FileRunField extends RunFieldCommon {\n kind: 'document' | 'image';\n /**\n * The formats this slot accepts, in the order the hint names them. The ONE\n * list the file control reads for all three of its answers - the hint under\n * the dropzone, the OS picker's filter and the check a picked or dropped file\n * must pass - so none of them can promise what another refuses.\n *\n * Not a wire fact: which bytes a runtime can decode is a property of the\n * runtime, so `buildRunFields` stamps the kind's whole table here\n * (`formatsForKind`), and a host whose own upload path takes less narrows the\n * tree with `narrowFileFormats`.\n */\n formats: readonly FileFormat[];\n}\n\nexport interface ObjectRunField extends RunFieldCommon {\n kind: 'object';\n fields: RunField[];\n}\n\nexport interface ListRunField extends RunFieldCommon {\n kind: 'list';\n /** The element descriptor (its `name` is unused; the index labels items). */\n item: RunField;\n /**\n * The FEWEST items the slot accepts. Absent for a variable `Concept[]` list,\n * which demands none.\n *\n * On a top-level slot this is the wire descriptor's `item_count` (present\n * exactly on a fixed `[N]` slot); an engine emits `minItems`/`maxItems` = N\n * on the same slot's schema, so the number `fieldFilled` gates on and the\n * keyword ajv reads agree by construction.\n *\n * The wire never puts `item_count` on a NESTED list, but the model behind a\n * structured concept may state `minItems` on an array property of its own -\n * so on nested nodes this is read off the schema, which is the direction\n * that cannot leave readiness more permissive than the gate.\n */\n itemCount?: number;\n /**\n * The MOST items the slot accepts, off the schema's `maxItems` (which a\n * fixed `[N]` slot also states as N).\n *\n * A separate field because it is a separate fact, and conflating them is a\n * bug that hides: `itemCount` was read as a minimum by readiness and as a\n * maximum by the control that stops offering `Add`, which agree only while\n * a contract states both bounds equal. A list told \"at least two\" was then\n * presented as a list of exactly two.\n */\n maxItemCount?: number;\n}\n\nexport interface UnknownRunField extends RunFieldCommon {\n kind: 'unknown';\n}\n\nexport type RunField =\n | TextRunField\n | ProseRunField\n | DateRunField\n | NumberRunField\n | BooleanRunField\n | EnumRunField\n | FileRunField\n | ObjectRunField\n | ListRunField\n | UnknownRunField;\n\n/** The short, friendly category label for a concept_ref (drives the pill dot). */\nexport type ConceptCategory =\n 'text' | 'date' | 'document' | 'image' | 'number' | 'boolean' | 'choice' | 'structured' | 'list';\n\nexport function conceptCategory(field: RunField): ConceptCategory {\n switch (field.kind) {\n case 'text':\n case 'prose':\n return 'text';\n case 'date':\n return 'date';\n case 'document':\n return 'document';\n case 'image':\n return 'image';\n case 'number':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'enum':\n return 'choice';\n case 'list':\n return 'list';\n default:\n return 'structured';\n }\n}\n"]}
@@ -62,7 +62,11 @@ function isNativeDateNode(node) {
62
62
  function isNativeCompositeNode(node) {
63
63
  return refinesNative(node, NATIVE_COMPOSITE_CONCEPT_REF);
64
64
  }
65
+ function isNativeValueNode(node) {
66
+ return isNativeDateNode(node) || isNativeHtmlNode(node) || isNativeCompositeNode(node);
67
+ }
65
68
  function refinesNative(node, ref) {
69
+ if (node.kind === "list") return false;
66
70
  if (node.conceptRef === ref) return true;
67
71
  return node.refines?.includes(ref) ?? false;
68
72
  }
@@ -189,6 +193,6 @@ function hasTypedScalarMarkers(value) {
189
193
  return isRecord(value) && TYPED_SCALAR_MARKERS.every((marker) => hasOwnProp(value, marker));
190
194
  }
191
195
 
192
- export { NATIVE_COMPOSITE_CONCEPT_REF, NATIVE_DATE_CONCEPT_REF, NATIVE_HTML_CONCEPT_REF, TYPED_SCALAR_MARKERS, formatDateContent, hasTypedScalarMarkers, isNativeCompositeNode, isNativeDateNode, isNativeHtmlNode, isViewableUrl, readCompositeContent, readDataUrl, readDateContent, readDocumentContent, readHtmlContent, readImageContent, viewableUrl };
193
- //# sourceMappingURL=chunk-AZZAAGNX.js.map
194
- //# sourceMappingURL=chunk-AZZAAGNX.js.map
196
+ export { NATIVE_COMPOSITE_CONCEPT_REF, NATIVE_DATE_CONCEPT_REF, NATIVE_HTML_CONCEPT_REF, TYPED_SCALAR_MARKERS, formatDateContent, hasTypedScalarMarkers, isNativeCompositeNode, isNativeDateNode, isNativeHtmlNode, isNativeValueNode, isViewableUrl, readCompositeContent, readDataUrl, readDateContent, readDocumentContent, readHtmlContent, readImageContent, viewableUrl };
197
+ //# sourceMappingURL=chunk-TID7ZN63.js.map
198
+ //# sourceMappingURL=chunk-TID7ZN63.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/native-content.ts"],"names":["parsed"],"mappings":"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","file":"chunk-TID7ZN63.js","sourcesContent":["/**\n * How to READ the native content models — the result side's twin of\n * `./file-formats`.\n *\n * A renderer handed a result knows the field's `kind` from the descriptor, and\n * for the kinds whose value is a structured content model rather than a bare\n * scalar (`document`, `image`, `date`) it must then read that model's members.\n * **That is reading the standard, not sniffing at it.** The distinction is the\n * whole reason this module exists as data rather than as heuristics inside a\n * control: a reader keyed by a STATED kind consults a pinned definition, where\n * a reader that works out *which* kind it is holding by counting properties is\n * guessing — and guessing is exactly what [../../docs/derivation-swap.md]\n * records removing from the input side.\n *\n * ## Where each shape comes from\n *\n * `native.Image` and `native.Document` are pinned by the standard, in\n * `docs/spec/native-concepts.md` § \"The Pinned Set (MTHDS 1.0.0)\". Both carry a\n * required `url` — a storage URI, an HTTP(S) URL, or a base64 data URL — and\n * optional presentation members beside it. Nothing here adds a member the spec\n * does not state.\n *\n * `native.Date` is pinned the same way (`{date, time}`), but a `date` FIELD\n * inside a structured concept is not a `native.Date` value: it is a python\n * `datetime.date`, and the serialization layer stamps it with its class. So a\n * date arrives in one of three legal shapes, all three of which a run really\n * produces:\n *\n * \"2026-03-14\" a plain ISO string\n * { date: \"2026-03-14\", __class__: \"date\", … } a typed scalar\n * { date: \"2026-03-14\", time: \"15:40:00\" } native.Date\n *\n * The typed-scalar envelope is the one shape the standard does NOT pin — it is\n * a serialization fact, measured off a real run rather than read off a spec —\n * and it is called out as such here so nobody mistakes it for normative.\n *\n * This module lives in core, not beside the control that renders it, for the\n * reason `file-formats.ts` established: a host that shows a result its own way\n * needs the same answer the control uses, and two copies of an answer is two\n * places for it to drift. Pure data and pure functions — no React.\n */\n\nimport { hasOwnProp, ownProp } from './own-property';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** A member the content models declare as `type = \"text\"`, when it is one. */\nfunction readText(record: Record<string, unknown>, key: string): string | undefined {\n const value = ownProp(record, key);\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction readNumber(record: Record<string, unknown>, key: string): number | undefined {\n const value = ownProp(record, key);\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\n/**\n * `native.Document`, read.\n *\n * `url` is the only required member; everything else is a producer's courtesy,\n * so a reader that demands any of them describes a value the standard permits\n * as malformed.\n */\nexport interface DocumentContentView {\n url: string;\n publicUrl?: string;\n mimeType?: string;\n filename?: string;\n title?: string;\n snippet?: string;\n}\n\n/** `native.Image`, read. `width`/`height` are optional but PAIRED by the spec. */\nexport interface ImageContentView {\n url: string;\n publicUrl?: string;\n caption?: string;\n mimeType?: string;\n filename?: string;\n width?: number;\n height?: number;\n sourcePrompt?: string;\n}\n\n/** `native.Date` (and the typed-scalar envelope a nested `date` field arrives in). */\nexport interface DateContentView {\n /** ISO 8601 calendar date, `YYYY-MM-DD`. */\n date: string;\n /** ISO 8601 time of day, present only when the source stated one. */\n time?: string;\n}\n\n/**\n * Read a `document`-kind value. `undefined` when the value carries no `url`,\n * which is the one member the standard requires — a caller renders its own\n * absence rather than being handed a half-value to test.\n */\nexport function readDocumentContent(value: unknown): DocumentContentView | undefined {\n if (!isRecord(value)) return undefined;\n const url = readText(value, 'url');\n if (url === undefined) return undefined;\n return {\n url,\n publicUrl: readText(value, 'public_url'),\n mimeType: readText(value, 'mime_type'),\n filename: readText(value, 'filename'),\n title: readText(value, 'title'),\n snippet: readText(value, 'snippet'),\n };\n}\n\n/** Read an `image`-kind value. Same contract as `readDocumentContent`. */\nexport function readImageContent(value: unknown): ImageContentView | undefined {\n if (!isRecord(value)) return undefined;\n const url = readText(value, 'url');\n if (url === undefined) return undefined;\n return {\n url,\n publicUrl: readText(value, 'public_url'),\n caption: readText(value, 'caption'),\n mimeType: readText(value, 'mime_type'),\n filename: readText(value, 'filename'),\n width: readNumber(value, 'width'),\n height: readNumber(value, 'height'),\n sourcePrompt: readText(value, 'source_prompt'),\n };\n}\n\n/**\n * `native.Html`, read.\n *\n * The only content model here that is NOT reached by a field kind, and the\n * reason is a deliberate decision of the standard: the kind vocabulary has no\n * `html` member, so a `native.Html` value is an `object` node over its two\n * declared fields. That is right for the descriptor — the kinds name how a value\n * is ENTERED, and markup is entered as text — and wrong for a result view, which\n * would otherwise print a page's source at a reader.\n *\n * So this one is keyed by CONCEPT rather than by kind, through\n * `isNativeHtmlNode` below. That is a membership test on facts the descriptor\n * states (`concept_ref`, `refines`), which the standard's own page calls out as\n * the supported way to ask \"does this refine `native.X`?\" — the opposite of\n * inspecting a value to guess what it is.\n */\nexport interface HtmlContentView {\n /** The markup itself. The one required member. */\n innerHtml: string;\n /** A class name for a wrapper a consumer may put around it, when stated. */\n cssClass?: string;\n}\n\n/** Read an `native.Html` value. `undefined` when it carries no markup. */\nexport function readHtmlContent(value: unknown): HtmlContentView | undefined {\n if (!isRecord(value)) return undefined;\n const innerHtml = readText(value, 'inner_html');\n if (innerHtml === undefined) return undefined;\n return { innerHtml, cssClass: readText(value, 'css_class') };\n}\n\n/** The concept refs whose content model is multi-property, spelled once each. */\nexport const NATIVE_HTML_CONCEPT_REF = 'native.Html';\nexport const NATIVE_DATE_CONCEPT_REF = 'native.Date';\nexport const NATIVE_COMPOSITE_CONCEPT_REF = 'native.Composite';\n\n/** One member of a composite: the name it was composed under, and its content. */\nexport interface CompositeMember {\n name: string;\n value: unknown;\n}\n\n/**\n * Read a `native.Composite` — a named composition of contents.\n *\n * The members are returned in the order the payload states them, which is the\n * order the method composed them in and the only order there is: a composite\n * declares no fields, so there is no authored order to prefer over it.\n *\n * `undefined` for anything that is not a record, so the caller falls back.\n */\nexport function readCompositeContent(value: unknown): CompositeMember[] | undefined {\n if (!isRecord(value)) return undefined;\n return Object.entries(value).map(([name, member]) => ({ name, value: member }));\n}\n\n/**\n * Whether a descriptor node carries markup — its concept IS `native.Html`, or\n * refines it.\n *\n * The refinement chain is checked, not just the leaf, because a method may\n * declare `legal.ClauseMarkup` refining `native.Html` and a reader of that result\n * wants the markup rendered just the same. `refines` is on the wire for exactly\n * this question.\n */\nexport function isNativeHtmlNode(node: NativeNode): boolean {\n return refinesNative(node, NATIVE_HTML_CONCEPT_REF);\n}\n\n/**\n * Whether a node IS a `native.Date` — the other content model the kind\n * vocabulary cannot name.\n *\n * `native.Date` is `{date, time}`, two properties, so nothing unwraps it and its\n * node is an `object`. Correct for the descriptor and wrong for a reader: a date\n * result rendered structurally is a two-field card whose second field says \"not\n * provided\", where the answer is a date. The `date` ARM already reads exactly\n * this model — `readDateContent` takes `{date, time}` — it just never fires,\n * because the kind says `object`.\n *\n * A `date` FIELD inside a structure is a different thing and needs none of this:\n * it carries `kind: \"date\"` already.\n */\nexport function isNativeDateNode(node: NativeNode): boolean {\n return refinesNative(node, NATIVE_DATE_CONCEPT_REF);\n}\n\n/**\n * Whether a node is a `native.Composite` — a named bag of contents.\n *\n * The third concept the kind vocabulary cannot name, and the one where it is\n * least able to: a composite declares no members at all, so its descriptor is\n * `kind: \"unknown\"` and its payload schema is `{additionalProperties: true}`\n * with no properties. Both are TRUE — the standard's escape hatch, honestly\n * used — and both leave a renderer with nothing to read, so the default\n * fallback prints the whole thing as a JSON blob.\n *\n * That is the worst available answer to a value that is, by definition, a map\n * of ordinary `StuffContent`s. Keyed by concept, a renderer can at least show\n * the members as the named things they are. See `CompositeValue`.\n */\nexport function isNativeCompositeNode(node: NativeNode): boolean {\n return refinesNative(node, NATIVE_COMPOSITE_CONCEPT_REF);\n}\n\n/**\n * Whether a node is one of the three natives the kind vocabulary cannot name —\n * a date, a page, a composite — which a renderer reads by CONCEPT before it\n * reads the kind. A list of one answers no (the predicates refuse a `list`\n * node), and its item answers yes.\n *\n * One answer, read twice: the result view renders a list whose item answers yes\n * one value per line rather than as a table, and the generative layer delegates\n * that list to the kernel, because no catalog component reads a native value.\n * Not re-exported: a host asks the three predicates it needs.\n */\nexport function isNativeValueNode(node: NativeNode): boolean {\n return isNativeDateNode(node) || isNativeHtmlNode(node) || isNativeCompositeNode(node);\n}\n\n/** What the native predicates read off a node: its stated kind and its concept. */\ninterface NativeNode {\n kind?: string;\n conceptRef?: string;\n refines?: readonly string[];\n}\n\n/**\n * The node IS one of the named native, or of a concept refining it.\n *\n * **A list of them is not one of them.** Plurality is not on the concept: a\n * plural node's `concept_ref` is its ELEMENT's, with the multiplicity stripped,\n * so a `native.Date[]` result is a `list` node whose concept reads\n * `native.Date`. Asking the concept alone answered yes for the whole list, and\n * every caller then treated the list as ONE date, one page or one composite: a\n * list of dates rendered as a single absent date, and a list of pages collected\n * no page at all. So a node whose stated kind is `list`\n * answers no, and its `item` answers for itself. The kind is the descriptor's\n * own statement, so this reads a stated fact and inspects no value.\n */\nfunction refinesNative(node: NativeNode, ref: string): boolean {\n if (node.kind === 'list') return false;\n if (node.conceptRef === ref) return true;\n return node.refines?.includes(ref) ?? false;\n}\n\n/**\n * Read a `date`-kind value, in any of the three shapes a run produces.\n *\n * The `date` member is read one level deep, because `native.Date`'s own `date`\n * member is a `datetime.date` and therefore arrives inside the same typed-scalar\n * envelope the whole value may already be wrapped in. One level is enough and\n * the recursion is bounded on purpose: a second envelope would be a\n * serialization bug, and following it forever would turn one into a hang.\n */\nexport function readDateContent(value: unknown): DateContentView | undefined {\n if (typeof value === 'string') {\n return value.length > 0 ? { date: value } : undefined;\n }\n if (!isRecord(value)) return undefined;\n\n const inner = ownProp(value, 'date');\n const date =\n typeof inner === 'string' ? inner : isRecord(inner) ? readText(inner, 'date') : undefined;\n if (date === undefined) return undefined;\n return { date, time: readText(value, 'time') };\n}\n\n/** `{date}` / `{date, time}` as one line, which is how both are read aloud. */\nexport function formatDateContent(content: DateContentView): string {\n return content.time ? `${content.date} ${content.time}` : content.date;\n}\n\n/**\n * The media types a `data:` URL may carry and still be viewable.\n *\n * An allow-list rather than a deny-list, because the question a sink asks is\n * \"can this paint without executing\", and only a closed set answers it. The\n * raster types are the ones the preview arms already name; `application/pdf` is\n * here because the input control previews a `data:application/pdf` value a\n * storage-less host wrote back, through an `<object>`, and reads this same\n * predicate to decide.\n *\n * `image/svg+xml` is deliberately absent: an SVG paints as a picture and\n * EXECUTES as a document the moment it is opened in a tab, which is one click\n * away from every image this package paints.\n */\nconst VIEWABLE_DATA_MEDIA_TYPES = new Set([\n 'image/png',\n 'image/jpeg',\n 'image/gif',\n 'image/webp',\n 'image/avif',\n 'application/pdf',\n]);\n\n/**\n * An origin no host can be, to resolve a relative reference against.\n *\n * `.invalid` is reserved by RFC 2606 and resolves nowhere, so nothing can be\n * fetched from it by accident. It exists only so a path can be parsed at all;\n * the answer taken from the parse is whether the reference STAYED on it.\n */\nconst RELATIVE_SENTINEL_ORIGIN = 'https://url-gate.invalid';\n\n/**\n * A scheme, by RFC 3986's grammar: a letter, then letters, digits, `+`, `-`, `.`\n *\n * Read off the string rather than inferred from a parse, because a parse needs a\n * base and the base is what makes the answer wrong — see {@link viewableUrl}.\n */\nconst OWN_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;\n\n/**\n * What the WHATWG URL parser strips before it parses, applied before we judge.\n *\n * Leading and trailing C0 controls and spaces are removed, and every internal\n * tab, line feed and carriage return. This is not tidying: a scheme test run on\n * the raw string and a browser running on the parsed one disagree about\n * `\" https://cdn/x.png\"` — `new URL()` reads it as `https:` and `/^https?:/`\n * does not — and a host that validated a member by parsing while this gate\n * prefix-matched would have judged a different string than the one painted.\n */\nfunction stripUrlWhitespace(candidate: string): string {\n // Written out rather than as a character-class regex: the range IS the control\n // characters, which is exactly what a regex may not spell (`no-control-regex`).\n let start = 0;\n let end = candidate.length;\n while (start < end && candidate.charCodeAt(start) <= 0x20) start += 1;\n while (end > start && candidate.charCodeAt(end - 1) <= 0x20) end -= 1;\n // Scan before rebuilding, and rebuild only from the first character that has\n // to go. Almost every URL has nothing to strip inside it, and the one that\n // does is the one that is huge: an inline `data:` image is judged three or\n // four times per render, and a per-character rebuild of a megabyte-long\n // payload each time is hundreds of milliseconds of frozen main thread for a\n // string that comes back identical.\n for (let index = start; index < end; index += 1) {\n const code = candidate.charCodeAt(index);\n // Tab, line feed, carriage return - removed wherever they appear, not only\n // at the edges, which is what the parser does with them.\n if (code === 0x09 || code === 0x0a || code === 0x0d) {\n let stripped = candidate.slice(start, index);\n for (let rest = index; rest < end; rest += 1) {\n const restCode = candidate.charCodeAt(rest);\n if (restCode !== 0x09 && restCode !== 0x0a && restCode !== 0x0d)\n stripped += candidate[rest];\n }\n return stripped;\n }\n }\n return start === 0 && end === candidate.length ? candidate : candidate.slice(start, end);\n}\n\n/** What a `data:` URL says about the bytes it carries. */\nexport interface DataUrlView {\n /** Lowercased, without its parameters; `text/plain` when the URL declares none. */\n mediaType: string;\n /** The size of the DECODED payload, in bytes. */\n bytes: number;\n}\n\n/**\n * A `data:` URL's media type and decoded size, or `undefined` for any other URL.\n *\n * A host that encodes a picked file in the browser writes the file itself into\n * the value's `url`, so a thirty-kilobyte PDF is a forty-thousand-character\n * string, and printed where a reference would be it tells a person nothing. Its\n * type and its size are what they can read.\n *\n * The size is COUNTED rather than decoded. A base64 payload's size follows from\n * how many symbols it holds, and decoding a megabyte to measure it would be the\n * frozen main thread {@link stripUrlWhitespace} is written to avoid. This is a\n * reading for display, not a gate: whether the URL may be painted is still\n * {@link viewableUrl}'s answer alone.\n */\nexport function readDataUrl(candidate: string): DataUrlView | undefined {\n const url = candidate.trimStart();\n if (!/^data:/i.test(url)) return undefined;\n const comma = url.indexOf(',');\n if (comma === -1) return undefined;\n const [declared = '', ...parameters] = url.slice('data:'.length, comma).split(';');\n const trimmed = declared.trim().toLowerCase();\n // A fragment is not payload. `#page=2` on an inline PDF is the viewer's, and\n // counting it as bytes is counting the URL rather than the file.\n const fragment = url.indexOf('#', comma);\n const payload = fragment === -1 ? url.slice(comma + 1) : url.slice(comma + 1, fragment);\n const base64 = parameters.some((parameter) => parameter.trim().toLowerCase() === 'base64');\n return {\n mediaType: trimmed === '' ? 'text/plain' : trimmed,\n bytes: base64 ? base64Bytes(payload) : percentEncodedBytes(payload),\n };\n}\n\n/**\n * Three bytes for every four symbols, ignoring what is not one: the `=`\n * padding, and the line breaks a MIME encoder wraps its output at. Both\n * alphabets count, the standard one and the URL-safe one.\n *\n * A percent-escape is resolved to the character it stands for before it is\n * judged, because a payload may be escaped anywhere — `YQ%3D%3D` is `YQ==`,\n * one byte, and counting `3` and `D` as symbols made it four.\n */\nfunction base64Bytes(payload: string): number {\n let symbols = 0;\n for (let index = 0; index < payload.length; index += 1) {\n let code = payload.charCodeAt(index);\n if (code === 0x25) {\n const escaped = Number.parseInt(payload.slice(index + 1, index + 3), 16);\n if (!Number.isNaN(escaped) && index + 2 < payload.length) {\n code = escaped;\n index += 2;\n }\n }\n if (\n (code >= 0x41 && code <= 0x5a) ||\n (code >= 0x61 && code <= 0x7a) ||\n (code >= 0x30 && code <= 0x39) ||\n code === 0x2b ||\n code === 0x2f ||\n code === 0x2d ||\n code === 0x5f\n )\n symbols += 1;\n }\n return Math.floor((symbols * 3) / 4);\n}\n\n/** A percent-escape is one byte; any other character is its UTF-8 length. */\nfunction percentEncodedBytes(payload: string): number {\n const escapes = payload.match(/%[0-9a-f]{2}/gi)?.length ?? 0;\n return new TextEncoder().encode(payload).length - escapes * 2;\n}\n\n/** The media type a `data:` URL declares, lowercased; `text/plain` when it declares none. */\nfunction dataUrlMediaType(parsed: URL): string {\n const declared = parsed.pathname.split(/[;,]/, 1)[0] ?? '';\n const trimmed = declared.trim().toLowerCase();\n return trimmed === '' ? 'text/plain' : trimmed;\n}\n\n/**\n * The URL a browser can paint, normalised — or `undefined` when there is none.\n *\n * **The string this returns is the string a sink must use.** That is the whole\n * point of returning one rather than answering yes or no: the gate judges a\n * normalised URL, so handing the caller back the raw member would let the two\n * diverge again on exactly the whitespace the parser strips.\n *\n * The gate parses rather than prefix-matches, and branches on the protocol the\n * parser reports:\n *\n * - `http:` and `https:` are viewable.\n * - `blob:` is viewable. A blob URL is bound to the origin that minted it, so a\n * payload cannot forge one that points anywhere else.\n * - `data:` is viewable only for a media type in {@link VIEWABLE_DATA_MEDIA_TYPES}.\n * `data:text/html` is a document at the EMBEDDING page's own origin, which is\n * what made an unsandboxed frame over a payload URL a real hole.\n * - Everything else is not: `javascript:`, `file:` (a run on a developer's\n * machine writes one, and a browser refuses to load it from an http page) and\n * `pipelex-storage://`, which cannot be resolved client-side at all — that is\n * the upload seam's job (see [../../docs/upload-seam.md]), and a renderer with\n * no resolver must show the reference rather than a broken `<img>`.\n *\n * **A root-relative path counts**, and leaving it out was a real bug rather\n * than an omission. A host's URL resolver naturally hands back a path on its\n * OWN origin — `/api/assets/…`, an authenticated route that streams the object\n * — which is the whole point of resolving rather than exposing storage. This\n * predicate rejected it for having no scheme, so the resolved URL was thrown\n * away and the arms fell back to the payload's `public_url`: a presigned URL\n * that had already expired. The image stayed broken with the fix in place.\n *\n * A path is rejected when it is protocol-relative — `//host/x`, and the two\n * backslash spellings the parser reads the same way (`\\\\host\\x`, `/\\host/x`) —\n * and then checked against the sentinel as well. An accepted path is returned\n * relative, because making it absolute against a sentinel would point it at\n * nowhere.\n */\nexport function viewableUrl(candidate: string | undefined): string | undefined {\n if (!candidate) return undefined;\n const stripped = stripUrlWhitespace(candidate);\n if (stripped === '') return undefined;\n\n // Whether the string carries a scheme is read OFF THE STRING, before any base\n // is applied, because the parser reads a candidate whose scheme equals the\n // base's as relative to that base. Deciding it with a second, base-less parse\n // let the two disagree: `https:cdn.example/x.png` was judged absolute and\n // resolved against the sentinel, so the gate handed a sink\n // `https://url-gate.invalid/cdn.example/x.png` — an internal placeholder, in a\n // real `<img src>`, in place of a URL that had worked. The grammar is RFC\n // 3986's, and it agrees with the parser on every string either accepts.\n if (OWN_SCHEME_RE.test(stripped)) {\n let parsed: URL;\n try {\n parsed = new URL(stripped);\n } catch {\n return undefined;\n }\n switch (parsed.protocol) {\n case 'http:':\n case 'https:':\n case 'blob:':\n return parsed.href;\n case 'data:':\n return VIEWABLE_DATA_MEDIA_TYPES.has(dataUrlMediaType(parsed)) ? parsed.href : undefined;\n default:\n return undefined;\n }\n }\n\n // No scheme of its own: a reference relative to wherever the page is. Only a\n // root-relative path qualifies.\n if (!stripped.startsWith('/')) return undefined;\n // Protocol-relative, rejected on the SPELLING. \"Did it stay on the sentinel\"\n // reads as the same rule and is blind in exactly one place — the sentinel's\n // own host — so `//url-gate.invalid/x` passed the test written to stop it.\n const afterSlash = stripped.charCodeAt(1);\n if (afterSlash === 0x2f || afterSlash === 0x5c) return undefined;\n let parsed: URL;\n try {\n parsed = new URL(stripped, RELATIVE_SENTINEL_ORIGIN);\n } catch {\n return undefined;\n }\n return parsed.origin === RELATIVE_SENTINEL_ORIGIN ? stripped : undefined;\n}\n\n/**\n * Whether a browser can paint this URL as-is — {@link viewableUrl} as a guard.\n *\n * Prefer `viewableUrl` wherever the URL is then USED: this answers the question\n * without handing back the normalised string, so a caller that acts on the raw\n * candidate acts on a string the gate did not judge.\n */\nexport function isViewableUrl(url: string | undefined): url is string {\n return viewableUrl(url) !== undefined;\n}\n\n/**\n * The typed-scalar markers the serialization layer stamps onto a value.\n *\n * Exported so a host can recognise (and strip) them; nothing in the kernel\n * BRANCHES on them, which is the point — a reader keyed by these would be\n * reading the serializer instead of the standard.\n */\nexport const TYPED_SCALAR_MARKERS = ['__class__', '__module__'] as const;\n\n/** Whether a record carries the typed-scalar markers. Informational. */\nexport function hasTypedScalarMarkers(value: unknown): boolean {\n return isRecord(value) && TYPED_SCALAR_MARKERS.every((marker) => hasOwnProp(value, marker));\n}\n"]}