@cratis/scene.engine 4.2.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/dist/cjs/editing/DescriptorCatalog.js +42 -0
  2. package/dist/cjs/editing/DescriptorCatalog.js.map +1 -0
  3. package/dist/cjs/editing/EditSession.js +44 -0
  4. package/dist/cjs/editing/EditSession.js.map +1 -0
  5. package/dist/cjs/editing/applyEdit.js +83 -0
  6. package/dist/cjs/editing/applyEdit.js.map +1 -0
  7. package/dist/cjs/editing/applyEffectiveConfiguration.js +62 -0
  8. package/dist/cjs/editing/applyEffectiveConfiguration.js.map +1 -0
  9. package/dist/cjs/editing/collectComponents.js +34 -0
  10. package/dist/cjs/editing/collectComponents.js.map +1 -0
  11. package/dist/cjs/editing/describeNode.js +63 -0
  12. package/dist/cjs/editing/describeNode.js.map +1 -0
  13. package/dist/cjs/editing/diagnostics.js +36 -0
  14. package/dist/cjs/editing/diagnostics.js.map +1 -0
  15. package/dist/cjs/editing/documentPaths.js +45 -0
  16. package/dist/cjs/editing/documentPaths.js.map +1 -0
  17. package/dist/cjs/editing/exposureEdits.js +106 -0
  18. package/dist/cjs/editing/exposureEdits.js.map +1 -0
  19. package/dist/cjs/editing/exposureGrants.js +115 -0
  20. package/dist/cjs/editing/exposureGrants.js.map +1 -0
  21. package/dist/cjs/editing/indexDocument.js +400 -0
  22. package/dist/cjs/editing/indexDocument.js.map +1 -0
  23. package/dist/cjs/editing/inspect.js +127 -0
  24. package/dist/cjs/editing/inspect.js.map +1 -0
  25. package/dist/cjs/editing/instanceEdits.js +255 -0
  26. package/dist/cjs/editing/instanceEdits.js.map +1 -0
  27. package/dist/cjs/editing/layoutTypeDescriptors.js +425 -0
  28. package/dist/cjs/editing/layoutTypeDescriptors.js.map +1 -0
  29. package/dist/cjs/editing/layoutTypeEdits.js +141 -0
  30. package/dist/cjs/editing/layoutTypeEdits.js.map +1 -0
  31. package/dist/cjs/editing/nodeIds.js +115 -0
  32. package/dist/cjs/editing/nodeIds.js.map +1 -0
  33. package/dist/cjs/editing/pathAccess.js +53 -0
  34. package/dist/cjs/editing/pathAccess.js.map +1 -0
  35. package/dist/cjs/editing/propertyEdits.js +118 -0
  36. package/dist/cjs/editing/propertyEdits.js.map +1 -0
  37. package/dist/cjs/editing/resolveEffectiveConfiguration.js +200 -0
  38. package/dist/cjs/editing/resolveEffectiveConfiguration.js.map +1 -0
  39. package/dist/cjs/editing/resolveTemplateChain.js +100 -0
  40. package/dist/cjs/editing/resolveTemplateChain.js.map +1 -0
  41. package/dist/cjs/editing/structureEdits.js +231 -0
  42. package/dist/cjs/editing/structureEdits.js.map +1 -0
  43. package/dist/cjs/editing/validateBinding.js +111 -0
  44. package/dist/cjs/editing/validateBinding.js.map +1 -0
  45. package/dist/cjs/editing/validateValue.js +114 -0
  46. package/dist/cjs/editing/validateValue.js.map +1 -0
  47. package/dist/cjs/index.js +51 -0
  48. package/dist/cjs/index.js.map +1 -1
  49. package/dist/esm/editing/DescriptorCatalog.d.ts +9 -0
  50. package/dist/esm/editing/DescriptorCatalog.d.ts.map +1 -0
  51. package/dist/esm/editing/DescriptorCatalog.js +38 -0
  52. package/dist/esm/editing/DescriptorCatalog.js.map +1 -0
  53. package/dist/esm/editing/DocumentIndex.d.ts +31 -0
  54. package/dist/esm/editing/DocumentIndex.d.ts.map +1 -0
  55. package/dist/esm/editing/DocumentIndex.js +2 -0
  56. package/dist/esm/editing/DocumentIndex.js.map +1 -0
  57. package/dist/esm/editing/EditOutcome.d.ts +7 -0
  58. package/dist/esm/editing/EditOutcome.d.ts.map +1 -0
  59. package/dist/esm/editing/EditOutcome.js +2 -0
  60. package/dist/esm/editing/EditOutcome.js.map +1 -0
  61. package/dist/esm/editing/EditSession.d.ts +20 -0
  62. package/dist/esm/editing/EditSession.d.ts.map +1 -0
  63. package/dist/esm/editing/EditSession.js +42 -0
  64. package/dist/esm/editing/EditSession.js.map +1 -0
  65. package/dist/esm/editing/EditingContext.d.ts +8 -0
  66. package/dist/esm/editing/EditingContext.d.ts.map +1 -0
  67. package/dist/esm/editing/EditingContext.js +2 -0
  68. package/dist/esm/editing/EditingContext.js.map +1 -0
  69. package/dist/esm/editing/TemplateChain.d.ts +13 -0
  70. package/dist/esm/editing/TemplateChain.d.ts.map +1 -0
  71. package/dist/esm/editing/TemplateChain.js +2 -0
  72. package/dist/esm/editing/TemplateChain.js.map +1 -0
  73. package/dist/esm/editing/applyEdit.d.ts +5 -0
  74. package/dist/esm/editing/applyEdit.d.ts.map +1 -0
  75. package/dist/esm/editing/applyEdit.js +81 -0
  76. package/dist/esm/editing/applyEdit.js.map +1 -0
  77. package/dist/esm/editing/applyEffectiveConfiguration.d.ts +3 -0
  78. package/dist/esm/editing/applyEffectiveConfiguration.d.ts.map +1 -0
  79. package/dist/esm/editing/applyEffectiveConfiguration.js +60 -0
  80. package/dist/esm/editing/applyEffectiveConfiguration.js.map +1 -0
  81. package/dist/esm/editing/collectComponents.d.ts +3 -0
  82. package/dist/esm/editing/collectComponents.d.ts.map +1 -0
  83. package/dist/esm/editing/collectComponents.js +32 -0
  84. package/dist/esm/editing/collectComponents.js.map +1 -0
  85. package/dist/esm/editing/describeNode.d.ts +11 -0
  86. package/dist/esm/editing/describeNode.d.ts.map +1 -0
  87. package/dist/esm/editing/describeNode.js +60 -0
  88. package/dist/esm/editing/describeNode.js.map +1 -0
  89. package/dist/esm/editing/diagnostics.d.ts +6 -0
  90. package/dist/esm/editing/diagnostics.d.ts.map +1 -0
  91. package/dist/esm/editing/diagnostics.js +32 -0
  92. package/dist/esm/editing/diagnostics.js.map +1 -0
  93. package/dist/esm/editing/documentPaths.d.ts +8 -0
  94. package/dist/esm/editing/documentPaths.d.ts.map +1 -0
  95. package/dist/esm/editing/documentPaths.js +41 -0
  96. package/dist/esm/editing/documentPaths.js.map +1 -0
  97. package/dist/esm/editing/exposureEdits.d.ts +5 -0
  98. package/dist/esm/editing/exposureEdits.d.ts.map +1 -0
  99. package/dist/esm/editing/exposureEdits.js +103 -0
  100. package/dist/esm/editing/exposureEdits.js.map +1 -0
  101. package/dist/esm/editing/exposureGrants.d.ts +21 -0
  102. package/dist/esm/editing/exposureGrants.d.ts.map +1 -0
  103. package/dist/esm/editing/exposureGrants.js +112 -0
  104. package/dist/esm/editing/exposureGrants.js.map +1 -0
  105. package/dist/esm/editing/given/a_layout_document.d.ts +14 -0
  106. package/dist/esm/editing/given/a_layout_document.d.ts.map +1 -0
  107. package/dist/esm/editing/given/a_layout_document.js +67 -0
  108. package/dist/esm/editing/given/a_layout_document.js.map +1 -0
  109. package/dist/esm/editing/given/a_scene_document.d.ts +20 -0
  110. package/dist/esm/editing/given/a_scene_document.d.ts.map +1 -0
  111. package/dist/esm/editing/given/a_scene_document.js +110 -0
  112. package/dist/esm/editing/given/a_scene_document.js.map +1 -0
  113. package/dist/esm/editing/index.d.ts +19 -0
  114. package/dist/esm/editing/index.d.ts.map +1 -0
  115. package/dist/esm/editing/index.js +19 -0
  116. package/dist/esm/editing/index.js.map +1 -0
  117. package/dist/esm/editing/indexDocument.d.ts +6 -0
  118. package/dist/esm/editing/indexDocument.d.ts.map +1 -0
  119. package/dist/esm/editing/indexDocument.js +396 -0
  120. package/dist/esm/editing/indexDocument.js.map +1 -0
  121. package/dist/esm/editing/inspect.d.ts +8 -0
  122. package/dist/esm/editing/inspect.d.ts.map +1 -0
  123. package/dist/esm/editing/inspect.js +124 -0
  124. package/dist/esm/editing/inspect.js.map +1 -0
  125. package/dist/esm/editing/instanceEdits.d.ts +9 -0
  126. package/dist/esm/editing/instanceEdits.d.ts.map +1 -0
  127. package/dist/esm/editing/instanceEdits.js +248 -0
  128. package/dist/esm/editing/instanceEdits.js.map +1 -0
  129. package/dist/esm/editing/layoutTypeDescriptors.d.ts +5 -0
  130. package/dist/esm/editing/layoutTypeDescriptors.d.ts.map +1 -0
  131. package/dist/esm/editing/layoutTypeDescriptors.js +421 -0
  132. package/dist/esm/editing/layoutTypeDescriptors.js.map +1 -0
  133. package/dist/esm/editing/layoutTypeEdits.d.ts +4 -0
  134. package/dist/esm/editing/layoutTypeEdits.d.ts.map +1 -0
  135. package/dist/esm/editing/layoutTypeEdits.js +139 -0
  136. package/dist/esm/editing/layoutTypeEdits.js.map +1 -0
  137. package/dist/esm/editing/nodeIds.d.ts +14 -0
  138. package/dist/esm/editing/nodeIds.d.ts.map +1 -0
  139. package/dist/esm/editing/nodeIds.js +102 -0
  140. package/dist/esm/editing/nodeIds.js.map +1 -0
  141. package/dist/esm/editing/pathAccess.d.ts +7 -0
  142. package/dist/esm/editing/pathAccess.d.ts.map +1 -0
  143. package/dist/esm/editing/pathAccess.js +48 -0
  144. package/dist/esm/editing/pathAccess.js.map +1 -0
  145. package/dist/esm/editing/propertyEdits.d.ts +15 -0
  146. package/dist/esm/editing/propertyEdits.d.ts.map +1 -0
  147. package/dist/esm/editing/propertyEdits.js +113 -0
  148. package/dist/esm/editing/propertyEdits.js.map +1 -0
  149. package/dist/esm/editing/resolveEffectiveConfiguration.d.ts +5 -0
  150. package/dist/esm/editing/resolveEffectiveConfiguration.d.ts.map +1 -0
  151. package/dist/esm/editing/resolveEffectiveConfiguration.js +198 -0
  152. package/dist/esm/editing/resolveEffectiveConfiguration.js.map +1 -0
  153. package/dist/esm/editing/resolveTemplateChain.d.ts +4 -0
  154. package/dist/esm/editing/resolveTemplateChain.d.ts.map +1 -0
  155. package/dist/esm/editing/resolveTemplateChain.js +98 -0
  156. package/dist/esm/editing/resolveTemplateChain.js.map +1 -0
  157. package/dist/esm/editing/structureEdits.d.ts +6 -0
  158. package/dist/esm/editing/structureEdits.d.ts.map +1 -0
  159. package/dist/esm/editing/structureEdits.js +227 -0
  160. package/dist/esm/editing/structureEdits.js.map +1 -0
  161. package/dist/esm/editing/validateBinding.d.ts +7 -0
  162. package/dist/esm/editing/validateBinding.d.ts.map +1 -0
  163. package/dist/esm/editing/validateBinding.js +108 -0
  164. package/dist/esm/editing/validateBinding.js.map +1 -0
  165. package/dist/esm/editing/validateValue.d.ts +8 -0
  166. package/dist/esm/editing/validateValue.d.ts.map +1 -0
  167. package/dist/esm/editing/validateValue.js +111 -0
  168. package/dist/esm/editing/validateValue.js.map +1 -0
  169. package/dist/esm/index.d.ts +1 -0
  170. package/dist/esm/index.d.ts.map +1 -1
  171. package/dist/esm/index.js +14 -0
  172. package/dist/esm/index.js.map +1 -1
  173. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  174. package/editing/DescriptorCatalog.ts +51 -0
  175. package/editing/DocumentIndex.ts +73 -0
  176. package/editing/EditOutcome.ts +21 -0
  177. package/editing/EditSession.ts +50 -0
  178. package/editing/EditingContext.ts +23 -0
  179. package/editing/TemplateChain.ts +36 -0
  180. package/editing/applyEdit.ts +69 -0
  181. package/editing/applyEffectiveConfiguration.ts +53 -0
  182. package/editing/collectComponents.ts +36 -0
  183. package/editing/describeNode.ts +61 -0
  184. package/editing/diagnostics.ts +30 -0
  185. package/editing/documentPaths.ts +40 -0
  186. package/editing/exposureEdits.ts +119 -0
  187. package/editing/exposureGrants.ts +159 -0
  188. package/editing/given/a_layout_document.ts +88 -0
  189. package/editing/given/a_scene_document.ts +135 -0
  190. package/editing/index.ts +21 -0
  191. package/editing/indexDocument.ts +237 -0
  192. package/editing/inspect.ts +142 -0
  193. package/editing/instanceEdits.ts +243 -0
  194. package/editing/layoutTypeDescriptors.ts +191 -0
  195. package/editing/layoutTypeEdits.ts +135 -0
  196. package/editing/nodeIds.ts +111 -0
  197. package/editing/pathAccess.ts +81 -0
  198. package/editing/propertyEdits.ts +119 -0
  199. package/editing/resolveEffectiveConfiguration.ts +202 -0
  200. package/editing/resolveTemplateChain.ts +109 -0
  201. package/editing/structureEdits.ts +234 -0
  202. package/editing/validateBinding.ts +103 -0
  203. package/editing/validateValue.ts +130 -0
  204. package/index.ts +1 -0
  205. package/package.json +5 -4
  206. package/dist/esm/for_aggregateContributions/when_aggregating_by_contribution_point.d.ts +0 -2
  207. package/dist/esm/for_aggregateContributions/when_aggregating_by_contribution_point.d.ts.map +0 -1
  208. package/dist/esm/for_aggregateContributions/when_aggregating_by_contribution_point.js +0 -23
  209. package/dist/esm/for_aggregateContributions/when_aggregating_by_contribution_point.js.map +0 -1
  210. package/dist/esm/for_buildStarterProfile/when_checking_against_the_shared_fixture_corpus.d.ts +0 -2
  211. package/dist/esm/for_buildStarterProfile/when_checking_against_the_shared_fixture_corpus.d.ts.map +0 -1
  212. package/dist/esm/for_buildStarterProfile/when_checking_against_the_shared_fixture_corpus.js +0 -16
  213. package/dist/esm/for_buildStarterProfile/when_checking_against_the_shared_fixture_corpus.js.map +0 -1
  214. package/dist/esm/for_computeSizeClass/when_computing_the_size_class.d.ts +0 -2
  215. package/dist/esm/for_computeSizeClass/when_computing_the_size_class.d.ts.map +0 -1
  216. package/dist/esm/for_computeSizeClass/when_computing_the_size_class.js +0 -17
  217. package/dist/esm/for_computeSizeClass/when_computing_the_size_class.js.map +0 -1
  218. package/dist/esm/for_evaluateFlowArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts +0 -2
  219. package/dist/esm/for_evaluateFlowArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts.map +0 -1
  220. package/dist/esm/for_evaluateFlowArrangement/when_evaluating_against_the_shared_fixture_corpus.js +0 -43
  221. package/dist/esm/for_evaluateFlowArrangement/when_evaluating_against_the_shared_fixture_corpus.js.map +0 -1
  222. package/dist/esm/for_evaluateFreeformArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts +0 -2
  223. package/dist/esm/for_evaluateFreeformArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts.map +0 -1
  224. package/dist/esm/for_evaluateFreeformArrangement/when_evaluating_against_the_shared_fixture_corpus.js +0 -46
  225. package/dist/esm/for_evaluateFreeformArrangement/when_evaluating_against_the_shared_fixture_corpus.js.map +0 -1
  226. package/dist/esm/for_evaluateFreeformSlotArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts +0 -2
  227. package/dist/esm/for_evaluateFreeformSlotArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts.map +0 -1
  228. package/dist/esm/for_evaluateFreeformSlotArrangement/when_evaluating_against_the_shared_fixture_corpus.js +0 -25
  229. package/dist/esm/for_evaluateFreeformSlotArrangement/when_evaluating_against_the_shared_fixture_corpus.js.map +0 -1
  230. package/dist/esm/for_incompatibleStarterThemes/when_checking_against_the_shared_fixture_corpus.d.ts +0 -2
  231. package/dist/esm/for_incompatibleStarterThemes/when_checking_against_the_shared_fixture_corpus.d.ts.map +0 -1
  232. package/dist/esm/for_incompatibleStarterThemes/when_checking_against_the_shared_fixture_corpus.js +0 -17
  233. package/dist/esm/for_incompatibleStarterThemes/when_checking_against_the_shared_fixture_corpus.js.map +0 -1
  234. package/dist/esm/for_packageCatalog/when_querying_against_the_shared_fixture_corpus.d.ts +0 -2
  235. package/dist/esm/for_packageCatalog/when_querying_against_the_shared_fixture_corpus.d.ts.map +0 -1
  236. package/dist/esm/for_packageCatalog/when_querying_against_the_shared_fixture_corpus.js +0 -22
  237. package/dist/esm/for_packageCatalog/when_querying_against_the_shared_fixture_corpus.js.map +0 -1
  238. package/dist/esm/for_packageDependencies/fixtures.d.ts +0 -38
  239. package/dist/esm/for_packageDependencies/fixtures.d.ts.map +0 -1
  240. package/dist/esm/for_packageDependencies/fixtures.js +0 -5
  241. package/dist/esm/for_packageDependencies/fixtures.js.map +0 -1
  242. package/dist/esm/for_packageDependencies/when_resolving_against_the_shared_fixture_corpus.d.ts +0 -2
  243. package/dist/esm/for_packageDependencies/when_resolving_against_the_shared_fixture_corpus.d.ts.map +0 -1
  244. package/dist/esm/for_packageDependencies/when_resolving_against_the_shared_fixture_corpus.js +0 -29
  245. package/dist/esm/for_packageDependencies/when_resolving_against_the_shared_fixture_corpus.js.map +0 -1
  246. package/dist/esm/for_packageVersionRange/when_checking_against_the_shared_fixture_corpus.d.ts +0 -2
  247. package/dist/esm/for_packageVersionRange/when_checking_against_the_shared_fixture_corpus.d.ts.map +0 -1
  248. package/dist/esm/for_packageVersionRange/when_checking_against_the_shared_fixture_corpus.js +0 -10
  249. package/dist/esm/for_packageVersionRange/when_checking_against_the_shared_fixture_corpus.js.map +0 -1
  250. package/dist/esm/for_panelKind/when_telling_the_panels_apart.d.ts +0 -2
  251. package/dist/esm/for_panelKind/when_telling_the_panels_apart.d.ts.map +0 -1
  252. package/dist/esm/for_panelKind/when_telling_the_panels_apart.js +0 -38
  253. package/dist/esm/for_panelKind/when_telling_the_panels_apart.js.map +0 -1
  254. package/dist/esm/for_renderElement/when_rendering_a_mixed_tree.d.ts +0 -2
  255. package/dist/esm/for_renderElement/when_rendering_a_mixed_tree.d.ts.map +0 -1
  256. package/dist/esm/for_renderElement/when_rendering_a_mixed_tree.js +0 -46
  257. package/dist/esm/for_renderElement/when_rendering_a_mixed_tree.js.map +0 -1
  258. package/dist/esm/for_resolveBehaviors/when_resolving_additive_attachments.d.ts +0 -2
  259. package/dist/esm/for_resolveBehaviors/when_resolving_additive_attachments.d.ts.map +0 -1
  260. package/dist/esm/for_resolveBehaviors/when_resolving_additive_attachments.js +0 -68
  261. package/dist/esm/for_resolveBehaviors/when_resolving_additive_attachments.js.map +0 -1
  262. package/dist/esm/for_resolveComponentName/when_resolving_against_the_shared_fixture_corpus.d.ts +0 -2
  263. package/dist/esm/for_resolveComponentName/when_resolving_against_the_shared_fixture_corpus.d.ts.map +0 -1
  264. package/dist/esm/for_resolveComponentName/when_resolving_against_the_shared_fixture_corpus.js +0 -20
  265. package/dist/esm/for_resolveComponentName/when_resolving_against_the_shared_fixture_corpus.js.map +0 -1
  266. package/dist/esm/for_resolveScreenTemplates/when_resolving_against_the_shared_fixture_corpus.d.ts +0 -2
  267. package/dist/esm/for_resolveScreenTemplates/when_resolving_against_the_shared_fixture_corpus.d.ts.map +0 -1
  268. package/dist/esm/for_resolveScreenTemplates/when_resolving_against_the_shared_fixture_corpus.js +0 -30
  269. package/dist/esm/for_resolveScreenTemplates/when_resolving_against_the_shared_fixture_corpus.js.map +0 -1
  270. package/dist/esm/for_resolveStrings/when_resolving_a_strings_reference.d.ts +0 -2
  271. package/dist/esm/for_resolveStrings/when_resolving_a_strings_reference.d.ts.map +0 -1
  272. package/dist/esm/for_resolveStrings/when_resolving_a_strings_reference.js +0 -69
  273. package/dist/esm/for_resolveStrings/when_resolving_a_strings_reference.js.map +0 -1
  274. package/dist/esm/for_runActions/given/a_recording_dispatcher.d.ts +0 -21
  275. package/dist/esm/for_runActions/given/a_recording_dispatcher.d.ts.map +0 -1
  276. package/dist/esm/for_runActions/given/a_recording_dispatcher.js +0 -45
  277. package/dist/esm/for_runActions/given/a_recording_dispatcher.js.map +0 -1
  278. package/dist/esm/for_runActions/when_running_a_sequence_of_actions.d.ts +0 -2
  279. package/dist/esm/for_runActions/when_running_a_sequence_of_actions.d.ts.map +0 -1
  280. package/dist/esm/for_runActions/when_running_a_sequence_of_actions.js +0 -126
  281. package/dist/esm/for_runActions/when_running_a_sequence_of_actions.js.map +0 -1
  282. package/dist/esm/for_runActions/when_running_against_the_shared_fixture_corpus.d.ts +0 -2
  283. package/dist/esm/for_runActions/when_running_against_the_shared_fixture_corpus.d.ts.map +0 -1
  284. package/dist/esm/for_runActions/when_running_against_the_shared_fixture_corpus.js +0 -61
  285. package/dist/esm/for_runActions/when_running_against_the_shared_fixture_corpus.js.map +0 -1
  286. package/dist/esm/for_themeCompatibility/when_checking_against_the_shared_fixture_corpus.d.ts +0 -2
  287. package/dist/esm/for_themeCompatibility/when_checking_against_the_shared_fixture_corpus.d.ts.map +0 -1
  288. package/dist/esm/for_themeCompatibility/when_checking_against_the_shared_fixture_corpus.js +0 -18
  289. package/dist/esm/for_themeCompatibility/when_checking_against_the_shared_fixture_corpus.js.map +0 -1
  290. package/for_aggregateContributions/when_aggregating_by_contribution_point.ts +0 -31
  291. package/for_buildStarterProfile/when_checking_against_the_shared_fixture_corpus.ts +0 -32
  292. package/for_computeSizeClass/when_computing_the_size_class.ts +0 -23
  293. package/for_evaluateFlowArrangement/when_evaluating_against_the_shared_fixture_corpus.ts +0 -67
  294. package/for_evaluateFreeformArrangement/when_evaluating_against_the_shared_fixture_corpus.ts +0 -68
  295. package/for_evaluateFreeformSlotArrangement/when_evaluating_against_the_shared_fixture_corpus.ts +0 -47
  296. package/for_incompatibleStarterThemes/when_checking_against_the_shared_fixture_corpus.ts +0 -34
  297. package/for_packageCatalog/when_querying_against_the_shared_fixture_corpus.ts +0 -26
  298. package/for_packageDependencies/fixtures.ts +0 -44
  299. package/for_packageDependencies/when_resolving_against_the_shared_fixture_corpus.ts +0 -39
  300. package/for_packageVersionRange/when_checking_against_the_shared_fixture_corpus.ts +0 -13
  301. package/for_panelKind/when_telling_the_panels_apart.ts +0 -47
  302. package/for_renderElement/when_rendering_a_mixed_tree.ts +0 -58
  303. package/for_resolveBehaviors/when_resolving_additive_attachments.ts +0 -100
  304. package/for_resolveComponentName/when_resolving_against_the_shared_fixture_corpus.ts +0 -36
  305. package/for_resolveScreenTemplates/when_resolving_against_the_shared_fixture_corpus.ts +0 -49
  306. package/for_resolveStrings/when_resolving_a_strings_reference.ts +0 -85
  307. package/for_runActions/given/a_recording_dispatcher.ts +0 -72
  308. package/for_runActions/when_running_a_sequence_of_actions.ts +0 -226
  309. package/for_runActions/when_running_against_the_shared_fixture_corpus.ts +0 -124
  310. package/for_themeCompatibility/when_checking_against_the_shared_fixture_corpus.ts +0 -37
@@ -0,0 +1,191 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import {
5
+ HorizontalAlignment, LayoutChildPlacement, LayoutType, LayoutTypeDescriptor, Orientation, PropertyChoice,
6
+ PropertyDescriptor, PropertyValueType, VerticalAlignment, Visibility,
7
+ } from '@cratis/scene.model';
8
+
9
+ function choices(enumObject: Record<string, string>): PropertyChoice[] {
10
+ return Object.values(enumObject).map(value => ({ value, label: value }));
11
+ }
12
+
13
+ const spacing = (path: string, label: string, group: string): PropertyDescriptor => ({
14
+ path, label, group, valueType: PropertyValueType.Number, default: 0, constraints: { minimum: 0 },
15
+ });
16
+
17
+ const flowNodeProperties: PropertyDescriptor[] = [
18
+ {
19
+ path: 'grow', label: 'Grow', group: 'Layout', valueType: PropertyValueType.Number,
20
+ description: 'How much of the free space in its container this node takes, relative to its siblings.',
21
+ constraints: { minimum: 0 },
22
+ },
23
+ {
24
+ path: 'span', label: 'Span', group: 'Layout', valueType: PropertyValueType.Number,
25
+ description: 'How many grid columns this node spans. Only meaningful inside a grid.',
26
+ constraints: { minimum: 1, integer: true },
27
+ },
28
+ ];
29
+
30
+ const flowContainerProperties: PropertyDescriptor[] = [spacing('gap', 'Gap', 'Layout'), ...flowNodeProperties];
31
+
32
+ const panelProperties: PropertyDescriptor[] = [
33
+ { path: 'name', label: 'Name', group: 'General', valueType: PropertyValueType.String },
34
+ { path: 'visibility', label: 'Visibility', group: 'General', valueType: PropertyValueType.Enum, choices: choices(Visibility), default: Visibility.Visible },
35
+ { path: 'isEnabled', label: 'Enabled', group: 'General', valueType: PropertyValueType.Boolean, default: true },
36
+ { path: 'opacity', label: 'Opacity', group: 'General', valueType: PropertyValueType.Number, default: 1, constraints: { minimum: 0, maximum: 1 } },
37
+ { path: 'horizontalAlignment', label: 'Horizontal alignment', group: 'Layout', valueType: PropertyValueType.Enum, choices: choices(HorizontalAlignment) },
38
+ { path: 'verticalAlignment', label: 'Vertical alignment', group: 'Layout', valueType: PropertyValueType.Enum, choices: choices(VerticalAlignment) },
39
+ { path: 'zIndex', label: 'Stacking order', group: 'Layout', valueType: PropertyValueType.Number, default: 0, constraints: { integer: true } },
40
+ ];
41
+
42
+ const orientation: PropertyDescriptor = {
43
+ path: 'orientation', label: 'Orientation', group: 'Layout', valueType: PropertyValueType.Enum, choices: choices(Orientation),
44
+ };
45
+
46
+ const panelChildren = { acceptsChildren: true, childPlacement: LayoutChildPlacement.Ordered, supportsSizeClassVariants: false };
47
+
48
+ /**
49
+ * The layout type -> capability matrix, as data. Every {@link LayoutType} has exactly one entry; the
50
+ * documentation's matrix is this table.
51
+ */
52
+ export const layoutTypeDescriptors: LayoutTypeDescriptor[] = [
53
+ {
54
+ type: LayoutType.FlowRow,
55
+ label: 'Row',
56
+ description: 'Reflows its children horizontally.',
57
+ capabilities: {
58
+ acceptsChildren: true, childPlacement: LayoutChildPlacement.Ordered, supportsGridSpan: false,
59
+ supportsSizeClassVariants: true, convertibleTo: [LayoutType.FlowColumn, LayoutType.FlowGrid],
60
+ },
61
+ properties: flowContainerProperties,
62
+ },
63
+ {
64
+ type: LayoutType.FlowColumn,
65
+ label: 'Column',
66
+ description: 'Reflows its children vertically.',
67
+ capabilities: {
68
+ acceptsChildren: true, childPlacement: LayoutChildPlacement.Ordered, supportsGridSpan: false,
69
+ supportsSizeClassVariants: true, convertibleTo: [LayoutType.FlowRow, LayoutType.FlowGrid],
70
+ },
71
+ properties: flowContainerProperties,
72
+ },
73
+ {
74
+ type: LayoutType.FlowGrid,
75
+ label: 'Grid',
76
+ description: 'Arranges its children in a grid; children can span columns.',
77
+ capabilities: {
78
+ acceptsChildren: true, childPlacement: LayoutChildPlacement.Ordered, supportsGridSpan: true,
79
+ supportsSizeClassVariants: true, convertibleTo: [LayoutType.FlowRow, LayoutType.FlowColumn],
80
+ },
81
+ properties: [
82
+ ...flowContainerProperties,
83
+ { path: 'columns', label: 'Columns', group: 'Layout', valueType: PropertyValueType.Number, constraints: { minimum: 1, integer: true } },
84
+ { path: 'rows', label: 'Rows', group: 'Layout', valueType: PropertyValueType.Number, constraints: { minimum: 1, integer: true } },
85
+ ],
86
+ },
87
+ {
88
+ type: LayoutType.FlowLeaf,
89
+ label: 'Content',
90
+ description: 'Positions one element within a flow.',
91
+ capabilities: {
92
+ acceptsChildren: false, maximumChildren: 1, childPlacement: LayoutChildPlacement.None, supportsGridSpan: true,
93
+ supportsSizeClassVariants: false, convertibleTo: [],
94
+ },
95
+ properties: flowNodeProperties,
96
+ },
97
+ {
98
+ type: LayoutType.FlowSlotLeaf,
99
+ label: 'Slot',
100
+ description: 'Positions one of the layout\'s own named slots within its arrangement.',
101
+ capabilities: {
102
+ acceptsChildren: false, childPlacement: LayoutChildPlacement.None, supportsGridSpan: true,
103
+ supportsSizeClassVariants: false, convertibleTo: [],
104
+ },
105
+ properties: [
106
+ { path: 'slotName', label: 'Slot', group: 'Layout', valueType: PropertyValueType.String, constraints: { required: true } },
107
+ ...flowNodeProperties,
108
+ ],
109
+ },
110
+ {
111
+ type: LayoutType.Freeform,
112
+ label: 'Freeform',
113
+ description: 'Places each child at its own position and size, one variant per size class.',
114
+ capabilities: {
115
+ acceptsChildren: true, childPlacement: LayoutChildPlacement.Positioned, supportsGridSpan: false,
116
+ supportsSizeClassVariants: true, convertibleTo: [],
117
+ },
118
+ properties: [],
119
+ },
120
+ {
121
+ type: LayoutType.StackPanel,
122
+ label: 'Stack panel',
123
+ description: 'Stacks its children in one direction with a fixed spacing.',
124
+ capabilities: { ...panelChildren, supportsGridSpan: false, convertibleTo: [LayoutType.WrapPanel, LayoutType.DockPanel] },
125
+ properties: [...panelProperties, orientation, spacing('spacing', 'Spacing', 'Layout')],
126
+ },
127
+ {
128
+ type: LayoutType.WrapPanel,
129
+ label: 'Wrap panel',
130
+ description: 'Lays children out along a line and wraps to the next when it is full.',
131
+ capabilities: { ...panelChildren, supportsGridSpan: false, convertibleTo: [LayoutType.StackPanel, LayoutType.DockPanel] },
132
+ properties: [
133
+ ...panelProperties,
134
+ orientation,
135
+ { path: 'itemWidth', label: 'Item width', group: 'Layout', valueType: PropertyValueType.Number, constraints: { minimum: 0 } },
136
+ { path: 'itemHeight', label: 'Item height', group: 'Layout', valueType: PropertyValueType.Number, constraints: { minimum: 0 } },
137
+ ],
138
+ },
139
+ {
140
+ type: LayoutType.DockPanel,
141
+ label: 'Dock panel',
142
+ description: 'Docks each child against an edge; the last child can fill what remains.',
143
+ capabilities: { ...panelChildren, supportsGridSpan: false, convertibleTo: [LayoutType.StackPanel, LayoutType.WrapPanel] },
144
+ properties: [
145
+ ...panelProperties,
146
+ { path: 'lastChildFill', label: 'Last child fills', group: 'Layout', valueType: PropertyValueType.Boolean, default: true },
147
+ ],
148
+ },
149
+ {
150
+ type: LayoutType.GridPanel,
151
+ label: 'Grid panel',
152
+ description: 'Positions children in rows and columns defined by the panel.',
153
+ capabilities: { ...panelChildren, supportsGridSpan: true, convertibleTo: [] },
154
+ properties: [
155
+ ...panelProperties,
156
+ { path: 'rows', label: 'Rows', group: 'Layout', valueType: PropertyValueType.Object, editorKind: 'gridDefinitions' },
157
+ { path: 'columns', label: 'Columns', group: 'Layout', valueType: PropertyValueType.Object, editorKind: 'gridDefinitions' },
158
+ ],
159
+ },
160
+ {
161
+ type: LayoutType.Canvas,
162
+ label: 'Canvas',
163
+ description: 'Places each child at an absolute position.',
164
+ capabilities: {
165
+ acceptsChildren: true, childPlacement: LayoutChildPlacement.Positioned, supportsGridSpan: false,
166
+ supportsSizeClassVariants: false, convertibleTo: [],
167
+ },
168
+ properties: [
169
+ ...panelProperties,
170
+ { path: 'extent', label: 'Extent', group: 'Layout', valueType: PropertyValueType.Object, editorKind: 'size' },
171
+ ],
172
+ },
173
+ ];
174
+
175
+ /**
176
+ * The editable properties of a placement within a freeform variant - an element's or a slot's position and size.
177
+ */
178
+ export const placementProperties: PropertyDescriptor[] = [
179
+ { path: 'x', label: 'Left', group: 'Placement', valueType: PropertyValueType.Number },
180
+ { path: 'y', label: 'Top', group: 'Placement', valueType: PropertyValueType.Number },
181
+ { path: 'width', label: 'Width', group: 'Placement', valueType: PropertyValueType.Number, constraints: { minimum: 0 } },
182
+ { path: 'height', label: 'Height', group: 'Placement', valueType: PropertyValueType.Number, constraints: { minimum: 0 } },
183
+ ];
184
+
185
+ /**
186
+ * The editable properties of a screen template or dialog template itself.
187
+ */
188
+ export const templateProperties: PropertyDescriptor[] = [
189
+ { path: 'displayName', label: 'Display name', group: 'General', valueType: PropertyValueType.String },
190
+ { path: 'description', label: 'Description', group: 'General', valueType: PropertyValueType.String },
191
+ ];
@@ -0,0 +1,135 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import {
5
+ ChangeLayoutTypeEdit, DiagnosticCode, FlowContainerKind, LayoutType, Orientation, SceneDiagnostic, SceneDocument,
6
+ SceneNodeKind,
7
+ } from '@cratis/scene.model';
8
+ import { errorDiagnostic, warningDiagnostic } from './diagnostics';
9
+ import { findLayoutTypeDescriptor } from './DescriptorCatalog';
10
+ import { getAtPath, mirroredPaths } from './documentPaths';
11
+ import { EditSession } from './EditSession';
12
+ import { cloneData } from './pathAccess';
13
+
14
+ type Node = Record<string, unknown>;
15
+
16
+ interface Conversion {
17
+ result: Node;
18
+
19
+ /** Properties that held a real value and are gone after the conversion. */
20
+ lost: string[];
21
+ }
22
+
23
+ const flowKinds: Partial<Record<LayoutType, FlowContainerKind>> = {
24
+ [LayoutType.FlowRow]: FlowContainerKind.Row,
25
+ [LayoutType.FlowColumn]: FlowContainerKind.Column,
26
+ [LayoutType.FlowGrid]: FlowContainerKind.Grid,
27
+ };
28
+
29
+ const panelOwnProperties: Partial<Record<LayoutType, string[]>> = {
30
+ [LayoutType.StackPanel]: ['orientation', 'spacing'],
31
+ [LayoutType.WrapPanel]: ['orientation', 'itemWidth', 'itemHeight'],
32
+ [LayoutType.DockPanel]: ['lastChildFill'],
33
+ };
34
+
35
+ const allPanelOwn = ['orientation', 'spacing', 'itemWidth', 'itemHeight', 'lastChildFill'];
36
+
37
+ function isSet(node: Node, property: string): boolean {
38
+ return node[property] !== undefined && !(property === 'spacing' && node[property] === 0);
39
+ }
40
+
41
+ function convertFlow(source: Node, to: LayoutType): Conversion {
42
+ const result = cloneData(source);
43
+ const lost: string[] = [];
44
+ const target = flowKinds[to]!;
45
+
46
+ if (source.kind === FlowContainerKind.Grid && target !== FlowContainerKind.Grid) {
47
+ for (const property of ['columns', 'rows']) {
48
+ if (source[property] !== undefined) lost.push(property);
49
+ delete result[property];
50
+ }
51
+
52
+ const spans = (result.children as Node[]).filter(child => child.span !== undefined);
53
+ if (spans.length > 0) lost.push('span (on children)');
54
+ for (const child of spans) delete child.span;
55
+ }
56
+
57
+ result.kind = target;
58
+ return { result, lost };
59
+ }
60
+
61
+ function convertPanel(source: Node, to: LayoutType): Conversion {
62
+ const result = cloneData(source);
63
+ const keep = panelOwnProperties[to] ?? [];
64
+ const lost = allPanelOwn.filter(property => !keep.includes(property) && isSet(source, property));
65
+ for (const property of allPanelOwn.filter(candidate => !keep.includes(candidate))) delete result[property];
66
+
67
+ if (to === LayoutType.StackPanel) {
68
+ result.orientation ??= Orientation.Vertical;
69
+ result.spacing ??= 0;
70
+ } else if (to === LayoutType.WrapPanel) {
71
+ result.orientation ??= Orientation.Horizontal;
72
+ } else {
73
+ result.lastChildFill ??= true;
74
+ }
75
+
76
+ return { result, lost };
77
+ }
78
+
79
+ /**
80
+ * Applies a layout type change to a copy of the document.
81
+ *
82
+ * A change must be one the layout type allows. Changes that keep everything - row to column, a dock panel to a
83
+ * stack panel - go ahead. Changes that would drop real values, such as a grid's columns and spans when it becomes a
84
+ * row, are refused with the list of what would be lost unless the edit accepts the loss, and then go ahead with a
85
+ * warning that lists the same.
86
+ */
87
+ export function applyChangeLayoutType(session: EditSession, edit: ChangeLayoutTypeEdit, clone: SceneDocument, diagnostics: SceneDiagnostic[]): void {
88
+ const record = session.index.nodes.get(edit.nodeId);
89
+ if (!record) {
90
+ diagnostics.push(errorDiagnostic(DiagnosticCode.UnknownNode, `There is no node '${edit.nodeId}'.`, { nodeId: edit.nodeId }));
91
+ return;
92
+ }
93
+
94
+ if (record.kind !== SceneNodeKind.FlowNode && record.kind !== SceneNodeKind.Element) {
95
+ diagnostics.push(errorDiagnostic(DiagnosticCode.NotALayoutNode, `'${record.label}' is not a layout node.`, { nodeId: edit.nodeId }));
96
+ return;
97
+ }
98
+
99
+ if (!session.isLocal(record)) {
100
+ diagnostics.push(errorDiagnostic(DiagnosticCode.NodeNotEditable, `'${edit.nodeId}' is inherited from '${session.sourceOf(record)}' and cannot be changed here.`, { nodeId: edit.nodeId }));
101
+ return;
102
+ }
103
+
104
+ const descriptor = findLayoutTypeDescriptor(session.context.catalog, record.typeId);
105
+ if (!descriptor) {
106
+ diagnostics.push(errorDiagnostic(DiagnosticCode.NotALayoutNode, `'${record.label}' is not a layout node.`, { nodeId: edit.nodeId }));
107
+ return;
108
+ }
109
+
110
+ if (!descriptor.capabilities.convertibleTo.includes(edit.to as LayoutType)) {
111
+ diagnostics.push(errorDiagnostic(DiagnosticCode.LayoutTypeNotConvertible, `A ${descriptor.label.toLowerCase()} cannot be changed into '${edit.to}'.`, { nodeId: edit.nodeId }));
112
+ return;
113
+ }
114
+
115
+ const isFlow = record.kind === SceneNodeKind.FlowNode;
116
+ const conversion = isFlow ? convertFlow(record.value as Node, edit.to as LayoutType) : convertPanel(record.value as Node, edit.to as LayoutType);
117
+
118
+ if (conversion.lost.length > 0) {
119
+ const lost = conversion.lost.join(', ');
120
+ if (!edit.acceptLoss) {
121
+ diagnostics.push(errorDiagnostic(
122
+ DiagnosticCode.LossyConversionNotAccepted,
123
+ `Changing to '${edit.to}' would drop ${lost}. Accept the loss to go ahead.`,
124
+ { nodeId: edit.nodeId }));
125
+ return;
126
+ }
127
+
128
+ diagnostics.push(warningDiagnostic(DiagnosticCode.LossyConversion, `Changed to '${edit.to}'; dropped ${lost}.`, { nodeId: edit.nodeId }));
129
+ }
130
+
131
+ for (const path of mirroredPaths(record.mirror, record.path)) {
132
+ const holder = getAtPath(clone, path.slice(0, -1)) as Record<string | number, unknown>;
133
+ holder[path[path.length - 1]] = cloneData(conversion.result);
134
+ }
135
+ }
@@ -0,0 +1,111 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { EditingScope, EditingScopeKind } from '@cratis/scene.model';
5
+
6
+ const separator = '#';
7
+
8
+ function child(parentId: string, segment: string): string {
9
+ return [parentId, segment].join(separator);
10
+ }
11
+
12
+ /**
13
+ * The id of a layout node.
14
+ */
15
+ export function layoutNodeId(name: string): string {
16
+ return `layout:${name}`;
17
+ }
18
+
19
+ /**
20
+ * The id of a screen template node.
21
+ */
22
+ export function screenTemplateNodeId(name: string): string {
23
+ return `screenTemplate:${name}`;
24
+ }
25
+
26
+ /**
27
+ * The id of a dialog template node.
28
+ */
29
+ export function dialogTemplateNodeId(name: string): string {
30
+ return `dialogTemplate:${name}`;
31
+ }
32
+
33
+ /**
34
+ * The id of a screen node.
35
+ */
36
+ export function screenNodeId(name: string): string {
37
+ return `screen:${name}`;
38
+ }
39
+
40
+ /**
41
+ * The id of a named slot of a layout, template or screen.
42
+ */
43
+ export function slotNodeId(ownerNodeId: string, slotName: string): string {
44
+ return child(ownerNodeId, `slot:${slotName}`);
45
+ }
46
+
47
+ /**
48
+ * The id of an element. Element ids are unique across a document and survive moves, so this is the stable way to
49
+ * address a component an author has placed.
50
+ */
51
+ export function elementNodeId(elementId: string): string {
52
+ return `element:${elementId}`;
53
+ }
54
+
55
+ /**
56
+ * The id of the arrangement a layout, template or dialog positions its own slots with.
57
+ */
58
+ export function ownerArrangementId(ownerNodeId: string): string {
59
+ return child(ownerNodeId, 'arrangement');
60
+ }
61
+
62
+ /**
63
+ * The id of a node of a flow arrangement.
64
+ *
65
+ * `holderId` is the slot or owner arrangement the flow belongs to, `variant` is `root` or `override<n>`, and
66
+ * `indexPath` walks down from that variant's root through each container's children. A flow node has no identity
67
+ * of its own, so the id is a structural address and only names the same node until the tree around it changes -
68
+ * read it from the same revision of the document you act on.
69
+ */
70
+ export function flowNodeId(holderId: string, variant: string, indexPath: number[]): string {
71
+ return child(holderId, `flow:${[variant, ...indexPath].join('.')}`);
72
+ }
73
+
74
+ /**
75
+ * The id of a freeform arrangement.
76
+ */
77
+ export function freeformNodeId(holderId: string): string {
78
+ return child(holderId, 'freeform');
79
+ }
80
+
81
+ /**
82
+ * The id of one placement within a freeform variant. `key` is the placed element's id, or the placed slot's name.
83
+ */
84
+ export function placementNodeId(holderId: string, variantIndex: number, key: string): string {
85
+ return child(`${freeformNodeId(holderId)}:${variantIndex}`, `placement:${key}`);
86
+ }
87
+
88
+ /**
89
+ * The node id of the thing an editing scope names.
90
+ */
91
+ export function scopeNodeId(scope: EditingScope): string {
92
+ switch (scope.kind) {
93
+ case EditingScopeKind.Screen: return screenNodeId(scope.name);
94
+ case EditingScopeKind.ScreenTemplate: return screenTemplateNodeId(scope.name);
95
+ case EditingScopeKind.DialogTemplate: return dialogTemplateNodeId(scope.name);
96
+ default: return layoutNodeId(scope.name);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * The instance id an editing scope contributes under: `screen:<name>`, `template:<name>`, `dialog:<name>` or
102
+ * `layout:<name>`. It is what an {@link InstanceContribution} is keyed by.
103
+ */
104
+ export function scopeInstanceId(scope: EditingScope): string {
105
+ switch (scope.kind) {
106
+ case EditingScopeKind.Screen: return `screen:${scope.name}`;
107
+ case EditingScopeKind.ScreenTemplate: return `template:${scope.name}`;
108
+ case EditingScopeKind.DialogTemplate: return `dialog:${scope.name}`;
109
+ default: return `layout:${scope.name}`;
110
+ }
111
+ }
@@ -0,0 +1,81 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ /**
5
+ * Reads a value at a dotted path in a plain object - `a.b.c` reads `bag.a.b.c` - or `undefined` when any step is
6
+ * missing.
7
+ */
8
+ export function getValueAtPath(bag: Record<string, unknown>, path: string): unknown {
9
+ let current: unknown = bag;
10
+ for (const key of path.split('.')) {
11
+ if (current === null || typeof current !== 'object') return undefined;
12
+ current = (current as Record<string, unknown>)[key];
13
+ }
14
+
15
+ return current;
16
+ }
17
+
18
+ /**
19
+ * Whether a dotted path holds a value.
20
+ */
21
+ export function hasValueAtPath(bag: Record<string, unknown>, path: string): boolean {
22
+ return getValueAtPath(bag, path) !== undefined;
23
+ }
24
+
25
+ /**
26
+ * Sets a value at a dotted path, creating intermediate objects. Mutates `bag`: callers hold a copy.
27
+ */
28
+ export function setValueAtPath(bag: Record<string, unknown>, path: string, value: unknown): void {
29
+ const keys = path.split('.');
30
+ let current = bag;
31
+ for (const key of keys.slice(0, -1)) {
32
+ const next = current[key];
33
+ if (next === null || typeof next !== 'object' || Array.isArray(next)) {
34
+ current[key] = {};
35
+ }
36
+
37
+ current = current[key] as Record<string, unknown>;
38
+ }
39
+
40
+ current[keys[keys.length - 1]] = value;
41
+ }
42
+
43
+ /**
44
+ * Removes the value at a dotted path, leaving intermediate objects in place. Mutates `bag`.
45
+ */
46
+ export function removeValueAtPath(bag: Record<string, unknown>, path: string): void {
47
+ const keys = path.split('.');
48
+ let current: unknown = bag;
49
+ for (const key of keys.slice(0, -1)) {
50
+ if (current === null || typeof current !== 'object') return;
51
+ current = (current as Record<string, unknown>)[key];
52
+ }
53
+
54
+ if (current !== null && typeof current === 'object') {
55
+ delete (current as Record<string, unknown>)[keys[keys.length - 1]];
56
+ }
57
+ }
58
+
59
+ /**
60
+ * A copy of plain JSON-shaped data that shares nothing with the original.
61
+ */
62
+ export function cloneData<T>(value: T): T {
63
+ return structuredClone(value);
64
+ }
65
+
66
+ /**
67
+ * Whether two plain values are structurally equal.
68
+ */
69
+ export function dataEquals(left: unknown, right: unknown): boolean {
70
+ if (left === right) return true;
71
+ if (left === null || right === null || typeof left !== 'object' || typeof right !== 'object') return false;
72
+ if (Array.isArray(left) !== Array.isArray(right)) return false;
73
+
74
+ const leftKeys = Object.keys(left as object);
75
+ const rightKeys = Object.keys(right as object);
76
+ if (leftKeys.length !== rightKeys.length) return false;
77
+
78
+ return leftKeys.every(key =>
79
+ Object.prototype.hasOwnProperty.call(right, key)
80
+ && dataEquals((left as Record<string, unknown>)[key], (right as Record<string, unknown>)[key]));
81
+ }
@@ -0,0 +1,119 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import {
5
+ DiagnosticCode, PropertyDescriptor, PropertyValueType, QueryBinding, ResetPropertyEdit, SceneDiagnostic, SceneDocument,
6
+ SceneNodeKind, SetPropertyEdit,
7
+ } from '@cratis/scene.model';
8
+ import { describeNode } from './describeNode';
9
+ import { errorDiagnostic } from './diagnostics';
10
+ import { NodeRecord } from './DocumentIndex';
11
+ import { getAtPath, mirroredPaths } from './documentPaths';
12
+ import { EditSession } from './EditSession';
13
+ import { cloneData, removeValueAtPath, setValueAtPath } from './pathAccess';
14
+ import { validateBinding } from './validateBinding';
15
+ import { validateValue } from './validateValue';
16
+
17
+ /**
18
+ * Finds the node and descriptor an edit of a property addresses, or says why it cannot.
19
+ */
20
+ export function resolveEditableProperty(
21
+ session: EditSession,
22
+ nodeId: string,
23
+ path: string,
24
+ diagnostics: SceneDiagnostic[],
25
+ ): { record: NodeRecord; descriptor: PropertyDescriptor } | undefined {
26
+ const record = session.index.nodes.get(nodeId);
27
+ if (!record) {
28
+ diagnostics.push(errorDiagnostic(DiagnosticCode.UnknownNode, `There is no node '${nodeId}'.`, { nodeId }));
29
+ return undefined;
30
+ }
31
+
32
+ if (!session.isLocal(record)) {
33
+ diagnostics.push(errorDiagnostic(
34
+ DiagnosticCode.NodeNotEditable,
35
+ `'${nodeId}' is inherited from '${session.sourceOf(record)}'. An exposed property is configured with an instance edit, not by changing the node.`,
36
+ { nodeId, path }));
37
+ return undefined;
38
+ }
39
+
40
+ const description = describeNode(record, session.context.catalog);
41
+ const descriptor = description.properties.find(candidate => candidate.path === path);
42
+ if (!descriptor) {
43
+ diagnostics.push(errorDiagnostic(
44
+ description.diagnostics.length > 0 ? DiagnosticCode.MissingComponentDescriptor : DiagnosticCode.UnknownProperty,
45
+ description.diagnostics[0]?.message ?? `'${record.typeId}' has no property '${path}'.`,
46
+ { nodeId, path }));
47
+ return undefined;
48
+ }
49
+
50
+ if (descriptor.readOnly) {
51
+ diagnostics.push(errorDiagnostic(DiagnosticCode.ReadOnlyProperty, `'${descriptor.label}' is read-only.`, { nodeId, path }));
52
+ return undefined;
53
+ }
54
+
55
+ return { record, descriptor };
56
+ }
57
+
58
+ /**
59
+ * Checks a value for a property, including a query binding against the host's candidates.
60
+ *
61
+ * @returns Whether the value is acceptable; the reasons are added to `diagnostics`.
62
+ */
63
+ export function checkValue(session: EditSession, descriptor: PropertyDescriptor, value: unknown, context: { nodeId?: string; component?: string; instance?: string }, diagnostics: SceneDiagnostic[]): boolean {
64
+ const problem = validateValue(descriptor, value);
65
+ if (problem) {
66
+ diagnostics.push(errorDiagnostic(DiagnosticCode.InvalidValue, `'${descriptor.label}': ${problem}.`, { ...context, path: descriptor.path }));
67
+ return false;
68
+ }
69
+
70
+ const candidates = session.context.queryCandidates;
71
+ if (descriptor.valueType === PropertyValueType.QueryReference && candidates && typeof value === 'object' && value !== null) {
72
+ const binding = value as QueryBinding;
73
+ const problems = validateBinding(descriptor, candidates.find(candidate => candidate.id === binding.queryId), binding);
74
+ diagnostics.push(...problems.map(problemDiagnostic => ({ ...problemDiagnostic, ...context })));
75
+ return problems.length === 0;
76
+ }
77
+
78
+ return true;
79
+ }
80
+
81
+ function bagFor(root: SceneDocument, record: NodeRecord, path: string[]): Record<string, unknown> | undefined {
82
+ const target = getAtPath(root, path) as Record<string, unknown> | undefined;
83
+ if (!target) return undefined;
84
+ const isComponent = record.kind === SceneNodeKind.Element && target.componentName !== undefined;
85
+ if (!isComponent) return target;
86
+ return (target.properties ??= {}) as Record<string, unknown>;
87
+ }
88
+
89
+ /**
90
+ * Applies a property set to a copy of the document.
91
+ */
92
+ export function applySetProperty(session: EditSession, edit: SetPropertyEdit, clone: SceneDocument, diagnostics: SceneDiagnostic[]): void {
93
+ const resolved = resolveEditableProperty(session, edit.nodeId, edit.path, diagnostics);
94
+ if (!resolved) return;
95
+ if (!checkValue(session, resolved.descriptor, edit.value, { nodeId: edit.nodeId }, diagnostics)) return;
96
+
97
+ for (const path of mirroredPaths(resolved.record.mirror, resolved.record.path)) {
98
+ const bag = bagFor(clone, resolved.record, path as string[]);
99
+ if (bag) setValueAtPath(bag, edit.path, cloneData(edit.value));
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Applies a property reset to a copy of the document.
105
+ */
106
+ export function applyResetProperty(session: EditSession, edit: ResetPropertyEdit, clone: SceneDocument, diagnostics: SceneDiagnostic[]): void {
107
+ const resolved = resolveEditableProperty(session, edit.nodeId, edit.path, diagnostics);
108
+ if (!resolved) return;
109
+
110
+ if (resolved.descriptor.constraints?.required && resolved.descriptor.default === undefined) {
111
+ diagnostics.push(errorDiagnostic(DiagnosticCode.InvalidValue, `'${resolved.descriptor.label}' is required and has no default to go back to.`, { nodeId: edit.nodeId, path: edit.path }));
112
+ return;
113
+ }
114
+
115
+ for (const path of mirroredPaths(resolved.record.mirror, resolved.record.path)) {
116
+ const bag = bagFor(clone, resolved.record, path as string[]);
117
+ if (bag) removeValueAtPath(bag, edit.path);
118
+ }
119
+ }