@caldik/client 0.1.6 → 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.
@@ -370,14 +374,89 @@ Omit `rememberMe` to use the default session duration.
370
374
 
371
375
  ### Session Check
372
376
 
373
- 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**
374
409
 
375
410
  ```ts
376
- const {
377
- authenticated,
378
- customer,
379
- expiresAt,
380
- } = 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
+ }
381
460
  ```
382
461
 
383
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"];
@@ -200,9 +201,10 @@ interface StorefrontOrder {
200
201
  subtotal: string;
201
202
  amount: string;
202
203
  discount: {
203
- code: string;
204
+ code: string | null;
204
205
  percentage: string;
205
206
  amount: string;
207
+ source: "code" | "loyalty";
206
208
  } | null;
207
209
  currency: string;
208
210
  }
@@ -294,15 +296,7 @@ interface CustomerEmailLoginInput {
294
296
  email: string;
295
297
  rememberMe?: boolean;
296
298
  }
297
- interface CustomerSessionResponse {
298
- success: boolean;
299
- authenticated: boolean;
300
- customer: {
301
- id: string;
302
- email: string | null;
303
- } | null;
304
- expiresAt: number | null;
305
- }
299
+ type CustomerSessionResponse = void;
306
300
  interface CustomerPasswordResetResponse extends CustomerLoginResponse {
307
301
  expiresAt: number;
308
302
  message: string;
@@ -330,14 +324,72 @@ interface CustomerProfileResponse {
330
324
  success: boolean;
331
325
  customer: {
332
326
  id: string;
327
+ shortId: string;
328
+ displayId: string;
329
+ name: string | null;
333
330
  email: string | null;
334
- 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>>;
335
335
  };
336
- balances: Array<{
336
+ }
337
+ interface CustomerBalanceResponse {
338
+ success: boolean;
339
+ balance: {
337
340
  currency: string;
338
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;
339
380
  }>;
340
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
+ }
341
393
  interface CustomerLoginSettings {
342
394
  passwordEnabled: boolean;
343
395
  }
@@ -373,6 +425,8 @@ interface CheckoutPaymentMethodsResponse {
373
425
  //#endregion
374
426
  //#region src/internal/types.d.ts
375
427
  interface RequestOptions {
428
+ /** Response intentionally has no JSON body. */
429
+ empty?: boolean;
376
430
  edgeCache?: boolean;
377
431
  /** Internal authorization policy. Protected hosted calls are the default. */
378
432
  access?: "public" | "runtime";
@@ -380,6 +434,29 @@ interface RequestOptions {
380
434
  headers?: Record<string, string>;
381
435
  }
382
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
383
460
  //#region src/internal/client.d.ts
384
461
  type HttpClientOptions = {
385
462
  apiKey?: string;
@@ -388,6 +465,10 @@ type HttpClientOptions = {
388
465
  request?: Request;
389
466
  onSetCookie?: (cookie: string) => void | Promise<void>;
390
467
  };
468
+ declare class CaldikHttpError extends Error {
469
+ readonly status: number;
470
+ constructor(message: string, status: number);
471
+ }
391
472
  declare class CaldikHttpClient {
392
473
  private readonly apiKey?;
393
474
  private readonly apiUrl;
@@ -404,59 +485,75 @@ declare class CaldikHttpClient {
404
485
  private captureCookies;
405
486
  private baseUrl;
406
487
  private request;
407
- get<T>(path: string, params?: Record<string, string | number>, options?: RequestOptions): Promise<T>;
408
- 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>;
409
490
  }
410
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
411
508
  //#region src/categories.d.ts
412
509
  declare class CategoriesApi {
413
510
  private client;
414
511
  constructor(client: CaldikHttpClient);
415
- list(options?: number | CategoryListOptions): Promise<CategoriesResponse>;
416
- get(categoryId: string): Promise<CategoryResponse>;
512
+ list(options?: number | CategoryListOptions): CaldikRequest<CategoriesResponse>;
513
+ get(categoryId: string): CaldikRequest<CategoryResponse>;
417
514
  }
418
515
  //#endregion
419
516
  //#region src/products.d.ts
420
517
  declare class ProductsApi {
421
518
  private client;
422
519
  constructor(client: CaldikHttpClient);
423
- list(): Promise<ProductsResponse>;
424
- get(productIdOrSlug: string, options?: ProductGetOptions): Promise<ProductResponse>;
520
+ list(): CaldikRequest<ProductsResponse>;
521
+ get(productIdOrSlug: string, options?: ProductGetOptions): CaldikRequest<ProductResponse>;
425
522
  }
426
523
  //#endregion
427
524
  //#region src/reviews.d.ts
428
525
  declare class ReviewsApi {
429
526
  private client;
430
527
  constructor(client: CaldikHttpClient);
431
- list(limit?: number): Promise<ReviewsResponse>;
528
+ list(limit?: number): CaldikRequest<ReviewsResponse>;
432
529
  }
433
530
  //#endregion
434
531
  //#region src/store.d.ts
435
532
  declare class StoreApi {
436
533
  private client;
437
534
  constructor(client: CaldikHttpClient);
438
- get(): Promise<StoreResponse>;
535
+ get(): CaldikRequest<StoreResponse>;
439
536
  }
440
537
  //#endregion
441
538
  //#region src/checkout.d.ts
442
539
  declare class CheckoutApi {
443
540
  private client;
444
541
  readonly paymentmethods: {
445
- list: () => Promise<CheckoutPaymentMethodsResponse>;
542
+ list: () => CaldikRequest<CheckoutPaymentMethodsResponse>;
446
543
  };
447
544
  readonly settings: {
448
- get: () => Promise<CheckoutSettingsResponse>;
545
+ get: () => CaldikRequest<CheckoutSettingsResponse>;
449
546
  };
450
547
  constructor(client: CaldikHttpClient);
451
- create(input: CreateOrderInput): Promise<OrderResponse>;
452
- payWithBalance(orderId: string): Promise<OrderResponse>;
548
+ create(input: CreateOrderInput): CaldikRequest<OrderResponse>;
549
+ payWithBalance(orderId: string): CaldikRequest<OrderResponse>;
453
550
  }
454
551
  //#endregion
455
552
  //#region src/customer.d.ts
456
553
  declare class CustomerApi {
457
554
  private client;
458
555
  readonly settings: {
459
- get: () => Promise<CustomerLoginSettingsResponse>;
556
+ get: () => CaldikRequest<CustomerLoginSettingsResponse>;
460
557
  };
461
558
  readonly login: {
462
559
  (email: string, options?: {
@@ -477,18 +574,27 @@ declare class CustomerApi {
477
574
  requestReset: (email: string) => Promise<CustomerPasswordResetResponse>;
478
575
  reset: (input: CustomerPasswordResetInput) => Promise<CustomerPasswordResetVerifyResponse>;
479
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
+ };
480
586
  constructor(client: CaldikHttpClient);
481
- /** Check a store-scoped session without raising an error for signed-out customers. */
482
- session(): Promise<CustomerSessionResponse>;
483
- /** Read the signed-in customer and their balances. */
484
- 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>;
485
591
  }
486
592
  //#endregion
487
593
  //#region src/subscriptions.d.ts
488
594
  declare class SubscriptionsApi {
489
595
  private client;
490
596
  constructor(client: CaldikHttpClient);
491
- create(input: CreateSubscriptionInput): Promise<SubscriptionResponse>;
597
+ create(input: CreateSubscriptionInput): CaldikRequest<SubscriptionResponse>;
492
598
  }
493
599
  //#endregion
494
600
  //#region src/discounts.d.ts
@@ -511,7 +617,7 @@ interface DiscountQuoteResponse {
511
617
  declare class DiscountsApi {
512
618
  private client;
513
619
  constructor(client: CaldikHttpClient);
514
- check(input: CheckDiscountInput): Promise<DiscountQuoteResponse>;
620
+ check(input: CheckDiscountInput): CaldikRequest<DiscountQuoteResponse>;
515
621
  }
516
622
  //#endregion
517
623
  //#region src/cart.d.ts
@@ -549,7 +655,7 @@ declare class CartApi {
549
655
  private resolve;
550
656
  private reset;
551
657
  private mutate;
552
- private save;
658
+ private saveDiscount;
553
659
  private quote;
554
660
  get(): Promise<CartSnapshot>;
555
661
  add(item: ProductCommerceItem): Promise<CartSnapshot>;
@@ -563,6 +669,8 @@ declare class CartApi {
563
669
  }
564
670
  //#endregion
565
671
  //#region src/index.d.ts
672
+ /** Populated automatically from the store's plugin contracts during caldik dev. */
673
+ interface CaldikPlugins {}
566
674
  interface CaldikClientOptions {
567
675
  /** Store ID override for external servers or local browser development. */
568
676
  storeId?: string;
@@ -581,10 +689,18 @@ declare class CaldikClient {
581
689
  readonly store: StoreApi;
582
690
  readonly checkout: CheckoutApi;
583
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;
584
698
  readonly subscriptions: SubscriptionsApi;
585
699
  readonly discounts: DiscountsApi;
586
700
  readonly cart: CartApi;
701
+ private readonly client;
587
702
  constructor(options?: CaldikClientOptions);
703
+ batch<T extends BatchInput>(input: T): Promise<BatchResult<T>>;
588
704
  }
589
705
  //#endregion
590
- 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, ProductImage, 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";
@@ -74,6 +124,7 @@ var CaldikHttpClient = class {
74
124
  return this.storeId || boundRuntimeStoreId();
75
125
  }
76
126
  hasCartSession() {
127
+ if (this.cookies.has("session")) return true;
77
128
  const prefix = `caldik_cart_session_${this.storeContextId()}`;
78
129
  return [...this.cookies.keys()].some((name) => name === prefix || name.startsWith(`${prefix}_`));
79
130
  }
@@ -135,13 +186,24 @@ var CaldikHttpClient = class {
135
186
  const error = await response.clone().json().catch(() => null);
136
187
  throw new CaldikHttpError(typeof error?.message === "string" && error.message ? error.message : `${response.status} ${response.statusText}`.trim(), response.status);
137
188
  }
189
+ if (options?.empty) return void 0;
138
190
  return response.json();
139
191
  }
140
192
  get(path, params, options) {
141
- 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));
142
199
  }
143
200
  post(path, input, options) {
144
- 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));
145
207
  }
146
208
  };
147
209
  //#endregion
@@ -224,8 +286,15 @@ var CustomerApi = class {
224
286
  login;
225
287
  register;
226
288
  password;
289
+ orders;
290
+ balance;
227
291
  constructor(client) {
228
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" }) };
229
298
  this.settings = { get: () => this.client.get("/customers/settings", void 0, { access: "public" }) };
230
299
  let pendingResetId;
231
300
  this.password = {
@@ -293,11 +362,14 @@ verify: async (code, challengeId = pendingRegistrationId) => {
293
362
  return response;
294
363
  } });
295
364
  }
296
- /** 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. */
297
366
  session() {
298
- 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
+ });
299
371
  }
300
- /** Read the signed-in customer and their balances. */
372
+ /** Read the signed-in customer's profile. */
301
373
  me() {
302
374
  return this.client.get("/customers/me", void 0, { access: "public" });
303
375
  }
@@ -378,7 +450,6 @@ function discountFromQuote(quote) {
378
450
  var CartApi = class {
379
451
  client;
380
452
  cache = {
381
- version: 2,
382
453
  reference: null,
383
454
  snapshot: {
384
455
  items: [],
@@ -411,18 +482,16 @@ var CartApi = class {
411
482
  if (!storage) return;
412
483
  try {
413
484
  const parsed = JSON.parse(storage.getItem(this.key()) || "null");
414
- if (parsed?.version === 2) {
485
+ if (parsed?.snapshot) {
415
486
  const snapshot = parsed.snapshot;
416
487
  if (snapshot && Array.isArray(snapshot.items) && snapshot.items.every(isCartItem) && (parsed.reference === null || isReference(parsed.reference))) this.cache = {
417
- version: 2,
418
488
  reference: parsed.reference,
419
489
  snapshot: cloneSnapshot({
420
490
  items: snapshot.items,
421
491
  discount: isCartDiscount(snapshot.discount) ? snapshot.discount : null
422
492
  })
423
493
  };
424
- } else if (parsed?.version === 1 && Array.isArray(parsed.items) && parsed.items.every(isCartItem)) this.cache = {
425
- version: 2,
494
+ } else if (parsed && Array.isArray(parsed.items) && parsed.items.every(isCartItem)) this.cache = {
426
495
  reference: null,
427
496
  snapshot: cloneSnapshot({
428
497
  items: parsed.items,
@@ -444,7 +513,6 @@ var CartApi = class {
444
513
  }
445
514
  write(cart, token = this.cache.reference?.token, discount = null) {
446
515
  this.cache = {
447
- version: 2,
448
516
  reference: {
449
517
  id: cart.id,
450
518
  token: cart.customerId ? null : token
@@ -492,7 +560,6 @@ var CartApi = class {
492
560
  }
493
561
  reset() {
494
562
  this.cache = {
495
- version: 2,
496
563
  reference: null,
497
564
  snapshot: {
498
565
  items: [],
@@ -502,25 +569,11 @@ var CartApi = class {
502
569
  this.persistCache();
503
570
  }
504
571
  async mutate(operation, seed = true) {
505
- let cart = await this.resolve(false, seed);
506
- let response;
507
- try {
508
- response = await operation(cart);
509
- } catch (error) {
510
- if (!(error instanceof CaldikHttpError) || error.status !== 409) throw error;
511
- const refreshed = await this.resolve();
512
- if (refreshed.id !== cart.id || refreshed.status !== "active") throw error;
513
- cart = refreshed;
514
- response = await operation(cart);
515
- }
572
+ const response = await operation(await this.resolve(false, seed));
516
573
  return this.write(response.cart);
517
574
  }
518
- save(cart, items, discountCode) {
519
- return this.client.post(`/carts/${cart.id}`, {
520
- items: commerceItems(items),
521
- version: cart.version,
522
- discountCode
523
- }, this.options());
575
+ saveDiscount(cart, code) {
576
+ return this.client.post(`/carts/${cart.id}/discount`, { code }, this.options());
524
577
  }
525
578
  quote(cart, code) {
526
579
  if (!cart.items.length) throw new Error("Cannot check a discount on an empty cart");
@@ -541,7 +594,7 @@ var CartApi = class {
541
594
  404,
542
595
  422
543
596
  ].includes(error.status)) throw error;
544
- cart = (await this.save(cart, cart.items, null)).cart;
597
+ cart = (await this.saveDiscount(cart, null)).cart;
545
598
  }
546
599
  return this.write(cart, this.cache.reference?.token, discount);
547
600
  });
@@ -554,10 +607,7 @@ var CartApi = class {
554
607
  productId: item.productId.trim(),
555
608
  quantity: quantity(item.quantity)
556
609
  };
557
- return this.mutate((cart) => this.client.post(`/carts/${cart.id}/items`, {
558
- item: normalized,
559
- version: cart.version
560
- }, this.options()));
610
+ return this.mutate((cart) => this.client.post(`/carts/${cart.id}/items`, { item: normalized }, this.options()));
561
611
  });
562
612
  }
563
613
  updateQuantity(itemId, nextQuantity) {
@@ -565,11 +615,10 @@ var CartApi = class {
565
615
  const normalized = quantity(nextQuantity, true);
566
616
  return this.mutate((cart) => {
567
617
  if (!cart.items.some((item) => item.id === itemId)) throw new Error("Cart item not found");
568
- const items = cart.items.map((item) => item.id === itemId ? {
569
- ...item,
618
+ return this.client.post(`/carts/${cart.id}/items/quantity`, {
619
+ itemId,
570
620
  quantity: normalized
571
- } : item).filter((item) => item.quantity > 0);
572
- return this.save(cart, items, null);
621
+ }, this.options());
573
622
  });
574
623
  });
575
624
  }
@@ -577,7 +626,7 @@ var CartApi = class {
577
626
  return this.updateQuantity(itemId, 0);
578
627
  }
579
628
  clear() {
580
- 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));
581
630
  }
582
631
  checkDiscount(code) {
583
632
  return this.enqueue(async () => this.quote(await this.resolve(), code));
@@ -587,7 +636,9 @@ var CartApi = class {
587
636
  let quote;
588
637
  const snapshot = await this.mutate(async (cart) => {
589
638
  quote = await this.quote(cart, code);
590
- 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;
591
642
  });
592
643
  snapshot.discount = discountFromQuote(quote);
593
644
  this.cache.snapshot = cloneSnapshot(snapshot);
@@ -596,22 +647,92 @@ var CartApi = class {
596
647
  });
597
648
  }
598
649
  removeDiscount() {
599
- return this.enqueue(() => this.mutate((cart) => this.save(cart, cart.items, null)));
650
+ return this.enqueue(() => this.mutate((cart) => this.saveDiscount(cart, null)));
600
651
  }
601
652
  checkout(input = {}) {
602
653
  return this.enqueue(async () => {
603
654
  const cart = await this.resolve(true);
604
655
  if (!cart.items.length) throw new Error("Cannot checkout an empty cart");
605
- const response = await this.client.post(`/carts/${cart.id}/checkout`, {
606
- ...input,
607
- version: cart.version
608
- }, this.options());
656
+ const response = await this.client.post(`/carts/${cart.id}/checkout`, input, this.options());
609
657
  this.reset();
610
658
  return response;
611
659
  });
612
660
  }
613
661
  };
614
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
615
736
  //#region src/payment-providers.generated.ts
616
737
  const paymentProviderIds = [
617
738
  "customer_balance",
@@ -662,9 +783,13 @@ var CaldikClient = class {
662
783
  store;
663
784
  checkout;
664
785
  customer;
786
+ customers;
787
+ orders;
788
+ plugins;
665
789
  subscriptions;
666
790
  discounts;
667
791
  cart;
792
+ client;
668
793
  constructor(options = {}) {
669
794
  const client = new CaldikHttpClient({
670
795
  apiKey: options.apiKey,
@@ -673,16 +798,23 @@ var CaldikClient = class {
673
798
  request: options.request,
674
799
  onSetCookie: options.onSetCookie
675
800
  });
801
+ this.client = client;
676
802
  this.categories = new CategoriesApi(client);
677
803
  this.products = new ProductsApi(client);
678
804
  this.reviews = new ReviewsApi(client);
679
805
  this.store = new StoreApi(client);
680
806
  this.checkout = new CheckoutApi(client);
681
807
  this.customer = new CustomerApi(client);
808
+ this.customers = this.customer;
809
+ this.orders = this.customer.orders;
810
+ this.plugins = createPluginApi(client);
682
811
  this.subscriptions = new SubscriptionsApi(client);
683
812
  this.discounts = new DiscountsApi(client);
684
813
  this.cart = new CartApi(client);
685
814
  }
815
+ batch(input) {
816
+ return batchRequests(this.client, input);
817
+ }
686
818
  };
687
819
  //#endregion
688
- 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.6",
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
+ }