@pageweaver/sdk 0.1.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.
@@ -0,0 +1,72 @@
1
+ import { PageWeaverError } from "./errors";
2
+ import type { DocumentStatus } from "./types";
3
+ /** Header carrying the `sha256=<hex>` signature. */
4
+ export declare const WEBHOOK_SIGNATURE_HEADER = "x-pageweaver-signature";
5
+ /** Header carrying the event name. */
6
+ export declare const WEBHOOK_EVENT_HEADER = "x-pageweaver-event";
7
+ /** Header carrying the unix-seconds send time. */
8
+ export declare const WEBHOOK_TIMESTAMP_HEADER = "x-pageweaver-timestamp";
9
+ /** Terminal document events. */
10
+ export type DocumentWebhookEventName = "document.completed" | "document.failed";
11
+ /** Terminal bulk-run (batch) events. */
12
+ export type BatchWebhookEventName = "batch.completed" | "batch.failed";
13
+ export type WebhookEventName = DocumentWebhookEventName | BatchWebhookEventName;
14
+ /** Body delivered for a single-document terminal event. `url` is a short-lived signed download URL. */
15
+ export interface DocumentWebhookPayload {
16
+ event: DocumentWebhookEventName;
17
+ documentId: string;
18
+ templateId: string | null;
19
+ version: number | null;
20
+ status: Extract<DocumentStatus, "done" | "failed">;
21
+ url?: string;
22
+ error?: string;
23
+ createdAt: string;
24
+ }
25
+ /** Body delivered for a bulk-run terminal event. `bundleUrl` is a signed URL to the assembled ZIP. */
26
+ export interface BatchWebhookPayload {
27
+ event: BatchWebhookEventName;
28
+ batchId: string;
29
+ templateId: string;
30
+ version: number;
31
+ status: "completed" | "failed";
32
+ totalRows: number;
33
+ succeeded: number;
34
+ failed: number;
35
+ invalidRows: number;
36
+ bundleUrl?: string;
37
+ createdAt: string;
38
+ }
39
+ export type WebhookPayload = DocumentWebhookPayload | BatchWebhookPayload;
40
+ /** Type guard: narrow a verified event to a document payload. */
41
+ export declare function isDocumentEvent(p: WebhookPayload): p is DocumentWebhookPayload;
42
+ /** Type guard: narrow a verified event to a batch payload. */
43
+ export declare function isBatchEvent(p: WebhookPayload): p is BatchWebhookPayload;
44
+ /** Compute the `sha256=<hex>` signature for a body. Exposed mainly for tests. */
45
+ export declare function signWebhookBody(secret: string, rawBody: string): string;
46
+ /** Constant-time check of a `sha256=<hex>` signature against the raw body. Never throws. */
47
+ export declare function verifyWebhookSignature(secret: string, rawBody: string, signature: string | undefined | null): boolean;
48
+ /** Thrown when a webhook signature does not match the body. */
49
+ export declare class PageWeaverWebhookSignatureError extends PageWeaverError {
50
+ constructor();
51
+ }
52
+ export interface VerifyWebhookOptions {
53
+ /** Your account webhook signing secret (`whsec_...`). */
54
+ secret: string;
55
+ /**
56
+ * The exact raw request body bytes, as a string. Use the unparsed body (e.g. `express.raw`),
57
+ * not a re-serialized object: re-stringifying can change the bytes and break the signature.
58
+ */
59
+ body: string;
60
+ /** The `X-PageWeaver-Signature` header value (a single string or the header array). */
61
+ signature: string | string[] | undefined | null;
62
+ }
63
+ /**
64
+ * Verify a webhook signature and return the parsed, typed event. Throws
65
+ * {@link PageWeaverWebhookSignatureError} if the signature is missing or wrong.
66
+ *
67
+ * ```ts
68
+ * const event = verifyWebhook({ secret, body: rawBody, signature: req.headers["x-pageweaver-signature"] });
69
+ * if (isDocumentEvent(event) && event.status === "done") { ... }
70
+ * ```
71
+ */
72
+ export declare function verifyWebhook(opts: VerifyWebhookOptions): WebhookPayload;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PageWeaverWebhookSignatureError = exports.WEBHOOK_TIMESTAMP_HEADER = exports.WEBHOOK_EVENT_HEADER = exports.WEBHOOK_SIGNATURE_HEADER = void 0;
4
+ exports.isDocumentEvent = isDocumentEvent;
5
+ exports.isBatchEvent = isBatchEvent;
6
+ exports.signWebhookBody = signWebhookBody;
7
+ exports.verifyWebhookSignature = verifyWebhookSignature;
8
+ exports.verifyWebhook = verifyWebhook;
9
+ const node_crypto_1 = require("node:crypto");
10
+ const errors_1 = require("./errors");
11
+ // PageWeaver signs each webhook delivery with HMAC-SHA256 over the exact request body, keyed by your
12
+ // account webhook secret (`whsec_...`), and sends it in the `X-PageWeaver-Signature` header formatted
13
+ // `sha256=<hex>`. Verify it on your endpoint before trusting the payload. This logic mirrors the
14
+ // server's signer (packages/webhooks) and is inlined here so the SDK has no internal dependencies.
15
+ /** Header carrying the `sha256=<hex>` signature. */
16
+ exports.WEBHOOK_SIGNATURE_HEADER = "x-pageweaver-signature";
17
+ /** Header carrying the event name. */
18
+ exports.WEBHOOK_EVENT_HEADER = "x-pageweaver-event";
19
+ /** Header carrying the unix-seconds send time. */
20
+ exports.WEBHOOK_TIMESTAMP_HEADER = "x-pageweaver-timestamp";
21
+ /** Type guard: narrow a verified event to a document payload. */
22
+ function isDocumentEvent(p) {
23
+ return p.event === "document.completed" || p.event === "document.failed";
24
+ }
25
+ /** Type guard: narrow a verified event to a batch payload. */
26
+ function isBatchEvent(p) {
27
+ return p.event === "batch.completed" || p.event === "batch.failed";
28
+ }
29
+ /** Compute the `sha256=<hex>` signature for a body. Exposed mainly for tests. */
30
+ function signWebhookBody(secret, rawBody) {
31
+ return `sha256=${(0, node_crypto_1.createHmac)("sha256", secret).update(rawBody, "utf8").digest("hex")}`;
32
+ }
33
+ /** Constant-time check of a `sha256=<hex>` signature against the raw body. Never throws. */
34
+ function verifyWebhookSignature(secret, rawBody, signature) {
35
+ if (!signature)
36
+ return false;
37
+ const expected = Buffer.from(signWebhookBody(secret, rawBody), "utf8");
38
+ const actual = Buffer.from(signature, "utf8");
39
+ return expected.length === actual.length && (0, node_crypto_1.timingSafeEqual)(expected, actual);
40
+ }
41
+ /** Thrown when a webhook signature does not match the body. */
42
+ class PageWeaverWebhookSignatureError extends errors_1.PageWeaverError {
43
+ constructor() {
44
+ super("Invalid webhook signature.");
45
+ this.name = "PageWeaverWebhookSignatureError";
46
+ }
47
+ }
48
+ exports.PageWeaverWebhookSignatureError = PageWeaverWebhookSignatureError;
49
+ /**
50
+ * Verify a webhook signature and return the parsed, typed event. Throws
51
+ * {@link PageWeaverWebhookSignatureError} if the signature is missing or wrong.
52
+ *
53
+ * ```ts
54
+ * const event = verifyWebhook({ secret, body: rawBody, signature: req.headers["x-pageweaver-signature"] });
55
+ * if (isDocumentEvent(event) && event.status === "done") { ... }
56
+ * ```
57
+ */
58
+ function verifyWebhook(opts) {
59
+ const signature = Array.isArray(opts.signature) ? opts.signature[0] : opts.signature;
60
+ if (!verifyWebhookSignature(opts.secret, opts.body, signature)) {
61
+ throw new PageWeaverWebhookSignatureError();
62
+ }
63
+ return JSON.parse(opts.body);
64
+ }
65
+ //# sourceMappingURL=webhooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":";;;AAoDA,0CAEC;AAGD,oCAEC;AAGD,0CAEC;AAGD,wDASC;AA+BD,sCAMC;AAjHD,6CAA0D;AAC1D,qCAA2C;AAG3C,qGAAqG;AACrG,sGAAsG;AACtG,iGAAiG;AACjG,mGAAmG;AAEnG,oDAAoD;AACvC,QAAA,wBAAwB,GAAG,wBAAwB,CAAC;AACjE,sCAAsC;AACzB,QAAA,oBAAoB,GAAG,oBAAoB,CAAC;AACzD,kDAAkD;AACrC,QAAA,wBAAwB,GAAG,wBAAwB,CAAC;AAqCjE,iEAAiE;AACjE,SAAgB,eAAe,CAAC,CAAiB;IAC/C,OAAO,CAAC,CAAC,KAAK,KAAK,oBAAoB,IAAI,CAAC,CAAC,KAAK,KAAK,iBAAiB,CAAC;AAC3E,CAAC;AAED,8DAA8D;AAC9D,SAAgB,YAAY,CAAC,CAAiB;IAC5C,OAAO,CAAC,CAAC,KAAK,KAAK,iBAAiB,IAAI,CAAC,CAAC,KAAK,KAAK,cAAc,CAAC;AACrE,CAAC;AAED,iFAAiF;AACjF,SAAgB,eAAe,CAAC,MAAc,EAAE,OAAe;IAC7D,OAAO,UAAU,IAAA,wBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACxF,CAAC;AAED,4FAA4F;AAC5F,SAAgB,sBAAsB,CACpC,MAAc,EACd,OAAe,EACf,SAAoC;IAEpC,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC9C,OAAO,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,IAAI,IAAA,6BAAe,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAChF,CAAC;AAED,+DAA+D;AAC/D,MAAa,+BAAgC,SAAQ,wBAAe;IAClE;QACE,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,iCAAiC,CAAC;IAChD,CAAC;CACF;AALD,0EAKC;AAcD;;;;;;;;GAQG;AACH,SAAgB,aAAa,CAAC,IAA0B;IACtD,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;IACrF,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,+BAA+B,EAAE,CAAC;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAmB,CAAC;AACjD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@pageweaver/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official TypeScript SDK for the PageWeaver PDF generation API.",
5
+ "license": "MIT",
6
+ "author": "PageWeaver",
7
+ "homepage": "https://pageweaver.io",
8
+ "keywords": [
9
+ "pageweaver",
10
+ "pdf",
11
+ "pdf-generation",
12
+ "html-to-pdf",
13
+ "sdk",
14
+ "api-client",
15
+ "typescript"
16
+ ],
17
+ "type": "commonjs",
18
+ "main": "dist/index.js",
19
+ "types": "dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.10.2",
39
+ "tsx": "^4.19.2",
40
+ "typescript": "^5.7.2"
41
+ },
42
+ "scripts": {
43
+ "build": "tsc -p tsconfig.build.json",
44
+ "dev": "tsc -w -p tsconfig.build.json",
45
+ "typecheck": "tsc --noEmit -p tsconfig.json",
46
+ "lint": "eslint src",
47
+ "test": "node --import tsx --test \"src/**/*.test.ts\"",
48
+ "sync:openapi": "node scripts/sync-openapi.mjs"
49
+ }
50
+ }