@nymbalhq/sdk 1.2.0 → 1.4.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/dist/index.d.ts +62 -8
- package/dist/index.js +104 -29
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Customer, CartItem, Address, PaymentIntent, OrderStatus, CheckoutPayment, VariantOption, ProductSortKey, Facet, Order, StoreCapabilities, Cart, CustomerAddress, ReviewsResult, ReviewResult } from '@nymbalhq/types';
|
|
2
|
-
export { PaymentClient, ProductSortKey, ReviewResult as ReviewSubmitResult, ReviewsResult as ReviewsListResult, StoreCapabilities } from '@nymbalhq/types';
|
|
1
|
+
import { Customer, CartItem, Address, PaymentIntent, OrderStatus, CheckoutPayment, VariantOption, ProductSortKey, Facet, Order, StoreCapabilities, Cart, CustomerAddress, ShopperOrder, ReviewsResult, ReviewResult, AlertKind } from '@nymbalhq/types';
|
|
2
|
+
export { AlertKind, PaymentClient, ProductSortKey, ReviewResult as ReviewSubmitResult, ReviewsResult as ReviewsListResult, StoreCapabilities } from '@nymbalhq/types';
|
|
3
3
|
|
|
4
4
|
interface DenormalisedVariant {
|
|
5
5
|
id: string;
|
|
@@ -100,6 +100,8 @@ interface CheckoutState {
|
|
|
100
100
|
} | null;
|
|
101
101
|
/** The order this cart already became, when the server refused a resubmit and named it. */
|
|
102
102
|
placedOrderNumber: string | null;
|
|
103
|
+
/** That order's key, when the refusal carried one. */
|
|
104
|
+
placedOrderKey: string | null;
|
|
103
105
|
}
|
|
104
106
|
interface AuthState {
|
|
105
107
|
customer: Customer | null;
|
|
@@ -144,6 +146,8 @@ interface CheckoutResult {
|
|
|
144
146
|
totalMinor: number;
|
|
145
147
|
orderStatus: OrderStatus;
|
|
146
148
|
payment: CheckoutPayment;
|
|
149
|
+
/** Reads the order with no session (`customer.orders.get(n, { key })`); absent from an API that mints none. */
|
|
150
|
+
orderKey?: string;
|
|
147
151
|
}
|
|
148
152
|
interface RegisterParams {
|
|
149
153
|
email: string;
|
|
@@ -171,6 +175,13 @@ interface CommerceAdapter {
|
|
|
171
175
|
capabilities?: {
|
|
172
176
|
get(): Promise<StoreCapabilities>;
|
|
173
177
|
};
|
|
178
|
+
/** The shopper's recently viewed products; an adapter without it shows none. */
|
|
179
|
+
recentlyViewed?: {
|
|
180
|
+
record(productId: string): Promise<void>;
|
|
181
|
+
list(): Promise<{
|
|
182
|
+
items: DenormalisedProduct[];
|
|
183
|
+
}>;
|
|
184
|
+
};
|
|
174
185
|
cart: {
|
|
175
186
|
get(): Promise<Cart>;
|
|
176
187
|
addItem(variantId: string, qty: number): Promise<Cart>;
|
|
@@ -202,7 +213,10 @@ interface CommerceAdapter {
|
|
|
202
213
|
listOrders(params?: {
|
|
203
214
|
cursor?: string;
|
|
204
215
|
}): Promise<OrderPage>;
|
|
205
|
-
|
|
216
|
+
/** `key` is the order's key from its checkout, for a reader with no session. */
|
|
217
|
+
getOrder(orderNumber: string, options?: {
|
|
218
|
+
key?: string;
|
|
219
|
+
}): Promise<ShopperOrder>;
|
|
206
220
|
};
|
|
207
221
|
reviews: {
|
|
208
222
|
getForProduct(productId: string, params?: {
|
|
@@ -216,6 +230,19 @@ interface CommerceAdapter {
|
|
|
216
230
|
body: string;
|
|
217
231
|
}): Promise<ReviewResult>;
|
|
218
232
|
};
|
|
233
|
+
alerts: {
|
|
234
|
+
/**
|
|
235
|
+
* Asks to be emailed when a variant is back; the store mails a confirmation first. The answer is `pending` whether
|
|
236
|
+
* or not the address has an account or an alert already.
|
|
237
|
+
*/
|
|
238
|
+
subscribe(request: {
|
|
239
|
+
email: string;
|
|
240
|
+
variantId: string;
|
|
241
|
+
kind: AlertKind;
|
|
242
|
+
}): Promise<{
|
|
243
|
+
status: 'pending';
|
|
244
|
+
}>;
|
|
245
|
+
};
|
|
219
246
|
}
|
|
220
247
|
|
|
221
248
|
interface CartStore {
|
|
@@ -334,15 +361,28 @@ interface CustomerAccount {
|
|
|
334
361
|
list(opts?: {
|
|
335
362
|
cursor?: string;
|
|
336
363
|
}): Promise<OrderPage>;
|
|
337
|
-
|
|
364
|
+
/** `key`, the order's key from its checkout, reads it with no session; without one, only its owner can. */
|
|
365
|
+
get(orderNumber: string, options?: {
|
|
366
|
+
key?: string;
|
|
367
|
+
}): Promise<ShopperOrder>;
|
|
338
368
|
};
|
|
339
369
|
}
|
|
340
370
|
/** The store's capabilities report, read once per client: what it can do does not change while it runs. */
|
|
341
371
|
interface StoreCapabilitiesReader {
|
|
342
372
|
get(): Promise<StoreCapabilities>;
|
|
343
373
|
}
|
|
374
|
+
/** The shopper's recently viewed products, kept by the store under the signed-in customer or the browser's visitor. */
|
|
375
|
+
interface RecentlyViewed {
|
|
376
|
+
/** Records a view of the product; the store ignores a product that is not active. */
|
|
377
|
+
record(productId: string): Promise<void>;
|
|
378
|
+
/** The shopper's active products, newest first. */
|
|
379
|
+
list(): Promise<{
|
|
380
|
+
items: DenormalisedProduct[];
|
|
381
|
+
}>;
|
|
382
|
+
}
|
|
344
383
|
interface NymbalClient {
|
|
345
384
|
capabilities: StoreCapabilitiesReader;
|
|
385
|
+
recentlyViewed: RecentlyViewed;
|
|
346
386
|
cart: CartStore;
|
|
347
387
|
product: ProductStore;
|
|
348
388
|
productList: ProductListStore;
|
|
@@ -403,22 +443,36 @@ interface FetchClientOptions {
|
|
|
403
443
|
getCartToken?: () => string | null;
|
|
404
444
|
onUnauthorized?: () => Promise<void>;
|
|
405
445
|
}
|
|
446
|
+
/** Per request: headers sent beside the client's own. */
|
|
447
|
+
interface RequestOptions {
|
|
448
|
+
headers?: Record<string, string>;
|
|
449
|
+
/**
|
|
450
|
+
* The token refresh itself (what `onUnauthorized` sends through this client): its 401 is thrown, never waited on or
|
|
451
|
+
* sent again, so a refused refresh cannot wait on itself. An unmarked one that is refused is cut off after
|
|
452
|
+
* 10 s with the original 401.
|
|
453
|
+
*/
|
|
454
|
+
authRefresh?: boolean;
|
|
455
|
+
}
|
|
406
456
|
declare class FetchClient {
|
|
407
457
|
private readonly baseUrl;
|
|
408
458
|
private readonly credentials;
|
|
409
459
|
private readonly getAccessToken;
|
|
410
460
|
private readonly getCartToken;
|
|
411
461
|
private readonly onUnauthorized;
|
|
412
|
-
|
|
462
|
+
/** The refresh a refused request started, which every request refused meanwhile waits on rather than its own. */
|
|
463
|
+
private refreshing;
|
|
413
464
|
/** The token `GET /api/csrf` answered: the API's `nymbal.csrf` cookie, which this origin may not be able to read. */
|
|
414
465
|
private csrfToken;
|
|
415
466
|
constructor(options: FetchClientOptions);
|
|
416
|
-
request<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
467
|
+
request<T>(method: string, path: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
417
468
|
/** Like `request`, keeping the envelope's `meta` (pagination cursors live there). */
|
|
418
|
-
requestWithMeta<T>(method: string, path: string, body?: unknown): Promise<{
|
|
469
|
+
requestWithMeta<T>(method: string, path: string, body?: unknown, options?: RequestOptions): Promise<{
|
|
419
470
|
data: T;
|
|
420
471
|
meta: Record<string, unknown>;
|
|
421
472
|
}>;
|
|
473
|
+
/** Whether the refresh ended within `REFRESH_WAIT_MS`; a refresh that fails throws here, as it always has. */
|
|
474
|
+
private waitFor;
|
|
475
|
+
private refresh;
|
|
422
476
|
private csrf;
|
|
423
477
|
private fetchCsrfToken;
|
|
424
478
|
private isCsrfRejection;
|
|
@@ -429,4 +483,4 @@ declare class FetchClient {
|
|
|
429
483
|
declare function createNymbalApiAdapter(fetchClient: FetchClient): CommerceAdapter;
|
|
430
484
|
|
|
431
485
|
export { FetchClient, NymbalApiError, createAuthStore, createCartStore, createCheckoutStore, createNymbalApiAdapter, createNymbalClient, createProductListStore, createProductStore, createSearchStore, createStore, createTokenManager, getPaymentDriver, registerPaymentDriver };
|
|
432
|
-
export type { AuthResult, AuthState, AuthStore, CartState, CartStore, CheckoutDetails, CheckoutResult, CheckoutState, CheckoutStep, CheckoutStore, CommerceAdapter, CustomerAccount, DenormalisedProduct, DenormalisedVariant, FetchClientOptions, Listener, NewAddress, NymbalClient, NymbalClientOptions, OrderPage, PaymentConfirmContext, PaymentConfirmation, PaymentDriver, ProductListParams, ProductListResult, ProductListState, ProductListStore, ProductSearchParams, ProductSearchResult, ProductState, ProductStore, RegisterParams, SearchState, SearchStore, ShippingMethod, Store, StoreCapabilitiesReader, TokenManager, TokenManagerOptions };
|
|
486
|
+
export type { AuthResult, AuthState, AuthStore, CartState, CartStore, CheckoutDetails, CheckoutResult, CheckoutState, CheckoutStep, CheckoutStore, CommerceAdapter, CustomerAccount, DenormalisedProduct, DenormalisedVariant, FetchClientOptions, Listener, NewAddress, NymbalClient, NymbalClientOptions, OrderPage, PaymentConfirmContext, PaymentConfirmation, PaymentDriver, ProductListParams, ProductListResult, ProductListState, ProductListStore, ProductSearchParams, ProductSearchResult, ProductState, ProductStore, RecentlyViewed, RegisterParams, SearchState, SearchStore, ShippingMethod, Store, StoreCapabilitiesReader, TokenManager, TokenManagerOptions };
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ var NymbalApiError = class extends Error {
|
|
|
11
11
|
status;
|
|
12
12
|
context;
|
|
13
13
|
};
|
|
14
|
+
var REFRESH_WAIT_MS = 1e4;
|
|
14
15
|
var CSRF_HEADER = "x-csrf-token";
|
|
15
16
|
var UNSAFE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
16
17
|
var FetchClient = class {
|
|
@@ -19,7 +20,8 @@ var FetchClient = class {
|
|
|
19
20
|
getAccessToken;
|
|
20
21
|
getCartToken;
|
|
21
22
|
onUnauthorized;
|
|
22
|
-
|
|
23
|
+
/** The refresh a refused request started, which every request refused meanwhile waits on rather than its own. */
|
|
24
|
+
refreshing = null;
|
|
23
25
|
/** The token `GET /api/csrf` answered: the API's `nymbal.csrf` cookie, which this origin may not be able to read. */
|
|
24
26
|
csrfToken = null;
|
|
25
27
|
constructor(options) {
|
|
@@ -29,28 +31,50 @@ var FetchClient = class {
|
|
|
29
31
|
this.getCartToken = options.getCartToken;
|
|
30
32
|
this.onUnauthorized = options.onUnauthorized;
|
|
31
33
|
}
|
|
32
|
-
async request(method, path, body) {
|
|
33
|
-
return (await this.requestWithMeta(method, path, body)).data;
|
|
34
|
+
async request(method, path, body, options = {}) {
|
|
35
|
+
return (await this.requestWithMeta(method, path, body, options)).data;
|
|
34
36
|
}
|
|
35
37
|
/** Like `request`, keeping the envelope's `meta` (pagination cursors live there). */
|
|
36
|
-
async requestWithMeta(method, path, body) {
|
|
37
|
-
|
|
38
|
+
async requestWithMeta(method, path, body, options = {}) {
|
|
39
|
+
const sentDuring = this.refreshing;
|
|
40
|
+
let { response, bearer } = await this.doFetch(method, path, body, options);
|
|
38
41
|
if (await this.isCsrfRejection(response)) {
|
|
39
42
|
this.csrfToken = null;
|
|
40
|
-
response = await this.doFetch(method, path, body);
|
|
43
|
+
({ response, bearer } = await this.doFetch(method, path, body, options));
|
|
41
44
|
}
|
|
42
|
-
if (response.status === 401 && this.onUnauthorized &&
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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();
|
|
48
55
|
}
|
|
49
|
-
const retry = await this.doFetch(method, path, body);
|
|
50
|
-
return this.parseResponse(retry);
|
|
51
56
|
}
|
|
52
57
|
return this.parseResponse(response);
|
|
53
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
|
+
}
|
|
54
78
|
csrf() {
|
|
55
79
|
this.csrfToken ??= this.fetchCsrfToken().catch((err) => {
|
|
56
80
|
this.csrfToken = null;
|
|
@@ -75,8 +99,8 @@ var FetchClient = class {
|
|
|
75
99
|
return false;
|
|
76
100
|
}
|
|
77
101
|
}
|
|
78
|
-
async doFetch(method, path, body) {
|
|
79
|
-
const headers = {};
|
|
102
|
+
async doFetch(method, path, body, options) {
|
|
103
|
+
const headers = { ...options.headers };
|
|
80
104
|
const cookies = [];
|
|
81
105
|
if (UNSAFE_METHODS.has(method.toUpperCase())) {
|
|
82
106
|
const csrf = await this.csrf();
|
|
@@ -97,12 +121,13 @@ var FetchClient = class {
|
|
|
97
121
|
if (cookies.length > 0) {
|
|
98
122
|
headers["Cookie"] = cookies.join("; ");
|
|
99
123
|
}
|
|
100
|
-
|
|
124
|
+
const response = await globalThis.fetch(`${this.baseUrl}${path}`, {
|
|
101
125
|
method,
|
|
102
126
|
headers,
|
|
103
127
|
credentials: this.credentials,
|
|
104
128
|
...body !== void 0 && { body: JSON.stringify(body) }
|
|
105
129
|
});
|
|
130
|
+
return { response, bearer: token };
|
|
106
131
|
}
|
|
107
132
|
async parseResponse(response) {
|
|
108
133
|
const text = await response.text();
|
|
@@ -123,6 +148,9 @@ var FetchClient = class {
|
|
|
123
148
|
};
|
|
124
149
|
|
|
125
150
|
// src/adapter/nymbal-api-adapter.ts
|
|
151
|
+
import {
|
|
152
|
+
ORDER_KEY_HEADER
|
|
153
|
+
} from "@nymbalhq/types";
|
|
126
154
|
function createNymbalApiAdapter(fetchClient) {
|
|
127
155
|
return {
|
|
128
156
|
capabilities: {
|
|
@@ -200,7 +228,9 @@ function createNymbalApiAdapter(fetchClient) {
|
|
|
200
228
|
refresh() {
|
|
201
229
|
return fetchClient.request(
|
|
202
230
|
"POST",
|
|
203
|
-
"/api/auth/refresh"
|
|
231
|
+
"/api/auth/refresh",
|
|
232
|
+
void 0,
|
|
233
|
+
{ authRefresh: true }
|
|
204
234
|
);
|
|
205
235
|
},
|
|
206
236
|
getProfile() {
|
|
@@ -227,8 +257,22 @@ function createNymbalApiAdapter(fetchClient) {
|
|
|
227
257
|
const nextCursor = typeof meta.nextCursor === "string" ? meta.nextCursor : null;
|
|
228
258
|
return { items: data ?? [], nextCursor };
|
|
229
259
|
},
|
|
230
|
-
getOrder(orderNumber) {
|
|
231
|
-
|
|
260
|
+
getOrder(orderNumber, options = {}) {
|
|
261
|
+
const { key } = options;
|
|
262
|
+
return fetchClient.request(
|
|
263
|
+
"GET",
|
|
264
|
+
`/api/orders/${encodeURIComponent(orderNumber)}`,
|
|
265
|
+
void 0,
|
|
266
|
+
key === void 0 ? {} : { headers: { [ORDER_KEY_HEADER]: key } }
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
recentlyViewed: {
|
|
271
|
+
async record(productId) {
|
|
272
|
+
await fetchClient.request("POST", "/api/recently-viewed", { productId });
|
|
273
|
+
},
|
|
274
|
+
list() {
|
|
275
|
+
return fetchClient.request("GET", "/api/recently-viewed");
|
|
232
276
|
}
|
|
233
277
|
},
|
|
234
278
|
reviews: {
|
|
@@ -245,6 +289,11 @@ function createNymbalApiAdapter(fetchClient) {
|
|
|
245
289
|
submit(review) {
|
|
246
290
|
return fetchClient.request("POST", "/api/reviews", review);
|
|
247
291
|
}
|
|
292
|
+
},
|
|
293
|
+
alerts: {
|
|
294
|
+
subscribe(request) {
|
|
295
|
+
return fetchClient.request("POST", "/api/alerts/subscribe", request);
|
|
296
|
+
}
|
|
248
297
|
}
|
|
249
298
|
};
|
|
250
299
|
}
|
|
@@ -685,12 +734,17 @@ var INITIAL_STATE5 = {
|
|
|
685
734
|
paymentStatus: "idle",
|
|
686
735
|
error: null,
|
|
687
736
|
order: null,
|
|
688
|
-
placedOrderNumber: null
|
|
737
|
+
placedOrderNumber: null,
|
|
738
|
+
placedOrderKey: null
|
|
689
739
|
};
|
|
690
740
|
function namedOrder(err) {
|
|
691
|
-
|
|
692
|
-
const orderNumber =
|
|
693
|
-
|
|
741
|
+
const context = err instanceof NymbalApiError ? err.context : void 0;
|
|
742
|
+
const orderNumber = context?.["orderNumber"];
|
|
743
|
+
const orderKey = context?.["orderKey"];
|
|
744
|
+
return typeof orderNumber === "string" ? {
|
|
745
|
+
placedOrderNumber: orderNumber,
|
|
746
|
+
placedOrderKey: typeof orderKey === "string" ? orderKey : null
|
|
747
|
+
} : { placedOrderNumber: null, placedOrderKey: null };
|
|
694
748
|
}
|
|
695
749
|
function createCheckoutStore(adapter, cart) {
|
|
696
750
|
const store = createStore(INITIAL_STATE5);
|
|
@@ -712,7 +766,12 @@ function createCheckoutStore(adapter, cart) {
|
|
|
712
766
|
store.setState({ error: "Shipping and billing addresses are required" });
|
|
713
767
|
return null;
|
|
714
768
|
}
|
|
715
|
-
store.setState({
|
|
769
|
+
store.setState({
|
|
770
|
+
paymentStatus: "processing",
|
|
771
|
+
error: null,
|
|
772
|
+
placedOrderNumber: null,
|
|
773
|
+
placedOrderKey: null
|
|
774
|
+
});
|
|
716
775
|
try {
|
|
717
776
|
const result = await adapter.checkout.create({
|
|
718
777
|
email: state.email,
|
|
@@ -733,13 +792,13 @@ function createCheckoutStore(adapter, cart) {
|
|
|
733
792
|
await cart?.load();
|
|
734
793
|
return result;
|
|
735
794
|
} catch (err) {
|
|
736
|
-
const
|
|
795
|
+
const placed = namedOrder(err);
|
|
737
796
|
store.setState({
|
|
738
797
|
paymentStatus: "failed",
|
|
739
798
|
error: err instanceof Error ? err.message : String(err),
|
|
740
|
-
|
|
799
|
+
...placed
|
|
741
800
|
});
|
|
742
|
-
if (placedOrderNumber !== null) await cart?.load();
|
|
801
|
+
if (placed.placedOrderNumber !== null) await cart?.load();
|
|
743
802
|
return null;
|
|
744
803
|
}
|
|
745
804
|
}
|
|
@@ -919,7 +978,7 @@ function createNymbalClient(options) {
|
|
|
919
978
|
},
|
|
920
979
|
orders: {
|
|
921
980
|
list: (opts) => adapter.customers.listOrders(opts),
|
|
922
|
-
get: (orderNumber) => adapter.customers.getOrder(orderNumber)
|
|
981
|
+
get: (orderNumber, options2) => adapter.customers.getOrder(orderNumber, options2)
|
|
923
982
|
}
|
|
924
983
|
};
|
|
925
984
|
let report = null;
|
|
@@ -935,6 +994,21 @@ function createNymbalClient(options) {
|
|
|
935
994
|
return report;
|
|
936
995
|
}
|
|
937
996
|
};
|
|
997
|
+
const recentlyViewedOf = () => {
|
|
998
|
+
const reader = adapter.recentlyViewed;
|
|
999
|
+
if (reader === void 0) {
|
|
1000
|
+
throw new Error("This client\u2019s adapter cannot record or list recently viewed products.");
|
|
1001
|
+
}
|
|
1002
|
+
return reader;
|
|
1003
|
+
};
|
|
1004
|
+
const recentlyViewed = {
|
|
1005
|
+
async record(productId) {
|
|
1006
|
+
await recentlyViewedOf().record(productId);
|
|
1007
|
+
},
|
|
1008
|
+
async list() {
|
|
1009
|
+
return recentlyViewedOf().list();
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
938
1012
|
tokenManager.scheduleRefresh(() => adapter.customers.refresh());
|
|
939
1013
|
function destroy() {
|
|
940
1014
|
cart.destroy();
|
|
@@ -947,6 +1021,7 @@ function createNymbalClient(options) {
|
|
|
947
1021
|
}
|
|
948
1022
|
return {
|
|
949
1023
|
capabilities,
|
|
1024
|
+
recentlyViewed,
|
|
950
1025
|
cart,
|
|
951
1026
|
product,
|
|
952
1027
|
productList,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapter/fetch-client.ts","../src/adapter/nymbal-api-adapter.ts","../src/auth/token-manager.ts","../src/store/create-store.ts","../src/store/cart-store.ts","../src/store/product-store.ts","../src/store/product-list-store.ts","../src/store/search-store.ts","../src/store/checkout-store.ts","../src/store/auth-store.ts","../src/client.ts","../src/payment-drivers.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\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 private handlingUnauthorized = false\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>(method: string, path: string, body?: unknown): Promise<T> {\n return (await this.requestWithMeta<T>(method, path, body)).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 ): Promise<{ data: T; meta: Record<string, unknown> }> {\n let response = await this.doFetch(method, path, body)\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 = await this.doFetch(method, path, body)\n }\n\n if (response.status === 401 && this.onUnauthorized && !this.handlingUnauthorized) {\n this.handlingUnauthorized = true\n try {\n await this.onUnauthorized()\n } finally {\n this.handlingUnauthorized = false\n }\n const retry = await this.doFetch(method, path, body)\n return this.parseResponse<T>(retry)\n }\n\n return this.parseResponse<T>(response)\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(method: string, path: string, body?: unknown): Promise<Response> {\n const headers: Record<string, string> = {}\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 return globalThis.fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n credentials: this.credentials,\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\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 type {\n Cart,\n Customer,\n CustomerAddress,\n Order,\n ReviewsResult,\n ReviewResult,\n 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 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 )\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(orderNumber: string) {\n return fetchClient.request<Order>('GET', `/api/orders/${encodeURIComponent(orderNumber)}`)\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}\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","export type Listener = () => void\n\nexport interface Store<TState> {\n getState(): TState\n setState(partial: Partial<TState> | ((prev: TState) => Partial<TState>)): void\n subscribe(listener: Listener): () => void\n destroy(): void\n}\n\nexport function createStore<TState extends object>(\n initialState: TState,\n): Store<TState> {\n let state = initialState\n const listeners = new Set<Listener>()\n\n function getState(): TState {\n return state\n }\n\n function setState(partial: Partial<TState> | ((prev: TState) => Partial<TState>)): void {\n const next = typeof partial === 'function' ? partial(state) : partial\n state = { ...state, ...next }\n for (const listener of listeners) {\n listener()\n }\n }\n\n function subscribe(listener: Listener): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n function destroy(): void {\n listeners.clear()\n }\n\n return { getState, setState, subscribe, destroy }\n}\n","import type { CartItem } from '@nymbalhq/types'\nimport type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport type { CartState } from '../types.js'\nimport { createStore } from './create-store.js'\n\nfunction deriveItemCount(items: CartItem[]): number {\n let count = 0\n for (const item of items) {\n count += item.qty\n }\n return count\n}\n\nconst INITIAL_STATE: CartState = {\n items: [],\n subtotalMinor: 0,\n currency: 'USD',\n itemCount: 0,\n loading: false,\n error: null,\n}\n\nexport interface CartStore {\n getState(): CartState\n subscribe(listener: () => void): () => void\n addItem(variantId: string, qty?: number): Promise<void>\n removeItem(variantId: string): Promise<void>\n updateQuantity(variantId: string, qty: number): Promise<void>\n clear(): Promise<void>\n load(): Promise<void>\n destroy(): void\n}\n\nexport function createCartStore(adapter: CommerceAdapter): CartStore {\n const store = createStore<CartState>(INITIAL_STATE)\n\n async function load(): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const cart = await adapter.cart.get()\n store.setState({\n items: cart.items,\n subtotalMinor: cart.subtotalMinor,\n currency: cart.currency,\n itemCount: deriveItemCount(cart.items),\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function addItem(variantId: string, qty = 1): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const cart = await adapter.cart.addItem(variantId, qty)\n store.setState({\n items: cart.items,\n subtotalMinor: cart.subtotalMinor,\n currency: cart.currency,\n itemCount: deriveItemCount(cart.items),\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function removeItem(variantId: string): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const cart = await adapter.cart.removeItem(variantId)\n store.setState({\n items: cart.items,\n subtotalMinor: cart.subtotalMinor,\n currency: cart.currency,\n itemCount: deriveItemCount(cart.items),\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function updateQuantity(variantId: string, qty: number): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const cart = await adapter.cart.updateQuantity(variantId, qty)\n store.setState({\n items: cart.items,\n subtotalMinor: cart.subtotalMinor,\n currency: cart.currency,\n itemCount: deriveItemCount(cart.items),\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function clear(): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n await adapter.cart.clear()\n store.setState({\n items: [],\n subtotalMinor: 0,\n itemCount: 0,\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n addItem,\n removeItem,\n updateQuantity,\n clear,\n load,\n destroy: store.destroy,\n }\n}\n","import type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport type { ProductState, DenormalisedVariant } from '../types.js'\nimport { createStore } from './create-store.js'\n\nconst INITIAL_STATE: ProductState = {\n product: null,\n selectedVariant: null,\n loading: false,\n error: null,\n}\n\nexport interface ProductStore {\n getState(): ProductState\n subscribe(listener: () => void): () => void\n loadBySlug(slug: string): Promise<void>\n selectVariant(variant: DenormalisedVariant | null): void\n destroy(): void\n}\n\nexport function createProductStore(adapter: CommerceAdapter): ProductStore {\n const store = createStore<ProductState>(INITIAL_STATE)\n\n async function loadBySlug(slug: string): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const product = await adapter.products.getBySlug(slug)\n const firstVariant = product.variants[0] ?? null\n store.setState({\n product,\n selectedVariant: firstVariant,\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n function selectVariant(variant: DenormalisedVariant | null): void {\n store.setState({ selectedVariant: variant })\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n loadBySlug,\n selectVariant,\n destroy: store.destroy,\n }\n}\n","import { DEFAULT_PRODUCT_SORT, type ProductSortKey } from '@nymbalhq/types'\nimport type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport type { ProductListState, ProductListParams } from '../types.js'\nimport { createStore } from './create-store.js'\n\nconst INITIAL_STATE: ProductListState = {\n products: [],\n facets: [],\n filters: {},\n sort: DEFAULT_PRODUCT_SORT,\n pagination: { cursor: null, hasMore: false },\n loading: false,\n error: null,\n}\n\nexport interface ProductListStore {\n getState(): ProductListState\n subscribe(listener: () => void): () => void\n load(params?: ProductListParams): Promise<void>\n loadMore(): Promise<void>\n applyFilter(name: string, value: string | string[]): Promise<void>\n removeFilter(name: string): Promise<void>\n setSort(sort: ProductSortKey): Promise<void>\n destroy(): void\n}\n\nexport function createProductListStore(adapter: CommerceAdapter): ProductListStore {\n const store = createStore<ProductListState>(INITIAL_STATE)\n let lastLimit: number | undefined\n // Only the latest request's answer may reach the state: an older one carries another sort's items and cursor.\n let generation = 0\n\n function buildParams(): ProductListParams {\n const state = store.getState()\n const params: ProductListParams = { sort: state.sort }\n if (lastLimit !== undefined) params.limit = lastLimit\n const category = state.filters['category']\n if (typeof category === 'string') {\n params.category = category\n }\n return params\n }\n\n /** With params, they become the listing's category and sort, so `setSort` and `loadMore` keep them. */\n async function load(params?: ProductListParams): Promise<void> {\n if (params !== undefined) {\n if (params.limit !== undefined) lastLimit = params.limit\n const filters = { ...store.getState().filters }\n if (params.category !== undefined) filters['category'] = params.category\n else delete filters['category']\n // Loading from this update on: an update that is not loading is a settled answer, which a server-rendered\n // listing gives its cards way to.\n store.setState({ filters, sort: params.sort ?? DEFAULT_PRODUCT_SORT, loading: true })\n }\n const request = buildParams()\n if (params?.cursor !== undefined) request.cursor = params.cursor\n const current = ++generation\n store.setState({ loading: true })\n try {\n const result = await adapter.products.list(request)\n if (current !== generation) return\n store.setState({\n products: result.items,\n facets: result.facets ?? store.getState().facets,\n pagination: { cursor: result.nextCursor, hasMore: result.nextCursor !== null },\n loading: false,\n error: null,\n })\n } catch (err) {\n if (current !== generation) return\n console.error('[ProductListStore] load failed', err)\n store.setState({ loading: false, error: err instanceof Error ? err.message : String(err) })\n }\n }\n\n async function loadMore(): Promise<void> {\n const state = store.getState()\n if (!state.pagination.hasMore || state.loading) return\n const current = ++generation\n store.setState({ loading: true })\n try {\n const params = buildParams()\n if (state.pagination.cursor !== null) {\n params.cursor = state.pagination.cursor\n }\n const result = await adapter.products.list(params)\n if (current !== generation) return\n store.setState({\n products: [...state.products, ...result.items],\n pagination: { cursor: result.nextCursor, hasMore: result.nextCursor !== null },\n loading: false,\n error: null,\n })\n } catch (err) {\n if (current !== generation) return\n console.error('[ProductListStore] loadMore failed', err)\n store.setState({ loading: false, error: err instanceof Error ? err.message : String(err) })\n }\n }\n\n async function applyFilter(name: string, value: string | string[]): Promise<void> {\n const filters = { ...store.getState().filters, [name]: value }\n store.setState({ filters })\n await load()\n }\n\n async function removeFilter(name: string): Promise<void> {\n const filters = { ...store.getState().filters }\n delete filters[name]\n store.setState({ filters })\n await load()\n }\n\n async function setSort(sort: ProductSortKey): Promise<void> {\n store.setState({ sort })\n await load()\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n load,\n loadMore,\n applyFilter,\n removeFilter,\n setSort,\n destroy: store.destroy,\n }\n}\n","import type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport type { SearchState } from '../types.js'\nimport { createStore } from './create-store.js'\n\nconst INITIAL_STATE: SearchState = {\n query: '',\n category: null,\n results: [],\n total: 0,\n nextCursor: null,\n facets: [],\n loading: false,\n}\n\nexport interface SearchStore {\n getState(): SearchState\n subscribe(listener: () => void): () => void\n /** Page 1 of `query`, in `options.category` (a slug) when given, otherwise in every category. */\n search(query: string, options?: { category?: string | null }): Promise<void>\n /** Appends the next page; a no-op when every match is loaded or a page is loading. */\n loadMore(): Promise<void>\n /** Page 1 of the current query in `category` (a slug), or in every category when null. */\n setCategory(category: string | null): Promise<void>\n clearSearch(): void\n destroy(): void\n}\n\nexport function createSearchStore(adapter: CommerceAdapter): SearchStore {\n const store = createStore<SearchState>(INITIAL_STATE)\n // Each search supersedes the last: an answer for an older one is dropped.\n let generation = 0\n\n async function search(query: string, options?: { category?: string | null }): Promise<void> {\n const run = ++generation\n if (!query.trim()) {\n store.setState({ ...INITIAL_STATE })\n return\n }\n const category = options?.category ?? null\n store.setState({ query, category, loading: true })\n try {\n const result = await (category === null\n ? adapter.products.search(query)\n : adapter.products.search(query, { category }))\n if (run !== generation) return\n store.setState({\n results: result.items,\n total: result.total,\n nextCursor: result.nextCursor,\n facets: result.facets ?? [],\n loading: false,\n })\n } catch (err) {\n if (run !== generation) return\n console.error('[SearchStore] search failed', err)\n store.setState({ loading: false })\n }\n }\n\n async function loadMore(): Promise<void> {\n const { query, category, nextCursor, loading } = store.getState()\n if (nextCursor === null || loading) return\n const run = generation\n store.setState({ loading: true })\n try {\n const result = await adapter.products.search(query, {\n cursor: nextCursor,\n ...(category !== null && { category }),\n })\n if (run !== generation) return\n store.setState({\n results: [...store.getState().results, ...result.items],\n total: result.total,\n nextCursor: result.nextCursor,\n loading: false,\n })\n } catch (err) {\n if (run !== generation) return\n console.error('[SearchStore] load more failed', err)\n store.setState({ loading: false })\n }\n }\n\n function setCategory(category: string | null): Promise<void> {\n return search(store.getState().query, { category })\n }\n\n function clearSearch(): void {\n generation++\n store.setState({ ...INITIAL_STATE })\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n search,\n loadMore,\n setCategory,\n clearSearch,\n destroy: store.destroy,\n }\n}\n","import type { Address } from '@nymbalhq/types'\nimport type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport { NymbalApiError } from '../adapter/fetch-client.js'\nimport type { CartStore } from './cart-store.js'\nimport type { CheckoutResult, CheckoutState, ShippingMethod } from '../types.js'\nimport { createStore } from './create-store.js'\n\nconst INITIAL_STATE: CheckoutState = {\n step: 'contact',\n email: '',\n shippingAddress: null,\n billingAddress: null,\n shippingMethod: null,\n paymentStatus: 'idle',\n error: null,\n order: null,\n placedOrderNumber: null,\n}\n\n/** The order a refused resubmit names (409 `checkout.cart_empty` or `checkout.payment_in_progress`), if any. */\nfunction namedOrder(err: unknown): string | null {\n if (!(err instanceof NymbalApiError)) return null\n const orderNumber = err.context?.['orderNumber']\n return typeof orderNumber === 'string' ? orderNumber : null\n}\n\nexport interface CheckoutStore {\n getState(): CheckoutState\n subscribe(listener: () => void): () => void\n setEmail(email: string): void\n setShippingAddress(address: Address): void\n setBillingAddress(address: Address): void\n setShippingMethod(method: ShippingMethod): void\n /** Places the order; resolves to the server's result, or null when there is none. */\n submitPayment(): Promise<CheckoutResult | null>\n /** The provider confirmed the payment on the client (Stripe's `confirmCardPayment`). */\n markConfirmed(): void\n reset(): void\n destroy(): void\n}\n\n/**\n * `cart`, when given, is loaded again once the server has answered a submit: placing an order changes the cart on the\n * server (emptied when confirmed, kept while awaiting payment), and whatever renders the cart store must show that.\n */\nexport function createCheckoutStore(adapter: CommerceAdapter, cart?: CartStore): CheckoutStore {\n const store = createStore<CheckoutState>(INITIAL_STATE)\n\n function setEmail(email: string): void {\n store.setState({ email, step: 'shipping' })\n }\n\n function setShippingAddress(address: Address): void {\n store.setState({ shippingAddress: address })\n }\n\n function setBillingAddress(address: Address): void {\n store.setState({ billingAddress: address })\n }\n\n function setShippingMethod(method: ShippingMethod): void {\n store.setState({ shippingMethod: method, step: 'payment' })\n }\n\n async function submitPayment(): Promise<CheckoutResult | null> {\n const state = store.getState()\n if (!state.shippingAddress || !state.billingAddress) {\n store.setState({ error: 'Shipping and billing addresses are required' })\n return null\n }\n store.setState({ paymentStatus: 'processing', error: null, placedOrderNumber: null })\n try {\n const result = await adapter.checkout.create({\n email: state.email,\n billingAddress: state.billingAddress,\n shippingAddress: state.shippingAddress,\n })\n const { status, reason } = result.payment\n store.setState({\n paymentStatus: status,\n order: {\n orderId: result.orderId,\n orderNumber: result.orderNumber,\n paymentIntent: result.paymentIntent,\n },\n error:\n status === 'failed'\n ? `Your payment did not go through${reason ? ` (${reason})` : ''}. Please try again.`\n : null,\n ...(status === 'succeeded' && { step: 'confirmation' as const }),\n })\n await cart?.load()\n return result\n } catch (err) {\n const placedOrderNumber = namedOrder(err)\n store.setState({\n paymentStatus: 'failed',\n error: err instanceof Error ? err.message : String(err),\n placedOrderNumber,\n })\n // A refusal naming an order: the cart became (or is becoming) that order on the server.\n if (placedOrderNumber !== null) await cart?.load()\n return null\n }\n }\n\n function markConfirmed(): void {\n store.setState({ paymentStatus: 'succeeded', step: 'confirmation' })\n }\n\n function reset(): void {\n store.setState(INITIAL_STATE)\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n setEmail,\n setShippingAddress,\n setBillingAddress,\n setShippingMethod,\n submitPayment,\n markConfirmed,\n reset,\n destroy: store.destroy,\n }\n}\n","import type { Customer } from '@nymbalhq/types'\nimport type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport type { TokenManager } from '../auth/token-manager.js'\nimport type { AuthState, RegisterParams } from '../types.js'\nimport { createStore } from './create-store.js'\n\nconst INITIAL_STATE: AuthState = {\n customer: null,\n isAuthenticated: false,\n loading: false,\n error: null,\n}\n\nexport interface AuthStore {\n getState(): AuthState\n subscribe(listener: () => void): () => void\n login(email: string, password: string): Promise<void>\n register(params: RegisterParams): Promise<void>\n logout(): Promise<void>\n refreshToken(): Promise<void>\n updateProfile(updates: Partial<Pick<Customer, 'firstName' | 'lastName' | 'phone'>>): Promise<void>\n loadProfile(): Promise<void>\n destroy(): void\n}\n\nexport function createAuthStore(\n adapter: CommerceAdapter,\n tokenManager: TokenManager,\n): AuthStore {\n const store = createStore<AuthState>(INITIAL_STATE)\n\n async function login(email: string, password: string): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const result = await adapter.customers.login(email, password)\n tokenManager.setTokens(result.accessToken, result.expiresIn)\n store.setState({\n customer: result.customer,\n isAuthenticated: true,\n loading: false,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n store.setState({ loading: false, error: message })\n throw err\n }\n }\n\n async function register(params: RegisterParams): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const result = await adapter.customers.register(params)\n tokenManager.setTokens(result.accessToken, result.expiresIn)\n store.setState({\n customer: result.customer,\n isAuthenticated: true,\n loading: false,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n store.setState({ loading: false, error: message })\n throw err\n }\n }\n\n async function logout(): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n await adapter.customers.logout()\n tokenManager.clear()\n store.setState({\n customer: null,\n isAuthenticated: false,\n loading: false,\n })\n } catch (err) {\n tokenManager.clear()\n store.setState({\n customer: null,\n isAuthenticated: false,\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function refreshToken(): Promise<void> {\n try {\n const result = await adapter.customers.refresh()\n tokenManager.setTokens(result.accessToken, result.expiresIn)\n } catch (err) {\n console.error('[AuthStore] token refresh failed, logging out', err)\n tokenManager.clear()\n store.setState({\n customer: null,\n isAuthenticated: false,\n })\n }\n }\n\n async function updateProfile(\n updates: Partial<Pick<Customer, 'firstName' | 'lastName' | 'phone'>>,\n ): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const customer = await adapter.customers.updateProfile(updates)\n store.setState({ customer, loading: false })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function loadProfile(): Promise<void> {\n if (!tokenManager.getAccessToken()) return\n store.setState({ loading: true, error: null })\n try {\n const customer = await adapter.customers.getProfile()\n store.setState({\n customer,\n isAuthenticated: true,\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n login,\n register,\n logout,\n refreshToken,\n updateProfile,\n loadProfile,\n destroy: store.destroy,\n }\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 } from './auth/token-manager.js'\nimport { createCartStore, type CartStore } from './store/cart-store.js'\nimport { createProductStore, type ProductStore } from './store/product-store.js'\nimport { createProductListStore, type ProductListStore } from './store/product-list-store.js'\nimport { createSearchStore, type SearchStore } from './store/search-store.js'\nimport { createCheckoutStore, type CheckoutStore } from './store/checkout-store.js'\nimport { createAuthStore, type AuthStore } from './store/auth-store.js'\nimport type { CustomerAddress, Order, StoreCapabilities } from '@nymbalhq/types'\nimport type { 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 get(orderNumber: string): Promise<Order>\n }\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\nexport interface NymbalClient {\n capabilities: StoreCapabilitiesReader\n cart: CartStore\n product: ProductStore\n productList: ProductListStore\n search: SearchStore\n checkout: CheckoutStore\n auth: AuthStore\n customer: CustomerAccount\n adapter: CommerceAdapter\n destroy(): void\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 cart = createCartStore(adapter)\n const product = createProductStore(adapter)\n const productList = createProductListStore(adapter)\n const search = createSearchStore(adapter)\n const checkout = createCheckoutStore(adapter, cart)\n const auth = createAuthStore(adapter, tokenManager)\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) => adapter.customers.getOrder(orderNumber),\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 tokenManager.scheduleRefresh(() => adapter.customers.refresh())\n\n function destroy(): void {\n cart.destroy()\n product.destroy()\n productList.destroy()\n search.destroy()\n checkout.destroy()\n auth.destroy()\n tokenManager.destroy()\n }\n\n return {\n capabilities,\n cart,\n product,\n productList,\n search,\n checkout,\n auth,\n customer,\n adapter,\n destroy,\n }\n}\n","import type { Address } from '@nymbalhq/types'\n\n/** What a checkout already knows about the shopper, which a driver may hand its processor with the payment. */\nexport interface PaymentConfirmContext {\n email?: string\n billing?: Address\n}\n\n/** What a payment driver's `confirm` answers: paid now, accepted without a payment yet, or refused with a reason. */\nexport type PaymentConfirmation = { ok: true; confirmed: boolean } | { ok: false; error: string }\n\n/**\n * The browser half of a payments adapter, named by the `driver` of GET /api/capabilities' payments client. `mount`\n * renders whatever the shopper pays with inside `el`; `confirm` completes a payment the API left `requires_action`,\n * with the shopper's email and billing address when the checkout has them.\n */\nexport interface PaymentDriver {\n mount(el: HTMLElement, options: Record<string, string>): Promise<void> | void\n confirm(clientSecret: string, context?: PaymentConfirmContext): Promise<PaymentConfirmation>\n}\n\n// On globalThis, like the registered client: a page that loads two copies of the SDK still has one registry.\nconst DRIVERS_KEY = Symbol.for('nymbal.paymentDrivers')\n\nfunction drivers(): Map<string, PaymentDriver> {\n const holder = globalThis as Record<symbol, Map<string, PaymentDriver> | undefined>\n holder[DRIVERS_KEY] ??= new Map()\n return holder[DRIVERS_KEY]\n}\n\n/** Registers `driver` under `name`, replacing any driver of that name. */\nexport function registerPaymentDriver(name: string, driver: PaymentDriver): void {\n drivers().set(name, driver)\n}\n\nexport function getPaymentDriver(name: string): PaymentDriver | undefined {\n return drivers().get(name)\n}\n"],"mappings":";AAAO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YACkB,MAChB,SACgB,QACA,SAChB;AACA,UAAM,OAAO;AALG;AAEA;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AAAA,EAPkB;AAAA,EAEA;AAAA,EACA;AAKpB;AAUA,IAAM,cAAc;AACpB,IAAM,iBAAiB,oBAAI,IAAI,CAAC,QAAQ,OAAO,SAAS,QAAQ,CAAC;AAQ1D,IAAM,cAAN,MAAkB;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,uBAAuB;AAAA;AAAA,EAEvB,YAAoC;AAAA,EAE5C,YAAY,SAA6B;AACvC,SAAK,UAAU,QAAQ,QAAQ,QAAQ,QAAQ,EAAE;AACjD,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,iBAAiB,QAAQ,mBAAmB,MAAM;AACvD,SAAK,eAAe,QAAQ;AAC5B,SAAK,iBAAiB,QAAQ;AAAA,EAChC;AAAA,EAEA,MAAM,QAAW,QAAgB,MAAc,MAA4B;AACzE,YAAQ,MAAM,KAAK,gBAAmB,QAAQ,MAAM,IAAI,GAAG;AAAA,EAC7D;AAAA;AAAA,EAGA,MAAM,gBACJ,QACA,MACA,MACqD;AACrD,QAAI,WAAW,MAAM,KAAK,QAAQ,QAAQ,MAAM,IAAI;AAGpD,QAAI,MAAM,KAAK,gBAAgB,QAAQ,GAAG;AACxC,WAAK,YAAY;AACjB,iBAAW,MAAM,KAAK,QAAQ,QAAQ,MAAM,IAAI;AAAA,IAClD;AAEA,QAAI,SAAS,WAAW,OAAO,KAAK,kBAAkB,CAAC,KAAK,sBAAsB;AAChF,WAAK,uBAAuB;AAC5B,UAAI;AACF,cAAM,KAAK,eAAe;AAAA,MAC5B,UAAE;AACA,aAAK,uBAAuB;AAAA,MAC9B;AACA,YAAM,QAAQ,MAAM,KAAK,QAAQ,QAAQ,MAAM,IAAI;AACnD,aAAO,KAAK,cAAiB,KAAK;AAAA,IACpC;AAEA,WAAO,KAAK,cAAiB,QAAQ;AAAA,EACvC;AAAA,EAEQ,OAAwB;AAC9B,SAAK,cAAc,KAAK,eAAe,EAAE,MAAM,CAAC,QAAiB;AAC/D,WAAK,YAAY;AACjB,YAAM;AAAA,IACR,CAAC;AACD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,iBAAkC;AAC9C,UAAM,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO,aAAa;AAAA,MAClE,QAAQ;AAAA,MACR,aAAa,KAAK;AAAA,IACpB,CAAC;AACD,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,cAAiC,QAAQ;AACrE,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,gBAAgB,UAAsC;AAClE,QAAI,SAAS,WAAW,IAAK,QAAO;AACpC,UAAM,OAAO,MAAM,SAAS,MAAM,EAAE,KAAK;AACzC,QAAI;AACF,aAAQ,KAAK,MAAM,IAAI,EAAwB,OAAO,SAAS;AAAA,IACjE,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,QAAQ,QAAgB,MAAc,MAAmC;AACrF,UAAM,UAAkC,CAAC;AACzC,UAAM,UAAoB,CAAC;AAE3B,QAAI,eAAe,IAAI,OAAO,YAAY,CAAC,GAAG;AAC5C,YAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,cAAQ,WAAW,IAAI;AAEvB,cAAQ,KAAK,eAAe,IAAI,EAAE;AAAA,IACpC;AAEA,QAAI,SAAS,QAAW;AACtB,cAAQ,cAAc,IAAI;AAAA,IAC5B;AAEA,UAAM,QAAQ,KAAK,eAAe;AAClC,QAAI,OAAO;AACT,cAAQ,eAAe,IAAI,UAAU,KAAK;AAAA,IAC5C;AAEA,UAAM,YAAY,KAAK,eAAe;AACtC,QAAI,WAAW;AACb,cAAQ,QAAQ,eAAe,SAAS,EAAE;AAAA,IAC5C;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,cAAQ,QAAQ,IAAI,QAAQ,KAAK,IAAI;AAAA,IACvC;AAEA,WAAO,WAAW,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,MAChD;AAAA,MACA;AAAA,MACA,aAAa,KAAK;AAAA,MAClB,GAAI,SAAS,UAAa,EAAE,MAAM,KAAK,UAAU,IAAI,EAAE;AAAA,IACzD,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,cACZ,UACqD;AACrD,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,QAAI,CAAC,MAAM;AACT,aAAO,EAAE,MAAM,QAAgB,MAAM,CAAC,EAAE;AAAA,IAC1C;AAEA,UAAM,WAAW,KAAK,MAAM,IAAI;AAEhC,QAAI,SAAS,OAAO;AAClB,YAAM,IAAI;AAAA,QACR,SAAS,MAAM;AAAA,QACf,SAAS,MAAM;AAAA,QACf,SAAS;AAAA,QACT,SAAS,MAAM;AAAA,MACjB;AAAA,IACF;AAEA,WAAO,EAAE,MAAM,SAAS,MAAW,MAAM,SAAS,QAAQ,CAAC,EAAE;AAAA,EAC/D;AACF;;;AC1IO,SAAS,uBAAuB,aAA2C;AAChF,SAAO;AAAA,IACL,cAAc;AAAA,MACZ,MAAM;AACJ,eAAO,YAAY,QAA2B,OAAO,mBAAmB;AAAA,MAC1E;AAAA,IACF;AAAA,IAEA,MAAM;AAAA,MACJ,MAAM;AACJ,eAAO,YAAY,QAAc,OAAO,WAAW;AAAA,MACrD;AAAA,MACA,QAAQ,WAAmB,KAAa;AACtC,eAAO,YAAY,QAAc,QAAQ,mBAAmB,EAAE,WAAW,IAAI,CAAC;AAAA,MAChF;AAAA,MACA,WAAW,WAAmB;AAC5B,eAAO,YAAY;AAAA,UACjB;AAAA,UACA,mBAAmB,mBAAmB,SAAS,CAAC;AAAA,QAClD;AAAA,MACF;AAAA,MACA,eAAe,WAAmB,KAAa;AAC7C,eAAO,YAAY;AAAA,UACjB;AAAA,UACA,mBAAmB,mBAAmB,SAAS,CAAC;AAAA,UAChD,EAAE,IAAI;AAAA,QACR;AAAA,MACF;AAAA,MACA,QAAQ;AACN,eAAO,YAAY,QAAc,UAAU,WAAW;AAAA,MACxD;AAAA,IACF;AAAA,IAEA,UAAU;AAAA,MACR,UAAU,MAAc;AACtB,eAAO,YAAY;AAAA,UACjB;AAAA,UACA,iBAAiB,mBAAmB,IAAI,CAAC;AAAA,QAC3C;AAAA,MACF;AAAA,MACA,KAAK,QAA4B;AAC/B,cAAM,SAAS,IAAI,gBAAgB;AACnC,YAAI,QAAQ,UAAU,OAAW,QAAO,IAAI,SAAS,OAAO,OAAO,KAAK,CAAC;AACzE,YAAI,QAAQ,WAAW,OAAW,QAAO,IAAI,UAAU,OAAO,MAAM;AACpE,YAAI,QAAQ,aAAa,OAAW,QAAO,IAAI,YAAY,OAAO,QAAQ;AAC1E,YAAI,QAAQ,SAAS,OAAW,QAAO,IAAI,QAAQ,OAAO,IAAI;AAC9D,cAAM,KAAK,OAAO,SAAS;AAC3B,eAAO,YAAY,QAA2B,OAAO,gBAAgB,KAAK,IAAI,EAAE,KAAK,EAAE,EAAE;AAAA,MAC3F;AAAA,MACA,OAAO,OAAe,QAA8B;AAClD,cAAM,SAAS,IAAI,gBAAgB;AACnC,eAAO,IAAI,KAAK,KAAK;AACrB,YAAI,QAAQ,UAAU,OAAW,QAAO,IAAI,SAAS,OAAO,OAAO,KAAK,CAAC;AACzE,YAAI,QAAQ,WAAW,OAAW,QAAO,IAAI,UAAU,OAAO,MAAM;AACpE,YAAI,QAAQ,aAAa,OAAW,QAAO,IAAI,YAAY,OAAO,QAAQ;AAC1E,eAAO,YAAY;AAAA,UACjB;AAAA,UACA,wBAAwB,OAAO,SAAS,CAAC;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAAA,IAEA,UAAU;AAAA,MACR,OAAO,SAA0B;AAC/B,eAAO,YAAY,QAAwB,QAAQ,iBAAiB,OAAO;AAAA,MAC7E;AAAA,IACF;AAAA,IAEA,WAAW;AAAA,MACT,SAAS,QAAwB;AAC/B,eAAO,YAAY,QAAoB,QAAQ,sBAAsB,MAAM;AAAA,MAC7E;AAAA,MACA,MAAM,OAAe,UAAkB;AACrC,eAAO,YAAY,QAAoB,QAAQ,mBAAmB,EAAE,OAAO,SAAS,CAAC;AAAA,MACvF;AAAA,MACA,MAAM,SAAS;AACb,cAAM,YAAY,QAAyB,QAAQ,kBAAkB;AAAA,MACvE;AAAA,MACA,UAAU;AACR,eAAO,YAAY;AAAA,UACjB;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,MACA,aAAa;AACX,eAAO,YAAY,QAAkB,OAAO,mBAAmB;AAAA,MACjE;AAAA,MACA,cAAc,SAAsE;AAClF,eAAO,YAAY,QAAkB,SAAS,qBAAqB,OAAO;AAAA,MAC5E;AAAA,MACA,gBAAgB;AACd,eAAO,YAAY,QAA2B,OAAO,6BAA6B;AAAA,MACpF;AAAA,MACA,WAAW,SAAqB;AAC9B,eAAO,YAAY,QAAyB,QAAQ,+BAA+B,OAAO;AAAA,MAC5F;AAAA,MACA,MAAM,cAAc,IAAY;AAC9B,cAAM,YAAY;AAAA,UAChB;AAAA,UACA,+BAA+B,mBAAmB,EAAE,CAAC;AAAA,QACvD;AAAA,MACF;AAAA,MACA,MAAM,WAAW,QAAkD;AACjE,cAAM,KACJ,QAAQ,WAAW,SAAY,WAAW,mBAAmB,OAAO,MAAM,CAAC,KAAK;AAClF,cAAM,EAAE,MAAM,KAAK,IAAI,MAAM,YAAY,gBAAyB,OAAO,cAAc,EAAE,EAAE;AAC3F,cAAM,aAAa,OAAO,KAAK,eAAe,WAAW,KAAK,aAAa;AAC3E,eAAO,EAAE,OAAO,QAAQ,CAAC,GAAG,WAAW;AAAA,MACzC;AAAA,MACA,SAAS,aAAqB;AAC5B,eAAO,YAAY,QAAe,OAAO,eAAe,mBAAmB,WAAW,CAAC,EAAE;AAAA,MAC3F;AAAA,IACF;AAAA,IAEA,SAAS;AAAA,MACP,cAAc,WAAmB,QAA8C;AAC7E,cAAM,SAAS,IAAI,gBAAgB;AACnC,YAAI,QAAQ,UAAU,OAAW,QAAO,IAAI,SAAS,OAAO,OAAO,KAAK,CAAC;AACzE,YAAI,QAAQ,WAAW,OAAW,QAAO,IAAI,UAAU,OAAO,MAAM;AACpE,cAAM,KAAK,OAAO,SAAS;AAC3B,eAAO,YAAY;AAAA,UACjB;AAAA,UACA,iBAAiB,mBAAmB,SAAS,CAAC,WAAW,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,QAC7E;AAAA,MACF;AAAA,MACA,OAAO,QAA4E;AACjF,eAAO,YAAY,QAAsB,QAAQ,gBAAgB,MAAM;AAAA,MACzE;AAAA,IACF;AAAA,EACF;AACF;;;AC9IA,IAAM,sBAAsB;AAErB,SAAS,mBAAmB,SAA6C;AAC9E,QAAM,cAAc,SAAS,WAAW;AACxC,QAAM,aAAa,SAAS,cAAc;AAE1C,MAAI,cAA6B;AACjC,MAAI,eAAqD;AACzD,MAAI,YAAgF;AAEpF,WAAS,iBAAgC;AACvC,QAAI,gBAAgB,kBAAkB,OAAO,WAAW,iBAAiB,aAAa;AACpF,aAAO,WAAW,aAAa,QAAQ,UAAU;AAAA,IACnD;AACA,WAAO;AAAA,EACT;AAEA,WAAS,eAAe,OAA4B;AAClD,kBAAc;AACd,QAAI,gBAAgB,kBAAkB,OAAO,WAAW,iBAAiB,aAAa;AACpF,UAAI,OAAO;AACT,mBAAW,aAAa,QAAQ,YAAY,KAAK;AAAA,MACnD,OAAO;AACL,mBAAW,aAAa,WAAW,UAAU;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAEA,WAAS,UAAU,aAAqB,WAAyB;AAC/D,mBAAe,WAAW;AAC1B,wBAAoB,SAAS;AAAA,EAC/B;AAEA,WAAS,QAAc;AACrB,mBAAe,IAAI;AACnB,QAAI,iBAAiB,MAAM;AACzB,mBAAa,YAAY;AACzB,qBAAe;AAAA,IACjB;AAAA,EACF;AAEA,WAAS,oBAAoB,WAAyB;AACpD,QAAI,iBAAiB,MAAM;AACzB,mBAAa,YAAY;AAAA,IAC3B;AACA,UAAM,UAAU,KAAK,IAAI,KAAM,YAAY,MAAM,GAAI;AACrD,mBAAe,WAAW,YAAY;AACpC,UAAI,CAAC,UAAW;AAChB,UAAI;AACF,cAAM,SAAS,MAAM,UAAU;AAC/B,kBAAU,OAAO,aAAa,OAAO,SAAS;AAAA,MAChD,SAAS,KAAK;AACZ,gBAAQ,MAAM,wDAAwD,GAAG;AACzE,cAAM;AAAA,MACR;AAAA,IACF,GAAG,OAAO;AAAA,EACZ;AAEA,WAAS,gBACP,IACM;AACN,gBAAY;AAAA,EACd;AAEA,WAAS,UAAgB;AACvB,UAAM;AACN,gBAAY;AAAA,EACd;AAEA,SAAO,EAAE,gBAAgB,WAAW,OAAO,iBAAiB,QAAQ;AACtE;;;AC1EO,SAAS,YACd,cACe;AACf,MAAI,QAAQ;AACZ,QAAM,YAAY,oBAAI,IAAc;AAEpC,WAAS,WAAmB;AAC1B,WAAO;AAAA,EACT;AAEA,WAAS,SAAS,SAAsE;AACtF,UAAM,OAAO,OAAO,YAAY,aAAa,QAAQ,KAAK,IAAI;AAC9D,YAAQ,EAAE,GAAG,OAAO,GAAG,KAAK;AAC5B,eAAW,YAAY,WAAW;AAChC,eAAS;AAAA,IACX;AAAA,EACF;AAEA,WAAS,UAAU,UAAgC;AACjD,cAAU,IAAI,QAAQ;AACtB,WAAO,MAAM;AACX,gBAAU,OAAO,QAAQ;AAAA,IAC3B;AAAA,EACF;AAEA,WAAS,UAAgB;AACvB,cAAU,MAAM;AAAA,EAClB;AAEA,SAAO,EAAE,UAAU,UAAU,WAAW,QAAQ;AAClD;;;AClCA,SAAS,gBAAgB,OAA2B;AAClD,MAAI,QAAQ;AACZ,aAAW,QAAQ,OAAO;AACxB,aAAS,KAAK;AAAA,EAChB;AACA,SAAO;AACT;AAEA,IAAM,gBAA2B;AAAA,EAC/B,OAAO,CAAC;AAAA,EACR,eAAe;AAAA,EACf,UAAU;AAAA,EACV,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AACT;AAaO,SAAS,gBAAgB,SAAqC;AACnE,QAAM,QAAQ,YAAuB,aAAa;AAElD,iBAAe,OAAsB;AACnC,UAAM,SAAS,EAAE,SAAS,MAAM,OAAO,KAAK,CAAC;AAC7C,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,KAAK,IAAI;AACpC,YAAM,SAAS;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,eAAe,KAAK;AAAA,QACpB,UAAU,KAAK;AAAA,QACf,WAAW,gBAAgB,KAAK,KAAK;AAAA,QACrC,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,iBAAe,QAAQ,WAAmB,MAAM,GAAkB;AAChE,UAAM,SAAS,EAAE,SAAS,MAAM,OAAO,KAAK,CAAC;AAC7C,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,KAAK,QAAQ,WAAW,GAAG;AACtD,YAAM,SAAS;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,eAAe,KAAK;AAAA,QACpB,UAAU,KAAK;AAAA,QACf,WAAW,gBAAgB,KAAK,KAAK;AAAA,QACrC,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,iBAAe,WAAW,WAAkC;AAC1D,UAAM,SAAS,EAAE,SAAS,MAAM,OAAO,KAAK,CAAC;AAC7C,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,KAAK,WAAW,SAAS;AACpD,YAAM,SAAS;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,eAAe,KAAK;AAAA,QACpB,UAAU,KAAK;AAAA,QACf,WAAW,gBAAgB,KAAK,KAAK;AAAA,QACrC,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,iBAAe,eAAe,WAAmB,KAA4B;AAC3E,UAAM,SAAS,EAAE,SAAS,MAAM,OAAO,KAAK,CAAC;AAC7C,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,KAAK,eAAe,WAAW,GAAG;AAC7D,YAAM,SAAS;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,eAAe,KAAK;AAAA,QACpB,UAAU,KAAK;AAAA,QACf,WAAW,gBAAgB,KAAK,KAAK;AAAA,QACrC,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,iBAAe,QAAuB;AACpC,UAAM,SAAS,EAAE,SAAS,MAAM,OAAO,KAAK,CAAC;AAC7C,QAAI;AACF,YAAM,QAAQ,KAAK,MAAM;AACzB,YAAM,SAAS;AAAA,QACb,OAAO,CAAC;AAAA,QACR,eAAe;AAAA,QACf,WAAW;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,WAAW,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,MAAM;AAAA,EACjB;AACF;;;ACxIA,IAAMA,iBAA8B;AAAA,EAClC,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,OAAO;AACT;AAUO,SAAS,mBAAmB,SAAwC;AACzE,QAAM,QAAQ,YAA0BA,cAAa;AAErD,iBAAe,WAAW,MAA6B;AACrD,UAAM,SAAS,EAAE,SAAS,MAAM,OAAO,KAAK,CAAC;AAC7C,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,SAAS,UAAU,IAAI;AACrD,YAAM,eAAe,QAAQ,SAAS,CAAC,KAAK;AAC5C,YAAM,SAAS;AAAA,QACb;AAAA,QACA,iBAAiB;AAAA,QACjB,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,WAAS,cAAc,SAA2C;AAChE,UAAM,SAAS,EAAE,iBAAiB,QAAQ,CAAC;AAAA,EAC7C;AAEA,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,WAAW,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA,SAAS,MAAM;AAAA,EACjB;AACF;;;ACnDA,SAAS,4BAAiD;AAK1D,IAAMC,iBAAkC;AAAA,EACtC,UAAU,CAAC;AAAA,EACX,QAAQ,CAAC;AAAA,EACT,SAAS,CAAC;AAAA,EACV,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,MAAM,SAAS,MAAM;AAAA,EAC3C,SAAS;AAAA,EACT,OAAO;AACT;AAaO,SAAS,uBAAuB,SAA4C;AACjF,QAAM,QAAQ,YAA8BA,cAAa;AACzD,MAAI;AAEJ,MAAI,aAAa;AAEjB,WAAS,cAAiC;AACxC,UAAM,QAAQ,MAAM,SAAS;AAC7B,UAAM,SAA4B,EAAE,MAAM,MAAM,KAAK;AACrD,QAAI,cAAc,OAAW,QAAO,QAAQ;AAC5C,UAAM,WAAW,MAAM,QAAQ,UAAU;AACzC,QAAI,OAAO,aAAa,UAAU;AAChC,aAAO,WAAW;AAAA,IACpB;AACA,WAAO;AAAA,EACT;AAGA,iBAAe,KAAK,QAA2C;AAC7D,QAAI,WAAW,QAAW;AACxB,UAAI,OAAO,UAAU,OAAW,aAAY,OAAO;AACnD,YAAM,UAAU,EAAE,GAAG,MAAM,SAAS,EAAE,QAAQ;AAC9C,UAAI,OAAO,aAAa,OAAW,SAAQ,UAAU,IAAI,OAAO;AAAA,UAC3D,QAAO,QAAQ,UAAU;AAG9B,YAAM,SAAS,EAAE,SAAS,MAAM,OAAO,QAAQ,sBAAsB,SAAS,KAAK,CAAC;AAAA,IACtF;AACA,UAAM,UAAU,YAAY;AAC5B,QAAI,QAAQ,WAAW,OAAW,SAAQ,SAAS,OAAO;AAC1D,UAAM,UAAU,EAAE;AAClB,UAAM,SAAS,EAAE,SAAS,KAAK,CAAC;AAChC,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,SAAS,KAAK,OAAO;AAClD,UAAI,YAAY,WAAY;AAC5B,YAAM,SAAS;AAAA,QACb,UAAU,OAAO;AAAA,QACjB,QAAQ,OAAO,UAAU,MAAM,SAAS,EAAE;AAAA,QAC1C,YAAY,EAAE,QAAQ,OAAO,YAAY,SAAS,OAAO,eAAe,KAAK;AAAA,QAC7E,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,YAAY,WAAY;AAC5B,cAAQ,MAAM,kCAAkC,GAAG;AACnD,YAAM,SAAS,EAAE,SAAS,OAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,CAAC;AAAA,IAC5F;AAAA,EACF;AAEA,iBAAe,WAA0B;AACvC,UAAM,QAAQ,MAAM,SAAS;AAC7B,QAAI,CAAC,MAAM,WAAW,WAAW,MAAM,QAAS;AAChD,UAAM,UAAU,EAAE;AAClB,UAAM,SAAS,EAAE,SAAS,KAAK,CAAC;AAChC,QAAI;AACF,YAAM,SAAS,YAAY;AAC3B,UAAI,MAAM,WAAW,WAAW,MAAM;AACpC,eAAO,SAAS,MAAM,WAAW;AAAA,MACnC;AACA,YAAM,SAAS,MAAM,QAAQ,SAAS,KAAK,MAAM;AACjD,UAAI,YAAY,WAAY;AAC5B,YAAM,SAAS;AAAA,QACb,UAAU,CAAC,GAAG,MAAM,UAAU,GAAG,OAAO,KAAK;AAAA,QAC7C,YAAY,EAAE,QAAQ,OAAO,YAAY,SAAS,OAAO,eAAe,KAAK;AAAA,QAC7E,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,YAAY,WAAY;AAC5B,cAAQ,MAAM,sCAAsC,GAAG;AACvD,YAAM,SAAS,EAAE,SAAS,OAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,CAAC;AAAA,IAC5F;AAAA,EACF;AAEA,iBAAe,YAAY,MAAc,OAAyC;AAChF,UAAM,UAAU,EAAE,GAAG,MAAM,SAAS,EAAE,SAAS,CAAC,IAAI,GAAG,MAAM;AAC7D,UAAM,SAAS,EAAE,QAAQ,CAAC;AAC1B,UAAM,KAAK;AAAA,EACb;AAEA,iBAAe,aAAa,MAA6B;AACvD,UAAM,UAAU,EAAE,GAAG,MAAM,SAAS,EAAE,QAAQ;AAC9C,WAAO,QAAQ,IAAI;AACnB,UAAM,SAAS,EAAE,QAAQ,CAAC;AAC1B,UAAM,KAAK;AAAA,EACb;AAEA,iBAAe,QAAQ,MAAqC;AAC1D,UAAM,SAAS,EAAE,KAAK,CAAC;AACvB,UAAM,KAAK;AAAA,EACb;AAEA,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,WAAW,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,MAAM;AAAA,EACjB;AACF;;;AC5HA,IAAMC,iBAA6B;AAAA,EACjC,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS,CAAC;AAAA,EACV,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,QAAQ,CAAC;AAAA,EACT,SAAS;AACX;AAeO,SAAS,kBAAkB,SAAuC;AACvE,QAAM,QAAQ,YAAyBA,cAAa;AAEpD,MAAI,aAAa;AAEjB,iBAAe,OAAO,OAAe,SAAuD;AAC1F,UAAM,MAAM,EAAE;AACd,QAAI,CAAC,MAAM,KAAK,GAAG;AACjB,YAAM,SAAS,EAAE,GAAGA,eAAc,CAAC;AACnC;AAAA,IACF;AACA,UAAM,WAAW,SAAS,YAAY;AACtC,UAAM,SAAS,EAAE,OAAO,UAAU,SAAS,KAAK,CAAC;AACjD,QAAI;AACF,YAAM,SAAS,OAAO,aAAa,OAC/B,QAAQ,SAAS,OAAO,KAAK,IAC7B,QAAQ,SAAS,OAAO,OAAO,EAAE,SAAS,CAAC;AAC/C,UAAI,QAAQ,WAAY;AACxB,YAAM,SAAS;AAAA,QACb,SAAS,OAAO;AAAA,QAChB,OAAO,OAAO;AAAA,QACd,YAAY,OAAO;AAAA,QACnB,QAAQ,OAAO,UAAU,CAAC;AAAA,QAC1B,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,QAAQ,WAAY;AACxB,cAAQ,MAAM,+BAA+B,GAAG;AAChD,YAAM,SAAS,EAAE,SAAS,MAAM,CAAC;AAAA,IACnC;AAAA,EACF;AAEA,iBAAe,WAA0B;AACvC,UAAM,EAAE,OAAO,UAAU,YAAY,QAAQ,IAAI,MAAM,SAAS;AAChE,QAAI,eAAe,QAAQ,QAAS;AACpC,UAAM,MAAM;AACZ,UAAM,SAAS,EAAE,SAAS,KAAK,CAAC;AAChC,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,SAAS,OAAO,OAAO;AAAA,QAClD,QAAQ;AAAA,QACR,GAAI,aAAa,QAAQ,EAAE,SAAS;AAAA,MACtC,CAAC;AACD,UAAI,QAAQ,WAAY;AACxB,YAAM,SAAS;AAAA,QACb,SAAS,CAAC,GAAG,MAAM,SAAS,EAAE,SAAS,GAAG,OAAO,KAAK;AAAA,QACtD,OAAO,OAAO;AAAA,QACd,YAAY,OAAO;AAAA,QACnB,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,QAAQ,WAAY;AACxB,cAAQ,MAAM,kCAAkC,GAAG;AACnD,YAAM,SAAS,EAAE,SAAS,MAAM,CAAC;AAAA,IACnC;AAAA,EACF;AAEA,WAAS,YAAY,UAAwC;AAC3D,WAAO,OAAO,MAAM,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC;AAAA,EACpD;AAEA,WAAS,cAAoB;AAC3B;AACA,UAAM,SAAS,EAAE,GAAGA,eAAc,CAAC;AAAA,EACrC;AAEA,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,WAAW,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,MAAM;AAAA,EACjB;AACF;;;AC9FA,IAAMC,iBAA+B;AAAA,EACnC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,OAAO;AAAA,EACP,OAAO;AAAA,EACP,mBAAmB;AACrB;AAGA,SAAS,WAAW,KAA6B;AAC/C,MAAI,EAAE,eAAe,gBAAiB,QAAO;AAC7C,QAAM,cAAc,IAAI,UAAU,aAAa;AAC/C,SAAO,OAAO,gBAAgB,WAAW,cAAc;AACzD;AAqBO,SAAS,oBAAoB,SAA0B,MAAiC;AAC7F,QAAM,QAAQ,YAA2BA,cAAa;AAEtD,WAAS,SAAS,OAAqB;AACrC,UAAM,SAAS,EAAE,OAAO,MAAM,WAAW,CAAC;AAAA,EAC5C;AAEA,WAAS,mBAAmB,SAAwB;AAClD,UAAM,SAAS,EAAE,iBAAiB,QAAQ,CAAC;AAAA,EAC7C;AAEA,WAAS,kBAAkB,SAAwB;AACjD,UAAM,SAAS,EAAE,gBAAgB,QAAQ,CAAC;AAAA,EAC5C;AAEA,WAAS,kBAAkB,QAA8B;AACvD,UAAM,SAAS,EAAE,gBAAgB,QAAQ,MAAM,UAAU,CAAC;AAAA,EAC5D;AAEA,iBAAe,gBAAgD;AAC7D,UAAM,QAAQ,MAAM,SAAS;AAC7B,QAAI,CAAC,MAAM,mBAAmB,CAAC,MAAM,gBAAgB;AACnD,YAAM,SAAS,EAAE,OAAO,8CAA8C,CAAC;AACvE,aAAO;AAAA,IACT;AACA,UAAM,SAAS,EAAE,eAAe,cAAc,OAAO,MAAM,mBAAmB,KAAK,CAAC;AACpF,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,SAAS,OAAO;AAAA,QAC3C,OAAO,MAAM;AAAA,QACb,gBAAgB,MAAM;AAAA,QACtB,iBAAiB,MAAM;AAAA,MACzB,CAAC;AACD,YAAM,EAAE,QAAQ,OAAO,IAAI,OAAO;AAClC,YAAM,SAAS;AAAA,QACb,eAAe;AAAA,QACf,OAAO;AAAA,UACL,SAAS,OAAO;AAAA,UAChB,aAAa,OAAO;AAAA,UACpB,eAAe,OAAO;AAAA,QACxB;AAAA,QACA,OACE,WAAW,WACP,kCAAkC,SAAS,KAAK,MAAM,MAAM,EAAE,wBAC9D;AAAA,QACN,GAAI,WAAW,eAAe,EAAE,MAAM,eAAwB;AAAA,MAChE,CAAC;AACD,YAAM,MAAM,KAAK;AACjB,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,YAAM,oBAAoB,WAAW,GAAG;AACxC,YAAM,SAAS;AAAA,QACb,eAAe;AAAA,QACf,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACtD;AAAA,MACF,CAAC;AAED,UAAI,sBAAsB,KAAM,OAAM,MAAM,KAAK;AACjD,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,gBAAsB;AAC7B,UAAM,SAAS,EAAE,eAAe,aAAa,MAAM,eAAe,CAAC;AAAA,EACrE;AAEA,WAAS,QAAc;AACrB,UAAM,SAASA,cAAa;AAAA,EAC9B;AAEA,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,WAAW,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,MAAM;AAAA,EACjB;AACF;;;ACxHA,IAAMC,iBAA2B;AAAA,EAC/B,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,OAAO;AACT;AAcO,SAAS,gBACd,SACA,cACW;AACX,QAAM,QAAQ,YAAuBA,cAAa;AAElD,iBAAe,MAAM,OAAe,UAAiC;AACnE,UAAM,SAAS,EAAE,SAAS,MAAM,OAAO,KAAK,CAAC;AAC7C,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,UAAU,MAAM,OAAO,QAAQ;AAC5D,mBAAa,UAAU,OAAO,aAAa,OAAO,SAAS;AAC3D,YAAM,SAAS;AAAA,QACb,UAAU,OAAO;AAAA,QACjB,iBAAiB;AAAA,QACjB,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAM,SAAS,EAAE,SAAS,OAAO,OAAO,QAAQ,CAAC;AACjD,YAAM;AAAA,IACR;AAAA,EACF;AAEA,iBAAe,SAAS,QAAuC;AAC7D,UAAM,SAAS,EAAE,SAAS,MAAM,OAAO,KAAK,CAAC;AAC7C,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,UAAU,SAAS,MAAM;AACtD,mBAAa,UAAU,OAAO,aAAa,OAAO,SAAS;AAC3D,YAAM,SAAS;AAAA,QACb,UAAU,OAAO;AAAA,QACjB,iBAAiB;AAAA,QACjB,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAM,SAAS,EAAE,SAAS,OAAO,OAAO,QAAQ,CAAC;AACjD,YAAM;AAAA,IACR;AAAA,EACF;AAEA,iBAAe,SAAwB;AACrC,UAAM,SAAS,EAAE,SAAS,MAAM,OAAO,KAAK,CAAC;AAC7C,QAAI;AACF,YAAM,QAAQ,UAAU,OAAO;AAC/B,mBAAa,MAAM;AACnB,YAAM,SAAS;AAAA,QACb,UAAU;AAAA,QACV,iBAAiB;AAAA,QACjB,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,mBAAa,MAAM;AACnB,YAAM,SAAS;AAAA,QACb,UAAU;AAAA,QACV,iBAAiB;AAAA,QACjB,SAAS;AAAA,QACT,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,iBAAe,eAA8B;AAC3C,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,UAAU,QAAQ;AAC/C,mBAAa,UAAU,OAAO,aAAa,OAAO,SAAS;AAAA,IAC7D,SAAS,KAAK;AACZ,cAAQ,MAAM,iDAAiD,GAAG;AAClE,mBAAa,MAAM;AACnB,YAAM,SAAS;AAAA,QACb,UAAU;AAAA,QACV,iBAAiB;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,iBAAe,cACb,SACe;AACf,UAAM,SAAS,EAAE,SAAS,MAAM,OAAO,KAAK,CAAC;AAC7C,QAAI;AACF,YAAM,WAAW,MAAM,QAAQ,UAAU,cAAc,OAAO;AAC9D,YAAM,SAAS,EAAE,UAAU,SAAS,MAAM,CAAC;AAAA,IAC7C,SAAS,KAAK;AACZ,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,iBAAe,cAA6B;AAC1C,QAAI,CAAC,aAAa,eAAe,EAAG;AACpC,UAAM,SAAS,EAAE,SAAS,MAAM,OAAO,KAAK,CAAC;AAC7C,QAAI;AACF,YAAM,WAAW,MAAM,QAAQ,UAAU,WAAW;AACpD,YAAM,SAAS;AAAA,QACb;AAAA,QACA,iBAAiB;AAAA,QACjB,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,WAAW,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,MAAM;AAAA,EACjB;AACF;;;AC5FO,SAAS,mBAAmB,SAA4C;AAC7E,QAAM,eAAe,mBAAmB;AAAA,IACtC,GAAI,QAAQ,iBAAiB,UAAa,EAAE,SAAS,QAAQ,aAAa;AAAA,EAC5E,CAAC;AAED,MAAI;AAEJ,MAAI,QAAQ,SAAS;AACnB,cAAU,QAAQ;AAAA,EACpB,OAAO;AACL,UAAM,cAAc,IAAI,YAAY;AAAA,MAClC,SAAS,QAAQ;AAAA,MACjB,GAAI,QAAQ,gBAAgB,UAAa,EAAE,aAAa,QAAQ,YAAY;AAAA,MAC5E,gBAAgB,MAAM,aAAa,eAAe;AAAA,MAClD,GAAI,QAAQ,iBAAiB,UAAa,EAAE,cAAc,QAAQ,aAAa;AAAA,MAC/E,gBAAgB,YAAY;AAC1B,YAAI;AACF,gBAAM,SAAS,MAAM,QAAQ,UAAU,QAAQ;AAC/C,uBAAa,UAAU,OAAO,aAAa,OAAO,SAAS;AAAA,QAC7D,SAAS,KAAK;AAEZ,kBAAQ,KAAK,uDAAuD,GAAG;AACvE,uBAAa,MAAM;AAAA,QACrB;AAAA,MACF;AAAA,IACF,CAAC;AACD,cAAU,uBAAuB,WAAW;AAAA,EAC9C;AAEA,QAAM,OAAO,gBAAgB,OAAO;AACpC,QAAM,UAAU,mBAAmB,OAAO;AAC1C,QAAM,cAAc,uBAAuB,OAAO;AAClD,QAAM,SAAS,kBAAkB,OAAO;AACxC,QAAM,WAAW,oBAAoB,SAAS,IAAI;AAClD,QAAM,OAAO,gBAAgB,SAAS,YAAY;AAElD,QAAM,WAA4B;AAAA,IAChC,WAAW;AAAA,MACT,MAAM,MAAM,QAAQ,UAAU,cAAc;AAAA,MAC5C,QAAQ,CAAC,YAAY,QAAQ,UAAU,WAAW,OAAO;AAAA,MACzD,QAAQ,CAAC,OAAO,QAAQ,UAAU,cAAc,EAAE;AAAA,IACpD;AAAA,IACA,QAAQ;AAAA,MACN,MAAM,CAAC,SAAS,QAAQ,UAAU,WAAW,IAAI;AAAA,MACjD,KAAK,CAAC,gBAAgB,QAAQ,UAAU,SAAS,WAAW;AAAA,IAC9D;AAAA,EACF;AAGA,MAAI,SAA4C;AAChD,QAAM,eAAwC;AAAA,IAC5C,MAAM;AACJ,UAAI,WAAW,MAAM;AACnB,cAAM,SAAS,QAAQ;AACvB,iBACE,WAAW,SACP,QAAQ,OAAO,IAAI,MAAM,gEAA2D,CAAC,IACrF,OAAO,IAAI;AACjB,eAAO,MAAM,MAAM;AACjB,mBAAS;AAAA,QACX,CAAC;AAAA,MACH;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAEA,eAAa,gBAAgB,MAAM,QAAQ,UAAU,QAAQ,CAAC;AAE9D,WAAS,UAAgB;AACvB,SAAK,QAAQ;AACb,YAAQ,QAAQ;AAChB,gBAAY,QAAQ;AACpB,WAAO,QAAQ;AACf,aAAS,QAAQ;AACjB,SAAK,QAAQ;AACb,iBAAa,QAAQ;AAAA,EACvB;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACxHA,IAAM,cAAc,uBAAO,IAAI,uBAAuB;AAEtD,SAAS,UAAsC;AAC7C,QAAM,SAAS;AACf,SAAO,WAAW,MAAM,oBAAI,IAAI;AAChC,SAAO,OAAO,WAAW;AAC3B;AAGO,SAAS,sBAAsB,MAAc,QAA6B;AAC/E,UAAQ,EAAE,IAAI,MAAM,MAAM;AAC5B;AAEO,SAAS,iBAAiB,MAAyC;AACxE,SAAO,QAAQ,EAAE,IAAI,IAAI;AAC3B;","names":["INITIAL_STATE","INITIAL_STATE","INITIAL_STATE","INITIAL_STATE","INITIAL_STATE"]}
|
|
1
|
+
{"version":3,"sources":["../src/adapter/fetch-client.ts","../src/adapter/nymbal-api-adapter.ts","../src/auth/token-manager.ts","../src/store/create-store.ts","../src/store/cart-store.ts","../src/store/product-store.ts","../src/store/product-list-store.ts","../src/store/search-store.ts","../src/store/checkout-store.ts","../src/store/auth-store.ts","../src/client.ts","../src/payment-drivers.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_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 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(orderNumber: string, options: { key?: string } = {}) {\n const { key } = options\n return fetchClient.request<ShopperOrder>(\n 'GET',\n `/api/orders/${encodeURIComponent(orderNumber)}`,\n undefined,\n key === undefined ? {} : { headers: { [ORDER_KEY_HEADER]: key } },\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","export type Listener = () => void\n\nexport interface Store<TState> {\n getState(): TState\n setState(partial: Partial<TState> | ((prev: TState) => Partial<TState>)): void\n subscribe(listener: Listener): () => void\n destroy(): void\n}\n\nexport function createStore<TState extends object>(\n initialState: TState,\n): Store<TState> {\n let state = initialState\n const listeners = new Set<Listener>()\n\n function getState(): TState {\n return state\n }\n\n function setState(partial: Partial<TState> | ((prev: TState) => Partial<TState>)): void {\n const next = typeof partial === 'function' ? partial(state) : partial\n state = { ...state, ...next }\n for (const listener of listeners) {\n listener()\n }\n }\n\n function subscribe(listener: Listener): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n function destroy(): void {\n listeners.clear()\n }\n\n return { getState, setState, subscribe, destroy }\n}\n","import type { CartItem } from '@nymbalhq/types'\nimport type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport type { CartState } from '../types.js'\nimport { createStore } from './create-store.js'\n\nfunction deriveItemCount(items: CartItem[]): number {\n let count = 0\n for (const item of items) {\n count += item.qty\n }\n return count\n}\n\nconst INITIAL_STATE: CartState = {\n items: [],\n subtotalMinor: 0,\n currency: 'USD',\n itemCount: 0,\n loading: false,\n error: null,\n}\n\nexport interface CartStore {\n getState(): CartState\n subscribe(listener: () => void): () => void\n addItem(variantId: string, qty?: number): Promise<void>\n removeItem(variantId: string): Promise<void>\n updateQuantity(variantId: string, qty: number): Promise<void>\n clear(): Promise<void>\n load(): Promise<void>\n destroy(): void\n}\n\nexport function createCartStore(adapter: CommerceAdapter): CartStore {\n const store = createStore<CartState>(INITIAL_STATE)\n\n async function load(): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const cart = await adapter.cart.get()\n store.setState({\n items: cart.items,\n subtotalMinor: cart.subtotalMinor,\n currency: cart.currency,\n itemCount: deriveItemCount(cart.items),\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function addItem(variantId: string, qty = 1): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const cart = await adapter.cart.addItem(variantId, qty)\n store.setState({\n items: cart.items,\n subtotalMinor: cart.subtotalMinor,\n currency: cart.currency,\n itemCount: deriveItemCount(cart.items),\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function removeItem(variantId: string): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const cart = await adapter.cart.removeItem(variantId)\n store.setState({\n items: cart.items,\n subtotalMinor: cart.subtotalMinor,\n currency: cart.currency,\n itemCount: deriveItemCount(cart.items),\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function updateQuantity(variantId: string, qty: number): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const cart = await adapter.cart.updateQuantity(variantId, qty)\n store.setState({\n items: cart.items,\n subtotalMinor: cart.subtotalMinor,\n currency: cart.currency,\n itemCount: deriveItemCount(cart.items),\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function clear(): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n await adapter.cart.clear()\n store.setState({\n items: [],\n subtotalMinor: 0,\n itemCount: 0,\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n addItem,\n removeItem,\n updateQuantity,\n clear,\n load,\n destroy: store.destroy,\n }\n}\n","import type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport type { ProductState, DenormalisedVariant } from '../types.js'\nimport { createStore } from './create-store.js'\n\nconst INITIAL_STATE: ProductState = {\n product: null,\n selectedVariant: null,\n loading: false,\n error: null,\n}\n\nexport interface ProductStore {\n getState(): ProductState\n subscribe(listener: () => void): () => void\n loadBySlug(slug: string): Promise<void>\n selectVariant(variant: DenormalisedVariant | null): void\n destroy(): void\n}\n\nexport function createProductStore(adapter: CommerceAdapter): ProductStore {\n const store = createStore<ProductState>(INITIAL_STATE)\n\n async function loadBySlug(slug: string): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const product = await adapter.products.getBySlug(slug)\n const firstVariant = product.variants[0] ?? null\n store.setState({\n product,\n selectedVariant: firstVariant,\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n function selectVariant(variant: DenormalisedVariant | null): void {\n store.setState({ selectedVariant: variant })\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n loadBySlug,\n selectVariant,\n destroy: store.destroy,\n }\n}\n","import { DEFAULT_PRODUCT_SORT, type ProductSortKey } from '@nymbalhq/types'\nimport type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport type { ProductListState, ProductListParams } from '../types.js'\nimport { createStore } from './create-store.js'\n\nconst INITIAL_STATE: ProductListState = {\n products: [],\n facets: [],\n filters: {},\n sort: DEFAULT_PRODUCT_SORT,\n pagination: { cursor: null, hasMore: false },\n loading: false,\n error: null,\n}\n\nexport interface ProductListStore {\n getState(): ProductListState\n subscribe(listener: () => void): () => void\n load(params?: ProductListParams): Promise<void>\n loadMore(): Promise<void>\n applyFilter(name: string, value: string | string[]): Promise<void>\n removeFilter(name: string): Promise<void>\n setSort(sort: ProductSortKey): Promise<void>\n destroy(): void\n}\n\nexport function createProductListStore(adapter: CommerceAdapter): ProductListStore {\n const store = createStore<ProductListState>(INITIAL_STATE)\n let lastLimit: number | undefined\n // Only the latest request's answer may reach the state: an older one carries another sort's items and cursor.\n let generation = 0\n\n function buildParams(): ProductListParams {\n const state = store.getState()\n const params: ProductListParams = { sort: state.sort }\n if (lastLimit !== undefined) params.limit = lastLimit\n const category = state.filters['category']\n if (typeof category === 'string') {\n params.category = category\n }\n return params\n }\n\n /** With params, they become the listing's category and sort, so `setSort` and `loadMore` keep them. */\n async function load(params?: ProductListParams): Promise<void> {\n if (params !== undefined) {\n if (params.limit !== undefined) lastLimit = params.limit\n const filters = { ...store.getState().filters }\n if (params.category !== undefined) filters['category'] = params.category\n else delete filters['category']\n // Loading from this update on: an update that is not loading is a settled answer, which a server-rendered\n // listing gives its cards way to.\n store.setState({ filters, sort: params.sort ?? DEFAULT_PRODUCT_SORT, loading: true })\n }\n const request = buildParams()\n if (params?.cursor !== undefined) request.cursor = params.cursor\n const current = ++generation\n store.setState({ loading: true })\n try {\n const result = await adapter.products.list(request)\n if (current !== generation) return\n store.setState({\n products: result.items,\n facets: result.facets ?? store.getState().facets,\n pagination: { cursor: result.nextCursor, hasMore: result.nextCursor !== null },\n loading: false,\n error: null,\n })\n } catch (err) {\n if (current !== generation) return\n console.error('[ProductListStore] load failed', err)\n store.setState({ loading: false, error: err instanceof Error ? err.message : String(err) })\n }\n }\n\n async function loadMore(): Promise<void> {\n const state = store.getState()\n if (!state.pagination.hasMore || state.loading) return\n const current = ++generation\n store.setState({ loading: true })\n try {\n const params = buildParams()\n if (state.pagination.cursor !== null) {\n params.cursor = state.pagination.cursor\n }\n const result = await adapter.products.list(params)\n if (current !== generation) return\n store.setState({\n products: [...state.products, ...result.items],\n pagination: { cursor: result.nextCursor, hasMore: result.nextCursor !== null },\n loading: false,\n error: null,\n })\n } catch (err) {\n if (current !== generation) return\n console.error('[ProductListStore] loadMore failed', err)\n store.setState({ loading: false, error: err instanceof Error ? err.message : String(err) })\n }\n }\n\n async function applyFilter(name: string, value: string | string[]): Promise<void> {\n const filters = { ...store.getState().filters, [name]: value }\n store.setState({ filters })\n await load()\n }\n\n async function removeFilter(name: string): Promise<void> {\n const filters = { ...store.getState().filters }\n delete filters[name]\n store.setState({ filters })\n await load()\n }\n\n async function setSort(sort: ProductSortKey): Promise<void> {\n store.setState({ sort })\n await load()\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n load,\n loadMore,\n applyFilter,\n removeFilter,\n setSort,\n destroy: store.destroy,\n }\n}\n","import type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport type { SearchState } from '../types.js'\nimport { createStore } from './create-store.js'\n\nconst INITIAL_STATE: SearchState = {\n query: '',\n category: null,\n results: [],\n total: 0,\n nextCursor: null,\n facets: [],\n loading: false,\n}\n\nexport interface SearchStore {\n getState(): SearchState\n subscribe(listener: () => void): () => void\n /** Page 1 of `query`, in `options.category` (a slug) when given, otherwise in every category. */\n search(query: string, options?: { category?: string | null }): Promise<void>\n /** Appends the next page; a no-op when every match is loaded or a page is loading. */\n loadMore(): Promise<void>\n /** Page 1 of the current query in `category` (a slug), or in every category when null. */\n setCategory(category: string | null): Promise<void>\n clearSearch(): void\n destroy(): void\n}\n\nexport function createSearchStore(adapter: CommerceAdapter): SearchStore {\n const store = createStore<SearchState>(INITIAL_STATE)\n // Each search supersedes the last: an answer for an older one is dropped.\n let generation = 0\n\n async function search(query: string, options?: { category?: string | null }): Promise<void> {\n const run = ++generation\n if (!query.trim()) {\n store.setState({ ...INITIAL_STATE })\n return\n }\n const category = options?.category ?? null\n store.setState({ query, category, loading: true })\n try {\n const result = await (category === null\n ? adapter.products.search(query)\n : adapter.products.search(query, { category }))\n if (run !== generation) return\n store.setState({\n results: result.items,\n total: result.total,\n nextCursor: result.nextCursor,\n facets: result.facets ?? [],\n loading: false,\n })\n } catch (err) {\n if (run !== generation) return\n console.error('[SearchStore] search failed', err)\n store.setState({ loading: false })\n }\n }\n\n async function loadMore(): Promise<void> {\n const { query, category, nextCursor, loading } = store.getState()\n if (nextCursor === null || loading) return\n const run = generation\n store.setState({ loading: true })\n try {\n const result = await adapter.products.search(query, {\n cursor: nextCursor,\n ...(category !== null && { category }),\n })\n if (run !== generation) return\n store.setState({\n results: [...store.getState().results, ...result.items],\n total: result.total,\n nextCursor: result.nextCursor,\n loading: false,\n })\n } catch (err) {\n if (run !== generation) return\n console.error('[SearchStore] load more failed', err)\n store.setState({ loading: false })\n }\n }\n\n function setCategory(category: string | null): Promise<void> {\n return search(store.getState().query, { category })\n }\n\n function clearSearch(): void {\n generation++\n store.setState({ ...INITIAL_STATE })\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n search,\n loadMore,\n setCategory,\n clearSearch,\n destroy: store.destroy,\n }\n}\n","import type { Address } from '@nymbalhq/types'\nimport type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport { NymbalApiError } from '../adapter/fetch-client.js'\nimport type { CartStore } from './cart-store.js'\nimport type { CheckoutResult, CheckoutState, ShippingMethod } from '../types.js'\nimport { createStore } from './create-store.js'\n\nconst INITIAL_STATE: CheckoutState = {\n step: 'contact',\n email: '',\n shippingAddress: null,\n billingAddress: null,\n shippingMethod: null,\n paymentStatus: 'idle',\n error: null,\n order: null,\n placedOrderNumber: null,\n placedOrderKey: null,\n}\n\n/**\n * The order a refused resubmit names (409 `checkout.cart_empty` or `checkout.payment_in_progress`), if any, and the key\n * the refusal carries to read it.\n */\nfunction namedOrder(err: unknown): Pick<CheckoutState, 'placedOrderNumber' | 'placedOrderKey'> {\n const context = err instanceof NymbalApiError ? err.context : undefined\n const orderNumber = context?.['orderNumber']\n const orderKey = context?.['orderKey']\n return typeof orderNumber === 'string'\n ? {\n placedOrderNumber: orderNumber,\n placedOrderKey: typeof orderKey === 'string' ? orderKey : null,\n }\n : { placedOrderNumber: null, placedOrderKey: null }\n}\n\nexport interface CheckoutStore {\n getState(): CheckoutState\n subscribe(listener: () => void): () => void\n setEmail(email: string): void\n setShippingAddress(address: Address): void\n setBillingAddress(address: Address): void\n setShippingMethod(method: ShippingMethod): void\n /** Places the order; resolves to the server's result, or null when there is none. */\n submitPayment(): Promise<CheckoutResult | null>\n /** The provider confirmed the payment on the client (Stripe's `confirmCardPayment`). */\n markConfirmed(): void\n reset(): void\n destroy(): void\n}\n\n/**\n * `cart`, when given, is loaded again once the server has answered a submit: placing an order changes the cart on the\n * server (emptied when confirmed, kept while awaiting payment), and whatever renders the cart store must show that.\n */\nexport function createCheckoutStore(adapter: CommerceAdapter, cart?: CartStore): CheckoutStore {\n const store = createStore<CheckoutState>(INITIAL_STATE)\n\n function setEmail(email: string): void {\n store.setState({ email, step: 'shipping' })\n }\n\n function setShippingAddress(address: Address): void {\n store.setState({ shippingAddress: address })\n }\n\n function setBillingAddress(address: Address): void {\n store.setState({ billingAddress: address })\n }\n\n function setShippingMethod(method: ShippingMethod): void {\n store.setState({ shippingMethod: method, step: 'payment' })\n }\n\n async function submitPayment(): Promise<CheckoutResult | null> {\n const state = store.getState()\n if (!state.shippingAddress || !state.billingAddress) {\n store.setState({ error: 'Shipping and billing addresses are required' })\n return null\n }\n store.setState({\n paymentStatus: 'processing',\n error: null,\n placedOrderNumber: null,\n placedOrderKey: null,\n })\n try {\n const result = await adapter.checkout.create({\n email: state.email,\n billingAddress: state.billingAddress,\n shippingAddress: state.shippingAddress,\n })\n const { status, reason } = result.payment\n store.setState({\n paymentStatus: status,\n order: {\n orderId: result.orderId,\n orderNumber: result.orderNumber,\n paymentIntent: result.paymentIntent,\n },\n error:\n status === 'failed'\n ? `Your payment did not go through${reason ? ` (${reason})` : ''}. Please try again.`\n : null,\n ...(status === 'succeeded' && { step: 'confirmation' as const }),\n })\n await cart?.load()\n return result\n } catch (err) {\n const placed = namedOrder(err)\n store.setState({\n paymentStatus: 'failed',\n error: err instanceof Error ? err.message : String(err),\n ...placed,\n })\n // A refusal naming an order: the cart became (or is becoming) that order on the server.\n if (placed.placedOrderNumber !== null) await cart?.load()\n return null\n }\n }\n\n function markConfirmed(): void {\n store.setState({ paymentStatus: 'succeeded', step: 'confirmation' })\n }\n\n function reset(): void {\n store.setState(INITIAL_STATE)\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n setEmail,\n setShippingAddress,\n setBillingAddress,\n setShippingMethod,\n submitPayment,\n markConfirmed,\n reset,\n destroy: store.destroy,\n }\n}\n","import type { Customer } from '@nymbalhq/types'\nimport type { CommerceAdapter } from '../adapter/commerce-adapter.js'\nimport type { TokenManager } from '../auth/token-manager.js'\nimport type { AuthState, RegisterParams } from '../types.js'\nimport { createStore } from './create-store.js'\n\nconst INITIAL_STATE: AuthState = {\n customer: null,\n isAuthenticated: false,\n loading: false,\n error: null,\n}\n\nexport interface AuthStore {\n getState(): AuthState\n subscribe(listener: () => void): () => void\n login(email: string, password: string): Promise<void>\n register(params: RegisterParams): Promise<void>\n logout(): Promise<void>\n refreshToken(): Promise<void>\n updateProfile(updates: Partial<Pick<Customer, 'firstName' | 'lastName' | 'phone'>>): Promise<void>\n loadProfile(): Promise<void>\n destroy(): void\n}\n\nexport function createAuthStore(\n adapter: CommerceAdapter,\n tokenManager: TokenManager,\n): AuthStore {\n const store = createStore<AuthState>(INITIAL_STATE)\n\n async function login(email: string, password: string): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const result = await adapter.customers.login(email, password)\n tokenManager.setTokens(result.accessToken, result.expiresIn)\n store.setState({\n customer: result.customer,\n isAuthenticated: true,\n loading: false,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n store.setState({ loading: false, error: message })\n throw err\n }\n }\n\n async function register(params: RegisterParams): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const result = await adapter.customers.register(params)\n tokenManager.setTokens(result.accessToken, result.expiresIn)\n store.setState({\n customer: result.customer,\n isAuthenticated: true,\n loading: false,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n store.setState({ loading: false, error: message })\n throw err\n }\n }\n\n async function logout(): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n await adapter.customers.logout()\n tokenManager.clear()\n store.setState({\n customer: null,\n isAuthenticated: false,\n loading: false,\n })\n } catch (err) {\n tokenManager.clear()\n store.setState({\n customer: null,\n isAuthenticated: false,\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function refreshToken(): Promise<void> {\n try {\n const result = await adapter.customers.refresh()\n tokenManager.setTokens(result.accessToken, result.expiresIn)\n } catch (err) {\n console.error('[AuthStore] token refresh failed, logging out', err)\n tokenManager.clear()\n store.setState({\n customer: null,\n isAuthenticated: false,\n })\n }\n }\n\n async function updateProfile(\n updates: Partial<Pick<Customer, 'firstName' | 'lastName' | 'phone'>>,\n ): Promise<void> {\n store.setState({ loading: true, error: null })\n try {\n const customer = await adapter.customers.updateProfile(updates)\n store.setState({ customer, loading: false })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n async function loadProfile(): Promise<void> {\n if (!tokenManager.getAccessToken()) return\n store.setState({ loading: true, error: null })\n try {\n const customer = await adapter.customers.getProfile()\n store.setState({\n customer,\n isAuthenticated: true,\n loading: false,\n })\n } catch (err) {\n store.setState({\n loading: false,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n return {\n getState: store.getState,\n subscribe: store.subscribe,\n login,\n register,\n logout,\n refreshToken,\n updateProfile,\n loadProfile,\n destroy: store.destroy,\n }\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 } from './auth/token-manager.js'\nimport { createCartStore, type CartStore } from './store/cart-store.js'\nimport { createProductStore, type ProductStore } from './store/product-store.js'\nimport { createProductListStore, type ProductListStore } from './store/product-list-store.js'\nimport { createSearchStore, type SearchStore } from './store/search-store.js'\nimport { createCheckoutStore, type CheckoutStore } from './store/checkout-store.js'\nimport { createAuthStore, type AuthStore } from './store/auth-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/** 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 adapter: CommerceAdapter\n destroy(): void\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 cart = createCartStore(adapter)\n const product = createProductStore(adapter)\n const productList = createProductListStore(adapter)\n const search = createSearchStore(adapter)\n const checkout = createCheckoutStore(adapter, cart)\n const auth = createAuthStore(adapter, tokenManager)\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 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 function destroy(): void {\n cart.destroy()\n product.destroy()\n productList.destroy()\n search.destroy()\n checkout.destroy()\n auth.destroy()\n tokenManager.destroy()\n }\n\n return {\n capabilities,\n recentlyViewed,\n cart,\n product,\n productList,\n search,\n checkout,\n auth,\n customer,\n adapter,\n destroy,\n }\n}\n","import type { Address } from '@nymbalhq/types'\n\n/** What a checkout already knows about the shopper, which a driver may hand its processor with the payment. */\nexport interface PaymentConfirmContext {\n email?: string\n billing?: Address\n}\n\n/** What a payment driver's `confirm` answers: paid now, accepted without a payment yet, or refused with a reason. */\nexport type PaymentConfirmation = { ok: true; confirmed: boolean } | { ok: false; error: string }\n\n/**\n * The browser half of a payments adapter, named by the `driver` of GET /api/capabilities' payments client. `mount`\n * renders whatever the shopper pays with inside `el`; `confirm` completes a payment the API left `requires_action`,\n * with the shopper's email and billing address when the checkout has them.\n */\nexport interface PaymentDriver {\n mount(el: HTMLElement, options: Record<string, string>): Promise<void> | void\n confirm(clientSecret: string, context?: PaymentConfirmContext): Promise<PaymentConfirmation>\n}\n\n// On globalThis, like the registered client: a page that loads two copies of the SDK still has one registry.\nconst DRIVERS_KEY = Symbol.for('nymbal.paymentDrivers')\n\nfunction drivers(): Map<string, PaymentDriver> {\n const holder = globalThis as Record<symbol, Map<string, PaymentDriver> | undefined>\n holder[DRIVERS_KEY] ??= new Map()\n return holder[DRIVERS_KEY]\n}\n\n/** Registers `driver` under `name`, replacing any driver of that name. */\nexport function registerPaymentDriver(name: string, driver: PaymentDriver): void {\n drivers().set(name, driver)\n}\n\nexport function getPaymentDriver(name: string): PaymentDriver | undefined {\n return drivers().get(name)\n}\n"],"mappings":"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","names":["INITIAL_STATE","INITIAL_STATE","INITIAL_STATE","INITIAL_STATE","INITIAL_STATE","options"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nymbalhq/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Framework-agnostic commerce SDK with pub/sub stores and adapter interface",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"dist"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@nymbalhq/types": "1.
|
|
30
|
+
"@nymbalhq/types": "1.4.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@typescript/native": "npm:typescript@^7.0.2",
|