@nymbalhq/sdk 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/chunk-333Z6VKZ.js +133 -0
  2. package/dist/chunk-333Z6VKZ.js.map +1 -0
  3. package/dist/chunk-4GP43MKT.js +19 -0
  4. package/dist/chunk-4GP43MKT.js.map +1 -0
  5. package/dist/chunk-BAWTE4QS.js +113 -0
  6. package/dist/chunk-BAWTE4QS.js.map +1 -0
  7. package/dist/chunk-EFRWJT3L.js +520 -0
  8. package/dist/chunk-EFRWJT3L.js.map +1 -0
  9. package/dist/chunk-FWEQBOEX.js +92 -0
  10. package/dist/chunk-FWEQBOEX.js.map +1 -0
  11. package/dist/chunk-KRVUYBKB.js +118 -0
  12. package/dist/chunk-KRVUYBKB.js.map +1 -0
  13. package/dist/chunk-L7EZURSK.js +130 -0
  14. package/dist/chunk-L7EZURSK.js.map +1 -0
  15. package/dist/chunk-QUSTGYBM.js +52 -0
  16. package/dist/chunk-QUSTGYBM.js.map +1 -0
  17. package/dist/chunk-U6QF2DRY.js +30 -0
  18. package/dist/chunk-U6QF2DRY.js.map +1 -0
  19. package/dist/core-DCBjP2Ev.d.ts +523 -0
  20. package/dist/core.d.ts +2 -0
  21. package/dist/core.js +17 -0
  22. package/dist/core.js.map +1 -0
  23. package/dist/index.d.ts +4 -429
  24. package/dist/index.js +32 -974
  25. package/dist/index.js.map +1 -1
  26. package/dist/ns/auth.d.ts +2 -0
  27. package/dist/ns/auth.js +4 -0
  28. package/dist/ns/auth.js.map +1 -0
  29. package/dist/ns/cart.d.ts +2 -0
  30. package/dist/ns/cart.js +4 -0
  31. package/dist/ns/cart.js.map +1 -0
  32. package/dist/ns/checkout.d.ts +2 -0
  33. package/dist/ns/checkout.js +5 -0
  34. package/dist/ns/checkout.js.map +1 -0
  35. package/dist/ns/product-list.d.ts +2 -0
  36. package/dist/ns/product-list.js +4 -0
  37. package/dist/ns/product-list.js.map +1 -0
  38. package/dist/ns/product.d.ts +2 -0
  39. package/dist/ns/product.js +4 -0
  40. package/dist/ns/product.js.map +1 -0
  41. package/dist/ns/search.d.ts +2 -0
  42. package/dist/ns/search.js +4 -0
  43. package/dist/ns/search.js.map +1 -0
  44. package/package.json +17 -2
@@ -0,0 +1,520 @@
1
+ // src/adapter/fetch-client.ts
2
+ var NymbalApiError = class extends Error {
3
+ constructor(code, message, status, context) {
4
+ super(message);
5
+ this.code = code;
6
+ this.status = status;
7
+ this.context = context;
8
+ this.name = "NymbalApiError";
9
+ }
10
+ code;
11
+ status;
12
+ context;
13
+ };
14
+ var REFRESH_WAIT_MS = 1e4;
15
+ var CSRF_HEADER = "x-csrf-token";
16
+ var UNSAFE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
17
+ var FetchClient = class {
18
+ baseUrl;
19
+ credentials;
20
+ getAccessToken;
21
+ getCartToken;
22
+ onUnauthorized;
23
+ /** The refresh a refused request started, which every request refused meanwhile waits on rather than its own. */
24
+ refreshing = null;
25
+ /** The token `GET /api/csrf` answered: the API's `nymbal.csrf` cookie, which this origin may not be able to read. */
26
+ csrfToken = null;
27
+ constructor(options) {
28
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
29
+ this.credentials = options.credentials ?? "include";
30
+ this.getAccessToken = options.getAccessToken ?? (() => null);
31
+ this.getCartToken = options.getCartToken;
32
+ this.onUnauthorized = options.onUnauthorized;
33
+ }
34
+ async request(method, path, body, options = {}) {
35
+ return (await this.requestWithMeta(method, path, body, options)).data;
36
+ }
37
+ /** Like `request`, keeping the envelope's `meta` (pagination cursors live there). */
38
+ async requestWithMeta(method, path, body, options = {}) {
39
+ const sentDuring = this.refreshing;
40
+ let { response, bearer } = await this.doFetch(method, path, body, options);
41
+ if (await this.isCsrfRejection(response)) {
42
+ this.csrfToken = null;
43
+ ({ response, bearer } = await this.doFetch(method, path, body, options));
44
+ }
45
+ if (response.status === 401 && this.onUnauthorized && options.authRefresh !== true) {
46
+ const pending = this.refreshing;
47
+ const retry = async () => this.parseResponse((await this.doFetch(method, path, body, options)).response);
48
+ if (pending === null && this.getAccessToken() !== bearer) {
49
+ return retry();
50
+ }
51
+ if (pending !== null && pending === sentDuring) {
52
+ if (await this.waitFor(pending) && this.getAccessToken() !== bearer) return retry();
53
+ } else if (await this.waitFor(pending ?? this.refresh(this.onUnauthorized))) {
54
+ return retry();
55
+ }
56
+ }
57
+ return this.parseResponse(response);
58
+ }
59
+ /** Whether the refresh ended within `REFRESH_WAIT_MS`; a refresh that fails throws here, as it always has. */
60
+ async waitFor(refresh) {
61
+ let timer;
62
+ const bound = new Promise((resolve) => {
63
+ timer = setTimeout(() => resolve(false), REFRESH_WAIT_MS);
64
+ });
65
+ try {
66
+ return await Promise.race([refresh.then(() => true), bound]);
67
+ } finally {
68
+ clearTimeout(timer);
69
+ }
70
+ }
71
+ refresh(onUnauthorized) {
72
+ const refresh = Promise.resolve().then(onUnauthorized).finally(() => {
73
+ this.refreshing = null;
74
+ });
75
+ this.refreshing = refresh;
76
+ return refresh;
77
+ }
78
+ csrf() {
79
+ this.csrfToken ??= this.fetchCsrfToken().catch((err) => {
80
+ this.csrfToken = null;
81
+ throw err;
82
+ });
83
+ return this.csrfToken;
84
+ }
85
+ async fetchCsrfToken() {
86
+ const response = await globalThis.fetch(`${this.baseUrl}/api/csrf`, {
87
+ method: "GET",
88
+ credentials: this.credentials
89
+ });
90
+ const { data } = await this.parseResponse(response);
91
+ return data.token;
92
+ }
93
+ async isCsrfRejection(response) {
94
+ if (response.status !== 403) return false;
95
+ const text = await response.clone().text();
96
+ try {
97
+ return JSON.parse(text).error?.code === "csrf.invalid";
98
+ } catch {
99
+ return false;
100
+ }
101
+ }
102
+ async doFetch(method, path, body, options) {
103
+ const headers = { ...options.headers };
104
+ const cookies = [];
105
+ if (UNSAFE_METHODS.has(method.toUpperCase())) {
106
+ const csrf = await this.csrf();
107
+ headers[CSRF_HEADER] = csrf;
108
+ cookies.push(`nymbal.csrf=${csrf}`);
109
+ }
110
+ if (body !== void 0) {
111
+ headers["Content-Type"] = "application/json";
112
+ }
113
+ const token = this.getAccessToken();
114
+ if (token) {
115
+ headers["Authorization"] = `Bearer ${token}`;
116
+ }
117
+ const cartToken = this.getCartToken?.();
118
+ if (cartToken) {
119
+ cookies.unshift(`nymbal.cart=${cartToken}`);
120
+ }
121
+ if (cookies.length > 0) {
122
+ headers["Cookie"] = cookies.join("; ");
123
+ }
124
+ const response = await globalThis.fetch(`${this.baseUrl}${path}`, {
125
+ method,
126
+ headers,
127
+ credentials: this.credentials,
128
+ ...body !== void 0 && { body: JSON.stringify(body) }
129
+ });
130
+ return { response, bearer: token };
131
+ }
132
+ async parseResponse(response) {
133
+ const text = await response.text();
134
+ if (!text) {
135
+ return { data: void 0, meta: {} };
136
+ }
137
+ const envelope = JSON.parse(text);
138
+ if (envelope.error) {
139
+ throw new NymbalApiError(
140
+ envelope.error.code,
141
+ envelope.error.message,
142
+ response.status,
143
+ envelope.error.context
144
+ );
145
+ }
146
+ return { data: envelope.data, meta: envelope.meta ?? {} };
147
+ }
148
+ };
149
+
150
+ // src/adapter/nymbal-api-adapter.ts
151
+ import {
152
+ ORDER_EMAIL_HEADER,
153
+ ORDER_KEY_HEADER
154
+ } from "@nymbalhq/types";
155
+ function createNymbalApiAdapter(fetchClient) {
156
+ const getOrder = (orderNumber, { key, email } = {}) => fetchClient.request(
157
+ "GET",
158
+ `/api/orders/${encodeURIComponent(orderNumber)}`,
159
+ void 0,
160
+ {
161
+ headers: {
162
+ ...key !== void 0 && { [ORDER_KEY_HEADER]: key },
163
+ ...email !== void 0 && { [ORDER_EMAIL_HEADER]: email }
164
+ }
165
+ }
166
+ );
167
+ return {
168
+ capabilities: {
169
+ get() {
170
+ return fetchClient.request("GET", "/api/capabilities");
171
+ }
172
+ },
173
+ cart: {
174
+ get() {
175
+ return fetchClient.request("GET", "/api/cart");
176
+ },
177
+ addItem(variantId, qty) {
178
+ return fetchClient.request("POST", "/api/cart/items", { variantId, qty });
179
+ },
180
+ removeItem(variantId) {
181
+ return fetchClient.request(
182
+ "DELETE",
183
+ `/api/cart/items/${encodeURIComponent(variantId)}`
184
+ );
185
+ },
186
+ updateQuantity(variantId, qty) {
187
+ return fetchClient.request(
188
+ "PATCH",
189
+ `/api/cart/items/${encodeURIComponent(variantId)}`,
190
+ { qty }
191
+ );
192
+ },
193
+ clear() {
194
+ return fetchClient.request("DELETE", "/api/cart");
195
+ }
196
+ },
197
+ products: {
198
+ getBySlug(slug) {
199
+ return fetchClient.request(
200
+ "GET",
201
+ `/api/products/${encodeURIComponent(slug)}`
202
+ );
203
+ },
204
+ list(params) {
205
+ const search = new URLSearchParams();
206
+ if (params?.limit !== void 0) search.set("limit", String(params.limit));
207
+ if (params?.cursor !== void 0) search.set("cursor", params.cursor);
208
+ if (params?.category !== void 0) search.set("category", params.category);
209
+ if (params?.sort !== void 0) search.set("sort", params.sort);
210
+ const qs = search.toString();
211
+ return fetchClient.request("GET", `/api/products${qs ? `?${qs}` : ""}`);
212
+ },
213
+ search(query, params) {
214
+ const search = new URLSearchParams();
215
+ search.set("q", query);
216
+ if (params?.limit !== void 0) search.set("limit", String(params.limit));
217
+ if (params?.cursor !== void 0) search.set("cursor", params.cursor);
218
+ if (params?.category !== void 0) search.set("category", params.category);
219
+ return fetchClient.request(
220
+ "GET",
221
+ `/api/products/search?${search.toString()}`
222
+ );
223
+ }
224
+ },
225
+ checkout: {
226
+ create(details) {
227
+ return fetchClient.request("POST", "/api/checkout", details);
228
+ }
229
+ },
230
+ customers: {
231
+ register(params) {
232
+ return fetchClient.request("POST", "/api/auth/register", params);
233
+ },
234
+ login(email, password) {
235
+ return fetchClient.request("POST", "/api/auth/login", { email, password });
236
+ },
237
+ async logout() {
238
+ await fetchClient.request("POST", "/api/auth/logout");
239
+ },
240
+ refresh() {
241
+ return fetchClient.request(
242
+ "POST",
243
+ "/api/auth/refresh",
244
+ void 0,
245
+ { authRefresh: true }
246
+ );
247
+ },
248
+ getProfile() {
249
+ return fetchClient.request("GET", "/api/customers/me");
250
+ },
251
+ updateProfile(updates) {
252
+ return fetchClient.request("PATCH", "/api/customers/me", updates);
253
+ },
254
+ listAddresses() {
255
+ return fetchClient.request("GET", "/api/customers/me/addresses");
256
+ },
257
+ addAddress(address) {
258
+ return fetchClient.request("POST", "/api/customers/me/addresses", address);
259
+ },
260
+ async removeAddress(id) {
261
+ await fetchClient.request(
262
+ "DELETE",
263
+ `/api/customers/me/addresses/${encodeURIComponent(id)}`
264
+ );
265
+ },
266
+ async listOrders(params) {
267
+ const qs = params?.cursor !== void 0 ? `?cursor=${encodeURIComponent(params.cursor)}` : "";
268
+ const { data, meta } = await fetchClient.requestWithMeta("GET", `/api/orders${qs}`);
269
+ const nextCursor = typeof meta.nextCursor === "string" ? meta.nextCursor : null;
270
+ return { items: data ?? [], nextCursor };
271
+ },
272
+ getOrder
273
+ },
274
+ orders: {
275
+ get: getOrder,
276
+ async requestLink({ orderNumber, email }) {
277
+ await fetchClient.request("POST", "/api/orders/link", {
278
+ orderNumber,
279
+ email
280
+ });
281
+ }
282
+ },
283
+ recentlyViewed: {
284
+ async record(productId) {
285
+ await fetchClient.request("POST", "/api/recently-viewed", { productId });
286
+ },
287
+ list() {
288
+ return fetchClient.request("GET", "/api/recently-viewed");
289
+ }
290
+ },
291
+ reviews: {
292
+ getForProduct(productId, params) {
293
+ const search = new URLSearchParams();
294
+ if (params?.limit !== void 0) search.set("limit", String(params.limit));
295
+ if (params?.cursor !== void 0) search.set("cursor", params.cursor);
296
+ const qs = search.toString();
297
+ return fetchClient.request(
298
+ "GET",
299
+ `/api/products/${encodeURIComponent(productId)}/reviews${qs ? `?${qs}` : ""}`
300
+ );
301
+ },
302
+ submit(review) {
303
+ return fetchClient.request("POST", "/api/reviews", review);
304
+ }
305
+ },
306
+ alerts: {
307
+ subscribe(request) {
308
+ return fetchClient.request("POST", "/api/alerts/subscribe", request);
309
+ }
310
+ }
311
+ };
312
+ }
313
+
314
+ // src/auth/token-manager.ts
315
+ var DEFAULT_STORAGE_KEY = "nymbal.auth.accessToken";
316
+ function createTokenManager(options) {
317
+ const storageMode = options?.storage ?? "memory";
318
+ const storageKey = options?.storageKey ?? DEFAULT_STORAGE_KEY;
319
+ let memoryToken = null;
320
+ let refreshTimer = null;
321
+ let refreshFn = null;
322
+ function getAccessToken() {
323
+ if (storageMode === "localStorage" && typeof globalThis.localStorage !== "undefined") {
324
+ return globalThis.localStorage.getItem(storageKey);
325
+ }
326
+ return memoryToken;
327
+ }
328
+ function setAccessToken(token) {
329
+ memoryToken = token;
330
+ if (storageMode === "localStorage" && typeof globalThis.localStorage !== "undefined") {
331
+ if (token) {
332
+ globalThis.localStorage.setItem(storageKey, token);
333
+ } else {
334
+ globalThis.localStorage.removeItem(storageKey);
335
+ }
336
+ }
337
+ }
338
+ function setTokens(accessToken, expiresIn) {
339
+ setAccessToken(accessToken);
340
+ scheduleAutoRefresh(expiresIn);
341
+ }
342
+ function clear() {
343
+ setAccessToken(null);
344
+ if (refreshTimer !== null) {
345
+ clearTimeout(refreshTimer);
346
+ refreshTimer = null;
347
+ }
348
+ }
349
+ function scheduleAutoRefresh(expiresIn) {
350
+ if (refreshTimer !== null) {
351
+ clearTimeout(refreshTimer);
352
+ }
353
+ const delayMs = Math.max(1e3, expiresIn * 0.8 * 1e3);
354
+ refreshTimer = setTimeout(async () => {
355
+ if (!refreshFn) return;
356
+ try {
357
+ const result = await refreshFn();
358
+ setTokens(result.accessToken, result.expiresIn);
359
+ } catch (err) {
360
+ console.error("[TokenManager] auto-refresh failed, clearing session", err);
361
+ clear();
362
+ }
363
+ }, delayMs);
364
+ }
365
+ function scheduleRefresh(fn) {
366
+ refreshFn = fn;
367
+ }
368
+ function destroy() {
369
+ clear();
370
+ refreshFn = null;
371
+ }
372
+ return { getAccessToken, setTokens, clear, scheduleRefresh, destroy };
373
+ }
374
+
375
+ // src/client.ts
376
+ var STORE_NAMESPACES = [
377
+ "cart",
378
+ "product",
379
+ "productList",
380
+ "search",
381
+ "checkout",
382
+ "auth"
383
+ ];
384
+ var FACTORIES_KEY = /* @__PURE__ */ Symbol.for("nymbal.sdk.namespaces");
385
+ function factories() {
386
+ const holder = globalThis;
387
+ holder[FACTORIES_KEY] ??= {};
388
+ return holder[FACTORIES_KEY];
389
+ }
390
+ function defineNamespace(name, factory) {
391
+ factories()[name] = factory;
392
+ }
393
+ var STORE_BUILT = /* @__PURE__ */ Symbol.for("nymbal.client.onStoreBuilt");
394
+ function isStore(value) {
395
+ return typeof value === "object" && value !== null && typeof value.subscribe === "function" && typeof value.getState === "function";
396
+ }
397
+ function onStoreBuilt(client, listener) {
398
+ const hook = client[STORE_BUILT];
399
+ if (typeof hook === "function") return hook(listener);
400
+ for (const value of Object.values(client)) {
401
+ if (isStore(value)) listener(value);
402
+ }
403
+ return () => {
404
+ };
405
+ }
406
+ function createNymbalClient(options) {
407
+ const tokenManager = createTokenManager({
408
+ ...options.tokenStorage !== void 0 && { storage: options.tokenStorage }
409
+ });
410
+ let adapter;
411
+ if (options.adapter) {
412
+ adapter = options.adapter;
413
+ } else {
414
+ const fetchClient = new FetchClient({
415
+ baseUrl: options.baseUrl,
416
+ ...options.credentials !== void 0 && { credentials: options.credentials },
417
+ getAccessToken: () => tokenManager.getAccessToken(),
418
+ ...options.getCartToken !== void 0 && { getCartToken: options.getCartToken },
419
+ onUnauthorized: async () => {
420
+ try {
421
+ const result = await adapter.customers.refresh();
422
+ tokenManager.setTokens(result.accessToken, result.expiresIn);
423
+ } catch (err) {
424
+ console.warn("[NymbalClient] token refresh on unauthorized failed", err);
425
+ tokenManager.clear();
426
+ }
427
+ }
428
+ });
429
+ adapter = createNymbalApiAdapter(fetchClient);
430
+ }
431
+ const customer = {
432
+ addresses: {
433
+ list: () => adapter.customers.listAddresses(),
434
+ create: (address) => adapter.customers.addAddress(address),
435
+ remove: (id) => adapter.customers.removeAddress(id)
436
+ },
437
+ orders: {
438
+ list: (opts) => adapter.customers.listOrders(opts),
439
+ get: (orderNumber, options2) => adapter.customers.getOrder(orderNumber, options2)
440
+ }
441
+ };
442
+ let report = null;
443
+ const capabilities = {
444
+ get() {
445
+ if (report === null) {
446
+ const reader = adapter.capabilities;
447
+ report = reader === void 0 ? Promise.reject(new Error("This client\u2019s adapter cannot read the store capabilities.")) : reader.get();
448
+ report.catch(() => {
449
+ report = null;
450
+ });
451
+ }
452
+ return report;
453
+ }
454
+ };
455
+ const noGuestOrders = () => Promise.reject(new Error("This client\u2019s adapter cannot open a guest\u2019s order."));
456
+ const orders = adapter.orders ?? { get: noGuestOrders, requestLink: noGuestOrders };
457
+ const recentlyViewedOf = () => {
458
+ const reader = adapter.recentlyViewed;
459
+ if (reader === void 0) {
460
+ throw new Error("This client\u2019s adapter cannot record or list recently viewed products.");
461
+ }
462
+ return reader;
463
+ };
464
+ const recentlyViewed = {
465
+ async record(productId) {
466
+ await recentlyViewedOf().record(productId);
467
+ },
468
+ async list() {
469
+ return recentlyViewedOf().list();
470
+ }
471
+ };
472
+ tokenManager.scheduleRefresh(() => adapter.customers.refresh());
473
+ const built = {};
474
+ const listeners = /* @__PURE__ */ new Set();
475
+ let destroyed = false;
476
+ function destroy() {
477
+ destroyed = true;
478
+ for (const store of Object.values(built)) store.destroy();
479
+ tokenManager.destroy();
480
+ }
481
+ const storeBuilt = (listener) => {
482
+ for (const store of Object.values(built)) listener(store);
483
+ listeners.add(listener);
484
+ return () => {
485
+ listeners.delete(listener);
486
+ };
487
+ };
488
+ const client = Object.defineProperty(
489
+ { capabilities, recentlyViewed, customer, orders, adapter, destroy },
490
+ STORE_BUILT,
491
+ { value: storeBuilt }
492
+ );
493
+ const context = { adapter, tokenManager, client };
494
+ function build(name) {
495
+ if (destroyed) throw new Error(`client.${name} was read after the client was destroyed`);
496
+ const factory = factories()[name];
497
+ if (factory === void 0) {
498
+ const module = name === "productList" ? "product-list" : name;
499
+ throw new Error(`client.${name} is not in this client: import '@nymbalhq/sdk/ns/${module}'`);
500
+ }
501
+ const store = built[name] = factory(context);
502
+ for (const listener of listeners) listener(store);
503
+ return store;
504
+ }
505
+ for (const name of STORE_NAMESPACES) {
506
+ Object.defineProperty(client, name, { get: () => built[name] ?? build(name) });
507
+ }
508
+ return client;
509
+ }
510
+
511
+ export {
512
+ NymbalApiError,
513
+ FetchClient,
514
+ createNymbalApiAdapter,
515
+ createTokenManager,
516
+ defineNamespace,
517
+ onStoreBuilt,
518
+ createNymbalClient
519
+ };
520
+ //# sourceMappingURL=chunk-EFRWJT3L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapter/fetch-client.ts","../src/adapter/nymbal-api-adapter.ts","../src/auth/token-manager.ts","../src/client.ts"],"sourcesContent":["export class NymbalApiError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n public readonly status: number,\n public readonly context?: Record<string, unknown>,\n ) {\n super(message)\n this.name = 'NymbalApiError'\n }\n}\n\nexport interface FetchClientOptions {\n baseUrl: string\n credentials?: RequestCredentials\n getAccessToken?: () => string | null\n getCartToken?: () => string | null\n onUnauthorized?: () => Promise<void>\n}\n\n/** Per request: headers sent beside the client's own. */\nexport interface RequestOptions {\n headers?: Record<string, string>\n /**\n * The token refresh itself (what `onUnauthorized` sends through this client): its 401 is thrown, never waited on or\n * sent again, so a refused refresh cannot wait on itself. An unmarked one that is refused is cut off after\n * 10 s with the original 401.\n */\n authRefresh?: boolean\n}\n\n/**\n * The longest a refused request waits on a refresh before its 401 is thrown: far beyond one healthy refresh round trip,\n * and the bound on a refresh that, unmarked, waits on itself.\n */\nconst REFRESH_WAIT_MS = 10_000\n\nconst CSRF_HEADER = 'x-csrf-token'\nconst UNSAFE_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE'])\n\ninterface Envelope<T> {\n data?: T\n meta?: Record<string, unknown>\n error?: { code: string; message: string; context?: Record<string, unknown> }\n}\n\nexport class FetchClient {\n private readonly baseUrl: string\n private readonly credentials: RequestCredentials\n private readonly getAccessToken: () => string | null\n private readonly getCartToken: (() => string | null) | undefined\n private readonly onUnauthorized: (() => Promise<void>) | undefined\n /** The refresh a refused request started, which every request refused meanwhile waits on rather than its own. */\n private refreshing: Promise<void> | null = null\n /** The token `GET /api/csrf` answered: the API's `nymbal.csrf` cookie, which this origin may not be able to read. */\n private csrfToken: Promise<string> | null = null\n\n constructor(options: FetchClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '')\n this.credentials = options.credentials ?? 'include'\n this.getAccessToken = options.getAccessToken ?? (() => null)\n this.getCartToken = options.getCartToken\n this.onUnauthorized = options.onUnauthorized\n }\n\n async request<T>(\n method: string,\n path: string,\n body?: unknown,\n options: RequestOptions = {},\n ): Promise<T> {\n return (await this.requestWithMeta<T>(method, path, body, options)).data\n }\n\n /** Like `request`, keeping the envelope's `meta` (pagination cursors live there). */\n async requestWithMeta<T>(\n method: string,\n path: string,\n body?: unknown,\n options: RequestOptions = {},\n ): Promise<{ data: T; meta: Record<string, unknown> }> {\n // Read before the first await: the refresh, if any, in flight when this request was sent.\n const sentDuring = this.refreshing\n let { response, bearer } = await this.doFetch(method, path, body, options)\n\n // A stale token (the cookie was replaced or expired) is fetched again once; a second refusal is thrown.\n if (await this.isCsrfRejection(response)) {\n this.csrfToken = null\n ;({ response, bearer } = await this.doFetch(method, path, body, options))\n }\n\n if (response.status === 401 && this.onUnauthorized && options.authRefresh !== true) {\n const pending = this.refreshing\n const retry = async () =>\n this.parseResponse<T>((await this.doFetch(method, path, body, options)).response)\n if (pending === null && this.getAccessToken() !== bearer) {\n // A refresh that ended after this was sent has replaced its token already.\n return retry()\n }\n if (pending !== null && pending === sentDuring) {\n // Sent during the refresh with the token it replaces: sent again once it has, and only if it did.\n if ((await this.waitFor(pending)) && this.getAccessToken() !== bearer) return retry()\n } else if (await this.waitFor(pending ?? this.refresh(this.onUnauthorized))) {\n // Every request refused with the old token waits on the one refresh and is sent again once.\n return retry()\n }\n }\n\n return this.parseResponse<T>(response)\n }\n\n /** Whether the refresh ended within `REFRESH_WAIT_MS`; a refresh that fails throws here, as it always has. */\n private async waitFor(refresh: Promise<void>): Promise<boolean> {\n let timer: ReturnType<typeof setTimeout> | undefined\n const bound = new Promise<false>((resolve) => {\n timer = setTimeout(() => resolve(false), REFRESH_WAIT_MS)\n })\n try {\n return await Promise.race([refresh.then(() => true as const), bound])\n } finally {\n clearTimeout(timer)\n }\n }\n\n private refresh(onUnauthorized: () => Promise<void>): Promise<void> {\n const refresh = Promise.resolve()\n .then(onUnauthorized)\n .finally(() => {\n this.refreshing = null\n })\n this.refreshing = refresh\n return refresh\n }\n\n private csrf(): Promise<string> {\n this.csrfToken ??= this.fetchCsrfToken().catch((err: unknown) => {\n this.csrfToken = null\n throw err\n })\n return this.csrfToken\n }\n\n private async fetchCsrfToken(): Promise<string> {\n const response = await globalThis.fetch(`${this.baseUrl}/api/csrf`, {\n method: 'GET',\n credentials: this.credentials,\n })\n const { data } = await this.parseResponse<{ token: string }>(response)\n return data.token\n }\n\n private async isCsrfRejection(response: Response): Promise<boolean> {\n if (response.status !== 403) return false\n const text = await response.clone().text()\n try {\n return (JSON.parse(text) as Envelope<unknown>).error?.code === 'csrf.invalid'\n } catch {\n return false\n }\n }\n\n private async doFetch(\n method: string,\n path: string,\n body: unknown,\n options: RequestOptions,\n ): Promise<{ response: Response; bearer: string | null }> {\n const headers: Record<string, string> = { ...options.headers }\n const cookies: string[] = []\n\n if (UNSAFE_METHODS.has(method.toUpperCase())) {\n const csrf = await this.csrf()\n headers[CSRF_HEADER] = csrf\n // Outside a browser there is no cookie jar to send the pair's cookie half; a browser drops this header.\n cookies.push(`nymbal.csrf=${csrf}`)\n }\n\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json'\n }\n\n const token = this.getAccessToken()\n if (token) {\n headers['Authorization'] = `Bearer ${token}`\n }\n\n const cartToken = this.getCartToken?.()\n if (cartToken) {\n cookies.unshift(`nymbal.cart=${cartToken}`)\n }\n if (cookies.length > 0) {\n headers['Cookie'] = cookies.join('; ')\n }\n\n const response = await globalThis.fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n credentials: this.credentials,\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n return { response, bearer: token }\n }\n\n private async parseResponse<T>(\n response: Response,\n ): Promise<{ data: T; meta: Record<string, unknown> }> {\n const text = await response.text()\n if (!text) {\n return { data: undefined as T, meta: {} }\n }\n\n const envelope = JSON.parse(text) as Envelope<T>\n\n if (envelope.error) {\n throw new NymbalApiError(\n envelope.error.code,\n envelope.error.message,\n response.status,\n envelope.error.context,\n )\n }\n\n return { data: envelope.data as T, meta: envelope.meta ?? {} }\n }\n}\n","import {\n ORDER_EMAIL_HEADER,\n ORDER_KEY_HEADER,\n type AlertKind,\n type Cart,\n type Customer,\n type CustomerAddress,\n type Order,\n type ReviewsResult,\n type ReviewResult,\n type ShopperOrder,\n type StoreCapabilities,\n} from '@nymbalhq/types'\nimport type { CommerceAdapter } from './commerce-adapter.js'\nimport type { FetchClient } from './fetch-client.js'\nimport type {\n DenormalisedProduct,\n ProductListParams,\n ProductListResult,\n ProductSearchParams,\n ProductSearchResult,\n CheckoutDetails,\n CheckoutResult,\n RegisterParams,\n AuthResult,\n NewAddress,\n OrderPage,\n} from '../types.js'\n\nexport function createNymbalApiAdapter(fetchClient: FetchClient): CommerceAdapter {\n // One read for a signed-in owner's order and a guest's: the key and the email, when given, go in headers.\n const getOrder = (orderNumber: string, { key, email }: { key?: string; email?: string } = {}) =>\n fetchClient.request<ShopperOrder>(\n 'GET',\n `/api/orders/${encodeURIComponent(orderNumber)}`,\n undefined,\n {\n headers: {\n ...(key !== undefined && { [ORDER_KEY_HEADER]: key }),\n ...(email !== undefined && { [ORDER_EMAIL_HEADER]: email }),\n },\n },\n )\n\n return {\n capabilities: {\n get() {\n return fetchClient.request<StoreCapabilities>('GET', '/api/capabilities')\n },\n },\n\n cart: {\n get() {\n return fetchClient.request<Cart>('GET', '/api/cart')\n },\n addItem(variantId: string, qty: number) {\n return fetchClient.request<Cart>('POST', '/api/cart/items', { variantId, qty })\n },\n removeItem(variantId: string) {\n return fetchClient.request<Cart>(\n 'DELETE',\n `/api/cart/items/${encodeURIComponent(variantId)}`,\n )\n },\n updateQuantity(variantId: string, qty: number) {\n return fetchClient.request<Cart>(\n 'PATCH',\n `/api/cart/items/${encodeURIComponent(variantId)}`,\n { qty },\n )\n },\n clear() {\n return fetchClient.request<Cart>('DELETE', '/api/cart')\n },\n },\n\n products: {\n getBySlug(slug: string) {\n return fetchClient.request<DenormalisedProduct>(\n 'GET',\n `/api/products/${encodeURIComponent(slug)}`,\n )\n },\n list(params?: ProductListParams) {\n const search = new URLSearchParams()\n if (params?.limit !== undefined) search.set('limit', String(params.limit))\n if (params?.cursor !== undefined) search.set('cursor', params.cursor)\n if (params?.category !== undefined) search.set('category', params.category)\n if (params?.sort !== undefined) search.set('sort', params.sort)\n const qs = search.toString()\n return fetchClient.request<ProductListResult>('GET', `/api/products${qs ? `?${qs}` : ''}`)\n },\n search(query: string, params?: ProductSearchParams) {\n const search = new URLSearchParams()\n search.set('q', query)\n if (params?.limit !== undefined) search.set('limit', String(params.limit))\n if (params?.cursor !== undefined) search.set('cursor', params.cursor)\n if (params?.category !== undefined) search.set('category', params.category)\n return fetchClient.request<ProductSearchResult>(\n 'GET',\n `/api/products/search?${search.toString()}`,\n )\n },\n },\n\n checkout: {\n create(details: CheckoutDetails) {\n return fetchClient.request<CheckoutResult>('POST', '/api/checkout', details)\n },\n },\n\n customers: {\n register(params: RegisterParams) {\n return fetchClient.request<AuthResult>('POST', '/api/auth/register', params)\n },\n login(email: string, password: string) {\n return fetchClient.request<AuthResult>('POST', '/api/auth/login', { email, password })\n },\n async logout() {\n await fetchClient.request<{ ok: boolean }>('POST', '/api/auth/logout')\n },\n refresh() {\n return fetchClient.request<{ accessToken: string; expiresIn: number }>(\n 'POST',\n '/api/auth/refresh',\n undefined,\n { authRefresh: true },\n )\n },\n getProfile() {\n return fetchClient.request<Customer>('GET', '/api/customers/me')\n },\n updateProfile(updates: Partial<Pick<Customer, 'firstName' | 'lastName' | 'phone'>>) {\n return fetchClient.request<Customer>('PATCH', '/api/customers/me', updates)\n },\n listAddresses() {\n return fetchClient.request<CustomerAddress[]>('GET', '/api/customers/me/addresses')\n },\n addAddress(address: NewAddress) {\n return fetchClient.request<CustomerAddress>('POST', '/api/customers/me/addresses', address)\n },\n async removeAddress(id: string) {\n await fetchClient.request<{ ok: boolean }>(\n 'DELETE',\n `/api/customers/me/addresses/${encodeURIComponent(id)}`,\n )\n },\n async listOrders(params?: { cursor?: string }): Promise<OrderPage> {\n const qs =\n params?.cursor !== undefined ? `?cursor=${encodeURIComponent(params.cursor)}` : ''\n const { data, meta } = await fetchClient.requestWithMeta<Order[]>('GET', `/api/orders${qs}`)\n const nextCursor = typeof meta.nextCursor === 'string' ? meta.nextCursor : null\n return { items: data ?? [], nextCursor }\n },\n getOrder,\n },\n\n orders: {\n get: getOrder,\n async requestLink({ orderNumber, email }: { orderNumber: string; email: string }) {\n await fetchClient.request<{ sent: string }>('POST', '/api/orders/link', {\n orderNumber,\n email,\n })\n },\n },\n\n recentlyViewed: {\n async record(productId: string) {\n await fetchClient.request<void>('POST', '/api/recently-viewed', { productId })\n },\n list() {\n return fetchClient.request<{ items: DenormalisedProduct[] }>('GET', '/api/recently-viewed')\n },\n },\n\n reviews: {\n getForProduct(productId: string, params?: { limit?: number; cursor?: string }) {\n const search = new URLSearchParams()\n if (params?.limit !== undefined) search.set('limit', String(params.limit))\n if (params?.cursor !== undefined) search.set('cursor', params.cursor)\n const qs = search.toString()\n return fetchClient.request<ReviewsResult>(\n 'GET',\n `/api/products/${encodeURIComponent(productId)}/reviews${qs ? `?${qs}` : ''}`,\n )\n },\n submit(review: { productId: string; rating: number; title: string; body: string }) {\n return fetchClient.request<ReviewResult>('POST', '/api/reviews', review)\n },\n },\n\n alerts: {\n subscribe(request: { email: string; variantId: string; kind: AlertKind }) {\n return fetchClient.request<{ status: 'pending' }>('POST', '/api/alerts/subscribe', request)\n },\n },\n }\n}\n","export interface TokenManagerOptions {\n storage?: 'memory' | 'localStorage'\n storageKey?: string\n}\n\nexport interface TokenManager {\n getAccessToken(): string | null\n setTokens(accessToken: string, expiresIn: number): void\n clear(): void\n scheduleRefresh(refreshFn: () => Promise<{ accessToken: string; expiresIn: number }>): void\n destroy(): void\n}\n\nconst DEFAULT_STORAGE_KEY = 'nymbal.auth.accessToken'\n\nexport function createTokenManager(options?: TokenManagerOptions): TokenManager {\n const storageMode = options?.storage ?? 'memory'\n const storageKey = options?.storageKey ?? DEFAULT_STORAGE_KEY\n\n let memoryToken: string | null = null\n let refreshTimer: ReturnType<typeof setTimeout> | null = null\n let refreshFn: (() => Promise<{ accessToken: string; expiresIn: number }>) | null = null\n\n function getAccessToken(): string | null {\n if (storageMode === 'localStorage' && typeof globalThis.localStorage !== 'undefined') {\n return globalThis.localStorage.getItem(storageKey)\n }\n return memoryToken\n }\n\n function setAccessToken(token: string | null): void {\n memoryToken = token\n if (storageMode === 'localStorage' && typeof globalThis.localStorage !== 'undefined') {\n if (token) {\n globalThis.localStorage.setItem(storageKey, token)\n } else {\n globalThis.localStorage.removeItem(storageKey)\n }\n }\n }\n\n function setTokens(accessToken: string, expiresIn: number): void {\n setAccessToken(accessToken)\n scheduleAutoRefresh(expiresIn)\n }\n\n function clear(): void {\n setAccessToken(null)\n if (refreshTimer !== null) {\n clearTimeout(refreshTimer)\n refreshTimer = null\n }\n }\n\n function scheduleAutoRefresh(expiresIn: number): void {\n if (refreshTimer !== null) {\n clearTimeout(refreshTimer)\n }\n const delayMs = Math.max(1000, expiresIn * 0.8 * 1000)\n refreshTimer = setTimeout(async () => {\n if (!refreshFn) return\n try {\n const result = await refreshFn()\n setTokens(result.accessToken, result.expiresIn)\n } catch (err) {\n console.error('[TokenManager] auto-refresh failed, clearing session', err)\n clear()\n }\n }, delayMs)\n }\n\n function scheduleRefresh(\n fn: () => Promise<{ accessToken: string; expiresIn: number }>,\n ): void {\n refreshFn = fn\n }\n\n function destroy(): void {\n clear()\n refreshFn = null\n }\n\n return { getAccessToken, setTokens, clear, scheduleRefresh, destroy }\n}\n","import type { CommerceAdapter } from './adapter/commerce-adapter.js'\nimport { FetchClient } from './adapter/fetch-client.js'\nimport { createNymbalApiAdapter } from './adapter/nymbal-api-adapter.js'\nimport { createTokenManager, type TokenManager } from './auth/token-manager.js'\nimport type { CartStore } from './store/cart-store.js'\nimport type { ProductStore } from './store/product-store.js'\nimport type { ProductListStore } from './store/product-list-store.js'\nimport type { SearchStore } from './store/search-store.js'\nimport type { CheckoutStore } from './store/checkout-store.js'\nimport type { AuthStore } from './store/auth-store.js'\nimport type { Store } from './store/create-store.js'\nimport type { CustomerAddress, ShopperOrder, StoreCapabilities } from '@nymbalhq/types'\nimport type { DenormalisedProduct, NewAddress, OrderPage } from './types.js'\n\nexport interface NymbalClientOptions {\n baseUrl: string\n adapter?: CommerceAdapter\n tokenStorage?: 'memory' | 'localStorage'\n credentials?: RequestCredentials\n getCartToken?: () => string | null\n}\n\n/** The signed-in customer's own account; every call sends the session's bearer token. */\nexport interface CustomerAccount {\n addresses: {\n list(): Promise<CustomerAddress[]>\n create(address: NewAddress): Promise<CustomerAddress>\n remove(id: string): Promise<void>\n }\n orders: {\n list(opts?: { cursor?: string }): Promise<OrderPage>\n /** `key`, the order's key from its checkout, reads it with no session; without one, only its owner can. */\n get(orderNumber: string, options?: { key?: string }): Promise<ShopperOrder>\n }\n}\n\n/**\n * A guest's orders, opened with the key from the checkout or the order email. Once the key has expired, the read is\n * refused with `orders.key_expired` and opens with the order's email beside the key; the email travels in a header,\n * never in a URL.\n */\nexport interface GuestOrders {\n get(orderNumber: string, options?: { key?: string; email?: string }): Promise<ShopperOrder>\n /** Asks the store to email the order's link to the order's own email; it answers the same whatever matched. */\n requestLink(request: { orderNumber: string; email: string }): Promise<void>\n}\n\n/** The store's capabilities report, read once per client: what it can do does not change while it runs. */\nexport interface StoreCapabilitiesReader {\n get(): Promise<StoreCapabilities>\n}\n\n/** The shopper's recently viewed products, kept by the store under the signed-in customer or the browser's visitor. */\nexport interface RecentlyViewed {\n /** Records a view of the product; the store ignores a product that is not active. */\n record(productId: string): Promise<void>\n /** The shopper's active products, newest first. */\n list(): Promise<{ items: DenormalisedProduct[] }>\n}\n\nexport interface NymbalClient {\n capabilities: StoreCapabilitiesReader\n recentlyViewed: RecentlyViewed\n cart: CartStore\n product: ProductStore\n productList: ProductListStore\n search: SearchStore\n checkout: CheckoutStore\n auth: AuthStore\n customer: CustomerAccount\n orders: GuestOrders\n adapter: CommerceAdapter\n destroy(): void\n}\n\n/** The client's store namespaces, each built on its first read by the factory its `@nymbalhq/sdk/ns/<module>` registers. */\nexport type StoreNamespace = 'cart' | 'product' | 'productList' | 'search' | 'checkout' | 'auth'\n\nconst STORE_NAMESPACES: StoreNamespace[] = [\n 'cart',\n 'product',\n 'productList',\n 'search',\n 'checkout',\n 'auth',\n]\n\n/** What a namespace's factory builds its store from: the client's adapter and token manager, and the client itself. */\nexport interface NamespaceContext {\n adapter: CommerceAdapter\n tokenManager: TokenManager\n client: NymbalClient\n}\n\ntype NamespaceFactory<K extends StoreNamespace> = (context: NamespaceContext) => NymbalClient[K]\n\ntype Factories = { [K in StoreNamespace]?: NamespaceFactory<K> }\n\n// On globalThis, like the payment drivers: a namespace module of one SDK copy registers for a client of another.\nconst FACTORIES_KEY = Symbol.for('nymbal.sdk.namespaces')\n\nfunction factories(): Factories {\n const holder = globalThis as Record<symbol, Factories | undefined>\n holder[FACTORIES_KEY] ??= {}\n return holder[FACTORIES_KEY]\n}\n\n/** Registers the factory of a store namespace; each `ns` module calls it on import. */\nexport function defineNamespace<K extends StoreNamespace>(\n name: K,\n factory: NamespaceFactory<K>,\n): void {\n factories()[name] = factory as Factories[K]\n}\n\n/** What `onStoreBuilt` hands its listener: any store, a hand-made client's included, reads and subscribes. */\ntype ObservedStore = Pick<Store<unknown>, 'getState' | 'subscribe'>\n\ntype ClientStore = ObservedStore & Pick<Store<unknown>, 'destroy'>\n\ntype StoreListener = (store: ObservedStore) => void\n\n// On a symbol, not in a module's registry, so a client made by another copy of the SDK still reports its stores.\nconst STORE_BUILT = Symbol.for('nymbal.client.onStoreBuilt')\n\ntype StoreBuiltHook = (listener: StoreListener) => () => void\n\nfunction isStore(value: unknown): value is ObservedStore {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as ObservedStore).subscribe === 'function' &&\n typeof (value as ObservedStore).getState === 'function'\n )\n}\n\n/**\n * Calls `listener` with each store namespace the client has built, and with each it builds later; returns the\n * unsubscribe. A client not made by `createNymbalClient` has its stores already: each is reported once, now.\n */\nexport function onStoreBuilt(client: NymbalClient, listener: StoreListener): () => void {\n const hook = (client as unknown as Record<symbol, StoreBuiltHook | undefined>)[STORE_BUILT]\n if (typeof hook === 'function') return hook(listener)\n for (const value of Object.values(client)) {\n if (isStore(value)) listener(value)\n }\n return () => {}\n}\n\nexport function createNymbalClient(options: NymbalClientOptions): NymbalClient {\n const tokenManager = createTokenManager({\n ...(options.tokenStorage !== undefined && { storage: options.tokenStorage }),\n })\n\n let adapter: CommerceAdapter\n\n if (options.adapter) {\n adapter = options.adapter\n } else {\n const fetchClient = new FetchClient({\n baseUrl: options.baseUrl,\n ...(options.credentials !== undefined && { credentials: options.credentials }),\n getAccessToken: () => tokenManager.getAccessToken(),\n ...(options.getCartToken !== undefined && { getCartToken: options.getCartToken }),\n onUnauthorized: async () => {\n try {\n const result = await adapter.customers.refresh()\n tokenManager.setTokens(result.accessToken, result.expiresIn)\n } catch (err) {\n // Token refresh failure is expected when the user session has expired.\n console.warn('[NymbalClient] token refresh on unauthorized failed', err)\n tokenManager.clear()\n }\n },\n })\n adapter = createNymbalApiAdapter(fetchClient)\n }\n\n const customer: CustomerAccount = {\n addresses: {\n list: () => adapter.customers.listAddresses(),\n create: (address) => adapter.customers.addAddress(address),\n remove: (id) => adapter.customers.removeAddress(id),\n },\n orders: {\n list: (opts) => adapter.customers.listOrders(opts),\n get: (orderNumber, options) => adapter.customers.getOrder(orderNumber, options),\n },\n }\n\n // A failed read is not kept, so the next get() asks again.\n let report: Promise<StoreCapabilities> | null = null\n const capabilities: StoreCapabilitiesReader = {\n get() {\n if (report === null) {\n const reader = adapter.capabilities\n report =\n reader === undefined\n ? Promise.reject(new Error('This client’s adapter cannot read the store capabilities.'))\n : reader.get()\n report.catch(() => {\n report = null\n })\n }\n return report\n },\n }\n\n // The adapter is the client's for its whole life, so whether it reads guests' orders is known here.\n const noGuestOrders = () =>\n Promise.reject(new Error('This client’s adapter cannot open a guest’s order.'))\n const orders: GuestOrders = adapter.orders ?? { get: noGuestOrders, requestLink: noGuestOrders }\n\n const recentlyViewedOf = (): NonNullable<CommerceAdapter['recentlyViewed']> => {\n const reader = adapter.recentlyViewed\n if (reader === undefined) {\n throw new Error('This client’s adapter cannot record or list recently viewed products.')\n }\n return reader\n }\n const recentlyViewed: RecentlyViewed = {\n async record(productId) {\n await recentlyViewedOf().record(productId)\n },\n async list() {\n return recentlyViewedOf().list()\n },\n }\n\n tokenManager.scheduleRefresh(() => adapter.customers.refresh())\n\n const built: { [K in StoreNamespace]?: ClientStore } = {}\n const listeners = new Set<StoreListener>()\n let destroyed = false\n\n function destroy(): void {\n destroyed = true\n for (const store of Object.values(built)) store.destroy()\n tokenManager.destroy()\n }\n\n const storeBuilt: StoreBuiltHook = (listener) => {\n for (const store of Object.values(built)) listener(store)\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n const client = Object.defineProperty(\n { capabilities, recentlyViewed, customer, orders, adapter, destroy },\n STORE_BUILT,\n { value: storeBuilt },\n ) as NymbalClient\n const context: NamespaceContext = { adapter, tokenManager, client }\n\n function build(name: StoreNamespace): ClientStore {\n // Building one now would leave a live store on a client whose destroy() has run.\n if (destroyed) throw new Error(`client.${name} was read after the client was destroyed`)\n const factory = factories()[name] as NamespaceFactory<StoreNamespace> | undefined\n if (factory === undefined) {\n const module = name === 'productList' ? 'product-list' : name\n throw new Error(`client.${name} is not in this client: import '@nymbalhq/sdk/ns/${module}'`)\n }\n const store = (built[name] = factory(context))\n for (const listener of listeners) listener(store)\n return store\n }\n\n // Defined properties are not enumerable: walking or spreading the client builds no store.\n for (const name of STORE_NAMESPACES) {\n Object.defineProperty(client, name, { get: () => built[name] ?? build(name) })\n }\n\n return client\n}\n"],"mappings":"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","names":["options"]}