@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 +85 -6
- package/dist/development.d.mts +17 -0
- package/dist/development.mjs +153 -0
- package/dist/index.d.mts +149 -33
- package/dist/index.mjs +178 -46
- 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.
|
|
@@ -370,14 +374,89 @@ Omit `rememberMe` to use the default session duration.
|
|
|
370
374
|
|
|
371
375
|
### Session Check
|
|
372
376
|
|
|
373
|
-
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
-
|
|
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
|
-
|
|
331
|
+
phone: string | null;
|
|
332
|
+
createdAt: number;
|
|
333
|
+
updatedAt: number;
|
|
334
|
+
extensions: Record<string, Record<string, string | number | boolean>>;
|
|
335
335
|
};
|
|
336
|
-
|
|
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):
|
|
408
|
-
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>;
|
|
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):
|
|
416
|
-
get(categoryId: string):
|
|
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():
|
|
424
|
-
get(productIdOrSlug: string, options?: ProductGetOptions):
|
|
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):
|
|
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():
|
|
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: () =>
|
|
542
|
+
list: () => CaldikRequest<CheckoutPaymentMethodsResponse>;
|
|
446
543
|
};
|
|
447
544
|
readonly settings: {
|
|
448
|
-
get: () =>
|
|
545
|
+
get: () => CaldikRequest<CheckoutSettingsResponse>;
|
|
449
546
|
};
|
|
450
547
|
constructor(client: CaldikHttpClient);
|
|
451
|
-
create(input: CreateOrderInput):
|
|
452
|
-
payWithBalance(orderId: string):
|
|
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: () =>
|
|
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
|
-
/**
|
|
482
|
-
session():
|
|
483
|
-
/** Read the signed-in customer
|
|
484
|
-
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>;
|
|
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):
|
|
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):
|
|
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
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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, {
|
|
367
|
+
return this.client.get("/customers/session", void 0, {
|
|
368
|
+
access: "public",
|
|
369
|
+
empty: true
|
|
370
|
+
});
|
|
299
371
|
}
|
|
300
|
-
/** Read the signed-in customer
|
|
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?.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
569
|
-
|
|
618
|
+
return this.client.post(`/carts/${cart.id}/items/quantity`, {
|
|
619
|
+
itemId,
|
|
570
620
|
quantity: normalized
|
|
571
|
-
}
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
},
|