@webiny/website-builder-sdk 6.4.11 → 6.6.0-alpha.1

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 (88) hide show
  1. package/ContentSdk.d.ts +13 -0
  2. package/ContentSdk.js +25 -0
  3. package/ContentSdk.js.map +1 -1
  4. package/ElementFactory.js +3 -1
  5. package/ElementFactory.js.map +1 -1
  6. package/asset/deliveryUrl.d.ts +41 -0
  7. package/asset/deliveryUrl.js +34 -0
  8. package/asset/deliveryUrl.js.map +1 -0
  9. package/asset/deliveryUrl.test.d.ts +1 -0
  10. package/asset/deliveryUrl.test.js +127 -0
  11. package/asset/deliveryUrl.test.js.map +1 -0
  12. package/asset/geometry.d.ts +7 -0
  13. package/asset/geometry.js +10 -0
  14. package/asset/geometry.js.map +1 -0
  15. package/asset/imageSrcSet.d.ts +63 -0
  16. package/asset/imageSrcSet.js +98 -0
  17. package/asset/imageSrcSet.js.map +1 -0
  18. package/asset/imageSrcSet.test.d.ts +1 -0
  19. package/asset/imageSrcSet.test.js +103 -0
  20. package/asset/imageSrcSet.test.js.map +1 -0
  21. package/asset/index.d.ts +5 -0
  22. package/asset/index.js +5 -0
  23. package/asset/normalize.d.ts +24 -0
  24. package/asset/normalize.js +102 -0
  25. package/asset/normalize.js.map +1 -0
  26. package/asset/normalize.test.d.ts +1 -0
  27. package/asset/normalize.test.js +256 -0
  28. package/asset/normalize.test.js.map +1 -0
  29. package/asset/types.d.ts +2 -0
  30. package/asset/types.js +0 -0
  31. package/createComponent.d.ts +5 -0
  32. package/createComponent.js +32 -0
  33. package/createComponent.js.map +1 -0
  34. package/dataProviders/ApiClient.d.ts +3 -1
  35. package/dataProviders/ApiClient.js +4 -2
  36. package/dataProviders/ApiClient.js.map +1 -1
  37. package/dataProviders/DefaultDataProvider.d.ts +4 -0
  38. package/dataProviders/DefaultDataProvider.js +35 -0
  39. package/dataProviders/DefaultDataProvider.js.map +1 -1
  40. package/dataProviders/GET_EXPERIMENT_PAUSED.d.ts +1 -0
  41. package/dataProviders/GET_EXPERIMENT_PAUSED.js +17 -0
  42. package/dataProviders/GET_EXPERIMENT_PAUSED.js.map +1 -0
  43. package/dataProviders/GET_PAGE_EXPERIMENT.d.ts +1 -0
  44. package/dataProviders/GET_PAGE_EXPERIMENT.js +32 -0
  45. package/dataProviders/GET_PAGE_EXPERIMENT.js.map +1 -0
  46. package/dataProviders/GET_VARIANT_CONTENT.d.ts +1 -0
  47. package/dataProviders/GET_VARIANT_CONTENT.js +24 -0
  48. package/dataProviders/GET_VARIANT_CONTENT.js.map +1 -0
  49. package/dataProviders/NullDataProvider.d.ts +4 -0
  50. package/dataProviders/NullDataProvider.js +9 -0
  51. package/dataProviders/NullDataProvider.js.map +1 -1
  52. package/experiments/PostHogAnalyticsProvider.d.ts +31 -0
  53. package/experiments/PostHogAnalyticsProvider.js +28 -0
  54. package/experiments/PostHogAnalyticsProvider.js.map +1 -0
  55. package/experiments/analyticsProvider.d.ts +4 -0
  56. package/experiments/analyticsProvider.js +11 -0
  57. package/experiments/analyticsProvider.js.map +1 -0
  58. package/experiments/bucketing.d.ts +27 -0
  59. package/experiments/bucketing.js +91 -0
  60. package/experiments/bucketing.js.map +1 -0
  61. package/experiments/bucketing.test.d.ts +1 -0
  62. package/experiments/bucketing.test.js +183 -0
  63. package/experiments/bucketing.test.js.map +1 -0
  64. package/experiments/index.d.ts +5 -0
  65. package/experiments/index.js +5 -0
  66. package/experiments/render.d.ts +79 -0
  67. package/experiments/render.js +149 -0
  68. package/experiments/render.js.map +1 -0
  69. package/experiments/types.d.ts +108 -0
  70. package/experiments/types.js +4 -0
  71. package/experiments/types.js.map +1 -0
  72. package/image/geometry.d.ts +54 -0
  73. package/image/geometry.js +112 -0
  74. package/image/geometry.js.map +1 -0
  75. package/image/geometry.test.d.ts +1 -0
  76. package/image/geometry.test.js +190 -0
  77. package/image/geometry.test.js.map +1 -0
  78. package/image/index.d.ts +3 -0
  79. package/image/index.js +3 -0
  80. package/image/presets.d.ts +6 -0
  81. package/image/presets.js +20 -0
  82. package/image/presets.js.map +1 -0
  83. package/image/types.d.ts +10 -0
  84. package/image/types.js +0 -0
  85. package/index.d.ts +4 -0
  86. package/index.js +4 -0
  87. package/package.json +7 -6
  88. package/types.d.ts +5 -0
package/ContentSdk.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import type { Component, 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";
@@ -29,9 +31,19 @@ export declare class ContentSdk implements IContentSdk, IRedirects {
29
31
  protected sdk?: InternalContentSdk;
30
32
  private isPreview;
31
33
  private lastConfig;
34
+ private dataProvider?;
32
35
  init(config: ContentSDKConfig, afterInit?: () => void): void;
33
36
  getEditingSdk(): EditingSdk | undefined;
34
37
  getPage(path: string): Promise<PublicPage | null>;
38
+ getPageExperiment(path: string): Promise<ActiveExperiment | null>;
39
+ getVariantContent(variantId: string): Promise<VariantContent | null>;
40
+ getExperimentPaused(experimentId: string): Promise<boolean>;
41
+ /**
42
+ * Resolve and render the right page for the current visitor (control or a variant),
43
+ * server-side. Bucketing, targeting, exposure emission, and cache-key handling are all
44
+ * encapsulated here so projects do not reimplement them. See {@link getPageWithExperiment}.
45
+ */
46
+ getPageWithExperiment(path: string, options?: GetPageWithExperimentOptions): Promise<ExperimentRenderResult>;
35
47
  listPages(options?: ListPagesOptions): Promise<ListPagesResult>;
36
48
  getAllRedirects(): Promise<Map<string, PublicRedirect>>;
37
49
  getRedirectByPath(path: string): Promise<PublicRedirect | undefined>;
@@ -40,6 +52,7 @@ export declare class ContentSdk implements IContentSdk, IRedirects {
40
52
  isEditing(): boolean;
41
53
  isPreviewing(): boolean;
42
54
  private assertInitialized;
55
+ private requireDataProvider;
43
56
  }
44
57
  export declare const contentSdk: ContentSdk;
45
58
  export {};
package/ContentSdk.js CHANGED
@@ -1,3 +1,4 @@
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";
@@ -45,6 +46,7 @@ class ContentSdk {
45
46
  const dataProvider = new DefaultDataProvider({
46
47
  apiClient
47
48
  });
49
+ this.dataProvider = dataProvider;
48
50
  let liveSdk = new LiveSdk(dataProvider);
49
51
  if (config.preview && !environment.isEditing()) {
50
52
  this.isPreview = true;
@@ -65,6 +67,25 @@ class ContentSdk {
65
67
  this.assertInitialized();
66
68
  return this.sdk.getPage(path);
67
69
  }
70
+ getPageExperiment(path) {
71
+ return this.requireDataProvider().getPageExperiment(path);
72
+ }
73
+ getVariantContent(variantId) {
74
+ return this.requireDataProvider().getVariantContent(variantId);
75
+ }
76
+ getExperimentPaused(experimentId) {
77
+ return this.requireDataProvider().getExperimentPaused(experimentId);
78
+ }
79
+ getPageWithExperiment(path, options) {
80
+ this.assertInitialized();
81
+ this.requireDataProvider();
82
+ return getPageWithExperiment({
83
+ getPage: (p)=>this.getPage(p),
84
+ getPageExperiment: (p)=>this.getPageExperiment(p),
85
+ getVariantContent: (id)=>this.getVariantContent(id),
86
+ getExperimentPaused: (id)=>this.getExperimentPaused(id)
87
+ }, path, options);
88
+ }
68
89
  listPages(options) {
69
90
  this.assertInitialized();
70
91
  return this.sdk.listPages(options);
@@ -94,6 +115,10 @@ class ContentSdk {
94
115
  assertInitialized() {
95
116
  if (!this.sdk) throw new Error("ContentSdk has not been initialized!");
96
117
  }
118
+ requireDataProvider() {
119
+ if (!this.dataProvider) throw new Error("ContentSdk has not been initialized!");
120
+ return this.dataProvider;
121
+ }
97
122
  constructor(){
98
123
  this.isPreview = false;
99
124
  }
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 previewParams?: string;\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({\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 let liveSdk: IContentSdk = new LiveSdk(dataProvider);\n\n if (config.preview && !environment.isEditing()) {\n this.isPreview = true;\n liveSdk = new PreviewSdk(dataProvider, liveSdk, config.previewParams);\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":"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"}
1
+ {"version":3,"file":"ContentSdk.js","sources":["../src/ContentSdk.ts"],"sourcesContent":["import type {\n Component,\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 { 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 previewParams?: string;\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 private dataProvider?: IDataProvider;\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({\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 this.dataProvider = dataProvider;\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, config.previewParams);\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 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 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 options\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 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 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","config","afterInit","configHash","JSON","apiClient","ApiClient","dataProvider","DefaultDataProvider","LiveSdk","environment","PreviewSdk","theme","Theme","viewportManager","EditingSdk","RedirectsProvider","variantId","experimentId","getPageWithExperiment","p","id","blueprint","componentRegistry","params","ComponentResolver","Error","contentSdk"],"mappings":"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"}
package/ElementFactory.js CHANGED
@@ -26,12 +26,14 @@ class ElementFactory {
26
26
  defaultOperations.addElement(element),
27
27
  defaultOperations.addToParent(element, index)
28
28
  ];
29
+ const baseStyles = bindings?.styles ?? componentManifest.defaults?.styles ?? {};
30
+ const resolvedStyles = false === componentManifest.applyDefaultStyles ? baseStyles : withDefaultStyles(baseStyles);
29
31
  documentOps.push(...this.generateOperations({
30
32
  element,
31
33
  inputsAst,
32
34
  bindings: {
33
35
  inputs: bindings?.inputs ?? componentManifest.defaults?.inputs ?? {},
34
- styles: withDefaultStyles(bindings?.styles ?? componentManifest.defaults?.styles ?? {}),
36
+ styles: resolvedStyles,
35
37
  overrides: bindings?.overrides ?? {}
36
38
  },
37
39
  operations: defaultOperations
@@ -1 +1 @@
1
- {"version":3,"file":"ElementFactory.js","sources":["../src/ElementFactory.ts"],"sourcesContent":["import { generateElementId } from \"./generateElementId.js\";\nimport type {\n DocumentElement,\n ComponentManifest,\n InputValueBinding,\n StyleValueBinding,\n CssProperties\n} from \"~/types.js\";\nimport { type IDocumentOperation, DocumentOperations } from \"./documentOperations/index.js\";\nimport {\n ComponentManifestToAstConverter,\n type InputAstNode\n} from \"./ComponentManifestToAstConverter.js\";\nimport { ComponentInputTraverser } from \"./ComponentInputTraverser.js\";\n\nconst defaultStyles = {\n display: \"flex\",\n flexDirection: \"column\"\n};\n\nconst withDefaultStyles = (styles: CssProperties) => {\n return { ...defaultStyles, ...styles };\n};\n\nexport interface ElementFactoryCreateElementParams {\n componentName: string;\n parentId: string;\n slot: string;\n index?: number;\n bindings?: {\n inputs?: Record<string, any>;\n styles?: Record<string, any>;\n overrides?: {\n [breakpoint: string]: {\n inputs?: Record<string, any>;\n styles?: Record<string, any>;\n };\n };\n };\n}\n\ninterface GenerateOperationsParams {\n element: DocumentElement;\n inputsAst: InputAstNode[];\n operations: ElementFactoryOperations;\n bindings: {\n inputs: Record<string, any>;\n styles: Record<string, any>;\n overrides: {\n [breakpoint: string]: {\n inputs?: Record<string, any>;\n styles?: Record<string, any>;\n };\n };\n };\n}\n\ninterface GenerateOperationsFromBindingsParams {\n elementId: string;\n inputsAst: InputAstNode[];\n bindings: {\n inputs: Record<string, any>;\n styles: Record<string, any>;\n };\n operations: ElementFactoryOperations;\n ignoreDefaultValues: boolean;\n}\n\ntype ElementFactoryOperations = {\n addElement: (element: DocumentElement) => IDocumentOperation;\n addToParent: (element: DocumentElement, index?: number) => IDocumentOperation;\n setInputBinding: (\n elementId: string,\n bindingPath: string,\n binding: InputValueBinding\n ) => IDocumentOperation;\n setStyleBinding: (\n elementId: string,\n bindingPath: string,\n binding: StyleValueBinding\n ) => IDocumentOperation;\n};\n\nconst defaultOperations: ElementFactoryOperations = {\n addElement: (element: DocumentElement) => {\n return new DocumentOperations.AddElement(element);\n },\n addToParent: (element: DocumentElement, index?: number) => {\n return new DocumentOperations.AddToParent(element, index);\n },\n setInputBinding: (elementId, bindingPath, binding) => {\n return new DocumentOperations.SetGlobalInputBinding(elementId, bindingPath, binding);\n },\n setStyleBinding: (elementId, bindingPath, binding) => {\n return new DocumentOperations.SetGlobalStyleBinding(elementId, bindingPath, binding);\n }\n};\n\nexport class ElementFactory {\n constructor(private components: Record<string, ComponentManifest>) {}\n\n public createElementFromComponent({\n componentName,\n parentId,\n slot,\n index,\n bindings\n }: ElementFactoryCreateElementParams) {\n const { element, componentManifest, inputsAst } = this.createElement(\n componentName,\n parentId,\n slot\n );\n\n const documentOps: IDocumentOperation[] = [\n defaultOperations.addElement(element),\n defaultOperations.addToParent(element, index)\n ];\n\n documentOps.push(\n ...this.generateOperations({\n element,\n inputsAst,\n bindings: {\n inputs: bindings?.inputs ?? componentManifest.defaults?.inputs ?? {},\n styles: withDefaultStyles(\n bindings?.styles ?? componentManifest.defaults?.styles ?? {}\n ),\n overrides: bindings?.overrides ?? {}\n },\n operations: defaultOperations\n })\n );\n\n return { element, operations: documentOps };\n }\n\n public generateOperations({\n element,\n inputsAst,\n bindings,\n operations\n }: GenerateOperationsParams): IDocumentOperation[] {\n const ops = this.generateOperationsFromBindings({\n elementId: element.id,\n inputsAst,\n bindings,\n operations,\n ignoreDefaultValues: false\n });\n\n if (bindings.overrides) {\n for (const [breakpoint, overrides] of Object.entries(bindings.overrides)) {\n ops.push(\n ...this.generateOperationsFromBindings({\n elementId: element.id,\n inputsAst,\n bindings: {\n inputs: overrides.inputs ?? {},\n styles: overrides.styles ?? {}\n },\n operations: {\n ...operations,\n setInputBinding: (elementId, bindingPath, binding) => {\n return new DocumentOperations.SetInputBindingOverride(\n elementId,\n bindingPath,\n binding,\n breakpoint\n );\n },\n setStyleBinding: (elementId, bindingPath, binding) => {\n return new DocumentOperations.SetStyleBindingOverride(\n elementId,\n bindingPath,\n binding,\n breakpoint\n );\n }\n },\n ignoreDefaultValues: true\n })\n );\n }\n }\n\n return ops;\n }\n\n private generateOperationsFromBindings({\n elementId,\n inputsAst,\n bindings,\n operations,\n ignoreDefaultValues\n }: GenerateOperationsFromBindingsParams): IDocumentOperation[] {\n const inputData = bindings.inputs;\n const traverser = new ComponentInputTraverser(inputsAst);\n\n const ops: IDocumentOperation[] = [];\n\n traverser.traverse(inputData, (node, path, value) => {\n const isCreateElement = value?.action === \"CreateElement\";\n const isList = node.list;\n const isObject = node.type === \"object\";\n\n if (isCreateElement) {\n const factory = new ElementFactory(this.components);\n const newElement = factory.createElementFromComponent({\n componentName: value.params.component,\n // undefined index = append to end of the slot array\n index: isList ? undefined : 0,\n slot: path,\n parentId: elementId,\n bindings: value.params\n });\n\n const newElementId = newElement.element.id;\n\n ops.push(...newElement.operations);\n\n if (isList) {\n // For list slots, AddToParent already manages the static array.\n // We only set metadata here.\n ops.push(\n operations.setInputBinding(elementId, path, {\n id: generateElementId(),\n type: node.type,\n translatable: node.input.translatable,\n list: node.list\n })\n );\n } else {\n ops.push(\n operations.setInputBinding(elementId, path, {\n id: generateElementId(),\n static: newElementId,\n type: node.type,\n translatable: node.input.translatable,\n list: node.list\n })\n );\n }\n } else if (isObject && isList) {\n return;\n } else {\n ops.push(\n operations.setInputBinding(elementId, path, {\n id: generateElementId(),\n static: ignoreDefaultValues\n ? undefined\n : (value ?? node.input.defaultValue),\n type: node.type,\n list: node.list,\n translatable: node.input.translatable\n })\n );\n }\n });\n\n // Process styles\n for (const key in bindings.styles) {\n ops.push(\n operations.setStyleBinding(elementId, key, {\n static: bindings.styles[key]\n })\n );\n }\n\n return ops;\n }\n\n private getComponentManifest(componentName: string): ComponentManifest {\n const manifest = this.components[componentName];\n if (!manifest) {\n const registered = Object.keys(this.components).join(\", \");\n throw new Error(\n `Component \"${componentName}\" not registered. Registered components: ${registered}`\n );\n }\n\n return manifest;\n }\n\n private createElement(componentName: string, parentId: string, slot: string) {\n const element: DocumentElement = {\n type: \"Webiny/Element\",\n id: generateElementId(),\n parent: { id: parentId, slot },\n component: { name: componentName }\n };\n\n const componentManifest = this.getComponentManifest(componentName);\n const inputsAst = ComponentManifestToAstConverter.convert(componentManifest.inputs ?? []);\n\n return {\n element,\n inputsAst,\n componentManifest\n };\n }\n}\n"],"names":["defaultStyles","withDefaultStyles","styles","defaultOperations","element","DocumentOperations","index","elementId","bindingPath","binding","ElementFactory","components","componentName","parentId","slot","bindings","componentManifest","inputsAst","documentOps","operations","ops","breakpoint","overrides","Object","ignoreDefaultValues","inputData","traverser","ComponentInputTraverser","node","path","value","isCreateElement","isList","isObject","factory","newElement","undefined","newElementId","generateElementId","key","manifest","registered","Error","ComponentManifestToAstConverter"],"mappings":"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"}
1
+ {"version":3,"file":"ElementFactory.js","sources":["../src/ElementFactory.ts"],"sourcesContent":["import { generateElementId } from \"./generateElementId.js\";\nimport type {\n DocumentElement,\n ComponentManifest,\n InputValueBinding,\n StyleValueBinding,\n CssProperties\n} from \"~/types.js\";\nimport { type IDocumentOperation, DocumentOperations } from \"./documentOperations/index.js\";\nimport {\n ComponentManifestToAstConverter,\n type InputAstNode\n} from \"./ComponentManifestToAstConverter.js\";\nimport { ComponentInputTraverser } from \"./ComponentInputTraverser.js\";\n\nconst defaultStyles = {\n display: \"flex\",\n flexDirection: \"column\"\n};\n\nconst withDefaultStyles = (styles: CssProperties) => {\n return { ...defaultStyles, ...styles };\n};\n\nexport interface ElementFactoryCreateElementParams {\n componentName: string;\n parentId: string;\n slot: string;\n index?: number;\n bindings?: {\n inputs?: Record<string, any>;\n styles?: Record<string, any>;\n overrides?: {\n [breakpoint: string]: {\n inputs?: Record<string, any>;\n styles?: Record<string, any>;\n };\n };\n };\n}\n\ninterface GenerateOperationsParams {\n element: DocumentElement;\n inputsAst: InputAstNode[];\n operations: ElementFactoryOperations;\n bindings: {\n inputs: Record<string, any>;\n styles: Record<string, any>;\n overrides: {\n [breakpoint: string]: {\n inputs?: Record<string, any>;\n styles?: Record<string, any>;\n };\n };\n };\n}\n\ninterface GenerateOperationsFromBindingsParams {\n elementId: string;\n inputsAst: InputAstNode[];\n bindings: {\n inputs: Record<string, any>;\n styles: Record<string, any>;\n };\n operations: ElementFactoryOperations;\n ignoreDefaultValues: boolean;\n}\n\ntype ElementFactoryOperations = {\n addElement: (element: DocumentElement) => IDocumentOperation;\n addToParent: (element: DocumentElement, index?: number) => IDocumentOperation;\n setInputBinding: (\n elementId: string,\n bindingPath: string,\n binding: InputValueBinding\n ) => IDocumentOperation;\n setStyleBinding: (\n elementId: string,\n bindingPath: string,\n binding: StyleValueBinding\n ) => IDocumentOperation;\n};\n\nconst defaultOperations: ElementFactoryOperations = {\n addElement: (element: DocumentElement) => {\n return new DocumentOperations.AddElement(element);\n },\n addToParent: (element: DocumentElement, index?: number) => {\n return new DocumentOperations.AddToParent(element, index);\n },\n setInputBinding: (elementId, bindingPath, binding) => {\n return new DocumentOperations.SetGlobalInputBinding(elementId, bindingPath, binding);\n },\n setStyleBinding: (elementId, bindingPath, binding) => {\n return new DocumentOperations.SetGlobalStyleBinding(elementId, bindingPath, binding);\n }\n};\n\nexport class ElementFactory {\n constructor(private components: Record<string, ComponentManifest>) {}\n\n public createElementFromComponent({\n componentName,\n parentId,\n slot,\n index,\n bindings\n }: ElementFactoryCreateElementParams) {\n const { element, componentManifest, inputsAst } = this.createElement(\n componentName,\n parentId,\n slot\n );\n\n const documentOps: IDocumentOperation[] = [\n defaultOperations.addElement(element),\n defaultOperations.addToParent(element, index)\n ];\n\n const baseStyles = bindings?.styles ?? componentManifest.defaults?.styles ?? {};\n const resolvedStyles =\n componentManifest.applyDefaultStyles === false\n ? baseStyles\n : withDefaultStyles(baseStyles);\n\n documentOps.push(\n ...this.generateOperations({\n element,\n inputsAst,\n bindings: {\n inputs: bindings?.inputs ?? componentManifest.defaults?.inputs ?? {},\n styles: resolvedStyles,\n overrides: bindings?.overrides ?? {}\n },\n operations: defaultOperations\n })\n );\n\n return { element, operations: documentOps };\n }\n\n public generateOperations({\n element,\n inputsAst,\n bindings,\n operations\n }: GenerateOperationsParams): IDocumentOperation[] {\n const ops = this.generateOperationsFromBindings({\n elementId: element.id,\n inputsAst,\n bindings,\n operations,\n ignoreDefaultValues: false\n });\n\n if (bindings.overrides) {\n for (const [breakpoint, overrides] of Object.entries(bindings.overrides)) {\n ops.push(\n ...this.generateOperationsFromBindings({\n elementId: element.id,\n inputsAst,\n bindings: {\n inputs: overrides.inputs ?? {},\n styles: overrides.styles ?? {}\n },\n operations: {\n ...operations,\n setInputBinding: (elementId, bindingPath, binding) => {\n return new DocumentOperations.SetInputBindingOverride(\n elementId,\n bindingPath,\n binding,\n breakpoint\n );\n },\n setStyleBinding: (elementId, bindingPath, binding) => {\n return new DocumentOperations.SetStyleBindingOverride(\n elementId,\n bindingPath,\n binding,\n breakpoint\n );\n }\n },\n ignoreDefaultValues: true\n })\n );\n }\n }\n\n return ops;\n }\n\n private generateOperationsFromBindings({\n elementId,\n inputsAst,\n bindings,\n operations,\n ignoreDefaultValues\n }: GenerateOperationsFromBindingsParams): IDocumentOperation[] {\n const inputData = bindings.inputs;\n const traverser = new ComponentInputTraverser(inputsAst);\n\n const ops: IDocumentOperation[] = [];\n\n traverser.traverse(inputData, (node, path, value) => {\n const isCreateElement = value?.action === \"CreateElement\";\n const isList = node.list;\n const isObject = node.type === \"object\";\n\n if (isCreateElement) {\n const factory = new ElementFactory(this.components);\n const newElement = factory.createElementFromComponent({\n componentName: value.params.component,\n // undefined index = append to end of the slot array\n index: isList ? undefined : 0,\n slot: path,\n parentId: elementId,\n bindings: value.params\n });\n\n const newElementId = newElement.element.id;\n\n ops.push(...newElement.operations);\n\n if (isList) {\n // For list slots, AddToParent already manages the static array.\n // We only set metadata here.\n ops.push(\n operations.setInputBinding(elementId, path, {\n id: generateElementId(),\n type: node.type,\n translatable: node.input.translatable,\n list: node.list\n })\n );\n } else {\n ops.push(\n operations.setInputBinding(elementId, path, {\n id: generateElementId(),\n static: newElementId,\n type: node.type,\n translatable: node.input.translatable,\n list: node.list\n })\n );\n }\n } else if (isObject && isList) {\n return;\n } else {\n ops.push(\n operations.setInputBinding(elementId, path, {\n id: generateElementId(),\n static: ignoreDefaultValues\n ? undefined\n : (value ?? node.input.defaultValue),\n type: node.type,\n list: node.list,\n translatable: node.input.translatable\n })\n );\n }\n });\n\n // Process styles\n for (const key in bindings.styles) {\n ops.push(\n operations.setStyleBinding(elementId, key, {\n static: bindings.styles[key]\n })\n );\n }\n\n return ops;\n }\n\n private getComponentManifest(componentName: string): ComponentManifest {\n const manifest = this.components[componentName];\n if (!manifest) {\n const registered = Object.keys(this.components).join(\", \");\n throw new Error(\n `Component \"${componentName}\" not registered. Registered components: ${registered}`\n );\n }\n\n return manifest;\n }\n\n private createElement(componentName: string, parentId: string, slot: string) {\n const element: DocumentElement = {\n type: \"Webiny/Element\",\n id: generateElementId(),\n parent: { id: parentId, slot },\n component: { name: componentName }\n };\n\n const componentManifest = this.getComponentManifest(componentName);\n const inputsAst = ComponentManifestToAstConverter.convert(componentManifest.inputs ?? []);\n\n return {\n element,\n inputsAst,\n componentManifest\n };\n }\n}\n"],"names":["defaultStyles","withDefaultStyles","styles","defaultOperations","element","DocumentOperations","index","elementId","bindingPath","binding","ElementFactory","components","componentName","parentId","slot","bindings","componentManifest","inputsAst","documentOps","baseStyles","resolvedStyles","operations","ops","breakpoint","overrides","Object","ignoreDefaultValues","inputData","traverser","ComponentInputTraverser","node","path","value","isCreateElement","isList","isObject","factory","newElement","undefined","newElementId","generateElementId","key","manifest","registered","Error","ComponentManifestToAstConverter"],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Build asset **delivery URLs** that frame the image server-side. The delivery
3
+ * pipeline applies the crop (`?crop`), an optional target aspect ratio + focal point
4
+ * (`?aspectRatio`, `?focal`), resizing (`?width`), and format (`?format`) — so the
5
+ * returned URL's pixels are already framed. Use with any renderer that loads a URL:
6
+ * `next/image`, a plain `<img>`, Angular, React Native, Open Graph tags, email, etc.
7
+ * Each distinct framing yields its own cacheable URL.
8
+ */
9
+ import type { Asset } from "./types.js";
10
+ export interface AssetUrlOptions {
11
+ /** Delivery width (snapped to the configured width ladder server-side). */
12
+ width?: number;
13
+ /** Output format, e.g. `"auto"` (negotiated via Accept), `"webp"`, `"avif"`. */
14
+ format?: string;
15
+ /** Encoder quality (1–100). */
16
+ quality?: number;
17
+ /** Include the per-usage crop (`image.crop`) as a server-side crop. Default `true`. */
18
+ crop?: boolean;
19
+ /**
20
+ * Frame to a target aspect ratio server-side (`"16:9"` or a `w/h` number),
21
+ * centered on the asset's focal point. Omit to deliver at the crop's own ratio.
22
+ */
23
+ aspectRatio?: number | string;
24
+ /**
25
+ * Include the asset's focal point (`image.focalPoint`) when `aspectRatio` is set.
26
+ * Default `true`.
27
+ */
28
+ focal?: boolean;
29
+ }
30
+ /**
31
+ * The `crop=top,left,bottom,right` query value for an asset's per-usage crop, or
32
+ * `undefined` when there is no crop (or a full-image, no-op crop).
33
+ */
34
+ export declare const getAssetCropParam: (asset: Pick<Asset, "image"> | null | undefined) => string | undefined;
35
+ /**
36
+ * Build a delivery URL for an asset, baking the per-usage crop into the delivered
37
+ * image (plus optional width/format/quality). Returns `""` for a missing asset.
38
+ */
39
+ export declare const getAssetUrl: (asset: (Pick<Asset, "src" | "image"> & {
40
+ src?: string;
41
+ }) | null | undefined, options?: AssetUrlOptions) => string;
@@ -0,0 +1,34 @@
1
+ const round = (n)=>Math.round(10000 * n) / 10000;
2
+ const getAssetCropParam = (asset)=>{
3
+ const c = asset?.image?.crop;
4
+ if (!c || 0 === c.top && 0 === c.left && 0 === c.bottom && 0 === c.right) return;
5
+ return [
6
+ c.top,
7
+ c.left,
8
+ c.bottom,
9
+ c.right
10
+ ].map(round).join(",");
11
+ };
12
+ const getAssetUrl = (asset, options = {})=>{
13
+ const src = asset?.src;
14
+ if (!src) return "";
15
+ const params = [];
16
+ if (false !== options.crop) {
17
+ const crop = getAssetCropParam(asset);
18
+ if (crop) params.push(`crop=${crop}`);
19
+ }
20
+ if (void 0 !== options.aspectRatio) {
21
+ params.push(`aspectRatio=${options.aspectRatio}`);
22
+ const fp = asset?.image?.focalPoint;
23
+ if (false !== options.focal && fp && !(0.5 === fp.x && 0.5 === fp.y)) params.push(`focal=${round(fp.x)},${round(fp.y)}`);
24
+ }
25
+ if (options.width) params.push(`width=${options.width}`);
26
+ if (options.format) params.push(`format=${options.format}`);
27
+ if (options.quality) params.push(`quality=${options.quality}`);
28
+ if (0 === params.length) return src;
29
+ const separator = src.includes("?") ? "&" : "?";
30
+ return `${src}${separator}${params.join("&")}`;
31
+ };
32
+ export { getAssetCropParam, getAssetUrl };
33
+
34
+ //# sourceMappingURL=deliveryUrl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset/deliveryUrl.js","sources":["../../src/asset/deliveryUrl.ts"],"sourcesContent":["/**\n * Build asset **delivery URLs** that frame the image server-side. The delivery\n * pipeline applies the crop (`?crop`), an optional target aspect ratio + focal point\n * (`?aspectRatio`, `?focal`), resizing (`?width`), and format (`?format`) — so the\n * returned URL's pixels are already framed. Use with any renderer that loads a URL:\n * `next/image`, a plain `<img>`, Angular, React Native, Open Graph tags, email, etc.\n * Each distinct framing yields its own cacheable URL.\n */\nimport type { Asset } from \"./types.js\";\n\nexport interface AssetUrlOptions {\n /** Delivery width (snapped to the configured width ladder server-side). */\n width?: number;\n /** Output format, e.g. `\"auto\"` (negotiated via Accept), `\"webp\"`, `\"avif\"`. */\n format?: string;\n /** Encoder quality (1–100). */\n quality?: number;\n /** Include the per-usage crop (`image.crop`) as a server-side crop. Default `true`. */\n crop?: boolean;\n /**\n * Frame to a target aspect ratio server-side (`\"16:9\"` or a `w/h` number),\n * centered on the asset's focal point. Omit to deliver at the crop's own ratio.\n */\n aspectRatio?: number | string;\n /**\n * Include the asset's focal point (`image.focalPoint`) when `aspectRatio` is set.\n * Default `true`.\n */\n focal?: boolean;\n}\n\nconst round = (n: number): number => Math.round(n * 10000) / 10000;\n\n/**\n * The `crop=top,left,bottom,right` query value for an asset's per-usage crop, or\n * `undefined` when there is no crop (or a full-image, no-op crop).\n */\nexport const getAssetCropParam = (\n asset: Pick<Asset, \"image\"> | null | undefined\n): string | undefined => {\n const c = asset?.image?.crop;\n if (!c || (c.top === 0 && c.left === 0 && c.bottom === 0 && c.right === 0)) {\n return undefined;\n }\n return [c.top, c.left, c.bottom, c.right].map(round).join(\",\");\n};\n\n/**\n * Build a delivery URL for an asset, baking the per-usage crop into the delivered\n * image (plus optional width/format/quality). Returns `\"\"` for a missing asset.\n */\nexport const getAssetUrl = (\n asset: (Pick<Asset, \"src\" | \"image\"> & { src?: string }) | null | undefined,\n options: AssetUrlOptions = {}\n): string => {\n const src = asset?.src;\n if (!src) {\n return \"\";\n }\n\n const params: string[] = [];\n if (options.crop !== false) {\n const crop = getAssetCropParam(asset);\n if (crop) {\n params.push(`crop=${crop}`);\n }\n }\n if (options.aspectRatio !== undefined) {\n params.push(`aspectRatio=${options.aspectRatio}`);\n const fp = asset?.image?.focalPoint;\n if (options.focal !== false && fp && !(fp.x === 0.5 && fp.y === 0.5)) {\n params.push(`focal=${round(fp.x)},${round(fp.y)}`);\n }\n }\n if (options.width) {\n params.push(`width=${options.width}`);\n }\n if (options.format) {\n params.push(`format=${options.format}`);\n }\n if (options.quality) {\n params.push(`quality=${options.quality}`);\n }\n\n if (params.length === 0) {\n return src;\n }\n const separator = src.includes(\"?\") ? \"&\" : \"?\";\n return `${src}${separator}${params.join(\"&\")}`;\n};\n"],"names":["round","n","Math","getAssetCropParam","asset","c","getAssetUrl","options","src","params","crop","undefined","fp","separator"],"mappings":"AA+BA,MAAMA,QAAQ,CAACC,IAAsBC,KAAK,KAAK,CAACD,AAAI,QAAJA,KAAa;AAMtD,MAAME,oBAAoB,CAC7BC;IAEA,MAAMC,IAAID,OAAO,OAAO;IACxB,IAAI,CAACC,KAAMA,AAAU,MAAVA,EAAE,GAAG,IAAUA,AAAW,MAAXA,EAAE,IAAI,IAAUA,AAAa,MAAbA,EAAE,MAAM,IAAUA,AAAY,MAAZA,EAAE,KAAK,EAC/D;IAEJ,OAAO;QAACA,EAAE,GAAG;QAAEA,EAAE,IAAI;QAAEA,EAAE,MAAM;QAAEA,EAAE,KAAK;KAAC,CAAC,GAAG,CAACL,OAAO,IAAI,CAAC;AAC9D;AAMO,MAAMM,cAAc,CACvBF,OACAG,UAA2B,CAAC,CAAC;IAE7B,MAAMC,MAAMJ,OAAO;IACnB,IAAI,CAACI,KACD,OAAO;IAGX,MAAMC,SAAmB,EAAE;IAC3B,IAAIF,AAAiB,UAAjBA,QAAQ,IAAI,EAAY;QACxB,MAAMG,OAAOP,kBAAkBC;QAC/B,IAAIM,MACAD,OAAO,IAAI,CAAC,CAAC,KAAK,EAAEC,MAAM;IAElC;IACA,IAAIH,AAAwBI,WAAxBJ,QAAQ,WAAW,EAAgB;QACnCE,OAAO,IAAI,CAAC,CAAC,YAAY,EAAEF,QAAQ,WAAW,EAAE;QAChD,MAAMK,KAAKR,OAAO,OAAO;QACzB,IAAIG,AAAkB,UAAlBA,QAAQ,KAAK,IAAcK,MAAM,CAAEA,CAAAA,AAAS,QAATA,GAAG,CAAC,IAAYA,AAAS,QAATA,GAAG,CAAC,AAAO,GAC9DH,OAAO,IAAI,CAAC,CAAC,MAAM,EAAET,MAAMY,GAAG,CAAC,EAAE,CAAC,EAAEZ,MAAMY,GAAG,CAAC,GAAG;IAEzD;IACA,IAAIL,QAAQ,KAAK,EACbE,OAAO,IAAI,CAAC,CAAC,MAAM,EAAEF,QAAQ,KAAK,EAAE;IAExC,IAAIA,QAAQ,MAAM,EACdE,OAAO,IAAI,CAAC,CAAC,OAAO,EAAEF,QAAQ,MAAM,EAAE;IAE1C,IAAIA,QAAQ,OAAO,EACfE,OAAO,IAAI,CAAC,CAAC,QAAQ,EAAEF,QAAQ,OAAO,EAAE;IAG5C,IAAIE,AAAkB,MAAlBA,OAAO,MAAM,EACb,OAAOD;IAEX,MAAMK,YAAYL,IAAI,QAAQ,CAAC,OAAO,MAAM;IAC5C,OAAO,GAAGA,MAAMK,YAAYJ,OAAO,IAAI,CAAC,MAAM;AAClD"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,127 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { getAssetCropParam, getAssetUrl } from "./deliveryUrl.js";
3
+ const asset = (crop)=>({
4
+ id: "f",
5
+ src: "https://cdn/x/pic.jpg",
6
+ name: "pic.jpg",
7
+ type: "image/jpeg",
8
+ size: 1,
9
+ image: {
10
+ width: 800,
11
+ height: 600,
12
+ ...crop ? {
13
+ crop
14
+ } : {}
15
+ }
16
+ });
17
+ describe("getAssetCropParam", ()=>{
18
+ it("serializes a crop as top,left,bottom,right", ()=>{
19
+ expect(getAssetCropParam(asset({
20
+ top: 0.1,
21
+ left: 0.2,
22
+ bottom: 0.1,
23
+ right: 0.05
24
+ }))).toBe("0.1,0.2,0.1,0.05");
25
+ });
26
+ it("is undefined for no crop or a full crop", ()=>{
27
+ expect(getAssetCropParam(asset())).toBeUndefined();
28
+ expect(getAssetCropParam(asset({
29
+ top: 0,
30
+ left: 0,
31
+ bottom: 0,
32
+ right: 0
33
+ }))).toBeUndefined();
34
+ expect(getAssetCropParam(null)).toBeUndefined();
35
+ });
36
+ });
37
+ describe("getAssetUrl", ()=>{
38
+ it("bakes the crop + width + format into the URL", ()=>{
39
+ const url = getAssetUrl(asset({
40
+ top: 0.1,
41
+ left: 0.2,
42
+ bottom: 0.1,
43
+ right: 0.05
44
+ }), {
45
+ width: 1200,
46
+ format: "auto"
47
+ });
48
+ expect(url).toBe("https://cdn/x/pic.jpg?crop=0.1,0.2,0.1,0.05&width=1200&format=auto");
49
+ });
50
+ it("omits the crop when there is none", ()=>{
51
+ expect(getAssetUrl(asset(), {
52
+ width: 640
53
+ })).toBe("https://cdn/x/pic.jpg?width=640");
54
+ });
55
+ it("can opt out of the crop", ()=>{
56
+ expect(getAssetUrl(asset({
57
+ top: 0.1,
58
+ left: 0,
59
+ bottom: 0,
60
+ right: 0
61
+ }), {
62
+ crop: false
63
+ })).toBe("https://cdn/x/pic.jpg");
64
+ });
65
+ it("returns the bare src when no options apply", ()=>{
66
+ expect(getAssetUrl(asset())).toBe("https://cdn/x/pic.jpg");
67
+ });
68
+ it("uses & when the src already has a query string", ()=>{
69
+ const url = getAssetUrl({
70
+ src: "https://cdn/x/pic.jpg?v=2",
71
+ image: {}
72
+ }, {
73
+ width: 640
74
+ });
75
+ expect(url).toBe("https://cdn/x/pic.jpg?v=2&width=640");
76
+ });
77
+ it("returns empty string for a missing asset", ()=>{
78
+ expect(getAssetUrl(null)).toBe("");
79
+ expect(getAssetUrl(void 0)).toBe("");
80
+ });
81
+ it("emits aspectRatio + focal (from the asset) when framing to a ratio", ()=>{
82
+ const a = {
83
+ ...asset(),
84
+ image: {
85
+ width: 800,
86
+ height: 600,
87
+ focalPoint: {
88
+ x: 0.7,
89
+ y: 0.4
90
+ }
91
+ }
92
+ };
93
+ expect(getAssetUrl(a, {
94
+ aspectRatio: "16:9",
95
+ width: 1200
96
+ })).toBe("https://cdn/x/pic.jpg?aspectRatio=16:9&focal=0.7,0.4&width=1200");
97
+ });
98
+ it("omits focal when it is centered or opted out", ()=>{
99
+ const centered = {
100
+ ...asset(),
101
+ image: {
102
+ focalPoint: {
103
+ x: 0.5,
104
+ y: 0.5
105
+ }
106
+ }
107
+ };
108
+ expect(getAssetUrl(centered, {
109
+ aspectRatio: 1
110
+ })).toBe("https://cdn/x/pic.jpg?aspectRatio=1");
111
+ const offCenter = {
112
+ ...asset(),
113
+ image: {
114
+ focalPoint: {
115
+ x: 0.7,
116
+ y: 0.4
117
+ }
118
+ }
119
+ };
120
+ expect(getAssetUrl(offCenter, {
121
+ aspectRatio: 1,
122
+ focal: false
123
+ })).toBe("https://cdn/x/pic.jpg?aspectRatio=1");
124
+ });
125
+ });
126
+
127
+ //# sourceMappingURL=deliveryUrl.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset/deliveryUrl.test.js","sources":["../../src/asset/deliveryUrl.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { getAssetCropParam, getAssetUrl } from \"./deliveryUrl.js\";\n\nconst asset = (crop?: any) => ({\n id: \"f\",\n src: \"https://cdn/x/pic.jpg\",\n name: \"pic.jpg\",\n type: \"image/jpeg\",\n size: 1,\n image: { width: 800, height: 600, ...(crop ? { crop } : {}) }\n});\n\ndescribe(\"getAssetCropParam\", () => {\n it(\"serializes a crop as top,left,bottom,right\", () => {\n expect(getAssetCropParam(asset({ top: 0.1, left: 0.2, bottom: 0.1, right: 0.05 }))).toBe(\n \"0.1,0.2,0.1,0.05\"\n );\n });\n\n it(\"is undefined for no crop or a full crop\", () => {\n expect(getAssetCropParam(asset())).toBeUndefined();\n expect(getAssetCropParam(asset({ top: 0, left: 0, bottom: 0, right: 0 }))).toBeUndefined();\n expect(getAssetCropParam(null)).toBeUndefined();\n });\n});\n\ndescribe(\"getAssetUrl\", () => {\n it(\"bakes the crop + width + format into the URL\", () => {\n const url = getAssetUrl(asset({ top: 0.1, left: 0.2, bottom: 0.1, right: 0.05 }), {\n width: 1200,\n format: \"auto\"\n });\n expect(url).toBe(\"https://cdn/x/pic.jpg?crop=0.1,0.2,0.1,0.05&width=1200&format=auto\");\n });\n\n it(\"omits the crop when there is none\", () => {\n expect(getAssetUrl(asset(), { width: 640 })).toBe(\"https://cdn/x/pic.jpg?width=640\");\n });\n\n it(\"can opt out of the crop\", () => {\n expect(\n getAssetUrl(asset({ top: 0.1, left: 0, bottom: 0, right: 0 }), { crop: false })\n ).toBe(\"https://cdn/x/pic.jpg\");\n });\n\n it(\"returns the bare src when no options apply\", () => {\n expect(getAssetUrl(asset())).toBe(\"https://cdn/x/pic.jpg\");\n });\n\n it(\"uses & when the src already has a query string\", () => {\n const url = getAssetUrl(\n { src: \"https://cdn/x/pic.jpg?v=2\", image: {} },\n {\n width: 640\n }\n );\n expect(url).toBe(\"https://cdn/x/pic.jpg?v=2&width=640\");\n });\n\n it(\"returns empty string for a missing asset\", () => {\n expect(getAssetUrl(null)).toBe(\"\");\n expect(getAssetUrl(undefined)).toBe(\"\");\n });\n\n it(\"emits aspectRatio + focal (from the asset) when framing to a ratio\", () => {\n const a = {\n ...asset(),\n image: { width: 800, height: 600, focalPoint: { x: 0.7, y: 0.4 } }\n };\n expect(getAssetUrl(a, { aspectRatio: \"16:9\", width: 1200 })).toBe(\n \"https://cdn/x/pic.jpg?aspectRatio=16:9&focal=0.7,0.4&width=1200\"\n );\n });\n\n it(\"omits focal when it is centered or opted out\", () => {\n const centered = { ...asset(), image: { focalPoint: { x: 0.5, y: 0.5 } } };\n expect(getAssetUrl(centered, { aspectRatio: 1 })).toBe(\n \"https://cdn/x/pic.jpg?aspectRatio=1\"\n );\n const offCenter = { ...asset(), image: { focalPoint: { x: 0.7, y: 0.4 } } };\n expect(getAssetUrl(offCenter, { aspectRatio: 1, focal: false })).toBe(\n \"https://cdn/x/pic.jpg?aspectRatio=1\"\n );\n });\n});\n"],"names":["asset","crop","describe","it","expect","getAssetCropParam","url","getAssetUrl","undefined","a","centered","offCenter"],"mappings":";;AAGA,MAAMA,QAAQ,CAACC,OAAgB;QAC3B,IAAI;QACJ,KAAK;QACL,MAAM;QACN,MAAM;QACN,MAAM;QACN,OAAO;YAAE,OAAO;YAAK,QAAQ;YAAK,GAAIA,OAAO;gBAAEA;YAAK,IAAI,CAAC,CAAC;QAAE;IAChE;AAEAC,SAAS,qBAAqB;IAC1BC,GAAG,8CAA8C;QAC7CC,OAAOC,kBAAkBL,MAAM;YAAE,KAAK;YAAK,MAAM;YAAK,QAAQ;YAAK,OAAO;QAAK,KAAK,IAAI,CACpF;IAER;IAEAG,GAAG,2CAA2C;QAC1CC,OAAOC,kBAAkBL,UAAU,aAAa;QAChDI,OAAOC,kBAAkBL,MAAM;YAAE,KAAK;YAAG,MAAM;YAAG,QAAQ;YAAG,OAAO;QAAE,KAAK,aAAa;QACxFI,OAAOC,kBAAkB,OAAO,aAAa;IACjD;AACJ;AAEAH,SAAS,eAAe;IACpBC,GAAG,gDAAgD;QAC/C,MAAMG,MAAMC,YAAYP,MAAM;YAAE,KAAK;YAAK,MAAM;YAAK,QAAQ;YAAK,OAAO;QAAK,IAAI;YAC9E,OAAO;YACP,QAAQ;QACZ;QACAI,OAAOE,KAAK,IAAI,CAAC;IACrB;IAEAH,GAAG,qCAAqC;QACpCC,OAAOG,YAAYP,SAAS;YAAE,OAAO;QAAI,IAAI,IAAI,CAAC;IACtD;IAEAG,GAAG,2BAA2B;QAC1BC,OACIG,YAAYP,MAAM;YAAE,KAAK;YAAK,MAAM;YAAG,QAAQ;YAAG,OAAO;QAAE,IAAI;YAAE,MAAM;QAAM,IAC/E,IAAI,CAAC;IACX;IAEAG,GAAG,8CAA8C;QAC7CC,OAAOG,YAAYP,UAAU,IAAI,CAAC;IACtC;IAEAG,GAAG,kDAAkD;QACjD,MAAMG,MAAMC,YACR;YAAE,KAAK;YAA6B,OAAO,CAAC;QAAE,GAC9C;YACI,OAAO;QACX;QAEJH,OAAOE,KAAK,IAAI,CAAC;IACrB;IAEAH,GAAG,4CAA4C;QAC3CC,OAAOG,YAAY,OAAO,IAAI,CAAC;QAC/BH,OAAOG,YAAYC,SAAY,IAAI,CAAC;IACxC;IAEAL,GAAG,sEAAsE;QACrE,MAAMM,IAAI;YACN,GAAGT,OAAO;YACV,OAAO;gBAAE,OAAO;gBAAK,QAAQ;gBAAK,YAAY;oBAAE,GAAG;oBAAK,GAAG;gBAAI;YAAE;QACrE;QACAI,OAAOG,YAAYE,GAAG;YAAE,aAAa;YAAQ,OAAO;QAAK,IAAI,IAAI,CAC7D;IAER;IAEAN,GAAG,gDAAgD;QAC/C,MAAMO,WAAW;YAAE,GAAGV,OAAO;YAAE,OAAO;gBAAE,YAAY;oBAAE,GAAG;oBAAK,GAAG;gBAAI;YAAE;QAAE;QACzEI,OAAOG,YAAYG,UAAU;YAAE,aAAa;QAAE,IAAI,IAAI,CAClD;QAEJ,MAAMC,YAAY;YAAE,GAAGX,OAAO;YAAE,OAAO;gBAAE,YAAY;oBAAE,GAAG;oBAAK,GAAG;gBAAI;YAAE;QAAE;QAC1EI,OAAOG,YAAYI,WAAW;YAAE,aAAa;YAAG,OAAO;QAAM,IAAI,IAAI,CACjE;IAER;AACJ"}
@@ -0,0 +1,7 @@
1
+ import { type ImageRenderData, type NormalizedRect } from "../image/geometry.js";
2
+ import type { AspectRatioInput } from "../image/types.js";
3
+ import type { Asset } from "./types.js";
4
+ type AssetLike = Pick<Asset, "image">;
5
+ export declare function getAssetVisibleRect(asset: AssetLike, aspectRatio?: AspectRatioInput): NormalizedRect;
6
+ export declare function getAssetImageRenderData(asset: AssetLike, aspectRatio?: AspectRatioInput): ImageRenderData;
7
+ export {};
@@ -0,0 +1,10 @@
1
+ import { getImageRenderData, getVisibleRect } from "../image/geometry.js";
2
+ function getAssetVisibleRect(asset, aspectRatio) {
3
+ return getVisibleRect(asset.image ?? {}, aspectRatio);
4
+ }
5
+ function getAssetImageRenderData(asset, aspectRatio) {
6
+ return getImageRenderData(asset.image ?? {}, aspectRatio);
7
+ }
8
+ export { getAssetImageRenderData, getAssetVisibleRect };
9
+
10
+ //# sourceMappingURL=geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset/geometry.js","sources":["../../src/asset/geometry.ts"],"sourcesContent":["import {\n getImageRenderData,\n getVisibleRect,\n type ImageRenderData,\n type NormalizedRect\n} from \"../image/geometry.js\";\nimport type { AspectRatioInput } from \"../image/types.js\";\nimport type { Asset } from \"./types.js\";\n\ntype AssetLike = Pick<Asset, \"image\">;\n\nexport function getAssetVisibleRect(\n asset: AssetLike,\n aspectRatio?: AspectRatioInput\n): NormalizedRect {\n return getVisibleRect(asset.image ?? {}, aspectRatio);\n}\n\nexport function getAssetImageRenderData(\n asset: AssetLike,\n aspectRatio?: AspectRatioInput\n): ImageRenderData {\n return getImageRenderData(asset.image ?? {}, aspectRatio);\n}\n"],"names":["getAssetVisibleRect","asset","aspectRatio","getVisibleRect","getAssetImageRenderData","getImageRenderData"],"mappings":";AAWO,SAASA,oBACZC,KAAgB,EAChBC,WAA8B;IAE9B,OAAOC,eAAeF,MAAM,KAAK,IAAI,CAAC,GAAGC;AAC7C;AAEO,SAASE,wBACZH,KAAgB,EAChBC,WAA8B;IAE9B,OAAOG,mBAAmBJ,MAAM,KAAK,IAAI,CAAC,GAAGC;AACjD"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Framework-agnostic responsive-image helpers built on {@link getAssetUrl}.
3
+ *
4
+ * Every renderer (Next.js, React, Vue, Nuxt — and any future Angular / React Native
5
+ * / vanilla adapter) needs the same three things from a `Asset`: a width
6
+ * ladder, a `srcSet` with the per-usage crop/focal/format baked into each width, and
7
+ * the delivered image's intrinsic (cropped) dimensions for layout-shift-free
8
+ * rendering. Keeping that logic here — instead of re-deriving it per framework —
9
+ * makes each renderer a thin adapter over the one delivery-URL contract.
10
+ */
11
+ import type { Asset } from "./types.js";
12
+ import { type AssetUrlOptions } from "./deliveryUrl.js";
13
+ /**
14
+ * Canonical delivery-width ladder. Kept in lockstep with the server's resize ladder
15
+ * (`imageResizeWidths` in `@webiny/api-file-manager`) so every width a renderer
16
+ * requests snaps to a cached variant instead of forcing a fresh transform.
17
+ */
18
+ export declare const IMAGE_RESIZE_WIDTHS: readonly [128, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840];
19
+ export interface ImageDimensions {
20
+ /** Delivered (cropped) width in pixels, or `0` when the intrinsic size is unknown. */
21
+ width: number;
22
+ /** Delivered (cropped) height in pixels, or `0` when the intrinsic size is unknown. */
23
+ height: number;
24
+ }
25
+ /**
26
+ * The intrinsic dimensions of the *delivered* image — i.e. after the per-usage crop
27
+ * (and an optional target aspect ratio) are applied. Renderers pass these to their
28
+ * layout primitive (e.g. `next/image`'s `width`/`height`) so the box reserves the
29
+ * right space and the page doesn't shift as the image loads. Returns `0`/`0` when the
30
+ * asset's intrinsic size is unknown.
31
+ */
32
+ export declare const getImageDimensions: (asset: Pick<Asset, "image"> | null | undefined, options?: {
33
+ aspectRatio?: number | string;
34
+ }) => ImageDimensions;
35
+ export interface ImageSrcSetOptions extends Omit<AssetUrlOptions, "width"> {
36
+ /** Widths to emit. Defaults to {@link IMAGE_RESIZE_WIDTHS}, trimmed by `cssWidth`. */
37
+ widths?: readonly number[];
38
+ /**
39
+ * A fixed CSS width for the rendered image (`"320px"` or `320`). When set, the
40
+ * ladder is trimmed to the smallest set that still covers it (incl. high-DPR);
41
+ * fluid widths (`%`, `vw`, unset) use the full ladder.
42
+ */
43
+ cssWidth?: number | string;
44
+ }
45
+ export interface ImageSrcSet {
46
+ /** Single-URL fallback for clients that ignore `srcSet` (the largest emitted width). */
47
+ src: string;
48
+ /** `"<url> 128w, <url> 384w, …"` — crop/focal/format baked into every width. */
49
+ srcSet: string;
50
+ /** Delivered (cropped) intrinsic dimensions; `0` when unknown. */
51
+ width: number;
52
+ height: number;
53
+ }
54
+ /**
55
+ * Build the responsive descriptor for an asset: a `srcSet` across the width ladder
56
+ * (each entry a delivery URL with the per-usage crop/focal/format baked in), a
57
+ * single-URL `src` fallback, and the delivered dimensions. Framework-agnostic — hand
58
+ * `src`/`srcSet` to any `<img>`-like primitive. Returns empty strings for a missing
59
+ * asset.
60
+ */
61
+ export declare const getImageSrcSet: (asset: (Pick<Asset, "src" | "image"> & {
62
+ src?: string;
63
+ }) | null | undefined, options?: ImageSrcSetOptions) => ImageSrcSet;
@@ -0,0 +1,98 @@
1
+ import { getAssetUrl } from "./deliveryUrl.js";
2
+ const IMAGE_RESIZE_WIDTHS = [
3
+ 128,
4
+ 384,
5
+ 640,
6
+ 750,
7
+ 828,
8
+ 1080,
9
+ 1200,
10
+ 1920,
11
+ 2048,
12
+ 3840
13
+ ];
14
+ const parseAspectRatio = (input)=>{
15
+ if (void 0 === input) return;
16
+ if ("number" == typeof input) return input > 0 ? input : void 0;
17
+ const trimmed = input.trim();
18
+ if (trimmed.includes(":")) {
19
+ const [w, h] = trimmed.split(":").map(Number);
20
+ return w > 0 && h > 0 ? w / h : void 0;
21
+ }
22
+ const n = Number(trimmed);
23
+ return n > 0 ? n : void 0;
24
+ };
25
+ const toPx = (cssWidth)=>{
26
+ if (void 0 === cssWidth) return;
27
+ if ("number" == typeof cssWidth) return cssWidth > 0 ? cssWidth : void 0;
28
+ const trimmed = cssWidth.trim();
29
+ if (trimmed.endsWith("px")) {
30
+ const n = parseInt(trimmed, 10);
31
+ return n > 0 ? n : void 0;
32
+ }
33
+ };
34
+ const resolveWidths = (widths, cssWidth)=>{
35
+ const px = toPx(cssWidth);
36
+ if (!px) return [
37
+ ...widths
38
+ ];
39
+ const result = [];
40
+ for (const w of widths){
41
+ result.push(w);
42
+ if (w >= 2 * px) break;
43
+ }
44
+ return result.length ? result : [
45
+ widths[widths.length - 1]
46
+ ];
47
+ };
48
+ const getImageDimensions = (asset, options = {})=>{
49
+ const img = asset?.image;
50
+ const iw = img?.width ?? 0;
51
+ const ih = img?.height ?? 0;
52
+ if (!iw || !ih) return {
53
+ width: 0,
54
+ height: 0
55
+ };
56
+ const crop = img?.crop;
57
+ const width = crop ? Math.round((1 - (crop.left ?? 0) - (crop.right ?? 0)) * iw) : iw;
58
+ const height = crop ? Math.round((1 - (crop.top ?? 0) - (crop.bottom ?? 0)) * ih) : ih;
59
+ const ar = parseAspectRatio(options.aspectRatio);
60
+ if (ar) return {
61
+ width,
62
+ height: Math.round(width / ar)
63
+ };
64
+ return {
65
+ width,
66
+ height
67
+ };
68
+ };
69
+ const getImageSrcSet = (asset, options = {})=>{
70
+ const { widths = IMAGE_RESIZE_WIDTHS, cssWidth, ...urlOptions } = options;
71
+ const dims = getImageDimensions(asset, {
72
+ aspectRatio: urlOptions.aspectRatio
73
+ });
74
+ if (!asset?.src) return {
75
+ src: "",
76
+ srcSet: "",
77
+ width: dims.width,
78
+ height: dims.height
79
+ };
80
+ const selected = resolveWidths(widths, cssWidth);
81
+ const srcSet = selected.map((w)=>`${getAssetUrl(asset, {
82
+ ...urlOptions,
83
+ width: w
84
+ })} ${w}w`).join(", ");
85
+ const src = getAssetUrl(asset, {
86
+ ...urlOptions,
87
+ width: selected[selected.length - 1]
88
+ });
89
+ return {
90
+ src,
91
+ srcSet,
92
+ width: dims.width,
93
+ height: dims.height
94
+ };
95
+ };
96
+ export { IMAGE_RESIZE_WIDTHS, getImageDimensions, getImageSrcSet };
97
+
98
+ //# sourceMappingURL=imageSrcSet.js.map