@elytracms/studio 0.0.91 → 0.0.93

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 (353) hide show
  1. package/convex/asset-alt-migration.test.ts +127 -0
  2. package/convex/asset-replace.test.ts +168 -0
  3. package/convex/asset-revalidate.test.ts +6 -6
  4. package/convex/assets.ts +41 -5
  5. package/convex/cms.ts +537 -177
  6. package/convex/contract-parity.test.ts +173 -19
  7. package/convex/delivery.ts +26 -13
  8. package/convex/document-revalidate.test.ts +17 -16
  9. package/convex/orphan-hydration.test.ts +116 -0
  10. package/convex/presence-cost.test.ts +89 -0
  11. package/convex/rail-write-parity.test.ts +17 -14
  12. package/convex/referenceMaintenance.ts +15 -15
  13. package/convex/schema.ts +70 -5
  14. package/convex/triggers.ts +21 -8
  15. package/convex/write-limits.ts +1 -1
  16. package/dist/components/ai/ai-chat-panel.js +33 -22
  17. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  18. package/dist/components/ai/proposal-card.js +2 -1
  19. package/dist/components/ai/proposal-card.js.map +1 -1
  20. package/dist/components/ai/translate-document-action.d.ts +8 -2
  21. package/dist/components/ai/translate-document-action.js.map +1 -1
  22. package/dist/components/ai/translate-document-dialog.d.ts +1 -1
  23. package/dist/components/ai/translate-document-dialog.js +14 -8
  24. package/dist/components/ai/translate-document-dialog.js.map +1 -1
  25. package/dist/components/ai/translate-skipped.d.ts +19 -0
  26. package/dist/components/ai/translate-skipped.js +46 -0
  27. package/dist/components/ai/translate-skipped.js.map +1 -0
  28. package/dist/components/app-views/default-modules.d.ts +23 -6
  29. package/dist/components/app-views/default-modules.js +33 -14
  30. package/dist/components/app-views/default-modules.js.map +1 -1
  31. package/dist/components/assets/asset-detail.js +18 -8
  32. package/dist/components/assets/asset-detail.js.map +1 -1
  33. package/dist/components/assets/asset-metadata.d.ts +15 -4
  34. package/dist/components/assets/asset-metadata.js +15 -2
  35. package/dist/components/assets/asset-metadata.js.map +1 -1
  36. package/dist/components/assets/asset-search.js +4 -1
  37. package/dist/components/assets/asset-search.js.map +1 -1
  38. package/dist/components/assets/asset-selection.d.ts +0 -8
  39. package/dist/components/assets/asset-selection.js +4 -23
  40. package/dist/components/assets/asset-selection.js.map +1 -1
  41. package/dist/components/assets/asset-thumb.js +3 -2
  42. package/dist/components/assets/asset-thumb.js.map +1 -1
  43. package/dist/components/assets/assets-collection-view-source.d.ts +3 -3
  44. package/dist/components/assets/assets-collection-view-source.js +9 -5
  45. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  46. package/dist/components/assets/assets-store.d.ts +14 -0
  47. package/dist/components/assets/assets-store.js +23 -1
  48. package/dist/components/assets/assets-store.js.map +1 -1
  49. package/dist/components/assets/replace-file.d.ts +22 -0
  50. package/dist/components/assets/replace-file.js +59 -0
  51. package/dist/components/assets/replace-file.js.map +1 -0
  52. package/dist/components/builder/blocks-model.d.ts +14 -26
  53. package/dist/components/builder/blocks-model.js +35 -41
  54. package/dist/components/builder/blocks-model.js.map +1 -1
  55. package/dist/components/builder/blocks-panel.d.ts +7 -3
  56. package/dist/components/builder/blocks-panel.js +6 -5
  57. package/dist/components/builder/blocks-panel.js.map +1 -1
  58. package/dist/components/builder/builder-store.d.ts +126 -30
  59. package/dist/components/builder/builder-store.js +169 -67
  60. package/dist/components/builder/builder-store.js.map +1 -1
  61. package/dist/components/builder/canvas-body.d.ts +30 -0
  62. package/dist/components/builder/canvas-body.js +38 -0
  63. package/dist/components/builder/canvas-body.js.map +1 -0
  64. package/dist/components/builder/canvas-chrome-css.js +7 -0
  65. package/dist/components/builder/canvas-chrome-css.js.map +1 -1
  66. package/dist/components/builder/canvas-dnd.d.ts +7 -7
  67. package/dist/components/builder/canvas-dnd.js +5 -5
  68. package/dist/components/builder/canvas-dnd.js.map +1 -1
  69. package/dist/components/builder/canvas-render-context.d.ts +30 -22
  70. package/dist/components/builder/canvas-render-context.js +44 -44
  71. package/dist/components/builder/canvas-render-context.js.map +1 -1
  72. package/dist/components/builder/canvas-runs.d.ts +45 -0
  73. package/dist/components/builder/canvas-runs.js +51 -0
  74. package/dist/components/builder/canvas-runs.js.map +1 -0
  75. package/dist/components/builder/canvas.d.ts +5 -4
  76. package/dist/components/builder/canvas.js +34 -6
  77. package/dist/components/builder/canvas.js.map +1 -1
  78. package/dist/components/builder/clipboard-actions.d.ts +15 -0
  79. package/dist/components/builder/clipboard-actions.js +50 -13
  80. package/dist/components/builder/clipboard-actions.js.map +1 -1
  81. package/dist/components/builder/composition-field-editor.d.ts +10 -16
  82. package/dist/components/builder/composition-field-editor.js +30 -40
  83. package/dist/components/builder/composition-field-editor.js.map +1 -1
  84. package/dist/components/builder/composition-patches.d.ts +7 -0
  85. package/dist/components/builder/composition-patches.js +256 -0
  86. package/dist/components/builder/composition-patches.js.map +1 -0
  87. package/dist/components/builder/delete-key.d.ts +5 -0
  88. package/dist/components/builder/delete-key.js +4 -0
  89. package/dist/components/builder/delete-key.js.map +1 -1
  90. package/dist/components/builder/insert-context.d.ts +17 -25
  91. package/dist/components/builder/insert-context.js +45 -92
  92. package/dist/components/builder/insert-context.js.map +1 -1
  93. package/dist/components/builder/page-tree.d.ts +3 -3
  94. package/dist/components/builder/page-tree.js +3 -3
  95. package/dist/components/builder/page-tree.js.map +1 -1
  96. package/dist/components/builder/placement-refusal.d.ts +18 -0
  97. package/dist/components/builder/placement-refusal.js +46 -0
  98. package/dist/components/builder/placement-refusal.js.map +1 -0
  99. package/dist/components/builder/property-inspector.js +4 -0
  100. package/dist/components/builder/property-inspector.js.map +1 -1
  101. package/dist/components/builder/reuse-context.js +65 -26
  102. package/dist/components/builder/reuse-context.js.map +1 -1
  103. package/dist/components/builder/reuse.d.ts +79 -8
  104. package/dist/components/builder/reuse.js +71 -23
  105. package/dist/components/builder/reuse.js.map +1 -1
  106. package/dist/components/builder/run-inspector.d.ts +11 -0
  107. package/dist/components/builder/run-inspector.js +33 -0
  108. package/dist/components/builder/run-inspector.js.map +1 -0
  109. package/dist/components/cms/asset-field.d.ts +38 -1
  110. package/dist/components/cms/asset-field.js +182 -58
  111. package/dist/components/cms/asset-field.js.map +1 -1
  112. package/dist/components/cms/asset-picker-core.d.ts +16 -0
  113. package/dist/components/cms/asset-picker-core.js +20 -0
  114. package/dist/components/cms/asset-picker-core.js.map +1 -1
  115. package/dist/components/cms/asset-picker.js +12 -18
  116. package/dist/components/cms/asset-picker.js.map +1 -1
  117. package/dist/components/cms/bulk-actions.d.ts +4 -20
  118. package/dist/components/cms/bulk-actions.js +3 -24
  119. package/dist/components/cms/bulk-actions.js.map +1 -1
  120. package/dist/components/cms/cell-edit.d.ts +5 -5
  121. package/dist/components/cms/cell-edit.js.map +1 -1
  122. package/dist/components/cms/cell.js.map +1 -1
  123. package/dist/components/cms/cms-store.d.ts +87 -45
  124. package/dist/components/cms/cms-store.js +347 -203
  125. package/dist/components/cms/cms-store.js.map +1 -1
  126. package/dist/components/cms/collection-table.js +7 -2
  127. package/dist/components/cms/collection-table.js.map +1 -1
  128. package/dist/components/cms/commit-policy.d.ts +24 -0
  129. package/dist/components/cms/commit-policy.js +37 -0
  130. package/dist/components/cms/commit-policy.js.map +1 -0
  131. package/dist/components/cms/detail-editor.js +83 -137
  132. package/dist/components/cms/detail-editor.js.map +1 -1
  133. package/dist/components/cms/document-diff-view.d.ts +3 -1
  134. package/dist/components/cms/document-diff-view.js +12 -7
  135. package/dist/components/cms/document-diff-view.js.map +1 -1
  136. package/dist/components/cms/draft-commit.d.ts +70 -0
  137. package/dist/components/cms/draft-commit.js +82 -0
  138. package/dist/components/cms/draft-commit.js.map +1 -0
  139. package/dist/components/cms/duplicate-document.d.ts +11 -13
  140. package/dist/components/cms/duplicate-document.js +26 -34
  141. package/dist/components/cms/duplicate-document.js.map +1 -1
  142. package/dist/components/cms/field-anchors.d.ts +7 -4
  143. package/dist/components/cms/field-anchors.js +9 -7
  144. package/dist/components/cms/field-anchors.js.map +1 -1
  145. package/dist/components/cms/field-collaboration.d.ts +47 -0
  146. package/dist/components/cms/field-collaboration.js +87 -0
  147. package/dist/components/cms/field-collaboration.js.map +1 -0
  148. package/dist/components/cms/field-control-lib.d.ts +22 -1
  149. package/dist/components/cms/field-control-lib.js +33 -4
  150. package/dist/components/cms/field-control-lib.js.map +1 -1
  151. package/dist/components/cms/field-control.js +17 -14
  152. package/dist/components/cms/field-control.js.map +1 -1
  153. package/dist/components/cms/gate-validation.d.ts +21 -30
  154. package/dist/components/cms/gate-validation.js +11 -22
  155. package/dist/components/cms/gate-validation.js.map +1 -1
  156. package/dist/components/cms/object-array-field.js +4 -4
  157. package/dist/components/cms/object-array-field.js.map +1 -1
  158. package/dist/components/cms/prop-fields.d.ts +7 -2
  159. package/dist/components/cms/prop-fields.js +13 -6
  160. package/dist/components/cms/prop-fields.js.map +1 -1
  161. package/dist/components/cms/reference-confirm.js +10 -4
  162. package/dist/components/cms/reference-confirm.js.map +1 -1
  163. package/dist/components/cms/rich-text/embed-picker.d.ts +1 -1
  164. package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
  165. package/dist/components/cms/rich-text/insert.d.ts +20 -9
  166. package/dist/components/cms/rich-text/insert.js +25 -17
  167. package/dist/components/cms/rich-text/insert.js.map +1 -1
  168. package/dist/components/cms/use-document-draft.d.ts +96 -0
  169. package/dist/components/cms/use-document-draft.js +283 -0
  170. package/dist/components/cms/use-document-draft.js.map +1 -0
  171. package/dist/components/cms/use-document-versions.js +2 -2
  172. package/dist/components/cms/use-document-versions.js.map +1 -1
  173. package/dist/components/cms/use-live-url.d.ts +6 -12
  174. package/dist/components/cms/use-live-url.js +16 -15
  175. package/dist/components/cms/use-live-url.js.map +1 -1
  176. package/dist/components/cms/use-publish-controller.d.ts +13 -18
  177. package/dist/components/cms/use-publish-controller.js +24 -41
  178. package/dist/components/cms/use-publish-controller.js.map +1 -1
  179. package/dist/components/cms/use-published-perspective.d.ts +2 -3
  180. package/dist/components/cms/use-published-perspective.js +4 -3
  181. package/dist/components/cms/use-published-perspective.js.map +1 -1
  182. package/dist/components/cms/versions-pane.js +8 -1
  183. package/dist/components/cms/versions-pane.js.map +1 -1
  184. package/dist/components/collection-view/cms-collection-view-source.js +24 -7
  185. package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
  186. package/dist/components/components/transfer-dialog.js +28 -23
  187. package/dist/components/components/transfer-dialog.js.map +1 -1
  188. package/dist/components/components/transfer.d.ts +31 -21
  189. package/dist/components/components/transfer.js +49 -35
  190. package/dist/components/components/transfer.js.map +1 -1
  191. package/dist/components/project-list.js.map +1 -1
  192. package/dist/components/project-session.d.ts +6 -3
  193. package/dist/components/project-session.js +14 -8
  194. package/dist/components/project-session.js.map +1 -1
  195. package/dist/components/project-switcher.js.map +1 -1
  196. package/dist/components/rail-view-source.js +2 -1
  197. package/dist/components/rail-view-source.js.map +1 -1
  198. package/dist/components/section-nav.js +1 -1
  199. package/dist/components/section-nav.js.map +1 -1
  200. package/dist/lib/ai/apply-content.js +56 -116
  201. package/dist/lib/ai/apply-content.js.map +1 -1
  202. package/dist/lib/ai/chat-context.d.ts +8 -0
  203. package/dist/lib/ai/chat-context.js +37 -6
  204. package/dist/lib/ai/chat-context.js.map +1 -1
  205. package/dist/lib/ai/chat-state.d.ts +14 -1
  206. package/dist/lib/ai/chat-state.js.map +1 -1
  207. package/dist/lib/ai/constraints.d.ts +20 -5
  208. package/dist/lib/ai/constraints.js +125 -33
  209. package/dist/lib/ai/constraints.js.map +1 -1
  210. package/dist/lib/ai/fake-provider.d.ts +16 -3
  211. package/dist/lib/ai/fake-provider.js +22 -6
  212. package/dist/lib/ai/fake-provider.js.map +1 -1
  213. package/dist/lib/ai/index.d.ts +2 -0
  214. package/dist/lib/ai/index.js +2 -0
  215. package/dist/lib/ai/index.js.map +1 -1
  216. package/dist/lib/ai/leash.d.ts +34 -0
  217. package/dist/lib/ai/leash.js +28 -0
  218. package/dist/lib/ai/leash.js.map +1 -0
  219. package/dist/lib/ai/provenance.d.ts +1 -1
  220. package/dist/lib/ai/provenance.js.map +1 -1
  221. package/dist/lib/ai/rich-text-input.d.ts +8 -8
  222. package/dist/lib/ai/rich-text-input.js +2 -1
  223. package/dist/lib/ai/rich-text-input.js.map +1 -1
  224. package/dist/lib/ai/rich-text-translate.d.ts +27 -4
  225. package/dist/lib/ai/rich-text-translate.js +28 -10
  226. package/dist/lib/ai/rich-text-translate.js.map +1 -1
  227. package/dist/lib/ai/shape.d.ts +13 -13
  228. package/dist/lib/ai/shape.js +69 -93
  229. package/dist/lib/ai/shape.js.map +1 -1
  230. package/dist/lib/ai/tools.d.ts +36 -41
  231. package/dist/lib/ai/tools.js +85 -87
  232. package/dist/lib/ai/tools.js.map +1 -1
  233. package/dist/lib/ai/translate-document.d.ts +39 -5
  234. package/dist/lib/ai/translate-document.js +160 -51
  235. package/dist/lib/ai/translate-document.js.map +1 -1
  236. package/dist/lib/ai/types.d.ts +34 -23
  237. package/dist/lib/ai/types.js.map +1 -1
  238. package/dist/lib/ai/update-fields.d.ts +21 -12
  239. package/dist/lib/ai/update-fields.js +118 -177
  240. package/dist/lib/ai/update-fields.js.map +1 -1
  241. package/dist/lib/ai/writer-form.d.ts +116 -0
  242. package/dist/lib/ai/writer-form.js +429 -0
  243. package/dist/lib/ai/writer-form.js.map +1 -0
  244. package/dist/lib/ai-current-document.d.ts +11 -11
  245. package/dist/lib/ai-current-document.js +4 -4
  246. package/dist/lib/ai-current-document.js.map +1 -1
  247. package/dist/lib/assets/upload.d.ts +31 -2
  248. package/dist/lib/assets/upload.js +61 -6
  249. package/dist/lib/assets/upload.js.map +1 -1
  250. package/dist/lib/clipboard/os-clipboard.d.ts +10 -2
  251. package/dist/lib/clipboard/os-clipboard.js +8 -4
  252. package/dist/lib/clipboard/os-clipboard.js.map +1 -1
  253. package/dist/lib/clipboard/paste-target.d.ts +18 -9
  254. package/dist/lib/clipboard/paste-target.js +8 -7
  255. package/dist/lib/clipboard/paste-target.js.map +1 -1
  256. package/dist/lib/cms/field-patch.d.ts +15 -0
  257. package/dist/lib/cms/field-patch.js +25 -0
  258. package/dist/lib/cms/field-patch.js.map +1 -0
  259. package/dist/lib/cms/published-documents.d.ts +1 -1
  260. package/dist/lib/cms/published-documents.js +1 -1
  261. package/dist/lib/cms/published-documents.js.map +1 -1
  262. package/dist/lib/cms-views/view-model.d.ts +3 -3
  263. package/dist/lib/collection-view/__fixtures__/document-writes.d.ts +6 -0
  264. package/dist/lib/collection-view/__fixtures__/document-writes.js +23 -0
  265. package/dist/lib/collection-view/__fixtures__/document-writes.js.map +1 -0
  266. package/dist/lib/collection-view/collection-view-source.d.ts +69 -6
  267. package/dist/lib/collection-view/collection-view-source.js.map +1 -1
  268. package/dist/lib/data/collection-content.d.ts +4 -13
  269. package/dist/lib/data/collection-content.js +11 -5
  270. package/dist/lib/data/collection-content.js.map +1 -1
  271. package/dist/lib/dnd/drop-rules.d.ts +25 -31
  272. package/dist/lib/dnd/drop-rules.js +53 -59
  273. package/dist/lib/dnd/drop-rules.js.map +1 -1
  274. package/dist/lib/engine-version.d.ts +1 -1
  275. package/dist/lib/engine-version.js +1 -1
  276. package/dist/lib/engine-version.js.map +1 -1
  277. package/dist/lib/i18n/messages.de.js +72 -15
  278. package/dist/lib/i18n/messages.de.js.map +1 -1
  279. package/dist/lib/i18n/messages.en.d.ts +61 -12
  280. package/dist/lib/i18n/messages.en.js +76 -16
  281. package/dist/lib/i18n/messages.en.js.map +1 -1
  282. package/dist/lib/operations/__fixtures__/write-document.d.ts +30 -0
  283. package/dist/lib/operations/__fixtures__/write-document.js +16 -0
  284. package/dist/lib/operations/__fixtures__/write-document.js.map +1 -0
  285. package/dist/lib/operations/cms-persistence.d.ts +124 -31
  286. package/dist/lib/operations/cms-persistence.js +447 -68
  287. package/dist/lib/operations/cms-persistence.js.map +1 -1
  288. package/dist/lib/operations/config-manifest-lookup.d.ts +35 -0
  289. package/dist/lib/operations/config-manifest-lookup.js +56 -0
  290. package/dist/lib/operations/config-manifest-lookup.js.map +1 -0
  291. package/dist/lib/operations/project-runtime-config.d.ts +39 -0
  292. package/dist/lib/operations/project-runtime-config.js +54 -0
  293. package/dist/lib/operations/project-runtime-config.js.map +1 -0
  294. package/dist/lib/operations/publish-with-revision.d.ts +64 -0
  295. package/dist/lib/operations/publish-with-revision.js +66 -0
  296. package/dist/lib/operations/publish-with-revision.js.map +1 -0
  297. package/dist/lib/operations/runtime-validator.d.ts +32 -0
  298. package/dist/lib/operations/runtime-validator.js +18 -0
  299. package/dist/lib/operations/runtime-validator.js.map +1 -0
  300. package/dist/lib/operations/session-hydration.d.ts +31 -14
  301. package/dist/lib/operations/session-hydration.js +12 -16
  302. package/dist/lib/operations/session-hydration.js.map +1 -1
  303. package/dist/lib/operations/studio-operations.d.ts +59 -2
  304. package/dist/lib/operations/studio-operations.js +76 -14
  305. package/dist/lib/operations/studio-operations.js.map +1 -1
  306. package/dist/lib/persistence/convex-adapter.d.ts +13 -5
  307. package/dist/lib/persistence/convex-adapter.js +189 -74
  308. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  309. package/dist/lib/persistence/convex-client.d.ts +65 -5
  310. package/dist/lib/persistence/convex-client.js +106 -3
  311. package/dist/lib/persistence/convex-client.js.map +1 -1
  312. package/dist/lib/persistence/index.js +17 -18
  313. package/dist/lib/persistence/index.js.map +1 -1
  314. package/dist/lib/persona/labels.js +4 -1
  315. package/dist/lib/persona/labels.js.map +1 -1
  316. package/dist/lib/query/operations-query.d.ts +1 -1
  317. package/dist/lib/query/operations-query.js +1 -1
  318. package/dist/lib/query/operations-query.js.map +1 -1
  319. package/dist/lib/references/impact.d.ts +21 -15
  320. package/dist/lib/references/impact.js +13 -26
  321. package/dist/lib/references/impact.js.map +1 -1
  322. package/dist/lib/references/references-store.d.ts +9 -0
  323. package/dist/lib/references/references-store.js +24 -4
  324. package/dist/lib/references/references-store.js.map +1 -1
  325. package/dist/lib/routes/route-issues.d.ts +6 -6
  326. package/dist/lib/routes/route-issues.js +26 -80
  327. package/dist/lib/routes/route-issues.js.map +1 -1
  328. package/dist/lib/workspace/config-issues.js +2 -1
  329. package/dist/lib/workspace/config-issues.js.map +1 -1
  330. package/dist/lib/workspace/index.d.ts +31 -1
  331. package/dist/lib/workspace/index.js +64 -17
  332. package/dist/lib/workspace/index.js.map +1 -1
  333. package/dist/lib/workspace/switcher-source.d.ts +3 -3
  334. package/dist/lib/workspace/switcher-source.js.map +1 -1
  335. package/dist/lib/workspace/workspace-source.d.ts +2 -2
  336. package/dist/lib/workspace/workspace-source.js +2 -1
  337. package/dist/lib/workspace/workspace-source.js.map +1 -1
  338. package/package.json +3 -3
  339. package/dist/components/cms/cms-composition-validators.d.ts +0 -16
  340. package/dist/components/cms/cms-composition-validators.js +0 -133
  341. package/dist/components/cms/cms-composition-validators.js.map +0 -1
  342. package/dist/components/cms/publish-clean.d.ts +0 -28
  343. package/dist/components/cms/publish-clean.js +0 -38
  344. package/dist/components/cms/publish-clean.js.map +0 -1
  345. package/dist/components/cms/use-save-mode.d.ts +0 -35
  346. package/dist/components/cms/use-save-mode.js +0 -54
  347. package/dist/components/cms/use-save-mode.js.map +0 -1
  348. package/dist/lib/assets/asset-placements.d.ts +0 -28
  349. package/dist/lib/assets/asset-placements.js +0 -83
  350. package/dist/lib/assets/asset-placements.js.map +0 -1
  351. package/dist/lib/cms/document-gate.d.ts +0 -47
  352. package/dist/lib/cms/document-gate.js +0 -61
  353. package/dist/lib/cms/document-gate.js.map +0 -1
@@ -1,6 +1,14 @@
1
1
  import type { FieldDef, JsonSchema } from '@elytracms/core/cms-core';
2
+ import type { WriterLeash } from './types.js';
2
3
  /** The note appended when a field's constraint projection is knowingly partial. */
3
4
  export declare const INCOMPLETE_CONSTRAINTS_NOTE = "further validation rules apply that are not expressible here \u2014 keep the value conventional and plausible";
5
+ /**
6
+ * What the catalog says about a composition (`blocks`) — its writer form is NONE
7
+ * (`FIELD_TYPES.blocks.writer === 'composition'`, ratified 4c, EC-593): the assistant
8
+ * changes component props, never which components there are. Said in the prose because
9
+ * the omission must be VISIBLE, not a field the model simply never hears of.
10
+ */
11
+ export declare const COMPOSITION_NOTE = "page-builder composition: the assistant never adds, moves or removes its components (that is done on the canvas) \u2014 change one component prop with blockId + prop (getDocument lists the components and their props)";
4
12
  /**
5
13
  * The prose form of `format: 'date-time'` (EC-505). The JSON-Schema keyword rides
6
14
  * on the target shape for the provider's validator; the CATALOG has no JSON Schema
@@ -46,15 +54,22 @@ export declare function richTextWriterSchema(field: FieldDef): JsonSchema;
46
54
  * - relation → `{ collection, id }` (the envelope minus `type`): the lens rebuilds
47
55
  * a bare reference into its envelope;
48
56
  * - rich text → a markdown string (`richTextWriterSchema`) — the engine converts it
49
- * wherever it sits (`apply-content.ts`);
50
- * - an `object` → its own projection with every subfield in ITS writer form.
57
+ * wherever it sits (`writer-form.ts`, EC-593);
58
+ * - an `object` → its own projection with every subfield in ITS writer form;
59
+ * - a composition (`blocks`) → NONE (`undefined`): its structure is the canvas's
60
+ * (ratified 4c, EC-593) — a target shape omits it and says so.
51
61
  *
52
62
  * Writer objects are closed (`additionalProperties: false`): a writer inventing keys
53
63
  * is a fault, where a stored envelope carrying extra keys is drift the lens
54
- * tolerates (EC-556 §B) — which is why the projection itself stays open. The
55
- * top-level relation/asset id ENUM (EC-381) is the target shape's own override.
64
+ * tolerates (EC-556 §B) — which is why the projection itself stays open.
65
+ *
66
+ * With a `leash` (the create target shape, EC-381 → EC-593) every relation / asset id
67
+ * — at EVERY depth, not only a top-level field — is an ENUM over what the model
68
+ * searched for that position (a relation's target, an asset's `accept`), and a position
69
+ * with nothing searched is omitted (empty beats wrong). Without one (the catalog prose)
70
+ * the plain writer forms are described.
56
71
  */
57
- export declare function writerFieldSchema(field: FieldDef): JsonSchema;
72
+ export declare function writerFieldSchema(field: FieldDef, leash?: WriterLeash): JsonSchema | undefined;
58
73
  /**
59
74
  * Drop `pattern` wherever `format` is present (EC-473/EC-474 req 6). Returns the
60
75
  * same object when there is nothing to strip, so callers can rely on identity.
@@ -28,6 +28,25 @@ import { BOUND_UNIT_NOUN, fieldJsonSchema, fieldTypeOf } from '@elytracms/core/c
28
28
  import { MARKDOWN_PROSE_DESCRIPTION } from '@elytracms/core/rich-text';
29
29
  /** The note appended when a field's constraint projection is knowingly partial. */
30
30
  export const INCOMPLETE_CONSTRAINTS_NOTE = 'further validation rules apply that are not expressible here — keep the value conventional and plausible';
31
+ /**
32
+ * What the catalog says about a composition (`blocks`) — its writer form is NONE
33
+ * (`FIELD_TYPES.blocks.writer === 'composition'`, ratified 4c, EC-593): the assistant
34
+ * changes component props, never which components there are. Said in the prose because
35
+ * the omission must be VISIBLE, not a field the model simply never hears of.
36
+ */
37
+ export const COMPOSITION_NOTE = 'page-builder composition: the assistant never adds, moves or removes its components (that is done on the canvas) — change one component prop with blockId + prop (getDocument lists the components and their props)';
38
+ /**
39
+ * Whether a field's validation has rules its projection cannot state (RULES D13): a
40
+ * schema whose projection is knowingly partial (`schemaComplete: false`), or a FUNCTION
41
+ * `validate` — normalisation stamps `schemaComplete` on a schema-form validator only, so
42
+ * a declared `validate` without it is a function no consumer can read.
43
+ */
44
+ function hasUnstatedRules(field) {
45
+ const validation = field.validation;
46
+ if (!validation)
47
+ return false;
48
+ return validation.schemaComplete === false || (validation.validate !== undefined && validation.schemaComplete === undefined);
49
+ }
31
50
  /**
32
51
  * The prose form of `format: 'date-time'` (EC-505). The JSON-Schema keyword rides
33
52
  * on the target shape for the provider's validator; the CATALOG has no JSON Schema
@@ -63,13 +82,29 @@ export function plainTextBoundPhrase(field) {
63
82
  return `at most ${max} ${noun}`;
64
83
  return undefined;
65
84
  }
66
- /** {@link plainTextBoundPhrase} for a field and every sub-field below it, name-prefixed. */
67
- function plainTextBoundClauses(field, prefix = '') {
85
+ /** The declared sub-fields of an object or an asset placement (`[]` for any other type). */
86
+ function subFieldsOf(field) {
87
+ return field.type === 'object' ? field.fields : field.type === 'asset' ? (field.fields ?? []) : [];
88
+ }
89
+ /**
90
+ * The clauses only the DECLARATION can say, for a field and every sub-field below it,
91
+ * name-prefixed (`note: at most 400 plain-text characters`, `label: required`):
92
+ * - a plain-text bound ({@link plainTextBoundPhrase}, EC-581);
93
+ * - a sub-field's `required` (RULES F3d — the shape's JSON `required` carried it, the
94
+ * prose never did); the field's own is said by {@link describeFieldConstraints};
95
+ * - a sub-field's rules the projection cannot state ({@link hasUnstatedRules}).
96
+ */
97
+ function declarationClauses(field, prefix = '') {
68
98
  const own = plainTextBoundPhrase(field);
69
99
  const out = own !== undefined ? [`${prefix}${own}`] : [];
70
- const subs = field.type === 'object' ? field.fields : field.type === 'asset' ? (field.fields ?? []) : [];
71
- for (const sub of subs)
72
- out.push(...plainTextBoundClauses(sub, `${prefix}${sub.name}: `));
100
+ for (const sub of subFieldsOf(field)) {
101
+ const subPrefix = `${prefix}${sub.name}: `;
102
+ if (sub.validation?.required === true)
103
+ out.push(`${subPrefix}required`);
104
+ out.push(...declarationClauses(sub, subPrefix));
105
+ if (hasUnstatedRules(sub))
106
+ out.push(`${subPrefix}${INCOMPLETE_CONSTRAINTS_NOTE}`);
107
+ }
73
108
  return out;
74
109
  }
75
110
  /**
@@ -103,35 +138,69 @@ export function richTextWriterSchema(field) {
103
138
  * - relation → `{ collection, id }` (the envelope minus `type`): the lens rebuilds
104
139
  * a bare reference into its envelope;
105
140
  * - rich text → a markdown string (`richTextWriterSchema`) — the engine converts it
106
- * wherever it sits (`apply-content.ts`);
107
- * - an `object` → its own projection with every subfield in ITS writer form.
141
+ * wherever it sits (`writer-form.ts`, EC-593);
142
+ * - an `object` → its own projection with every subfield in ITS writer form;
143
+ * - a composition (`blocks`) → NONE (`undefined`): its structure is the canvas's
144
+ * (ratified 4c, EC-593) — a target shape omits it and says so.
108
145
  *
109
146
  * Writer objects are closed (`additionalProperties: false`): a writer inventing keys
110
147
  * is a fault, where a stored envelope carrying extra keys is drift the lens
111
- * tolerates (EC-556 §B) — which is why the projection itself stays open. The
112
- * top-level relation/asset id ENUM (EC-381) is the target shape's own override.
148
+ * tolerates (EC-556 §B) — which is why the projection itself stays open.
149
+ *
150
+ * With a `leash` (the create target shape, EC-381 → EC-593) every relation / asset id
151
+ * — at EVERY depth, not only a top-level field — is an ENUM over what the model
152
+ * searched for that position (a relation's target, an asset's `accept`), and a position
153
+ * with nothing searched is omitted (empty beats wrong). Without one (the catalog prose)
154
+ * the plain writer forms are described.
113
155
  */
114
- export function writerFieldSchema(field) {
115
- return writerForm(field, fieldJsonSchema(field));
156
+ export function writerFieldSchema(field, leash) {
157
+ return writerForm(field, fieldJsonSchema(field), leash);
158
+ }
159
+ /** An id enum over searched candidates, labelled for the model to choose against. */
160
+ function candidateEnum(candidates, asset) {
161
+ const label = candidates.map((c) => (c.label ? `${c.id} (${c.label})` : c.id)).join(', ');
162
+ return {
163
+ type: 'string',
164
+ enum: candidates.map((c) => c.id),
165
+ description: asset
166
+ ? `Choose an EXISTING library asset id, one of: ${label}. Leave empty if none fits.`
167
+ : `Choose one of: ${label}`,
168
+ };
116
169
  }
117
170
  /** EC-581: the writer form is DECLARED per type (`FIELD_TYPES[type].writer`); built here. */
118
- function writerForm(field, base) {
171
+ function writerForm(field, base, leash) {
119
172
  switch (fieldTypeOf(field).writer) {
120
173
  case 'markdown':
121
174
  return richTextWriterSchema(field);
175
+ case 'composition':
176
+ return undefined;
122
177
  case 'placement': {
123
178
  if (field.type !== 'asset')
124
179
  return base;
125
180
  // `type`/`id` belong to the envelope; the projection never lets a sub-field
126
181
  // of that name shadow them, and neither does the writer form.
127
182
  const subfields = (field.fields ?? []).filter((sub) => sub.name !== 'type' && sub.name !== 'id');
128
- return perItem(field.multiple, base, (item) => subfields.length === 0 ? bareAssetId(item) : writerObject(subfields, item));
183
+ const candidates = leash?.assetCandidates(field);
184
+ if (candidates !== undefined && candidates.length === 0)
185
+ return undefined;
186
+ const idSchema = (item) => candidates !== undefined ? candidateEnum(candidates, true) : bareAssetId(item);
187
+ return perItem(field.multiple, base, (item) => subfields.length === 0
188
+ ? idSchema(item)
189
+ : writerObject(subfields, { ...item, properties: { ...propertiesOf(item), id: idSchema(item) } }, leash));
190
+ }
191
+ case 'reference': {
192
+ if (field.type !== 'relation')
193
+ return base;
194
+ if (!leash)
195
+ return perItem(field.multiple, base, bareReference);
196
+ const candidates = leash.relationCandidates(field.target);
197
+ if (candidates.length === 0)
198
+ return undefined;
199
+ return perItem(field.multiple, base, () => candidateEnum(candidates, false));
129
200
  }
130
- case 'reference':
131
- return field.type === 'relation' ? perItem(field.multiple, base, bareReference) : base;
132
201
  case 'object':
133
202
  return field.type === 'object'
134
- ? perItem(field.multiple, base, (item) => writerObject(field.fields, item))
203
+ ? perItem(field.multiple, base, (item) => writerObject(field.fields, item, leash))
135
204
  : base;
136
205
  case 'value':
137
206
  return base;
@@ -159,15 +228,27 @@ function bareReference(envelope) {
159
228
  : [];
160
229
  return { ...envelope, properties, required, additionalProperties: false };
161
230
  }
162
- /** An object-shaped node with each declared subfield in its writer form, closed. */
163
- function writerObject(fields, node) {
231
+ /**
232
+ * An object-shaped node with each declared subfield in its writer form, closed. A
233
+ * sub-field with no writer form here (nothing searched for its id) is omitted, and
234
+ * leaves the `required` list with it.
235
+ */
236
+ function writerObject(fields, node, leash) {
164
237
  const properties = { ...propertiesOf(node) };
238
+ let required = Array.isArray(node.required) ? node.required : undefined;
165
239
  for (const sub of fields) {
166
240
  const subBase = properties[sub.name];
167
- if (subBase !== undefined)
168
- properties[sub.name] = writerForm(sub, subBase);
241
+ if (subBase === undefined)
242
+ continue;
243
+ const written = writerForm(sub, subBase, leash);
244
+ if (written !== undefined)
245
+ properties[sub.name] = written;
246
+ else {
247
+ delete properties[sub.name];
248
+ required = required?.filter((name) => name !== sub.name);
249
+ }
169
250
  }
170
- return { ...node, properties, additionalProperties: false };
251
+ return { ...node, properties, ...(required ? { required } : {}), additionalProperties: false };
171
252
  }
172
253
  /**
173
254
  * Drop `pattern` wherever `format` is present (EC-473/EC-474 req 6). Returns the
@@ -229,14 +310,19 @@ export function toModelFacingSchema(schema) {
229
310
  * Human-readable phrases for the constraint keywords of ONE schema node, in a stable
230
311
  * order — the form a model reads best. Node-level only: nested `items`/`properties`
231
312
  * are {@link describeSchemaDeep}'s business. Total over the projection's numeric,
232
- * string and array vocabulary; `enum`/`const` are deliberately not phrased — they
233
- * ride the target SHAPE as JSON Schema, and a `select`'s options or a relation's
234
- * `collection` const would otherwise repeat on every catalog line.
313
+ * string, array and option vocabulary. An `enum` IS phrased (RULES D14, EC-593): the
314
+ * catalog is prose only, so a `select`'s options never reached the update path that
315
+ * authors through it. A `const` is not (a relation's `collection` is the envelope's,
316
+ * not a rule the writer chooses against).
235
317
  */
236
318
  function describeSchemaConstraints(schema) {
237
319
  const out = [];
238
320
  const num = (value) => typeof value === 'number' ? value : undefined;
239
321
  const text = (value) => typeof value === 'string' ? value : undefined;
322
+ if (Array.isArray(schema.enum) && schema.enum.length > 0) {
323
+ // Quoted and `|`-separated: the clause itself is joined with commas.
324
+ out.push(`one of ${schema.enum.map((option) => JSON.stringify(option)).join(' | ')}`);
325
+ }
240
326
  const minLength = num(schema.minLength);
241
327
  const maxLength = num(schema.maxLength);
242
328
  if (minLength !== undefined && maxLength !== undefined) {
@@ -335,18 +421,24 @@ export function describeFieldConstraints(field) {
335
421
  const out = [];
336
422
  if (field.validation?.required === true)
337
423
  out.push('required');
338
- // EC-556: the prose tells a WRITER what to write, so it reads the writer form.
339
- out.push(...describeSchemaDeep(writerFieldSchema(field)));
340
- // EC-581: a bound whose unit has no schema keyword — rich text's plain-text
341
- // characters — is said in its unit, read off the per-FieldType definition, at every
342
- // depth the writer form reaches (`note: at most 400 plain-text characters …`).
343
- out.push(...plainTextBoundClauses(field));
424
+ // EC-556: the prose tells a WRITER what to write, so it reads the writer form —
425
+ // and a type with NO writer form says why (EC-593, ratified 4c).
426
+ const writer = writerFieldSchema(field);
427
+ if (writer === undefined)
428
+ out.push(COMPOSITION_NOTE);
429
+ else
430
+ out.push(...describeSchemaDeep(writer));
431
+ // EC-581 / EC-593: what only the declaration can say — a plain-text bound, a
432
+ // sub-field's `required`, a sub-field's unstated rules — at every depth the writer
433
+ // form reaches (`note: at most 400 plain-text characters …`, `label: required`).
434
+ out.push(...declarationClauses(field));
344
435
  if (field.validation?.unique === true)
345
436
  out.push('unique across the collection');
346
437
  // The one thing the projection cannot say about itself: that it is partial (an
347
- // opaque vendor, a `.refine()`, or — EC-522 — a rule at a position the field's
348
- // shape has no counterpart for). Set at config resolve, read here, nowhere else.
349
- if (field.validation?.schemaComplete === false)
438
+ // opaque vendor, a `.refine()`, — EC-522 — a rule at a position the field's shape
439
+ // has no counterpart for, or — RULES D13 — a validate FUNCTION, which no projection
440
+ // can read at all). Set at config resolve, read here, nowhere else.
441
+ if (hasUnstatedRules(field))
350
442
  out.push(INCOMPLETE_CONSTRAINTS_NOTE);
351
443
  return out;
352
444
  }
@@ -1 +1 @@
1
- {"version":3,"file":"constraints.js","sourceRoot":"","sources":["../../../src/lib/ai/constraints.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Model-facing constraint rendering (EC-473, tuned by EC-505).\n *\n * The engine has ONE constraint projection: `fieldJsonSchema` merges a field's\n * declarative rules (`validation.min`/`max`/`pattern`) with the fragment derived\n * from a schema-form `validation.validate` (EC-522: nested `items`/`properties`\n * bounds included, fitted to the field's type at config resolve). This module turns\n * that projection into what the MODEL should see — which is not the same artifact a\n * JSON-Schema consumer wants:\n *\n * - **a regex is a poor instruction for a model.** `z.string().email()` emits a\n * ~200-character pattern and `z.iso.date()` a leap-year monster (measured,\n * EC-474 §3); `format` + bounds says the same thing in four tokens. So on the AI\n * path a `pattern` is dropped wherever a `format` is present. The JSON Schema\n * OUTPUT artifact keeps it — different consumer, different needs.\n * - **a date is one instant string** (EC-505 → EC-586). The projection used to say\n * `type: ['string','number']` (an epoch was stored as-is) and this module narrowed\n * it for the model; since EC-586 every write canonicalises a date to ONE ISO-8601\n * UTC instant, so the projection itself says `type: 'string'` and there is nothing\n * left to narrow.\n * - **incompleteness must be stated.** When `validation.schemaComplete` is `false`\n * the projection is a partial view of the rule, and the model is told so rather\n * than being handed a half-truth as the whole rule. (An `ai.hint` is exactly the\n * right place to say what the dropped refinement wants — the note nudges toward\n * writing one.)\n */\nimport { BOUND_UNIT_NOUN, fieldJsonSchema, fieldTypeOf } from '@elytracms/core/cms-core'\nimport type { FieldDef, JsonSchema } from '@elytracms/core/cms-core'\nimport { MARKDOWN_PROSE_DESCRIPTION } from '@elytracms/core/rich-text'\n\n/** The note appended when a field's constraint projection is knowingly partial. */\nexport const INCOMPLETE_CONSTRAINTS_NOTE =\n 'further validation rules apply that are not expressible here — keep the value conventional and plausible'\n\n/**\n * The prose form of `format: 'date-time'` (EC-505). The JSON-Schema keyword rides\n * on the target shape for the provider's validator; the CATALOG has no JSON Schema\n * at all — it is prose only, and the chat update path authors date values through\n * it. So the format is said in words too, or the only channel that path has says\n * nothing about the one format the engine accepts.\n *\n * Kept to three words deliberately: measured at 6 tokens, against 20 for the same\n * note with a worked example — and it rides every date field of every turn.\n */\nexport const ISO_DATE_NOTE = 'ISO 8601 date'\n\nfunction isSchemaNode(value: unknown): value is JsonSchema {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * The phrase for a field's declared `min`/`max` in the UNIT its type declares\n * (`FIELD_TYPES[type].bounds`, EC-581) — or `undefined` when the field declares none\n * or the unit has a JSON-Schema keyword the projection already carries. Today that is\n * rich text: its bounds count PLAIN-TEXT characters of the stored value (ratified\n * 2a(iii)), which no keyword on the markdown string the model writes can express.\n */\nexport function plainTextBoundPhrase(field: FieldDef): string | undefined {\n if (fieldTypeOf(field).bounds(field) !== 'plain-text') return undefined\n const min = typeof field.validation?.min === 'number' ? field.validation.min : undefined\n const max = typeof field.validation?.max === 'number' ? field.validation.max : undefined\n const noun = BOUND_UNIT_NOUN['plain-text']\n if (min !== undefined && max !== undefined) return `${min}–${max} ${noun}`\n if (min !== undefined) return `at least ${min} ${noun}`\n if (max !== undefined) return `at most ${max} ${noun}`\n return undefined\n}\n\n/** {@link plainTextBoundPhrase} for a field and every sub-field below it, name-prefixed. */\nfunction plainTextBoundClauses(field: FieldDef, prefix = ''): string[] {\n const own = plainTextBoundPhrase(field)\n const out = own !== undefined ? [`${prefix}${own}`] : []\n const subs = field.type === 'object' ? field.fields : field.type === 'asset' ? (field.fields ?? []) : []\n for (const sub of subs) out.push(...plainTextBoundClauses(sub, `${prefix}${sub.name}: `))\n return out\n}\n\n/**\n * A rich-text field's WRITER form (EC-379, D2 of EC-556): a MARKDOWN string. The\n * description carries the allowed subset (from the engine converter — one source\n * of truth); the engine converts the markdown into the v2 storage value and\n * rejects out-of-subset constructs with explicit issues.\n *\n * EC-563/EC-581: the declared bounds are said in their unit — plain-text characters,\n * markup not counted — in the description, read off the per-FieldType definition.\n * They used to ride as `minLength`/`maxLength` on the markdown string: a different\n * unit (markdown characters) than anything enforced, and a provider validating the\n * structured output would have enforced the wrong one.\n */\nexport function richTextWriterSchema(field: FieldDef): JsonSchema {\n const schema: JsonSchema = { type: 'string' }\n const bound = plainTextBoundPhrase(field)\n schema.description = `Rich text, written as ${MARKDOWN_PROSE_DESCRIPTION} Write genuine, publishable prose.${bound ? ` Length: ${bound}.` : ''}`\n return schema\n}\n\n/**\n * The WRITER-facing form of one field, at EVERY depth (EC-556): what a model should\n * hand in. Derived from the validator-facing projection (`fieldJsonSchema` — the\n * value at rest, every merged constraint included) by one rule: **the writer omits\n * exactly what the write-time lens (`selfTypeFieldValue`) restores.**\n *\n * - asset, no per-use sub-fields → the bare id (the projection's `id` property):\n * the lens wraps a bare id string into `{ type: 'asset', id }`;\n * - asset WITH sub-fields → the envelope itself, `type` included: the lens wraps a\n * string, never an object missing its `type`, and the sub-fields need a place;\n * - relation → `{ collection, id }` (the envelope minus `type`): the lens rebuilds\n * a bare reference into its envelope;\n * - rich text → a markdown string (`richTextWriterSchema`) — the engine converts it\n * wherever it sits (`apply-content.ts`);\n * - an `object` → its own projection with every subfield in ITS writer form.\n *\n * Writer objects are closed (`additionalProperties: false`): a writer inventing keys\n * is a fault, where a stored envelope carrying extra keys is drift the lens\n * tolerates (EC-556 §B) — which is why the projection itself stays open. The\n * top-level relation/asset id ENUM (EC-381) is the target shape's own override.\n */\nexport function writerFieldSchema(field: FieldDef): JsonSchema {\n return writerForm(field, fieldJsonSchema(field))\n}\n\n/** EC-581: the writer form is DECLARED per type (`FIELD_TYPES[type].writer`); built here. */\nfunction writerForm(field: FieldDef, base: JsonSchema): JsonSchema {\n switch (fieldTypeOf(field).writer) {\n case 'markdown':\n return richTextWriterSchema(field)\n case 'placement': {\n if (field.type !== 'asset') return base\n // `type`/`id` belong to the envelope; the projection never lets a sub-field\n // of that name shadow them, and neither does the writer form.\n const subfields = (field.fields ?? []).filter((sub) => sub.name !== 'type' && sub.name !== 'id')\n return perItem(field.multiple, base, (item) =>\n subfields.length === 0 ? bareAssetId(item) : writerObject(subfields, item),\n )\n }\n case 'reference':\n return field.type === 'relation' ? perItem(field.multiple, base, bareReference) : base\n case 'object':\n return field.type === 'object'\n ? perItem(field.multiple, base, (item) => writerObject(field.fields, item))\n : base\n case 'value':\n return base\n }\n}\n\n/** Apply `one` to the item schema of a `multiple` field, or to the field's own schema. */\nfunction perItem(multiple: boolean, base: JsonSchema, one: (item: JsonSchema) => JsonSchema): JsonSchema {\n if (!multiple) return one(base)\n return isSchemaNode(base.items) ? { ...base, items: one(base.items) } : base\n}\n\nfunction propertiesOf(node: JsonSchema): Record<string, JsonSchema> {\n const properties = node.properties\n return isSchemaNode(properties) ? (properties as Record<string, JsonSchema>) : {}\n}\n\n/** The envelope's `id` property — the bare id string the lens wraps. */\nfunction bareAssetId(envelope: JsonSchema): JsonSchema {\n return propertiesOf(envelope).id ?? { type: 'string' }\n}\n\n/** The reference envelope minus the `type` discriminator the lens restores. */\nfunction bareReference(envelope: JsonSchema): JsonSchema {\n const { type: _discriminator, ...properties } = propertiesOf(envelope)\n const required = Array.isArray(envelope.required)\n ? envelope.required.filter((key) => key !== 'type')\n : []\n return { ...envelope, properties, required, additionalProperties: false }\n}\n\n/** An object-shaped node with each declared subfield in its writer form, closed. */\nfunction writerObject(fields: readonly FieldDef[], node: JsonSchema): JsonSchema {\n const properties: Record<string, JsonSchema> = { ...propertiesOf(node) }\n for (const sub of fields) {\n const subBase = properties[sub.name]\n if (subBase !== undefined) properties[sub.name] = writerForm(sub, subBase)\n }\n return { ...node, properties, additionalProperties: false }\n}\n\n/**\n * Drop `pattern` wherever `format` is present (EC-473/EC-474 req 6). Returns the\n * same object when there is nothing to strip, so callers can rely on identity.\n */\nexport function stripRedundantPattern(schema: JsonSchema): JsonSchema {\n if (schema.format === undefined || schema.pattern === undefined) return schema\n const { pattern: _pattern, ...rest } = schema\n return rest\n}\n\n/**\n * Apply a node transform through a schema tree (`items` + `properties`), preserving\n * object identity wherever nothing changed — `buildTargetShape` relies on that to\n * avoid copying a property it did not touch.\n */\nfunction mapSchemaDeep(schema: JsonSchema, transform: (node: JsonSchema) => JsonSchema): JsonSchema {\n let out = transform(schema)\n const items = out.items\n if (typeof items === 'object' && items !== null && !Array.isArray(items)) {\n const next = mapSchemaDeep(items as JsonSchema, transform)\n if (next !== items) out = { ...out, items: next }\n }\n const properties = out.properties\n if (typeof properties === 'object' && properties !== null && !Array.isArray(properties)) {\n let changed = false\n const nextProps: Record<string, JsonSchema> = {}\n for (const [key, value] of Object.entries(properties as Record<string, unknown>)) {\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n const next = mapSchemaDeep(value as JsonSchema, transform)\n nextProps[key] = next\n if (next !== value) changed = true\n } else {\n nextProps[key] = value as JsonSchema\n }\n }\n if (changed) out = { ...out, properties: nextProps }\n }\n return out\n}\n\n/** Recursively strip redundant patterns through `items` / `properties`. */\nexport function stripRedundantPatternsDeep(schema: JsonSchema): JsonSchema {\n return mapSchemaDeep(schema, stripRedundantPattern)\n}\n\n/**\n * The whole model-facing rewrite of a projected schema (EC-505): redundant patterns\n * stripped everywhere in the tree — an `object` field's SUBfield gets the same\n * treatment as a top-level one. Identity-preserving. (The date-union narrowing that\n * also ran here is gone: a date is one instant string at rest since EC-586.)\n */\nexport function toModelFacingSchema(schema: JsonSchema): JsonSchema {\n return mapSchemaDeep(schema, stripRedundantPattern)\n}\n\n/**\n * Human-readable phrases for the constraint keywords of ONE schema node, in a stable\n * order — the form a model reads best. Node-level only: nested `items`/`properties`\n * are {@link describeSchemaDeep}'s business. Total over the projection's numeric,\n * string and array vocabulary; `enum`/`const` are deliberately not phrased — they\n * ride the target SHAPE as JSON Schema, and a `select`'s options or a relation's\n * `collection` const would otherwise repeat on every catalog line.\n */\nfunction describeSchemaConstraints(schema: JsonSchema): string[] {\n const out: string[] = []\n const num = (value: unknown): number | undefined =>\n typeof value === 'number' ? value : undefined\n const text = (value: unknown): string | undefined =>\n typeof value === 'string' ? value : undefined\n\n const minLength = num(schema.minLength)\n const maxLength = num(schema.maxLength)\n if (minLength !== undefined && maxLength !== undefined) {\n out.push(`${minLength}–${maxLength} characters`)\n } else if (minLength !== undefined) out.push(`at least ${minLength} characters`)\n else if (maxLength !== undefined) out.push(`at most ${maxLength} characters`)\n\n const minimum = num(schema.minimum)\n const maximum = num(schema.maximum)\n if (minimum !== undefined && maximum !== undefined) out.push(`between ${minimum} and ${maximum}`)\n else if (minimum !== undefined) out.push(`at least ${minimum}`)\n else if (maximum !== undefined) out.push(`at most ${maximum}`)\n\n // EC-522: the projection carried these already; the prose never said them.\n const exclusiveMinimum = num(schema.exclusiveMinimum)\n const exclusiveMaximum = num(schema.exclusiveMaximum)\n if (exclusiveMinimum !== undefined) out.push(`more than ${exclusiveMinimum}`)\n if (exclusiveMaximum !== undefined) out.push(`less than ${exclusiveMaximum}`)\n const multipleOf = num(schema.multipleOf)\n if (multipleOf !== undefined) out.push(`a multiple of ${multipleOf}`)\n\n const minItems = num(schema.minItems)\n const maxItems = num(schema.maxItems)\n if (minItems !== undefined) out.push(`at least ${minItems} items`)\n if (maxItems !== undefined) out.push(`at most ${maxItems} items`)\n if (schema.uniqueItems === true) out.push('no duplicate items')\n\n // EC-473: date bounds finally reach a consumer. `formatMinimum`/`formatMaximum`\n // is the projection's machine form; this is the form a model acts on.\n const from = text(schema.formatMinimum)\n const to = text(schema.formatMaximum)\n if (from !== undefined) out.push(`not earlier than ${from}`)\n if (to !== undefined) out.push(`not later than ${to}`)\n\n const format = text(schema.format)\n // EC-505: `date-time` is said in words rather than skipped — the catalog carries\n // no JSON Schema, so this is the only place the update path learns the format.\n if (format === 'date-time') out.push(ISO_DATE_NOTE)\n else if (format !== undefined) out.push(`format: ${format}`)\n // Only a pattern that is NOT explained by a format is worth the tokens.\n const pattern = text(schema.pattern)\n if (pattern !== undefined && format === undefined) out.push(`matching /${pattern}/`)\n\n return out\n}\n\n/**\n * The phrases of one projected schema node AND everything below it (EC-522): a\n * `multiple` field's per-item bounds become an `each item: …` clause, an object's\n * subfield bounds a `<name>: …` clause, recursively. The projection is the ONLY\n * input — since EC-522 the merge carries nested `items`/`properties` constraints\n * itself, so this module never looks past `fieldJsonSchema` (EC-505's direct read\n * of the derived fragment's `items` is gone, and with it the second place that\n * understood fragment structure).\n *\n * Which side a schema addresses is still the executor's call, not this module's:\n * `validateDocument` hands the validator the field's WHOLE value — a `multiple`\n * field's array — so the per-item form is `z.array(z.string().min(3))`, and a\n * bare `z.string().min(3)` there is a config error at load (EC-522), never a\n * silently mis-applied bound.\n */\nfunction describeSchemaDeep(schema: JsonSchema): string[] {\n const out = describeSchemaConstraints(schema)\n if (isSchemaNode(schema.items)) {\n const itemParts = describeSchemaDeep(schema.items)\n if (itemParts.length > 0) out.push(`each item: ${itemParts.join(', ')}`)\n }\n if (isSchemaNode(schema.properties)) {\n for (const [name, sub] of Object.entries(schema.properties)) {\n if (!isSchemaNode(sub)) continue\n const subParts = describeSchemaDeep(sub)\n if (subParts.length > 0) out.push(`${name}: ${subParts.join(', ')}`)\n }\n }\n return out\n}\n\n/**\n * Human-readable constraint phrases for one field, in a stable order — the form a\n * model reads best (and the form the collections catalog carries, where there is no\n * JSON Schema to hand the model at all). Empty when the field constrains nothing.\n */\nexport function describeFieldConstraints(field: FieldDef): string[] {\n const out: string[] = []\n if (field.validation?.required === true) out.push('required')\n // EC-556: the prose tells a WRITER what to write, so it reads the writer form.\n out.push(...describeSchemaDeep(writerFieldSchema(field)))\n // EC-581: a bound whose unit has no schema keyword — rich text's plain-text\n // characters — is said in its unit, read off the per-FieldType definition, at every\n // depth the writer form reaches (`note: at most 400 plain-text characters …`).\n out.push(...plainTextBoundClauses(field))\n if (field.validation?.unique === true) out.push('unique across the collection')\n // The one thing the projection cannot say about itself: that it is partial (an\n // opaque vendor, a `.refine()`, or — EC-522 — a rule at a position the field's\n // shape has no counterpart for). Set at config resolve, read here, nowhere else.\n if (field.validation?.schemaComplete === false) out.push(INCOMPLETE_CONSTRAINTS_NOTE)\n return out\n}\n\n/** The constraint phrases as one clause (`min 2 characters; required`), or `undefined`. */\nexport function constraintSentence(field: FieldDef): string | undefined {\n const parts = describeFieldConstraints(field)\n return parts.length > 0 ? parts.join('; ') : undefined\n}\n"]}
1
+ {"version":3,"file":"constraints.js","sourceRoot":"","sources":["../../../src/lib/ai/constraints.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Model-facing constraint rendering (EC-473, tuned by EC-505).\n *\n * The engine has ONE constraint projection: `fieldJsonSchema` merges a field's\n * declarative rules (`validation.min`/`max`/`pattern`) with the fragment derived\n * from a schema-form `validation.validate` (EC-522: nested `items`/`properties`\n * bounds included, fitted to the field's type at config resolve). This module turns\n * that projection into what the MODEL should see — which is not the same artifact a\n * JSON-Schema consumer wants:\n *\n * - **a regex is a poor instruction for a model.** `z.string().email()` emits a\n * ~200-character pattern and `z.iso.date()` a leap-year monster (measured,\n * EC-474 §3); `format` + bounds says the same thing in four tokens. So on the AI\n * path a `pattern` is dropped wherever a `format` is present. The JSON Schema\n * OUTPUT artifact keeps it — different consumer, different needs.\n * - **a date is one instant string** (EC-505 → EC-586). The projection used to say\n * `type: ['string','number']` (an epoch was stored as-is) and this module narrowed\n * it for the model; since EC-586 every write canonicalises a date to ONE ISO-8601\n * UTC instant, so the projection itself says `type: 'string'` and there is nothing\n * left to narrow.\n * - **incompleteness must be stated.** When `validation.schemaComplete` is `false`\n * the projection is a partial view of the rule, and the model is told so rather\n * than being handed a half-truth as the whole rule. (An `ai.hint` is exactly the\n * right place to say what the dropped refinement wants — the note nudges toward\n * writing one.)\n */\nimport { BOUND_UNIT_NOUN, fieldJsonSchema, fieldTypeOf } from '@elytracms/core/cms-core'\nimport type { FieldDef, JsonSchema } from '@elytracms/core/cms-core'\nimport { MARKDOWN_PROSE_DESCRIPTION } from '@elytracms/core/rich-text'\nimport type { AssetCandidate, RelationCandidate, WriterLeash } from './types'\n\n/** The note appended when a field's constraint projection is knowingly partial. */\nexport const INCOMPLETE_CONSTRAINTS_NOTE =\n 'further validation rules apply that are not expressible here — keep the value conventional and plausible'\n\n/**\n * What the catalog says about a composition (`blocks`) — its writer form is NONE\n * (`FIELD_TYPES.blocks.writer === 'composition'`, ratified 4c, EC-593): the assistant\n * changes component props, never which components there are. Said in the prose because\n * the omission must be VISIBLE, not a field the model simply never hears of.\n */\nexport const COMPOSITION_NOTE =\n 'page-builder composition: the assistant never adds, moves or removes its components (that is done on the canvas) — change one component prop with blockId + prop (getDocument lists the components and their props)'\n\n/**\n * Whether a field's validation has rules its projection cannot state (RULES D13): a\n * schema whose projection is knowingly partial (`schemaComplete: false`), or a FUNCTION\n * `validate` — normalisation stamps `schemaComplete` on a schema-form validator only, so\n * a declared `validate` without it is a function no consumer can read.\n */\nfunction hasUnstatedRules(field: FieldDef): boolean {\n const validation = field.validation\n if (!validation) return false\n return validation.schemaComplete === false || (validation.validate !== undefined && validation.schemaComplete === undefined)\n}\n\n/**\n * The prose form of `format: 'date-time'` (EC-505). The JSON-Schema keyword rides\n * on the target shape for the provider's validator; the CATALOG has no JSON Schema\n * at all — it is prose only, and the chat update path authors date values through\n * it. So the format is said in words too, or the only channel that path has says\n * nothing about the one format the engine accepts.\n *\n * Kept to three words deliberately: measured at 6 tokens, against 20 for the same\n * note with a worked example — and it rides every date field of every turn.\n */\nexport const ISO_DATE_NOTE = 'ISO 8601 date'\n\nfunction isSchemaNode(value: unknown): value is JsonSchema {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * The phrase for a field's declared `min`/`max` in the UNIT its type declares\n * (`FIELD_TYPES[type].bounds`, EC-581) — or `undefined` when the field declares none\n * or the unit has a JSON-Schema keyword the projection already carries. Today that is\n * rich text: its bounds count PLAIN-TEXT characters of the stored value (ratified\n * 2a(iii)), which no keyword on the markdown string the model writes can express.\n */\nexport function plainTextBoundPhrase(field: FieldDef): string | undefined {\n if (fieldTypeOf(field).bounds(field) !== 'plain-text') return undefined\n const min = typeof field.validation?.min === 'number' ? field.validation.min : undefined\n const max = typeof field.validation?.max === 'number' ? field.validation.max : undefined\n const noun = BOUND_UNIT_NOUN['plain-text']\n if (min !== undefined && max !== undefined) return `${min}–${max} ${noun}`\n if (min !== undefined) return `at least ${min} ${noun}`\n if (max !== undefined) return `at most ${max} ${noun}`\n return undefined\n}\n\n/** The declared sub-fields of an object or an asset placement (`[]` for any other type). */\nfunction subFieldsOf(field: FieldDef): readonly FieldDef[] {\n return field.type === 'object' ? field.fields : field.type === 'asset' ? (field.fields ?? []) : []\n}\n\n/**\n * The clauses only the DECLARATION can say, for a field and every sub-field below it,\n * name-prefixed (`note: at most 400 plain-text characters`, `label: required`):\n * - a plain-text bound ({@link plainTextBoundPhrase}, EC-581);\n * - a sub-field's `required` (RULES F3d — the shape's JSON `required` carried it, the\n * prose never did); the field's own is said by {@link describeFieldConstraints};\n * - a sub-field's rules the projection cannot state ({@link hasUnstatedRules}).\n */\nfunction declarationClauses(field: FieldDef, prefix = ''): string[] {\n const own = plainTextBoundPhrase(field)\n const out = own !== undefined ? [`${prefix}${own}`] : []\n for (const sub of subFieldsOf(field)) {\n const subPrefix = `${prefix}${sub.name}: `\n if (sub.validation?.required === true) out.push(`${subPrefix}required`)\n out.push(...declarationClauses(sub, subPrefix))\n if (hasUnstatedRules(sub)) out.push(`${subPrefix}${INCOMPLETE_CONSTRAINTS_NOTE}`)\n }\n return out\n}\n\n/**\n * A rich-text field's WRITER form (EC-379, D2 of EC-556): a MARKDOWN string. The\n * description carries the allowed subset (from the engine converter — one source\n * of truth); the engine converts the markdown into the v2 storage value and\n * rejects out-of-subset constructs with explicit issues.\n *\n * EC-563/EC-581: the declared bounds are said in their unit — plain-text characters,\n * markup not counted — in the description, read off the per-FieldType definition.\n * They used to ride as `minLength`/`maxLength` on the markdown string: a different\n * unit (markdown characters) than anything enforced, and a provider validating the\n * structured output would have enforced the wrong one.\n */\nexport function richTextWriterSchema(field: FieldDef): JsonSchema {\n const schema: JsonSchema = { type: 'string' }\n const bound = plainTextBoundPhrase(field)\n schema.description = `Rich text, written as ${MARKDOWN_PROSE_DESCRIPTION} Write genuine, publishable prose.${bound ? ` Length: ${bound}.` : ''}`\n return schema\n}\n\n/**\n * The WRITER-facing form of one field, at EVERY depth (EC-556): what a model should\n * hand in. Derived from the validator-facing projection (`fieldJsonSchema` — the\n * value at rest, every merged constraint included) by one rule: **the writer omits\n * exactly what the write-time lens (`selfTypeFieldValue`) restores.**\n *\n * - asset, no per-use sub-fields → the bare id (the projection's `id` property):\n * the lens wraps a bare id string into `{ type: 'asset', id }`;\n * - asset WITH sub-fields → the envelope itself, `type` included: the lens wraps a\n * string, never an object missing its `type`, and the sub-fields need a place;\n * - relation → `{ collection, id }` (the envelope minus `type`): the lens rebuilds\n * a bare reference into its envelope;\n * - rich text → a markdown string (`richTextWriterSchema`) — the engine converts it\n * wherever it sits (`writer-form.ts`, EC-593);\n * - an `object` → its own projection with every subfield in ITS writer form;\n * - a composition (`blocks`) → NONE (`undefined`): its structure is the canvas's\n * (ratified 4c, EC-593) — a target shape omits it and says so.\n *\n * Writer objects are closed (`additionalProperties: false`): a writer inventing keys\n * is a fault, where a stored envelope carrying extra keys is drift the lens\n * tolerates (EC-556 §B) — which is why the projection itself stays open.\n *\n * With a `leash` (the create target shape, EC-381 → EC-593) every relation / asset id\n * — at EVERY depth, not only a top-level field — is an ENUM over what the model\n * searched for that position (a relation's target, an asset's `accept`), and a position\n * with nothing searched is omitted (empty beats wrong). Without one (the catalog prose)\n * the plain writer forms are described.\n */\nexport function writerFieldSchema(field: FieldDef, leash?: WriterLeash): JsonSchema | undefined {\n return writerForm(field, fieldJsonSchema(field), leash)\n}\n\n/** An id enum over searched candidates, labelled for the model to choose against. */\nfunction candidateEnum(candidates: readonly (RelationCandidate | AssetCandidate)[], asset: boolean): JsonSchema {\n const label = candidates.map((c) => (c.label ? `${c.id} (${c.label})` : c.id)).join(', ')\n return {\n type: 'string',\n enum: candidates.map((c) => c.id),\n description: asset\n ? `Choose an EXISTING library asset id, one of: ${label}. Leave empty if none fits.`\n : `Choose one of: ${label}`,\n }\n}\n\n/** EC-581: the writer form is DECLARED per type (`FIELD_TYPES[type].writer`); built here. */\nfunction writerForm(field: FieldDef, base: JsonSchema, leash?: WriterLeash): JsonSchema | undefined {\n switch (fieldTypeOf(field).writer) {\n case 'markdown':\n return richTextWriterSchema(field)\n case 'composition':\n return undefined\n case 'placement': {\n if (field.type !== 'asset') return base\n // `type`/`id` belong to the envelope; the projection never lets a sub-field\n // of that name shadow them, and neither does the writer form.\n const subfields = (field.fields ?? []).filter((sub) => sub.name !== 'type' && sub.name !== 'id')\n const candidates = leash?.assetCandidates(field)\n if (candidates !== undefined && candidates.length === 0) return undefined\n const idSchema = (item: JsonSchema): JsonSchema =>\n candidates !== undefined ? candidateEnum(candidates, true) : bareAssetId(item)\n return perItem(field.multiple, base, (item) =>\n subfields.length === 0\n ? idSchema(item)\n : writerObject(subfields, { ...item, properties: { ...propertiesOf(item), id: idSchema(item) } }, leash),\n )\n }\n case 'reference': {\n if (field.type !== 'relation') return base\n if (!leash) return perItem(field.multiple, base, bareReference)\n const candidates = leash.relationCandidates(field.target)\n if (candidates.length === 0) return undefined\n return perItem(field.multiple, base, () => candidateEnum(candidates, false))\n }\n case 'object':\n return field.type === 'object'\n ? perItem(field.multiple, base, (item) => writerObject(field.fields, item, leash))\n : base\n case 'value':\n return base\n }\n}\n\n/** Apply `one` to the item schema of a `multiple` field, or to the field's own schema. */\nfunction perItem(multiple: boolean, base: JsonSchema, one: (item: JsonSchema) => JsonSchema): JsonSchema {\n if (!multiple) return one(base)\n return isSchemaNode(base.items) ? { ...base, items: one(base.items) } : base\n}\n\nfunction propertiesOf(node: JsonSchema): Record<string, JsonSchema> {\n const properties = node.properties\n return isSchemaNode(properties) ? (properties as Record<string, JsonSchema>) : {}\n}\n\n/** The envelope's `id` property — the bare id string the lens wraps. */\nfunction bareAssetId(envelope: JsonSchema): JsonSchema {\n return propertiesOf(envelope).id ?? { type: 'string' }\n}\n\n/** The reference envelope minus the `type` discriminator the lens restores. */\nfunction bareReference(envelope: JsonSchema): JsonSchema {\n const { type: _discriminator, ...properties } = propertiesOf(envelope)\n const required = Array.isArray(envelope.required)\n ? envelope.required.filter((key) => key !== 'type')\n : []\n return { ...envelope, properties, required, additionalProperties: false }\n}\n\n/**\n * An object-shaped node with each declared subfield in its writer form, closed. A\n * sub-field with no writer form here (nothing searched for its id) is omitted, and\n * leaves the `required` list with it.\n */\nfunction writerObject(fields: readonly FieldDef[], node: JsonSchema, leash?: WriterLeash): JsonSchema {\n const properties: Record<string, JsonSchema> = { ...propertiesOf(node) }\n let required = Array.isArray(node.required) ? (node.required as string[]) : undefined\n for (const sub of fields) {\n const subBase = properties[sub.name]\n if (subBase === undefined) continue\n const written = writerForm(sub, subBase, leash)\n if (written !== undefined) properties[sub.name] = written\n else {\n delete properties[sub.name]\n required = required?.filter((name) => name !== sub.name)\n }\n }\n return { ...node, properties, ...(required ? { required } : {}), additionalProperties: false }\n}\n\n/**\n * Drop `pattern` wherever `format` is present (EC-473/EC-474 req 6). Returns the\n * same object when there is nothing to strip, so callers can rely on identity.\n */\nexport function stripRedundantPattern(schema: JsonSchema): JsonSchema {\n if (schema.format === undefined || schema.pattern === undefined) return schema\n const { pattern: _pattern, ...rest } = schema\n return rest\n}\n\n/**\n * Apply a node transform through a schema tree (`items` + `properties`), preserving\n * object identity wherever nothing changed — `buildTargetShape` relies on that to\n * avoid copying a property it did not touch.\n */\nfunction mapSchemaDeep(schema: JsonSchema, transform: (node: JsonSchema) => JsonSchema): JsonSchema {\n let out = transform(schema)\n const items = out.items\n if (typeof items === 'object' && items !== null && !Array.isArray(items)) {\n const next = mapSchemaDeep(items as JsonSchema, transform)\n if (next !== items) out = { ...out, items: next }\n }\n const properties = out.properties\n if (typeof properties === 'object' && properties !== null && !Array.isArray(properties)) {\n let changed = false\n const nextProps: Record<string, JsonSchema> = {}\n for (const [key, value] of Object.entries(properties as Record<string, unknown>)) {\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n const next = mapSchemaDeep(value as JsonSchema, transform)\n nextProps[key] = next\n if (next !== value) changed = true\n } else {\n nextProps[key] = value as JsonSchema\n }\n }\n if (changed) out = { ...out, properties: nextProps }\n }\n return out\n}\n\n/** Recursively strip redundant patterns through `items` / `properties`. */\nexport function stripRedundantPatternsDeep(schema: JsonSchema): JsonSchema {\n return mapSchemaDeep(schema, stripRedundantPattern)\n}\n\n/**\n * The whole model-facing rewrite of a projected schema (EC-505): redundant patterns\n * stripped everywhere in the tree — an `object` field's SUBfield gets the same\n * treatment as a top-level one. Identity-preserving. (The date-union narrowing that\n * also ran here is gone: a date is one instant string at rest since EC-586.)\n */\nexport function toModelFacingSchema(schema: JsonSchema): JsonSchema {\n return mapSchemaDeep(schema, stripRedundantPattern)\n}\n\n/**\n * Human-readable phrases for the constraint keywords of ONE schema node, in a stable\n * order — the form a model reads best. Node-level only: nested `items`/`properties`\n * are {@link describeSchemaDeep}'s business. Total over the projection's numeric,\n * string, array and option vocabulary. An `enum` IS phrased (RULES D14, EC-593): the\n * catalog is prose only, so a `select`'s options never reached the update path that\n * authors through it. A `const` is not (a relation's `collection` is the envelope's,\n * not a rule the writer chooses against).\n */\nfunction describeSchemaConstraints(schema: JsonSchema): string[] {\n const out: string[] = []\n const num = (value: unknown): number | undefined =>\n typeof value === 'number' ? value : undefined\n const text = (value: unknown): string | undefined =>\n typeof value === 'string' ? value : undefined\n\n if (Array.isArray(schema.enum) && schema.enum.length > 0) {\n // Quoted and `|`-separated: the clause itself is joined with commas.\n out.push(`one of ${schema.enum.map((option) => JSON.stringify(option)).join(' | ')}`)\n }\n\n const minLength = num(schema.minLength)\n const maxLength = num(schema.maxLength)\n if (minLength !== undefined && maxLength !== undefined) {\n out.push(`${minLength}–${maxLength} characters`)\n } else if (minLength !== undefined) out.push(`at least ${minLength} characters`)\n else if (maxLength !== undefined) out.push(`at most ${maxLength} characters`)\n\n const minimum = num(schema.minimum)\n const maximum = num(schema.maximum)\n if (minimum !== undefined && maximum !== undefined) out.push(`between ${minimum} and ${maximum}`)\n else if (minimum !== undefined) out.push(`at least ${minimum}`)\n else if (maximum !== undefined) out.push(`at most ${maximum}`)\n\n // EC-522: the projection carried these already; the prose never said them.\n const exclusiveMinimum = num(schema.exclusiveMinimum)\n const exclusiveMaximum = num(schema.exclusiveMaximum)\n if (exclusiveMinimum !== undefined) out.push(`more than ${exclusiveMinimum}`)\n if (exclusiveMaximum !== undefined) out.push(`less than ${exclusiveMaximum}`)\n const multipleOf = num(schema.multipleOf)\n if (multipleOf !== undefined) out.push(`a multiple of ${multipleOf}`)\n\n const minItems = num(schema.minItems)\n const maxItems = num(schema.maxItems)\n if (minItems !== undefined) out.push(`at least ${minItems} items`)\n if (maxItems !== undefined) out.push(`at most ${maxItems} items`)\n if (schema.uniqueItems === true) out.push('no duplicate items')\n\n // EC-473: date bounds finally reach a consumer. `formatMinimum`/`formatMaximum`\n // is the projection's machine form; this is the form a model acts on.\n const from = text(schema.formatMinimum)\n const to = text(schema.formatMaximum)\n if (from !== undefined) out.push(`not earlier than ${from}`)\n if (to !== undefined) out.push(`not later than ${to}`)\n\n const format = text(schema.format)\n // EC-505: `date-time` is said in words rather than skipped — the catalog carries\n // no JSON Schema, so this is the only place the update path learns the format.\n if (format === 'date-time') out.push(ISO_DATE_NOTE)\n else if (format !== undefined) out.push(`format: ${format}`)\n // Only a pattern that is NOT explained by a format is worth the tokens.\n const pattern = text(schema.pattern)\n if (pattern !== undefined && format === undefined) out.push(`matching /${pattern}/`)\n\n return out\n}\n\n/**\n * The phrases of one projected schema node AND everything below it (EC-522): a\n * `multiple` field's per-item bounds become an `each item: …` clause, an object's\n * subfield bounds a `<name>: …` clause, recursively. The projection is the ONLY\n * input — since EC-522 the merge carries nested `items`/`properties` constraints\n * itself, so this module never looks past `fieldJsonSchema` (EC-505's direct read\n * of the derived fragment's `items` is gone, and with it the second place that\n * understood fragment structure).\n *\n * Which side a schema addresses is still the executor's call, not this module's:\n * `validateDocument` hands the validator the field's WHOLE value — a `multiple`\n * field's array — so the per-item form is `z.array(z.string().min(3))`, and a\n * bare `z.string().min(3)` there is a config error at load (EC-522), never a\n * silently mis-applied bound.\n */\nfunction describeSchemaDeep(schema: JsonSchema): string[] {\n const out = describeSchemaConstraints(schema)\n if (isSchemaNode(schema.items)) {\n const itemParts = describeSchemaDeep(schema.items)\n if (itemParts.length > 0) out.push(`each item: ${itemParts.join(', ')}`)\n }\n if (isSchemaNode(schema.properties)) {\n for (const [name, sub] of Object.entries(schema.properties)) {\n if (!isSchemaNode(sub)) continue\n const subParts = describeSchemaDeep(sub)\n if (subParts.length > 0) out.push(`${name}: ${subParts.join(', ')}`)\n }\n }\n return out\n}\n\n/**\n * Human-readable constraint phrases for one field, in a stable order — the form a\n * model reads best (and the form the collections catalog carries, where there is no\n * JSON Schema to hand the model at all). Empty when the field constrains nothing.\n */\nexport function describeFieldConstraints(field: FieldDef): string[] {\n const out: string[] = []\n if (field.validation?.required === true) out.push('required')\n // EC-556: the prose tells a WRITER what to write, so it reads the writer form —\n // and a type with NO writer form says why (EC-593, ratified 4c).\n const writer = writerFieldSchema(field)\n if (writer === undefined) out.push(COMPOSITION_NOTE)\n else out.push(...describeSchemaDeep(writer))\n // EC-581 / EC-593: what only the declaration can say — a plain-text bound, a\n // sub-field's `required`, a sub-field's unstated rules — at every depth the writer\n // form reaches (`note: at most 400 plain-text characters …`, `label: required`).\n out.push(...declarationClauses(field))\n if (field.validation?.unique === true) out.push('unique across the collection')\n // The one thing the projection cannot say about itself: that it is partial (an\n // opaque vendor, a `.refine()`, — EC-522 — a rule at a position the field's shape\n // has no counterpart for, or — RULES D13 — a validate FUNCTION, which no projection\n // can read at all). Set at config resolve, read here, nowhere else.\n if (hasUnstatedRules(field)) out.push(INCOMPLETE_CONSTRAINTS_NOTE)\n return out\n}\n\n/** The constraint phrases as one clause (`min 2 characters; required`), or `undefined`. */\nexport function constraintSentence(field: FieldDef): string | undefined {\n const parts = describeFieldConstraints(field)\n return parts.length > 0 ? parts.join('; ') : undefined\n}\n"]}
@@ -112,6 +112,17 @@ export declare function fakeUpdateValueFrom(userText: string): string;
112
112
  * else falls back to the configured default field.
113
113
  */
114
114
  export declare function fakeUpdateFieldFrom(userText: string, fallback: string): string;
115
+ /**
116
+ * The scripted update flow's full TARGET (EC-593): a field, or one component prop
117
+ * inside it — `set the <field> component <id> <prop> to: …` names a component by its
118
+ * node id at any depth (`Set the body component p-home-quote-note value to: Hi`), so the
119
+ * keyless playground can drive a component-prop edit through the one converter.
120
+ */
121
+ export declare function fakeUpdateTargetFrom(userText: string, fallback: string): {
122
+ field: string;
123
+ blockId?: string;
124
+ prop?: string;
125
+ };
115
126
  /**
116
127
  * A scripted, STATELESS fake chat model that drives the FULL create flow — and,
117
128
  * for a user turn matching the update pattern, the EC-378 UPDATE flow — over the
@@ -135,11 +146,13 @@ export declare function fakeUpdateFieldFrom(userText: string, fallback: string):
135
146
  * 1. no tool result yet → `getDocument({})` — target omitted on purpose:
136
147
  * the executor resolves the document OPEN in the
137
148
  * editor (the EC-378 current-document seam)
138
- * 2. document read, no update → `updateFields({ changes: [{ field:
139
- * fakeUpdateFieldFrom(<text>, updateField),
149
+ * 2. document read, no update → `updateFields({ changes: [{
150
+ * ...fakeUpdateTargetFrom(<text>, updateField),
140
151
  * value: fakeUpdateValueFrom(<last user text>) }] })`
141
152
  * — "Set the body to: ## Neu" drives a markdown
142
- * rich-text update deterministically (EC-379)
153
+ * rich-text update deterministically (EC-379);
154
+ * "Set the body component <id> <prop> to: X" one
155
+ * component prop at any depth (EC-593)
143
156
  * 3. update result present → the update answer, word by word.
144
157
  */
145
158
  export declare function createFakeCreateFlowChatModel(options?: FakeCreateFlowChatModelOptions): FakeChatModel;
@@ -180,8 +180,22 @@ export function fakeUpdateValueFrom(userText) {
180
180
  * else falls back to the configured default field.
181
181
  */
182
182
  export function fakeUpdateFieldFrom(userText, fallback) {
183
- const match = /\b(?:set|change|update)\s+the\s+([A-Za-z]\w*)\s+to\s*:/i.exec(userText);
184
- return match?.[1] ?? fallback;
183
+ return fakeUpdateTargetFrom(userText, fallback).field;
184
+ }
185
+ /**
186
+ * The scripted update flow's full TARGET (EC-593): a field, or one component prop
187
+ * inside it — `set the <field> component <id> <prop> to: …` names a component by its
188
+ * node id at any depth (`Set the body component p-home-quote-note value to: Hi`), so the
189
+ * keyless playground can drive a component-prop edit through the one converter.
190
+ */
191
+ export function fakeUpdateTargetFrom(userText, fallback) {
192
+ const match = /\b(?:set|change|update)\s+the\s+([A-Za-z]\w*)(?:\s+component\s+([\w-]+)\s+([A-Za-z]\w*))?\s+to\s*:/i.exec(userText);
193
+ if (!match)
194
+ return { field: fallback };
195
+ return {
196
+ field: match[1],
197
+ ...(match[2] !== undefined && match[3] !== undefined ? { blockId: match[2], prop: match[3] } : {}),
198
+ };
185
199
  }
186
200
  /**
187
201
  * A scripted, STATELESS fake chat model that drives the FULL create flow — and,
@@ -206,11 +220,13 @@ export function fakeUpdateFieldFrom(userText, fallback) {
206
220
  * 1. no tool result yet → `getDocument({})` — target omitted on purpose:
207
221
  * the executor resolves the document OPEN in the
208
222
  * editor (the EC-378 current-document seam)
209
- * 2. document read, no update → `updateFields({ changes: [{ field:
210
- * fakeUpdateFieldFrom(<text>, updateField),
223
+ * 2. document read, no update → `updateFields({ changes: [{
224
+ * ...fakeUpdateTargetFrom(<text>, updateField),
211
225
  * value: fakeUpdateValueFrom(<last user text>) }] })`
212
226
  * — "Set the body to: ## Neu" drives a markdown
213
- * rich-text update deterministically (EC-379)
227
+ * rich-text update deterministically (EC-379);
228
+ * "Set the body component <id> <prop> to: X" one
229
+ * component prop at any depth (EC-593)
214
230
  * 3. update result present → the update answer, word by word.
215
231
  */
216
232
  export function createFakeCreateFlowChatModel(options = {}) {
@@ -233,7 +249,7 @@ export function createFakeCreateFlowChatModel(options = {}) {
233
249
  return toolTurnParts('Proposing the change. ', 'updateFields', {
234
250
  changes: [
235
251
  {
236
- field: fakeUpdateFieldFrom(userText, updateField),
252
+ ...fakeUpdateTargetFrom(userText, updateField),
237
253
  value: fakeUpdateValueFrom(userText),
238
254
  },
239
255
  ],