@salesforce/storefront-next-runtime 0.3.0-alpha.0 → 0.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,7 +31,7 @@ function createPatternMatcher(patterns) {
31
31
  }
32
32
 
33
33
  //#endregion
34
- //#region src/multi-site/apply-url-config.ts
34
+ //#region src/site-context/apply-url-config.ts
35
35
  const DEFAULT_EXCLUDED_ROUTES = ["/resource/**", "/action/**"];
36
36
  /**
37
37
  * Separates routes into excluded (stay at root) and included (go under prefix).
@@ -61,11 +61,11 @@ function normalizeRoutePaths(routes) {
61
61
  }));
62
62
  }
63
63
  /**
64
- * Creates the `multi-site-wrapper` parent route entry with the given prefix.
64
+ * Creates the `site-context-wrapper` parent route entry with the given prefix.
65
65
  */
66
66
  function createPrefixWrapper(prefix, children, wrapperFile) {
67
67
  return {
68
- id: "multi-site-wrapper",
68
+ id: "site-context-wrapper",
69
69
  file: wrapperFile,
70
70
  path: prefix.slice(1),
71
71
  children
@@ -97,7 +97,7 @@ function cloneRootIndexRoutes(routes) {
97
97
  return duplicates;
98
98
  }
99
99
  /**
100
- * Applies multi-site URL configuration to a set of route entries.
100
+ * Applies site context URL configuration to a set of route entries.
101
101
  *
102
102
  * Wraps non-excluded routes under a parent route with the configured URL prefix
103
103
  * (e.g. `/:siteId/:localeId`), while keeping excluded routes (action/resource by default)
@@ -1 +1 @@
1
- {"version":3,"file":"apply-url-config.js","names":["prefixPatterns: string[]","excludedRoutes: RouteConfigEntry[]","includedRoutes: RouteConfigEntry[]","duplicates: RouteConfigEntry[]"],"sources":["../src/utils/index.ts","../src/multi-site/apply-url-config.ts"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Creates a matcher function from an array of path patterns.\n * Supports `/**` suffix wildcards (e.g. '/resource/**', '/action/**').\n * Exact paths without wildcards are matched literally.\n */\nexport function createPatternMatcher(patterns: string[]): (path: string) => boolean {\n const exactMatches = new Set<string>();\n const prefixPatterns: string[] = [];\n\n for (const pattern of patterns) {\n if (pattern.endsWith('/**')) {\n prefixPatterns.push(pattern.slice(0, -3));\n } else {\n exactMatches.add(pattern);\n }\n }\n\n return (path: string) => {\n if (exactMatches.has(path)) return true;\n return prefixPatterns.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { RouteConfigEntry } from '@react-router/dev/routes';\nimport { createPatternMatcher } from '../utils';\nimport type { Url } from '../config/types';\n\nconst DEFAULT_EXCLUDED_ROUTES = ['/resource/**', '/action/**'];\n\n/**\n * Separates routes into excluded (stay at root) and included (go under prefix).\n */\nexport function partitionRoutes(\n routes: RouteConfigEntry[],\n excludePatterns: string[]\n): { excludedRoutes: RouteConfigEntry[]; includedRoutes: RouteConfigEntry[] } {\n const isExcluded = createPatternMatcher(excludePatterns);\n const excludedRoutes: RouteConfigEntry[] = [];\n const includedRoutes: RouteConfigEntry[] = [];\n\n for (const route of routes) {\n // Normalize path for matching — ensure leading slash so patterns like '/resource/**' work\n // regardless of whether the route path comes with or without a leading slash.\n // E.g Some routes comes from React Router flatRoutes objects where the path has no leading splash\n const matchPath = route.path?.startsWith('/') ? route.path : `/${route.path}`;\n if (route.path && isExcluded(matchPath)) {\n excludedRoutes.push(route);\n } else {\n includedRoutes.push(route);\n }\n }\n\n return { excludedRoutes, includedRoutes };\n}\n\n/**\n * Normalizes route paths by stripping leading `/` so they're relative under a\n * parent route (React Router requirement).\n */\nexport function normalizeRoutePaths(routes: RouteConfigEntry[]): RouteConfigEntry[] {\n return routes.map((route) => ({\n ...route,\n // Check for leading splash because React Router route object can contain no leading splash for a child route\n // E.g Some routes comes from React Router flatRoutes objects where the path has no leading splash\n path: route.path?.startsWith('/') ? route.path.slice(1) : route.path,\n }));\n}\n\n/**\n * Creates the `multi-site-wrapper` parent route entry with the given prefix.\n */\nexport function createPrefixWrapper(\n prefix: string,\n children: RouteConfigEntry[],\n wrapperFile: string\n): RouteConfigEntry {\n return {\n id: 'multi-site-wrapper',\n file: wrapperFile,\n path: prefix.slice(1),\n children,\n };\n}\n\n/**\n * Finds the root index route (`/`) and duplicates it with its parent layout.\n * Looks at the top level for pathless layouts whose direct children include an index route.\n * e.g. _app (pathless) → _app._index (index: true)\n * Returns: _app--root-duplicate → _app._index--root-duplicate\n */\nexport function cloneRootIndexRoutes(routes: RouteConfigEntry[]): RouteConfigEntry[] {\n const duplicates: RouteConfigEntry[] = [];\n\n for (const route of routes) {\n if (route.index === true) {\n duplicates.push({\n ...route,\n id: `${route.id}--root-duplicate`,\n });\n } else if (!route.path && route.children) {\n const indexChild = route.children.find((child) => child.index === true);\n if (indexChild) {\n duplicates.push({\n ...route,\n id: `${route.id}--root-duplicate`,\n children: [{ ...indexChild, id: `${indexChild.id}--root-duplicate` }],\n });\n }\n }\n }\n\n return duplicates;\n}\n\n/**\n * Applies multi-site URL configuration to a set of route entries.\n *\n * Wraps non-excluded routes under a parent route with the configured URL prefix\n * (e.g. `/:siteId/:localeId`), while keeping excluded routes (action/resource by default)\n * at the root level. The homepage index route (and its parent layout) is always\n * duplicated at `/` so the root URL still serves content.\n *\n * @param options - Configuration for URL customisation.\n * @param options.routes - The flat route entries discovered from the filesystem.\n * @param options.urlConfig - URL customisation configuration (prefix, excludeRoutes).\n * @param options.wrapperFile - Path to the wrapper component file, relative to appDirectory.\n * @returns The transformed route entries with prefix wrapping applied.\n */\nexport function applyUrlConfig(options: {\n routes: RouteConfigEntry[];\n urlConfig?: Url;\n wrapperFile: string;\n}): RouteConfigEntry[] {\n const { routes, urlConfig, wrapperFile } = options;\n if (!urlConfig) return routes;\n if (!urlConfig.prefix?.startsWith('/')) {\n throw new Error(`urlConfig.prefix must start with a leading slash (\"/\"). Received: \"${urlConfig.prefix}\"`);\n }\n if (urlConfig.prefix === '/') return routes;\n\n const excludePatterns = urlConfig.excludeRoutes ?? DEFAULT_EXCLUDED_ROUTES;\n\n const { excludedRoutes, includedRoutes } = partitionRoutes(routes, excludePatterns);\n\n const wrappableRoutes = normalizeRoutePaths(includedRoutes);\n // the route that wraps the included routes under prefix\n const wrapperRoute = createPrefixWrapper(urlConfig.prefix, wrappableRoutes, wrapperFile);\n\n // duplicate the app root index to keep app homepage to server at '/'\n const rootDuplicates = cloneRootIndexRoutes(includedRoutes);\n\n return [...rootDuplicates, wrapperRoute, ...excludedRoutes];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,qBAAqB,UAA+C;CAChF,MAAM,+BAAe,IAAI,KAAa;CACtC,MAAMA,iBAA2B,EAAE;AAEnC,MAAK,MAAM,WAAW,SAClB,KAAI,QAAQ,SAAS,MAAM,CACvB,gBAAe,KAAK,QAAQ,MAAM,GAAG,GAAG,CAAC;KAEzC,cAAa,IAAI,QAAQ;AAIjC,SAAQ,SAAiB;AACrB,MAAI,aAAa,IAAI,KAAK,CAAE,QAAO;AACnC,SAAO,eAAe,MAAM,WAAW,SAAS,UAAU,KAAK,WAAW,GAAG,OAAO,GAAG,CAAC;;;;;;AChBhG,MAAM,0BAA0B,CAAC,gBAAgB,aAAa;;;;AAK9D,SAAgB,gBACZ,QACA,iBAC0E;CAC1E,MAAM,aAAa,qBAAqB,gBAAgB;CACxD,MAAMC,iBAAqC,EAAE;CAC7C,MAAMC,iBAAqC,EAAE;AAE7C,MAAK,MAAM,SAAS,QAAQ;EAIxB,MAAM,YAAY,MAAM,MAAM,WAAW,IAAI,GAAG,MAAM,OAAO,IAAI,MAAM;AACvE,MAAI,MAAM,QAAQ,WAAW,UAAU,CACnC,gBAAe,KAAK,MAAM;MAE1B,gBAAe,KAAK,MAAM;;AAIlC,QAAO;EAAE;EAAgB;EAAgB;;;;;;AAO7C,SAAgB,oBAAoB,QAAgD;AAChF,QAAO,OAAO,KAAK,WAAW;EAC1B,GAAG;EAGH,MAAM,MAAM,MAAM,WAAW,IAAI,GAAG,MAAM,KAAK,MAAM,EAAE,GAAG,MAAM;EACnE,EAAE;;;;;AAMP,SAAgB,oBACZ,QACA,UACA,aACgB;AAChB,QAAO;EACH,IAAI;EACJ,MAAM;EACN,MAAM,OAAO,MAAM,EAAE;EACrB;EACH;;;;;;;;AASL,SAAgB,qBAAqB,QAAgD;CACjF,MAAMC,aAAiC,EAAE;AAEzC,MAAK,MAAM,SAAS,OAChB,KAAI,MAAM,UAAU,KAChB,YAAW,KAAK;EACZ,GAAG;EACH,IAAI,GAAG,MAAM,GAAG;EACnB,CAAC;UACK,CAAC,MAAM,QAAQ,MAAM,UAAU;EACtC,MAAM,aAAa,MAAM,SAAS,MAAM,UAAU,MAAM,UAAU,KAAK;AACvE,MAAI,WACA,YAAW,KAAK;GACZ,GAAG;GACH,IAAI,GAAG,MAAM,GAAG;GAChB,UAAU,CAAC;IAAE,GAAG;IAAY,IAAI,GAAG,WAAW,GAAG;IAAmB,CAAC;GACxE,CAAC;;AAKd,QAAO;;;;;;;;;;;;;;;;AAiBX,SAAgB,eAAe,SAIR;CACnB,MAAM,EAAE,QAAQ,WAAW,gBAAgB;AAC3C,KAAI,CAAC,UAAW,QAAO;AACvB,KAAI,CAAC,UAAU,QAAQ,WAAW,IAAI,CAClC,OAAM,IAAI,MAAM,sEAAsE,UAAU,OAAO,GAAG;AAE9G,KAAI,UAAU,WAAW,IAAK,QAAO;CAIrC,MAAM,EAAE,gBAAgB,mBAAmB,gBAAgB,QAFnC,UAAU,iBAAiB,wBAEgC;CAEnF,MAAM,kBAAkB,oBAAoB,eAAe;CAE3D,MAAM,eAAe,oBAAoB,UAAU,QAAQ,iBAAiB,YAAY;AAKxF,QAAO;EAAC,GAFe,qBAAqB,eAAe;EAEhC;EAAc,GAAG;EAAe"}
1
+ {"version":3,"file":"apply-url-config.js","names":["prefixPatterns: string[]","excludedRoutes: RouteConfigEntry[]","includedRoutes: RouteConfigEntry[]","duplicates: RouteConfigEntry[]"],"sources":["../src/utils/index.ts","../src/site-context/apply-url-config.ts"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Creates a matcher function from an array of path patterns.\n * Supports `/**` suffix wildcards (e.g. '/resource/**', '/action/**').\n * Exact paths without wildcards are matched literally.\n */\nexport function createPatternMatcher(patterns: string[]): (path: string) => boolean {\n const exactMatches = new Set<string>();\n const prefixPatterns: string[] = [];\n\n for (const pattern of patterns) {\n if (pattern.endsWith('/**')) {\n prefixPatterns.push(pattern.slice(0, -3));\n } else {\n exactMatches.add(pattern);\n }\n }\n\n return (path: string) => {\n if (exactMatches.has(path)) return true;\n return prefixPatterns.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { RouteConfigEntry } from '@react-router/dev/routes';\nimport { createPatternMatcher } from '../utils';\nimport type { Url } from '../config/types';\n\nconst DEFAULT_EXCLUDED_ROUTES = ['/resource/**', '/action/**'];\n\n/**\n * Separates routes into excluded (stay at root) and included (go under prefix).\n */\nexport function partitionRoutes(\n routes: RouteConfigEntry[],\n excludePatterns: string[]\n): { excludedRoutes: RouteConfigEntry[]; includedRoutes: RouteConfigEntry[] } {\n const isExcluded = createPatternMatcher(excludePatterns);\n const excludedRoutes: RouteConfigEntry[] = [];\n const includedRoutes: RouteConfigEntry[] = [];\n\n for (const route of routes) {\n // Normalize path for matching — ensure leading slash so patterns like '/resource/**' work\n // regardless of whether the route path comes with or without a leading slash.\n // E.g Some routes comes from React Router flatRoutes objects where the path has no leading splash\n const matchPath = route.path?.startsWith('/') ? route.path : `/${route.path}`;\n if (route.path && isExcluded(matchPath)) {\n excludedRoutes.push(route);\n } else {\n includedRoutes.push(route);\n }\n }\n\n return { excludedRoutes, includedRoutes };\n}\n\n/**\n * Normalizes route paths by stripping leading `/` so they're relative under a\n * parent route (React Router requirement).\n */\nexport function normalizeRoutePaths(routes: RouteConfigEntry[]): RouteConfigEntry[] {\n return routes.map((route) => ({\n ...route,\n // Check for leading splash because React Router route object can contain no leading splash for a child route\n // E.g Some routes comes from React Router flatRoutes objects where the path has no leading splash\n path: route.path?.startsWith('/') ? route.path.slice(1) : route.path,\n }));\n}\n\n/**\n * Creates the `site-context-wrapper` parent route entry with the given prefix.\n */\nexport function createPrefixWrapper(\n prefix: string,\n children: RouteConfigEntry[],\n wrapperFile: string\n): RouteConfigEntry {\n return {\n id: 'site-context-wrapper',\n file: wrapperFile,\n path: prefix.slice(1),\n children,\n };\n}\n\n/**\n * Finds the root index route (`/`) and duplicates it with its parent layout.\n * Looks at the top level for pathless layouts whose direct children include an index route.\n * e.g. _app (pathless) → _app._index (index: true)\n * Returns: _app--root-duplicate → _app._index--root-duplicate\n */\nexport function cloneRootIndexRoutes(routes: RouteConfigEntry[]): RouteConfigEntry[] {\n const duplicates: RouteConfigEntry[] = [];\n\n for (const route of routes) {\n if (route.index === true) {\n duplicates.push({\n ...route,\n id: `${route.id}--root-duplicate`,\n });\n } else if (!route.path && route.children) {\n const indexChild = route.children.find((child) => child.index === true);\n if (indexChild) {\n duplicates.push({\n ...route,\n id: `${route.id}--root-duplicate`,\n children: [{ ...indexChild, id: `${indexChild.id}--root-duplicate` }],\n });\n }\n }\n }\n\n return duplicates;\n}\n\n/**\n * Applies site context URL configuration to a set of route entries.\n *\n * Wraps non-excluded routes under a parent route with the configured URL prefix\n * (e.g. `/:siteId/:localeId`), while keeping excluded routes (action/resource by default)\n * at the root level. The homepage index route (and its parent layout) is always\n * duplicated at `/` so the root URL still serves content.\n *\n * @param options - Configuration for URL customisation.\n * @param options.routes - The flat route entries discovered from the filesystem.\n * @param options.urlConfig - URL customisation configuration (prefix, excludeRoutes).\n * @param options.wrapperFile - Path to the wrapper component file, relative to appDirectory.\n * @returns The transformed route entries with prefix wrapping applied.\n */\nexport function applyUrlConfig(options: {\n routes: RouteConfigEntry[];\n urlConfig?: Url;\n wrapperFile: string;\n}): RouteConfigEntry[] {\n const { routes, urlConfig, wrapperFile } = options;\n if (!urlConfig) return routes;\n if (!urlConfig.prefix?.startsWith('/')) {\n throw new Error(`urlConfig.prefix must start with a leading slash (\"/\"). Received: \"${urlConfig.prefix}\"`);\n }\n if (urlConfig.prefix === '/') return routes;\n\n const excludePatterns = urlConfig.excludeRoutes ?? DEFAULT_EXCLUDED_ROUTES;\n\n const { excludedRoutes, includedRoutes } = partitionRoutes(routes, excludePatterns);\n\n const wrappableRoutes = normalizeRoutePaths(includedRoutes);\n // the route that wraps the included routes under prefix\n const wrapperRoute = createPrefixWrapper(urlConfig.prefix, wrappableRoutes, wrapperFile);\n\n // duplicate the app root index to keep app homepage to server at '/'\n const rootDuplicates = cloneRootIndexRoutes(includedRoutes);\n\n return [...rootDuplicates, wrapperRoute, ...excludedRoutes];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,qBAAqB,UAA+C;CAChF,MAAM,+BAAe,IAAI,KAAa;CACtC,MAAMA,iBAA2B,EAAE;AAEnC,MAAK,MAAM,WAAW,SAClB,KAAI,QAAQ,SAAS,MAAM,CACvB,gBAAe,KAAK,QAAQ,MAAM,GAAG,GAAG,CAAC;KAEzC,cAAa,IAAI,QAAQ;AAIjC,SAAQ,SAAiB;AACrB,MAAI,aAAa,IAAI,KAAK,CAAE,QAAO;AACnC,SAAO,eAAe,MAAM,WAAW,SAAS,UAAU,KAAK,WAAW,GAAG,OAAO,GAAG,CAAC;;;;;;AChBhG,MAAM,0BAA0B,CAAC,gBAAgB,aAAa;;;;AAK9D,SAAgB,gBACZ,QACA,iBAC0E;CAC1E,MAAM,aAAa,qBAAqB,gBAAgB;CACxD,MAAMC,iBAAqC,EAAE;CAC7C,MAAMC,iBAAqC,EAAE;AAE7C,MAAK,MAAM,SAAS,QAAQ;EAIxB,MAAM,YAAY,MAAM,MAAM,WAAW,IAAI,GAAG,MAAM,OAAO,IAAI,MAAM;AACvE,MAAI,MAAM,QAAQ,WAAW,UAAU,CACnC,gBAAe,KAAK,MAAM;MAE1B,gBAAe,KAAK,MAAM;;AAIlC,QAAO;EAAE;EAAgB;EAAgB;;;;;;AAO7C,SAAgB,oBAAoB,QAAgD;AAChF,QAAO,OAAO,KAAK,WAAW;EAC1B,GAAG;EAGH,MAAM,MAAM,MAAM,WAAW,IAAI,GAAG,MAAM,KAAK,MAAM,EAAE,GAAG,MAAM;EACnE,EAAE;;;;;AAMP,SAAgB,oBACZ,QACA,UACA,aACgB;AAChB,QAAO;EACH,IAAI;EACJ,MAAM;EACN,MAAM,OAAO,MAAM,EAAE;EACrB;EACH;;;;;;;;AASL,SAAgB,qBAAqB,QAAgD;CACjF,MAAMC,aAAiC,EAAE;AAEzC,MAAK,MAAM,SAAS,OAChB,KAAI,MAAM,UAAU,KAChB,YAAW,KAAK;EACZ,GAAG;EACH,IAAI,GAAG,MAAM,GAAG;EACnB,CAAC;UACK,CAAC,MAAM,QAAQ,MAAM,UAAU;EACtC,MAAM,aAAa,MAAM,SAAS,MAAM,UAAU,MAAM,UAAU,KAAK;AACvE,MAAI,WACA,YAAW,KAAK;GACZ,GAAG;GACH,IAAI,GAAG,MAAM,GAAG;GAChB,UAAU,CAAC;IAAE,GAAG;IAAY,IAAI,GAAG,WAAW,GAAG;IAAmB,CAAC;GACxE,CAAC;;AAKd,QAAO;;;;;;;;;;;;;;;;AAiBX,SAAgB,eAAe,SAIR;CACnB,MAAM,EAAE,QAAQ,WAAW,gBAAgB;AAC3C,KAAI,CAAC,UAAW,QAAO;AACvB,KAAI,CAAC,UAAU,QAAQ,WAAW,IAAI,CAClC,OAAM,IAAI,MAAM,sEAAsE,UAAU,OAAO,GAAG;AAE9G,KAAI,UAAU,WAAW,IAAK,QAAO;CAIrC,MAAM,EAAE,gBAAgB,mBAAmB,gBAAgB,QAFnC,UAAU,iBAAiB,wBAEgC;CAEnF,MAAM,kBAAkB,oBAAoB,eAAe;CAE3D,MAAM,eAAe,oBAAoB,UAAU,QAAQ,iBAAiB,YAAY;AAKxF,QAAO;EAAC,GAFe,qBAAqB,eAAe;EAEhC;EAAc,GAAG;EAAe"}
package/dist/config.d.ts CHANGED
@@ -3,7 +3,7 @@ import { n as DefineConfigOptions, r as defineConfig, t as BaseConfig } from "./
3
3
  import * as react0 from "react";
4
4
  import { ReactNode } from "react";
5
5
  import * as react_jsx_runtime1 from "react/jsx-runtime";
6
- import * as react_router2 from "react-router";
6
+ import * as react_router0 from "react-router";
7
7
  import { MiddlewareFunction, RouterContextProvider } from "react-router";
8
8
 
9
9
  //#region src/config/get-config.d.ts
@@ -39,7 +39,7 @@ declare function useConfig<T extends Record<string, unknown> = Record<string, un
39
39
  * Populated by `createAppConfigMiddleware` with the `app` section of config.
40
40
  * Accessible in loaders, actions, and middleware via `context.get(appConfigContext)`.
41
41
  */
42
- declare const appConfigContext: react_router2.RouterContext<Record<string, unknown>>;
42
+ declare const appConfigContext: react_router0.RouterContext<Record<string, unknown>>;
43
43
  /**
44
44
  * React context for application configuration.
45
45
  *
@@ -73,7 +73,7 @@ declare function ConfigProvider({
73
73
  /**
74
74
  * Create app config middleware for both server and client.
75
75
  *
76
- * Follows the same factory pattern as `createMultiSiteMiddleware`.
76
+ * Follows the same factory pattern as `createSiteContextMiddleware`.
77
77
  *
78
78
  * The server middleware:
79
79
  * - Validates required Commerce API fields on first request (one-time)
package/dist/config.js CHANGED
@@ -216,7 +216,10 @@ const mergeEnvConfig = (env = typeof process !== "undefined" ? process.env : {},
216
216
  const normalizedPath = path.toLowerCase();
217
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.`);
218
218
  if (baseConfig && validPaths.length > 0) {
219
- if (!validPaths.includes(normalizedPath)) throw new Error(`Invalid environment variable "${varName}": Config path "${path}" does not exist in config.server.ts.\n\nCheck your config.server.ts for available configuration paths, or add this path to your base configuration.`);
219
+ if (!validPaths.includes(normalizedPath)) {
220
+ console.warn(`[Config Warning] Ignoring environment variable "${varName}": Config path "${path}" does not exist in config.server.ts.`);
221
+ continue;
222
+ }
220
223
  }
221
224
  totalValueSize += varValue.length;
222
225
  envVars.push({
@@ -359,7 +362,7 @@ function useConfig() {
359
362
  /**
360
363
  * Create app config middleware for both server and client.
361
364
  *
362
- * Follows the same factory pattern as `createMultiSiteMiddleware`.
365
+ * Follows the same factory pattern as `createSiteContextMiddleware`.
363
366
  *
364
367
  * The server middleware:
365
368
  * - Validates required Commerce API fields on first request (one-time)
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":["result: Record<string, unknown>","configCurrent: unknown","paths: string[]","envVars: EnvVar[]","conflicts: Array<{ parent: string; child: string }>","merged: Record<string, unknown>","createRouterContext","required: Record<string, string>","envVarMap: Record<string, string>","server: MiddlewareFunction<Response>","client: MiddlewareFunction<Record<string, unknown>>"],"sources":["../src/config/utils.ts","../src/config/schema.ts","../src/config/context.tsx","../src/config/get-config.ts","../src/config/middleware.ts"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Type guard to check if value is a plain object (not array, null, or other types)\n */\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n};\n\n/**\n * Deep merge two objects, with source values overriding target values\n * Arrays are replaced, not merged\n *\n * @param target - The base object\n * @param source - The object with values to merge in\n * @returns A new merged object\n *\n * @example\n * deepMerge(\n * { a: { b: 1, c: 2 } },\n * { a: { b: 3, d: 4 } }\n * )\n * // Returns: { a: { b: 3, c: 2, d: 4 } }\n */\nexport const deepMerge = <T extends Record<string, unknown>>(target: T, source: Record<string, unknown>): T => {\n const result: Record<string, unknown> = { ...target };\n\n for (const key in source) {\n const sourceValue = source[key];\n const targetValue = result[key];\n\n if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {\n result[key] = deepMerge(targetValue, sourceValue);\n } else {\n result[key] = sourceValue;\n }\n }\n\n return result as T;\n};\n\n/**\n * Convert a path string with double underscore separators to a nested object\n * Normalizes keys to match baseConfig casing (case-insensitive lookup, preserves baseConfig case)\n *\n * @param path - The path string (e.g., 'app__pages__cart__quantityUpdateDebounce')\n * @param value - The value to set at the path\n * @param baseConfig - Optional base config for case normalization\n * @returns A nested object\n *\n * @example\n * pathToObject('app__pages__cart__maxQuantity', 999)\n * // Returns: { app: { pages: { cart: { maxQuantity: 999 } } } }\n *\n * @example\n * // With baseConfig normalization:\n * pathToObject('APP__SITE__LOCALE', 'en-GB', { app: { site: { locale: 'en-GB' } } })\n * // Returns: { app: { site: { locale: 'en-GB' } } } (normalized to baseConfig casing)\n */\nexport const pathToObject = (\n path: string,\n value: unknown,\n baseConfig?: Record<string, unknown>\n): Record<string, unknown> => {\n const keys = path.split('__');\n const result: Record<string, unknown> = {};\n\n let current = result;\n let configCurrent: unknown = baseConfig;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n\n let normalizedKey = key;\n if (configCurrent && typeof configCurrent === 'object' && !Array.isArray(configCurrent)) {\n const actualKey = Object.keys(configCurrent).find((k) => k.toLowerCase() === key.toLowerCase());\n if (actualKey) {\n normalizedKey = actualKey;\n configCurrent = (configCurrent as Record<string, unknown>)[actualKey];\n } else {\n configCurrent = null;\n }\n }\n\n current[normalizedKey] = {};\n current = current[normalizedKey] as Record<string, unknown>;\n }\n\n const lastKey = keys[keys.length - 1];\n let normalizedLastKey = lastKey;\n if (configCurrent && typeof configCurrent === 'object' && !Array.isArray(configCurrent)) {\n const actualKey = Object.keys(configCurrent).find((k) => k.toLowerCase() === lastKey.toLowerCase());\n if (actualKey) {\n normalizedLastKey = actualKey;\n }\n }\n\n current[normalizedLastKey] = value;\n return result;\n};\n\n/**\n * Parse environment variable value with optimistic JSON parsing\n * Tries to parse as JSON first, falls back to string if invalid\n * Supports multi-line formatted JSON by normalizing whitespace before parsing\n *\n * @param varValue - The environment variable value\n * @param varName - Optional variable name for better error messages\n * @returns The parsed value (JSON type if valid JSON, otherwise string)\n *\n * @example\n * // Primitives\n * parseEnvValue('42') // → 42 (number)\n * parseEnvValue('true') // → true (boolean)\n * parseEnvValue('hello') // → 'hello' (string)\n *\n * @example\n * // Single-line JSON\n * parseEnvValue('[\"Apple\",\"Google\"]') // → ['Apple', 'Google'] (array)\n * parseEnvValue('{\"key\":\"value\"}') // → {key: 'value'} (object)\n *\n * @example\n * // Multi-line formatted JSON (whitespace normalized automatically)\n * parseEnvValue('[\n * {\"id\": \"en-GB\"},\n * {\"id\": \"fr-FR\"}\n * ]') // → [{id: 'en-GB'}, {id: 'fr-FR'}] (array)\n */\nexport const parseEnvValue = (varValue: string, varName?: string): unknown => {\n try {\n return JSON.parse(varValue);\n } catch {\n const trimmed = varValue.trim();\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n try {\n const normalized = varValue.replace(/\\s+/g, ' ').trim();\n return JSON.parse(normalized);\n } catch {\n if (process.env.NODE_ENV === 'development') {\n const preview = varValue.length > 50 ? `${varValue.substring(0, 50)}...` : varValue;\n const varInfo = varName ? ` in \"${varName}\"` : '';\n // eslint-disable-next-line no-console\n console.warn(\n `[Config Warning] Value${varInfo} looks like JSON but failed to parse: \"${preview}\". ` +\n `Using as string instead. Check for syntax errors if this was meant to be JSON.`\n );\n }\n }\n }\n return varValue;\n }\n};\n\n/**\n * Extract all valid paths from a config object (recursively traverses the object structure)\n * Returns paths in lowercase with double underscore separators\n *\n * @param obj - The config object to extract paths from\n * @param prefix - Current path prefix (used for recursion)\n * @returns Array of valid config paths\n *\n * @example\n * extractValidPaths({ app: { site: { locale: 'en-GB' } } })\n * // Returns: ['app__site__locale']\n */\nexport const extractValidPaths = (obj: unknown, prefix = ''): string[] => {\n if (!isPlainObject(obj)) {\n return prefix ? [prefix] : [];\n }\n\n const paths: string[] = [];\n for (const [key, value] of Object.entries(obj)) {\n const normalizedKey = key.toLowerCase();\n const currentPath = prefix ? `${prefix}__${normalizedKey}` : normalizedKey;\n\n if (isPlainObject(value)) {\n paths.push(currentPath); // Allow setting whole object (e.g. PUBLIC__app__commerceAgent)\n // Recursively extract paths from nested objects\n paths.push(...extractValidPaths(value, currentPath));\n } else {\n // Leaf node - this is a valid config path\n paths.push(currentPath);\n }\n }\n\n return paths;\n};\n\ninterface EnvVar {\n name: string;\n path: string;\n value: string;\n depth: number;\n}\n\n/**\n * Options for mergeEnvConfig\n */\nexport interface MergeEnvConfigOptions {\n /**\n * Config paths that cannot be overridden by environment variables.\n * Paths are matched case-insensitively with double underscore separators.\n * Any env var targeting a protected path or a sub-path of it will throw an error.\n *\n * @example ['app__engagement'] — prevents PUBLIC__app__engagement__* from being set via env\n */\n protectedPaths?: string[];\n}\n\n/**\n * Merge environment variables with PUBLIC__ prefix into config.\n *\n * Uses double underscore (__) to target nested config paths.\n * All PUBLIC__ prefixed variables are exposed to the client (bundled into window.__APP_CONFIG__).\n *\n * Server-only secrets should NEVER use this — read them directly from process.env in server code.\n *\n * Environment variables:\n * - `PUBLIC__<path>` (optional): Override any config path. e.g. `PUBLIC__app__commerce__api__clientId=abc123`\n * - `NODE_ENV` (optional): When set to 'development', enables conflict warnings for overlapping paths\n *\n * @param env - Environment variables object (defaults to process.env)\n * @param baseConfig - Optional base config for strict path validation and case normalization\n * @param options - Optional configuration including protected paths\n * @returns Object with overrides to merge into base config\n *\n * @example\n * // Environment variables:\n * // PUBLIC__app__commerce__api__clientId=abc123\n * // PUBLIC__app__pages__cart__quantityUpdateDebounce=1000\n * // PUBLIC__app__features__socialLogin__providers=[\"Apple\",\"Google\"]\n *\n * mergeEnvConfig()\n * // Returns:\n * // {\n * // app: {\n * // commerce: { api: { clientId: 'abc123' } },\n * // pages: { cart: { quantityUpdateDebounce: 1000 } },\n * // features: { socialLogin: { providers: ['Apple', 'Google'] } }\n * // }\n * // }\n */\nexport const mergeEnvConfig = (\n env: Record<string, string | undefined> = typeof process !== 'undefined' ? process.env : {},\n baseConfig?: Record<string, unknown>,\n options?: MergeEnvConfigOptions\n): Record<string, unknown> => {\n const PUBLIC_PREFIX = 'PUBLIC__';\n const MAX_VAR_NAME_LENGTH = 512; // MRT limit: 512 characters\n const MAX_TOTAL_VALUE_SIZE = 32 * 1024; // MRT limit: 32 KB\n const MAX_DEPTH = 10;\n\n const protectedPaths = options?.protectedPaths ?? [];\n const validPaths = baseConfig ? extractValidPaths(baseConfig) : [];\n\n const envVars: EnvVar[] = [];\n let totalValueSize = 0;\n\n for (const [varName, varValue] of Object.entries(env)) {\n if (varValue === undefined || varValue === null || !varName.startsWith(PUBLIC_PREFIX)) continue;\n\n if (varName.length > MAX_VAR_NAME_LENGTH) {\n throw new Error(\n `Environment variable name \"${varName}\" exceeds MRT limit of ${MAX_VAR_NAME_LENGTH} characters. ` +\n `Current length: ${varName.length} characters. ` +\n `Consider using shorter paths or consolidating configuration using JSON values.`\n );\n }\n\n const path = varName.substring(PUBLIC_PREFIX.length);\n\n if (!path) {\n throw new Error(\n `Invalid environment variable \"${varName}\": Path cannot be empty after PUBLIC__ prefix. ` +\n `Expected format: PUBLIC__path__to__value (e.g., PUBLIC__app__site__locale)`\n );\n }\n\n const depth = path.split('__').length;\n if (depth > MAX_DEPTH) {\n throw new Error(\n `Environment variable \"${varName}\" exceeds maximum path depth of ${MAX_DEPTH}. ` +\n `Current depth: ${depth}. ` +\n `Consider consolidating with JSON values or reducing nesting levels.`\n );\n }\n\n const normalizedPath = path.toLowerCase();\n const isProtected = protectedPaths.some(\n (protectedPath) => normalizedPath === protectedPath || normalizedPath.startsWith(`${protectedPath}__`)\n );\n\n if (isProtected) {\n throw new Error(\n `Environment variable \"${varName}\" attempts to override protected config path \"${path}\".\\n\\n` +\n `The engagement configuration cannot be overridden via environment variables. ` +\n `Update config.server.ts directly to change engagement settings.`\n );\n }\n\n if (baseConfig && validPaths.length > 0) {\n if (!validPaths.includes(normalizedPath)) {\n throw new Error(\n `Invalid environment variable \"${varName}\": Config path \"${path}\" does not exist in config.server.ts.\\n\\n` +\n `Check your config.server.ts for available configuration paths, or add this path to your base configuration.`\n );\n }\n }\n\n totalValueSize += varValue.length;\n\n envVars.push({\n name: varName,\n path,\n value: varValue,\n depth: path.split('__').length,\n });\n }\n\n if (totalValueSize > MAX_TOTAL_VALUE_SIZE) {\n throw new Error(\n `Total size of PUBLIC__ environment variable values exceeds MRT limit of ${MAX_TOTAL_VALUE_SIZE} bytes (32 KB). ` +\n `Current size: ${totalValueSize} bytes. ` +\n `Consider consolidating configuration using JSON values to reduce the number of variables, ` +\n `or move non-essential configuration to defaults in config.server.ts.`\n );\n }\n\n envVars.sort((a, b) => a.depth - b.depth);\n\n const conflicts: Array<{ parent: string; child: string }> = [];\n for (let i = 0; i < envVars.length; i++) {\n for (let j = i + 1; j < envVars.length; j++) {\n const shorter = envVars[i].path;\n const longer = envVars[j].path;\n if (longer.startsWith(`${shorter}__`)) {\n conflicts.push({\n parent: envVars[i].name,\n child: envVars[j].name,\n });\n }\n }\n }\n\n if (conflicts.length > 0 && process.env.NODE_ENV === 'development') {\n // eslint-disable-next-line no-console\n console.warn(\n `[Config Warning] Conflicting environment variables detected. More specific paths will override parent paths:\\n${conflicts\n .map((c) => ` ${c.parent} ← overridden by → ${c.child}`)\n .join('\\n')}`\n );\n }\n\n let merged: Record<string, unknown> = {};\n\n for (const envVar of envVars) {\n try {\n const parsedValue = parseEnvValue(envVar.value, envVar.name);\n const pathObject = pathToObject(envVar.path, parsedValue, baseConfig);\n merged = deepMerge(merged, pathObject);\n } catch (error) {\n throw new Error(\n `Failed to process environment variable \"${envVar.name}\" with value \"${envVar.value}\": ` +\n `${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n return merged;\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { deepMerge, mergeEnvConfig } from './utils';\n\n/**\n * Base configuration type for storefront-next projects.\n *\n * Generic parameter `App` represents the template's application config shape.\n * The SDK does not prescribe what fields `app` must contain — templates define\n * their own `AppConfig` type with SCAPI credentials, pages, features, etc.\n * and pass it as `BaseConfig<AppConfig>`.\n *\n * The SDK accesses specific `app` fields (e.g., `commerce.api.clientId`) at\n * runtime via the middleware validation, not via compile-time type constraints.\n *\n * @typeParam App - The template's application config shape (defaults to `Record<string, unknown>`)\n *\n * @example\n * // In the template's types file:\n * type AppConfig = { commerce: { api: {...} }; pages: {...}; features: {...} };\n * type Config = BaseConfig<AppConfig>;\n *\n * // In config.server.ts:\n * export default defineConfig<Config>({ metadata: {...}, app: {...} });\n */\nexport type BaseConfig<App extends Record<string, unknown> = Record<string, unknown>> = {\n metadata: {\n projectName: string;\n projectSlug: string;\n };\n runtime?: {\n defaultMrtProject?: string;\n defaultMrtTarget?: string;\n ssrOnly?: string[];\n ssrShared?: string[];\n ssrParameters?: Record<string, string | number | boolean>;\n };\n app: App;\n};\n\nexport interface DefineConfigOptions {\n /**\n * Config paths that cannot be overridden by environment variables.\n * Paths use double underscore separators and are matched case-insensitively.\n *\n * @example ['app__engagement'] — prevents PUBLIC__app__engagement__* from being set via env\n */\n protectedPaths?: string[];\n}\n\n/**\n * Define a type-safe storefront configuration with IDE autocomplete.\n *\n * Automatically merges `PUBLIC__` prefixed environment variables into the config\n * at load time. Validates env vars against the base config structure (strict mode —\n * only allows overriding existing paths).\n *\n * Environment variables:\n * - `PUBLIC__<path>` (optional): Override any config path using double underscore separators.\n * e.g. `PUBLIC__app__commerce__api__clientId=abc123` maps to `config.app.commerce.api.clientId`\n * - `PUBLIC__app__pages__cart__quantityUpdateDebounce=1000` maps to a number (optimistic JSON parsing)\n * - `PUBLIC__app__features__socialLogin__providers=[\"Apple\",\"Google\"]` maps to an array\n *\n * @param config - The base configuration object with all defaults\n * @param options - Optional settings (e.g., protectedPaths to prevent env var overrides)\n * @returns The config with environment variable overrides merged in\n *\n * @example\n * // In config.server.ts:\n * import { defineConfig } from '@salesforce/storefront-next-runtime/config';\n *\n * export default defineConfig({\n * metadata: { projectName: 'My Store', projectSlug: 'my-store' },\n * app: {\n * commerce: { api: { clientId: '', organizationId: '', shortCode: '' }, sites: [] },\n * defaultSiteId: 'RefArch',\n * },\n * }, { protectedPaths: ['app__engagement'] });\n */\nexport function defineConfig<T extends BaseConfig>(config: T, options?: DefineConfigOptions): T {\n const envOverrides = mergeEnvConfig(process.env, config as unknown as Record<string, unknown>, {\n protectedPaths: options?.protectedPaths,\n });\n return deepMerge(config, envOverrides);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Configuration Context and Provider\n *\n * Provides configuration access throughout the application using React Router's\n * context system. Supports both server and client rendering with proper hydration.\n */\n\nimport { createContext, type ReactNode } from 'react';\nimport { createContext as createRouterContext } from 'react-router';\nimport type { BaseConfig } from './schema';\n\n/**\n * Router context for application configuration.\n *\n * Populated by `createAppConfigMiddleware` with the `app` section of config.\n * Accessible in loaders, actions, and middleware via `context.get(appConfigContext)`.\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const appConfigContext = createRouterContext<Record<string, unknown>>();\n\n/**\n * React context for application configuration.\n *\n * Used by the `useConfig()` hook in React components.\n * Populated by `ConfigProvider` in the component tree.\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const ConfigContext = createContext<Record<string, unknown> | null>(null);\n\n/**\n * Extract the `app` section from a full config object.\n *\n * @param staticConfig - The full config object (output of `defineConfig()`)\n * @returns The `app` section of the config\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport function createAppConfig<T extends BaseConfig>(staticConfig: T): T['app'] {\n return staticConfig.app;\n}\n\ninterface ConfigProviderProps {\n config: Record<string, unknown>;\n children: ReactNode;\n}\n\n/**\n * React context provider for application configuration.\n *\n * Wrap your component tree with this to enable `useConfig()` in child components.\n * Typically placed in the root layout component.\n */\nexport function ConfigProvider({ config, children }: ConfigProviderProps) {\n return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Configuration access for loaders, actions, utilities, and React components.\n *\n * Two functions:\n * - `getConfig()` — For loaders, actions, and utilities\n * - `useConfig()` — For React components (hook required for React Context)\n */\n\nimport { useContext } from 'react';\nimport type { RouterContextProvider } from 'react-router';\nimport { ConfigContext, appConfigContext } from './context';\n\ndeclare global {\n interface Window {\n __APP_CONFIG__?: Record<string, unknown>;\n }\n}\n\n/**\n * Get configuration in loaders, actions, and utilities.\n *\n * Pass context parameter in server loaders/actions.\n * Omit context parameter in client loaders (uses window.__APP_CONFIG__).\n *\n * @param context - Router context for server loaders/actions\n * @returns App configuration\n */\nexport function getConfig<T extends Record<string, unknown> = Record<string, unknown>>(\n context?: Readonly<RouterContextProvider>\n): T {\n if (context) {\n const config = context.get(appConfigContext);\n if (!config) {\n throw new Error(\n 'Configuration not available in router context. ' +\n 'Ensure appConfigMiddleware.server runs before other middleware.'\n );\n }\n return config as T;\n }\n\n if (typeof window !== 'undefined' && window.__APP_CONFIG__) {\n return window.__APP_CONFIG__ as T;\n }\n\n throw new Error(\n 'Configuration not available. This can happen if:\\n' +\n '1. Server: Pass context parameter: getConfig(context)\\n' +\n '2. Client: Ensure window.__APP_CONFIG__ was injected during SSR\\n' +\n '3. React component: Use useConfig() hook instead of getConfig()'\n );\n}\n\n/**\n * Get configuration in React components.\n *\n * Must use this hook (not getConfig) because React Context requires useContext().\n *\n * @returns App configuration\n */\nexport function useConfig<T extends Record<string, unknown> = Record<string, unknown>>(): T {\n const config = useContext(ConfigContext);\n if (!config) {\n throw new Error(\n 'useConfig must be used within ConfigProvider. ' +\n 'Ensure ConfigProvider wraps your component tree in root.tsx'\n );\n }\n return config as T;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { MiddlewareFunction } from 'react-router';\nimport { appConfigContext } from './context';\nimport type { BaseConfig } from './schema';\n\n/**\n * Create app config middleware for both server and client.\n *\n * Follows the same factory pattern as `createMultiSiteMiddleware`.\n *\n * The server middleware:\n * - Validates required Commerce API fields on first request (one-time)\n * - Sets `appConfigContext` in router context with `config.app`\n *\n * The client middleware:\n * - Reads `window.__APP_CONFIG__` (injected during SSR)\n * - Sets `appConfigContext` in router context\n *\n * Environment variables:\n * - `SCAPI_PROXY_HOST` (optional): When set, skips `shortCode` validation\n * (workspace environments route through a proxy that doesn't require shortCode)\n * - `NODE_ENV` (optional): When set to 'test', skips validation entirely\n *\n * @param config - The full config object (output of `defineConfig()`)\n * @returns Object with `server` and `client` middleware functions\n *\n * @example\n * import { createAppConfigMiddleware } from '@salesforce/storefront-next-runtime/config';\n * import config from '@/config/server';\n *\n * const appConfigMiddleware = createAppConfigMiddleware(config);\n *\n * export const middleware = [appConfigMiddleware.server, ...otherMiddleware];\n * export const clientMiddleware = [appConfigMiddleware.client, ...otherClientMiddleware];\n */\nexport function createAppConfigMiddleware<T extends BaseConfig>(\n config: T\n): {\n server: MiddlewareFunction<Response>;\n client: MiddlewareFunction<Record<string, unknown>>;\n} {\n let validationRun = false;\n\n function validateConfig(): void {\n if (validationRun || process.env.NODE_ENV === 'test') {\n return;\n }\n\n const api = (config.app as Record<string, unknown> & { commerce?: { api?: Record<string, string> } }).commerce\n ?.api;\n\n const required: Record<string, string> = {\n clientId: api?.clientId ?? '',\n organizationId: api?.organizationId ?? '',\n };\n\n if (!process.env.SCAPI_PROXY_HOST) {\n required.shortCode = api?.shortCode ?? '';\n }\n\n const missing = Object.entries(required)\n .filter(([_, value]) => !value)\n .map(([key]) => key);\n\n if (missing.length > 0) {\n const envVarMap: Record<string, string> = {\n clientId: 'PUBLIC__app__commerce__api__clientId',\n organizationId: 'PUBLIC__app__commerce__api__organizationId',\n shortCode: 'PUBLIC__app__commerce__api__shortCode',\n };\n\n throw new Error(\n `Missing required Commerce API configuration: ${missing.join(', ')}\\n\\n` +\n `Set these environment variables in your MRT deployment or .env file:\\n${missing\n .map((key) => ` ${envVarMap[key]}=your-value`)\n .join('\\n')}\\n\\n` +\n `Example .env file:\\n` +\n `PUBLIC__app__commerce__api__clientId=your-client-id\\n` +\n `PUBLIC__app__commerce__api__organizationId=your-org-id\\n` +\n `PUBLIC__app__commerce__api__shortCode=your-short-code\\n\\n` +\n `See docs/README-CONFIG.md for complete configuration documentation.`\n );\n }\n\n validationRun = true;\n }\n\n const server: MiddlewareFunction<Response> = ({ context }, next) => {\n validateConfig();\n context.set(appConfigContext, config.app);\n return next();\n };\n\n const client: MiddlewareFunction<Record<string, unknown>> = async ({ context }, next) => {\n const appConfig = typeof window !== 'undefined' ? window.__APP_CONFIG__ : undefined;\n\n if (!appConfig) {\n throw new Error(\n 'window.__APP_CONFIG__ not available. ' +\n 'Check that server loader is injecting config into HTML via Layout component.'\n );\n }\n\n context.set(appConfigContext, appConfig);\n\n return next();\n };\n\n return { server, client };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config.js","names":["result: Record<string, unknown>","configCurrent: unknown","paths: string[]","envVars: EnvVar[]","conflicts: Array<{ parent: string; child: string }>","merged: Record<string, unknown>","createRouterContext","required: Record<string, string>","envVarMap: Record<string, string>","server: MiddlewareFunction<Response>","client: MiddlewareFunction<Record<string, unknown>>"],"sources":["../src/config/utils.ts","../src/config/schema.ts","../src/config/context.tsx","../src/config/get-config.ts","../src/config/middleware.ts"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Type guard to check if value is a plain object (not array, null, or other types)\n */\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n};\n\n/**\n * Deep merge two objects, with source values overriding target values\n * Arrays are replaced, not merged\n *\n * @param target - The base object\n * @param source - The object with values to merge in\n * @returns A new merged object\n *\n * @example\n * deepMerge(\n * { a: { b: 1, c: 2 } },\n * { a: { b: 3, d: 4 } }\n * )\n * // Returns: { a: { b: 3, c: 2, d: 4 } }\n */\nexport const deepMerge = <T extends Record<string, unknown>>(target: T, source: Record<string, unknown>): T => {\n const result: Record<string, unknown> = { ...target };\n\n for (const key in source) {\n const sourceValue = source[key];\n const targetValue = result[key];\n\n if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {\n result[key] = deepMerge(targetValue, sourceValue);\n } else {\n result[key] = sourceValue;\n }\n }\n\n return result as T;\n};\n\n/**\n * Convert a path string with double underscore separators to a nested object\n * Normalizes keys to match baseConfig casing (case-insensitive lookup, preserves baseConfig case)\n *\n * @param path - The path string (e.g., 'app__pages__cart__quantityUpdateDebounce')\n * @param value - The value to set at the path\n * @param baseConfig - Optional base config for case normalization\n * @returns A nested object\n *\n * @example\n * pathToObject('app__pages__cart__maxQuantity', 999)\n * // Returns: { app: { pages: { cart: { maxQuantity: 999 } } } }\n *\n * @example\n * // With baseConfig normalization:\n * pathToObject('APP__SITE__LOCALE', 'en-GB', { app: { site: { locale: 'en-GB' } } })\n * // Returns: { app: { site: { locale: 'en-GB' } } } (normalized to baseConfig casing)\n */\nexport const pathToObject = (\n path: string,\n value: unknown,\n baseConfig?: Record<string, unknown>\n): Record<string, unknown> => {\n const keys = path.split('__');\n const result: Record<string, unknown> = {};\n\n let current = result;\n let configCurrent: unknown = baseConfig;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n\n let normalizedKey = key;\n if (configCurrent && typeof configCurrent === 'object' && !Array.isArray(configCurrent)) {\n const actualKey = Object.keys(configCurrent).find((k) => k.toLowerCase() === key.toLowerCase());\n if (actualKey) {\n normalizedKey = actualKey;\n configCurrent = (configCurrent as Record<string, unknown>)[actualKey];\n } else {\n configCurrent = null;\n }\n }\n\n current[normalizedKey] = {};\n current = current[normalizedKey] as Record<string, unknown>;\n }\n\n const lastKey = keys[keys.length - 1];\n let normalizedLastKey = lastKey;\n if (configCurrent && typeof configCurrent === 'object' && !Array.isArray(configCurrent)) {\n const actualKey = Object.keys(configCurrent).find((k) => k.toLowerCase() === lastKey.toLowerCase());\n if (actualKey) {\n normalizedLastKey = actualKey;\n }\n }\n\n current[normalizedLastKey] = value;\n return result;\n};\n\n/**\n * Parse environment variable value with optimistic JSON parsing\n * Tries to parse as JSON first, falls back to string if invalid\n * Supports multi-line formatted JSON by normalizing whitespace before parsing\n *\n * @param varValue - The environment variable value\n * @param varName - Optional variable name for better error messages\n * @returns The parsed value (JSON type if valid JSON, otherwise string)\n *\n * @example\n * // Primitives\n * parseEnvValue('42') // → 42 (number)\n * parseEnvValue('true') // → true (boolean)\n * parseEnvValue('hello') // → 'hello' (string)\n *\n * @example\n * // Single-line JSON\n * parseEnvValue('[\"Apple\",\"Google\"]') // → ['Apple', 'Google'] (array)\n * parseEnvValue('{\"key\":\"value\"}') // → {key: 'value'} (object)\n *\n * @example\n * // Multi-line formatted JSON (whitespace normalized automatically)\n * parseEnvValue('[\n * {\"id\": \"en-GB\"},\n * {\"id\": \"fr-FR\"}\n * ]') // → [{id: 'en-GB'}, {id: 'fr-FR'}] (array)\n */\nexport const parseEnvValue = (varValue: string, varName?: string): unknown => {\n try {\n return JSON.parse(varValue);\n } catch {\n const trimmed = varValue.trim();\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n try {\n const normalized = varValue.replace(/\\s+/g, ' ').trim();\n return JSON.parse(normalized);\n } catch {\n if (process.env.NODE_ENV === 'development') {\n const preview = varValue.length > 50 ? `${varValue.substring(0, 50)}...` : varValue;\n const varInfo = varName ? ` in \"${varName}\"` : '';\n // eslint-disable-next-line no-console\n console.warn(\n `[Config Warning] Value${varInfo} looks like JSON but failed to parse: \"${preview}\". ` +\n `Using as string instead. Check for syntax errors if this was meant to be JSON.`\n );\n }\n }\n }\n return varValue;\n }\n};\n\n/**\n * Extract all valid paths from a config object (recursively traverses the object structure)\n * Returns paths in lowercase with double underscore separators\n *\n * @param obj - The config object to extract paths from\n * @param prefix - Current path prefix (used for recursion)\n * @returns Array of valid config paths\n *\n * @example\n * extractValidPaths({ app: { site: { locale: 'en-GB' } } })\n * // Returns: ['app__site__locale']\n */\nexport const extractValidPaths = (obj: unknown, prefix = ''): string[] => {\n if (!isPlainObject(obj)) {\n return prefix ? [prefix] : [];\n }\n\n const paths: string[] = [];\n for (const [key, value] of Object.entries(obj)) {\n const normalizedKey = key.toLowerCase();\n const currentPath = prefix ? `${prefix}__${normalizedKey}` : normalizedKey;\n\n if (isPlainObject(value)) {\n paths.push(currentPath); // Allow setting whole object (e.g. PUBLIC__app__commerceAgent)\n // Recursively extract paths from nested objects\n paths.push(...extractValidPaths(value, currentPath));\n } else {\n // Leaf node - this is a valid config path\n paths.push(currentPath);\n }\n }\n\n return paths;\n};\n\ninterface EnvVar {\n name: string;\n path: string;\n value: string;\n depth: number;\n}\n\n/**\n * Options for mergeEnvConfig\n */\nexport interface MergeEnvConfigOptions {\n /**\n * Config paths that cannot be overridden by environment variables.\n * Paths are matched case-insensitively with double underscore separators.\n * Any env var targeting a protected path or a sub-path of it will throw an error.\n *\n * @example ['app__engagement'] — prevents PUBLIC__app__engagement__* from being set via env\n */\n protectedPaths?: string[];\n}\n\n/**\n * Merge environment variables with PUBLIC__ prefix into config.\n *\n * Uses double underscore (__) to target nested config paths.\n * All PUBLIC__ prefixed variables are exposed to the client (bundled into window.__APP_CONFIG__).\n *\n * Server-only secrets should NEVER use this — read them directly from process.env in server code.\n *\n * Environment variables:\n * - `PUBLIC__<path>` (optional): Override any config path. e.g. `PUBLIC__app__commerce__api__clientId=abc123`\n * - `NODE_ENV` (optional): When set to 'development', enables conflict warnings for overlapping paths\n *\n * @param env - Environment variables object (defaults to process.env)\n * @param baseConfig - Optional base config for strict path validation and case normalization\n * @param options - Optional configuration including protected paths\n * @returns Object with overrides to merge into base config\n *\n * @example\n * // Environment variables:\n * // PUBLIC__app__commerce__api__clientId=abc123\n * // PUBLIC__app__pages__cart__quantityUpdateDebounce=1000\n * // PUBLIC__app__features__socialLogin__providers=[\"Apple\",\"Google\"]\n *\n * mergeEnvConfig()\n * // Returns:\n * // {\n * // app: {\n * // commerce: { api: { clientId: 'abc123' } },\n * // pages: { cart: { quantityUpdateDebounce: 1000 } },\n * // features: { socialLogin: { providers: ['Apple', 'Google'] } }\n * // }\n * // }\n */\nexport const mergeEnvConfig = (\n env: Record<string, string | undefined> = typeof process !== 'undefined' ? process.env : {},\n baseConfig?: Record<string, unknown>,\n options?: MergeEnvConfigOptions\n): Record<string, unknown> => {\n const PUBLIC_PREFIX = 'PUBLIC__';\n const MAX_VAR_NAME_LENGTH = 512; // MRT limit: 512 characters\n const MAX_TOTAL_VALUE_SIZE = 32 * 1024; // MRT limit: 32 KB\n const MAX_DEPTH = 10;\n\n const protectedPaths = options?.protectedPaths ?? [];\n const validPaths = baseConfig ? extractValidPaths(baseConfig) : [];\n\n const envVars: EnvVar[] = [];\n let totalValueSize = 0;\n\n for (const [varName, varValue] of Object.entries(env)) {\n if (varValue === undefined || varValue === null || !varName.startsWith(PUBLIC_PREFIX)) continue;\n\n if (varName.length > MAX_VAR_NAME_LENGTH) {\n throw new Error(\n `Environment variable name \"${varName}\" exceeds MRT limit of ${MAX_VAR_NAME_LENGTH} characters. ` +\n `Current length: ${varName.length} characters. ` +\n `Consider using shorter paths or consolidating configuration using JSON values.`\n );\n }\n\n const path = varName.substring(PUBLIC_PREFIX.length);\n\n if (!path) {\n throw new Error(\n `Invalid environment variable \"${varName}\": Path cannot be empty after PUBLIC__ prefix. ` +\n `Expected format: PUBLIC__path__to__value (e.g., PUBLIC__app__site__locale)`\n );\n }\n\n const depth = path.split('__').length;\n if (depth > MAX_DEPTH) {\n throw new Error(\n `Environment variable \"${varName}\" exceeds maximum path depth of ${MAX_DEPTH}. ` +\n `Current depth: ${depth}. ` +\n `Consider consolidating with JSON values or reducing nesting levels.`\n );\n }\n\n const normalizedPath = path.toLowerCase();\n const isProtected = protectedPaths.some(\n (protectedPath) => normalizedPath === protectedPath || normalizedPath.startsWith(`${protectedPath}__`)\n );\n\n if (isProtected) {\n throw new Error(\n `Environment variable \"${varName}\" attempts to override protected config path \"${path}\".\\n\\n` +\n `The engagement configuration cannot be overridden via environment variables. ` +\n `Update config.server.ts directly to change engagement settings.`\n );\n }\n\n if (baseConfig && validPaths.length > 0) {\n if (!validPaths.includes(normalizedPath)) {\n // eslint-disable-next-line no-console\n console.warn(\n `[Config Warning] Ignoring environment variable \"${varName}\": Config path \"${path}\" does not exist in config.server.ts.`\n );\n continue;\n }\n }\n\n totalValueSize += varValue.length;\n\n envVars.push({\n name: varName,\n path,\n value: varValue,\n depth: path.split('__').length,\n });\n }\n\n if (totalValueSize > MAX_TOTAL_VALUE_SIZE) {\n throw new Error(\n `Total size of PUBLIC__ environment variable values exceeds MRT limit of ${MAX_TOTAL_VALUE_SIZE} bytes (32 KB). ` +\n `Current size: ${totalValueSize} bytes. ` +\n `Consider consolidating configuration using JSON values to reduce the number of variables, ` +\n `or move non-essential configuration to defaults in config.server.ts.`\n );\n }\n\n envVars.sort((a, b) => a.depth - b.depth);\n\n const conflicts: Array<{ parent: string; child: string }> = [];\n for (let i = 0; i < envVars.length; i++) {\n for (let j = i + 1; j < envVars.length; j++) {\n const shorter = envVars[i].path;\n const longer = envVars[j].path;\n if (longer.startsWith(`${shorter}__`)) {\n conflicts.push({\n parent: envVars[i].name,\n child: envVars[j].name,\n });\n }\n }\n }\n\n if (conflicts.length > 0 && process.env.NODE_ENV === 'development') {\n // eslint-disable-next-line no-console\n console.warn(\n `[Config Warning] Conflicting environment variables detected. More specific paths will override parent paths:\\n${conflicts\n .map((c) => ` ${c.parent} ← overridden by → ${c.child}`)\n .join('\\n')}`\n );\n }\n\n let merged: Record<string, unknown> = {};\n\n for (const envVar of envVars) {\n try {\n const parsedValue = parseEnvValue(envVar.value, envVar.name);\n const pathObject = pathToObject(envVar.path, parsedValue, baseConfig);\n merged = deepMerge(merged, pathObject);\n } catch (error) {\n throw new Error(\n `Failed to process environment variable \"${envVar.name}\" with value \"${envVar.value}\": ` +\n `${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n return merged;\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { deepMerge, mergeEnvConfig } from './utils';\n\n/**\n * Base configuration type for storefront-next projects.\n *\n * Generic parameter `App` represents the template's application config shape.\n * The SDK does not prescribe what fields `app` must contain — templates define\n * their own `AppConfig` type with SCAPI credentials, pages, features, etc.\n * and pass it as `BaseConfig<AppConfig>`.\n *\n * The SDK accesses specific `app` fields (e.g., `commerce.api.clientId`) at\n * runtime via the middleware validation, not via compile-time type constraints.\n *\n * @typeParam App - The template's application config shape (defaults to `Record<string, unknown>`)\n *\n * @example\n * // In the template's types file:\n * type AppConfig = { commerce: { api: {...} }; pages: {...}; features: {...} };\n * type Config = BaseConfig<AppConfig>;\n *\n * // In config.server.ts:\n * export default defineConfig<Config>({ metadata: {...}, app: {...} });\n */\nexport type BaseConfig<App extends Record<string, unknown> = Record<string, unknown>> = {\n metadata: {\n projectName: string;\n projectSlug: string;\n };\n runtime?: {\n defaultMrtProject?: string;\n defaultMrtTarget?: string;\n ssrOnly?: string[];\n ssrShared?: string[];\n ssrParameters?: Record<string, string | number | boolean>;\n };\n app: App;\n};\n\nexport interface DefineConfigOptions {\n /**\n * Config paths that cannot be overridden by environment variables.\n * Paths use double underscore separators and are matched case-insensitively.\n *\n * @example ['app__engagement'] — prevents PUBLIC__app__engagement__* from being set via env\n */\n protectedPaths?: string[];\n}\n\n/**\n * Define a type-safe storefront configuration with IDE autocomplete.\n *\n * Automatically merges `PUBLIC__` prefixed environment variables into the config\n * at load time. Validates env vars against the base config structure (strict mode —\n * only allows overriding existing paths).\n *\n * Environment variables:\n * - `PUBLIC__<path>` (optional): Override any config path using double underscore separators.\n * e.g. `PUBLIC__app__commerce__api__clientId=abc123` maps to `config.app.commerce.api.clientId`\n * - `PUBLIC__app__pages__cart__quantityUpdateDebounce=1000` maps to a number (optimistic JSON parsing)\n * - `PUBLIC__app__features__socialLogin__providers=[\"Apple\",\"Google\"]` maps to an array\n *\n * @param config - The base configuration object with all defaults\n * @param options - Optional settings (e.g., protectedPaths to prevent env var overrides)\n * @returns The config with environment variable overrides merged in\n *\n * @example\n * // In config.server.ts:\n * import { defineConfig } from '@salesforce/storefront-next-runtime/config';\n *\n * export default defineConfig({\n * metadata: { projectName: 'My Store', projectSlug: 'my-store' },\n * app: {\n * commerce: { api: { clientId: '', organizationId: '', shortCode: '' }, sites: [] },\n * defaultSiteId: 'RefArch',\n * },\n * }, { protectedPaths: ['app__engagement'] });\n */\nexport function defineConfig<T extends BaseConfig>(config: T, options?: DefineConfigOptions): T {\n const envOverrides = mergeEnvConfig(process.env, config as unknown as Record<string, unknown>, {\n protectedPaths: options?.protectedPaths,\n });\n return deepMerge(config, envOverrides);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Configuration Context and Provider\n *\n * Provides configuration access throughout the application using React Router's\n * context system. Supports both server and client rendering with proper hydration.\n */\n\nimport { createContext, type ReactNode } from 'react';\nimport { createContext as createRouterContext } from 'react-router';\nimport type { BaseConfig } from './schema';\n\n/**\n * Router context for application configuration.\n *\n * Populated by `createAppConfigMiddleware` with the `app` section of config.\n * Accessible in loaders, actions, and middleware via `context.get(appConfigContext)`.\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const appConfigContext = createRouterContext<Record<string, unknown>>();\n\n/**\n * React context for application configuration.\n *\n * Used by the `useConfig()` hook in React components.\n * Populated by `ConfigProvider` in the component tree.\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const ConfigContext = createContext<Record<string, unknown> | null>(null);\n\n/**\n * Extract the `app` section from a full config object.\n *\n * @param staticConfig - The full config object (output of `defineConfig()`)\n * @returns The `app` section of the config\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport function createAppConfig<T extends BaseConfig>(staticConfig: T): T['app'] {\n return staticConfig.app;\n}\n\ninterface ConfigProviderProps {\n config: Record<string, unknown>;\n children: ReactNode;\n}\n\n/**\n * React context provider for application configuration.\n *\n * Wrap your component tree with this to enable `useConfig()` in child components.\n * Typically placed in the root layout component.\n */\nexport function ConfigProvider({ config, children }: ConfigProviderProps) {\n return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Configuration access for loaders, actions, utilities, and React components.\n *\n * Two functions:\n * - `getConfig()` — For loaders, actions, and utilities\n * - `useConfig()` — For React components (hook required for React Context)\n */\n\nimport { useContext } from 'react';\nimport type { RouterContextProvider } from 'react-router';\nimport { ConfigContext, appConfigContext } from './context';\n\ndeclare global {\n interface Window {\n __APP_CONFIG__?: Record<string, unknown>;\n }\n}\n\n/**\n * Get configuration in loaders, actions, and utilities.\n *\n * Pass context parameter in server loaders/actions.\n * Omit context parameter in client loaders (uses window.__APP_CONFIG__).\n *\n * @param context - Router context for server loaders/actions\n * @returns App configuration\n */\nexport function getConfig<T extends Record<string, unknown> = Record<string, unknown>>(\n context?: Readonly<RouterContextProvider>\n): T {\n if (context) {\n const config = context.get(appConfigContext);\n if (!config) {\n throw new Error(\n 'Configuration not available in router context. ' +\n 'Ensure appConfigMiddleware.server runs before other middleware.'\n );\n }\n return config as T;\n }\n\n if (typeof window !== 'undefined' && window.__APP_CONFIG__) {\n return window.__APP_CONFIG__ as T;\n }\n\n throw new Error(\n 'Configuration not available. This can happen if:\\n' +\n '1. Server: Pass context parameter: getConfig(context)\\n' +\n '2. Client: Ensure window.__APP_CONFIG__ was injected during SSR\\n' +\n '3. React component: Use useConfig() hook instead of getConfig()'\n );\n}\n\n/**\n * Get configuration in React components.\n *\n * Must use this hook (not getConfig) because React Context requires useContext().\n *\n * @returns App configuration\n */\nexport function useConfig<T extends Record<string, unknown> = Record<string, unknown>>(): T {\n const config = useContext(ConfigContext);\n if (!config) {\n throw new Error(\n 'useConfig must be used within ConfigProvider. ' +\n 'Ensure ConfigProvider wraps your component tree in root.tsx'\n );\n }\n return config as T;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { MiddlewareFunction } from 'react-router';\nimport { appConfigContext } from './context';\nimport type { BaseConfig } from './schema';\n\n/**\n * Create app config middleware for both server and client.\n *\n * Follows the same factory pattern as `createSiteContextMiddleware`.\n *\n * The server middleware:\n * - Validates required Commerce API fields on first request (one-time)\n * - Sets `appConfigContext` in router context with `config.app`\n *\n * The client middleware:\n * - Reads `window.__APP_CONFIG__` (injected during SSR)\n * - Sets `appConfigContext` in router context\n *\n * Environment variables:\n * - `SCAPI_PROXY_HOST` (optional): When set, skips `shortCode` validation\n * (workspace environments route through a proxy that doesn't require shortCode)\n * - `NODE_ENV` (optional): When set to 'test', skips validation entirely\n *\n * @param config - The full config object (output of `defineConfig()`)\n * @returns Object with `server` and `client` middleware functions\n *\n * @example\n * import { createAppConfigMiddleware } from '@salesforce/storefront-next-runtime/config';\n * import config from '@/config/server';\n *\n * const appConfigMiddleware = createAppConfigMiddleware(config);\n *\n * export const middleware = [appConfigMiddleware.server, ...otherMiddleware];\n * export const clientMiddleware = [appConfigMiddleware.client, ...otherClientMiddleware];\n */\nexport function createAppConfigMiddleware<T extends BaseConfig>(\n config: T\n): {\n server: MiddlewareFunction<Response>;\n client: MiddlewareFunction<Record<string, unknown>>;\n} {\n let validationRun = false;\n\n function validateConfig(): void {\n if (validationRun || process.env.NODE_ENV === 'test') {\n return;\n }\n\n const api = (config.app as Record<string, unknown> & { commerce?: { api?: Record<string, string> } }).commerce\n ?.api;\n\n const required: Record<string, string> = {\n clientId: api?.clientId ?? '',\n organizationId: api?.organizationId ?? '',\n };\n\n if (!process.env.SCAPI_PROXY_HOST) {\n required.shortCode = api?.shortCode ?? '';\n }\n\n const missing = Object.entries(required)\n .filter(([_, value]) => !value)\n .map(([key]) => key);\n\n if (missing.length > 0) {\n const envVarMap: Record<string, string> = {\n clientId: 'PUBLIC__app__commerce__api__clientId',\n organizationId: 'PUBLIC__app__commerce__api__organizationId',\n shortCode: 'PUBLIC__app__commerce__api__shortCode',\n };\n\n throw new Error(\n `Missing required Commerce API configuration: ${missing.join(', ')}\\n\\n` +\n `Set these environment variables in your MRT deployment or .env file:\\n${missing\n .map((key) => ` ${envVarMap[key]}=your-value`)\n .join('\\n')}\\n\\n` +\n `Example .env file:\\n` +\n `PUBLIC__app__commerce__api__clientId=your-client-id\\n` +\n `PUBLIC__app__commerce__api__organizationId=your-org-id\\n` +\n `PUBLIC__app__commerce__api__shortCode=your-short-code\\n\\n` +\n `See docs/README-CONFIG.md for complete configuration documentation.`\n );\n }\n\n validationRun = true;\n }\n\n const server: MiddlewareFunction<Response> = ({ context }, next) => {\n validateConfig();\n context.set(appConfigContext, config.app);\n return next();\n };\n\n const client: MiddlewareFunction<Record<string, unknown>> = async ({ context }, next) => {\n const appConfig = typeof window !== 'undefined' ? window.__APP_CONFIG__ : undefined;\n\n if (!appConfig) {\n throw new Error(\n 'window.__APP_CONFIG__ not available. ' +\n 'Check that server loader is injecting config into HTML via Layout component.'\n );\n }\n\n context.set(appConfigContext, appConfig);\n\n return next();\n };\n\n return { server, client };\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { n as ComponentModule, o as FrameworkAdapter } from "./types3.js";
3
3
  import { g as IsomorphicConfiguration } from "./index.js";
4
4
  import { i as RegionDecoratorProps, t as ComponentDecoratorProps } from "./component.types.js";
5
5
  import React$1 from "react";
6
- import * as react_jsx_runtime2 from "react/jsx-runtime";
6
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
7
7
 
8
8
  //#region src/design/react/core/PageDesignerProvider.d.ts
9
9
  type PageDesignerContextType = {
@@ -49,7 +49,7 @@ declare function PageDesignerPageMetadataProvider({
49
49
  children
50
50
  }: React.PropsWithChildren<{
51
51
  page: ShopperExperience.schemas['Page'];
52
- }>): react_jsx_runtime2.JSX.Element;
52
+ }>): react_jsx_runtime0.JSX.Element;
53
53
  //#endregion
54
54
  //#region src/design/react/core/RegionContext.d.ts
55
55
  interface RegionContextType {
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","names":[],"sources":["../src/events/types.ts","../src/events/events.ts","../src/events/mediator.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"events.d.ts","names":[],"sources":["../src/events/types.ts","../src/events/events.ts","../src/events/mediator.ts"],"sourcesContent":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/design/messaging-api/domain-types.ts","../src/design/messaging-api/api-types.ts","../src/design/messaging-api/client.ts","../src/design/messaging-api/host.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/design/messaging-api/domain-types.ts","../src/design/messaging-api/api-types.ts","../src/design/messaging-api/client.ts","../src/design/messaging-api/host.ts"],"sourcesContent":[],"mappings":"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"}
@@ -32,7 +32,8 @@ async function loadConfig() {
32
32
  }).import(configPath);
33
33
  return mod.default ?? mod;
34
34
  } catch (error) {
35
- throw new Error(`[storefront-next-runtime] Found config.server.ts at ${configPath} but failed to import it.`, { cause: error });
35
+ const reason = error instanceof Error ? error.message : String(error);
36
+ throw new Error(`[storefront-next-runtime] Found config.server.ts at ${configPath} but failed to import it.\n${reason}`, { cause: error });
36
37
  }
37
38
  }
38
39
 
@@ -1 +1 @@
1
- {"version":3,"file":"load-config.js","names":[],"sources":["../src/config/load-config.ts"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { BaseConfig } from './schema';\n\n/**\n * Dynamically imports `config.server.ts` from the project root (CWD) and returns\n * the full configuration object. This runs at route discovery time under vite-node\n * (typegen, dev, build), which handles the TS transformation.\n *\n * Uses jiti to transpile TypeScript on the fly, which works regardless of whether\n * the caller runs under vite-node, a plain Node process, or any other runtime.\n * This avoids the fragile assumption that vite-node will intercept dynamic imports\n * from pre-compiled npm packages (it won't — Vite externalizes node_modules).\n *\n * Returns the full config including `metadata`, `runtime`, and `app` sections.\n * Callers that only need `app` can destructure: `const { app } = await loadConfig()`.\n *\n * - If the config file is missing, throws with a clear message.\n * - If the config file exists but fails to import, throws with the original error as cause.\n *\n * @returns The full configuration object.\n * @throws If `config.server.ts` is not found or fails to import.\n */\nexport async function loadConfig<T extends BaseConfig = BaseConfig>(): Promise<T> {\n const configPath = path.resolve(process.cwd(), 'config.server.ts');\n\n if (!fs.existsSync(configPath)) {\n throw new Error(\n `[storefront-next-runtime] config.server.ts is required but not found at ${configPath}. ` +\n `Create this file with defineConfig() to configure your storefront application.`\n );\n }\n\n try {\n const { createJiti } = await import('jiti');\n\n const jiti = createJiti(import.meta.url, {\n fsCache: false,\n interopDefault: true,\n });\n\n const mod = await jiti.import(configPath);\n const config = (mod as Record<string, unknown>).default ?? mod;\n return config as T;\n } catch (error) {\n throw new Error(`[storefront-next-runtime] Found config.server.ts at ${configPath} but failed to import it.`, {\n cause: error,\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsCA,eAAsB,aAA4D;CAC9E,MAAM,aAAa,KAAK,QAAQ,QAAQ,KAAK,EAAE,mBAAmB;AAElE,KAAI,CAAC,GAAG,WAAW,WAAW,CAC1B,OAAM,IAAI,MACN,2EAA2E,WAAW,kFAEzF;AAGL,KAAI;EACA,MAAM,EAAE,eAAe,MAAM,OAAO;EAOpC,MAAM,MAAM,MALC,WAAW,OAAO,KAAK,KAAK;GACrC,SAAS;GACT,gBAAgB;GACnB,CAAC,CAEqB,OAAO,WAAW;AAEzC,SADgB,IAAgC,WAAW;UAEtD,OAAO;AACZ,QAAM,IAAI,MAAM,uDAAuD,WAAW,4BAA4B,EAC1G,OAAO,OACV,CAAC"}
1
+ {"version":3,"file":"load-config.js","names":[],"sources":["../src/config/load-config.ts"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { BaseConfig } from './schema';\n\n/**\n * Dynamically imports `config.server.ts` from the project root (CWD) and returns\n * the full configuration object. This runs at route discovery time under vite-node\n * (typegen, dev, build), which handles the TS transformation.\n *\n * Uses jiti to transpile TypeScript on the fly, which works regardless of whether\n * the caller runs under vite-node, a plain Node process, or any other runtime.\n * This avoids the fragile assumption that vite-node will intercept dynamic imports\n * from pre-compiled npm packages (it won't — Vite externalizes node_modules).\n *\n * Returns the full config including `metadata`, `runtime`, and `app` sections.\n * Callers that only need `app` can destructure: `const { app } = await loadConfig()`.\n *\n * - If the config file is missing, throws with a clear message.\n * - If the config file exists but fails to import, throws with the original error as cause.\n *\n * @returns The full configuration object.\n * @throws If `config.server.ts` is not found or fails to import.\n */\nexport async function loadConfig<T extends BaseConfig = BaseConfig>(): Promise<T> {\n const configPath = path.resolve(process.cwd(), 'config.server.ts');\n\n if (!fs.existsSync(configPath)) {\n throw new Error(\n `[storefront-next-runtime] config.server.ts is required but not found at ${configPath}. ` +\n `Create this file with defineConfig() to configure your storefront application.`\n );\n }\n\n try {\n const { createJiti } = await import('jiti');\n\n const jiti = createJiti(import.meta.url, {\n fsCache: false,\n interopDefault: true,\n });\n\n const mod = await jiti.import(configPath);\n const config = (mod as Record<string, unknown>).default ?? mod;\n return config as T;\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(\n `[storefront-next-runtime] Found config.server.ts at ${configPath} but failed to import it.\\n${reason}`,\n {\n cause: error,\n }\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsCA,eAAsB,aAA4D;CAC9E,MAAM,aAAa,KAAK,QAAQ,QAAQ,KAAK,EAAE,mBAAmB;AAElE,KAAI,CAAC,GAAG,WAAW,WAAW,CAC1B,OAAM,IAAI,MACN,2EAA2E,WAAW,kFAEzF;AAGL,KAAI;EACA,MAAM,EAAE,eAAe,MAAM,OAAO;EAOpC,MAAM,MAAM,MALC,WAAW,OAAO,KAAK,KAAK;GACrC,SAAS;GACT,gBAAgB;GACnB,CAAC,CAEqB,OAAO,WAAW;AAEzC,SADgB,IAAgC,WAAW;UAEtD,OAAO;EACZ,MAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACrE,QAAM,IAAI,MACN,uDAAuD,WAAW,6BAA6B,UAC/F,EACI,OAAO,OACV,CACJ"}
package/dist/routing.d.ts CHANGED
@@ -3,7 +3,7 @@ import { RouteConfigEntry } from "@react-router/dev/routes";
3
3
  //#region src/routing/flat-routes.d.ts
4
4
 
5
5
  /**
6
- * Discovers all file-based routes, merges extension routes, and applies multi-site
6
+ * Discovers all file-based routes, merges extension routes, and applies site context
7
7
  * URL configuration if defined in the project's `config.server.ts`.
8
8
  *
9
9
  * 1. Discover routes from the filesystem using React Router's `flatRoutes`.
package/dist/routing.js CHANGED
@@ -101,7 +101,7 @@ async function discoverExtensionRoutes(ignoredRouteFiles, routes) {
101
101
  }
102
102
  }
103
103
  /**
104
- * Discovers all file-based routes, merges extension routes, and applies multi-site
104
+ * Discovers all file-based routes, merges extension routes, and applies site context
105
105
  * URL configuration if defined in the project's `config.server.ts`.
106
106
  *
107
107
  * 1. Discover routes from the filesystem using React Router's `flatRoutes`.
@@ -1 +1 @@
1
- {"version":3,"file":"routing.js","names":["path","_flatRoutes"],"sources":["../src/routing/merge-routes.ts","../src/routing/flat-routes.ts"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { type RouteConfigEntry } from '@react-router/dev/routes';\n\n/**\n * Find the nearest route by its ID in the route tree\n * @param routes - The route subtree to search\n * @param layoutId - The route ID to find (e.g., \"routes/_app\" or \"routes/_app.account\")\n * @param rootPath - The full route path from the root to the current route (default: '')\n * @returns An object with routes array, routeIndex, and path, or null if not found. Returns exact match if found, otherwise returns route where route.id is a prefix of layoutId\n */\nfunction findNearestRoute(\n routes: RouteConfigEntry[],\n layoutId: string,\n rootPath: string = ''\n): { routes: RouteConfigEntry[]; routeIndex: number; path: string } | null {\n for (let i = 0; i < routes.length; i++) {\n const route = routes[i];\n const path = route.path ? `${rootPath}/${route.path}` : rootPath;\n if (route.id === layoutId) {\n return { routes, routeIndex: i, path };\n }\n if (route.children) {\n const found = findNearestRoute(route.children, layoutId, path);\n if (found) {\n return found;\n }\n }\n // Check if route.id is a prefix of layoutId, indicating a nested route\n if (route.id && layoutId.startsWith(route.id)) {\n return { routes, routeIndex: i, path };\n }\n }\n return null;\n}\n\n/**\n * Merges extension routes into the main routes array, handling route nesting.\n * Routes without IDs are added directly to the routes array. Routes with IDs are processed\n * to remove the extension prefix and are either:\n * - Added as children of existing routes (if a nearest route is found via prefix matching)\n * - Replace existing routes (if an exact match is found)\n * - Added directly to the routes array (if no matching route exists)\n *\n * When adding as a child, the parent route's path is clipped from the child route's path.\n *\n * @param routes - The main routes array to merge into (mutated in place)\n * @param extensionRoutes - The extension routes to merge\n * @param extensionIdPrefix - The prefix to remove from extension route IDs (e.g., \"extensions/store-locator/\")\n */\nexport function mergeRoutes(\n routes: RouteConfigEntry[],\n extensionRoutes: RouteConfigEntry[],\n extensionIdPrefix: string\n): void {\n for (const route of extensionRoutes) {\n if (!route.id) {\n routes.unshift(route);\n continue;\n }\n const routeId = route.id.replace(extensionIdPrefix, '');\n const nearestRouteResult = findNearestRoute(routes, routeId);\n if (nearestRouteResult) {\n const nearestRoute = nearestRouteResult.routes[nearestRouteResult.routeIndex];\n if (nearestRoute.id === routeId) {\n // Replacing an existing route, we assume we can just swap out the implementation\n nearestRouteResult.routes[nearestRouteResult.routeIndex].file = route.file;\n } else {\n // This is a new child of an existing route, insert it at the beginning of the children array\n // and clip out the parent path from the route path\n let path = route.path?.slice(nearestRouteResult.path.length);\n if (path?.startsWith('/')) {\n path = path.slice(1);\n }\n path = path ? path : undefined;\n if (!nearestRoute.children) {\n nearestRoute.children = [];\n }\n nearestRoute.children.unshift({\n ...route,\n id: routeId,\n path,\n });\n }\n } else {\n // This is a new route, insert it at the beginning of the routes array\n routes.unshift({\n ...route,\n id: routeId,\n });\n }\n }\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { flatRoutes as _flatRoutes } from '@react-router/fs-routes';\nimport type { RouteConfigEntry } from '@react-router/dev/routes';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { mergeRoutes } from './merge-routes';\nimport { applyUrlConfig } from '../multi-site/apply-url-config';\nimport { loadConfig } from '../config/load-config';\nimport type { Url } from '../config/types';\n\nconst APP_SRC_DIR = 'src';\nconst EXTENSIONS_DIR = 'extensions';\n// This file must live at the root of `appDirectory` (src/app-wrapper.tsx) and must NOT\n// be moved into a subdirectory. React Router's typegen resolves route module types using\n// paths relative to `appDirectory` — placing it elsewhere breaks generated type references.\nconst APP_WRAPPER_FILE = 'app-wrapper.tsx';\n\n/**\n * Scans `src/extensions/` for extension route directories and merges any discovered\n * routes into the base route tree. Mutates `routes` in place via `mergeRoutes`.\n */\nasync function discoverExtensionRoutes(ignoredRouteFiles: string[], routes: RouteConfigEntry[]): Promise<void> {\n const extensionsDir = path.join('.', APP_SRC_DIR, EXTENSIONS_DIR);\n\n // Sort to ensure deterministic route order across platforms (readdir order is filesystem-dependent)\n const extensions = await fs.readdir(extensionsDir).then(\n (entries) => entries.sort(),\n () => []\n );\n for (const ext of extensions) {\n // React Router rootDirectory uses forward slashes regardless of OS\n const routesDir = `${EXTENSIONS_DIR}/${ext}/routes`;\n const routesDirFull = path.join('.', APP_SRC_DIR, EXTENSIONS_DIR, ext, 'routes');\n try {\n await fs.access(routesDirFull);\n const extensionRoutes = await _flatRoutes({\n ignoredRouteFiles,\n rootDirectory: routesDir,\n });\n mergeRoutes(routes, extensionRoutes, `${EXTENSIONS_DIR}/${ext}/`);\n } catch {\n // Extension has no routes directory — skip\n }\n }\n}\n\n/**\n * Discovers all file-based routes, merges extension routes, and applies multi-site\n * URL configuration if defined in the project's `config.server.ts`.\n *\n * 1. Discover routes from the filesystem using React Router's `flatRoutes`.\n * 2. Scans `src/extensions/` for extension routes and merges them into the route tree.\n * 3. Load `config.server.ts` from the project root and, if `app.url` is configured,\n * wraps routes under the URL prefix (e.g. `/:siteId/:localeId`).\n *\n * @param options.ignoredRouteFiles - Glob patterns for files to ignore. Defaults to test files.\n * @param options.rootDirectory - Root directory for route discovery, relative to appDirectory.\n * @returns The final route config entries for React Router.\n */\nexport async function flatRoutes(options?: {\n ignoredRouteFiles?: string[];\n rootDirectory?: string;\n}): Promise<RouteConfigEntry[]> {\n const { ignoredRouteFiles = ['**/*.test.{ts,tsx}'], rootDirectory } = options ?? {};\n\n // 1. Discover base routes from filesystem\n const routes = await _flatRoutes({ ignoredRouteFiles, rootDirectory });\n\n // 2. Discover and merge extension routes\n await discoverExtensionRoutes(ignoredRouteFiles, routes);\n\n // 3. Try to load URL config from template's config file\n const { app } = await loadConfig();\n const urlConfig = app?.url as Url | undefined;\n if (urlConfig?.prefix) {\n try {\n await fs.access(path.join('.', APP_SRC_DIR, APP_WRAPPER_FILE));\n } catch {\n throw new Error(\n `[storefront-next-runtime] URL prefix \"${urlConfig.prefix}\" is configured but ` +\n `\"${APP_SRC_DIR}/${APP_WRAPPER_FILE}\" does not exist. ` +\n `Create this file with: export { default } from '@salesforce/storefront-next-runtime/routing/app-wrapper';`\n );\n }\n\n return applyUrlConfig({\n routes,\n urlConfig,\n wrapperFile: APP_WRAPPER_FILE,\n });\n }\n\n return routes;\n}\n"],"mappings":";;;;;;;;;;;;;;AAwBA,SAAS,iBACL,QACA,UACA,WAAmB,IACoD;AACvE,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;EACpC,MAAM,QAAQ,OAAO;EACrB,MAAMA,SAAO,MAAM,OAAO,GAAG,SAAS,GAAG,MAAM,SAAS;AACxD,MAAI,MAAM,OAAO,SACb,QAAO;GAAE;GAAQ,YAAY;GAAG;GAAM;AAE1C,MAAI,MAAM,UAAU;GAChB,MAAM,QAAQ,iBAAiB,MAAM,UAAU,UAAUA,OAAK;AAC9D,OAAI,MACA,QAAO;;AAIf,MAAI,MAAM,MAAM,SAAS,WAAW,MAAM,GAAG,CACzC,QAAO;GAAE;GAAQ,YAAY;GAAG;GAAM;;AAG9C,QAAO;;;;;;;;;;;;;;;;AAiBX,SAAgB,YACZ,QACA,iBACA,mBACI;AACJ,MAAK,MAAM,SAAS,iBAAiB;AACjC,MAAI,CAAC,MAAM,IAAI;AACX,UAAO,QAAQ,MAAM;AACrB;;EAEJ,MAAM,UAAU,MAAM,GAAG,QAAQ,mBAAmB,GAAG;EACvD,MAAM,qBAAqB,iBAAiB,QAAQ,QAAQ;AAC5D,MAAI,oBAAoB;GACpB,MAAM,eAAe,mBAAmB,OAAO,mBAAmB;AAClE,OAAI,aAAa,OAAO,QAEpB,oBAAmB,OAAO,mBAAmB,YAAY,OAAO,MAAM;QACnE;IAGH,IAAIA,SAAO,MAAM,MAAM,MAAM,mBAAmB,KAAK,OAAO;AAC5D,QAAIA,QAAM,WAAW,IAAI,CACrB,UAAOA,OAAK,MAAM,EAAE;AAExB,aAAOA,SAAOA,SAAO;AACrB,QAAI,CAAC,aAAa,SACd,cAAa,WAAW,EAAE;AAE9B,iBAAa,SAAS,QAAQ;KAC1B,GAAG;KACH,IAAI;KACJ;KACH,CAAC;;QAIN,QAAO,QAAQ;GACX,GAAG;GACH,IAAI;GACP,CAAC;;;;;;AC9Ed,MAAM,cAAc;AACpB,MAAM,iBAAiB;AAIvB,MAAM,mBAAmB;;;;;AAMzB,eAAe,wBAAwB,mBAA6B,QAA2C;CAC3G,MAAM,gBAAgB,KAAK,KAAK,KAAK,aAAa,eAAe;CAGjE,MAAM,aAAa,MAAM,GAAG,QAAQ,cAAc,CAAC,MAC9C,YAAY,QAAQ,MAAM,QACrB,EAAE,CACX;AACD,MAAK,MAAM,OAAO,YAAY;EAE1B,MAAM,YAAY,GAAG,eAAe,GAAG,IAAI;EAC3C,MAAM,gBAAgB,KAAK,KAAK,KAAK,aAAa,gBAAgB,KAAK,SAAS;AAChF,MAAI;AACA,SAAM,GAAG,OAAO,cAAc;AAK9B,eAAY,QAJY,MAAMC,aAAY;IACtC;IACA,eAAe;IAClB,CAAC,EACmC,GAAG,eAAe,GAAG,IAAI,GAAG;UAC7D;;;;;;;;;;;;;;;;AAmBhB,eAAsB,WAAW,SAGD;CAC5B,MAAM,EAAE,oBAAoB,CAAC,qBAAqB,EAAE,kBAAkB,WAAW,EAAE;CAGnF,MAAM,SAAS,MAAMA,aAAY;EAAE;EAAmB;EAAe,CAAC;AAGtE,OAAM,wBAAwB,mBAAmB,OAAO;CAGxD,MAAM,EAAE,QAAQ,MAAM,YAAY;CAClC,MAAM,YAAY,KAAK;AACvB,KAAI,WAAW,QAAQ;AACnB,MAAI;AACA,SAAM,GAAG,OAAO,KAAK,KAAK,KAAK,aAAa,iBAAiB,CAAC;UAC1D;AACJ,SAAM,IAAI,MACN,yCAAyC,UAAU,OAAO,uBAClD,YAAY,GAAG,iBAAiB,6HAE3C;;AAGL,SAAO,eAAe;GAClB;GACA;GACA,aAAa;GAChB,CAAC;;AAGN,QAAO"}
1
+ {"version":3,"file":"routing.js","names":["path","_flatRoutes"],"sources":["../src/routing/merge-routes.ts","../src/routing/flat-routes.ts"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { type RouteConfigEntry } from '@react-router/dev/routes';\n\n/**\n * Find the nearest route by its ID in the route tree\n * @param routes - The route subtree to search\n * @param layoutId - The route ID to find (e.g., \"routes/_app\" or \"routes/_app.account\")\n * @param rootPath - The full route path from the root to the current route (default: '')\n * @returns An object with routes array, routeIndex, and path, or null if not found. Returns exact match if found, otherwise returns route where route.id is a prefix of layoutId\n */\nfunction findNearestRoute(\n routes: RouteConfigEntry[],\n layoutId: string,\n rootPath: string = ''\n): { routes: RouteConfigEntry[]; routeIndex: number; path: string } | null {\n for (let i = 0; i < routes.length; i++) {\n const route = routes[i];\n const path = route.path ? `${rootPath}/${route.path}` : rootPath;\n if (route.id === layoutId) {\n return { routes, routeIndex: i, path };\n }\n if (route.children) {\n const found = findNearestRoute(route.children, layoutId, path);\n if (found) {\n return found;\n }\n }\n // Check if route.id is a prefix of layoutId, indicating a nested route\n if (route.id && layoutId.startsWith(route.id)) {\n return { routes, routeIndex: i, path };\n }\n }\n return null;\n}\n\n/**\n * Merges extension routes into the main routes array, handling route nesting.\n * Routes without IDs are added directly to the routes array. Routes with IDs are processed\n * to remove the extension prefix and are either:\n * - Added as children of existing routes (if a nearest route is found via prefix matching)\n * - Replace existing routes (if an exact match is found)\n * - Added directly to the routes array (if no matching route exists)\n *\n * When adding as a child, the parent route's path is clipped from the child route's path.\n *\n * @param routes - The main routes array to merge into (mutated in place)\n * @param extensionRoutes - The extension routes to merge\n * @param extensionIdPrefix - The prefix to remove from extension route IDs (e.g., \"extensions/store-locator/\")\n */\nexport function mergeRoutes(\n routes: RouteConfigEntry[],\n extensionRoutes: RouteConfigEntry[],\n extensionIdPrefix: string\n): void {\n for (const route of extensionRoutes) {\n if (!route.id) {\n routes.unshift(route);\n continue;\n }\n const routeId = route.id.replace(extensionIdPrefix, '');\n const nearestRouteResult = findNearestRoute(routes, routeId);\n if (nearestRouteResult) {\n const nearestRoute = nearestRouteResult.routes[nearestRouteResult.routeIndex];\n if (nearestRoute.id === routeId) {\n // Replacing an existing route, we assume we can just swap out the implementation\n nearestRouteResult.routes[nearestRouteResult.routeIndex].file = route.file;\n } else {\n // This is a new child of an existing route, insert it at the beginning of the children array\n // and clip out the parent path from the route path\n let path = route.path?.slice(nearestRouteResult.path.length);\n if (path?.startsWith('/')) {\n path = path.slice(1);\n }\n path = path ? path : undefined;\n if (!nearestRoute.children) {\n nearestRoute.children = [];\n }\n nearestRoute.children.unshift({\n ...route,\n id: routeId,\n path,\n });\n }\n } else {\n // This is a new route, insert it at the beginning of the routes array\n routes.unshift({\n ...route,\n id: routeId,\n });\n }\n }\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { flatRoutes as _flatRoutes } from '@react-router/fs-routes';\nimport type { RouteConfigEntry } from '@react-router/dev/routes';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { mergeRoutes } from './merge-routes';\nimport { applyUrlConfig } from '../site-context/apply-url-config';\nimport { loadConfig } from '../config/load-config';\nimport type { Url } from '../config/types';\n\nconst APP_SRC_DIR = 'src';\nconst EXTENSIONS_DIR = 'extensions';\n// This file must live at the root of `appDirectory` (src/app-wrapper.tsx) and must NOT\n// be moved into a subdirectory. React Router's typegen resolves route module types using\n// paths relative to `appDirectory` — placing it elsewhere breaks generated type references.\nconst APP_WRAPPER_FILE = 'app-wrapper.tsx';\n\n/**\n * Scans `src/extensions/` for extension route directories and merges any discovered\n * routes into the base route tree. Mutates `routes` in place via `mergeRoutes`.\n */\nasync function discoverExtensionRoutes(ignoredRouteFiles: string[], routes: RouteConfigEntry[]): Promise<void> {\n const extensionsDir = path.join('.', APP_SRC_DIR, EXTENSIONS_DIR);\n\n // Sort to ensure deterministic route order across platforms (readdir order is filesystem-dependent)\n const extensions = await fs.readdir(extensionsDir).then(\n (entries) => entries.sort(),\n () => []\n );\n for (const ext of extensions) {\n // React Router rootDirectory uses forward slashes regardless of OS\n const routesDir = `${EXTENSIONS_DIR}/${ext}/routes`;\n const routesDirFull = path.join('.', APP_SRC_DIR, EXTENSIONS_DIR, ext, 'routes');\n try {\n await fs.access(routesDirFull);\n const extensionRoutes = await _flatRoutes({\n ignoredRouteFiles,\n rootDirectory: routesDir,\n });\n mergeRoutes(routes, extensionRoutes, `${EXTENSIONS_DIR}/${ext}/`);\n } catch {\n // Extension has no routes directory — skip\n }\n }\n}\n\n/**\n * Discovers all file-based routes, merges extension routes, and applies site context\n * URL configuration if defined in the project's `config.server.ts`.\n *\n * 1. Discover routes from the filesystem using React Router's `flatRoutes`.\n * 2. Scans `src/extensions/` for extension routes and merges them into the route tree.\n * 3. Load `config.server.ts` from the project root and, if `app.url` is configured,\n * wraps routes under the URL prefix (e.g. `/:siteId/:localeId`).\n *\n * @param options.ignoredRouteFiles - Glob patterns for files to ignore. Defaults to test files.\n * @param options.rootDirectory - Root directory for route discovery, relative to appDirectory.\n * @returns The final route config entries for React Router.\n */\nexport async function flatRoutes(options?: {\n ignoredRouteFiles?: string[];\n rootDirectory?: string;\n}): Promise<RouteConfigEntry[]> {\n const { ignoredRouteFiles = ['**/*.test.{ts,tsx}'], rootDirectory } = options ?? {};\n\n // 1. Discover base routes from filesystem\n const routes = await _flatRoutes({ ignoredRouteFiles, rootDirectory });\n\n // 2. Discover and merge extension routes\n await discoverExtensionRoutes(ignoredRouteFiles, routes);\n\n // 3. Try to load URL config from template's config file\n const { app } = await loadConfig();\n const urlConfig = app?.url as Url | undefined;\n if (urlConfig?.prefix) {\n try {\n await fs.access(path.join('.', APP_SRC_DIR, APP_WRAPPER_FILE));\n } catch {\n throw new Error(\n `[storefront-next-runtime] URL prefix \"${urlConfig.prefix}\" is configured but ` +\n `\"${APP_SRC_DIR}/${APP_WRAPPER_FILE}\" does not exist. ` +\n `Create this file with: export { default } from '@salesforce/storefront-next-runtime/routing/app-wrapper';`\n );\n }\n\n return applyUrlConfig({\n routes,\n urlConfig,\n wrapperFile: APP_WRAPPER_FILE,\n });\n }\n\n return routes;\n}\n"],"mappings":";;;;;;;;;;;;;;AAwBA,SAAS,iBACL,QACA,UACA,WAAmB,IACoD;AACvE,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;EACpC,MAAM,QAAQ,OAAO;EACrB,MAAMA,SAAO,MAAM,OAAO,GAAG,SAAS,GAAG,MAAM,SAAS;AACxD,MAAI,MAAM,OAAO,SACb,QAAO;GAAE;GAAQ,YAAY;GAAG;GAAM;AAE1C,MAAI,MAAM,UAAU;GAChB,MAAM,QAAQ,iBAAiB,MAAM,UAAU,UAAUA,OAAK;AAC9D,OAAI,MACA,QAAO;;AAIf,MAAI,MAAM,MAAM,SAAS,WAAW,MAAM,GAAG,CACzC,QAAO;GAAE;GAAQ,YAAY;GAAG;GAAM;;AAG9C,QAAO;;;;;;;;;;;;;;;;AAiBX,SAAgB,YACZ,QACA,iBACA,mBACI;AACJ,MAAK,MAAM,SAAS,iBAAiB;AACjC,MAAI,CAAC,MAAM,IAAI;AACX,UAAO,QAAQ,MAAM;AACrB;;EAEJ,MAAM,UAAU,MAAM,GAAG,QAAQ,mBAAmB,GAAG;EACvD,MAAM,qBAAqB,iBAAiB,QAAQ,QAAQ;AAC5D,MAAI,oBAAoB;GACpB,MAAM,eAAe,mBAAmB,OAAO,mBAAmB;AAClE,OAAI,aAAa,OAAO,QAEpB,oBAAmB,OAAO,mBAAmB,YAAY,OAAO,MAAM;QACnE;IAGH,IAAIA,SAAO,MAAM,MAAM,MAAM,mBAAmB,KAAK,OAAO;AAC5D,QAAIA,QAAM,WAAW,IAAI,CACrB,UAAOA,OAAK,MAAM,EAAE;AAExB,aAAOA,SAAOA,SAAO;AACrB,QAAI,CAAC,aAAa,SACd,cAAa,WAAW,EAAE;AAE9B,iBAAa,SAAS,QAAQ;KAC1B,GAAG;KACH,IAAI;KACJ;KACH,CAAC;;QAIN,QAAO,QAAQ;GACX,GAAG;GACH,IAAI;GACP,CAAC;;;;;;AC9Ed,MAAM,cAAc;AACpB,MAAM,iBAAiB;AAIvB,MAAM,mBAAmB;;;;;AAMzB,eAAe,wBAAwB,mBAA6B,QAA2C;CAC3G,MAAM,gBAAgB,KAAK,KAAK,KAAK,aAAa,eAAe;CAGjE,MAAM,aAAa,MAAM,GAAG,QAAQ,cAAc,CAAC,MAC9C,YAAY,QAAQ,MAAM,QACrB,EAAE,CACX;AACD,MAAK,MAAM,OAAO,YAAY;EAE1B,MAAM,YAAY,GAAG,eAAe,GAAG,IAAI;EAC3C,MAAM,gBAAgB,KAAK,KAAK,KAAK,aAAa,gBAAgB,KAAK,SAAS;AAChF,MAAI;AACA,SAAM,GAAG,OAAO,cAAc;AAK9B,eAAY,QAJY,MAAMC,aAAY;IACtC;IACA,eAAe;IAClB,CAAC,EACmC,GAAG,eAAe,GAAG,IAAI,GAAG;UAC7D;;;;;;;;;;;;;;;;AAmBhB,eAAsB,WAAW,SAGD;CAC5B,MAAM,EAAE,oBAAoB,CAAC,qBAAqB,EAAE,kBAAkB,WAAW,EAAE;CAGnF,MAAM,SAAS,MAAMA,aAAY;EAAE;EAAmB;EAAe,CAAC;AAGtE,OAAM,wBAAwB,mBAAmB,OAAO;CAGxD,MAAM,EAAE,QAAQ,MAAM,YAAY;CAClC,MAAM,YAAY,KAAK;AACvB,KAAI,WAAW,QAAQ;AACnB,MAAI;AACA,SAAM,GAAG,OAAO,KAAK,KAAK,KAAK,aAAa,iBAAiB,CAAC;UAC1D;AACJ,SAAM,IAAI,MACN,yCAAyC,UAAU,OAAO,uBAClD,YAAY,GAAG,iBAAiB,6HAE3C;;AAGL,SAAO,eAAe;GAClB;GACA;GACA,aAAa;GAChB,CAAC;;AAGN,QAAO"}
@@ -1,11 +1,11 @@
1
1
  import { n as Site$1, r as Url, t as Locale$1 } from "./types.js";
2
2
  import { PropsWithChildren } from "react";
3
- import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+ import * as react_jsx_runtime2 from "react/jsx-runtime";
4
4
  import * as react_router0 from "react-router";
5
5
  import { Cookie, MiddlewareFunction, RouterContextProvider } from "react-router";
6
6
  import { RouteConfigEntry } from "@react-router/dev/routes";
7
7
 
8
- //#region src/multi-site/types.d.ts
8
+ //#region src/site-context/types.d.ts
9
9
 
10
10
  type Locale = Locale$1 & {
11
11
  alias?: string;
@@ -15,17 +15,17 @@ type Site = Omit<Site$1, 'supportedLocales'> & {
15
15
  alias?: string;
16
16
  supportedLocales: Locale[];
17
17
  };
18
- type MultiSiteContext = {
18
+ type SiteContext = {
19
19
  site: Site;
20
20
  locale: Locale;
21
21
  siteCookie: Cookie;
22
22
  localeCookie: Cookie;
23
23
  };
24
24
  /**
25
- * Configuration passed into the multi-site middleware
25
+ * Configuration passed into the site context middleware
26
26
  * Configured by the consumer
27
27
  */
28
- type MultiSiteConfig = {
28
+ type SiteConfig = {
29
29
  sites: Site[];
30
30
  defaultSiteId: string;
31
31
  defaultLocale: string;
@@ -43,7 +43,7 @@ type DetectionConfig = {
43
43
  caches?: Array<'cookie'>;
44
44
  };
45
45
  //#endregion
46
- //#region src/multi-site/site-context.d.ts
46
+ //#region src/site-context/site-context.d.ts
47
47
  /**
48
48
  * Provides the current site to the component tree.
49
49
  * Follows the same pattern as CurrencyProvider.
@@ -56,16 +56,16 @@ declare function SiteProvider({
56
56
  children
57
57
  }: PropsWithChildren<{
58
58
  value: Site;
59
- }>): react_jsx_runtime0.JSX.Element;
59
+ }>): react_jsx_runtime2.JSX.Element;
60
60
  /**
61
61
  * React hook to get the current site.
62
62
  * Returns undefined when no SiteProvider is mounted.
63
63
  */
64
64
  declare function useSite(): Site | undefined;
65
65
  //#endregion
66
- //#region src/multi-site/apply-url-config.d.ts
66
+ //#region src/site-context/apply-url-config.d.ts
67
67
  /**
68
- * Applies multi-site URL configuration to a set of route entries.
68
+ * Applies site context URL configuration to a set of route entries.
69
69
  *
70
70
  * Wraps non-excluded routes under a parent route with the configured URL prefix
71
71
  * (e.g. `/:siteId/:localeId`), while keeping excluded routes (action/resource by default)
@@ -84,7 +84,7 @@ declare function applyUrlConfig(options: {
84
84
  wrapperFile: string;
85
85
  }): RouteConfigEntry[];
86
86
  //#endregion
87
- //#region src/multi-site/build-url.d.ts
87
+ //#region src/site-context/build-url.d.ts
88
88
  /**
89
89
  * Resolves a prefix template by replacing parameter placeholders with values.
90
90
  * ('/:siteId/:localeId', { siteId: 'global', localeId: 'en-GB' }) → '/global/en-GB'
@@ -113,9 +113,9 @@ declare function stripPathPrefix(pathname: string, prefixPattern: string): strin
113
113
  */
114
114
  declare function sanitizePrefix(pathname: string, pathPrefix: string): string;
115
115
  /**
116
- * Builds a fully-qualified URL with multi-site prefix and search params.
116
+ * Builds a fully-qualified URL with site context prefix and search params.
117
117
  *
118
- * Only keys defined in urlConfig.search are set by multi-site. Any other query params
118
+ * Only keys defined in urlConfig.search are set by site context. Any other query params
119
119
  * already present on the `to` URL (including duplicate keys) are preserved as-is.
120
120
  * e.g. to='/api/search?refine=color:blue&refine=size:M', search='?lng=:localeId'
121
121
  * → '/api/search?refine=color:blue&refine=size:M&lng=en-GB'
@@ -134,10 +134,10 @@ declare function buildUrl({
134
134
  params: Record<string, string>;
135
135
  }): string;
136
136
  //#endregion
137
- //#region src/multi-site/middleware.d.ts
138
- declare const multiSiteContext: react_router0.RouterContext<MultiSiteContext | null>;
137
+ //#region src/site-context/middleware.d.ts
138
+ declare const siteContext: react_router0.RouterContext<SiteContext | null>;
139
139
  /**
140
- * Helper function to get multi-site cookies from router context.
140
+ * Helper function to get site context cookies from router context.
141
141
  * Useful in server actions and loaders that need to read/set cookies.
142
142
  *
143
143
  * @param context - Router context provider
@@ -146,7 +146,7 @@ declare const multiSiteContext: react_router0.RouterContext<MultiSiteContext | n
146
146
  * @example
147
147
  * ```typescript
148
148
  * export const action: ActionFunction = async ({ request, context }) => {
149
- * const cookies = getMultiSiteCookies(context);
149
+ * const cookies = getSiteContextCookies(context);
150
150
  * if (cookies) {
151
151
  * const cookieHeader = await cookies.localeCookie.serialize(locale);
152
152
  * // ... use cookieHeader
@@ -154,26 +154,26 @@ declare const multiSiteContext: react_router0.RouterContext<MultiSiteContext | n
154
154
  * };
155
155
  * ```
156
156
  */
157
- declare function getMultiSiteCookies(context: Readonly<RouterContextProvider>): {
157
+ declare function getSiteContextCookies(context: Readonly<RouterContextProvider>): {
158
158
  siteCookie: react_router0.Cookie;
159
159
  localeCookie: react_router0.Cookie;
160
160
  } | null;
161
161
  /**
162
- * Creates a multi-site middleware that resolves the current site from
162
+ * Creates a site context middleware that resolves the current site from
163
163
  * the request (path, cookie, header, query, or default) and stores the
164
164
  * result in the router context.
165
165
  *
166
166
  * Does not import or read from app config context; the consumer supplies config.
167
167
  */
168
- declare function createMultiSiteMiddleware(config: MultiSiteConfig): MiddlewareFunction<Response>;
168
+ declare function createSiteContextMiddleware(config: SiteConfig): MiddlewareFunction<Response>;
169
169
  //#endregion
170
- //#region src/multi-site/cookies.d.ts
170
+ //#region src/site-context/cookies.d.ts
171
171
  /**
172
- * WeakMap to pass resolved locale from multi-site middleware to i18next's findLocale.
172
+ * WeakMap to pass resolved locale from site context middleware to i18next's findLocale.
173
173
  * WeakMap allows garbage collection when requests are done.
174
174
  * This is necessary because findLocale() only receives the Request object, not the router context.
175
175
  */
176
176
  declare const requestToLocaleMap: WeakMap<Request, string>;
177
177
  //#endregion
178
- export { type DetectionConfig, type Locale, type MultiSiteConfig, type MultiSiteContext, type Site, SiteProvider, applyUrlConfig, buildUrl, createMultiSiteMiddleware, getMultiSiteCookies, multiSiteContext, requestToLocaleMap, resolvePrefix, sanitizePrefix, stripPathPrefix, useSite };
179
- //# sourceMappingURL=multi-site.d.ts.map
178
+ export { type DetectionConfig, type Locale, type Site, type SiteConfig, type SiteContext, SiteProvider, applyUrlConfig, buildUrl, createSiteContextMiddleware, getSiteContextCookies, requestToLocaleMap, resolvePrefix, sanitizePrefix, siteContext, stripPathPrefix, useSite };
179
+ //# sourceMappingURL=site-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-context.d.ts","names":[],"sources":["../src/site-context/types.ts","../src/site-context/site-context.tsx","../src/site-context/apply-url-config.ts","../src/site-context/build-url.ts","../src/site-context/middleware.ts","../src/site-context/cookies.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAiCgB,KAbJ,MAAA,GAAS,QAaL,GAAA;EACE,KAAA,CAAA,EAAA,MAAA;CAAM;AAOZ,KAjBA,IAAA,GAAO,IAiBG,CAjBE,MAiBF,EAAA,kBAAA,CAAA,GAAA;EACX,IAAA,CAAA,EAAA,MAAA;EAGe,KAAA,CAAA,EAAA,MAAA;EACE,gBAAA,EAnBN,MAmBM,EAAA;CAAe;AAc/B,KA9BA,WAAA,GA8Be;EAGf,IAAA,EAhCF,IAgCE;UA/BA;cACI;gBACE;ACPlB,CAAA;;;;;AAAoF,KDcxE,UAAA,GCdwE;EAAA,KAAA,EDezE,ICfyE,EAAA;EASpE,aAAO,EAAA,MAAA;;wBDSG;0BACE;AE0E5B,CAAA;ACnBA;AAmBgB,KH5DJ,eAAA,GG4DY,MAAA,GAAA,aAAA,GAAA,QAAA,GAAA,QAAA;AACpB,KH1DQ,eAAA,GG0DR;EACA,KAAA,EH1DO,eG0DP,EAAA;EACA,mBAAA,CAAA,EAAA,MAAA;EAGY,iBAAA,CAAA,EAAA,MAAA;EACJ,YAAA,CAAA,EAAA,MAAA;EAAM,YAAA,CAAA,EAAA,MAAA;WH1DL;;;;;;;AA5Bb;;;;AAK2C,iBCnB3B,YAAA,CDmB2B;EAAA,KAAA;EAAA;AAiB3C,CAjB2C,ECnBO,iBDmBP,CAAA;EAc/B,KAAA,ECjCiE,IDiCjE;AAGZ,CAAA,CAAA,CAAA,ECpCoF,kBAAA,CAAA,GAAA,CAAA,ODqCzE;;;;ACrCX;AAA+B,iBASf,OAAA,CAAA,CATe,EASJ,IATI,GAAA,SAAA;;;;AC6F/B;;;;;;;;AC/DA;AA0BA;AAkBA;AAmBA;;AAEI,iBDFY,cAAA,CCEZ,OAAA,EAAA;EACA,MAAA,EDFQ,gBCER,EAAA;EAGY,SAAA,CAAA,EDJA,GCIA;EACJ,WAAA,EAAA,MAAA;CAAM,CAAA,EDHd,gBCGc,EAAA;;;;;AF3FlB;;iBEqBgB,aAAA,yBAAsC;;AD+DtD;;;;;;;;AC/DA;AA0BA;AAkBA;AAmBA;;;AAGI,iBAxCY,eAAA,CAwCZ,QAAA,EAAA,MAAA,EAAA,aAAA,EAAA,MAAA,CAAA,EAAA,MAAA;;;;;;iBAtBY,cAAA;AC9EhB;AAsBA;;;;;;AAoEA;;;;;iBDOgB,QAAA;;;;;;cAMA;EEnFH,MAAA,EFoFD,MEpFC,CAAA,MAAmD,EAAA,MAAA,CAAA;;;;ALVhD,cIVH,WJUG,EIVQ,aAAA,CAAA,aJUR,CIVQ,WJUR,GAAA,IAAA,CAAA;;;AAQhB;;;;;AAmBA;AAGA;;;;ACpCA;;;;;;AAAoF,iBGkBpE,qBAAA,CHlBoE,OAAA,EGkBrC,QHlBqC,CGkB5B,qBHlB4B,CAAA,CAAA,EAAA;EASpE,UAAO,EGSgC,aAAA,CAAA,MHTxB;;;;ACoF/B;;;;;;iBEPgB,2BAAA,SAAoC,aAAa,mBAAmB;;;;;;;;AH7EpE,cIOH,kBJPkB,EIOA,OJPA,CIOA,OJPA,EAAA,MAAA,CAAA"}
@@ -3,7 +3,7 @@ import { createContext, useContext } from "react";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  import { createContext as createContext$1, createCookie } from "react-router";
5
5
 
6
- //#region src/multi-site/site-context.tsx
6
+ //#region src/site-context/site-context.tsx
7
7
  const SiteContext = createContext(void 0);
8
8
  /**
9
9
  * Provides the current site to the component tree.
@@ -27,7 +27,7 @@ function useSite() {
27
27
  }
28
28
 
29
29
  //#endregion
30
- //#region src/multi-site/build-url.ts
30
+ //#region src/site-context/build-url.ts
31
31
  /**
32
32
  * Parses search config string into key-value pairs, preserving ':param' placeholders.
33
33
  * '?lng=:localeId&site=:siteId' → { lng: ':localeId', site: ':siteId' }
@@ -108,9 +108,9 @@ function sanitizePrefix(pathname, pathPrefix) {
108
108
  return pathname;
109
109
  }
110
110
  /**
111
- * Builds a fully-qualified URL with multi-site prefix and search params.
111
+ * Builds a fully-qualified URL with site context prefix and search params.
112
112
  *
113
- * Only keys defined in urlConfig.search are set by multi-site. Any other query params
113
+ * Only keys defined in urlConfig.search are set by site context. Any other query params
114
114
  * already present on the `to` URL (including duplicate keys) are preserved as-is.
115
115
  * e.g. to='/api/search?refine=color:blue&refine=size:M', search='?lng=:localeId'
116
116
  * → '/api/search?refine=color:blue&refine=size:M&lng=en-GB'
@@ -138,7 +138,7 @@ function buildUrl({ to, urlConfig, params }) {
138
138
  }
139
139
 
140
140
  //#endregion
141
- //#region src/multi-site/utils.ts
141
+ //#region src/site-context/utils.ts
142
142
  /**
143
143
  * Extract a string value from the URL path segment at the given index.
144
144
  */
@@ -159,7 +159,7 @@ async function readCookieFromRequest(request, cookie) {
159
159
  }
160
160
 
161
161
  //#endregion
162
- //#region src/multi-site/site-detection.ts
162
+ //#region src/site-context/site-detection.ts
163
163
  /**
164
164
  * Detect site reference from cookie.
165
165
  */
@@ -200,7 +200,7 @@ async function resolveSite(request, settings) {
200
200
  }
201
201
 
202
202
  //#endregion
203
- //#region src/multi-site/configs.ts
203
+ //#region src/site-context/configs.ts
204
204
  /**
205
205
  * Default site detection configuration
206
206
  */
@@ -235,9 +235,9 @@ const DEFAULT_LOCALE_DETECTION = {
235
235
  };
236
236
 
237
237
  //#endregion
238
- //#region src/multi-site/cookies.ts
238
+ //#region src/site-context/cookies.ts
239
239
  /**
240
- * Cookie options for multi-site cookies
240
+ * Cookie options for site context cookies
241
241
  */
242
242
  const COOKIE_OPTIONS = {
243
243
  path: "/",
@@ -249,20 +249,20 @@ const COOKIE_OPTIONS = {
249
249
  * Creates a cookie instance with the given name.
250
250
  *
251
251
  * @param name - Cookie name
252
- * @returns Cookie instance configured with multi-site options
252
+ * @returns Cookie instance configured with site context options
253
253
  */
254
- function createMultiSiteCookie(name) {
254
+ function createSiteContextCookie(name) {
255
255
  return createCookie(name, COOKIE_OPTIONS);
256
256
  }
257
257
  /**
258
- * WeakMap to pass resolved locale from multi-site middleware to i18next's findLocale.
258
+ * WeakMap to pass resolved locale from site context middleware to i18next's findLocale.
259
259
  * WeakMap allows garbage collection when requests are done.
260
260
  * This is necessary because findLocale() only receives the Request object, not the router context.
261
261
  */
262
262
  const requestToLocaleMap = /* @__PURE__ */ new WeakMap();
263
263
 
264
264
  //#endregion
265
- //#region src/multi-site/locale-detection.ts
265
+ //#region src/site-context/locale-detection.ts
266
266
  /**
267
267
  * Read locale from cookie.
268
268
  */
@@ -310,10 +310,10 @@ async function resolveLocale(request, settings, site) {
310
310
  }
311
311
 
312
312
  //#endregion
313
- //#region src/multi-site/middleware.ts
314
- const multiSiteContext = createContext$1(null);
313
+ //#region src/site-context/middleware.ts
314
+ const siteContext = createContext$1(null);
315
315
  /**
316
- * Helper function to get multi-site cookies from router context.
316
+ * Helper function to get site context cookies from router context.
317
317
  * Useful in server actions and loaders that need to read/set cookies.
318
318
  *
319
319
  * @param context - Router context provider
@@ -322,7 +322,7 @@ const multiSiteContext = createContext$1(null);
322
322
  * @example
323
323
  * ```typescript
324
324
  * export const action: ActionFunction = async ({ request, context }) => {
325
- * const cookies = getMultiSiteCookies(context);
325
+ * const cookies = getSiteContextCookies(context);
326
326
  * if (cookies) {
327
327
  * const cookieHeader = await cookies.localeCookie.serialize(locale);
328
328
  * // ... use cookieHeader
@@ -330,12 +330,12 @@ const multiSiteContext = createContext$1(null);
330
330
  * };
331
331
  * ```
332
332
  */
333
- function getMultiSiteCookies(context) {
334
- const multiSite = context.get(multiSiteContext);
335
- if (!multiSite) return null;
333
+ function getSiteContextCookies(context) {
334
+ const siteCtx = context.get(siteContext);
335
+ if (!siteCtx) return null;
336
336
  return {
337
- siteCookie: multiSite.siteCookie,
338
- localeCookie: multiSite.localeCookie
337
+ siteCookie: siteCtx.siteCookie,
338
+ localeCookie: siteCtx.localeCookie
339
339
  };
340
340
  }
341
341
  /**
@@ -346,7 +346,7 @@ function getMultiSiteCookies(context) {
346
346
  *
347
347
  * @param request - Incoming request
348
348
  * @param response - Response from next()
349
- * @param settings - Multi-site settings with cookie instances and detection config
349
+ * @param settings - Site context settings with cookie instances and detection config
350
350
  * @param site - Resolved site for this request
351
351
  * @param locale - Resolved locale for this request
352
352
  * @returns Object with shouldSetSiteCookie and shouldSetLocaleCookie booleans
@@ -369,13 +369,13 @@ async function shouldSetCookies(request, response, settings, site, locale) {
369
369
  };
370
370
  }
371
371
  /**
372
- * Creates a multi-site middleware that resolves the current site from
372
+ * Creates a site context middleware that resolves the current site from
373
373
  * the request (path, cookie, header, query, or default) and stores the
374
374
  * result in the router context.
375
375
  *
376
376
  * Does not import or read from app config context; the consumer supplies config.
377
377
  */
378
- function createMultiSiteMiddleware(config) {
378
+ function createSiteContextMiddleware(config) {
379
379
  const siteDetectionConfig = {
380
380
  ...DEFAULT_SITE_DETECTION,
381
381
  ...config.siteDetectionConfig
@@ -384,8 +384,8 @@ function createMultiSiteMiddleware(config) {
384
384
  ...DEFAULT_LOCALE_DETECTION,
385
385
  ...config.localeDetectionConfig
386
386
  };
387
- const siteCookie = createMultiSiteCookie(siteDetectionConfig.lookupCookie);
388
- const localeCookie = createMultiSiteCookie(localeDetectionConfig.lookupCookie);
387
+ const siteCookie = createSiteContextCookie(siteDetectionConfig.lookupCookie);
388
+ const localeCookie = createSiteContextCookie(localeDetectionConfig.lookupCookie);
389
389
  const settings = {
390
390
  ...config,
391
391
  siteDetectionConfig,
@@ -393,10 +393,10 @@ function createMultiSiteMiddleware(config) {
393
393
  siteCookie,
394
394
  localeCookie
395
395
  };
396
- const multiSiteMiddleware = async ({ request, context }, next) => {
396
+ const siteContextMiddleware = async ({ request, context }, next) => {
397
397
  const site = await resolveSite(request, settings);
398
398
  const locale = await resolveLocale(request, settings, site);
399
- context.set(multiSiteContext, {
399
+ context.set(siteContext, {
400
400
  site,
401
401
  locale,
402
402
  siteCookie: settings.siteCookie,
@@ -411,9 +411,9 @@ function createMultiSiteMiddleware(config) {
411
411
  if (localeSetCookie) response.headers.append("Set-Cookie", localeSetCookie);
412
412
  return response;
413
413
  };
414
- return multiSiteMiddleware;
414
+ return siteContextMiddleware;
415
415
  }
416
416
 
417
417
  //#endregion
418
- export { SiteProvider, applyUrlConfig, buildUrl, createMultiSiteMiddleware, getMultiSiteCookies, multiSiteContext, requestToLocaleMap, resolvePrefix, sanitizePrefix, stripPathPrefix, useSite };
419
- //# sourceMappingURL=multi-site.js.map
418
+ export { SiteProvider, applyUrlConfig, buildUrl, createSiteContextMiddleware, getSiteContextCookies, requestToLocaleMap, resolvePrefix, sanitizePrefix, siteContext, stripPathPrefix, useSite };
419
+ //# sourceMappingURL=site-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-context.js","names":["result: Record<string, string>","resolvers: Record<DetectionMethod, () => Promise<string | null>>","DEFAULT_SITE_DETECTION: Required<DetectionConfig>","DEFAULT_LOCALE_DETECTION: Required<DetectionConfig>","locale: Locale | null","resolvers: Record<DetectionMethod, () => Promise<string | null>>","createContext","siteDetectionConfig: SiteSettings['siteDetectionConfig']","localeDetectionConfig: SiteSettings['localeDetectionConfig']","settings: SiteSettings","siteContextMiddleware: MiddlewareFunction<Response>"],"sources":["../src/site-context/site-context.tsx","../src/site-context/build-url.ts","../src/site-context/utils.ts","../src/site-context/site-detection.ts","../src/site-context/configs.ts","../src/site-context/cookies.ts","../src/site-context/locale-detection.ts","../src/site-context/middleware.ts"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createContext, useContext, type PropsWithChildren } from 'react';\nimport type { Site } from './types';\n\nconst SiteContext = createContext<Site | undefined>(undefined);\n\n/**\n * Provides the current site to the component tree.\n * Follows the same pattern as CurrencyProvider.\n *\n * Mounted in the template (e.g., app-wrapper.tsx or root.tsx) with the resolved\n * site value from the loader/middleware.\n */\nexport function SiteProvider({ value, children }: PropsWithChildren<{ value: Site }>) {\n return <SiteContext.Provider value={value}>{children}</SiteContext.Provider>;\n}\n\n/**\n * React hook to get the current site.\n * Returns undefined when no SiteProvider is mounted.\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport function useSite(): Site | undefined {\n return useContext(SiteContext);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { Url } from '../config/types';\n\n/**\n * Parses search config string into key-value pairs, preserving ':param' placeholders.\n * '?lng=:localeId&site=:siteId' → { lng: ':localeId', site: ':siteId' }\n */\nexport function parseSearchConfig(search: string): Record<string, string> {\n const searchParams = new URLSearchParams(search);\n const result: Record<string, string> = {};\n for (const [key, value] of searchParams) {\n result[key] = value;\n }\n return result;\n}\n\n/**\n * Extracts parameter names from a prefix string.\n * '/:siteId/:localeId' → ['siteId', 'localeId']\n */\nexport function extractPrefixParams(prefix: string): string[] {\n const matches = prefix.match(/:(\\w+)/g);\n return matches ? matches.map((m) => m.slice(1)) : [];\n}\n\n/**\n * Splits a URL string into its component parts.\n * '/product/123?color=red#details' → { pathname: '/product/123', search: 'color=red', hash: '#details' }\n */\nexport function decomposeUrl(url: string): { pathname: string; search: string; hash: string } {\n const hashIdx = url.indexOf('#');\n const hash = hashIdx >= 0 ? url.slice(hashIdx) : '';\n const withoutHash = hashIdx >= 0 ? url.slice(0, hashIdx) : url;\n const searchIdx = withoutHash.indexOf('?');\n const search = searchIdx >= 0 ? withoutHash.slice(searchIdx + 1) : '';\n const pathname = searchIdx >= 0 ? withoutHash.slice(0, searchIdx) : withoutHash;\n return { pathname, search, hash };\n}\n\n/**\n * Resolves a prefix template by replacing parameter placeholders with values.\n * ('/:siteId/:localeId', { siteId: 'global', localeId: 'en-GB' }) → '/global/en-GB'\n */\nexport function resolvePrefix(prefix: string, params: Record<string, string>): string {\n let resolved = prefix;\n for (const paramName of extractPrefixParams(prefix)) {\n const value = params[paramName];\n if (value) {\n resolved = resolved.replace(`:${paramName}`, value);\n }\n }\n return resolved;\n}\n\n/**\n * Strips the URL prefix segments from a pathname based on a prefix pattern.\n * Since all routes are configured with the prefix baked in, segment counting is sufficient.\n *\n * @param pathname - Full pathname (e.g. '/global/en-GB/checkout')\n * @param prefixPattern - URL prefix pattern from config (e.g. '/:siteId/:localeId')\n * @returns Pathname with prefix stripped (e.g. '/checkout'), or original if\n * the pathname has fewer segments than the prefix\n *\n * @example\n * stripPathPrefix('/global/en-GB/checkout', '/:siteId/:localeId') // → '/checkout'\n * stripPathPrefix('/checkout', '/:siteId/:localeId') // → '/checkout' (fewer segments → unchanged)\n * stripPathPrefix('/checkout', '') // → '/checkout' (no prefix configured)\n * stripPathPrefix('/', '/:siteId/:localeId') // → '/'\n */\nexport function stripPathPrefix(pathname: string, prefixPattern: string): string {\n if (!prefixPattern) return pathname;\n\n const prefixSegmentCount = prefixPattern.split('/').filter(Boolean).length;\n const pathSegments = pathname.split('/').filter(Boolean);\n\n if (pathSegments.length <= prefixSegmentCount) {\n return pathSegments.length === prefixSegmentCount ? '/' : pathname;\n }\n\n return `/${pathSegments.slice(prefixSegmentCount).join('/')}`;\n}\n\n/**\n * Sanitize a resolved prefix from a pathname if present.\n * sanitizePrefix('/global/en-GB/product/123', '/global/en-GB') → '/product/123'\n * sanitizePrefix('/product/123', '/global/en-GB') → '/product/123' (no-op)\n */\nexport function sanitizePrefix(pathname: string, pathPrefix: string): string {\n if (!pathPrefix) return pathname;\n if (pathname === pathPrefix) return '';\n if (pathname.startsWith(`${pathPrefix}/`)) return pathname.slice(pathPrefix.length);\n return pathname;\n}\n\n/**\n * Builds a fully-qualified URL with site context prefix and search params.\n *\n * Only keys defined in urlConfig.search are set by site context. Any other query params\n * already present on the `to` URL (including duplicate keys) are preserved as-is.\n * e.g. to='/api/search?refine=color:blue&refine=size:M', search='?lng=:localeId'\n * → '/api/search?refine=color:blue&refine=size:M&lng=en-GB'\n *\n * @example\n * buildUrl({ to: '/product/123', urlConfig: { prefix: '/:siteId', search: '?lng=:localeId' }, params: { siteId: 'global', localeId: 'en-GB' } })\n * // → '/global/product/123?lng=en-GB'\n */\nexport function buildUrl({\n to,\n urlConfig,\n params,\n}: {\n to: string;\n urlConfig?: Url;\n params: Record<string, string>;\n}): string {\n if (!urlConfig) return to;\n if (!to || to === '#' || to.startsWith('http') || to.startsWith('//')) return to;\n\n const { pathname, search: existingSearch, hash } = decomposeUrl(to);\n\n const pathPrefix = urlConfig.prefix && urlConfig.prefix !== '/' ? resolvePrefix(urlConfig.prefix, params) : '';\n // sanitize prefix to make sure there is no prefix duplication at any case\n const path = pathPrefix ? `${pathPrefix}${sanitizePrefix(pathname, pathPrefix)}` : pathname;\n\n const searchParams = new URLSearchParams(existingSearch);\n if (urlConfig.search) {\n const searchConfig = parseSearchConfig(urlConfig.search);\n for (const [queryKey, value] of Object.entries(searchConfig)) {\n if (value.startsWith(':')) {\n const paramValue = params[value.slice(1)];\n if (paramValue) {\n searchParams.set(queryKey, paramValue);\n }\n } else {\n searchParams.set(queryKey, value);\n }\n }\n }\n\n const search = searchParams.toString();\n return `${path}${search ? `?${search}` : ''}${hash}`;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Cookie } from 'react-router';\n\n/**\n * Extract a string value from the URL path segment at the given index.\n */\nexport function lookupFromPath(pathname: string, pathIndex: number): string | null {\n const pathSegments = pathname.split('/').filter(Boolean);\n\n if (pathSegments.length <= pathIndex) return null;\n\n return pathSegments[pathIndex];\n}\n\n/**\n * Detect a string value from cookie using the given cookie parser.\n *\n * Returns a promise that resolves to the cookie value.\n */\nexport async function readCookieFromRequest(request: Request, cookie: Cookie): Promise<string | null> {\n const cookies = request.headers.get('Cookie');\n if (!cookies) return null;\n\n const cookieValue = await cookie.parse(cookies);\n return cookieValue;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Cookie } from 'react-router';\nimport type { Site, SiteSettings, DetectionMethod } from './types';\nimport { readCookieFromRequest, lookupFromPath } from './utils';\n\n/**\n * Detect site reference from cookie.\n */\nexport async function readSiteFromCookie(request: Request, cookie: Cookie): Promise<string | null> {\n return readCookieFromRequest(request, cookie);\n}\n\n/**\n * Get site object using the site id or alias\n * 1. Check siteIdentifier against each site's alias; if matched, return that site.\n * 2. Else check against each site's id; if matched, return that site.\n * 3. If no match, return null.\n */\nfunction getSiteFromIdOrAlias(siteIdentifier: string | null, sites: Site[]): Site | null {\n if (!siteIdentifier) return null;\n return sites.find((site) => site.alias === siteIdentifier || site.id === siteIdentifier) ?? null;\n}\n\n/**\n * Resolve site using the configured detection order.\n * Returns the first valid site from the first source that yields a valid value.\n */\nexport async function resolveSite(request: Request, settings: SiteSettings): Promise<Site> {\n const { sites, defaultSiteId, siteDetectionConfig, siteCookie } = settings;\n\n const requestUrl = new URL(request.url);\n\n // When a base path is configured (e.g., '/shop'), we need to skip its path segments.\n // React Router handles the base path internally for hooks like useParams or useLocation,\n // but it does not strip it from request.url. The offset is calculated dynamically from\n // the number of segments in the base path as future-proof in case we support multi-segment\n // base paths in the future.\n const basePathOffset = process.env.MRT_ENV_BASE_PATH\n ? process.env.MRT_ENV_BASE_PATH.split('/').filter(Boolean).length\n : 0;\n\n const resolvers: Record<DetectionMethod, () => Promise<string | null>> = {\n path: () =>\n Promise.resolve(\n lookupFromPath(requestUrl.pathname, siteDetectionConfig.lookupFromPathIndex + basePathOffset)\n ),\n querystring: () => Promise.resolve(requestUrl.searchParams.get(siteDetectionConfig.lookupQuerystring)),\n header: () => Promise.resolve(request.headers.get(siteDetectionConfig.lookupHeader)),\n cookie: async () => readSiteFromCookie(request, siteCookie),\n };\n\n for (const method of siteDetectionConfig.order) {\n const siteIdOrAlias = await resolvers[method]?.();\n const resolvedSite = getSiteFromIdOrAlias(siteIdOrAlias, sites);\n if (resolvedSite) return resolvedSite;\n }\n\n // If no site id was found, use the default site id\n const site = getSiteFromIdOrAlias(defaultSiteId, sites);\n\n // If default site id is invalid, throw an error\n if (!site) {\n throw new Error(`Default site ${defaultSiteId} not found.`);\n }\n\n return site;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { DetectionConfig } from './types';\n\n/**\n * Default site detection configuration\n */\nexport const DEFAULT_SITE_DETECTION: Required<DetectionConfig> = {\n order: ['path', 'querystring', 'cookie', 'header'],\n lookupFromPathIndex: 0,\n lookupQuerystring: 'site',\n lookupCookie: 'site_id',\n lookupHeader: 'X-Site-Id',\n caches: ['cookie'],\n};\n\n/**\n * Default locale detection configuration\n */\nexport const DEFAULT_LOCALE_DETECTION: Required<DetectionConfig> = {\n order: ['path', 'querystring', 'cookie', 'header'],\n lookupFromPathIndex: 1,\n lookupQuerystring: 'lng',\n lookupCookie: 'lng',\n lookupHeader: 'Accept-Language',\n caches: ['cookie'],\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createCookie, type Cookie } from 'react-router';\n\n/**\n * Cookie options for site context cookies\n */\nexport const COOKIE_OPTIONS = {\n path: '/',\n sameSite: 'lax' as const,\n secure: process.env.NODE_ENV === 'production',\n httpOnly: true,\n};\n\n/**\n * Creates a cookie instance with the given name.\n *\n * @param name - Cookie name\n * @returns Cookie instance configured with site context options\n */\nexport function createSiteContextCookie(name: string): Cookie {\n return createCookie(name, COOKIE_OPTIONS);\n}\n\n/**\n * WeakMap to pass resolved locale from site context middleware to i18next's findLocale.\n * WeakMap allows garbage collection when requests are done.\n * This is necessary because findLocale() only receives the Request object, not the router context.\n */\nexport const requestToLocaleMap = new WeakMap<Request, string>();\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Cookie } from 'react-router';\nimport type { DetectionMethod, Locale, SiteSettings, Site } from './types';\nimport { readCookieFromRequest, lookupFromPath } from './utils';\n\n/**\n * Read locale from cookie.\n */\nexport async function readLocaleFromCookie(request: Request, cookie: Cookie): Promise<string | null> {\n return readCookieFromRequest(request, cookie);\n}\n\n/**\n * Get locale object using the locale id or alias.\n * 1. Check localeIdOrAlias against each locale's alias; if matched, return that locale.\n * 2. Else check against each locale's id; if matched, return that locale.\n * 3. If no match, return null (caller should use defaultLocale).\n *\n * @param localeIdentifier - The locale id or alias to get the locale from. Null is allowed because this may come from\n * extrenal sources such as cookies, headers, or query parameters.\n * @param locales - The list of locales to search through.\n * @returns The locale object if found, otherwise null.\n */\nfunction getLocaleFromIdOrAlias(localeIdentifier: string | undefined | null, locales: Locale[]): Locale | null {\n if (!localeIdentifier) return null;\n return locales.find((locale) => locale.alias === localeIdentifier || locale.id === localeIdentifier) ?? null;\n}\n\n/**\n * Resolve locale using the configured detection order.\n * Returns the first valid locale from the first source that yields a valid value.\n */\nexport async function resolveLocale(request: Request, settings: SiteSettings, site: Site): Promise<Locale> {\n const { defaultLocale, localeDetectionConfig, localeCookie } = settings;\n const { supportedLocales } = site;\n\n let locale: Locale | null = null;\n const requestUrl = new URL(request.url);\n\n // When a base path is configured (e.g., '/shop'), we need to skip its path segments.\n // React Router handles the base path internally for hooks like useParams or useLocation,\n // but it does not strip it from request.url. The offset is calculated dynamically from\n // the number of segments in the base path as future-proof in case we support multi-segment\n // base paths in the future.\n const basePathOffset = process.env.MRT_ENV_BASE_PATH\n ? process.env.MRT_ENV_BASE_PATH.split('/').filter(Boolean).length\n : 0;\n\n const resolvers: Record<DetectionMethod, () => Promise<string | null>> = {\n path: () =>\n Promise.resolve(\n lookupFromPath(requestUrl.pathname, localeDetectionConfig.lookupFromPathIndex + basePathOffset)\n ),\n querystring: () => Promise.resolve(requestUrl.searchParams.get(localeDetectionConfig.lookupQuerystring)),\n header: () => Promise.resolve(request.headers.get(localeDetectionConfig.lookupHeader)),\n cookie: async () => readLocaleFromCookie(request, localeCookie),\n };\n\n for (const method of localeDetectionConfig.order) {\n const localeIdOrAlias = await resolvers[method]?.();\n const resolvedLocale = getLocaleFromIdOrAlias(localeIdOrAlias, supportedLocales);\n if (resolvedLocale) return resolvedLocale;\n }\n\n // If no locale was found, use the default locale\n if (!locale) {\n locale = getLocaleFromIdOrAlias(defaultLocale, supportedLocales);\n }\n\n // If default locale is invalid, throw an error\n if (!locale) {\n throw new Error(\n `Default locale ${defaultLocale} not found in the list of supported locales for site ${site.id}.`\n );\n }\n\n return locale;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createContext, type MiddlewareFunction, type RouterContextProvider } from 'react-router';\nimport { resolveSite } from './site-detection';\nimport type { SiteConfig, SiteContext, SiteSettings, Site, Locale } from './types';\nimport { DEFAULT_SITE_DETECTION, DEFAULT_LOCALE_DETECTION } from './configs';\nimport { createSiteContextCookie, requestToLocaleMap } from './cookies';\nimport { resolveLocale } from './locale-detection';\n\nexport const siteContext = createContext<SiteContext | null>(null);\n\ntype MiddlewareArgs = { request: Request; context: Readonly<RouterContextProvider> };\n\n/**\n * Helper function to get site context cookies from router context.\n * Useful in server actions and loaders that need to read/set cookies.\n *\n * @param context - Router context provider\n * @returns Object with siteCookie and localeCookie instances, or null if context not set\n *\n * @example\n * ```typescript\n * export const action: ActionFunction = async ({ request, context }) => {\n * const cookies = getSiteContextCookies(context);\n * if (cookies) {\n * const cookieHeader = await cookies.localeCookie.serialize(locale);\n * // ... use cookieHeader\n * }\n * };\n * ```\n */\nexport function getSiteContextCookies(context: Readonly<RouterContextProvider>) {\n const siteCtx = context.get(siteContext);\n if (!siteCtx) return null;\n return {\n siteCookie: siteCtx.siteCookie,\n localeCookie: siteCtx.localeCookie,\n };\n}\n\n/**\n * Helper function to determine if cookies should be set based on:\n * 1. Whether caching is enabled for each cookie type\n * 2. Whether the resolved value differs from the existing cookie\n * 3. Whether cookies were already set by actions/loaders in the response\n *\n * @param request - Incoming request\n * @param response - Response from next()\n * @param settings - Site context settings with cookie instances and detection config\n * @param site - Resolved site for this request\n * @param locale - Resolved locale for this request\n * @returns Object with shouldSetSiteCookie and shouldSetLocaleCookie booleans\n */\nasync function shouldSetCookies(\n request: Request,\n response: Response,\n settings: SiteSettings,\n site: Site,\n locale: Locale\n): Promise<{ shouldSetSiteCookie: boolean; shouldSetLocaleCookie: boolean }> {\n const cacheSite = settings.siteDetectionConfig.caches?.includes('cookie');\n const cacheLocale = settings.localeDetectionConfig.caches?.includes('cookie');\n\n // Early return if no cookie caching is enabled\n if (!cacheSite && !cacheLocale) {\n return { shouldSetSiteCookie: false, shouldSetLocaleCookie: false };\n }\n\n // Check if cookies were already set by actions/loaders in the response.\n // If they were, we don't want to override them.\n const responseSetCookies = response.headers.getSetCookie?.() || [];\n const isSettingSiteCookieInResponse = responseSetCookies.some((cookie) =>\n cookie.startsWith(`${settings.siteCookie.name}=`)\n );\n const isSettingLocaleCookieInResponse = responseSetCookies.some((cookie) =>\n cookie.startsWith(`${settings.localeCookie.name}=`)\n );\n\n const requestCookieHeader = request.headers.get('Cookie');\n const [existingSiteCookie, existingLocaleCookie] = await Promise.all([\n settings.siteCookie.parse(requestCookieHeader),\n settings.localeCookie.parse(requestCookieHeader),\n ]);\n\n // Set cookie if: doesn't exist yet OR resolved value differs from existing.\n // Skip if an action/loader already set it in the response.\n return {\n shouldSetSiteCookie: cacheSite && !isSettingSiteCookieInResponse && existingSiteCookie !== site.id,\n shouldSetLocaleCookie: cacheLocale && !isSettingLocaleCookieInResponse && existingLocaleCookie !== locale.id,\n };\n}\n\n/**\n * Creates a site context middleware that resolves the current site from\n * the request (path, cookie, header, query, or default) and stores the\n * result in the router context.\n *\n * Does not import or read from app config context; the consumer supplies config.\n */\nexport function createSiteContextMiddleware(config: SiteConfig): MiddlewareFunction<Response> {\n // Merge config with defaults so every detection option has a value\n const siteDetectionConfig: SiteSettings['siteDetectionConfig'] = {\n ...DEFAULT_SITE_DETECTION,\n ...config.siteDetectionConfig,\n };\n const localeDetectionConfig: SiteSettings['localeDetectionConfig'] = {\n ...DEFAULT_LOCALE_DETECTION,\n ...config.localeDetectionConfig,\n };\n\n // Create cookies based on configured names\n const siteCookie = createSiteContextCookie(siteDetectionConfig.lookupCookie);\n const localeCookie = createSiteContextCookie(localeDetectionConfig.lookupCookie);\n\n const settings: SiteSettings = {\n ...config,\n siteDetectionConfig,\n localeDetectionConfig,\n siteCookie,\n localeCookie,\n };\n\n const siteContextMiddleware: MiddlewareFunction<Response> = async (\n { request, context }: MiddlewareArgs,\n next: () => Promise<Response>\n ): Promise<Response> => {\n const site = await resolveSite(request, settings);\n const locale = await resolveLocale(request, settings, site);\n\n // Store full Site, Locale, and Cookie objects in context for downstream middlewares (currency, loaders, etc.)\n context.set(siteContext, {\n site,\n locale,\n siteCookie: settings.siteCookie,\n localeCookie: settings.localeCookie,\n });\n\n // Store locale in a WeakMap so i18next's findLocale can access it\n // This is necessary because findLocale only receives Request and cannot access the router context\n requestToLocaleMap.set(request, locale.id);\n\n const response = await next();\n\n // Determine if cookies should be set\n const { shouldSetSiteCookie, shouldSetLocaleCookie } = await shouldSetCookies(\n request,\n response,\n settings,\n site,\n locale\n );\n\n // Early return if no cookies need to be set\n if (!shouldSetSiteCookie && !shouldSetLocaleCookie) {\n return response;\n }\n\n const [siteSetCookie, localeSetCookie] = await Promise.all([\n shouldSetSiteCookie ? settings.siteCookie.serialize(site.id, { path: '/' }) : Promise.resolve(null),\n shouldSetLocaleCookie ? settings.localeCookie.serialize(locale.id, { path: '/' }) : Promise.resolve(null),\n ]);\n\n if (siteSetCookie) response.headers.append('Set-Cookie', siteSetCookie);\n if (localeSetCookie) response.headers.append('Set-Cookie', localeSetCookie);\n\n return response;\n };\n\n return siteContextMiddleware;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/storefront-next-runtime",
3
- "version": "0.3.0-alpha.0",
3
+ "version": "0.3.0-alpha.1",
4
4
  "description": "Runtime agnostic libraries for SFCC Storefront Next",
5
5
  "type": "module",
6
6
  "exports": {
@@ -62,10 +62,10 @@
62
62
  "default": "./dist/events.js"
63
63
  }
64
64
  },
65
- "./multi-site": {
65
+ "./site-context": {
66
66
  "import": {
67
- "types": "./dist/multi-site.d.ts",
68
- "default": "./dist/multi-site.js"
67
+ "types": "./dist/site-context.d.ts",
68
+ "default": "./dist/site-context.js"
69
69
  }
70
70
  },
71
71
  "./scapi": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"multi-site.d.ts","names":[],"sources":["../src/multi-site/types.ts","../src/multi-site/site-context.tsx","../src/multi-site/apply-url-config.ts","../src/multi-site/build-url.ts","../src/multi-site/middleware.ts","../src/multi-site/cookies.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAiCgB,KAbJ,MAAA,GAAS,QAaL,GAAA;EACE,KAAA,CAAA,EAAA,MAAA;CAAM;AAOZ,KAjBA,IAAA,GAAO,IAiBQ,CAjBH,MAiBG,EAAA,kBAAA,CAAA,GAAA;EAChB,IAAA,CAAA,EAAA,MAAA;EAGe,KAAA,CAAA,EAAA,MAAA;EACE,gBAAA,EAnBN,MAmBM,EAAA;CAAe;AAc/B,KA9BA,gBAAA,GA8Be;EAGf,IAAA,EAhCF,IAgCE;UA/BA;cACI;gBACE;ACPlB,CAAA;;;;;AAAoF,KDcxE,eAAA,GCdwE;EAAA,KAAA,EDezE,ICfyE,EAAA;EASpE,aAAO,EAAA,MAAA;;wBDSG;0BACE;AE0E5B,CAAA;ACnBA;AAmBgB,KH5DJ,eAAA,GG4DY,MAAA,GAAA,aAAA,GAAA,QAAA,GAAA,QAAA;AACpB,KH1DQ,eAAA,GG0DR;EACA,KAAA,EH1DO,eG0DP,EAAA;EACA,mBAAA,CAAA,EAAA,MAAA;EAGY,iBAAA,CAAA,EAAA,MAAA;EACJ,YAAA,CAAA,EAAA,MAAA;EAAM,YAAA,CAAA,EAAA,MAAA;WH1DL;;;;;;;AA5Bb;;;;AAK2C,iBCnB3B,YAAA,CDmB2B;EAAA,KAAA;EAAA;AAiB3C,CAjB2C,ECnBO,iBDmBP,CAAA;EAc/B,KAAA,ECjCiE,IDiCjE;AAGZ,CAAA,CAAA,CAAA,ECpCoF,kBAAA,CAAA,GAAA,CAAA,ODqCzE;;;;ACrCX;AAA+B,iBASf,OAAA,CAAA,CATe,EASJ,IATI,GAAA,SAAA;;;;AC6F/B;;;;;;;;AC/DA;AA0BA;AAkBA;AAmBA;;AAEI,iBDFY,cAAA,CCEZ,OAAA,EAAA;EACA,MAAA,EDFQ,gBCER,EAAA;EAGY,SAAA,CAAA,EDJA,GCIA;EACJ,WAAA,EAAA,MAAA;CAAM,CAAA,EDHd,gBCGc,EAAA;;;;;AF3FlB;;iBEqBgB,aAAA,yBAAsC;;AD+DtD;;;;;;;;AC/DA;AA0BA;AAkBA;AAmBA;;;AAGI,iBAxCY,eAAA,CAwCZ,QAAA,EAAA,MAAA,EAAA,aAAA,EAAA,MAAA,CAAA,EAAA,MAAA;;;;;;iBAtBY,cAAA;AC9EhB;AAsBA;;;;;;AAoEA;;;;;iBDOgB,QAAA;;;;;;cAMA;EEnFH,MAAA,EFoFD,MEpFC,CAAA,MAAmD,EAAA,MAAA,CAAA;;;;ALVhD,cIVH,gBJUG,EIVa,aAAA,CAAA,aJUb,CIVa,gBJUb,GAAA,IAAA,CAAA;;;AAQhB;;;;;AAmBA;AAGA;;;;ACpCA;;;;;;AAAoF,iBGkBpE,mBAAA,CHlBoE,OAAA,EGkBvC,QHlBuC,CGkB9B,qBHlB8B,CAAA,CAAA,EAAA;EASpE,UAAO,EGS8B,aAAA,CAAA,MHTtB;;;;ACoF/B;;;;;;iBEPgB,yBAAA,SAAkC,kBAAkB,mBAAmB;;;;;;;;AH7EvE,cIOH,kBJPkB,EIOA,OJPA,CIOA,OJPA,EAAA,MAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"multi-site.js","names":["result: Record<string, string>","resolvers: Record<DetectionMethod, () => Promise<string | null>>","DEFAULT_SITE_DETECTION: Required<DetectionConfig>","DEFAULT_LOCALE_DETECTION: Required<DetectionConfig>","locale: Locale | null","resolvers: Record<DetectionMethod, () => Promise<string | null>>","createContext","siteDetectionConfig: MultiSiteSettings['siteDetectionConfig']","localeDetectionConfig: MultiSiteSettings['localeDetectionConfig']","settings: MultiSiteSettings","multiSiteMiddleware: MiddlewareFunction<Response>"],"sources":["../src/multi-site/site-context.tsx","../src/multi-site/build-url.ts","../src/multi-site/utils.ts","../src/multi-site/site-detection.ts","../src/multi-site/configs.ts","../src/multi-site/cookies.ts","../src/multi-site/locale-detection.ts","../src/multi-site/middleware.ts"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createContext, useContext, type PropsWithChildren } from 'react';\nimport type { Site } from './types';\n\nconst SiteContext = createContext<Site | undefined>(undefined);\n\n/**\n * Provides the current site to the component tree.\n * Follows the same pattern as CurrencyProvider.\n *\n * Mounted in the template (e.g., app-wrapper.tsx or root.tsx) with the resolved\n * site value from the loader/middleware.\n */\nexport function SiteProvider({ value, children }: PropsWithChildren<{ value: Site }>) {\n return <SiteContext.Provider value={value}>{children}</SiteContext.Provider>;\n}\n\n/**\n * React hook to get the current site.\n * Returns undefined when no SiteProvider is mounted.\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport function useSite(): Site | undefined {\n return useContext(SiteContext);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { Url } from '../config/types';\n\n/**\n * Parses search config string into key-value pairs, preserving ':param' placeholders.\n * '?lng=:localeId&site=:siteId' → { lng: ':localeId', site: ':siteId' }\n */\nexport function parseSearchConfig(search: string): Record<string, string> {\n const searchParams = new URLSearchParams(search);\n const result: Record<string, string> = {};\n for (const [key, value] of searchParams) {\n result[key] = value;\n }\n return result;\n}\n\n/**\n * Extracts parameter names from a prefix string.\n * '/:siteId/:localeId' → ['siteId', 'localeId']\n */\nexport function extractPrefixParams(prefix: string): string[] {\n const matches = prefix.match(/:(\\w+)/g);\n return matches ? matches.map((m) => m.slice(1)) : [];\n}\n\n/**\n * Splits a URL string into its component parts.\n * '/product/123?color=red#details' → { pathname: '/product/123', search: 'color=red', hash: '#details' }\n */\nexport function decomposeUrl(url: string): { pathname: string; search: string; hash: string } {\n const hashIdx = url.indexOf('#');\n const hash = hashIdx >= 0 ? url.slice(hashIdx) : '';\n const withoutHash = hashIdx >= 0 ? url.slice(0, hashIdx) : url;\n const searchIdx = withoutHash.indexOf('?');\n const search = searchIdx >= 0 ? withoutHash.slice(searchIdx + 1) : '';\n const pathname = searchIdx >= 0 ? withoutHash.slice(0, searchIdx) : withoutHash;\n return { pathname, search, hash };\n}\n\n/**\n * Resolves a prefix template by replacing parameter placeholders with values.\n * ('/:siteId/:localeId', { siteId: 'global', localeId: 'en-GB' }) → '/global/en-GB'\n */\nexport function resolvePrefix(prefix: string, params: Record<string, string>): string {\n let resolved = prefix;\n for (const paramName of extractPrefixParams(prefix)) {\n const value = params[paramName];\n if (value) {\n resolved = resolved.replace(`:${paramName}`, value);\n }\n }\n return resolved;\n}\n\n/**\n * Strips the URL prefix segments from a pathname based on a prefix pattern.\n * Since all routes are configured with the prefix baked in, segment counting is sufficient.\n *\n * @param pathname - Full pathname (e.g. '/global/en-GB/checkout')\n * @param prefixPattern - URL prefix pattern from config (e.g. '/:siteId/:localeId')\n * @returns Pathname with prefix stripped (e.g. '/checkout'), or original if\n * the pathname has fewer segments than the prefix\n *\n * @example\n * stripPathPrefix('/global/en-GB/checkout', '/:siteId/:localeId') // → '/checkout'\n * stripPathPrefix('/checkout', '/:siteId/:localeId') // → '/checkout' (fewer segments → unchanged)\n * stripPathPrefix('/checkout', '') // → '/checkout' (no prefix configured)\n * stripPathPrefix('/', '/:siteId/:localeId') // → '/'\n */\nexport function stripPathPrefix(pathname: string, prefixPattern: string): string {\n if (!prefixPattern) return pathname;\n\n const prefixSegmentCount = prefixPattern.split('/').filter(Boolean).length;\n const pathSegments = pathname.split('/').filter(Boolean);\n\n if (pathSegments.length <= prefixSegmentCount) {\n return pathSegments.length === prefixSegmentCount ? '/' : pathname;\n }\n\n return `/${pathSegments.slice(prefixSegmentCount).join('/')}`;\n}\n\n/**\n * Sanitize a resolved prefix from a pathname if present.\n * sanitizePrefix('/global/en-GB/product/123', '/global/en-GB') → '/product/123'\n * sanitizePrefix('/product/123', '/global/en-GB') → '/product/123' (no-op)\n */\nexport function sanitizePrefix(pathname: string, pathPrefix: string): string {\n if (!pathPrefix) return pathname;\n if (pathname === pathPrefix) return '';\n if (pathname.startsWith(`${pathPrefix}/`)) return pathname.slice(pathPrefix.length);\n return pathname;\n}\n\n/**\n * Builds a fully-qualified URL with multi-site prefix and search params.\n *\n * Only keys defined in urlConfig.search are set by multi-site. Any other query params\n * already present on the `to` URL (including duplicate keys) are preserved as-is.\n * e.g. to='/api/search?refine=color:blue&refine=size:M', search='?lng=:localeId'\n * → '/api/search?refine=color:blue&refine=size:M&lng=en-GB'\n *\n * @example\n * buildUrl({ to: '/product/123', urlConfig: { prefix: '/:siteId', search: '?lng=:localeId' }, params: { siteId: 'global', localeId: 'en-GB' } })\n * // → '/global/product/123?lng=en-GB'\n */\nexport function buildUrl({\n to,\n urlConfig,\n params,\n}: {\n to: string;\n urlConfig?: Url;\n params: Record<string, string>;\n}): string {\n if (!urlConfig) return to;\n if (!to || to === '#' || to.startsWith('http') || to.startsWith('//')) return to;\n\n const { pathname, search: existingSearch, hash } = decomposeUrl(to);\n\n const pathPrefix = urlConfig.prefix && urlConfig.prefix !== '/' ? resolvePrefix(urlConfig.prefix, params) : '';\n // sanitize prefix to make sure there is no prefix duplication at any case\n const path = pathPrefix ? `${pathPrefix}${sanitizePrefix(pathname, pathPrefix)}` : pathname;\n\n const searchParams = new URLSearchParams(existingSearch);\n if (urlConfig.search) {\n const searchConfig = parseSearchConfig(urlConfig.search);\n for (const [queryKey, value] of Object.entries(searchConfig)) {\n if (value.startsWith(':')) {\n const paramValue = params[value.slice(1)];\n if (paramValue) {\n searchParams.set(queryKey, paramValue);\n }\n } else {\n searchParams.set(queryKey, value);\n }\n }\n }\n\n const search = searchParams.toString();\n return `${path}${search ? `?${search}` : ''}${hash}`;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Cookie } from 'react-router';\n\n/**\n * Extract a string value from the URL path segment at the given index.\n */\nexport function lookupFromPath(pathname: string, pathIndex: number): string | null {\n const pathSegments = pathname.split('/').filter(Boolean);\n\n if (pathSegments.length <= pathIndex) return null;\n\n return pathSegments[pathIndex];\n}\n\n/**\n * Detect a string value from cookie using the given cookie parser.\n *\n * Returns a promise that resolves to the cookie value.\n */\nexport async function readCookieFromRequest(request: Request, cookie: Cookie): Promise<string | null> {\n const cookies = request.headers.get('Cookie');\n if (!cookies) return null;\n\n const cookieValue = await cookie.parse(cookies);\n return cookieValue;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Cookie } from 'react-router';\nimport type { Site, MultiSiteSettings, DetectionMethod } from './types';\nimport { readCookieFromRequest, lookupFromPath } from './utils';\n\n/**\n * Detect site reference from cookie.\n */\nexport async function readSiteFromCookie(request: Request, cookie: Cookie): Promise<string | null> {\n return readCookieFromRequest(request, cookie);\n}\n\n/**\n * Get site object using the site id or alias\n * 1. Check siteIdentifier against each site's alias; if matched, return that site.\n * 2. Else check against each site's id; if matched, return that site.\n * 3. If no match, return null.\n */\nfunction getSiteFromIdOrAlias(siteIdentifier: string | null, sites: Site[]): Site | null {\n if (!siteIdentifier) return null;\n return sites.find((site) => site.alias === siteIdentifier || site.id === siteIdentifier) ?? null;\n}\n\n/**\n * Resolve site using the configured detection order.\n * Returns the first valid site from the first source that yields a valid value.\n */\nexport async function resolveSite(request: Request, settings: MultiSiteSettings): Promise<Site> {\n const { sites, defaultSiteId, siteDetectionConfig, siteCookie } = settings;\n\n const requestUrl = new URL(request.url);\n\n // When a base path is configured (e.g., '/shop'), we need to skip its path segments.\n // React Router handles the base path internally for hooks like useParams or useLocation,\n // but it does not strip it from request.url. The offset is calculated dynamically from\n // the number of segments in the base path as future-proof in case we support multi-segment\n // base paths in the future.\n const basePathOffset = process.env.MRT_ENV_BASE_PATH\n ? process.env.MRT_ENV_BASE_PATH.split('/').filter(Boolean).length\n : 0;\n\n const resolvers: Record<DetectionMethod, () => Promise<string | null>> = {\n path: () =>\n Promise.resolve(\n lookupFromPath(requestUrl.pathname, siteDetectionConfig.lookupFromPathIndex + basePathOffset)\n ),\n querystring: () => Promise.resolve(requestUrl.searchParams.get(siteDetectionConfig.lookupQuerystring)),\n header: () => Promise.resolve(request.headers.get(siteDetectionConfig.lookupHeader)),\n cookie: async () => readSiteFromCookie(request, siteCookie),\n };\n\n for (const method of siteDetectionConfig.order) {\n const siteIdOrAlias = await resolvers[method]?.();\n const resolvedSite = getSiteFromIdOrAlias(siteIdOrAlias, sites);\n if (resolvedSite) return resolvedSite;\n }\n\n // If no site id was found, use the default site id\n const site = getSiteFromIdOrAlias(defaultSiteId, sites);\n\n // If default site id is invalid, throw an error\n if (!site) {\n throw new Error(`Default site ${defaultSiteId} not found.`);\n }\n\n return site;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { DetectionConfig } from './types';\n\n/**\n * Default site detection configuration\n */\nexport const DEFAULT_SITE_DETECTION: Required<DetectionConfig> = {\n order: ['path', 'querystring', 'cookie', 'header'],\n lookupFromPathIndex: 0,\n lookupQuerystring: 'site',\n lookupCookie: 'site_id',\n lookupHeader: 'X-Site-Id',\n caches: ['cookie'],\n};\n\n/**\n * Default locale detection configuration\n */\nexport const DEFAULT_LOCALE_DETECTION: Required<DetectionConfig> = {\n order: ['path', 'querystring', 'cookie', 'header'],\n lookupFromPathIndex: 1,\n lookupQuerystring: 'lng',\n lookupCookie: 'lng',\n lookupHeader: 'Accept-Language',\n caches: ['cookie'],\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createCookie, type Cookie } from 'react-router';\n\n/**\n * Cookie options for multi-site cookies\n */\nexport const COOKIE_OPTIONS = {\n path: '/',\n sameSite: 'lax' as const,\n secure: process.env.NODE_ENV === 'production',\n httpOnly: true,\n};\n\n/**\n * Creates a cookie instance with the given name.\n *\n * @param name - Cookie name\n * @returns Cookie instance configured with multi-site options\n */\nexport function createMultiSiteCookie(name: string): Cookie {\n return createCookie(name, COOKIE_OPTIONS);\n}\n\n/**\n * WeakMap to pass resolved locale from multi-site middleware to i18next's findLocale.\n * WeakMap allows garbage collection when requests are done.\n * This is necessary because findLocale() only receives the Request object, not the router context.\n */\nexport const requestToLocaleMap = new WeakMap<Request, string>();\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Cookie } from 'react-router';\nimport type { DetectionMethod, Locale, MultiSiteSettings, Site } from './types';\nimport { readCookieFromRequest, lookupFromPath } from './utils';\n\n/**\n * Read locale from cookie.\n */\nexport async function readLocaleFromCookie(request: Request, cookie: Cookie): Promise<string | null> {\n return readCookieFromRequest(request, cookie);\n}\n\n/**\n * Get locale object using the locale id or alias.\n * 1. Check localeIdOrAlias against each locale's alias; if matched, return that locale.\n * 2. Else check against each locale's id; if matched, return that locale.\n * 3. If no match, return null (caller should use defaultLocale).\n *\n * @param localeIdentifier - The locale id or alias to get the locale from. Null is allowed because this may come from\n * extrenal sources such as cookies, headers, or query parameters.\n * @param locales - The list of locales to search through.\n * @returns The locale object if found, otherwise null.\n */\nfunction getLocaleFromIdOrAlias(localeIdentifier: string | undefined | null, locales: Locale[]): Locale | null {\n if (!localeIdentifier) return null;\n return locales.find((locale) => locale.alias === localeIdentifier || locale.id === localeIdentifier) ?? null;\n}\n\n/**\n * Resolve locale using the configured detection order.\n * Returns the first valid locale from the first source that yields a valid value.\n */\nexport async function resolveLocale(request: Request, settings: MultiSiteSettings, site: Site): Promise<Locale> {\n const { defaultLocale, localeDetectionConfig, localeCookie } = settings;\n const { supportedLocales } = site;\n\n let locale: Locale | null = null;\n const requestUrl = new URL(request.url);\n\n // When a base path is configured (e.g., '/shop'), we need to skip its path segments.\n // React Router handles the base path internally for hooks like useParams or useLocation,\n // but it does not strip it from request.url. The offset is calculated dynamically from\n // the number of segments in the base path as future-proof in case we support multi-segment\n // base paths in the future.\n const basePathOffset = process.env.MRT_ENV_BASE_PATH\n ? process.env.MRT_ENV_BASE_PATH.split('/').filter(Boolean).length\n : 0;\n\n const resolvers: Record<DetectionMethod, () => Promise<string | null>> = {\n path: () =>\n Promise.resolve(\n lookupFromPath(requestUrl.pathname, localeDetectionConfig.lookupFromPathIndex + basePathOffset)\n ),\n querystring: () => Promise.resolve(requestUrl.searchParams.get(localeDetectionConfig.lookupQuerystring)),\n header: () => Promise.resolve(request.headers.get(localeDetectionConfig.lookupHeader)),\n cookie: async () => readLocaleFromCookie(request, localeCookie),\n };\n\n for (const method of localeDetectionConfig.order) {\n const localeIdOrAlias = await resolvers[method]?.();\n const resolvedLocale = getLocaleFromIdOrAlias(localeIdOrAlias, supportedLocales);\n if (resolvedLocale) return resolvedLocale;\n }\n\n // If no locale was found, use the default locale\n if (!locale) {\n locale = getLocaleFromIdOrAlias(defaultLocale, supportedLocales);\n }\n\n // If default locale is invalid, throw an error\n if (!locale) {\n throw new Error(\n `Default locale ${defaultLocale} not found in the list of supported locales for site ${site.id}.`\n );\n }\n\n return locale;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createContext, type MiddlewareFunction, type RouterContextProvider } from 'react-router';\nimport { resolveSite } from './site-detection';\nimport type { MultiSiteConfig, MultiSiteContext, MultiSiteSettings, Site, Locale } from './types';\nimport { DEFAULT_SITE_DETECTION, DEFAULT_LOCALE_DETECTION } from './configs';\nimport { createMultiSiteCookie, requestToLocaleMap } from './cookies';\nimport { resolveLocale } from './locale-detection';\n\nexport const multiSiteContext = createContext<MultiSiteContext | null>(null);\n\ntype MiddlewareArgs = { request: Request; context: Readonly<RouterContextProvider> };\n\n/**\n * Helper function to get multi-site cookies from router context.\n * Useful in server actions and loaders that need to read/set cookies.\n *\n * @param context - Router context provider\n * @returns Object with siteCookie and localeCookie instances, or null if context not set\n *\n * @example\n * ```typescript\n * export const action: ActionFunction = async ({ request, context }) => {\n * const cookies = getMultiSiteCookies(context);\n * if (cookies) {\n * const cookieHeader = await cookies.localeCookie.serialize(locale);\n * // ... use cookieHeader\n * }\n * };\n * ```\n */\nexport function getMultiSiteCookies(context: Readonly<RouterContextProvider>) {\n const multiSite = context.get(multiSiteContext);\n if (!multiSite) return null;\n return {\n siteCookie: multiSite.siteCookie,\n localeCookie: multiSite.localeCookie,\n };\n}\n\n/**\n * Helper function to determine if cookies should be set based on:\n * 1. Whether caching is enabled for each cookie type\n * 2. Whether the resolved value differs from the existing cookie\n * 3. Whether cookies were already set by actions/loaders in the response\n *\n * @param request - Incoming request\n * @param response - Response from next()\n * @param settings - Multi-site settings with cookie instances and detection config\n * @param site - Resolved site for this request\n * @param locale - Resolved locale for this request\n * @returns Object with shouldSetSiteCookie and shouldSetLocaleCookie booleans\n */\nasync function shouldSetCookies(\n request: Request,\n response: Response,\n settings: MultiSiteSettings,\n site: Site,\n locale: Locale\n): Promise<{ shouldSetSiteCookie: boolean; shouldSetLocaleCookie: boolean }> {\n const cacheSite = settings.siteDetectionConfig.caches?.includes('cookie');\n const cacheLocale = settings.localeDetectionConfig.caches?.includes('cookie');\n\n // Early return if no cookie caching is enabled\n if (!cacheSite && !cacheLocale) {\n return { shouldSetSiteCookie: false, shouldSetLocaleCookie: false };\n }\n\n // Check if cookies were already set by actions/loaders in the response.\n // If they were, we don't want to override them.\n const responseSetCookies = response.headers.getSetCookie?.() || [];\n const isSettingSiteCookieInResponse = responseSetCookies.some((cookie) =>\n cookie.startsWith(`${settings.siteCookie.name}=`)\n );\n const isSettingLocaleCookieInResponse = responseSetCookies.some((cookie) =>\n cookie.startsWith(`${settings.localeCookie.name}=`)\n );\n\n const requestCookieHeader = request.headers.get('Cookie');\n const [existingSiteCookie, existingLocaleCookie] = await Promise.all([\n settings.siteCookie.parse(requestCookieHeader),\n settings.localeCookie.parse(requestCookieHeader),\n ]);\n\n // Set cookie if: doesn't exist yet OR resolved value differs from existing.\n // Skip if an action/loader already set it in the response.\n return {\n shouldSetSiteCookie: cacheSite && !isSettingSiteCookieInResponse && existingSiteCookie !== site.id,\n shouldSetLocaleCookie: cacheLocale && !isSettingLocaleCookieInResponse && existingLocaleCookie !== locale.id,\n };\n}\n\n/**\n * Creates a multi-site middleware that resolves the current site from\n * the request (path, cookie, header, query, or default) and stores the\n * result in the router context.\n *\n * Does not import or read from app config context; the consumer supplies config.\n */\nexport function createMultiSiteMiddleware(config: MultiSiteConfig): MiddlewareFunction<Response> {\n // Merge config with defaults so every detection option has a value\n const siteDetectionConfig: MultiSiteSettings['siteDetectionConfig'] = {\n ...DEFAULT_SITE_DETECTION,\n ...config.siteDetectionConfig,\n };\n const localeDetectionConfig: MultiSiteSettings['localeDetectionConfig'] = {\n ...DEFAULT_LOCALE_DETECTION,\n ...config.localeDetectionConfig,\n };\n\n // Create cookies based on configured names\n const siteCookie = createMultiSiteCookie(siteDetectionConfig.lookupCookie);\n const localeCookie = createMultiSiteCookie(localeDetectionConfig.lookupCookie);\n\n const settings: MultiSiteSettings = {\n ...config,\n siteDetectionConfig,\n localeDetectionConfig,\n siteCookie,\n localeCookie,\n };\n\n const multiSiteMiddleware: MiddlewareFunction<Response> = async (\n { request, context }: MiddlewareArgs,\n next: () => Promise<Response>\n ): Promise<Response> => {\n const site = await resolveSite(request, settings);\n const locale = await resolveLocale(request, settings, site);\n\n // Store full Site, Locale, and Cookie objects in context for downstream middlewares (currency, loaders, etc.)\n context.set(multiSiteContext, {\n site,\n locale,\n siteCookie: settings.siteCookie,\n localeCookie: settings.localeCookie,\n });\n\n // Store locale in a WeakMap so i18next's findLocale can access it\n // This is necessary because findLocale only receives Request and cannot access the router context\n requestToLocaleMap.set(request, locale.id);\n\n const response = await next();\n\n // Determine if cookies should be set\n const { shouldSetSiteCookie, shouldSetLocaleCookie } = await shouldSetCookies(\n request,\n response,\n settings,\n site,\n locale\n );\n\n // Early return if no cookies need to be set\n if (!shouldSetSiteCookie && !shouldSetLocaleCookie) {\n return response;\n }\n\n const [siteSetCookie, localeSetCookie] = await Promise.all([\n shouldSetSiteCookie ? settings.siteCookie.serialize(site.id, { path: '/' }) : Promise.resolve(null),\n shouldSetLocaleCookie ? settings.localeCookie.serialize(locale.id, { path: '/' }) : Promise.resolve(null),\n ]);\n\n if (siteSetCookie) response.headers.append('Set-Cookie', siteSetCookie);\n if (localeSetCookie) response.headers.append('Set-Cookie', localeSetCookie);\n\n return response;\n };\n\n return multiSiteMiddleware;\n}\n"],"mappings":";;;;;;AAkBA,MAAM,cAAc,cAAgC,OAAU;;;;;;;;AAS9D,SAAgB,aAAa,EAAE,OAAO,YAAgD;AAClF,QAAO,oBAAC,YAAY;EAAgB;EAAQ;GAAgC;;;;;;AAQhF,SAAgB,UAA4B;AACxC,QAAO,WAAW,YAAY;;;;;;;;;AChBlC,SAAgB,kBAAkB,QAAwC;CACtE,MAAM,eAAe,IAAI,gBAAgB,OAAO;CAChD,MAAMA,SAAiC,EAAE;AACzC,MAAK,MAAM,CAAC,KAAK,UAAU,aACvB,QAAO,OAAO;AAElB,QAAO;;;;;;AAOX,SAAgB,oBAAoB,QAA0B;CAC1D,MAAM,UAAU,OAAO,MAAM,UAAU;AACvC,QAAO,UAAU,QAAQ,KAAK,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE;;;;;;AAOxD,SAAgB,aAAa,KAAiE;CAC1F,MAAM,UAAU,IAAI,QAAQ,IAAI;CAChC,MAAM,OAAO,WAAW,IAAI,IAAI,MAAM,QAAQ,GAAG;CACjD,MAAM,cAAc,WAAW,IAAI,IAAI,MAAM,GAAG,QAAQ,GAAG;CAC3D,MAAM,YAAY,YAAY,QAAQ,IAAI;CAC1C,MAAM,SAAS,aAAa,IAAI,YAAY,MAAM,YAAY,EAAE,GAAG;AAEnE,QAAO;EAAE,UADQ,aAAa,IAAI,YAAY,MAAM,GAAG,UAAU,GAAG;EACjD;EAAQ;EAAM;;;;;;AAOrC,SAAgB,cAAc,QAAgB,QAAwC;CAClF,IAAI,WAAW;AACf,MAAK,MAAM,aAAa,oBAAoB,OAAO,EAAE;EACjD,MAAM,QAAQ,OAAO;AACrB,MAAI,MACA,YAAW,SAAS,QAAQ,IAAI,aAAa,MAAM;;AAG3D,QAAO;;;;;;;;;;;;;;;;;AAkBX,SAAgB,gBAAgB,UAAkB,eAA+B;AAC7E,KAAI,CAAC,cAAe,QAAO;CAE3B,MAAM,qBAAqB,cAAc,MAAM,IAAI,CAAC,OAAO,QAAQ,CAAC;CACpE,MAAM,eAAe,SAAS,MAAM,IAAI,CAAC,OAAO,QAAQ;AAExD,KAAI,aAAa,UAAU,mBACvB,QAAO,aAAa,WAAW,qBAAqB,MAAM;AAG9D,QAAO,IAAI,aAAa,MAAM,mBAAmB,CAAC,KAAK,IAAI;;;;;;;AAQ/D,SAAgB,eAAe,UAAkB,YAA4B;AACzE,KAAI,CAAC,WAAY,QAAO;AACxB,KAAI,aAAa,WAAY,QAAO;AACpC,KAAI,SAAS,WAAW,GAAG,WAAW,GAAG,CAAE,QAAO,SAAS,MAAM,WAAW,OAAO;AACnF,QAAO;;;;;;;;;;;;;;AAeX,SAAgB,SAAS,EACrB,IACA,WACA,UAKO;AACP,KAAI,CAAC,UAAW,QAAO;AACvB,KAAI,CAAC,MAAM,OAAO,OAAO,GAAG,WAAW,OAAO,IAAI,GAAG,WAAW,KAAK,CAAE,QAAO;CAE9E,MAAM,EAAE,UAAU,QAAQ,gBAAgB,SAAS,aAAa,GAAG;CAEnE,MAAM,aAAa,UAAU,UAAU,UAAU,WAAW,MAAM,cAAc,UAAU,QAAQ,OAAO,GAAG;CAE5G,MAAM,OAAO,aAAa,GAAG,aAAa,eAAe,UAAU,WAAW,KAAK;CAEnF,MAAM,eAAe,IAAI,gBAAgB,eAAe;AACxD,KAAI,UAAU,QAAQ;EAClB,MAAM,eAAe,kBAAkB,UAAU,OAAO;AACxD,OAAK,MAAM,CAAC,UAAU,UAAU,OAAO,QAAQ,aAAa,CACxD,KAAI,MAAM,WAAW,IAAI,EAAE;GACvB,MAAM,aAAa,OAAO,MAAM,MAAM,EAAE;AACxC,OAAI,WACA,cAAa,IAAI,UAAU,WAAW;QAG1C,cAAa,IAAI,UAAU,MAAM;;CAK7C,MAAM,SAAS,aAAa,UAAU;AACtC,QAAO,GAAG,OAAO,SAAS,IAAI,WAAW,KAAK;;;;;;;;ACrIlD,SAAgB,eAAe,UAAkB,WAAkC;CAC/E,MAAM,eAAe,SAAS,MAAM,IAAI,CAAC,OAAO,QAAQ;AAExD,KAAI,aAAa,UAAU,UAAW,QAAO;AAE7C,QAAO,aAAa;;;;;;;AAQxB,eAAsB,sBAAsB,SAAkB,QAAwC;CAClG,MAAM,UAAU,QAAQ,QAAQ,IAAI,SAAS;AAC7C,KAAI,CAAC,QAAS,QAAO;AAGrB,QADoB,MAAM,OAAO,MAAM,QAAQ;;;;;;;;ACfnD,eAAsB,mBAAmB,SAAkB,QAAwC;AAC/F,QAAO,sBAAsB,SAAS,OAAO;;;;;;;;AASjD,SAAS,qBAAqB,gBAA+B,OAA4B;AACrF,KAAI,CAAC,eAAgB,QAAO;AAC5B,QAAO,MAAM,MAAM,SAAS,KAAK,UAAU,kBAAkB,KAAK,OAAO,eAAe,IAAI;;;;;;AAOhG,eAAsB,YAAY,SAAkB,UAA4C;CAC5F,MAAM,EAAE,OAAO,eAAe,qBAAqB,eAAe;CAElE,MAAM,aAAa,IAAI,IAAI,QAAQ,IAAI;CAOvC,MAAM,iBAAiB,QAAQ,IAAI,oBAC7B,QAAQ,IAAI,kBAAkB,MAAM,IAAI,CAAC,OAAO,QAAQ,CAAC,SACzD;CAEN,MAAMC,YAAmE;EACrE,YACI,QAAQ,QACJ,eAAe,WAAW,UAAU,oBAAoB,sBAAsB,eAAe,CAChG;EACL,mBAAmB,QAAQ,QAAQ,WAAW,aAAa,IAAI,oBAAoB,kBAAkB,CAAC;EACtG,cAAc,QAAQ,QAAQ,QAAQ,QAAQ,IAAI,oBAAoB,aAAa,CAAC;EACpF,QAAQ,YAAY,mBAAmB,SAAS,WAAW;EAC9D;AAED,MAAK,MAAM,UAAU,oBAAoB,OAAO;EAE5C,MAAM,eAAe,qBADC,MAAM,UAAU,WAAW,EACQ,MAAM;AAC/D,MAAI,aAAc,QAAO;;CAI7B,MAAM,OAAO,qBAAqB,eAAe,MAAM;AAGvD,KAAI,CAAC,KACD,OAAM,IAAI,MAAM,gBAAgB,cAAc,aAAa;AAG/D,QAAO;;;;;;;;AC3DX,MAAaC,yBAAoD;CAC7D,OAAO;EAAC;EAAQ;EAAe;EAAU;EAAS;CAClD,qBAAqB;CACrB,mBAAmB;CACnB,cAAc;CACd,cAAc;CACd,QAAQ,CAAC,SAAS;CACrB;;;;AAKD,MAAaC,2BAAsD;CAC/D,OAAO;EAAC;EAAQ;EAAe;EAAU;EAAS;CAClD,qBAAqB;CACrB,mBAAmB;CACnB,cAAc;CACd,cAAc;CACd,QAAQ,CAAC,SAAS;CACrB;;;;;;;ACnBD,MAAa,iBAAiB;CAC1B,MAAM;CACN,UAAU;CACV,QAAQ,QAAQ,IAAI,aAAa;CACjC,UAAU;CACb;;;;;;;AAQD,SAAgB,sBAAsB,MAAsB;AACxD,QAAO,aAAa,MAAM,eAAe;;;;;;;AAQ7C,MAAa,qCAAqB,IAAI,SAA0B;;;;;;;ACpBhE,eAAsB,qBAAqB,SAAkB,QAAwC;AACjG,QAAO,sBAAsB,SAAS,OAAO;;;;;;;;;;;;;AAcjD,SAAS,uBAAuB,kBAA6C,SAAkC;AAC3G,KAAI,CAAC,iBAAkB,QAAO;AAC9B,QAAO,QAAQ,MAAM,WAAW,OAAO,UAAU,oBAAoB,OAAO,OAAO,iBAAiB,IAAI;;;;;;AAO5G,eAAsB,cAAc,SAAkB,UAA6B,MAA6B;CAC5G,MAAM,EAAE,eAAe,uBAAuB,iBAAiB;CAC/D,MAAM,EAAE,qBAAqB;CAE7B,IAAIC,SAAwB;CAC5B,MAAM,aAAa,IAAI,IAAI,QAAQ,IAAI;CAOvC,MAAM,iBAAiB,QAAQ,IAAI,oBAC7B,QAAQ,IAAI,kBAAkB,MAAM,IAAI,CAAC,OAAO,QAAQ,CAAC,SACzD;CAEN,MAAMC,YAAmE;EACrE,YACI,QAAQ,QACJ,eAAe,WAAW,UAAU,sBAAsB,sBAAsB,eAAe,CAClG;EACL,mBAAmB,QAAQ,QAAQ,WAAW,aAAa,IAAI,sBAAsB,kBAAkB,CAAC;EACxG,cAAc,QAAQ,QAAQ,QAAQ,QAAQ,IAAI,sBAAsB,aAAa,CAAC;EACtF,QAAQ,YAAY,qBAAqB,SAAS,aAAa;EAClE;AAED,MAAK,MAAM,UAAU,sBAAsB,OAAO;EAE9C,MAAM,iBAAiB,uBADC,MAAM,UAAU,WAAW,EACY,iBAAiB;AAChF,MAAI,eAAgB,QAAO;;AAI/B,KAAI,CAAC,OACD,UAAS,uBAAuB,eAAe,iBAAiB;AAIpE,KAAI,CAAC,OACD,OAAM,IAAI,MACN,kBAAkB,cAAc,uDAAuD,KAAK,GAAG,GAClG;AAGL,QAAO;;;;;ACpEX,MAAa,mBAAmBC,gBAAuC,KAAK;;;;;;;;;;;;;;;;;;;AAsB5E,SAAgB,oBAAoB,SAA0C;CAC1E,MAAM,YAAY,QAAQ,IAAI,iBAAiB;AAC/C,KAAI,CAAC,UAAW,QAAO;AACvB,QAAO;EACH,YAAY,UAAU;EACtB,cAAc,UAAU;EAC3B;;;;;;;;;;;;;;;AAgBL,eAAe,iBACX,SACA,UACA,UACA,MACA,QACyE;CACzE,MAAM,YAAY,SAAS,oBAAoB,QAAQ,SAAS,SAAS;CACzE,MAAM,cAAc,SAAS,sBAAsB,QAAQ,SAAS,SAAS;AAG7E,KAAI,CAAC,aAAa,CAAC,YACf,QAAO;EAAE,qBAAqB;EAAO,uBAAuB;EAAO;CAKvE,MAAM,qBAAqB,SAAS,QAAQ,gBAAgB,IAAI,EAAE;CAClE,MAAM,gCAAgC,mBAAmB,MAAM,WAC3D,OAAO,WAAW,GAAG,SAAS,WAAW,KAAK,GAAG,CACpD;CACD,MAAM,kCAAkC,mBAAmB,MAAM,WAC7D,OAAO,WAAW,GAAG,SAAS,aAAa,KAAK,GAAG,CACtD;CAED,MAAM,sBAAsB,QAAQ,QAAQ,IAAI,SAAS;CACzD,MAAM,CAAC,oBAAoB,wBAAwB,MAAM,QAAQ,IAAI,CACjE,SAAS,WAAW,MAAM,oBAAoB,EAC9C,SAAS,aAAa,MAAM,oBAAoB,CACnD,CAAC;AAIF,QAAO;EACH,qBAAqB,aAAa,CAAC,iCAAiC,uBAAuB,KAAK;EAChG,uBAAuB,eAAe,CAAC,mCAAmC,yBAAyB,OAAO;EAC7G;;;;;;;;;AAUL,SAAgB,0BAA0B,QAAuD;CAE7F,MAAMC,sBAAgE;EAClE,GAAG;EACH,GAAG,OAAO;EACb;CACD,MAAMC,wBAAoE;EACtE,GAAG;EACH,GAAG,OAAO;EACb;CAGD,MAAM,aAAa,sBAAsB,oBAAoB,aAAa;CAC1E,MAAM,eAAe,sBAAsB,sBAAsB,aAAa;CAE9E,MAAMC,WAA8B;EAChC,GAAG;EACH;EACA;EACA;EACA;EACH;CAED,MAAMC,sBAAoD,OACtD,EAAE,SAAS,WACX,SACoB;EACpB,MAAM,OAAO,MAAM,YAAY,SAAS,SAAS;EACjD,MAAM,SAAS,MAAM,cAAc,SAAS,UAAU,KAAK;AAG3D,UAAQ,IAAI,kBAAkB;GAC1B;GACA;GACA,YAAY,SAAS;GACrB,cAAc,SAAS;GAC1B,CAAC;AAIF,qBAAmB,IAAI,SAAS,OAAO,GAAG;EAE1C,MAAM,WAAW,MAAM,MAAM;EAG7B,MAAM,EAAE,qBAAqB,0BAA0B,MAAM,iBACzD,SACA,UACA,UACA,MACA,OACH;AAGD,MAAI,CAAC,uBAAuB,CAAC,sBACzB,QAAO;EAGX,MAAM,CAAC,eAAe,mBAAmB,MAAM,QAAQ,IAAI,CACvD,sBAAsB,SAAS,WAAW,UAAU,KAAK,IAAI,EAAE,MAAM,KAAK,CAAC,GAAG,QAAQ,QAAQ,KAAK,EACnG,wBAAwB,SAAS,aAAa,UAAU,OAAO,IAAI,EAAE,MAAM,KAAK,CAAC,GAAG,QAAQ,QAAQ,KAAK,CAC5G,CAAC;AAEF,MAAI,cAAe,UAAS,QAAQ,OAAO,cAAc,cAAc;AACvE,MAAI,gBAAiB,UAAS,QAAQ,OAAO,cAAc,gBAAgB;AAE3E,SAAO;;AAGX,QAAO"}