@nymbalhq/sdk 0.0.0-stage → 0.2.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,372 @@
1
+ import { Customer, CartItem, Address, PaymentIntent, OrderStatus, CheckoutPayment, VariantOption, ProductSortKey, Facet, Order, Cart, CustomerAddress, ReviewsResult, ReviewResult } from '@nymbalhq/types';
2
+ export { ProductSortKey, ReviewResult as ReviewSubmitResult, ReviewsResult as ReviewsListResult } from '@nymbalhq/types';
3
+
4
+ interface DenormalisedVariant {
5
+ id: string;
6
+ sku: string;
7
+ name: string;
8
+ priceMinor: number;
9
+ stock: number;
10
+ options: VariantOption[];
11
+ }
12
+ interface DenormalisedProduct {
13
+ id: string;
14
+ slug: string;
15
+ name: string;
16
+ description: string;
17
+ shortDescription: string;
18
+ status: string;
19
+ type: string;
20
+ media: unknown;
21
+ variantCount: number;
22
+ inStock: boolean;
23
+ priceRange: {
24
+ minMinor: number;
25
+ maxMinor: number;
26
+ } | null;
27
+ currency: string | null;
28
+ priceMinor: number | null;
29
+ variants: DenormalisedVariant[];
30
+ categoryIds: string[];
31
+ categories: Array<{
32
+ id: string;
33
+ name: string;
34
+ slug: string;
35
+ }>;
36
+ createdAt: string;
37
+ updatedAt: string;
38
+ }
39
+ interface CartState {
40
+ items: CartItem[];
41
+ subtotalMinor: number;
42
+ currency: string;
43
+ itemCount: number;
44
+ loading: boolean;
45
+ error: string | null;
46
+ }
47
+ interface ProductState {
48
+ product: DenormalisedProduct | null;
49
+ selectedVariant: DenormalisedVariant | null;
50
+ loading: boolean;
51
+ error: string | null;
52
+ }
53
+ interface ProductListState {
54
+ products: DenormalisedProduct[];
55
+ facets: Facet[];
56
+ filters: Record<string, string | string[]>;
57
+ sort: ProductSortKey;
58
+ pagination: {
59
+ cursor: string | null;
60
+ hasMore: boolean;
61
+ };
62
+ loading: boolean;
63
+ error: string | null;
64
+ }
65
+ interface SearchState {
66
+ query: string;
67
+ results: DenormalisedProduct[];
68
+ total: number;
69
+ loading: boolean;
70
+ }
71
+ type CheckoutStep = 'contact' | 'shipping' | 'payment' | 'confirmation';
72
+ interface ShippingMethod {
73
+ carrier: string;
74
+ service: string;
75
+ amountMinor: number;
76
+ currency: string;
77
+ estimatedDays: number;
78
+ }
79
+ interface CheckoutState {
80
+ step: CheckoutStep;
81
+ email: string;
82
+ shippingAddress: Address | null;
83
+ billingAddress: Address | null;
84
+ shippingMethod: ShippingMethod | null;
85
+ /** What the server said about the payment; `succeeded` only once the order is paid. */
86
+ paymentStatus: 'idle' | 'processing' | 'requires_action' | 'succeeded' | 'failed';
87
+ error: string | null;
88
+ order: {
89
+ orderId: string;
90
+ orderNumber: string;
91
+ paymentIntent: PaymentIntent;
92
+ } | null;
93
+ /** The order this cart already became, when the server refused a resubmit and named it. */
94
+ placedOrderNumber: string | null;
95
+ }
96
+ interface AuthState {
97
+ customer: Customer | null;
98
+ isAuthenticated: boolean;
99
+ loading: boolean;
100
+ error: string | null;
101
+ }
102
+ interface ProductListParams {
103
+ limit?: number;
104
+ cursor?: string;
105
+ category?: string;
106
+ sort?: ProductSortKey;
107
+ }
108
+ interface ProductListResult {
109
+ items: DenormalisedProduct[];
110
+ nextCursor: string | null;
111
+ facets?: Facet[];
112
+ }
113
+ interface CheckoutDetails {
114
+ email: string;
115
+ billingAddress: Address;
116
+ shippingAddress: Address;
117
+ notes?: string;
118
+ }
119
+ interface CheckoutResult {
120
+ orderId: string;
121
+ orderNumber: string;
122
+ paymentIntent: PaymentIntent;
123
+ currency: string;
124
+ totalMinor: number;
125
+ orderStatus: OrderStatus;
126
+ payment: CheckoutPayment;
127
+ }
128
+ interface RegisterParams {
129
+ email: string;
130
+ password: string;
131
+ firstName?: string;
132
+ lastName?: string;
133
+ phone?: string;
134
+ }
135
+ interface AuthResult {
136
+ customer: Customer;
137
+ accessToken: string;
138
+ expiresIn: number;
139
+ }
140
+ type NewAddress = Address & {
141
+ isDefaultBilling?: boolean;
142
+ isDefaultShipping?: boolean;
143
+ };
144
+ interface OrderPage {
145
+ items: Order[];
146
+ nextCursor: string | null;
147
+ }
148
+
149
+ interface CommerceAdapter {
150
+ cart: {
151
+ get(): Promise<Cart>;
152
+ addItem(variantId: string, qty: number): Promise<Cart>;
153
+ removeItem(variantId: string): Promise<Cart>;
154
+ updateQuantity(variantId: string, qty: number): Promise<Cart>;
155
+ clear(): Promise<Cart>;
156
+ };
157
+ products: {
158
+ getBySlug(slug: string): Promise<DenormalisedProduct>;
159
+ list(params?: ProductListParams): Promise<ProductListResult>;
160
+ search(query: string, params?: {
161
+ limit?: number;
162
+ cursor?: string;
163
+ }): Promise<ProductListResult>;
164
+ };
165
+ checkout: {
166
+ create(details: CheckoutDetails): Promise<CheckoutResult>;
167
+ };
168
+ customers: {
169
+ register(params: RegisterParams): Promise<AuthResult>;
170
+ login(email: string, password: string): Promise<AuthResult>;
171
+ logout(): Promise<void>;
172
+ refresh(): Promise<{
173
+ accessToken: string;
174
+ expiresIn: number;
175
+ }>;
176
+ getProfile(): Promise<Customer>;
177
+ updateProfile(updates: Partial<Pick<Customer, 'firstName' | 'lastName' | 'phone'>>): Promise<Customer>;
178
+ listAddresses(): Promise<CustomerAddress[]>;
179
+ addAddress(address: NewAddress): Promise<CustomerAddress>;
180
+ removeAddress(id: string): Promise<void>;
181
+ listOrders(params?: {
182
+ cursor?: string;
183
+ }): Promise<OrderPage>;
184
+ getOrder(orderNumber: string): Promise<Order>;
185
+ };
186
+ reviews: {
187
+ getForProduct(productId: string, params?: {
188
+ limit?: number;
189
+ cursor?: string;
190
+ }): Promise<ReviewsResult>;
191
+ submit(review: {
192
+ productId: string;
193
+ rating: number;
194
+ title: string;
195
+ body: string;
196
+ }): Promise<ReviewResult>;
197
+ };
198
+ }
199
+
200
+ interface CartStore {
201
+ getState(): CartState;
202
+ subscribe(listener: () => void): () => void;
203
+ addItem(variantId: string, qty?: number): Promise<void>;
204
+ removeItem(variantId: string): Promise<void>;
205
+ updateQuantity(variantId: string, qty: number): Promise<void>;
206
+ clear(): Promise<void>;
207
+ load(): Promise<void>;
208
+ destroy(): void;
209
+ }
210
+ declare function createCartStore(adapter: CommerceAdapter): CartStore;
211
+
212
+ interface ProductStore {
213
+ getState(): ProductState;
214
+ subscribe(listener: () => void): () => void;
215
+ loadBySlug(slug: string): Promise<void>;
216
+ selectVariant(variant: DenormalisedVariant | null): void;
217
+ destroy(): void;
218
+ }
219
+ declare function createProductStore(adapter: CommerceAdapter): ProductStore;
220
+
221
+ interface ProductListStore {
222
+ getState(): ProductListState;
223
+ subscribe(listener: () => void): () => void;
224
+ load(params?: ProductListParams): Promise<void>;
225
+ loadMore(): Promise<void>;
226
+ applyFilter(name: string, value: string | string[]): Promise<void>;
227
+ removeFilter(name: string): Promise<void>;
228
+ setSort(sort: ProductSortKey): Promise<void>;
229
+ destroy(): void;
230
+ }
231
+ declare function createProductListStore(adapter: CommerceAdapter): ProductListStore;
232
+
233
+ interface SearchStore {
234
+ getState(): SearchState;
235
+ subscribe(listener: () => void): () => void;
236
+ search(query: string): Promise<void>;
237
+ clearSearch(): void;
238
+ destroy(): void;
239
+ }
240
+ declare function createSearchStore(adapter: CommerceAdapter): SearchStore;
241
+
242
+ interface CheckoutStore {
243
+ getState(): CheckoutState;
244
+ subscribe(listener: () => void): () => void;
245
+ setEmail(email: string): void;
246
+ setShippingAddress(address: Address): void;
247
+ setBillingAddress(address: Address): void;
248
+ setShippingMethod(method: ShippingMethod): void;
249
+ /** Places the order; resolves to the server's result, or null when there is none. */
250
+ submitPayment(): Promise<CheckoutResult | null>;
251
+ /** The provider confirmed the payment on the client (Stripe's `confirmCardPayment`). */
252
+ markConfirmed(): void;
253
+ reset(): void;
254
+ destroy(): void;
255
+ }
256
+ /**
257
+ * `cart`, when given, is loaded again once the server has answered a submit: placing an order changes the cart on the
258
+ * server (emptied when confirmed, kept while awaiting payment), and whatever renders the cart store must show that.
259
+ */
260
+ declare function createCheckoutStore(adapter: CommerceAdapter, cart?: CartStore): CheckoutStore;
261
+
262
+ interface TokenManagerOptions {
263
+ storage?: 'memory' | 'localStorage';
264
+ storageKey?: string;
265
+ }
266
+ interface TokenManager {
267
+ getAccessToken(): string | null;
268
+ setTokens(accessToken: string, expiresIn: number): void;
269
+ clear(): void;
270
+ scheduleRefresh(refreshFn: () => Promise<{
271
+ accessToken: string;
272
+ expiresIn: number;
273
+ }>): void;
274
+ destroy(): void;
275
+ }
276
+ declare function createTokenManager(options?: TokenManagerOptions): TokenManager;
277
+
278
+ interface AuthStore {
279
+ getState(): AuthState;
280
+ subscribe(listener: () => void): () => void;
281
+ login(email: string, password: string): Promise<void>;
282
+ register(params: RegisterParams): Promise<void>;
283
+ logout(): Promise<void>;
284
+ refreshToken(): Promise<void>;
285
+ updateProfile(updates: Partial<Pick<Customer, 'firstName' | 'lastName' | 'phone'>>): Promise<void>;
286
+ loadProfile(): Promise<void>;
287
+ destroy(): void;
288
+ }
289
+ declare function createAuthStore(adapter: CommerceAdapter, tokenManager: TokenManager): AuthStore;
290
+
291
+ interface NymbalClientOptions {
292
+ baseUrl: string;
293
+ adapter?: CommerceAdapter;
294
+ tokenStorage?: 'memory' | 'localStorage';
295
+ credentials?: RequestCredentials;
296
+ getCartToken?: () => string | null;
297
+ }
298
+ /** The signed-in customer's own account; every call sends the session's bearer token. */
299
+ interface CustomerAccount {
300
+ addresses: {
301
+ list(): Promise<CustomerAddress[]>;
302
+ create(address: NewAddress): Promise<CustomerAddress>;
303
+ remove(id: string): Promise<void>;
304
+ };
305
+ orders: {
306
+ list(opts?: {
307
+ cursor?: string;
308
+ }): Promise<OrderPage>;
309
+ get(orderNumber: string): Promise<Order>;
310
+ };
311
+ }
312
+ interface NymbalClient {
313
+ cart: CartStore;
314
+ product: ProductStore;
315
+ productList: ProductListStore;
316
+ search: SearchStore;
317
+ checkout: CheckoutStore;
318
+ auth: AuthStore;
319
+ customer: CustomerAccount;
320
+ adapter: CommerceAdapter;
321
+ destroy(): void;
322
+ }
323
+ declare function createNymbalClient(options: NymbalClientOptions): NymbalClient;
324
+
325
+ type Listener = () => void;
326
+ interface Store<TState> {
327
+ getState(): TState;
328
+ setState(partial: Partial<TState> | ((prev: TState) => Partial<TState>)): void;
329
+ subscribe(listener: Listener): () => void;
330
+ destroy(): void;
331
+ }
332
+ declare function createStore<TState extends object>(initialState: TState): Store<TState>;
333
+
334
+ declare class NymbalApiError extends Error {
335
+ readonly code: string;
336
+ readonly status: number;
337
+ readonly context?: Record<string, unknown> | undefined;
338
+ constructor(code: string, message: string, status: number, context?: Record<string, unknown> | undefined);
339
+ }
340
+ interface FetchClientOptions {
341
+ baseUrl: string;
342
+ credentials?: RequestCredentials;
343
+ getAccessToken?: () => string | null;
344
+ getCartToken?: () => string | null;
345
+ onUnauthorized?: () => Promise<void>;
346
+ }
347
+ declare class FetchClient {
348
+ private readonly baseUrl;
349
+ private readonly credentials;
350
+ private readonly getAccessToken;
351
+ private readonly getCartToken;
352
+ private readonly onUnauthorized;
353
+ private handlingUnauthorized;
354
+ /** The token `GET /api/csrf` answered: the API's `nymbal.csrf` cookie, which this origin may not be able to read. */
355
+ private csrfToken;
356
+ constructor(options: FetchClientOptions);
357
+ request<T>(method: string, path: string, body?: unknown): Promise<T>;
358
+ /** Like `request`, keeping the envelope's `meta` (pagination cursors live there). */
359
+ requestWithMeta<T>(method: string, path: string, body?: unknown): Promise<{
360
+ data: T;
361
+ meta: Record<string, unknown>;
362
+ }>;
363
+ private csrf;
364
+ private fetchCsrfToken;
365
+ private isCsrfRejection;
366
+ private doFetch;
367
+ private parseResponse;
368
+ }
369
+
370
+ declare function createNymbalApiAdapter(fetchClient: FetchClient): CommerceAdapter;
371
+
372
+ export { type AuthResult, type AuthState, type AuthStore, type CartState, type CartStore, type CheckoutDetails, type CheckoutResult, type CheckoutState, type CheckoutStep, type CheckoutStore, type CommerceAdapter, type CustomerAccount, type DenormalisedProduct, type DenormalisedVariant, FetchClient, type FetchClientOptions, type Listener, type NewAddress, NymbalApiError, type NymbalClient, type NymbalClientOptions, type OrderPage, type ProductListParams, type ProductListResult, type ProductListState, type ProductListStore, type ProductState, type ProductStore, type RegisterParams, type SearchState, type SearchStore, type ShippingMethod, type Store, type TokenManager, type TokenManagerOptions, createAuthStore, createCartStore, createCheckoutStore, createNymbalApiAdapter, createNymbalClient, createProductListStore, createProductStore, createSearchStore, createStore, createTokenManager };