@webiny/website-builder-sdk 0.0.0-unstable.eb196ccd2f → 0.0.0-unstable.eb81deee7e

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 (307) hide show
  1. package/BindingsApi.js +29 -36
  2. package/BindingsApi.js.map +1 -1
  3. package/BindingsProcessor.js +34 -43
  4. package/BindingsProcessor.js.map +1 -1
  5. package/BindingsProcessor.test.js +82 -77
  6. package/BindingsProcessor.test.js.map +1 -1
  7. package/BindingsResolver.d.ts +5 -2
  8. package/BindingsResolver.js +99 -122
  9. package/BindingsResolver.js.map +1 -1
  10. package/BindingsResolver.test.js +399 -362
  11. package/BindingsResolver.test.js.map +1 -1
  12. package/ComponentInputTraverser.js +32 -49
  13. package/ComponentInputTraverser.js.map +1 -1
  14. package/ComponentManifestToAstConverter.js +20 -21
  15. package/ComponentManifestToAstConverter.js.map +1 -1
  16. package/ComponentRegistry.d.ts +9 -0
  17. package/ComponentRegistry.js +38 -22
  18. package/ComponentRegistry.js.map +1 -1
  19. package/ComponentResolver.d.ts +3 -2
  20. package/ComponentResolver.js +39 -29
  21. package/ComponentResolver.js.map +1 -1
  22. package/ComponentResolver.test.d.ts +1 -0
  23. package/ComponentResolver.test.js +81 -0
  24. package/ComponentResolver.test.js.map +1 -0
  25. package/ConstraintEvaluator.d.ts +36 -0
  26. package/ConstraintEvaluator.js +288 -0
  27. package/ConstraintEvaluator.js.map +1 -0
  28. package/ConstraintEvaluator.test.d.ts +1 -0
  29. package/ConstraintEvaluator.test.js +1634 -0
  30. package/ConstraintEvaluator.test.js.map +1 -0
  31. package/ContentSdk.d.ts +17 -3
  32. package/ContentSdk.js +113 -90
  33. package/ContentSdk.js.map +1 -1
  34. package/DocumentStore.d.ts +1 -1
  35. package/DocumentStore.js +47 -59
  36. package/DocumentStore.js.map +1 -1
  37. package/DocumentStoreManager.js +17 -16
  38. package/DocumentStoreManager.js.map +1 -1
  39. package/EditingSdk.d.ts +2 -2
  40. package/EditingSdk.js +92 -124
  41. package/EditingSdk.js.map +1 -1
  42. package/ElementFactory.d.ts +2 -3
  43. package/ElementFactory.js +164 -163
  44. package/ElementFactory.js.map +1 -1
  45. package/ElementFactory.objectListDefaults.test.d.ts +1 -0
  46. package/ElementFactory.objectListDefaults.test.js +423 -0
  47. package/ElementFactory.objectListDefaults.test.js.map +1 -0
  48. package/ElementFactory.test.d.ts +1 -0
  49. package/ElementFactory.test.js +353 -0
  50. package/ElementFactory.test.js.map +1 -0
  51. package/Environment.js +18 -19
  52. package/Environment.js.map +1 -1
  53. package/FunctionConverter.js +8 -7
  54. package/FunctionConverter.js.map +1 -1
  55. package/HashObject.js +11 -12
  56. package/HashObject.js.map +1 -1
  57. package/HotkeyManager.js +41 -48
  58. package/HotkeyManager.js.map +1 -1
  59. package/IBindingsUpdater.js +0 -3
  60. package/IRedirects.js +0 -3
  61. package/InheritanceProcessor.d.ts +2 -2
  62. package/InheritanceProcessor.js +99 -139
  63. package/InheritanceProcessor.js.map +1 -1
  64. package/InheritanceProcessor.test.js +178 -178
  65. package/InheritanceProcessor.test.js.map +1 -1
  66. package/InheritedValueResolver.d.ts +18 -1
  67. package/InheritedValueResolver.js +18 -20
  68. package/InheritedValueResolver.js.map +1 -1
  69. package/InputBindingsProcessor.d.ts +23 -2
  70. package/InputBindingsProcessor.js +200 -289
  71. package/InputBindingsProcessor.js.map +1 -1
  72. package/InputBindingsProcessorTokens.test.d.ts +1 -0
  73. package/InputBindingsProcessorTokens.test.js +400 -0
  74. package/InputBindingsProcessorTokens.test.js.map +1 -0
  75. package/InputsBindingsProcessor.test.js +334 -313
  76. package/InputsBindingsProcessor.test.js.map +1 -1
  77. package/InputsUpdater.d.ts +1 -1
  78. package/InputsUpdater.js +23 -26
  79. package/InputsUpdater.js.map +1 -1
  80. package/LiveSdk.d.ts +2 -2
  81. package/LiveSdk.js +12 -13
  82. package/LiveSdk.js.map +1 -1
  83. package/Logger.d.ts +2 -1
  84. package/Logger.js +9 -8
  85. package/Logger.js.map +1 -1
  86. package/MouseTracker.js +77 -83
  87. package/MouseTracker.js.map +1 -1
  88. package/NullSdk.d.ts +4 -3
  89. package/NullSdk.js +22 -14
  90. package/NullSdk.js.map +1 -1
  91. package/PreviewDocument.d.ts +1 -0
  92. package/PreviewDocument.js +30 -30
  93. package/PreviewDocument.js.map +1 -1
  94. package/PreviewSdk.d.ts +4 -3
  95. package/PreviewSdk.js +19 -17
  96. package/PreviewSdk.js.map +1 -1
  97. package/PreviewViewport.d.ts +0 -1
  98. package/PreviewViewport.js +50 -82
  99. package/PreviewViewport.js.map +1 -1
  100. package/README.md +10 -2
  101. package/ResizeObserver.js +24 -31
  102. package/ResizeObserver.js.map +1 -1
  103. package/StylesBindingsProcessor.d.ts +12 -2
  104. package/StylesBindingsProcessor.js +62 -79
  105. package/StylesBindingsProcessor.js.map +1 -1
  106. package/StylesUpdater.d.ts +1 -1
  107. package/StylesUpdater.js +20 -25
  108. package/StylesUpdater.js.map +1 -1
  109. package/Theme.js +30 -32
  110. package/Theme.js.map +1 -1
  111. package/ViewportManager.d.ts +2 -0
  112. package/ViewportManager.js +89 -97
  113. package/ViewportManager.js.map +1 -1
  114. package/assertBindingShapes.d.ts +31 -0
  115. package/assertBindingShapes.js +64 -0
  116. package/assertBindingShapes.js.map +1 -0
  117. package/assertBindingShapes.test.d.ts +1 -0
  118. package/assertBindingShapes.test.js +131 -0
  119. package/assertBindingShapes.test.js.map +1 -0
  120. package/asset/deliveryUrl.d.ts +41 -0
  121. package/asset/deliveryUrl.js +34 -0
  122. package/asset/deliveryUrl.js.map +1 -0
  123. package/asset/deliveryUrl.test.d.ts +1 -0
  124. package/asset/deliveryUrl.test.js +127 -0
  125. package/asset/deliveryUrl.test.js.map +1 -0
  126. package/asset/geometry.d.ts +7 -0
  127. package/asset/geometry.js +10 -0
  128. package/asset/geometry.js.map +1 -0
  129. package/asset/imageSrcSet.d.ts +63 -0
  130. package/asset/imageSrcSet.js +98 -0
  131. package/asset/imageSrcSet.js.map +1 -0
  132. package/asset/imageSrcSet.test.d.ts +1 -0
  133. package/asset/imageSrcSet.test.js +103 -0
  134. package/asset/imageSrcSet.test.js.map +1 -0
  135. package/asset/index.d.ts +5 -0
  136. package/asset/index.js +5 -0
  137. package/asset/normalize.d.ts +24 -0
  138. package/asset/normalize.js +106 -0
  139. package/asset/normalize.js.map +1 -0
  140. package/asset/normalize.test.d.ts +1 -0
  141. package/asset/normalize.test.js +263 -0
  142. package/asset/normalize.test.js.map +1 -0
  143. package/asset/types.d.ts +2 -0
  144. package/asset/types.js +0 -0
  145. package/constants.d.ts +1 -0
  146. package/constants.js +7 -5
  147. package/constants.js.map +1 -1
  148. package/contentEntry/ContentEntryBindingsResolver.d.ts +30 -0
  149. package/contentEntry/ContentEntryBindingsResolver.js +45 -0
  150. package/contentEntry/ContentEntryBindingsResolver.js.map +1 -0
  151. package/contentEntry/ContentEntryCache.d.ts +46 -0
  152. package/contentEntry/ContentEntryCache.js +50 -0
  153. package/contentEntry/ContentEntryCache.js.map +1 -0
  154. package/contentEntry/resolveContentEntry.d.ts +74 -0
  155. package/contentEntry/resolveContentEntry.js +53 -0
  156. package/contentEntry/resolveContentEntry.js.map +1 -0
  157. package/contentEntry/resolveContentEntry.test.d.ts +1 -0
  158. package/contentEntry/resolveContentEntry.test.js +213 -0
  159. package/contentEntry/resolveContentEntry.test.js.map +1 -0
  160. package/createComponent.d.ts +5 -0
  161. package/createComponent.js +32 -0
  162. package/createComponent.js.map +1 -0
  163. package/createElement.js +5 -6
  164. package/createElement.js.map +1 -1
  165. package/createInput.d.ts +132 -16
  166. package/createInput.js +92 -111
  167. package/createInput.js.map +1 -1
  168. package/createTheme.js +2 -3
  169. package/createTheme.js.map +1 -1
  170. package/dataProviders/ApiClient.d.ts +10 -5
  171. package/dataProviders/ApiClient.js +40 -49
  172. package/dataProviders/ApiClient.js.map +1 -1
  173. package/dataProviders/DefaultDataProvider.d.ts +7 -3
  174. package/dataProviders/DefaultDataProvider.js +91 -45
  175. package/dataProviders/DefaultDataProvider.js.map +1 -1
  176. package/dataProviders/GET_EXPERIMENT_PAUSED.d.ts +1 -0
  177. package/dataProviders/GET_EXPERIMENT_PAUSED.js +17 -0
  178. package/dataProviders/GET_EXPERIMENT_PAUSED.js.map +1 -0
  179. package/dataProviders/GET_PAGE_BY_ID.d.ts +1 -1
  180. package/dataProviders/GET_PAGE_BY_ID.js +4 -1
  181. package/dataProviders/GET_PAGE_BY_ID.js.map +1 -1
  182. package/dataProviders/GET_PAGE_BY_PATH.d.ts +1 -1
  183. package/dataProviders/GET_PAGE_BY_PATH.js +4 -1
  184. package/dataProviders/GET_PAGE_BY_PATH.js.map +1 -1
  185. package/dataProviders/GET_PAGE_EXPERIMENT.d.ts +1 -0
  186. package/dataProviders/GET_PAGE_EXPERIMENT.js +32 -0
  187. package/dataProviders/GET_PAGE_EXPERIMENT.js.map +1 -0
  188. package/dataProviders/GET_VARIANT_CONTENT.d.ts +1 -0
  189. package/dataProviders/GET_VARIANT_CONTENT.js +24 -0
  190. package/dataProviders/GET_VARIANT_CONTENT.js.map +1 -0
  191. package/dataProviders/LIST_PUBLISHED_PAGES.d.ts +1 -1
  192. package/dataProviders/LIST_PUBLISHED_PAGES.js +17 -5
  193. package/dataProviders/LIST_PUBLISHED_PAGES.js.map +1 -1
  194. package/dataProviders/NullDataProvider.d.ts +16 -4
  195. package/dataProviders/NullDataProvider.js +30 -13
  196. package/dataProviders/NullDataProvider.js.map +1 -1
  197. package/dataProviders/RedirectsProvider.js +24 -27
  198. package/dataProviders/RedirectsProvider.js.map +1 -1
  199. package/defaultBreakpoints.js +43 -22
  200. package/defaultBreakpoints.js.map +1 -1
  201. package/documentOperations/$addElementReferenceToParent.d.ts +1 -1
  202. package/documentOperations/$addElementReferenceToParent.js +30 -26
  203. package/documentOperations/$addElementReferenceToParent.js.map +1 -1
  204. package/documentOperations/AddElement.js +8 -7
  205. package/documentOperations/AddElement.js.map +1 -1
  206. package/documentOperations/AddToParent.d.ts +2 -2
  207. package/documentOperations/AddToParent.js +14 -13
  208. package/documentOperations/AddToParent.js.map +1 -1
  209. package/documentOperations/IDocumentOperation.js +0 -3
  210. package/documentOperations/RemoveElement.js +10 -7
  211. package/documentOperations/RemoveElement.js.map +1 -1
  212. package/documentOperations/SetGlobalInputBinding.js +23 -22
  213. package/documentOperations/SetGlobalInputBinding.js.map +1 -1
  214. package/documentOperations/SetGlobalStyleBinding.js +23 -23
  215. package/documentOperations/SetGlobalStyleBinding.js.map +1 -1
  216. package/documentOperations/SetInputBindingOverride.js +30 -29
  217. package/documentOperations/SetInputBindingOverride.js.map +1 -1
  218. package/documentOperations/SetStyleBindingOverride.js +30 -31
  219. package/documentOperations/SetStyleBindingOverride.js.map +1 -1
  220. package/documentOperations/index.js +9 -8
  221. package/documentOperations/index.js.map +1 -1
  222. package/experiments/PostHogAnalyticsProvider.d.ts +31 -0
  223. package/experiments/PostHogAnalyticsProvider.js +28 -0
  224. package/experiments/PostHogAnalyticsProvider.js.map +1 -0
  225. package/experiments/analyticsProvider.d.ts +4 -0
  226. package/experiments/analyticsProvider.js +11 -0
  227. package/experiments/analyticsProvider.js.map +1 -0
  228. package/experiments/bucketing.d.ts +27 -0
  229. package/experiments/bucketing.js +91 -0
  230. package/experiments/bucketing.js.map +1 -0
  231. package/experiments/bucketing.test.d.ts +1 -0
  232. package/experiments/bucketing.test.js +183 -0
  233. package/experiments/bucketing.test.js.map +1 -0
  234. package/experiments/index.d.ts +5 -0
  235. package/experiments/index.js +5 -0
  236. package/experiments/render.d.ts +79 -0
  237. package/experiments/render.js +149 -0
  238. package/experiments/render.js.map +1 -0
  239. package/experiments/types.d.ts +108 -0
  240. package/experiments/types.js +4 -0
  241. package/experiments/types.js.map +1 -0
  242. package/findMatchingAstNode.js +11 -13
  243. package/findMatchingAstNode.js.map +1 -1
  244. package/generateElementId.d.ts +1 -1
  245. package/generateElementId.js +2 -2
  246. package/generateElementId.js.map +1 -1
  247. package/headersProvider.js +4 -3
  248. package/headersProvider.js.map +1 -1
  249. package/image/geometry.d.ts +54 -0
  250. package/image/geometry.js +112 -0
  251. package/image/geometry.js.map +1 -0
  252. package/image/geometry.test.d.ts +1 -0
  253. package/image/geometry.test.js +190 -0
  254. package/image/geometry.test.js.map +1 -0
  255. package/image/index.d.ts +3 -0
  256. package/image/index.js +3 -0
  257. package/image/presets.d.ts +6 -0
  258. package/image/presets.js +20 -0
  259. package/image/presets.js.map +1 -0
  260. package/image/types.d.ts +10 -0
  261. package/image/types.js +0 -0
  262. package/index.d.ts +12 -1
  263. package/index.js +11 -3
  264. package/jsonPatch.d.ts +1 -1
  265. package/jsonPatch.js +5 -9
  266. package/jsonPatch.js.map +1 -1
  267. package/messages.js +12 -11
  268. package/messages.js.map +1 -1
  269. package/messenger/MessageOrigin.js +12 -11
  270. package/messenger/MessageOrigin.js.map +1 -1
  271. package/messenger/Messenger.js +59 -70
  272. package/messenger/Messenger.js.map +1 -1
  273. package/messenger/index.js +0 -2
  274. package/package.json +19 -22
  275. package/registerComponentGroup.js +5 -6
  276. package/registerComponentGroup.js.map +1 -1
  277. package/tokenBinding.d.ts +26 -0
  278. package/tokenBinding.js +15 -0
  279. package/tokenBinding.js.map +1 -0
  280. package/tokenBinding.test.d.ts +1 -0
  281. package/tokenBinding.test.js +165 -0
  282. package/tokenBinding.test.js.map +1 -0
  283. package/types/ShorthandCssProperties.js +0 -3
  284. package/types/WebsiteBuilderTheme.d.ts +54 -45
  285. package/types/WebsiteBuilderTheme.js +0 -3
  286. package/types.d.ts +294 -13
  287. package/types.js +0 -3
  288. package/IBindingsUpdater.js.map +0 -1
  289. package/IRedirects.js.map +0 -1
  290. package/documentOperations/IDocumentOperation.js.map +0 -1
  291. package/index.js.map +0 -1
  292. package/lexical/createDefaultLexicalTheme.d.ts +0 -2
  293. package/lexical/createDefaultLexicalTheme.js +0 -84
  294. package/lexical/createDefaultLexicalTheme.js.map +0 -1
  295. package/lexical/createLexicalTheme.d.ts +0 -2
  296. package/lexical/createLexicalTheme.js +0 -7
  297. package/lexical/createLexicalTheme.js.map +0 -1
  298. package/lexical/deepMerge.d.ts +0 -1
  299. package/lexical/deepMerge.js +0 -23
  300. package/lexical/deepMerge.js.map +0 -1
  301. package/messenger/index.js.map +0 -1
  302. package/types/LexicalEditorTheme.d.ts +0 -2
  303. package/types/LexicalEditorTheme.js +0 -3
  304. package/types/LexicalEditorTheme.js.map +0 -1
  305. package/types/ShorthandCssProperties.js.map +0 -1
  306. package/types/WebsiteBuilderTheme.js.map +0 -1
  307. package/types.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"names":["BindingsResolver","createSlotInput","createTextInput","ComponentManifestToAstConverter","describe","baseElement","id","type","component","name","rootElement","it","state","user","bindings","inputs","text","expression","static","styles","paddingTop","inputAst","convert","resolver","resolved","resolveElement","element","elementBindings","expect","toBe","toEqual","root","children","list","qizw1hgqjvj8g5a43szzc","title","backgroundColor","marginTop","cwld8kxy0qhhtaql42lr5","elements","parent","slot","renderer","label","fields","leftColumn","rightColumn","toBeUndefined","defaultValue","onResolved","value","input","products","$repeat","toHaveLength","invalid"],"sources":["BindingsResolver.test.ts"],"sourcesContent":["import { BindingsResolver } from \"./BindingsResolver.js\";\nimport type {\n DocumentElement,\n DocumentState,\n DocumentElementBindings,\n DocumentBindings\n} from \"~/types.js\";\nimport { createSlotInput, createTextInput } from \"~/createInput.js\";\nimport { ComponentManifestToAstConverter } from \"~/ComponentManifestToAstConverter.js\";\n\ndescribe(\"BindingsResolver\", () => {\n const baseElement: DocumentElement = {\n id: \"test1\",\n type: \"Webiny/Element\",\n component: {\n name: \"Webiny/Text\"\n }\n };\n\n const rootElement: DocumentElement = {\n id: \"root\",\n type: \"Webiny/Element\",\n component: {\n name: \"Webiny/Root\"\n }\n };\n\n it(\"resolves input with expression binding\", () => {\n const state: DocumentState = {\n user: { name: \"Alice\" }\n };\n\n const bindings: DocumentElementBindings = {\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n expression: \"$state.user.name\",\n static: \"Static fallback\"\n }\n },\n styles: {\n paddingTop: {\n static: \"10px\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved.inputs.text).toBe(\"Alice\");\n expect(resolved.styles).toEqual({ paddingTop: \"10px\" });\n });\n\n it(\"resolves nested objects\", () => {\n const state: DocumentState = {};\n\n const bindings: DocumentBindings = {\n root: {\n inputs: {\n children: {\n id: \"children\",\n type: \"slot\",\n list: true,\n static: [\"qizw1hgqjvj8g5a43szzc\"]\n }\n }\n },\n qizw1hgqjvj8g5a43szzc: {\n inputs: {\n title: {\n id: \"title\",\n static: \"Default Columns Title\",\n type: \"text\"\n },\n \"leftColumn/0\": {\n id: \"slot\",\n static: [\"7znyr9z2cpizegnrk2rhu\"],\n type: \"slot\",\n list: true\n },\n \"rightColumn/0\": {\n id: \"slot\",\n static: [\"cwld8kxy0qhhtaql42lr5\"],\n type: \"slot\",\n list: true\n }\n },\n styles: {\n paddingTop: {\n static: \"20px\"\n },\n backgroundColor: {\n static: \"#5c9a12\"\n }\n }\n },\n \"7znyr9z2cpizegnrk2rhu\": {\n inputs: {\n title: {\n id: \"title\",\n static: \"Left Column Title\",\n type: \"text\"\n },\n children: {\n id: \"slot\",\n static: [],\n type: \"slot\",\n list: true\n }\n },\n styles: {\n backgroundColor: {\n static: \"red\"\n },\n marginTop: {\n static: \"20px\"\n }\n }\n },\n cwld8kxy0qhhtaql42lr5: {\n inputs: {\n title: {\n id: \"title\",\n static: \"Right Column Title\",\n type: \"text\"\n },\n children: {\n id: \"slot\",\n static: [],\n type: \"slot\",\n list: true\n }\n },\n styles: {\n backgroundColor: {\n static: \"blue\"\n },\n marginTop: {\n static: \"20px\"\n }\n }\n }\n };\n\n const elements = {\n root: {\n type: \"Webiny/Element\",\n id: \"root\",\n component: {\n name: \"Webiny/Root\"\n }\n },\n qizw1hgqjvj8g5a43szzc: {\n type: \"Webiny/Element\",\n id: \"qizw1hgqjvj8g5a43szzc\",\n parent: {\n id: \"root\",\n slot: \"children\"\n },\n component: {\n name: \"Webiny/TwoColumns\"\n }\n },\n \"7znyr9z2cpizegnrk2rhu\": {\n type: \"Webiny/Element\",\n id: \"7znyr9z2cpizegnrk2rhu\",\n parent: {\n id: \"qizw1hgqjvj8g5a43szzc\",\n slot: \"leftColumn/0\"\n },\n component: {\n name: \"Webiny/TextWithDropzone\"\n }\n },\n cwld8kxy0qhhtaql42lr5: {\n type: \"Webiny/Element\",\n id: \"cwld8kxy0qhhtaql42lr5\",\n parent: {\n id: \"qizw1hgqjvj8g5a43szzc\",\n slot: \"rightColumn/0\"\n },\n component: {\n name: \"Webiny/TextWithDropzone\"\n }\n }\n };\n\n const inputs = [\n {\n type: \"text\",\n renderer: \"Webiny/Input\",\n name: \"title\",\n label: \"Title\",\n fields: []\n },\n {\n type: \"slot\",\n list: true,\n renderer: \"Webiny/Slot\",\n name: \"leftColumn\",\n fields: []\n },\n {\n type: \"slot\",\n list: true,\n renderer: \"Webiny/Slot\",\n name: \"rightColumn\",\n fields: []\n }\n ];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: elements[\"qizw1hgqjvj8g5a43szzc\"] as DocumentElement,\n inputAst,\n elementBindings: bindings[\"qizw1hgqjvj8g5a43szzc\"]\n });\n\n expect(resolved.inputs.title).toBe(\"Default Columns Title\");\n expect(resolved.inputs.leftColumn).toEqual([[\"7znyr9z2cpizegnrk2rhu\"]]);\n expect(resolved.inputs.rightColumn).toEqual([[\"cwld8kxy0qhhtaql42lr5\"]]);\n });\n\n it(\"falls back to static if no expression is provided\", () => {\n const state: DocumentState = {};\n\n const bindings: DocumentElementBindings = {\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n static: \"Static only\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved.inputs.text).toBe(\"Static only\");\n });\n\n it(\"uses undefined if expression fails and no static exists\", () => {\n const state: DocumentState = {};\n\n const bindings: DocumentElementBindings = {\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n static: \"Fallback\",\n expression: \"$.unknown.value\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved.inputs.text).toBeUndefined();\n });\n\n it(\"uses input's `defaultValue` if binding doesn't exist\", () => {\n const state: DocumentState = {};\n\n const bindings: DocumentElementBindings = {\n inputs: {}\n };\n\n const inputs = [createSlotInput({ name: \"children\", defaultValue: [] })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: rootElement,\n inputAst,\n elementBindings: bindings,\n onResolved(value, input) {\n if (input.type === \"slot\") {\n return \"slot\";\n }\n\n return value;\n }\n });\n\n expect(resolved.inputs.children).toEqual(\"slot\");\n });\n\n it(\"handles $repeat using expression and maps items\", () => {\n const state: DocumentState = {\n products: [{ title: \"Shirt\" }, { title: \"Hat\" }]\n };\n\n const bindings: DocumentElementBindings = {\n $repeat: {\n expression: \"$state.products\"\n },\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n expression: \"$.title\",\n static: \"Unnamed\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const resolved = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved).toHaveLength(2);\n expect(resolved[0].inputs.text).toBe(\"Shirt\");\n expect(resolved[1].inputs.text).toBe(\"Hat\");\n });\n\n it(\"returns empty array if $repeat doesn't resolve to an array\", () => {\n const state: DocumentState = {\n invalid: 42\n };\n\n const bindings: DocumentElementBindings = {\n $repeat: {\n expression: \"$state.invalid\"\n },\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n static: \"Should not be used\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const resolved = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved).toEqual([]);\n });\n\n it(\"resolves binding that refers to a specific array index\", () => {\n const state: DocumentState = {\n list: [{ text: \"First item text\" }, { text: \"Second item text\" }]\n };\n\n const bindings: DocumentElementBindings = {\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n static: \"Static fallback\",\n expression: \"$state.list.0.text\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved.inputs.text).toBe(\"First item text\");\n });\n});\n"],"mappings":"AAAA,SAASA,gBAAgB;AAOzB,SAASC,eAAe,EAAEC,eAAe;AACzC,SAASC,+BAA+B;AAExCC,QAAQ,CAAC,kBAAkB,EAAE,MAAM;EAC/B,MAAMC,WAA4B,GAAG;IACjCC,EAAE,EAAE,OAAO;IACXC,IAAI,EAAE,gBAAgB;IACtBC,SAAS,EAAE;MACPC,IAAI,EAAE;IACV;EACJ,CAAC;EAED,MAAMC,WAA4B,GAAG;IACjCJ,EAAE,EAAE,MAAM;IACVC,IAAI,EAAE,gBAAgB;IACtBC,SAAS,EAAE;MACPC,IAAI,EAAE;IACV;EACJ,CAAC;EAEDE,EAAE,CAAC,wCAAwC,EAAE,MAAM;IAC/C,MAAMC,KAAoB,GAAG;MACzBC,IAAI,EAAE;QAAEJ,IAAI,EAAE;MAAQ;IAC1B,CAAC;IAED,MAAMK,QAAiC,GAAG;MACtCC,MAAM,EAAE;QACJC,IAAI,EAAE;UACFV,EAAE,EAAE,MAAM;UACVC,IAAI,EAAE,MAAM;UACZU,UAAU,EAAE,kBAAkB;UAC9BC,MAAM,EAAE;QACZ;MACJ,CAAC;MACDC,MAAM,EAAE;QACJC,UAAU,EAAE;UACRF,MAAM,EAAE;QACZ;MACJ;IACJ,CAAC;IAED,MAAMH,MAAM,GAAG,CAACb,eAAe,CAAC;MAAEO,IAAI,EAAE;IAAO,CAAC,CAAC,CAAC;IAClD,MAAMY,QAAQ,GAAGlB,+BAA+B,CAACmB,OAAO,CAACP,MAAM,CAAC;IAChE,MAAMQ,QAAQ,GAAG,IAAIvB,gBAAgB,CAACY,KAAK,CAAC;IAC5C,MAAM,CAACY,QAAQ,CAAC,GAAGD,QAAQ,CAACE,cAAc,CAAC;MACvCC,OAAO,EAAErB,WAAW;MACpBgB,QAAQ;MACRM,eAAe,EAAEb;IACrB,CAAC,CAAC;IAEFc,MAAM,CAACJ,QAAQ,CAACT,MAAM,CAACC,IAAI,CAAC,CAACa,IAAI,CAAC,OAAO,CAAC;IAC1CD,MAAM,CAACJ,QAAQ,CAACL,MAAM,CAAC,CAACW,OAAO,CAAC;MAAEV,UAAU,EAAE;IAAO,CAAC,CAAC;EAC3D,CAAC,CAAC;EAEFT,EAAE,CAAC,yBAAyB,EAAE,MAAM;IAChC,MAAMC,KAAoB,GAAG,CAAC,CAAC;IAE/B,MAAME,QAA0B,GAAG;MAC/BiB,IAAI,EAAE;QACFhB,MAAM,EAAE;UACJiB,QAAQ,EAAE;YACN1B,EAAE,EAAE,UAAU;YACdC,IAAI,EAAE,MAAM;YACZ0B,IAAI,EAAE,IAAI;YACVf,MAAM,EAAE,CAAC,uBAAuB;UACpC;QACJ;MACJ,CAAC;MACDgB,qBAAqB,EAAE;QACnBnB,MAAM,EAAE;UACJoB,KAAK,EAAE;YACH7B,EAAE,EAAE,OAAO;YACXY,MAAM,EAAE,uBAAuB;YAC/BX,IAAI,EAAE;UACV,CAAC;UACD,cAAc,EAAE;YACZD,EAAE,EAAE,MAAM;YACVY,MAAM,EAAE,CAAC,uBAAuB,CAAC;YACjCX,IAAI,EAAE,MAAM;YACZ0B,IAAI,EAAE;UACV,CAAC;UACD,eAAe,EAAE;YACb3B,EAAE,EAAE,MAAM;YACVY,MAAM,EAAE,CAAC,uBAAuB,CAAC;YACjCX,IAAI,EAAE,MAAM;YACZ0B,IAAI,EAAE;UACV;QACJ,CAAC;QACDd,MAAM,EAAE;UACJC,UAAU,EAAE;YACRF,MAAM,EAAE;UACZ,CAAC;UACDkB,eAAe,EAAE;YACblB,MAAM,EAAE;UACZ;QACJ;MACJ,CAAC;MACD,uBAAuB,EAAE;QACrBH,MAAM,EAAE;UACJoB,KAAK,EAAE;YACH7B,EAAE,EAAE,OAAO;YACXY,MAAM,EAAE,mBAAmB;YAC3BX,IAAI,EAAE;UACV,CAAC;UACDyB,QAAQ,EAAE;YACN1B,EAAE,EAAE,MAAM;YACVY,MAAM,EAAE,EAAE;YACVX,IAAI,EAAE,MAAM;YACZ0B,IAAI,EAAE;UACV;QACJ,CAAC;QACDd,MAAM,EAAE;UACJiB,eAAe,EAAE;YACblB,MAAM,EAAE;UACZ,CAAC;UACDmB,SAAS,EAAE;YACPnB,MAAM,EAAE;UACZ;QACJ;MACJ,CAAC;MACDoB,qBAAqB,EAAE;QACnBvB,MAAM,EAAE;UACJoB,KAAK,EAAE;YACH7B,EAAE,EAAE,OAAO;YACXY,MAAM,EAAE,oBAAoB;YAC5BX,IAAI,EAAE;UACV,CAAC;UACDyB,QAAQ,EAAE;YACN1B,EAAE,EAAE,MAAM;YACVY,MAAM,EAAE,EAAE;YACVX,IAAI,EAAE,MAAM;YACZ0B,IAAI,EAAE;UACV;QACJ,CAAC;QACDd,MAAM,EAAE;UACJiB,eAAe,EAAE;YACblB,MAAM,EAAE;UACZ,CAAC;UACDmB,SAAS,EAAE;YACPnB,MAAM,EAAE;UACZ;QACJ;MACJ;IACJ,CAAC;IAED,MAAMqB,QAAQ,GAAG;MACbR,IAAI,EAAE;QACFxB,IAAI,EAAE,gBAAgB;QACtBD,EAAE,EAAE,MAAM;QACVE,SAAS,EAAE;UACPC,IAAI,EAAE;QACV;MACJ,CAAC;MACDyB,qBAAqB,EAAE;QACnB3B,IAAI,EAAE,gBAAgB;QACtBD,EAAE,EAAE,uBAAuB;QAC3BkC,MAAM,EAAE;UACJlC,EAAE,EAAE,MAAM;UACVmC,IAAI,EAAE;QACV,CAAC;QACDjC,SAAS,EAAE;UACPC,IAAI,EAAE;QACV;MACJ,CAAC;MACD,uBAAuB,EAAE;QACrBF,IAAI,EAAE,gBAAgB;QACtBD,EAAE,EAAE,uBAAuB;QAC3BkC,MAAM,EAAE;UACJlC,EAAE,EAAE,uBAAuB;UAC3BmC,IAAI,EAAE;QACV,CAAC;QACDjC,SAAS,EAAE;UACPC,IAAI,EAAE;QACV;MACJ,CAAC;MACD6B,qBAAqB,EAAE;QACnB/B,IAAI,EAAE,gBAAgB;QACtBD,EAAE,EAAE,uBAAuB;QAC3BkC,MAAM,EAAE;UACJlC,EAAE,EAAE,uBAAuB;UAC3BmC,IAAI,EAAE;QACV,CAAC;QACDjC,SAAS,EAAE;UACPC,IAAI,EAAE;QACV;MACJ;IACJ,CAAC;IAED,MAAMM,MAAM,GAAG,CACX;MACIR,IAAI,EAAE,MAAM;MACZmC,QAAQ,EAAE,cAAc;MACxBjC,IAAI,EAAE,OAAO;MACbkC,KAAK,EAAE,OAAO;MACdC,MAAM,EAAE;IACZ,CAAC,EACD;MACIrC,IAAI,EAAE,MAAM;MACZ0B,IAAI,EAAE,IAAI;MACVS,QAAQ,EAAE,aAAa;MACvBjC,IAAI,EAAE,YAAY;MAClBmC,MAAM,EAAE;IACZ,CAAC,EACD;MACIrC,IAAI,EAAE,MAAM;MACZ0B,IAAI,EAAE,IAAI;MACVS,QAAQ,EAAE,aAAa;MACvBjC,IAAI,EAAE,aAAa;MACnBmC,MAAM,EAAE;IACZ,CAAC,CACJ;IACD,MAAMvB,QAAQ,GAAGlB,+BAA+B,CAACmB,OAAO,CAACP,MAAM,CAAC;IAChE,MAAMQ,QAAQ,GAAG,IAAIvB,gBAAgB,CAACY,KAAK,CAAC;IAC5C,MAAM,CAACY,QAAQ,CAAC,GAAGD,QAAQ,CAACE,cAAc,CAAC;MACvCC,OAAO,EAAEa,QAAQ,CAAC,uBAAuB,CAAoB;MAC7DlB,QAAQ;MACRM,eAAe,EAAEb,QAAQ,CAAC,uBAAuB;IACrD,CAAC,CAAC;IAEFc,MAAM,CAACJ,QAAQ,CAACT,MAAM,CAACoB,KAAK,CAAC,CAACN,IAAI,CAAC,uBAAuB,CAAC;IAC3DD,MAAM,CAACJ,QAAQ,CAACT,MAAM,CAAC8B,UAAU,CAAC,CAACf,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC;IACvEF,MAAM,CAACJ,QAAQ,CAACT,MAAM,CAAC+B,WAAW,CAAC,CAAChB,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC;EAC5E,CAAC,CAAC;EAEFnB,EAAE,CAAC,mDAAmD,EAAE,MAAM;IAC1D,MAAMC,KAAoB,GAAG,CAAC,CAAC;IAE/B,MAAME,QAAiC,GAAG;MACtCC,MAAM,EAAE;QACJC,IAAI,EAAE;UACFV,EAAE,EAAE,MAAM;UACVC,IAAI,EAAE,MAAM;UACZW,MAAM,EAAE;QACZ;MACJ;IACJ,CAAC;IAED,MAAMH,MAAM,GAAG,CAACb,eAAe,CAAC;MAAEO,IAAI,EAAE;IAAO,CAAC,CAAC,CAAC;IAClD,MAAMY,QAAQ,GAAGlB,+BAA+B,CAACmB,OAAO,CAACP,MAAM,CAAC;IAChE,MAAMQ,QAAQ,GAAG,IAAIvB,gBAAgB,CAACY,KAAK,CAAC;IAC5C,MAAM,CAACY,QAAQ,CAAC,GAAGD,QAAQ,CAACE,cAAc,CAAC;MACvCC,OAAO,EAAErB,WAAW;MACpBgB,QAAQ;MACRM,eAAe,EAAEb;IACrB,CAAC,CAAC;IAEFc,MAAM,CAACJ,QAAQ,CAACT,MAAM,CAACC,IAAI,CAAC,CAACa,IAAI,CAAC,aAAa,CAAC;EACpD,CAAC,CAAC;EAEFlB,EAAE,CAAC,yDAAyD,EAAE,MAAM;IAChE,MAAMC,KAAoB,GAAG,CAAC,CAAC;IAE/B,MAAME,QAAiC,GAAG;MACtCC,MAAM,EAAE;QACJC,IAAI,EAAE;UACFV,EAAE,EAAE,MAAM;UACVC,IAAI,EAAE,MAAM;UACZW,MAAM,EAAE,UAAU;UAClBD,UAAU,EAAE;QAChB;MACJ;IACJ,CAAC;IAED,MAAMF,MAAM,GAAG,CAACb,eAAe,CAAC;MAAEO,IAAI,EAAE;IAAO,CAAC,CAAC,CAAC;IAClD,MAAMY,QAAQ,GAAGlB,+BAA+B,CAACmB,OAAO,CAACP,MAAM,CAAC;IAChE,MAAMQ,QAAQ,GAAG,IAAIvB,gBAAgB,CAACY,KAAK,CAAC;IAC5C,MAAM,CAACY,QAAQ,CAAC,GAAGD,QAAQ,CAACE,cAAc,CAAC;MACvCC,OAAO,EAAErB,WAAW;MACpBgB,QAAQ;MACRM,eAAe,EAAEb;IACrB,CAAC,CAAC;IAEFc,MAAM,CAACJ,QAAQ,CAACT,MAAM,CAACC,IAAI,CAAC,CAAC+B,aAAa,CAAC,CAAC;EAChD,CAAC,CAAC;EAEFpC,EAAE,CAAC,sDAAsD,EAAE,MAAM;IAC7D,MAAMC,KAAoB,GAAG,CAAC,CAAC;IAE/B,MAAME,QAAiC,GAAG;MACtCC,MAAM,EAAE,CAAC;IACb,CAAC;IAED,MAAMA,MAAM,GAAG,CAACd,eAAe,CAAC;MAAEQ,IAAI,EAAE,UAAU;MAAEuC,YAAY,EAAE;IAAG,CAAC,CAAC,CAAC;IACxE,MAAM3B,QAAQ,GAAGlB,+BAA+B,CAACmB,OAAO,CAACP,MAAM,CAAC;IAChE,MAAMQ,QAAQ,GAAG,IAAIvB,gBAAgB,CAACY,KAAK,CAAC;IAC5C,MAAM,CAACY,QAAQ,CAAC,GAAGD,QAAQ,CAACE,cAAc,CAAC;MACvCC,OAAO,EAAEhB,WAAW;MACpBW,QAAQ;MACRM,eAAe,EAAEb,QAAQ;MACzBmC,UAAUA,CAACC,KAAK,EAAEC,KAAK,EAAE;QACrB,IAAIA,KAAK,CAAC5C,IAAI,KAAK,MAAM,EAAE;UACvB,OAAO,MAAM;QACjB;QAEA,OAAO2C,KAAK;MAChB;IACJ,CAAC,CAAC;IAEFtB,MAAM,CAACJ,QAAQ,CAACT,MAAM,CAACiB,QAAQ,CAAC,CAACF,OAAO,CAAC,MAAM,CAAC;EACpD,CAAC,CAAC;EAEFnB,EAAE,CAAC,iDAAiD,EAAE,MAAM;IACxD,MAAMC,KAAoB,GAAG;MACzBwC,QAAQ,EAAE,CAAC;QAAEjB,KAAK,EAAE;MAAQ,CAAC,EAAE;QAAEA,KAAK,EAAE;MAAM,CAAC;IACnD,CAAC;IAED,MAAMrB,QAAiC,GAAG;MACtCuC,OAAO,EAAE;QACLpC,UAAU,EAAE;MAChB,CAAC;MACDF,MAAM,EAAE;QACJC,IAAI,EAAE;UACFV,EAAE,EAAE,MAAM;UACVC,IAAI,EAAE,MAAM;UACZU,UAAU,EAAE,SAAS;UACrBC,MAAM,EAAE;QACZ;MACJ;IACJ,CAAC;IAED,MAAMH,MAAM,GAAG,CAACb,eAAe,CAAC;MAAEO,IAAI,EAAE;IAAO,CAAC,CAAC,CAAC;IAClD,MAAMY,QAAQ,GAAGlB,+BAA+B,CAACmB,OAAO,CAACP,MAAM,CAAC;IAChE,MAAMQ,QAAQ,GAAG,IAAIvB,gBAAgB,CAACY,KAAK,CAAC;IAC5C,MAAMY,QAAQ,GAAGD,QAAQ,CAACE,cAAc,CAAC;MACrCC,OAAO,EAAErB,WAAW;MACpBgB,QAAQ;MACRM,eAAe,EAAEb;IACrB,CAAC,CAAC;IAEFc,MAAM,CAACJ,QAAQ,CAAC,CAAC8B,YAAY,CAAC,CAAC,CAAC;IAChC1B,MAAM,CAACJ,QAAQ,CAAC,CAAC,CAAC,CAACT,MAAM,CAACC,IAAI,CAAC,CAACa,IAAI,CAAC,OAAO,CAAC;IAC7CD,MAAM,CAACJ,QAAQ,CAAC,CAAC,CAAC,CAACT,MAAM,CAACC,IAAI,CAAC,CAACa,IAAI,CAAC,KAAK,CAAC;EAC/C,CAAC,CAAC;EAEFlB,EAAE,CAAC,4DAA4D,EAAE,MAAM;IACnE,MAAMC,KAAoB,GAAG;MACzB2C,OAAO,EAAE;IACb,CAAC;IAED,MAAMzC,QAAiC,GAAG;MACtCuC,OAAO,EAAE;QACLpC,UAAU,EAAE;MAChB,CAAC;MACDF,MAAM,EAAE;QACJC,IAAI,EAAE;UACFV,EAAE,EAAE,MAAM;UACVC,IAAI,EAAE,MAAM;UACZW,MAAM,EAAE;QACZ;MACJ;IACJ,CAAC;IAED,MAAMH,MAAM,GAAG,CAACb,eAAe,CAAC;MAAEO,IAAI,EAAE;IAAO,CAAC,CAAC,CAAC;IAClD,MAAMY,QAAQ,GAAGlB,+BAA+B,CAACmB,OAAO,CAACP,MAAM,CAAC;IAChE,MAAMQ,QAAQ,GAAG,IAAIvB,gBAAgB,CAACY,KAAK,CAAC;IAC5C,MAAMY,QAAQ,GAAGD,QAAQ,CAACE,cAAc,CAAC;MACrCC,OAAO,EAAErB,WAAW;MACpBgB,QAAQ;MACRM,eAAe,EAAEb;IACrB,CAAC,CAAC;IAEFc,MAAM,CAACJ,QAAQ,CAAC,CAACM,OAAO,CAAC,EAAE,CAAC;EAChC,CAAC,CAAC;EAEFnB,EAAE,CAAC,wDAAwD,EAAE,MAAM;IAC/D,MAAMC,KAAoB,GAAG;MACzBqB,IAAI,EAAE,CAAC;QAAEjB,IAAI,EAAE;MAAkB,CAAC,EAAE;QAAEA,IAAI,EAAE;MAAmB,CAAC;IACpE,CAAC;IAED,MAAMF,QAAiC,GAAG;MACtCC,MAAM,EAAE;QACJC,IAAI,EAAE;UACFV,EAAE,EAAE,MAAM;UACVC,IAAI,EAAE,MAAM;UACZW,MAAM,EAAE,iBAAiB;UACzBD,UAAU,EAAE;QAChB;MACJ;IACJ,CAAC;IAED,MAAMF,MAAM,GAAG,CAACb,eAAe,CAAC;MAAEO,IAAI,EAAE;IAAO,CAAC,CAAC,CAAC;IAClD,MAAMY,QAAQ,GAAGlB,+BAA+B,CAACmB,OAAO,CAACP,MAAM,CAAC;IAChE,MAAMQ,QAAQ,GAAG,IAAIvB,gBAAgB,CAACY,KAAK,CAAC;IAC5C,MAAM,CAACY,QAAQ,CAAC,GAAGD,QAAQ,CAACE,cAAc,CAAC;MACvCC,OAAO,EAAErB,WAAW;MACpBgB,QAAQ;MACRM,eAAe,EAAEb;IACrB,CAAC,CAAC;IAEFc,MAAM,CAACJ,QAAQ,CAACT,MAAM,CAACC,IAAI,CAAC,CAACa,IAAI,CAAC,iBAAiB,CAAC;EACxD,CAAC,CAAC;AACN,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"BindingsResolver.test.js","sources":["../src/BindingsResolver.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { BindingsResolver } from \"./BindingsResolver.js\";\nimport type {\n DocumentElement,\n DocumentState,\n DocumentElementBindings,\n DocumentBindings\n} from \"~/types.js\";\nimport { createSlotInput, createTextInput } from \"~/createInput.js\";\nimport { ComponentManifestToAstConverter } from \"~/ComponentManifestToAstConverter.js\";\n\ndescribe(\"BindingsResolver\", () => {\n const baseElement: DocumentElement = {\n id: \"test1\",\n type: \"Webiny/Element\",\n component: {\n name: \"Webiny/Text\"\n }\n };\n\n const rootElement: DocumentElement = {\n id: \"root\",\n type: \"Webiny/Element\",\n component: {\n name: \"Webiny/Root\"\n }\n };\n\n it(\"resolves input with expression binding\", () => {\n const state: DocumentState = {\n user: { name: \"Alice\" }\n };\n\n const bindings: DocumentElementBindings = {\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n expression: \"$state.user.name\",\n static: \"Static fallback\"\n }\n },\n styles: {\n paddingTop: {\n static: \"10px\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved.inputs.text).toBe(\"Alice\");\n expect(resolved.styles).toEqual({ paddingTop: \"10px\" });\n });\n\n it(\"resolves nested objects\", () => {\n const state: DocumentState = {};\n\n const bindings: DocumentBindings = {\n root: {\n inputs: {\n children: {\n id: \"children\",\n type: \"slot\",\n list: true,\n static: [\"qizw1hgqjvj8g5a43szzc\"]\n }\n }\n },\n qizw1hgqjvj8g5a43szzc: {\n inputs: {\n title: {\n id: \"title\",\n static: \"Default Columns Title\",\n type: \"text\"\n },\n \"leftColumn/0\": {\n id: \"slot\",\n static: [\"7znyr9z2cpizegnrk2rhu\"],\n type: \"slot\",\n list: true\n },\n \"rightColumn/0\": {\n id: \"slot\",\n static: [\"cwld8kxy0qhhtaql42lr5\"],\n type: \"slot\",\n list: true\n }\n },\n styles: {\n paddingTop: {\n static: \"20px\"\n },\n backgroundColor: {\n static: \"#5c9a12\"\n }\n }\n },\n \"7znyr9z2cpizegnrk2rhu\": {\n inputs: {\n title: {\n id: \"title\",\n static: \"Left Column Title\",\n type: \"text\"\n },\n children: {\n id: \"slot\",\n static: [],\n type: \"slot\",\n list: true\n }\n },\n styles: {\n backgroundColor: {\n static: \"red\"\n },\n marginTop: {\n static: \"20px\"\n }\n }\n },\n cwld8kxy0qhhtaql42lr5: {\n inputs: {\n title: {\n id: \"title\",\n static: \"Right Column Title\",\n type: \"text\"\n },\n children: {\n id: \"slot\",\n static: [],\n type: \"slot\",\n list: true\n }\n },\n styles: {\n backgroundColor: {\n static: \"blue\"\n },\n marginTop: {\n static: \"20px\"\n }\n }\n }\n };\n\n const elements = {\n root: {\n type: \"Webiny/Element\",\n id: \"root\",\n component: {\n name: \"Webiny/Root\"\n }\n },\n qizw1hgqjvj8g5a43szzc: {\n type: \"Webiny/Element\",\n id: \"qizw1hgqjvj8g5a43szzc\",\n parent: {\n id: \"root\",\n slot: \"children\"\n },\n component: {\n name: \"Webiny/TwoColumns\"\n }\n },\n \"7znyr9z2cpizegnrk2rhu\": {\n type: \"Webiny/Element\",\n id: \"7znyr9z2cpizegnrk2rhu\",\n parent: {\n id: \"qizw1hgqjvj8g5a43szzc\",\n slot: \"leftColumn/0\"\n },\n component: {\n name: \"Webiny/TextWithDropzone\"\n }\n },\n cwld8kxy0qhhtaql42lr5: {\n type: \"Webiny/Element\",\n id: \"cwld8kxy0qhhtaql42lr5\",\n parent: {\n id: \"qizw1hgqjvj8g5a43szzc\",\n slot: \"rightColumn/0\"\n },\n component: {\n name: \"Webiny/TextWithDropzone\"\n }\n }\n };\n\n const inputs = [\n {\n type: \"text\",\n renderer: \"Webiny/Input\",\n name: \"title\",\n label: \"Title\",\n fields: []\n },\n {\n type: \"slot\",\n list: true,\n renderer: \"Webiny/Slot\",\n name: \"leftColumn\",\n fields: []\n },\n {\n type: \"slot\",\n list: true,\n renderer: \"Webiny/Slot\",\n name: \"rightColumn\",\n fields: []\n }\n ];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: elements[\"qizw1hgqjvj8g5a43szzc\"] as DocumentElement,\n inputAst,\n elementBindings: bindings[\"qizw1hgqjvj8g5a43szzc\"]\n });\n\n expect(resolved.inputs.title).toBe(\"Default Columns Title\");\n expect(resolved.inputs.leftColumn).toEqual([[\"7znyr9z2cpizegnrk2rhu\"]]);\n expect(resolved.inputs.rightColumn).toEqual([[\"cwld8kxy0qhhtaql42lr5\"]]);\n });\n\n it(\"falls back to static if no expression is provided\", () => {\n const state: DocumentState = {};\n\n const bindings: DocumentElementBindings = {\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n static: \"Static only\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved.inputs.text).toBe(\"Static only\");\n });\n\n it(\"uses undefined if expression fails and no static exists\", () => {\n const state: DocumentState = {};\n\n const bindings: DocumentElementBindings = {\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n static: \"Fallback\",\n expression: \"$.unknown.value\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved.inputs.text).toBeUndefined();\n });\n\n it(\"uses input's `defaultValue` if binding doesn't exist\", () => {\n const state: DocumentState = {};\n\n const bindings: DocumentElementBindings = {\n inputs: {}\n };\n\n const inputs = [createSlotInput({ name: \"children\", defaultValue: [] })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: rootElement,\n inputAst,\n elementBindings: bindings,\n onResolved(value, input) {\n if (input.type === \"slot\") {\n return \"slot\";\n }\n\n return value;\n }\n });\n\n expect(resolved.inputs.children).toEqual(\"slot\");\n });\n\n it(\"handles $repeat using expression and maps items\", () => {\n const state: DocumentState = {\n products: [{ title: \"Shirt\" }, { title: \"Hat\" }]\n };\n\n const bindings: DocumentElementBindings = {\n $repeat: {\n expression: \"$state.products\"\n },\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n expression: \"$.title\",\n static: \"Unnamed\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const resolved = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved).toHaveLength(2);\n expect(resolved[0].inputs.text).toBe(\"Shirt\");\n expect(resolved[1].inputs.text).toBe(\"Hat\");\n });\n\n it(\"returns empty array if $repeat doesn't resolve to an array\", () => {\n const state: DocumentState = {\n invalid: 42\n };\n\n const bindings: DocumentElementBindings = {\n $repeat: {\n expression: \"$state.invalid\"\n },\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n static: \"Should not be used\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const resolved = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved).toEqual([]);\n });\n\n it(\"resolves binding that refers to a specific array index\", () => {\n const state: DocumentState = {\n list: [{ text: \"First item text\" }, { text: \"Second item text\" }]\n };\n\n const bindings: DocumentElementBindings = {\n inputs: {\n text: {\n id: \"text\",\n type: \"text\",\n static: \"Static fallback\",\n expression: \"$state.list.0.text\"\n }\n }\n };\n\n const inputs = [createTextInput({ name: \"text\" })];\n const inputAst = ComponentManifestToAstConverter.convert(inputs);\n const resolver = new BindingsResolver(state);\n const [resolved] = resolver.resolveElement({\n element: baseElement,\n inputAst,\n elementBindings: bindings\n });\n\n expect(resolved.inputs.text).toBe(\"First item text\");\n });\n});\n"],"names":["describe","baseElement","rootElement","it","state","bindings","inputs","createTextInput","inputAst","ComponentManifestToAstConverter","resolver","BindingsResolver","resolved","expect","elements","createSlotInput","value","input"],"mappings":"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"}
@@ -1,56 +1,39 @@
1
- // Visitor signature: called for every resolved leaf
2
-
3
- /**
4
- * ComponentInputTraverser
5
- *
6
- * Walks an input AST and a matching data object,
7
- * invoking `visitor` for each leaf value found.
8
- */
9
- export class ComponentInputTraverser {
10
- constructor(ast) {
11
- this.ast = ast;
12
- }
13
-
14
- /**
15
- * Traverse the given `data` object according to the AST,
16
- * calling `visitor(node, path, value)` for each leaf.
17
- */
18
- traverse(data, visitor) {
19
- for (const node of this.ast) {
20
- const rootValue = data[node.name];
21
- this.traverseNode(node, rootValue, node.name, visitor);
1
+ class ComponentInputTraverser {
2
+ constructor(ast){
3
+ this.ast = ast;
22
4
  }
23
- }
24
- traverseNode(node, value, currentPath, visitor) {
25
- if (node.list) {
26
- value = value ?? [];
27
- if (value.length === 0) {
28
- visitor(node, currentPath, value);
29
- }
30
- value.forEach((item, index) => {
31
- if (node.children.length > 0) {
32
- const itemPath = `${currentPath}/${index}`;
33
- for (const child of node.children) {
34
- const childValue = item?.[child.name];
35
- const childPath = `${itemPath}/${child.name}`;
36
- this.traverseNode(child, childValue, childPath, visitor);
37
- }
38
- } else {
39
- visitor(node, currentPath, item);
5
+ traverse(data, visitor) {
6
+ for (const node of this.ast){
7
+ const rootValue = data[node.name];
8
+ this.traverseNode(node, rootValue, node.name, visitor);
40
9
  }
41
- });
42
- } else {
43
- if (node.children.length > 0) {
44
- for (const child of node.children) {
45
- const childValue = value?.[child.name];
46
- const childPath = `${currentPath}/${child.name}`;
47
- this.traverseNode(child, childValue, childPath, visitor);
10
+ }
11
+ traverseNode(node, value, currentPath, visitor) {
12
+ if (node.list) {
13
+ value = value ?? [];
14
+ if (0 === value.length) visitor(node, currentPath, value);
15
+ if (0 === node.children.length && "slot" !== node.type) {
16
+ if (value.length > 0) visitor(node, currentPath, value);
17
+ return;
18
+ }
19
+ value.forEach((item, index)=>{
20
+ if (node.children.length > 0) {
21
+ const itemPath = `${currentPath}/${index}`;
22
+ for (const child of node.children){
23
+ const childValue = item?.[child.name];
24
+ const childPath = `${itemPath}/${child.name}`;
25
+ this.traverseNode(child, childValue, childPath, visitor);
26
+ }
27
+ } else "slot" === node.type ? visitor(node, currentPath, item) : visitor(node, `${currentPath}/${index}`, item);
28
+ });
29
+ } else if (node.children.length > 0) for (const child of node.children){
30
+ const childValue = value?.[child.name];
31
+ const childPath = `${currentPath}/${child.name}`;
32
+ this.traverseNode(child, childValue, childPath, visitor);
48
33
  }
49
- } else {
50
- visitor(node, currentPath, value);
51
- }
34
+ else visitor(node, currentPath, value);
52
35
  }
53
- }
54
36
  }
37
+ export { ComponentInputTraverser };
55
38
 
56
39
  //# sourceMappingURL=ComponentInputTraverser.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["ComponentInputTraverser","constructor","ast","traverse","data","visitor","node","rootValue","name","traverseNode","value","currentPath","list","length","forEach","item","index","children","itemPath","child","childValue","childPath"],"sources":["ComponentInputTraverser.ts"],"sourcesContent":["import type { InputAstNode } from \"./ComponentManifestToAstConverter.js\";\n\n// Visitor signature: called for every resolved leaf\nexport type InputVisitor = (node: InputAstNode, path: string, value: any) => void;\n\n/**\n * ComponentInputTraverser\n *\n * Walks an input AST and a matching data object,\n * invoking `visitor` for each leaf value found.\n */\nexport class ComponentInputTraverser {\n private readonly ast: InputAstNode[];\n\n constructor(ast: InputAstNode[]) {\n this.ast = ast;\n }\n\n /**\n * Traverse the given `data` object according to the AST,\n * calling `visitor(node, path, value)` for each leaf.\n */\n public traverse(data: Record<string, any>, visitor: InputVisitor): void {\n for (const node of this.ast) {\n const rootValue = data[node.name];\n this.traverseNode(node, rootValue, node.name, visitor);\n }\n }\n\n private traverseNode(\n node: InputAstNode,\n value: any,\n currentPath: string,\n visitor: InputVisitor\n ): void {\n if (node.list) {\n value = value ?? [];\n\n if (value.length === 0) {\n visitor(node, currentPath, value);\n }\n\n value.forEach((item: any, index: number) => {\n if (node.children.length > 0) {\n const itemPath = `${currentPath}/${index}`;\n for (const child of node.children) {\n const childValue = item?.[child.name];\n const childPath = `${itemPath}/${child.name}`;\n this.traverseNode(child, childValue, childPath, visitor);\n }\n } else {\n visitor(node, currentPath, item);\n }\n });\n } else {\n if (node.children.length > 0) {\n for (const child of node.children) {\n const childValue = value?.[child.name];\n const childPath = `${currentPath}/${child.name}`;\n this.traverseNode(child, childValue, childPath, visitor);\n }\n } else {\n visitor(node, currentPath, value);\n }\n }\n }\n}\n"],"mappings":"AAEA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,uBAAuB,CAAC;EAGjCC,WAAWA,CAACC,GAAmB,EAAE;IAC7B,IAAI,CAACA,GAAG,GAAGA,GAAG;EAClB;;EAEA;AACJ;AACA;AACA;EACWC,QAAQA,CAACC,IAAyB,EAAEC,OAAqB,EAAQ;IACpE,KAAK,MAAMC,IAAI,IAAI,IAAI,CAACJ,GAAG,EAAE;MACzB,MAAMK,SAAS,GAAGH,IAAI,CAACE,IAAI,CAACE,IAAI,CAAC;MACjC,IAAI,CAACC,YAAY,CAACH,IAAI,EAAEC,SAAS,EAAED,IAAI,CAACE,IAAI,EAAEH,OAAO,CAAC;IAC1D;EACJ;EAEQI,YAAYA,CAChBH,IAAkB,EAClBI,KAAU,EACVC,WAAmB,EACnBN,OAAqB,EACjB;IACJ,IAAIC,IAAI,CAACM,IAAI,EAAE;MACXF,KAAK,GAAGA,KAAK,IAAI,EAAE;MAEnB,IAAIA,KAAK,CAACG,MAAM,KAAK,CAAC,EAAE;QACpBR,OAAO,CAACC,IAAI,EAAEK,WAAW,EAAED,KAAK,CAAC;MACrC;MAEAA,KAAK,CAACI,OAAO,CAAC,CAACC,IAAS,EAAEC,KAAa,KAAK;QACxC,IAAIV,IAAI,CAACW,QAAQ,CAACJ,MAAM,GAAG,CAAC,EAAE;UAC1B,MAAMK,QAAQ,GAAG,GAAGP,WAAW,IAAIK,KAAK,EAAE;UAC1C,KAAK,MAAMG,KAAK,IAAIb,IAAI,CAACW,QAAQ,EAAE;YAC/B,MAAMG,UAAU,GAAGL,IAAI,GAAGI,KAAK,CAACX,IAAI,CAAC;YACrC,MAAMa,SAAS,GAAG,GAAGH,QAAQ,IAAIC,KAAK,CAACX,IAAI,EAAE;YAC7C,IAAI,CAACC,YAAY,CAACU,KAAK,EAAEC,UAAU,EAAEC,SAAS,EAAEhB,OAAO,CAAC;UAC5D;QACJ,CAAC,MAAM;UACHA,OAAO,CAACC,IAAI,EAAEK,WAAW,EAAEI,IAAI,CAAC;QACpC;MACJ,CAAC,CAAC;IACN,CAAC,MAAM;MACH,IAAIT,IAAI,CAACW,QAAQ,CAACJ,MAAM,GAAG,CAAC,EAAE;QAC1B,KAAK,MAAMM,KAAK,IAAIb,IAAI,CAACW,QAAQ,EAAE;UAC/B,MAAMG,UAAU,GAAGV,KAAK,GAAGS,KAAK,CAACX,IAAI,CAAC;UACtC,MAAMa,SAAS,GAAG,GAAGV,WAAW,IAAIQ,KAAK,CAACX,IAAI,EAAE;UAChD,IAAI,CAACC,YAAY,CAACU,KAAK,EAAEC,UAAU,EAAEC,SAAS,EAAEhB,OAAO,CAAC;QAC5D;MACJ,CAAC,MAAM;QACHA,OAAO,CAACC,IAAI,EAAEK,WAAW,EAAED,KAAK,CAAC;MACrC;IACJ;EACJ;AACJ","ignoreList":[]}
1
+ {"version":3,"file":"ComponentInputTraverser.js","sources":["../src/ComponentInputTraverser.ts"],"sourcesContent":["import type { InputAstNode } from \"./ComponentManifestToAstConverter.js\";\n\n// Visitor signature: called for every resolved leaf\nexport type InputVisitor = (node: InputAstNode, path: string, value: any) => void;\n\n/**\n * ComponentInputTraverser\n *\n * Walks an input AST and a matching data object,\n * invoking `visitor` for each leaf value found.\n */\nexport class ComponentInputTraverser {\n private readonly ast: InputAstNode[];\n\n constructor(ast: InputAstNode[]) {\n this.ast = ast;\n }\n\n /**\n * Traverse the given `data` object according to the AST,\n * calling `visitor(node, path, value)` for each leaf.\n */\n public traverse(data: Record<string, any>, visitor: InputVisitor): void {\n for (const node of this.ast) {\n const rootValue = data[node.name];\n this.traverseNode(node, rootValue, node.name, visitor);\n }\n }\n\n private traverseNode(\n node: InputAstNode,\n value: any,\n currentPath: string,\n visitor: InputVisitor\n ): void {\n if (node.list) {\n value = value ?? [];\n\n if (value.length === 0) {\n visitor(node, currentPath, value);\n }\n\n // A list of primitives (e.g. tags) is a single leaf: its value is the whole array.\n // Visiting each item would write every item to the same path, leaving only the last.\n // Slot lists are still visited per item, as each item creates its own element.\n if (node.children.length === 0 && node.type !== \"slot\") {\n if (value.length > 0) {\n visitor(node, currentPath, value);\n }\n return;\n }\n\n value.forEach((item: any, index: number) => {\n if (node.children.length > 0) {\n const itemPath = `${currentPath}/${index}`;\n for (const child of node.children) {\n const childValue = item?.[child.name];\n const childPath = `${itemPath}/${child.name}`;\n this.traverseNode(child, childValue, childPath, visitor);\n }\n } else if (node.type === \"slot\") {\n /*\n * A slot list keeps one path: the elements it holds are managed by `AddToParent`,\n * which maintains the static array itself. Indexing here would write past it.\n */\n visitor(node, currentPath, item);\n } else {\n /*\n * An indexed path per item, exactly as an object list gets above.\n *\n * This used to pass `currentPath` for every item, so a caller writing one binding\n * per visit wrote them all to the same path and each overwrote the last: a list of\n * five came out as the fifth item alone, a bare string on a binding still marked\n * `list: true`. Every component doing `items.map(...)` then threw, which on a\n * server-rendered page is a 500 for the whole page rather than one broken section.\n *\n * `BindingsResolver` reads a scalar list by collecting `name/0`, `name/1`, … so\n * the indexed path is also the shape the reader already looks for.\n */\n visitor(node, `${currentPath}/${index}`, item);\n }\n });\n } else {\n if (node.children.length > 0) {\n for (const child of node.children) {\n const childValue = value?.[child.name];\n const childPath = `${currentPath}/${child.name}`;\n this.traverseNode(child, childValue, childPath, visitor);\n }\n } else {\n visitor(node, currentPath, value);\n }\n }\n }\n}\n"],"names":["ComponentInputTraverser","ast","data","visitor","node","rootValue","value","currentPath","item","index","itemPath","child","childValue","childPath"],"mappings":"AAWO,MAAMA;IAGT,YAAYC,GAAmB,CAAE;QAC7B,IAAI,CAAC,GAAG,GAAGA;IACf;IAMO,SAASC,IAAyB,EAAEC,OAAqB,EAAQ;QACpE,KAAK,MAAMC,QAAQ,IAAI,CAAC,GAAG,CAAE;YACzB,MAAMC,YAAYH,IAAI,CAACE,KAAK,IAAI,CAAC;YACjC,IAAI,CAAC,YAAY,CAACA,MAAMC,WAAWD,KAAK,IAAI,EAAED;QAClD;IACJ;IAEQ,aACJC,IAAkB,EAClBE,KAAU,EACVC,WAAmB,EACnBJ,OAAqB,EACjB;QACJ,IAAIC,KAAK,IAAI,EAAE;YACXE,QAAQA,SAAS,EAAE;YAEnB,IAAIA,AAAiB,MAAjBA,MAAM,MAAM,EACZH,QAAQC,MAAMG,aAAaD;YAM/B,IAAIF,AAAyB,MAAzBA,KAAK,QAAQ,CAAC,MAAM,IAAUA,AAAc,WAAdA,KAAK,IAAI,EAAa;gBACpD,IAAIE,MAAM,MAAM,GAAG,GACfH,QAAQC,MAAMG,aAAaD;gBAE/B;YACJ;YAEAA,MAAM,OAAO,CAAC,CAACE,MAAWC;gBACtB,IAAIL,KAAK,QAAQ,CAAC,MAAM,GAAG,GAAG;oBAC1B,MAAMM,WAAW,GAAGH,YAAY,CAAC,EAAEE,OAAO;oBAC1C,KAAK,MAAME,SAASP,KAAK,QAAQ,CAAE;wBAC/B,MAAMQ,aAAaJ,MAAM,CAACG,MAAM,IAAI,CAAC;wBACrC,MAAME,YAAY,GAAGH,SAAS,CAAC,EAAEC,MAAM,IAAI,EAAE;wBAC7C,IAAI,CAAC,YAAY,CAACA,OAAOC,YAAYC,WAAWV;oBACpD;gBACJ,OAAWC,AAAc,WAAdA,KAAK,IAAI,GAKhBD,QAAQC,MAAMG,aAAaC,QAc3BL,QAAQC,MAAM,GAAGG,YAAY,CAAC,EAAEE,OAAO,EAAED;YAEjD;QACJ,OACI,IAAIJ,KAAK,QAAQ,CAAC,MAAM,GAAG,GACvB,KAAK,MAAMO,SAASP,KAAK,QAAQ,CAAE;YAC/B,MAAMQ,aAAaN,OAAO,CAACK,MAAM,IAAI,CAAC;YACtC,MAAME,YAAY,GAAGN,YAAY,CAAC,EAAEI,MAAM,IAAI,EAAE;YAChD,IAAI,CAAC,YAAY,CAACA,OAAOC,YAAYC,WAAWV;QACpD;aAEAA,QAAQC,MAAMG,aAAaD;IAGvC;AACJ"}
@@ -1,24 +1,23 @@
1
- export class ComponentManifestToAstConverter {
2
- static convert(inputs) {
3
- return this.buildAst(inputs);
4
- }
5
- static buildAst(inputs, basePath = "") {
6
- return inputs.map(input => {
7
- const path = basePath ? `${basePath}/${input.name}` : input.name;
8
- const node = {
9
- name: input.name,
10
- type: input.type,
11
- list: input.list || false,
12
- path,
13
- children: [],
14
- input
15
- };
16
- if (input.type === "object" && input.fields) {
17
- node.children = this.buildAst(input.fields, path);
18
- }
19
- return node;
20
- });
21
- }
1
+ class ComponentManifestToAstConverter {
2
+ static convert(inputs) {
3
+ return this.buildAst(inputs);
4
+ }
5
+ static buildAst(inputs, basePath = "") {
6
+ return inputs.map((input)=>{
7
+ const path = basePath ? `${basePath}/${input.name}` : input.name;
8
+ const node = {
9
+ name: input.name,
10
+ type: input.type,
11
+ list: input.list || false,
12
+ path,
13
+ children: [],
14
+ input
15
+ };
16
+ if ("object" === input.type && input.fields) node.children = this.buildAst(input.fields, path);
17
+ return node;
18
+ });
19
+ }
22
20
  }
21
+ export { ComponentManifestToAstConverter };
23
22
 
24
23
  //# sourceMappingURL=ComponentManifestToAstConverter.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["ComponentManifestToAstConverter","convert","inputs","buildAst","basePath","map","input","path","name","node","type","list","children","fields"],"sources":["ComponentManifestToAstConverter.ts"],"sourcesContent":["import type { ComponentInput } from \"./types.js\";\n\nexport type InputAstNode = {\n name: string;\n type: string;\n list: boolean;\n path: string;\n children: InputAstNode[];\n input: ComponentInput;\n};\n\nexport class ComponentManifestToAstConverter {\n static convert(inputs: ComponentInput[]): InputAstNode[] {\n return this.buildAst(inputs);\n }\n\n private static buildAst(inputs: ComponentInput[], basePath = \"\"): InputAstNode[] {\n return inputs.map(input => {\n const path = basePath ? `${basePath}/${input.name}` : input.name;\n const node: InputAstNode = {\n name: input.name,\n type: input.type,\n list: input.list || false,\n path,\n children: [],\n input\n };\n\n if (input.type === \"object\" && input.fields) {\n node.children = this.buildAst(input.fields, path);\n }\n\n return node;\n });\n }\n}\n"],"mappings":"AAWA,OAAO,MAAMA,+BAA+B,CAAC;EACzC,OAAOC,OAAOA,CAACC,MAAwB,EAAkB;IACrD,OAAO,IAAI,CAACC,QAAQ,CAACD,MAAM,CAAC;EAChC;EAEA,OAAeC,QAAQA,CAACD,MAAwB,EAAEE,QAAQ,GAAG,EAAE,EAAkB;IAC7E,OAAOF,MAAM,CAACG,GAAG,CAACC,KAAK,IAAI;MACvB,MAAMC,IAAI,GAAGH,QAAQ,GAAG,GAAGA,QAAQ,IAAIE,KAAK,CAACE,IAAI,EAAE,GAAGF,KAAK,CAACE,IAAI;MAChE,MAAMC,IAAkB,GAAG;QACvBD,IAAI,EAAEF,KAAK,CAACE,IAAI;QAChBE,IAAI,EAAEJ,KAAK,CAACI,IAAI;QAChBC,IAAI,EAAEL,KAAK,CAACK,IAAI,IAAI,KAAK;QACzBJ,IAAI;QACJK,QAAQ,EAAE,EAAE;QACZN;MACJ,CAAC;MAED,IAAIA,KAAK,CAACI,IAAI,KAAK,QAAQ,IAAIJ,KAAK,CAACO,MAAM,EAAE;QACzCJ,IAAI,CAACG,QAAQ,GAAG,IAAI,CAACT,QAAQ,CAACG,KAAK,CAACO,MAAM,EAAEN,IAAI,CAAC;MACrD;MAEA,OAAOE,IAAI;IACf,CAAC,CAAC;EACN;AACJ","ignoreList":[]}
1
+ {"version":3,"file":"ComponentManifestToAstConverter.js","sources":["../src/ComponentManifestToAstConverter.ts"],"sourcesContent":["import type { ComponentInput } from \"./types.js\";\n\nexport type InputAstNode = {\n name: string;\n type: string;\n list: boolean;\n path: string;\n children: InputAstNode[];\n input: ComponentInput;\n};\n\nexport class ComponentManifestToAstConverter {\n static convert(inputs: ComponentInput[]): InputAstNode[] {\n return this.buildAst(inputs);\n }\n\n private static buildAst(inputs: ComponentInput[], basePath = \"\"): InputAstNode[] {\n return inputs.map(input => {\n const path = basePath ? `${basePath}/${input.name}` : input.name;\n const node: InputAstNode = {\n name: input.name,\n type: input.type,\n list: input.list || false,\n path,\n children: [],\n input\n };\n\n if (input.type === \"object\" && input.fields) {\n node.children = this.buildAst(input.fields, path);\n }\n\n return node;\n });\n }\n}\n"],"names":["ComponentManifestToAstConverter","inputs","basePath","input","path","node"],"mappings":"AAWO,MAAMA;IACT,OAAO,QAAQC,MAAwB,EAAkB;QACrD,OAAO,IAAI,CAAC,QAAQ,CAACA;IACzB;IAEA,OAAe,SAASA,MAAwB,EAAEC,WAAW,EAAE,EAAkB;QAC7E,OAAOD,OAAO,GAAG,CAACE,CAAAA;YACd,MAAMC,OAAOF,WAAW,GAAGA,SAAS,CAAC,EAAEC,MAAM,IAAI,EAAE,GAAGA,MAAM,IAAI;YAChE,MAAME,OAAqB;gBACvB,MAAMF,MAAM,IAAI;gBAChB,MAAMA,MAAM,IAAI;gBAChB,MAAMA,MAAM,IAAI,IAAI;gBACpBC;gBACA,UAAU,EAAE;gBACZD;YACJ;YAEA,IAAIA,AAAe,aAAfA,MAAM,IAAI,IAAiBA,MAAM,MAAM,EACvCE,KAAK,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAACF,MAAM,MAAM,EAAEC;YAGhD,OAAOC;QACX;IACJ;AACJ"}
@@ -8,6 +8,15 @@ export declare class ComponentRegistry {
8
8
  private listeners;
9
9
  register(component: Component): void;
10
10
  get(name: string): Component | undefined;
11
+ /**
12
+ * Every registered name.
13
+ *
14
+ * Exists so a failed lookup can say what it DOES have. "Unknown component: X" is the same message
15
+ * whether nothing registered at all, the component registered under a different name, or
16
+ * hydration dropped it — three different bugs with three different fixes, and one investigation
17
+ * had to separate them by hand because the message could not.
18
+ */
19
+ names(): string[];
11
20
  /** subscribe to *all* registrations */
12
21
  onRegister(fn: (reg: Registration) => void): () => boolean;
13
22
  }
@@ -1,25 +1,41 @@
1
- export class ComponentRegistry {
2
- registry = new Map();
3
- listeners = new Set();
4
- register(component) {
5
- const name = component.manifest.name;
6
- this.registry.set(name, component);
7
- // notify subscribers
8
- this.listeners.forEach(fn => fn({
9
- name,
10
- component
11
- }));
12
- }
13
- get(name) {
14
- return this.registry.get(name);
15
- }
16
-
17
- /** subscribe to *all* registrations */
18
- onRegister(fn) {
19
- this.listeners.add(fn);
20
- return () => this.listeners.delete(fn);
21
- }
1
+ import { functionConverter } from "./FunctionConverter.js";
2
+ class ComponentRegistry {
3
+ register(component) {
4
+ const name = component.manifest.name;
5
+ component.manifest.tags = component.manifest.tags ?? [];
6
+ if (component.manifest.constraints) component.manifest.constraints = component.manifest.constraints.map((fn)=>functionConverter.serialize(fn));
7
+ if (component.manifest.descendantConstraints) component.manifest.descendantConstraints = component.manifest.descendantConstraints.map((fn)=>functionConverter.serialize(fn));
8
+ if (component.manifest.canDelete && "function" == typeof component.manifest.canDelete) component.manifest.canDelete = functionConverter.serialize(component.manifest.canDelete);
9
+ if (component.manifest.onChange) component.manifest.onChange = serializeHandlers(component.manifest.onChange);
10
+ if (component.manifest.onDescendantChange) component.manifest.onDescendantChange = serializeHandlers(component.manifest.onDescendantChange);
11
+ this.registry.set(name, component);
12
+ this.listeners.forEach((fn)=>fn({
13
+ name,
14
+ component
15
+ }));
16
+ }
17
+ get(name) {
18
+ return this.registry.get(name);
19
+ }
20
+ names() {
21
+ return [
22
+ ...this.registry.keys()
23
+ ];
24
+ }
25
+ onRegister(fn) {
26
+ this.listeners.add(fn);
27
+ return ()=>this.listeners.delete(fn);
28
+ }
29
+ constructor(){
30
+ this.registry = new Map();
31
+ this.listeners = new Set();
32
+ }
33
+ }
34
+ function serializeHandlers(handler) {
35
+ if (Array.isArray(handler)) return handler.map((h)=>functionConverter.serialize(h));
36
+ return functionConverter.serialize(handler);
22
37
  }
23
- export const componentRegistry = new ComponentRegistry();
38
+ const componentRegistry = new ComponentRegistry();
39
+ export { ComponentRegistry, componentRegistry };
24
40
 
25
41
  //# sourceMappingURL=ComponentRegistry.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["ComponentRegistry","registry","Map","listeners","Set","register","component","name","manifest","set","forEach","fn","get","onRegister","add","delete","componentRegistry"],"sources":["ComponentRegistry.ts"],"sourcesContent":["import type { Component } from \"~/types.js\";\n\ntype Registration = { name: string; component: Component };\n\nexport class ComponentRegistry {\n private registry = new Map<string, Component>();\n private listeners = new Set<(reg: Registration) => void>();\n\n public register(component: Component) {\n const name = component.manifest.name;\n this.registry.set(name, component);\n // notify subscribers\n this.listeners.forEach(fn => fn({ name, component }));\n }\n\n public get(name: string) {\n return this.registry.get(name);\n }\n\n /** subscribe to *all* registrations */\n public onRegister(fn: (reg: Registration) => void) {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n }\n}\n\nexport const componentRegistry = new ComponentRegistry();\n"],"mappings":"AAIA,OAAO,MAAMA,iBAAiB,CAAC;EACnBC,QAAQ,GAAG,IAAIC,GAAG,CAAoB,CAAC;EACvCC,SAAS,GAAG,IAAIC,GAAG,CAA8B,CAAC;EAEnDC,QAAQA,CAACC,SAAoB,EAAE;IAClC,MAAMC,IAAI,GAAGD,SAAS,CAACE,QAAQ,CAACD,IAAI;IACpC,IAAI,CAACN,QAAQ,CAACQ,GAAG,CAACF,IAAI,EAAED,SAAS,CAAC;IAClC;IACA,IAAI,CAACH,SAAS,CAACO,OAAO,CAACC,EAAE,IAAIA,EAAE,CAAC;MAAEJ,IAAI;MAAED;IAAU,CAAC,CAAC,CAAC;EACzD;EAEOM,GAAGA,CAACL,IAAY,EAAE;IACrB,OAAO,IAAI,CAACN,QAAQ,CAACW,GAAG,CAACL,IAAI,CAAC;EAClC;;EAEA;EACOM,UAAUA,CAACF,EAA+B,EAAE;IAC/C,IAAI,CAACR,SAAS,CAACW,GAAG,CAACH,EAAE,CAAC;IACtB,OAAO,MAAM,IAAI,CAACR,SAAS,CAACY,MAAM,CAACJ,EAAE,CAAC;EAC1C;AACJ;AAEA,OAAO,MAAMK,iBAAiB,GAAG,IAAIhB,iBAAiB,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"ComponentRegistry.js","sources":["../src/ComponentRegistry.ts"],"sourcesContent":["import type { Component } from \"~/types.js\";\nimport { functionConverter } from \"~/FunctionConverter.js\";\n\ntype Registration = { name: string; component: Component };\n\nexport class ComponentRegistry {\n private registry = new Map<string, Component>();\n private listeners = new Set<(reg: Registration) => void>();\n\n public register(component: Component) {\n const name = component.manifest.name;\n // Normalize optional fields so downstream code never sees undefined.\n component.manifest.tags = component.manifest.tags ?? [];\n // Serialize constraint and handler functions for cross-boundary transport\n if (component.manifest.constraints) {\n // @ts-expect-error Serialized form is string[], but type expects function[].\n component.manifest.constraints = component.manifest.constraints.map(fn =>\n functionConverter.serialize(fn)\n );\n }\n if (component.manifest.descendantConstraints) {\n // @ts-expect-error Serialized form is string[], but type expects function[].\n component.manifest.descendantConstraints = component.manifest.descendantConstraints.map(\n fn => functionConverter.serialize(fn)\n );\n }\n if (component.manifest.canDelete && typeof component.manifest.canDelete === \"function\") {\n // @ts-expect-error Serialized form is a string, but type expects a function.\n component.manifest.canDelete = functionConverter.serialize(\n component.manifest.canDelete\n );\n }\n if (component.manifest.onChange) {\n // @ts-expect-error Serialized form is a string (or string[]), but type expects a function.\n component.manifest.onChange = serializeHandlers(component.manifest.onChange);\n }\n if (component.manifest.onDescendantChange) {\n // @ts-expect-error Serialized form is a string (or string[]), but type expects a function.\n component.manifest.onDescendantChange = serializeHandlers(\n component.manifest.onDescendantChange\n );\n }\n this.registry.set(name, component);\n // notify subscribers\n this.listeners.forEach(fn => fn({ name, component }));\n }\n\n public get(name: string) {\n return this.registry.get(name);\n }\n\n /**\n * Every registered name.\n *\n * Exists so a failed lookup can say what it DOES have. \"Unknown component: X\" is the same message\n * whether nothing registered at all, the component registered under a different name, or\n * hydration dropped it — three different bugs with three different fixes, and one investigation\n * had to separate them by hand because the message could not.\n */\n public names(): string[] {\n return [...this.registry.keys()];\n }\n\n /** subscribe to *all* registrations */\n public onRegister(fn: (reg: Registration) => void) {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n }\n}\n\nfunction serializeHandlers(\n handler: ((...args: any[]) => any) | ((...args: any[]) => any)[]\n): string | string[] {\n if (Array.isArray(handler)) {\n return handler.map(h => functionConverter.serialize(h));\n }\n return functionConverter.serialize(handler);\n}\n\nexport const componentRegistry = new ComponentRegistry();\n"],"names":["ComponentRegistry","component","name","fn","functionConverter","serializeHandlers","Map","Set","handler","Array","h","componentRegistry"],"mappings":";AAKO,MAAMA;IAIF,SAASC,SAAoB,EAAE;QAClC,MAAMC,OAAOD,UAAU,QAAQ,CAAC,IAAI;QAEpCA,UAAU,QAAQ,CAAC,IAAI,GAAGA,UAAU,QAAQ,CAAC,IAAI,IAAI,EAAE;QAEvD,IAAIA,UAAU,QAAQ,CAAC,WAAW,EAE9BA,UAAU,QAAQ,CAAC,WAAW,GAAGA,UAAU,QAAQ,CAAC,WAAW,CAAC,GAAG,CAACE,CAAAA,KAChEC,kBAAkB,SAAS,CAACD;QAGpC,IAAIF,UAAU,QAAQ,CAAC,qBAAqB,EAExCA,UAAU,QAAQ,CAAC,qBAAqB,GAAGA,UAAU,QAAQ,CAAC,qBAAqB,CAAC,GAAG,CACnFE,CAAAA,KAAMC,kBAAkB,SAAS,CAACD;QAG1C,IAAIF,UAAU,QAAQ,CAAC,SAAS,IAAI,AAAwC,cAAxC,OAAOA,UAAU,QAAQ,CAAC,SAAS,EAEnEA,UAAU,QAAQ,CAAC,SAAS,GAAGG,kBAAkB,SAAS,CACtDH,UAAU,QAAQ,CAAC,SAAS;QAGpC,IAAIA,UAAU,QAAQ,CAAC,QAAQ,EAE3BA,UAAU,QAAQ,CAAC,QAAQ,GAAGI,kBAAkBJ,UAAU,QAAQ,CAAC,QAAQ;QAE/E,IAAIA,UAAU,QAAQ,CAAC,kBAAkB,EAErCA,UAAU,QAAQ,CAAC,kBAAkB,GAAGI,kBACpCJ,UAAU,QAAQ,CAAC,kBAAkB;QAG7C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACC,MAAMD;QAExB,IAAI,CAAC,SAAS,CAAC,OAAO,CAACE,CAAAA,KAAMA,GAAG;gBAAED;gBAAMD;YAAU;IACtD;IAEO,IAAIC,IAAY,EAAE;QACrB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACA;IAC7B;IAUO,QAAkB;QACrB,OAAO;eAAI,IAAI,CAAC,QAAQ,CAAC,IAAI;SAAG;IACpC;IAGO,WAAWC,EAA+B,EAAE;QAC/C,IAAI,CAAC,SAAS,CAAC,GAAG,CAACA;QACnB,OAAO,IAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAACA;IACvC;;aA7DQ,QAAQ,GAAG,IAAIG;aACf,SAAS,GAAG,IAAIC;;AA6D5B;AAEA,SAASF,kBACLG,OAAgE;IAEhE,IAAIC,MAAM,OAAO,CAACD,UACd,OAAOA,QAAQ,GAAG,CAACE,CAAAA,IAAKN,kBAAkB,SAAS,CAACM;IAExD,OAAON,kBAAkB,SAAS,CAACI;AACvC;AAEO,MAAMG,oBAAoB,IAAIX"}
@@ -1,14 +1,15 @@
1
1
  import type { ComponentRegistry } from "./ComponentRegistry.js";
2
- import type { DocumentElement, DocumentElementBindings, DocumentState, ResolvedComponent } from "./types.js";
2
+ import type { DocumentCache, DocumentElement, DocumentElementBindings, DocumentState, ResolvedComponent } from "./types.js";
3
3
  import type { OnResolved } from "./BindingsResolver.js";
4
4
  export type ResolveElementParams = {
5
5
  element: DocumentElement;
6
6
  elementBindings: DocumentElementBindings;
7
7
  state: DocumentState;
8
+ cache?: DocumentCache;
8
9
  onResolved?: OnResolved;
9
10
  };
10
11
  export declare class ComponentResolver {
11
12
  private components;
12
13
  constructor(registry: ComponentRegistry);
13
- resolve({ element, elementBindings, onResolved, state }: ResolveElementParams): ResolvedComponent[] | null;
14
+ resolve({ element, elementBindings, onResolved, state, cache }: ResolveElementParams): ResolvedComponent[] | null;
14
15
  }
@@ -1,36 +1,46 @@
1
1
  import { logger } from "./Logger.js";
2
2
  import { BindingsResolver } from "./BindingsResolver.js";
3
+ import { ContentEntryBindingsResolver } from "./contentEntry/ContentEntryBindingsResolver.js";
3
4
  import { ComponentManifestToAstConverter } from "./ComponentManifestToAstConverter.js";
4
- export class ComponentResolver {
5
- constructor(registry) {
6
- this.components = registry;
7
- }
8
- resolve({
9
- element,
10
- elementBindings = {},
11
- onResolved,
12
- state
13
- }) {
14
- const componentName = element.component.name;
15
- const blueprint = this.components.get(componentName);
16
- if (!blueprint) {
17
- logger.warn(`Unknown component: ${componentName}`);
18
- return null;
5
+ class ComponentResolver {
6
+ constructor(registry){
7
+ this.components = registry;
8
+ }
9
+ resolve({ element, elementBindings = {}, onResolved, state, cache }) {
10
+ const componentName = element.component.name;
11
+ const blueprint = this.components.get(componentName);
12
+ if (!blueprint) {
13
+ const available = this.components.names();
14
+ logger.warn(0 === available.length ? `Unknown component: ${componentName} — no components are registered at all.` : `Unknown component: ${componentName} — ${available.length} registered${describeNearest(componentName, available)}`);
15
+ return null;
16
+ }
17
+ const embedded = cache?.contentEntries;
18
+ const resolvedEntries = embedded ? new Map(Object.entries(embedded)) : void 0;
19
+ const resolver = new ContentEntryBindingsResolver(new BindingsResolver(state), resolvedEntries);
20
+ const instances = resolver.resolveElement({
21
+ element,
22
+ elementBindings,
23
+ inputAst: ComponentManifestToAstConverter.convert(blueprint.manifest.inputs ?? []),
24
+ onResolved
25
+ });
26
+ return instances.map((instance)=>({
27
+ component: blueprint.component,
28
+ manifest: blueprint.manifest,
29
+ styles: instance.styles,
30
+ inputs: instance.inputs
31
+ }));
19
32
  }
20
- const bindingsResolver = new BindingsResolver(state);
21
- const instances = bindingsResolver.resolveElement({
22
- element,
23
- elementBindings,
24
- inputAst: ComponentManifestToAstConverter.convert(blueprint.manifest.inputs ?? []),
25
- onResolved
26
- });
27
- return instances.map(instance => ({
28
- component: blueprint.component,
29
- manifest: blueprint.manifest,
30
- styles: instance.styles,
31
- inputs: instance.inputs
32
- }));
33
- }
34
33
  }
34
+ const describeNearest = (wanted, available)=>{
35
+ const tail = (name)=>name.split("/").pop()?.toLowerCase().replace(/[^a-z0-9]/g, "") ?? "";
36
+ const wantedTail = tail(wanted);
37
+ const near = available.filter((name)=>{
38
+ const candidate = tail(name);
39
+ return candidate.length > 0 && (candidate === wantedTail || candidate.includes(wantedTail) || wantedTail.includes(candidate));
40
+ }).slice(0, 3);
41
+ if (0 === near.length) return `, none matching. Registered: ${available.slice(0, 3).join(", ")}${available.length > 3 ? ", …" : ""}`;
42
+ return `, did you mean ${near.map((name)=>`"${name}"`).join(" or ")}?`;
43
+ };
44
+ export { ComponentResolver };
35
45
 
36
46
  //# sourceMappingURL=ComponentResolver.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["logger","BindingsResolver","ComponentManifestToAstConverter","ComponentResolver","constructor","registry","components","resolve","element","elementBindings","onResolved","state","componentName","component","name","blueprint","get","warn","bindingsResolver","instances","resolveElement","inputAst","convert","manifest","inputs","map","instance","styles"],"sources":["ComponentResolver.ts"],"sourcesContent":["import type { ComponentRegistry } from \"~/ComponentRegistry.js\";\nimport type {\n DocumentElement,\n DocumentElementBindings,\n DocumentState,\n ResolvedComponent\n} from \"~/types.js\";\nimport { logger } from \"./Logger.js\";\nimport type { OnResolved } from \"./BindingsResolver.js\";\nimport { BindingsResolver } from \"./BindingsResolver.js\";\nimport { ComponentManifestToAstConverter } from \"~/ComponentManifestToAstConverter.js\";\n\nexport type ResolveElementParams = {\n element: DocumentElement;\n elementBindings: DocumentElementBindings;\n state: DocumentState;\n onResolved?: OnResolved;\n};\n\nexport class ComponentResolver {\n private components: ComponentRegistry;\n\n constructor(registry: ComponentRegistry) {\n this.components = registry;\n }\n\n resolve({\n element,\n elementBindings = {},\n onResolved,\n state\n }: ResolveElementParams): ResolvedComponent[] | null {\n const componentName = element.component.name;\n const blueprint = this.components.get(componentName);\n\n if (!blueprint) {\n logger.warn(`Unknown component: ${componentName}`);\n return null;\n }\n\n const bindingsResolver = new BindingsResolver(state);\n const instances = bindingsResolver.resolveElement({\n element,\n elementBindings,\n inputAst: ComponentManifestToAstConverter.convert(blueprint.manifest.inputs ?? []),\n onResolved\n });\n\n return instances.map(instance => ({\n component: blueprint.component,\n manifest: blueprint.manifest!,\n styles: instance.styles,\n inputs: instance.inputs\n }));\n }\n}\n"],"mappings":"AAOA,SAASA,MAAM;AAEf,SAASC,gBAAgB;AACzB,SAASC,+BAA+B;AASxC,OAAO,MAAMC,iBAAiB,CAAC;EAG3BC,WAAWA,CAACC,QAA2B,EAAE;IACrC,IAAI,CAACC,UAAU,GAAGD,QAAQ;EAC9B;EAEAE,OAAOA,CAAC;IACJC,OAAO;IACPC,eAAe,GAAG,CAAC,CAAC;IACpBC,UAAU;IACVC;EACkB,CAAC,EAA8B;IACjD,MAAMC,aAAa,GAAGJ,OAAO,CAACK,SAAS,CAACC,IAAI;IAC5C,MAAMC,SAAS,GAAG,IAAI,CAACT,UAAU,CAACU,GAAG,CAACJ,aAAa,CAAC;IAEpD,IAAI,CAACG,SAAS,EAAE;MACZf,MAAM,CAACiB,IAAI,CAAC,sBAAsBL,aAAa,EAAE,CAAC;MAClD,OAAO,IAAI;IACf;IAEA,MAAMM,gBAAgB,GAAG,IAAIjB,gBAAgB,CAACU,KAAK,CAAC;IACpD,MAAMQ,SAAS,GAAGD,gBAAgB,CAACE,cAAc,CAAC;MAC9CZ,OAAO;MACPC,eAAe;MACfY,QAAQ,EAAEnB,+BAA+B,CAACoB,OAAO,CAACP,SAAS,CAACQ,QAAQ,CAACC,MAAM,IAAI,EAAE,CAAC;MAClFd;IACJ,CAAC,CAAC;IAEF,OAAOS,SAAS,CAACM,GAAG,CAACC,QAAQ,KAAK;MAC9Bb,SAAS,EAAEE,SAAS,CAACF,SAAS;MAC9BU,QAAQ,EAAER,SAAS,CAACQ,QAAS;MAC7BI,MAAM,EAAED,QAAQ,CAACC,MAAM;MACvBH,MAAM,EAAEE,QAAQ,CAACF;IACrB,CAAC,CAAC,CAAC;EACP;AACJ","ignoreList":[]}
1
+ {"version":3,"file":"ComponentResolver.js","sources":["../src/ComponentResolver.ts"],"sourcesContent":["import type { ComponentRegistry } from \"~/ComponentRegistry.js\";\nimport type {\n DocumentCache,\n DocumentElement,\n DocumentElementBindings,\n DocumentState,\n ResolvedComponent\n} from \"~/types.js\";\nimport { logger } from \"./Logger.js\";\nimport type { OnResolved } from \"./BindingsResolver.js\";\nimport { BindingsResolver } from \"./BindingsResolver.js\";\nimport { ContentEntryBindingsResolver } from \"./contentEntry/ContentEntryBindingsResolver.js\";\nimport { ComponentManifestToAstConverter } from \"~/ComponentManifestToAstConverter.js\";\nexport type ResolveElementParams = {\n element: DocumentElement;\n elementBindings: DocumentElementBindings;\n state: DocumentState;\n cache?: DocumentCache;\n onResolved?: OnResolved;\n};\n\nexport class ComponentResolver {\n private components: ComponentRegistry;\n\n constructor(registry: ComponentRegistry) {\n this.components = registry;\n }\n\n resolve({\n element,\n elementBindings = {},\n onResolved,\n state,\n cache\n }: ResolveElementParams): ResolvedComponent[] | null {\n const componentName = element.component.name;\n const blueprint = this.components.get(componentName);\n\n if (!blueprint) {\n /*\n * What was wanted AND what is available. A bare \"unknown\" cannot distinguish an empty\n * registry from a name mismatch, and a name mismatch is what made every component on\n * every migrated page unresolvable while each stage reported success: pages referenced\n * \"Site/FAQ Section\" and the bundles registered \"Custom/FaqSection\". The near-miss is\n * usually the whole diagnosis, so it is offered rather than left to be discovered.\n */\n const available = this.components.names();\n logger.warn(\n available.length === 0\n ? `Unknown component: ${componentName} — no components are registered at all.`\n : `Unknown component: ${componentName} — ${available.length} registered${describeNearest(componentName, available)}`\n );\n return null;\n }\n\n // Build the pre-resolved entries map from the document cache\n // (populated by `resolveContentEntries` on the server).\n const embedded = cache?.contentEntries as Record<string, unknown> | undefined;\n const resolvedEntries = embedded ? new Map(Object.entries(embedded)) : undefined;\n\n const resolver = new ContentEntryBindingsResolver(\n new BindingsResolver(state),\n resolvedEntries\n );\n\n const instances = resolver.resolveElement({\n element,\n elementBindings,\n inputAst: ComponentManifestToAstConverter.convert(blueprint.manifest.inputs ?? []),\n onResolved\n });\n\n return instances.map(instance => ({\n component: blueprint.component,\n manifest: blueprint.manifest!,\n styles: instance.styles,\n inputs: instance.inputs\n }));\n }\n}\n\n/**\n * The registered names closest to the one that was wanted, as a suffix for the warning.\n *\n * Matched on the last path segment, because the mismatch that prompted this was a namespace one:\n * \"2424Tulane/FAQ Section\" against \"Custom/FaqSection\" share nothing but their tail, and the tail is\n * what makes the relationship obvious on sight.\n */\nconst describeNearest = (wanted: string, available: string[]): string => {\n const tail = (name: string) =>\n name\n .split(\"/\")\n .pop()\n ?.toLowerCase()\n .replace(/[^a-z0-9]/g, \"\") ?? \"\";\n const wantedTail = tail(wanted);\n\n const near = available\n .filter(name => {\n const candidate = tail(name);\n return (\n candidate.length > 0 &&\n (candidate === wantedTail ||\n candidate.includes(wantedTail) ||\n wantedTail.includes(candidate))\n );\n })\n .slice(0, 3);\n\n if (near.length === 0) {\n return `, none matching. Registered: ${available.slice(0, 3).join(\", \")}${available.length > 3 ? \", …\" : \"\"}`;\n }\n return `, did you mean ${near.map(name => `\"${name}\"`).join(\" or \")}?`;\n};\n"],"names":["ComponentResolver","registry","element","elementBindings","onResolved","state","cache","componentName","blueprint","available","logger","describeNearest","embedded","resolvedEntries","Map","Object","undefined","resolver","ContentEntryBindingsResolver","BindingsResolver","instances","ComponentManifestToAstConverter","instance","wanted","tail","name","wantedTail","near","candidate"],"mappings":";;;;AAqBO,MAAMA;IAGT,YAAYC,QAA2B,CAAE;QACrC,IAAI,CAAC,UAAU,GAAGA;IACtB;IAEA,QAAQ,EACJC,OAAO,EACPC,kBAAkB,CAAC,CAAC,EACpBC,UAAU,EACVC,KAAK,EACLC,KAAK,EACc,EAA8B;QACjD,MAAMC,gBAAgBL,QAAQ,SAAS,CAAC,IAAI;QAC5C,MAAMM,YAAY,IAAI,CAAC,UAAU,CAAC,GAAG,CAACD;QAEtC,IAAI,CAACC,WAAW;YAQZ,MAAMC,YAAY,IAAI,CAAC,UAAU,CAAC,KAAK;YACvCC,OAAO,IAAI,CACPD,AAAqB,MAArBA,UAAU,MAAM,GACV,CAAC,mBAAmB,EAAEF,cAAc,uCAAuC,CAAC,GAC5E,CAAC,mBAAmB,EAAEA,cAAc,GAAG,EAAEE,UAAU,MAAM,CAAC,WAAW,EAAEE,gBAAgBJ,eAAeE,YAAY;YAE5H,OAAO;QACX;QAIA,MAAMG,WAAWN,OAAO;QACxB,MAAMO,kBAAkBD,WAAW,IAAIE,IAAIC,OAAO,OAAO,CAACH,aAAaI;QAEvE,MAAMC,WAAW,IAAIC,6BACjB,IAAIC,iBAAiBd,QACrBQ;QAGJ,MAAMO,YAAYH,SAAS,cAAc,CAAC;YACtCf;YACAC;YACA,UAAUkB,gCAAgC,OAAO,CAACb,UAAU,QAAQ,CAAC,MAAM,IAAI,EAAE;YACjFJ;QACJ;QAEA,OAAOgB,UAAU,GAAG,CAACE,CAAAA,WAAa;gBAC9B,WAAWd,UAAU,SAAS;gBAC9B,UAAUA,UAAU,QAAQ;gBAC5B,QAAQc,SAAS,MAAM;gBACvB,QAAQA,SAAS,MAAM;YAC3B;IACJ;AACJ;AASA,MAAMX,kBAAkB,CAACY,QAAgBd;IACrC,MAAMe,OAAO,CAACC,OACVA,KACK,KAAK,CAAC,KACN,GAAG,IACF,cACD,QAAQ,cAAc,OAAO;IACtC,MAAMC,aAAaF,KAAKD;IAExB,MAAMI,OAAOlB,UACR,MAAM,CAACgB,CAAAA;QACJ,MAAMG,YAAYJ,KAAKC;QACvB,OACIG,UAAU,MAAM,GAAG,KAClBA,CAAAA,cAAcF,cACXE,UAAU,QAAQ,CAACF,eACnBA,WAAW,QAAQ,CAACE,UAAS;IAEzC,GACC,KAAK,CAAC,GAAG;IAEd,IAAID,AAAgB,MAAhBA,KAAK,MAAM,EACX,OAAO,CAAC,6BAA6B,EAAElB,UAAU,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQA,UAAU,MAAM,GAAG,IAAI,QAAQ,IAAI;IAEjH,OAAO,CAAC,eAAe,EAAEkB,KAAK,GAAG,CAACF,CAAAA,OAAQ,CAAC,CAAC,EAAEA,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC1E"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,81 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { ComponentResolver } from "./ComponentResolver.js";
3
+ import { ComponentRegistry } from "./ComponentRegistry.js";
4
+ import { logger } from "./Logger.js";
5
+ const registryWith = (names)=>{
6
+ const registry = new ComponentRegistry();
7
+ for (const name of names)registry.register({
8
+ component: ()=>null,
9
+ manifest: {
10
+ name,
11
+ inputs: []
12
+ }
13
+ });
14
+ return registry;
15
+ };
16
+ const ComponentResolver_test_element = (componentName)=>({
17
+ id: "el-1",
18
+ type: "Webiny/Element",
19
+ component: {
20
+ name: componentName
21
+ },
22
+ parent: {
23
+ id: "root",
24
+ slot: "children"
25
+ }
26
+ });
27
+ const resolve = (registry, wanted)=>new ComponentResolver(registry).resolve({
28
+ element: ComponentResolver_test_element(wanted),
29
+ elementBindings: {
30
+ inputs: {}
31
+ },
32
+ state: {}
33
+ });
34
+ const captureWarnings = ()=>{
35
+ const messages = [];
36
+ vi.spyOn(logger, "warn").mockImplementation((message)=>{
37
+ messages.push(String(message));
38
+ });
39
+ return messages;
40
+ };
41
+ afterEach(()=>{
42
+ vi.restoreAllMocks();
43
+ });
44
+ describe("ComponentResolver — what it says when a component is missing", ()=>{
45
+ it("distinguishes an empty registry from a mismatch", ()=>{
46
+ const messages = captureWarnings();
47
+ expect(resolve(registryWith([]), "Site/FAQ Section")).toBeNull();
48
+ expect(messages[0]).toContain("no components are registered at all");
49
+ });
50
+ it("offers the near miss, which is usually the whole diagnosis", ()=>{
51
+ const messages = captureWarnings();
52
+ const registry = registryWith([
53
+ "Custom/FaqSection",
54
+ "Custom/HeroBanner"
55
+ ]);
56
+ expect(resolve(registry, "2424Tulane/FAQ Section")).toBeNull();
57
+ expect(messages[0]).toContain("2 registered");
58
+ expect(messages[0]).toContain("Custom/FaqSection");
59
+ expect(messages[0]).toContain("did you mean");
60
+ });
61
+ it("says what it has when nothing is close", ()=>{
62
+ const messages = captureWarnings();
63
+ const registry = registryWith([
64
+ "Custom/Footer",
65
+ "Custom/Header"
66
+ ]);
67
+ expect(resolve(registry, "Site/Completely Different")).toBeNull();
68
+ expect(messages[0]).toContain("none matching");
69
+ expect(messages[0]).toContain("Custom/Footer");
70
+ });
71
+ it("says nothing when the component resolves", ()=>{
72
+ const messages = captureWarnings();
73
+ const registry = registryWith([
74
+ "Custom/FaqSection"
75
+ ]);
76
+ expect(resolve(registry, "Custom/FaqSection")).not.toBeNull();
77
+ expect(messages).toEqual([]);
78
+ });
79
+ });
80
+
81
+ //# sourceMappingURL=ComponentResolver.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComponentResolver.test.js","sources":["../src/ComponentResolver.test.ts"],"sourcesContent":["import { describe, expect, it, vi, afterEach } from \"vitest\";\nimport { ComponentResolver } from \"./ComponentResolver.js\";\nimport { ComponentRegistry } from \"./ComponentRegistry.js\";\nimport { logger } from \"./Logger.js\";\n\nconst registryWith = (names: string[]) => {\n const registry = new ComponentRegistry();\n for (const name of names) {\n registry.register({\n component: (() => null) as never,\n manifest: { name, inputs: [] } as never\n });\n }\n return registry;\n};\n\nconst element = (componentName: string) =>\n ({\n id: \"el-1\",\n type: \"Webiny/Element\",\n component: { name: componentName },\n parent: { id: \"root\", slot: \"children\" }\n }) as never;\n\nconst resolve = (registry: ComponentRegistry, wanted: string) =>\n new ComponentResolver(registry).resolve({\n element: element(wanted),\n elementBindings: { inputs: {} } as never,\n state: {} as never\n });\n\nconst captureWarnings = () => {\n const messages: string[] = [];\n vi.spyOn(logger, \"warn\").mockImplementation((message: unknown) => {\n messages.push(String(message));\n });\n return messages;\n};\n\nafterEach(() => {\n vi.restoreAllMocks();\n});\n\ndescribe(\"ComponentResolver — what it says when a component is missing\", () => {\n it(\"distinguishes an empty registry from a mismatch\", () => {\n const messages = captureWarnings();\n\n expect(resolve(registryWith([]), \"Site/FAQ Section\")).toBeNull();\n expect(messages[0]).toContain(\"no components are registered at all\");\n });\n\n it(\"offers the near miss, which is usually the whole diagnosis\", () => {\n /*\n * The real case: pages referenced \"2424Tulane/FAQ Section\" and bundles registered\n * \"Custom/FaqSection\". 199 of 199 differed, every page rendered nothing, and the warning said\n * only \"Unknown component\".\n */\n const messages = captureWarnings();\n const registry = registryWith([\"Custom/FaqSection\", \"Custom/HeroBanner\"]);\n\n expect(resolve(registry, \"2424Tulane/FAQ Section\")).toBeNull();\n expect(messages[0]).toContain(\"2 registered\");\n expect(messages[0]).toContain(\"Custom/FaqSection\");\n expect(messages[0]).toContain(\"did you mean\");\n });\n\n it(\"says what it has when nothing is close\", () => {\n const messages = captureWarnings();\n const registry = registryWith([\"Custom/Footer\", \"Custom/Header\"]);\n\n expect(resolve(registry, \"Site/Completely Different\")).toBeNull();\n expect(messages[0]).toContain(\"none matching\");\n expect(messages[0]).toContain(\"Custom/Footer\");\n });\n\n it(\"says nothing when the component resolves\", () => {\n const messages = captureWarnings();\n const registry = registryWith([\"Custom/FaqSection\"]);\n\n expect(resolve(registry, \"Custom/FaqSection\")).not.toBeNull();\n expect(messages).toEqual([]);\n });\n});\n"],"names":["registryWith","names","registry","ComponentRegistry","name","element","componentName","resolve","wanted","ComponentResolver","captureWarnings","messages","vi","logger","message","String","afterEach","describe","it","expect"],"mappings":";;;;AAKA,MAAMA,eAAe,CAACC;IAClB,MAAMC,WAAW,IAAIC;IACrB,KAAK,MAAMC,QAAQH,MACfC,SAAS,QAAQ,CAAC;QACd,WAAY,IAAM;QAClB,UAAU;YAAEE;YAAM,QAAQ,EAAE;QAAC;IACjC;IAEJ,OAAOF;AACX;AAEA,MAAMG,iCAAU,CAACC,gBACZ;QACG,IAAI;QACJ,MAAM;QACN,WAAW;YAAE,MAAMA;QAAc;QACjC,QAAQ;YAAE,IAAI;YAAQ,MAAM;QAAW;IAC3C;AAEJ,MAAMC,UAAU,CAACL,UAA6BM,SAC1C,IAAIC,kBAAkBP,UAAU,OAAO,CAAC;QACpC,SAASG,+BAAQG;QACjB,iBAAiB;YAAE,QAAQ,CAAC;QAAE;QAC9B,OAAO,CAAC;IACZ;AAEJ,MAAME,kBAAkB;IACpB,MAAMC,WAAqB,EAAE;IAC7BC,GAAG,KAAK,CAACC,QAAQ,QAAQ,kBAAkB,CAAC,CAACC;QACzCH,SAAS,IAAI,CAACI,OAAOD;IACzB;IACA,OAAOH;AACX;AAEAK,UAAU;IACNJ,GAAG,eAAe;AACtB;AAEAK,SAAS,gEAAgE;IACrEC,GAAG,mDAAmD;QAClD,MAAMP,WAAWD;QAEjBS,OAAOZ,QAAQP,aAAa,EAAE,GAAG,qBAAqB,QAAQ;QAC9DmB,OAAOR,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IAClC;IAEAO,GAAG,8DAA8D;QAM7D,MAAMP,WAAWD;QACjB,MAAMR,WAAWF,aAAa;YAAC;YAAqB;SAAoB;QAExEmB,OAAOZ,QAAQL,UAAU,2BAA2B,QAAQ;QAC5DiB,OAAOR,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;QAC9BQ,OAAOR,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;QAC9BQ,OAAOR,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IAClC;IAEAO,GAAG,0CAA0C;QACzC,MAAMP,WAAWD;QACjB,MAAMR,WAAWF,aAAa;YAAC;YAAiB;SAAgB;QAEhEmB,OAAOZ,QAAQL,UAAU,8BAA8B,QAAQ;QAC/DiB,OAAOR,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;QAC9BQ,OAAOR,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IAClC;IAEAO,GAAG,4CAA4C;QAC3C,MAAMP,WAAWD;QACjB,MAAMR,WAAWF,aAAa;YAAC;SAAoB;QAEnDmB,OAAOZ,QAAQL,UAAU,sBAAsB,GAAG,CAAC,QAAQ;QAC3DiB,OAAOR,UAAU,OAAO,CAAC,EAAE;IAC/B;AACJ"}