@salsita-ai/sdk 0.50.0-develop.da8d27f5d7 → 0.50.0-develop.e5ed4a0b66

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 (298) hide show
  1. package/README.md +19 -11
  2. package/dist/application-spec/errors/diagnostic-codes.d.ts +6 -0
  3. package/dist/application-spec/errors/diagnostic-codes.js +1 -1
  4. package/dist/application-spec/errors/diagnostic-messages.d.ts +6 -0
  5. package/dist/application-spec/errors/diagnostic-messages.js +1 -1
  6. package/dist/application-spec/generate-application/code/domain-runtime-files.js +1 -1
  7. package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
  8. package/dist/application-spec/generate-application/code/templates/application-types.js +6 -6
  9. package/dist/application-spec/generate-application/code/templates/configurator-domain-code.d.ts +1 -0
  10. package/dist/application-spec/generate-application/code/templates/configurator-domain-code.js +11 -8
  11. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-domain-block.d.ts +3 -1
  12. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-domain-block.js +70 -66
  13. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-phx-globals.js +3 -0
  14. package/dist/application-spec/generate-application/code/templates/domain-spec.js +5 -5
  15. package/dist/application-spec/generate-application/code/templates/domain.js +2 -2
  16. package/dist/application-spec/generate-application/code/templates/global-definition/generate-action-types.d.ts +11 -0
  17. package/dist/application-spec/generate-application/code/templates/global-definition/generate-action-types.js +8 -0
  18. package/dist/application-spec/generate-application/code/templates/global-definition/index.d.ts +1 -0
  19. package/dist/application-spec/generate-application/code/templates/global-definition/index.js +1 -1
  20. package/dist/application-spec/generate-application/code/templates/globals.js +1 -0
  21. package/dist/application-spec/generate-application/code/templates/index.d.ts +7 -6
  22. package/dist/application-spec/generate-application/code/templates/index.js +1 -1
  23. package/dist/application-spec/generate-application/code/templates/skills/agents-md.js +2 -0
  24. package/dist/application-spec/generate-application/code/templates/skills/phoenix-cli-skill.js +1 -0
  25. package/dist/application-spec/generate-application/code/templates/{phoenix-create-configurator-skill.d.ts → skills/phoenix-create-configurator-skill.d.ts} +1 -1
  26. package/dist/application-spec/generate-application/code/templates/{phoenix-create-configurator-skill.js → skills/phoenix-create-configurator-skill.js} +43 -13
  27. package/dist/application-spec/generate-application/code/templates/skills/phoenix-feedback-skill.js +54 -0
  28. package/dist/application-spec/generate-application/code/templates/{phoenix-review-skill.js → skills/phoenix-review-skill.js} +7 -5
  29. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill.js +1 -0
  30. package/dist/application-spec/generate-application/code/templates/skills/phoenix-variants-skill.d.ts +1 -0
  31. package/dist/application-spec/generate-application/code/templates/skills/phoenix-variants-skill.js +6 -0
  32. package/dist/application-spec/generate-application/code/templates/{common → skills}/sdk-content.d.ts +52 -3
  33. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +82 -0
  34. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.d.ts +2 -0
  35. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.js +1 -1
  36. package/dist/application-spec/generate-application/runtime/sections/actions-runtime.d.ts +14 -0
  37. package/dist/application-spec/generate-application/runtime/sections/actions-runtime.js +1 -0
  38. package/dist/application-spec/generate-application/runtime/sections/index.d.ts +1 -0
  39. package/dist/application-spec/generate-application/runtime/sections/index.js +1 -1
  40. package/dist/application-spec/parse-application-files/categorize/categorize-files.d.ts +3 -1
  41. package/dist/application-spec/parse-application-files/categorize/categorize-files.js +1 -1
  42. package/dist/application-spec/parse-application-files/merge/index.d.ts +1 -0
  43. package/dist/application-spec/parse-application-files/merge/index.js +1 -1
  44. package/dist/application-spec/parse-application-files/merge/merged-actions.d.ts +11 -0
  45. package/dist/application-spec/parse-application-files/merge/merged-actions.js +1 -0
  46. package/dist/application-spec/parse-application-files/merge/merged-spec.d.ts +2 -0
  47. package/dist/application-spec/parse-application-files/merge/merged-spec.js +1 -1
  48. package/dist/application-spec/parse-application-files/raw-spec/index.d.ts +1 -0
  49. package/dist/application-spec/parse-application-files/raw-spec/index.js +1 -1
  50. package/dist/application-spec/parse-application-files/raw-spec/raw-actions.d.ts +18 -0
  51. package/dist/application-spec/parse-application-files/raw-spec/raw-actions.js +1 -0
  52. package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.d.ts +14 -2
  53. package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.js +1 -1
  54. package/dist/application-spec/parse-application-files/raw-spec/raw-spec-file.d.ts +2 -0
  55. package/dist/application-spec/parse-application-files/raw-spec/raw-spec-file.js +1 -1
  56. package/dist/application-spec/parse-application-files/resolve/resolve-import-modules.js +1 -1
  57. package/dist/application-spec/parse-application-files/spec/application/application-spec.js +1 -1
  58. package/dist/application-spec/parse-application-files/spec/application/configurator-spec/configurator-spec.d.ts +4 -2
  59. package/dist/application-spec/parse-application-files/spec/application/configurator-spec/configurator-spec.js +1 -1
  60. package/dist/application-spec/parse-application-files/spec/common/index.d.ts +1 -0
  61. package/dist/application-spec/parse-application-files/spec/common/index.js +1 -1
  62. package/dist/application-spec/parse-application-files/spec/common/schema-ref-resolver.d.ts +23 -0
  63. package/dist/application-spec/parse-application-files/spec/common/schema-ref-resolver.js +1 -0
  64. package/dist/application-spec/parse-application-files/spec/model/names/action-name.d.ts +10 -0
  65. package/dist/application-spec/parse-application-files/spec/model/names/action-name.js +1 -0
  66. package/dist/application-spec/parse-application-files/spec/model/names/index.d.ts +1 -0
  67. package/dist/application-spec/parse-application-files/spec/model/names/index.js +1 -1
  68. package/dist/application-spec/parse-application-files/spec/sections/actions.d.ts +35 -0
  69. package/dist/application-spec/parse-application-files/spec/sections/actions.js +1 -0
  70. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-field-type.d.ts +3 -14
  71. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-field-type.js +1 -1
  72. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.d.ts +12 -3
  73. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.js +1 -1
  74. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant.d.ts +5 -3
  75. package/dist/application-spec/parse-application-files/spec/sections/index.d.ts +1 -0
  76. package/dist/application-spec/parse-application-files/spec/sections/index.js +1 -1
  77. package/dist/client/phoenix-api.d.ts +5 -1
  78. package/dist/configurator/application-runtime/extensions/configurator-extension-systems.d.ts +2 -1
  79. package/dist/configurator/application-runtime/extensions/configurator-extension-systems.js +1 -1
  80. package/dist/configurator/application-runtime/extensions/selectors.d.ts +2 -1
  81. package/dist/configurator/application-runtime/extensions/selectors.js +1 -1
  82. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +168 -0
  83. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -0
  84. package/dist/configurator/{assistant → application-runtime/extensions/systems/assistant-system}/browser-details.d.ts +1 -1
  85. package/dist/configurator/application-runtime/extensions/systems/assistant-system/browser-details.js +1 -0
  86. package/dist/configurator/application-runtime/extensions/systems/assistant-system/change-recorder.d.ts +85 -0
  87. package/dist/configurator/application-runtime/extensions/systems/assistant-system/change-recorder.js +1 -0
  88. package/dist/configurator/{assistant → application-runtime/extensions/systems/assistant-system}/graphql-tools.d.ts +20 -16
  89. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.js +1 -0
  90. package/dist/configurator/application-runtime/extensions/systems/graphql-system/entity-meta.d.ts +4 -0
  91. package/dist/configurator/application-runtime/extensions/systems/graphql-system/entity-meta.js +1 -1
  92. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +16 -1
  93. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
  94. package/dist/configurator/application-runtime/extensions/systems/graphql-system/side-effects.d.ts +9 -0
  95. package/dist/configurator/application-runtime/extensions/systems/graphql-system/side-effects.js +1 -0
  96. package/dist/configurator/application-runtime/extensions/systems/index.d.ts +1 -0
  97. package/dist/configurator/application-runtime/extensions/systems/index.js +1 -1
  98. package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.d.ts +2 -0
  99. package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.js +1 -1
  100. package/dist/configurator/common/frontend-analytics.d.ts +21 -21
  101. package/dist/configurator/index.d.ts +0 -1
  102. package/dist/configurator/index.js +1 -1
  103. package/dist/configurator/pages/LoadedConfiguratorDetailPage.js +1 -1
  104. package/dist/configurator/scene/scene-analytics/scene-analytics.d.ts +2 -2
  105. package/dist/configurator/ui/assistant/AssistantChips.d.ts +11 -0
  106. package/dist/configurator/ui/assistant/AssistantChips.js +1 -0
  107. package/dist/configurator/ui/assistant/AssistantComposer.d.ts +6 -0
  108. package/dist/configurator/ui/assistant/AssistantComposer.js +1 -0
  109. package/dist/configurator/ui/assistant/AssistantMarkdown.d.ts +7 -0
  110. package/dist/configurator/ui/assistant/AssistantMarkdown.js +1 -0
  111. package/dist/configurator/ui/assistant/AssistantPanel.d.ts +11 -0
  112. package/dist/configurator/ui/assistant/AssistantPanel.js +1 -0
  113. package/dist/configurator/ui/assistant/AssistantThread.d.ts +6 -0
  114. package/dist/configurator/ui/assistant/AssistantThread.js +1 -0
  115. package/dist/configurator/ui/assistant/assistant-failure.d.ts +18 -0
  116. package/dist/configurator/ui/assistant/assistant-failure.js +1 -0
  117. package/dist/configurator/ui/assistant/index.d.ts +2 -0
  118. package/dist/configurator/ui/assistant/index.js +1 -0
  119. package/dist/configurator/ui/assistant/streamed-markdown.d.ts +6 -0
  120. package/dist/configurator/ui/assistant/streamed-markdown.js +2 -0
  121. package/dist/configurator/ui/assistant/theme.d.ts +2 -0
  122. package/dist/configurator/ui/assistant/theme.js +1 -0
  123. package/dist/configurator/ui/assistant/thread-items.d.ts +37 -0
  124. package/dist/configurator/ui/assistant/thread-items.js +1 -0
  125. package/dist/configurator/ui/assistant/tool-chip.d.ts +37 -0
  126. package/dist/configurator/ui/assistant/tool-chip.js +1 -0
  127. package/dist/configurator/ui/assistant/use-follow-stream.d.ts +17 -0
  128. package/dist/configurator/ui/assistant/use-follow-stream.js +1 -0
  129. package/dist/configurator/ui/assistant/use-is-assistant-panel-open.d.ts +5 -0
  130. package/dist/configurator/ui/assistant/use-is-assistant-panel-open.js +1 -0
  131. package/dist/configurator/ui/configurator-layout/universal/DesktopLayout.js +5 -5
  132. package/dist/configurator/ui/configurator-layout/universal/MobileLayout.d.ts +2 -0
  133. package/dist/configurator/ui/configurator-layout/universal/MobileLayout.js +4 -4
  134. package/dist/configurator/ui/legacy/mobile-controls/MobileControls.d.ts +1 -1
  135. package/dist/configurator/ui/phoenix-layout-elements/AssistantToolbarButton.d.ts +10 -0
  136. package/dist/configurator/ui/phoenix-layout-elements/AssistantToolbarButton.js +1 -0
  137. package/dist/configurator/ui/phoenix-layout-elements/index.d.ts +1 -0
  138. package/dist/configurator/ui/phoenix-layout-elements/index.js +1 -1
  139. package/dist/docs/index.json +12 -2
  140. package/dist/docs/layouts/toolbar.mdx +6 -0
  141. package/dist/docs/other-features/domain-actions.mdx +418 -0
  142. package/dist/docs/other-features/side-effects.mdx +26 -0
  143. package/dist/docs/phoenix/admin.mdx +2 -2
  144. package/dist/docs/phoenix/best-practices.mdx +1 -0
  145. package/dist/docs/phoenix/finalization.mdx +1 -1
  146. package/dist/docs/phoenix/integration/headless-mode-external.mdx +5 -5
  147. package/dist/docs/phoenix/integration/widget-mode-external.mdx +5 -5
  148. package/dist/docs/phoenix/migrations.mdx +45 -5
  149. package/dist/docs/phoenix/phoenix-cli.mdx +139 -20
  150. package/dist/docs/phoenix/project-structure.mdx +7 -3
  151. package/dist/docs/phoenix/sdk-builder-workflow.mdx +12 -8
  152. package/dist/domain/actions/action-factory.d.ts +10 -0
  153. package/dist/domain/actions/action-factory.js +1 -0
  154. package/dist/domain/actions/prepare-action-implementations.d.ts +15 -0
  155. package/dist/domain/actions/prepare-action-implementations.js +5 -0
  156. package/dist/domain/application/application-config.d.ts +3 -1
  157. package/dist/domain/application/assistant-mutations.d.ts +15 -14
  158. package/dist/domain/application/assistant-mutations.js +1 -1
  159. package/dist/domain/components/updates.d.ts +16 -0
  160. package/dist/domain/core/action.d.ts +67 -0
  161. package/dist/domain/core/action.js +1 -0
  162. package/dist/domain/core/domain-factory.d.ts +4 -0
  163. package/dist/domain/core/index.d.ts +1 -0
  164. package/dist/domain/domain-builder.d.ts +7 -0
  165. package/dist/domain/domain-builder.js +1 -1
  166. package/dist/domain/domain-context/action-system/action-system.d.ts +40 -0
  167. package/dist/domain/domain-context/action-system/action-system.js +1 -0
  168. package/dist/domain/domain-context/domain-context-systems.d.ts +5 -2
  169. package/dist/domain/domain-context/index.d.ts +2 -1
  170. package/dist/domain/domain-context/index.js +1 -1
  171. package/dist/domain/domain-context/store-system/store-system.js +1 -1
  172. package/dist/domain/domain-runtime/domain-context/domain-context.js +1 -1
  173. package/dist/domain/domain-runtime/domain-context/selectors.d.ts +4 -0
  174. package/dist/domain/domain-runtime/domain-context/selectors.js +1 -1
  175. package/dist/domain/domain-runtime/domain-runtime.d.ts +12 -0
  176. package/dist/domain/domain-runtime/spec/initialize.js +1 -1
  177. package/dist/domain/domain-runtime/spec/types.d.ts +15 -1
  178. package/dist/domain/index.d.ts +2 -0
  179. package/dist/domain/index.js +1 -1
  180. package/dist/domain/mocks/create-mock-domain-runtime.d.ts +6 -0
  181. package/dist/domain/system-components/phoenix-layout-elements/assistant-toolbar-button.d.ts +44 -0
  182. package/dist/domain/system-components/phoenix-layout-elements/assistant-toolbar-button.js +1 -0
  183. package/dist/domain/system-components/phoenix-layout-elements/index.d.ts +1 -0
  184. package/dist/domain/system-components/phoenix-layout-elements/index.js +1 -1
  185. package/dist/domain/system-components/phoenix-layout-elements/phoenix-layout-element.d.ts +2 -1
  186. package/dist/domain/system-components/phoenix-layout-elements/phoenix-layout-element.js +1 -1
  187. package/dist/editor/Editor.js +1 -1
  188. package/dist/editor/domain-editors/DomainSpecContext.js +1 -1
  189. package/dist/editor/domain-editors/apply-spec-update.js +1 -1
  190. package/dist/editor/domain-editors/use-implementation-files.d.ts +7 -3
  191. package/dist/editor/domain-editors/use-implementation-files.js +1 -1
  192. package/dist/editor/graphql/GraphQLPanel.js +1 -1
  193. package/dist/editor/variants/VariantAnswerFooter.d.ts +17 -0
  194. package/dist/editor/variants/VariantAnswerFooter.js +1 -0
  195. package/dist/editor/variants/VariantPreviewHandler.d.ts +6 -0
  196. package/dist/editor/variants/VariantPreviewHandler.js +1 -0
  197. package/dist/editor/variants/variant-preview-styles.d.ts +49 -0
  198. package/dist/editor/variants/variant-preview-styles.js +1 -0
  199. package/dist/i18n/locales/cs/messages.js +1 -1
  200. package/dist/i18n/locales/da/messages.js +1 -1
  201. package/dist/i18n/locales/de/messages.js +1 -1
  202. package/dist/i18n/locales/de-AT/messages.js +1 -1
  203. package/dist/i18n/locales/en/messages.js +1 -1
  204. package/dist/i18n/locales/en-AU/messages.js +1 -1
  205. package/dist/i18n/locales/en-GB/messages.js +1 -1
  206. package/dist/i18n/locales/es/messages.js +1 -1
  207. package/dist/i18n/locales/fr/messages.js +1 -1
  208. package/dist/i18n/locales/hu/messages.js +1 -1
  209. package/dist/i18n/locales/it/messages.js +1 -1
  210. package/dist/i18n/locales/nl/messages.js +1 -1
  211. package/dist/i18n/locales/pl/messages.js +1 -1
  212. package/dist/i18n/locales/pt/messages.js +1 -1
  213. package/dist/i18n/locales/ro/messages.js +1 -1
  214. package/dist/i18n/locales/sk/messages.js +1 -1
  215. package/dist/i18n/locales/tr/messages.js +1 -1
  216. package/dist/legacy/domain-spec/__generated/domain-schema.d.ts +6 -0
  217. package/dist/phoenix/application-spec/application-spec-builder.js +1 -1
  218. package/dist/phoenix/application-spec/generate-assistant-prompt.d.ts +4 -3
  219. package/dist/phoenix/application-spec/generate-assistant-prompt.js +1 -1
  220. package/dist/phoenix/cli.js +1 -1
  221. package/dist/phoenix/commands/build.d.ts +5 -0
  222. package/dist/phoenix/commands/build.js +1 -1
  223. package/dist/phoenix/commands/deploy.js +2 -2
  224. package/dist/phoenix/commands/dev.js +5 -5
  225. package/dist/phoenix/commands/feedback.d.ts +2 -1
  226. package/dist/phoenix/commands/feedback.js +1 -1
  227. package/dist/phoenix/commands/index.d.ts +1 -0
  228. package/dist/phoenix/commands/index.js +1 -1
  229. package/dist/phoenix/commands/login.d.ts +24 -6
  230. package/dist/phoenix/commands/login.js +2 -2
  231. package/dist/phoenix/commands/story-dev.js +2 -2
  232. package/dist/phoenix/commands/variants.d.ts +35 -0
  233. package/dist/phoenix/commands/variants.js +1 -0
  234. package/dist/phoenix/editor-server/commands-router.d.ts +68 -1
  235. package/dist/phoenix/editor-server/commands-router.js +1 -1
  236. package/dist/phoenix/editor-server/editor-router.d.ts +65 -0
  237. package/dist/phoenix/editor-server/editor-server.d.ts +3 -3
  238. package/dist/phoenix/editor-server/editor-server.js +1 -1
  239. package/dist/phoenix/editor-server/protocol.d.ts +1 -0
  240. package/dist/phoenix/editor-server/spec-router.js +1 -1
  241. package/dist/phoenix/editor-server/trpc.d.ts +3 -0
  242. package/dist/phoenix/services/application-spec-project-watcher.d.ts +13 -2
  243. package/dist/phoenix/services/application-spec-project-watcher.js +3 -3
  244. package/dist/phoenix/services/index.d.ts +5 -0
  245. package/dist/phoenix/services/index.js +1 -1
  246. package/dist/phoenix/services/phoenix-application.d.ts +21 -0
  247. package/dist/phoenix/services/phoenix-application.js +1 -0
  248. package/dist/phoenix/services/phoenix-store.d.ts +48 -0
  249. package/dist/phoenix/services/phoenix-store.js +1 -0
  250. package/dist/phoenix/services/phx-banner-plugin.d.ts +32 -0
  251. package/dist/phoenix/services/phx-banner-plugin.js +1 -0
  252. package/dist/phoenix/services/phx-licenses-plugin.d.ts +55 -0
  253. package/dist/phoenix/services/phx-licenses-plugin.js +8 -0
  254. package/dist/phoenix/services/phx-variant-previews-plugin.d.ts +9 -0
  255. package/dist/phoenix/services/phx-variant-previews-plugin.js +1 -0
  256. package/dist/phoenix/services/variant-file-session.d.ts +36 -0
  257. package/dist/phoenix/services/variant-file-session.js +1 -0
  258. package/dist/phoenix/services/variant-preview-service.d.ts +80 -0
  259. package/dist/phoenix/services/variant-preview-service.js +1 -0
  260. package/dist/phoenix/services/vite-config.d.ts +2 -0
  261. package/dist/phoenix/services/vite-config.js +1 -1
  262. package/dist/phoenix/utils/index.d.ts +1 -0
  263. package/dist/phoenix/utils/index.js +1 -1
  264. package/dist/phoenix/utils/read-git-commit.d.ts +14 -0
  265. package/dist/phoenix/utils/read-git-commit.js +1 -0
  266. package/dist/react-sdk/application-runtime/common-services/analytics/analytics-service.d.ts +2 -2
  267. package/dist/react-sdk/application-runtime/common-services/analytics/domain-controls-analytics.d.ts +21 -21
  268. package/dist/react-sdk/application-runtime/hooks/data/use-published-data.d.ts +1 -1
  269. package/dist/react-sdk/application-runtime/runtime/create-application-runtime.d.ts +12 -0
  270. package/dist/react-sdk/domain-context/common-hooks/use-domain-context.d.ts +3 -0
  271. package/dist/ui-sdk/common/use-viewport-size.d.ts +4 -1
  272. package/dist/ui-sdk/common/use-viewport-size.js +1 -1
  273. package/dist/ui-sdk/components/mobile-controls/index.d.ts +1 -1
  274. package/dist/ui-sdk/theme/types.d.ts +4 -0
  275. package/package.json +5 -8
  276. package/schema/domain-spec.json +24 -0
  277. package/dist/application-spec/generate-application/code/templates/agents-md.js +0 -2
  278. package/dist/application-spec/generate-application/code/templates/common/sdk-content.js +0 -67
  279. package/dist/application-spec/generate-application/code/templates/phoenix-cli-skill.js +0 -1
  280. package/dist/application-spec/generate-application/code/templates/phoenix-feedback-skill.js +0 -31
  281. package/dist/application-spec/generate-application/code/templates/phoenix-skill.js +0 -1
  282. package/dist/configurator/assistant/browser-details.js +0 -1
  283. package/dist/configurator/assistant/graphql-tools.js +0 -1
  284. package/dist/configurator/assistant/index.d.ts +0 -1
  285. package/dist/configurator/assistant/index.js +0 -1
  286. package/dist/configurator/assistant/use-assistant-chat.d.ts +0 -20
  287. package/dist/configurator/assistant/use-assistant-chat.js +0 -1
  288. package/dist/extras/assistant/AssistantChat.d.ts +0 -6
  289. package/dist/extras/assistant/AssistantChat.js +0 -1
  290. package/dist/extras/assistant/index.d.ts +0 -1
  291. package/dist/extras/assistant/index.js +0 -1
  292. /package/dist/application-spec/generate-application/code/templates/{common → skills}/agent-skill.d.ts +0 -0
  293. /package/dist/application-spec/generate-application/code/templates/{common → skills}/agent-skill.js +0 -0
  294. /package/dist/application-spec/generate-application/code/templates/{agents-md.d.ts → skills/agents-md.d.ts} +0 -0
  295. /package/dist/application-spec/generate-application/code/templates/{phoenix-cli-skill.d.ts → skills/phoenix-cli-skill.d.ts} +0 -0
  296. /package/dist/application-spec/generate-application/code/templates/{phoenix-feedback-skill.d.ts → skills/phoenix-feedback-skill.d.ts} +0 -0
  297. /package/dist/application-spec/generate-application/code/templates/{phoenix-review-skill.d.ts → skills/phoenix-review-skill.d.ts} +0 -0
  298. /package/dist/application-spec/generate-application/code/templates/{phoenix-skill.d.ts → skills/phoenix-skill.d.ts} +0 -0
@@ -68,6 +68,16 @@ export interface RawAssistantRecipes {
68
68
  readonly hints?: readonly AssistantHint[];
69
69
  readonly groups: Readonly<Record<string, RawAssistantRecipeGroup | null>>;
70
70
  }
71
+ /**
72
+ * One entry of the assistant `actions` map, keyed by the name of an action declared under the
73
+ * top-level `actions` block: it exposes the action as a `Mutation` field named `as` (the action name
74
+ * by default) with `description` (the action's own by default). An empty entry (`movePartBy:` in
75
+ * YAML) exposes the action with its defaults.
76
+ */
77
+ export interface RawAssistantAction {
78
+ readonly description?: string;
79
+ readonly as?: string;
80
+ }
71
81
  /**
72
82
  * Experimental assistant manifest authored under the `assistant` root key. `entities` whitelists the
73
83
  * stored fields the generated GraphQL schema exposes, keyed by entity type or alias name and then by
@@ -77,8 +87,9 @@ export interface RawAssistantRecipes {
77
87
  * have to be exposed in `entities`. `childRecipes` names, per
78
88
  * entity type or alias, the `childEntityRecipes` groups whose recipes the agent may list and add
79
89
  * with `addChild`. `replaceRecipes` names, per entity type or alias, the `replaceEntityRecipes`
80
- * groups whose recipes the agent may list and replace the entity by with `replaceEntity`. Nothing is
81
- * exposed without an entry.
90
+ * groups whose recipes the agent may list and replace the entity by with `replaceEntity`. `actions`
91
+ * names the declared actions the agent may run as custom mutations. Nothing is exposed without an
92
+ * entry.
82
93
  *
83
94
  * `ai` and `instructions` are for the server only: the provider, model tier and reasoning level the
84
95
  * deployment's assistant runs with, and the integrator's own guidance for it, appended to the system
@@ -90,6 +101,7 @@ export interface RawAssistant {
90
101
  readonly writable?: Readonly<Record<string, Readonly<Record<string, RawAssistantWritableField | null>>>>;
91
102
  readonly childRecipes?: Readonly<Record<string, RawAssistantRecipes>>;
92
103
  readonly replaceRecipes?: Readonly<Record<string, RawAssistantRecipes>>;
104
+ readonly actions?: Readonly<Record<string, RawAssistantAction | null>>;
93
105
  readonly instructions?: string;
94
106
  readonly ai: AssistantAiSettings;
95
107
  }
@@ -1 +1 @@
1
- import{z as t}from"zod";import{ASSISTANT_AI_PROVIDERS as e,ASSISTANT_MODEL_TIERS as o,ASSISTANT_REASONING_LEVELS as i}from"@salsita-ai/contracts/assistant";export const ASSISTANT_OPTION_COMPONENT_TYPES=["materialSwatch","attributeSelection","distanceDimensionSelect"];export const ASSISTANT_HINTS=["sameForAllInstances","constantDuringSession"];let n=t.string().regex(/^(?!__)[_A-Za-z][_0-9A-Za-z]*$/,'must be a GraphQL name: letters, digits and "_", not starting with a digit or "__"'),r=e=>t.enum(e,{errorMap:()=>({message:`must be one of: ${e.join(", ")}`})}),s=t.object({description:t.string().optional(),as:n.optional()}).strict(),a=t.array(r(ASSISTANT_HINTS)),c=t.object({options:t.object({componentType:r(ASSISTANT_OPTION_COMPONENT_TYPES),instanceName:t.string().min(1,"must not be empty; name the component instance that holds the choices"),as:n.optional(),validate:t.boolean().optional(),allowDryRuns:t.boolean().optional(),hints:a.optional()}).strict().optional()}).strict(),l=t.object({as:t.string().trim().min(1,"must not be empty; remove the key to use the instance name").optional(),description:t.string().optional(),allowDryRuns:t.boolean().optional()}).strict(),p=e=>t.object({hints:a.optional(),groups:t.record(t.string(),l.nullable(),{required_error:`is required: the recipe groups of the type go under "groups", keyed by the instance name of a "${e}" component`})}).strict(),m=`is required: declare provider (${e.join(", ")}), model (${o.join(", ")}) and reasoning (${i.join(", ")})`,d=t.object({provider:r(e),model:r(o),reasoning:r(i)},{required_error:m,invalid_type_error:m}).strict();export const rawAssistantSchema=t.object({entities:t.record(t.string(),t.record(t.string(),s.nullable())).optional(),deletable:t.record(t.string(),t.object({}).strict().nullable()).optional(),writable:t.record(t.string(),t.record(t.string(),c.nullable())).optional(),childRecipes:t.record(t.string(),p("childEntityRecipes")).optional(),replaceRecipes:t.record(t.string(),p("replaceEntityRecipes")).optional(),instructions:t.string().trim().min(1,"must not be empty; remove the key when there are no instructions").optional(),ai:d}).strict();
1
+ import{z as t}from"zod";import{ASSISTANT_AI_PROVIDERS as e,ASSISTANT_MODEL_TIERS as o,ASSISTANT_REASONING_LEVELS as i}from"@salsita-ai/contracts/assistant";export const ASSISTANT_OPTION_COMPONENT_TYPES=["materialSwatch","attributeSelection","distanceDimensionSelect"];export const ASSISTANT_HINTS=["sameForAllInstances","constantDuringSession"];let n=t.string().regex(/^(?!__)[_A-Za-z][_0-9A-Za-z]*$/,'must be a GraphQL name: letters, digits and "_", not starting with a digit or "__"'),r=e=>t.enum(e,{errorMap:()=>({message:`must be one of: ${e.join(", ")}`})}),s=t.object({description:t.string().optional(),as:n.optional()}).strict(),a=t.array(r(ASSISTANT_HINTS)),c=t.object({options:t.object({componentType:r(ASSISTANT_OPTION_COMPONENT_TYPES),instanceName:t.string().min(1,"must not be empty; name the component instance that holds the choices"),as:n.optional(),validate:t.boolean().optional(),allowDryRuns:t.boolean().optional(),hints:a.optional()}).strict().optional()}).strict(),l=t.object({as:t.string().trim().min(1,"must not be empty; remove the key to use the instance name").optional(),description:t.string().optional(),allowDryRuns:t.boolean().optional()}).strict(),p=e=>t.object({hints:a.optional(),groups:t.record(t.string(),l.nullable(),{required_error:`is required: the recipe groups of the type go under "groups", keyed by the instance name of a "${e}" component`})}).strict(),d=t.object({description:t.string().optional(),as:n.optional()}).strict(),m=`is required: declare provider (${e.join(", ")}), model (${o.join(", ")}) and reasoning (${i.join(", ")})`,g=t.object({provider:r(e),model:r(o),reasoning:r(i)},{required_error:m,invalid_type_error:m}).strict();export const rawAssistantSchema=t.object({entities:t.record(t.string(),t.record(t.string(),s.nullable())).optional(),deletable:t.record(t.string(),t.object({}).strict().nullable()).optional(),writable:t.record(t.string(),t.record(t.string(),c.nullable())).optional(),childRecipes:t.record(t.string(),p("childEntityRecipes")).optional(),replaceRecipes:t.record(t.string(),p("replaceEntityRecipes")).optional(),actions:t.record(t.string(),d.nullable()).optional(),instructions:t.string().trim().min(1,"must not be empty; remove the key when there are no instructions").optional(),ai:g}).strict();
@@ -2,6 +2,7 @@ import { Branded } from '@salsita-ai/common/utils';
2
2
  import { type EnhancedJsonObjectSchemaType, type ParseContext } from '../../core/index.js';
3
3
  import type { ParsedYamlFile } from './parse-yaml-spec-file.js';
4
4
  import { type RawAcl } from './raw-acl.js';
5
+ import { type RawActions } from './raw-actions.js';
5
6
  import { type RawAdminPages } from './raw-admin-pages.js';
6
7
  import { type RawAssistant } from './raw-assistant.js';
7
8
  import { type RawBudgets } from './raw-budgets.js';
@@ -24,6 +25,7 @@ export interface RawSpecContent {
24
25
  readonly presets?: RawPresets;
25
26
  readonly $data?: RawDataSpec;
26
27
  readonly expressions?: RawExpressions;
28
+ readonly actions?: RawActions;
27
29
  readonly invariants?: RawInvariants;
28
30
  readonly relations?: RawRelations;
29
31
  readonly defaults?: RawDefaults;
@@ -1 +1 @@
1
- import{z as o}from"zod";import{Branded as a}from"@salsita-ai/common/utils";import{enhancedJsonObjectSchema as t,validateNoLegacyCodeRef as e}from"../../core/index.js";import{DiagnosticCode as n,diagnosticMessages as r}from"../../errors/index.js";import{rawAclSchema as i}from"./raw-acl.js";import{rawAdminPagesSchema as s}from"./raw-admin-pages.js";import{rawAssistantSchema as l}from"./raw-assistant.js";import{rawBudgetsSchema as p}from"./raw-budgets.js";import{rawChunksSchema as m}from"./raw-chunks.js";import{rawComponentsSchema as c}from"./raw-components.js";import{rawConstantsSchema as f}from"./raw-constants.js";import{rawDataSpecSchema as d}from"./raw-data.js";import{rawDefaultsSchema as u}from"./raw-defaults.js";import{rawEnvSchema as j}from"./raw-env.js";import{rawExpressionsSchema as w}from"./raw-expressions.js";import{rawInvariantsSchema as b}from"./raw-invariants.js";import{rawModelSchema as g}from"./raw-model.js";import{rawPresetsSchema as S}from"./raw-presets.js";import{rawRelationsSchema as D}from"./raw-relations.js";import{rawResourcesConfigSchema as h,rawResourcesSchema as v}from"./raw-resources.js";import{rawStateValuesSchema as O}from"./raw-states.js";import{rawTransformersSchema as E}from"./raw-transformers.js";export const DATA_SCHEMA_KEYS=["publishedData","sessionData","frontendData","backendData","finalizeData","prefetchedFinalizeData","finalizeResult","callbackData"];let R=o.object({$admin:o.boolean().optional(),model:g.optional(),components:c.optional(),presets:S.optional(),$data:d.optional(),expressions:w.optional(),invariants:b.optional(),constants:f.optional(),relations:D.optional(),defaults:u.optional(),publishedData:t.optional(),sessionData:t.optional(),frontendData:t.optional(),backendData:t.optional(),finalizeData:t.optional(),prefetchedFinalizeData:t.optional(),finalizeResult:t.optional(),callbackData:t.optional(),navigationStates:O.optional(),viewStates:O.optional(),toolStates:O.optional(),exportStates:O.optional(),themePreset:o.string().optional(),themeOptions:o.record(o.unknown()).optional(),preview:o.enum(["always","never","auto","hide"]).optional(),alwaysExpand:o.boolean().optional(),availableLocales:o.array(o.string()).optional(),textLanguages:o.array(o.string()).optional(),transformers:E.optional(),project:o.string().regex(/^[a-z0-9-]{3,}$/,"must be a project slug (lowercase letters, digits and dashes, at least 3) or `default`").optional(),applicationVersion:o.string().optional(),workspace:o.boolean().optional(),webComponentTag:o.string().optional(),pageTitle:o.string().optional(),useAuthHook:o.boolean().optional(),useClerk:o.boolean().optional(),enableAdminRelease:o.boolean().optional(),jsonSchemaValidator:o.enum(["ajv","csp-compatible"]).optional(),gltfDecoder:o.enum(["js","wasm"]).optional(),appLoader:o.enum(["legacy","universal"]).optional(),collectFinalizeData:o.boolean().optional(),env:j.optional(),acl:i.optional(),resources:v.optional(),resourcesConfig:h.optional(),budgets:p.optional(),chunks:m.optional(),assistant:l.optional(),$adminPages:s.optional()}).strict();export const parseRawSpecFile=(o,t)=>{e(o.content,t);let i=R.safeParse(o.content);if(!i.success){for(let o of i.error.issues)t.error(n.SCHEMA_PARSE_ERROR,r.SCHEMA_PARSE_ERROR(o.path.join("."),o.message));return}let{constants:s,...l}=i.data,p=((o,a)=>{let t=Object.fromEntries(Object.entries(o.expressions??{}).map(([o,a])=>[o,{...a}]));if(void 0===o.constants)return Object.keys(t).length>0?t:void 0;for(let[e,i]of Object.entries(o.constants))for(let[o,s]of(t[e]||(t[e]={}),Object.entries(i))){if(void 0!==t[e][o]){a.at("constants",e,o).error(n.CONSTANTS_EXPRESSION_CONFLICT,r[n.CONSTANTS_EXPRESSION_CONFLICT](e,o));continue}t[e][o]={kind:"value",constant:!0,value:s,schema:{}}}return Object.keys(t).length>0?t:void 0})(i.data,t);return a({name:o.name,content:{...l,expressions:p}})};
1
+ import{z as o}from"zod";import{Branded as a}from"@salsita-ai/common/utils";import{enhancedJsonObjectSchema as t,validateNoLegacyCodeRef as e}from"../../core/index.js";import{DiagnosticCode as n,diagnosticMessages as i}from"../../errors/index.js";import{rawAclSchema as r}from"./raw-acl.js";import{rawActionsSchema as s}from"./raw-actions.js";import{rawAdminPagesSchema as l}from"./raw-admin-pages.js";import{rawAssistantSchema as p}from"./raw-assistant.js";import{rawBudgetsSchema as m}from"./raw-budgets.js";import{rawChunksSchema as c}from"./raw-chunks.js";import{rawComponentsSchema as f}from"./raw-components.js";import{rawConstantsSchema as d}from"./raw-constants.js";import{rawDataSpecSchema as u}from"./raw-data.js";import{rawDefaultsSchema as j}from"./raw-defaults.js";import{rawEnvSchema as w}from"./raw-env.js";import{rawExpressionsSchema as b}from"./raw-expressions.js";import{rawInvariantsSchema as g}from"./raw-invariants.js";import{rawModelSchema as S}from"./raw-model.js";import{rawPresetsSchema as D}from"./raw-presets.js";import{rawRelationsSchema as h}from"./raw-relations.js";import{rawResourcesConfigSchema as v,rawResourcesSchema as O}from"./raw-resources.js";import{rawStateValuesSchema as E}from"./raw-states.js";import{rawTransformersSchema as R}from"./raw-transformers.js";export const DATA_SCHEMA_KEYS=["publishedData","sessionData","frontendData","backendData","finalizeData","prefetchedFinalizeData","finalizeResult","callbackData"];let k=o.object({$admin:o.boolean().optional(),model:S.optional(),components:f.optional(),presets:D.optional(),$data:u.optional(),expressions:b.optional(),actions:s.optional(),invariants:g.optional(),constants:d.optional(),relations:h.optional(),defaults:j.optional(),publishedData:t.optional(),sessionData:t.optional(),frontendData:t.optional(),backendData:t.optional(),finalizeData:t.optional(),prefetchedFinalizeData:t.optional(),finalizeResult:t.optional(),callbackData:t.optional(),navigationStates:E.optional(),viewStates:E.optional(),toolStates:E.optional(),exportStates:E.optional(),themePreset:o.string().optional(),themeOptions:o.record(o.unknown()).optional(),preview:o.enum(["always","never","auto","hide"]).optional(),alwaysExpand:o.boolean().optional(),availableLocales:o.array(o.string()).optional(),textLanguages:o.array(o.string()).optional(),transformers:R.optional(),project:o.string().regex(/^[a-z0-9-]{3,}$/,"must be a project slug (lowercase letters, digits and dashes, at least 3) or `default`").optional(),applicationVersion:o.string().optional(),workspace:o.boolean().optional(),webComponentTag:o.string().optional(),pageTitle:o.string().optional(),useAuthHook:o.boolean().optional(),useClerk:o.boolean().optional(),enableAdminRelease:o.boolean().optional(),jsonSchemaValidator:o.enum(["ajv","csp-compatible"]).optional(),gltfDecoder:o.enum(["js","wasm"]).optional(),appLoader:o.enum(["legacy","universal"]).optional(),collectFinalizeData:o.boolean().optional(),env:w.optional(),acl:r.optional(),resources:O.optional(),resourcesConfig:v.optional(),budgets:m.optional(),chunks:c.optional(),assistant:p.optional(),$adminPages:l.optional()}).strict();export const parseRawSpecFile=(o,t)=>{e(o.content,t);let r=k.safeParse(o.content);if(!r.success){for(let o of r.error.issues)t.error(n.SCHEMA_PARSE_ERROR,i.SCHEMA_PARSE_ERROR(o.path.join("."),o.message));return}let{constants:s,...l}=r.data,p=((o,a)=>{let t=Object.fromEntries(Object.entries(o.expressions??{}).map(([o,a])=>[o,{...a}]));if(void 0===o.constants)return Object.keys(t).length>0?t:void 0;for(let[e,r]of Object.entries(o.constants))for(let[o,s]of(t[e]||(t[e]={}),Object.entries(r))){if(void 0!==t[e][o]){a.at("constants",e,o).error(n.CONSTANTS_EXPRESSION_CONFLICT,i[n.CONSTANTS_EXPRESSION_CONFLICT](e,o));continue}t[e][o]={kind:"value",constant:!0,value:s,schema:{}}}return Object.keys(t).length>0?t:void 0})(r.data,t);return a({name:o.name,content:{...l,expressions:p}})};
@@ -1 +1 @@
1
- import{mapSchema as e,resolveProjectFileImport as t,resolveProjectPath as r}from"../../core/index.js";import{DiagnosticCode as o,diagnosticMessages as s}from"../../errors/index.js";import{DATA_SCHEMA_KEYS as m}from"../raw-spec/index.js";let n=(m,n,p,i)=>e(m,(e,m)=>e.import?{...e,import:e.import.map((e,a)=>{if(!e.module.startsWith("."))return e;let c=r(n,e.module),O=i.at(...m,"import",String(a),"module");c.escapedProjectRoot?O.warning(o.IMPORT_MODULE_OUTSIDE_PROJECT,s[o.IMPORT_MODULE_OUTSIDE_PROJECT](e.module,c.path)):t(n,e.module,p)||O.error(o.IMPORT_MODULE_NOT_FOUND,s[o.IMPORT_MODULE_NOT_FOUND](e.module,c.path));let d=c.path.startsWith(".")?c.path:`./${c.path}`;return{...e,module:d}})}:e);export const resolveImportModules=(e,t,r)=>{let o=e.content.model?Object.fromEntries(Object.entries(e.content.model).map(([o,s])=>[o,{...s,...s.commonTypes?{commonTypes:Object.fromEntries(Object.entries(s.commonTypes).map(([s,m])=>[s,n(m,e.name,t,r.at("model",o,"commonTypes",s))]))}:{},...s.shapes?{shapes:Object.fromEntries(Object.entries(s.shapes).map(([s,m])=>[s,Object.fromEntries(Object.entries(m).map(([m,p])=>[m,n(p,e.name,t,r.at("model",o,"shapes",s,m))]))]))}:{}}])):void 0,s=e.content.expressions?Object.fromEntries(Object.entries(e.content.expressions).map(([o,s])=>[o,Object.fromEntries(Object.entries(s).map(([s,m])=>[s,{...m,schema:n(m.schema,e.name,t,r.at("expressions",o,s))}]))])):void 0,p=Object.fromEntries(m.flatMap(o=>{let s,m=e.content[o];return m?[[o,{...s=n(m,e.name,t,r.at(o)),type:"object",properties:s.properties??{},required:s.required??[],additionalProperties:!1}]]:[]}));return{...e.content,...o?{model:o}:{},...s?{expressions:s}:{},...p}};
1
+ import{mapSchema as e,resolveProjectFileImport as t,resolveProjectPath as r}from"../../core/index.js";import{DiagnosticCode as o,diagnosticMessages as s}from"../../errors/index.js";import{DATA_SCHEMA_KEYS as n}from"../raw-spec/index.js";let m=(n,m,a,i)=>e(n,(e,n)=>e.import?{...e,import:e.import.map((e,p)=>{if(!e.module.startsWith("."))return e;let c=r(m,e.module),O=i.at(...n,"import",String(p),"module");c.escapedProjectRoot?O.warning(o.IMPORT_MODULE_OUTSIDE_PROJECT,s[o.IMPORT_MODULE_OUTSIDE_PROJECT](e.module,c.path)):t(m,e.module,a)||O.error(o.IMPORT_MODULE_NOT_FOUND,s[o.IMPORT_MODULE_NOT_FOUND](e.module,c.path));let l=c.path.startsWith(".")?c.path:`./${c.path}`;return{...e,module:l}})}:e);export const resolveImportModules=(e,t,r)=>{let o=e.content.model?Object.fromEntries(Object.entries(e.content.model).map(([o,s])=>[o,{...s,...s.commonTypes?{commonTypes:Object.fromEntries(Object.entries(s.commonTypes).map(([s,n])=>[s,m(n,e.name,t,r.at("model",o,"commonTypes",s))]))}:{},...s.shapes?{shapes:Object.fromEntries(Object.entries(s.shapes).map(([s,n])=>[s,Object.fromEntries(Object.entries(n).map(([n,a])=>[n,m(a,e.name,t,r.at("model",o,"shapes",s,n))]))]))}:{}}])):void 0,s=e.content.expressions?Object.fromEntries(Object.entries(e.content.expressions).map(([o,s])=>[o,Object.fromEntries(Object.entries(s).map(([s,n])=>[s,{...n,schema:m(n.schema,e.name,t,r.at("expressions",o,s))}]))])):void 0,a=e.content.actions?Object.fromEntries(Object.entries(e.content.actions).map(([o,s])=>{let n=(s,n)=>Object.fromEntries(Object.entries(s).map(([s,a])=>[s,m(a,e.name,t,r.at("actions",o,n,s))]));return[o,{...s,params:n(s.params,"params"),result:n(s.result,"result")}]})):void 0,i=Object.fromEntries(n.flatMap(o=>{let s,n=e.content[o];return n?[[o,{...s=m(n,e.name,t,r.at(o)),type:"object",properties:s.properties??{},required:s.required??[],additionalProperties:!1}]]:[]}));return{...e.content,...o?{model:o}:{},...s?{expressions:s}:{},...a?{actions:a}:{},...i}};
@@ -1 +1 @@
1
- import{Branded as e}from"@salsita-ai/common/utils";import{AppMetadata as s,BundleSizeBudgets as a,DataSpec as t,Infra as n}from"../sections/index.js";import{AdminSpec as r}from"./admin-spec/index.js";import{ConfiguratorSpec as o}from"./configurator-spec/index.js";export const ApplicationSpec=(i,l)=>{let{merged:m,migrationFiles:p,adminMigrationFiles:d,transformerFiles:c,phxDefinitions:u,evalExpressionParser:w}=i,f=s(m.appMetadata,l);if(!f)return;let S=t({mergedDataSpec:m.dataSpec,mergedChunks:m.chunks},l);if(!S||l.hasErrors)return;let g=l.startSpan("ConfiguratorSpec","ApplicationSpec"),h=o({rawModel:m.model,rawPresets:m.presets,rawExpressions:m.expressions,rawInvariants:m.invariants,rawRelations:m.relations,rawDefaults:m.defaults,dataSchemas:m.dataSchemas,dataSpec:S,appMetadata:f,rawTheme:m.theme,rawUi:m.ui,rawTransformers:m.transformers,rawAssistant:m.assistant,migrationFiles:p,transformerFiles:c,rawComponents:m.components,rawStates:m.configuratorStates,phxDefinitions:u,evalExpressionParser:w,label:"model"},l);if(g(),!h)return;let M=l.startSpan("AdminSpec","ApplicationSpec"),x=r({rawModel:m.adminModel,rawPresets:m.adminPresets,rawExpressions:m.adminExpressions,rawInvariants:m.adminInvariants,rawRelations:m.adminRelations,rawDefaults:m.adminDefaults,migrationFiles:d,rawComponents:m.adminComponents,rawAdminPages:m.adminPages,rawStates:m.adminStates,dataSpec:S,phxDefinitions:u,evalExpressionParser:w,label:"adminModel",requireMigrationFiles:!0},l);if(M(),!x)return;let v=n(m.infra,l);if(!v)return;let y=a(m.budgets);return l.count("entityTypes",h.latestModel.entityTypes.length),l.count("aliases",h.latestModel.aliases.size),l.count("commonTypes",h.latestModel.commonTypes.size),l.count("shapes",h.latestModel.shapes.size),l.count("components",h.components.length),l.count("modelVersions",h.resolvedVersions.versionedModels.length),l.count("presets",h.presets.presets.length),e({configurator:h,admin:x,appMetadata:f,infra:v,budgets:y,dataSpec:S})};
1
+ import{Branded as e}from"@salsita-ai/common/utils";import{AppMetadata as s,BundleSizeBudgets as a,DataSpec as t,Infra as n}from"../sections/index.js";import{AdminSpec as r}from"./admin-spec/index.js";import{ConfiguratorSpec as o}from"./configurator-spec/index.js";export const ApplicationSpec=(i,l)=>{let{merged:m,migrationFiles:p,adminMigrationFiles:d,transformerFiles:c,phxDefinitions:u,evalExpressionParser:w}=i,f=s(m.appMetadata,l);if(!f)return;let S=t({mergedDataSpec:m.dataSpec,mergedChunks:m.chunks},l);if(!S||l.hasErrors)return;let g=l.startSpan("ConfiguratorSpec","ApplicationSpec"),h=o({rawModel:m.model,rawPresets:m.presets,rawExpressions:m.expressions,rawActions:m.actions,rawInvariants:m.invariants,rawRelations:m.relations,rawDefaults:m.defaults,dataSchemas:m.dataSchemas,dataSpec:S,appMetadata:f,rawTheme:m.theme,rawUi:m.ui,rawTransformers:m.transformers,rawAssistant:m.assistant,migrationFiles:p,transformerFiles:c,rawComponents:m.components,rawStates:m.configuratorStates,phxDefinitions:u,evalExpressionParser:w,label:"model"},l);if(g(),!h)return;let M=l.startSpan("AdminSpec","ApplicationSpec"),x=r({rawModel:m.adminModel,rawPresets:m.adminPresets,rawExpressions:m.adminExpressions,rawInvariants:m.adminInvariants,rawRelations:m.adminRelations,rawDefaults:m.adminDefaults,migrationFiles:d,rawComponents:m.adminComponents,rawAdminPages:m.adminPages,rawStates:m.adminStates,dataSpec:S,phxDefinitions:u,evalExpressionParser:w,label:"adminModel",requireMigrationFiles:!0},l);if(M(),!x)return;let A=n(m.infra,l);if(!A)return;let v=a(m.budgets);return l.count("entityTypes",h.latestModel.entityTypes.length),l.count("aliases",h.latestModel.aliases.size),l.count("commonTypes",h.latestModel.commonTypes.size),l.count("shapes",h.latestModel.shapes.size),l.count("components",h.components.length),l.count("modelVersions",h.resolvedVersions.versionedModels.length),l.count("presets",h.presets.presets.length),e({configurator:h,admin:x,appMetadata:f,infra:A,budgets:v,dataSpec:S})};
@@ -1,12 +1,12 @@
1
1
  import { Branded } from '@salsita-ai/common/utils';
2
2
  import { type EvalExpressionParser, type ParseContext, type PhoenixDefinitions } from '../../../../core/index.js';
3
3
  import type { CategorizedFile } from '../../../categorize/index.js';
4
- import type { MergedAssistant, MergedComponents, MergedConfiguratorStates, MergedDataSchemas, MergedDefaults, MergedExpressions, MergedInvariants, MergedModel, MergedPresets, MergedRelations, MergedTheme, MergedTransformers, MergedUi } from '../../../merge/index.js';
4
+ import type { MergedActions, MergedAssistant, MergedComponents, MergedConfiguratorStates, MergedDataSchemas, MergedDefaults, MergedExpressions, MergedInvariants, MergedModel, MergedPresets, MergedRelations, MergedTheme, MergedTransformers, MergedUi } from '../../../merge/index.js';
5
5
  import { Components } from '../../components/index.js';
6
6
  import { Expressions } from '../../expressions/index.js';
7
7
  import { Invariants } from '../../invariants/index.js';
8
8
  import { ConfigurationSchema, DataSchemas, Defaults, ModelVersion, Presets } from '../../model/index.js';
9
- import { type AppMetadata, AssistantServerSettings, AssistantSpec, type DataSpec, Theme, Transformers, Ui } from '../../sections/index.js';
9
+ import { Actions, type AppMetadata, AssistantServerSettings, AssistantSpec, type DataSpec, Theme, Transformers, Ui } from '../../sections/index.js';
10
10
  import { ResolvedVersions } from '../resolved-versions.js';
11
11
  import { ConfiguratorStates } from './states.js';
12
12
  export type ConfiguratorSpec = Branded<'ConfiguratorSpec', {
@@ -17,6 +17,7 @@ export type ConfiguratorSpec = Branded<'ConfiguratorSpec', {
17
17
  readonly configurationSchema: ConfigurationSchema;
18
18
  readonly presets: Presets;
19
19
  readonly expressions: Expressions;
20
+ readonly actions: Actions;
20
21
  readonly invariants: Invariants;
21
22
  readonly dataSchemas: DataSchemas;
22
23
  readonly defaults: Defaults;
@@ -31,6 +32,7 @@ interface ConfiguratorSpecProps {
31
32
  readonly rawModel: MergedModel;
32
33
  readonly rawPresets: MergedPresets;
33
34
  readonly rawExpressions: MergedExpressions;
35
+ readonly rawActions: MergedActions;
34
36
  readonly rawInvariants: MergedInvariants;
35
37
  readonly rawRelations: MergedRelations;
36
38
  readonly rawDefaults: MergedDefaults;
@@ -1 +1 @@
1
- import{Branded as e}from"@salsita-ai/common/utils";import{DiagnosticCode as s,diagnosticMessages as t}from"../../../../errors/index.js";import{Components as r,UserComponents as o}from"../../components/index.js";import{Expressions as i}from"../../expressions/index.js";import{Invariants as n}from"../../invariants/index.js";import{ConfigurationSchema as a,DataSchemas as m,Defaults as p,ModelVersion as l,Presets as d}from"../../model/index.js";import{Relations as f}from"../../relations/index.js";import{AssistantServerSettings as x,AssistantSpec as S,Theme as v,Transformers as c,Ui as j}from"../../sections/index.js";import{ResolvedVersions as u}from"../resolved-versions.js";import{ConfiguratorStates as g}from"./states.js";export const ConfiguratorSpec=(C,I)=>{let{rawModel:M,rawPresets:h,rawExpressions:y,rawInvariants:E,rawRelations:N,rawDefaults:T,dataSchemas:L,dataSpec:G,appMetadata:O,rawTheme:_,rawUi:b,rawTransformers:w,rawAssistant:A,migrationFiles:D,transformerFiles:V,rawComponents:k,phxDefinitions:K,evalExpressionParser:q,label:z}=C;if(0===Object.keys(M).length)return void I.at(z).error(s.MISSING_MODEL,t.MISSING_MODEL(z));M.latest||I.at(z).error(s.MISSING_LATEST,t.MISSING_LATEST(z));let B=new Map;for(let[e,s]of Object.entries(M)){let t=l(e,s,K,I.at(z,e));t&&B.set(t.versionKey,t)}if(I.hasErrors)return;let F=u(B,D,I);if(!F)return;let H=F.versionedModels.find(e=>e.isLatest)?.modelVersion;if(!H)return;let J=F.latestVersion,P=a(H,J),Q=f(N,H,I),R=n({rawInvariants:E,rawExpressions:y,rawRelations:N,rawComponents:k},H,I),U=d(h,H,Q,I),W=o(k,{allNames:H.allNames,resolvedShapes:H.resolvedShapes,parsingContext:I}),X=p(T,H,I.at(z,"defaults")),Y=c(w,V,I),Z=i({rawExpressions:y,inferredExpressions:[...Q.expressions,...R.expressions,...W.expressions]},{latestModel:H,aliases:H.aliases,entityTypes:H.entityTypes,evalExpressionParser:q,parsingContext:I}),$=r({userComponents:W.components,generatedComponents:Q.components,invariantComponents:R.components},{aliases:H.aliases,parsingContext:I}),ee=m({merged:L,dataSpec:G,appMetadata:O,phxDefinitions:K,parsingContext:I}),es=g(C.rawStates,I.at("states"));if(void 0!==X&&void 0!==Z&&void 0!==$&&void 0!==ee)return e({latestModel:H,schemaVersion:J,dataSchemas:ee,resolvedVersions:F,components:$,configurationSchema:P,presets:U,expressions:Z,invariants:R,defaults:X,states:es,theme:v(_),ui:j(b),transformers:Y,assistant:S(A,{model:H,states:es,phxDefinitions:K,expressions:Z,components:$},I),assistantServerSettings:x(A)})};
1
+ import{Branded as e}from"@salsita-ai/common/utils";import{DiagnosticCode as s,diagnosticMessages as t}from"../../../../errors/index.js";import{Components as o,UserComponents as r}from"../../components/index.js";import{Expressions as i}from"../../expressions/index.js";import{Invariants as n}from"../../invariants/index.js";import{ConfigurationSchema as a,DataSchemas as m,Defaults as p,ModelVersion as l,Presets as d}from"../../model/index.js";import{Relations as f}from"../../relations/index.js";import{Actions as x,AssistantServerSettings as S,AssistantSpec as v,Theme as c,Transformers as j,Ui as u}from"../../sections/index.js";import{ResolvedVersions as g}from"../resolved-versions.js";import{ConfiguratorStates as C}from"./states.js";export const ConfiguratorSpec=(I,M)=>{let{rawModel:h,rawPresets:y,rawExpressions:E,rawActions:N,rawInvariants:T,rawRelations:L,rawDefaults:G,dataSchemas:O,dataSpec:_,appMetadata:b,rawTheme:w,rawUi:A,rawTransformers:D,rawAssistant:V,migrationFiles:k,transformerFiles:K,rawComponents:q,phxDefinitions:z,evalExpressionParser:B,label:F}=I;if(0===Object.keys(h).length)return void M.at(F).error(s.MISSING_MODEL,t.MISSING_MODEL(F));h.latest||M.at(F).error(s.MISSING_LATEST,t.MISSING_LATEST(F));let H=new Map;for(let[e,s]of Object.entries(h)){let t=l(e,s,z,M.at(F,e));t&&H.set(t.versionKey,t)}if(M.hasErrors)return;let J=g(H,k,M);if(!J)return;let P=J.versionedModels.find(e=>e.isLatest)?.modelVersion;if(!P)return;let Q=J.latestVersion,R=a(P,Q),U=f(L,P,M),W=n({rawInvariants:T,rawExpressions:E,rawRelations:L,rawComponents:q},P,M),X=d(y,P,U,M),Y=r(q,{allNames:P.allNames,resolvedShapes:P.resolvedShapes,parsingContext:M}),Z=p(G,P,M.at(F,"defaults")),$=j(D,K,M),ee=i({rawExpressions:E,inferredExpressions:[...U.expressions,...W.expressions,...Y.expressions]},{latestModel:P,aliases:P.aliases,entityTypes:P.entityTypes,evalExpressionParser:B,parsingContext:M}),es=o({userComponents:Y.components,generatedComponents:U.components,invariantComponents:W.components},{aliases:P.aliases,parsingContext:M}),et=m({merged:O,dataSpec:_,appMetadata:b,phxDefinitions:z,parsingContext:M}),eo=C(I.rawStates,M.at("states")),er=x(N,{latestModel:P,phxDefinitions:z},M.at("actions"));if(void 0!==Z&&void 0!==ee&&void 0!==er&&void 0!==es&&void 0!==et)return e({latestModel:P,schemaVersion:Q,dataSchemas:et,resolvedVersions:J,components:es,configurationSchema:R,presets:X,expressions:ee,actions:er,invariants:W,defaults:Z,states:eo,theme:c(w),ui:u(A),transformers:$,assistant:v(V,{model:P,states:eo,phxDefinitions:z,expressions:ee,components:es,actions:er},M),assistantServerSettings:S(V)})};
@@ -1 +1,2 @@
1
+ export * from './schema-ref-resolver.js';
1
2
  export * from './validate-schema-refs.js';
@@ -1 +1 @@
1
- export*from"./validate-schema-refs.js";
1
+ export*from"./schema-ref-resolver.js";export*from"./validate-schema-refs.js";
@@ -0,0 +1,23 @@
1
+ import { type EnhancedJsonSchemaType, type PhoenixDefinitions } from '../../../core/index.js';
2
+ /**
3
+ * Resolves a `$ref` to the schema it names, through phoenix definitions and common types.
4
+ * `undefined` for a ref that names nothing known.
5
+ */
6
+ export type SchemaRefResolver = ($ref: string) => EnhancedJsonSchemaType | undefined;
7
+ /** The name of the common type a `common:` or internal `#/model/{version}/commonTypes/{name}` ref names. */
8
+ export declare const commonTypeRefName: ($ref: string) => string | undefined;
9
+ /** The phoenix definition a ref names: full URL, `phx:`, or `#/definitions/` as used inside the definitions. */
10
+ export declare const phxDefinitionRefName: ($ref: string) => string | undefined;
11
+ /**
12
+ * Resolves refs to phoenix definitions (full URL, `phx:`, or `#/definitions/` as used inside the
13
+ * definitions themselves) and to common types (`common:` or internal path).
14
+ */
15
+ export declare const createSchemaRefResolver: (commonTypes: ReadonlyMap<string, {
16
+ readonly schema: EnhancedJsonSchemaType;
17
+ }>, phxDefinitions: PhoenixDefinitions) => SchemaRefResolver;
18
+ /**
19
+ * Whether a value of the schema can be `null`: a `null` type, enum value or const, a nullable
20
+ * `anyOf`/`oneOf` member, or a ref to a nullable schema. A ref that cannot be resolved counts as
21
+ * nullable, since nothing proves otherwise; a ref cycle does not.
22
+ */
23
+ export declare const isNullableSchema: (schema: EnhancedJsonSchemaType, resolveRef: SchemaRefResolver, visitedRefs?: ReadonlySet<string>) => boolean;
@@ -0,0 +1 @@
1
+ import{isArray as e}from"@salsita-ai/common/utils";import{COMMON_SHORT_PREFIX as t,DEFINITION_REF_PATTERN as n,INTERNAL_REF_PATTERN as o,extractPhxDefName as l}from"../../../core/index.js";export const commonTypeRefName=e=>e.startsWith(t)?e.slice(t.length):o.exec(e)?.[2];export const phxDefinitionRefName=e=>l(e)??n.exec(e)?.[1];export const createSchemaRefResolver=(e,t)=>n=>{let o=phxDefinitionRefName(n);if(void 0!==o)return t[o]?.schema;let l=commonTypeRefName(n);return void 0===l?void 0:e.get(l)?.schema};export const isNullableSchema=(t,n,o=new Set)=>{if(t.$ref){if(o.has(t.$ref))return!1;let e=n(t.$ref);return void 0===e||isNullableSchema(e,n,new Set([...o,t.$ref]))}let l=[...t.anyOf??[],...t.oneOf??[]];return"null"===t.type||e(t.type)&&t.type.includes("null")||t.enum?.includes(null)===!0||null===t.const||l.some(e=>isNullableSchema(e,n,o))};
@@ -0,0 +1,10 @@
1
+ import { Branded } from '@salsita-ai/common/utils';
2
+ import type { ParseContext } from '../../../../core/index.js';
3
+ /**
4
+ * Branded action name: camelCase without a prefix (e.g. "movePartBy", "swapCabinets"). An action is
5
+ * a named domain operation, so its name doubles as the key of its implementation and of its
6
+ * generated types.
7
+ */
8
+ export type ActionName = Branded<'ActionName', string>;
9
+ /** Validates that a name follows the action naming convention (camelCase, no prefix). */
10
+ export declare const ActionName: (name: string, ctx: ParseContext) => ActionName | undefined;
@@ -0,0 +1 @@
1
+ import{Branded as o}from"@salsita-ai/common/utils";import{DiagnosticCode as r,diagnosticMessages as t}from"../../../../errors/index.js";let i=/^[a-z][A-Za-z0-9]*$/;export const ActionName=(A,e)=>i.test(A)?o(A):void e.error(r.INVALID_ACTION_NAME,t.INVALID_ACTION_NAME(A));
@@ -1,3 +1,4 @@
1
+ export * from './action-name.js';
1
2
  export * from './alias-name.js';
2
3
  export * from './entity-or-alias-name.js';
3
4
  export * from './entity-type-name.js';
@@ -1 +1 @@
1
- export*from"./alias-name.js";export*from"./entity-or-alias-name.js";export*from"./entity-type-name.js";export*from"./expression-name.js";export*from"./instance-name.js";export*from"./invariant-name.js";export*from"./relation-name.js";export*from"./shape-property-name.js";export*from"./violation-type-name.js";
1
+ export*from"./action-name.js";export*from"./alias-name.js";export*from"./entity-or-alias-name.js";export*from"./entity-type-name.js";export*from"./expression-name.js";export*from"./instance-name.js";export*from"./invariant-name.js";export*from"./relation-name.js";export*from"./shape-property-name.js";export*from"./violation-type-name.js";
@@ -0,0 +1,35 @@
1
+ import { Branded } from '@salsita-ai/common/utils';
2
+ import { type EnhancedJsonSchemaType, type JsonSchema, type ParseContext, type PhoenixDefinitions } from '../../../core/index.js';
3
+ import type { MergedActions } from '../../merge/index.js';
4
+ import { ActionName, type ModelVersion } from '../model/index.js';
5
+ /** One parameter of an action: its declared schema, and whether a call has to pass it (a nullable param is optional). */
6
+ export interface ActionParam {
7
+ readonly schema: EnhancedJsonSchemaType;
8
+ readonly required: boolean;
9
+ }
10
+ /**
11
+ * One validated action declaration. `params` and `result` keep the enhanced schemas for codegen and
12
+ * for consumers that expose the action; `paramsSchema` is the AJV-ready object schema of the parameters, with the
13
+ * refs resolved into `$defs`, which the action system validates the arguments of a call against.
14
+ */
15
+ export type Action = Branded<'Action', {
16
+ readonly name: ActionName;
17
+ readonly description?: string;
18
+ readonly params: Readonly<Record<string, ActionParam>>;
19
+ readonly result: Readonly<Record<string, EnhancedJsonSchemaType>>;
20
+ readonly paramsSchema: JsonSchema;
21
+ }>;
22
+ /** The validated `actions` section: every declared action by name, in declaration order; empty when the spec declares none. */
23
+ export type Actions = Branded<'Actions', ReadonlyMap<ActionName, Action>>;
24
+ interface ActionsContext {
25
+ readonly latestModel: ModelVersion;
26
+ readonly phxDefinitions: PhoenixDefinitions;
27
+ }
28
+ /**
29
+ * Validates the merged `actions` block against the latest model: action names follow the naming
30
+ * rule, every `$ref` and `references` of a param or result member resolves, and each action gets
31
+ * the runtime schema of its parameters. Returns `undefined` after reporting when any action is
32
+ * invalid, so nothing downstream builds on a partial list.
33
+ */
34
+ export declare const Actions: (merged: MergedActions, context: ActionsContext, ctx: ParseContext) => Actions | undefined;
35
+ export {};
@@ -0,0 +1 @@
1
+ import{Branded as e}from"@salsita-ai/common/utils";import{buildPhxDefSchemas as t,collectSchemaRefs as r,toRuntimeSchema as o}from"../../../core/index.js";import{DiagnosticCode as s,diagnosticMessages as i}from"../../../errors/index.js";import{createSchemaRefResolver as a,isNullableSchema as m,validateSchemaRefs as n}from"../common/index.js";import{ActionName as p}from"../model/index.js";let l=(e,{latestModel:t,phxDefinitions:o},a,m)=>{let p=new Set(t.commonTypes.keys());for(let[l,c]of Object.entries(e)){let{refs:e,references:d}=r(c),f=a.at(l);for(let r of(n(e,t.versionKey,p,o,f),d))[...t.allNames].some(e=>e===r.value)||f.at(...r.path).error(s.ACTION_UNKNOWN_REFERENCE_TARGET,i.ACTION_UNKNOWN_REFERENCE_TARGET(m,l,r.value))}};export const Actions=(r,s,i)=>{let{latestModel:n,phxDefinitions:c}=s,d=i.errors.length,f=a(n.commonTypes,c),u=new Map([...Object.entries(t(c)),...[...n.commonTypes].map(([e,t])=>[e,t.schema])]),j=new Map;for(let[t,a]of Object.entries(r)){let r=i.at(t),n=p(t,r);if(l(a.params,s,r.at("params"),`actions.${t}.params`),l(a.result,s,r.at("result"),`actions.${t}.result`),!n)continue;let c=Object.fromEntries(Object.entries(a.params).map(([e,t])=>[e,{schema:t,required:!m(t,f)}])),d=o({type:"object",properties:a.params,required:Object.entries(c).flatMap(([e,{required:t}])=>t?[e]:[]),additionalProperties:!1},u);j.set(n,e({name:n,...void 0===a.description?{}:{description:a.description},params:c,result:a.result,paramsSchema:d}))}return i.errors.length>d?void 0:e(j)};
@@ -1,17 +1,11 @@
1
1
  import { type GraphQLOutputType, GraphQLScalarType } from 'graphql';
2
- import { type EnhancedJsonSchemaType, type PhoenixDefinitions } from '../../../../core/index.js';
3
- import type { CommonTypes } from '../../model/index.js';
2
+ import { type EnhancedJsonSchemaType } from '../../../../core/index.js';
3
+ import { type SchemaRefResolver } from '../../common/index.js';
4
4
  /**
5
5
  * Opaque scalar for values the assistant schema does not type: objects, custom TypeScript types and
6
6
  * loose unions. A value is plain serializable data and passes through unchanged.
7
7
  */
8
8
  export declare const GraphQLJSON: GraphQLScalarType<unknown, unknown>;
9
- /**
10
- * Resolves a `$ref` to the schema it names: nullability is read through phoenix definitions and
11
- * common types, and a common type also gives the field its type and default description.
12
- * `undefined` for a ref that names nothing known.
13
- */
14
- export type AssistantRefResolver = ($ref: string) => EnhancedJsonSchemaType | undefined;
15
9
  /**
16
10
  * The GraphQL type of one whitelisted stored field or declared expression. `enumValues` lists the
17
11
  * allowed values of a string enum, which the schema exposes as `String` and names in the field
@@ -28,11 +22,6 @@ export interface AssistantFieldType {
28
22
  /** The value is localizable, or a list of localizable values, and the schema exposes it as `String`. */
29
23
  readonly localized?: true;
30
24
  }
31
- /**
32
- * Resolves refs to phoenix definitions (full URL, `phx:`, or `#/definitions/` as used inside the
33
- * definitions themselves) and to common types (`common:` or internal path).
34
- */
35
- export declare const createAssistantRefResolver: (commonTypes: CommonTypes, phxDefinitions: PhoenixDefinitions) => AssistantRefResolver;
36
25
  /**
37
26
  * Maps the JSON Schema of a whitelisted stored field or declared expression to its GraphQL type,
38
27
  * following the resolution order of `schemaToTypeString`: refs to localizable phoenix definitions →
@@ -41,4 +30,4 @@ export declare const createAssistantRefResolver: (commonTypes: CommonTypes, phxD
41
30
  * `String` with their values, primitives → scalars, arrays of mappable items → lists, and everything
42
31
  * else → `JSON`. Every type, `JSON` included, is non-null unless the schema admits `null`.
43
32
  */
44
- export declare const assistantFieldType: (schema: EnhancedJsonSchemaType, resolveRef: AssistantRefResolver) => AssistantFieldType;
33
+ export declare const assistantFieldType: (schema: EnhancedJsonSchemaType, resolveRef: SchemaRefResolver) => AssistantFieldType;
@@ -1 +1 @@
1
- import{GraphQLBoolean as e,GraphQLFloat as t,GraphQLID as r,GraphQLInt as n,GraphQLList as i,GraphQLNonNull as l,GraphQLScalarType as a,GraphQLString as o,getNamedType as s}from"graphql";import{isArray as u}from"@salsita-ai/common/utils";import{COMMON_SHORT_PREFIX as f,DEFINITION_REF_PATTERN as p,INTERNAL_REF_PATTERN as d,extractPhxDefName as y}from"../../../../core/index.js";export const GraphQLJSON=new a({name:"JSON",description:"Any JSON value, returned as stored. Always plain serializable data."});let c={number:t,integer:n,string:o,boolean:e},m=new Set(["Localizable","EnhancedLocalizable","MessageDescriptor","EnhancedMessageDescriptor","MultilingualString"]),v={type:GraphQLJSON},h=e=>e.startsWith(f)?e.slice(f.length):d.exec(e)?.[2],g=e=>y(e)??p.exec(e)?.[1];export const createAssistantRefResolver=(e,t)=>r=>{let n=g(r);if(void 0!==n)return t[n]?.schema;let i=h(r);return void 0===i?void 0:e.get(i)?.schema};let S=(e,t,r=new Set)=>{if(e.$ref){if(r.has(e.$ref))return!1;let n=t(e.$ref);return void 0===n||S(n,t,new Set([...r,e.$ref]))}let n=[...e.anyOf??[],...e.oneOf??[]];return"null"===e.type||u(e.type)&&e.type.includes("null")||e.enum?.includes(null)===!0||null===e.const||n.some(e=>S(e,t,r))},O=({anyOf:e,oneOf:t})=>[...e??[],...t??[]].filter(e=>"null"!==e.type),w=(e,t,n)=>{let l;if(void 0!==(l=void 0===e.$ref?void 0:g(e.$ref))&&m.has(l))return{type:o,localized:!0};if(e.tsType)return v;if(e.$ref){var a=e.$ref;if("DomainEntityId"===y(a))return{type:r};let i=h(a),l=void 0===i||n.has(i)?void 0:t(a);return void 0!==i&&l?w(l,t,new Set([...n,i])):v}if(e.anyOf||e.oneOf)return((e,t,r)=>{let n=O(e).map(e=>w(e,t,r)),[i]=n;if(1===n.length&&i)return i;let l=n.flatMap(({type:e,enumValues:t})=>e===o&&t?[t]:[]);if(n.length<2||l.length!==n.length||e.anyOf&&e.oneOf)return v;let a=l.flatMap(e=>[...new Set(e)]);return{type:o,enumValues:e.oneOf?a.filter(e=>a.indexOf(e)===a.lastIndexOf(e)):[...new Set(a)]}})(e,t,n);if(e.enum){let t;return(t=e.enum.filter(e=>null!==e)).every(e=>"string"==typeof e)?{type:o,enumValues:t}:v}if(u(e.type)){let r=e.type.filter(e=>"null"!==e),[i]=r;return 1===r.length&&i?w({...e,type:i},t,n):v}if("array"===e.type){var f=e.items;if(!f||u(f))return v;let r=$(f,t,n);return s(r.type)===GraphQLJSON?v:{type:new i(r.type),enumValues:r.enumValues,localized:r.localized}}let p="string"==typeof e.type?c[e.type]:void 0;return p?{type:p}:v},$=(e,t,r)=>{let n=w(e,t,r);return{...n,type:S(e,t)?n.type:new l(n.type)}},x=e=>{let t=O(e);return e.$ref??(1===t.length?t[0]?.$ref:void 0)},z=(e,t,r)=>{let n=h(e),i=void 0===n||r.has(n)?void 0:t(e);if(void 0===n||!i)return;let l=x(i);return void 0===i.description&&l?z(l,t,new Set([...r,n])):i.description};export const assistantFieldType=(e,t)=>{let r=x(e),n=void 0===e.description&&r?z(r,t,new Set):e.description;return{...$(e,t,new Set),...void 0===n?{}:{description:n}}};
1
+ import{GraphQLBoolean as e,GraphQLFloat as t,GraphQLID as r,GraphQLInt as n,GraphQLList as i,GraphQLNonNull as l,GraphQLScalarType as o,GraphQLString as a,getNamedType as p}from"graphql";import{isArray as s}from"@salsita-ai/common/utils";import{extractPhxDefName as f}from"../../../../core/index.js";import{commonTypeRefName as u,isNullableSchema as d,phxDefinitionRefName as y}from"../../common/index.js";export const GraphQLJSON=new o({name:"JSON",description:"Any JSON value, returned as stored. Always plain serializable data."});let m={number:t,integer:n,string:a,boolean:e},c=new Set(["Localizable","EnhancedLocalizable","MessageDescriptor","EnhancedMessageDescriptor","MultilingualString"]),v={type:GraphQLJSON},g=({anyOf:e,oneOf:t})=>[...e??[],...t??[]].filter(e=>"null"!==e.type),h=(e,t,n)=>{let l;if(void 0!==(l=void 0===e.$ref?void 0:y(e.$ref))&&c.has(l))return{type:a,localized:!0};if(e.tsType)return v;if(e.$ref){var o=e.$ref;if("DomainEntityId"===f(o))return{type:r};let i=u(o),l=void 0===i||n.has(i)?void 0:t(o);return void 0!==i&&l?h(l,t,new Set([...n,i])):v}if(e.anyOf||e.oneOf)return((e,t,r)=>{let n=g(e).map(e=>h(e,t,r)),[i]=n;if(1===n.length&&i)return i;let l=n.flatMap(({type:e,enumValues:t})=>e===a&&t?[t]:[]);if(n.length<2||l.length!==n.length||e.anyOf&&e.oneOf)return v;let o=l.flatMap(e=>[...new Set(e)]);return{type:a,enumValues:e.oneOf?o.filter(e=>o.indexOf(e)===o.lastIndexOf(e)):[...new Set(o)]}})(e,t,n);if(e.enum){let t;return(t=e.enum.filter(e=>null!==e)).every(e=>"string"==typeof e)?{type:a,enumValues:t}:v}if(s(e.type)){let r=e.type.filter(e=>"null"!==e),[i]=r;return 1===r.length&&i?h({...e,type:i},t,n):v}if("array"===e.type){var d=e.items;if(!d||s(d))return v;let r=S(d,t,n);return p(r.type)===GraphQLJSON?v:{type:new i(r.type),enumValues:r.enumValues,localized:r.localized}}let O="string"==typeof e.type?m[e.type]:void 0;return O?{type:O}:v},S=(e,t,r)=>{let n=h(e,t,r);return{...n,type:d(e,t)?n.type:new l(n.type)}},O=e=>{let t=g(e);return e.$ref??(1===t.length?t[0]?.$ref:void 0)},w=(e,t,r)=>{let n=u(e),i=void 0===n||r.has(n)?void 0:t(e);if(void 0===n||!i)return;let l=O(i);return void 0===i.description&&l?w(l,t,new Set([...r,n])):i.description};export const assistantFieldType=(e,t)=>{let r=O(e),n=void 0===e.description&&r?w(r,t,new Set):e.description;return{...S(e,t,new Set),...void 0===n?{}:{description:n}}};
@@ -4,6 +4,7 @@ import { type RawAssistant } from '../../../raw-spec/index.js';
4
4
  import type { Components } from '../../components/index.js';
5
5
  import type { Expressions } from '../../expressions/index.js';
6
6
  import type { EntityTypeName, ModelVersion } from '../../model/index.js';
7
+ import type { Actions } from '../actions.js';
7
8
  /**
8
9
  * The field sources of every entity object type: GraphQL object type name → GraphQL field name →
9
10
  * its {@link AssistantFieldSource}. Only entity object types appear, since values always resolve on
@@ -27,11 +28,16 @@ export type AssistantRecipeGroups = Readonly<Record<string, readonly AssistantRe
27
28
  * aliases expanded to their members.
28
29
  */
29
30
  export type AssistantFieldOptionsByType = Readonly<Record<string, readonly AssistantFieldOptions[]>>;
31
+ /**
32
+ * The custom mutations the manifest exposes: `Mutation` field name → the declared action it runs,
33
+ * in manifest order. Empty when the manifest exposes no action.
34
+ */
35
+ export type AssistantActions = Readonly<Record<string, string>>;
30
36
  /**
31
37
  * The printed assistant GraphQL schema, the field sources its runtime resolvers read through, the
32
38
  * entity types `deleteEntity` may delete, the fields the `modify<Type>` mutations may write, the
33
- * recipe groups `addChild` may add from, the ones `replaceEntity` may replace by and the options edges
34
- * of writable fields, aliases expanded,
39
+ * recipe groups `addChild` may add from, the ones `replaceEntity` may replace by, the options edges
40
+ * of writable fields, aliases expanded, and the actions the custom mutations run,
35
41
  * so the runtime checks the same sets the schema describes.
36
42
  */
37
43
  export interface AssistantSchema {
@@ -42,6 +48,7 @@ export interface AssistantSchema {
42
48
  readonly childRecipes: AssistantRecipeGroups;
43
49
  readonly replaceRecipes: AssistantRecipeGroups;
44
50
  readonly fieldOptions: AssistantFieldOptionsByType;
51
+ readonly actions: AssistantActions;
45
52
  }
46
53
  /** What the assistant schema is built from, besides the manifest. */
47
54
  export interface AssistantSchemaInput {
@@ -53,6 +60,8 @@ export interface AssistantSchemaInput {
53
60
  readonly expressions: Expressions;
54
61
  /** The parsed components, whose instances recipe group entries and the `options` of writable fields name. */
55
62
  readonly components: Components;
63
+ /** The declared actions, which `actions` manifest entries expose as custom mutations. */
64
+ readonly actions: Actions;
56
65
  }
57
66
  /** The declared UI state values: each non-empty kind becomes a `Navigation` field listing its values. */
58
67
  export interface AssistantStates {
@@ -67,4 +76,4 @@ export interface AssistantStates {
67
76
  * selection and navigation entry points. Returns `undefined` after reporting diagnostics when the
68
77
  * manifest does not fit the model or the generated schema is not valid GraphQL.
69
78
  */
70
- export declare const createAssistantSchema: (manifest: RawAssistant, { model, states, phxDefinitions, expressions, components }: AssistantSchemaInput, ctx: ParseContext) => AssistantSchema | undefined;
79
+ export declare const createAssistantSchema: (manifest: RawAssistant, { model, states, phxDefinitions, expressions, components, actions }: AssistantSchemaInput, ctx: ParseContext) => AssistantSchema | undefined;
@@ -1 +1 @@
1
- import{GraphQLBoolean as e,GraphQLID as t,GraphQLInputObjectType as n,GraphQLInterfaceType as i,GraphQLList as r,GraphQLNonNull as o,GraphQLObjectType as s,GraphQLSchema as a,GraphQLString as p,getNullableType as d,isInputType as l,printSchema as c,validateSchema as h}from"graphql";import{DiagnosticCode as f,diagnosticMessages as y}from"../../../../errors/index.js";import{ASSISTANT_HINTS as u}from"../../../raw-spec/index.js";import{GraphQLJSON as g,assistantFieldType as m,createAssistantRefResolver as S}from"./assistant-field-type.js";let w="AnyDomainEntity",T="RootEntity",N="RecipeGroup",E="Recipe",A="DryRun",I="DryRunError",_="FieldOption",v={section:"childRecipes",component:"childEntityRecipes",mutation:"addChild"},O={section:"replaceRecipes",component:"replaceEntityRecipes",mutation:"replaceEntity"},b=new Set(["id","type","meta"]),R=new Set(["Query","Mutation","Navigation",N,E,A,I,_,"JSON","String","Int","Float","Boolean","ID"]),$=(e,t)=>{if(!t||0===t.length)return e;let n=`One of: ${t.map(e=>`"${e}"`).join(", ")}.`;return e?/[.!?]$/.test(e)?`${e} ${n}`:`${e}. ${n}`:n},D=new Set(["id","type"]),M="The raw stored value: a string, a message descriptor or a multilingual object.",j=(e,t)=>Object.entries(e).find(([e])=>e===t)?.[1],P=(e,t,{model:n,components:i})=>{let r=new Set([e,...[...n.aliases].filter(([,t])=>t.includes(e)).map(([e])=>e)]);return i.filter(({componentName:e,typeOrAlias:n})=>e===t&&r.has(n)).map(({instanceName:e})=>String(e))},U=(...e)=>u.filter(t=>e.some(e=>e?.includes(t))),C=(e,t,n,i)=>{let{model:r}=n,o=new Map,s=new Map;for(let[a,{groups:p,hints:d}]of Object.entries(e)){let e=r.entityTypes.find(e=>e===a),l=e?[e]:[...r.aliases].find(([e])=>e===a)?.[1];if(!l){i.at(a).error(f.ASSISTANT_UNKNOWN_TYPE,y.ASSISTANT_UNKNOWN_TYPE(t.section,a));continue}for(let[e,r]of(l.forEach(e=>s.set(e,U(s.get(e),d))),Object.entries(p))){let s=i.at(a,"groups",e),p=`${a}.groups.${e}`,d={groupId:r?.as??e,instanceName:e,...r?.description===void 0?{}:{description:r.description},dryRuns:r?.allowDryRuns??!0};for(let i of l){let r=P(i,t.component,n),l=o.get(i)??[],c=l.find(e=>e.group.groupId===d.groupId);r.includes(e)?c?s.error(f.ASSISTANT_DUPLICATE_RECIPE_GROUP,y.ASSISTANT_DUPLICATE_RECIPE_GROUP(t,i,d.groupId,[c.entry,p])):o.set(i,[...l,{group:d,entry:p,entryCtx:s}]):s.error(f.ASSISTANT_UNKNOWN_RECIPE_GROUP,y.ASSISTANT_UNKNOWN_RECIPE_GROUP(t,a,e,i,r))}}}let a=r.entityTypes.flatMap(e=>{let t=o.get(e);return t?[{entityType:e,groups:t}]:[]});return{groups:Object.fromEntries(a.map(({entityType:e,groups:t})=>[e,t.map(({group:e})=>e)])),hints:s,metaEntries:new Map(a.flatMap(({entityType:e,groups:[t]})=>t?[[e,t.entryCtx]]:[]))}},k=e=>`${e}Meta`,W={sameForAllInstances:e=>`All ${e} instances have the same recipe groups and recipes.`,constantDuringSession:()=>"They do not change during the session."},L={sameForAllInstances:e=>`All ${e} instances have the same options.`,constantDuringSession:()=>"The options do not change during the session."},F=(e,t,n)=>e.map(e=>` ${t[e](n)}`).join(""),K=({section:e},{groups:t,hints:n},{entityType:i,purpose:s,recipeGroup:a})=>{let p=t.filter(({dryRuns:e})=>!e).map(({groupId:e})=>e);return 0===t.length?{}:{[e]:{type:new o(new r(new o(a))),description:`The recipe groups for ${s} this ${i}: ${t.map(({groupId:e})=>e).join(", ")}. A group is listed only while the configurator offers it on the entity.${p.length>0?` Recipes of these groups cannot be dry-run: ${p.join(", ")}.`:""}${F(n,W,i)}`}}},G=({entityType:e,childRecipes:t,replaceRecipes:n,optionsEdges:i},{recipeGroup:a,fieldOption:p})=>new s({name:k(e),fields:Object.assign(K(v,t,{entityType:e,purpose:"adding children to",recipeGroup:a}),K(O,n,{entityType:e,purpose:"replacing",recipeGroup:a}),...i.map(({options:{edgeName:t,field:n,validate:i,dryRuns:s},hints:a})=>({[t]:{type:new r(new o(p)),description:`The values "${n}" of this ${e} can take, as the configurator offers them in the current state of the entity. Null when the configurator does not offer a choice on this entity. ${i?`modify${e} rejects a "${n}" value that is not in the list.`:`modify${e} does not check "${n}" against the list.`}${s?"":" Its options cannot be dry-run."}${F(a,L,e)}`}})))}),V=e=>Object.entries(e).map(([e,t])=>`${e} (${t.map(({groupId:e})=>e).join(", ")})`),Y=(e,t)=>{let{type:n,enumValues:i,localized:r,description:o}=m(e,t);return{type:{kind:"value",type:n},localized:!0===r,description:o,enumValues:i}},x=(e,t,n,i,r)=>{let o=t.startsWith("$")?((e,t,n,i)=>{let r=((e,t,{expressions:n,entityTypes:i})=>{let r=i.find(t=>t===e);for(let i of void 0===r?[e]:n.entityExpressions.get(r)?.shapeGroups??[]){let e=[...n.expressions.get(i)?.values()??[]].find(e=>e.name===t);if(e?.declaredSchema)return{implementation:e.implementation,declaredSchema:e.declaredSchema}}})(e.name,t,n);if(!r)return void i.error(f.ASSISTANT_UNKNOWN_FIELD,y.ASSISTANT_UNKNOWN_FIELD("entities",e.name,t));let{implementation:o}=r;if("violations"===o.kind)return void i.error(f.ASSISTANT_UNSUPPORTED_EXPRESSION,y.ASSISTANT_UNSUPPORTED_EXPRESSION(e.name,t));if("relation"===o.kind){let[{target:e,cardinality:t}]=o.params;return{type:{kind:"edge",target:e,cardinality:t},localized:!1,description:void 0}}return Y(r.declaredSchema,n.resolveRef)})(e,t,i,r):((e,t,{resolveRef:n},i)=>{let r=Object.entries(e.shape.properties).find(([e])=>e===t)?.[1];return r?Y(r,n):void i.error(f.ASSISTANT_UNKNOWN_FIELD,y.ASSISTANT_UNKNOWN_FIELD("entities",e.name,t))})(e,t,i,r);if(!o)return;let s=n?.as??(t.startsWith("$")?`_${t.slice(1)}`:t);return b.has(s)?void r.error(f.ASSISTANT_RESERVED_FIELD_NAME,y.ASSISTANT_RESERVED_FIELD_NAME(e.name,t,s)):{name:s,source:t,type:o.type,localized:o.localized,description:$(n?.description??o.description,o.enumValues),describedByManifest:n?.description!==void 0,enumValues:o.enumValues,valueDescription:n?.description??o.description}},q=(e,t,n,i,r)=>{let o=new Map,s=new Set(n);for(let i of[...t.flat(),...n]){let t=o.get(i.name);t?t.source!==i.source?r.error(f.ASSISTANT_FIELD_NAME_CONFLICT,y.ASSISTANT_FIELD_NAME_CONFLICT(e,i.name,[t.source,i.source])):s.has(i)&&i.describedByManifest&&o.set(i.name,{...t,description:i.description,describedByManifest:!0,valueDescription:i.valueDescription}):o.set(i.name,i)}return[...o.values()].map(e=>{let t=e.enumValues&&i(e.source);return t?{...e,enumValues:t,description:$(e.valueDescription,t)}:e})},B=({target:e,cardinality:t},n)=>{let i=n(e);switch(t){case"1-1":return new o(i);case"0-1":return i;case"0-N":case"1-N":return new o(new r(new o(i)))}},z=(e,n,i)=>{let r={id:{type:new o(t),description:i?`Unique id of the entity, e.g. "${i}-1".`:void 0},type:{type:new o(p),description:i?`Entity type name, e.g. "${i}".`:void 0}};for(let t of e)r[t.name]={type:"edge"===t.type.kind?B(t.type,n):t.type.type,description:t.description};return r},H=e=>Object.fromEntries(e.map(({name:e,source:t,localized:n})=>[e,n?{source:t,localized:!0}:{source:t}]));export const createAssistantSchema=(u,{model:b,states:W,phxDefinitions:L,expressions:F,components:K},Y)=>{let B,Q,X,J=Y.errors.length,Z=Y.at("entities"),ee=S(b.commonTypes,L),et=b.rootEntityTypes[0]??b.entityTypes[0];for(let e of b.resolvedShapes.keys())R.has(e)&&Y.error(f.ASSISTANT_RESERVED_TYPE_NAME,y.ASSISTANT_RESERVED_TYPE_NAME(e,[...R]));let en=((e,t,n)=>{let i=new Map;for(let r of e){let e=[];for(let[i,o]of Object.entries(r.entries)){let s=x(r,i,o,t,n.at(r.name,i));s&&e.push(s)}i.set(r.name,e)}return i})(((e,t,n)=>{let i=[];for(let[n,r]of t.resolvedShapes){let t=e[n];t&&i.push({name:n,shape:r,entries:t})}let r=new Set(t.resolvedShapes.keys());for(let t of Object.keys(e))r.has(t)||n.at(t).error(f.ASSISTANT_UNKNOWN_TYPE,y.ASSISTANT_UNKNOWN_TYPE("entities",t));return i})(u.entities??{},b,Z),{resolveRef:ee,expressions:F,entityTypes:b.entityTypes},Z),ei=q(w,[],en.get(w)??[],()=>void 0,Y),er=new Map,eo=C(u.childRecipes??{},v,{model:b,components:K},Y.at(v.section)),es=C(u.replaceRecipes??{},O,{model:b,components:K},Y.at(O.section)),ea=eo.groups,ep=es.groups,ed=new Map,el=e=>{let[t,...n]=[...b.aliases].find(([t])=>t===e)?.[1]??[];return(t&&0===n.length?er.get(t):void 0)??ef.get(e)??eh},ec=e=>er.get(e)??el(e),eh=new i({name:w,description:"Every domain entity.",fields:()=>z(ei,ec,et)}),ef=new Map([[w,eh]]),ey=new Map([[w,ei]]);for(let e of b.aliases.keys()){if(e===w)continue;let t=q(e,[ei],en.get(e)??[],()=>void 0,Y);ey.set(e,t),ef.set(e,new i({name:e,interfaces:[eh],fields:()=>z(t,ec)}))}let eu={};for(let e of b.entityTypes){let t=[w,...[...b.aliases].filter(([t,n])=>t!==w&&n.includes(e)).map(([e])=>e)],n=[...b.resolvedShapes].find(([t])=>t===e)?.[1],i=q(e,t.map(e=>ey.get(e)??[]),en.get(e)??[],e=>{let t=Object.entries(n?.properties??{}).find(([t])=>t===e)?.[1];return t&&m(t,ee).enumValues},Y);eu[e]=H(i),er.set(e,new s({name:e,interfaces:t.flatMap(e=>ef.get(e)??[]),fields:()=>{let t=ed.get(e);return{...z(i,ec),...t?{meta:{type:new o(t),description:"What the configurator offers on this entity besides its fields."}}:{}}}}))}let eg=((e,t,n)=>{let i=new Set;for(let r of Object.keys(e)){let e=t.entityTypes.find(e=>e===r),o=e?[e]:[...t.aliases].find(([e])=>e===r)?.[1];o?o.forEach(e=>i.add(e)):n.at(r).error(f.ASSISTANT_UNKNOWN_TYPE,y.ASSISTANT_UNKNOWN_TYPE("deletable",r))}return t.entityTypes.filter(e=>i.has(e))})(u.deletable??{},b,Y.at("deletable")),em=((e,t,n)=>{let{model:i}=t,r=((e,t,n)=>{let i=new Map;for(let[r,o]of Object.entries(e)){let e=t.entityTypes.find(e=>e===r),s=e?[e]:[...t.aliases].find(([e])=>e===r)?.[1],a=[...t.resolvedShapes].find(([e])=>e===r)?.[1];if(!s||!a){n.at(r).error(f.ASSISTANT_UNKNOWN_TYPE,y.ASSISTANT_UNKNOWN_TYPE("writable",r));continue}for(let e of Object.keys(o)){let t=n.at(r,e);e.startsWith("$")||D.has(e)?t.error(f.ASSISTANT_NOT_WRITABLE,y.ASSISTANT_NOT_WRITABLE(r,e)):j(a.properties,e)?s.forEach(t=>{let n=i.get(t)??new Map;i.set(t,n.has(e)?n:n.set(e,r))}):t.error(f.ASSISTANT_UNKNOWN_FIELD,y.ASSISTANT_UNKNOWN_FIELD("writable",r,e))}}return i})(e,i,n);return i.entityTypes.flatMap(e=>{let o=[...i.resolvedShapes].find(([t])=>t===e)?.[1],s=[...r.get(e)??[]].flatMap(([i,r])=>{let s=o&&j(o.properties,i);return(s&&((e,{property:t,writableKey:n},i,{resolveRef:r,exposed:o,sources:s},a)=>{let p=Object.entries(j(s,e)??{}),c=o.get(e)?.find(({source:e})=>e===t)?.name??p.find(([,{source:e}])=>e===t)?.[0],h=void 0===c?p.find(([e])=>e===t)?.[1]:void 0;if(h)return void a.at(n,t).error(f.ASSISTANT_FIELD_NAME_CONFLICT,y.ASSISTANT_FIELD_NAME_CONFLICT(e,t,[h.source,t],`${n}.${t}`));let{type:u,enumValues:S,localized:w,description:T}=m(i,r),N=d(u),E=c??t,A=void 0===c?$(T,S):void 0;return w?{name:E,property:t,type:g,description:A?`${A} ${M}`:M}:{name:E,property:t,type:l(N)?N:g,description:A}})(e,{property:i,writableKey:r},s,t,n))??[]});return s.length>0?[{entityType:e,fields:s}]:[]})})(u.writable??{},{model:b,resolveRef:ee,exposed:en,sources:eu},Y.at("writable")),eS=((e,t,n,i)=>{let{model:r}=n,o=new Map;for(let[s,a]of Object.entries(e)){let e=r.entityTypes.find(e=>e===s),p=e?[e]:[...r.aliases].find(([e])=>e===s)?.[1]??[];for(let[e,r]of Object.entries(a)){let a=r?.options;if(!a)continue;let d=i.at(s,e,"options"),l=`${s}.${e}`;for(let i of p){let r=t.find(e=>e.entityType===i)?.fields.find(t=>t.property===e);if(!r)continue;let s=a.as??`${r.name}Options`,p=P(i,a.componentType,n),c=o.get(i)??[],h=c.find(e=>e.options.edgeName===s||e.options.field===r.name);if(p.includes(a.instanceName))if(s===v.section||s===O.section)d.error(f.ASSISTANT_RESERVED_OPTIONS_EDGE_NAME,y.ASSISTANT_RESERVED_OPTIONS_EDGE_NAME(l,s,r.name));else if(h)d.error(f.ASSISTANT_DUPLICATE_OPTIONS_EDGE,y.ASSISTANT_DUPLICATE_OPTIONS_EDGE(i,h.options.edgeName===s?{edgeName:s}:{field:r.name},[h.entry,l]));else{let e={field:r.name,edgeName:s,componentType:a.componentType,instanceName:a.instanceName,validate:a.validate??!0,dryRuns:a.allowDryRuns??!0};o.set(i,[...c,{options:e,hints:U(a.hints),entry:l,entryCtx:d}])}else d.error(f.ASSISTANT_UNKNOWN_OPTIONS_COMPONENT,y.ASSISTANT_UNKNOWN_OPTIONS_COMPONENT(l,a,i,p))}}}return o})(u.writable??{},em,{model:b,components:K},Y.at("writable"));var ew,eT,eN,eE=[eo.metaEntries,es.metaEntries,new Map([...eS].flatMap(([e,[t]])=>t?[[e,t.entryCtx]]:[]))];for(let e of b.entityTypes){let t=eE.flatMap(t=>t.get(e)??[])[0],n=k(e);t&&[...b.resolvedShapes.keys()].some(e=>e===n)&&t.error(f.ASSISTANT_META_TYPE_NAME_CONFLICT,y.ASSISTANT_META_TYPE_NAME_CONFLICT(e,n))}let eA=(ew=()=>el(ef.has(T)?T:w),eT=()=>el(w),B=new s({name:I,description:"One reason a change cannot be made right now.",fields:{type:{type:new o(p),description:'Kind of the problem, or "uncaught".'},entityId:{type:t,description:"The entity the problem is on, when it is on one."},message:{type:p,description:"The problem as the configurator words it for the user."}}}),new s({name:A,description:"What using a recipe or writing an option would do, tried out on the configuration the query reads. Nothing is changed.",fields:()=>({ok:{type:new o(e),description:"Whether the change would succeed on the configuration the query reads."},root:{type:ew(),description:"The root entity of the configuration as it would be after the change; null when ok is false. Everything selected under it reads that configuration."},changedEntities:{type:new o(new r(new o(eT()))),description:"Every entity the change would add or modify, read from that configuration; empty when ok is false. An entity whose id the configuration does not have yet would be added, and its id may differ in the real mutation. An entity that is written to is listed even when it gets the values it already has."},deletedEntityIds:{type:new o(new r(new o(t))),description:"The ids of the entities the change would remove; empty when ok is false."},errors:{type:new o(new r(new o(B))),description:"Why the change cannot be made; empty when ok is true."}})})),eI={recipeGroup:(eN=[...Object.keys(ea).length>0?[v.mutation]:[],...Object.keys(ep).length>0?[O.mutation]:[]],Q=eN.join(" or "),X=new s({name:E,description:"One recipe of a recipe group: something that can be added to the entity, or something the entity can be replaced with, depending on the edge the group is listed under.",fields:{key:{type:new o(p),description:`Identifies the recipe within its group; pass it to ${Q} as recipeKey. Keys can change with the configuration and with the language, so read them from the listing rather than remembering them.`},name:{type:new o(p),description:"Name shown to the user."},description:{type:p},imageUrl:{type:p,description:"Thumbnail shown to the user."},dryRun:{type:eA,description:"Tries the recipe out. Each selected dry-run costs a full recalculation, so select it only on the recipes you are deciding between. Null for a group whose recipes cannot be dry-run."}}}),new s({name:N,description:"A group of recipes of an entity, as the configurator offers them to the user.",fields:{groupId:{type:new o(p),description:`Identifies the group on its entity; pass it to ${Q} as recipeGroupId.`},name:{type:new o(p),description:"Name shown to the user."},description:{type:p},recipes:{type:new o(new r(new o(X)))}}})),fieldOption:new s({name:_,description:"One choice of a field, as the configurator offers it to the user.",fields:{value:{type:new o(g),description:"The value to write to the field."},name:{type:p,description:"Name shown to the user."},description:{type:p},imageUrl:{type:p,description:"Image shown to the user."},color:{type:p,description:'Color shown to the user, as "#rrggbb".'},dryRun:{type:eA,description:"Tries writing the value to the field. Each selected dry-run costs a full recalculation, so select it only on the options you are deciding between. Null for a field whose options cannot be dry-run."}}})};for(let e of b.entityTypes){let t=j(ea,e)??[],n=j(ep,e)??[],i=eS.get(e)??[];(t.length>0||n.length>0||i.length>0)&&ed.set(e,G({entityType:e,childRecipes:{groups:t,hints:eo.hints.get(e)??[]},replaceRecipes:{groups:n,hints:es.hints.get(e)??[]},optionsEdges:i},eI))}let e_=Object.fromEntries(b.entityTypes.flatMap(e=>{let t=eS.get(e);return t?[[e,t.map(({options:e})=>e)]]:[]}));if(Y.errors.length>J)return;let ev=((e,t)=>{let n;try{let t=e();if(n=h(t).map(e=>e.message),0===n.length)return c(t)}catch(e){n=[e instanceof Error?e.message:String(e)]}t.error(f.ASSISTANT_INVALID_SCHEMA,y.ASSISTANT_INVALID_SCHEMA(n))})(()=>{var e,i;let d;return new a({query:(e=el(ef.has(T)?T:w),i=el(w),d=(e=>{let t={};for(let[n,i,r]of[["navigationState","Active navigation state.",e.navigationStates],["viewState","Active view state.",e.viewStates],["toolState","Active tool state.",e.toolStates]])r.length>1&&(t[n]={type:new o(p),description:$(i,r)});if(0!==Object.keys(t).length)return new s({name:"Navigation",description:"The active UI states of the configurator.",fields:t})})(W),new s({name:"Query",fields:{root:{type:new o(e),description:"The root entity of the configuration."},entities:{type:new o(new r(i)),args:{ids:{type:new r(new o(t))}},description:`Every entity of the configuration, or, when ids are given, those entities in the same order with null for an unknown id. Ids have the form "<Type>-<number>"${et?`, e.g. "${et}-1"`:""}; any other id is an error.`},selectedEntity:{type:new o(i),description:"The selected entity, or the root entity when nothing is selected."},...d?{navigation:{type:new o(d)}}:{}}})),mutation:(({deletableTypes:e,writableTypes:i,childRecipes:a,replaceRecipes:d,objectTypes:l,anyDomainEntity:c})=>{let h,f;if(0!==e.length||0!==i.length||0!==Object.keys(a).length||0!==Object.keys(d).length)return new s({name:"Mutation",fields:Object.assign(0===e.length?{}:{deleteEntity:{type:new o(new r(new o(t))),args:{id:{type:new o(t)}},description:`Deletes the entity with the given id, together with the entities composed into it, and returns the ids of every entity the deletion removed, the given one first. Deletable entity types: ${e.join(", ")}. Nothing can be read from removed entities afterwards, so query what you need from them before deleting.`}},0===(h=V(a)).length?{}:{addChild:{type:new o(new r(new o(c))),args:{parentId:{type:new o(t)},recipeGroupId:{type:new o(p)},recipeKey:{type:new o(p)}},description:`Adds a child to the entity with the id parentId by one recipe of one of its recipe groups. Entity types with recipe groups: ${h.join("; ")}. Read the groupId and the recipe key from "meta { childRecipes }" of the parent. Returns every entity the change added, the recipe's own first and then whatever the configurator added in response, such as a part the new child needs; the list is empty when the configurator removed them again.`}},0===(f=V(d)).length?{}:{replaceEntity:{type:new o(new r(new o(c))),args:{entityId:{type:new o(t)},recipeGroupId:{type:new o(p)},recipeKey:{type:new o(p)}},description:`Replaces the entity with the id entityId by one recipe of one of its replace recipe groups. Entity types with replace recipe groups: ${f.join("; ")}. Read the groupId and the recipe key from "meta { replaceRecipes }" of the entity. The replacement is a new entity with a new id: the id passed in no longer exists afterwards, and the replacement has the field values of its recipe, not those of the entity it replaces. Returns every entity the change added, the replacement first and then whatever the configurator added in response, such as a part the replacement needs; the list is empty when the configurator removed them again.`}},...i.flatMap(e=>{let i=l.get(e.entityType);return i?[(({entityType:e,fields:i},r)=>{let s=Object.fromEntries(i.map(({name:e,type:t,description:n})=>[e,{type:t,description:n}]));return{[`modify${e}`]:{type:r,args:{id:{type:new o(t)},delta:{type:new o(new n({name:`${e}Delta`,fields:s}))}},description:`Changes writable fields of the one ${e} with the given id: ${i.map(({name:e})=>e).join(", ")}. The delta sets only the fields it names and needs at least one; values are in stored units, as the field descriptions say. Returns the entity as it is after the change, including what the configurator adjusted in response, or null when the change caused the entity to be removed.`}}})(e,i)]:[]}))})})({deletableTypes:eg,writableTypes:em,childRecipes:ea,replaceRecipes:ep,objectTypes:er,anyDomainEntity:el(w)}),types:[...ef.values(),...er.values()]})},Y);return void 0===ev?void 0:{sdl:ev,fields:eu,deletableTypes:eg,writableFields:Object.fromEntries(em.map(({entityType:e,fields:t})=>[e,Object.fromEntries(t.map(({name:e,property:t})=>[e,t]))])),childRecipes:ea,replaceRecipes:ep,fieldOptions:e_}};
1
+ import{GraphQLBoolean as e,GraphQLID as t,GraphQLInputObjectType as n,GraphQLInterfaceType as i,GraphQLList as r,GraphQLNonNull as o,GraphQLObjectType as s,GraphQLSchema as a,GraphQLString as p,getNullableType as d,isInputType as l,isNonNullType as c,printSchema as h,validateSchema as f}from"graphql";import{isArray as u}from"@salsita-ai/common/utils";import{DiagnosticCode as y,diagnosticMessages as m}from"../../../../errors/index.js";import{ASSISTANT_HINTS as g}from"../../../raw-spec/index.js";import{createSchemaRefResolver as S,isNullableSchema as T,phxDefinitionRefName as w}from"../../common/index.js";import{GraphQLJSON as N,assistantFieldType as A}from"./assistant-field-type.js";let E="AnyDomainEntity",I="RootEntity",_="RecipeGroup",O="Recipe",v="DryRun",b="DryRunError",$="FieldOption",R={section:"childRecipes",component:"childEntityRecipes",mutation:"addChild"},j={section:"replaceRecipes",component:"replaceEntityRecipes",mutation:"replaceEntity"},M=new Set(["id","type","meta"]),D=new Set(["Query","Mutation","Navigation",_,O,v,b,$,"JSON","String","Int","Float","Boolean","ID"]),C=(e,t)=>{if(!t||0===t.length)return e;let n=`One of: ${t.map(e=>`"${e}"`).join(", ")}.`;return e?/[.!?]$/.test(e)?`${e} ${n}`:`${e}. ${n}`:n},U=new Set(["id","type"]),P="The raw stored value: a string, a message descriptor or a multilingual object.",k=(e,t)=>Object.entries(e).find(([e])=>e===t)?.[1],L=(e,t,{model:n,components:i})=>{let r=new Set([e,...[...n.aliases].filter(([,t])=>t.includes(e)).map(([e])=>e)]);return i.filter(({componentName:e,typeOrAlias:n})=>e===t&&r.has(n)).map(({instanceName:e})=>String(e))},W=(...e)=>g.filter(t=>e.some(e=>e?.includes(t))),F=(e,t,n,i)=>{let{model:r}=n,o=new Map,s=new Map;for(let[a,{groups:p,hints:d}]of Object.entries(e)){let e=r.entityTypes.find(e=>e===a),l=e?[e]:[...r.aliases].find(([e])=>e===a)?.[1];if(!l){i.at(a).error(y.ASSISTANT_UNKNOWN_TYPE,m.ASSISTANT_UNKNOWN_TYPE(t.section,a));continue}for(let[e,r]of(l.forEach(e=>s.set(e,W(s.get(e),d))),Object.entries(p))){let s=i.at(a,"groups",e),p=`${a}.groups.${e}`,d={groupId:r?.as??e,instanceName:e,...r?.description===void 0?{}:{description:r.description},dryRuns:r?.allowDryRuns??!0};for(let i of l){let r=L(i,t.component,n),l=o.get(i)??[],c=l.find(e=>e.group.groupId===d.groupId);r.includes(e)?c?s.error(y.ASSISTANT_DUPLICATE_RECIPE_GROUP,m.ASSISTANT_DUPLICATE_RECIPE_GROUP(t,i,d.groupId,[c.entry,p])):o.set(i,[...l,{group:d,entry:p,entryCtx:s}]):s.error(y.ASSISTANT_UNKNOWN_RECIPE_GROUP,m.ASSISTANT_UNKNOWN_RECIPE_GROUP(t,a,e,i,r))}}}let a=r.entityTypes.flatMap(e=>{let t=o.get(e);return t?[{entityType:e,groups:t}]:[]});return{groups:Object.fromEntries(a.map(({entityType:e,groups:t})=>[e,t.map(({group:e})=>e)])),hints:s,metaEntries:new Map(a.flatMap(({entityType:e,groups:[t]})=>t?[[e,t.entryCtx]]:[]))}},K=e=>`${e}Meta`,G={sameForAllInstances:e=>`All ${e} instances have the same recipe groups and recipes.`,constantDuringSession:()=>"They do not change during the session."},Y={sameForAllInstances:e=>`All ${e} instances have the same options.`,constantDuringSession:()=>"The options do not change during the session."},V=(e,t,n)=>e.map(e=>` ${t[e](n)}`).join(""),x=({section:e},{groups:t,hints:n},{entityType:i,purpose:s,recipeGroup:a})=>{let p=t.filter(({dryRuns:e})=>!e).map(({groupId:e})=>e);return 0===t.length?{}:{[e]:{type:new o(new r(new o(a))),description:`The recipe groups for ${s} this ${i}: ${t.map(({groupId:e})=>e).join(", ")}. A group is listed only while the configurator offers it on the entity.${p.length>0?` Recipes of these groups cannot be dry-run: ${p.join(", ")}.`:""}${V(n,G,i)}`}}},q=({entityType:e,childRecipes:t,replaceRecipes:n,optionsEdges:i},{recipeGroup:a,fieldOption:p})=>new s({name:K(e),fields:Object.assign(x(R,t,{entityType:e,purpose:"adding children to",recipeGroup:a}),x(j,n,{entityType:e,purpose:"replacing",recipeGroup:a}),...i.map(({options:{edgeName:t,field:n,validate:i,dryRuns:s},hints:a})=>({[t]:{type:new r(new o(p)),description:`The values "${n}" of this ${e} can take, as the configurator offers them in the current state of the entity. Null when the configurator does not offer a choice on this entity. ${i?`modify${e} rejects a "${n}" value that is not in the list.`:`modify${e} does not check "${n}" against the list.`}${s?"":" Its options cannot be dry-run."}${V(a,Y,e)}`}})))}),B=e=>Object.entries(e).map(([e,t])=>`${e} (${t.map(({groupId:e})=>e).join(", ")})`),z=e=>`${e.charAt(0).toUpperCase()+e.slice(1)}Result`,H=e=>{let t=(({anyOf:e,oneOf:t})=>[...e??[],...t??[]].filter(e=>"null"!==e.type))(e);return e.$ref||1!==t.length?e:t[0]},Q=(e,t)=>{let{type:n,enumValues:i,localized:r,description:s}=A(e,t),a=d(n),p=r||!l(a)?N:a;return{type:c(n)?new o(p):p,description:r?`${C(s,i)??""} ${P}`.trim():C(s,i)}},X=(e,t)=>{let n=(e=>{let t=H(e),n=t?.type==="array"&&t.items&&!u(t.items)?H(t.items):void 0,i=n??t;if(i?.$ref!==void 0&&"$DomainEntity"===w(i.$ref))return{target:i.references??E,list:void 0!==n}})(e);if(n)return{kind:"edge",...n,nullable:T(e,t),description:e.description};let{type:i,enumValues:r,localized:s,description:a}=A(e,t);return s?{kind:"value",type:c(i)?new o(N):N,description:`${a??""} ${P}`.trim()}:{kind:"value",type:i,description:C(a,r)}},J=(e,t)=>{let{type:n,enumValues:i,localized:r,description:o}=A(e,t);return{type:{kind:"value",type:n},localized:!0===r,description:o,enumValues:i}},Z=(e,t,n,i,r)=>{let o=t.startsWith("$")?((e,t,n,i)=>{let r=((e,t,{expressions:n,entityTypes:i})=>{let r=i.find(t=>t===e);for(let i of void 0===r?[e]:n.entityExpressions.get(r)?.shapeGroups??[]){let e=[...n.expressions.get(i)?.values()??[]].find(e=>e.name===t);if(e?.declaredSchema)return{implementation:e.implementation,declaredSchema:e.declaredSchema}}})(e.name,t,n);if(!r)return void i.error(y.ASSISTANT_UNKNOWN_FIELD,m.ASSISTANT_UNKNOWN_FIELD("entities",e.name,t));let{implementation:o}=r;if("violations"===o.kind)return void i.error(y.ASSISTANT_UNSUPPORTED_EXPRESSION,m.ASSISTANT_UNSUPPORTED_EXPRESSION(e.name,t));if("relation"===o.kind){let[{target:e,cardinality:t}]=o.params;return{type:{kind:"edge",target:e,cardinality:t},localized:!1,description:void 0}}return J(r.declaredSchema,n.resolveRef)})(e,t,i,r):((e,t,{resolveRef:n},i)=>{let r=Object.entries(e.shape.properties).find(([e])=>e===t)?.[1];return r?J(r,n):void i.error(y.ASSISTANT_UNKNOWN_FIELD,m.ASSISTANT_UNKNOWN_FIELD("entities",e.name,t))})(e,t,i,r);if(!o)return;let s=n?.as??(t.startsWith("$")?`_${t.slice(1)}`:t);return M.has(s)?void r.error(y.ASSISTANT_RESERVED_FIELD_NAME,m.ASSISTANT_RESERVED_FIELD_NAME(e.name,t,s)):{name:s,source:t,type:o.type,localized:o.localized,description:C(n?.description??o.description,o.enumValues),describedByManifest:n?.description!==void 0,enumValues:o.enumValues,valueDescription:n?.description??o.description}},ee=(e,t,n,i,r)=>{let o=new Map,s=new Set(n);for(let i of[...t.flat(),...n]){let t=o.get(i.name);t?t.source!==i.source?r.error(y.ASSISTANT_FIELD_NAME_CONFLICT,m.ASSISTANT_FIELD_NAME_CONFLICT(e,i.name,[t.source,i.source])):s.has(i)&&i.describedByManifest&&o.set(i.name,{...t,description:i.description,describedByManifest:!0,valueDescription:i.valueDescription}):o.set(i.name,i)}return[...o.values()].map(e=>{let t=e.enumValues&&i(e.source);return t?{...e,enumValues:t,description:C(e.valueDescription,t)}:e})},et=({target:e,cardinality:t},n)=>{let i=n(e);switch(t){case"1-1":return new o(i);case"0-1":return i;case"0-N":case"1-N":return new o(new r(new o(i)))}},en=(e,n,i)=>{let r={id:{type:new o(t),description:i?`Unique id of the entity, e.g. "${i}-1".`:void 0},type:{type:new o(p),description:i?`Entity type name, e.g. "${i}".`:void 0}};for(let t of e)r[t.name]={type:"edge"===t.type.kind?et(t.type,n):t.type.type,description:t.description};return r},ei=e=>Object.fromEntries(e.map(({name:e,source:t,localized:n})=>[e,n?{source:t,localized:!0}:{source:t}]));export const createAssistantSchema=(c,{model:u,states:g,phxDefinitions:T,expressions:w,components:M,actions:G},Y)=>{let V,x,H,J=Y.errors.length,et=Y.at("entities"),er=S(u.commonTypes,T),eo=u.rootEntityTypes[0]??u.entityTypes[0];for(let e of u.resolvedShapes.keys())D.has(e)&&Y.error(y.ASSISTANT_RESERVED_TYPE_NAME,m.ASSISTANT_RESERVED_TYPE_NAME(e,[...D]));let es=((e,t,n)=>{let i=new Map;for(let r of e){let e=[];for(let[i,o]of Object.entries(r.entries)){let s=Z(r,i,o,t,n.at(r.name,i));s&&e.push(s)}i.set(r.name,e)}return i})(((e,t,n)=>{let i=[];for(let[n,r]of t.resolvedShapes){let t=e[n];t&&i.push({name:n,shape:r,entries:t})}let r=new Set(t.resolvedShapes.keys());for(let t of Object.keys(e))r.has(t)||n.at(t).error(y.ASSISTANT_UNKNOWN_TYPE,m.ASSISTANT_UNKNOWN_TYPE("entities",t));return i})(c.entities??{},u,et),{resolveRef:er,expressions:w,entityTypes:u.entityTypes},et),ea=ee(E,[],es.get(E)??[],()=>void 0,Y),ep=new Map,ed=F(c.childRecipes??{},R,{model:u,components:M},Y.at(R.section)),el=F(c.replaceRecipes??{},j,{model:u,components:M},Y.at(j.section)),ec=ed.groups,eh=el.groups,ef=new Map,eu=e=>{let[t,...n]=[...u.aliases].find(([t])=>t===e)?.[1]??[];return(t&&0===n.length?ep.get(t):void 0)??eg.get(e)??em},ey=e=>ep.get(e)??eu(e),em=new i({name:E,description:"Every domain entity.",fields:()=>en(ea,ey,eo)}),eg=new Map([[E,em]]),eS=new Map([[E,ea]]);for(let e of u.aliases.keys()){if(e===E)continue;let t=ee(e,[ea],es.get(e)??[],()=>void 0,Y);eS.set(e,t),eg.set(e,new i({name:e,interfaces:[em],fields:()=>en(t,ey)}))}let eT={};for(let e of u.entityTypes){let t=[E,...[...u.aliases].filter(([t,n])=>t!==E&&n.includes(e)).map(([e])=>e)],n=[...u.resolvedShapes].find(([t])=>t===e)?.[1],i=ee(e,t.map(e=>eS.get(e)??[]),es.get(e)??[],e=>{let t=Object.entries(n?.properties??{}).find(([t])=>t===e)?.[1];return t&&A(t,er).enumValues},Y);eT[e]=ei(i),ep.set(e,new s({name:e,interfaces:t.flatMap(e=>eg.get(e)??[]),fields:()=>{let t=ef.get(e);return{...en(i,ey),...t?{meta:{type:new o(t),description:"What the configurator offers on this entity besides its fields."}}:{}}}}))}let ew=((e,t,n)=>{let i=new Set;for(let r of Object.keys(e)){let e=t.entityTypes.find(e=>e===r),o=e?[e]:[...t.aliases].find(([e])=>e===r)?.[1];o?o.forEach(e=>i.add(e)):n.at(r).error(y.ASSISTANT_UNKNOWN_TYPE,m.ASSISTANT_UNKNOWN_TYPE("deletable",r))}return t.entityTypes.filter(e=>i.has(e))})(c.deletable??{},u,Y.at("deletable")),eN=((e,t,n)=>{let{model:i}=t,r=((e,t,n)=>{let i=new Map;for(let[r,o]of Object.entries(e)){let e=t.entityTypes.find(e=>e===r),s=e?[e]:[...t.aliases].find(([e])=>e===r)?.[1],a=[...t.resolvedShapes].find(([e])=>e===r)?.[1];if(!s||!a){n.at(r).error(y.ASSISTANT_UNKNOWN_TYPE,m.ASSISTANT_UNKNOWN_TYPE("writable",r));continue}for(let e of Object.keys(o)){let t=n.at(r,e);e.startsWith("$")||U.has(e)?t.error(y.ASSISTANT_NOT_WRITABLE,m.ASSISTANT_NOT_WRITABLE(r,e)):k(a.properties,e)?s.forEach(t=>{let n=i.get(t)??new Map;i.set(t,n.has(e)?n:n.set(e,r))}):t.error(y.ASSISTANT_UNKNOWN_FIELD,m.ASSISTANT_UNKNOWN_FIELD("writable",r,e))}}return i})(e,i,n);return i.entityTypes.flatMap(e=>{let o=[...i.resolvedShapes].find(([t])=>t===e)?.[1],s=[...r.get(e)??[]].flatMap(([i,r])=>{let s=o&&k(o.properties,i);return(s&&((e,{property:t,writableKey:n},i,{resolveRef:r,exposed:o,sources:s},a)=>{let p=Object.entries(k(s,e)??{}),c=o.get(e)?.find(({source:e})=>e===t)?.name??p.find(([,{source:e}])=>e===t)?.[0],h=void 0===c?p.find(([e])=>e===t)?.[1]:void 0;if(h)return void a.at(n,t).error(y.ASSISTANT_FIELD_NAME_CONFLICT,m.ASSISTANT_FIELD_NAME_CONFLICT(e,t,[h.source,t],`${n}.${t}`));let{type:f,enumValues:u,localized:g,description:S}=A(i,r),T=d(f),w=c??t,E=void 0===c?C(S,u):void 0;return g?{name:w,property:t,type:N,description:E?`${E} ${P}`:P}:{name:w,property:t,type:l(T)?T:N,description:E}})(e,{property:i,writableKey:r},s,t,n))??[]});return s.length>0?[{entityType:e,fields:s}]:[]})})(c.writable??{},{model:u,resolveRef:er,exposed:es,sources:eT},Y.at("writable")),eA=((e,t,n,i)=>{let{model:r}=n,o=new Map;for(let[s,a]of Object.entries(e)){let e=r.entityTypes.find(e=>e===s),p=e?[e]:[...r.aliases].find(([e])=>e===s)?.[1]??[];for(let[e,r]of Object.entries(a)){let a=r?.options;if(!a)continue;let d=i.at(s,e,"options"),l=`${s}.${e}`;for(let i of p){let r=t.find(e=>e.entityType===i)?.fields.find(t=>t.property===e);if(!r)continue;let s=a.as??`${r.name}Options`,p=L(i,a.componentType,n),c=o.get(i)??[],h=c.find(e=>e.options.edgeName===s||e.options.field===r.name);if(p.includes(a.instanceName))if(s===R.section||s===j.section)d.error(y.ASSISTANT_RESERVED_OPTIONS_EDGE_NAME,m.ASSISTANT_RESERVED_OPTIONS_EDGE_NAME(l,s,r.name));else if(h)d.error(y.ASSISTANT_DUPLICATE_OPTIONS_EDGE,m.ASSISTANT_DUPLICATE_OPTIONS_EDGE(i,h.options.edgeName===s?{edgeName:s}:{field:r.name},[h.entry,l]));else{let e={field:r.name,edgeName:s,componentType:a.componentType,instanceName:a.instanceName,validate:a.validate??!0,dryRuns:a.allowDryRuns??!0};o.set(i,[...c,{options:e,hints:W(a.hints),entry:l,entryCtx:d}])}else d.error(y.ASSISTANT_UNKNOWN_OPTIONS_COMPONENT,m.ASSISTANT_UNKNOWN_OPTIONS_COMPONENT(l,a,i,p))}}}return o})(c.writable??{},eN,{model:u,components:M},Y.at("writable"));var eE,eI,e_,eO=[ed.metaEntries,el.metaEntries,new Map([...eA].flatMap(([e,[t]])=>t?[[e,t.entryCtx]]:[]))];for(let e of u.entityTypes){let t=eO.flatMap(t=>t.get(e)??[])[0],n=K(e);t&&[...u.resolvedShapes.keys()].some(e=>e===n)&&t.error(y.ASSISTANT_META_TYPE_NAME_CONFLICT,m.ASSISTANT_META_TYPE_NAME_CONFLICT(e,n))}let ev=(eE=()=>eu(eg.has(I)?I:E),eI=()=>eu(E),V=new s({name:b,description:"One reason a change cannot be made right now.",fields:{type:{type:new o(p),description:'Kind of the problem, or "uncaught".'},entityId:{type:t,description:"The entity the problem is on, when it is on one."},message:{type:p,description:"The problem as the configurator words it for the user."}}}),new s({name:v,description:"What using a recipe or writing an option would do, tried out on the configuration the query reads. Nothing is changed.",fields:()=>({ok:{type:new o(e),description:"Whether the change would succeed on the configuration the query reads."},root:{type:eE(),description:"The root entity of the configuration as it would be after the change; null when ok is false. Everything selected under it reads that configuration."},changedEntities:{type:new o(new r(new o(eI()))),description:"Every entity the change would add or modify, read from that configuration; empty when ok is false. An entity whose id the configuration does not have yet would be added, and its id may differ in the real mutation. An entity that is written to is listed even when it gets the values it already has."},deletedEntityIds:{type:new o(new r(new o(t))),description:"The ids of the entities the change would remove; empty when ok is false."},sideEffects:{type:new o(new r(new o(p))),description:"What the configurator would do beyond the change itself, such as parts it would add or replace, in its own words; empty when ok is false."},errors:{type:new o(new r(new o(V))),description:"Why the change cannot be made; empty when ok is true."}})})),eb={recipeGroup:(e_=[...Object.keys(ec).length>0?[R.mutation]:[],...Object.keys(eh).length>0?[j.mutation]:[]],x=e_.join(" or "),H=new s({name:O,description:"One recipe of a recipe group: something that can be added to the entity, or something the entity can be replaced with, depending on the edge the group is listed under.",fields:{key:{type:new o(p),description:`Identifies the recipe within its group; pass it to ${x} as recipeKey. Keys can change with the configuration and with the language, so read them from the listing rather than remembering them.`},name:{type:new o(p),description:"Name shown to the user."},description:{type:p},imageUrl:{type:p,description:"Thumbnail shown to the user."},dryRun:{type:ev,description:"Tries the recipe out. Each selected dry-run costs a full recalculation, so select it only on the recipes you are deciding between. Null for a group whose recipes cannot be dry-run."}}}),new s({name:_,description:"A group of recipes of an entity, as the configurator offers them to the user.",fields:{groupId:{type:new o(p),description:`Identifies the group on its entity; pass it to ${x} as recipeGroupId.`},name:{type:new o(p),description:"Name shown to the user."},description:{type:p},recipes:{type:new o(new r(new o(H)))}}})),fieldOption:new s({name:$,description:"One choice of a field, as the configurator offers it to the user.",fields:{value:{type:new o(N),description:"The value to write to the field."},name:{type:p,description:"Name shown to the user."},description:{type:p},imageUrl:{type:p,description:"Image shown to the user."},color:{type:p,description:'Color shown to the user, as "#rrggbb".'},dryRun:{type:ev,description:"Tries writing the value to the field. Each selected dry-run costs a full recalculation, so select it only on the options you are deciding between. Null for a field whose options cannot be dry-run."}}})};for(let e of u.entityTypes){let t=k(ec,e)??[],n=k(eh,e)??[],i=eA.get(e)??[];(t.length>0||n.length>0||i.length>0)&&ef.set(e,q({entityType:e,childRecipes:{groups:t,hints:ed.hints.get(e)??[]},replaceRecipes:{groups:n,hints:el.hints.get(e)??[]},optionsEdges:i},eb))}let e$=Object.fromEntries(u.entityTypes.flatMap(e=>{let t=eA.get(e);return t?[[e,t.map(({options:e})=>e)]]:[]})),eR=((e,{actions:t,resolveRef:n,builtInMutations:i,typeNames:r},o)=>{let s=[],a=[...t.values()].map(e=>String(e.name));for(let[p,d]of Object.entries(e)){let e=o.at(p),l=[...t.values()].find(e=>e.name===p);if(!l){e.error(y.ASSISTANT_UNKNOWN_ACTION,m.ASSISTANT_UNKNOWN_ACTION(p,a));continue}let c=d?.as??p,h=s.find(e=>e.fieldName===c),f=z(c);if(i.has(c)||h){e.error(y.ASSISTANT_MUTATION_NAME_CONFLICT,m.ASSISTANT_MUTATION_NAME_CONFLICT(p,c,h?`"assistant.actions.${h.actionName}" already exposes`:"the built-in mutation of that name already takes"));continue}if(r.has(f)||s.some(e=>z(e.fieldName)===f)){e.error(y.ASSISTANT_ACTION_RESULT_TYPE_CONFLICT,m.ASSISTANT_ACTION_RESULT_TYPE_CONFLICT(p,c,f));continue}s.push({fieldName:c,actionName:p,description:d?.description??l.description,args:Object.fromEntries(Object.entries(l.params).map(([e,{schema:t}])=>[e,Q(t,n)])),result:Object.fromEntries(Object.entries(l.result).map(([e,t])=>[e,X(t,n)]))})}return s})(c.actions??{},{actions:G,resolveRef:er,builtInMutations:new Set(["deleteEntity",R.mutation,j.mutation,...eN.map(({entityType:e})=>`modify${e}`)]),typeNames:new Set([...D,...u.resolvedShapes.keys(),...ef.keys()])},Y.at("actions"));if(Y.errors.length>J)return;let ej=((e,t)=>{let n;try{let t=e();if(n=f(t).map(e=>e.message),0===n.length)return h(t)}catch(e){n=[e instanceof Error?e.message:String(e)]}t.error(y.ASSISTANT_INVALID_SCHEMA,m.ASSISTANT_INVALID_SCHEMA(n))})(()=>{var e,i;let d;return new a({query:(e=eu(eg.has(I)?I:E),i=eu(E),d=(e=>{let t={};for(let[n,i,r]of[["navigationState","Active navigation state.",e.navigationStates],["viewState","Active view state.",e.viewStates],["toolState","Active tool state.",e.toolStates]])r.length>1&&(t[n]={type:new o(p),description:C(i,r)});if(0!==Object.keys(t).length)return new s({name:"Navigation",description:"The active UI states of the configurator.",fields:t})})(g),new s({name:"Query",fields:{root:{type:new o(e),description:"The root entity of the configuration."},entities:{type:new o(new r(i)),args:{ids:{type:new r(new o(t))}},description:`Every entity of the configuration, or, when ids are given, those entities in the same order with null for an unknown id. Ids have the form "<Type>-<number>"${eo?`, e.g. "${eo}-1"`:""}; any other id is an error.`},selectedEntity:{type:new o(i),description:"The selected entity, or the root entity when nothing is selected."},...d?{navigation:{type:new o(d)}}:{}}})),mutation:(({deletableTypes:e,writableTypes:i,childRecipes:a,replaceRecipes:d,actionFields:l,objectTypes:c,anyDomainEntity:h,targetOutputType:f})=>{let u,y;if(0!==e.length||0!==i.length||0!==Object.keys(a).length||0!==Object.keys(d).length||0!==l.length)return new s({name:"Mutation",fields:Object.assign(0===e.length?{}:{deleteEntity:{type:new o(new r(new o(t))),args:{id:{type:new o(t)}},description:`Deletes the entity with the given id, together with the entities composed into it, and returns the ids of every entity the deletion removed, the given one first. Deletable entity types: ${e.join(", ")}. Nothing can be read from removed entities afterwards, so query what you need from them before deleting.`}},0===(u=B(a)).length?{}:{addChild:{type:new o(new r(new o(h))),args:{parentId:{type:new o(t)},recipeGroupId:{type:new o(p)},recipeKey:{type:new o(p)}},description:`Adds a child to the entity with the id parentId by one recipe of one of its recipe groups. Entity types with recipe groups: ${u.join("; ")}. Read the groupId and the recipe key from "meta { childRecipes }" of the parent. Returns every entity the change added, the recipe's own first and then whatever the configurator added in response, such as a part the new child needs; the list is empty when the configurator removed them again.`}},0===(y=B(d)).length?{}:{replaceEntity:{type:new o(new r(new o(h))),args:{entityId:{type:new o(t)},recipeGroupId:{type:new o(p)},recipeKey:{type:new o(p)}},description:`Replaces the entity with the id entityId by one recipe of one of its replace recipe groups. Entity types with replace recipe groups: ${y.join("; ")}. Read the groupId and the recipe key from "meta { replaceRecipes }" of the entity. The replacement is a new entity with a new id: the id passed in no longer exists afterwards, and the replacement has the field values of its recipe, not those of the entity it replaces. Returns every entity the change added, the replacement first and then whatever the configurator added in response, such as a part the replacement needs; the list is empty when the configurator removed them again.`}},...i.flatMap(e=>{let i=c.get(e.entityType);return i?[(({entityType:e,fields:i},r)=>{let s=Object.fromEntries(i.map(({name:e,type:t,description:n})=>[e,{type:t,description:n}]));return{[`modify${e}`]:{type:r,args:{id:{type:new o(t)},delta:{type:new o(new n({name:`${e}Delta`,fields:s}))}},description:`Changes writable fields of the one ${e} with the given id: ${i.map(({name:e})=>e).join(", ")}. The delta sets only the fields it names and needs at least one; values are in stored units, as the field descriptions say. Returns the entity as it is after the change, including what the configurator adjusted in response, or null when the change caused the entity to be removed.`}}})(e,i)]:[]}),Object.fromEntries(l.map(e=>[e.fieldName,{type:new o(new s({name:z(e.fieldName),description:`What ${e.fieldName} returns, read from the configuration after the change.`,fields:()=>Object.fromEntries(Object.entries(e.result).map(([e,t])=>[e,{type:"edge"===t.kind?(({target:e,list:t,nullable:n},i)=>{let s=t?new r(new o(i(e))):i(e);return n?s:new o(s)})(t,f):t.type,description:t.description}]))})),args:e.args,description:e.description}])))})})({deletableTypes:ew,writableTypes:eN,childRecipes:ec,replaceRecipes:eh,actionFields:eR,objectTypes:ep,anyDomainEntity:eu(E),targetOutputType:ey}),types:[...eg.values(),...ep.values()]})},Y);return void 0===ej?void 0:{sdl:ej,fields:eT,deletableTypes:ew,writableFields:Object.fromEntries(eN.map(({entityType:e,fields:t})=>[e,Object.fromEntries(t.map(({name:e,property:t})=>[e,t]))])),childRecipes:ec,replaceRecipes:eh,fieldOptions:e$,actions:Object.fromEntries(eR.map(({fieldName:e,actionName:t})=>[e,t]))}};
@@ -2,7 +2,7 @@ import { Branded } from '@salsita-ai/common/utils';
2
2
  import type { ParseContext } from '../../../../core/index.js';
3
3
  import type { MergedAssistant } from '../../../merge/index.js';
4
4
  import type { EntityTypeName } from '../../model/index.js';
5
- import { type AssistantFieldOptionsByType, type AssistantFieldSources, type AssistantRecipeGroups, type AssistantSchemaInput, type AssistantWritableFields } from './assistant-schema.js';
5
+ import { type AssistantActions, type AssistantFieldOptionsByType, type AssistantFieldSources, type AssistantRecipeGroups, type AssistantSchemaInput, type AssistantWritableFields } from './assistant-schema.js';
6
6
  /**
7
7
  * The experimental assistant manifest with its GraphQL SDL, prepared once at parse time so the
8
8
  * `.phoenix` codegen, the in-browser hot-reload runtime and deployment registration all carry the
@@ -10,8 +10,9 @@ import { type AssistantFieldOptionsByType, type AssistantFieldSources, type Assi
10
10
  * expressions the runtime resolvers read; `deletableTypes` lists the entity types `deleteEntity`
11
11
  * may delete; `writableFields` maps the delta fields of each `modify<Type>` mutation to the stored
12
12
  * properties they write; `childRecipes` and `replaceRecipes` list the recipe groups each entity type exposes; `fieldOptions` lists
13
- * the options edges of each entity type's writable fields. `null` when the application declares no
14
- * `assistant` section or it is invalid.
13
+ * the options edges of each entity type's writable fields; `actions` maps each custom `Mutation`
14
+ * field to the declared action it runs. `null` when the application declares no `assistant`
15
+ * section or it is invalid.
15
16
  */
16
17
  export type AssistantSpec = Branded<'AssistantSpec', {
17
18
  readonly sdl: string;
@@ -21,5 +22,6 @@ export type AssistantSpec = Branded<'AssistantSpec', {
21
22
  readonly childRecipes: AssistantRecipeGroups;
22
23
  readonly replaceRecipes: AssistantRecipeGroups;
23
24
  readonly fieldOptions: AssistantFieldOptionsByType;
25
+ readonly actions: AssistantActions;
24
26
  }> | null;
25
27
  export declare const AssistantSpec: (merged: MergedAssistant, input: AssistantSchemaInput, ctx: ParseContext) => AssistantSpec;
@@ -1,3 +1,4 @@
1
+ export * from './actions.js';
1
2
  export * from './app-metadata.js';
2
3
  export * from './assistant/index.js';
3
4
  export * from './application-version.js';
@@ -1 +1 @@
1
- export*from"./app-metadata.js";export*from"./assistant/index.js";export*from"./application-version.js";export*from"./bundle-size-budgets.js";export*from"./data-spec/index.js";export*from"./env.js";export*from"./infra.js";export*from"./state-value.js";export*from"./theme.js";export*from"./transformers.js";export*from"./ui.js";
1
+ export*from"./actions.js";export*from"./app-metadata.js";export*from"./assistant/index.js";export*from"./application-version.js";export*from"./bundle-size-budgets.js";export*from"./data-spec/index.js";export*from"./env.js";export*from"./infra.js";export*from"./state-value.js";export*from"./theme.js";export*from"./transformers.js";export*from"./ui.js";
@@ -618,7 +618,7 @@ export interface paths {
618
618
  }
619
619
  readonly get?: never
620
620
  readonly put?: never
621
- /** @description Create a build for `phoenix deploy` and a presigned POST that uploads its source archive. S3 refuses uploads over `maxBytes`. */
621
+ /** @description Create a build for `phoenix deploy` and a presigned POST that uploads its source archive. S3 refuses uploads over `maxBytes`. A project whose domain model changed under an already deployed model version is refused before the upload. */
622
622
  readonly post: operations['createUploadBuild']
623
623
  readonly delete?: never
624
624
  readonly options?: never
@@ -4433,6 +4433,10 @@ export interface operations {
4433
4433
  readonly requestBody: {
4434
4434
  readonly content: {
4435
4435
  readonly 'application/json': {
4436
+ /** @description The admin model schema of the project, checked like `configurationSchema`. */
4437
+ readonly adminConfigurationSchema?: components['schemas']['ConfigurationSchema']
4438
+ /** @description The configurator model schema of the project. When it differs from the schema deployed under the same model version, no build is created and the response is 400. */
4439
+ readonly configurationSchema?: components['schemas']['ConfigurationSchema']
4436
4440
  /** @description Version of the CLI that uploads the sources. */
4437
4441
  readonly sdkVersion: string
4438
4442
  /** @description Size of the source archive the CLI will upload. */
@@ -1,5 +1,5 @@
1
1
  import type { $DomainEntity, DomainEntity } from '#package/domain';
2
- import type { AnimationSystem, CameraControlSystem, CameraSystem, CanvasSystem, DragAndDropSystem, ExportSystem, GraphQLSystem, ImageRenderingSystem, LayoutSystem, SketchingSystem } from './systems/index.js';
2
+ import type { AnimationSystem, AssistantSystem, CameraControlSystem, CameraSystem, CanvasSystem, DragAndDropSystem, ExportSystem, GraphQLSystem, ImageRenderingSystem, LayoutSystem, SketchingSystem } from './systems/index.js';
3
3
  /**
4
4
  * The configurator systems of one slot. Every system reaches the domain through runtime selectors,
5
5
  * so its own entity types are all it needs and the erased aliases are what the runtime hands around.
@@ -18,6 +18,7 @@ export interface ConfiguratorExtensionSystems<ENTITIES extends DomainEntity<stri
18
18
  readonly exportSystem: ExportSystem<ENTITIES, ALLOWED_ROOT_ENTITIES, $MAP, NAVIGATION_STATES, VIEW_STATES, TOOL_STATES, EXPORT_STATES>;
19
19
  readonly cameraSystem: CameraSystem<ENTITIES>;
20
20
  readonly graphQLSystem: GraphQLSystem;
21
+ readonly assistantSystem: AssistantSystem;
21
22
  }
22
23
  /** The erased shape the runtime slice constructs; consumers that know their entities narrow on read. */
23
24
  export type AnyConfiguratorExtensionSystems = ConfiguratorExtensionSystems<any, any, any, any, any, any, any>;
@@ -1 +1 @@
1
- let e=["canvasSystem","cameraControlSystem","animationSystem","dragAndDropSystem","imageRenderingSystem","layoutSystem","sketchingSystem","exportSystem","cameraSystem","graphQLSystem"];export const isConfiguratorExtensionSystems=t=>e.every(e=>e in t);
1
+ let e=["canvasSystem","cameraControlSystem","animationSystem","dragAndDropSystem","imageRenderingSystem","layoutSystem","sketchingSystem","exportSystem","cameraSystem","graphQLSystem","assistantSystem"];export const isConfiguratorExtensionSystems=t=>e.every(e=>e in t);