@salesforce/storefront-next-runtime 0.4.1 → 1.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/config.d.ts +33 -221
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +34 -116
- package/dist/config.js.map +1 -1
- package/dist/data-store.d.ts +185 -15
- package/dist/data-store.d.ts.map +1 -1
- package/dist/data-store.js +412 -10
- package/dist/data-store.js.map +1 -1
- package/dist/design-data.d.ts +266 -62
- package/dist/design-data.d.ts.map +1 -1
- package/dist/design-data.js +399 -14
- package/dist/design-data.js.map +1 -1
- package/dist/design-mode.d.ts +3 -2
- package/dist/design-mode.d.ts.map +1 -1
- package/dist/events.d.ts +32 -6
- package/dist/events.d.ts.map +1 -1
- package/dist/i18n-client.d.ts.map +1 -1
- package/dist/i18n-client.js.map +1 -1
- package/dist/i18n.d.ts +1 -2
- package/dist/i18n.d.ts.map +1 -1
- package/dist/modeDetection.js +0 -18
- package/dist/modeDetection.js.map +1 -1
- package/dist/scapi.d.ts +2185 -466
- package/dist/scapi.d.ts.map +1 -1
- package/dist/scapi.js +1 -1
- package/dist/scapi.js.map +1 -1
- package/dist/schema.d.ts +17 -15
- package/dist/schema.d.ts.map +1 -1
- package/dist/site-context.d.ts +43 -27
- package/dist/site-context.d.ts.map +1 -1
- package/dist/site-context.js +2 -2
- package/dist/site-context2.js +41 -31
- package/dist/site-context2.js.map +1 -1
- package/dist/types.d.ts +19 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types2.d.ts +89 -63
- package/dist/types2.d.ts.map +1 -1
- package/package.json +2 -20
- package/dist/custom-global-preferences.d.ts +0 -20
- package/dist/custom-global-preferences.d.ts.map +0 -1
- package/dist/custom-global-preferences.js +0 -31
- package/dist/custom-global-preferences.js.map +0 -1
- package/dist/custom-site-preferences.d.ts +0 -20
- package/dist/custom-site-preferences.d.ts.map +0 -1
- package/dist/custom-site-preferences.js +0 -31
- package/dist/custom-site-preferences.js.map +0 -1
- package/dist/data-store-custom-global-preferences.d.ts +0 -2
- package/dist/data-store-custom-global-preferences.js +0 -6
- package/dist/data-store-custom-site-preferences.d.ts +0 -2
- package/dist/data-store-custom-site-preferences.js +0 -6
- package/dist/data-store-gcp-preferences.d.ts +0 -2
- package/dist/data-store-gcp-preferences.js +0 -6
- package/dist/gcp-preferences.d.ts +0 -52
- package/dist/gcp-preferences.d.ts.map +0 -1
- package/dist/gcp-preferences.js +0 -64
- package/dist/gcp-preferences.js.map +0 -1
- package/dist/utils.js +0 -90
- package/dist/utils.js.map +0 -1
package/dist/data-store.js
CHANGED
|
@@ -1,14 +1,403 @@
|
|
|
1
|
-
import "./site-context2.js";
|
|
1
|
+
import { i as siteContext } from "./site-context2.js";
|
|
2
2
|
import "./apply-url-config.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { i as getCustomGlobalPreferences, n as customGlobalPreferencesContext, r as customGlobalPreferencesMiddleware, t as DEFAULT_CUSTOM_GLOBAL_PREFERENCES_KEY } from "./custom-global-preferences.js";
|
|
6
|
-
import { a as getGcpPreferences, i as getGcpApiKey, n as gcpPreferencesContext, r as gcpPreferencesMiddleware, t as DEFAULT_GCP_PREFERENCES_KEY } from "./gcp-preferences.js";
|
|
7
|
-
import { DataStore, DataStoreNotFoundError, DataStoreServiceError, DataStoreUnavailableError } from "@salesforce/mrt-utilities/data-store";
|
|
3
|
+
import { createContext } from "react-router";
|
|
4
|
+
import { DataStore, DataStore as DataStore$1, DataStoreNotFoundError, DataStoreNotFoundError as DataStoreNotFoundError$1, DataStoreServiceError, DataStoreServiceError as DataStoreServiceError$1, DataStoreUnavailableError, DataStoreUnavailableError as DataStoreUnavailableError$1 } from "@salesforce/mrt-utilities/data-store";
|
|
8
5
|
|
|
6
|
+
//#region src/data-store/logger-context.ts
|
|
7
|
+
function formatMessage(message, metadata) {
|
|
8
|
+
if (!metadata) return message;
|
|
9
|
+
try {
|
|
10
|
+
return `${message} ${JSON.stringify(metadata, replacerForErrors)}`;
|
|
11
|
+
} catch {
|
|
12
|
+
return `${message} [unserializable metadata]`;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function replacerForErrors(_key, value) {
|
|
16
|
+
if (value instanceof Error) return {
|
|
17
|
+
name: value.name,
|
|
18
|
+
message: value.message,
|
|
19
|
+
...value.stack && { stack: value.stack }
|
|
20
|
+
};
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Default logger used when nothing has been injected via
|
|
25
|
+
* {@link dataStoreLoggerContext}. Routes warnings to `console.warn` and
|
|
26
|
+
* errors to `console.error` so diagnostics remain visible in environments
|
|
27
|
+
* (tests, scripts, hosts that haven't wired a structured logger) where the
|
|
28
|
+
* SDK is invoked outside the storefront template. `info` and `debug` are
|
|
29
|
+
* no-ops to avoid noisy default output.
|
|
30
|
+
*/
|
|
31
|
+
const consoleLogger = Object.freeze({
|
|
32
|
+
error(message, metadata) {
|
|
33
|
+
console.error(formatMessage(message, metadata));
|
|
34
|
+
},
|
|
35
|
+
warn(message, metadata) {
|
|
36
|
+
console.warn(formatMessage(message, metadata));
|
|
37
|
+
},
|
|
38
|
+
info() {},
|
|
39
|
+
debug() {}
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Router context the SDK reads to obtain a request-scoped structured logger.
|
|
43
|
+
*
|
|
44
|
+
* Hosts (e.g. the storefront template) populate this from their own logging
|
|
45
|
+
* middleware. When unset, {@link getDataStoreLogger} falls back to a
|
|
46
|
+
* console-based logger so warnings remain visible.
|
|
47
|
+
*
|
|
48
|
+
* Defaults to `null` (not `undefined`) because React Router's
|
|
49
|
+
* `context.get()` throws when `defaultValue === undefined`.
|
|
50
|
+
*/
|
|
51
|
+
const dataStoreLoggerContext = createContext(null);
|
|
52
|
+
/**
|
|
53
|
+
* Read the data-store logger from router context, falling back to a
|
|
54
|
+
* console-based default when nothing has been injected.
|
|
55
|
+
*
|
|
56
|
+
* Use this from inside SDK middleware/loaders that have access to a
|
|
57
|
+
* {@link RouterContextProvider}.
|
|
58
|
+
*/
|
|
59
|
+
function getDataStoreLogger(context) {
|
|
60
|
+
return context.get(dataStoreLoggerContext) ?? consoleLogger;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/data-store/utils.ts
|
|
65
|
+
/**
|
|
66
|
+
* Creates a typed React Router context for data store entries.
|
|
67
|
+
*
|
|
68
|
+
* Initializes the context with `null` so middleware can populate it during requests.
|
|
69
|
+
*
|
|
70
|
+
* @returns React Router context key for data store values
|
|
71
|
+
*/
|
|
72
|
+
function createDataStoreContext() {
|
|
73
|
+
return createContext(null);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Creates a React Router middleware that fetches a single MRT data store entry on every
|
|
77
|
+
* request and stores the resulting value in the supplied router context.
|
|
78
|
+
*
|
|
79
|
+
* Failure handling is controlled by `options.onUnavailable`:
|
|
80
|
+
* - `'throw'` (default for the factory): rethrow `DataStoreUnavailableError` and
|
|
81
|
+
* `DataStoreServiceError` with a stable error message. Fail-fast — the request errors out.
|
|
82
|
+
* - `'fallback'`: log a warning and resolve to `options.fallbackValue` when configured, or
|
|
83
|
+
* to the missing state (context not populated) when no `fallbackValue` is provided. The
|
|
84
|
+
* request continues without crashing the middleware chain.
|
|
85
|
+
*
|
|
86
|
+
* `DataStoreNotFoundError` is always treated as "missing" (warn, do not populate context),
|
|
87
|
+
* regardless of `onUnavailable` — a not-found entry is an expected steady-state for
|
|
88
|
+
* features that haven't been published yet, not a service failure.
|
|
89
|
+
*
|
|
90
|
+
* Errors thrown from `options.transform` propagate to the caller — they indicate a
|
|
91
|
+
* programmer error in the middleware definition, not data-store unavailability.
|
|
92
|
+
*
|
|
93
|
+
* @param options - See {@link DataStoreMiddlewareOptions}.
|
|
94
|
+
* @returns React Router middleware for server requests.
|
|
95
|
+
*
|
|
96
|
+
* @env AWS_REGION (required): AWS region for the data store table (e.g., `us-east-1`).
|
|
97
|
+
* @env MOBIFY_PROPERTY_ID (required): MRT property identifier.
|
|
98
|
+
* @env DEPLOY_TARGET (required): MRT deploy target (e.g., `production`).
|
|
99
|
+
*/
|
|
100
|
+
function createDataStoreMiddleware(options) {
|
|
101
|
+
const { entryKey, context: contextKey, onUnavailable = "throw", fallbackValue } = options;
|
|
102
|
+
const transform = options.transform ?? ((value) => value);
|
|
103
|
+
const dataStoreMiddleware$1 = async ({ context }, next) => {
|
|
104
|
+
const result = await loadDataStoreEntry({
|
|
105
|
+
entryKey: typeof entryKey === "function" ? entryKey(context) : entryKey,
|
|
106
|
+
context,
|
|
107
|
+
transform,
|
|
108
|
+
onUnavailable,
|
|
109
|
+
fallbackValue
|
|
110
|
+
});
|
|
111
|
+
if (result.state === "value" || result.state === "fallback") context.set(contextKey, result.value);
|
|
112
|
+
return next();
|
|
113
|
+
};
|
|
114
|
+
return dataStoreMiddleware$1;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Lazy variant of {@link createDataStoreMiddleware}. Instead of fetching the
|
|
118
|
+
* entry up-front during middleware execution, this stores a memoized loader
|
|
119
|
+
* in the router context. Consumers call {@link readLazyDataStoreEntry} to
|
|
120
|
+
* trigger the fetch on demand — pages that never read the value never pay
|
|
121
|
+
* for the data-store call.
|
|
122
|
+
*
|
|
123
|
+
* Repeated reads within the same request share the in-flight promise so
|
|
124
|
+
* the entry is fetched at most once per request.
|
|
125
|
+
*
|
|
126
|
+
* Failure handling matches the eager variant: `onUnavailable` and
|
|
127
|
+
* `fallbackValue` are honored when the underlying fetch fails. The fallback
|
|
128
|
+
* value (or `null` for the missing state) surfaces through
|
|
129
|
+
* {@link readLazyDataStoreEntry}.
|
|
130
|
+
*
|
|
131
|
+
* Use this for entries that only a subset of routes consume (e.g. config
|
|
132
|
+
* read by a single feature) rather than entries needed on every request.
|
|
133
|
+
*/
|
|
134
|
+
function createLazyDataStoreMiddleware(options) {
|
|
135
|
+
const { entryKey, context: contextKey, onUnavailable = "throw", fallbackValue } = options;
|
|
136
|
+
const transform = options.transform ?? ((value) => value);
|
|
137
|
+
const lazyMiddleware = async ({ context }, next) => {
|
|
138
|
+
let pending;
|
|
139
|
+
const loader = () => {
|
|
140
|
+
if (!pending) pending = loadDataStoreEntry({
|
|
141
|
+
entryKey: typeof entryKey === "function" ? entryKey(context) : entryKey,
|
|
142
|
+
context,
|
|
143
|
+
transform,
|
|
144
|
+
onUnavailable,
|
|
145
|
+
fallbackValue
|
|
146
|
+
}).then((result) => result.state === "missing" ? null : result.value);
|
|
147
|
+
return pending;
|
|
148
|
+
};
|
|
149
|
+
context.set(contextKey, loader);
|
|
150
|
+
return next();
|
|
151
|
+
};
|
|
152
|
+
return lazyMiddleware;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Reads a value populated by {@link createLazyDataStoreMiddleware}. Triggers
|
|
156
|
+
* the underlying data-store fetch on first call and reuses the cached
|
|
157
|
+
* promise on subsequent calls within the same request.
|
|
158
|
+
*
|
|
159
|
+
* Returns `null` when the lazy middleware did not run (no loader in
|
|
160
|
+
* context) or when the entry is missing/invalid.
|
|
161
|
+
*/
|
|
162
|
+
async function readLazyDataStoreEntry(context, contextKey) {
|
|
163
|
+
const loader = context.get(contextKey);
|
|
164
|
+
if (typeof loader !== "function") return null;
|
|
165
|
+
return loader();
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Internal helper shared by the eager and lazy middleware factories.
|
|
169
|
+
* Performs the fetch + transform pipeline and resolves all three error
|
|
170
|
+
* paths (unavailable / not-found / service-error) consistently. Returns a
|
|
171
|
+
* tagged result so callers can decide whether to populate the context
|
|
172
|
+
* synchronously (eager middleware) or hand the value back to a lazy reader.
|
|
173
|
+
*/
|
|
174
|
+
async function loadDataStoreEntry(args) {
|
|
175
|
+
const { entryKey, context, transform, onUnavailable, fallbackValue } = args;
|
|
176
|
+
const logger = getDataStoreLogger(context);
|
|
177
|
+
try {
|
|
178
|
+
const entry = await getDataStoreEntry(entryKey);
|
|
179
|
+
if (!entry?.value || typeof entry.value !== "object") {
|
|
180
|
+
logger.warn(`Data store entry '${entryKey}' not found or invalid.`, { entryKey });
|
|
181
|
+
return { state: "missing" };
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
state: "value",
|
|
185
|
+
value: transform(entry.value)
|
|
186
|
+
};
|
|
187
|
+
} catch (error) {
|
|
188
|
+
if (error instanceof DataStoreNotFoundError$1) {
|
|
189
|
+
logger.warn(`Data store entry '${entryKey}' not found.`, {
|
|
190
|
+
entryKey,
|
|
191
|
+
error
|
|
192
|
+
});
|
|
193
|
+
return { state: "missing" };
|
|
194
|
+
}
|
|
195
|
+
if (error instanceof DataStoreUnavailableError$1 || error instanceof DataStoreServiceError$1) return resolveDataStoreFallback({
|
|
196
|
+
entryKey,
|
|
197
|
+
context,
|
|
198
|
+
error,
|
|
199
|
+
onUnavailable,
|
|
200
|
+
fallbackValue,
|
|
201
|
+
logger
|
|
202
|
+
});
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function resolveDataStoreFallback(args) {
|
|
207
|
+
const { entryKey, context, error, onUnavailable, fallbackValue, logger } = args;
|
|
208
|
+
const reason = error instanceof DataStoreServiceError$1 ? "service error" : "unavailable";
|
|
209
|
+
if (onUnavailable === "fallback") {
|
|
210
|
+
if (typeof fallbackValue !== "undefined") {
|
|
211
|
+
const resolved = typeof fallbackValue === "function" ? fallbackValue(context) : fallbackValue;
|
|
212
|
+
logger.warn(`Data store ${reason} for '${entryKey}'. Using configured fallback value.`, {
|
|
213
|
+
entryKey,
|
|
214
|
+
reason,
|
|
215
|
+
error
|
|
216
|
+
});
|
|
217
|
+
return {
|
|
218
|
+
state: "fallback",
|
|
219
|
+
value: resolved
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
logger.warn(`Data store ${reason} for '${entryKey}'. No fallback configured; treating entry as missing.`, {
|
|
223
|
+
entryKey,
|
|
224
|
+
reason,
|
|
225
|
+
error
|
|
226
|
+
});
|
|
227
|
+
return { state: "missing" };
|
|
228
|
+
}
|
|
229
|
+
if (error instanceof DataStoreUnavailableError$1) throw new Error("Data store is unavailable. Ensure AWS_REGION, MOBIFY_PROPERTY_ID, and DEPLOY_TARGET are set.");
|
|
230
|
+
throw new Error(`Data store request failed for '${entryKey}'.`);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Read a data-store entry through the singleton MRT utilities API.
|
|
234
|
+
* The underlying implementation (production DynamoDB vs development pseudo store)
|
|
235
|
+
* is resolved by `@salesforce/mrt-utilities/data-store` export conditions.
|
|
236
|
+
*
|
|
237
|
+
* @param key - Data-store entry key
|
|
238
|
+
* @returns Data-store entry or null when missing/invalid shape
|
|
239
|
+
*/
|
|
240
|
+
async function getDataStoreEntry(key) {
|
|
241
|
+
const entry = await DataStore$1.getDataStore().getEntry(key);
|
|
242
|
+
if (!entry || typeof entry !== "object") return null;
|
|
243
|
+
return entry;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Creates an entryKey function that prefixes the given suffix with the current site ID.
|
|
247
|
+
*
|
|
248
|
+
* @param suffix - The entry key suffix (e.g., "custom-site-preferences")
|
|
249
|
+
* @returns A function compatible with `DataStoreMiddlewareOptions.entryKey`
|
|
250
|
+
*/
|
|
251
|
+
function prefixWithSiteId(suffix) {
|
|
252
|
+
return (context) => {
|
|
253
|
+
const siteId = context.get(siteContext)?.site?.id;
|
|
254
|
+
if (!siteId) throw new Error("Site id not found. Ensure site context middleware runs before data-store middleware.");
|
|
255
|
+
return `${siteId}-${suffix}`;
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/data-store/middleware/custom-site-preferences.ts
|
|
261
|
+
const sitePreferencesContext = createDataStoreContext();
|
|
262
|
+
/**
|
|
263
|
+
* Read site preferences from router context.
|
|
264
|
+
*
|
|
265
|
+
* @param context - Router context provider
|
|
266
|
+
* @returns Site preferences data stored by data-store middleware
|
|
267
|
+
*/
|
|
268
|
+
function getSitePreferences(context) {
|
|
269
|
+
const data = context.get(sitePreferencesContext);
|
|
270
|
+
if (!data) {
|
|
271
|
+
getDataStoreLogger(context).warn("Data store context not found. Ensure data-store middleware runs before loaders and the required env vars are set.");
|
|
272
|
+
return {};
|
|
273
|
+
}
|
|
274
|
+
return data;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Middleware that reads the site-scoped `custom-site-preferences` entry from the MRT data
|
|
278
|
+
* store and stores it in {@link sitePreferencesContext}. The entry key is prefixed with
|
|
279
|
+
* the current site id (e.g. `acme-custom-site-preferences`).
|
|
280
|
+
*
|
|
281
|
+
* Defaults to graceful degradation: if the data store is unavailable or returns a service
|
|
282
|
+
* error, the request continues with `{}` as the preferences value rather than crashing.
|
|
283
|
+
* Set `SFNEXT_DATA_STORE_UNAVAILABLE_MODE=throw` in the environment to opt back into
|
|
284
|
+
* fail-fast behavior. The env var is read once at module load.
|
|
285
|
+
*
|
|
286
|
+
* Must run after the site-context middleware (so the site id is available for the entry
|
|
287
|
+
* key) and before any loader that calls {@link getSitePreferences}.
|
|
288
|
+
*/
|
|
289
|
+
const customSitePreferencesMiddleware = createDataStoreMiddleware({
|
|
290
|
+
entryKey: prefixWithSiteId("custom-site-preferences"),
|
|
291
|
+
context: sitePreferencesContext,
|
|
292
|
+
onUnavailable: process.env.SFNEXT_DATA_STORE_UNAVAILABLE_MODE === "throw" ? "throw" : "fallback",
|
|
293
|
+
fallbackValue: {}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/data-store/middleware/custom-global-preferences.ts
|
|
298
|
+
const DEFAULT_CUSTOM_GLOBAL_PREFERENCES_KEY = "custom-global-preferences";
|
|
299
|
+
const customGlobalPreferencesContext = createDataStoreContext();
|
|
300
|
+
/**
|
|
301
|
+
* Read custom global preferences from router context.
|
|
302
|
+
*
|
|
303
|
+
* @param context - Router context provider
|
|
304
|
+
* @returns Custom global preferences data stored by data-store middleware
|
|
305
|
+
*/
|
|
306
|
+
function getCustomGlobalPreferences(context) {
|
|
307
|
+
const data = context.get(customGlobalPreferencesContext);
|
|
308
|
+
if (!data) {
|
|
309
|
+
getDataStoreLogger(context).warn("Custom global preferences context not found. Ensure data-store middleware runs before loaders and the required env vars are set.");
|
|
310
|
+
return {};
|
|
311
|
+
}
|
|
312
|
+
return data;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Middleware that reads the global `custom-global-preferences` entry from the MRT data
|
|
316
|
+
* store and stores it in {@link customGlobalPreferencesContext}.
|
|
317
|
+
*
|
|
318
|
+
* Defaults to graceful degradation: if the data store is unavailable or returns a service
|
|
319
|
+
* error, the request continues with `{}` as the preferences value rather than crashing.
|
|
320
|
+
* Set `SFNEXT_DATA_STORE_UNAVAILABLE_MODE=throw` in the environment to opt back into
|
|
321
|
+
* fail-fast behavior. The env var is read once at module load.
|
|
322
|
+
*/
|
|
323
|
+
const customGlobalPreferencesMiddleware = createDataStoreMiddleware({
|
|
324
|
+
entryKey: DEFAULT_CUSTOM_GLOBAL_PREFERENCES_KEY,
|
|
325
|
+
context: customGlobalPreferencesContext,
|
|
326
|
+
onUnavailable: process.env.SFNEXT_DATA_STORE_UNAVAILABLE_MODE === "throw" ? "throw" : "fallback",
|
|
327
|
+
fallbackValue: {}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
//#endregion
|
|
331
|
+
//#region src/data-store/middleware/gcp-preferences.ts
|
|
332
|
+
const DEFAULT_GCP_PREFERENCES_KEY = "gcp";
|
|
333
|
+
/**
|
|
334
|
+
* Map keys inside the `gcp` data store entry. The ECOM MRT sync job writes
|
|
335
|
+
* to these exact keys; keep in sync with the sync job contract.
|
|
336
|
+
*/
|
|
337
|
+
const API_KEY_MAP_KEY = "api-key";
|
|
338
|
+
const gcpPreferencesContext = createDataStoreContext();
|
|
339
|
+
/**
|
|
340
|
+
* Read the GCP (Google Cloud Platform) preferences object from router context.
|
|
341
|
+
*
|
|
342
|
+
* The preferences are sourced from the MRT data store entry `gcp`, which is
|
|
343
|
+
* populated only for storefronts connecting to production ECOM instances.
|
|
344
|
+
* In non-production environments, or when the entry is missing, returns an
|
|
345
|
+
* object whose fields are all empty/default.
|
|
346
|
+
*
|
|
347
|
+
* @param context - Router context provider
|
|
348
|
+
* @returns GCP preferences object; fields are empty/default when the entry is unavailable
|
|
349
|
+
*/
|
|
350
|
+
function getGcpPreferences(context) {
|
|
351
|
+
const data = context.get(gcpPreferencesContext);
|
|
352
|
+
if (data === null) {
|
|
353
|
+
getDataStoreLogger(context).warn("GCP preferences context not found. Ensure gcpPreferencesMiddleware runs before loaders, or expect empty values in environments without the MRT data store entry.");
|
|
354
|
+
return { apiKey: "" };
|
|
355
|
+
}
|
|
356
|
+
return data;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Convenience getter for the Google Cloud API key alone.
|
|
360
|
+
*
|
|
361
|
+
* Equivalent to `getGcpPreferences(context).apiKey`.
|
|
362
|
+
*
|
|
363
|
+
* @param context - Router context provider
|
|
364
|
+
* @returns The GCP API key, or an empty string when unavailable
|
|
365
|
+
*/
|
|
366
|
+
function getGcpApiKey(context) {
|
|
367
|
+
return getGcpPreferences(context).apiKey;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Middleware that reads the OOTB GCP preferences from the MRT data store and
|
|
371
|
+
* stores them in the router context. The entry shape is `{ "api-key": string, ... }`
|
|
372
|
+
* under data store key `gcp`. Missing/invalid fields coerce to empty/default values.
|
|
373
|
+
*
|
|
374
|
+
* Only available for storefronts connecting to production ECOM instances. When the entry
|
|
375
|
+
* is not synced (e.g. the GCP feature flag is off in ECOM), the underlying fetch surfaces
|
|
376
|
+
* as `DataStoreNotFoundError` and the context is left unset; consumers see the empty
|
|
377
|
+
* default `{ apiKey: '' }` via {@link getGcpPreferences}.
|
|
378
|
+
*
|
|
379
|
+
* Defaults to graceful degradation: if the data store is unavailable or returns a service
|
|
380
|
+
* error, the request continues with `{ apiKey: '' }` rather than crashing. Set
|
|
381
|
+
* `SFNEXT_DATA_STORE_UNAVAILABLE_MODE=throw` in the environment to opt back into
|
|
382
|
+
* fail-fast behavior. The env var is read once at module load.
|
|
383
|
+
*
|
|
384
|
+
* Must run before any loader/middleware that reads `getGcpPreferences(context)` or
|
|
385
|
+
* `getGcpApiKey(context)`.
|
|
386
|
+
*/
|
|
387
|
+
const gcpPreferencesMiddleware = createDataStoreMiddleware({
|
|
388
|
+
entryKey: DEFAULT_GCP_PREFERENCES_KEY,
|
|
389
|
+
context: gcpPreferencesContext,
|
|
390
|
+
onUnavailable: process.env.SFNEXT_DATA_STORE_UNAVAILABLE_MODE === "throw" ? "throw" : "fallback",
|
|
391
|
+
fallbackValue: { apiKey: "" },
|
|
392
|
+
transform: (value) => {
|
|
393
|
+
const rawKey = value[API_KEY_MAP_KEY];
|
|
394
|
+
return { apiKey: typeof rawKey === "string" ? rawKey : "" };
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
//#endregion
|
|
9
399
|
//#region src/data-store/middleware/login-preferences.ts
|
|
10
400
|
const loginPreferencesContext = createDataStoreContext();
|
|
11
|
-
const DATA_STORE_UNAVAILABLE_MODE = process.env.SFNEXT_DATA_STORE_UNAVAILABLE_MODE;
|
|
12
401
|
/**
|
|
13
402
|
* Read login preferences from router context.
|
|
14
403
|
*
|
|
@@ -18,15 +407,28 @@ const DATA_STORE_UNAVAILABLE_MODE = process.env.SFNEXT_DATA_STORE_UNAVAILABLE_MO
|
|
|
18
407
|
function getLoginPreferences(context) {
|
|
19
408
|
const data = context.get(loginPreferencesContext);
|
|
20
409
|
if (!data) {
|
|
21
|
-
|
|
410
|
+
getDataStoreLogger(context).warn("Login preferences context not found. Ensure data-store middleware runs before loaders and the required env vars are set.");
|
|
22
411
|
return {};
|
|
23
412
|
}
|
|
24
413
|
return data;
|
|
25
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* Middleware that reads the site-scoped `login-preferences` entry from the MRT data store
|
|
417
|
+
* and stores its `data` field in {@link loginPreferencesContext}. The entry key is
|
|
418
|
+
* prefixed with the current site id (e.g. `acme-login-preferences`).
|
|
419
|
+
*
|
|
420
|
+
* Defaults to graceful degradation: if the data store is unavailable or returns a service
|
|
421
|
+
* error, the request continues with `{ emailVerificationEnabled: false }` rather than
|
|
422
|
+
* crashing. Set `SFNEXT_DATA_STORE_UNAVAILABLE_MODE=throw` in the environment to opt back
|
|
423
|
+
* into fail-fast behavior. The env var is read once at module load.
|
|
424
|
+
*
|
|
425
|
+
* Must run after the site-context middleware (so the site id is available for the entry
|
|
426
|
+
* key) and before any loader that calls {@link getLoginPreferences}.
|
|
427
|
+
*/
|
|
26
428
|
const loginPreferencesMiddleware = createDataStoreMiddleware({
|
|
27
429
|
entryKey: prefixWithSiteId("login-preferences"),
|
|
28
430
|
context: loginPreferencesContext,
|
|
29
|
-
onUnavailable:
|
|
431
|
+
onUnavailable: process.env.SFNEXT_DATA_STORE_UNAVAILABLE_MODE === "throw" ? "throw" : "fallback",
|
|
30
432
|
fallbackValue: { emailVerificationEnabled: false },
|
|
31
433
|
transform: (value) => value.data
|
|
32
434
|
});
|
|
@@ -41,5 +443,5 @@ const dataStoreMiddleware = [
|
|
|
41
443
|
];
|
|
42
444
|
|
|
43
445
|
//#endregion
|
|
44
|
-
export {
|
|
446
|
+
export { DataStore, DataStoreNotFoundError, DataStoreServiceError, DataStoreUnavailableError, createDataStoreContext, createDataStoreMiddleware, createLazyDataStoreMiddleware, dataStoreLoggerContext, dataStoreMiddleware, getCustomGlobalPreferences, getDataStoreEntry, getDataStoreLogger, getGcpApiKey, getGcpPreferences, getLoginPreferences, getSitePreferences, readLazyDataStoreEntry };
|
|
45
447
|
//# sourceMappingURL=data-store.js.map
|
package/dist/data-store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-store.js","names":[],"sources":["../src/data-store/middleware/login-preferences.ts","../src/data-store/index.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, prefixWithSiteId } from '../utils';\n\nexport type LoginPreferences = {\n emailVerificationEnabled?: boolean;\n};\n\nexport const loginPreferencesContext = createDataStoreContext<LoginPreferences>();\nconst DATA_STORE_UNAVAILABLE_MODE = process.env.SFNEXT_DATA_STORE_UNAVAILABLE_MODE;\n\n/**\n * Read login preferences from router context.\n *\n * @param context - Router context provider\n * @returns Login preferences data stored by data-store middleware\n */\nexport function getLoginPreferences(context: Readonly<RouterContextProvider>): LoginPreferences {\n const data = context.get(loginPreferencesContext);\n if (!data) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Login preferences context not found. Ensure data-store middleware runs before loaders and the required env vars are set.'\n );\n return {};\n }\n return data;\n}\n\nexport const loginPreferencesMiddleware = createDataStoreMiddleware<LoginPreferences>({\n entryKey: prefixWithSiteId('login-preferences'),\n context: loginPreferencesContext,\n onUnavailable: DATA_STORE_UNAVAILABLE_MODE === 'fallback' ? 'fallback' : 'throw',\n fallbackValue: { emailVerificationEnabled: false },\n transform: (value) => value.data as LoginPreferences,\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\nexport { createDataStoreMiddleware } from './utils';\nexport { createDataStoreContext } from './utils';\nexport { getDataStoreEntry } from './utils';\nexport {\n DEFAULT_SITE_PREFERENCES_KEY,\n getSitePreferences,\n sitePreferencesContext,\n} from './middleware/custom-site-preferences';\nexport {\n customGlobalPreferencesContext,\n DEFAULT_CUSTOM_GLOBAL_PREFERENCES_KEY,\n getCustomGlobalPreferences,\n} from './middleware/custom-global-preferences';\nexport {\n DEFAULT_GCP_PREFERENCES_KEY,\n gcpPreferencesContext,\n getGcpApiKey,\n getGcpPreferences,\n} from './middleware/gcp-preferences';\nexport type { DataStoreMiddlewareOptions } from './utils';\nexport type { SitePreferences } from './middleware/custom-site-preferences';\nexport type { DataStoreContextKey, DataStoreEntryKey } from './utils';\nexport type { DataStoreEntry } from './utils';\nexport type { CustomGlobalPreferences } from './middleware/custom-global-preferences';\nexport { getLoginPreferences, loginPreferencesContext } from './middleware/login-preferences';\nexport type { LoginPreferences } from './middleware/login-preferences';\nexport type { GcpPreferences } from './middleware/gcp-preferences';\nexport { DataStore } from '@salesforce/mrt-utilities/data-store';\nexport {\n DataStoreNotFoundError,\n DataStoreServiceError,\n DataStoreUnavailableError,\n} from '@salesforce/mrt-utilities/data-store';\n\nimport { customSitePreferencesMiddleware } from './middleware/custom-site-preferences';\nimport { customGlobalPreferencesMiddleware } from './middleware/custom-global-preferences';\nimport { gcpPreferencesMiddleware } from './middleware/gcp-preferences';\nimport { loginPreferencesMiddleware } from './middleware/login-preferences';\n\nexport const dataStoreMiddleware = [\n customSitePreferencesMiddleware,\n customGlobalPreferencesMiddleware,\n gcpPreferencesMiddleware,\n loginPreferencesMiddleware,\n];\n"],"mappings":";;;;;;;;;AAuBA,MAAa,0BAA0B,wBAA0C;AACjF,MAAM,8BAA8B,QAAQ,IAAI;;;;;;;AAQhD,SAAgB,oBAAoB,SAA4D;CAC5F,MAAM,OAAO,QAAQ,IAAI,wBAAwB;AACjD,KAAI,CAAC,MAAM;AAEP,UAAQ,KACJ,2HACH;AACD,SAAO,EAAE;;AAEb,QAAO;;AAGX,MAAa,6BAA6B,0BAA4C;CAClF,UAAU,iBAAiB,oBAAoB;CAC/C,SAAS;CACT,eAAe,gCAAgC,aAAa,aAAa;CACzE,eAAe,EAAE,0BAA0B,OAAO;CAClD,YAAY,UAAU,MAAM;CAC/B,CAAC;;;;ACKF,MAAa,sBAAsB;CAC/B;CACA;CACA;CACA;CACH"}
|
|
1
|
+
{"version":3,"file":"data-store.js","names":["consoleLogger: DataStoreLogger","dataStoreMiddleware: MiddlewareFunction<Response>","dataStoreMiddleware","lazyMiddleware: MiddlewareFunction<Response>","pending: Promise<T | null> | undefined","loader: LazyDataStoreLoader<T>","DataStoreNotFoundError","DataStoreUnavailableError","DataStoreServiceError","DataStore"],"sources":["../src/data-store/logger-context.ts","../src/data-store/utils.ts","../src/data-store/middleware/custom-site-preferences.ts","../src/data-store/middleware/custom-global-preferences.ts","../src/data-store/middleware/gcp-preferences.ts","../src/data-store/middleware/login-preferences.ts","../src/data-store/index.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// TODO: Remove this file once the SDK-level `loggerContext` lifted from the storefront\n// template lands. This data-store-scoped logger seam exists only because the SDK can't\n// import the template's `loggerContext` (inverted dep). Once the logger is platform-level\n// infra in the SDK, the data-store middleware should read that context directly and this\n// file can be deleted.\n\nimport { createContext, type RouterContextProvider } from 'react-router';\n\n/**\n * Minimal structured-logger interface the data-store middleware depends on.\n *\n * Matches the shape of the host application's `Logger` (see the storefront\n * template's `src/lib/logger.ts`) so a host can pass through its own logger\n * object via {@link dataStoreLoggerContext} without an adapter.\n *\n * The data-store middleware emits at `warn` level today; the full interface\n * is exposed so future SDK middlewares that need richer levels stay\n * consistent with this contract.\n */\nexport interface DataStoreLogger {\n error(message: string, metadata?: Record<string, unknown>): void;\n warn(message: string, metadata?: Record<string, unknown>): void;\n info(message: string, metadata?: Record<string, unknown>): void;\n debug(message: string, metadata?: Record<string, unknown>): void;\n}\n\nfunction formatMessage(message: string, metadata?: Record<string, unknown>): string {\n if (!metadata) return message;\n try {\n return `${message} ${JSON.stringify(metadata, replacerForErrors)}`;\n } catch {\n // Metadata may contain cycles, BigInt, or other unserializable values. The default\n // logger must never throw — that would defeat the data-store middleware's fail-soft\n // contract. Drop the metadata payload rather than crashing the request.\n return `${message} [unserializable metadata]`;\n }\n}\n\nfunction replacerForErrors(_key: string, value: unknown): unknown {\n if (value instanceof Error) {\n return { name: value.name, message: value.message, ...(value.stack && { stack: value.stack }) };\n }\n return value;\n}\n\n/**\n * Default logger used when nothing has been injected via\n * {@link dataStoreLoggerContext}. Routes warnings to `console.warn` and\n * errors to `console.error` so diagnostics remain visible in environments\n * (tests, scripts, hosts that haven't wired a structured logger) where the\n * SDK is invoked outside the storefront template. `info` and `debug` are\n * no-ops to avoid noisy default output.\n */\nconst consoleLogger: DataStoreLogger = Object.freeze({\n error(message: string, metadata?: Record<string, unknown>): void {\n // eslint-disable-next-line no-console\n console.error(formatMessage(message, metadata));\n },\n warn(message: string, metadata?: Record<string, unknown>): void {\n // eslint-disable-next-line no-console\n console.warn(formatMessage(message, metadata));\n },\n info(): void {\n // intentional no-op: keeps the default logger quiet outside warn/error\n },\n debug(): void {\n // intentional no-op: keeps the default logger quiet outside warn/error\n },\n});\n\n/**\n * Router context the SDK reads to obtain a request-scoped structured logger.\n *\n * Hosts (e.g. the storefront template) populate this from their own logging\n * middleware. When unset, {@link getDataStoreLogger} falls back to a\n * console-based logger so warnings remain visible.\n *\n * Defaults to `null` (not `undefined`) because React Router's\n * `context.get()` throws when `defaultValue === undefined`.\n */\nexport const dataStoreLoggerContext = createContext<DataStoreLogger | null>(null);\n\n/**\n * Read the data-store logger from router context, falling back to a\n * console-based default when nothing has been injected.\n *\n * Use this from inside SDK middleware/loaders that have access to a\n * {@link RouterContextProvider}.\n */\nexport function getDataStoreLogger(context: Readonly<RouterContextProvider>): DataStoreLogger {\n return context.get(dataStoreLoggerContext) ?? consoleLogger;\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, type RouterContextProvider, createContext } from 'react-router';\nimport {\n DataStore,\n DataStoreNotFoundError,\n DataStoreServiceError,\n DataStoreUnavailableError,\n} from '@salesforce/mrt-utilities/data-store';\nimport { siteContext } from '../site-context';\nimport { type DataStoreLogger, getDataStoreLogger } from './logger-context';\n\nexport type DataStoreContextKey<T> = ReturnType<typeof createContext<T | null>>;\n\nexport type DataStoreEntryKey = string | ((context: Readonly<RouterContextProvider>) => string);\n\nexport type DataStoreEntry<TValue = unknown> = {\n value?: TValue;\n};\n\n/**\n * Options for {@link createDataStoreMiddleware} and {@link createLazyDataStoreMiddleware}.\n *\n * @typeParam T - The shape stored in `context` after the entry is fetched and transformed.\n */\nexport type DataStoreMiddlewareOptions<T> = {\n /**\n * The data store entry key, or a function that derives it from request context (used\n * for site-scoped keys — see {@link prefixWithSiteId}).\n */\n entryKey: DataStoreEntryKey;\n /**\n * The React Router context the resolved value is written to. Create one with\n * {@link createDataStoreContext}.\n */\n context: DataStoreContextKey<T>;\n /**\n * Optional projection from the raw entry value to the typed shape stored in context.\n * Defaults to the identity function (the raw value cast to `T`). Throws from this\n * function propagate to the caller; do not use it as a place to handle data-store\n * errors.\n */\n transform?: (value: Record<string, unknown>) => T;\n /**\n * How the middleware reacts when the data store cannot serve the entry\n * (`DataStoreUnavailableError` or `DataStoreServiceError`). `DataStoreNotFoundError`\n * is always handled gracefully and ignores this setting.\n *\n * - `'throw'` *(factory default)*: rethrow with a stable error message. Use when the\n * entry is required and a failure should surface as a 5xx.\n * - `'fallback'`: warn and resolve to {@link DataStoreMiddlewareOptions.fallbackValue}\n * (or the missing state if no fallback is configured). Use for optional preferences\n * where graceful degradation is preferred.\n *\n * The four built-in middlewares (`customSitePreferencesMiddleware`, etc.) override\n * this default to `'fallback'` so the storefront stays up during transient outages,\n * and expose `SFNEXT_DATA_STORE_UNAVAILABLE_MODE=throw` as an opt-in escape hatch.\n */\n onUnavailable?: 'throw' | 'fallback';\n /**\n * Value to populate the context with when `onUnavailable === 'fallback'` and the data\n * store fetch fails. Either a constant or a function that derives the value from the\n * router context (useful for fallbacks that need request-scoped information). When\n * omitted, the middleware leaves the context unset on failure (downstream consumers\n * see the context's default value, typically `null`).\n */\n fallbackValue?: T | ((context: Readonly<RouterContextProvider>) => T);\n};\n\n/**\n * Creates a typed React Router context for data store entries.\n *\n * Initializes the context with `null` so middleware can populate it during requests.\n *\n * @returns React Router context key for data store values\n */\nexport function createDataStoreContext<T>(): DataStoreContextKey<T> {\n return createContext<T | null>(null);\n}\n\n/**\n * Creates a React Router middleware that fetches a single MRT data store entry on every\n * request and stores the resulting value in the supplied router context.\n *\n * Failure handling is controlled by `options.onUnavailable`:\n * - `'throw'` (default for the factory): rethrow `DataStoreUnavailableError` and\n * `DataStoreServiceError` with a stable error message. Fail-fast — the request errors out.\n * - `'fallback'`: log a warning and resolve to `options.fallbackValue` when configured, or\n * to the missing state (context not populated) when no `fallbackValue` is provided. The\n * request continues without crashing the middleware chain.\n *\n * `DataStoreNotFoundError` is always treated as \"missing\" (warn, do not populate context),\n * regardless of `onUnavailable` — a not-found entry is an expected steady-state for\n * features that haven't been published yet, not a service failure.\n *\n * Errors thrown from `options.transform` propagate to the caller — they indicate a\n * programmer error in the middleware definition, not data-store unavailability.\n *\n * @param options - See {@link DataStoreMiddlewareOptions}.\n * @returns React Router middleware for server requests.\n *\n * @env AWS_REGION (required): AWS region for the data store table (e.g., `us-east-1`).\n * @env MOBIFY_PROPERTY_ID (required): MRT property identifier.\n * @env DEPLOY_TARGET (required): MRT deploy target (e.g., `production`).\n */\nexport function createDataStoreMiddleware<T>(options: DataStoreMiddlewareOptions<T>): MiddlewareFunction<Response> {\n const { entryKey, context: contextKey, onUnavailable = 'throw', fallbackValue } = options;\n const transform = options.transform ?? ((value: Record<string, unknown>) => value as T);\n\n const dataStoreMiddleware: MiddlewareFunction<Response> = async ({ context }, next) => {\n const resolvedEntryKey = typeof entryKey === 'function' ? entryKey(context) : entryKey;\n const result = await loadDataStoreEntry({\n entryKey: resolvedEntryKey,\n context,\n transform,\n onUnavailable,\n fallbackValue,\n });\n\n if (result.state === 'value' || result.state === 'fallback') {\n context.set(contextKey, result.value);\n }\n\n return next();\n };\n\n return dataStoreMiddleware;\n}\n\n/**\n * Lazy variant of {@link createDataStoreMiddleware}. Instead of fetching the\n * entry up-front during middleware execution, this stores a memoized loader\n * in the router context. Consumers call {@link readLazyDataStoreEntry} to\n * trigger the fetch on demand — pages that never read the value never pay\n * for the data-store call.\n *\n * Repeated reads within the same request share the in-flight promise so\n * the entry is fetched at most once per request.\n *\n * Failure handling matches the eager variant: `onUnavailable` and\n * `fallbackValue` are honored when the underlying fetch fails. The fallback\n * value (or `null` for the missing state) surfaces through\n * {@link readLazyDataStoreEntry}.\n *\n * Use this for entries that only a subset of routes consume (e.g. config\n * read by a single feature) rather than entries needed on every request.\n */\nexport function createLazyDataStoreMiddleware<T>(options: DataStoreMiddlewareOptions<T>): MiddlewareFunction<Response> {\n const { entryKey, context: contextKey, onUnavailable = 'throw', fallbackValue } = options;\n const transform = options.transform ?? ((value: Record<string, unknown>) => value as T);\n\n const lazyMiddleware: MiddlewareFunction<Response> = async ({ context }, next) => {\n let pending: Promise<T | null> | undefined;\n\n const loader: LazyDataStoreLoader<T> = () => {\n if (!pending) {\n const resolvedEntryKey = typeof entryKey === 'function' ? entryKey(context) : entryKey;\n pending = loadDataStoreEntry({\n entryKey: resolvedEntryKey,\n context,\n transform,\n onUnavailable,\n fallbackValue,\n }).then((result) => (result.state === 'missing' ? null : result.value));\n }\n return pending;\n };\n\n context.set(contextKey as unknown as DataStoreContextKey<LazyDataStoreLoader<T>>, loader);\n\n return next();\n };\n\n return lazyMiddleware;\n}\n\n/**\n * Reads a value populated by {@link createLazyDataStoreMiddleware}. Triggers\n * the underlying data-store fetch on first call and reuses the cached\n * promise on subsequent calls within the same request.\n *\n * Returns `null` when the lazy middleware did not run (no loader in\n * context) or when the entry is missing/invalid.\n */\nexport async function readLazyDataStoreEntry<T>(\n context: Readonly<RouterContextProvider>,\n contextKey: DataStoreContextKey<T>\n): Promise<T | null> {\n const loader = context.get(contextKey as unknown as DataStoreContextKey<LazyDataStoreLoader<T>>);\n if (typeof loader !== 'function') return null;\n return loader();\n}\n\ntype LazyDataStoreLoader<T> = () => Promise<T | null>;\n\ntype LoadResult<T> = { state: 'value'; value: T } | { state: 'fallback'; value: T } | { state: 'missing' };\n\n/**\n * Internal helper shared by the eager and lazy middleware factories.\n * Performs the fetch + transform pipeline and resolves all three error\n * paths (unavailable / not-found / service-error) consistently. Returns a\n * tagged result so callers can decide whether to populate the context\n * synchronously (eager middleware) or hand the value back to a lazy reader.\n */\nasync function loadDataStoreEntry<T>(args: {\n entryKey: string;\n context: Readonly<RouterContextProvider>;\n transform: (value: Record<string, unknown>) => T;\n onUnavailable: 'throw' | 'fallback';\n fallbackValue: T | ((context: Readonly<RouterContextProvider>) => T) | undefined;\n}): Promise<LoadResult<T>> {\n const { entryKey, context, transform, onUnavailable, fallbackValue } = args;\n const logger = getDataStoreLogger(context);\n\n try {\n const entry = await getDataStoreEntry(entryKey);\n\n if (!entry?.value || typeof entry.value !== 'object') {\n logger.warn(`Data store entry '${entryKey}' not found or invalid.`, { entryKey });\n return { state: 'missing' };\n }\n\n return { state: 'value', value: transform(entry.value as Record<string, unknown>) };\n } catch (error) {\n if (error instanceof DataStoreNotFoundError) {\n logger.warn(`Data store entry '${entryKey}' not found.`, { entryKey, error });\n return { state: 'missing' };\n }\n if (error instanceof DataStoreUnavailableError || error instanceof DataStoreServiceError) {\n return resolveDataStoreFallback({\n entryKey,\n context,\n error,\n onUnavailable,\n fallbackValue,\n logger,\n });\n }\n throw error;\n }\n}\n\nfunction resolveDataStoreFallback<T>(args: {\n entryKey: string;\n context: Readonly<RouterContextProvider>;\n error: DataStoreUnavailableError | DataStoreServiceError;\n onUnavailable: 'throw' | 'fallback';\n fallbackValue: T | ((context: Readonly<RouterContextProvider>) => T) | undefined;\n logger: DataStoreLogger;\n}): LoadResult<T> {\n const { entryKey, context, error, onUnavailable, fallbackValue, logger } = args;\n const reason = error instanceof DataStoreServiceError ? 'service error' : 'unavailable';\n\n if (onUnavailable === 'fallback') {\n if (typeof fallbackValue !== 'undefined') {\n const resolved =\n typeof fallbackValue === 'function'\n ? (fallbackValue as (ctx: Readonly<RouterContextProvider>) => T)(context)\n : fallbackValue;\n logger.warn(`Data store ${reason} for '${entryKey}'. Using configured fallback value.`, {\n entryKey,\n reason,\n error,\n });\n return { state: 'fallback', value: resolved };\n }\n logger.warn(`Data store ${reason} for '${entryKey}'. No fallback configured; treating entry as missing.`, {\n entryKey,\n reason,\n error,\n });\n return { state: 'missing' };\n }\n\n if (error instanceof DataStoreUnavailableError) {\n throw new Error('Data store is unavailable. Ensure AWS_REGION, MOBIFY_PROPERTY_ID, and DEPLOY_TARGET are set.');\n }\n throw new Error(`Data store request failed for '${entryKey}'.`);\n}\n\n/**\n * Read a data-store entry through the singleton MRT utilities API.\n * The underlying implementation (production DynamoDB vs development pseudo store)\n * is resolved by `@salesforce/mrt-utilities/data-store` export conditions.\n *\n * @param key - Data-store entry key\n * @returns Data-store entry or null when missing/invalid shape\n */\nexport async function getDataStoreEntry<TValue = unknown>(key: string): Promise<DataStoreEntry<TValue> | null> {\n const entry = (await DataStore.getDataStore().getEntry(key)) as DataStoreEntry<TValue> | undefined;\n if (!entry || typeof entry !== 'object') {\n return null;\n }\n return entry;\n}\n\n/**\n * Creates an entryKey function that prefixes the given suffix with the current site ID.\n *\n * @param suffix - The entry key suffix (e.g., \"custom-site-preferences\")\n * @returns A function compatible with `DataStoreMiddlewareOptions.entryKey`\n */\nexport function prefixWithSiteId(suffix: string): (context: Readonly<RouterContextProvider>) => string {\n return (context) => {\n const siteId = context.get(siteContext)?.site?.id;\n if (!siteId)\n throw new Error('Site id not found. Ensure site context middleware runs before data-store middleware.');\n return `${siteId}-${suffix}`;\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 */\n\nimport type { RouterContextProvider } from 'react-router';\nimport { getDataStoreLogger } from '../logger-context';\nimport { createDataStoreContext, createDataStoreMiddleware, prefixWithSiteId } from '../utils';\n\nexport type SitePreferences = Record<string, unknown>;\n\nexport const DEFAULT_SITE_PREFERENCES_KEY = 'site-preferences';\nexport const sitePreferencesContext = createDataStoreContext<SitePreferences>();\n\n/**\n * Read site preferences from router context.\n *\n * @param context - Router context provider\n * @returns Site preferences data stored by data-store middleware\n */\nexport function getSitePreferences(context: Readonly<RouterContextProvider>): SitePreferences {\n const data = context.get(sitePreferencesContext);\n if (!data) {\n getDataStoreLogger(context).warn(\n 'Data store context not found. Ensure data-store middleware runs before loaders and the required env vars are set.'\n );\n return {};\n }\n return data;\n}\n\n/**\n * Middleware that reads the site-scoped `custom-site-preferences` entry from the MRT data\n * store and stores it in {@link sitePreferencesContext}. The entry key is prefixed with\n * the current site id (e.g. `acme-custom-site-preferences`).\n *\n * Defaults to graceful degradation: if the data store is unavailable or returns a service\n * error, the request continues with `{}` as the preferences value rather than crashing.\n * Set `SFNEXT_DATA_STORE_UNAVAILABLE_MODE=throw` in the environment to opt back into\n * fail-fast behavior. The env var is read once at module load.\n *\n * Must run after the site-context middleware (so the site id is available for the entry\n * key) and before any loader that calls {@link getSitePreferences}.\n */\nexport const customSitePreferencesMiddleware = createDataStoreMiddleware({\n entryKey: prefixWithSiteId('custom-site-preferences'),\n context: sitePreferencesContext,\n onUnavailable: process.env.SFNEXT_DATA_STORE_UNAVAILABLE_MODE === 'throw' ? 'throw' : 'fallback',\n fallbackValue: {},\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 { RouterContextProvider } from 'react-router';\nimport { getDataStoreLogger } from '../logger-context';\nimport { createDataStoreContext, createDataStoreMiddleware } from '../utils';\n\nexport type CustomGlobalPreferences = Record<string, unknown>;\n\nexport const DEFAULT_CUSTOM_GLOBAL_PREFERENCES_KEY = 'custom-global-preferences';\nexport const customGlobalPreferencesContext = createDataStoreContext<CustomGlobalPreferences>();\n\n/**\n * Read custom global preferences from router context.\n *\n * @param context - Router context provider\n * @returns Custom global preferences data stored by data-store middleware\n */\nexport function getCustomGlobalPreferences(context: Readonly<RouterContextProvider>): CustomGlobalPreferences {\n const data = context.get(customGlobalPreferencesContext);\n if (!data) {\n getDataStoreLogger(context).warn(\n 'Custom global preferences context not found. Ensure data-store middleware runs before loaders and the required env vars are set.'\n );\n return {};\n }\n return data;\n}\n\n/**\n * Middleware that reads the global `custom-global-preferences` entry from the MRT data\n * store and stores it in {@link customGlobalPreferencesContext}.\n *\n * Defaults to graceful degradation: if the data store is unavailable or returns a service\n * error, the request continues with `{}` as the preferences value rather than crashing.\n * Set `SFNEXT_DATA_STORE_UNAVAILABLE_MODE=throw` in the environment to opt back into\n * fail-fast behavior. The env var is read once at module load.\n */\nexport const customGlobalPreferencesMiddleware = createDataStoreMiddleware({\n entryKey: DEFAULT_CUSTOM_GLOBAL_PREFERENCES_KEY,\n context: customGlobalPreferencesContext,\n onUnavailable: process.env.SFNEXT_DATA_STORE_UNAVAILABLE_MODE === 'throw' ? 'throw' : 'fallback',\n fallbackValue: {},\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 { RouterContextProvider } from 'react-router';\nimport { getDataStoreLogger } from '../logger-context';\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 getDataStoreLogger(context).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. When the entry\n * is not synced (e.g. the GCP feature flag is off in ECOM), the underlying fetch surfaces\n * as `DataStoreNotFoundError` and the context is left unset; consumers see the empty\n * default `{ apiKey: '' }` via {@link getGcpPreferences}.\n *\n * Defaults to graceful degradation: if the data store is unavailable or returns a service\n * error, the request continues with `{ apiKey: '' }` rather than crashing. Set\n * `SFNEXT_DATA_STORE_UNAVAILABLE_MODE=throw` in the environment to opt back into\n * fail-fast behavior. The env var is read once at module load.\n *\n * Must run before any loader/middleware that reads `getGcpPreferences(context)` or\n * `getGcpApiKey(context)`.\n */\nexport const gcpPreferencesMiddleware = createDataStoreMiddleware<GcpPreferences>({\n entryKey: DEFAULT_GCP_PREFERENCES_KEY,\n context: gcpPreferencesContext,\n onUnavailable: process.env.SFNEXT_DATA_STORE_UNAVAILABLE_MODE === 'throw' ? 'throw' : 'fallback',\n fallbackValue: { apiKey: '' },\n transform: (value) => {\n const rawKey = value[API_KEY_MAP_KEY];\n return { apiKey: typeof rawKey === 'string' ? rawKey : '' };\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 */\n\nimport type { RouterContextProvider } from 'react-router';\nimport { getDataStoreLogger } from '../logger-context';\nimport { createDataStoreContext, createDataStoreMiddleware, prefixWithSiteId } from '../utils';\n\nexport type LoginPreferences = {\n emailVerificationEnabled?: boolean;\n};\n\nexport const loginPreferencesContext = createDataStoreContext<LoginPreferences>();\n\n/**\n * Read login preferences from router context.\n *\n * @param context - Router context provider\n * @returns Login preferences data stored by data-store middleware\n */\nexport function getLoginPreferences(context: Readonly<RouterContextProvider>): LoginPreferences {\n const data = context.get(loginPreferencesContext);\n if (!data) {\n getDataStoreLogger(context).warn(\n 'Login preferences context not found. Ensure data-store middleware runs before loaders and the required env vars are set.'\n );\n return {};\n }\n return data;\n}\n\n/**\n * Middleware that reads the site-scoped `login-preferences` entry from the MRT data store\n * and stores its `data` field in {@link loginPreferencesContext}. The entry key is\n * prefixed with the current site id (e.g. `acme-login-preferences`).\n *\n * Defaults to graceful degradation: if the data store is unavailable or returns a service\n * error, the request continues with `{ emailVerificationEnabled: false }` rather than\n * crashing. Set `SFNEXT_DATA_STORE_UNAVAILABLE_MODE=throw` in the environment to opt back\n * into fail-fast behavior. The env var is read once at module load.\n *\n * Must run after the site-context middleware (so the site id is available for the entry\n * key) and before any loader that calls {@link getLoginPreferences}.\n */\nexport const loginPreferencesMiddleware = createDataStoreMiddleware<LoginPreferences>({\n entryKey: prefixWithSiteId('login-preferences'),\n context: loginPreferencesContext,\n onUnavailable: process.env.SFNEXT_DATA_STORE_UNAVAILABLE_MODE === 'throw' ? 'throw' : 'fallback',\n fallbackValue: { emailVerificationEnabled: false },\n transform: (value) => value.data as LoginPreferences,\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\nexport { createDataStoreMiddleware } from './utils';\nexport { createLazyDataStoreMiddleware, readLazyDataStoreEntry } from './utils';\nexport { createDataStoreContext } from './utils';\nexport { getDataStoreEntry } from './utils';\nexport { dataStoreLoggerContext, getDataStoreLogger } from './logger-context';\nexport type { DataStoreLogger } from './logger-context';\nexport { getSitePreferences } from './middleware/custom-site-preferences';\nexport { getCustomGlobalPreferences } from './middleware/custom-global-preferences';\nexport { getGcpApiKey, getGcpPreferences } from './middleware/gcp-preferences';\nexport type { DataStoreMiddlewareOptions } from './utils';\nexport type { SitePreferences } from './middleware/custom-site-preferences';\nexport type { DataStoreContextKey, DataStoreEntryKey } from './utils';\nexport type { DataStoreEntry } from './utils';\nexport type { CustomGlobalPreferences } from './middleware/custom-global-preferences';\nexport { getLoginPreferences } from './middleware/login-preferences';\nexport type { LoginPreferences } from './middleware/login-preferences';\nexport type { GcpPreferences } from './middleware/gcp-preferences';\nexport { DataStore } from '@salesforce/mrt-utilities/data-store';\nexport {\n DataStoreNotFoundError,\n DataStoreServiceError,\n DataStoreUnavailableError,\n} from '@salesforce/mrt-utilities/data-store';\n\nimport { customSitePreferencesMiddleware } from './middleware/custom-site-preferences';\nimport { customGlobalPreferencesMiddleware } from './middleware/custom-global-preferences';\nimport { gcpPreferencesMiddleware } from './middleware/gcp-preferences';\nimport { loginPreferencesMiddleware } from './middleware/login-preferences';\n\nexport const dataStoreMiddleware = [\n customSitePreferencesMiddleware,\n customGlobalPreferencesMiddleware,\n gcpPreferencesMiddleware,\n loginPreferencesMiddleware,\n];\n"],"mappings":";;;;;;AA0CA,SAAS,cAAc,SAAiB,UAA4C;AAChF,KAAI,CAAC,SAAU,QAAO;AACtB,KAAI;AACA,SAAO,GAAG,QAAQ,GAAG,KAAK,UAAU,UAAU,kBAAkB;SAC5D;AAIJ,SAAO,GAAG,QAAQ;;;AAI1B,SAAS,kBAAkB,MAAc,OAAyB;AAC9D,KAAI,iBAAiB,MACjB,QAAO;EAAE,MAAM,MAAM;EAAM,SAAS,MAAM;EAAS,GAAI,MAAM,SAAS,EAAE,OAAO,MAAM,OAAO;EAAG;AAEnG,QAAO;;;;;;;;;;AAWX,MAAMA,gBAAiC,OAAO,OAAO;CACjD,MAAM,SAAiB,UAA0C;AAE7D,UAAQ,MAAM,cAAc,SAAS,SAAS,CAAC;;CAEnD,KAAK,SAAiB,UAA0C;AAE5D,UAAQ,KAAK,cAAc,SAAS,SAAS,CAAC;;CAElD,OAAa;CAGb,QAAc;CAGjB,CAAC;;;;;;;;;;;AAYF,MAAa,yBAAyB,cAAsC,KAAK;;;;;;;;AASjF,SAAgB,mBAAmB,SAA2D;AAC1F,QAAO,QAAQ,IAAI,uBAAuB,IAAI;;;;;;;;;;;;AChBlD,SAAgB,yBAAoD;AAChE,QAAO,cAAwB,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BxC,SAAgB,0BAA6B,SAAsE;CAC/G,MAAM,EAAE,UAAU,SAAS,YAAY,gBAAgB,SAAS,kBAAkB;CAClF,MAAM,YAAY,QAAQ,eAAe,UAAmC;CAE5E,MAAMC,wBAAoD,OAAO,EAAE,WAAW,SAAS;EAEnF,MAAM,SAAS,MAAM,mBAAmB;GACpC,UAFqB,OAAO,aAAa,aAAa,SAAS,QAAQ,GAAG;GAG1E;GACA;GACA;GACA;GACH,CAAC;AAEF,MAAI,OAAO,UAAU,WAAW,OAAO,UAAU,WAC7C,SAAQ,IAAI,YAAY,OAAO,MAAM;AAGzC,SAAO,MAAM;;AAGjB,QAAOC;;;;;;;;;;;;;;;;;;;;AAqBX,SAAgB,8BAAiC,SAAsE;CACnH,MAAM,EAAE,UAAU,SAAS,YAAY,gBAAgB,SAAS,kBAAkB;CAClF,MAAM,YAAY,QAAQ,eAAe,UAAmC;CAE5E,MAAMC,iBAA+C,OAAO,EAAE,WAAW,SAAS;EAC9E,IAAIC;EAEJ,MAAMC,eAAuC;AACzC,OAAI,CAAC,QAED,WAAU,mBAAmB;IACzB,UAFqB,OAAO,aAAa,aAAa,SAAS,QAAQ,GAAG;IAG1E;IACA;IACA;IACA;IACH,CAAC,CAAC,MAAM,WAAY,OAAO,UAAU,YAAY,OAAO,OAAO,MAAO;AAE3E,UAAO;;AAGX,UAAQ,IAAI,YAAsE,OAAO;AAEzF,SAAO,MAAM;;AAGjB,QAAO;;;;;;;;;;AAWX,eAAsB,uBAClB,SACA,YACiB;CACjB,MAAM,SAAS,QAAQ,IAAI,WAAqE;AAChG,KAAI,OAAO,WAAW,WAAY,QAAO;AACzC,QAAO,QAAQ;;;;;;;;;AAcnB,eAAe,mBAAsB,MAMV;CACvB,MAAM,EAAE,UAAU,SAAS,WAAW,eAAe,kBAAkB;CACvE,MAAM,SAAS,mBAAmB,QAAQ;AAE1C,KAAI;EACA,MAAM,QAAQ,MAAM,kBAAkB,SAAS;AAE/C,MAAI,CAAC,OAAO,SAAS,OAAO,MAAM,UAAU,UAAU;AAClD,UAAO,KAAK,qBAAqB,SAAS,0BAA0B,EAAE,UAAU,CAAC;AACjF,UAAO,EAAE,OAAO,WAAW;;AAG/B,SAAO;GAAE,OAAO;GAAS,OAAO,UAAU,MAAM,MAAiC;GAAE;UAC9E,OAAO;AACZ,MAAI,iBAAiBC,0BAAwB;AACzC,UAAO,KAAK,qBAAqB,SAAS,eAAe;IAAE;IAAU;IAAO,CAAC;AAC7E,UAAO,EAAE,OAAO,WAAW;;AAE/B,MAAI,iBAAiBC,+BAA6B,iBAAiBC,wBAC/D,QAAO,yBAAyB;GAC5B;GACA;GACA;GACA;GACA;GACA;GACH,CAAC;AAEN,QAAM;;;AAId,SAAS,yBAA4B,MAOnB;CACd,MAAM,EAAE,UAAU,SAAS,OAAO,eAAe,eAAe,WAAW;CAC3E,MAAM,SAAS,iBAAiBA,0BAAwB,kBAAkB;AAE1E,KAAI,kBAAkB,YAAY;AAC9B,MAAI,OAAO,kBAAkB,aAAa;GACtC,MAAM,WACF,OAAO,kBAAkB,aAClB,cAA8D,QAAQ,GACvE;AACV,UAAO,KAAK,cAAc,OAAO,QAAQ,SAAS,sCAAsC;IACpF;IACA;IACA;IACH,CAAC;AACF,UAAO;IAAE,OAAO;IAAY,OAAO;IAAU;;AAEjD,SAAO,KAAK,cAAc,OAAO,QAAQ,SAAS,wDAAwD;GACtG;GACA;GACA;GACH,CAAC;AACF,SAAO,EAAE,OAAO,WAAW;;AAG/B,KAAI,iBAAiBD,4BACjB,OAAM,IAAI,MAAM,+FAA+F;AAEnH,OAAM,IAAI,MAAM,kCAAkC,SAAS,IAAI;;;;;;;;;;AAWnE,eAAsB,kBAAoC,KAAqD;CAC3G,MAAM,QAAS,MAAME,YAAU,cAAc,CAAC,SAAS,IAAI;AAC3D,KAAI,CAAC,SAAS,OAAO,UAAU,SAC3B,QAAO;AAEX,QAAO;;;;;;;;AASX,SAAgB,iBAAiB,QAAsE;AACnG,SAAQ,YAAY;EAChB,MAAM,SAAS,QAAQ,IAAI,YAAY,EAAE,MAAM;AAC/C,MAAI,CAAC,OACD,OAAM,IAAI,MAAM,uFAAuF;AAC3G,SAAO,GAAG,OAAO,GAAG;;;;;;AC1S5B,MAAa,yBAAyB,wBAAyC;;;;;;;AAQ/E,SAAgB,mBAAmB,SAA2D;CAC1F,MAAM,OAAO,QAAQ,IAAI,uBAAuB;AAChD,KAAI,CAAC,MAAM;AACP,qBAAmB,QAAQ,CAAC,KACxB,oHACH;AACD,SAAO,EAAE;;AAEb,QAAO;;;;;;;;;;;;;;;AAgBX,MAAa,kCAAkC,0BAA0B;CACrE,UAAU,iBAAiB,0BAA0B;CACrD,SAAS;CACT,eAAe,QAAQ,IAAI,uCAAuC,UAAU,UAAU;CACtF,eAAe,EAAE;CACpB,CAAC;;;;ACtCF,MAAa,wCAAwC;AACrD,MAAa,iCAAiC,wBAAiD;;;;;;;AAQ/F,SAAgB,2BAA2B,SAAmE;CAC1G,MAAM,OAAO,QAAQ,IAAI,+BAA+B;AACxD,KAAI,CAAC,MAAM;AACP,qBAAmB,QAAQ,CAAC,KACxB,mIACH;AACD,SAAO,EAAE;;AAEb,QAAO;;;;;;;;;;;AAYX,MAAa,oCAAoC,0BAA0B;CACvE,UAAU;CACV,SAAS;CACT,eAAe,QAAQ,IAAI,uCAAuC,UAAU,UAAU;CACtF,eAAe,EAAE;CACpB,CAAC;;;;ACxBF,MAAa,8BAA8B;;;;;AAM3C,MAAM,kBAAkB;AAExB,MAAa,wBAAwB,wBAAwC;;;;;;;;;;;;AAa7E,SAAgB,kBAAkB,SAA0D;CACxF,MAAM,OAAO,QAAQ,IAAI,sBAAsB;AAC/C,KAAI,SAAS,MAAM;AACf,qBAAmB,QAAQ,CAAC,KACxB,mKACH;AACD,SAAO,EAAE,QAAQ,IAAI;;AAEzB,QAAO;;;;;;;;;;AAWX,SAAgB,aAAa,SAAkD;AAC3E,QAAO,kBAAkB,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;AAqBtC,MAAa,2BAA2B,0BAA0C;CAC9E,UAAU;CACV,SAAS;CACT,eAAe,QAAQ,IAAI,uCAAuC,UAAU,UAAU;CACtF,eAAe,EAAE,QAAQ,IAAI;CAC7B,YAAY,UAAU;EAClB,MAAM,SAAS,MAAM;AACrB,SAAO,EAAE,QAAQ,OAAO,WAAW,WAAW,SAAS,IAAI;;CAElE,CAAC;;;;AC/EF,MAAa,0BAA0B,wBAA0C;;;;;;;AAQjF,SAAgB,oBAAoB,SAA4D;CAC5F,MAAM,OAAO,QAAQ,IAAI,wBAAwB;AACjD,KAAI,CAAC,MAAM;AACP,qBAAmB,QAAQ,CAAC,KACxB,2HACH;AACD,SAAO,EAAE;;AAEb,QAAO;;;;;;;;;;;;;;;AAgBX,MAAa,6BAA6B,0BAA4C;CAClF,UAAU,iBAAiB,oBAAoB;CAC/C,SAAS;CACT,eAAe,QAAQ,IAAI,uCAAuC,UAAU,UAAU;CACtF,eAAe,EAAE,0BAA0B,OAAO;CAClD,YAAY,UAAU,MAAM;CAC/B,CAAC;;;;ACjBF,MAAa,sBAAsB;CAC/B;CACA;CACA;CACA;CACH"}
|