@magicstoreai/hydrogen 0.1.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.
@@ -0,0 +1,790 @@
1
+ import * as _magicstoreai_storefront_client from '@magicstoreai/storefront-client';
2
+ import { StorefrontClient, operations, Schema, MagicStoreError, StorefrontClientOptions } from '@magicstoreai/storefront-client';
3
+ export { MagicStoreError } from '@magicstoreai/storefront-client';
4
+ import * as react from 'react';
5
+ import { ReactNode, ComponentPropsWithoutRef, ElementType } from 'react';
6
+
7
+ /** Where the SDK keeps what outlives a page: the customer session, the cart id, the guest wishlist. */
8
+ interface KeyValueStorage {
9
+ get(key: string): string | null;
10
+ set(key: string, value: string): void;
11
+ remove(key: string): void;
12
+ }
13
+ /** In memory — for tests, the server, and when the browser refuses storage. */
14
+ declare function memoryStorage(initial?: Record<string, string>): KeyValueStorage;
15
+ /**
16
+ * `localStorage` when the browser allows it; memory otherwise (server render, private mode,
17
+ * blocked site data). Every access is guarded: storage can throw at any time.
18
+ */
19
+ declare function browserStorage(): KeyValueStorage;
20
+ /**
21
+ * A tiny observable value — what the React hooks subscribe to. `serverValue` is what a server render
22
+ * and hydration see: storage is browser-only, so both must render the same "not known yet" state
23
+ * and let the stored one arrive after hydration (otherwise React reports a hydration mismatch).
24
+ */
25
+ declare class Observable<T> {
26
+ private value;
27
+ private listeners;
28
+ private readonly serverValue;
29
+ constructor(value: T, serverValue?: T);
30
+ get(): T;
31
+ protected set(value: T): void;
32
+ subscribe: (listener: () => void) => (() => void);
33
+ getSnapshot: () => T;
34
+ getServerSnapshot: () => T;
35
+ }
36
+
37
+ type EventsBody = NonNullable<operations['analyticsEventsStore']['requestBody']>['content']['application/json'];
38
+ type AnalyticsEvent = EventsBody['events'][number];
39
+ type AnalyticsEventType = Schema<'StorefrontEventType'>;
40
+ type AnalyticsPayload = AnalyticsEvent['payload'];
41
+ /**
42
+ * The visitor's id for the shop's analytics and funnel: a UUID minted once per visitor and kept.
43
+ * The same value goes out as `X-Session-Id`, which ties carts and orders to the visit.
44
+ */
45
+ declare function visitorSessionId(storage: KeyValueStorage, key?: string): string;
46
+ /**
47
+ * What the visitor looks at, sent in batches (`POST /analytics/events`). Cart and checkout steps
48
+ * are recorded by the server — never report them. Failures are dropped: analytics must never get
49
+ * in the way of shopping.
50
+ */
51
+ declare class AnalyticsController {
52
+ private readonly client;
53
+ private readonly sessionId;
54
+ private readonly options;
55
+ private queue;
56
+ private timer;
57
+ constructor(client: StorefrontClient, sessionId: string, options?: {
58
+ flushAfterMs?: number;
59
+ context?: AnalyticsPayload;
60
+ });
61
+ /** A page view — starts the session with its first-touch attribution (UTM, click ids, referrer). */
62
+ pageView(path: string, extra?: AnalyticsPayload): void;
63
+ productView(productId: string, variantId?: string | null): void;
64
+ collectionView(collectionId: string): void;
65
+ search(query: string, resultsCount?: number): void;
66
+ custom(name: string, properties?: Record<string, unknown>): void;
67
+ track(type: AnalyticsEventType, payload: AnalyticsPayload): void;
68
+ /** Sends what is queued now — call it when the page is hidden. */
69
+ flush(): Promise<void>;
70
+ }
71
+ /** The first-touch attribution a landing URL carries, for the first `pageView`. */
72
+ declare function attributionFrom(url: URL, referrer?: string): AnalyticsPayload;
73
+
74
+ type Cart = Schema<'Cart'>;
75
+ type CartLine = Schema<'CartLine'>;
76
+ type CartAttribute = {
77
+ key: string;
78
+ value: string | null;
79
+ };
80
+ /** A line to add: the product, its variant (null for a product without variants) and how many. */
81
+ interface CartLineInput {
82
+ productId: string;
83
+ variantId?: string | null;
84
+ quantity?: number;
85
+ attributes?: CartAttribute[];
86
+ }
87
+ interface CartState {
88
+ cart: Cart | null;
89
+ /** `loading` while the stored cart is fetched; `updating` while a change is on its way. */
90
+ status: 'idle' | 'loading' | 'updating';
91
+ /** The last change that failed — the cart is back to what the server holds. */
92
+ error: MagicStoreError | null;
93
+ }
94
+ /**
95
+ * The visitor's cart. Its id is a capability and lives only in storage; the cart itself always
96
+ * comes from the server, so prices, discounts and availability are the server's. Quantity changes
97
+ * show at once and roll back if the server refuses them. Changes run one after another.
98
+ */
99
+ declare class CartController extends Observable<CartState> {
100
+ private readonly client;
101
+ private readonly storage;
102
+ private readonly key;
103
+ private queue;
104
+ constructor(client: StorefrontClient, storage: KeyValueStorage, key?: string);
105
+ get cart(): Cart | null;
106
+ get cartId(): string | null;
107
+ /** Fetches the stored cart, if any. A cart that is gone is forgotten. */
108
+ load(): Promise<Cart | null>;
109
+ /** Adds lines; the first add creates the cart. */
110
+ addLines(lines: CartLineInput[]): Promise<Cart | null>;
111
+ /** The line's quantity outright; 0 removes it. Shown at once, rolled back if refused. */
112
+ updateLine(lineId: string, quantity: number): Promise<Cart | null>;
113
+ removeLine(lineId: string): Promise<Cart | null>;
114
+ /** The cart's codes as a whole — `[]` takes the code off. A code that does not apply is kept, flagged. */
115
+ setDiscountCodes(discountCodes: string[]): Promise<Cart | null>;
116
+ setNote(note: string | null): Promise<Cart | null>;
117
+ setAttributes(attributes: CartAttribute[]): Promise<Cart | null>;
118
+ /** The gift promotion chosen from `cart.giftOptions`; null takes it off. */
119
+ setGift(promotionId: string | null): Promise<Cart | null>;
120
+ /** Points to spend on this cart (signed-in customers). */
121
+ setPoints(points: number): Promise<Cart | null>;
122
+ /**
123
+ * After sign-in: the cart becomes the customer's. When they already had an open cart, this one
124
+ * merges into it and the answer is THAT cart — its id replaces the stored one.
125
+ */
126
+ attachCustomer(): Promise<Cart | null>;
127
+ /** After sign-out: the cart is the customer's, not this visitor's any more. */
128
+ forget(): void;
129
+ /** Shows `optimistic` at once (when given), sends the change, and adopts the server's cart or rolls back. */
130
+ private mutate;
131
+ private adopt;
132
+ private fail;
133
+ private patch;
134
+ private enqueue;
135
+ }
136
+
137
+ type Money$1 = Schema<'Money'>;
138
+ type MoneyFormat = Schema<'Shop'>['moneyFormat'];
139
+ /** The currency's symbol or word in a locale: `сум`, `so‘m`, `$`. */
140
+ declare function currencySymbol(currencyCode: string, locale: string): string;
141
+ /**
142
+ * `12500.00` → `12 500`: groups of three separated by a space, the fraction kept only when it is
143
+ * not zero. The amount is never rounded here — the API already applied the shop's rounding, and
144
+ * the string is exactly what will be charged.
145
+ */
146
+ declare function groupAmount(amount: string): string;
147
+ /**
148
+ * Money as the shop shows it: `shop.moneyFormat.format` places the symbol or the code, and a shop
149
+ * with no format uses the currency's own convention (`12 500 сум`, `$129.99`).
150
+ */
151
+ declare function formatMoney(money: Money$1, options: {
152
+ locale: string;
153
+ format?: MoneyFormat | null;
154
+ }): string;
155
+ /** The amount as a number, for arithmetic you must do on the client (sorting, a progress bar). */
156
+ declare function moneyAmount(money: Money$1): number;
157
+
158
+ type CustomerSession = Schema<'CustomerSession'>;
159
+ type Customer = Schema<'Customer'>;
160
+ interface SessionState {
161
+ session: CustomerSession | null;
162
+ }
163
+ /**
164
+ * The signed-in customer and their tokens. The access token lives an hour and is refreshed on
165
+ * demand; the refresh token works once, so concurrent callers share one refresh.
166
+ */
167
+ declare class CustomerSessionController extends Observable<SessionState> {
168
+ private readonly storage;
169
+ private readonly key;
170
+ private readonly now;
171
+ private refreshing;
172
+ private client;
173
+ constructor(storage: KeyValueStorage, key?: string, now?: () => number);
174
+ /** The client the session signs in and refreshes through (created with this controller's token). */
175
+ attach(client: StorefrontClient): void;
176
+ get session(): CustomerSession | null;
177
+ get customer(): Customer | null;
178
+ /** The bearer for the next call: refreshed when it is about to expire; null when signed out. */
179
+ accessToken: () => Promise<string | null>;
180
+ /** One refresh at a time: every caller waiting meanwhile gets its result. */
181
+ refresh(): Promise<string | null>;
182
+ private doRefresh;
183
+ /** Texts a sign-in code. */
184
+ requestOtp(phone: string): Promise<Schema<'OtpChallenge'>>;
185
+ verifyOtp(phone: string, code: string, referralCode?: string): Promise<CustomerSession>;
186
+ signInWithTelegram(initData: string, referralCode?: string): Promise<CustomerSession>;
187
+ signInWithOq(oqToken: string): Promise<CustomerSession>;
188
+ signInWithClick(webSession: string): Promise<CustomerSession>;
189
+ /** Ends this sign-in on the server too; signed out locally whatever the server says. */
190
+ signOut(): Promise<void>;
191
+ /** The customer as the server has them now (after a profile change). */
192
+ reloadCustomer(): Promise<Customer | null>;
193
+ private signedIn;
194
+ private store;
195
+ private requireClient;
196
+ }
197
+
198
+ type ProductVariant = Schema<'ProductVariant'>;
199
+ type ProductOption = Schema<'ProductOption'>;
200
+ /** What variant selection needs of a product — `Product` and `ProductDetail` both fit. */
201
+ interface SelectableProduct {
202
+ options: ProductOption[];
203
+ variants: ProductVariant[];
204
+ }
205
+ type SelectedOptions = Record<string, string>;
206
+ declare function optionsOf(variant: ProductVariant): SelectedOptions;
207
+ /** The variant whose options are exactly these, or null while the selection is incomplete. */
208
+ declare function variantFor(product: SelectableProduct, selected: SelectedOptions): ProductVariant | null;
209
+ /**
210
+ * Where selection starts: the given variant, else the first one for sale, else the first one.
211
+ * A product with a single variant and no options is always "selected".
212
+ */
213
+ declare function initialSelection(product: SelectableProduct, variantId?: string | null): SelectedOptions;
214
+ /**
215
+ * Whether picking `value` for `name` — keeping the other choices — lands on a variant for sale.
216
+ * What a storefront uses to grey out a size that is sold out in the chosen colour.
217
+ */
218
+ declare function isOptionValueAvailable(product: SelectableProduct, selected: SelectedOptions, name: string, value: string): boolean;
219
+ /**
220
+ * Choosing a value keeps the other choices when that combination exists; otherwise it moves to the
221
+ * closest variant that has the new value (for sale first), so the selection never dead-ends.
222
+ */
223
+ declare function selectOption(product: SelectableProduct, selected: SelectedOptions, name: string, value: string): SelectedOptions;
224
+
225
+ interface WishlistState {
226
+ /** Product ids, most recent first. */
227
+ productIds: string[];
228
+ /** Whose list it is: a guest's lives in this browser, a customer's on the server. */
229
+ owner: 'guest' | 'customer';
230
+ status: 'idle' | 'loading';
231
+ }
232
+ /** The server keeps up to this many; a guest list is capped the same. */
233
+ declare const WISHLIST_LIMIT = 500;
234
+ /**
235
+ * Saved products. A guest's list is kept in this browser; on sign-in it moves into the customer's
236
+ * list on the server and the local copy is cleared. Changes show at once and roll back if refused.
237
+ */
238
+ declare class WishlistController extends Observable<WishlistState> {
239
+ private readonly client;
240
+ private readonly storage;
241
+ private readonly key;
242
+ constructor(client: StorefrontClient, storage: KeyValueStorage, key?: string);
243
+ has(productId: string): boolean;
244
+ add(productId: string): Promise<void>;
245
+ remove(productId: string): Promise<void>;
246
+ toggle(productId: string): Promise<void>;
247
+ /** After sign-in: the guest's products join the customer's list, then the list is the server's. */
248
+ signedIn(): Promise<void>;
249
+ /** After sign-out: back to an empty guest list — the customer's list stays on the server. */
250
+ signedOut(): void;
251
+ /** The customer's list as the server has it. */
252
+ reload(): Promise<void>;
253
+ private setIds;
254
+ }
255
+
256
+ type MoneyProps<As extends ElementType> = {
257
+ data: Money$1 | null | undefined;
258
+ as?: As;
259
+ /** What to render for a missing price (the API sends null for "unknown", never 0). */
260
+ fallback?: ReactNode;
261
+ } & Omit<ComponentPropsWithoutRef<As>, 'children'>;
262
+ /**
263
+ * A price as the shop shows it (`shop.moneyFormat`, the storefront's locale).
264
+ *
265
+ * @example
266
+ * <Money data={product.price} fallback={<span>—</span>} />
267
+ */
268
+ declare function Money<As extends ElementType = 'span'>({ data, as, fallback, ...rest }: MoneyProps<As>): react.JSX.Element;
269
+ type ImageProps = {
270
+ data: Schema<'Image'> | null | undefined;
271
+ /** Used when the image has no alt text of its own. */
272
+ alt?: string;
273
+ fallback?: ReactNode;
274
+ } & Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'alt'>;
275
+ /**
276
+ * An API image with lazy loading by default. Its intrinsic size is set (no layout shift) unless
277
+ * you pass `width` / `height` to draw it at another size, e.g. a 64×64 cart thumbnail.
278
+ *
279
+ * @example
280
+ * <Image data={product.featuredImage} alt={product.title} fallback={<div className="noimg" />} />
281
+ */
282
+ declare function Image({ data, alt, fallback, loading, decoding, width, height, ...rest }: ImageProps): react.JSX.Element;
283
+ interface PaginationState {
284
+ page: number;
285
+ totalPages: number;
286
+ hasPreviousPage: boolean;
287
+ hasNextPage: boolean;
288
+ previousPage: number | null;
289
+ nextPage: number | null;
290
+ /** Page numbers around the current one, with `null` for a gap: `[1, null, 4, 5, 6, null, 12]`. */
291
+ pages: Array<number | null>;
292
+ }
293
+ declare function paginationState(meta: Pagination, around?: number): PaginationState;
294
+ type Pagination = Schema<'Pagination'>;
295
+ /**
296
+ * Headless pagination over `meta.pagination`: you render the links, it does the arithmetic.
297
+ *
298
+ * @example
299
+ * <Pagination meta={products.meta.pagination}>
300
+ * {({ pages }) =>
301
+ * pages.map((page, i) =>
302
+ * page === null ? <span key={i}>…</span> : <a key={i} href={`?page=${page}`}>{page}</a>,
303
+ * )
304
+ * }
305
+ * </Pagination>
306
+ */
307
+ declare function Pagination({ meta, around, children, }: {
308
+ meta: Pagination;
309
+ around?: number;
310
+ children: (state: PaginationState) => ReactNode;
311
+ }): react.JSX.Element;
312
+
313
+ type Shop = Schema<'Shop'>;
314
+ interface MagicStore {
315
+ client: StorefrontClient;
316
+ /** Null until `/shop` has loaded, unless the page passed `shop` in. */
317
+ shop: Shop | null;
318
+ locale: string;
319
+ session: CustomerSessionController;
320
+ cart: CartController;
321
+ wishlist: WishlistController;
322
+ analytics: AnalyticsController;
323
+ sessionId: string;
324
+ }
325
+ interface MagicStoreProviderProps extends Pick<StorefrontClientOptions, 'shopDomain' | 'baseUrl' | 'storefrontToken' | 'fetch' | 'retry'> {
326
+ /** The language the storefront renders in; the shop's default when omitted. */
327
+ locale?: string;
328
+ /** The shop, when the page already has it (server-rendered) — saves a `/shop` call. */
329
+ shop?: Shop;
330
+ /** Where the session, cart id and guest wishlist are kept; `localStorage` by default. */
331
+ storage?: KeyValueStorage;
332
+ children: ReactNode;
333
+ }
334
+ /**
335
+ * The root of a storefront: one client, and the customer, cart, wishlist and analytics built on it.
336
+ * On sign-in the cart becomes the customer's and the guest wishlist moves into their list; on
337
+ * sign-out both are left behind with the customer.
338
+ *
339
+ * @example
340
+ * // app/providers.tsx ('use client'); `shop` comes from a server-side `api.shop()`.
341
+ * <MagicStoreProvider shopDomain="shop.example.uz" shop={shop} locale="ru">
342
+ * {children}
343
+ * </MagicStoreProvider>
344
+ */
345
+ declare function MagicStoreProvider(props: MagicStoreProviderProps): react.JSX.Element;
346
+ /**
347
+ * Everything the provider holds: client, shop, locale and the controllers. Throws outside
348
+ * `<MagicStoreProvider>`.
349
+ *
350
+ * @example
351
+ * const { locale, sessionId } = useMagicStore();
352
+ */
353
+ declare function useMagicStore(): MagicStore;
354
+ /**
355
+ * The typed API client, for anything the hooks do not cover.
356
+ *
357
+ * @example
358
+ * const client = useStorefrontClient();
359
+ * const { data } = await client.searchSuggestions({ query: { q } });
360
+ */
361
+ declare function useStorefrontClient(): StorefrontClient;
362
+ /**
363
+ * The shop (`GET /shop`): name, currency, money format, locales, features, branding…
364
+ *
365
+ * @example
366
+ * const shop = useShop();
367
+ * return <span>{shop?.name}</span>;
368
+ */
369
+ declare function useShop(): Shop | null;
370
+
371
+ /**
372
+ * The signed-in customer and every way to sign in and out.
373
+ *
374
+ * @wraps CustomerSessionController
375
+ * @example
376
+ * const { customer, isSignedIn, requestOtp, verifyOtp, signOut } = useCustomer();
377
+ * await requestOtp('+998901234567');
378
+ * await verifyOtp('+998901234567', code);
379
+ */
380
+ declare function useCustomer(): {
381
+ customer: {
382
+ createdAt: string | null;
383
+ deletionRequestedAt: string | null;
384
+ gender: "MALE" | "FEMALE" | null;
385
+ id: string;
386
+ locale: string | null;
387
+ name: string | null;
388
+ phone: string | null;
389
+ referralCode: string | null;
390
+ } | null;
391
+ isSignedIn: boolean;
392
+ requestOtp: (phone: string) => Promise<{
393
+ codeLength: number;
394
+ expiresAt: string;
395
+ }>;
396
+ verifyOtp: (phone: string, code: string, referralCode?: string) => Promise<{
397
+ accessToken: string;
398
+ customer: _magicstoreai_storefront_client.components["schemas"]["Customer"];
399
+ expiresAt: string;
400
+ refreshToken: string;
401
+ refreshTokenExpiresAt: string;
402
+ }>;
403
+ signInWithTelegram: (initData: string, referralCode?: string) => Promise<{
404
+ accessToken: string;
405
+ customer: _magicstoreai_storefront_client.components["schemas"]["Customer"];
406
+ expiresAt: string;
407
+ refreshToken: string;
408
+ refreshTokenExpiresAt: string;
409
+ }>;
410
+ signInWithOq: (oqToken: string) => Promise<{
411
+ accessToken: string;
412
+ customer: _magicstoreai_storefront_client.components["schemas"]["Customer"];
413
+ expiresAt: string;
414
+ refreshToken: string;
415
+ refreshTokenExpiresAt: string;
416
+ }>;
417
+ signInWithClick: (webSession: string) => Promise<{
418
+ accessToken: string;
419
+ customer: _magicstoreai_storefront_client.components["schemas"]["Customer"];
420
+ expiresAt: string;
421
+ refreshToken: string;
422
+ refreshTokenExpiresAt: string;
423
+ }>;
424
+ signOut: () => Promise<void>;
425
+ reload: () => Promise<{
426
+ createdAt: string | null;
427
+ deletionRequestedAt: string | null;
428
+ gender: "MALE" | "FEMALE" | null;
429
+ id: string;
430
+ locale: string | null;
431
+ name: string | null;
432
+ phone: string | null;
433
+ referralCode: string | null;
434
+ } | null>;
435
+ };
436
+ /**
437
+ * The visitor's cart and every change to it. The first `addLines` creates the cart.
438
+ *
439
+ * @wraps CartController
440
+ * @example
441
+ * const { cart, totalQuantity, status, error, addLine } = useCart();
442
+ * await addLine({ productId: product.id, variantId: selectedVariant?.id ?? null, quantity: 1 });
443
+ */
444
+ declare function useCart(): {
445
+ totalQuantity: number;
446
+ addLines: (lines: CartLineInput[]) => Promise<{
447
+ attributes: {
448
+ key: string;
449
+ value: string;
450
+ }[];
451
+ buyerIdentity: {
452
+ customerId: string;
453
+ } | null;
454
+ cost: {
455
+ discountTotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
456
+ pointsDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
457
+ subtotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
458
+ total: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
459
+ welcomeDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
460
+ };
461
+ createdAt: string | null;
462
+ discountCodes: _magicstoreai_storefront_client.components["schemas"]["CartDiscountCode"][];
463
+ expiresAt: string | null;
464
+ giftOptions: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"][];
465
+ id: string;
466
+ lines: _magicstoreai_storefront_client.components["schemas"]["CartLine"][];
467
+ note: string | null;
468
+ points: _magicstoreai_storefront_client.components["schemas"]["CartPoints"] | null;
469
+ selectedGift: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"] | null;
470
+ status: "ACTIVE" | "COMPLETED" | "MERGED";
471
+ totalQuantity: number;
472
+ updatedAt: string | null;
473
+ } | null>;
474
+ addLine: (line: CartLineInput) => Promise<{
475
+ attributes: {
476
+ key: string;
477
+ value: string;
478
+ }[];
479
+ buyerIdentity: {
480
+ customerId: string;
481
+ } | null;
482
+ cost: {
483
+ discountTotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
484
+ pointsDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
485
+ subtotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
486
+ total: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
487
+ welcomeDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
488
+ };
489
+ createdAt: string | null;
490
+ discountCodes: _magicstoreai_storefront_client.components["schemas"]["CartDiscountCode"][];
491
+ expiresAt: string | null;
492
+ giftOptions: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"][];
493
+ id: string;
494
+ lines: _magicstoreai_storefront_client.components["schemas"]["CartLine"][];
495
+ note: string | null;
496
+ points: _magicstoreai_storefront_client.components["schemas"]["CartPoints"] | null;
497
+ selectedGift: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"] | null;
498
+ status: "ACTIVE" | "COMPLETED" | "MERGED";
499
+ totalQuantity: number;
500
+ updatedAt: string | null;
501
+ } | null>;
502
+ updateLine: (lineId: string, quantity: number) => Promise<{
503
+ attributes: {
504
+ key: string;
505
+ value: string;
506
+ }[];
507
+ buyerIdentity: {
508
+ customerId: string;
509
+ } | null;
510
+ cost: {
511
+ discountTotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
512
+ pointsDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
513
+ subtotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
514
+ total: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
515
+ welcomeDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
516
+ };
517
+ createdAt: string | null;
518
+ discountCodes: _magicstoreai_storefront_client.components["schemas"]["CartDiscountCode"][];
519
+ expiresAt: string | null;
520
+ giftOptions: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"][];
521
+ id: string;
522
+ lines: _magicstoreai_storefront_client.components["schemas"]["CartLine"][];
523
+ note: string | null;
524
+ points: _magicstoreai_storefront_client.components["schemas"]["CartPoints"] | null;
525
+ selectedGift: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"] | null;
526
+ status: "ACTIVE" | "COMPLETED" | "MERGED";
527
+ totalQuantity: number;
528
+ updatedAt: string | null;
529
+ } | null>;
530
+ removeLine: (lineId: string) => Promise<{
531
+ attributes: {
532
+ key: string;
533
+ value: string;
534
+ }[];
535
+ buyerIdentity: {
536
+ customerId: string;
537
+ } | null;
538
+ cost: {
539
+ discountTotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
540
+ pointsDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
541
+ subtotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
542
+ total: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
543
+ welcomeDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
544
+ };
545
+ createdAt: string | null;
546
+ discountCodes: _magicstoreai_storefront_client.components["schemas"]["CartDiscountCode"][];
547
+ expiresAt: string | null;
548
+ giftOptions: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"][];
549
+ id: string;
550
+ lines: _magicstoreai_storefront_client.components["schemas"]["CartLine"][];
551
+ note: string | null;
552
+ points: _magicstoreai_storefront_client.components["schemas"]["CartPoints"] | null;
553
+ selectedGift: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"] | null;
554
+ status: "ACTIVE" | "COMPLETED" | "MERGED";
555
+ totalQuantity: number;
556
+ updatedAt: string | null;
557
+ } | null>;
558
+ setDiscountCodes: (codes: string[]) => Promise<{
559
+ attributes: {
560
+ key: string;
561
+ value: string;
562
+ }[];
563
+ buyerIdentity: {
564
+ customerId: string;
565
+ } | null;
566
+ cost: {
567
+ discountTotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
568
+ pointsDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
569
+ subtotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
570
+ total: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
571
+ welcomeDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
572
+ };
573
+ createdAt: string | null;
574
+ discountCodes: _magicstoreai_storefront_client.components["schemas"]["CartDiscountCode"][];
575
+ expiresAt: string | null;
576
+ giftOptions: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"][];
577
+ id: string;
578
+ lines: _magicstoreai_storefront_client.components["schemas"]["CartLine"][];
579
+ note: string | null;
580
+ points: _magicstoreai_storefront_client.components["schemas"]["CartPoints"] | null;
581
+ selectedGift: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"] | null;
582
+ status: "ACTIVE" | "COMPLETED" | "MERGED";
583
+ totalQuantity: number;
584
+ updatedAt: string | null;
585
+ } | null>;
586
+ setNote: (note: string | null) => Promise<{
587
+ attributes: {
588
+ key: string;
589
+ value: string;
590
+ }[];
591
+ buyerIdentity: {
592
+ customerId: string;
593
+ } | null;
594
+ cost: {
595
+ discountTotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
596
+ pointsDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
597
+ subtotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
598
+ total: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
599
+ welcomeDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
600
+ };
601
+ createdAt: string | null;
602
+ discountCodes: _magicstoreai_storefront_client.components["schemas"]["CartDiscountCode"][];
603
+ expiresAt: string | null;
604
+ giftOptions: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"][];
605
+ id: string;
606
+ lines: _magicstoreai_storefront_client.components["schemas"]["CartLine"][];
607
+ note: string | null;
608
+ points: _magicstoreai_storefront_client.components["schemas"]["CartPoints"] | null;
609
+ selectedGift: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"] | null;
610
+ status: "ACTIVE" | "COMPLETED" | "MERGED";
611
+ totalQuantity: number;
612
+ updatedAt: string | null;
613
+ } | null>;
614
+ setAttributes: (attributes: CartAttribute[]) => Promise<{
615
+ attributes: {
616
+ key: string;
617
+ value: string;
618
+ }[];
619
+ buyerIdentity: {
620
+ customerId: string;
621
+ } | null;
622
+ cost: {
623
+ discountTotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
624
+ pointsDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
625
+ subtotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
626
+ total: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
627
+ welcomeDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
628
+ };
629
+ createdAt: string | null;
630
+ discountCodes: _magicstoreai_storefront_client.components["schemas"]["CartDiscountCode"][];
631
+ expiresAt: string | null;
632
+ giftOptions: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"][];
633
+ id: string;
634
+ lines: _magicstoreai_storefront_client.components["schemas"]["CartLine"][];
635
+ note: string | null;
636
+ points: _magicstoreai_storefront_client.components["schemas"]["CartPoints"] | null;
637
+ selectedGift: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"] | null;
638
+ status: "ACTIVE" | "COMPLETED" | "MERGED";
639
+ totalQuantity: number;
640
+ updatedAt: string | null;
641
+ } | null>;
642
+ setGift: (promotionId: string | null) => Promise<{
643
+ attributes: {
644
+ key: string;
645
+ value: string;
646
+ }[];
647
+ buyerIdentity: {
648
+ customerId: string;
649
+ } | null;
650
+ cost: {
651
+ discountTotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
652
+ pointsDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
653
+ subtotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
654
+ total: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
655
+ welcomeDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
656
+ };
657
+ createdAt: string | null;
658
+ discountCodes: _magicstoreai_storefront_client.components["schemas"]["CartDiscountCode"][];
659
+ expiresAt: string | null;
660
+ giftOptions: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"][];
661
+ id: string;
662
+ lines: _magicstoreai_storefront_client.components["schemas"]["CartLine"][];
663
+ note: string | null;
664
+ points: _magicstoreai_storefront_client.components["schemas"]["CartPoints"] | null;
665
+ selectedGift: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"] | null;
666
+ status: "ACTIVE" | "COMPLETED" | "MERGED";
667
+ totalQuantity: number;
668
+ updatedAt: string | null;
669
+ } | null>;
670
+ setPoints: (points: number) => Promise<{
671
+ attributes: {
672
+ key: string;
673
+ value: string;
674
+ }[];
675
+ buyerIdentity: {
676
+ customerId: string;
677
+ } | null;
678
+ cost: {
679
+ discountTotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
680
+ pointsDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
681
+ subtotal: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
682
+ total: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
683
+ welcomeDiscount: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
684
+ };
685
+ createdAt: string | null;
686
+ discountCodes: _magicstoreai_storefront_client.components["schemas"]["CartDiscountCode"][];
687
+ expiresAt: string | null;
688
+ giftOptions: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"][];
689
+ id: string;
690
+ lines: _magicstoreai_storefront_client.components["schemas"]["CartLine"][];
691
+ note: string | null;
692
+ points: _magicstoreai_storefront_client.components["schemas"]["CartPoints"] | null;
693
+ selectedGift: _magicstoreai_storefront_client.components["schemas"]["CartGiftOption"] | null;
694
+ status: "ACTIVE" | "COMPLETED" | "MERGED";
695
+ totalQuantity: number;
696
+ updatedAt: string | null;
697
+ } | null>;
698
+ cart: Cart | null;
699
+ status: "idle" | "loading" | "updating";
700
+ error: _magicstoreai_storefront_client.MagicStoreError | null;
701
+ };
702
+ /**
703
+ * Saved products: a guest's in this browser, a customer's on the server (merged at sign-in).
704
+ *
705
+ * @wraps WishlistController
706
+ * @example
707
+ * const wishlist = useWishlist();
708
+ * <button aria-pressed={wishlist.has(product.id)} onClick={() => wishlist.toggle(product.id)}>♥</button>
709
+ */
710
+ declare function useWishlist(): {
711
+ has: (productId: string) => boolean;
712
+ add: (productId: string) => Promise<void>;
713
+ remove: (productId: string) => Promise<void>;
714
+ toggle: (productId: string) => Promise<void>;
715
+ productIds: string[];
716
+ owner: "guest" | "customer";
717
+ status: "idle" | "loading";
718
+ };
719
+ /**
720
+ * Reports what the visitor looks at. Stable across renders.
721
+ *
722
+ * @wraps AnalyticsController
723
+ * @example
724
+ * const analytics = useAnalytics();
725
+ * useEffect(() => analytics.productView(product.id), [analytics, product.id]);
726
+ */
727
+ declare function useAnalytics(): AnalyticsController;
728
+ /**
729
+ * Options → variant. `selectedVariant` is null only while a choice is missing; `isAvailable` says
730
+ * whether a value leads to a variant for sale given the other choices.
731
+ *
732
+ * @example
733
+ * const { selectedOptions, selectedVariant, setOption, isAvailable } = useVariantSelection(product);
734
+ * <button disabled={!isAvailable('Size', 'M')} onClick={() => setOption('Size', 'M')}>M</button>
735
+ */
736
+ declare function useVariantSelection(product: SelectableProduct, initialVariantId?: string | null): {
737
+ selectedOptions: SelectedOptions;
738
+ selectedVariant: {
739
+ availableForSale: boolean;
740
+ barcode: string | null;
741
+ compareAtPrice: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
742
+ gtin: string | null;
743
+ id: string;
744
+ image: _magicstoreai_storefront_client.components["schemas"]["Image"] | null;
745
+ mpn: string | null;
746
+ price: _magicstoreai_storefront_client.components["schemas"]["Money"] | null;
747
+ quantityAvailable: number | null;
748
+ requiresShipping: boolean;
749
+ selectedOptions: {
750
+ name: string;
751
+ value: string;
752
+ }[];
753
+ sku: string | null;
754
+ title: string;
755
+ weight: {
756
+ unit: string;
757
+ value: number;
758
+ } | null;
759
+ } | null;
760
+ setOption: (name: string, value: string) => void;
761
+ isAvailable: (name: string, value: string) => boolean;
762
+ };
763
+
764
+ type ProductContextValue = ReturnType<typeof useVariantSelection> & {
765
+ product: SelectableProduct;
766
+ };
767
+ /**
768
+ * Shares one product's variant selection with everything under it (options, price, add-to-cart).
769
+ *
770
+ * @example
771
+ * <ProductProvider product={product}>
772
+ * <VariantPicker />
773
+ * <AddToCart />
774
+ * </ProductProvider>
775
+ */
776
+ declare function ProductProvider<P extends SelectableProduct>({ product, initialVariantId, children, }: {
777
+ product: P;
778
+ initialVariantId?: string | null;
779
+ children: ReactNode;
780
+ }): react.JSX.Element;
781
+ /**
782
+ * The product and variant selection of the nearest `<ProductProvider>`; throws outside one.
783
+ *
784
+ * @example
785
+ * const { product, selectedVariant } = useProduct();
786
+ * return <Money data={selectedVariant?.price ?? product.price} />;
787
+ */
788
+ declare function useProduct(): ProductContextValue;
789
+
790
+ export { AnalyticsController, type AnalyticsEvent, type AnalyticsEventType, type AnalyticsPayload, type Cart, type CartAttribute, CartController, type CartLine, type CartLineInput, type CartState, type Customer, type CustomerSession, CustomerSessionController, Image, type KeyValueStorage, type MagicStore, MagicStoreProvider, type MagicStoreProviderProps, Money, type MoneyFormat, Pagination, type PaginationState, type ProductOption, ProductProvider, type ProductVariant, type SelectableProduct, type SelectedOptions, type SessionState, type Shop, WISHLIST_LIMIT, WishlistController, type WishlistState, attributionFrom, browserStorage, currencySymbol, formatMoney, groupAmount, initialSelection, isOptionValueAvailable, memoryStorage, moneyAmount, optionsOf, paginationState, selectOption, useAnalytics, useCart, useCustomer, useMagicStore, useProduct, useShop, useStorefrontClient, useVariantSelection, useWishlist, variantFor, visitorSessionId };