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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/BindingsApi.js +29 -36
  2. package/BindingsApi.js.map +1 -1
  3. package/BindingsProcessor.js +34 -43
  4. package/BindingsProcessor.js.map +1 -1
  5. package/BindingsProcessor.test.js +82 -78
  6. package/BindingsProcessor.test.js.map +1 -1
  7. package/BindingsResolver.d.ts +4 -1
  8. package/BindingsResolver.js +99 -119
  9. package/BindingsResolver.js.map +1 -1
  10. package/BindingsResolver.test.js +399 -363
  11. package/BindingsResolver.test.js.map +1 -1
  12. package/ComponentInputTraverser.js +32 -49
  13. package/ComponentInputTraverser.js.map +1 -1
  14. package/ComponentManifestToAstConverter.js +20 -21
  15. package/ComponentManifestToAstConverter.js.map +1 -1
  16. package/ComponentRegistry.d.ts +9 -0
  17. package/ComponentRegistry.js +30 -44
  18. package/ComponentRegistry.js.map +1 -1
  19. package/ComponentResolver.d.ts +3 -2
  20. package/ComponentResolver.js +39 -29
  21. package/ComponentResolver.js.map +1 -1
  22. package/ComponentResolver.test.d.ts +1 -0
  23. package/ComponentResolver.test.js +81 -0
  24. package/ComponentResolver.test.js.map +1 -0
  25. package/ConstraintEvaluator.js +246 -353
  26. package/ConstraintEvaluator.js.map +1 -1
  27. package/ConstraintEvaluator.test.js +1616 -1438
  28. package/ConstraintEvaluator.test.js.map +1 -1
  29. package/ContentSdk.d.ts +14 -0
  30. package/ContentSdk.js +113 -90
  31. package/ContentSdk.js.map +1 -1
  32. package/DocumentStore.js +47 -59
  33. package/DocumentStore.js.map +1 -1
  34. package/DocumentStoreManager.js +17 -16
  35. package/DocumentStoreManager.js.map +1 -1
  36. package/EditingSdk.js +92 -121
  37. package/EditingSdk.js.map +1 -1
  38. package/ElementFactory.js +164 -172
  39. package/ElementFactory.js.map +1 -1
  40. package/ElementFactory.objectListDefaults.test.d.ts +1 -0
  41. package/ElementFactory.objectListDefaults.test.js +423 -0
  42. package/ElementFactory.objectListDefaults.test.js.map +1 -0
  43. package/ElementFactory.test.js +335 -262
  44. package/ElementFactory.test.js.map +1 -1
  45. package/Environment.js +18 -19
  46. package/Environment.js.map +1 -1
  47. package/FunctionConverter.js +8 -7
  48. package/FunctionConverter.js.map +1 -1
  49. package/HashObject.js +11 -26
  50. package/HashObject.js.map +1 -1
  51. package/HotkeyManager.js +40 -47
  52. package/HotkeyManager.js.map +1 -1
  53. package/IBindingsUpdater.js +0 -3
  54. package/IRedirects.js +0 -3
  55. package/InheritanceProcessor.js +99 -139
  56. package/InheritanceProcessor.js.map +1 -1
  57. package/InheritanceProcessor.test.js +178 -179
  58. package/InheritanceProcessor.test.js.map +1 -1
  59. package/InheritedValueResolver.d.ts +18 -1
  60. package/InheritedValueResolver.js +18 -20
  61. package/InheritedValueResolver.js.map +1 -1
  62. package/InputBindingsProcessor.d.ts +23 -2
  63. package/InputBindingsProcessor.js +200 -307
  64. package/InputBindingsProcessor.js.map +1 -1
  65. package/InputBindingsProcessorTokens.test.d.ts +1 -0
  66. package/InputBindingsProcessorTokens.test.js +400 -0
  67. package/InputBindingsProcessorTokens.test.js.map +1 -0
  68. package/InputsBindingsProcessor.test.js +334 -315
  69. package/InputsBindingsProcessor.test.js.map +1 -1
  70. package/InputsUpdater.js +23 -26
  71. package/InputsUpdater.js.map +1 -1
  72. package/LiveSdk.js +12 -13
  73. package/LiveSdk.js.map +1 -1
  74. package/Logger.js +9 -8
  75. package/Logger.js.map +1 -1
  76. package/MouseTracker.js +77 -83
  77. package/MouseTracker.js.map +1 -1
  78. package/NullSdk.js +22 -21
  79. package/NullSdk.js.map +1 -1
  80. package/PreviewDocument.d.ts +1 -0
  81. package/PreviewDocument.js +30 -30
  82. package/PreviewDocument.js.map +1 -1
  83. package/PreviewSdk.d.ts +2 -1
  84. package/PreviewSdk.js +19 -17
  85. package/PreviewSdk.js.map +1 -1
  86. package/PreviewViewport.js +51 -63
  87. package/PreviewViewport.js.map +1 -1
  88. package/ResizeObserver.js +24 -31
  89. package/ResizeObserver.js.map +1 -1
  90. package/StylesBindingsProcessor.d.ts +12 -2
  91. package/StylesBindingsProcessor.js +62 -79
  92. package/StylesBindingsProcessor.js.map +1 -1
  93. package/StylesUpdater.js +20 -25
  94. package/StylesUpdater.js.map +1 -1
  95. package/Theme.js +30 -25
  96. package/Theme.js.map +1 -1
  97. package/ViewportManager.js +89 -101
  98. package/ViewportManager.js.map +1 -1
  99. package/assertBindingShapes.d.ts +31 -0
  100. package/assertBindingShapes.js +64 -0
  101. package/assertBindingShapes.js.map +1 -0
  102. package/assertBindingShapes.test.d.ts +1 -0
  103. package/assertBindingShapes.test.js +131 -0
  104. package/assertBindingShapes.test.js.map +1 -0
  105. package/asset/deliveryUrl.d.ts +41 -0
  106. package/asset/deliveryUrl.js +34 -0
  107. package/asset/deliveryUrl.js.map +1 -0
  108. package/asset/deliveryUrl.test.d.ts +1 -0
  109. package/asset/deliveryUrl.test.js +127 -0
  110. package/asset/deliveryUrl.test.js.map +1 -0
  111. package/asset/geometry.d.ts +7 -0
  112. package/asset/geometry.js +10 -0
  113. package/asset/geometry.js.map +1 -0
  114. package/asset/imageSrcSet.d.ts +63 -0
  115. package/asset/imageSrcSet.js +98 -0
  116. package/asset/imageSrcSet.js.map +1 -0
  117. package/asset/imageSrcSet.test.d.ts +1 -0
  118. package/asset/imageSrcSet.test.js +103 -0
  119. package/asset/imageSrcSet.test.js.map +1 -0
  120. package/asset/index.d.ts +5 -0
  121. package/asset/index.js +5 -0
  122. package/asset/normalize.d.ts +24 -0
  123. package/asset/normalize.js +106 -0
  124. package/asset/normalize.js.map +1 -0
  125. package/asset/normalize.test.d.ts +1 -0
  126. package/asset/normalize.test.js +263 -0
  127. package/asset/normalize.test.js.map +1 -0
  128. package/asset/types.d.ts +2 -0
  129. package/asset/types.js +0 -0
  130. package/constants.js +7 -6
  131. package/constants.js.map +1 -1
  132. package/contentEntry/ContentEntryBindingsResolver.d.ts +30 -0
  133. package/contentEntry/ContentEntryBindingsResolver.js +45 -0
  134. package/contentEntry/ContentEntryBindingsResolver.js.map +1 -0
  135. package/contentEntry/ContentEntryCache.d.ts +46 -0
  136. package/contentEntry/ContentEntryCache.js +50 -0
  137. package/contentEntry/ContentEntryCache.js.map +1 -0
  138. package/contentEntry/resolveContentEntry.d.ts +74 -0
  139. package/contentEntry/resolveContentEntry.js +53 -0
  140. package/contentEntry/resolveContentEntry.js.map +1 -0
  141. package/contentEntry/resolveContentEntry.test.d.ts +1 -0
  142. package/contentEntry/resolveContentEntry.test.js +213 -0
  143. package/contentEntry/resolveContentEntry.test.js.map +1 -0
  144. package/createComponent.d.ts +5 -0
  145. package/createComponent.js +32 -0
  146. package/createComponent.js.map +1 -0
  147. package/createElement.js +5 -6
  148. package/createElement.js.map +1 -1
  149. package/createInput.d.ts +10 -2
  150. package/createInput.js +92 -143
  151. package/createInput.js.map +1 -1
  152. package/createTheme.js +2 -3
  153. package/createTheme.js.map +1 -1
  154. package/dataProviders/ApiClient.d.ts +10 -5
  155. package/dataProviders/ApiClient.js +40 -49
  156. package/dataProviders/ApiClient.js.map +1 -1
  157. package/dataProviders/DefaultDataProvider.d.ts +4 -0
  158. package/dataProviders/DefaultDataProvider.js +91 -58
  159. package/dataProviders/DefaultDataProvider.js.map +1 -1
  160. package/dataProviders/GET_EXPERIMENT_PAUSED.d.ts +1 -0
  161. package/dataProviders/GET_EXPERIMENT_PAUSED.js +17 -0
  162. package/dataProviders/GET_EXPERIMENT_PAUSED.js.map +1 -0
  163. package/dataProviders/GET_PAGE_BY_ID.js +2 -1
  164. package/dataProviders/GET_PAGE_BY_ID.js.map +1 -1
  165. package/dataProviders/GET_PAGE_BY_PATH.js +2 -1
  166. package/dataProviders/GET_PAGE_BY_PATH.js.map +1 -1
  167. package/dataProviders/GET_PAGE_EXPERIMENT.d.ts +1 -0
  168. package/dataProviders/GET_PAGE_EXPERIMENT.js +32 -0
  169. package/dataProviders/GET_PAGE_EXPERIMENT.js.map +1 -0
  170. package/dataProviders/GET_VARIANT_CONTENT.d.ts +1 -0
  171. package/dataProviders/GET_VARIANT_CONTENT.js +24 -0
  172. package/dataProviders/GET_VARIANT_CONTENT.js.map +1 -0
  173. package/dataProviders/LIST_PUBLISHED_PAGES.js +2 -1
  174. package/dataProviders/LIST_PUBLISHED_PAGES.js.map +1 -1
  175. package/dataProviders/NullDataProvider.d.ts +4 -0
  176. package/dataProviders/NullDataProvider.js +30 -20
  177. package/dataProviders/NullDataProvider.js.map +1 -1
  178. package/dataProviders/RedirectsProvider.js +24 -27
  179. package/dataProviders/RedirectsProvider.js.map +1 -1
  180. package/defaultBreakpoints.js +43 -22
  181. package/defaultBreakpoints.js.map +1 -1
  182. package/documentOperations/$addElementReferenceToParent.js +29 -32
  183. package/documentOperations/$addElementReferenceToParent.js.map +1 -1
  184. package/documentOperations/AddElement.js +8 -7
  185. package/documentOperations/AddElement.js.map +1 -1
  186. package/documentOperations/AddToParent.js +14 -13
  187. package/documentOperations/AddToParent.js.map +1 -1
  188. package/documentOperations/IDocumentOperation.js +0 -3
  189. package/documentOperations/RemoveElement.js +9 -15
  190. package/documentOperations/RemoveElement.js.map +1 -1
  191. package/documentOperations/SetGlobalInputBinding.js +23 -22
  192. package/documentOperations/SetGlobalInputBinding.js.map +1 -1
  193. package/documentOperations/SetGlobalStyleBinding.js +23 -23
  194. package/documentOperations/SetGlobalStyleBinding.js.map +1 -1
  195. package/documentOperations/SetInputBindingOverride.js +30 -29
  196. package/documentOperations/SetInputBindingOverride.js.map +1 -1
  197. package/documentOperations/SetStyleBindingOverride.js +30 -31
  198. package/documentOperations/SetStyleBindingOverride.js.map +1 -1
  199. package/documentOperations/index.js +9 -8
  200. package/documentOperations/index.js.map +1 -1
  201. package/experiments/PostHogAnalyticsProvider.d.ts +31 -0
  202. package/experiments/PostHogAnalyticsProvider.js +28 -0
  203. package/experiments/PostHogAnalyticsProvider.js.map +1 -0
  204. package/experiments/analyticsProvider.d.ts +4 -0
  205. package/experiments/analyticsProvider.js +11 -0
  206. package/experiments/analyticsProvider.js.map +1 -0
  207. package/experiments/bucketing.d.ts +27 -0
  208. package/experiments/bucketing.js +91 -0
  209. package/experiments/bucketing.js.map +1 -0
  210. package/experiments/bucketing.test.d.ts +1 -0
  211. package/experiments/bucketing.test.js +183 -0
  212. package/experiments/bucketing.test.js.map +1 -0
  213. package/experiments/index.d.ts +5 -0
  214. package/experiments/index.js +5 -0
  215. package/experiments/render.d.ts +79 -0
  216. package/experiments/render.js +149 -0
  217. package/experiments/render.js.map +1 -0
  218. package/experiments/types.d.ts +108 -0
  219. package/experiments/types.js +4 -0
  220. package/experiments/types.js.map +1 -0
  221. package/findMatchingAstNode.js +11 -13
  222. package/findMatchingAstNode.js.map +1 -1
  223. package/generateElementId.js +2 -1
  224. package/generateElementId.js.map +1 -1
  225. package/headersProvider.js +4 -3
  226. package/headersProvider.js.map +1 -1
  227. package/image/geometry.d.ts +54 -0
  228. package/image/geometry.js +112 -0
  229. package/image/geometry.js.map +1 -0
  230. package/image/geometry.test.d.ts +1 -0
  231. package/image/geometry.test.js +190 -0
  232. package/image/geometry.test.js.map +1 -0
  233. package/image/index.d.ts +3 -0
  234. package/image/index.js +3 -0
  235. package/image/presets.d.ts +6 -0
  236. package/image/presets.js +20 -0
  237. package/image/presets.js.map +1 -0
  238. package/image/types.d.ts +10 -0
  239. package/image/types.js +0 -0
  240. package/index.d.ts +10 -0
  241. package/index.js +10 -2
  242. package/jsonPatch.js +5 -9
  243. package/jsonPatch.js.map +1 -1
  244. package/messages.js +12 -11
  245. package/messages.js.map +1 -1
  246. package/messenger/MessageOrigin.js +12 -11
  247. package/messenger/MessageOrigin.js.map +1 -1
  248. package/messenger/Messenger.js +58 -69
  249. package/messenger/Messenger.js.map +1 -1
  250. package/messenger/index.js +0 -2
  251. package/package.json +12 -9
  252. package/registerComponentGroup.js +5 -6
  253. package/registerComponentGroup.js.map +1 -1
  254. package/tokenBinding.d.ts +26 -0
  255. package/tokenBinding.js +15 -0
  256. package/tokenBinding.js.map +1 -0
  257. package/tokenBinding.test.d.ts +1 -0
  258. package/tokenBinding.test.js +165 -0
  259. package/tokenBinding.test.js.map +1 -0
  260. package/types/ShorthandCssProperties.js +0 -3
  261. package/types/WebsiteBuilderTheme.d.ts +12 -0
  262. package/types/WebsiteBuilderTheme.js +0 -3
  263. package/types.d.ts +96 -1
  264. package/types.js +0 -3
  265. package/IBindingsUpdater.js.map +0 -1
  266. package/IRedirects.js.map +0 -1
  267. package/documentOperations/IDocumentOperation.js.map +0 -1
  268. package/index.js.map +0 -1
  269. package/messenger/index.js.map +0 -1
  270. package/types/ShorthandCssProperties.js.map +0 -1
  271. package/types/WebsiteBuilderTheme.js.map +0 -1
  272. package/types.js.map +0 -1
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The reserved variant id for the control bucket. The control bucket renders the baseline
3
+ * revision content; it has no Variant object of its own.
4
+ */
5
+ export declare const CONTROL_VARIANT_ID = "control";
6
+ export type DeviceType = "desktop" | "mobile" | "tablet";
7
+ export interface ExperimentTrafficSplit {
8
+ control: number;
9
+ variants: Record<string, number>;
10
+ }
11
+ export interface ExperimentTargeting {
12
+ trafficPercentage: number;
13
+ geo?: string[];
14
+ device?: DeviceType[];
15
+ }
16
+ /**
17
+ * Provider-agnostic analytics configuration carried by an experiment. Adapters read this and
18
+ * map it to their own shape. No provider-specific field name appears here.
19
+ */
20
+ export interface ExperimentAnalyticsConfig {
21
+ provider: string;
22
+ [key: string]: unknown;
23
+ }
24
+ export interface ActiveExperimentVariant {
25
+ variantId: string;
26
+ name: string;
27
+ }
28
+ /**
29
+ * A variant's full content snapshot, shaped for rendering. Overlaid onto the baseline page so
30
+ * the result is a complete, renderable page.
31
+ */
32
+ export interface VariantContent {
33
+ id: string;
34
+ properties: Record<string, any>;
35
+ bindings: Record<string, any>;
36
+ elements: Record<string, any>;
37
+ extensions: Record<string, any>;
38
+ metadata: Record<string, any>;
39
+ }
40
+ /**
41
+ * The active experiment for a path as returned by the Website Builder API. This is the only
42
+ * experiment shape the render path deals with — it is provider-agnostic.
43
+ */
44
+ export interface ActiveExperiment {
45
+ experimentId: string;
46
+ revisionId: string;
47
+ pageEntryId: string;
48
+ path: string;
49
+ status: string;
50
+ tenantId: string;
51
+ controlVariantId: string;
52
+ trafficSplit: ExperimentTrafficSplit;
53
+ targeting: ExperimentTargeting;
54
+ analytics: ExperimentAnalyticsConfig;
55
+ variants: ActiveExperimentVariant[];
56
+ }
57
+ /**
58
+ * Request-time context used to bucket a visitor. Derived server-side from request headers /
59
+ * cookies — never from a logged-in identity.
60
+ */
61
+ export interface VisitorContext {
62
+ /** A stable visitor id (from the bucketing cookie, or a generated id). */
63
+ visitorId: string;
64
+ /** ISO country code, if the CDN/edge provided one. */
65
+ country?: string;
66
+ /** Device type, derived from the user agent. */
67
+ device?: DeviceType;
68
+ }
69
+ export interface VariantAssignment {
70
+ /** The assigned variant id, or the control variant id. */
71
+ variantId: string;
72
+ /** True when the control bucket (baseline revision) was assigned. */
73
+ isControl: boolean;
74
+ /**
75
+ * True when the visitor was not entered into the experiment (targeting or traffic
76
+ * percentage). The control is served and no exposure is emitted.
77
+ */
78
+ excluded: boolean;
79
+ /** True when a forced-variant query parameter overrode bucketing (QA). Never reported. */
80
+ forced: boolean;
81
+ }
82
+ /**
83
+ * The single canonical exposure event. Each analytics provider adapter maps from this into its
84
+ * own event shape. No PostHog (or other provider) field names may appear here.
85
+ */
86
+ export interface ExposureEvent {
87
+ experimentId: string;
88
+ /** Optional human-readable experiment key for provider mapping (from analytics config). */
89
+ experimentKey?: string;
90
+ /** The assigned variant id (the control variant id for the control bucket). */
91
+ variantId: string;
92
+ revisionId: string;
93
+ tenantId: string;
94
+ /** Stable distinct visitor id. */
95
+ visitorId: string;
96
+ pageId: string;
97
+ path: string;
98
+ timestamp: string;
99
+ }
100
+ /**
101
+ * The provider seam. A v2 GA4 adapter is a drop-in implementation of this interface — the data
102
+ * model, assignment logic, and render path never change.
103
+ */
104
+ export interface AnalyticsProvider {
105
+ /** Provider id, matched against an experiment's analytics.provider. */
106
+ readonly name: string;
107
+ emitExposure(event: ExposureEvent): Promise<void>;
108
+ }
@@ -0,0 +1,4 @@
1
+ const CONTROL_VARIANT_ID = "control";
2
+ export { CONTROL_VARIANT_ID };
3
+
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"experiments/types.js","sources":["../../src/experiments/types.ts"],"sourcesContent":["/**\n * The reserved variant id for the control bucket. The control bucket renders the baseline\n * revision content; it has no Variant object of its own.\n */\nexport const CONTROL_VARIANT_ID = \"control\";\n\nexport type DeviceType = \"desktop\" | \"mobile\" | \"tablet\";\n\nexport interface ExperimentTrafficSplit {\n control: number;\n variants: Record<string, number>;\n}\n\nexport interface ExperimentTargeting {\n trafficPercentage: number;\n geo?: string[];\n device?: DeviceType[];\n}\n\n/**\n * Provider-agnostic analytics configuration carried by an experiment. Adapters read this and\n * map it to their own shape. No provider-specific field name appears here.\n */\nexport interface ExperimentAnalyticsConfig {\n provider: string;\n [key: string]: unknown;\n}\n\nexport interface ActiveExperimentVariant {\n variantId: string;\n name: string;\n}\n\n/**\n * A variant's full content snapshot, shaped for rendering. Overlaid onto the baseline page so\n * the result is a complete, renderable page.\n */\nexport interface VariantContent {\n id: string;\n properties: Record<string, any>;\n bindings: Record<string, any>;\n elements: Record<string, any>;\n extensions: Record<string, any>;\n metadata: Record<string, any>;\n}\n\n/**\n * The active experiment for a path as returned by the Website Builder API. This is the only\n * experiment shape the render path deals with — it is provider-agnostic.\n */\nexport interface ActiveExperiment {\n experimentId: string;\n revisionId: string;\n pageEntryId: string;\n path: string;\n status: string;\n tenantId: string;\n controlVariantId: string;\n trafficSplit: ExperimentTrafficSplit;\n targeting: ExperimentTargeting;\n analytics: ExperimentAnalyticsConfig;\n variants: ActiveExperimentVariant[];\n}\n\n/**\n * Request-time context used to bucket a visitor. Derived server-side from request headers /\n * cookies — never from a logged-in identity.\n */\nexport interface VisitorContext {\n /** A stable visitor id (from the bucketing cookie, or a generated id). */\n visitorId: string;\n /** ISO country code, if the CDN/edge provided one. */\n country?: string;\n /** Device type, derived from the user agent. */\n device?: DeviceType;\n}\n\nexport interface VariantAssignment {\n /** The assigned variant id, or the control variant id. */\n variantId: string;\n /** True when the control bucket (baseline revision) was assigned. */\n isControl: boolean;\n /**\n * True when the visitor was not entered into the experiment (targeting or traffic\n * percentage). The control is served and no exposure is emitted.\n */\n excluded: boolean;\n /** True when a forced-variant query parameter overrode bucketing (QA). Never reported. */\n forced: boolean;\n}\n\n/**\n * The single canonical exposure event. Each analytics provider adapter maps from this into its\n * own event shape. No PostHog (or other provider) field names may appear here.\n */\nexport interface ExposureEvent {\n experimentId: string;\n /** Optional human-readable experiment key for provider mapping (from analytics config). */\n experimentKey?: string;\n /** The assigned variant id (the control variant id for the control bucket). */\n variantId: string;\n revisionId: string;\n tenantId: string;\n /** Stable distinct visitor id. */\n visitorId: string;\n pageId: string;\n path: string;\n timestamp: string;\n}\n\n/**\n * The provider seam. A v2 GA4 adapter is a drop-in implementation of this interface — the data\n * model, assignment logic, and render path never change.\n */\nexport interface AnalyticsProvider {\n /** Provider id, matched against an experiment's analytics.provider. */\n readonly name: string;\n emitExposure(event: ExposureEvent): Promise<void>;\n}\n"],"names":["CONTROL_VARIANT_ID"],"mappings":"AAIO,MAAMA,qBAAqB"}
@@ -1,17 +1,15 @@
1
- export function findMatchingAstNode(path, ast) {
2
- // Remove all array indices, e.g. columns[0].children → columns.children
3
- const cleanedPath = path.replace(/\[\d+\]/g, "");
4
- const pathParts = cleanedPath.split(".");
5
- let nodes = ast;
6
- let currentNode;
7
- for (const part of pathParts) {
8
- currentNode = nodes.find(n => n.name === part);
9
- if (!currentNode) {
10
- return undefined;
1
+ function findMatchingAstNode(path, ast) {
2
+ const cleanedPath = path.replace(/\[\d+\]/g, "");
3
+ const pathParts = cleanedPath.split(".");
4
+ let nodes = ast;
5
+ let currentNode;
6
+ for (const part of pathParts){
7
+ currentNode = nodes.find((n)=>n.name === part);
8
+ if (!currentNode) return;
9
+ nodes = currentNode.children;
11
10
  }
12
- nodes = currentNode.children;
13
- }
14
- return currentNode;
11
+ return currentNode;
15
12
  }
13
+ export { findMatchingAstNode };
16
14
 
17
15
  //# sourceMappingURL=findMatchingAstNode.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["findMatchingAstNode","path","ast","cleanedPath","replace","pathParts","split","nodes","currentNode","part","find","n","name","undefined","children"],"sources":["findMatchingAstNode.ts"],"sourcesContent":["import type { InputAstNode } from \"~/ComponentManifestToAstConverter.js\";\n\nexport function findMatchingAstNode(path: string, ast: InputAstNode[]): InputAstNode | undefined {\n // Remove all array indices, e.g. columns[0].children → columns.children\n const cleanedPath = path.replace(/\\[\\d+\\]/g, \"\");\n const pathParts = cleanedPath.split(\".\");\n\n let nodes = ast;\n let currentNode: InputAstNode | undefined;\n\n for (const part of pathParts) {\n currentNode = nodes.find(n => n.name === part);\n if (!currentNode) {\n return undefined;\n }\n\n nodes = currentNode.children;\n }\n\n return currentNode;\n}\n"],"mappings":"AAEA,OAAO,SAASA,mBAAmBA,CAACC,IAAY,EAAEC,GAAmB,EAA4B;EAC7F;EACA,MAAMC,WAAW,GAAGF,IAAI,CAACG,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;EAChD,MAAMC,SAAS,GAAGF,WAAW,CAACG,KAAK,CAAC,GAAG,CAAC;EAExC,IAAIC,KAAK,GAAGL,GAAG;EACf,IAAIM,WAAqC;EAEzC,KAAK,MAAMC,IAAI,IAAIJ,SAAS,EAAE;IAC1BG,WAAW,GAAGD,KAAK,CAACG,IAAI,CAACC,CAAC,IAAIA,CAAC,CAACC,IAAI,KAAKH,IAAI,CAAC;IAC9C,IAAI,CAACD,WAAW,EAAE;MACd,OAAOK,SAAS;IACpB;IAEAN,KAAK,GAAGC,WAAW,CAACM,QAAQ;EAChC;EAEA,OAAON,WAAW;AACtB","ignoreList":[]}
1
+ {"version":3,"file":"findMatchingAstNode.js","sources":["../src/findMatchingAstNode.ts"],"sourcesContent":["import type { InputAstNode } from \"~/ComponentManifestToAstConverter.js\";\n\nexport function findMatchingAstNode(path: string, ast: InputAstNode[]): InputAstNode | undefined {\n // Remove all array indices, e.g. columns[0].children → columns.children\n const cleanedPath = path.replace(/\\[\\d+\\]/g, \"\");\n const pathParts = cleanedPath.split(\".\");\n\n let nodes = ast;\n let currentNode: InputAstNode | undefined;\n\n for (const part of pathParts) {\n currentNode = nodes.find(n => n.name === part);\n if (!currentNode) {\n return undefined;\n }\n\n nodes = currentNode.children;\n }\n\n return currentNode;\n}\n"],"names":["findMatchingAstNode","path","ast","cleanedPath","pathParts","nodes","currentNode","part","n"],"mappings":"AAEO,SAASA,oBAAoBC,IAAY,EAAEC,GAAmB;IAEjE,MAAMC,cAAcF,KAAK,OAAO,CAAC,YAAY;IAC7C,MAAMG,YAAYD,YAAY,KAAK,CAAC;IAEpC,IAAIE,QAAQH;IACZ,IAAII;IAEJ,KAAK,MAAMC,QAAQH,UAAW;QAC1BE,cAAcD,MAAM,IAAI,CAACG,CAAAA,IAAKA,EAAE,IAAI,KAAKD;QACzC,IAAI,CAACD,aACD;QAGJD,QAAQC,YAAY,QAAQ;IAChC;IAEA,OAAOA;AACX"}
@@ -1,6 +1,7 @@
1
1
  import { customAlphabet } from "nanoid";
2
2
  import { lowercase, numbers } from "nanoid-dictionary";
3
3
  const DEFAULT_SIZE = 21;
4
- export const generateElementId = customAlphabet(`${lowercase}${numbers}`, DEFAULT_SIZE);
4
+ const generateElementId = customAlphabet(`${lowercase}${numbers}`, DEFAULT_SIZE);
5
+ export { generateElementId };
5
6
 
6
7
  //# sourceMappingURL=generateElementId.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["customAlphabet","lowercase","numbers","DEFAULT_SIZE","generateElementId"],"sources":["generateElementId.ts"],"sourcesContent":["import { customAlphabet } from \"nanoid\";\nimport { lowercase, numbers } from \"nanoid-dictionary\";\n\nconst DEFAULT_SIZE = 21;\n\nexport const generateElementId = customAlphabet(`${lowercase}${numbers}`, DEFAULT_SIZE);\n"],"mappings":"AAAA,SAASA,cAAc,QAAQ,QAAQ;AACvC,SAASC,SAAS,EAAEC,OAAO,QAAQ,mBAAmB;AAEtD,MAAMC,YAAY,GAAG,EAAE;AAEvB,OAAO,MAAMC,iBAAiB,GAAGJ,cAAc,CAAC,GAAGC,SAAS,GAAGC,OAAO,EAAE,EAAEC,YAAY,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"generateElementId.js","sources":["../src/generateElementId.ts"],"sourcesContent":["import { customAlphabet } from \"nanoid\";\nimport { lowercase, numbers } from \"nanoid-dictionary\";\n\nconst DEFAULT_SIZE = 21;\n\nexport const generateElementId = customAlphabet(`${lowercase}${numbers}`, DEFAULT_SIZE);\n"],"names":["DEFAULT_SIZE","generateElementId","customAlphabet","lowercase","numbers"],"mappings":";;AAGA,MAAMA,eAAe;AAEd,MAAMC,oBAAoBC,eAAe,GAAGC,YAAYC,SAAS,EAAEJ"}
@@ -1,7 +1,8 @@
1
1
  let headersProvider;
2
- export const setHeadersProvider = provider => {
3
- headersProvider = provider;
2
+ const setHeadersProvider = (provider)=>{
3
+ headersProvider = provider;
4
4
  };
5
- export const getHeadersProvider = () => headersProvider;
5
+ const getHeadersProvider = ()=>headersProvider;
6
+ export { getHeadersProvider, setHeadersProvider };
6
7
 
7
8
  //# sourceMappingURL=headersProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["headersProvider","setHeadersProvider","provider","getHeadersProvider"],"sources":["headersProvider.ts"],"sourcesContent":["type HeadersProvider = () => Promise<Headers>;\n\nlet headersProvider: HeadersProvider;\n\nexport const setHeadersProvider = (provider: HeadersProvider) => {\n headersProvider = provider;\n};\n\nexport const getHeadersProvider = () => headersProvider;\n"],"mappings":"AAEA,IAAIA,eAAgC;AAEpC,OAAO,MAAMC,kBAAkB,GAAIC,QAAyB,IAAK;EAC7DF,eAAe,GAAGE,QAAQ;AAC9B,CAAC;AAED,OAAO,MAAMC,kBAAkB,GAAGA,CAAA,KAAMH,eAAe","ignoreList":[]}
1
+ {"version":3,"file":"headersProvider.js","sources":["../src/headersProvider.ts"],"sourcesContent":["type HeadersProvider = () => Promise<Headers>;\n\nlet headersProvider: HeadersProvider;\n\nexport const setHeadersProvider = (provider: HeadersProvider) => {\n headersProvider = provider;\n};\n\nexport const getHeadersProvider = () => headersProvider;\n"],"names":["headersProvider","setHeadersProvider","provider","getHeadersProvider"],"mappings":"AAEA,IAAIA;AAEG,MAAMC,qBAAqB,CAACC;IAC/BF,kBAAkBE;AACtB;AAEO,MAAMC,qBAAqB,IAAMH"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Pure geometry core for crop + focal point rendering.
3
+ *
4
+ * This module is the single source of truth shared by:
5
+ * - the Admin editor previews (square / 4:3 / 16:9),
6
+ * - the Next.js render helper (`getImageProps` / `<Image>`),
7
+ * - and any future URL-transform loader (`?rect=…`).
8
+ *
9
+ * Keeping the math here guarantees that what an editor previews is exactly what
10
+ * a visitor sees. Everything is framework-agnostic and side-effect free.
11
+ */
12
+ import type { AssetCrop, AssetImage } from "@webiny/sdk";
13
+ import type { AspectRatioInput } from "./types.js";
14
+ /** A rectangle in normalized (0..1) coordinates of the original image. */
15
+ export interface NormalizedRect {
16
+ x: number;
17
+ y: number;
18
+ width: number;
19
+ height: number;
20
+ }
21
+ /**
22
+ * CSS needed to render an image cropped to a target aspect ratio while honoring
23
+ * both the crop rectangle and the focal point — without any server-side processing.
24
+ *
25
+ * `objectPosition` is the simpler, focal-point-only path (apply to an `object-fit:
26
+ * cover` image). `container` + `image` is the accurate path that also honors the
27
+ * crop rectangle via an overflow-clipped wrapper.
28
+ */
29
+ export interface ImageRenderData {
30
+ rect: NormalizedRect;
31
+ objectPosition: string;
32
+ container: {
33
+ position: "relative";
34
+ width: "100%";
35
+ aspectRatio: string;
36
+ overflow: "hidden";
37
+ };
38
+ image: {
39
+ position: "absolute";
40
+ width: string;
41
+ height: string;
42
+ left: string;
43
+ top: string;
44
+ maxWidth: "none";
45
+ objectFit: "cover";
46
+ objectPosition: string;
47
+ };
48
+ intrinsicWidth: number;
49
+ intrinsicHeight: number;
50
+ }
51
+ export declare function resolveAssetImage(override?: AssetImage | null, assetDefault?: AssetImage | null): AssetImage;
52
+ export declare function getCropRect(crop?: AssetCrop | null): NormalizedRect;
53
+ export declare function getVisibleRect(image: AssetImage, aspectRatio?: AspectRatioInput): NormalizedRect;
54
+ export declare function getImageRenderData(image: AssetImage, aspectRatio?: AspectRatioInput): ImageRenderData;
@@ -0,0 +1,112 @@
1
+ const clamp = (value, min = 0, max = 1)=>{
2
+ if (Number.isNaN(value)) return min;
3
+ return Math.min(max, Math.max(min, value));
4
+ };
5
+ const round = (n)=>Math.round(1000 * n) / 1000;
6
+ const DEFAULT_CROP = {
7
+ top: 0,
8
+ left: 0,
9
+ bottom: 0,
10
+ right: 0
11
+ };
12
+ const DEFAULT_FOCAL_POINT = {
13
+ x: 0.5,
14
+ y: 0.5
15
+ };
16
+ function resolveAssetImage(override, assetDefault) {
17
+ return {
18
+ crop: override?.crop ?? assetDefault?.crop,
19
+ focalPoint: override?.focalPoint ?? assetDefault?.focalPoint,
20
+ alt: override?.alt ?? assetDefault?.alt,
21
+ caption: override?.caption ?? assetDefault?.caption
22
+ };
23
+ }
24
+ function getCropRect(crop) {
25
+ const top = clamp(crop?.top ?? DEFAULT_CROP.top);
26
+ const left = clamp(crop?.left ?? DEFAULT_CROP.left);
27
+ const bottom = clamp(crop?.bottom ?? DEFAULT_CROP.bottom);
28
+ const right = clamp(crop?.right ?? DEFAULT_CROP.right);
29
+ const width = Math.max(0, 1 - left - right);
30
+ const height = Math.max(0, 1 - top - bottom);
31
+ return {
32
+ x: left,
33
+ y: top,
34
+ width,
35
+ height
36
+ };
37
+ }
38
+ const resolveAspectRatio = (aspectRatio)=>{
39
+ if ("number" == typeof aspectRatio) return aspectRatio > 0 ? aspectRatio : 1;
40
+ const { width, height } = aspectRatio;
41
+ return width > 0 && height > 0 ? width / height : 1;
42
+ };
43
+ const getFocalCenter = (fp)=>({
44
+ x: clamp(fp?.x ?? DEFAULT_FOCAL_POINT.x),
45
+ y: clamp(fp?.y ?? DEFAULT_FOCAL_POINT.y)
46
+ });
47
+ function getVisibleRect(image, aspectRatio) {
48
+ const cropRect = getCropRect(image.crop);
49
+ if (void 0 === aspectRatio || cropRect.width <= 0 || cropRect.height <= 0) return cropRect;
50
+ const iw = (image.width ?? 0) > 0 ? image.width : 1;
51
+ const ih = (image.height ?? 0) > 0 ? image.height : 1;
52
+ const targetAR = resolveAspectRatio(aspectRatio);
53
+ const cropWpx = cropRect.width * iw;
54
+ const cropHpx = cropRect.height * ih;
55
+ const cropAR = cropWpx / cropHpx;
56
+ let finalWpx;
57
+ let finalHpx;
58
+ if (targetAR > cropAR) {
59
+ finalWpx = cropWpx;
60
+ finalHpx = cropWpx / targetAR;
61
+ } else {
62
+ finalHpx = cropHpx;
63
+ finalWpx = cropHpx * targetAR;
64
+ }
65
+ const finalW = finalWpx / iw;
66
+ const finalH = finalHpx / ih;
67
+ const center = getFocalCenter(image.focalPoint);
68
+ const maxX = cropRect.x + cropRect.width - finalW;
69
+ const maxY = cropRect.y + cropRect.height - finalH;
70
+ const x = clamp(center.x - finalW / 2, cropRect.x, Math.max(cropRect.x, maxX));
71
+ const y = clamp(center.y - finalH / 2, cropRect.y, Math.max(cropRect.y, maxY));
72
+ return {
73
+ x,
74
+ y,
75
+ width: finalW,
76
+ height: finalH
77
+ };
78
+ }
79
+ function getImageRenderData(image, aspectRatio) {
80
+ const rect = getVisibleRect(image, aspectRatio);
81
+ const center = getFocalCenter(image.focalPoint);
82
+ const safeW = rect.width > 0 ? rect.width : 1;
83
+ const safeH = rect.height > 0 ? rect.height : 1;
84
+ const iw = image.width || 1;
85
+ const ih = image.height || 1;
86
+ const boxAspectRatio = void 0 !== aspectRatio ? resolveAspectRatio(aspectRatio) : rect.width * iw / (rect.height * ih);
87
+ return {
88
+ rect,
89
+ objectPosition: `${round(100 * center.x)}% ${round(100 * center.y)}%`,
90
+ container: {
91
+ position: "relative",
92
+ width: "100%",
93
+ aspectRatio: `${round(boxAspectRatio)}`,
94
+ overflow: "hidden"
95
+ },
96
+ image: {
97
+ position: "absolute",
98
+ width: `${round(100 / safeW)}%`,
99
+ height: `${round(100 / safeH)}%`,
100
+ left: `${round(100 * -(rect.x / safeW))}%`,
101
+ top: `${round(100 * -(rect.y / safeH))}%`,
102
+ maxWidth: "none",
103
+ objectFit: "cover",
104
+ objectPosition: `${round(100 * center.x)}% ${round(100 * center.y)}%`
105
+ },
106
+ intrinsicWidth: iw,
107
+ intrinsicHeight: ih
108
+ };
109
+ }
110
+ export { getCropRect, getImageRenderData, getVisibleRect, resolveAssetImage };
111
+
112
+ //# sourceMappingURL=geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image/geometry.js","sources":["../../src/image/geometry.ts"],"sourcesContent":["/**\n * Pure geometry core for crop + focal point rendering.\n *\n * This module is the single source of truth shared by:\n * - the Admin editor previews (square / 4:3 / 16:9),\n * - the Next.js render helper (`getImageProps` / `<Image>`),\n * - and any future URL-transform loader (`?rect=…`).\n *\n * Keeping the math here guarantees that what an editor previews is exactly what\n * a visitor sees. Everything is framework-agnostic and side-effect free.\n */\nimport type { AssetCrop, AssetFocalPoint, AssetImage } from \"@webiny/sdk\";\nimport type { AspectRatioInput } from \"./types.js\";\n\n/** A rectangle in normalized (0..1) coordinates of the original image. */\nexport interface NormalizedRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * CSS needed to render an image cropped to a target aspect ratio while honoring\n * both the crop rectangle and the focal point — without any server-side processing.\n *\n * `objectPosition` is the simpler, focal-point-only path (apply to an `object-fit:\n * cover` image). `container` + `image` is the accurate path that also honors the\n * crop rectangle via an overflow-clipped wrapper.\n */\nexport interface ImageRenderData {\n rect: NormalizedRect;\n objectPosition: string;\n container: {\n position: \"relative\";\n width: \"100%\";\n aspectRatio: string;\n overflow: \"hidden\";\n };\n image: {\n position: \"absolute\";\n width: string;\n height: string;\n left: string;\n top: string;\n maxWidth: \"none\";\n objectFit: \"cover\";\n objectPosition: string;\n };\n intrinsicWidth: number;\n intrinsicHeight: number;\n}\n\nconst clamp = (value: number, min = 0, max = 1): number => {\n if (Number.isNaN(value)) {\n return min;\n }\n return Math.min(max, Math.max(min, value));\n};\n\nconst round = (n: number): number => Math.round(n * 1000) / 1000;\n\nconst DEFAULT_CROP: AssetCrop = { top: 0, left: 0, bottom: 0, right: 0 };\nconst DEFAULT_FOCAL_POINT: AssetFocalPoint = { x: 0.5, y: 0.5 };\n\nexport function resolveAssetImage(\n override?: AssetImage | null,\n assetDefault?: AssetImage | null\n): AssetImage {\n return {\n crop: override?.crop ?? assetDefault?.crop,\n focalPoint: override?.focalPoint ?? assetDefault?.focalPoint,\n alt: override?.alt ?? assetDefault?.alt,\n caption: override?.caption ?? assetDefault?.caption\n };\n}\n\nexport function getCropRect(crop?: AssetCrop | null): NormalizedRect {\n const top = clamp(crop?.top ?? DEFAULT_CROP.top);\n const left = clamp(crop?.left ?? DEFAULT_CROP.left);\n const bottom = clamp(crop?.bottom ?? DEFAULT_CROP.bottom);\n const right = clamp(crop?.right ?? DEFAULT_CROP.right);\n\n const width = Math.max(0, 1 - left - right);\n const height = Math.max(0, 1 - top - bottom);\n\n return { x: left, y: top, width, height };\n}\n\nconst resolveAspectRatio = (aspectRatio: AspectRatioInput): number => {\n if (typeof aspectRatio === \"number\") {\n return aspectRatio > 0 ? aspectRatio : 1;\n }\n const { width, height } = aspectRatio;\n return width > 0 && height > 0 ? width / height : 1;\n};\n\nconst getFocalCenter = (fp?: AssetFocalPoint | null): { x: number; y: number } => ({\n x: clamp(fp?.x ?? DEFAULT_FOCAL_POINT.x),\n y: clamp(fp?.y ?? DEFAULT_FOCAL_POINT.y)\n});\n\nexport function getVisibleRect(image: AssetImage, aspectRatio?: AspectRatioInput): NormalizedRect {\n const cropRect = getCropRect(image.crop);\n\n if (aspectRatio === undefined || cropRect.width <= 0 || cropRect.height <= 0) {\n return cropRect;\n }\n\n const iw = (image.width ?? 0) > 0 ? image.width! : 1;\n const ih = (image.height ?? 0) > 0 ? image.height! : 1;\n const targetAR = resolveAspectRatio(aspectRatio);\n\n const cropWpx = cropRect.width * iw;\n const cropHpx = cropRect.height * ih;\n const cropAR = cropWpx / cropHpx;\n\n let finalWpx: number;\n let finalHpx: number;\n if (targetAR > cropAR) {\n finalWpx = cropWpx;\n finalHpx = cropWpx / targetAR;\n } else {\n finalHpx = cropHpx;\n finalWpx = cropHpx * targetAR;\n }\n\n const finalW = finalWpx / iw;\n const finalH = finalHpx / ih;\n\n const center = getFocalCenter(image.focalPoint);\n const maxX = cropRect.x + cropRect.width - finalW;\n const maxY = cropRect.y + cropRect.height - finalH;\n const x = clamp(center.x - finalW / 2, cropRect.x, Math.max(cropRect.x, maxX));\n const y = clamp(center.y - finalH / 2, cropRect.y, Math.max(cropRect.y, maxY));\n\n return { x, y, width: finalW, height: finalH };\n}\n\nexport function getImageRenderData(\n image: AssetImage,\n aspectRatio?: AspectRatioInput\n): ImageRenderData {\n const rect = getVisibleRect(image, aspectRatio);\n const center = getFocalCenter(image.focalPoint);\n\n const safeW = rect.width > 0 ? rect.width : 1;\n const safeH = rect.height > 0 ? rect.height : 1;\n\n const iw = image.width || 1;\n const ih = image.height || 1;\n\n const boxAspectRatio =\n aspectRatio !== undefined\n ? resolveAspectRatio(aspectRatio)\n : (rect.width * iw) / (rect.height * ih);\n\n return {\n rect,\n objectPosition: `${round(center.x * 100)}% ${round(center.y * 100)}%`,\n container: {\n position: \"relative\",\n width: \"100%\",\n aspectRatio: `${round(boxAspectRatio)}`,\n overflow: \"hidden\"\n },\n image: {\n position: \"absolute\",\n width: `${round(100 / safeW)}%`,\n height: `${round(100 / safeH)}%`,\n left: `${round(-(rect.x / safeW) * 100)}%`,\n top: `${round(-(rect.y / safeH) * 100)}%`,\n maxWidth: \"none\",\n objectFit: \"cover\",\n objectPosition: `${round(center.x * 100)}% ${round(center.y * 100)}%`\n },\n intrinsicWidth: iw,\n intrinsicHeight: ih\n };\n}\n"],"names":["clamp","value","min","max","Number","Math","round","n","DEFAULT_CROP","DEFAULT_FOCAL_POINT","resolveAssetImage","override","assetDefault","getCropRect","crop","top","left","bottom","right","width","height","resolveAspectRatio","aspectRatio","getFocalCenter","fp","getVisibleRect","image","cropRect","undefined","iw","ih","targetAR","cropWpx","cropHpx","cropAR","finalWpx","finalHpx","finalW","finalH","center","maxX","maxY","x","y","getImageRenderData","rect","safeW","safeH","boxAspectRatio"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,190 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { getCropRect, getImageRenderData, getVisibleRect, resolveAssetImage } from "./geometry.js";
3
+ const geometry_test_image = (overrides)=>({
4
+ width: 1000,
5
+ height: 1000,
6
+ ...overrides
7
+ });
8
+ describe("resolveAssetImage", ()=>{
9
+ it("prefers the override per property, falling back to the asset default", ()=>{
10
+ const assetDefault = {
11
+ crop: {
12
+ top: 0.1,
13
+ left: 0.1,
14
+ bottom: 0.1,
15
+ right: 0.1
16
+ },
17
+ focalPoint: {
18
+ x: 0.2,
19
+ y: 0.2
20
+ },
21
+ alt: "default alt",
22
+ caption: "default caption"
23
+ };
24
+ const override = {
25
+ focalPoint: {
26
+ x: 0.8,
27
+ y: 0.8
28
+ },
29
+ alt: "override alt"
30
+ };
31
+ const result = resolveAssetImage(override, assetDefault);
32
+ expect(result.crop).toEqual(assetDefault.crop);
33
+ expect(result.caption).toBe("default caption");
34
+ expect(result.focalPoint).toEqual(override.focalPoint);
35
+ expect(result.alt).toBe("override alt");
36
+ });
37
+ it("handles null/undefined inputs", ()=>{
38
+ expect(resolveAssetImage(null, null)).toEqual({
39
+ crop: void 0,
40
+ focalPoint: void 0,
41
+ alt: void 0,
42
+ caption: void 0
43
+ });
44
+ });
45
+ });
46
+ describe("getCropRect", ()=>{
47
+ it("returns the full image when no crop is set", ()=>{
48
+ expect(getCropRect()).toEqual({
49
+ x: 0,
50
+ y: 0,
51
+ width: 1,
52
+ height: 1
53
+ });
54
+ });
55
+ it("converts edge insets into a rectangle", ()=>{
56
+ const rect = getCropRect({
57
+ top: 0.1,
58
+ left: 0.2,
59
+ bottom: 0.3,
60
+ right: 0.05
61
+ });
62
+ expect(rect.x).toBeCloseTo(0.2, 10);
63
+ expect(rect.y).toBeCloseTo(0.1, 10);
64
+ expect(rect.width).toBeCloseTo(0.75, 10);
65
+ expect(rect.height).toBeCloseTo(0.6, 10);
66
+ });
67
+ it("does not produce negative dimensions when insets overlap", ()=>{
68
+ const rect = getCropRect({
69
+ top: 0.7,
70
+ left: 0.7,
71
+ bottom: 0.7,
72
+ right: 0.7
73
+ });
74
+ expect(rect.width).toBe(0);
75
+ expect(rect.height).toBe(0);
76
+ });
77
+ it("clamps out-of-range values", ()=>{
78
+ const rect = getCropRect({
79
+ top: -1,
80
+ left: 2,
81
+ bottom: 0,
82
+ right: 0
83
+ });
84
+ expect(rect.x).toBe(1);
85
+ expect(rect.y).toBe(0);
86
+ });
87
+ });
88
+ describe("getVisibleRect", ()=>{
89
+ it("returns the crop rect when no aspect ratio is requested", ()=>{
90
+ const rect = getVisibleRect(geometry_test_image({
91
+ crop: {
92
+ top: 0.1,
93
+ left: 0.1,
94
+ bottom: 0.1,
95
+ right: 0.1
96
+ }
97
+ }));
98
+ expect(rect).toEqual({
99
+ x: 0.1,
100
+ y: 0.1,
101
+ width: 0.8,
102
+ height: 0.8
103
+ });
104
+ });
105
+ it("fits a 16:9 rect inside a square image, centered by default", ()=>{
106
+ const rect = getVisibleRect(geometry_test_image(), 16 / 9);
107
+ expect(rect.width).toBeCloseTo(1, 5);
108
+ expect(rect.height).toBeCloseTo(0.5625, 5);
109
+ expect(rect.y).toBeCloseTo(0.21875, 5);
110
+ expect(rect.x).toBeCloseTo(0, 5);
111
+ });
112
+ it("shifts the visible rect toward the focal point and clamps to bounds", ()=>{
113
+ const rect = getVisibleRect(geometry_test_image({
114
+ focalPoint: {
115
+ x: 0.5,
116
+ y: 0.05
117
+ }
118
+ }), 16 / 9);
119
+ expect(rect.y).toBeCloseTo(0, 5);
120
+ });
121
+ it("keeps the visible rect within the crop rectangle", ()=>{
122
+ const rect = getVisibleRect(geometry_test_image({
123
+ crop: {
124
+ top: 0.25,
125
+ left: 0,
126
+ bottom: 0.25,
127
+ right: 0
128
+ },
129
+ focalPoint: {
130
+ x: 0.5,
131
+ y: 0.9
132
+ }
133
+ }), 1);
134
+ expect(rect.y).toBeGreaterThanOrEqual(0.25 - 1e-9);
135
+ expect(rect.y + rect.height).toBeLessThanOrEqual(0.75 + 1e-9);
136
+ });
137
+ it("accepts an explicit width/height aspect ratio", ()=>{
138
+ const byObject = getVisibleRect(geometry_test_image(), {
139
+ width: 16,
140
+ height: 9
141
+ });
142
+ const byNumber = getVisibleRect(geometry_test_image(), 16 / 9);
143
+ expect(byObject).toEqual(byNumber);
144
+ });
145
+ });
146
+ describe("getImageRenderData", ()=>{
147
+ it("derives objectPosition from the focal point", ()=>{
148
+ const data = getImageRenderData(geometry_test_image({
149
+ focalPoint: {
150
+ x: 0.25,
151
+ y: 0.75
152
+ }
153
+ }), 1);
154
+ expect(data.objectPosition).toBe("25% 75%");
155
+ });
156
+ it("produces an overflow-hidden container with the requested aspect ratio", ()=>{
157
+ const data = getImageRenderData(geometry_test_image(), 16 / 9);
158
+ expect(data.container.overflow).toBe("hidden");
159
+ expect(data.container.aspectRatio).toBe(`${Math.round(16 / 9 * 1000) / 1000}`);
160
+ });
161
+ it("scales and offsets the image so the visible rect fills the container", ()=>{
162
+ const data = getImageRenderData(geometry_test_image(), 16 / 9);
163
+ expect(data.image.width).toBe("100%");
164
+ expect(data.image.height).toBe(`${Math.round(100 / 0.5625 * 1000) / 1000}%`);
165
+ expect(parseFloat(data.image.left)).toBe(0);
166
+ expect(parseFloat(data.image.top)).toBeLessThan(0);
167
+ });
168
+ it("stays finite for a degenerate (collapsed) crop", ()=>{
169
+ const data = getImageRenderData(geometry_test_image({
170
+ crop: {
171
+ top: 0.7,
172
+ left: 0.7,
173
+ bottom: 0.7,
174
+ right: 0.7
175
+ }
176
+ }), 1);
177
+ expect(Number.isFinite(parseFloat(data.image.width))).toBe(true);
178
+ expect(Number.isFinite(parseFloat(data.image.height))).toBe(true);
179
+ });
180
+ it("passes through intrinsic dimensions", ()=>{
181
+ const data = getImageRenderData(geometry_test_image({
182
+ width: 1920,
183
+ height: 1080
184
+ }), 16 / 9);
185
+ expect(data.intrinsicWidth).toBe(1920);
186
+ expect(data.intrinsicHeight).toBe(1080);
187
+ });
188
+ });
189
+
190
+ //# sourceMappingURL=geometry.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image/geometry.test.js","sources":["../../src/image/geometry.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { getCropRect, getImageRenderData, getVisibleRect, resolveAssetImage } from \"./geometry.js\";\nimport type { AssetImage } from \"@webiny/sdk\";\n\nconst image = (overrides?: Partial<AssetImage>): AssetImage => ({\n width: 1000,\n height: 1000,\n ...overrides\n});\n\ndescribe(\"resolveAssetImage\", () => {\n it(\"prefers the override per property, falling back to the asset default\", () => {\n const assetDefault: AssetImage = {\n crop: { top: 0.1, left: 0.1, bottom: 0.1, right: 0.1 },\n focalPoint: { x: 0.2, y: 0.2 },\n alt: \"default alt\",\n caption: \"default caption\"\n };\n const override: AssetImage = {\n focalPoint: { x: 0.8, y: 0.8 },\n alt: \"override alt\"\n };\n\n const result = resolveAssetImage(override, assetDefault);\n\n expect(result.crop).toEqual(assetDefault.crop);\n expect(result.caption).toBe(\"default caption\");\n expect(result.focalPoint).toEqual(override.focalPoint);\n expect(result.alt).toBe(\"override alt\");\n });\n\n it(\"handles null/undefined inputs\", () => {\n expect(resolveAssetImage(null, null)).toEqual({\n crop: undefined,\n focalPoint: undefined,\n alt: undefined,\n caption: undefined\n });\n });\n});\n\ndescribe(\"getCropRect\", () => {\n it(\"returns the full image when no crop is set\", () => {\n expect(getCropRect()).toEqual({ x: 0, y: 0, width: 1, height: 1 });\n });\n\n it(\"converts edge insets into a rectangle\", () => {\n const rect = getCropRect({ top: 0.1, left: 0.2, bottom: 0.3, right: 0.05 });\n expect(rect.x).toBeCloseTo(0.2, 10);\n expect(rect.y).toBeCloseTo(0.1, 10);\n expect(rect.width).toBeCloseTo(0.75, 10);\n expect(rect.height).toBeCloseTo(0.6, 10);\n });\n\n it(\"does not produce negative dimensions when insets overlap\", () => {\n const rect = getCropRect({ top: 0.7, left: 0.7, bottom: 0.7, right: 0.7 });\n expect(rect.width).toBe(0);\n expect(rect.height).toBe(0);\n });\n\n it(\"clamps out-of-range values\", () => {\n const rect = getCropRect({ top: -1, left: 2, bottom: 0, right: 0 });\n expect(rect.x).toBe(1);\n expect(rect.y).toBe(0);\n });\n});\n\ndescribe(\"getVisibleRect\", () => {\n it(\"returns the crop rect when no aspect ratio is requested\", () => {\n const rect = getVisibleRect(\n image({ crop: { top: 0.1, left: 0.1, bottom: 0.1, right: 0.1 } })\n );\n expect(rect).toEqual({ x: 0.1, y: 0.1, width: 0.8, height: 0.8 });\n });\n\n it(\"fits a 16:9 rect inside a square image, centered by default\", () => {\n const rect = getVisibleRect(image(), 16 / 9);\n expect(rect.width).toBeCloseTo(1, 5);\n expect(rect.height).toBeCloseTo(0.5625, 5);\n expect(rect.y).toBeCloseTo((1 - 0.5625) / 2, 5);\n expect(rect.x).toBeCloseTo(0, 5);\n });\n\n it(\"shifts the visible rect toward the focal point and clamps to bounds\", () => {\n const rect = getVisibleRect(image({ focalPoint: { x: 0.5, y: 0.05 } }), 16 / 9);\n expect(rect.y).toBeCloseTo(0, 5);\n });\n\n it(\"keeps the visible rect within the crop rectangle\", () => {\n const rect = getVisibleRect(\n image({\n crop: { top: 0.25, left: 0, bottom: 0.25, right: 0 },\n focalPoint: { x: 0.5, y: 0.9 }\n }),\n 1\n );\n expect(rect.y).toBeGreaterThanOrEqual(0.25 - 1e-9);\n expect(rect.y + rect.height).toBeLessThanOrEqual(0.75 + 1e-9);\n });\n\n it(\"accepts an explicit width/height aspect ratio\", () => {\n const byObject = getVisibleRect(image(), { width: 16, height: 9 });\n const byNumber = getVisibleRect(image(), 16 / 9);\n expect(byObject).toEqual(byNumber);\n });\n});\n\ndescribe(\"getImageRenderData\", () => {\n it(\"derives objectPosition from the focal point\", () => {\n const data = getImageRenderData(image({ focalPoint: { x: 0.25, y: 0.75 } }), 1);\n expect(data.objectPosition).toBe(\"25% 75%\");\n });\n\n it(\"produces an overflow-hidden container with the requested aspect ratio\", () => {\n const data = getImageRenderData(image(), 16 / 9);\n expect(data.container.overflow).toBe(\"hidden\");\n expect(data.container.aspectRatio).toBe(`${Math.round((16 / 9) * 1000) / 1000}`);\n });\n\n it(\"scales and offsets the image so the visible rect fills the container\", () => {\n const data = getImageRenderData(image(), 16 / 9);\n expect(data.image.width).toBe(\"100%\");\n expect(data.image.height).toBe(`${Math.round((100 / 0.5625) * 1000) / 1000}%`);\n expect(parseFloat(data.image.left)).toBe(0);\n expect(parseFloat(data.image.top)).toBeLessThan(0);\n });\n\n it(\"stays finite for a degenerate (collapsed) crop\", () => {\n const data = getImageRenderData(\n image({ crop: { top: 0.7, left: 0.7, bottom: 0.7, right: 0.7 } }),\n 1\n );\n expect(Number.isFinite(parseFloat(data.image.width))).toBe(true);\n expect(Number.isFinite(parseFloat(data.image.height))).toBe(true);\n });\n\n it(\"passes through intrinsic dimensions\", () => {\n const data = getImageRenderData(image({ width: 1920, height: 1080 }), 16 / 9);\n expect(data.intrinsicWidth).toBe(1920);\n expect(data.intrinsicHeight).toBe(1080);\n });\n});\n"],"names":["image","overrides","describe","it","assetDefault","override","result","resolveAssetImage","expect","undefined","getCropRect","rect","getVisibleRect","byObject","byNumber","data","getImageRenderData","Math","parseFloat","Number"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from "./types.js";
2
+ export * from "./presets.js";
3
+ export * from "./geometry.js";
package/image/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./types.js";
2
+ export * from "./presets.js";
3
+ export * from "./geometry.js";
@@ -0,0 +1,6 @@
1
+ import type { AspectRatioPreset } from "./types.js";
2
+ /**
3
+ * Default aspect ratios shown in the image editor's preview strip.
4
+ * A later phase will let a field lock to one of these (or a custom ratio).
5
+ */
6
+ export declare const ASPECT_RATIO_PRESETS: AspectRatioPreset[];
@@ -0,0 +1,20 @@
1
+ const ASPECT_RATIO_PRESETS = [
2
+ {
3
+ id: "square",
4
+ label: "Square",
5
+ ratio: 1
6
+ },
7
+ {
8
+ id: "4:3",
9
+ label: "4:3",
10
+ ratio: 4 / 3
11
+ },
12
+ {
13
+ id: "16:9",
14
+ label: "16:9",
15
+ ratio: 16 / 9
16
+ }
17
+ ];
18
+ export { ASPECT_RATIO_PRESETS };
19
+
20
+ //# sourceMappingURL=presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image/presets.js","sources":["../../src/image/presets.ts"],"sourcesContent":["import type { AspectRatioPreset } from \"./types.js\";\n\n/**\n * Default aspect ratios shown in the image editor's preview strip.\n * A later phase will let a field lock to one of these (or a custom ratio).\n */\nexport const ASPECT_RATIO_PRESETS: AspectRatioPreset[] = [\n { id: \"square\", label: \"Square\", ratio: 1 },\n { id: \"4:3\", label: \"4:3\", ratio: 4 / 3 },\n { id: \"16:9\", label: \"16:9\", ratio: 16 / 9 }\n];\n"],"names":["ASPECT_RATIO_PRESETS"],"mappings":"AAMO,MAAMA,uBAA4C;IACrD;QAAE,IAAI;QAAU,OAAO;QAAU,OAAO;IAAE;IAC1C;QAAE,IAAI;QAAO,OAAO;QAAO,OAAO,IAAI;IAAE;IACxC;QAAE,IAAI;QAAQ,OAAO;QAAQ,OAAO,KAAK;IAAE;CAC9C"}