@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 +89 -6
- package/dist/development.d.mts +17 -0
- package/dist/development.mjs +153 -0
- package/dist/index.d.mts +158 -33
- package/dist/index.mjs +184 -53
- package/package.json +9 -3
- package/types/plugins.d.ts +7 -0
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
|
-
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
-
|
|
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
|
-
|
|
331
|
+
phone: string | null;
|
|
332
|
+
createdAt: number;
|
|
333
|
+
updatedAt: number;
|
|
334
|
+
extensions: Record<string, Record<string, string | number | boolean>>;
|
|
327
335
|
};
|
|
328
|
-
|
|
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):
|
|
399
|
-
post<T>(path: string, input?: unknown, options?: RequestOptions):
|
|
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):
|
|
407
|
-
get(categoryId: string):
|
|
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():
|
|
415
|
-
get(productIdOrSlug: string, options?: ProductGetOptions):
|
|
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):
|
|
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():
|
|
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: () =>
|
|
542
|
+
list: () => CaldikRequest<CheckoutPaymentMethodsResponse>;
|
|
437
543
|
};
|
|
438
544
|
readonly settings: {
|
|
439
|
-
get: () =>
|
|
545
|
+
get: () => CaldikRequest<CheckoutSettingsResponse>;
|
|
440
546
|
};
|
|
441
547
|
constructor(client: CaldikHttpClient);
|
|
442
|
-
create(input: CreateOrderInput):
|
|
443
|
-
payWithBalance(orderId: string):
|
|
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: () =>
|
|
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
|
-
/**
|
|
473
|
-
session():
|
|
474
|
-
/** Read the signed-in customer
|
|
475
|
-
me():
|
|
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):
|
|
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):
|
|
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
|
|
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
|
-
|
|
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"
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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, {
|
|
367
|
+
return this.client.get("/customers/session", void 0, {
|
|
368
|
+
access: "public",
|
|
369
|
+
empty: true
|
|
370
|
+
});
|
|
300
371
|
}
|
|
301
|
-
/** Read the signed-in customer
|
|
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?.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
570
|
-
|
|
618
|
+
return this.client.post(`/carts/${cart.id}/items/quantity`, {
|
|
619
|
+
itemId,
|
|
571
620
|
quantity: normalized
|
|
572
|
-
}
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
},
|