@cstar.help/js 0.12.0 → 0.13.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/dist/auth/index.cjs +227 -13
- package/dist/auth/index.d.cts +4 -4
- package/dist/auth/index.d.ts +4 -4
- package/dist/auth/index.js +225 -13
- package/dist/chat/index.cjs +46 -26
- package/dist/chat/index.d.cts +28 -24
- package/dist/chat/index.d.ts +28 -24
- package/dist/chat/index.js +46 -26
- package/dist/community/index.cjs +233 -9
- package/dist/community/index.d.cts +11 -0
- package/dist/community/index.d.ts +11 -0
- package/dist/community/index.js +231 -9
- package/dist/index.cjs +268 -73
- package/dist/index.d.cts +216 -73
- package/dist/index.d.ts +216 -73
- package/dist/index.js +267 -73
- package/dist/library/index.cjs +254 -9
- package/dist/library/index.d.cts +26 -3
- package/dist/library/index.d.ts +26 -3
- package/dist/library/index.js +252 -9
- package/dist/quickhelp/index.cjs +176 -2
- package/dist/quickhelp/index.d.cts +11 -0
- package/dist/quickhelp/index.d.ts +11 -0
- package/dist/quickhelp/index.js +174 -2
- package/dist/webhook/index.cjs +0 -18
- package/dist/webhook/index.d.cts +18 -29
- package/dist/webhook/index.d.ts +18 -29
- package/dist/webhook/index.js +0 -23
- package/dist/webhook/node.cjs +57 -0
- package/dist/webhook/node.d.cts +440 -0
- package/dist/webhook/node.d.ts +440 -0
- package/dist/webhook/node.js +30 -0
- package/package.json +14 -2
package/dist/quickhelp/index.js
CHANGED
|
@@ -1,3 +1,155 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var ErrorType = {
|
|
3
|
+
AUTHENTICATION_ERROR: "authentication_error",
|
|
4
|
+
PERMISSION_ERROR: "permission_error",
|
|
5
|
+
INVALID_REQUEST_ERROR: "invalid_request_error",
|
|
6
|
+
NOT_FOUND_ERROR: "not_found_error",
|
|
7
|
+
CONFLICT_ERROR: "conflict_error",
|
|
8
|
+
RATE_LIMIT_ERROR: "rate_limit_error",
|
|
9
|
+
FEATURE_NOT_CONFIGURED: "feature_not_configured",
|
|
10
|
+
API_ERROR: "api_error",
|
|
11
|
+
SERVER_ERROR: "server_error"
|
|
12
|
+
};
|
|
13
|
+
var DEFAULT_DOC_URL = "https://cstar.help/developers/api/errors";
|
|
14
|
+
function normalizeArgs(messageOrStatus, maybeBody, defaults) {
|
|
15
|
+
if (typeof messageOrStatus === "string") {
|
|
16
|
+
return {
|
|
17
|
+
statusCode: defaults?.code === "rate_limit_error" ? 429 : 0,
|
|
18
|
+
body: {
|
|
19
|
+
type: defaults?.type ?? ErrorType.API_ERROR,
|
|
20
|
+
code: defaults?.code ?? "sdk_error",
|
|
21
|
+
message: messageOrStatus,
|
|
22
|
+
doc_url: defaults?.doc_url ?? DEFAULT_DOC_URL,
|
|
23
|
+
...defaults?.feature ? { feature: defaults.feature } : {},
|
|
24
|
+
...defaults?.enable_url ? { enable_url: defaults.enable_url } : {}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
statusCode: messageOrStatus,
|
|
30
|
+
body: maybeBody ?? {
|
|
31
|
+
type: defaults?.type ?? ErrorType.API_ERROR,
|
|
32
|
+
code: defaults?.code ?? "unknown_error",
|
|
33
|
+
message: defaults?.message ?? `Request failed with status ${messageOrStatus}`,
|
|
34
|
+
doc_url: defaults?.doc_url ?? DEFAULT_DOC_URL
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
var CStarError = class extends Error {
|
|
39
|
+
constructor(messageOrStatus, body) {
|
|
40
|
+
const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body);
|
|
41
|
+
super(resolved.message);
|
|
42
|
+
this.name = "CStarError";
|
|
43
|
+
this.type = resolved.type;
|
|
44
|
+
this.code = resolved.code;
|
|
45
|
+
this.param = resolved.param;
|
|
46
|
+
this.docUrl = resolved.doc_url;
|
|
47
|
+
this.requestId = resolved.request_id;
|
|
48
|
+
this.statusCode = statusCode;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
var CStarAuthenticationError = class extends CStarError {
|
|
52
|
+
constructor(messageOrStatus, body) {
|
|
53
|
+
const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
|
|
54
|
+
type: ErrorType.AUTHENTICATION_ERROR,
|
|
55
|
+
code: "authentication_failed",
|
|
56
|
+
doc_url: `${DEFAULT_DOC_URL}#authentication-error`
|
|
57
|
+
});
|
|
58
|
+
super(statusCode, resolved);
|
|
59
|
+
this.name = "CStarAuthenticationError";
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
var CStarRateLimitError = class extends CStarError {
|
|
63
|
+
constructor(messageOrStatus, body, retryAfter) {
|
|
64
|
+
const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
|
|
65
|
+
type: ErrorType.RATE_LIMIT_ERROR,
|
|
66
|
+
code: "rate_limit_exceeded",
|
|
67
|
+
doc_url: `${DEFAULT_DOC_URL}#rate-limit-error`
|
|
68
|
+
});
|
|
69
|
+
super(statusCode === 0 ? 429 : statusCode, resolved);
|
|
70
|
+
this.name = "CStarRateLimitError";
|
|
71
|
+
this.retryAfter = retryAfter ?? 60;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var CStarNotFoundError = class extends CStarError {
|
|
75
|
+
constructor(messageOrStatus, body) {
|
|
76
|
+
const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
|
|
77
|
+
type: ErrorType.NOT_FOUND_ERROR,
|
|
78
|
+
code: "resource_not_found",
|
|
79
|
+
doc_url: `${DEFAULT_DOC_URL}#not-found-error`
|
|
80
|
+
});
|
|
81
|
+
super(statusCode, resolved);
|
|
82
|
+
this.name = "CStarNotFoundError";
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
var CStarValidationError = class extends CStarError {
|
|
86
|
+
constructor(messageOrStatus, body) {
|
|
87
|
+
const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
|
|
88
|
+
type: ErrorType.INVALID_REQUEST_ERROR,
|
|
89
|
+
code: "validation_error",
|
|
90
|
+
doc_url: `${DEFAULT_DOC_URL}#validation-error`
|
|
91
|
+
});
|
|
92
|
+
super(statusCode, resolved);
|
|
93
|
+
this.name = "CStarValidationError";
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var CStarPermissionError = class extends CStarError {
|
|
97
|
+
constructor(messageOrStatus, body) {
|
|
98
|
+
const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
|
|
99
|
+
type: ErrorType.PERMISSION_ERROR,
|
|
100
|
+
code: "permission_denied",
|
|
101
|
+
doc_url: `${DEFAULT_DOC_URL}#permission-error`
|
|
102
|
+
});
|
|
103
|
+
super(statusCode, resolved);
|
|
104
|
+
this.name = "CStarPermissionError";
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var CStarConflictError = class extends CStarError {
|
|
108
|
+
constructor(messageOrStatus, body) {
|
|
109
|
+
const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
|
|
110
|
+
type: ErrorType.CONFLICT_ERROR,
|
|
111
|
+
code: "conflict",
|
|
112
|
+
doc_url: `${DEFAULT_DOC_URL}#conflict-error`
|
|
113
|
+
});
|
|
114
|
+
super(statusCode, resolved);
|
|
115
|
+
this.name = "CStarConflictError";
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
var CStarServerError = class extends CStarError {
|
|
119
|
+
constructor(messageOrStatus, body) {
|
|
120
|
+
const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
|
|
121
|
+
type: ErrorType.SERVER_ERROR,
|
|
122
|
+
code: "server_error",
|
|
123
|
+
doc_url: `${DEFAULT_DOC_URL}#server-error`
|
|
124
|
+
});
|
|
125
|
+
super(statusCode, resolved);
|
|
126
|
+
this.name = "CStarServerError";
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
var CStarFeatureDisabledError = class extends CStarError {
|
|
130
|
+
constructor(messageOrStatus, body) {
|
|
131
|
+
const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
|
|
132
|
+
type: ErrorType.FEATURE_NOT_CONFIGURED,
|
|
133
|
+
code: "feature_not_configured",
|
|
134
|
+
doc_url: `${DEFAULT_DOC_URL}#feature-not-configured`
|
|
135
|
+
});
|
|
136
|
+
super(statusCode, resolved);
|
|
137
|
+
this.name = "CStarFeatureDisabledError";
|
|
138
|
+
this.feature = resolved.feature ?? "unknown";
|
|
139
|
+
this.enableUrl = resolved.enable_url ?? "";
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
var TYPE_TO_CLASS = {
|
|
143
|
+
[ErrorType.AUTHENTICATION_ERROR]: CStarAuthenticationError,
|
|
144
|
+
[ErrorType.PERMISSION_ERROR]: CStarPermissionError,
|
|
145
|
+
[ErrorType.RATE_LIMIT_ERROR]: CStarRateLimitError,
|
|
146
|
+
[ErrorType.NOT_FOUND_ERROR]: CStarNotFoundError,
|
|
147
|
+
[ErrorType.INVALID_REQUEST_ERROR]: CStarValidationError,
|
|
148
|
+
[ErrorType.CONFLICT_ERROR]: CStarConflictError,
|
|
149
|
+
[ErrorType.SERVER_ERROR]: CStarServerError,
|
|
150
|
+
[ErrorType.FEATURE_NOT_CONFIGURED]: CStarFeatureDisabledError
|
|
151
|
+
};
|
|
152
|
+
|
|
1
153
|
// src/quickhelp/index.ts
|
|
2
154
|
var DEFAULT_BASE_URL = "https://www.cstar.help";
|
|
3
155
|
var QuickHelpClient = class {
|
|
@@ -7,6 +159,16 @@ var QuickHelpClient = class {
|
|
|
7
159
|
}
|
|
8
160
|
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
9
161
|
this.teamId = config.teamId;
|
|
162
|
+
this.publishableKey = config.publishableKey ?? null;
|
|
163
|
+
this.mode = this.publishableKey?.startsWith("pk_test_") ? "test" : "live";
|
|
164
|
+
if (this.publishableKey && !/^pk_(test|live)_/.test(this.publishableKey)) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
"[cStar] QuickHelpClient: publishableKey must start with `pk_test_` or `pk_live_`. Secret keys (`sk_*`) cannot be used in browser/anonymous contexts."
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
authHeaders() {
|
|
171
|
+
return this.publishableKey ? { Authorization: `Bearer ${this.publishableKey}` } : {};
|
|
10
172
|
}
|
|
11
173
|
/**
|
|
12
174
|
* Fetch the sanitized public config (title, subtitle, placeholder,
|
|
@@ -17,7 +179,7 @@ var QuickHelpClient = class {
|
|
|
17
179
|
*/
|
|
18
180
|
async config() {
|
|
19
181
|
const url = `${this.baseUrl}/api/quickhelp/config/${encodeURIComponent(this.teamId)}`;
|
|
20
|
-
const res = await globalThis.fetch(url, { method: "GET" });
|
|
182
|
+
const res = await globalThis.fetch(url, { method: "GET", headers: this.authHeaders() });
|
|
21
183
|
if (res.status === 404) return null;
|
|
22
184
|
if (!res.ok) {
|
|
23
185
|
const body2 = await res.json().catch(() => null);
|
|
@@ -36,7 +198,7 @@ var QuickHelpClient = class {
|
|
|
36
198
|
const url = `${this.baseUrl}/api/quickhelp/ask`;
|
|
37
199
|
const res = await globalThis.fetch(url, {
|
|
38
200
|
method: "POST",
|
|
39
|
-
headers: { "Content-Type": "application/json" },
|
|
201
|
+
headers: { "Content-Type": "application/json", ...this.authHeaders() },
|
|
40
202
|
body: JSON.stringify({
|
|
41
203
|
team_id: this.teamId,
|
|
42
204
|
session_id: params.sessionId ?? null,
|
|
@@ -45,6 +207,16 @@ var QuickHelpClient = class {
|
|
|
45
207
|
});
|
|
46
208
|
const body = await res.json().catch(() => null);
|
|
47
209
|
if (!res.ok || !body?.success || !body.data) {
|
|
210
|
+
if (res.status === 503 || body?.error?.code === "feature_not_configured") {
|
|
211
|
+
throw new CStarFeatureDisabledError(res.status, {
|
|
212
|
+
type: "feature_not_configured",
|
|
213
|
+
code: "feature_not_configured",
|
|
214
|
+
message: body?.error?.message ?? "Quick Help is not available on this team. Enable an AI Agent (Settings \u2192 AI Agents) and configure Quick Help (Settings \u2192 Quick Help) before calling .ask().",
|
|
215
|
+
doc_url: "https://cstar.help/developers/api/errors#feature-not-configured",
|
|
216
|
+
feature: "quickHelp",
|
|
217
|
+
enable_url: `${this.baseUrl}/team/${encodeURIComponent(this.teamId)}/settings?section=quickHelp`
|
|
218
|
+
});
|
|
219
|
+
}
|
|
48
220
|
const message = body?.error?.message ?? `Quick Help ask failed: ${res.status}`;
|
|
49
221
|
const err = new QuickHelpError(message, {
|
|
50
222
|
status: res.status,
|
package/dist/webhook/index.cjs
CHANGED
|
@@ -21,9 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var webhook_exports = {};
|
|
22
22
|
__export(webhook_exports, {
|
|
23
23
|
WebhookSignatureVerificationError: () => WebhookSignatureVerificationError,
|
|
24
|
-
constructEvent: () => constructEvent,
|
|
25
24
|
constructEventAsync: () => constructEventAsync,
|
|
26
|
-
verifySignature: () => verifySignature,
|
|
27
25
|
verifySignatureAsync: () => verifySignatureAsync
|
|
28
26
|
});
|
|
29
27
|
module.exports = __toCommonJS(webhook_exports);
|
|
@@ -33,14 +31,6 @@ var WebhookSignatureVerificationError = class extends Error {
|
|
|
33
31
|
this.name = "WebhookSignatureVerificationError";
|
|
34
32
|
}
|
|
35
33
|
};
|
|
36
|
-
function constructEvent(rawBody, signature, secret) {
|
|
37
|
-
if (!verifySignature(rawBody, signature, secret)) {
|
|
38
|
-
throw new WebhookSignatureVerificationError(
|
|
39
|
-
"Webhook signature verification failed. Ensure you are using the raw request body and the correct endpoint secret."
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
return JSON.parse(rawBody);
|
|
43
|
-
}
|
|
44
34
|
async function constructEventAsync(rawBody, signature, secret) {
|
|
45
35
|
const valid = await verifySignatureAsync(rawBody, signature, secret);
|
|
46
36
|
if (!valid) {
|
|
@@ -50,12 +40,6 @@ async function constructEventAsync(rawBody, signature, secret) {
|
|
|
50
40
|
}
|
|
51
41
|
return JSON.parse(rawBody);
|
|
52
42
|
}
|
|
53
|
-
function verifySignature(rawBody, signature, secret) {
|
|
54
|
-
const crypto2 = require("crypto");
|
|
55
|
-
const expected = "sha256=" + crypto2.createHmac("sha256", secret).update(rawBody).digest("hex");
|
|
56
|
-
if (signature.length !== expected.length) return false;
|
|
57
|
-
return crypto2.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
|
|
58
|
-
}
|
|
59
43
|
async function verifySignatureAsync(rawBody, signature, secret) {
|
|
60
44
|
const encoder = new TextEncoder();
|
|
61
45
|
const key = await crypto.subtle.importKey(
|
|
@@ -77,8 +61,6 @@ async function verifySignatureAsync(rawBody, signature, secret) {
|
|
|
77
61
|
// Annotate the CommonJS export names for ESM import in node:
|
|
78
62
|
0 && (module.exports = {
|
|
79
63
|
WebhookSignatureVerificationError,
|
|
80
|
-
constructEvent,
|
|
81
64
|
constructEventAsync,
|
|
82
|
-
verifySignature,
|
|
83
65
|
verifySignatureAsync
|
|
84
66
|
});
|
package/dist/webhook/index.d.cts
CHANGED
|
@@ -298,6 +298,10 @@ interface TicketClosedPayload {
|
|
|
298
298
|
channel: string;
|
|
299
299
|
resolved_at: string;
|
|
300
300
|
};
|
|
301
|
+
changes?: Record<string, {
|
|
302
|
+
from: string;
|
|
303
|
+
to: string;
|
|
304
|
+
}>;
|
|
301
305
|
}
|
|
302
306
|
/** Payload for `ticket.created` events. */
|
|
303
307
|
interface TicketCreatedPayload {
|
|
@@ -350,12 +354,13 @@ interface TicketUpdatedPayload {
|
|
|
350
354
|
priority: string;
|
|
351
355
|
channel: string;
|
|
352
356
|
};
|
|
353
|
-
changes
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
357
|
+
changes?: Record<string, {
|
|
358
|
+
from: string;
|
|
359
|
+
to: string;
|
|
360
|
+
} | {
|
|
361
|
+
from: Array<string>;
|
|
362
|
+
to: Array<string>;
|
|
363
|
+
}>;
|
|
359
364
|
}
|
|
360
365
|
/** Maps each event type to its payload interface — used internally for the discriminated union. */
|
|
361
366
|
interface WebhookEventDataMap {
|
|
@@ -412,23 +417,12 @@ declare class WebhookSignatureVerificationError extends Error {
|
|
|
412
417
|
constructor(message: string);
|
|
413
418
|
}
|
|
414
419
|
/**
|
|
415
|
-
* Verify a webhook signature and parse the event
|
|
420
|
+
* Verify a webhook signature and parse the event.
|
|
416
421
|
*
|
|
417
|
-
* Uses
|
|
418
|
-
*
|
|
422
|
+
* Uses the Web Crypto API — works in browsers, Cloudflare Workers, Vercel Edge,
|
|
423
|
+
* Deno, Bun, and Node.js 18+. This is the recommended API for all runtimes.
|
|
419
424
|
*
|
|
420
|
-
*
|
|
421
|
-
* @param signature - Value of the `X-Signature` header
|
|
422
|
-
* @param secret - Your webhook endpoint's secret (`whsec_*`)
|
|
423
|
-
* @returns Parsed and verified webhook event
|
|
424
|
-
* @throws {WebhookSignatureVerificationError} If verification fails
|
|
425
|
-
*/
|
|
426
|
-
declare function constructEvent(rawBody: string, signature: string, secret: string): WebhookEvent;
|
|
427
|
-
/**
|
|
428
|
-
* Verify a webhook signature and parse the event (async, works everywhere).
|
|
429
|
-
*
|
|
430
|
-
* Uses the Web Crypto API — compatible with browsers, Deno, Cloudflare Workers,
|
|
431
|
-
* and Node.js 18+.
|
|
425
|
+
* For a synchronous Node-only variant, import from `@cstar.help/js/webhook/node`.
|
|
432
426
|
*
|
|
433
427
|
* @param rawBody - Raw request body as a string (NOT parsed JSON)
|
|
434
428
|
* @param signature - Value of the `X-Signature` header
|
|
@@ -438,14 +432,9 @@ declare function constructEvent(rawBody: string, signature: string, secret: stri
|
|
|
438
432
|
*/
|
|
439
433
|
declare function constructEventAsync(rawBody: string, signature: string, secret: string): Promise<WebhookEvent>;
|
|
440
434
|
/**
|
|
441
|
-
* Verify an HMAC-SHA256 signature
|
|
442
|
-
*
|
|
443
|
-
*/
|
|
444
|
-
declare function verifySignature(rawBody: string, signature: string, secret: string): boolean;
|
|
445
|
-
/**
|
|
446
|
-
* Verify an HMAC-SHA256 signature (async, works everywhere).
|
|
447
|
-
* Uses the Web Crypto API with constant-time comparison.
|
|
435
|
+
* Verify an HMAC-SHA256 signature using the Web Crypto API.
|
|
436
|
+
* Constant-time comparison to prevent timing attacks.
|
|
448
437
|
*/
|
|
449
438
|
declare function verifySignatureAsync(rawBody: string, signature: string, secret: string): Promise<boolean>;
|
|
450
439
|
|
|
451
|
-
export { type WebhookEvent, WebhookSignatureVerificationError,
|
|
440
|
+
export { type WebhookEvent, WebhookSignatureVerificationError, constructEventAsync, verifySignatureAsync };
|
package/dist/webhook/index.d.ts
CHANGED
|
@@ -298,6 +298,10 @@ interface TicketClosedPayload {
|
|
|
298
298
|
channel: string;
|
|
299
299
|
resolved_at: string;
|
|
300
300
|
};
|
|
301
|
+
changes?: Record<string, {
|
|
302
|
+
from: string;
|
|
303
|
+
to: string;
|
|
304
|
+
}>;
|
|
301
305
|
}
|
|
302
306
|
/** Payload for `ticket.created` events. */
|
|
303
307
|
interface TicketCreatedPayload {
|
|
@@ -350,12 +354,13 @@ interface TicketUpdatedPayload {
|
|
|
350
354
|
priority: string;
|
|
351
355
|
channel: string;
|
|
352
356
|
};
|
|
353
|
-
changes
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
357
|
+
changes?: Record<string, {
|
|
358
|
+
from: string;
|
|
359
|
+
to: string;
|
|
360
|
+
} | {
|
|
361
|
+
from: Array<string>;
|
|
362
|
+
to: Array<string>;
|
|
363
|
+
}>;
|
|
359
364
|
}
|
|
360
365
|
/** Maps each event type to its payload interface — used internally for the discriminated union. */
|
|
361
366
|
interface WebhookEventDataMap {
|
|
@@ -412,23 +417,12 @@ declare class WebhookSignatureVerificationError extends Error {
|
|
|
412
417
|
constructor(message: string);
|
|
413
418
|
}
|
|
414
419
|
/**
|
|
415
|
-
* Verify a webhook signature and parse the event
|
|
420
|
+
* Verify a webhook signature and parse the event.
|
|
416
421
|
*
|
|
417
|
-
* Uses
|
|
418
|
-
*
|
|
422
|
+
* Uses the Web Crypto API — works in browsers, Cloudflare Workers, Vercel Edge,
|
|
423
|
+
* Deno, Bun, and Node.js 18+. This is the recommended API for all runtimes.
|
|
419
424
|
*
|
|
420
|
-
*
|
|
421
|
-
* @param signature - Value of the `X-Signature` header
|
|
422
|
-
* @param secret - Your webhook endpoint's secret (`whsec_*`)
|
|
423
|
-
* @returns Parsed and verified webhook event
|
|
424
|
-
* @throws {WebhookSignatureVerificationError} If verification fails
|
|
425
|
-
*/
|
|
426
|
-
declare function constructEvent(rawBody: string, signature: string, secret: string): WebhookEvent;
|
|
427
|
-
/**
|
|
428
|
-
* Verify a webhook signature and parse the event (async, works everywhere).
|
|
429
|
-
*
|
|
430
|
-
* Uses the Web Crypto API — compatible with browsers, Deno, Cloudflare Workers,
|
|
431
|
-
* and Node.js 18+.
|
|
425
|
+
* For a synchronous Node-only variant, import from `@cstar.help/js/webhook/node`.
|
|
432
426
|
*
|
|
433
427
|
* @param rawBody - Raw request body as a string (NOT parsed JSON)
|
|
434
428
|
* @param signature - Value of the `X-Signature` header
|
|
@@ -438,14 +432,9 @@ declare function constructEvent(rawBody: string, signature: string, secret: stri
|
|
|
438
432
|
*/
|
|
439
433
|
declare function constructEventAsync(rawBody: string, signature: string, secret: string): Promise<WebhookEvent>;
|
|
440
434
|
/**
|
|
441
|
-
* Verify an HMAC-SHA256 signature
|
|
442
|
-
*
|
|
443
|
-
*/
|
|
444
|
-
declare function verifySignature(rawBody: string, signature: string, secret: string): boolean;
|
|
445
|
-
/**
|
|
446
|
-
* Verify an HMAC-SHA256 signature (async, works everywhere).
|
|
447
|
-
* Uses the Web Crypto API with constant-time comparison.
|
|
435
|
+
* Verify an HMAC-SHA256 signature using the Web Crypto API.
|
|
436
|
+
* Constant-time comparison to prevent timing attacks.
|
|
448
437
|
*/
|
|
449
438
|
declare function verifySignatureAsync(rawBody: string, signature: string, secret: string): Promise<boolean>;
|
|
450
439
|
|
|
451
|
-
export { type WebhookEvent, WebhookSignatureVerificationError,
|
|
440
|
+
export { type WebhookEvent, WebhookSignatureVerificationError, constructEventAsync, verifySignatureAsync };
|
package/dist/webhook/index.js
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/webhook/index.ts
|
|
9
2
|
var WebhookSignatureVerificationError = class extends Error {
|
|
10
3
|
constructor(message) {
|
|
@@ -12,14 +5,6 @@ var WebhookSignatureVerificationError = class extends Error {
|
|
|
12
5
|
this.name = "WebhookSignatureVerificationError";
|
|
13
6
|
}
|
|
14
7
|
};
|
|
15
|
-
function constructEvent(rawBody, signature, secret) {
|
|
16
|
-
if (!verifySignature(rawBody, signature, secret)) {
|
|
17
|
-
throw new WebhookSignatureVerificationError(
|
|
18
|
-
"Webhook signature verification failed. Ensure you are using the raw request body and the correct endpoint secret."
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
return JSON.parse(rawBody);
|
|
22
|
-
}
|
|
23
8
|
async function constructEventAsync(rawBody, signature, secret) {
|
|
24
9
|
const valid = await verifySignatureAsync(rawBody, signature, secret);
|
|
25
10
|
if (!valid) {
|
|
@@ -29,12 +14,6 @@ async function constructEventAsync(rawBody, signature, secret) {
|
|
|
29
14
|
}
|
|
30
15
|
return JSON.parse(rawBody);
|
|
31
16
|
}
|
|
32
|
-
function verifySignature(rawBody, signature, secret) {
|
|
33
|
-
const crypto2 = __require("crypto");
|
|
34
|
-
const expected = "sha256=" + crypto2.createHmac("sha256", secret).update(rawBody).digest("hex");
|
|
35
|
-
if (signature.length !== expected.length) return false;
|
|
36
|
-
return crypto2.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
|
|
37
|
-
}
|
|
38
17
|
async function verifySignatureAsync(rawBody, signature, secret) {
|
|
39
18
|
const encoder = new TextEncoder();
|
|
40
19
|
const key = await crypto.subtle.importKey(
|
|
@@ -55,8 +34,6 @@ async function verifySignatureAsync(rawBody, signature, secret) {
|
|
|
55
34
|
}
|
|
56
35
|
export {
|
|
57
36
|
WebhookSignatureVerificationError,
|
|
58
|
-
constructEvent,
|
|
59
37
|
constructEventAsync,
|
|
60
|
-
verifySignature,
|
|
61
38
|
verifySignatureAsync
|
|
62
39
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
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/node.ts
|
|
21
|
+
var node_exports = {};
|
|
22
|
+
__export(node_exports, {
|
|
23
|
+
WebhookSignatureVerificationError: () => WebhookSignatureVerificationError,
|
|
24
|
+
constructEvent: () => constructEvent,
|
|
25
|
+
verifySignature: () => verifySignature
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(node_exports);
|
|
28
|
+
var import_node_crypto = require("crypto");
|
|
29
|
+
|
|
30
|
+
// src/webhook/index.ts
|
|
31
|
+
var WebhookSignatureVerificationError = class extends Error {
|
|
32
|
+
constructor(message) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "WebhookSignatureVerificationError";
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/webhook/node.ts
|
|
39
|
+
function constructEvent(rawBody, signature, secret) {
|
|
40
|
+
if (!verifySignature(rawBody, signature, secret)) {
|
|
41
|
+
throw new WebhookSignatureVerificationError(
|
|
42
|
+
"Webhook signature verification failed. Ensure you are using the raw request body and the correct endpoint secret."
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return JSON.parse(rawBody);
|
|
46
|
+
}
|
|
47
|
+
function verifySignature(rawBody, signature, secret) {
|
|
48
|
+
const expected = "sha256=" + (0, import_node_crypto.createHmac)("sha256", secret).update(rawBody).digest("hex");
|
|
49
|
+
if (signature.length !== expected.length) return false;
|
|
50
|
+
return (0, import_node_crypto.timingSafeEqual)(Buffer.from(signature), Buffer.from(expected));
|
|
51
|
+
}
|
|
52
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
53
|
+
0 && (module.exports = {
|
|
54
|
+
WebhookSignatureVerificationError,
|
|
55
|
+
constructEvent,
|
|
56
|
+
verifySignature
|
|
57
|
+
});
|