@webiny/website-builder-sdk 0.0.0-unstable.7be00a75a9 → 0.0.0-unstable.7c2def32a7

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 (170) hide show
  1. package/BindingsResolver.d.ts +4 -1
  2. package/BindingsResolver.js.map +1 -1
  3. package/ComponentInputTraverser.js +4 -0
  4. package/ComponentInputTraverser.js.map +1 -1
  5. package/ComponentResolver.d.ts +6 -3
  6. package/ComponentResolver.js +9 -4
  7. package/ComponentResolver.js.map +1 -1
  8. package/ContentSdk.d.ts +67 -1
  9. package/ContentSdk.js +90 -8
  10. package/ContentSdk.js.map +1 -1
  11. package/DocumentStoreManager.d.ts +9 -2
  12. package/DocumentStoreManager.js +24 -5
  13. package/DocumentStoreManager.js.map +1 -1
  14. package/EditingSdk.d.ts +7 -1
  15. package/EditingSdk.js +5 -3
  16. package/EditingSdk.js.map +1 -1
  17. package/ElementFactory.js +7 -2
  18. package/ElementFactory.js.map +1 -1
  19. package/ElementFactory.test.js +102 -0
  20. package/ElementFactory.test.js.map +1 -1
  21. package/LiveSdk.d.ts +3 -1
  22. package/LiveSdk.js +3 -2
  23. package/LiveSdk.js.map +1 -1
  24. package/PreviewDocument.d.ts +3 -1
  25. package/PreviewDocument.js +5 -2
  26. package/PreviewDocument.js.map +1 -1
  27. package/PreviewSdk.d.ts +4 -1
  28. package/PreviewSdk.js +6 -2
  29. package/PreviewSdk.js.map +1 -1
  30. package/ResizeObserver.js +1 -1
  31. package/ResizeObserver.js.map +1 -1
  32. package/StylesBindingsProcessor.js +3 -2
  33. package/StylesBindingsProcessor.js.map +1 -1
  34. package/Theme.js +2 -1
  35. package/Theme.js.map +1 -1
  36. package/asset/deliveryUrl.d.ts +41 -0
  37. package/asset/deliveryUrl.js +34 -0
  38. package/asset/deliveryUrl.js.map +1 -0
  39. package/asset/deliveryUrl.test.d.ts +1 -0
  40. package/asset/deliveryUrl.test.js +127 -0
  41. package/asset/deliveryUrl.test.js.map +1 -0
  42. package/asset/geometry.d.ts +7 -0
  43. package/asset/geometry.js +10 -0
  44. package/asset/geometry.js.map +1 -0
  45. package/asset/imageSrcSet.d.ts +63 -0
  46. package/asset/imageSrcSet.js +98 -0
  47. package/asset/imageSrcSet.js.map +1 -0
  48. package/asset/imageSrcSet.test.d.ts +1 -0
  49. package/asset/imageSrcSet.test.js +103 -0
  50. package/asset/imageSrcSet.test.js.map +1 -0
  51. package/asset/index.d.ts +5 -0
  52. package/asset/index.js +5 -0
  53. package/asset/normalize.d.ts +24 -0
  54. package/asset/normalize.js +106 -0
  55. package/asset/normalize.js.map +1 -0
  56. package/asset/normalize.test.d.ts +1 -0
  57. package/asset/normalize.test.js +263 -0
  58. package/asset/normalize.test.js.map +1 -0
  59. package/asset/types.d.ts +2 -0
  60. package/asset/types.js +0 -0
  61. package/componentLoader/ComponentLoader.d.ts +16 -0
  62. package/componentLoader/ComponentLoader.js +0 -0
  63. package/componentLoader/RegistryOnlyComponentLoader.d.ts +9 -0
  64. package/componentLoader/RegistryOnlyComponentLoader.js +8 -0
  65. package/componentLoader/RegistryOnlyComponentLoader.js.map +1 -0
  66. package/componentLoader/createComponentLoader.d.ts +7 -0
  67. package/componentLoader/createComponentLoader.js +10 -0
  68. package/componentLoader/createComponentLoader.js.map +1 -0
  69. package/componentLoader/defaultComponentLoader.d.ts +7 -0
  70. package/componentLoader/defaultComponentLoader.js +8 -0
  71. package/componentLoader/defaultComponentLoader.js.map +1 -0
  72. package/componentLoader/index.d.ts +3 -0
  73. package/componentLoader/index.js +3 -0
  74. package/contentClientState.test.d.ts +1 -0
  75. package/contentClientState.test.js +384 -0
  76. package/contentClientState.test.js.map +1 -0
  77. package/contentEntry/ContentEntryBindingsResolver.d.ts +33 -0
  78. package/contentEntry/ContentEntryBindingsResolver.js +52 -0
  79. package/contentEntry/ContentEntryBindingsResolver.js.map +1 -0
  80. package/contentEntry/ContentEntryCache.d.ts +49 -0
  81. package/contentEntry/ContentEntryCache.js +51 -0
  82. package/contentEntry/ContentEntryCache.js.map +1 -0
  83. package/contentEntry/resolveContentEntry.d.ts +74 -0
  84. package/contentEntry/resolveContentEntry.js +53 -0
  85. package/contentEntry/resolveContentEntry.js.map +1 -0
  86. package/contentEntry/resolveContentEntry.test.d.ts +1 -0
  87. package/contentEntry/resolveContentEntry.test.js +213 -0
  88. package/contentEntry/resolveContentEntry.test.js.map +1 -0
  89. package/createComponent.d.ts +5 -0
  90. package/createComponent.js +32 -0
  91. package/createComponent.js.map +1 -0
  92. package/createContentClient.d.ts +17 -0
  93. package/createContentClient.js +16 -0
  94. package/createContentClient.js.map +1 -0
  95. package/createContentClient.test.d.ts +1 -0
  96. package/createContentClient.test.js +165 -0
  97. package/createContentClient.test.js.map +1 -0
  98. package/createInput.d.ts +10 -2
  99. package/createInput.js +8 -1
  100. package/createInput.js.map +1 -1
  101. package/dataProviders/ApiClient.d.ts +10 -5
  102. package/dataProviders/ApiClient.js +10 -10
  103. package/dataProviders/ApiClient.js.map +1 -1
  104. package/dataProviders/DefaultDataProvider.d.ts +4 -0
  105. package/dataProviders/DefaultDataProvider.js +35 -0
  106. package/dataProviders/DefaultDataProvider.js.map +1 -1
  107. package/dataProviders/GET_EXPERIMENT_PAUSED.d.ts +1 -0
  108. package/dataProviders/GET_EXPERIMENT_PAUSED.js +17 -0
  109. package/dataProviders/GET_EXPERIMENT_PAUSED.js.map +1 -0
  110. package/dataProviders/GET_PAGE_BY_ID.d.ts +1 -1
  111. package/dataProviders/GET_PAGE_BY_ID.js +2 -0
  112. package/dataProviders/GET_PAGE_BY_ID.js.map +1 -1
  113. package/dataProviders/GET_PAGE_BY_PATH.d.ts +1 -1
  114. package/dataProviders/GET_PAGE_BY_PATH.js +2 -0
  115. package/dataProviders/GET_PAGE_BY_PATH.js.map +1 -1
  116. package/dataProviders/GET_PAGE_EXPERIMENT.d.ts +1 -0
  117. package/dataProviders/GET_PAGE_EXPERIMENT.js +32 -0
  118. package/dataProviders/GET_PAGE_EXPERIMENT.js.map +1 -0
  119. package/dataProviders/GET_VARIANT_CONTENT.d.ts +1 -0
  120. package/dataProviders/GET_VARIANT_CONTENT.js +24 -0
  121. package/dataProviders/GET_VARIANT_CONTENT.js.map +1 -0
  122. package/dataProviders/NullDataProvider.d.ts +4 -0
  123. package/dataProviders/NullDataProvider.js +9 -0
  124. package/dataProviders/NullDataProvider.js.map +1 -1
  125. package/dataProviders/queries.test.d.ts +1 -0
  126. package/dataProviders/queries.test.js +45 -0
  127. package/dataProviders/queries.test.js.map +1 -0
  128. package/defaultBreakpoints.js +23 -3
  129. package/defaultBreakpoints.js.map +1 -1
  130. package/experiments/PostHogAnalyticsProvider.d.ts +31 -0
  131. package/experiments/PostHogAnalyticsProvider.js +28 -0
  132. package/experiments/PostHogAnalyticsProvider.js.map +1 -0
  133. package/experiments/analyticsProvider.d.ts +4 -0
  134. package/experiments/analyticsProvider.js +11 -0
  135. package/experiments/analyticsProvider.js.map +1 -0
  136. package/experiments/bucketing.d.ts +27 -0
  137. package/experiments/bucketing.js +91 -0
  138. package/experiments/bucketing.js.map +1 -0
  139. package/experiments/bucketing.test.d.ts +1 -0
  140. package/experiments/bucketing.test.js +183 -0
  141. package/experiments/bucketing.test.js.map +1 -0
  142. package/experiments/index.d.ts +5 -0
  143. package/experiments/index.js +5 -0
  144. package/experiments/render.d.ts +82 -0
  145. package/experiments/render.js +149 -0
  146. package/experiments/render.js.map +1 -0
  147. package/experiments/types.d.ts +108 -0
  148. package/experiments/types.js +4 -0
  149. package/experiments/types.js.map +1 -0
  150. package/headersProvider.d.ts +1 -2
  151. package/headersProvider.js.map +1 -1
  152. package/image/geometry.d.ts +54 -0
  153. package/image/geometry.js +112 -0
  154. package/image/geometry.js.map +1 -0
  155. package/image/geometry.test.d.ts +1 -0
  156. package/image/geometry.test.js +190 -0
  157. package/image/geometry.test.js.map +1 -0
  158. package/image/index.d.ts +3 -0
  159. package/image/index.js +3 -0
  160. package/image/presets.d.ts +6 -0
  161. package/image/presets.js +20 -0
  162. package/image/presets.js.map +1 -0
  163. package/image/types.d.ts +10 -0
  164. package/image/types.js +0 -0
  165. package/index.d.ts +12 -0
  166. package/index.js +12 -0
  167. package/jsonPatch.js +3 -3
  168. package/package.json +7 -6
  169. package/types/WebsiteBuilderTheme.d.ts +5 -0
  170. package/types.d.ts +75 -1
@@ -9,7 +9,10 @@ export type ResolveElementParams = {
9
9
  inputAst: InputAstNode[];
10
10
  onResolved?: OnResolved;
11
11
  };
12
- export declare class BindingsResolver {
12
+ export interface IBindingsResolver {
13
+ resolveElement(params: ResolveElementParams): ResolvedElement[];
14
+ }
15
+ export declare class BindingsResolver implements IBindingsResolver {
13
16
  private readonly state;
14
17
  constructor(state: DocumentState);
15
18
  resolveElement({ element, elementBindings, inputAst, onResolved }: ResolveElementParams): ResolvedElement[];
@@ -1 +1 @@
1
- {"version":3,"file":"BindingsResolver.js","sources":["../src/BindingsResolver.ts"],"sourcesContent":["import { toJS } from \"mobx\";\nimport type {\n ComponentInput,\n DocumentElement,\n DocumentElementBindings,\n DocumentElementStyleBindings,\n DocumentState,\n ResolvedElement,\n SerializableCSSStyleDeclaration,\n ValueBinding\n} from \"~/types.js\";\nimport type { InputAstNode } from \"./ComponentManifestToAstConverter.js\";\n\nexport interface OnResolved {\n (value: any, input: ComponentInput): any;\n}\n\nexport type ResolveElementParams = {\n element: DocumentElement;\n elementBindings: DocumentElementBindings;\n inputAst: InputAstNode[];\n onResolved?: OnResolved;\n};\n\nexport class BindingsResolver {\n private readonly state: DocumentState;\n\n constructor(state: DocumentState) {\n this.state = state;\n }\n\n public resolveElement({\n element,\n elementBindings,\n inputAst,\n onResolved\n }: ResolveElementParams): ResolvedElement[] {\n const repeatBindingArray = elementBindings.$repeat;\n\n if (repeatBindingArray) {\n const items = this.resolveBinding(repeatBindingArray, { state: toJS(this.state) });\n\n if (!Array.isArray(items)) {\n return [];\n }\n\n return items.map(item => {\n return this.resolveSingleInstance(\n element,\n elementBindings,\n item,\n inputAst,\n onResolved\n );\n });\n }\n\n return [\n this.resolveSingleInstance(element, elementBindings, undefined, inputAst, onResolved)\n ];\n }\n\n private resolveSingleInstance(\n element: DocumentElement,\n elementBindings: DocumentElementBindings,\n item: Record<string, any> | undefined,\n inputAst: InputAstNode[],\n onResolved?: OnResolved\n ): ResolvedElement {\n const resolvedElement: ResolvedElement = {\n id: element.id,\n inputs: {},\n styles: {}\n };\n\n const context = { state: this.state, $: item };\n const bindings = elementBindings.inputs ?? {};\n\n const resolveInputsFromAst = (\n nodes: InputAstNode[],\n prefix: string[],\n target: Record<string, any>\n ) => {\n for (const node of nodes) {\n const pathParts = [...prefix, node.name];\n const path = pathParts.join(\"/\");\n const binding = bindings[path];\n const value = this.resolveBinding(binding, context) ?? node.input.defaultValue;\n\n const finalValue = onResolved ? onResolved(value, node.input) : value;\n\n if (node.children.length > 0) {\n if (node.list) {\n const uniqueIndexes = this.getUniqueIndexesFromPath(path, bindings);\n\n target[node.name] = uniqueIndexes.map(index => {\n const childTarget: Record<string, any> = {};\n resolveInputsFromAst(\n node.children,\n [...pathParts.slice(0, -1), `${node.name}/${index}`],\n childTarget\n );\n return childTarget;\n });\n } else {\n const childTarget: Record<string, any> = {};\n resolveInputsFromAst(node.children, pathParts, childTarget);\n target[node.name] = childTarget;\n }\n } else if (node.list) {\n // List node with no children (e.g., slot or primitive list)\n const uniqueIndexes = this.getUniqueIndexesFromPath(path, bindings);\n\n // If binding is e.g., `leftColumn/0`, we'll have `0` in unique indexes.\n if (uniqueIndexes.length > 0) {\n target[node.name] = uniqueIndexes.map(index => {\n const binding = bindings[`${node.name}/${index}`];\n const value =\n this.resolveBinding(binding, context) ?? node.input.defaultValue;\n return onResolved ? onResolved(value, node.input) : value;\n });\n } else {\n // If binding is a simple `children`, we simply assign the resolved value.\n target[node.name] = finalValue;\n }\n } else if (finalValue !== undefined) {\n target[node.name] = finalValue;\n }\n }\n };\n\n resolveInputsFromAst(inputAst, [], resolvedElement.inputs);\n\n // Resolve styles\n const styles: DocumentElementStyleBindings = elementBindings.styles\n ? (elementBindings.styles ?? {})\n : {};\n\n const resolvedStyles: SerializableCSSStyleDeclaration = {};\n\n for (const [path, binding] of Object.entries(styles)) {\n if (binding) {\n // @ts-expect-error We're positive this is correct.\n resolvedStyles[path] = this.resolveBinding(binding, context);\n }\n }\n\n return {\n ...resolvedElement,\n styles: resolvedStyles\n };\n }\n\n private getUniqueIndexesFromPath(flatKey: string, bindings: DocumentElementBindings) {\n const pattern = new RegExp(`^${flatKey}\\\\/(\\\\d+)`);\n\n const indexes = Object.keys(bindings).reduce((acc: number[], key) => {\n const match = key.match(pattern);\n if (match) {\n acc.push(parseInt(match[1], 10));\n }\n return acc;\n }, []);\n\n return Array.from(new Set(indexes)).sort((a, b) => a - b);\n }\n\n private resolveBinding(binding: ValueBinding | undefined, context: Record<string, any>): any {\n if (typeof binding === \"undefined\") {\n return undefined;\n }\n\n if (binding.expression) {\n return this.evaluateExpression(binding.expression, context);\n }\n\n return binding.static;\n }\n\n private evaluateExpression(expression: string | undefined, context: Record<string, any> = {}) {\n if (!expression || expression === \"$noop\") {\n return undefined;\n }\n\n try {\n let finalExpression = expression.trim();\n\n if (finalExpression.startsWith(\"$state\")) {\n finalExpression = finalExpression.replace(/^\\$state/, \"state\");\n }\n\n finalExpression = finalExpression.replace(/\\.([0-9]+)/g, \"[$1]\");\n\n const scopedFn = new Function(...Object.keys(context), `return ${finalExpression};`);\n return scopedFn(...Object.values(context));\n } catch {\n return undefined;\n }\n }\n}\n"],"names":["BindingsResolver","state","element","elementBindings","inputAst","onResolved","repeatBindingArray","items","toJS","Array","item","undefined","resolvedElement","context","bindings","resolveInputsFromAst","nodes","prefix","target","node","pathParts","path","binding","value","finalValue","uniqueIndexes","index","childTarget","styles","resolvedStyles","Object","flatKey","pattern","RegExp","indexes","acc","key","match","parseInt","Set","a","b","expression","finalExpression","scopedFn","Function"],"mappings":";AAwBO,MAAMA;IAGT,YAAYC,KAAoB,CAAE;QAC9B,IAAI,CAAC,KAAK,GAAGA;IACjB;IAEO,eAAe,EAClBC,OAAO,EACPC,eAAe,EACfC,QAAQ,EACRC,UAAU,EACS,EAAqB;QACxC,MAAMC,qBAAqBH,gBAAgB,OAAO;QAElD,IAAIG,oBAAoB;YACpB,MAAMC,QAAQ,IAAI,CAAC,cAAc,CAACD,oBAAoB;gBAAE,OAAOE,KAAK,IAAI,CAAC,KAAK;YAAE;YAEhF,IAAI,CAACC,MAAM,OAAO,CAACF,QACf,OAAO,EAAE;YAGb,OAAOA,MAAM,GAAG,CAACG,CAAAA,OACN,IAAI,CAAC,qBAAqB,CAC7BR,SACAC,iBACAO,MACAN,UACAC;QAGZ;QAEA,OAAO;YACH,IAAI,CAAC,qBAAqB,CAACH,SAASC,iBAAiBQ,QAAWP,UAAUC;SAC7E;IACL;IAEQ,sBACJH,OAAwB,EACxBC,eAAwC,EACxCO,IAAqC,EACrCN,QAAwB,EACxBC,UAAuB,EACR;QACf,MAAMO,kBAAmC;YACrC,IAAIV,QAAQ,EAAE;YACd,QAAQ,CAAC;YACT,QAAQ,CAAC;QACb;QAEA,MAAMW,UAAU;YAAE,OAAO,IAAI,CAAC,KAAK;YAAE,GAAGH;QAAK;QAC7C,MAAMI,WAAWX,gBAAgB,MAAM,IAAI,CAAC;QAE5C,MAAMY,uBAAuB,CACzBC,OACAC,QACAC;YAEA,KAAK,MAAMC,QAAQH,MAAO;gBACtB,MAAMI,YAAY;uBAAIH;oBAAQE,KAAK,IAAI;iBAAC;gBACxC,MAAME,OAAOD,UAAU,IAAI,CAAC;gBAC5B,MAAME,UAAUR,QAAQ,CAACO,KAAK;gBAC9B,MAAME,QAAQ,IAAI,CAAC,cAAc,CAACD,SAAST,YAAYM,KAAK,KAAK,CAAC,YAAY;gBAE9E,MAAMK,aAAanB,aAAaA,WAAWkB,OAAOJ,KAAK,KAAK,IAAII;gBAEhE,IAAIJ,KAAK,QAAQ,CAAC,MAAM,GAAG,GACvB,IAAIA,KAAK,IAAI,EAAE;oBACX,MAAMM,gBAAgB,IAAI,CAAC,wBAAwB,CAACJ,MAAMP;oBAE1DI,MAAM,CAACC,KAAK,IAAI,CAAC,GAAGM,cAAc,GAAG,CAACC,CAAAA;wBAClC,MAAMC,cAAmC,CAAC;wBAC1CZ,qBACII,KAAK,QAAQ,EACb;+BAAIC,UAAU,KAAK,CAAC,GAAG;4BAAK,GAAGD,KAAK,IAAI,CAAC,CAAC,EAAEO,OAAO;yBAAC,EACpDC;wBAEJ,OAAOA;oBACX;gBACJ,OAAO;oBACH,MAAMA,cAAmC,CAAC;oBAC1CZ,qBAAqBI,KAAK,QAAQ,EAAEC,WAAWO;oBAC/CT,MAAM,CAACC,KAAK,IAAI,CAAC,GAAGQ;gBACxB;qBACG,IAAIR,KAAK,IAAI,EAAE;oBAElB,MAAMM,gBAAgB,IAAI,CAAC,wBAAwB,CAACJ,MAAMP;oBAG1D,IAAIW,cAAc,MAAM,GAAG,GACvBP,MAAM,CAACC,KAAK,IAAI,CAAC,GAAGM,cAAc,GAAG,CAACC,CAAAA;wBAClC,MAAMJ,UAAUR,QAAQ,CAAC,GAAGK,KAAK,IAAI,CAAC,CAAC,EAAEO,OAAO,CAAC;wBACjD,MAAMH,QACF,IAAI,CAAC,cAAc,CAACD,SAAST,YAAYM,KAAK,KAAK,CAAC,YAAY;wBACpE,OAAOd,aAAaA,WAAWkB,OAAOJ,KAAK,KAAK,IAAII;oBACxD;yBAGAL,MAAM,CAACC,KAAK,IAAI,CAAC,GAAGK;gBAE5B,OAAO,IAAIA,AAAeb,WAAfa,YACPN,MAAM,CAACC,KAAK,IAAI,CAAC,GAAGK;YAE5B;QACJ;QAEAT,qBAAqBX,UAAU,EAAE,EAAEQ,gBAAgB,MAAM;QAGzD,MAAMgB,SAAuCzB,gBAAgB,MAAM,GAC5DA,gBAAgB,MAAM,IAAI,CAAC,IAC5B,CAAC;QAEP,MAAM0B,iBAAkD,CAAC;QAEzD,KAAK,MAAM,CAACR,MAAMC,QAAQ,IAAIQ,OAAO,OAAO,CAACF,QACzC,IAAIN,SAEAO,cAAc,CAACR,KAAK,GAAG,IAAI,CAAC,cAAc,CAACC,SAAST;QAI5D,OAAO;YACH,GAAGD,eAAe;YAClB,QAAQiB;QACZ;IACJ;IAEQ,yBAAyBE,OAAe,EAAEjB,QAAiC,EAAE;QACjF,MAAMkB,UAAU,IAAIC,OAAO,CAAC,CAAC,EAAEF,QAAQ,SAAS,CAAC;QAEjD,MAAMG,UAAUJ,OAAO,IAAI,CAAChB,UAAU,MAAM,CAAC,CAACqB,KAAeC;YACzD,MAAMC,QAAQD,IAAI,KAAK,CAACJ;YACxB,IAAIK,OACAF,IAAI,IAAI,CAACG,SAASD,KAAK,CAAC,EAAE,EAAE;YAEhC,OAAOF;QACX,GAAG,EAAE;QAEL,OAAO1B,MAAM,IAAI,CAAC,IAAI8B,IAAIL,UAAU,IAAI,CAAC,CAACM,GAAGC,IAAMD,IAAIC;IAC3D;IAEQ,eAAenB,OAAiC,EAAET,OAA4B,EAAO;QACzF,IAAI,AAAmB,WAAZS,SACP;QAGJ,IAAIA,QAAQ,UAAU,EAClB,OAAO,IAAI,CAAC,kBAAkB,CAACA,QAAQ,UAAU,EAAET;QAGvD,OAAOS,QAAQ,MAAM;IACzB;IAEQ,mBAAmBoB,UAA8B,EAAE7B,UAA+B,CAAC,CAAC,EAAE;QAC1F,IAAI,CAAC6B,cAAcA,AAAe,YAAfA,YACf;QAGJ,IAAI;YACA,IAAIC,kBAAkBD,WAAW,IAAI;YAErC,IAAIC,gBAAgB,UAAU,CAAC,WAC3BA,kBAAkBA,gBAAgB,OAAO,CAAC,YAAY;YAG1DA,kBAAkBA,gBAAgB,OAAO,CAAC,eAAe;YAEzD,MAAMC,WAAW,IAAIC,YAAYf,OAAO,IAAI,CAACjB,UAAU,CAAC,OAAO,EAAE8B,gBAAgB,CAAC,CAAC;YACnF,OAAOC,YAAYd,OAAO,MAAM,CAACjB;QACrC,EAAE,OAAM;YACJ;QACJ;IACJ;AACJ"}
1
+ {"version":3,"file":"BindingsResolver.js","sources":["../src/BindingsResolver.ts"],"sourcesContent":["import { toJS } from \"mobx\";\nimport type {\n ComponentInput,\n DocumentElement,\n DocumentElementBindings,\n DocumentElementStyleBindings,\n DocumentState,\n ResolvedElement,\n SerializableCSSStyleDeclaration,\n ValueBinding\n} from \"~/types.js\";\nimport type { InputAstNode } from \"./ComponentManifestToAstConverter.js\";\n\nexport interface OnResolved {\n (value: any, input: ComponentInput): any;\n}\n\nexport type ResolveElementParams = {\n element: DocumentElement;\n elementBindings: DocumentElementBindings;\n inputAst: InputAstNode[];\n onResolved?: OnResolved;\n};\n\nexport interface IBindingsResolver {\n resolveElement(params: ResolveElementParams): ResolvedElement[];\n}\n\nexport class BindingsResolver implements IBindingsResolver {\n private readonly state: DocumentState;\n\n constructor(state: DocumentState) {\n this.state = state;\n }\n\n public resolveElement({\n element,\n elementBindings,\n inputAst,\n onResolved\n }: ResolveElementParams): ResolvedElement[] {\n const repeatBindingArray = elementBindings.$repeat;\n\n if (repeatBindingArray) {\n const items = this.resolveBinding(repeatBindingArray, { state: toJS(this.state) });\n\n if (!Array.isArray(items)) {\n return [];\n }\n\n return items.map(item => {\n return this.resolveSingleInstance(\n element,\n elementBindings,\n item,\n inputAst,\n onResolved\n );\n });\n }\n\n return [\n this.resolveSingleInstance(element, elementBindings, undefined, inputAst, onResolved)\n ];\n }\n\n private resolveSingleInstance(\n element: DocumentElement,\n elementBindings: DocumentElementBindings,\n item: Record<string, any> | undefined,\n inputAst: InputAstNode[],\n onResolved?: OnResolved\n ): ResolvedElement {\n const resolvedElement: ResolvedElement = {\n id: element.id,\n inputs: {},\n styles: {}\n };\n\n const context = { state: this.state, $: item };\n const bindings = elementBindings.inputs ?? {};\n\n const resolveInputsFromAst = (\n nodes: InputAstNode[],\n prefix: string[],\n target: Record<string, any>\n ) => {\n for (const node of nodes) {\n const pathParts = [...prefix, node.name];\n const path = pathParts.join(\"/\");\n const binding = bindings[path];\n const value = this.resolveBinding(binding, context) ?? node.input.defaultValue;\n\n const finalValue = onResolved ? onResolved(value, node.input) : value;\n\n if (node.children.length > 0) {\n if (node.list) {\n const uniqueIndexes = this.getUniqueIndexesFromPath(path, bindings);\n\n target[node.name] = uniqueIndexes.map(index => {\n const childTarget: Record<string, any> = {};\n resolveInputsFromAst(\n node.children,\n [...pathParts.slice(0, -1), `${node.name}/${index}`],\n childTarget\n );\n return childTarget;\n });\n } else {\n const childTarget: Record<string, any> = {};\n resolveInputsFromAst(node.children, pathParts, childTarget);\n target[node.name] = childTarget;\n }\n } else if (node.list) {\n // List node with no children (e.g., slot or primitive list)\n const uniqueIndexes = this.getUniqueIndexesFromPath(path, bindings);\n\n // If binding is e.g., `leftColumn/0`, we'll have `0` in unique indexes.\n if (uniqueIndexes.length > 0) {\n target[node.name] = uniqueIndexes.map(index => {\n const binding = bindings[`${node.name}/${index}`];\n const value =\n this.resolveBinding(binding, context) ?? node.input.defaultValue;\n return onResolved ? onResolved(value, node.input) : value;\n });\n } else {\n // If binding is a simple `children`, we simply assign the resolved value.\n target[node.name] = finalValue;\n }\n } else if (finalValue !== undefined) {\n target[node.name] = finalValue;\n }\n }\n };\n\n resolveInputsFromAst(inputAst, [], resolvedElement.inputs);\n\n // Resolve styles\n const styles: DocumentElementStyleBindings = elementBindings.styles\n ? (elementBindings.styles ?? {})\n : {};\n\n const resolvedStyles: SerializableCSSStyleDeclaration = {};\n\n for (const [path, binding] of Object.entries(styles)) {\n if (binding) {\n // @ts-expect-error We're positive this is correct.\n resolvedStyles[path] = this.resolveBinding(binding, context);\n }\n }\n\n return {\n ...resolvedElement,\n styles: resolvedStyles\n };\n }\n\n private getUniqueIndexesFromPath(flatKey: string, bindings: DocumentElementBindings) {\n const pattern = new RegExp(`^${flatKey}\\\\/(\\\\d+)`);\n\n const indexes = Object.keys(bindings).reduce((acc: number[], key) => {\n const match = key.match(pattern);\n if (match) {\n acc.push(parseInt(match[1], 10));\n }\n return acc;\n }, []);\n\n return Array.from(new Set(indexes)).sort((a, b) => a - b);\n }\n\n private resolveBinding(binding: ValueBinding | undefined, context: Record<string, any>): any {\n if (typeof binding === \"undefined\") {\n return undefined;\n }\n\n if (binding.expression) {\n return this.evaluateExpression(binding.expression, context);\n }\n\n return binding.static;\n }\n\n private evaluateExpression(expression: string | undefined, context: Record<string, any> = {}) {\n if (!expression || expression === \"$noop\") {\n return undefined;\n }\n\n try {\n let finalExpression = expression.trim();\n\n if (finalExpression.startsWith(\"$state\")) {\n finalExpression = finalExpression.replace(/^\\$state/, \"state\");\n }\n\n finalExpression = finalExpression.replace(/\\.([0-9]+)/g, \"[$1]\");\n\n const scopedFn = new Function(...Object.keys(context), `return ${finalExpression};`);\n return scopedFn(...Object.values(context));\n } catch {\n return undefined;\n }\n }\n}\n"],"names":["BindingsResolver","state","element","elementBindings","inputAst","onResolved","repeatBindingArray","items","toJS","Array","item","undefined","resolvedElement","context","bindings","resolveInputsFromAst","nodes","prefix","target","node","pathParts","path","binding","value","finalValue","uniqueIndexes","index","childTarget","styles","resolvedStyles","Object","flatKey","pattern","RegExp","indexes","acc","key","match","parseInt","Set","a","b","expression","finalExpression","scopedFn","Function"],"mappings":"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"}
@@ -12,6 +12,10 @@ class ComponentInputTraverser {
12
12
  if (node.list) {
13
13
  value = value ?? [];
14
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
+ }
15
19
  value.forEach((item, index)=>{
16
20
  if (node.children.length > 0) {
17
21
  const itemPath = `${currentPath}/${index}`;
@@ -1 +1 @@
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 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"],"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;YAG/BA,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,OACIA,QAAQC,MAAMG,aAAaC;YAEnC;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
+ {"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 {\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"],"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,OACIA,QAAQC,MAAMG,aAAaC;YAEnC;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,14 +1,17 @@
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
+ import type { ContentEntryCache } from "./contentEntry/ContentEntryCache.js";
4
5
  export type ResolveElementParams = {
5
6
  element: DocumentElement;
6
7
  elementBindings: DocumentElementBindings;
7
8
  state: DocumentState;
9
+ cache?: DocumentCache;
8
10
  onResolved?: OnResolved;
9
11
  };
10
12
  export declare class ComponentResolver {
11
13
  private components;
12
- constructor(registry: ComponentRegistry);
13
- resolve({ element, elementBindings, onResolved, state }: ResolveElementParams): ResolvedComponent[] | null;
14
+ private contentEntryCache;
15
+ constructor(registry: ComponentRegistry, cache?: ContentEntryCache);
16
+ resolve({ element, elementBindings, onResolved, state, cache }: ResolveElementParams): ResolvedComponent[] | null;
14
17
  }
@@ -1,19 +1,24 @@
1
1
  import { logger } from "./Logger.js";
2
2
  import { BindingsResolver } from "./BindingsResolver.js";
3
+ import { ContentEntryBindingsResolver } from "./contentEntry/ContentEntryBindingsResolver.js";
4
+ import { contentEntryCache } from "./contentEntry/ContentEntryCache.js";
3
5
  import { ComponentManifestToAstConverter } from "./ComponentManifestToAstConverter.js";
4
6
  class ComponentResolver {
5
- constructor(registry){
7
+ constructor(registry, cache = contentEntryCache){
6
8
  this.components = registry;
9
+ this.contentEntryCache = cache;
7
10
  }
8
- resolve({ element, elementBindings = {}, onResolved, state }) {
11
+ resolve({ element, elementBindings = {}, onResolved, state, cache }) {
9
12
  const componentName = element.component.name;
10
13
  const blueprint = this.components.get(componentName);
11
14
  if (!blueprint) {
12
15
  logger.warn(`Unknown component: ${componentName}`);
13
16
  return null;
14
17
  }
15
- const bindingsResolver = new BindingsResolver(state);
16
- const instances = bindingsResolver.resolveElement({
18
+ const embedded = cache?.contentEntries;
19
+ const resolvedEntries = embedded ? new Map(Object.entries(embedded)) : void 0;
20
+ const resolver = new ContentEntryBindingsResolver(new BindingsResolver(state), resolvedEntries, this.contentEntryCache);
21
+ const instances = resolver.resolveElement({
17
22
  element,
18
23
  elementBindings,
19
24
  inputAst: ComponentManifestToAstConverter.convert(blueprint.manifest.inputs ?? []),
@@ -1 +1 @@
1
- {"version":3,"file":"ComponentResolver.js","sources":["../src/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"],"names":["ComponentResolver","registry","element","elementBindings","onResolved","state","componentName","blueprint","logger","bindingsResolver","BindingsResolver","instances","ComponentManifestToAstConverter","instance"],"mappings":";;;AAmBO,MAAMA;IAGT,YAAYC,QAA2B,CAAE;QACrC,IAAI,CAAC,UAAU,GAAGA;IACtB;IAEA,QAAQ,EACJC,OAAO,EACPC,kBAAkB,CAAC,CAAC,EACpBC,UAAU,EACVC,KAAK,EACc,EAA8B;QACjD,MAAMC,gBAAgBJ,QAAQ,SAAS,CAAC,IAAI;QAC5C,MAAMK,YAAY,IAAI,CAAC,UAAU,CAAC,GAAG,CAACD;QAEtC,IAAI,CAACC,WAAW;YACZC,OAAO,IAAI,CAAC,CAAC,mBAAmB,EAAEF,eAAe;YACjD,OAAO;QACX;QAEA,MAAMG,mBAAmB,IAAIC,iBAAiBL;QAC9C,MAAMM,YAAYF,iBAAiB,cAAc,CAAC;YAC9CP;YACAC;YACA,UAAUS,gCAAgC,OAAO,CAACL,UAAU,QAAQ,CAAC,MAAM,IAAI,EAAE;YACjFH;QACJ;QAEA,OAAOO,UAAU,GAAG,CAACE,CAAAA,WAAa;gBAC9B,WAAWN,UAAU,SAAS;gBAC9B,UAAUA,UAAU,QAAQ;gBAC5B,QAAQM,SAAS,MAAM;gBACvB,QAAQA,SAAS,MAAM;YAC3B;IACJ;AACJ"}
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 type { ContentEntryCache } from \"./contentEntry/ContentEntryCache.js\";\nimport { contentEntryCache } from \"./contentEntry/ContentEntryCache.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 private contentEntryCache: ContentEntryCache;\n\n constructor(registry: ComponentRegistry, cache: ContentEntryCache = contentEntryCache) {\n this.components = registry;\n this.contentEntryCache = cache;\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 logger.warn(`Unknown component: ${componentName}`);\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 this.contentEntryCache\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"],"names":["ComponentResolver","registry","cache","contentEntryCache","element","elementBindings","onResolved","state","componentName","blueprint","logger","embedded","resolvedEntries","Map","Object","undefined","resolver","ContentEntryBindingsResolver","BindingsResolver","instances","ComponentManifestToAstConverter","instance"],"mappings":";;;;;AAuBO,MAAMA;IAIT,YAAYC,QAA2B,EAAEC,QAA2BC,iBAAiB,CAAE;QACnF,IAAI,CAAC,UAAU,GAAGF;QAClB,IAAI,CAAC,iBAAiB,GAAGC;IAC7B;IAEA,QAAQ,EACJE,OAAO,EACPC,kBAAkB,CAAC,CAAC,EACpBC,UAAU,EACVC,KAAK,EACLL,KAAK,EACc,EAA8B;QACjD,MAAMM,gBAAgBJ,QAAQ,SAAS,CAAC,IAAI;QAC5C,MAAMK,YAAY,IAAI,CAAC,UAAU,CAAC,GAAG,CAACD;QAEtC,IAAI,CAACC,WAAW;YACZC,OAAO,IAAI,CAAC,CAAC,mBAAmB,EAAEF,eAAe;YACjD,OAAO;QACX;QAIA,MAAMG,WAAWT,OAAO;QACxB,MAAMU,kBAAkBD,WAAW,IAAIE,IAAIC,OAAO,OAAO,CAACH,aAAaI;QAEvE,MAAMC,WAAW,IAAIC,6BACjB,IAAIC,iBAAiBX,QACrBK,iBACA,IAAI,CAAC,iBAAiB;QAG1B,MAAMO,YAAYH,SAAS,cAAc,CAAC;YACtCZ;YACAC;YACA,UAAUe,gCAAgC,OAAO,CAACX,UAAU,QAAQ,CAAC,MAAM,IAAI,EAAE;YACjFH;QACJ;QAEA,OAAOa,UAAU,GAAG,CAACE,CAAAA,WAAa;gBAC9B,WAAWZ,UAAU,SAAS;gBAC9B,UAAUA,UAAU,QAAQ;gBAC5B,QAAQY,SAAS,MAAM;gBACvB,QAAQA,SAAS,MAAM;YAC3B;IACJ;AACJ"}
package/ContentSdk.d.ts CHANGED
@@ -1,9 +1,17 @@
1
- import type { Component, IContentSdk, ListPagesOptions, ListPagesResult, PublicPage, PublicRedirect, ResolvedComponent } from "./types.js";
1
+ import type { Component, Document, IContentSdk, ListPagesOptions, ListPagesResult, PublicPage, PublicRedirect, ResolvedComponent } from "./types.js";
2
+ import type { ActiveExperiment, VariantContent } from "./experiments/types.js";
3
+ import { type ExperimentRenderResult, type GetPageWithExperimentOptions } from "./experiments/render.js";
2
4
  import { LiveSdk } from "./LiveSdk.js";
3
5
  import { EditingSdk } from "./EditingSdk.js";
4
6
  import { type ResolveElementParams } from "./ComponentResolver.js";
7
+ import type { ComponentRegistry } from "./ComponentRegistry.js";
8
+ import type { ContentEntryCache } from "./contentEntry/ContentEntryCache.js";
9
+ import type { DocumentStoreManager } from "./DocumentStoreManager.js";
5
10
  import type { WebsiteBuilderThemeInput } from "./types/WebsiteBuilderTheme.js";
6
11
  import type { IRedirects } from "./IRedirects.js";
12
+ import type { HeadersProvider } from "./headersProvider.js";
13
+ import type { ContentEntryLoader } from "./contentEntry/resolveContentEntry.js";
14
+ import type { ComponentLoaderFactory } from "./componentLoader/ComponentLoader.js";
7
15
  export type ApiConfig = {
8
16
  apiKey: string;
9
17
  apiHost: string;
@@ -11,7 +19,36 @@ export type ApiConfig = {
11
19
  };
12
20
  export type ContentSDKConfig = ApiConfig & {
13
21
  preview?: boolean;
22
+ previewParams?: string;
14
23
  theme?: WebsiteBuilderThemeInput;
24
+ /**
25
+ * Request headers for server-side preview and experiments. When omitted, the provider set
26
+ * with `setHeadersProvider` is used. Meant for apps that serve requests outside the
27
+ * framework hook; it never carries the tenant.
28
+ */
29
+ headersProvider?: HeadersProvider;
30
+ /**
31
+ * Loads the CMS entries that `contentEntry` inputs reference, for this SDK's content
32
+ * entry cache. Pass one bound to the same tenant as this SDK. When omitted, the cache's
33
+ * loader is left as it is.
34
+ */
35
+ contentEntryLoader?: ContentEntryLoader;
36
+ /**
37
+ * Creates the loader for components a document references but nobody registered. It gets
38
+ * this SDK's API client and API config. When omitted, the default set with
39
+ * `setDefaultComponentLoader` is used (`@webiny/sdk-nextjs` sets its remote component
40
+ * loading), and without one nothing is loaded.
41
+ */
42
+ componentLoader?: ComponentLoaderFactory;
43
+ };
44
+ /**
45
+ * The state a `ContentSdk` renders with. Each piece defaults to its module-level singleton, so
46
+ * `new ContentSdk()` shares state with `contentSdk`. `createContentClient` passes new instances.
47
+ */
48
+ export type ContentSdkState = {
49
+ componentRegistry?: ComponentRegistry;
50
+ contentEntryCache?: ContentEntryCache;
51
+ documentStoreManager?: DocumentStoreManager;
15
52
  };
16
53
  declare class InternalContentSdk implements IContentSdk, IRedirects {
17
54
  private activeSdk;
@@ -28,17 +65,46 @@ export declare class ContentSdk implements IContentSdk, IRedirects {
28
65
  protected sdk?: InternalContentSdk;
29
66
  private isPreview;
30
67
  private lastConfig;
68
+ private dataProvider?;
69
+ private headersProvider?;
70
+ private componentLoaderFactory?;
71
+ private componentLoaderParams?;
72
+ private componentLoader?;
73
+ private readonly componentRegistry;
74
+ private readonly contentEntryCache;
75
+ private readonly documentStoreManager;
76
+ constructor(state?: ContentSdkState);
31
77
  init(config: ContentSDKConfig, afterInit?: () => void): void;
32
78
  getEditingSdk(): EditingSdk | undefined;
33
79
  getPage(path: string): Promise<PublicPage | null>;
80
+ getPageExperiment(path: string): Promise<ActiveExperiment | null>;
81
+ getVariantContent(variantId: string): Promise<VariantContent | null>;
82
+ getExperimentPaused(experimentId: string): Promise<boolean>;
83
+ /**
84
+ * Resolve and render the right page for the current visitor (control or a variant),
85
+ * server-side. Bucketing, targeting, exposure emission, and cache-key handling are all
86
+ * encapsulated here so projects do not reimplement them. See {@link getPageWithExperiment}.
87
+ */
88
+ getPageWithExperiment(path: string, options?: GetPageWithExperimentOptions): Promise<ExperimentRenderResult>;
34
89
  listPages(options?: ListPagesOptions): Promise<ListPagesResult>;
35
90
  getAllRedirects(): Promise<Map<string, PublicRedirect>>;
36
91
  getRedirectByPath(path: string): Promise<PublicRedirect | undefined>;
37
92
  registerComponent(blueprint: Component): void;
38
93
  resolveElement(params: ResolveElementParams): ResolvedComponent[] | null;
94
+ /**
95
+ * Load every component the document references that isn't registered yet, through the
96
+ * component loader, and register what it returns. Call it before rendering the document.
97
+ * Nothing is cached here: an unregistered component goes to the loader on every call.
98
+ */
99
+ loadDocumentComponents(document: Document): Promise<void>;
100
+ private getComponentLoader;
101
+ getComponentRegistry(): ComponentRegistry;
102
+ getContentEntryCache(): ContentEntryCache;
103
+ getDocumentStoreManager(): DocumentStoreManager;
39
104
  isEditing(): boolean;
40
105
  isPreviewing(): boolean;
41
106
  private assertInitialized;
107
+ private requireDataProvider;
42
108
  }
43
109
  export declare const contentSdk: ContentSdk;
44
110
  export {};
package/ContentSdk.js CHANGED
@@ -1,14 +1,18 @@
1
+ import { getPageWithExperiment } from "./experiments/render.js";
1
2
  import { environment } from "./Environment.js";
2
3
  import { LiveSdk } from "./LiveSdk.js";
3
4
  import { EditingSdk } from "./EditingSdk.js";
4
5
  import { ComponentResolver } from "./ComponentResolver.js";
5
6
  import { PreviewSdk } from "./PreviewSdk.js";
6
7
  import { componentRegistry } from "./ComponentRegistry.js";
8
+ import { contentEntryCache } from "./contentEntry/ContentEntryCache.js";
9
+ import { documentStoreManager } from "./DocumentStoreManager.js";
7
10
  import { ApiClient } from "./dataProviders/ApiClient.js";
8
11
  import { DefaultDataProvider } from "./dataProviders/DefaultDataProvider.js";
9
12
  import { Theme } from "./Theme.js";
10
13
  import { viewportManager } from "./ViewportManager.js";
11
14
  import { RedirectsProvider } from "./dataProviders/RedirectsProvider.js";
15
+ import { createComponentLoader } from "./componentLoader/createComponentLoader.js";
12
16
  class InternalContentSdk {
13
17
  constructor(redirectsProvider, liveSdk, editingSdk){
14
18
  this.redirectsProvider = redirectsProvider;
@@ -32,23 +36,50 @@ class InternalContentSdk {
32
36
  }
33
37
  }
34
38
  class ContentSdk {
39
+ constructor(state = {}){
40
+ this.isPreview = false;
41
+ this.componentRegistry = state.componentRegistry ?? componentRegistry;
42
+ this.contentEntryCache = state.contentEntryCache ?? contentEntryCache;
43
+ this.documentStoreManager = state.documentStoreManager ?? documentStoreManager;
44
+ }
35
45
  init(config, afterInit) {
36
46
  const configHash = JSON.stringify(config);
37
47
  if (this.lastConfig && this.lastConfig === configHash) return;
38
48
  this.lastConfig = configHash;
39
- const apiClient = new ApiClient(config.apiHost, config.apiKey, config.apiTenant);
49
+ this.headersProvider = config.headersProvider;
50
+ if (config.contentEntryLoader) this.contentEntryCache.setLoader(config.contentEntryLoader);
51
+ const apiClient = new ApiClient({
52
+ apiHost: config.apiHost,
53
+ apiKey: config.apiKey,
54
+ apiTenant: config.apiTenant,
55
+ preview: config.preview
56
+ });
40
57
  const dataProvider = new DefaultDataProvider({
41
58
  apiClient
42
59
  });
43
- let liveSdk = new LiveSdk(dataProvider);
60
+ this.componentLoaderFactory = config.componentLoader;
61
+ this.componentLoaderParams = {
62
+ apiClient,
63
+ config: {
64
+ apiHost: config.apiHost,
65
+ apiKey: config.apiKey,
66
+ apiTenant: config.apiTenant
67
+ }
68
+ };
69
+ this.componentLoader = void 0;
70
+ this.dataProvider = dataProvider;
71
+ let liveSdk = new LiveSdk(dataProvider, this.documentStoreManager);
44
72
  if (config.preview && !environment.isEditing()) {
45
73
  this.isPreview = true;
46
- liveSdk = new PreviewSdk(dataProvider, liveSdk);
74
+ liveSdk = new PreviewSdk(dataProvider, liveSdk, config.previewParams, config.headersProvider);
47
75
  }
48
76
  const theme = Theme.from(config.theme ?? {});
49
77
  if (environment.isClient()) viewportManager.setBreakpoints(theme.breakpoints);
50
78
  let editingSdk;
51
- if (environment.isEditing()) editingSdk = new EditingSdk(liveSdk, theme);
79
+ if (environment.isEditing()) editingSdk = new EditingSdk(liveSdk, theme, {
80
+ componentRegistry: this.componentRegistry,
81
+ documentStoreManager: this.documentStoreManager
82
+ });
52
83
  this.sdk = new InternalContentSdk(new RedirectsProvider(apiClient), liveSdk, editingSdk);
53
84
  if ("function" == typeof afterInit) afterInit();
54
85
  }
@@ -60,6 +91,29 @@ class ContentSdk {
60
91
  this.assertInitialized();
61
92
  return this.sdk.getPage(path);
62
93
  }
94
+ getPageExperiment(path) {
95
+ return this.requireDataProvider().getPageExperiment(path);
96
+ }
97
+ getVariantContent(variantId) {
98
+ return this.requireDataProvider().getVariantContent(variantId);
99
+ }
100
+ getExperimentPaused(experimentId) {
101
+ return this.requireDataProvider().getExperimentPaused(experimentId);
102
+ }
103
+ getPageWithExperiment(path, options) {
104
+ this.assertInitialized();
105
+ this.requireDataProvider();
106
+ const experimentOptions = {
107
+ ...options
108
+ };
109
+ if (this.headersProvider && !experimentOptions.headersProvider) experimentOptions.headersProvider = this.headersProvider;
110
+ return getPageWithExperiment({
111
+ getPage: (p)=>this.getPage(p),
112
+ getPageExperiment: (p)=>this.getPageExperiment(p),
113
+ getVariantContent: (id)=>this.getVariantContent(id),
114
+ getExperimentPaused: (id)=>this.getExperimentPaused(id)
115
+ }, path, experimentOptions);
116
+ }
63
117
  listPages(options) {
64
118
  this.assertInitialized();
65
119
  return this.sdk.listPages(options);
@@ -74,11 +128,38 @@ class ContentSdk {
74
128
  }
75
129
  registerComponent(blueprint) {
76
130
  this.assertInitialized();
77
- componentRegistry.register(blueprint);
131
+ this.componentRegistry.register(blueprint);
78
132
  }
79
133
  resolveElement(params) {
80
134
  this.assertInitialized();
81
- return new ComponentResolver(componentRegistry).resolve(params);
135
+ return new ComponentResolver(this.componentRegistry, this.contentEntryCache).resolve(params);
136
+ }
137
+ async loadDocumentComponents(document) {
138
+ this.assertInitialized();
139
+ const names = new Set();
140
+ for (const element of Object.values(document.elements)){
141
+ const name = element.component?.name;
142
+ if (name && !this.componentRegistry.get(name)) names.add(name);
143
+ }
144
+ const loader = this.getComponentLoader();
145
+ const loads = Array.from(names).map((name)=>loader.loadComponent({
146
+ name
147
+ }));
148
+ const components = await Promise.all(loads);
149
+ for (const component of components)if (component) this.componentRegistry.register(component);
150
+ }
151
+ getComponentLoader() {
152
+ if (!this.componentLoader) this.componentLoader = createComponentLoader(this.componentLoaderFactory, this.componentLoaderParams);
153
+ return this.componentLoader;
154
+ }
155
+ getComponentRegistry() {
156
+ return this.componentRegistry;
157
+ }
158
+ getContentEntryCache() {
159
+ return this.contentEntryCache;
160
+ }
161
+ getDocumentStoreManager() {
162
+ return this.documentStoreManager;
82
163
  }
83
164
  isEditing() {
84
165
  return environment.isEditing();
@@ -89,8 +170,9 @@ class ContentSdk {
89
170
  assertInitialized() {
90
171
  if (!this.sdk) throw new Error("ContentSdk has not been initialized!");
91
172
  }
92
- constructor(){
93
- this.isPreview = false;
173
+ requireDataProvider() {
174
+ if (!this.dataProvider) throw new Error("ContentSdk has not been initialized!");
175
+ return this.dataProvider;
94
176
  }
95
177
  }
96
178
  const contentSdk = new ContentSdk();
package/ContentSdk.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ContentSdk.js","sources":["../src/ContentSdk.ts"],"sourcesContent":["import type {\n Component,\n IContentSdk,\n ListPagesOptions,\n ListPagesResult,\n PublicPage,\n PublicRedirect,\n ResolvedComponent\n} from \"~/types.js\";\nimport { environment } from \"./Environment.js\";\nimport { LiveSdk } from \"./LiveSdk.js\";\nimport { EditingSdk } from \"./EditingSdk.js\";\nimport { ComponentResolver, type ResolveElementParams } from \"~/ComponentResolver.js\";\nimport { PreviewSdk } from \"./PreviewSdk.js\";\nimport { componentRegistry } from \"~/ComponentRegistry.js\";\nimport { ApiClient } from \"~/dataProviders/ApiClient.js\";\nimport { DefaultDataProvider } from \"~/dataProviders/DefaultDataProvider.js\";\nimport type { WebsiteBuilderThemeInput } from \"./types/WebsiteBuilderTheme.js\";\nimport { Theme } from \"./Theme.js\";\nimport { viewportManager } from \"./ViewportManager.js\";\nimport type { IRedirects } from \"~/IRedirects.js\";\nimport { RedirectsProvider } from \"~/dataProviders/RedirectsProvider.js\";\n\nexport type ApiConfig = {\n apiKey: string;\n apiHost: string;\n apiTenant: string;\n};\n\nexport type ContentSDKConfig = ApiConfig & {\n preview?: boolean;\n theme?: WebsiteBuilderThemeInput;\n};\n\nclass InternalContentSdk implements IContentSdk, IRedirects {\n private activeSdk: IContentSdk;\n private editingSdk: EditingSdk | undefined;\n private redirectsProvider: IRedirects;\n\n constructor(redirectsProvider: IRedirects, liveSdk: LiveSdk, editingSdk?: EditingSdk) {\n this.redirectsProvider = redirectsProvider;\n this.activeSdk = editingSdk ?? liveSdk;\n this.editingSdk = editingSdk;\n }\n\n getEditingSdk() {\n return this.editingSdk;\n }\n\n async getPage(path: string): Promise<PublicPage | null> {\n return this.activeSdk.getPage(path);\n }\n\n listPages(options?: ListPagesOptions): Promise<ListPagesResult> {\n return this.activeSdk.listPages(options);\n }\n\n getAllRedirects(): Promise<Map<string, PublicRedirect>> {\n return this.redirectsProvider.getAllRedirects();\n }\n\n getRedirectByPath(path: string): Promise<PublicRedirect | undefined> {\n return this.redirectsProvider.getRedirectByPath(path);\n }\n}\n\nexport class ContentSdk implements IContentSdk, IRedirects {\n protected sdk?: InternalContentSdk;\n private isPreview = false;\n private lastConfig: any;\n\n public init(config: ContentSDKConfig, afterInit?: () => void): void {\n const configHash = JSON.stringify(config);\n if (this.lastConfig && this.lastConfig === configHash) {\n return;\n }\n\n this.lastConfig = configHash;\n const apiClient = new ApiClient(config.apiHost, config.apiKey, config.apiTenant);\n\n const dataProvider = new DefaultDataProvider({ apiClient });\n\n let liveSdk: IContentSdk = new LiveSdk(dataProvider);\n\n if (config.preview && !environment.isEditing()) {\n this.isPreview = true;\n liveSdk = new PreviewSdk(dataProvider, liveSdk);\n }\n\n const theme = Theme.from(config.theme ?? {});\n\n if (environment.isClient()) {\n viewportManager.setBreakpoints(theme.breakpoints);\n }\n\n let editingSdk;\n if (environment.isEditing()) {\n editingSdk = new EditingSdk(liveSdk, theme);\n }\n\n this.sdk = new InternalContentSdk(\n new RedirectsProvider(apiClient),\n liveSdk as LiveSdk,\n editingSdk\n );\n\n if (typeof afterInit === \"function\") {\n afterInit();\n }\n }\n\n public getEditingSdk() {\n this.assertInitialized();\n return this.sdk.getEditingSdk();\n }\n\n public getPage(path: string) {\n this.assertInitialized();\n return this.sdk.getPage(path);\n }\n\n public listPages(options?: ListPagesOptions) {\n this.assertInitialized();\n return this.sdk.listPages(options);\n }\n\n public async getAllRedirects() {\n this.assertInitialized();\n return this.sdk.getAllRedirects();\n }\n\n getRedirectByPath(path: string): Promise<PublicRedirect | undefined> {\n this.assertInitialized();\n return this.sdk.getRedirectByPath(path);\n }\n\n registerComponent(blueprint: Component): void {\n this.assertInitialized();\n componentRegistry.register(blueprint);\n }\n\n resolveElement(params: ResolveElementParams): ResolvedComponent[] | null {\n this.assertInitialized();\n return new ComponentResolver(componentRegistry).resolve(params);\n }\n\n isEditing() {\n return environment.isEditing();\n }\n\n isPreviewing() {\n return this.isPreview;\n }\n\n private assertInitialized(): asserts this is this & {\n sdk: IContentSdk;\n } {\n if (!this.sdk) {\n throw new Error(`ContentSdk has not been initialized!`);\n }\n }\n}\n\nexport const contentSdk = new ContentSdk();\n"],"names":["InternalContentSdk","redirectsProvider","liveSdk","editingSdk","path","options","ContentSdk","config","afterInit","configHash","JSON","apiClient","ApiClient","dataProvider","DefaultDataProvider","LiveSdk","environment","PreviewSdk","theme","Theme","viewportManager","EditingSdk","RedirectsProvider","blueprint","componentRegistry","params","ComponentResolver","Error","contentSdk"],"mappings":";;;;;;;;;;;AAkCA,MAAMA;IAKF,YAAYC,iBAA6B,EAAEC,OAAgB,EAAEC,UAAuB,CAAE;QAClF,IAAI,CAAC,iBAAiB,GAAGF;QACzB,IAAI,CAAC,SAAS,GAAGE,cAAcD;QAC/B,IAAI,CAAC,UAAU,GAAGC;IACtB;IAEA,gBAAgB;QACZ,OAAO,IAAI,CAAC,UAAU;IAC1B;IAEA,MAAM,QAAQC,IAAY,EAA8B;QACpD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAACA;IAClC;IAEA,UAAUC,OAA0B,EAA4B;QAC5D,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,CAACA;IACpC;IAEA,kBAAwD;QACpD,OAAO,IAAI,CAAC,iBAAiB,CAAC,eAAe;IACjD;IAEA,kBAAkBD,IAAY,EAAuC;QACjE,OAAO,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,CAACA;IACpD;AACJ;AAEO,MAAME;IAKF,KAAKC,MAAwB,EAAEC,SAAsB,EAAQ;QAChE,MAAMC,aAAaC,KAAK,SAAS,CAACH;QAClC,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,KAAKE,YACvC;QAGJ,IAAI,CAAC,UAAU,GAAGA;QAClB,MAAME,YAAY,IAAIC,UAAUL,OAAO,OAAO,EAAEA,OAAO,MAAM,EAAEA,OAAO,SAAS;QAE/E,MAAMM,eAAe,IAAIC,oBAAoB;YAAEH;QAAU;QAEzD,IAAIT,UAAuB,IAAIa,QAAQF;QAEvC,IAAIN,OAAO,OAAO,IAAI,CAACS,YAAY,SAAS,IAAI;YAC5C,IAAI,CAAC,SAAS,GAAG;YACjBd,UAAU,IAAIe,WAAWJ,cAAcX;QAC3C;QAEA,MAAMgB,QAAQC,MAAM,IAAI,CAACZ,OAAO,KAAK,IAAI,CAAC;QAE1C,IAAIS,YAAY,QAAQ,IACpBI,gBAAgB,cAAc,CAACF,MAAM,WAAW;QAGpD,IAAIf;QACJ,IAAIa,YAAY,SAAS,IACrBb,aAAa,IAAIkB,WAAWnB,SAASgB;QAGzC,IAAI,CAAC,GAAG,GAAG,IAAIlB,mBACX,IAAIsB,kBAAkBX,YACtBT,SACAC;QAGJ,IAAI,AAAqB,cAArB,OAAOK,WACPA;IAER;IAEO,gBAAgB;QACnB,IAAI,CAAC,iBAAiB;QACtB,OAAO,IAAI,CAAC,GAAG,CAAC,aAAa;IACjC;IAEO,QAAQJ,IAAY,EAAE;QACzB,IAAI,CAAC,iBAAiB;QACtB,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAACA;IAC5B;IAEO,UAAUC,OAA0B,EAAE;QACzC,IAAI,CAAC,iBAAiB;QACtB,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAACA;IAC9B;IAEA,MAAa,kBAAkB;QAC3B,IAAI,CAAC,iBAAiB;QACtB,OAAO,IAAI,CAAC,GAAG,CAAC,eAAe;IACnC;IAEA,kBAAkBD,IAAY,EAAuC;QACjE,IAAI,CAAC,iBAAiB;QACtB,OAAO,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAACA;IACtC;IAEA,kBAAkBmB,SAAoB,EAAQ;QAC1C,IAAI,CAAC,iBAAiB;QACtBC,kBAAkB,QAAQ,CAACD;IAC/B;IAEA,eAAeE,MAA4B,EAA8B;QACrE,IAAI,CAAC,iBAAiB;QACtB,OAAO,IAAIC,kBAAkBF,mBAAmB,OAAO,CAACC;IAC5D;IAEA,YAAY;QACR,OAAOT,YAAY,SAAS;IAChC;IAEA,eAAe;QACX,OAAO,IAAI,CAAC,SAAS;IACzB;IAEQ,oBAEN;QACE,IAAI,CAAC,IAAI,CAAC,GAAG,EACT,MAAM,IAAIW,MAAM;IAExB;;aA5FQ,SAAS,GAAG;;AA6FxB;AAEO,MAAMC,aAAa,IAAItB"}
1
+ {"version":3,"file":"ContentSdk.js","sources":["../src/ContentSdk.ts"],"sourcesContent":["import type {\n Component,\n Document,\n IContentSdk,\n IDataProvider,\n ListPagesOptions,\n ListPagesResult,\n PublicPage,\n PublicRedirect,\n ResolvedComponent\n} from \"~/types.js\";\nimport type { ActiveExperiment, VariantContent } from \"~/experiments/types.js\";\nimport {\n getPageWithExperiment,\n type ExperimentRenderResult,\n type GetPageWithExperimentOptions\n} from \"~/experiments/render.js\";\nimport { environment } from \"./Environment.js\";\nimport { LiveSdk } from \"./LiveSdk.js\";\nimport { EditingSdk } from \"./EditingSdk.js\";\nimport { ComponentResolver, type ResolveElementParams } from \"~/ComponentResolver.js\";\nimport { PreviewSdk } from \"./PreviewSdk.js\";\nimport type { ComponentRegistry } from \"~/ComponentRegistry.js\";\nimport { componentRegistry } from \"~/ComponentRegistry.js\";\nimport type { ContentEntryCache } from \"~/contentEntry/ContentEntryCache.js\";\nimport { contentEntryCache } from \"~/contentEntry/ContentEntryCache.js\";\nimport type { DocumentStoreManager } from \"~/DocumentStoreManager.js\";\nimport { documentStoreManager } from \"~/DocumentStoreManager.js\";\nimport { ApiClient } from \"~/dataProviders/ApiClient.js\";\nimport { DefaultDataProvider } from \"~/dataProviders/DefaultDataProvider.js\";\nimport type { WebsiteBuilderThemeInput } from \"./types/WebsiteBuilderTheme.js\";\nimport { Theme } from \"./Theme.js\";\nimport { viewportManager } from \"./ViewportManager.js\";\nimport type { IRedirects } from \"~/IRedirects.js\";\nimport { RedirectsProvider } from \"~/dataProviders/RedirectsProvider.js\";\nimport type { HeadersProvider } from \"~/headersProvider.js\";\nimport type { ContentEntryLoader } from \"~/contentEntry/resolveContentEntry.js\";\nimport type { ComponentLoader, ComponentLoaderFactory } from \"~/componentLoader/ComponentLoader.js\";\nimport type { ComponentLoaderFactoryParams } from \"~/componentLoader/ComponentLoader.js\";\nimport { createComponentLoader } from \"~/componentLoader/createComponentLoader.js\";\n\nexport type ApiConfig = {\n apiKey: string;\n apiHost: string;\n apiTenant: string;\n};\n\nexport type ContentSDKConfig = ApiConfig & {\n preview?: boolean;\n previewParams?: string;\n theme?: WebsiteBuilderThemeInput;\n /**\n * Request headers for server-side preview and experiments. When omitted, the provider set\n * with `setHeadersProvider` is used. Meant for apps that serve requests outside the\n * framework hook; it never carries the tenant.\n */\n headersProvider?: HeadersProvider;\n /**\n * Loads the CMS entries that `contentEntry` inputs reference, for this SDK's content\n * entry cache. Pass one bound to the same tenant as this SDK. When omitted, the cache's\n * loader is left as it is.\n */\n contentEntryLoader?: ContentEntryLoader;\n /**\n * Creates the loader for components a document references but nobody registered. It gets\n * this SDK's API client and API config. When omitted, the default set with\n * `setDefaultComponentLoader` is used (`@webiny/sdk-nextjs` sets its remote component\n * loading), and without one nothing is loaded.\n */\n componentLoader?: ComponentLoaderFactory;\n};\n\n/**\n * The state a `ContentSdk` renders with. Each piece defaults to its module-level singleton, so\n * `new ContentSdk()` shares state with `contentSdk`. `createContentClient` passes new instances.\n */\nexport type ContentSdkState = {\n componentRegistry?: ComponentRegistry;\n contentEntryCache?: ContentEntryCache;\n documentStoreManager?: DocumentStoreManager;\n};\n\nclass InternalContentSdk implements IContentSdk, IRedirects {\n private activeSdk: IContentSdk;\n private editingSdk: EditingSdk | undefined;\n private redirectsProvider: IRedirects;\n\n constructor(redirectsProvider: IRedirects, liveSdk: LiveSdk, editingSdk?: EditingSdk) {\n this.redirectsProvider = redirectsProvider;\n this.activeSdk = editingSdk ?? liveSdk;\n this.editingSdk = editingSdk;\n }\n\n getEditingSdk() {\n return this.editingSdk;\n }\n\n async getPage(path: string): Promise<PublicPage | null> {\n return this.activeSdk.getPage(path);\n }\n\n listPages(options?: ListPagesOptions): Promise<ListPagesResult> {\n return this.activeSdk.listPages(options);\n }\n\n getAllRedirects(): Promise<Map<string, PublicRedirect>> {\n return this.redirectsProvider.getAllRedirects();\n }\n\n getRedirectByPath(path: string): Promise<PublicRedirect | undefined> {\n return this.redirectsProvider.getRedirectByPath(path);\n }\n}\n\nexport class ContentSdk implements IContentSdk, IRedirects {\n protected sdk?: InternalContentSdk;\n private isPreview = false;\n private lastConfig: any;\n private dataProvider?: IDataProvider;\n private headersProvider?: HeadersProvider;\n private componentLoaderFactory?: ComponentLoaderFactory;\n private componentLoaderParams?: ComponentLoaderFactoryParams;\n private componentLoader?: ComponentLoader;\n private readonly componentRegistry: ComponentRegistry;\n private readonly contentEntryCache: ContentEntryCache;\n private readonly documentStoreManager: DocumentStoreManager;\n\n constructor(state: ContentSdkState = {}) {\n this.componentRegistry = state.componentRegistry ?? componentRegistry;\n this.contentEntryCache = state.contentEntryCache ?? contentEntryCache;\n this.documentStoreManager = state.documentStoreManager ?? documentStoreManager;\n }\n\n public init(config: ContentSDKConfig, afterInit?: () => void): void {\n const configHash = JSON.stringify(config);\n if (this.lastConfig && this.lastConfig === configHash) {\n return;\n }\n\n this.lastConfig = configHash;\n this.headersProvider = config.headersProvider;\n if (config.contentEntryLoader) {\n this.contentEntryCache.setLoader(config.contentEntryLoader);\n }\n const apiClient = new ApiClient({\n apiHost: config.apiHost,\n apiKey: config.apiKey,\n apiTenant: config.apiTenant,\n preview: config.preview\n });\n\n const dataProvider = new DefaultDataProvider({ apiClient });\n\n // The loader is created on first use, so a default set after this call still applies.\n this.componentLoaderFactory = config.componentLoader;\n this.componentLoaderParams = {\n apiClient,\n config: {\n apiHost: config.apiHost,\n apiKey: config.apiKey,\n apiTenant: config.apiTenant\n }\n };\n this.componentLoader = undefined;\n this.dataProvider = dataProvider;\n\n let liveSdk: IContentSdk = new LiveSdk(dataProvider, this.documentStoreManager);\n\n if (config.preview && !environment.isEditing()) {\n this.isPreview = true;\n liveSdk = new PreviewSdk(\n dataProvider,\n liveSdk,\n config.previewParams,\n config.headersProvider\n );\n }\n\n const theme = Theme.from(config.theme ?? {});\n\n if (environment.isClient()) {\n viewportManager.setBreakpoints(theme.breakpoints);\n }\n\n let editingSdk;\n if (environment.isEditing()) {\n editingSdk = new EditingSdk(liveSdk, theme, {\n componentRegistry: this.componentRegistry,\n documentStoreManager: this.documentStoreManager\n });\n }\n\n this.sdk = new InternalContentSdk(\n new RedirectsProvider(apiClient),\n liveSdk as LiveSdk,\n editingSdk\n );\n\n if (typeof afterInit === \"function\") {\n afterInit();\n }\n }\n\n public getEditingSdk() {\n this.assertInitialized();\n return this.sdk.getEditingSdk();\n }\n\n public getPage(path: string) {\n this.assertInitialized();\n return this.sdk.getPage(path);\n }\n\n public getPageExperiment(path: string): Promise<ActiveExperiment | null> {\n return this.requireDataProvider().getPageExperiment(path);\n }\n\n public getVariantContent(variantId: string): Promise<VariantContent | null> {\n return this.requireDataProvider().getVariantContent(variantId);\n }\n\n public getExperimentPaused(experimentId: string): Promise<boolean> {\n return this.requireDataProvider().getExperimentPaused(experimentId);\n }\n\n /**\n * Resolve and render the right page for the current visitor (control or a variant),\n * server-side. Bucketing, targeting, exposure emission, and cache-key handling are all\n * encapsulated here so projects do not reimplement them. See {@link getPageWithExperiment}.\n */\n public getPageWithExperiment(\n path: string,\n options?: GetPageWithExperimentOptions\n ): Promise<ExperimentRenderResult> {\n this.assertInitialized();\n this.requireDataProvider();\n const experimentOptions: GetPageWithExperimentOptions = { ...options };\n if (this.headersProvider && !experimentOptions.headersProvider) {\n experimentOptions.headersProvider = this.headersProvider;\n }\n return getPageWithExperiment(\n {\n getPage: p => this.getPage(p),\n getPageExperiment: p => this.getPageExperiment(p),\n getVariantContent: id => this.getVariantContent(id),\n getExperimentPaused: id => this.getExperimentPaused(id)\n },\n path,\n experimentOptions\n );\n }\n\n public listPages(options?: ListPagesOptions) {\n this.assertInitialized();\n return this.sdk.listPages(options);\n }\n\n public async getAllRedirects() {\n this.assertInitialized();\n return this.sdk.getAllRedirects();\n }\n\n getRedirectByPath(path: string): Promise<PublicRedirect | undefined> {\n this.assertInitialized();\n return this.sdk.getRedirectByPath(path);\n }\n\n registerComponent(blueprint: Component): void {\n this.assertInitialized();\n this.componentRegistry.register(blueprint);\n }\n\n resolveElement(params: ResolveElementParams): ResolvedComponent[] | null {\n this.assertInitialized();\n return new ComponentResolver(this.componentRegistry, this.contentEntryCache).resolve(\n params\n );\n }\n\n /**\n * Load every component the document references that isn't registered yet, through the\n * component loader, and register what it returns. Call it before rendering the document.\n * Nothing is cached here: an unregistered component goes to the loader on every call.\n */\n public async loadDocumentComponents(document: Document): Promise<void> {\n this.assertInitialized();\n const names = new Set<string>();\n for (const element of Object.values(document.elements)) {\n const name = element.component?.name;\n if (name && !this.componentRegistry.get(name)) {\n names.add(name);\n }\n }\n\n const loader = this.getComponentLoader();\n const loads = Array.from(names).map(name => loader.loadComponent({ name }));\n const components = await Promise.all(loads);\n\n for (const component of components) {\n if (component) {\n this.componentRegistry.register(component);\n }\n }\n }\n\n private getComponentLoader(): ComponentLoader {\n if (!this.componentLoader) {\n this.componentLoader = createComponentLoader(\n this.componentLoaderFactory,\n this.componentLoaderParams!\n );\n }\n return this.componentLoader;\n }\n\n public getComponentRegistry(): ComponentRegistry {\n return this.componentRegistry;\n }\n\n public getContentEntryCache(): ContentEntryCache {\n return this.contentEntryCache;\n }\n\n public getDocumentStoreManager(): DocumentStoreManager {\n return this.documentStoreManager;\n }\n\n isEditing() {\n return environment.isEditing();\n }\n\n isPreviewing() {\n return this.isPreview;\n }\n\n private assertInitialized(): asserts this is this & {\n sdk: IContentSdk;\n } {\n if (!this.sdk) {\n throw new Error(`ContentSdk has not been initialized!`);\n }\n }\n\n private requireDataProvider(): IDataProvider {\n if (!this.dataProvider) {\n throw new Error(`ContentSdk has not been initialized!`);\n }\n return this.dataProvider;\n }\n}\n\nexport const contentSdk = new ContentSdk();\n"],"names":["InternalContentSdk","redirectsProvider","liveSdk","editingSdk","path","options","ContentSdk","state","componentRegistry","contentEntryCache","documentStoreManager","config","afterInit","configHash","JSON","apiClient","ApiClient","dataProvider","DefaultDataProvider","undefined","LiveSdk","environment","PreviewSdk","theme","Theme","viewportManager","EditingSdk","RedirectsProvider","variantId","experimentId","experimentOptions","getPageWithExperiment","p","id","blueprint","params","ComponentResolver","document","names","Set","element","Object","name","loader","loads","Array","components","Promise","component","createComponentLoader","Error","contentSdk"],"mappings":"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"}
@@ -1,11 +1,18 @@
1
1
  import type { Document } from "./types.js";
2
2
  import { DocumentStore } from "./DocumentStore.js";
3
- declare class DocumentStoreManager {
3
+ export declare const DEFAULT_MAX_DOCUMENT_STORES = 10000;
4
+ export type DocumentStoreManagerOptions = {
5
+ maxEntries?: number;
6
+ };
7
+ export declare class DocumentStoreManager {
4
8
  private stores;
9
+ private readonly maxEntries;
10
+ constructor(options?: DocumentStoreManagerOptions);
5
11
  getStores(): Map<string, DocumentStore<Document>>;
6
12
  getStore<TDocument extends Document>(id: string): DocumentStore<TDocument>;
7
13
  removeStore(id: string): void;
8
14
  hasStore(id: string): boolean;
15
+ clear(): void;
16
+ private evict;
9
17
  }
10
18
  export declare const documentStoreManager: DocumentStoreManager;
11
- export {};
@@ -1,11 +1,24 @@
1
1
  import { DocumentStore } from "./DocumentStore.js";
2
+ const DEFAULT_MAX_DOCUMENT_STORES = 10000;
2
3
  class DocumentStoreManager {
4
+ constructor(options = {}){
5
+ this.stores = new Map();
6
+ this.maxEntries = options.maxEntries ?? DEFAULT_MAX_DOCUMENT_STORES;
7
+ }
3
8
  getStores() {
4
9
  return this.stores;
5
10
  }
6
11
  getStore(id) {
7
- if (!this.stores.has(id)) this.stores.set(id, new DocumentStore());
8
- return this.stores.get(id);
12
+ const existing = this.stores.get(id);
13
+ if (existing) {
14
+ this.stores.delete(id);
15
+ this.stores.set(id, existing);
16
+ return existing;
17
+ }
18
+ const store = new DocumentStore();
19
+ this.stores.set(id, store);
20
+ this.evict();
21
+ return store;
9
22
  }
10
23
  removeStore(id) {
11
24
  this.stores.delete(id);
@@ -13,11 +26,17 @@ class DocumentStoreManager {
13
26
  hasStore(id) {
14
27
  return this.stores.has(id);
15
28
  }
16
- constructor(){
17
- this.stores = new Map();
29
+ clear() {
30
+ this.stores.clear();
31
+ }
32
+ evict() {
33
+ while(this.stores.size > this.maxEntries){
34
+ const oldest = this.stores.keys().next().value;
35
+ this.stores.delete(oldest);
36
+ }
18
37
  }
19
38
  }
20
39
  const documentStoreManager = new DocumentStoreManager();
21
- export { documentStoreManager };
40
+ export { DEFAULT_MAX_DOCUMENT_STORES, DocumentStoreManager, documentStoreManager };
22
41
 
23
42
  //# sourceMappingURL=DocumentStoreManager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentStoreManager.js","sources":["../src/DocumentStoreManager.ts"],"sourcesContent":["import type { Document } from \"./types.js\";\nimport { DocumentStore } from \"./DocumentStore.js\";\n\nclass DocumentStoreManager {\n private stores = new Map<string, DocumentStore>();\n\n getStores() {\n return this.stores;\n }\n\n getStore<TDocument extends Document>(id: string): DocumentStore<TDocument> {\n if (!this.stores.has(id)) {\n this.stores.set(id, new DocumentStore<TDocument>());\n }\n return this.stores.get(id)! as DocumentStore<TDocument>;\n }\n\n removeStore(id: string) {\n this.stores.delete(id);\n }\n\n hasStore(id: string): boolean {\n return this.stores.has(id);\n }\n}\n\nexport const documentStoreManager = new DocumentStoreManager();\n"],"names":["DocumentStoreManager","id","DocumentStore","Map","documentStoreManager"],"mappings":";AAGA,MAAMA;IAGF,YAAY;QACR,OAAO,IAAI,CAAC,MAAM;IACtB;IAEA,SAAqCC,EAAU,EAA4B;QACvE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAACA,KACjB,IAAI,CAAC,MAAM,CAAC,GAAG,CAACA,IAAI,IAAIC;QAE5B,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAACD;IAC3B;IAEA,YAAYA,EAAU,EAAE;QACpB,IAAI,CAAC,MAAM,CAAC,MAAM,CAACA;IACvB;IAEA,SAASA,EAAU,EAAW;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAACA;IAC3B;;aAnBQ,MAAM,GAAG,IAAIE;;AAoBzB;AAEO,MAAMC,uBAAuB,IAAIJ"}
1
+ {"version":3,"file":"DocumentStoreManager.js","sources":["../src/DocumentStoreManager.ts"],"sourcesContent":["import type { Document } from \"./types.js\";\nimport { DocumentStore } from \"./DocumentStore.js\";\n\n// High enough that an app serving one site never reaches it.\nexport const DEFAULT_MAX_DOCUMENT_STORES = 10_000;\n\nexport type DocumentStoreManagerOptions = {\n // Stores kept before the least recently used one is dropped.\n maxEntries?: number;\n};\n\nexport class DocumentStoreManager {\n private stores = new Map<string, DocumentStore>();\n private readonly maxEntries: number;\n\n constructor(options: DocumentStoreManagerOptions = {}) {\n this.maxEntries = options.maxEntries ?? DEFAULT_MAX_DOCUMENT_STORES;\n }\n\n getStores() {\n return this.stores;\n }\n\n getStore<TDocument extends Document>(id: string): DocumentStore<TDocument> {\n const existing = this.stores.get(id);\n if (existing) {\n // Move it to the end, so the map stays ordered from least to most recently used.\n this.stores.delete(id);\n this.stores.set(id, existing);\n return existing as DocumentStore<TDocument>;\n }\n\n const store = new DocumentStore<TDocument>();\n this.stores.set(id, store);\n this.evict();\n return store;\n }\n\n removeStore(id: string) {\n this.stores.delete(id);\n }\n\n hasStore(id: string): boolean {\n return this.stores.has(id);\n }\n\n clear() {\n this.stores.clear();\n }\n\n private evict() {\n while (this.stores.size > this.maxEntries) {\n const oldest = this.stores.keys().next().value!;\n this.stores.delete(oldest);\n }\n }\n}\n\nexport const documentStoreManager = new DocumentStoreManager();\n"],"names":["DEFAULT_MAX_DOCUMENT_STORES","DocumentStoreManager","options","Map","id","existing","store","DocumentStore","oldest","documentStoreManager"],"mappings":";AAIO,MAAMA,8BAA8B;AAOpC,MAAMC;IAIT,YAAYC,UAAuC,CAAC,CAAC,CAAE;aAH/C,MAAM,GAAG,IAAIC;QAIjB,IAAI,CAAC,UAAU,GAAGD,QAAQ,UAAU,IAAIF;IAC5C;IAEA,YAAY;QACR,OAAO,IAAI,CAAC,MAAM;IACtB;IAEA,SAAqCI,EAAU,EAA4B;QACvE,MAAMC,WAAW,IAAI,CAAC,MAAM,CAAC,GAAG,CAACD;QACjC,IAAIC,UAAU;YAEV,IAAI,CAAC,MAAM,CAAC,MAAM,CAACD;YACnB,IAAI,CAAC,MAAM,CAAC,GAAG,CAACA,IAAIC;YACpB,OAAOA;QACX;QAEA,MAAMC,QAAQ,IAAIC;QAClB,IAAI,CAAC,MAAM,CAAC,GAAG,CAACH,IAAIE;QACpB,IAAI,CAAC,KAAK;QACV,OAAOA;IACX;IAEA,YAAYF,EAAU,EAAE;QACpB,IAAI,CAAC,MAAM,CAAC,MAAM,CAACA;IACvB;IAEA,SAASA,EAAU,EAAW;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAACA;IAC3B;IAEA,QAAQ;QACJ,IAAI,CAAC,MAAM,CAAC,KAAK;IACrB;IAEQ,QAAQ;QACZ,MAAO,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAE;YACvC,MAAMI,SAAS,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,GAAG,KAAK;YAC9C,IAAI,CAAC,MAAM,CAAC,MAAM,CAACA;QACvB;IACJ;AACJ;AAEO,MAAMC,uBAAuB,IAAIR"}
package/EditingSdk.d.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  import type { ComponentGroup, IContentSdk, ListPagesOptions, ListPagesResult, PublicPage } from "./types.js";
2
2
  import { Messenger } from "./messenger/index.js";
3
+ import type { ComponentRegistry } from "./ComponentRegistry.js";
4
+ import type { DocumentStoreManager } from "./DocumentStoreManager.js";
3
5
  import type { WebsiteBuilderTheme } from "./types/WebsiteBuilderTheme.js";
6
+ export type EditingSdkState = {
7
+ componentRegistry?: ComponentRegistry;
8
+ documentStoreManager?: DocumentStoreManager;
9
+ };
4
10
  export declare class EditingSdk implements IContentSdk {
5
11
  readonly messenger: Messenger;
6
12
  private positionReportingEnabled;
@@ -9,7 +15,7 @@ export declare class EditingSdk implements IContentSdk {
9
15
  private documentStore;
10
16
  private previewDocument;
11
17
  private lastBoxesHash;
12
- constructor(liveSdk: IContentSdk, theme: WebsiteBuilderTheme);
18
+ constructor(liveSdk: IContentSdk, theme: WebsiteBuilderTheme, state?: EditingSdkState);
13
19
  getPage(path: string): Promise<PublicPage | null>;
14
20
  listPages(options?: ListPagesOptions): Promise<ListPagesResult>;
15
21
  registerComponentGroup(group: ComponentGroup): void;
package/EditingSdk.js CHANGED
@@ -9,17 +9,19 @@ import { documentStoreManager } from "./DocumentStoreManager.js";
9
9
  import { PreviewDocument } from "./PreviewDocument.js";
10
10
  import { hashObject } from "./HashObject.js";
11
11
  class EditingSdk {
12
- constructor(liveSdk, theme){
12
+ constructor(liveSdk, theme, state = {}){
13
13
  this.positionReportingEnabled = false;
14
14
  this.previewViewport = null;
15
15
  this.lastBoxesHash = 0;
16
16
  this.liveSdk = liveSdk;
17
+ const registry = state.componentRegistry ?? componentRegistry;
18
+ const stores = state.documentStoreManager ?? documentStoreManager;
17
19
  const source = new MessageOrigin(()=>window, window.location.origin);
18
20
  const target = new MessageOrigin(()=>window.parent, this.getReferrerOrigin());
19
21
  this.previewDocument = PreviewDocument.createFromWindow();
20
- this.documentStore = documentStoreManager.getStore(this.previewDocument.getId());
22
+ this.documentStore = stores.getStore(this.previewDocument.getId());
21
23
  this.messenger = new Messenger(source, target, "wb.editor.*");
22
- componentRegistry.onRegister((reg)=>{
24
+ registry.onRegister((reg)=>{
23
25
  this.messenger.send("preview.component.register", reg.component.manifest);
24
26
  });
25
27
  this.setupListeners();