@adobe-commerce/elsie 1.2.2-alpha1 → 1.3.0-alpha01

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.
package/config/vite.mjs CHANGED
@@ -25,18 +25,11 @@ import banner from 'vite-plugin-banner';
25
25
  const env = loadEnv('', process.cwd());
26
26
 
27
27
  // Load Elsie Config
28
- const elsieConfig = await import(
29
- path.resolve(process.cwd(), './.elsie.js')
30
- ).then((m) => m.default);
31
-
32
- const packageJSON = await import(
33
- path.resolve(process.cwd(), './package.json'),
34
- {
35
- assert: {
36
- type: 'json',
37
- },
38
- }
39
- ).then((m) => m.default);
28
+ const elsieConfig = await import(path.resolve(process.cwd(), './.elsie.js')).then((m) => m.default);
29
+
30
+ // Read package.json using createRequire (compatible with Node 20 and 22)
31
+ const require = createRequire(import.meta.url);
32
+ const packageJSON = require(path.resolve(process.cwd(), './package.json'));
40
33
 
41
34
  // Paths
42
35
  const paths = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe-commerce/elsie",
3
- "version": "1.2.2-alpha1",
3
+ "version": "1.3.0-alpha01",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "description": "Domain Package SDK",
6
6
  "engines": {
@@ -0,0 +1,237 @@
1
+ import { deepmerge } from '../deepmerge';
2
+
3
+ interface ConfigHeaders {
4
+ all?: Record<string, string>;
5
+ [key: string]: Record<string, string> | undefined;
6
+ }
7
+
8
+ interface ConfigPublic {
9
+ default: ConfigRoot;
10
+ [key: string]: ConfigRoot;
11
+ }
12
+
13
+ interface ConfigRoot {
14
+ headers?: ConfigHeaders;
15
+ [key: string]: any;
16
+ }
17
+
18
+ interface Config {
19
+ public: ConfigPublic;
20
+ [key: string]: any;
21
+ }
22
+
23
+ // Private state
24
+ let config: Config | null = null;
25
+ let rootPath: string | null = null;
26
+ let rootConfig: ConfigRoot | null = null;
27
+
28
+ /**
29
+ * Reset the config state
30
+ */
31
+ function resetConfig() {
32
+ config = null;
33
+ rootPath = null;
34
+ rootConfig = null;
35
+ }
36
+
37
+ /**
38
+ * Builds the URL for the config file.
39
+ *
40
+ * @returns {URL} - The URL for the config file.
41
+ */
42
+ function buildConfigURL() {
43
+ return new URL(`${window.location.origin}/config.json`);
44
+ }
45
+
46
+ /**
47
+ * Retrieves a value from a config object using dot notation.
48
+ *
49
+ * @param {Object} obj - The config object.
50
+ * @param {string} key - The key to retrieve (supports dot notation).
51
+ * @returns {any} - The value of the key.
52
+ */
53
+ function getValue(obj: Record<string, any>, key: string): any {
54
+ return key.split('.').reduce((current: Record<string, any>, part: string) => {
55
+ if (!Object.prototype.hasOwnProperty.call(current, part)) {
56
+ console.warn(`Property ${key} does not exist in the object`);
57
+ return undefined;
58
+ }
59
+ return current[part];
60
+ }, obj);
61
+ }
62
+
63
+ /**
64
+ * Get cookie
65
+ * @param {string} cookieName - The name of the cookie to get
66
+ * @returns {string} - The value of the cookie
67
+ */
68
+ function getCookie(cookieName: string): string | undefined {
69
+ const cookies = document.cookie.split(';');
70
+ let foundValue;
71
+
72
+ cookies.forEach((cookie) => {
73
+ const [name, value] = cookie.trim().split('=');
74
+ if (name === cookieName) {
75
+ foundValue = decodeURIComponent(value);
76
+ }
77
+ });
78
+
79
+ return foundValue;
80
+ }
81
+
82
+ /**
83
+ * Get root path
84
+ * @param {Object} [configObj=config] - The config object.
85
+ * @returns {string} - The root path.
86
+ */
87
+ function getRootPath(configObj: Config | null = config): string {
88
+ if (!configObj) {
89
+ console.warn('No config found. Please call initializeConfig() first.');
90
+ return '/';
91
+ }
92
+
93
+ const value = Object.keys(configObj?.public)
94
+ // Sort by number of non-empty segments to find the deepest path
95
+ .sort((a, b) => {
96
+ const aSegments = a.split('/').filter(Boolean).length;
97
+ const bSegments = b.split('/').filter(Boolean).length;
98
+ return bSegments - aSegments;
99
+ })
100
+ .find(
101
+ (key) =>
102
+ window.location.pathname === key ||
103
+ window.location.pathname.startsWith(key)
104
+ );
105
+
106
+ return value ?? '/';
107
+ }
108
+
109
+ /**
110
+ * Get list of root paths from public config
111
+ * @returns {Array} - The list of root paths.
112
+ */
113
+ function getListOfRootPaths(): string[] {
114
+ if (!config) {
115
+ console.warn('No config found. Please call initializeConfig() first.');
116
+ return [];
117
+ }
118
+
119
+ return Object.keys(config.public).filter((root) => root !== 'default');
120
+ }
121
+
122
+ /**
123
+ * Checks if the public config contains more than "default"
124
+ * @returns {boolean} - true if public config contains more than "default"
125
+ */
126
+ function isMultistore(): boolean {
127
+ return getListOfRootPaths().length >= 1;
128
+ }
129
+
130
+ /**
131
+ * Retrieves headers from config entries like commerce.headers.pdp.my-header, etc and
132
+ * returns as object of all headers like { my-header: value, ... }
133
+ * @param {string} scope - The scope of the headers to retrieve.
134
+ * @returns {Object} - The headers.
135
+ */
136
+ function getHeaders(scope: string): Record<string, string> {
137
+ if (!rootConfig) {
138
+ throw new Error(
139
+ 'Configuration not initialized. Call initializeConfig() first.'
140
+ );
141
+ }
142
+ const headers = rootConfig.headers ?? {};
143
+ return {
144
+ ...(headers.all ?? {}),
145
+ ...(headers[scope] ?? {}),
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Applies config overrides from metadata.
151
+ *
152
+ * @param {Object} [configObj=config] - The base config.
153
+ * @param {string} [root=rootPath] - The root path.
154
+ * @returns {Object} - The config with overrides applied.
155
+ */
156
+ function applyConfigOverrides(
157
+ configObj: Config | null,
158
+ root: string | null
159
+ ): ConfigRoot {
160
+ const defaultConfig = configObj!.public?.default;
161
+
162
+ if (root === '/' || !configObj!.public[root as keyof ConfigPublic])
163
+ return defaultConfig;
164
+
165
+ return deepmerge(
166
+ defaultConfig,
167
+ configObj!.public[root as keyof ConfigPublic]
168
+ );
169
+ }
170
+
171
+ /**
172
+ * Fetches config from remote and saves in session, then returns it, otherwise
173
+ * returns if it already exists.
174
+ *
175
+ * @returns {Promise<Object>} - The config JSON from session storage
176
+ */
177
+ async function getConfigFromSession(): Promise<Config> {
178
+ try {
179
+ const configJSON = window.sessionStorage.getItem('config');
180
+ if (!configJSON) {
181
+ throw new Error('No config in session storage');
182
+ }
183
+
184
+ const parsedConfig = JSON.parse(configJSON);
185
+ if (
186
+ !parsedConfig[':expiry'] ||
187
+ parsedConfig[':expiry'] < Math.round(Date.now() / 1000)
188
+ ) {
189
+ throw new Error('Config expired');
190
+ }
191
+ return parsedConfig;
192
+ } catch (e) {
193
+ const config = await fetch(buildConfigURL());
194
+ if (!config.ok) throw new Error('Failed to fetch config');
195
+ const configJSON = await config.json();
196
+ configJSON[':expiry'] = Math.round(Date.now() / 1000) + 7200;
197
+ window.sessionStorage.setItem('config', JSON.stringify(configJSON));
198
+ return configJSON;
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Initializes the configuration system.
204
+ * @returns {Promise<void>}
205
+ */
206
+ async function initializeConfig(): Promise<ConfigRoot> {
207
+ config = await getConfigFromSession();
208
+ rootPath = getRootPath(config);
209
+ rootConfig = applyConfigOverrides(config, rootPath);
210
+ return rootConfig;
211
+ }
212
+
213
+ /**
214
+ * Retrieves a configuration value.
215
+ *
216
+ * @param {string} configParam - The configuration parameter to retrieve.
217
+ * @returns {string|undefined} - The value of the configuration parameter, or undefined.
218
+ */
219
+ function getConfigValue(configParam: string): any {
220
+ if (!rootConfig) {
221
+ throw new Error(
222
+ 'Configuration not initialized. Call initializeConfig() first.'
223
+ );
224
+ }
225
+ return getValue(rootConfig, configParam);
226
+ }
227
+
228
+ export {
229
+ initializeConfig,
230
+ getCookie,
231
+ getRootPath,
232
+ getListOfRootPaths,
233
+ isMultistore,
234
+ getConfigValue,
235
+ getHeaders,
236
+ resetConfig,
237
+ };