@salesforce/storefront-next-runtime 0.4.2 → 1.0.0-alpha.0

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 (60) hide show
  1. package/README.md +9 -3
  2. package/dist/config.d.ts +33 -221
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +34 -116
  5. package/dist/config.js.map +1 -1
  6. package/dist/data-store.d.ts +185 -15
  7. package/dist/data-store.d.ts.map +1 -1
  8. package/dist/data-store.js +412 -10
  9. package/dist/data-store.js.map +1 -1
  10. package/dist/design-data.d.ts +266 -62
  11. package/dist/design-data.d.ts.map +1 -1
  12. package/dist/design-data.js +399 -14
  13. package/dist/design-data.js.map +1 -1
  14. package/dist/design-mode.d.ts +3 -2
  15. package/dist/design-mode.d.ts.map +1 -1
  16. package/dist/design-react-core.d.ts +2 -2
  17. package/dist/events.d.ts +32 -6
  18. package/dist/events.d.ts.map +1 -1
  19. package/dist/i18n-client.d.ts.map +1 -1
  20. package/dist/i18n-client.js.map +1 -1
  21. package/dist/i18n.d.ts +1 -2
  22. package/dist/i18n.d.ts.map +1 -1
  23. package/dist/modeDetection.js +0 -18
  24. package/dist/modeDetection.js.map +1 -1
  25. package/dist/scapi.d.ts +2185 -466
  26. package/dist/scapi.d.ts.map +1 -1
  27. package/dist/scapi.js +1 -1
  28. package/dist/scapi.js.map +1 -1
  29. package/dist/schema.d.ts +17 -15
  30. package/dist/schema.d.ts.map +1 -1
  31. package/dist/site-context.d.ts +43 -27
  32. package/dist/site-context.d.ts.map +1 -1
  33. package/dist/site-context.js +2 -2
  34. package/dist/site-context2.js +41 -31
  35. package/dist/site-context2.js.map +1 -1
  36. package/dist/types.d.ts +19 -3
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/types2.d.ts +89 -63
  39. package/dist/types2.d.ts.map +1 -1
  40. package/package.json +1 -19
  41. package/dist/custom-global-preferences.d.ts +0 -20
  42. package/dist/custom-global-preferences.d.ts.map +0 -1
  43. package/dist/custom-global-preferences.js +0 -31
  44. package/dist/custom-global-preferences.js.map +0 -1
  45. package/dist/custom-site-preferences.d.ts +0 -20
  46. package/dist/custom-site-preferences.d.ts.map +0 -1
  47. package/dist/custom-site-preferences.js +0 -31
  48. package/dist/custom-site-preferences.js.map +0 -1
  49. package/dist/data-store-custom-global-preferences.d.ts +0 -2
  50. package/dist/data-store-custom-global-preferences.js +0 -6
  51. package/dist/data-store-custom-site-preferences.d.ts +0 -2
  52. package/dist/data-store-custom-site-preferences.js +0 -6
  53. package/dist/data-store-gcp-preferences.d.ts +0 -2
  54. package/dist/data-store-gcp-preferences.js +0 -6
  55. package/dist/gcp-preferences.d.ts +0 -52
  56. package/dist/gcp-preferences.d.ts.map +0 -1
  57. package/dist/gcp-preferences.js +0 -64
  58. package/dist/gcp-preferences.js.map +0 -1
  59. package/dist/utils.js +0 -90
  60. package/dist/utils.js.map +0 -1
package/README.md CHANGED
@@ -55,9 +55,15 @@ Utilities and middleware for reading scoped entries from the MRT data access lay
55
55
  - `AWS_REGION` (required): AWS region for the data store table (e.g., `us-east-1`)
56
56
  - `MOBIFY_PROPERTY_ID` (required): MRT property identifier (e.g., `abcd1234`)
57
57
  - `DEPLOY_TARGET` (required): MRT deploy target (e.g., `production`)
58
- - `SFNEXT_DATA_STORE_UNAVAILABLE_MODE` (optional): controls middleware behavior when MRT data store is unavailable
59
- - `throw` (default): fail fast by throwing
60
- - `fallback`: use middleware-defined safe fallback values and continue request execution
58
+ - `SFNEXT_DATA_STORE_UNAVAILABLE_MODE` (optional): controls built-in middleware behavior when the
59
+ MRT data store is unavailable or returns a service error
60
+ - `fallback` (default): use middleware-defined safe fallback values and continue request execution
61
+ - `throw`: opt back into fail-fast behavior — middleware throws and the request errors out
62
+
63
+ Applies to the four built-in middlewares (`customSitePreferencesMiddleware`,
64
+ `customGlobalPreferencesMiddleware`, `gcpPreferencesMiddleware`, `loginPreferencesMiddleware`).
65
+ Customer-authored middlewares created via `createDataStoreMiddleware` default to `'throw'`; pass
66
+ `onUnavailable: 'fallback'` and a `fallbackValue` to opt into graceful degradation.
61
67
 
62
68
  These are managed by Managed Runtime and are not typically set by SDK consumers directly.
63
69
 
package/dist/config.d.ts CHANGED
@@ -1,61 +1,30 @@
1
1
  import { n as Site, r as Url, t as Locale } from "./types.js";
2
2
  import { n as DefineConfigOptions, r as defineConfig, t as BaseConfig } from "./schema.js";
3
- import * as react0 from "react";
4
3
  import { ReactNode } from "react";
5
- import * as react_jsx_runtime2 from "react/jsx-runtime";
6
- import * as react_router11 from "react-router";
7
- import { MiddlewareFunction, RouterContextProvider } from "react-router";
4
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
5
+ import * as react_router0 from "react-router";
6
+ import { RouterContextProvider } from "react-router";
8
7
 
9
- //#region src/config/get-config.d.ts
10
-
11
- declare global {
12
- interface Window {
13
- __APP_CONFIG__?: Record<string, unknown>;
14
- }
15
- }
16
- /**
17
- * Get configuration in loaders, actions, and utilities.
18
- *
19
- * Pass context parameter in server loaders/actions.
20
- * Omit context parameter in client loaders (uses window.__APP_CONFIG__).
21
- *
22
- * @param context - Router context for server loaders/actions
23
- * @returns App configuration
24
- */
25
- declare function getConfig<T extends Record<string, unknown> = Record<string, unknown>>(context?: Readonly<RouterContextProvider>): T;
26
- /**
27
- * Get configuration in React components.
28
- *
29
- * Must use this hook (not getConfig) because React Context requires useContext().
30
- *
31
- * @returns App configuration
32
- */
33
- declare function useConfig<T extends Record<string, unknown> = Record<string, unknown>>(): T;
34
- //#endregion
35
8
  //#region src/config/context.d.ts
9
+
36
10
  /**
37
- * Router context for application configuration.
38
- *
39
- * Populated by `createAppConfigMiddleware` with the `app` section of config.
40
- * Accessible in loaders, actions, and middleware via `context.get(appConfigContext)`.
41
- */
42
- declare const appConfigContext: react_router11.RouterContext<Record<string, unknown>>;
43
- /**
44
- * React context for application configuration.
45
- *
46
- * Used by the `useConfig()` hook in React components.
47
- * Populated by `ConfigProvider` in the component tree.
11
+ * Augmentation hook for typing `getConfig()` / `useConfig()` /
12
+ * `appConfigContext`. Templates augment once via `declare module` so call
13
+ * sites don't need a per-call generic. Without augmentation, property
14
+ * accesses type to `unknown`. See README-CONFIG.md for the augmentation
15
+ * snippet and the multi-template caveat.
48
16
  */
49
- declare const ConfigContext: react0.Context<Record<string, unknown> | null>;
17
+ interface AppConfigShape {
18
+ [key: string]: unknown;
19
+ }
50
20
  /**
51
- * Extract the `app` section from a full config object.
52
- *
53
- * @param staticConfig - The full config object (output of `defineConfig()`)
54
- * @returns The `app` section of the config
21
+ * Router context for application configuration. Populated by the template's
22
+ * app-config middleware; read via `context.get(appConfigContext)` in loaders,
23
+ * actions, and other middleware. Returns the augmented `AppConfigShape`.
55
24
  */
56
- declare function createAppConfig<T extends BaseConfig>(staticConfig: T): T['app'];
25
+ declare const appConfigContext: react_router0.RouterContext<AppConfigShape>;
57
26
  interface ConfigProviderProps {
58
- config: Record<string, unknown>;
27
+ config: AppConfigShape;
59
28
  children: ReactNode;
60
29
  }
61
30
  /**
@@ -67,183 +36,26 @@ interface ConfigProviderProps {
67
36
  declare function ConfigProvider({
68
37
  config,
69
38
  children
70
- }: ConfigProviderProps): react_jsx_runtime2.JSX.Element;
71
- //#endregion
72
- //#region src/config/middleware.d.ts
73
- /**
74
- * Create app config middleware for both server and client.
75
- *
76
- * Follows the same factory pattern as `createSiteContextMiddleware`.
77
- *
78
- * The server middleware:
79
- * - Validates required Commerce API fields on first request (one-time)
80
- * - Sets `appConfigContext` in router context with `config.app`
81
- *
82
- * The client middleware:
83
- * - Reads `window.__APP_CONFIG__` (injected during SSR)
84
- * - Sets `appConfigContext` in router context
85
- *
86
- * Environment variables:
87
- * - `SCAPI_PROXY_HOST` (optional): When set, skips `shortCode` validation
88
- * (workspace environments route through a proxy that doesn't require shortCode)
89
- * - `NODE_ENV` (optional): When set to 'test', skips validation entirely
90
- *
91
- * @param config - The full config object (output of `defineConfig()`)
92
- * @returns Object with `server` and `client` middleware functions
93
- *
94
- * @example
95
- * import { createAppConfigMiddleware } from '@salesforce/storefront-next-runtime/config';
96
- * import config from '@/config/server';
97
- *
98
- * const appConfigMiddleware = createAppConfigMiddleware(config);
99
- *
100
- * export const middleware = [appConfigMiddleware.server, ...otherMiddleware];
101
- * export const clientMiddleware = [appConfigMiddleware.client, ...otherClientMiddleware];
102
- */
103
- declare function createAppConfigMiddleware<T extends BaseConfig>(config: T): {
104
- server: MiddlewareFunction<Response>;
105
- client: MiddlewareFunction<Record<string, unknown>>;
106
- };
39
+ }: ConfigProviderProps): react_jsx_runtime0.JSX.Element;
107
40
  //#endregion
108
- //#region src/config/utils.d.ts
109
- /**
110
- * Copyright 2026 Salesforce, Inc.
111
- *
112
- * Licensed under the Apache License, Version 2.0 (the "License");
113
- * you may not use this file except in compliance with the License.
114
- * You may obtain a copy of the License at
115
- *
116
- * http://www.apache.org/licenses/LICENSE-2.0
117
- *
118
- * Unless required by applicable law or agreed to in writing, software
119
- * distributed under the License is distributed on an "AS IS" BASIS,
120
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121
- * See the License for the specific language governing permissions and
122
- * limitations under the License.
123
- */
124
- /**
125
- * Deep merge two objects, with source values overriding target values
126
- * Arrays are replaced, not merged
127
- *
128
- * @param target - The base object
129
- * @param source - The object with values to merge in
130
- * @returns A new merged object
131
- *
132
- * @example
133
- * deepMerge(
134
- * { a: { b: 1, c: 2 } },
135
- * { a: { b: 3, d: 4 } }
136
- * )
137
- * // Returns: { a: { b: 3, c: 2, d: 4 } }
138
- */
139
- declare const deepMerge: <T extends Record<string, unknown>>(target: T, source: Record<string, unknown>) => T;
140
- /**
141
- * Convert a path string with double underscore separators to a nested object
142
- * Normalizes keys to match baseConfig casing (case-insensitive lookup, preserves baseConfig case)
143
- *
144
- * @param path - The path string (e.g., 'app__pages__cart__quantityUpdateDebounce')
145
- * @param value - The value to set at the path
146
- * @param baseConfig - Optional base config for case normalization
147
- * @returns A nested object
148
- *
149
- * @example
150
- * pathToObject('app__pages__cart__maxQuantity', 999)
151
- * // Returns: { app: { pages: { cart: { maxQuantity: 999 } } } }
152
- *
153
- * @example
154
- * // With baseConfig normalization:
155
- * pathToObject('APP__SITE__LOCALE', 'en-GB', { app: { site: { locale: 'en-GB' } } })
156
- * // Returns: { app: { site: { locale: 'en-GB' } } } (normalized to baseConfig casing)
157
- */
158
- declare const pathToObject: (path: string, value: unknown, baseConfig?: Record<string, unknown>) => Record<string, unknown>;
159
- /**
160
- * Parse environment variable value with optimistic JSON parsing
161
- * Tries to parse as JSON first, falls back to string if invalid
162
- * Supports multi-line formatted JSON by normalizing whitespace before parsing
163
- *
164
- * @param varValue - The environment variable value
165
- * @param varName - Optional variable name for better error messages
166
- * @returns The parsed value (JSON type if valid JSON, otherwise string)
167
- *
168
- * @example
169
- * // Primitives
170
- * parseEnvValue('42') // → 42 (number)
171
- * parseEnvValue('true') // → true (boolean)
172
- * parseEnvValue('hello') // → 'hello' (string)
173
- *
174
- * @example
175
- * // Single-line JSON
176
- * parseEnvValue('["Apple","Google"]') // → ['Apple', 'Google'] (array)
177
- * parseEnvValue('{"key":"value"}') // → {key: 'value'} (object)
178
- *
179
- * @example
180
- * // Multi-line formatted JSON (whitespace normalized automatically)
181
- * parseEnvValue('[
182
- * {"id": "en-GB"},
183
- * {"id": "fr-FR"}
184
- * ]') // → [{id: 'en-GB'}, {id: 'fr-FR'}] (array)
185
- */
186
- declare const parseEnvValue: (varValue: string, varName?: string) => unknown;
187
- /**
188
- * Extract all valid paths from a config object (recursively traverses the object structure)
189
- * Returns paths in lowercase with double underscore separators
190
- *
191
- * @param obj - The config object to extract paths from
192
- * @param prefix - Current path prefix (used for recursion)
193
- * @returns Array of valid config paths
194
- *
195
- * @example
196
- * extractValidPaths({ app: { site: { locale: 'en-GB' } } })
197
- * // Returns: ['app__site__locale']
198
- */
199
- declare const extractValidPaths: (obj: unknown, prefix?: string) => string[];
41
+ //#region src/config/get-config.d.ts
42
+ declare global {
43
+ interface Window {
44
+ __APP_CONFIG__?: Record<string, unknown>;
45
+ }
46
+ }
200
47
  /**
201
- * Options for mergeEnvConfig
48
+ * Get configuration in loaders, actions, and utilities. Pass `context` on the
49
+ * server; omit it on the client (reads `window.__APP_CONFIG__`). Returns the
50
+ * augmented `AppConfigShape` — pass an explicit generic only for narrower or
51
+ * unrelated shapes (rare).
202
52
  */
203
- interface MergeEnvConfigOptions {
204
- /**
205
- * Config paths that cannot be overridden by environment variables.
206
- * Paths are matched case-insensitively with double underscore separators.
207
- * Any env var targeting a protected path or a sub-path of it will throw an error.
208
- *
209
- * @example ['app__engagement'] — prevents PUBLIC__app__engagement__* from being set via env
210
- */
211
- protectedPaths?: string[];
212
- }
53
+ declare function getConfig<T extends Record<string, unknown> = AppConfigShape>(context?: Readonly<RouterContextProvider>): T;
213
54
  /**
214
- * Merge environment variables with PUBLIC__ prefix into config.
215
- *
216
- * Uses double underscore (__) to target nested config paths.
217
- * All PUBLIC__ prefixed variables are exposed to the client (bundled into window.__APP_CONFIG__).
218
- *
219
- * Server-only secrets should NEVER use this — read them directly from process.env in server code.
220
- *
221
- * Environment variables:
222
- * - `PUBLIC__<path>` (optional): Override any config path. e.g. `PUBLIC__app__commerce__api__clientId=abc123`
223
- * - `NODE_ENV` (optional): When set to 'development', enables conflict warnings for overlapping paths
224
- *
225
- * @param env - Environment variables object (defaults to process.env)
226
- * @param baseConfig - Optional base config for strict path validation and case normalization
227
- * @param options - Optional configuration including protected paths
228
- * @returns Object with overrides to merge into base config
229
- *
230
- * @example
231
- * // Environment variables:
232
- * // PUBLIC__app__commerce__api__clientId=abc123
233
- * // PUBLIC__app__pages__cart__quantityUpdateDebounce=1000
234
- * // PUBLIC__app__features__socialLogin__providers=["Apple","Google"]
235
- *
236
- * mergeEnvConfig()
237
- * // Returns:
238
- * // {
239
- * // app: {
240
- * // commerce: { api: { clientId: 'abc123' } },
241
- * // pages: { cart: { quantityUpdateDebounce: 1000 } },
242
- * // features: { socialLogin: { providers: ['Apple', 'Google'] } }
243
- * // }
244
- * // }
55
+ * Get configuration in React components (use this instead of `getConfig` —
56
+ * React Context requires `useContext`). Returns the augmented `AppConfigShape`.
245
57
  */
246
- declare const mergeEnvConfig: (env?: Record<string, string | undefined>, baseConfig?: Record<string, unknown>, options?: MergeEnvConfigOptions) => Record<string, unknown>;
58
+ declare function useConfig<T extends Record<string, unknown> = AppConfigShape>(): T;
247
59
  //#endregion
248
- export { type BaseConfig, ConfigContext, ConfigProvider, type DefineConfigOptions, type Locale, type MergeEnvConfigOptions, type Site, type Url, appConfigContext, createAppConfig, createAppConfigMiddleware, deepMerge, defineConfig, extractValidPaths, getConfig, mergeEnvConfig, parseEnvValue, pathToObject, useConfig };
60
+ export { type AppConfigShape, type BaseConfig, ConfigProvider, type DefineConfigOptions, type Locale, type Site, type Url, appConfigContext, defineConfig, getConfig, useConfig };
249
61
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","names":[],"sources":["../src/config/get-config.ts","../src/config/context.tsx","../src/config/middleware.ts","../src/config/utils.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;QA2C8D,MAAA,CAAA;EACvC,UAAA,MAAA,CAAA;IAAT,cAAA,CAAA,EAdW,MAcX,CAAA,MAAA,EAAA,OAAA,CAAA;EACX;;AA+BH;;;;;;;;AC1CA;AASa,iBDAG,SCAU,CAAA,UDAU,MCAV,CAAA,MAAA,EAAA,OAAA,CAAA,GDAoC,MCApC,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA,OAAA,CAAA,EDCZ,QCDY,CDCH,qBCDG,CAAA,CAAA,EDEvB,CCFuB;AAS1B;;;;;AAEC;AAaD;AAAiC,iBDSjB,SCTiB,CAAA,UDSG,MCTH,CAAA,MAAA,EAAA,OAAA,CAAA,GDS6B,MCT7B,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA,CAAA,EDSyD,CCTzD;;;;;;;;;AAjCpB,cAAA,gBAAgB,EAAA,cAAA,CAAA,aAAA,CAAA,MAAA,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;AAS7B;AASA;;;;;AAIU,cAbG,aAagB,EAbH,MAAA,CAAA,OAeH,CAfG,MAeH,CAAA,MAAA,EAAA,OAAA,CAAA,GAAA,IAAA,CAAA;AASvB;;;;;;iBAfgB,0BAA0B,0BAA0B,IAAI;UAI9D,mBAAA;UACE;ECPI,QAAA,EDQF,SCRE;;;;;;;;iBDiBA,cAAA;;;GAAqC,sBAAmB,kBAAA,CAAA,GAAA,CAAA;;;;;;;ADSxE;;;;;;;;AC1CA;AASA;AASA;;;;;AAEC;AAaD;;;;;;;;;ACjBA;AAAoD,iBAApC,yBAAoC,CAAA,UAAA,UAAA,CAAA,CAAA,MAAA,EACxC,CADwC,CAAA,EAAA;EACxC,MAAA,EAEA,kBAFA,CAEmB,QAFnB,CAAA;EAEmB,MAAA,EACnB,kBADmB,CACA,MADA,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;CAAnB;;;;;;;;;;;;AF5B8C;;;;;AAkB1D;;;;;;;AAiCA;;;;;;;;AC1CA;AASa,cELA,SFKmE,EAAA,CAAtD,UELU,MFKV,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA,MAAA,EEL2C,CFK3C,EAAA,MAAA,EELsD,MFKtD,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,GELgF,CFKhF;AAS1B;;;;;AAEC;AAaD;;;;;;;;;ACjBA;;;AAG+B,cCoBlB,YDpBkB,EAAA,CAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,UAAA,CAAA,ECuBd,MDvBc,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,GCwB5B,MDxB4B,CAAA,MAAA,EAAA,OAAA,CAAA;;;;;;;;ACf/B;;;;;;AAmCA;AAqEA;AAqCA;AAiCA;AA4CA;;;;;;;;;;cAlHa;;;;;;;;;;;;;cAqCA;;;;UAiCI,qBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4CJ,uBACJ,iDACQ,mCACH,0BACX"}
1
+ {"version":3,"file":"config.d.ts","names":[],"sources":["../src/config/context.tsx","../src/config/get-config.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;AC4EA;;AAA8D,UDtC7C,cAAA,CCsC6C;EAAmB,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;;;;;;;cD5BpE,kBAAgB,aAAA,CAAA,cAAA;UAWnB,mBAAA;UACE;YACE;;;;;;;;iBASE,cAAA;;;GAAqC,sBAAmB,kBAAA,CAAA,GAAA,CAAA;;;;;ICtCzC,cAAA,CAAA,EAIN,MAJM,CAAA,MAAA,EAAA,OAAA,CAAA;EAAA;;;;AAc/B;;;;AACc,iBADE,SACF,CAAA,UADsB,MACtB,CAAA,MAAA,EAAA,OAAA,CAAA,GADgD,cAChD,CAAA,CAAA,OAAA,CAAA,EAAA,QAAA,CAAS,qBAAT,CAAA,CAAA,EACX,CADW;;;AA6Bd;;AAA8D,iBAA9C,SAA8C,CAAA,UAA1B,MAA0B,CAAA,MAAA,EAAA,OAAA,CAAA,GAAA,cAAA,CAAA,CAAA,CAAA,EAAmB,CAAnB"}
package/dist/config.js CHANGED
@@ -182,18 +182,20 @@ const extractValidPaths = (obj, prefix = "") => {
182
182
  * @returns Object with overrides to merge into base config
183
183
  *
184
184
  * @example
185
- * // Environment variables:
186
- * // PUBLIC__app__commerce__api__clientId=abc123
187
- * // PUBLIC__app__pages__cart__quantityUpdateDebounce=1000
188
- * // PUBLIC__app__features__socialLogin__providers=["Apple","Google"]
185
+ * // Environment variables (template-specific paths shown):
186
+ * // PUBLIC__app__some__nested__value=abc123
187
+ * // PUBLIC__app__some__numericKnob=1000
188
+ * // PUBLIC__app__some__listKnob=["A","B"]
189
189
  *
190
190
  * mergeEnvConfig()
191
191
  * // Returns:
192
192
  * // {
193
193
  * // app: {
194
- * // commerce: { api: { clientId: 'abc123' } },
195
- * // pages: { cart: { quantityUpdateDebounce: 1000 } },
196
- * // features: { socialLogin: { providers: ['Apple', 'Google'] } }
194
+ * // some: {
195
+ * // nested: { value: 'abc123' },
196
+ * // numericKnob: 1000,
197
+ * // listKnob: ['A', 'B']
198
+ * // }
197
199
  * // }
198
200
  * // }
199
201
  */
@@ -214,7 +216,7 @@ const mergeEnvConfig = (env = typeof process !== "undefined" ? process.env : {},
214
216
  const depth = path.split("__").length;
215
217
  if (depth > MAX_DEPTH) throw new Error(`Environment variable "${varName}" exceeds maximum path depth of ${MAX_DEPTH}. Current depth: ${depth}. Consider consolidating with JSON values or reducing nesting levels.`);
216
218
  const normalizedPath = path.toLowerCase();
217
- if (protectedPaths.some((protectedPath) => normalizedPath === protectedPath || normalizedPath.startsWith(`${protectedPath}__`))) throw new Error(`Environment variable "${varName}" attempts to override protected config path "${path}".\n\nThe engagement configuration cannot be overridden via environment variables. Update config.server.ts directly to change engagement settings.`);
219
+ if (protectedPaths.some((protectedPath) => normalizedPath === protectedPath || normalizedPath.startsWith(`${protectedPath}__`))) throw new Error(`Environment variable "${varName}" attempts to override protected config path "${path}".\n\nProtected paths cannot be overridden via environment variables. Update config.server.ts directly, or remove the path from \`protectedPaths\` if env override is intended.`);
218
220
  if (baseConfig && validPaths.length > 0) {
219
221
  if (!validPaths.includes(normalizedPath)) {
220
222
  console.warn(`[Config Warning] Ignoring environment variable "${varName}": Config path "${path}" does not exist in config.server.ts.`);
@@ -256,15 +258,18 @@ const mergeEnvConfig = (env = typeof process !== "undefined" ? process.env : {},
256
258
  /**
257
259
  * Define a type-safe storefront configuration with IDE autocomplete.
258
260
  *
259
- * Automatically merges `PUBLIC__` prefixed environment variables into the config
260
- * at load time. Validates env vars against the base config structure (strict mode
261
- * only allows overriding existing paths).
261
+ * Reads `process.env` at call time and merges any `PUBLIC__`-prefixed
262
+ * variables into the config (validated against the base config structure —
263
+ * env vars targeting paths that don't exist in the base config are ignored
264
+ * with a warning). This is a server-only side effect by design; calling
265
+ * `defineConfig` from a browser bundle silently no-ops because `PUBLIC__`
266
+ * vars are not present in the client environment.
262
267
  *
263
268
  * Environment variables:
264
269
  * - `PUBLIC__<path>` (optional): Override any config path using double underscore separators.
265
- * e.g. `PUBLIC__app__commerce__api__clientId=abc123` maps to `config.app.commerce.api.clientId`
266
- * - `PUBLIC__app__pages__cart__quantityUpdateDebounce=1000` maps to a number (optimistic JSON parsing)
267
- * - `PUBLIC__app__features__socialLogin__providers=["Apple","Google"]` maps to an array
270
+ * e.g. `PUBLIC__app__some__nested__value=abc123` maps to `config.app.some.nested.value`
271
+ * - JSON values are parsed optimistically: numbers, booleans, arrays, and objects all work.
272
+ * `PUBLIC__app__features__providers=["A","B"]` parses to an array.
268
273
  *
269
274
  * @param config - The base configuration object with all defaults
270
275
  * @param options - Optional settings (e.g., protectedPaths to prevent env var overrides)
@@ -277,10 +282,9 @@ const mergeEnvConfig = (env = typeof process !== "undefined" ? process.env : {},
277
282
  * export default defineConfig({
278
283
  * metadata: { projectName: 'My Store', projectSlug: 'my-store' },
279
284
  * app: {
280
- * commerce: { api: { clientId: '', organizationId: '', shortCode: '' }, sites: [] },
281
- * defaultSiteId: 'RefArch',
285
+ * // template-specific shape
282
286
  * },
283
- * }, { protectedPaths: ['app__engagement'] });
287
+ * }, { protectedPaths: ['app__analytics'] });
284
288
  */
285
289
  function defineConfig(config, options) {
286
290
  return deepMerge(config, mergeEnvConfig(process.env, config, { protectedPaths: options?.protectedPaths }));
@@ -289,29 +293,19 @@ function defineConfig(config, options) {
289
293
  //#endregion
290
294
  //#region src/config/context.tsx
291
295
  /**
292
- * Router context for application configuration.
293
- *
294
- * Populated by `createAppConfigMiddleware` with the `app` section of config.
295
- * Accessible in loaders, actions, and middleware via `context.get(appConfigContext)`.
296
+ * Router context for application configuration. Populated by the template's
297
+ * app-config middleware; read via `context.get(appConfigContext)` in loaders,
298
+ * actions, and other middleware. Returns the augmented `AppConfigShape`.
296
299
  */
297
300
  const appConfigContext = createContext$1();
298
301
  /**
299
- * React context for application configuration.
302
+ * Internal React context backing `useConfig()`.
300
303
  *
301
- * Used by the `useConfig()` hook in React components.
302
- * Populated by `ConfigProvider` in the component tree.
304
+ * Not exported from the public barrel components must read config via
305
+ * `useConfig()` so the React tree has a single source of truth.
303
306
  */
304
307
  const ConfigContext = createContext(null);
305
308
  /**
306
- * Extract the `app` section from a full config object.
307
- *
308
- * @param staticConfig - The full config object (output of `defineConfig()`)
309
- * @returns The `app` section of the config
310
- */
311
- function createAppConfig(staticConfig) {
312
- return staticConfig.app;
313
- }
314
- /**
315
309
  * React context provider for application configuration.
316
310
  *
317
311
  * Wrap your component tree with this to enable `useConfig()` in child components.
@@ -327,13 +321,10 @@ function ConfigProvider({ config, children }) {
327
321
  //#endregion
328
322
  //#region src/config/get-config.ts
329
323
  /**
330
- * Get configuration in loaders, actions, and utilities.
331
- *
332
- * Pass context parameter in server loaders/actions.
333
- * Omit context parameter in client loaders (uses window.__APP_CONFIG__).
334
- *
335
- * @param context - Router context for server loaders/actions
336
- * @returns App configuration
324
+ * Get configuration in loaders, actions, and utilities. Pass `context` on the
325
+ * server; omit it on the client (reads `window.__APP_CONFIG__`). Returns the
326
+ * augmented `AppConfigShape` pass an explicit generic only for narrower or
327
+ * unrelated shapes (rare).
337
328
  */
338
329
  function getConfig(context) {
339
330
  if (context) {
@@ -345,11 +336,8 @@ function getConfig(context) {
345
336
  throw new Error("Configuration not available. This can happen if:\n1. Server: Pass context parameter: getConfig(context)\n2. Client: Ensure window.__APP_CONFIG__ was injected during SSR\n3. React component: Use useConfig() hook instead of getConfig()");
346
337
  }
347
338
  /**
348
- * Get configuration in React components.
349
- *
350
- * Must use this hook (not getConfig) because React Context requires useContext().
351
- *
352
- * @returns App configuration
339
+ * Get configuration in React components (use this instead of `getConfig` —
340
+ * React Context requires `useContext`). Returns the augmented `AppConfigShape`.
353
341
  */
354
342
  function useConfig() {
355
343
  const config = useContext(ConfigContext);
@@ -358,75 +346,5 @@ function useConfig() {
358
346
  }
359
347
 
360
348
  //#endregion
361
- //#region src/config/middleware.ts
362
- /**
363
- * Create app config middleware for both server and client.
364
- *
365
- * Follows the same factory pattern as `createSiteContextMiddleware`.
366
- *
367
- * The server middleware:
368
- * - Validates required Commerce API fields on first request (one-time)
369
- * - Sets `appConfigContext` in router context with `config.app`
370
- *
371
- * The client middleware:
372
- * - Reads `window.__APP_CONFIG__` (injected during SSR)
373
- * - Sets `appConfigContext` in router context
374
- *
375
- * Environment variables:
376
- * - `SCAPI_PROXY_HOST` (optional): When set, skips `shortCode` validation
377
- * (workspace environments route through a proxy that doesn't require shortCode)
378
- * - `NODE_ENV` (optional): When set to 'test', skips validation entirely
379
- *
380
- * @param config - The full config object (output of `defineConfig()`)
381
- * @returns Object with `server` and `client` middleware functions
382
- *
383
- * @example
384
- * import { createAppConfigMiddleware } from '@salesforce/storefront-next-runtime/config';
385
- * import config from '@/config/server';
386
- *
387
- * const appConfigMiddleware = createAppConfigMiddleware(config);
388
- *
389
- * export const middleware = [appConfigMiddleware.server, ...otherMiddleware];
390
- * export const clientMiddleware = [appConfigMiddleware.client, ...otherClientMiddleware];
391
- */
392
- function createAppConfigMiddleware(config) {
393
- let validationRun = false;
394
- function validateConfig() {
395
- if (validationRun || process.env.NODE_ENV === "test") return;
396
- const api = config.app.commerce?.api;
397
- const required = {
398
- clientId: api?.clientId ?? "",
399
- organizationId: api?.organizationId ?? ""
400
- };
401
- if (!process.env.SCAPI_PROXY_HOST) required.shortCode = api?.shortCode ?? "";
402
- const missing = Object.entries(required).filter(([_, value]) => !value).map(([key]) => key);
403
- if (missing.length > 0) {
404
- const envVarMap = {
405
- clientId: "PUBLIC__app__commerce__api__clientId",
406
- organizationId: "PUBLIC__app__commerce__api__organizationId",
407
- shortCode: "PUBLIC__app__commerce__api__shortCode"
408
- };
409
- throw new Error(`Missing required Commerce API configuration: ${missing.join(", ")}\n\nSet these environment variables in your MRT deployment or .env file:\n${missing.map((key) => ` ${envVarMap[key]}=your-value`).join("\n")}\n\nExample .env file:\nPUBLIC__app__commerce__api__clientId=your-client-id\nPUBLIC__app__commerce__api__organizationId=your-org-id\nPUBLIC__app__commerce__api__shortCode=your-short-code\n\nSee docs/README-CONFIG.md for complete configuration documentation.`);
410
- }
411
- validationRun = true;
412
- }
413
- const server = ({ context }, next) => {
414
- validateConfig();
415
- context.set(appConfigContext, config.app);
416
- return next();
417
- };
418
- const client = async ({ context }, next) => {
419
- const appConfig = typeof window !== "undefined" ? window.__APP_CONFIG__ : void 0;
420
- if (!appConfig) throw new Error("window.__APP_CONFIG__ not available. Check that server loader is injecting config into HTML via Layout component.");
421
- context.set(appConfigContext, appConfig);
422
- return next();
423
- };
424
- return {
425
- server,
426
- client
427
- };
428
- }
429
-
430
- //#endregion
431
- export { ConfigContext, ConfigProvider, appConfigContext, createAppConfig, createAppConfigMiddleware, deepMerge, defineConfig, extractValidPaths, getConfig, mergeEnvConfig, parseEnvValue, pathToObject, useConfig };
349
+ export { ConfigProvider, appConfigContext, defineConfig, getConfig, useConfig };
432
350
  //# sourceMappingURL=config.js.map