@minipim/sdk 0.2.0 → 0.3.1

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.
Files changed (49) hide show
  1. package/README.md +3 -1
  2. package/dist/attributes.cjs +80 -0
  3. package/dist/attributes.d.cts +48 -0
  4. package/dist/attributes.d.ts +12 -11
  5. package/dist/attributes.js +46 -71
  6. package/dist/index.cjs +163 -0
  7. package/dist/index.d.cts +132 -0
  8. package/dist/index.d.ts +128 -13
  9. package/dist/index.js +117 -16
  10. package/dist/openapi.cjs +18 -0
  11. package/dist/openapi.d.cts +7204 -0
  12. package/dist/openapi.d.ts +345 -9
  13. package/dist/openapi.js +0 -6
  14. package/dist/webhook-edge.cjs +68 -0
  15. package/dist/webhook-edge.d.cts +26 -0
  16. package/dist/webhook-edge.d.ts +6 -4
  17. package/dist/webhook-edge.js +40 -31
  18. package/dist/{webhook-shared.d.ts → webhook-shared-CjMkFYVb.d.cts} +3 -6
  19. package/dist/webhook-shared-CjMkFYVb.d.ts +25 -0
  20. package/dist/webhook.cjs +53 -0
  21. package/dist/webhook.d.cts +26 -0
  22. package/dist/webhook.d.ts +5 -4
  23. package/dist/webhook.js +27 -39
  24. package/package.json +45 -13
  25. package/dist/attributes.d.ts.map +0 -1
  26. package/dist/attributes.js.map +0 -1
  27. package/dist/client.d.ts +0 -66
  28. package/dist/client.d.ts.map +0 -1
  29. package/dist/client.js +0 -43
  30. package/dist/client.js.map +0 -1
  31. package/dist/errors.d.ts +0 -27
  32. package/dist/errors.d.ts.map +0 -1
  33. package/dist/errors.js +0 -30
  34. package/dist/errors.js.map +0 -1
  35. package/dist/index.d.ts.map +0 -1
  36. package/dist/index.js.map +0 -1
  37. package/dist/openapi.d.ts.map +0 -1
  38. package/dist/openapi.js.map +0 -1
  39. package/dist/paginate.d.ts +0 -34
  40. package/dist/paginate.d.ts.map +0 -1
  41. package/dist/paginate.js +0 -43
  42. package/dist/paginate.js.map +0 -1
  43. package/dist/webhook-edge.d.ts.map +0 -1
  44. package/dist/webhook-edge.js.map +0 -1
  45. package/dist/webhook-shared.d.ts.map +0 -1
  46. package/dist/webhook-shared.js +0 -30
  47. package/dist/webhook-shared.js.map +0 -1
  48. package/dist/webhook.d.ts.map +0 -1
  49. package/dist/webhook.js.map +0 -1
package/dist/openapi.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * This file was auto-generated by openapi-typescript.
3
3
  * Do not make direct changes to the file.
4
4
  */
5
- export interface paths {
5
+ interface paths {
6
6
  "/healthz": {
7
7
  parameters: {
8
8
  query?: never;
@@ -29,6 +29,7 @@ export interface paths {
29
29
  "application/json": {
30
30
  status: string;
31
31
  service: string;
32
+ version: string;
32
33
  };
33
34
  };
34
35
  };
@@ -80,6 +81,57 @@ export interface paths {
80
81
  patch?: never;
81
82
  trace?: never;
82
83
  };
84
+ "/v1/changelog": {
85
+ parameters: {
86
+ query?: never;
87
+ header?: never;
88
+ path?: never;
89
+ cookie?: never;
90
+ };
91
+ /**
92
+ * Release changelog
93
+ * @description Structured release notes, newest first. The `version` of the first entry is the deployed version (also reported by /healthz).
94
+ */
95
+ get: {
96
+ parameters: {
97
+ query?: never;
98
+ header?: never;
99
+ path?: never;
100
+ cookie?: never;
101
+ };
102
+ requestBody?: never;
103
+ responses: {
104
+ /** @description Default Response */
105
+ 200: {
106
+ headers: {
107
+ [name: string]: unknown;
108
+ };
109
+ content: {
110
+ "application/json": {
111
+ version: string;
112
+ entries: {
113
+ version: string;
114
+ date: string;
115
+ title: string;
116
+ highlights: {
117
+ /** @enum {string} */
118
+ type: "feature" | "fix" | "perf" | "docs";
119
+ text: string;
120
+ }[];
121
+ }[];
122
+ };
123
+ };
124
+ };
125
+ };
126
+ };
127
+ put?: never;
128
+ post?: never;
129
+ delete?: never;
130
+ options?: never;
131
+ head?: never;
132
+ patch?: never;
133
+ trace?: never;
134
+ };
83
135
  "/v1/organizations": {
84
136
  parameters: {
85
137
  query?: never;
@@ -1174,9 +1226,10 @@ export interface paths {
1174
1226
  familyId?: string;
1175
1227
  categoryId?: string;
1176
1228
  brand?: string;
1229
+ tag?: string | string[];
1177
1230
  connectorId?: string;
1178
1231
  updatedSince?: string;
1179
- sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt";
1232
+ sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt" | "relevance";
1180
1233
  sortDir?: "asc" | "desc";
1181
1234
  limit?: number;
1182
1235
  offset?: number;
@@ -1215,6 +1268,7 @@ export interface paths {
1215
1268
  attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
1216
1269
  [key: string]: unknown;
1217
1270
  };
1271
+ tags: string[];
1218
1272
  /** Format: uuid */
1219
1273
  createdBy: string | null;
1220
1274
  /** Format: uuid */
@@ -1263,6 +1317,8 @@ export interface paths {
1263
1317
  value?: unknown;
1264
1318
  }[];
1265
1319
  };
1320
+ /** @default [] */
1321
+ tags?: string[];
1266
1322
  };
1267
1323
  };
1268
1324
  };
@@ -1291,6 +1347,7 @@ export interface paths {
1291
1347
  attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
1292
1348
  [key: string]: unknown;
1293
1349
  };
1350
+ tags: string[];
1294
1351
  /** Format: uuid */
1295
1352
  createdBy: string | null;
1296
1353
  /** Format: uuid */
@@ -1340,9 +1397,10 @@ export interface paths {
1340
1397
  familyId?: string;
1341
1398
  categoryId?: string;
1342
1399
  brand?: string;
1400
+ tag?: string | string[];
1343
1401
  connectorId?: string;
1344
1402
  updatedSince?: string;
1345
- sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt";
1403
+ sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt" | "relevance";
1346
1404
  sortDir?: "asc" | "desc";
1347
1405
  locale?: string;
1348
1406
  channel?: string;
@@ -1383,6 +1441,10 @@ export interface paths {
1383
1441
  value: string;
1384
1442
  count: number;
1385
1443
  }[];
1444
+ tags: {
1445
+ value: string;
1446
+ count: number;
1447
+ }[];
1386
1448
  connectors: {
1387
1449
  /** Format: uuid */
1388
1450
  connectorId: string;
@@ -1403,6 +1465,48 @@ export interface paths {
1403
1465
  patch?: never;
1404
1466
  trace?: never;
1405
1467
  };
1468
+ "/v1/products/tags": {
1469
+ parameters: {
1470
+ query?: never;
1471
+ header?: never;
1472
+ path?: never;
1473
+ cookie?: never;
1474
+ };
1475
+ /**
1476
+ * List all product tags in use, with counts
1477
+ * @description Every distinct tag currently on at least one product, ordered by usage. Tags are free-form, normalized to lowercase slugs on write (`Featured` → `featured`). Filter products by tag with `GET /v1/products?tag=<tag>` (repeatable; multiple tags AND together).
1478
+ */
1479
+ get: {
1480
+ parameters: {
1481
+ query?: never;
1482
+ header?: never;
1483
+ path?: never;
1484
+ cookie?: never;
1485
+ };
1486
+ requestBody?: never;
1487
+ responses: {
1488
+ /** @description Default Response */
1489
+ 200: {
1490
+ headers: {
1491
+ [name: string]: unknown;
1492
+ };
1493
+ content: {
1494
+ "application/json": {
1495
+ value: string;
1496
+ count: number;
1497
+ }[];
1498
+ };
1499
+ };
1500
+ };
1501
+ };
1502
+ put?: never;
1503
+ post?: never;
1504
+ delete?: never;
1505
+ options?: never;
1506
+ head?: never;
1507
+ patch?: never;
1508
+ trace?: never;
1509
+ };
1406
1510
  "/v1/products/{id}": {
1407
1511
  parameters: {
1408
1512
  query?: never;
@@ -1499,6 +1603,7 @@ export interface paths {
1499
1603
  attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
1500
1604
  [key: string]: unknown;
1501
1605
  };
1606
+ tags: string[];
1502
1607
  /** Format: uuid */
1503
1608
  createdBy: string | null;
1504
1609
  /** Format: uuid */
@@ -1619,6 +1724,7 @@ export interface paths {
1619
1724
  value?: unknown;
1620
1725
  }[];
1621
1726
  };
1727
+ tags?: string[];
1622
1728
  };
1623
1729
  };
1624
1730
  };
@@ -1647,6 +1753,7 @@ export interface paths {
1647
1753
  attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
1648
1754
  [key: string]: unknown;
1649
1755
  };
1756
+ tags: string[];
1650
1757
  /** Format: uuid */
1651
1758
  createdBy: string | null;
1652
1759
  /** Format: uuid */
@@ -1725,6 +1832,9 @@ export interface paths {
1725
1832
  missing: {
1726
1833
  code: string;
1727
1834
  locale: string | null;
1835
+ label?: string;
1836
+ variantsMissing?: number;
1837
+ variantsTotal?: number;
1728
1838
  }[];
1729
1839
  }[];
1730
1840
  };
@@ -2089,6 +2199,61 @@ export interface paths {
2089
2199
  patch?: never;
2090
2200
  trace?: never;
2091
2201
  };
2202
+ "/v1/products/bulk-set-tags": {
2203
+ parameters: {
2204
+ query?: never;
2205
+ header?: never;
2206
+ path?: never;
2207
+ cookie?: never;
2208
+ };
2209
+ get?: never;
2210
+ put?: never;
2211
+ /**
2212
+ * Bulk-modify tags across products
2213
+ * @description `mode=add` unions the listed tags into each product, `mode=remove` strips them, `mode=replace` overwrites the full list. Tags are normalized (slugified + deduped) exactly like single-product writes.
2214
+ */
2215
+ post: {
2216
+ parameters: {
2217
+ query?: never;
2218
+ header?: never;
2219
+ path?: never;
2220
+ cookie?: never;
2221
+ };
2222
+ requestBody: {
2223
+ content: {
2224
+ "application/json": {
2225
+ productIds: string[];
2226
+ /** @enum {string} */
2227
+ mode: "add" | "remove" | "replace";
2228
+ tags: string[];
2229
+ };
2230
+ };
2231
+ };
2232
+ responses: {
2233
+ /** @description Default Response */
2234
+ 200: {
2235
+ headers: {
2236
+ [name: string]: unknown;
2237
+ };
2238
+ content: {
2239
+ "application/json": {
2240
+ results: {
2241
+ /** Format: uuid */
2242
+ productId: string;
2243
+ ok: boolean;
2244
+ error?: string;
2245
+ }[];
2246
+ };
2247
+ };
2248
+ };
2249
+ };
2250
+ };
2251
+ delete?: never;
2252
+ options?: never;
2253
+ head?: never;
2254
+ patch?: never;
2255
+ trace?: never;
2256
+ };
2092
2257
  "/v1/products/{id}/variants": {
2093
2258
  parameters: {
2094
2259
  query?: never;
@@ -2139,6 +2304,36 @@ export interface paths {
2139
2304
  }[];
2140
2305
  };
2141
2306
  };
2307
+ /** @description Default Response */
2308
+ 404: {
2309
+ headers: {
2310
+ [name: string]: unknown;
2311
+ };
2312
+ content: {
2313
+ "application/json": {
2314
+ error: {
2315
+ code: string;
2316
+ message: string;
2317
+ details?: unknown;
2318
+ };
2319
+ };
2320
+ };
2321
+ };
2322
+ /** @description Default Response */
2323
+ 422: {
2324
+ headers: {
2325
+ [name: string]: unknown;
2326
+ };
2327
+ content: {
2328
+ "application/json": {
2329
+ error: {
2330
+ code: string;
2331
+ message: string;
2332
+ details?: unknown;
2333
+ };
2334
+ };
2335
+ };
2336
+ };
2142
2337
  };
2143
2338
  };
2144
2339
  put?: never;
@@ -5854,7 +6049,7 @@ export interface paths {
5854
6049
  put?: never;
5855
6050
  /**
5856
6051
  * Trigger a sync (pull / push / both)
5857
- * @description Synchronous in v1 — runs in-request and returns after completion. Larger syncs will move to a background worker; the call site stays the same (returns a sync record id immediately, status fills in async).
6052
+ * @description Asynchronous: returns the sync record immediately with status `running`; the work continues in the background. Poll `GET /v1/connectors/{id}/syncs` for the terminal status (`completed`/`partial`/`failed`) and stats. Pulls download media binaries and routinely take minutes running in-request exceeded proxy timeouts, which severed responses mid-sync and invited retry double-pulls. Caveat: a server restart mid-sync leaves the row `running` forever; a BullMQ worker will make this durable later.
5858
6053
  */
5859
6054
  post: {
5860
6055
  parameters: {
@@ -6365,6 +6560,146 @@ export interface paths {
6365
6560
  patch?: never;
6366
6561
  trace?: never;
6367
6562
  };
6563
+ "/v1/connectors/{id}/deliveries": {
6564
+ parameters: {
6565
+ query?: never;
6566
+ header?: never;
6567
+ path?: never;
6568
+ cookie?: never;
6569
+ };
6570
+ /**
6571
+ * Recent outbound webhook deliveries for a connector
6572
+ * @description Last 50 delivery attempts (event name, HTTP status, ok, error) for push connectors that POST events to a destination (e.g. headless). Answers "did any events actually arrive?" without grepping receiver logs.
6573
+ */
6574
+ get: {
6575
+ parameters: {
6576
+ query?: never;
6577
+ header?: never;
6578
+ path: {
6579
+ id: string;
6580
+ };
6581
+ cookie?: never;
6582
+ };
6583
+ requestBody?: never;
6584
+ responses: {
6585
+ /** @description Default Response */
6586
+ 200: {
6587
+ headers: {
6588
+ [name: string]: unknown;
6589
+ };
6590
+ content: {
6591
+ "application/json": {
6592
+ /** Format: uuid */
6593
+ id: string;
6594
+ eventName: string;
6595
+ status: number | null;
6596
+ ok: boolean;
6597
+ error: string | null;
6598
+ /** Format: date-time */
6599
+ createdAt: string;
6600
+ }[];
6601
+ };
6602
+ };
6603
+ /** @description Default Response */
6604
+ 404: {
6605
+ headers: {
6606
+ [name: string]: unknown;
6607
+ };
6608
+ content: {
6609
+ "application/json": {
6610
+ error: {
6611
+ code: string;
6612
+ message: string;
6613
+ details?: unknown;
6614
+ };
6615
+ };
6616
+ };
6617
+ };
6618
+ };
6619
+ };
6620
+ put?: never;
6621
+ post?: never;
6622
+ delete?: never;
6623
+ options?: never;
6624
+ head?: never;
6625
+ patch?: never;
6626
+ trace?: never;
6627
+ };
6628
+ "/v1/connectors/{id}/webhooks/test": {
6629
+ parameters: {
6630
+ query?: never;
6631
+ header?: never;
6632
+ path?: never;
6633
+ cookie?: never;
6634
+ };
6635
+ get?: never;
6636
+ put?: never;
6637
+ /**
6638
+ * Send a signed test event to the connector destination
6639
+ * @description Delivers a synthetic `webhook.test` event (HMAC-signed like any real delivery) so integrators can verify endpoint wiring without mutating catalog data. Returns the delivery outcome and logs it like any other.
6640
+ */
6641
+ post: {
6642
+ parameters: {
6643
+ query?: never;
6644
+ header?: never;
6645
+ path: {
6646
+ id: string;
6647
+ };
6648
+ cookie?: never;
6649
+ };
6650
+ requestBody?: never;
6651
+ responses: {
6652
+ /** @description Default Response */
6653
+ 200: {
6654
+ headers: {
6655
+ [name: string]: unknown;
6656
+ };
6657
+ content: {
6658
+ "application/json": {
6659
+ ok: boolean;
6660
+ status: number | null;
6661
+ error?: string;
6662
+ };
6663
+ };
6664
+ };
6665
+ /** @description Default Response */
6666
+ 404: {
6667
+ headers: {
6668
+ [name: string]: unknown;
6669
+ };
6670
+ content: {
6671
+ "application/json": {
6672
+ error: {
6673
+ code: string;
6674
+ message: string;
6675
+ details?: unknown;
6676
+ };
6677
+ };
6678
+ };
6679
+ };
6680
+ /** @description Default Response */
6681
+ 422: {
6682
+ headers: {
6683
+ [name: string]: unknown;
6684
+ };
6685
+ content: {
6686
+ "application/json": {
6687
+ error: {
6688
+ code: string;
6689
+ message: string;
6690
+ details?: unknown;
6691
+ };
6692
+ };
6693
+ };
6694
+ };
6695
+ };
6696
+ };
6697
+ delete?: never;
6698
+ options?: never;
6699
+ head?: never;
6700
+ patch?: never;
6701
+ trace?: never;
6702
+ };
6368
6703
  "/v1/connectors/{id}/webhooks/enable": {
6369
6704
  parameters: {
6370
6705
  query?: never;
@@ -6854,8 +7189,8 @@ export interface paths {
6854
7189
  trace?: never;
6855
7190
  };
6856
7191
  }
6857
- export type webhooks = Record<string, never>;
6858
- export interface components {
7192
+ type webhooks = Record<string, never>;
7193
+ interface components {
6859
7194
  schemas: never;
6860
7195
  responses: never;
6861
7196
  parameters: never;
@@ -6863,6 +7198,7 @@ export interface components {
6863
7198
  headers: never;
6864
7199
  pathItems: never;
6865
7200
  }
6866
- export type $defs = Record<string, never>;
6867
- export type operations = Record<string, never>;
6868
- //# sourceMappingURL=openapi.d.ts.map
7201
+ type $defs = Record<string, never>;
7202
+ type operations = Record<string, never>;
7203
+
7204
+ export type { $defs, components, operations, paths, webhooks };
package/dist/openapi.js CHANGED
@@ -1,6 +0,0 @@
1
- /**
2
- * This file was auto-generated by openapi-typescript.
3
- * Do not make direct changes to the file.
4
- */
5
- export {};
6
- //# sourceMappingURL=openapi.js.map
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/webhook-edge.ts
21
+ var webhook_edge_exports = {};
22
+ __export(webhook_edge_exports, {
23
+ verifyMinipimWebhookEdge: () => verifyMinipimWebhookEdge
24
+ });
25
+ module.exports = __toCommonJS(webhook_edge_exports);
26
+
27
+ // src/webhook-shared.ts
28
+ function decodeHex(s) {
29
+ if (s.length % 2 !== 0) return new Uint8Array(0);
30
+ const out = new Uint8Array(s.length / 2);
31
+ for (let i = 0; i < out.length; i++) {
32
+ const byte = parseInt(s.slice(i * 2, i * 2 + 2), 16);
33
+ if (Number.isNaN(byte)) return new Uint8Array(0);
34
+ out[i] = byte;
35
+ }
36
+ return out;
37
+ }
38
+ function stripPrefix(sig) {
39
+ return sig.startsWith("sha256=") ? sig.slice(7) : sig;
40
+ }
41
+ function timingSafeEqualBytes(a, b) {
42
+ if (a.length !== b.length) return false;
43
+ let diff = 0;
44
+ for (let i = 0; i < a.length; i++) diff |= (a[i] ?? 0) ^ (b[i] ?? 0);
45
+ return diff === 0;
46
+ }
47
+
48
+ // src/webhook-edge.ts
49
+ async function verifyMinipimWebhookEdge(opts) {
50
+ const enc = new TextEncoder();
51
+ const keyData = enc.encode(opts.secret);
52
+ const bodyData = typeof opts.rawBody === "string" ? enc.encode(opts.rawBody) : new Uint8Array(opts.rawBody);
53
+ const key = await crypto.subtle.importKey(
54
+ "raw",
55
+ keyData,
56
+ { name: "HMAC", hash: "SHA-256" },
57
+ false,
58
+ ["sign"]
59
+ );
60
+ const sigBuf = await crypto.subtle.sign("HMAC", key, bodyData);
61
+ const expected = new Uint8Array(sigBuf);
62
+ const got = decodeHex(stripPrefix(opts.signature));
63
+ return timingSafeEqualBytes(expected, got);
64
+ }
65
+ // Annotate the CommonJS export names for ESM import in node:
66
+ 0 && (module.exports = {
67
+ verifyMinipimWebhookEdge
68
+ });
@@ -0,0 +1,26 @@
1
+ import { V as VerifyWebhookOptions } from './webhook-shared-CjMkFYVb.cjs';
2
+
3
+ /**
4
+ * Edge / browser webhook signature verification, via WebCrypto. Use in
5
+ * Cloudflare Workers, Vercel Edge Runtime, Deno, or browsers. Async because
6
+ * `crypto.subtle` is async. For Node use `@minipim/sdk/webhook` (synchronous).
7
+ *
8
+ * This entry point imports no `node:` modules, so it's safe in any Edge
9
+ * bundle.
10
+ */
11
+
12
+ /**
13
+ * Verify a MiniPim webhook signature in an Edge/WebCrypto runtime. Same
14
+ * contract as the Node `verifyMinipimWebhook` but async.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { verifyMinipimWebhookEdge } from '@minipim/sdk/webhook-edge';
19
+ *
20
+ * const ok = await verifyMinipimWebhookEdge({ rawBody, signature, secret });
21
+ * if (!ok) return new Response('invalid signature', { status: 401 });
22
+ * ```
23
+ */
24
+ declare function verifyMinipimWebhookEdge(opts: VerifyWebhookOptions): Promise<boolean>;
25
+
26
+ export { VerifyWebhookOptions, verifyMinipimWebhookEdge };
@@ -1,3 +1,5 @@
1
+ import { V as VerifyWebhookOptions } from './webhook-shared-CjMkFYVb.js';
2
+
1
3
  /**
2
4
  * Edge / browser webhook signature verification, via WebCrypto. Use in
3
5
  * Cloudflare Workers, Vercel Edge Runtime, Deno, or browsers. Async because
@@ -6,8 +8,7 @@
6
8
  * This entry point imports no `node:` modules, so it's safe in any Edge
7
9
  * bundle.
8
10
  */
9
- import { type VerifyWebhookOptions } from './webhook-shared.js';
10
- export type { VerifyWebhookOptions } from './webhook-shared.js';
11
+
11
12
  /**
12
13
  * Verify a MiniPim webhook signature in an Edge/WebCrypto runtime. Same
13
14
  * contract as the Node `verifyMinipimWebhook` but async.
@@ -20,5 +21,6 @@ export type { VerifyWebhookOptions } from './webhook-shared.js';
20
21
  * if (!ok) return new Response('invalid signature', { status: 401 });
21
22
  * ```
22
23
  */
23
- export declare function verifyMinipimWebhookEdge(opts: VerifyWebhookOptions): Promise<boolean>;
24
- //# sourceMappingURL=webhook-edge.d.ts.map
24
+ declare function verifyMinipimWebhookEdge(opts: VerifyWebhookOptions): Promise<boolean>;
25
+
26
+ export { VerifyWebhookOptions, verifyMinipimWebhookEdge };
@@ -1,32 +1,41 @@
1
- /**
2
- * Edge / browser webhook signature verification, via WebCrypto. Use in
3
- * Cloudflare Workers, Vercel Edge Runtime, Deno, or browsers. Async because
4
- * `crypto.subtle` is async. For Node use `@minipim/sdk/webhook` (synchronous).
5
- *
6
- * This entry point imports no `node:` modules, so it's safe in any Edge
7
- * bundle.
8
- */
9
- import { decodeHex, stripPrefix, timingSafeEqualBytes, } from './webhook-shared.js';
10
- /**
11
- * Verify a MiniPim webhook signature in an Edge/WebCrypto runtime. Same
12
- * contract as the Node `verifyMinipimWebhook` but async.
13
- *
14
- * @example
15
- * ```ts
16
- * import { verifyMinipimWebhookEdge } from '@minipim/sdk/webhook-edge';
17
- *
18
- * const ok = await verifyMinipimWebhookEdge({ rawBody, signature, secret });
19
- * if (!ok) return new Response('invalid signature', { status: 401 });
20
- * ```
21
- */
22
- export async function verifyMinipimWebhookEdge(opts) {
23
- const enc = new TextEncoder();
24
- const keyData = enc.encode(opts.secret);
25
- const bodyData = typeof opts.rawBody === 'string' ? enc.encode(opts.rawBody) : new Uint8Array(opts.rawBody);
26
- const key = await crypto.subtle.importKey('raw', keyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
27
- const sigBuf = await crypto.subtle.sign('HMAC', key, bodyData);
28
- const expected = new Uint8Array(sigBuf);
29
- const got = decodeHex(stripPrefix(opts.signature));
30
- return timingSafeEqualBytes(expected, got);
1
+ // src/webhook-shared.ts
2
+ function decodeHex(s) {
3
+ if (s.length % 2 !== 0) return new Uint8Array(0);
4
+ const out = new Uint8Array(s.length / 2);
5
+ for (let i = 0; i < out.length; i++) {
6
+ const byte = parseInt(s.slice(i * 2, i * 2 + 2), 16);
7
+ if (Number.isNaN(byte)) return new Uint8Array(0);
8
+ out[i] = byte;
9
+ }
10
+ return out;
31
11
  }
32
- //# sourceMappingURL=webhook-edge.js.map
12
+ function stripPrefix(sig) {
13
+ return sig.startsWith("sha256=") ? sig.slice(7) : sig;
14
+ }
15
+ function timingSafeEqualBytes(a, b) {
16
+ if (a.length !== b.length) return false;
17
+ let diff = 0;
18
+ for (let i = 0; i < a.length; i++) diff |= (a[i] ?? 0) ^ (b[i] ?? 0);
19
+ return diff === 0;
20
+ }
21
+
22
+ // src/webhook-edge.ts
23
+ async function verifyMinipimWebhookEdge(opts) {
24
+ const enc = new TextEncoder();
25
+ const keyData = enc.encode(opts.secret);
26
+ const bodyData = typeof opts.rawBody === "string" ? enc.encode(opts.rawBody) : new Uint8Array(opts.rawBody);
27
+ const key = await crypto.subtle.importKey(
28
+ "raw",
29
+ keyData,
30
+ { name: "HMAC", hash: "SHA-256" },
31
+ false,
32
+ ["sign"]
33
+ );
34
+ const sigBuf = await crypto.subtle.sign("HMAC", key, bodyData);
35
+ const expected = new Uint8Array(sigBuf);
36
+ const got = decodeHex(stripPrefix(opts.signature));
37
+ return timingSafeEqualBytes(expected, got);
38
+ }
39
+ export {
40
+ verifyMinipimWebhookEdge
41
+ };