@forumulate/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.
- package/LICENSE +21 -0
- package/README.md +114 -0
- package/dist/index.d.ts +320 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +256 -0
- package/dist/index.js.map +1 -0
- package/dist/schema.d.ts +86204 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +7 -0
- package/dist/schema.js.map +1 -0
- package/dist/typed.d.ts +121 -0
- package/dist/typed.d.ts.map +1 -0
- package/dist/typed.js +14 -0
- package/dist/typed.js.map +1 -0
- package/dist/types.d.ts +179 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/webhooks.d.ts +78 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +65 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +51 -0
package/dist/schema.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
|
package/dist/typed.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { paths } from './schema';
|
|
2
|
+
export type { paths };
|
|
3
|
+
/** HTTP verbs a generated path entry can carry (openapi-typescript's key set). */
|
|
4
|
+
export type HttpMethod = 'get' | 'put' | 'post' | 'delete' | 'options' | 'head' | 'patch' | 'trace';
|
|
5
|
+
/**
|
|
6
|
+
* The verbs that actually exist on a path. openapi-typescript stamps every
|
|
7
|
+
* absent verb as `put?: never`, so we drop the `never` slots to leave real
|
|
8
|
+
* operations only — this is what powers method autocomplete on `typed()`.
|
|
9
|
+
*/
|
|
10
|
+
export type MethodsOf<P extends keyof paths> = {
|
|
11
|
+
[M in keyof paths[P]]: paths[P][M] extends never ? never : M extends HttpMethod ? M : never;
|
|
12
|
+
}[keyof paths[P]];
|
|
13
|
+
/** The generated operation object for a `(path, method)` pair. */
|
|
14
|
+
export type OperationOf<P extends keyof paths, M extends keyof paths[P]> = paths[P][M];
|
|
15
|
+
/** `true` when `T` has at least one key (used to decide required vs. optional). */
|
|
16
|
+
type HasKeys<T> = [keyof T] extends [never] ? false : true;
|
|
17
|
+
/** The `params` slot: required when the route has path params, absent otherwise. */
|
|
18
|
+
type ParamsPart<Op> = Op extends {
|
|
19
|
+
parameters: {
|
|
20
|
+
path: infer P;
|
|
21
|
+
};
|
|
22
|
+
} ? HasKeys<P> extends true ? {
|
|
23
|
+
params: P;
|
|
24
|
+
} : {
|
|
25
|
+
params?: undefined;
|
|
26
|
+
} : {
|
|
27
|
+
params?: undefined;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The `query` slot: present when the route declares query params, and required
|
|
31
|
+
* only when the route has at least one REQUIRED one.
|
|
32
|
+
*
|
|
33
|
+
* The required pattern is tested first, then the optional one — the same order
|
|
34
|
+
* and for the same reason as `BodyPart` below. openapi-typescript emits
|
|
35
|
+
* `query?: {…}` (an OPTIONAL property) whenever every query parameter on the
|
|
36
|
+
* route is itself optional, and an optional property does not satisfy
|
|
37
|
+
* `{ query: infer Q }`. Matching only the required form therefore fell through
|
|
38
|
+
* to `{ query?: undefined }` and made `typed()` REFUSE a perfectly legal query
|
|
39
|
+
* argument on every such route — which is most of them: of 1,220 operations,
|
|
40
|
+
* the ones with all-optional query params are the common case.
|
|
41
|
+
*
|
|
42
|
+
* `GET /orgs/{orgId}/spaces` is the case that caught it: `deleted`, `limit` and
|
|
43
|
+
* `cursor` are all optional, so passing any of them was a type error even
|
|
44
|
+
* though the route has always accepted them. `ParamsPart` above is unaffected
|
|
45
|
+
* only by luck — a path parameter is never optional, so it is always emitted in
|
|
46
|
+
* the required form.
|
|
47
|
+
*
|
|
48
|
+
* `NonNullable` strips the `| undefined` that inferring through an optional
|
|
49
|
+
* property adds, so `HasKeys` sees the object rather than a union with
|
|
50
|
+
* `undefined` (which would otherwise always look like it has keys).
|
|
51
|
+
*/
|
|
52
|
+
type QueryPart<Op> = Op extends {
|
|
53
|
+
parameters: {
|
|
54
|
+
query: infer Q;
|
|
55
|
+
};
|
|
56
|
+
} ? HasKeys<NonNullable<Q>> extends true ? {
|
|
57
|
+
query: NonNullable<Q>;
|
|
58
|
+
} : {
|
|
59
|
+
query?: undefined;
|
|
60
|
+
} : Op extends {
|
|
61
|
+
parameters: {
|
|
62
|
+
query?: infer Q;
|
|
63
|
+
};
|
|
64
|
+
} ? HasKeys<NonNullable<Q>> extends true ? {
|
|
65
|
+
query?: NonNullable<Q>;
|
|
66
|
+
} : {
|
|
67
|
+
query?: undefined;
|
|
68
|
+
} : {
|
|
69
|
+
query?: undefined;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* The `body` slot. A required `requestBody` (no `?` in the schema) yields a
|
|
73
|
+
* required `body`; an optional one yields an optional `body`. The required
|
|
74
|
+
* pattern is tested first because an optional `requestBody` does not satisfy it.
|
|
75
|
+
*/
|
|
76
|
+
type BodyPart<Op> = Op extends {
|
|
77
|
+
requestBody: {
|
|
78
|
+
content: {
|
|
79
|
+
'application/json': infer B;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
} ? {
|
|
83
|
+
body: B;
|
|
84
|
+
} : Op extends {
|
|
85
|
+
requestBody?: {
|
|
86
|
+
content: {
|
|
87
|
+
'application/json': infer B;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
} ? {
|
|
91
|
+
body?: B;
|
|
92
|
+
} : {
|
|
93
|
+
body?: undefined;
|
|
94
|
+
};
|
|
95
|
+
/** Flatten an intersection into a single object literal for readable hovers. */
|
|
96
|
+
type Simplify<T> = {
|
|
97
|
+
[K in keyof T]: T[K];
|
|
98
|
+
} & {};
|
|
99
|
+
/**
|
|
100
|
+
* The `init` argument for `typed(method, path, init)`, derived from the route:
|
|
101
|
+
* path params, query params and JSON body are each required exactly when the
|
|
102
|
+
* spec says so, plus a `raw` escape to get the untouched `Response` back.
|
|
103
|
+
*/
|
|
104
|
+
export type TypedRequestInit<Op> = Simplify<ParamsPart<Op> & QueryPart<Op> & BodyPart<Op> & {
|
|
105
|
+
raw?: boolean;
|
|
106
|
+
}>;
|
|
107
|
+
/** Pull the `application/json` payload out of one response entry. */
|
|
108
|
+
type JsonBody<R> = R extends {
|
|
109
|
+
content: {
|
|
110
|
+
'application/json': infer D;
|
|
111
|
+
};
|
|
112
|
+
} ? D : unknown;
|
|
113
|
+
/**
|
|
114
|
+
* The success payload for a route: the 200, then 201, then 202 body, or
|
|
115
|
+
* `undefined` for a 204. Routes whose spec response is empty resolve to
|
|
116
|
+
* `unknown` — the value is still whatever the API returns, just not narrowed.
|
|
117
|
+
*/
|
|
118
|
+
export type TypedResponse<Op> = Op extends {
|
|
119
|
+
responses: infer R;
|
|
120
|
+
} ? 200 extends keyof R ? JsonBody<R[200]> : 201 extends keyof R ? JsonBody<R[201]> : 202 extends keyof R ? JsonBody<R[202]> : 204 extends keyof R ? undefined : unknown : unknown;
|
|
121
|
+
//# sourceMappingURL=typed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typed.d.ts","sourceRoot":"","sources":["../src/typed.ts"],"names":[],"mappings":"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"}
|
package/dist/typed.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Type-level machinery that turns the machine-generated `paths` interface in
|
|
3
|
+
// `schema.ts` into an ergonomic, fully-typed request signature. This is the
|
|
4
|
+
// bridge that gives the hand-written client typed coverage of the WHOLE REST
|
|
5
|
+
// API (every route in `openapi.json`) instead of only the ~30 curated helpers.
|
|
6
|
+
//
|
|
7
|
+
// Everything here is types-only — no runtime code, no runtime dependency. The
|
|
8
|
+
// generated `schema.ts` is likewise types-only, so adding it costs nothing at
|
|
9
|
+
// runtime and cannot drift from the client's behaviour.
|
|
10
|
+
//
|
|
11
|
+
// See `ForumulateClient.typed()` in `index.ts` for the method these types shape,
|
|
12
|
+
// and the header of `schema.ts` for how to regenerate the source `paths`.
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
//# sourceMappingURL=typed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typed.js","sourceRoot":"","sources":["../src/typed.ts"],"names":[],"mappings":";AAAA,6EAA6E;AAC7E,4EAA4E;AAC5E,6EAA6E;AAC7E,+EAA+E;AAC/E,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,wDAAwD;AACxD,EAAE;AACF,iFAAiF;AACjF,0EAA0E"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
export type Role = 'OWNER' | 'ADMIN' | 'MAINTAINER' | 'EDITOR' | 'VIEWER';
|
|
2
|
+
export type SpaceType = 'DOCS' | 'BLOG' | 'FORUM';
|
|
3
|
+
export type SpaceVisibility = 'PRIVATE' | 'PUBLIC';
|
|
4
|
+
export type WebhookFormat = 'json' | 'slack' | 'discord';
|
|
5
|
+
/** A ProseMirror/TipTap-style document node. */
|
|
6
|
+
export interface BlockNode {
|
|
7
|
+
type: string;
|
|
8
|
+
content?: BlockNode[];
|
|
9
|
+
text?: string;
|
|
10
|
+
attrs?: Record<string, unknown>;
|
|
11
|
+
marks?: {
|
|
12
|
+
type: string;
|
|
13
|
+
attrs?: Record<string, unknown>;
|
|
14
|
+
}[];
|
|
15
|
+
}
|
|
16
|
+
export interface BlockDoc {
|
|
17
|
+
type: 'doc';
|
|
18
|
+
content: BlockNode[];
|
|
19
|
+
}
|
|
20
|
+
export interface Organization {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
slug: string;
|
|
24
|
+
}
|
|
25
|
+
export interface Space {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
slug: string;
|
|
29
|
+
type: SpaceType;
|
|
30
|
+
visibility: SpaceVisibility;
|
|
31
|
+
}
|
|
32
|
+
export interface Collection {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
slug: string;
|
|
36
|
+
}
|
|
37
|
+
export interface Page {
|
|
38
|
+
id: string;
|
|
39
|
+
title: string;
|
|
40
|
+
slug: string;
|
|
41
|
+
status?: 'draft' | 'published';
|
|
42
|
+
}
|
|
43
|
+
export interface SearchHit {
|
|
44
|
+
pageId: string;
|
|
45
|
+
title: string;
|
|
46
|
+
snippet: string;
|
|
47
|
+
}
|
|
48
|
+
/** A result from the org-wide unified search (docs, forum, blog). */
|
|
49
|
+
export interface UnifiedHit {
|
|
50
|
+
sourceType: 'docs' | 'topic' | 'blog';
|
|
51
|
+
id: string;
|
|
52
|
+
spaceId: string;
|
|
53
|
+
spaceName: string;
|
|
54
|
+
title: string;
|
|
55
|
+
snippet: string;
|
|
56
|
+
url: string;
|
|
57
|
+
score: number;
|
|
58
|
+
}
|
|
59
|
+
export interface Comment {
|
|
60
|
+
id: string;
|
|
61
|
+
parentId: string | null;
|
|
62
|
+
authorName: string;
|
|
63
|
+
body: string;
|
|
64
|
+
status: 'visible' | 'pending' | 'hidden';
|
|
65
|
+
reactions: Record<string, number>;
|
|
66
|
+
createdAt: string;
|
|
67
|
+
}
|
|
68
|
+
export interface Webhook {
|
|
69
|
+
id: string;
|
|
70
|
+
url: string;
|
|
71
|
+
events: string[];
|
|
72
|
+
format: WebhookFormat;
|
|
73
|
+
active: boolean;
|
|
74
|
+
}
|
|
75
|
+
export interface PublicPage {
|
|
76
|
+
title: string;
|
|
77
|
+
content: BlockDoc;
|
|
78
|
+
metadata: {
|
|
79
|
+
icon?: string;
|
|
80
|
+
coverUrl?: string;
|
|
81
|
+
layout?: 'default' | 'wide';
|
|
82
|
+
};
|
|
83
|
+
variables: Record<string, string>;
|
|
84
|
+
version: string;
|
|
85
|
+
locale: string;
|
|
86
|
+
}
|
|
87
|
+
/** What a shape says is still missing from a piece. Computed, never enforced. */
|
|
88
|
+
export interface Conformance {
|
|
89
|
+
conforms: boolean;
|
|
90
|
+
missing: string[];
|
|
91
|
+
extra: string[];
|
|
92
|
+
duplicate: string[];
|
|
93
|
+
}
|
|
94
|
+
/** One piece of a space — a page, a topic or an incident, depending on the space. */
|
|
95
|
+
export interface Item {
|
|
96
|
+
id: string;
|
|
97
|
+
kind: 'page' | 'topic' | 'incident';
|
|
98
|
+
title: string;
|
|
99
|
+
slug?: string;
|
|
100
|
+
conformance: Conformance;
|
|
101
|
+
/** When it last changed, ISO 8601. What a walk is ordered by. */
|
|
102
|
+
updatedAt?: string;
|
|
103
|
+
/** Markdown per named slot, when `include` asked for `sections`. */
|
|
104
|
+
sections?: Record<string, string>;
|
|
105
|
+
/** The whole piece as markdown, when `include` asked for `body`. */
|
|
106
|
+
markdown?: string;
|
|
107
|
+
/**
|
|
108
|
+
* WHICH of `sections` and `markdown` holds this piece's content.
|
|
109
|
+
*
|
|
110
|
+
* `sections` is populated only for a piece written against a shape, so a
|
|
111
|
+
* free-form piece returns `{}` — indistinguishable, without this, from a
|
|
112
|
+
* piece that is genuinely empty. Asking for `sections` alone and trusting an
|
|
113
|
+
* empty object is how a sync reports success having carried nothing.
|
|
114
|
+
*/
|
|
115
|
+
contentIn?: 'sections' | 'markdown';
|
|
116
|
+
}
|
|
117
|
+
/** Where a walk through a collection continues from. */
|
|
118
|
+
export interface PageInfo {
|
|
119
|
+
has_more: boolean;
|
|
120
|
+
next_cursor?: string | null;
|
|
121
|
+
total?: number;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The shape types, mirrored rather than imported.
|
|
125
|
+
*
|
|
126
|
+
* This package is published on its own, so it cannot depend on the workspace
|
|
127
|
+
* package these are defined in — the same reason `Page` and `Space` are
|
|
128
|
+
* mirrored here. They are structural, so they stay assignable either way.
|
|
129
|
+
*/
|
|
130
|
+
export interface ShapeSection {
|
|
131
|
+
key: string;
|
|
132
|
+
label: string;
|
|
133
|
+
kind: 'prose' | 'list' | 'steps' | 'table';
|
|
134
|
+
required?: boolean;
|
|
135
|
+
hint?: string;
|
|
136
|
+
example?: string;
|
|
137
|
+
schemaProperty?: string;
|
|
138
|
+
render?: 'labelled' | 'lede' | 'plain';
|
|
139
|
+
}
|
|
140
|
+
export interface ShapeField {
|
|
141
|
+
key: string;
|
|
142
|
+
label: string;
|
|
143
|
+
type: 'text' | 'select' | 'url' | 'date' | 'boolean';
|
|
144
|
+
options?: readonly string[];
|
|
145
|
+
hint?: string;
|
|
146
|
+
placeholder?: string;
|
|
147
|
+
}
|
|
148
|
+
/** What a space is: what a reader arrives at, how they move, who may add. */
|
|
149
|
+
export interface SpaceArchitecture {
|
|
150
|
+
index: 'landing' | 'list' | 'board' | 'single';
|
|
151
|
+
order: 'manual' | 'chronological' | 'activity' | 'votes' | 'status';
|
|
152
|
+
nav: 'tree' | 'categories' | 'tags' | 'sections' | 'none';
|
|
153
|
+
whoCreates: 'staff' | 'members' | 'anyone';
|
|
154
|
+
summary: readonly string[];
|
|
155
|
+
}
|
|
156
|
+
export interface EffectiveShape {
|
|
157
|
+
space: SpaceArchitecture;
|
|
158
|
+
params: ShapeField[];
|
|
159
|
+
sections: ShapeSection[];
|
|
160
|
+
/** Keys an override named that no longer exist. Reported, never fatal. */
|
|
161
|
+
unknown: string[];
|
|
162
|
+
}
|
|
163
|
+
/** A space's own changes, stored as a patch so upstream improvements still land. */
|
|
164
|
+
export interface ShapeOverride {
|
|
165
|
+
space?: Partial<SpaceArchitecture>;
|
|
166
|
+
sections?: {
|
|
167
|
+
add?: ShapeSection[];
|
|
168
|
+
omit?: string[];
|
|
169
|
+
order?: string[];
|
|
170
|
+
set?: Record<string, Partial<Omit<ShapeSection, 'key'>>>;
|
|
171
|
+
};
|
|
172
|
+
params?: {
|
|
173
|
+
add?: ShapeField[];
|
|
174
|
+
omit?: string[];
|
|
175
|
+
order?: string[];
|
|
176
|
+
set?: Record<string, Partial<Omit<ShapeField, 'key'>>>;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Wire types for the Forumulate REST API. Kept intentionally lightweight and
|
|
3
|
+
// hand-curated for the stable surface; the full machine-readable schema is
|
|
4
|
+
// available from the running API at `/docs-json` (see README → Regenerating).
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifying a webhook delivery, as the receiver actually has to do it.
|
|
3
|
+
*
|
|
4
|
+
* The published contract said:
|
|
5
|
+
*
|
|
6
|
+
* X-Forumulate-Signature: v1,sha256=<hex>
|
|
7
|
+
*
|
|
8
|
+
* and a reader does the obvious thing with that — compute one value and
|
|
9
|
+
* compare the header to it. Every delivery to an endpoint still on the default
|
|
10
|
+
* `signatureVersion: 'both'`, and every delivery during a secret rotation,
|
|
11
|
+
* then fails to verify, because the real header is a comma-separated LIST:
|
|
12
|
+
*
|
|
13
|
+
* v1,sha256=<a>, v1,sha256=<b>, sha256=<c>, sha256=<d>
|
|
14
|
+
* ^ current secret, v1 ^ previous secret, v1
|
|
15
|
+
* ^ the legacy body-only signature, both secrets
|
|
16
|
+
*
|
|
17
|
+
* The sender was right and the document was wrong. Two secrets ride along
|
|
18
|
+
* through a rotation so nobody's verifier breaks on a day we choose, and the
|
|
19
|
+
* legacy body-only signature rides along until the endpoint's owner turns it
|
|
20
|
+
* off — and a receiver is meant to accept the delivery if ANY entry matches
|
|
21
|
+
* the one it computes. That is three facts a sentence in a reference page kept
|
|
22
|
+
* getting wrong, so here is the function instead.
|
|
23
|
+
*
|
|
24
|
+
* Verify `v1`. It signs `<timestamp>.<raw body>`, which is what makes a
|
|
25
|
+
* captured delivery un-replayable: the legacy `sha256=` covers the body alone
|
|
26
|
+
* and is therefore replayable forever. This accepts only `v1` unless you ask
|
|
27
|
+
* for the legacy one explicitly.
|
|
28
|
+
*
|
|
29
|
+
* import { verifyWebhookSignature } from '@forumulate/sdk';
|
|
30
|
+
*
|
|
31
|
+
* app.post('/hooks/forumulate', async (req, res) => {
|
|
32
|
+
* const ok = await verifyWebhookSignature({
|
|
33
|
+
* secret: process.env.FORUMULATE_WEBHOOK_SECRET!,
|
|
34
|
+
* // The RAW body, before any JSON parsing. A re-serialised body has
|
|
35
|
+
* // different bytes and will never verify.
|
|
36
|
+
* body: req.rawBody,
|
|
37
|
+
* signature: req.header('X-Forumulate-Signature'),
|
|
38
|
+
* timestamp: req.header('X-Forumulate-Timestamp'),
|
|
39
|
+
* });
|
|
40
|
+
* if (!ok) return res.sendStatus(401);
|
|
41
|
+
* // X-Forumulate-Delivery is the key to dedupe on — retries reuse it.
|
|
42
|
+
* res.sendStatus(200);
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* WebCrypto, so this is the same code in Node, Bun, Deno, Cloudflare Workers
|
|
46
|
+
* and a browser. It is async for that reason alone.
|
|
47
|
+
*/
|
|
48
|
+
export interface VerifyWebhookInput {
|
|
49
|
+
/** The endpoint's signing secret. */
|
|
50
|
+
secret: string;
|
|
51
|
+
/** The raw request body, exactly as received — bytes, or the string of them. */
|
|
52
|
+
body: string | Uint8Array;
|
|
53
|
+
/** The `X-Forumulate-Signature` header value, whole. */
|
|
54
|
+
signature: string | null | undefined;
|
|
55
|
+
/** The `X-Forumulate-Timestamp` header value (unix seconds). */
|
|
56
|
+
timestamp: string | number | null | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* How old a delivery may be, in seconds. Default 300.
|
|
59
|
+
*
|
|
60
|
+
* This is the part that makes the timestamp worth signing: without a window,
|
|
61
|
+
* a captured delivery verifies forever and the signature proves only that we
|
|
62
|
+
* sent it ONCE. Set it to 0 to skip the check (for a replay of a stored
|
|
63
|
+
* delivery in a test, say).
|
|
64
|
+
*/
|
|
65
|
+
toleranceSeconds?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Accept the legacy body-only `sha256=` signature as well. Default false.
|
|
68
|
+
*
|
|
69
|
+
* It is still sent while an endpoint's `signatureVersion` is `both`, and it
|
|
70
|
+
* is replayable by construction, so accepting it is a decision rather than a
|
|
71
|
+
* default.
|
|
72
|
+
*/
|
|
73
|
+
acceptLegacy?: boolean;
|
|
74
|
+
/** Overridable for tests. Defaults to now. */
|
|
75
|
+
now?: () => number;
|
|
76
|
+
}
|
|
77
|
+
export declare function verifyWebhookSignature(input: VerifyWebhookInput): Promise<boolean>;
|
|
78
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,MAAM,WAAW,kBAAkB;IACjC,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,wDAAwD;IACxD,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACrC,gEAAgE;IAChE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAgCxF"}
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
4
|
+
async function verifyWebhookSignature(input) {
|
|
5
|
+
const { secret, signature, acceptLegacy = false, toleranceSeconds = 300 } = input;
|
|
6
|
+
if (!secret || !signature)
|
|
7
|
+
return false;
|
|
8
|
+
const timestamp = Number(input.timestamp);
|
|
9
|
+
if (!Number.isFinite(timestamp) || timestamp <= 0)
|
|
10
|
+
return false;
|
|
11
|
+
if (toleranceSeconds > 0) {
|
|
12
|
+
const now = Math.floor((input.now?.() ?? Date.now()) / 1000);
|
|
13
|
+
// Both directions: a delivery from the future is as suspicious as a stale
|
|
14
|
+
// one, and clock skew is why the window is seconds rather than exact.
|
|
15
|
+
if (Math.abs(now - timestamp) > toleranceSeconds)
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const body = typeof input.body === 'string' ? input.body : new TextDecoder().decode(input.body);
|
|
19
|
+
// Entries, not an equality check: a rotation and the legacy overlap both put
|
|
20
|
+
// more than one signature in this header, and `header === expected` is the
|
|
21
|
+
// mistake the document invited.
|
|
22
|
+
const offered = signature.split(',').map((part) => part.trim().toLowerCase());
|
|
23
|
+
const expected = [`v1,sha256=${await hmacHex(secret, `${timestamp}.${body}`)}`];
|
|
24
|
+
if (acceptLegacy)
|
|
25
|
+
expected.push(`sha256=${await hmacHex(secret, body)}`);
|
|
26
|
+
for (const candidate of expected) {
|
|
27
|
+
// `v1,sha256=<hex>` contains the comma we just split on, so put it back.
|
|
28
|
+
const [prefix, value] = candidate.split('=', 2);
|
|
29
|
+
const wanted = `${prefix}=${value}`;
|
|
30
|
+
const rejoined = offered.join(',');
|
|
31
|
+
// Membership against the rejoined list rather than the split parts, so the
|
|
32
|
+
// `v1,` prefix survives the split that produced the parts.
|
|
33
|
+
if (!rejoined.includes(wanted))
|
|
34
|
+
continue;
|
|
35
|
+
if (timingSafeEqual(wanted, extractMatch(rejoined, wanted)))
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
/** The substring of `haystack` at `needle`'s position, for a constant-time compare. */
|
|
41
|
+
function extractMatch(haystack, needle) {
|
|
42
|
+
const at = haystack.indexOf(needle);
|
|
43
|
+
return at < 0 ? '' : haystack.slice(at, at + needle.length);
|
|
44
|
+
}
|
|
45
|
+
async function hmacHex(secret, message) {
|
|
46
|
+
const encoder = new TextEncoder();
|
|
47
|
+
const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
48
|
+
const mac = await crypto.subtle.sign('HMAC', key, encoder.encode(message));
|
|
49
|
+
return [...new Uint8Array(mac)].map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Length-independent, content-constant-time comparison.
|
|
53
|
+
*
|
|
54
|
+
* Both sides here are hex of a fixed length, so this is belt and braces — but
|
|
55
|
+
* a verifier is exactly the place where "it does not matter here" ages badly.
|
|
56
|
+
*/
|
|
57
|
+
function timingSafeEqual(a, b) {
|
|
58
|
+
if (a.length !== b.length)
|
|
59
|
+
return false;
|
|
60
|
+
let diff = 0;
|
|
61
|
+
for (let i = 0; i < a.length; i++)
|
|
62
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
63
|
+
return diff === 0;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forumulate/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Typed TypeScript client for the Forumulate REST API.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://forumulate.com/reference",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://forumulate.com/community"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"forumulate",
|
|
12
|
+
"docs",
|
|
13
|
+
"documentation",
|
|
14
|
+
"api",
|
|
15
|
+
"sdk",
|
|
16
|
+
"typescript",
|
|
17
|
+
"openapi",
|
|
18
|
+
"knowledge-base",
|
|
19
|
+
"forum"
|
|
20
|
+
],
|
|
21
|
+
"main": "dist/index.js",
|
|
22
|
+
"types": "dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"tsx": "^4.19.2",
|
|
33
|
+
"typescript": "^5.6.3",
|
|
34
|
+
"vitest": "^2.1.4",
|
|
35
|
+
"@forumulate/api": "0.0.0"
|
|
36
|
+
},
|
|
37
|
+
"author": "Forumulate",
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"default": "./dist/index.js"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc -p tsconfig.json",
|
|
46
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"dump-spec": "tsx scripts/dump-openapi.ts",
|
|
49
|
+
"generate-schema": "npx -y openapi-typescript@7 openapi.json -o src/schema.ts"
|
|
50
|
+
}
|
|
51
|
+
}
|