@caldik/client 0.1.5 → 0.1.7

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
@@ -165,6 +165,10 @@ The following fields are omitted by default unless explicitly requested using `i
165
165
 
166
166
  ### Cart Management
167
167
 
168
+ The SDK manages the `session` cookie and server persistence automatically. Cart
169
+ requests, responses, and saved browser state do not require version numbers.
170
+ Existing saved carts are restored automatically after upgrading.
171
+
168
172
  **Get the cart**
169
173
 
170
174
  Refresh the current items and applied discount from the server.
@@ -263,6 +267,10 @@ a storefront API proxy). Cookies set only on a separate API hostname are not
263
267
  included in requests to the storefront server. A private store key does not
264
268
  replace a shopper's customer session.
265
269
 
270
+ Customer and cart cookies are scoped to the storefront origin. When you pass
271
+ `request`, the SDK forwards its `Origin` header, or the request URL's origin when
272
+ that header is absent, so server calls use the same session as the storefront.
273
+
266
274
  ### Checkout with Explicit Items
267
275
 
268
276
  Use `caldik.checkout.create()` to create an order from explicitly provided items.
@@ -366,14 +374,89 @@ Omit `rememberMe` to use the default session duration.
366
374
 
367
375
  ### Session Check
368
376
 
369
- Retrieve the current customer's authentication status, account details, and session expiration.
377
+ Check whether the shopper is authenticated. HTTP 200 has no body and resolves to
378
+ `undefined`; HTTP 401 rejects with `CaldikHttpError`. Customer details are returned
379
+ by `me()`.
380
+
381
+ ```ts
382
+ await caldik.customer.session()
383
+ ```
384
+
385
+ ### Current Customer
386
+
387
+ **Get the signed-in customer**
388
+
389
+ ```ts
390
+ const { customer } = await caldik.customers.me()
391
+ console.log(customer.name, customer.email)
392
+ ```
393
+
394
+ **List the customer's orders**
395
+
396
+ ```ts
397
+ const { orders, nextCursor } = await caldik.customers.orders.list({ limit: 5 })
398
+ ```
399
+
400
+ **Read the next page**
401
+
402
+ ```ts
403
+ if (nextCursor) {
404
+ const nextPage = await caldik.customers.orders.list({ cursor: nextCursor })
405
+ }
406
+ ```
407
+
408
+ **Get one of the customer's orders**
370
409
 
371
410
  ```ts
372
- const {
373
- authenticated,
374
- customer,
375
- expiresAt,
376
- } = await caldik.customer.session()
411
+ const { order } = await caldik.customers.orders.get("ORDER_...")
412
+ console.log(order.items)
413
+ ```
414
+
415
+ **Get the customer's balance**
416
+
417
+ ```ts
418
+ const { balance } = await caldik.customers.balance.get()
419
+ console.log(balance.amount, balance.currency)
420
+ ```
421
+
422
+ The default currency is the store's currency. Pass `{ currency: "USD" }` to read
423
+ another balance. Amounts are decimal strings; a missing balance is `"0.00"`.
424
+
425
+ ### Batch Reads
426
+
427
+ **Fetch several resources in one HTTP request**
428
+
429
+ ```ts
430
+ const data = await caldik.batch({
431
+ customer: caldik.customers.me(),
432
+ orders: caldik.orders.list({ limit: 5 }),
433
+ balance: caldik.customers.balance.get(),
434
+ rank: caldik.plugins.loyalty.getRank(),
435
+ })
436
+
437
+ console.log(data.customer.customer.email)
438
+ console.log(data.rank.rank?.name)
439
+ ```
440
+
441
+ Batches accept up to 25 distinct, unstarted read requests from the same client.
442
+ Build the requests in the batch expression; already awaited requests cannot be
443
+ batched. Catalog reads and read-only plugin methods are also supported. Each
444
+ result retains the same shape as an individual call, including `undefined` for
445
+ a successful session check. Batches are independent reads, not a transaction.
446
+
447
+ **Handle a partially failed batch**
448
+
449
+ ```ts
450
+ import { CaldikBatchError } from "@caldik/client"
451
+
452
+ try {
453
+ const data = await caldik.batch({ customer: caldik.customers.me() })
454
+ } catch (error) {
455
+ if (error instanceof CaldikBatchError) {
456
+ console.log(error.errors) // HTTP status and response for each failed key
457
+ console.log(error.results) // successful results remain available
458
+ }
459
+ }
377
460
  ```
378
461
 
379
462
  ### Password Reset
@@ -0,0 +1,17 @@
1
+ //#region development/refresh-plugin-types.d.mts
2
+ type PluginContractFetcher = (url: URL, options: RequestInit) => Promise<Response>;
3
+ declare function refreshPluginTypes(options: {
4
+ storeId: string;
5
+ apiUrl?: string;
6
+ output?: string;
7
+ projectRoot?: string;
8
+ signal?: AbortSignal;
9
+ timeoutMs?: number;
10
+ fetcher?: PluginContractFetcher;
11
+ }): Promise<{
12
+ changed: boolean;
13
+ output: string;
14
+ types: string;
15
+ }>;
16
+ //#endregion
17
+ export { type PluginContractFetcher, refreshPluginTypes };
@@ -0,0 +1,153 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ //#region development/plugin-types.mjs
6
+ const types = /* @__PURE__ */ new Set([
7
+ "object",
8
+ "array",
9
+ "string",
10
+ "number",
11
+ "integer",
12
+ "boolean",
13
+ "null"
14
+ ]);
15
+ const safeKey = (value) => typeof value === "string" && /^[a-zA-Z][a-zA-Z0-9_\-]{0,63}$/.test(value) && ![
16
+ "constructor",
17
+ "prototype",
18
+ "then"
19
+ ].includes(value);
20
+ function schemaType(schema, depth = 0) {
21
+ if (depth > 8 || !schema || typeof schema !== "object" || Array.isArray(schema)) throw new Error("Invalid plugin type schema");
22
+ if (schema.enum) {
23
+ if (!Array.isArray(schema.enum) || !schema.enum.length || schema.enum.length > 64 || schema.enum.some((value) => value !== null && ![
24
+ "string",
25
+ "number",
26
+ "boolean"
27
+ ].includes(typeof value))) throw new Error("Invalid plugin enum");
28
+ return schema.enum.map((value) => JSON.stringify(value)).join(" | ");
29
+ }
30
+ if (schema.anyOf) {
31
+ if (!Array.isArray(schema.anyOf) || !schema.anyOf.length || schema.anyOf.length > 8) throw new Error("Invalid plugin union");
32
+ return schema.anyOf.map((value) => `(${schemaType(value, depth + 1)})`).join(" | ");
33
+ }
34
+ if (!types.has(schema.type)) throw new Error("Unsupported plugin type");
35
+ if (schema.type === "array") return `Array<${schemaType(schema.items, depth + 1)}>`;
36
+ if (schema.type === "object") {
37
+ const properties = schema.properties ?? {};
38
+ if (!properties || typeof properties !== "object" || Array.isArray(properties) || Object.keys(properties).length > 32) throw new Error("Invalid plugin object");
39
+ const required = schema.required ?? [];
40
+ if (!Array.isArray(required) || !required.every((value) => typeof value === "string" && Object.hasOwn(properties, value))) throw new Error("Invalid required properties");
41
+ return `{ ${Object.entries(properties).map(([name, value]) => `${JSON.stringify(name)}${required.includes(name) ? "" : "?"}: ${schemaType(value, depth + 1)};`).join(" ")} }`;
42
+ }
43
+ return schema.type === "integer" ? "number" : schema.type;
44
+ }
45
+ function generatePluginTypes(metadata) {
46
+ if (!metadata || !Array.isArray(metadata.plugins) || metadata.plugins.length > 100) throw new Error("Invalid installed plugin metadata");
47
+ const names = /* @__PURE__ */ new Set();
48
+ const lines = [
49
+ "// Generated by Caldik. Plugin contracts refresh automatically during caldik dev.",
50
+ "import type { CaldikRequest } from \"@caldik/client\";",
51
+ "",
52
+ "declare module \"@caldik/client\" {",
53
+ " interface CaldikPlugins {"
54
+ ];
55
+ for (const plugin of [...metadata.plugins].sort((a, b) => String(a.namespace).localeCompare(String(b.namespace)))) {
56
+ if (!safeKey(plugin.namespace) || names.has(plugin.namespace) || !plugin.methods || typeof plugin.methods !== "object" || Array.isArray(plugin.methods) || Object.keys(plugin.methods).length > 16) throw new Error("Invalid plugin namespace");
57
+ names.add(plugin.namespace);
58
+ lines.push(` readonly ${JSON.stringify(plugin.namespace)}: {`);
59
+ for (const [name, method] of Object.entries(plugin.methods).sort(([a], [b]) => a.localeCompare(b))) {
60
+ if (!safeKey(name) || !method || typeof method !== "object") throw new Error("Invalid plugin method");
61
+ const input = schemaType(method.input);
62
+ const output = schemaType(method.output);
63
+ const empty = method.input.type === "object" && !Object.keys(method.input.properties ?? {}).length;
64
+ const optional = method.input.type === "object" && !method.input.required?.length;
65
+ lines.push(` readonly ${JSON.stringify(name)}: (${empty ? "" : `input${optional ? "?" : ""}: ${input}`}) => CaldikRequest<${output}>;`);
66
+ }
67
+ lines.push(" };");
68
+ }
69
+ lines.push(" }", "}", "");
70
+ return lines.join("\n");
71
+ }
72
+ const packageTypesPath = resolve(fileURLToPath(new URL("../", import.meta.url)), "types/plugins.d.ts");
73
+ const generatedPrefix = "// Generated by Caldik.";
74
+ async function readOptional(path) {
75
+ try {
76
+ return await readFile(path, "utf8");
77
+ } catch (error) {
78
+ if (error.code === "ENOENT") return "";
79
+ throw error;
80
+ }
81
+ }
82
+ /** Replace the directory entry so Bun's hard-linked package cache is never modified. */
83
+ async function writeGeneratedTypes(output, types) {
84
+ const previous = await readOptional(output);
85
+ if (previous && !previous.startsWith(generatedPrefix)) throw new Error("Output contains handwritten declarations; choose another output path");
86
+ if (previous === types) return false;
87
+ await mkdir(dirname(output), { recursive: true });
88
+ const temporary = `${output}.${randomUUID()}.tmp`;
89
+ try {
90
+ await writeFile(temporary, types, "utf8");
91
+ await rename(temporary, output);
92
+ } finally {
93
+ await rm(temporary, { force: true });
94
+ }
95
+ return true;
96
+ }
97
+ async function responseText(response) {
98
+ const reader = response.body?.getReader();
99
+ if (!reader) throw new Error("Empty plugin contract response");
100
+ const chunks = [];
101
+ let bytes = 0;
102
+ try {
103
+ for (;;) {
104
+ const { value, done } = await reader.read();
105
+ if (done) break;
106
+ bytes += value.byteLength;
107
+ if (bytes > 1048576) throw new Error("Plugin contracts exceed 1 MiB");
108
+ chunks.push(value);
109
+ }
110
+ } finally {
111
+ await reader.cancel().catch(() => void 0);
112
+ }
113
+ return Buffer.concat(chunks).toString("utf8");
114
+ }
115
+ async function refreshPluginTypes({ storeId, apiUrl = "https://api.caldik.com", output = packageTypesPath, projectRoot, signal, timeoutMs = 5e3, fetcher = fetch }) {
116
+ signal?.throwIfAborted();
117
+ if (typeof storeId !== "string" || !/^STORE_[a-f0-9]{32}$/i.test(storeId)) throw new Error("The development store ID is invalid");
118
+ const base = new URL(apiUrl);
119
+ if (!["https:", "http:"].includes(base.protocol) || base.username || base.password || base.search || base.hash) throw new Error("API URL must be an HTTP(S) base URL without credentials, query or fragment");
120
+ const url = new URL(`${base.href.replace(/\/+$/, "")}/v1/stores/${encodeURIComponent(storeId)}/plugins/sdk`);
121
+ output = resolve(output);
122
+ if (!output.endsWith(".d.ts")) throw new Error("Plugin types must use a .d.ts output path");
123
+ const writeTypes = async (types) => {
124
+ const changed = await writeGeneratedTypes(output, types);
125
+ if (projectRoot && output === packageTypesPath) {
126
+ const snapshot = resolve(projectRoot, await stat(resolve(projectRoot, "src")).then((value) => value.isDirectory()).catch(() => false) ? "src/caldik.plugins.d.ts" : "caldik.plugins.d.ts");
127
+ const current = await readOptional(snapshot);
128
+ if (!current || current.startsWith(generatedPrefix)) await writeGeneratedTypes(snapshot, types);
129
+ }
130
+ return changed;
131
+ };
132
+ const context = `// Context: ${createHash("sha256").update(url.href).digest("hex")}\n`;
133
+ const previous = await readOptional(output);
134
+ if (previous.startsWith(generatedPrefix) && previous.includes("// Context: ") && !previous.includes(context)) await writeTypes(generatePluginTypes({ plugins: [] }));
135
+ const requestSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs);
136
+ const response = await fetcher(url, {
137
+ signal: requestSignal,
138
+ credentials: "omit",
139
+ headers: { Accept: "application/json" }
140
+ });
141
+ if (!response.ok) throw new Error(`Plugin contract request failed (HTTP ${response.status})`);
142
+ const metadata = JSON.parse(await responseText(response));
143
+ if (metadata.success !== true || metadata.storeId !== storeId) throw new Error("The API returned invalid contracts or contracts for another store");
144
+ const types = generatePluginTypes(metadata).replace(/\n/, `\n${context}`);
145
+ requestSignal.throwIfAborted();
146
+ return {
147
+ changed: await writeTypes(types),
148
+ output,
149
+ types
150
+ };
151
+ }
152
+ //#endregion
153
+ export { refreshPluginTypes };
package/dist/index.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ /// <reference path="../types/plugins.d.ts" />
1
2
  //#region src/payment-providers.generated.d.ts
2
3
  declare const paymentProviderIds: readonly ["customer_balance", "stripe", "ovgc"];
3
4
  declare const checkoutPaymentProviderIds: readonly ["customer_balance", "stripe", "ovgc"];
@@ -118,6 +119,7 @@ interface Product {
118
119
  deliveryType: string;
119
120
  minimumQuantity: number | null;
120
121
  maximumQuantity: number | null;
122
+ images: ProductImage[];
121
123
  variants: ProductVariant[];
122
124
  tags: ProductTag[];
123
125
  customFields: ProductCustomField[];
@@ -129,6 +131,13 @@ interface ProductVariant {
129
131
  position: number;
130
132
  price: string;
131
133
  compareAtPrice: string | null;
134
+ images: ProductImage[];
135
+ }
136
+ interface ProductImage {
137
+ id: string;
138
+ url: string;
139
+ alt: string | null;
140
+ position: number;
132
141
  }
133
142
  interface ProductTag {
134
143
  label: string;
@@ -192,9 +201,10 @@ interface StorefrontOrder {
192
201
  subtotal: string;
193
202
  amount: string;
194
203
  discount: {
195
- code: string;
204
+ code: string | null;
196
205
  percentage: string;
197
206
  amount: string;
207
+ source: "code" | "loyalty";
198
208
  } | null;
199
209
  currency: string;
200
210
  }
@@ -286,15 +296,7 @@ interface CustomerEmailLoginInput {
286
296
  email: string;
287
297
  rememberMe?: boolean;
288
298
  }
289
- interface CustomerSessionResponse {
290
- success: boolean;
291
- authenticated: boolean;
292
- customer: {
293
- id: string;
294
- email: string | null;
295
- } | null;
296
- expiresAt: number | null;
297
- }
299
+ type CustomerSessionResponse = void;
298
300
  interface CustomerPasswordResetResponse extends CustomerLoginResponse {
299
301
  expiresAt: number;
300
302
  message: string;
@@ -322,14 +324,72 @@ interface CustomerProfileResponse {
322
324
  success: boolean;
323
325
  customer: {
324
326
  id: string;
327
+ shortId: string;
328
+ displayId: string;
329
+ name: string | null;
325
330
  email: string | null;
326
- extensions?: Record<string, Record<string, string | number | boolean>>;
331
+ phone: string | null;
332
+ createdAt: number;
333
+ updatedAt: number;
334
+ extensions: Record<string, Record<string, string | number | boolean>>;
327
335
  };
328
- balances: Array<{
336
+ }
337
+ interface CustomerBalanceResponse {
338
+ success: boolean;
339
+ balance: {
329
340
  currency: string;
330
341
  amount: string;
342
+ };
343
+ }
344
+ interface CustomerOrderListOptions {
345
+ limit?: number;
346
+ cursor?: string;
347
+ }
348
+ interface CustomerOrder {
349
+ id: string;
350
+ shortId: string;
351
+ displayId: string;
352
+ paymentStatus: string;
353
+ fulfillmentStatus: string;
354
+ amount: string;
355
+ subtotal: string;
356
+ currency: string;
357
+ createdAt: number;
358
+ updatedAt: number;
359
+ discount: {
360
+ code: string | null;
361
+ percentage: string;
362
+ amount: string;
363
+ source: "code" | "loyalty";
364
+ } | null;
365
+ }
366
+ interface CustomerOrderItem {
367
+ id: string;
368
+ productId: string | null;
369
+ productVariantId: string | null;
370
+ name: string;
371
+ description: string | null;
372
+ quantity: number;
373
+ unitAmount: string;
374
+ totalAmount: string;
375
+ customFields: Array<{
376
+ id: string;
377
+ name: string;
378
+ inputType: string;
379
+ value: string | number | boolean;
331
380
  }>;
332
381
  }
382
+ interface CustomerOrdersResponse {
383
+ success: boolean;
384
+ orders: CustomerOrder[];
385
+ nextCursor: string | null;
386
+ }
387
+ interface CustomerOrderResponse {
388
+ success: boolean;
389
+ order: CustomerOrder & {
390
+ items: CustomerOrderItem[];
391
+ };
392
+ }
333
393
  interface CustomerLoginSettings {
334
394
  passwordEnabled: boolean;
335
395
  }
@@ -365,6 +425,8 @@ interface CheckoutPaymentMethodsResponse {
365
425
  //#endregion
366
426
  //#region src/internal/types.d.ts
367
427
  interface RequestOptions {
428
+ /** Response intentionally has no JSON body. */
429
+ empty?: boolean;
368
430
  edgeCache?: boolean;
369
431
  /** Internal authorization policy. Protected hosted calls are the default. */
370
432
  access?: "public" | "runtime";
@@ -372,6 +434,29 @@ interface RequestOptions {
372
434
  headers?: Record<string, string>;
373
435
  }
374
436
  //#endregion
437
+ //#region src/internal/request.d.ts
438
+ interface RequestDescriptor {
439
+ method: "GET" | "POST";
440
+ path: string;
441
+ params?: Record<string, string | number>;
442
+ input?: unknown;
443
+ empty?: boolean;
444
+ }
445
+ /** Normal Promise scheduled on the next microtask so synchronous calls can join a batch. */
446
+ declare class CaldikRequest<T> extends Promise<T> {
447
+ readonly owner: object;
448
+ readonly descriptor: RequestDescriptor;
449
+ static get [Symbol.species](): PromiseConstructor;
450
+ private dispatched;
451
+ private bound;
452
+ private resolveRequest;
453
+ private rejectRequest;
454
+ constructor(owner: object, descriptor: RequestDescriptor, execute: () => Promise<T>);
455
+ get started(): boolean;
456
+ cancel(error: unknown): void;
457
+ bind(promise: Promise<T>): void;
458
+ }
459
+ //#endregion
375
460
  //#region src/internal/client.d.ts
376
461
  type HttpClientOptions = {
377
462
  apiKey?: string;
@@ -380,12 +465,17 @@ type HttpClientOptions = {
380
465
  request?: Request;
381
466
  onSetCookie?: (cookie: string) => void | Promise<void>;
382
467
  };
468
+ declare class CaldikHttpError extends Error {
469
+ readonly status: number;
470
+ constructor(message: string, status: number);
471
+ }
383
472
  declare class CaldikHttpClient {
384
473
  private readonly apiKey?;
385
474
  private readonly apiUrl;
386
475
  private readonly storeId?;
387
476
  private readonly cookies;
388
477
  private readonly onSetCookie?;
478
+ private readonly storefrontOrigin?;
389
479
  private readonly authenticationListeners;
390
480
  constructor(options?: HttpClientOptions);
391
481
  storeContextId(): string | null;
@@ -395,59 +485,75 @@ declare class CaldikHttpClient {
395
485
  private captureCookies;
396
486
  private baseUrl;
397
487
  private request;
398
- get<T>(path: string, params?: Record<string, string | number>, options?: RequestOptions): Promise<T>;
399
- post<T>(path: string, input?: unknown, options?: RequestOptions): Promise<T>;
488
+ get<T>(path: string, params?: Record<string, string | number>, options?: RequestOptions): CaldikRequest<T>;
489
+ post<T>(path: string, input?: unknown, options?: RequestOptions): CaldikRequest<T>;
400
490
  }
401
491
  //#endregion
492
+ //#region src/batch.d.ts
493
+ type BatchInput = Record<string, CaldikRequest<any>>;
494
+ type BatchResult<T extends BatchInput> = { [K in keyof T]: Awaited<T[K]> };
495
+ declare class CaldikBatchError extends Error {
496
+ readonly results: Record<string, unknown>;
497
+ readonly errors: Record<string, {
498
+ status: number;
499
+ body: unknown;
500
+ }>;
501
+ constructor(results: Record<string, unknown>, errors: Record<string, {
502
+ status: number;
503
+ body: unknown;
504
+ }>);
505
+ }
506
+ declare function batchRequests<T extends BatchInput>(client: CaldikHttpClient, input: T): Promise<BatchResult<T>>;
507
+ //#endregion
402
508
  //#region src/categories.d.ts
403
509
  declare class CategoriesApi {
404
510
  private client;
405
511
  constructor(client: CaldikHttpClient);
406
- list(options?: number | CategoryListOptions): Promise<CategoriesResponse>;
407
- get(categoryId: string): Promise<CategoryResponse>;
512
+ list(options?: number | CategoryListOptions): CaldikRequest<CategoriesResponse>;
513
+ get(categoryId: string): CaldikRequest<CategoryResponse>;
408
514
  }
409
515
  //#endregion
410
516
  //#region src/products.d.ts
411
517
  declare class ProductsApi {
412
518
  private client;
413
519
  constructor(client: CaldikHttpClient);
414
- list(): Promise<ProductsResponse>;
415
- get(productIdOrSlug: string, options?: ProductGetOptions): Promise<ProductResponse>;
520
+ list(): CaldikRequest<ProductsResponse>;
521
+ get(productIdOrSlug: string, options?: ProductGetOptions): CaldikRequest<ProductResponse>;
416
522
  }
417
523
  //#endregion
418
524
  //#region src/reviews.d.ts
419
525
  declare class ReviewsApi {
420
526
  private client;
421
527
  constructor(client: CaldikHttpClient);
422
- list(limit?: number): Promise<ReviewsResponse>;
528
+ list(limit?: number): CaldikRequest<ReviewsResponse>;
423
529
  }
424
530
  //#endregion
425
531
  //#region src/store.d.ts
426
532
  declare class StoreApi {
427
533
  private client;
428
534
  constructor(client: CaldikHttpClient);
429
- get(): Promise<StoreResponse>;
535
+ get(): CaldikRequest<StoreResponse>;
430
536
  }
431
537
  //#endregion
432
538
  //#region src/checkout.d.ts
433
539
  declare class CheckoutApi {
434
540
  private client;
435
541
  readonly paymentmethods: {
436
- list: () => Promise<CheckoutPaymentMethodsResponse>;
542
+ list: () => CaldikRequest<CheckoutPaymentMethodsResponse>;
437
543
  };
438
544
  readonly settings: {
439
- get: () => Promise<CheckoutSettingsResponse>;
545
+ get: () => CaldikRequest<CheckoutSettingsResponse>;
440
546
  };
441
547
  constructor(client: CaldikHttpClient);
442
- create(input: CreateOrderInput): Promise<OrderResponse>;
443
- payWithBalance(orderId: string): Promise<OrderResponse>;
548
+ create(input: CreateOrderInput): CaldikRequest<OrderResponse>;
549
+ payWithBalance(orderId: string): CaldikRequest<OrderResponse>;
444
550
  }
445
551
  //#endregion
446
552
  //#region src/customer.d.ts
447
553
  declare class CustomerApi {
448
554
  private client;
449
555
  readonly settings: {
450
- get: () => Promise<CustomerLoginSettingsResponse>;
556
+ get: () => CaldikRequest<CustomerLoginSettingsResponse>;
451
557
  };
452
558
  readonly login: {
453
559
  (email: string, options?: {
@@ -468,18 +574,27 @@ declare class CustomerApi {
468
574
  requestReset: (email: string) => Promise<CustomerPasswordResetResponse>;
469
575
  reset: (input: CustomerPasswordResetInput) => Promise<CustomerPasswordResetVerifyResponse>;
470
576
  };
577
+ readonly orders: {
578
+ list: (options?: CustomerOrderListOptions) => CaldikRequest<CustomerOrdersResponse>;
579
+ get: (id: string) => CaldikRequest<CustomerOrderResponse>;
580
+ };
581
+ readonly balance: {
582
+ get: (options?: {
583
+ currency?: string;
584
+ }) => CaldikRequest<CustomerBalanceResponse>;
585
+ };
471
586
  constructor(client: CaldikHttpClient);
472
- /** Check a store-scoped session without raising an error for signed-out customers. */
473
- session(): Promise<CustomerSessionResponse>;
474
- /** Read the signed-in customer and their balances. */
475
- me(): Promise<CustomerProfileResponse>;
587
+ /** Resolves without a body on HTTP 200; rejects with status 401 when signed out. */
588
+ session(): CaldikRequest<void>;
589
+ /** Read the signed-in customer's profile. */
590
+ me(): CaldikRequest<CustomerProfileResponse>;
476
591
  }
477
592
  //#endregion
478
593
  //#region src/subscriptions.d.ts
479
594
  declare class SubscriptionsApi {
480
595
  private client;
481
596
  constructor(client: CaldikHttpClient);
482
- create(input: CreateSubscriptionInput): Promise<SubscriptionResponse>;
597
+ create(input: CreateSubscriptionInput): CaldikRequest<SubscriptionResponse>;
483
598
  }
484
599
  //#endregion
485
600
  //#region src/discounts.d.ts
@@ -502,7 +617,7 @@ interface DiscountQuoteResponse {
502
617
  declare class DiscountsApi {
503
618
  private client;
504
619
  constructor(client: CaldikHttpClient);
505
- check(input: CheckDiscountInput): Promise<DiscountQuoteResponse>;
620
+ check(input: CheckDiscountInput): CaldikRequest<DiscountQuoteResponse>;
506
621
  }
507
622
  //#endregion
508
623
  //#region src/cart.d.ts
@@ -540,7 +655,7 @@ declare class CartApi {
540
655
  private resolve;
541
656
  private reset;
542
657
  private mutate;
543
- private save;
658
+ private saveDiscount;
544
659
  private quote;
545
660
  get(): Promise<CartSnapshot>;
546
661
  add(item: ProductCommerceItem): Promise<CartSnapshot>;
@@ -554,6 +669,8 @@ declare class CartApi {
554
669
  }
555
670
  //#endregion
556
671
  //#region src/index.d.ts
672
+ /** Populated automatically from the store's plugin contracts during caldik dev. */
673
+ interface CaldikPlugins {}
557
674
  interface CaldikClientOptions {
558
675
  /** Store ID override for external servers or local browser development. */
559
676
  storeId?: string;
@@ -572,10 +689,18 @@ declare class CaldikClient {
572
689
  readonly store: StoreApi;
573
690
  readonly checkout: CheckoutApi;
574
691
  readonly customer: CustomerApi;
692
+ readonly customers: CustomerApi;
693
+ readonly orders: {
694
+ list: (options?: CustomerOrderListOptions) => CaldikRequest<CustomerOrdersResponse>;
695
+ get: (id: string) => CaldikRequest<CustomerOrderResponse>;
696
+ };
697
+ readonly plugins: CaldikPlugins;
575
698
  readonly subscriptions: SubscriptionsApi;
576
699
  readonly discounts: DiscountsApi;
577
700
  readonly cart: CartApi;
701
+ private readonly client;
578
702
  constructor(options?: CaldikClientOptions);
703
+ batch<T extends BatchInput>(input: T): Promise<BatchResult<T>>;
579
704
  }
580
705
  //#endregion
581
- 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, ProductGetOptions, ProductMetadata, ProductResponse, ProductTag, ProductVariant, ProductsResponse, Review, ReviewsResponse, Store, StoreResponse, StoreSocials, StoreWebhookEvent, StoreWebhookEventName, StorefrontMetadata, StorefrontOrder, SubscriptionPaymentProviderId, SubscriptionResponse, checkoutPaymentProviderIds, paymentProviderIds, storeWebhookEventNames, subscriptionPaymentProviderIds };
706
+ export { AppliedDiscount, BatchInput, BatchResult, CaldikBatchError, CaldikClient, CaldikClientOptions, CaldikHttpError, CaldikPlugins, CaldikRequest, CartApi, CartCheckoutInput, CartDiscount, CartItem, CartSnapshot, CategoriesResponse, Category, CategoryDetails, CategoryInclude, CategoryListOptions, CategoryMetadata, CategoryProduct, CategoryResponse, CheckDiscountInput, CheckoutPayment, CheckoutPaymentMethod, CheckoutPaymentMethodsResponse, CheckoutPaymentProviderId, CheckoutSettings, CheckoutSettingsResponse, CommerceItem, CreateOrderInput, CreateSubscriptionInput, CustomerBalanceResponse, CustomerEmailLoginInput, CustomerLoginResponse, CustomerLoginSettings, CustomerLoginSettingsResponse, CustomerLoginVerifyResponse, CustomerOrder, CustomerOrderItem, CustomerOrderListOptions, CustomerOrderResponse, CustomerOrdersResponse, CustomerPasswordInput, CustomerPasswordResetInput, CustomerPasswordResetResponse, CustomerPasswordResetVerifyResponse, CustomerPasswordResponse, CustomerProfileResponse, CustomerRegistrationInput, CustomerRegistrationResponse, CustomerSessionResponse, DiscountQuoteResponse, DiscountsApi, ManualCommerceItem, OrderResponse, PaymentProviderId, Product, ProductCommerceItem, ProductCustomField, ProductCustomFieldInputType, ProductCustomFieldOption, ProductGetOptions, ProductImage, ProductMetadata, ProductResponse, ProductTag, ProductVariant, ProductsResponse, Review, ReviewsResponse, Store, StoreResponse, StoreSocials, StoreWebhookEvent, StoreWebhookEventName, StorefrontMetadata, StorefrontOrder, SubscriptionPaymentProviderId, SubscriptionResponse, batchRequests, checkoutPaymentProviderIds, paymentProviderIds, storeWebhookEventNames, subscriptionPaymentProviderIds };
package/dist/index.mjs CHANGED
@@ -23,6 +23,56 @@ function defaultApiUrl(browser) {
23
23
  return environmentApiUrl(browser) || (typeof API_URL !== "undefined" ? API_URL : "https://api.caldik.com");
24
24
  }
25
25
  //#endregion
26
+ //#region src/internal/request.ts
27
+ /** Normal Promise scheduled on the next microtask so synchronous calls can join a batch. */
28
+ var CaldikRequest = class extends Promise {
29
+ owner;
30
+ descriptor;
31
+ static get [Symbol.species]() {
32
+ return Promise;
33
+ }
34
+ dispatched = false;
35
+ bound = false;
36
+ resolveRequest;
37
+ rejectRequest;
38
+ constructor(owner, descriptor, execute) {
39
+ let resolve;
40
+ let reject;
41
+ super((yes, no) => {
42
+ resolve = yes;
43
+ reject = no;
44
+ });
45
+ this.owner = owner;
46
+ this.descriptor = descriptor;
47
+ this.resolveRequest = resolve;
48
+ this.rejectRequest = reject;
49
+ queueMicrotask(() => {
50
+ if (this.bound) return;
51
+ this.dispatched = true;
52
+ try {
53
+ execute().then(resolve, reject);
54
+ } catch (error) {
55
+ reject(error);
56
+ }
57
+ });
58
+ }
59
+ get started() {
60
+ return this.dispatched || this.bound;
61
+ }
62
+ cancel(error) {
63
+ if (this.started) return;
64
+ this.bound = true;
65
+ this.rejectRequest(error);
66
+ this.catch(() => void 0);
67
+ }
68
+ bind(promise) {
69
+ if (this.started) throw new Error("An already started request cannot be batched");
70
+ this.bound = true;
71
+ promise.then(this.resolveRequest, this.rejectRequest);
72
+ this.catch(() => void 0);
73
+ }
74
+ };
75
+ //#endregion
26
76
  //#region src/internal/client.ts
27
77
  const STORE_ID_PATTERN = /^STORE_[a-f0-9]{32}$/i;
28
78
  const STOREFRONT_STORE_ID_ATTRIBUTE = "data-caldik-store-id";
@@ -47,22 +97,19 @@ function browserRuntimeStoreId() {
47
97
  const buildStoreId = browserBuildStoreId();
48
98
  return buildStoreId && STORE_ID_PATTERN.test(buildStoreId) ? buildStoreId : null;
49
99
  }
50
- function isLocalBrowserRuntime() {
51
- if (!isBrowserRuntime() || typeof location === "undefined") return false;
52
- const hostname = location.hostname.toLowerCase();
53
- return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
54
- }
55
100
  var CaldikHttpClient = class {
56
101
  apiKey;
57
102
  apiUrl;
58
103
  storeId;
59
104
  cookies = /* @__PURE__ */ new Map();
60
105
  onSetCookie;
106
+ storefrontOrigin;
61
107
  authenticationListeners = /* @__PURE__ */ new Set();
62
108
  constructor(options = {}) {
63
109
  this.apiKey = options.apiKey?.trim() || void 0;
64
110
  this.apiUrl = options.apiUrl || defaultApiUrl(isBrowserRuntime());
65
111
  this.onSetCookie = options.onSetCookie;
112
+ if (options.request && !isBrowserRuntime()) this.storefrontOrigin = options.request.headers.get("Origin") || new URL(options.request.url).origin;
66
113
  if (!isBrowserRuntime()) for (const part of (options.request?.headers.get("Cookie") || "").split(";")) {
67
114
  const separator = part.indexOf("=");
68
115
  if (separator > 0) this.cookies.set(part.slice(0, separator).trim(), part.slice(separator + 1).trim());
@@ -77,7 +124,9 @@ var CaldikHttpClient = class {
77
124
  return this.storeId || boundRuntimeStoreId();
78
125
  }
79
126
  hasCartSession() {
80
- return this.cookies.has(`caldik_cart_session_${this.storeContextId()}`);
127
+ if (this.cookies.has("session")) return true;
128
+ const prefix = `caldik_cart_session_${this.storeContextId()}`;
129
+ return [...this.cookies.keys()].some((name) => name === prefix || name.startsWith(`${prefix}_`));
81
130
  }
82
131
  onCustomerAuthentication(listener) {
83
132
  this.authenticationListeners.add(listener);
@@ -118,10 +167,11 @@ var CaldikHttpClient = class {
118
167
  const url = new URL(`${this.baseUrl()}${path}`);
119
168
  if (params) for (const [key, value] of Object.entries(params)) url.searchParams.set(key, String(value));
120
169
  const headers = { ...options?.headers };
170
+ if (!isBrowserRuntime() && this.storefrontOrigin) headers.Origin = this.storefrontOrigin;
121
171
  if (!isBrowserRuntime()) headers["User-Agent"] = "CaldikClient/0.0.2";
122
172
  if (!isBrowserRuntime() && options?.access === "public" && this.cookies.size) headers.Cookie = [...this.cookies].map(([name, value]) => `${name}=${value}`).join("; ");
123
173
  if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
124
- else if (options?.access === "public" && (!isBrowserRuntime() || isLocalBrowserRuntime())) headers["X-Caldik-Public-Request"] = "1";
174
+ else if (options?.access === "public") headers["X-Caldik-Public-Request"] = "1";
125
175
  if (input !== void 0) headers["Content-Type"] = "application/json";
126
176
  if (options?.edgeCache) headers["X-Cache-Policy"] = "public";
127
177
  const response = await fetch(url, {
@@ -136,13 +186,24 @@ var CaldikHttpClient = class {
136
186
  const error = await response.clone().json().catch(() => null);
137
187
  throw new CaldikHttpError(typeof error?.message === "string" && error.message ? error.message : `${response.status} ${response.statusText}`.trim(), response.status);
138
188
  }
189
+ if (options?.empty) return void 0;
139
190
  return response.json();
140
191
  }
141
192
  get(path, params, options) {
142
- return this.request("GET", path, void 0, params, options);
193
+ return new CaldikRequest(this, {
194
+ method: "GET",
195
+ path,
196
+ params,
197
+ empty: options?.empty
198
+ }, () => this.request("GET", path, void 0, params, options));
143
199
  }
144
200
  post(path, input, options) {
145
- return this.request("POST", path, input, void 0, options);
201
+ return new CaldikRequest(this, {
202
+ method: "POST",
203
+ path,
204
+ input,
205
+ empty: options?.empty
206
+ }, () => this.request("POST", path, input, void 0, options));
146
207
  }
147
208
  };
148
209
  //#endregion
@@ -225,8 +286,15 @@ var CustomerApi = class {
225
286
  login;
226
287
  register;
227
288
  password;
289
+ orders;
290
+ balance;
228
291
  constructor(client) {
229
292
  this.client = client;
293
+ this.orders = {
294
+ list: (options = {}) => client.get("/customers/orders", { ...options }, { access: "public" }),
295
+ get: (id) => client.get(`/customers/orders/${encodeURIComponent(id)}`, void 0, { access: "public" })
296
+ };
297
+ this.balance = { get: (options = {}) => client.get("/customers/balance", { ...options }, { access: "public" }) };
230
298
  this.settings = { get: () => this.client.get("/customers/settings", void 0, { access: "public" }) };
231
299
  let pendingResetId;
232
300
  this.password = {
@@ -294,11 +362,14 @@ verify: async (code, challengeId = pendingRegistrationId) => {
294
362
  return response;
295
363
  } });
296
364
  }
297
- /** Check a store-scoped session without raising an error for signed-out customers. */
365
+ /** Resolves without a body on HTTP 200; rejects with status 401 when signed out. */
298
366
  session() {
299
- return this.client.get("/customers/session", void 0, { access: "public" });
367
+ return this.client.get("/customers/session", void 0, {
368
+ access: "public",
369
+ empty: true
370
+ });
300
371
  }
301
- /** Read the signed-in customer and their balances. */
372
+ /** Read the signed-in customer's profile. */
302
373
  me() {
303
374
  return this.client.get("/customers/me", void 0, { access: "public" });
304
375
  }
@@ -379,7 +450,6 @@ function discountFromQuote(quote) {
379
450
  var CartApi = class {
380
451
  client;
381
452
  cache = {
382
- version: 2,
383
453
  reference: null,
384
454
  snapshot: {
385
455
  items: [],
@@ -412,18 +482,16 @@ var CartApi = class {
412
482
  if (!storage) return;
413
483
  try {
414
484
  const parsed = JSON.parse(storage.getItem(this.key()) || "null");
415
- if (parsed?.version === 2) {
485
+ if (parsed?.snapshot) {
416
486
  const snapshot = parsed.snapshot;
417
487
  if (snapshot && Array.isArray(snapshot.items) && snapshot.items.every(isCartItem) && (parsed.reference === null || isReference(parsed.reference))) this.cache = {
418
- version: 2,
419
488
  reference: parsed.reference,
420
489
  snapshot: cloneSnapshot({
421
490
  items: snapshot.items,
422
491
  discount: isCartDiscount(snapshot.discount) ? snapshot.discount : null
423
492
  })
424
493
  };
425
- } else if (parsed?.version === 1 && Array.isArray(parsed.items) && parsed.items.every(isCartItem)) this.cache = {
426
- version: 2,
494
+ } else if (parsed && Array.isArray(parsed.items) && parsed.items.every(isCartItem)) this.cache = {
427
495
  reference: null,
428
496
  snapshot: cloneSnapshot({
429
497
  items: parsed.items,
@@ -445,7 +513,6 @@ var CartApi = class {
445
513
  }
446
514
  write(cart, token = this.cache.reference?.token, discount = null) {
447
515
  this.cache = {
448
- version: 2,
449
516
  reference: {
450
517
  id: cart.id,
451
518
  token: cart.customerId ? null : token
@@ -493,7 +560,6 @@ var CartApi = class {
493
560
  }
494
561
  reset() {
495
562
  this.cache = {
496
- version: 2,
497
563
  reference: null,
498
564
  snapshot: {
499
565
  items: [],
@@ -503,25 +569,11 @@ var CartApi = class {
503
569
  this.persistCache();
504
570
  }
505
571
  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
- }
572
+ const response = await operation(await this.resolve(false, seed));
517
573
  return this.write(response.cart);
518
574
  }
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());
575
+ saveDiscount(cart, code) {
576
+ return this.client.post(`/carts/${cart.id}/discount`, { code }, this.options());
525
577
  }
526
578
  quote(cart, code) {
527
579
  if (!cart.items.length) throw new Error("Cannot check a discount on an empty cart");
@@ -542,7 +594,7 @@ var CartApi = class {
542
594
  404,
543
595
  422
544
596
  ].includes(error.status)) throw error;
545
- cart = (await this.save(cart, cart.items, null)).cart;
597
+ cart = (await this.saveDiscount(cart, null)).cart;
546
598
  }
547
599
  return this.write(cart, this.cache.reference?.token, discount);
548
600
  });
@@ -555,10 +607,7 @@ var CartApi = class {
555
607
  productId: item.productId.trim(),
556
608
  quantity: quantity(item.quantity)
557
609
  };
558
- return this.mutate((cart) => this.client.post(`/carts/${cart.id}/items`, {
559
- item: normalized,
560
- version: cart.version
561
- }, this.options()));
610
+ return this.mutate((cart) => this.client.post(`/carts/${cart.id}/items`, { item: normalized }, this.options()));
562
611
  });
563
612
  }
564
613
  updateQuantity(itemId, nextQuantity) {
@@ -566,11 +615,10 @@ var CartApi = class {
566
615
  const normalized = quantity(nextQuantity, true);
567
616
  return this.mutate((cart) => {
568
617
  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,
618
+ return this.client.post(`/carts/${cart.id}/items/quantity`, {
619
+ itemId,
571
620
  quantity: normalized
572
- } : item).filter((item) => item.quantity > 0);
573
- return this.save(cart, items, null);
621
+ }, this.options());
574
622
  });
575
623
  });
576
624
  }
@@ -578,7 +626,7 @@ var CartApi = class {
578
626
  return this.updateQuantity(itemId, 0);
579
627
  }
580
628
  clear() {
581
- return this.enqueue(() => this.mutate((cart) => this.save(cart, [], null), false));
629
+ return this.enqueue(() => this.mutate((cart) => this.client.post(`/carts/${cart.id}/clear`, {}, this.options()), false));
582
630
  }
583
631
  checkDiscount(code) {
584
632
  return this.enqueue(async () => this.quote(await this.resolve(), code));
@@ -588,7 +636,9 @@ var CartApi = class {
588
636
  let quote;
589
637
  const snapshot = await this.mutate(async (cart) => {
590
638
  quote = await this.quote(cart, code);
591
- return this.save(cart, cart.items, quote.discount.code);
639
+ const response = await this.saveDiscount(cart, quote.discount.code);
640
+ quote = await this.quote(response.cart, quote.discount.code);
641
+ return response;
592
642
  });
593
643
  snapshot.discount = discountFromQuote(quote);
594
644
  this.cache.snapshot = cloneSnapshot(snapshot);
@@ -597,22 +647,92 @@ var CartApi = class {
597
647
  });
598
648
  }
599
649
  removeDiscount() {
600
- return this.enqueue(() => this.mutate((cart) => this.save(cart, cart.items, null)));
650
+ return this.enqueue(() => this.mutate((cart) => this.saveDiscount(cart, null)));
601
651
  }
602
652
  checkout(input = {}) {
603
653
  return this.enqueue(async () => {
604
654
  const cart = await this.resolve(true);
605
655
  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());
656
+ const response = await this.client.post(`/carts/${cart.id}/checkout`, input, this.options());
610
657
  this.reset();
611
658
  return response;
612
659
  });
613
660
  }
614
661
  };
615
662
  //#endregion
663
+ //#region src/batch.ts
664
+ var CaldikBatchError = class extends Error {
665
+ results;
666
+ errors;
667
+ constructor(results, errors) {
668
+ super(`Batch requests failed: ${Object.keys(errors).join(", ")}`);
669
+ this.results = results;
670
+ this.errors = errors;
671
+ this.name = "CaldikBatchError";
672
+ }
673
+ };
674
+ function batchRequests(client, input) {
675
+ const entries = Object.entries(input);
676
+ const seen = /* @__PURE__ */ new Set();
677
+ try {
678
+ if (!entries.length || entries.length > 25) throw new Error("A batch must contain between 1 and 25 requests");
679
+ for (const [key, request] of entries) {
680
+ if (!/^[a-zA-Z][a-zA-Z0-9_]{0,63}$/.test(key) || ["constructor", "prototype"].includes(key)) throw new Error("Invalid batch key");
681
+ if (!(request instanceof CaldikRequest) || request.owner !== client || request.started || seen.has(request)) throw new Error("Batch values must be unstarted, distinct requests from the same Caldik client");
682
+ if (request.descriptor.method !== "GET" && !/^\/plugins\/sdk\/[^/]+\/[^/]+$/.test(request.descriptor.path)) throw new Error("Batches support read requests only");
683
+ seen.add(request);
684
+ }
685
+ } catch (error) {
686
+ for (const [, request] of entries) if (request instanceof CaldikRequest) request.cancel(error);
687
+ throw error;
688
+ }
689
+ const response = client.post("/batch", { requests: Object.fromEntries(entries.map(([key, request]) => [key, {
690
+ method: request.descriptor.method,
691
+ path: request.descriptor.path,
692
+ query: request.descriptor.params,
693
+ input: request.descriptor.input
694
+ }])) }, { access: "public" }).then((value) => value.results);
695
+ for (const [key, request] of entries) request.bind(response.then((results) => {
696
+ const result = results[key];
697
+ if (!result) throw new Error(`Missing batch result: ${key}`);
698
+ if (result.status < 200 || result.status >= 300) {
699
+ const body = result.body;
700
+ throw new CaldikHttpError(body?.message || `HTTP ${result.status}`, result.status);
701
+ }
702
+ return request.descriptor.empty ? void 0 : result.body;
703
+ }));
704
+ return response.then((results) => {
705
+ const values = {};
706
+ const errors = {};
707
+ for (const [key, request] of entries) {
708
+ const result = results[key];
709
+ if (!result) throw new Error(`Missing batch result: ${key}`);
710
+ if (result.status >= 200 && result.status < 300) values[key] = request.descriptor.empty ? void 0 : result.body;
711
+ else errors[key] = result;
712
+ }
713
+ if (Object.keys(errors).length) throw new CaldikBatchError(values, errors);
714
+ return values;
715
+ });
716
+ }
717
+ //#endregion
718
+ //#region src/plugins.ts
719
+ function createPluginApi(client) {
720
+ const namespaces = /* @__PURE__ */ new Map();
721
+ const safe = (value) => /^[a-zA-Z][a-zA-Z0-9_\-]{0,63}$/.test(value) && ![
722
+ "constructor",
723
+ "prototype",
724
+ "then"
725
+ ].includes(value);
726
+ return new Proxy(Object.create(null), { get(_target, namespace) {
727
+ if (typeof namespace !== "string" || !safe(namespace)) return void 0;
728
+ if (!namespaces.has(namespace)) namespaces.set(namespace, new Proxy(Object.create(null), { get(_target, method) {
729
+ if (typeof method !== "string" || !safe(method)) return void 0;
730
+ return (input = {}) => client.post(`/plugins/sdk/${encodeURIComponent(namespace)}/${encodeURIComponent(method)}`, input, { access: "public" });
731
+ } }));
732
+ return namespaces.get(namespace);
733
+ } });
734
+ }
735
+ //#endregion
616
736
  //#region src/payment-providers.generated.ts
617
737
  const paymentProviderIds = [
618
738
  "customer_balance",
@@ -663,9 +783,13 @@ var CaldikClient = class {
663
783
  store;
664
784
  checkout;
665
785
  customer;
786
+ customers;
787
+ orders;
788
+ plugins;
666
789
  subscriptions;
667
790
  discounts;
668
791
  cart;
792
+ client;
669
793
  constructor(options = {}) {
670
794
  const client = new CaldikHttpClient({
671
795
  apiKey: options.apiKey,
@@ -674,16 +798,23 @@ var CaldikClient = class {
674
798
  request: options.request,
675
799
  onSetCookie: options.onSetCookie
676
800
  });
801
+ this.client = client;
677
802
  this.categories = new CategoriesApi(client);
678
803
  this.products = new ProductsApi(client);
679
804
  this.reviews = new ReviewsApi(client);
680
805
  this.store = new StoreApi(client);
681
806
  this.checkout = new CheckoutApi(client);
682
807
  this.customer = new CustomerApi(client);
808
+ this.customers = this.customer;
809
+ this.orders = this.customer.orders;
810
+ this.plugins = createPluginApi(client);
683
811
  this.subscriptions = new SubscriptionsApi(client);
684
812
  this.discounts = new DiscountsApi(client);
685
813
  this.cart = new CartApi(client);
686
814
  }
815
+ batch(input) {
816
+ return batchRequests(this.client, input);
817
+ }
687
818
  };
688
819
  //#endregion
689
- export { CaldikClient, CartApi, DiscountsApi, checkoutPaymentProviderIds, paymentProviderIds, storeWebhookEventNames, subscriptionPaymentProviderIds };
820
+ export { CaldikBatchError, CaldikClient, CaldikHttpError, CaldikRequest, CartApi, DiscountsApi, batchRequests, checkoutPaymentProviderIds, paymentProviderIds, storeWebhookEventNames, subscriptionPaymentProviderIds };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caldik/client",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.mts",
@@ -8,16 +8,22 @@
8
8
  ".": {
9
9
  "types": "./dist/index.d.mts",
10
10
  "import": "./dist/index.mjs"
11
+ },
12
+ "./development": {
13
+ "types": "./dist/development.d.mts",
14
+ "default": "./dist/development.mjs"
11
15
  }
12
16
  },
13
17
  "files": [
14
- "dist"
18
+ "dist",
19
+ "types"
15
20
  ],
16
21
  "publishConfig": {
17
22
  "access": "public"
18
23
  },
19
24
  "scripts": {
20
- "build": "tsdown",
25
+ "build": "tsdown && node development/prepare-types.mjs",
26
+ "prepack": "node development/prepare-types.mjs",
21
27
  "sync:api-contracts": "bun scripts/sync-api-contracts.ts",
22
28
  "test": "bun test"
23
29
  },
@@ -0,0 +1,7 @@
1
+ // Generated by Caldik. Plugin contracts refresh automatically during caldik dev.
2
+ import type { CaldikRequest } from "@caldik/client";
3
+
4
+ declare module "@caldik/client" {
5
+ interface CaldikPlugins {
6
+ }
7
+ }