@minipim/sdk 0.2.0 → 0.3.0
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 +3 -1
- package/dist/attributes.cjs +80 -0
- package/dist/attributes.d.cts +48 -0
- package/dist/attributes.d.ts +12 -11
- package/dist/attributes.js +46 -71
- package/dist/index.cjs +163 -0
- package/dist/index.d.cts +132 -0
- package/dist/index.d.ts +128 -13
- package/dist/index.js +117 -16
- package/dist/openapi.cjs +18 -0
- package/dist/openapi.d.cts +7042 -0
- package/dist/openapi.d.ts +183 -9
- package/dist/openapi.js +0 -6
- package/dist/webhook-edge.cjs +68 -0
- package/dist/webhook-edge.d.cts +26 -0
- package/dist/webhook-edge.d.ts +6 -4
- package/dist/webhook-edge.js +40 -31
- package/dist/{webhook-shared.d.ts → webhook-shared-CjMkFYVb.d.cts} +3 -6
- package/dist/webhook-shared-CjMkFYVb.d.ts +25 -0
- package/dist/webhook.cjs +53 -0
- package/dist/webhook.d.cts +26 -0
- package/dist/webhook.d.ts +5 -4
- package/dist/webhook.js +27 -39
- package/package.json +45 -13
- package/dist/attributes.d.ts.map +0 -1
- package/dist/attributes.js.map +0 -1
- package/dist/client.d.ts +0 -66
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -43
- package/dist/client.js.map +0 -1
- package/dist/errors.d.ts +0 -27
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -30
- package/dist/errors.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/openapi.d.ts.map +0 -1
- package/dist/openapi.js.map +0 -1
- package/dist/paginate.d.ts +0 -34
- package/dist/paginate.d.ts.map +0 -1
- package/dist/paginate.js +0 -43
- package/dist/paginate.js.map +0 -1
- package/dist/webhook-edge.d.ts.map +0 -1
- package/dist/webhook-edge.js.map +0 -1
- package/dist/webhook-shared.d.ts.map +0 -1
- package/dist/webhook-shared.js +0 -30
- package/dist/webhook-shared.js.map +0 -1
- package/dist/webhook.d.ts.map +0 -1
- 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
|
-
|
|
5
|
+
interface paths {
|
|
6
6
|
"/healthz": {
|
|
7
7
|
parameters: {
|
|
8
8
|
query?: never;
|
|
@@ -1176,7 +1176,7 @@ export interface paths {
|
|
|
1176
1176
|
brand?: string;
|
|
1177
1177
|
connectorId?: string;
|
|
1178
1178
|
updatedSince?: string;
|
|
1179
|
-
sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt";
|
|
1179
|
+
sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt" | "relevance";
|
|
1180
1180
|
sortDir?: "asc" | "desc";
|
|
1181
1181
|
limit?: number;
|
|
1182
1182
|
offset?: number;
|
|
@@ -1342,7 +1342,7 @@ export interface paths {
|
|
|
1342
1342
|
brand?: string;
|
|
1343
1343
|
connectorId?: string;
|
|
1344
1344
|
updatedSince?: string;
|
|
1345
|
-
sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt";
|
|
1345
|
+
sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt" | "relevance";
|
|
1346
1346
|
sortDir?: "asc" | "desc";
|
|
1347
1347
|
locale?: string;
|
|
1348
1348
|
channel?: string;
|
|
@@ -1725,6 +1725,9 @@ export interface paths {
|
|
|
1725
1725
|
missing: {
|
|
1726
1726
|
code: string;
|
|
1727
1727
|
locale: string | null;
|
|
1728
|
+
label?: string;
|
|
1729
|
+
variantsMissing?: number;
|
|
1730
|
+
variantsTotal?: number;
|
|
1728
1731
|
}[];
|
|
1729
1732
|
}[];
|
|
1730
1733
|
};
|
|
@@ -2139,6 +2142,36 @@ export interface paths {
|
|
|
2139
2142
|
}[];
|
|
2140
2143
|
};
|
|
2141
2144
|
};
|
|
2145
|
+
/** @description Default Response */
|
|
2146
|
+
404: {
|
|
2147
|
+
headers: {
|
|
2148
|
+
[name: string]: unknown;
|
|
2149
|
+
};
|
|
2150
|
+
content: {
|
|
2151
|
+
"application/json": {
|
|
2152
|
+
error: {
|
|
2153
|
+
code: string;
|
|
2154
|
+
message: string;
|
|
2155
|
+
details?: unknown;
|
|
2156
|
+
};
|
|
2157
|
+
};
|
|
2158
|
+
};
|
|
2159
|
+
};
|
|
2160
|
+
/** @description Default Response */
|
|
2161
|
+
422: {
|
|
2162
|
+
headers: {
|
|
2163
|
+
[name: string]: unknown;
|
|
2164
|
+
};
|
|
2165
|
+
content: {
|
|
2166
|
+
"application/json": {
|
|
2167
|
+
error: {
|
|
2168
|
+
code: string;
|
|
2169
|
+
message: string;
|
|
2170
|
+
details?: unknown;
|
|
2171
|
+
};
|
|
2172
|
+
};
|
|
2173
|
+
};
|
|
2174
|
+
};
|
|
2142
2175
|
};
|
|
2143
2176
|
};
|
|
2144
2177
|
put?: never;
|
|
@@ -5854,7 +5887,7 @@ export interface paths {
|
|
|
5854
5887
|
put?: never;
|
|
5855
5888
|
/**
|
|
5856
5889
|
* Trigger a sync (pull / push / both)
|
|
5857
|
-
* @description
|
|
5890
|
+
* @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
5891
|
*/
|
|
5859
5892
|
post: {
|
|
5860
5893
|
parameters: {
|
|
@@ -6365,6 +6398,146 @@ export interface paths {
|
|
|
6365
6398
|
patch?: never;
|
|
6366
6399
|
trace?: never;
|
|
6367
6400
|
};
|
|
6401
|
+
"/v1/connectors/{id}/deliveries": {
|
|
6402
|
+
parameters: {
|
|
6403
|
+
query?: never;
|
|
6404
|
+
header?: never;
|
|
6405
|
+
path?: never;
|
|
6406
|
+
cookie?: never;
|
|
6407
|
+
};
|
|
6408
|
+
/**
|
|
6409
|
+
* Recent outbound webhook deliveries for a connector
|
|
6410
|
+
* @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.
|
|
6411
|
+
*/
|
|
6412
|
+
get: {
|
|
6413
|
+
parameters: {
|
|
6414
|
+
query?: never;
|
|
6415
|
+
header?: never;
|
|
6416
|
+
path: {
|
|
6417
|
+
id: string;
|
|
6418
|
+
};
|
|
6419
|
+
cookie?: never;
|
|
6420
|
+
};
|
|
6421
|
+
requestBody?: never;
|
|
6422
|
+
responses: {
|
|
6423
|
+
/** @description Default Response */
|
|
6424
|
+
200: {
|
|
6425
|
+
headers: {
|
|
6426
|
+
[name: string]: unknown;
|
|
6427
|
+
};
|
|
6428
|
+
content: {
|
|
6429
|
+
"application/json": {
|
|
6430
|
+
/** Format: uuid */
|
|
6431
|
+
id: string;
|
|
6432
|
+
eventName: string;
|
|
6433
|
+
status: number | null;
|
|
6434
|
+
ok: boolean;
|
|
6435
|
+
error: string | null;
|
|
6436
|
+
/** Format: date-time */
|
|
6437
|
+
createdAt: string;
|
|
6438
|
+
}[];
|
|
6439
|
+
};
|
|
6440
|
+
};
|
|
6441
|
+
/** @description Default Response */
|
|
6442
|
+
404: {
|
|
6443
|
+
headers: {
|
|
6444
|
+
[name: string]: unknown;
|
|
6445
|
+
};
|
|
6446
|
+
content: {
|
|
6447
|
+
"application/json": {
|
|
6448
|
+
error: {
|
|
6449
|
+
code: string;
|
|
6450
|
+
message: string;
|
|
6451
|
+
details?: unknown;
|
|
6452
|
+
};
|
|
6453
|
+
};
|
|
6454
|
+
};
|
|
6455
|
+
};
|
|
6456
|
+
};
|
|
6457
|
+
};
|
|
6458
|
+
put?: never;
|
|
6459
|
+
post?: never;
|
|
6460
|
+
delete?: never;
|
|
6461
|
+
options?: never;
|
|
6462
|
+
head?: never;
|
|
6463
|
+
patch?: never;
|
|
6464
|
+
trace?: never;
|
|
6465
|
+
};
|
|
6466
|
+
"/v1/connectors/{id}/webhooks/test": {
|
|
6467
|
+
parameters: {
|
|
6468
|
+
query?: never;
|
|
6469
|
+
header?: never;
|
|
6470
|
+
path?: never;
|
|
6471
|
+
cookie?: never;
|
|
6472
|
+
};
|
|
6473
|
+
get?: never;
|
|
6474
|
+
put?: never;
|
|
6475
|
+
/**
|
|
6476
|
+
* Send a signed test event to the connector destination
|
|
6477
|
+
* @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.
|
|
6478
|
+
*/
|
|
6479
|
+
post: {
|
|
6480
|
+
parameters: {
|
|
6481
|
+
query?: never;
|
|
6482
|
+
header?: never;
|
|
6483
|
+
path: {
|
|
6484
|
+
id: string;
|
|
6485
|
+
};
|
|
6486
|
+
cookie?: never;
|
|
6487
|
+
};
|
|
6488
|
+
requestBody?: never;
|
|
6489
|
+
responses: {
|
|
6490
|
+
/** @description Default Response */
|
|
6491
|
+
200: {
|
|
6492
|
+
headers: {
|
|
6493
|
+
[name: string]: unknown;
|
|
6494
|
+
};
|
|
6495
|
+
content: {
|
|
6496
|
+
"application/json": {
|
|
6497
|
+
ok: boolean;
|
|
6498
|
+
status: number | null;
|
|
6499
|
+
error?: string;
|
|
6500
|
+
};
|
|
6501
|
+
};
|
|
6502
|
+
};
|
|
6503
|
+
/** @description Default Response */
|
|
6504
|
+
404: {
|
|
6505
|
+
headers: {
|
|
6506
|
+
[name: string]: unknown;
|
|
6507
|
+
};
|
|
6508
|
+
content: {
|
|
6509
|
+
"application/json": {
|
|
6510
|
+
error: {
|
|
6511
|
+
code: string;
|
|
6512
|
+
message: string;
|
|
6513
|
+
details?: unknown;
|
|
6514
|
+
};
|
|
6515
|
+
};
|
|
6516
|
+
};
|
|
6517
|
+
};
|
|
6518
|
+
/** @description Default Response */
|
|
6519
|
+
422: {
|
|
6520
|
+
headers: {
|
|
6521
|
+
[name: string]: unknown;
|
|
6522
|
+
};
|
|
6523
|
+
content: {
|
|
6524
|
+
"application/json": {
|
|
6525
|
+
error: {
|
|
6526
|
+
code: string;
|
|
6527
|
+
message: string;
|
|
6528
|
+
details?: unknown;
|
|
6529
|
+
};
|
|
6530
|
+
};
|
|
6531
|
+
};
|
|
6532
|
+
};
|
|
6533
|
+
};
|
|
6534
|
+
};
|
|
6535
|
+
delete?: never;
|
|
6536
|
+
options?: never;
|
|
6537
|
+
head?: never;
|
|
6538
|
+
patch?: never;
|
|
6539
|
+
trace?: never;
|
|
6540
|
+
};
|
|
6368
6541
|
"/v1/connectors/{id}/webhooks/enable": {
|
|
6369
6542
|
parameters: {
|
|
6370
6543
|
query?: never;
|
|
@@ -6854,8 +7027,8 @@ export interface paths {
|
|
|
6854
7027
|
trace?: never;
|
|
6855
7028
|
};
|
|
6856
7029
|
}
|
|
6857
|
-
|
|
6858
|
-
|
|
7030
|
+
type webhooks = Record<string, never>;
|
|
7031
|
+
interface components {
|
|
6859
7032
|
schemas: never;
|
|
6860
7033
|
responses: never;
|
|
6861
7034
|
parameters: never;
|
|
@@ -6863,6 +7036,7 @@ export interface components {
|
|
|
6863
7036
|
headers: never;
|
|
6864
7037
|
pathItems: never;
|
|
6865
7038
|
}
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
7039
|
+
type $defs = Record<string, never>;
|
|
7040
|
+
type operations = Record<string, never>;
|
|
7041
|
+
|
|
7042
|
+
export type { $defs, components, operations, paths, webhooks };
|
package/dist/openapi.js
CHANGED
|
@@ -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 };
|
package/dist/webhook-edge.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
24
|
-
|
|
24
|
+
declare function verifyMinipimWebhookEdge(opts: VerifyWebhookOptions): Promise<boolean>;
|
|
25
|
+
|
|
26
|
+
export { VerifyWebhookOptions, verifyMinipimWebhookEdge };
|
package/dist/webhook-edge.js
CHANGED
|
@@ -1,32 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
+
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* free of `node:crypto` and WebCrypto so importing this file drags neither
|
|
4
4
|
* runtime's crypto into a bundle.
|
|
5
5
|
*/
|
|
6
|
-
|
|
6
|
+
interface VerifyWebhookOptions {
|
|
7
7
|
/**
|
|
8
8
|
* The **raw** request body — bytes or a string. **Not** the parsed JSON.
|
|
9
9
|
* Re-stringifying the parsed body reorders keys / changes whitespace and
|
|
@@ -21,8 +21,5 @@ export interface VerifyWebhookOptions {
|
|
|
21
21
|
/** The secret configured on the headless connector. */
|
|
22
22
|
secret: string;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
export
|
|
26
|
-
/** Constant-time byte compare — used by the Edge verifier (Node uses crypto.timingSafeEqual). */
|
|
27
|
-
export declare function timingSafeEqualBytes(a: Uint8Array, b: Uint8Array): boolean;
|
|
28
|
-
//# sourceMappingURL=webhook-shared.d.ts.map
|
|
24
|
+
|
|
25
|
+
export type { VerifyWebhookOptions as V };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers shared by the Node and Edge webhook verifiers. Deliberately
|
|
3
|
+
* free of `node:crypto` and WebCrypto so importing this file drags neither
|
|
4
|
+
* runtime's crypto into a bundle.
|
|
5
|
+
*/
|
|
6
|
+
interface VerifyWebhookOptions {
|
|
7
|
+
/**
|
|
8
|
+
* The **raw** request body — bytes or a string. **Not** the parsed JSON.
|
|
9
|
+
* Re-stringifying the parsed body reorders keys / changes whitespace and
|
|
10
|
+
* the HMAC will not match.
|
|
11
|
+
*
|
|
12
|
+
* Next.js route handler: `const raw = await req.text()`.
|
|
13
|
+
* Express: `express.raw({ type: 'application/json' })`, then `req.body`.
|
|
14
|
+
*/
|
|
15
|
+
rawBody: string | Uint8Array;
|
|
16
|
+
/**
|
|
17
|
+
* Value of the `X-MiniPim-Signature-256` header — `sha256=<hex>`. Pass it
|
|
18
|
+
* verbatim; the verifier strips the prefix.
|
|
19
|
+
*/
|
|
20
|
+
signature: string;
|
|
21
|
+
/** The secret configured on the headless connector. */
|
|
22
|
+
secret: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type { VerifyWebhookOptions as V };
|
package/dist/webhook.cjs
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
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.ts
|
|
21
|
+
var webhook_exports = {};
|
|
22
|
+
__export(webhook_exports, {
|
|
23
|
+
verifyMinipimWebhook: () => verifyMinipimWebhook
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(webhook_exports);
|
|
26
|
+
var import_node_crypto = require("crypto");
|
|
27
|
+
|
|
28
|
+
// src/webhook-shared.ts
|
|
29
|
+
function decodeHex(s) {
|
|
30
|
+
if (s.length % 2 !== 0) return new Uint8Array(0);
|
|
31
|
+
const out = new Uint8Array(s.length / 2);
|
|
32
|
+
for (let i = 0; i < out.length; i++) {
|
|
33
|
+
const byte = parseInt(s.slice(i * 2, i * 2 + 2), 16);
|
|
34
|
+
if (Number.isNaN(byte)) return new Uint8Array(0);
|
|
35
|
+
out[i] = byte;
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
function stripPrefix(sig) {
|
|
40
|
+
return sig.startsWith("sha256=") ? sig.slice(7) : sig;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/webhook.ts
|
|
44
|
+
function verifyMinipimWebhook(opts) {
|
|
45
|
+
const expected = (0, import_node_crypto.createHmac)("sha256", opts.secret).update(opts.rawBody).digest();
|
|
46
|
+
const got = decodeHex(stripPrefix(opts.signature));
|
|
47
|
+
if (got.length !== expected.length) return false;
|
|
48
|
+
return (0, import_node_crypto.timingSafeEqual)(expected, got);
|
|
49
|
+
}
|
|
50
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
51
|
+
0 && (module.exports = {
|
|
52
|
+
verifyMinipimWebhook
|
|
53
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { V as VerifyWebhookOptions } from './webhook-shared-CjMkFYVb.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Verify a MiniPim webhook signature in Node. Returns `true` only when the
|
|
5
|
+
* signature is valid; returns `false` on any problem (wrong signature,
|
|
6
|
+
* malformed header, length mismatch). Constant-time comparison.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { verifyMinipimWebhook } from '@minipim/sdk/webhook';
|
|
11
|
+
*
|
|
12
|
+
* export async function POST(req: Request) {
|
|
13
|
+
* const raw = await req.text();
|
|
14
|
+
* const sig = req.headers.get('x-minipim-signature-256') ?? '';
|
|
15
|
+
* if (!verifyMinipimWebhook({ rawBody: raw, signature: sig, secret })) {
|
|
16
|
+
* return new Response('invalid signature', { status: 401 });
|
|
17
|
+
* }
|
|
18
|
+
* const event = JSON.parse(raw);
|
|
19
|
+
* // ...handle event, respond 2xx
|
|
20
|
+
* return Response.json({ ok: true });
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
declare function verifyMinipimWebhook(opts: VerifyWebhookOptions): boolean;
|
|
25
|
+
|
|
26
|
+
export { VerifyWebhookOptions, verifyMinipimWebhook };
|
package/dist/webhook.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { V as VerifyWebhookOptions } from './webhook-shared-CjMkFYVb.js';
|
|
2
|
+
|
|
3
3
|
/**
|
|
4
4
|
* Verify a MiniPim webhook signature in Node. Returns `true` only when the
|
|
5
5
|
* signature is valid; returns `false` on any problem (wrong signature,
|
|
@@ -21,5 +21,6 @@ export type { VerifyWebhookOptions } from './webhook-shared.js';
|
|
|
21
21
|
* }
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
declare function verifyMinipimWebhook(opts: VerifyWebhookOptions): boolean;
|
|
25
|
+
|
|
26
|
+
export { VerifyWebhookOptions, verifyMinipimWebhook };
|
package/dist/webhook.js
CHANGED
|
@@ -1,40 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* signature is valid; returns `false` on any problem (wrong signature,
|
|
15
|
-
* malformed header, length mismatch). Constant-time comparison.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```ts
|
|
19
|
-
* import { verifyMinipimWebhook } from '@minipim/sdk/webhook';
|
|
20
|
-
*
|
|
21
|
-
* export async function POST(req: Request) {
|
|
22
|
-
* const raw = await req.text();
|
|
23
|
-
* const sig = req.headers.get('x-minipim-signature-256') ?? '';
|
|
24
|
-
* if (!verifyMinipimWebhook({ rawBody: raw, signature: sig, secret })) {
|
|
25
|
-
* return new Response('invalid signature', { status: 401 });
|
|
26
|
-
* }
|
|
27
|
-
* const event = JSON.parse(raw);
|
|
28
|
-
* // ...handle event, respond 2xx
|
|
29
|
-
* return Response.json({ ok: true });
|
|
30
|
-
* }
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export function verifyMinipimWebhook(opts) {
|
|
34
|
-
const expected = createHmac('sha256', opts.secret).update(opts.rawBody).digest();
|
|
35
|
-
const got = decodeHex(stripPrefix(opts.signature));
|
|
36
|
-
if (got.length !== expected.length)
|
|
37
|
-
return false;
|
|
38
|
-
return timingSafeEqual(expected, got);
|
|
1
|
+
// src/webhook.ts
|
|
2
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
3
|
+
|
|
4
|
+
// src/webhook-shared.ts
|
|
5
|
+
function decodeHex(s) {
|
|
6
|
+
if (s.length % 2 !== 0) return new Uint8Array(0);
|
|
7
|
+
const out = new Uint8Array(s.length / 2);
|
|
8
|
+
for (let i = 0; i < out.length; i++) {
|
|
9
|
+
const byte = parseInt(s.slice(i * 2, i * 2 + 2), 16);
|
|
10
|
+
if (Number.isNaN(byte)) return new Uint8Array(0);
|
|
11
|
+
out[i] = byte;
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
39
14
|
}
|
|
40
|
-
|
|
15
|
+
function stripPrefix(sig) {
|
|
16
|
+
return sig.startsWith("sha256=") ? sig.slice(7) : sig;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/webhook.ts
|
|
20
|
+
function verifyMinipimWebhook(opts) {
|
|
21
|
+
const expected = createHmac("sha256", opts.secret).update(opts.rawBody).digest();
|
|
22
|
+
const got = decodeHex(stripPrefix(opts.signature));
|
|
23
|
+
if (got.length !== expected.length) return false;
|
|
24
|
+
return timingSafeEqual(expected, got);
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
verifyMinipimWebhook
|
|
28
|
+
};
|