@caldik/client 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,15 @@
1
1
  # Caldik SDK
2
+ TThe official Caldik SDK for interacting with Caldik storefronts, products, carts, checkouts, and customer accounts.
2
3
 
3
- The official Caldik SDK for interacting with Caldik storefronts, products, carts, checkouts, and customer accounts.
4
+ ## Installation
4
5
 
5
- ## Caldik-hosted Storefront
6
+ ```bash
7
+ npm install @caldik/client
8
+ ```
9
+
10
+ ## Getting Started
11
+
12
+ ### Caldik-hosted Storefront
6
13
 
7
14
  No credentials or store configuration are required when running inside a Caldik-hosted storefront.
8
15
 
@@ -14,7 +21,7 @@ const caldik = new CaldikClient()
14
21
  const { products } = await caldik.products.list()
15
22
  ```
16
23
 
17
- ## External Server
24
+ ### External Server
18
25
 
19
26
  External integrations must explicitly provide a store ID and a private `sk_` API key.
20
27
 
@@ -27,40 +34,196 @@ const caldik = new CaldikClient({
27
34
  })
28
35
  ```
29
36
 
37
+ > **Security:** Private API keys must only be used in trusted server-side environments. Never expose `sk_` keys in client-side code.
38
+
39
+
40
+ ## Orders and Items
41
+
42
+ ### List Products
43
+
44
+ Retrieve products associated with the current store.
45
+
46
+ **Basic usage**
47
+
48
+ ```ts
49
+ const { products } = await caldik.products.list()
50
+ ```
51
+
52
+ **Limit results**
53
+
54
+ Pass a number to limit the number of products returned.
55
+
56
+ ```ts
57
+ const { products } = await caldik.products.list(20)
58
+ ```
59
+
60
+ ### Get Product
61
+
62
+ Retrieve a specific product by its ID.
63
+
64
+ **Basic usage**
65
+
66
+ ```ts
67
+ const { product } = await caldik.products.get("product_123")
68
+ ```
69
+
70
+ ### Get Category
71
+
72
+ Retrieve a specific category by its ID.
73
+
74
+ **Basic usage**
75
+
76
+ ```ts
77
+ const { category } = await caldik.categories.get("CAT_ID")
78
+ ```
79
+
80
+ ### List Categories
81
+
82
+ Retrieve categories associated with the current store.
83
+
84
+ **Basic usage**
85
+
86
+ ```ts
87
+ const { categories } = await caldik.categories.list()
88
+ ```
89
+
90
+ **Limit results**
91
+
92
+ Pass a number to limit the number of categories returned.
93
+
94
+ ```ts
95
+ const { categories } = await caldik.categories.list(20)
96
+ ```
97
+
98
+ **Advanced usage**
99
+
100
+ Use an options object to configure the limit and request additional fields.
101
+
102
+ ```ts
103
+ const { categories } = await caldik.categories.list({
104
+ limit: 20,
105
+ include: ["productCount", "startingPrice"],
106
+ })
107
+ ```
108
+
109
+ **Optional fields**
110
+
111
+ The following fields are omitted by default unless explicitly requested using `include`:
112
+
113
+ | Field | Description |
114
+ |---|---|
115
+ | `productCount` | Number of products associated with the category. |
116
+ | `startingPrice` | Lowest available product price in the category, in cents. |
117
+
118
+
30
119
  ## Cart and Discounts
31
120
 
32
121
  ### Cart Management
33
122
 
34
- Add products to the cart, update quantities, or remove items.
123
+ **Get the cart**
124
+
125
+ Refresh the current items and applied discount from the server.
35
126
 
36
127
  ```ts
37
- const cart = caldik.cart.add({
128
+ const { items, discount } = await caldik.cart.get()
129
+ ```
130
+
131
+ **Add an item**
132
+
133
+ ```ts
134
+ const cart = await caldik.cart.add({
38
135
  productId: "PRODUCT_...",
39
136
  productVariantId: "PRODUCT_VARIANT_...",
40
137
  quantity: 1,
41
138
  })
139
+ ```
140
+
141
+ **Update item quantity**
142
+
143
+ Set an item's quantity. A quantity of zero removes it.
144
+
145
+ ```ts
146
+ await caldik.cart.updateQuantity(cart.items[0].id, 3)
147
+ ```
148
+
149
+ **Remove an item**
150
+
151
+ ```ts
152
+ await caldik.cart.remove(cart.items[0].id)
153
+ ```
154
+
155
+ **Clear the cart**
156
+
157
+ Remove all items and the applied discount from the persisted cart.
158
+
159
+ ```ts
160
+ await caldik.cart.clear()
161
+ ```
42
162
 
43
- // Update item quantity
44
- caldik.cart.updateQuantity(cart.items[0].id, 3)
163
+ **Check a discount**
45
164
 
46
- // Remove an item
47
- caldik.cart.remove(cart.items[0].id)
165
+ Get a price and discount quote for the current items without applying the code.
166
+
167
+ ```ts
168
+ const quote = await caldik.cart.checkDiscount("SAVE10")
48
169
  ```
49
170
 
50
- ### Apply Discounts
171
+ **Apply a discount**
51
172
 
52
- Apply a discount code to the current cart.
173
+ Validate the code and save it to the current cart.
53
174
 
54
175
  ```ts
55
- await caldik.cart.applyDiscount("SAVE10")
176
+ const discountedCart = await caldik.cart.applyDiscount("SAVE10")
56
177
  ```
57
178
 
58
- ### Checkout
179
+ **Remove the discount**
59
180
 
60
- Create a checkout session using the current cart or explicitly provided items.
181
+ Remove the applied code while keeping the cart's items.
182
+
183
+ ```ts
184
+ await caldik.cart.removeDiscount()
185
+ ```
186
+
187
+ **Checkout the cart**
188
+
189
+ Create an order using the persisted cart. The API revalidates prices and discounts.
61
190
 
62
191
  ```ts
63
192
  const result = await caldik.cart.checkout({
193
+ paymentMethod: "stripe",
194
+ termsAccepted: acceptedTermsCheckbox,
195
+ successUrl: "https://shop.example/success",
196
+ cancelUrl: "https://shop.example/cart",
197
+ })
198
+ ```
199
+
200
+ ### Server-rendered Storefronts
201
+
202
+ Create a client for each incoming request so shopper sessions stay isolated.
203
+ Pass the request and forward returned session cookies to the outgoing response:
204
+
205
+ ```ts
206
+ const responseHeaders = new Headers()
207
+ const caldik = new CaldikClient({
208
+ request,
209
+ onSetCookie: (cookie) => responseHeaders.append("Set-Cookie", cookie),
210
+ // External servers also supply storeId and their private apiKey.
211
+ })
212
+ const cart = await caldik.cart.get()
213
+ // Include responseHeaders in your framework's HTTP response.
214
+ ```
215
+
216
+ Browser and server calls must share the same cookie context (for example through
217
+ a storefront API proxy). Cookies set only on a separate API hostname are not
218
+ included in requests to the storefront server. A private store key does not
219
+ replace a shopper's customer session.
220
+
221
+ ### Checkout with Explicit Items
222
+
223
+ Use `caldik.checkout.create()` to create an order from explicitly provided items.
224
+
225
+ ```ts
226
+ const result = await caldik.checkout.create({
64
227
  paymentMethod: "stripe",
65
228
  items: [
66
229
  {
@@ -96,11 +259,24 @@ Username authentication is available through the **Customer Usernames** plugin.
96
259
 
97
260
  Customers can log in using a six-digit verification code sent to their email address.
98
261
 
262
+ **Send a verification code**
263
+
99
264
  ```ts
100
- // Send a six-digit verification code
101
265
  await caldik.customer.login("customer@example.com")
266
+ ```
102
267
 
103
- // Verify the code
268
+ Alternatively, use an options object to configure the session.
269
+
270
+ ```ts
271
+ await caldik.customer.login({
272
+ email: "customer@example.com",
273
+ rememberMe: true, // Optional, defaults to false
274
+ })
275
+ ```
276
+
277
+ **Verify the code**
278
+
279
+ ```ts
104
280
  await caldik.customer.login.verify(code)
105
281
  ```
106
282
 
@@ -112,6 +288,7 @@ Email and password authentication requires password login to be enabled in the s
112
288
  await caldik.customer.login({
113
289
  email: "customer@example.com",
114
290
  password,
291
+ rememberMe: true, // Optional, defaults to false
115
292
  })
116
293
  ```
117
294
 
@@ -125,11 +302,58 @@ Once installed, customers can authenticate using their username instead of their
125
302
  await caldik.customer.login({
126
303
  username: "customer",
127
304
  password,
305
+ rememberMe: true, // Optional, defaults to false
128
306
  })
129
307
  ```
130
308
 
131
309
  > **Note:** Username authentication requires both the Customer Usernames plugin and password authentication to be enabled.
132
310
 
311
+ ### Session Duration
312
+
313
+ All customer authentication methods support the optional `rememberMe` parameter.
314
+
315
+ | `rememberMe` | Session Duration |
316
+ |---|---|
317
+ | `false` (default) | 24 hours |
318
+ | `true` | 30 days |
319
+
320
+ Omit `rememberMe` to use the default session duration.
321
+
322
+ ### Session Check
323
+
324
+ Retrieve the current customer's authentication status, account details, and session expiration.
325
+
326
+ ```ts
327
+ const {
328
+ authenticated,
329
+ customer,
330
+ expiresAt,
331
+ } = await caldik.customer.session()
332
+ ```
333
+
334
+ ### Password Reset
335
+
336
+ Customers can reset their passwords using a verification code sent to their email address.
337
+
338
+ **Request a password reset**
339
+
340
+ ```ts
341
+ const { challengeId } =
342
+ await caldik.customer.password.requestReset(email)
343
+ ```
344
+
345
+ Password reset codes expire after **10 minutes**.
346
+
347
+ **Reset the password**
348
+
349
+ ```ts
350
+ await caldik.customer.password.reset({
351
+ challengeId,
352
+ code,
353
+ password: newPassword,
354
+ })
355
+ ```
356
+
133
357
  ## Customer Registration
134
358
 
135
359
  Customers can register using their email address, with optional password and username support.
@@ -142,14 +366,18 @@ Password registration must be enabled in the store dashboard.
142
366
 
143
367
  Register a customer using their email address and password.
144
368
 
369
+ **Register a customer**
370
+
145
371
  ```ts
146
- // Register a new customer
147
372
  await caldik.customer.register({
148
373
  email: "customer@example.com",
149
374
  password,
150
375
  })
376
+ ```
377
+
378
+ **Verify the email address**
151
379
 
152
- // Verify the email using the six-digit code
380
+ ```ts
153
381
  await caldik.customer.register.verify(code)
154
382
  ```
155
383
 
@@ -159,15 +387,19 @@ To register customers with a username, install and enable the **Customer Usernam
159
387
 
160
388
  Once installed, the registration endpoint accepts an additional `username` field.
161
389
 
390
+ **Register a customer with a username**
391
+
162
392
  ```ts
163
- // Register a new customer with a username
164
393
  await caldik.customer.register({
165
394
  email: "customer@example.com",
166
395
  password,
167
396
  username: "customer",
168
397
  })
398
+ ```
399
+
400
+ **Verify the email address**
169
401
 
170
- // Verify the email using the six-digit code
402
+ ```ts
171
403
  await caldik.customer.register.verify(code)
172
404
  ```
173
405
 
@@ -179,28 +411,36 @@ Retrieve customer authentication settings, checkout configuration, and available
179
411
 
180
412
  ### Customer Settings
181
413
 
414
+ Retrieve the store's customer authentication settings.
415
+
182
416
  ```ts
183
417
  const { settings: loginSettings } = await caldik.customer.settings.get()
184
418
  ```
185
419
 
186
420
  ### Checkout Settings
187
421
 
422
+ Retrieve the store's checkout configuration.
423
+
188
424
  ```ts
189
425
  const { settings: checkoutSettings } = await caldik.checkout.settings.get()
190
426
  ```
191
427
 
192
428
  ### Available Payment Methods
193
429
 
430
+ Retrieve the payment methods available for checkout.
431
+
194
432
  ```ts
195
433
  const { paymentMethods } = await caldik.checkout.paymentmethods.list()
196
434
  ```
197
435
 
198
436
  ### Combined Example
199
437
 
438
+ Retrieve customer settings, checkout settings, and available payment methods together.
439
+
200
440
  ```ts
201
441
  const { settings: loginSettings } = await caldik.customer.settings.get()
202
442
 
203
443
  const { settings: checkoutSettings } = await caldik.checkout.settings.get()
204
444
 
205
445
  const { paymentMethods } = await caldik.checkout.paymentmethods.list()
206
- ```
446
+ ```
package/dist/index.d.mts CHANGED
@@ -24,7 +24,10 @@ interface Category {
24
24
  name: string;
25
25
  slug: string;
26
26
  description: string;
27
- price: string;
27
+ /** Present when startingPrice is included. Prefer startingPrice. */
28
+ price?: string;
29
+ productCount?: number;
30
+ startingPrice?: string | null;
28
31
  logo: string | null;
29
32
  banner: string | null;
30
33
  metadata: CategoryMetadata[];
@@ -33,6 +36,11 @@ interface CategoriesResponse {
33
36
  success: boolean;
34
37
  categories: Category[];
35
38
  }
39
+ type CategoryInclude = "productCount" | "startingPrice" | "product count" | "starting price";
40
+ interface CategoryListOptions {
41
+ limit?: number;
42
+ include?: readonly CategoryInclude[];
43
+ }
36
44
  interface CategoryProduct {
37
45
  id: string;
38
46
  name: string;
@@ -245,10 +253,41 @@ interface CustomerPasswordResponse extends CustomerLoginVerifyResponse {
245
253
  extensions?: Record<string, Record<string, string | number | boolean>>;
246
254
  };
247
255
  }
248
- interface CustomerPasswordInput {
249
- /** A username from an installed identity plugin, or the customer's email. */
256
+ type CustomerPasswordInput = ({
250
257
  username: string;
258
+ email?: never;
259
+ } | {
260
+ email: string;
261
+ username?: never;
262
+ }) & {
251
263
  password: string;
264
+ rememberMe?: boolean;
265
+ };
266
+ interface CustomerEmailLoginInput {
267
+ email: string;
268
+ rememberMe?: boolean;
269
+ }
270
+ interface CustomerSessionResponse {
271
+ success: boolean;
272
+ authenticated: boolean;
273
+ customer: {
274
+ id: string;
275
+ email: string | null;
276
+ } | null;
277
+ expiresAt: number | null;
278
+ }
279
+ interface CustomerPasswordResetResponse extends CustomerLoginResponse {
280
+ expiresAt: number;
281
+ message: string;
282
+ }
283
+ interface CustomerPasswordResetInput {
284
+ challengeId?: string;
285
+ code: string;
286
+ password: string;
287
+ }
288
+ interface CustomerPasswordResetVerifyResponse {
289
+ success: boolean;
290
+ message: string;
252
291
  }
253
292
  interface CustomerRegistrationInput {
254
293
  email: string;
@@ -310,6 +349,8 @@ interface RequestOptions {
310
349
  edgeCache?: boolean;
311
350
  /** Internal authorization policy. Protected hosted calls are the default. */
312
351
  access?: "public" | "runtime";
352
+ /** Scoped credentials used by stateful storefront resources. */
353
+ headers?: Record<string, string>;
313
354
  }
314
355
  //#endregion
315
356
  //#region src/internal/client.d.ts
@@ -317,13 +358,22 @@ type HttpClientOptions = {
317
358
  apiKey?: string;
318
359
  apiUrl?: string;
319
360
  storeId?: string;
361
+ request?: Request;
362
+ onSetCookie?: (cookie: string) => void | Promise<void>;
320
363
  };
321
364
  declare class CaldikHttpClient {
322
365
  private readonly apiKey?;
323
366
  private readonly apiUrl;
324
367
  private readonly storeId?;
368
+ private readonly cookies;
369
+ private readonly onSetCookie?;
370
+ private readonly authenticationListeners;
325
371
  constructor(options?: HttpClientOptions);
326
372
  storeContextId(): string | null;
373
+ hasCartSession(): boolean;
374
+ onCustomerAuthentication(listener: () => Promise<unknown>): void;
375
+ customerAuthenticated(): Promise<void>;
376
+ private captureCookies;
327
377
  private baseUrl;
328
378
  private request;
329
379
  get<T>(path: string, params?: Record<string, string | number>, options?: RequestOptions): Promise<T>;
@@ -334,7 +384,7 @@ declare class CaldikHttpClient {
334
384
  declare class CategoriesApi {
335
385
  private client;
336
386
  constructor(client: CaldikHttpClient);
337
- list(limit?: number): Promise<CategoriesResponse>;
387
+ list(options?: number | CategoryListOptions): Promise<CategoriesResponse>;
338
388
  get(categoryId: string): Promise<CategoryResponse>;
339
389
  }
340
390
  //#endregion
@@ -381,7 +431,10 @@ declare class CustomerApi {
381
431
  get: () => Promise<CustomerLoginSettingsResponse>;
382
432
  };
383
433
  readonly login: {
384
- (email: string): Promise<CustomerLoginResponse>;
434
+ (email: string, options?: {
435
+ rememberMe?: boolean;
436
+ }): Promise<CustomerLoginResponse>;
437
+ (input: CustomerEmailLoginInput): Promise<CustomerLoginResponse>;
385
438
  (input: CustomerPasswordInput): Promise<CustomerPasswordResponse>;
386
439
  } & {
387
440
  /** Pass challengeId to resume a code request after reloading the page. */verify: (code: string, challengeId?: string | undefined) => Promise<CustomerLoginVerifyResponse>;
@@ -392,7 +445,13 @@ declare class CustomerApi {
392
445
  } & {
393
446
  /** Verify the email and atomically activate the account. Supports resuming after a reload. */verify: (code: string, challengeId?: string | undefined) => Promise<CustomerPasswordResponse>;
394
447
  };
448
+ readonly password: {
449
+ requestReset: (email: string) => Promise<CustomerPasswordResetResponse>;
450
+ reset: (input: CustomerPasswordResetInput) => Promise<CustomerPasswordResetVerifyResponse>;
451
+ };
395
452
  constructor(client: CaldikHttpClient);
453
+ /** Check a store-scoped session without raising an error for signed-out customers. */
454
+ session(): Promise<CustomerSessionResponse>;
396
455
  /** Read the signed-in customer and their balances. */
397
456
  me(): Promise<CustomerProfileResponse>;
398
457
  }
@@ -428,10 +487,8 @@ declare class DiscountsApi {
428
487
  }
429
488
  //#endregion
430
489
  //#region src/cart.d.ts
431
- interface CartItem {
490
+ interface CartItem extends ProductCommerceItem {
432
491
  id: string;
433
- productId: string;
434
- productVariantId?: string;
435
492
  quantity: number;
436
493
  }
437
494
  interface CartDiscount {
@@ -446,24 +503,35 @@ interface CartSnapshot {
446
503
  items: CartItem[];
447
504
  discount: CartDiscount | null;
448
505
  }
449
- type CartCheckoutInput = Omit<CreateOrderInput, "items" | "discountCode">;
506
+ type CartCheckoutInput = Omit<CreateOrderInput, "items" | "discountCode" | "customerId">;
507
+ /** One persisted cart, automatically scoped to the current shopper session. */
450
508
  declare class CartApi {
451
509
  private client;
452
- private memory;
510
+ private cache;
511
+ private queue;
512
+ private initialized;
453
513
  constructor(client: CaldikHttpClient);
454
- private storageKey;
455
- private read;
514
+ private storage;
515
+ private key;
516
+ private readCache;
517
+ private persistCache;
456
518
  private write;
457
- get(): CartSnapshot;
458
- add(item: ProductCommerceItem): CartSnapshot;
459
- updateQuantity(itemId: string, nextQuantity: number): CartSnapshot;
460
- remove(itemId: string): CartSnapshot;
461
- clear(): CartSnapshot;
462
- private commerceItems;
519
+ private enqueue;
520
+ private options;
521
+ private resolve;
522
+ private reset;
523
+ private mutate;
524
+ private save;
525
+ private quote;
526
+ get(): Promise<CartSnapshot>;
527
+ add(item: ProductCommerceItem): Promise<CartSnapshot>;
528
+ updateQuantity(itemId: string, nextQuantity: number): Promise<CartSnapshot>;
529
+ remove(itemId: string): Promise<CartSnapshot>;
530
+ clear(): Promise<CartSnapshot>;
463
531
  checkDiscount(code: string): Promise<DiscountQuoteResponse>;
464
532
  applyDiscount(code: string): Promise<CartSnapshot>;
465
- removeDiscount(): CartSnapshot;
466
- checkout(input: CartCheckoutInput): Promise<OrderResponse>;
533
+ removeDiscount(): Promise<CartSnapshot>;
534
+ checkout(input?: CartCheckoutInput): Promise<OrderResponse>;
467
535
  }
468
536
  //#endregion
469
537
  //#region src/index.d.ts
@@ -473,6 +541,10 @@ interface CaldikClientOptions {
473
541
  /** Private API key for external server integrations. Never use in browsers. */
474
542
  apiKey?: string;
475
543
  apiUrl?: string;
544
+ /** Incoming storefront request. Create one client per server request to isolate shoppers. */
545
+ request?: Request;
546
+ /** Forward session cookies to the outgoing storefront response in server code. */
547
+ onSetCookie?: (cookie: string) => void | Promise<void>;
476
548
  }
477
549
  declare class CaldikClient {
478
550
  readonly categories: CategoriesApi;
@@ -487,4 +559,4 @@ declare class CaldikClient {
487
559
  constructor(options?: CaldikClientOptions);
488
560
  }
489
561
  //#endregion
490
- export { AppliedDiscount, CaldikClient, CaldikClientOptions, CartApi, CartCheckoutInput, CartDiscount, CartItem, CartSnapshot, CategoriesResponse, Category, CategoryDetails, CategoryMetadata, CategoryProduct, CategoryResponse, CheckDiscountInput, CheckoutPayment, CheckoutPaymentMethod, CheckoutPaymentMethodsResponse, CheckoutPaymentProviderId, CheckoutSettings, CheckoutSettingsResponse, CommerceItem, CreateOrderInput, CreateSubscriptionInput, CustomerLoginResponse, CustomerLoginSettings, CustomerLoginSettingsResponse, CustomerLoginVerifyResponse, CustomerPasswordInput, CustomerPasswordResponse, CustomerProfileResponse, CustomerRegistrationInput, CustomerRegistrationResponse, DiscountQuoteResponse, DiscountsApi, ManualCommerceItem, OrderResponse, PaymentProviderId, Product, ProductCommerceItem, ProductCustomField, ProductCustomFieldInputType, ProductCustomFieldOption, ProductMetadata, ProductResponse, ProductTag, ProductVariant, ProductsResponse, Review, ReviewsResponse, Store, StoreResponse, StoreWebhookEvent, StoreWebhookEventName, StorefrontMetadata, StorefrontOrder, SubscriptionPaymentProviderId, SubscriptionResponse, checkoutPaymentProviderIds, paymentProviderIds, storeWebhookEventNames, subscriptionPaymentProviderIds };
562
+ export { AppliedDiscount, CaldikClient, CaldikClientOptions, CartApi, CartCheckoutInput, CartDiscount, CartItem, CartSnapshot, CategoriesResponse, Category, CategoryDetails, CategoryInclude, CategoryListOptions, CategoryMetadata, CategoryProduct, CategoryResponse, CheckDiscountInput, CheckoutPayment, CheckoutPaymentMethod, CheckoutPaymentMethodsResponse, CheckoutPaymentProviderId, CheckoutSettings, CheckoutSettingsResponse, CommerceItem, CreateOrderInput, CreateSubscriptionInput, CustomerEmailLoginInput, CustomerLoginResponse, CustomerLoginSettings, CustomerLoginSettingsResponse, CustomerLoginVerifyResponse, CustomerPasswordInput, CustomerPasswordResetInput, CustomerPasswordResetResponse, CustomerPasswordResetVerifyResponse, CustomerPasswordResponse, CustomerProfileResponse, CustomerRegistrationInput, CustomerRegistrationResponse, CustomerSessionResponse, DiscountQuoteResponse, DiscountsApi, ManualCommerceItem, OrderResponse, PaymentProviderId, Product, ProductCommerceItem, ProductCustomField, ProductCustomFieldInputType, ProductCustomFieldOption, ProductMetadata, ProductResponse, ProductTag, ProductVariant, ProductsResponse, Review, ReviewsResponse, Store, StoreResponse, StoreWebhookEvent, StoreWebhookEventName, StorefrontMetadata, StorefrontOrder, SubscriptionPaymentProviderId, SubscriptionResponse, checkoutPaymentProviderIds, paymentProviderIds, storeWebhookEventNames, subscriptionPaymentProviderIds };
package/dist/index.mjs CHANGED
@@ -26,6 +26,14 @@ function defaultApiUrl(browser) {
26
26
  //#region src/internal/client.ts
27
27
  const STORE_ID_PATTERN = /^STORE_[a-f0-9]{32}$/i;
28
28
  const STOREFRONT_STORE_ID_ATTRIBUTE = "data-caldik-store-id";
29
+ var CaldikHttpError = class extends Error {
30
+ status;
31
+ constructor(message, status) {
32
+ super(message);
33
+ this.status = status;
34
+ this.name = "CaldikHttpError";
35
+ }
36
+ };
29
37
  function isBrowserRuntime() {
30
38
  return typeof window !== "undefined" && typeof document !== "undefined";
31
39
  }
@@ -48,9 +56,17 @@ var CaldikHttpClient = class {
48
56
  apiKey;
49
57
  apiUrl;
50
58
  storeId;
59
+ cookies = /* @__PURE__ */ new Map();
60
+ onSetCookie;
61
+ authenticationListeners = /* @__PURE__ */ new Set();
51
62
  constructor(options = {}) {
52
63
  this.apiKey = options.apiKey?.trim() || void 0;
53
64
  this.apiUrl = options.apiUrl || defaultApiUrl(isBrowserRuntime());
65
+ this.onSetCookie = options.onSetCookie;
66
+ if (!isBrowserRuntime()) for (const part of (options.request?.headers.get("Cookie") || "").split(";")) {
67
+ const separator = part.indexOf("=");
68
+ if (separator > 0) this.cookies.set(part.slice(0, separator).trim(), part.slice(separator + 1).trim());
69
+ }
54
70
  const configuredStoreId = options.storeId?.trim() || void 0;
55
71
  if (this.apiKey && !this.apiKey.startsWith("sk_")) throw new Error("Caldik external integrations require a private sk_ API key");
56
72
  if (configuredStoreId && !STORE_ID_PATTERN.test(configuredStoreId)) throw new Error("Caldik storeId is invalid");
@@ -60,6 +76,31 @@ var CaldikHttpClient = class {
60
76
  storeContextId() {
61
77
  return this.storeId || boundRuntimeStoreId();
62
78
  }
79
+ hasCartSession() {
80
+ return this.cookies.has(`caldik_cart_session_${this.storeContextId()}`);
81
+ }
82
+ onCustomerAuthentication(listener) {
83
+ this.authenticationListeners.add(listener);
84
+ }
85
+ async customerAuthenticated() {
86
+ await Promise.allSettled([...this.authenticationListeners].map((listener) => listener()));
87
+ }
88
+ async captureCookies(response) {
89
+ for (const cookie of response.headers.getSetCookie()) {
90
+ const [pair = "", ...attributes] = cookie.split(";");
91
+ const separator = pair.indexOf("=");
92
+ if (separator < 1) continue;
93
+ const name = pair.slice(0, separator).trim();
94
+ if (attributes.some((attribute) => {
95
+ const [key, ...value] = attribute.trim().split("=");
96
+ if (key?.toLowerCase() === "max-age") return Number(value.join("=")) <= 0;
97
+ if (key?.toLowerCase() === "expires") return Date.parse(value.join("=")) <= Date.now();
98
+ return false;
99
+ })) this.cookies.delete(name);
100
+ else this.cookies.set(name, pair.slice(separator + 1).trim());
101
+ await this.onSetCookie?.(cookie);
102
+ }
103
+ }
63
104
  baseUrl() {
64
105
  if (isBrowserRuntime()) {
65
106
  const storeId = this.storeId;
@@ -76,8 +117,9 @@ var CaldikHttpClient = class {
76
117
  async request(method, path, input, params, options) {
77
118
  const url = new URL(`${this.baseUrl()}${path}`);
78
119
  if (params) for (const [key, value] of Object.entries(params)) url.searchParams.set(key, String(value));
79
- const headers = {};
120
+ const headers = { ...options?.headers };
80
121
  if (!isBrowserRuntime()) headers["User-Agent"] = "CaldikClient/0.0.2";
122
+ if (!isBrowserRuntime() && options?.access === "public" && this.cookies.size) headers.Cookie = [...this.cookies].map(([name, value]) => `${name}=${value}`).join("; ");
81
123
  if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
82
124
  else if (options?.access === "public" && (!isBrowserRuntime() || isLocalBrowserRuntime())) headers["X-Caldik-Public-Request"] = "1";
83
125
  if (input !== void 0) headers["Content-Type"] = "application/json";
@@ -89,10 +131,10 @@ var CaldikHttpClient = class {
89
131
  cache: "default",
90
132
  credentials: "include"
91
133
  });
134
+ if (!isBrowserRuntime() && options?.access === "public") await this.captureCookies(response);
92
135
  if (!response.ok) {
93
136
  const error = await response.clone().json().catch(() => null);
94
- const message = typeof error?.message === "string" && error.message ? error.message : `${response.status} ${response.statusText}`.trim();
95
- throw new Error(message);
137
+ throw new CaldikHttpError(typeof error?.message === "string" && error.message ? error.message : `${response.status} ${response.statusText}`.trim(), response.status);
96
138
  }
97
139
  return response.json();
98
140
  }
@@ -110,8 +152,12 @@ var CategoriesApi = class {
110
152
  constructor(client) {
111
153
  this.client = client;
112
154
  }
113
- list(limit) {
114
- return this.client.get("/categories", limit ? { limit } : void 0, { access: "public" });
155
+ list(options) {
156
+ const { limit, include } = typeof options === "number" ? { limit: options } : options ?? {};
157
+ return this.client.get("/categories", {
158
+ ...limit !== void 0 ? { limit } : {},
159
+ ...include?.length ? { include: include.join(",") } : {}
160
+ }, { access: "public" });
115
161
  }
116
162
  get(categoryId) {
117
163
  return this.client.get(`/categories/${categoryId}`, void 0, { access: "public" });
@@ -178,13 +224,40 @@ var CustomerApi = class {
178
224
  settings;
179
225
  login;
180
226
  register;
227
+ password;
181
228
  constructor(client) {
182
229
  this.client = client;
183
230
  this.settings = { get: () => this.client.get("/customers/settings", void 0, { access: "public" }) };
231
+ let pendingResetId;
232
+ this.password = {
233
+ requestReset: async (email) => {
234
+ const response = await client.post("/customers/password/reset", { email }, { access: "public" });
235
+ pendingResetId = response.challengeId;
236
+ return response;
237
+ },
238
+ reset: async (input) => {
239
+ const challengeId = input.challengeId ?? pendingResetId;
240
+ if (!challengeId) throw new Error("Request a password reset first or provide its challengeId");
241
+ if (!/^\d{6}$/.test(input.code)) throw new Error("A six-digit reset code is required");
242
+ const response = await client.post("/customers/password/reset/verify", {
243
+ ...input,
244
+ challengeId
245
+ }, { access: "public" });
246
+ if (pendingResetId === challengeId) pendingResetId = void 0;
247
+ return response;
248
+ }
249
+ };
184
250
  let pendingChallengeId;
185
- async function login(input) {
186
- if (typeof input !== "string") return client.post("/customers/login/password", input, { access: "public" });
187
- const response = await client.post("/customers/login", { email: input }, { access: "public" });
251
+ async function login(input, options) {
252
+ if (typeof input !== "string" && "password" in input) {
253
+ const response = await client.post("/customers/login/password", input, { access: "public" });
254
+ await client.customerAuthenticated();
255
+ return response;
256
+ }
257
+ const response = await client.post("/customers/login", typeof input === "string" ? {
258
+ email: input,
259
+ ...options
260
+ } : input, { access: "public" });
188
261
  pendingChallengeId = response.challengeId;
189
262
  return response;
190
263
  }
@@ -198,6 +271,7 @@ verify: async (code, challengeId = pendingChallengeId) => {
198
271
  challengeId
199
272
  }, { access: "public" });
200
273
  if (pendingChallengeId === challengeId) pendingChallengeId = void 0;
274
+ await client.customerAuthenticated();
201
275
  return response;
202
276
  } });
203
277
  let pendingRegistrationId;
@@ -216,9 +290,14 @@ verify: async (code, challengeId = pendingRegistrationId) => {
216
290
  challengeId
217
291
  }, { access: "public" });
218
292
  if (pendingRegistrationId === challengeId) pendingRegistrationId = void 0;
293
+ await client.customerAuthenticated();
219
294
  return response;
220
295
  } });
221
296
  }
297
+ /** Check a store-scoped session without raising an error for signed-out customers. */
298
+ session() {
299
+ return this.client.get("/customers/session", void 0, { access: "public" });
300
+ }
222
301
  /** Read the signed-in customer and their balances. */
223
302
  me() {
224
303
  return this.client.get("/customers/me", void 0, { access: "public" });
@@ -248,20 +327,18 @@ var DiscountsApi = class {
248
327
  };
249
328
  //#endregion
250
329
  //#region src/cart.ts
251
- const CART_VERSION = 1;
252
330
  const MAX_QUANTITY = 999999;
253
- function cartItemId(item) {
254
- return item.productVariantId ? `${item.productId}:${item.productVariantId}` : item.productId;
255
- }
256
- function quantity(value, allowZero = false) {
257
- const normalized = value ?? 1;
331
+ function quantity(value = 1, allowZero = false) {
258
332
  const minimum = allowZero ? 0 : 1;
259
- if (!Number.isInteger(normalized) || normalized < minimum || normalized > MAX_QUANTITY) throw new Error(`Cart quantity must be an integer between ${minimum} and ${MAX_QUANTITY}`);
260
- return normalized;
333
+ if (!Number.isInteger(value) || value < minimum || value > MAX_QUANTITY) throw new Error(`Cart quantity must be an integer between ${minimum} and ${MAX_QUANTITY}`);
334
+ return value;
261
335
  }
262
336
  function cloneSnapshot(state) {
263
337
  return {
264
- items: state.items.map((item) => ({ ...item })),
338
+ items: state.items.map((item) => ({
339
+ ...item,
340
+ ...item.customFields ? { customFields: { ...item.customFields } } : {}
341
+ })),
265
342
  discount: state.discount ? { ...state.discount } : null
266
343
  };
267
344
  }
@@ -272,140 +349,267 @@ function isCartItem(value) {
272
349
  }
273
350
  function isCartDiscount(value) {
274
351
  if (!value || typeof value !== "object") return false;
275
- const discount = value;
276
- return typeof discount.code === "string" && typeof discount.percentage === "string" && typeof discount.subtotal === "string" && typeof discount.discountAmount === "string" && typeof discount.total === "string" && typeof discount.currency === "string";
352
+ return [
353
+ "code",
354
+ "percentage",
355
+ "subtotal",
356
+ "discountAmount",
357
+ "total",
358
+ "currency"
359
+ ].every((key) => typeof value[key] === "string");
277
360
  }
361
+ function isReference(value) {
362
+ if (!value || typeof value !== "object") return false;
363
+ const reference = value;
364
+ return typeof reference.id === "string" && /^CART_[a-f0-9]{32}$/.test(reference.id) && (reference.token == null || typeof reference.token === "string");
365
+ }
366
+ function commerceItems(items) {
367
+ return items.map(({ id: _id, ...item }) => item);
368
+ }
369
+ function discountFromQuote(quote) {
370
+ return {
371
+ ...quote.discount,
372
+ subtotal: quote.subtotal,
373
+ discountAmount: quote.discountAmount,
374
+ total: quote.total,
375
+ currency: quote.currency
376
+ };
377
+ }
378
+ /** One persisted cart, automatically scoped to the current shopper session. */
278
379
  var CartApi = class {
279
380
  client;
280
- memory = {
281
- version: CART_VERSION,
282
- items: [],
283
- discount: null
381
+ cache = {
382
+ version: 2,
383
+ reference: null,
384
+ snapshot: {
385
+ items: [],
386
+ discount: null
387
+ }
284
388
  };
389
+ queue = Promise.resolve();
390
+ initialized = false;
285
391
  constructor(client) {
286
392
  this.client = client;
393
+ client.onCustomerAuthentication(() => {
394
+ this.readCache();
395
+ return this.initialized || this.cache.reference || this.cache.snapshot.items.length || client.hasCartSession() ? this.enqueue(async () => {
396
+ await this.resolve();
397
+ }) : Promise.resolve();
398
+ });
399
+ }
400
+ storage() {
401
+ try {
402
+ return typeof window !== "undefined" && typeof document !== "undefined" && typeof localStorage !== "undefined" ? localStorage : null;
403
+ } catch {
404
+ return null;
405
+ }
287
406
  }
288
- storageKey() {
289
- const storeId = this.client.storeContextId();
290
- return storeId ? `caldik:cart:${storeId}` : null;
291
- }
292
- read() {
293
- const key = this.storageKey();
294
- if (key && typeof localStorage !== "undefined") try {
295
- const value = localStorage.getItem(key);
296
- if (value) {
297
- const parsed = JSON.parse(value);
298
- if (parsed.version === CART_VERSION && Array.isArray(parsed.items) && parsed.items.every(isCartItem)) this.memory = {
299
- version: CART_VERSION,
407
+ key() {
408
+ return `caldik:cart:${this.client.storeContextId()}`;
409
+ }
410
+ readCache() {
411
+ const storage = this.storage();
412
+ if (!storage) return;
413
+ try {
414
+ const parsed = JSON.parse(storage.getItem(this.key()) || "null");
415
+ if (parsed?.version === 2) {
416
+ const snapshot = parsed.snapshot;
417
+ if (snapshot && Array.isArray(snapshot.items) && snapshot.items.every(isCartItem) && (parsed.reference === null || isReference(parsed.reference))) this.cache = {
418
+ version: 2,
419
+ reference: parsed.reference,
420
+ snapshot: cloneSnapshot({
421
+ items: snapshot.items,
422
+ discount: isCartDiscount(snapshot.discount) ? snapshot.discount : null
423
+ })
424
+ };
425
+ } else if (parsed?.version === 1 && Array.isArray(parsed.items) && parsed.items.every(isCartItem)) this.cache = {
426
+ version: 2,
427
+ reference: null,
428
+ snapshot: cloneSnapshot({
300
429
  items: parsed.items,
301
430
  discount: isCartDiscount(parsed.discount) ? parsed.discount : null
302
- };
431
+ })
432
+ };
433
+ if (!this.cache.reference) {
434
+ const legacy = JSON.parse(storage.getItem(`caldik:server-cart:${this.client.storeContextId()}`) || "null");
435
+ if (isReference(legacy)) this.cache.reference = legacy;
303
436
  }
304
437
  } catch {}
305
- return this.memory;
306
438
  }
307
- write(state) {
308
- this.memory = {
309
- version: CART_VERSION,
310
- ...cloneSnapshot(state)
311
- };
312
- const key = this.storageKey();
313
- if (key && typeof localStorage !== "undefined") try {
314
- localStorage.setItem(key, JSON.stringify(this.memory));
439
+ persistCache() {
440
+ try {
441
+ const storage = this.storage();
442
+ storage?.setItem(this.key(), JSON.stringify(this.cache));
443
+ storage?.removeItem(`caldik:server-cart:${this.client.storeContextId()}`);
315
444
  } catch {}
316
- return this.get();
445
+ }
446
+ write(cart, token = this.cache.reference?.token, discount = null) {
447
+ this.cache = {
448
+ version: 2,
449
+ reference: {
450
+ id: cart.id,
451
+ token: cart.customerId ? null : token
452
+ },
453
+ snapshot: cloneSnapshot({
454
+ items: cart.items,
455
+ discount
456
+ })
457
+ };
458
+ this.persistCache();
459
+ return cloneSnapshot(this.cache.snapshot);
460
+ }
461
+ enqueue(operation) {
462
+ const run = () => {
463
+ const locks = this.storage() && typeof navigator !== "undefined" ? navigator.locks : void 0;
464
+ return locks ? locks.request(this.key(), operation) : operation();
465
+ };
466
+ const result = this.queue.then(run, run);
467
+ this.queue = result.catch(() => void 0);
468
+ return result;
469
+ }
470
+ options() {
471
+ return {
472
+ access: "public",
473
+ ...this.cache.reference?.token ? { headers: { "X-Caldik-Cart-Token": this.cache.reference.token } } : {}
474
+ };
475
+ }
476
+ async resolve(checkout = false, seed = true) {
477
+ this.readCache();
478
+ const input = this.cache.reference ? { cartId: this.cache.reference.id } : seed && this.cache.snapshot.items.length ? {
479
+ items: commerceItems(this.cache.snapshot.items),
480
+ discountCode: this.cache.snapshot.discount?.code
481
+ } : {};
482
+ const response = await this.client.post("/carts/session", {
483
+ ...input,
484
+ ...checkout ? { checkout: true } : {}
485
+ }, this.options());
486
+ this.initialized = true;
487
+ this.write(response.cart, response.token ?? null);
488
+ if (!checkout && response.cart.status === "checked_out") {
489
+ this.reset();
490
+ return this.resolve(false, false);
491
+ }
492
+ return response.cart;
493
+ }
494
+ reset() {
495
+ this.cache = {
496
+ version: 2,
497
+ reference: null,
498
+ snapshot: {
499
+ items: [],
500
+ discount: null
501
+ }
502
+ };
503
+ this.persistCache();
504
+ }
505
+ async mutate(operation, seed = true) {
506
+ let cart = await this.resolve(false, seed);
507
+ let response;
508
+ try {
509
+ response = await operation(cart);
510
+ } catch (error) {
511
+ if (!(error instanceof CaldikHttpError) || error.status !== 409) throw error;
512
+ const refreshed = await this.resolve();
513
+ if (refreshed.id !== cart.id || refreshed.status !== "active") throw error;
514
+ cart = refreshed;
515
+ response = await operation(cart);
516
+ }
517
+ return this.write(response.cart);
518
+ }
519
+ save(cart, items, discountCode) {
520
+ return this.client.post(`/carts/${cart.id}`, {
521
+ items: commerceItems(items),
522
+ version: cart.version,
523
+ discountCode
524
+ }, this.options());
525
+ }
526
+ quote(cart, code) {
527
+ if (!cart.items.length) throw new Error("Cannot check a discount on an empty cart");
528
+ return this.client.post("/discounts/check", {
529
+ code,
530
+ items: commerceItems(cart.items)
531
+ }, { access: "public" });
317
532
  }
318
533
  get() {
319
- return cloneSnapshot(this.read());
534
+ return this.enqueue(async () => {
535
+ let cart = await this.resolve();
536
+ let discount = null;
537
+ if (cart.discountCode) try {
538
+ discount = discountFromQuote(await this.quote(cart, cart.discountCode));
539
+ } catch (error) {
540
+ if (!(error instanceof CaldikHttpError) || ![
541
+ 400,
542
+ 404,
543
+ 422
544
+ ].includes(error.status)) throw error;
545
+ cart = (await this.save(cart, cart.items, null)).cart;
546
+ }
547
+ return this.write(cart, this.cache.reference?.token, discount);
548
+ });
320
549
  }
321
550
  add(item) {
322
- const productId = item.productId.trim();
323
- const productVariantId = item.productVariantId?.trim() || "";
324
- if (!productId) throw new Error("Cart productId is required");
325
- const state = this.get();
326
- const id = cartItemId({
327
- productId,
328
- productVariantId
551
+ return this.enqueue(async () => {
552
+ if (!item.productId.trim()) throw new Error("Cart productId is required");
553
+ const normalized = {
554
+ ...item,
555
+ productId: item.productId.trim(),
556
+ quantity: quantity(item.quantity)
557
+ };
558
+ return this.mutate((cart) => this.client.post(`/carts/${cart.id}/items`, {
559
+ item: normalized,
560
+ version: cart.version
561
+ }, this.options()));
329
562
  });
330
- const existing = state.items.find((entry) => entry.id === id);
331
- const addedQuantity = quantity(item.quantity);
332
- if (existing) existing.quantity = quantity(existing.quantity + addedQuantity);
333
- else state.items.push({
334
- id,
335
- productId,
336
- ...productVariantId ? { productVariantId } : {},
337
- quantity: addedQuantity
338
- });
339
- state.discount = null;
340
- return this.write(state);
341
563
  }
342
564
  updateQuantity(itemId, nextQuantity) {
343
- const state = this.get();
344
- const normalized = quantity(nextQuantity, true);
345
- const index = state.items.findIndex((item) => item.id === itemId);
346
- if (index === -1) throw new Error("Cart item not found");
347
- if (normalized === 0) state.items.splice(index, 1);
348
- else state.items[index].quantity = normalized;
349
- state.discount = null;
350
- return this.write(state);
565
+ return this.enqueue(async () => {
566
+ const normalized = quantity(nextQuantity, true);
567
+ return this.mutate((cart) => {
568
+ if (!cart.items.some((item) => item.id === itemId)) throw new Error("Cart item not found");
569
+ const items = cart.items.map((item) => item.id === itemId ? {
570
+ ...item,
571
+ quantity: normalized
572
+ } : item).filter((item) => item.quantity > 0);
573
+ return this.save(cart, items, null);
574
+ });
575
+ });
351
576
  }
352
577
  remove(itemId) {
353
- const state = this.get();
354
- const items = state.items.filter((item) => item.id !== itemId);
355
- if (items.length === state.items.length) throw new Error("Cart item not found");
356
- return this.write({
357
- items,
358
- discount: null
359
- });
578
+ return this.updateQuantity(itemId, 0);
360
579
  }
361
580
  clear() {
362
- return this.write({
363
- items: [],
364
- discount: null
581
+ return this.enqueue(() => this.mutate((cart) => this.save(cart, [], null), false));
582
+ }
583
+ checkDiscount(code) {
584
+ return this.enqueue(async () => this.quote(await this.resolve(), code));
585
+ }
586
+ applyDiscount(code) {
587
+ return this.enqueue(async () => {
588
+ let quote;
589
+ const snapshot = await this.mutate(async (cart) => {
590
+ quote = await this.quote(cart, code);
591
+ return this.save(cart, cart.items, quote.discount.code);
592
+ });
593
+ snapshot.discount = discountFromQuote(quote);
594
+ this.cache.snapshot = cloneSnapshot(snapshot);
595
+ this.persistCache();
596
+ return snapshot;
365
597
  });
366
598
  }
367
- commerceItems() {
368
- return this.get().items.map(({ productId, productVariantId, quantity }) => ({
369
- productId,
370
- ...productVariantId ? { productVariantId } : {},
371
- quantity
372
- }));
373
- }
374
- async checkDiscount(code) {
375
- const items = this.commerceItems();
376
- if (items.length === 0) throw new Error("Cannot check a discount on an empty cart");
377
- return this.client.post("/discounts/check", {
378
- code,
379
- items
380
- }, { access: "public" });
381
- }
382
- async applyDiscount(code) {
383
- const quote = await this.checkDiscount(code);
384
- const state = this.get();
385
- state.discount = {
386
- ...quote.discount,
387
- subtotal: quote.subtotal,
388
- discountAmount: quote.discountAmount,
389
- total: quote.total,
390
- currency: quote.currency
391
- };
392
- return this.write(state);
393
- }
394
599
  removeDiscount() {
395
- const state = this.get();
396
- state.discount = null;
397
- return this.write(state);
398
- }
399
- async checkout(input) {
400
- const state = this.get();
401
- if (state.items.length === 0) throw new Error("Cannot checkout an empty cart");
402
- const response = await this.client.post("/checkouts", {
403
- ...input,
404
- items: this.commerceItems(),
405
- ...state.discount ? { discountCode: state.discount.code } : {}
406
- }, { access: "public" });
407
- this.clear();
408
- return response;
600
+ return this.enqueue(() => this.mutate((cart) => this.save(cart, cart.items, null)));
601
+ }
602
+ checkout(input = {}) {
603
+ return this.enqueue(async () => {
604
+ const cart = await this.resolve(true);
605
+ if (!cart.items.length) throw new Error("Cannot checkout an empty cart");
606
+ const response = await this.client.post(`/carts/${cart.id}/checkout`, {
607
+ ...input,
608
+ version: cart.version
609
+ }, this.options());
610
+ this.reset();
611
+ return response;
612
+ });
409
613
  }
410
614
  };
411
615
  //#endregion
@@ -466,7 +670,9 @@ var CaldikClient = class {
466
670
  const client = new CaldikHttpClient({
467
671
  apiKey: options.apiKey,
468
672
  apiUrl: options.apiUrl,
469
- storeId: options.storeId
673
+ storeId: options.storeId,
674
+ request: options.request,
675
+ onSetCookie: options.onSetCookie
470
676
  });
471
677
  this.categories = new CategoriesApi(client);
472
678
  this.products = new ProductsApi(client);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caldik/client",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.mts",