@salesforce/storefront-next-runtime 0.3.1 → 0.4.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +82 -0
  2. package/dist/DesignComponent.js +37 -12
  3. package/dist/DesignComponent.js.map +1 -1
  4. package/dist/DesignContext.js +47 -2
  5. package/dist/DesignContext.js.map +1 -1
  6. package/dist/DesignFrame.js +1 -1
  7. package/dist/DesignRegion.js +1 -1
  8. package/dist/config.d.ts +4 -4
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/custom-global-preferences.d.ts +20 -0
  11. package/dist/custom-global-preferences.d.ts.map +1 -0
  12. package/dist/custom-global-preferences.js +28 -0
  13. package/dist/custom-global-preferences.js.map +1 -0
  14. package/dist/custom-site-preferences.d.ts +20 -0
  15. package/dist/custom-site-preferences.d.ts.map +1 -0
  16. package/dist/custom-site-preferences.js +28 -0
  17. package/dist/custom-site-preferences.js.map +1 -0
  18. package/dist/data-store-custom-global-preferences.d.ts +2 -0
  19. package/dist/data-store-custom-global-preferences.js +6 -0
  20. package/dist/data-store-custom-site-preferences.d.ts +2 -0
  21. package/dist/data-store-custom-site-preferences.js +6 -0
  22. package/dist/data-store-gcp-preferences.d.ts +2 -0
  23. package/dist/data-store-gcp-preferences.js +6 -0
  24. package/dist/data-store.d.ts +97 -0
  25. package/dist/data-store.d.ts.map +1 -0
  26. package/dist/data-store.js +42 -0
  27. package/dist/data-store.js.map +1 -0
  28. package/dist/design-data.d.ts +82 -88
  29. package/dist/design-data.d.ts.map +1 -1
  30. package/dist/design-data.js +95 -57
  31. package/dist/design-data.js.map +1 -1
  32. package/dist/design-messaging.d.ts +2 -2
  33. package/dist/design-react-core.d.ts +2 -2
  34. package/dist/events.d.ts +34 -6
  35. package/dist/events.d.ts.map +1 -1
  36. package/dist/events.js +6 -6
  37. package/dist/events.js.map +1 -1
  38. package/dist/gcp-preferences.d.ts +52 -0
  39. package/dist/gcp-preferences.d.ts.map +1 -0
  40. package/dist/gcp-preferences.js +61 -0
  41. package/dist/gcp-preferences.js.map +1 -0
  42. package/dist/i18n-client.d.ts +38 -0
  43. package/dist/i18n-client.d.ts.map +1 -0
  44. package/dist/i18n-client.js +72 -0
  45. package/dist/i18n-client.js.map +1 -0
  46. package/dist/i18n.d.ts +63 -0
  47. package/dist/i18n.d.ts.map +1 -0
  48. package/dist/i18n.js +98 -0
  49. package/dist/i18n.js.map +1 -0
  50. package/dist/index.d.ts +60 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/messaging-api.js +3 -1
  53. package/dist/messaging-api.js.map +1 -1
  54. package/dist/scapi.d.ts +247 -2
  55. package/dist/scapi.d.ts.map +1 -1
  56. package/dist/scapi.js +1 -1
  57. package/dist/scapi.js.map +1 -1
  58. package/dist/site-context.d.ts +94 -18
  59. package/dist/site-context.d.ts.map +1 -1
  60. package/dist/site-context.js +2 -417
  61. package/dist/site-context2.js +513 -0
  62. package/dist/site-context2.js.map +1 -0
  63. package/dist/types2.d.ts +210 -0
  64. package/dist/types2.d.ts.map +1 -1
  65. package/dist/utils.js +179 -0
  66. package/dist/utils.js.map +1 -0
  67. package/package.json +63 -4
  68. package/dist/site-context.js.map +0 -1
@@ -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_runtime0 from "react/jsx-runtime";
6
+ import * as react_jsx_runtime1 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_runtime0.JSX.Element;
52
+ }>): react_jsx_runtime1.JSX.Element;
53
53
  //#endregion
54
54
  //#region src/design/react/core/RegionContext.d.ts
55
55
  interface RegionContextType {
package/dist/events.d.ts CHANGED
@@ -114,6 +114,11 @@ interface ClickSearchSuggestionEvent extends BaseEvent {
114
114
  searchInputText: string;
115
115
  suggestion: string;
116
116
  }
117
+ /** Shopper opened agentic commerce (e.g. header or search assistant entry point). */
118
+ interface CommerceAgentEngagementEvent extends BaseEvent {
119
+ eventType: 'commerce_agent_engagement';
120
+ surface: 'header' | 'search';
121
+ }
117
122
  /**
118
123
  * Interface for custom analytics events.
119
124
  * Extend this interface via module augmentation.
@@ -133,7 +138,7 @@ interface AnalyticsEventExtensions {}
133
138
  *
134
139
  * Custom types can be added by extending the AnalyticsEventExtensions interface.
135
140
  */
136
- type AnalyticsEvent = ViewPageEvent | ViewProductEvent | ViewSearchEvent | ViewCategoryEvent | ViewRecommenderEvent | ClickProductInCategoryEvent | ClickProductInSearchEvent | ClickProductInRecommenderEvent | CartItemAddEvent | CheckoutStartEvent | CheckoutStepEvent | ViewSearchSuggestionEvent | ClickSearchSuggestionEvent | AnalyticsEventExtensions[keyof AnalyticsEventExtensions];
141
+ type AnalyticsEvent = ViewPageEvent | ViewProductEvent | ViewSearchEvent | ViewCategoryEvent | ViewRecommenderEvent | ClickProductInCategoryEvent | ClickProductInSearchEvent | ClickProductInRecommenderEvent | CartItemAddEvent | CheckoutStartEvent | CheckoutStepEvent | ViewSearchSuggestionEvent | ClickSearchSuggestionEvent | CommerceAgentEngagementEvent | AnalyticsEventExtensions[keyof AnalyticsEventExtensions];
137
142
  /**
138
143
  * Helper type for mapping event_type to the corresponding event type.
139
144
  */
@@ -149,20 +154,43 @@ type EventSiteInfo = {
149
154
  siteId: string;
150
155
  localeId: string;
151
156
  };
157
+ /**
158
+ * Consent categories for granular tracking control.
159
+ *
160
+ * Adapters declare which consent category they require via configuration.
161
+ * The event system passes the shopper's granted categories (consentPreferences) to each adapter,
162
+ * allowing per-adapter consent decisions.
163
+ *
164
+ * This is typed as `string` so projects can define categories that match their
165
+ * consent management platform. Common conventions:
166
+ *
167
+ * - `'necessary'` — Essential cookies/tracking required for site functionality
168
+ * - `'analytics'` — Usage analytics and performance measurement
169
+ * - `'marketing'` — Marketing, advertising, and retargeting
170
+ * - `'personalization'` — Product recommendations and personalized experiences
171
+ */
172
+ type ConsentCategory = string;
173
+ /**
174
+ * The set of consent categories a shopper has granted.
175
+ *
176
+ * Each adapter checks whether its required `consentCategory` is included
177
+ * in the preferences before sending events.
178
+ */
179
+ type ConsentPreferences = ConsentCategory[];
152
180
  /**
153
181
  * Minimal interface for engagement adapters that can send analytics events.
154
- * Engagemet Adapters must implement this interface to work with the event mediator.
182
+ * Engagement Adapters must implement this interface to work with the event mediator.
155
183
  */
156
184
  interface EventAdapter {
157
185
  name: string;
158
- sendEvent?: (event: AnalyticsEvent, siteInfo?: EventSiteInfo) => Promise<unknown>;
186
+ sendEvent?: (event: AnalyticsEvent, siteInfo?: EventSiteInfo, consentPreferences?: ConsentPreferences) => Promise<unknown>;
159
187
  }
160
188
  /**
161
189
  * Generic event mediator interface for tracking events.
162
190
  * This can be used for analytics, telemetry, or any other event tracking system.
163
191
  */
164
192
  type EventMediator = {
165
- track: (event: AnalyticsEvent, siteInfo?: EventSiteInfo) => void;
193
+ track: (event: AnalyticsEvent, siteInfo?: EventSiteInfo, consentPreferences?: ConsentPreferences) => void;
166
194
  };
167
195
  //#endregion
168
196
  //#region src/events/events.d.ts
@@ -190,7 +218,7 @@ declare function createEvent<T extends AnalyticsEvent['eventType']>(eventType: T
190
218
  * @param event - The view page event to send
191
219
  * @param eventMediator - The event mediator to send the event to
192
220
  */
193
- declare function sendViewPageEvent(event: ViewPageEvent, eventMediator: EventMediator, siteInfo?: EventSiteInfo): void;
221
+ declare function sendViewPageEvent(event: ViewPageEvent, eventMediator: EventMediator, siteInfo?: EventSiteInfo, consentPreferences?: ConsentPreferences): void;
194
222
  //#endregion
195
223
  //#region src/events/mediator.d.ts
196
224
  /**
@@ -209,5 +237,5 @@ declare function getEventMediator(getAdapters: () => EventAdapter[]): EventMedia
209
237
  */
210
238
  declare function resetEventMediator(): void;
211
239
  //#endregion
212
- export { AnalyticsEvent, AnalyticsEventExtensions, AnalyticsPayload, AnalyticsUser, BaseEvent, CartItemAddEvent, CheckoutStartEvent, CheckoutStepEvent, ClickProductInCategoryEvent, ClickProductInRecommenderEvent, ClickProductInSearchEvent, ClickSearchSuggestionEvent, EventAdapter, EventMediator, EventPayload, EventSiteInfo, EventTypeMap, PayloadTbd, ViewCategoryEvent, ViewPageEvent, ViewProductEvent, ViewRecommenderEvent, ViewSearchEvent, ViewSearchSuggestionEvent, createEvent, getEventMediator, resetEventMediator, sendViewPageEvent };
240
+ export { AnalyticsEvent, AnalyticsEventExtensions, AnalyticsPayload, AnalyticsUser, BaseEvent, CartItemAddEvent, CheckoutStartEvent, CheckoutStepEvent, ClickProductInCategoryEvent, ClickProductInRecommenderEvent, ClickProductInSearchEvent, ClickSearchSuggestionEvent, CommerceAgentEngagementEvent, ConsentCategory, ConsentPreferences, EventAdapter, EventMediator, EventPayload, EventSiteInfo, EventTypeMap, PayloadTbd, ViewCategoryEvent, ViewPageEvent, ViewProductEvent, ViewRecommenderEvent, ViewSearchEvent, ViewSearchSuggestionEvent, createEvent, getEventMediator, resetEventMediator, sendViewPageEvent };
213
241
  //# sourceMappingURL=events.d.ts.map
@@ -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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"}
package/dist/events.js CHANGED
@@ -28,8 +28,8 @@ function createEvent(eventType, data) {
28
28
  * @param event - The view page event to send
29
29
  * @param eventMediator - The event mediator to send the event to
30
30
  */
31
- function sendViewPageEvent(event, eventMediator, siteInfo) {
32
- eventMediator.track(event, siteInfo);
31
+ function sendViewPageEvent(event, eventMediator, siteInfo, consentPreferences) {
32
+ eventMediator.track(event, siteInfo, consentPreferences);
33
33
  }
34
34
 
35
35
  //#endregion
@@ -48,8 +48,8 @@ let mediatorInstance;
48
48
  * @returns EventMediator instance
49
49
  */
50
50
  function createEventMediator(getAdapters) {
51
- return { track: (event, siteInfo) => {
52
- processEventWithAdapters(event, getAdapters, siteInfo).catch((error) => {
51
+ return { track: (event, siteInfo, consentPreferences) => {
52
+ processEventWithAdapters(event, getAdapters, siteInfo, consentPreferences).catch((error) => {
53
53
  console.error("Analytics tracking failed:", error);
54
54
  });
55
55
  } };
@@ -82,7 +82,7 @@ function resetEventMediator() {
82
82
  * @param event - The analytics event to process
83
83
  * @param getAdapters - Function that returns the current array of event adapters
84
84
  */
85
- async function processEventWithAdapters(event, getAdapters, siteInfo) {
85
+ async function processEventWithAdapters(event, getAdapters, siteInfo, consentPreferences) {
86
86
  const eventAdapters = getAdapters();
87
87
  if (eventAdapters.length === 0) {
88
88
  console.warn(`There are no active adapters to send the event to`);
@@ -90,7 +90,7 @@ async function processEventWithAdapters(event, getAdapters, siteInfo) {
90
90
  }
91
91
  const promises = eventAdapters.map(async (adapter) => {
92
92
  try {
93
- if (typeof adapter.sendEvent === "function") await adapter.sendEvent(event, siteInfo);
93
+ if (typeof adapter.sendEvent === "function") await adapter.sendEvent(event, siteInfo, consentPreferences);
94
94
  else console.warn(`Adapter ${adapter.name} does not implement sendEvent`);
95
95
  } catch (error) {
96
96
  console.error(`Failed to send event to ${adapter.name}:`, error);
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","names":["mediatorInstance: EventMediator | undefined"],"sources":["../src/events/events.ts","../src/events/mediator.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\nimport type { AnalyticsEvent, EventMediator, EventPayload, EventSiteInfo, EventTypeMap, ViewPageEvent } from './types';\n\n/**\n * Type-safe event creation function\n *\n * This generic function allows creating any event type under AnalyticsEvent\n * with full type safety. The event type is inferred from the string literal\n * passed as the first parameter, and TypeScript will enforce the correct\n * data properties for that specific event type.\n *\n * @example\n * ```typescript\n * const viewPageEvent = createEvent('view_page', { path: '/products', payload });\n * const viewProductEvent = createEvent('view_product', { product, payload });\n * ```\n */\nexport function createEvent<T extends AnalyticsEvent['eventType']>(\n eventType: T,\n data: EventPayload<T>\n): EventTypeMap[T] {\n return {\n eventType,\n ...data,\n } as EventTypeMap[T];\n}\n\n/**\n * Send a view page event to the event mediator\n *\n * This wrapper function is used in the automated page view event tracking client middleware.\n * This function exists to support build-time checks and type safety.\n *\n * @param event - The view page event to send\n * @param eventMediator - The event mediator to send the event to\n */\nexport function sendViewPageEvent(event: ViewPageEvent, eventMediator: EventMediator, siteInfo?: EventSiteInfo): void {\n eventMediator.track(event, siteInfo);\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 { EventMediator, AnalyticsEvent, EventAdapter, EventSiteInfo } from './types';\n\n// Module-level storage for the event mediator singleton\n// This ensures a single mediator instance across all usages\nlet mediatorInstance: EventMediator | undefined;\n\n/**\n * Create an event mediator instance\n *\n * Creates a new EventMediator that processes events through the provided adapters.\n * The mediator uses the getAdapters function on each track() invocation to ensure\n * it always uses the latest adapters from the adapter registry.\n *\n * @param getAdapters - Function that returns the current array of engagement adapters.\n * This function is called on each track() invocation to ensure\n * the mediator always uses the latest adapters from the adapter registry.\n * @returns EventMediator instance\n */\nfunction createEventMediator(getAdapters: () => EventAdapter[]): EventMediator {\n return {\n track: (event: AnalyticsEvent, siteInfo?: EventSiteInfo) => {\n processEventWithAdapters(event, getAdapters, siteInfo).catch((error) => {\n // eslint-disable-next-line no-console\n console.error('Analytics tracking failed:', error);\n });\n },\n };\n}\n\n/**\n * Get the event mediator singleton instance\n *\n * Returns the singleton EventMediator instance, creating it if it doesn't exist.\n *\n * @param getAdapters - Function that returns the current array of engagement adapters.\n * @returns EventMediator instance (singleton) or undefined if not on client side\n */\nexport function getEventMediator(getAdapters: () => EventAdapter[]): EventMediator | undefined {\n // If mediator already exists, return it\n if (mediatorInstance) {\n return mediatorInstance;\n }\n\n // Only create on client side\n if (typeof window === 'undefined') {\n return undefined;\n }\n\n // Create the event mediator singleton\n mediatorInstance = createEventMediator(getAdapters);\n return mediatorInstance;\n}\n\n/**\n * Reset the event mediator singleton (for testing only)\n *\n * This function clears the singleton instance, allowing tests to create a fresh mediator.\n */\nexport function resetEventMediator(): void {\n mediatorInstance = undefined;\n}\n\n/**\n * Process an event with all registered adapters\n *\n * @param event - The analytics event to process\n * @param getAdapters - Function that returns the current array of event adapters\n */\nasync function processEventWithAdapters(\n event: AnalyticsEvent,\n getAdapters: () => EventAdapter[],\n siteInfo?: EventSiteInfo\n): Promise<void> {\n // Get the current array of event adapters\n const eventAdapters = getAdapters();\n if (eventAdapters.length === 0) {\n // eslint-disable-next-line no-console\n console.warn(`There are no active adapters to send the event to`);\n return;\n }\n\n // Send event to all registered adapters that implement sendEvent in parallel\n const promises = eventAdapters.map(async (adapter) => {\n try {\n if (typeof adapter.sendEvent === 'function') {\n await adapter.sendEvent(event, siteInfo);\n } else {\n // eslint-disable-next-line no-console\n console.warn(`Adapter ${adapter.name} does not implement sendEvent`);\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(`Failed to send event to ${adapter.name}:`, error);\n }\n });\n\n await Promise.allSettled(promises);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgCA,SAAgB,YACZ,WACA,MACe;AACf,QAAO;EACH;EACA,GAAG;EACN;;;;;;;;;;;AAYL,SAAgB,kBAAkB,OAAsB,eAA8B,UAAgC;AAClH,eAAc,MAAM,OAAO,SAAS;;;;;AChCxC,IAAIA;;;;;;;;;;;;;AAcJ,SAAS,oBAAoB,aAAkD;AAC3E,QAAO,EACH,QAAQ,OAAuB,aAA6B;AACxD,2BAAyB,OAAO,aAAa,SAAS,CAAC,OAAO,UAAU;AAEpE,WAAQ,MAAM,8BAA8B,MAAM;IACpD;IAET;;;;;;;;;;AAWL,SAAgB,iBAAiB,aAA8D;AAE3F,KAAI,iBACA,QAAO;AAIX,KAAI,OAAO,WAAW,YAClB;AAIJ,oBAAmB,oBAAoB,YAAY;AACnD,QAAO;;;;;;;AAQX,SAAgB,qBAA2B;AACvC,oBAAmB;;;;;;;;AASvB,eAAe,yBACX,OACA,aACA,UACa;CAEb,MAAM,gBAAgB,aAAa;AACnC,KAAI,cAAc,WAAW,GAAG;AAE5B,UAAQ,KAAK,oDAAoD;AACjE;;CAIJ,MAAM,WAAW,cAAc,IAAI,OAAO,YAAY;AAClD,MAAI;AACA,OAAI,OAAO,QAAQ,cAAc,WAC7B,OAAM,QAAQ,UAAU,OAAO,SAAS;OAGxC,SAAQ,KAAK,WAAW,QAAQ,KAAK,+BAA+B;WAEnE,OAAO;AAEZ,WAAQ,MAAM,2BAA2B,QAAQ,KAAK,IAAI,MAAM;;GAEtE;AAEF,OAAM,QAAQ,WAAW,SAAS"}
1
+ {"version":3,"file":"events.js","names":["mediatorInstance: EventMediator | undefined"],"sources":["../src/events/events.ts","../src/events/mediator.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\nimport type {\n AnalyticsEvent,\n ConsentPreferences,\n EventMediator,\n EventPayload,\n EventSiteInfo,\n EventTypeMap,\n ViewPageEvent,\n} from './types';\n\n/**\n * Type-safe event creation function\n *\n * This generic function allows creating any event type under AnalyticsEvent\n * with full type safety. The event type is inferred from the string literal\n * passed as the first parameter, and TypeScript will enforce the correct\n * data properties for that specific event type.\n *\n * @example\n * ```typescript\n * const viewPageEvent = createEvent('view_page', { path: '/products', payload });\n * const viewProductEvent = createEvent('view_product', { product, payload });\n * ```\n */\nexport function createEvent<T extends AnalyticsEvent['eventType']>(\n eventType: T,\n data: EventPayload<T>\n): EventTypeMap[T] {\n return {\n eventType,\n ...data,\n } as EventTypeMap[T];\n}\n\n/**\n * Send a view page event to the event mediator\n *\n * This wrapper function is used in the automated page view event tracking client middleware.\n * This function exists to support build-time checks and type safety.\n *\n * @param event - The view page event to send\n * @param eventMediator - The event mediator to send the event to\n */\nexport function sendViewPageEvent(\n event: ViewPageEvent,\n eventMediator: EventMediator,\n siteInfo?: EventSiteInfo,\n consentPreferences?: ConsentPreferences\n): void {\n eventMediator.track(event, siteInfo, consentPreferences);\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 { EventMediator, AnalyticsEvent, EventAdapter, EventSiteInfo, ConsentPreferences } from './types';\n\n// Module-level storage for the event mediator singleton\n// This ensures a single mediator instance across all usages\nlet mediatorInstance: EventMediator | undefined;\n\n/**\n * Create an event mediator instance\n *\n * Creates a new EventMediator that processes events through the provided adapters.\n * The mediator uses the getAdapters function on each track() invocation to ensure\n * it always uses the latest adapters from the adapter registry.\n *\n * @param getAdapters - Function that returns the current array of engagement adapters.\n * This function is called on each track() invocation to ensure\n * the mediator always uses the latest adapters from the adapter registry.\n * @returns EventMediator instance\n */\nfunction createEventMediator(getAdapters: () => EventAdapter[]): EventMediator {\n return {\n track: (event: AnalyticsEvent, siteInfo?: EventSiteInfo, consentPreferences?: ConsentPreferences) => {\n processEventWithAdapters(event, getAdapters, siteInfo, consentPreferences).catch((error) => {\n // eslint-disable-next-line no-console\n console.error('Analytics tracking failed:', error);\n });\n },\n };\n}\n\n/**\n * Get the event mediator singleton instance\n *\n * Returns the singleton EventMediator instance, creating it if it doesn't exist.\n *\n * @param getAdapters - Function that returns the current array of engagement adapters.\n * @returns EventMediator instance (singleton) or undefined if not on client side\n */\nexport function getEventMediator(getAdapters: () => EventAdapter[]): EventMediator | undefined {\n // If mediator already exists, return it\n if (mediatorInstance) {\n return mediatorInstance;\n }\n\n // Only create on client side\n if (typeof window === 'undefined') {\n return undefined;\n }\n\n // Create the event mediator singleton\n mediatorInstance = createEventMediator(getAdapters);\n return mediatorInstance;\n}\n\n/**\n * Reset the event mediator singleton (for testing only)\n *\n * This function clears the singleton instance, allowing tests to create a fresh mediator.\n */\nexport function resetEventMediator(): void {\n mediatorInstance = undefined;\n}\n\n/**\n * Process an event with all registered adapters\n *\n * @param event - The analytics event to process\n * @param getAdapters - Function that returns the current array of event adapters\n */\nasync function processEventWithAdapters(\n event: AnalyticsEvent,\n getAdapters: () => EventAdapter[],\n siteInfo?: EventSiteInfo,\n consentPreferences?: ConsentPreferences\n): Promise<void> {\n // Get the current array of event adapters\n const eventAdapters = getAdapters();\n if (eventAdapters.length === 0) {\n // eslint-disable-next-line no-console\n console.warn(`There are no active adapters to send the event to`);\n return;\n }\n\n // Send event to all registered adapters that implement sendEvent in parallel\n const promises = eventAdapters.map(async (adapter) => {\n try {\n if (typeof adapter.sendEvent === 'function') {\n await adapter.sendEvent(event, siteInfo, consentPreferences);\n } else {\n // eslint-disable-next-line no-console\n console.warn(`Adapter ${adapter.name} does not implement sendEvent`);\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(`Failed to send event to ${adapter.name}:`, error);\n }\n });\n\n await Promise.allSettled(promises);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAwCA,SAAgB,YACZ,WACA,MACe;AACf,QAAO;EACH;EACA,GAAG;EACN;;;;;;;;;;;AAYL,SAAgB,kBACZ,OACA,eACA,UACA,oBACI;AACJ,eAAc,MAAM,OAAO,UAAU,mBAAmB;;;;;AC7C5D,IAAIA;;;;;;;;;;;;;AAcJ,SAAS,oBAAoB,aAAkD;AAC3E,QAAO,EACH,QAAQ,OAAuB,UAA0B,uBAA4C;AACjG,2BAAyB,OAAO,aAAa,UAAU,mBAAmB,CAAC,OAAO,UAAU;AAExF,WAAQ,MAAM,8BAA8B,MAAM;IACpD;IAET;;;;;;;;;;AAWL,SAAgB,iBAAiB,aAA8D;AAE3F,KAAI,iBACA,QAAO;AAIX,KAAI,OAAO,WAAW,YAClB;AAIJ,oBAAmB,oBAAoB,YAAY;AACnD,QAAO;;;;;;;AAQX,SAAgB,qBAA2B;AACvC,oBAAmB;;;;;;;;AASvB,eAAe,yBACX,OACA,aACA,UACA,oBACa;CAEb,MAAM,gBAAgB,aAAa;AACnC,KAAI,cAAc,WAAW,GAAG;AAE5B,UAAQ,KAAK,oDAAoD;AACjE;;CAIJ,MAAM,WAAW,cAAc,IAAI,OAAO,YAAY;AAClD,MAAI;AACA,OAAI,OAAO,QAAQ,cAAc,WAC7B,OAAM,QAAQ,UAAU,OAAO,UAAU,mBAAmB;OAG5D,SAAQ,KAAK,WAAW,QAAQ,KAAK,+BAA+B;WAEnE,OAAO;AAEZ,WAAQ,MAAM,2BAA2B,QAAQ,KAAK,IAAI,MAAM;;GAEtE;AAEF,OAAM,QAAQ,WAAW,SAAS"}
@@ -0,0 +1,52 @@
1
+ import * as react_router0 from "react-router";
2
+ import { RouterContextProvider } from "react-router";
3
+
4
+ //#region src/data-store/middleware/gcp-preferences.d.ts
5
+
6
+ /**
7
+ * OOTB Google Cloud Platform preferences sourced from the MRT data store.
8
+ *
9
+ * Additional fields (e.g. `projectId`, `region`) may be added here as the
10
+ * ECOM MRT sync job expands the `gcp` entry. Consumers should read the
11
+ * object as a whole via `getGcpPreferences`, or use a specific convenience
12
+ * getter like `getGcpApiKey` for a single field.
13
+ */
14
+ type GcpPreferences = {
15
+ apiKey: string;
16
+ };
17
+ declare const DEFAULT_GCP_PREFERENCES_KEY = "gcp";
18
+ declare const gcpPreferencesContext: react_router0.RouterContext<GcpPreferences | null>;
19
+ /**
20
+ * Read the GCP (Google Cloud Platform) preferences object from router context.
21
+ *
22
+ * The preferences are sourced from the MRT data store entry `gcp`, which is
23
+ * populated only for storefronts connecting to production ECOM instances.
24
+ * In non-production environments, or when the entry is missing, returns an
25
+ * object whose fields are all empty/default.
26
+ *
27
+ * @param context - Router context provider
28
+ * @returns GCP preferences object; fields are empty/default when the entry is unavailable
29
+ */
30
+ declare function getGcpPreferences(context: Readonly<RouterContextProvider>): GcpPreferences;
31
+ /**
32
+ * Convenience getter for the Google Cloud API key alone.
33
+ *
34
+ * Equivalent to `getGcpPreferences(context).apiKey`.
35
+ *
36
+ * @param context - Router context provider
37
+ * @returns The GCP API key, or an empty string when unavailable
38
+ */
39
+ declare function getGcpApiKey(context: Readonly<RouterContextProvider>): string;
40
+ /**
41
+ * Middleware that reads the OOTB GCP preferences from the MRT data store and
42
+ * stores them in the router context. The entry shape is `{ "api-key": string, ... }`
43
+ * under data store key `gcp`. Missing/invalid fields coerce to empty/default values.
44
+ *
45
+ * Only available for storefronts connecting to production ECOM instances.
46
+ * Must run before any loader/middleware that reads `getGcpPreferences(context)`
47
+ * or `getGcpApiKey(context)`.
48
+ */
49
+ declare const gcpPreferencesMiddleware: react_router0.MiddlewareFunction<Response>;
50
+ //#endregion
51
+ export { getGcpApiKey as a, gcpPreferencesMiddleware as i, GcpPreferences as n, getGcpPreferences as o, gcpPreferencesContext as r, DEFAULT_GCP_PREFERENCES_KEY as t };
52
+ //# sourceMappingURL=gcp-preferences.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcp-preferences.d.ts","names":[],"sources":["../src/data-store/middleware/gcp-preferences.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;KA2BY,cAAA;;;cAIC,2BAAA;cAQA,uBAAqB,aAAA,CAAA,cAAA;;;;;;;;;;;;iBAalB,iBAAA,UAA2B,SAAS,yBAAyB;;;;;;;;;iBAoB7D,YAAA,UAAsB,SAAS;;;;;;;;;;cAalC,0BAAwB,aAAA,CAAA,mBAAA"}
@@ -0,0 +1,61 @@
1
+ import { n as createDataStoreMiddleware, t as createDataStoreContext } from "./utils.js";
2
+
3
+ //#region src/data-store/middleware/gcp-preferences.ts
4
+ const DEFAULT_GCP_PREFERENCES_KEY = "gcp";
5
+ /**
6
+ * Map keys inside the `gcp` data store entry. The ECOM MRT sync job writes
7
+ * to these exact keys; keep in sync with the sync job contract.
8
+ */
9
+ const API_KEY_MAP_KEY = "api-key";
10
+ const gcpPreferencesContext = createDataStoreContext();
11
+ /**
12
+ * Read the GCP (Google Cloud Platform) preferences object from router context.
13
+ *
14
+ * The preferences are sourced from the MRT data store entry `gcp`, which is
15
+ * populated only for storefronts connecting to production ECOM instances.
16
+ * In non-production environments, or when the entry is missing, returns an
17
+ * object whose fields are all empty/default.
18
+ *
19
+ * @param context - Router context provider
20
+ * @returns GCP preferences object; fields are empty/default when the entry is unavailable
21
+ */
22
+ function getGcpPreferences(context) {
23
+ const data = context.get(gcpPreferencesContext);
24
+ if (data === null) {
25
+ console.warn("GCP preferences context not found. Ensure gcpPreferencesMiddleware runs before loaders, or expect empty values in environments without the MRT data store entry.");
26
+ return { apiKey: "" };
27
+ }
28
+ return data;
29
+ }
30
+ /**
31
+ * Convenience getter for the Google Cloud API key alone.
32
+ *
33
+ * Equivalent to `getGcpPreferences(context).apiKey`.
34
+ *
35
+ * @param context - Router context provider
36
+ * @returns The GCP API key, or an empty string when unavailable
37
+ */
38
+ function getGcpApiKey(context) {
39
+ return getGcpPreferences(context).apiKey;
40
+ }
41
+ /**
42
+ * Middleware that reads the OOTB GCP preferences from the MRT data store and
43
+ * stores them in the router context. The entry shape is `{ "api-key": string, ... }`
44
+ * under data store key `gcp`. Missing/invalid fields coerce to empty/default values.
45
+ *
46
+ * Only available for storefronts connecting to production ECOM instances.
47
+ * Must run before any loader/middleware that reads `getGcpPreferences(context)`
48
+ * or `getGcpApiKey(context)`.
49
+ */
50
+ const gcpPreferencesMiddleware = createDataStoreMiddleware({
51
+ entryKey: DEFAULT_GCP_PREFERENCES_KEY,
52
+ context: gcpPreferencesContext,
53
+ transform: (value) => {
54
+ const rawKey = value[API_KEY_MAP_KEY];
55
+ return { apiKey: typeof rawKey === "string" ? rawKey : "" };
56
+ }
57
+ });
58
+
59
+ //#endregion
60
+ export { getGcpPreferences as a, getGcpApiKey as i, gcpPreferencesContext as n, gcpPreferencesMiddleware as r, DEFAULT_GCP_PREFERENCES_KEY as t };
61
+ //# sourceMappingURL=gcp-preferences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcp-preferences.js","names":[],"sources":["../src/data-store/middleware/gcp-preferences.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\nimport type { RouterContextProvider } from 'react-router';\nimport { createDataStoreContext, createDataStoreMiddleware } from '../utils';\n\n/**\n * OOTB Google Cloud Platform preferences sourced from the MRT data store.\n *\n * Additional fields (e.g. `projectId`, `region`) may be added here as the\n * ECOM MRT sync job expands the `gcp` entry. Consumers should read the\n * object as a whole via `getGcpPreferences`, or use a specific convenience\n * getter like `getGcpApiKey` for a single field.\n */\nexport type GcpPreferences = {\n apiKey: string;\n};\n\nexport const DEFAULT_GCP_PREFERENCES_KEY = 'gcp';\n\n/**\n * Map keys inside the `gcp` data store entry. The ECOM MRT sync job writes\n * to these exact keys; keep in sync with the sync job contract.\n */\nconst API_KEY_MAP_KEY = 'api-key';\n\nexport const gcpPreferencesContext = createDataStoreContext<GcpPreferences>();\n\n/**\n * Read the GCP (Google Cloud Platform) preferences object from router context.\n *\n * The preferences are sourced from the MRT data store entry `gcp`, which is\n * populated only for storefronts connecting to production ECOM instances.\n * In non-production environments, or when the entry is missing, returns an\n * object whose fields are all empty/default.\n *\n * @param context - Router context provider\n * @returns GCP preferences object; fields are empty/default when the entry is unavailable\n */\nexport function getGcpPreferences(context: Readonly<RouterContextProvider>): GcpPreferences {\n const data = context.get(gcpPreferencesContext);\n if (data === null) {\n // eslint-disable-next-line no-console\n console.warn(\n 'GCP preferences context not found. Ensure gcpPreferencesMiddleware runs before loaders, or expect empty values in environments without the MRT data store entry.'\n );\n return { apiKey: '' };\n }\n return data;\n}\n\n/**\n * Convenience getter for the Google Cloud API key alone.\n *\n * Equivalent to `getGcpPreferences(context).apiKey`.\n *\n * @param context - Router context provider\n * @returns The GCP API key, or an empty string when unavailable\n */\nexport function getGcpApiKey(context: Readonly<RouterContextProvider>): string {\n return getGcpPreferences(context).apiKey;\n}\n\n/**\n * Middleware that reads the OOTB GCP preferences from the MRT data store and\n * stores them in the router context. The entry shape is `{ \"api-key\": string, ... }`\n * under data store key `gcp`. Missing/invalid fields coerce to empty/default values.\n *\n * Only available for storefronts connecting to production ECOM instances.\n * Must run before any loader/middleware that reads `getGcpPreferences(context)`\n * or `getGcpApiKey(context)`.\n */\nexport const gcpPreferencesMiddleware = createDataStoreMiddleware<GcpPreferences>({\n entryKey: DEFAULT_GCP_PREFERENCES_KEY,\n context: gcpPreferencesContext,\n transform: (value) => {\n const rawKey = value[API_KEY_MAP_KEY];\n return { apiKey: typeof rawKey === 'string' ? rawKey : '' };\n },\n});\n"],"mappings":";;;AA+BA,MAAa,8BAA8B;;;;;AAM3C,MAAM,kBAAkB;AAExB,MAAa,wBAAwB,wBAAwC;;;;;;;;;;;;AAa7E,SAAgB,kBAAkB,SAA0D;CACxF,MAAM,OAAO,QAAQ,IAAI,sBAAsB;AAC/C,KAAI,SAAS,MAAM;AAEf,UAAQ,KACJ,mKACH;AACD,SAAO,EAAE,QAAQ,IAAI;;AAEzB,QAAO;;;;;;;;;;AAWX,SAAgB,aAAa,SAAkD;AAC3E,QAAO,kBAAkB,QAAQ,CAAC;;;;;;;;;;;AAYtC,MAAa,2BAA2B,0BAA0C;CAC9E,UAAU;CACV,SAAS;CACT,YAAY,UAAU;EAClB,MAAM,SAAS,MAAM;AACrB,SAAO,EAAE,QAAQ,OAAO,WAAW,WAAW,SAAS,IAAI;;CAElE,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { ResourceLanguage, i18n } from "i18next";
2
+
3
+ //#region src/i18n/types.d.ts
4
+
5
+ /**
6
+ * Callback that dynamically imports all translations for a given language.
7
+ * Must be defined in template code so Vite can resolve the `import()` path at build time
8
+ * and split translations into per-language chunks.
9
+ *
10
+ * @example
11
+ * const loadLocale: LocaleLoader = (language) => import(`@/locales/${language}/index.ts`);
12
+ */
13
+ type LocaleLoader = (language: string) => Promise<{
14
+ default: ResourceLanguage;
15
+ }>;
16
+ //#endregion
17
+ //#region src/i18n/client.d.ts
18
+
19
+ /**
20
+ * Initialize i18next on the client side.
21
+ * Pass a `loadLocale` callback containing the dynamic import so Vite can resolve it
22
+ * at build time relative to the template's source tree.
23
+ *
24
+ * @example
25
+ * // In root.tsx — Vite resolves the import() relative to this file
26
+ * initI18next({
27
+ * language: document.documentElement.lang || undefined,
28
+ * loadLocale: (language) => import(`@/locales/${language}/index.ts`),
29
+ * });
30
+ */
31
+ declare function initI18next(options?: {
32
+ language?: string;
33
+ instance?: i18n;
34
+ loadLocale?: LocaleLoader;
35
+ }): i18n;
36
+ //#endregion
37
+ export { initI18next };
38
+ //# sourceMappingURL=i18n-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n-client.d.ts","names":[],"sources":["../src/i18n/types.ts","../src/i18n/client.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;KAoCY,YAAA,yBAAqC;WAAmB;;;;;;;;;;;;;;;;;iBCwBpD,WAAA;;aAAsD;eAAmB;IAAiB"}
@@ -0,0 +1,72 @@
1
+ import i18next from "i18next";
2
+ import { initReactI18next } from "react-i18next";
3
+ import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector";
4
+
5
+ //#region src/i18n/defaults.ts
6
+ /** Shared i18next interpolation config. Disables HTML escaping (React handles that) and adds `{{ value, number }}` formatting via `toLocaleString`. */
7
+ const defaultInterpolation = {
8
+ escapeValue: false,
9
+ format: (value, format) => {
10
+ if (format === "number" && typeof value === "number") return value.toLocaleString();
11
+ return value;
12
+ }
13
+ };
14
+
15
+ //#endregion
16
+ //#region src/i18n/client.ts
17
+ /**
18
+ * Custom i18next backend that calls the provided `loadLocale` callback to dynamically
19
+ * import translations. Keeping the import() call in the template lets Vite resolve the
20
+ * dynamic path at build time and split translations into per-language chunks.
21
+ */
22
+ function createDynamicImportBackend(instance, loadLocale) {
23
+ return {
24
+ type: "backend",
25
+ init() {},
26
+ read(language, namespace, callback) {
27
+ loadLocale(language).then((module) => {
28
+ const translations = module.default;
29
+ Object.entries(translations).forEach(([ns, nsTranslations]) => {
30
+ instance.addResourceBundle(language, ns, nsTranslations, true, true);
31
+ });
32
+ callback(null, translations[namespace] ?? {});
33
+ }).catch((error) => {
34
+ callback(error, false);
35
+ });
36
+ }
37
+ };
38
+ }
39
+ /**
40
+ * Initialize i18next on the client side.
41
+ * Pass a `loadLocale` callback containing the dynamic import so Vite can resolve it
42
+ * at build time relative to the template's source tree.
43
+ *
44
+ * @example
45
+ * // In root.tsx — Vite resolves the import() relative to this file
46
+ * initI18next({
47
+ * language: document.documentElement.lang || undefined,
48
+ * loadLocale: (language) => import(`@/locales/${language}/index.ts`),
49
+ * });
50
+ */
51
+ function initI18next(options) {
52
+ const language = options?.language;
53
+ const instance = options?.instance ?? i18next;
54
+ const loadLocale = options?.loadLocale;
55
+ if (language) instance.language = language;
56
+ const i18nextInstance = instance.use(initReactI18next);
57
+ if (loadLocale) i18nextInstance.use(createDynamicImportBackend(instance, loadLocale));
58
+ if (!language) i18nextInstance.use(I18nextBrowserLanguageDetector);
59
+ i18nextInstance.init({
60
+ ns: [],
61
+ ...language ? { lng: language } : { detection: {
62
+ order: ["htmlTag"],
63
+ caches: []
64
+ } },
65
+ interpolation: defaultInterpolation
66
+ });
67
+ return instance;
68
+ }
69
+
70
+ //#endregion
71
+ export { initI18next };
72
+ //# sourceMappingURL=i18n-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n-client.js","names":["defaultInterpolation: InterpolationOptions"],"sources":["../src/i18n/defaults.ts","../src/i18n/client.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 { InterpolationOptions } from 'i18next';\n\n/** Shared i18next interpolation config. Disables HTML escaping (React handles that) and adds `{{ value, number }}` formatting via `toLocaleString`. */\nexport const defaultInterpolation: InterpolationOptions = {\n escapeValue: false,\n format: (value, format) => {\n if (format === 'number' && typeof value === 'number') {\n return value.toLocaleString();\n }\n return value;\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 i18next, { type i18n, type BackendModule, type ReadCallback } from 'i18next';\nimport { initReactI18next } from 'react-i18next';\nimport I18nextBrowserLanguageDetector from 'i18next-browser-languagedetector';\nimport { defaultInterpolation } from './defaults.js';\nimport type { LocaleLoader } from './types.js';\n\n/**\n * Custom i18next backend that calls the provided `loadLocale` callback to dynamically\n * import translations. Keeping the import() call in the template lets Vite resolve the\n * dynamic path at build time and split translations into per-language chunks.\n */\nfunction createDynamicImportBackend(instance: i18n, loadLocale: LocaleLoader): BackendModule {\n return {\n type: 'backend',\n init() {\n // No initialization needed\n },\n read(language: string, namespace: string, callback: ReadCallback) {\n loadLocale(language)\n .then((module) => {\n const translations = module.default;\n Object.entries(translations).forEach(([ns, nsTranslations]) => {\n instance.addResourceBundle(language, ns, nsTranslations, true, true);\n });\n callback(null, translations[namespace] ?? {});\n })\n .catch((error: Error) => {\n callback(error, false);\n });\n },\n };\n}\n\n/**\n * Initialize i18next on the client side.\n * Pass a `loadLocale` callback containing the dynamic import so Vite can resolve it\n * at build time relative to the template's source tree.\n *\n * @example\n * // In root.tsx — Vite resolves the import() relative to this file\n * initI18next({\n * language: document.documentElement.lang || undefined,\n * loadLocale: (language) => import(`@/locales/${language}/index.ts`),\n * });\n */\nexport function initI18next(options?: { language?: string; instance?: i18n; loadLocale?: LocaleLoader }): i18n {\n // NOTE: For any changes to this function, verify that Vite HMR still works with translations\n\n const language = options?.language;\n const instance = options?.instance ?? i18next;\n const loadLocale = options?.loadLocale;\n\n if (language) {\n instance.language = language;\n }\n\n const i18nextInstance = instance.use(initReactI18next);\n\n if (loadLocale) {\n i18nextInstance.use(createDynamicImportBackend(instance, loadLocale));\n }\n\n if (!language) {\n i18nextInstance.use(I18nextBrowserLanguageDetector);\n }\n\n void i18nextInstance.init({\n ns: [],\n ...(language\n ? { lng: language }\n : {\n detection: { order: ['htmlTag'], caches: [] },\n }),\n interpolation: defaultInterpolation,\n });\n\n return instance;\n}\n"],"mappings":";;;;;;AAkBA,MAAaA,uBAA6C;CACtD,aAAa;CACb,SAAS,OAAO,WAAW;AACvB,MAAI,WAAW,YAAY,OAAO,UAAU,SACxC,QAAO,MAAM,gBAAgB;AAEjC,SAAO;;CAEd;;;;;;;;;ACAD,SAAS,2BAA2B,UAAgB,YAAyC;AACzF,QAAO;EACH,MAAM;EACN,OAAO;EAGP,KAAK,UAAkB,WAAmB,UAAwB;AAC9D,cAAW,SAAS,CACf,MAAM,WAAW;IACd,MAAM,eAAe,OAAO;AAC5B,WAAO,QAAQ,aAAa,CAAC,SAAS,CAAC,IAAI,oBAAoB;AAC3D,cAAS,kBAAkB,UAAU,IAAI,gBAAgB,MAAM,KAAK;MACtE;AACF,aAAS,MAAM,aAAa,cAAc,EAAE,CAAC;KAC/C,CACD,OAAO,UAAiB;AACrB,aAAS,OAAO,MAAM;KACxB;;EAEb;;;;;;;;;;;;;;AAeL,SAAgB,YAAY,SAAmF;CAG3G,MAAM,WAAW,SAAS;CAC1B,MAAM,WAAW,SAAS,YAAY;CACtC,MAAM,aAAa,SAAS;AAE5B,KAAI,SACA,UAAS,WAAW;CAGxB,MAAM,kBAAkB,SAAS,IAAI,iBAAiB;AAEtD,KAAI,WACA,iBAAgB,IAAI,2BAA2B,UAAU,WAAW,CAAC;AAGzE,KAAI,CAAC,SACD,iBAAgB,IAAI,+BAA+B;AAGvD,CAAK,gBAAgB,KAAK;EACtB,IAAI,EAAE;EACN,GAAI,WACE,EAAE,KAAK,UAAU,GACjB,EACI,WAAW;GAAE,OAAO,CAAC,UAAU;GAAE,QAAQ,EAAE;GAAE,EAChD;EACP,eAAe;EAClB,CAAC;AAEF,QAAO"}
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ import { MiddlewareFunction, RouterContextProvider } from "react-router";
2
+ import * as i18next0 from "i18next";
3
+ import { InterpolationOptions, Resource, ResourceLanguage, ThirdPartyModule, i18n } from "i18next";
4
+
5
+ //#region src/i18n/context.d.ts
6
+
7
+ /**
8
+ * Gets the i18next instance and translation function for non-component code.
9
+ * Use `useTranslation` hook for React components. Mirrors the `getConfig`/`useConfig` pattern.
10
+ */
11
+ declare function getTranslation(context?: Readonly<RouterContextProvider>): {
12
+ i18next: i18n;
13
+ t: i18next0.TFunction<["translation", ...string[]], undefined>;
14
+ };
15
+ /**
16
+ * Gets the active locale string from server context.
17
+ * Returns undefined on the client (locale is on the document element or URL).
18
+ */
19
+ declare function getLocale(context: Readonly<RouterContextProvider>): string | undefined;
20
+ /**
21
+ * Sets up a mock i18n context on a RouterContextProvider for use in tests.
22
+ * Replaces the need to import the internal i18nextContext key directly.
23
+ */
24
+ declare function mockI18nContext(contextProvider: RouterContextProvider, options?: {
25
+ locale?: string;
26
+ instance?: i18n;
27
+ }): void;
28
+ //#endregion
29
+ //#region src/i18n/types.d.ts
30
+
31
+ /** Config passed to `createI18nMiddleware`. All values come from the template — the SDK never reads config values directly. */
32
+ interface I18nMiddlewareConfig {
33
+ resources: Resource;
34
+ supportedLanguages: string[];
35
+ fallbackLanguage: string;
36
+ interpolation?: InterpolationOptions;
37
+ plugins?: ThirdPartyModule[];
38
+ }
39
+ /**
40
+ * Callback that dynamically imports all translations for a given language.
41
+ * Must be defined in template code so Vite can resolve the `import()` path at build time
42
+ * and split translations into per-language chunks.
43
+ *
44
+ * @example
45
+ * const loadLocale: LocaleLoader = (language) => import(`@/locales/${language}/index.ts`);
46
+ */
47
+ type LocaleLoader = (language: string) => Promise<{
48
+ default: ResourceLanguage;
49
+ }>;
50
+ //#endregion
51
+ //#region src/i18n/middleware.d.ts
52
+ /**
53
+ * Creates a server-side i18next middleware from the provided config.
54
+ * Lazy-initializes on first request so supported languages can come from runtime config.
55
+ */
56
+ declare function createI18nMiddleware(config: I18nMiddlewareConfig): MiddlewareFunction<Response>;
57
+ //#endregion
58
+ //#region src/i18n/defaults.d.ts
59
+ /** Shared i18next interpolation config. Disables HTML escaping (React handles that) and adds `{{ value, number }}` formatting via `toLocaleString`. */
60
+ declare const defaultInterpolation: InterpolationOptions;
61
+ //#endregion
62
+ export { type I18nMiddlewareConfig, type LocaleLoader, type Resource, type ResourceLanguage, createI18nMiddleware, defaultInterpolation, getLocale, getTranslation, mockI18nContext };
63
+ //# sourceMappingURL=i18n.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.d.ts","names":[],"sources":["../src/i18n/context.ts","../src/i18n/types.ts","../src/i18n/middleware.ts","../src/i18n/defaults.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AE2BA;AAA6C,iBFI7B,cAAA,CEJ6B,OAAA,CAAA,EFIJ,QEJI,CFIK,qBEJL,CAAA,CAAA,EAAA;EAA0C,OAAA,MAAA;EAAnB,CAAA,oBAAA,CAAA,CAAA,aAAA,EAAA,GAAA,MAAA,EAAA,CAAA,EAAA,SAAA,CAAA;CAAkB;;;;ACTtF;iBHqCgB,SAAA,UAAmB,SAAS;;;;;iBAQ5B,eAAA,kBACK;;aACsB;;;;;;ACzCvB,UAJH,oBAAA,CAIG;EACN,SAAA,EAJC,QAID;EAAgB,kBAAA,EAAA,MAAA,EAAA;EAWlB,gBAAY,EAAA,MAAA;kBAZJ;YACN;;ACEd;;;;;;;;ACTa,KFkBD,YAAA,GEVX,CAAA,QAAA,EAAA,MARkC,EAAA,GFkBc,OElBd,CAAA;WFkBiC;;;;;;;ADLpE;AAAkD,iBEJlC,oBAAA,CFIkC,MAAA,EEJL,oBFIK,CAAA,EEJkB,kBFIlB,CEJqC,QFIrC,CAAA;;;ACXlD;AACe,cEHF,oBFGE,EEHoB,oBFGpB"}
package/dist/i18n.js ADDED
@@ -0,0 +1,98 @@
1
+ import { a as requestToLocaleMap } from "./site-context2.js";
2
+ import "./apply-url-config.js";
3
+ import { createContext } from "react-router";
4
+ import i18next from "i18next";
5
+ import { initReactI18next } from "react-i18next";
6
+ import { createI18nextMiddleware } from "remix-i18next/middleware";
7
+
8
+ //#region src/i18n/context.ts
9
+ const i18nextContext = createContext(null);
10
+ /**
11
+ * Gets the i18next instance and translation function for non-component code.
12
+ * Use `useTranslation` hook for React components. Mirrors the `getConfig`/`useConfig` pattern.
13
+ */
14
+ function getTranslation(context) {
15
+ if (context && typeof window === "undefined") {
16
+ const i18nextData = context.get(i18nextContext);
17
+ if (!i18nextData) throw new Error("i18next data not found in context. Ensure i18next middleware runs before loaders.");
18
+ const i18nextInstance = i18nextData.getI18nextInstance();
19
+ return {
20
+ i18next: i18nextInstance,
21
+ t: i18nextInstance.t
22
+ };
23
+ }
24
+ return {
25
+ i18next,
26
+ t: i18next.t
27
+ };
28
+ }
29
+ /**
30
+ * Gets the active locale string from server context.
31
+ * Returns undefined on the client (locale is on the document element or URL).
32
+ */
33
+ function getLocale(context) {
34
+ return context.get(i18nextContext)?.getLocale();
35
+ }
36
+ /**
37
+ * Sets up a mock i18n context on a RouterContextProvider for use in tests.
38
+ * Replaces the need to import the internal i18nextContext key directly.
39
+ */
40
+ function mockI18nContext(contextProvider, options = {}) {
41
+ const { locale = "en-GB", instance = i18next } = options;
42
+ contextProvider.set(i18nextContext, {
43
+ getLocale: () => locale,
44
+ getI18nextInstance: () => instance
45
+ });
46
+ }
47
+
48
+ //#endregion
49
+ //#region src/i18n/defaults.ts
50
+ /** Shared i18next interpolation config. Disables HTML escaping (React handles that) and adds `{{ value, number }}` formatting via `toLocaleString`. */
51
+ const defaultInterpolation = {
52
+ escapeValue: false,
53
+ format: (value, format) => {
54
+ if (format === "number" && typeof value === "number") return value.toLocaleString();
55
+ return value;
56
+ }
57
+ };
58
+
59
+ //#endregion
60
+ //#region src/i18n/middleware.ts
61
+ /**
62
+ * Creates a server-side i18next middleware from the provided config.
63
+ * Lazy-initializes on first request so supported languages can come from runtime config.
64
+ */
65
+ function createI18nMiddleware(config) {
66
+ const { resources, supportedLanguages, fallbackLanguage, interpolation, plugins = [] } = config;
67
+ let cached = null;
68
+ return async (args, next) => {
69
+ if (!cached) cached = createI18nextMiddleware({
70
+ detection: {
71
+ order: ["custom"],
72
+ findLocale: async (request) => {
73
+ return requestToLocaleMap.get(request) ?? null;
74
+ },
75
+ fallbackLanguage,
76
+ supportedLanguages
77
+ },
78
+ i18next: {
79
+ resources,
80
+ interpolation: {
81
+ ...defaultInterpolation,
82
+ ...interpolation
83
+ }
84
+ },
85
+ plugins: [initReactI18next, ...plugins]
86
+ });
87
+ const [originalMiddleware, getLocale$1, getInstance] = cached;
88
+ args.context.set(i18nextContext, {
89
+ getLocale: () => getLocale$1(args.context),
90
+ getI18nextInstance: () => getInstance(args.context)
91
+ });
92
+ return originalMiddleware(args, next);
93
+ };
94
+ }
95
+
96
+ //#endregion
97
+ export { createI18nMiddleware, defaultInterpolation, getLocale, getTranslation, mockI18nContext };
98
+ //# sourceMappingURL=i18n.js.map