@forumulate/sdk 0.4.0 → 0.7.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 +124 -15
- package/dist/content.d.ts +318 -0
- package/dist/content.js +81 -0
- package/dist/helpers.d.ts +303 -0
- package/dist/helpers.js +180 -0
- package/dist/http.d.ts +86 -0
- package/dist/http.js +200 -0
- package/dist/index.d.ts +471 -177
- package/dist/index.js +114 -269
- package/dist/operations.gen.d.ts +2597 -0
- package/dist/operations.gen.js +360 -0
- package/dist/pagination.d.ts +27 -0
- package/dist/pagination.js +79 -0
- package/dist/resources.d.ts +181 -0
- package/dist/resources.js +129 -0
- package/dist/schema.d.ts +2 -2
- package/dist/translations.d.ts +301 -0
- package/dist/translations.js +255 -0
- package/dist/typed.d.ts +2 -2
- package/dist/webhooks.d.ts +8 -0
- package/dist/webhooks.js +15 -0
- package/package.json +8 -4
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The positional helpers from 0.5 — `forumulate.spaces.create(orgId, input)`.
|
|
3
|
+
*
|
|
4
|
+
* They predate the generated namespaces and keep their signatures because
|
|
5
|
+
* code in the wild calls them. Each one wins its name inside its namespace
|
|
6
|
+
* (see `Merged` in resources.ts); every operation they do not cover is
|
|
7
|
+
* generated beside them. `coverage.test.ts` records which operation each one
|
|
8
|
+
* calls, so a helper cannot silently hide a different route.
|
|
9
|
+
*/
|
|
10
|
+
import type { Query } from './http';
|
|
11
|
+
import { constructWebhookEvent, verifyWebhookSignature } from './webhooks';
|
|
12
|
+
import type { Organization, Space, SpaceType, SpaceVisibility, Collection, Page, BlockDoc, SearchHit, UnifiedHit, Comment, Webhook, WebhookFormat, PublicPage, Conformance, DomainSetup, DomainSummary, Item, PageInfo, EffectiveShape, ShapeOverride } from './types';
|
|
13
|
+
export type HelperRequest = <T>(method: string, path: string, opts?: {
|
|
14
|
+
body?: unknown;
|
|
15
|
+
query?: Query;
|
|
16
|
+
raw?: boolean;
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
}) => Promise<T>;
|
|
19
|
+
export declare function positionalHelpers(request: HelperRequest): {
|
|
20
|
+
auth: {
|
|
21
|
+
me: () => Promise<{
|
|
22
|
+
user: {
|
|
23
|
+
id: string;
|
|
24
|
+
email: string;
|
|
25
|
+
name: string;
|
|
26
|
+
};
|
|
27
|
+
}>;
|
|
28
|
+
/** Mint a Personal Access Token (requires an authenticated session). */
|
|
29
|
+
createToken: (input: {
|
|
30
|
+
name: string;
|
|
31
|
+
scopes?: string[];
|
|
32
|
+
}) => Promise<{
|
|
33
|
+
token: string;
|
|
34
|
+
pat: {
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
};
|
|
38
|
+
}>;
|
|
39
|
+
};
|
|
40
|
+
orgs: {
|
|
41
|
+
list: () => Promise<{
|
|
42
|
+
orgs: Organization[];
|
|
43
|
+
}>;
|
|
44
|
+
create: (input: {
|
|
45
|
+
name: string;
|
|
46
|
+
slug: string;
|
|
47
|
+
}) => Promise<{
|
|
48
|
+
organization: Organization;
|
|
49
|
+
}>;
|
|
50
|
+
};
|
|
51
|
+
spaces: {
|
|
52
|
+
list: (orgId: string) => Promise<{
|
|
53
|
+
spaces: Space[];
|
|
54
|
+
}>;
|
|
55
|
+
create: (orgId: string, input: {
|
|
56
|
+
name: string;
|
|
57
|
+
slug: string;
|
|
58
|
+
type: SpaceType;
|
|
59
|
+
visibility: SpaceVisibility;
|
|
60
|
+
}) => Promise<{
|
|
61
|
+
space: Space;
|
|
62
|
+
}>;
|
|
63
|
+
createCollection: (orgId: string, spaceId: string, input: {
|
|
64
|
+
name: string;
|
|
65
|
+
slug: string;
|
|
66
|
+
}) => Promise<{
|
|
67
|
+
collection: Collection;
|
|
68
|
+
}>;
|
|
69
|
+
createPage: (orgId: string, spaceId: string, collectionId: string, input: {
|
|
70
|
+
title: string;
|
|
71
|
+
slug: string;
|
|
72
|
+
}) => Promise<{
|
|
73
|
+
page: Page;
|
|
74
|
+
}>;
|
|
75
|
+
setPageContent: (orgId: string, spaceId: string, pageId: string, input: {
|
|
76
|
+
title: string;
|
|
77
|
+
content: BlockDoc;
|
|
78
|
+
}) => Promise<{
|
|
79
|
+
page: Page;
|
|
80
|
+
}>;
|
|
81
|
+
publishPage: (orgId: string, spaceId: string, pageId: string) => Promise<{
|
|
82
|
+
page: Page;
|
|
83
|
+
}>;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Shapes and pieces — the surface an agent works through.
|
|
87
|
+
*
|
|
88
|
+
* `shape` says what one piece of a space is made of and hands back JSON
|
|
89
|
+
* Schema; `items` writes one, whatever a piece happens to be in that space:
|
|
90
|
+
* a documentation page, a blog post, a release, an incident. The reply says
|
|
91
|
+
* which required sections are still empty, so an agent can create a piece,
|
|
92
|
+
* ask what it forgot, and close the gap.
|
|
93
|
+
*/
|
|
94
|
+
shapes: {
|
|
95
|
+
get: (orgId: string, spaceId: string) => Promise<{
|
|
96
|
+
shape: EffectiveShape;
|
|
97
|
+
base: EffectiveShape;
|
|
98
|
+
presets: {
|
|
99
|
+
name: string;
|
|
100
|
+
label: string;
|
|
101
|
+
description: string;
|
|
102
|
+
}[];
|
|
103
|
+
jsonSchema: Record<string, unknown>;
|
|
104
|
+
}>;
|
|
105
|
+
set: (orgId: string, spaceId: string, patch: ShapeOverride) => Promise<{
|
|
106
|
+
shape: EffectiveShape;
|
|
107
|
+
}>;
|
|
108
|
+
applyPreset: (orgId: string, spaceId: string, name: string) => Promise<{
|
|
109
|
+
shape: EffectiveShape;
|
|
110
|
+
}>;
|
|
111
|
+
};
|
|
112
|
+
items: {
|
|
113
|
+
create: (orgId: string, spaceId: string, input: {
|
|
114
|
+
title: string;
|
|
115
|
+
slug?: string;
|
|
116
|
+
/** Which kind of piece, where the space holds more than one. */
|
|
117
|
+
preset?: string | null;
|
|
118
|
+
sections?: Record<string, string>;
|
|
119
|
+
body?: string;
|
|
120
|
+
params?: Record<string, unknown>;
|
|
121
|
+
}) => Promise<{
|
|
122
|
+
item: Item;
|
|
123
|
+
}>;
|
|
124
|
+
/**
|
|
125
|
+
* What is in a space: how something finds a piece it has no words for.
|
|
126
|
+
*
|
|
127
|
+
* Pass `include: ['body']` to get content. Asking for `sections` ALONE is
|
|
128
|
+
* the trap: it is populated only for pieces written against a shape, so a
|
|
129
|
+
* space of free-form pages answers with empty objects and a sync carries
|
|
130
|
+
* nothing while reporting success. Every row says which field holds its
|
|
131
|
+
* content in `contentIn`.
|
|
132
|
+
*
|
|
133
|
+
* Any option here puts the response in its paged form, so `page` and
|
|
134
|
+
* `sync` come back — `page.total` is what a walk should check its own
|
|
135
|
+
* count against, and `sync.checkpoint` is what an incremental sync stores.
|
|
136
|
+
*/
|
|
137
|
+
list: (orgId: string, spaceId: string, opts?: number | {
|
|
138
|
+
limit?: number;
|
|
139
|
+
include?: ("sections" | "body")[];
|
|
140
|
+
cursor?: string;
|
|
141
|
+
/** A `sync.checkpoint` from a previous walk, not a timestamp you made. */
|
|
142
|
+
updatedSince?: string;
|
|
143
|
+
includeDeleted?: boolean;
|
|
144
|
+
fields?: string[];
|
|
145
|
+
}) => Promise<{
|
|
146
|
+
items: Item[];
|
|
147
|
+
page?: PageInfo;
|
|
148
|
+
sync?: {
|
|
149
|
+
checkpoint: string;
|
|
150
|
+
};
|
|
151
|
+
}>;
|
|
152
|
+
get: (orgId: string, spaceId: string, itemId: string) => Promise<{
|
|
153
|
+
item: Item & {
|
|
154
|
+
sections: Record<string, string>;
|
|
155
|
+
};
|
|
156
|
+
}>;
|
|
157
|
+
/**
|
|
158
|
+
* Make it readable. Kinds that are live on creation say so and change nothing.
|
|
159
|
+
*
|
|
160
|
+
* `conformance` describes the piece AFTER publishing — which declared slots
|
|
161
|
+
* are filled and which are still missing — so an agent that has just made
|
|
162
|
+
* its work readable can tell a complete piece from one with holes in it
|
|
163
|
+
* without fetching it again. The server has always sent it and this type
|
|
164
|
+
* omitted it, so a TypeScript caller could not see the field the docs
|
|
165
|
+
* promise (E-11).
|
|
166
|
+
*/
|
|
167
|
+
publish: (orgId: string, spaceId: string, itemId: string) => Promise<{
|
|
168
|
+
published: boolean;
|
|
169
|
+
note?: string;
|
|
170
|
+
conformance: Conformance;
|
|
171
|
+
}>;
|
|
172
|
+
/**
|
|
173
|
+
* Replace a piece's whole body — as markdown, or as blocks.
|
|
174
|
+
*
|
|
175
|
+
* The create door has always taken markdown and this one took blocks only,
|
|
176
|
+
* so "make it from this file" worked and "update it from the same file" did
|
|
177
|
+
* not. Both now; blocks win if a caller sends both.
|
|
178
|
+
*/
|
|
179
|
+
setContent: (orgId: string, spaceId: string, itemId: string, input: {
|
|
180
|
+
body?: string;
|
|
181
|
+
content?: BlockDoc;
|
|
182
|
+
}) => Promise<{
|
|
183
|
+
conformance: Conformance;
|
|
184
|
+
}>;
|
|
185
|
+
/** Replace one named section. Idempotent — sending it twice writes it once. */
|
|
186
|
+
setSection: (orgId: string, spaceId: string, itemId: string, key: string, markdown: string) => Promise<{
|
|
187
|
+
conformance: Conformance;
|
|
188
|
+
}>;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* The addresses a space answers at.
|
|
192
|
+
*
|
|
193
|
+
* Published because the question a customer asks about a custom domain —
|
|
194
|
+
* "why is it not live yet" — is answerable only from `verified`, `active` and
|
|
195
|
+
* the setup records, and the SDK could not reach any of them (DX-2). A failed
|
|
196
|
+
* verification is a 200 with `verified: false`: read the body, not the status.
|
|
197
|
+
*/
|
|
198
|
+
domains: {
|
|
199
|
+
list: (orgId: string, spaceId: string) => Promise<{
|
|
200
|
+
domains: DomainSummary[];
|
|
201
|
+
}>;
|
|
202
|
+
add: (orgId: string, spaceId: string, input: {
|
|
203
|
+
hostname: string;
|
|
204
|
+
basePath?: string;
|
|
205
|
+
}) => Promise<{
|
|
206
|
+
domain: DomainSummary;
|
|
207
|
+
}>;
|
|
208
|
+
/** Re-run the ownership check. `activate: false` proves it without switching traffic. */
|
|
209
|
+
verify: (orgId: string, spaceId: string, domainId: string, activate?: boolean) => Promise<{
|
|
210
|
+
domain: DomainSummary;
|
|
211
|
+
}>;
|
|
212
|
+
/** Switch traffic to an address whose ownership is already proven. */
|
|
213
|
+
activate: (orgId: string, spaceId: string, domainId: string) => Promise<{
|
|
214
|
+
domain: DomainSummary;
|
|
215
|
+
}>;
|
|
216
|
+
/** What to configure, and what is still missing. */
|
|
217
|
+
setup: (orgId: string, spaceId: string, domainId: string) => Promise<DomainSetup>;
|
|
218
|
+
};
|
|
219
|
+
webhooks: {
|
|
220
|
+
list: (orgId: string) => Promise<{
|
|
221
|
+
webhooks: Webhook[];
|
|
222
|
+
}>;
|
|
223
|
+
create: (orgId: string, input: {
|
|
224
|
+
url: string;
|
|
225
|
+
events: string[];
|
|
226
|
+
format?: WebhookFormat;
|
|
227
|
+
}) => Promise<{
|
|
228
|
+
webhook: Webhook;
|
|
229
|
+
}>;
|
|
230
|
+
/** Check a delivery's `X-Forumulate-Signature`; see {@link verifyWebhookSignature}. */
|
|
231
|
+
verifySignature: typeof verifyWebhookSignature;
|
|
232
|
+
/** Verify a delivery and parse it; throws when the signature does not hold. */
|
|
233
|
+
constructEvent: typeof constructWebhookEvent;
|
|
234
|
+
};
|
|
235
|
+
snapshots: {
|
|
236
|
+
list: (orgId: string, spaceId: string) => Promise<{
|
|
237
|
+
snapshots: unknown[];
|
|
238
|
+
}>;
|
|
239
|
+
create: (orgId: string, spaceId: string, label: string) => Promise<{
|
|
240
|
+
snapshot: unknown;
|
|
241
|
+
}>;
|
|
242
|
+
restore: (orgId: string, spaceId: string, id: string) => Promise<{
|
|
243
|
+
restored: number;
|
|
244
|
+
skipped: number;
|
|
245
|
+
}>;
|
|
246
|
+
};
|
|
247
|
+
forum: {
|
|
248
|
+
listTopics: (orgId: string, spaceId: string, query?: {
|
|
249
|
+
sort?: string;
|
|
250
|
+
category?: string;
|
|
251
|
+
page?: number;
|
|
252
|
+
}) => Promise<{
|
|
253
|
+
topics: unknown[];
|
|
254
|
+
hasMore?: boolean;
|
|
255
|
+
}>;
|
|
256
|
+
createTopic: (orgId: string, spaceId: string, input: {
|
|
257
|
+
title: string;
|
|
258
|
+
content: BlockDoc;
|
|
259
|
+
kind?: string;
|
|
260
|
+
tags?: string[];
|
|
261
|
+
categoryId?: string | null;
|
|
262
|
+
}) => Promise<{
|
|
263
|
+
topic: unknown;
|
|
264
|
+
}>;
|
|
265
|
+
reply: (orgId: string, spaceId: string, topicId: string, content: BlockDoc) => Promise<{
|
|
266
|
+
post: unknown;
|
|
267
|
+
}>;
|
|
268
|
+
};
|
|
269
|
+
search: {
|
|
270
|
+
unified: (orgId: string, q: string, opts?: {
|
|
271
|
+
type?: string;
|
|
272
|
+
limit?: number;
|
|
273
|
+
}) => Promise<{
|
|
274
|
+
hits: UnifiedHit[];
|
|
275
|
+
}>;
|
|
276
|
+
};
|
|
277
|
+
analytics: {
|
|
278
|
+
orgEvents: (orgId: string, days?: number) => Promise<unknown>;
|
|
279
|
+
};
|
|
280
|
+
public: {
|
|
281
|
+
getPage: (orgId: string, spaceId: string, pageId: string) => Promise<PublicPage>;
|
|
282
|
+
search: (orgId: string, spaceId: string, query: string) => Promise<{
|
|
283
|
+
hits: SearchHit[];
|
|
284
|
+
}>;
|
|
285
|
+
listComments: (orgId: string, spaceId: string, pageId: string) => Promise<{
|
|
286
|
+
comments: Comment[];
|
|
287
|
+
}>;
|
|
288
|
+
postComment: (orgId: string, spaceId: string, pageId: string, input: {
|
|
289
|
+
authorName: string;
|
|
290
|
+
body: string;
|
|
291
|
+
parentId?: string | null;
|
|
292
|
+
}) => Promise<{
|
|
293
|
+
comment: Comment;
|
|
294
|
+
}>;
|
|
295
|
+
reactToComment: (orgId: string, spaceId: string, commentId: string, emoji: string) => Promise<{
|
|
296
|
+
comment: Comment;
|
|
297
|
+
}>;
|
|
298
|
+
/** Download the whole space as a PDF. Returns the raw `Response` (read `.blob()` / `.arrayBuffer()`). */
|
|
299
|
+
pdf: (orgId: string, spaceId: string) => Promise<Response>;
|
|
300
|
+
};
|
|
301
|
+
};
|
|
302
|
+
export type PositionalHelpers = ReturnType<typeof positionalHelpers>;
|
|
303
|
+
//# sourceMappingURL=helpers.d.ts.map
|
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.positionalHelpers = positionalHelpers;
|
|
4
|
+
const webhooks_1 = require("./webhooks");
|
|
5
|
+
function positionalHelpers(request) {
|
|
6
|
+
return {
|
|
7
|
+
// ── Identity ──────────────────────────────────────────────────────────────
|
|
8
|
+
auth: {
|
|
9
|
+
// `register` and `login` were removed in 0.6: the Atlas cutover removed `/auth/register`
|
|
10
|
+
// and `/auth/login`, so both failed for every caller. Sign-in is Atlas (a browser
|
|
11
|
+
// flow); an integration authenticates with a Personal Access Token.
|
|
12
|
+
me: () => request('GET', '/auth/me'),
|
|
13
|
+
/** Mint a Personal Access Token (requires an authenticated session). */
|
|
14
|
+
createToken: (input) => request('POST', '/pats', {
|
|
15
|
+
body: input,
|
|
16
|
+
}),
|
|
17
|
+
},
|
|
18
|
+
// ── Organizations ─────────────────────────────────────────────────────────
|
|
19
|
+
orgs: {
|
|
20
|
+
list: () => request('GET', '/orgs'),
|
|
21
|
+
create: (input) => request('POST', '/orgs', { body: input }),
|
|
22
|
+
},
|
|
23
|
+
// ── Spaces & content ───────────────────────────────────────────────────────
|
|
24
|
+
spaces: {
|
|
25
|
+
list: (orgId) => request('GET', `/orgs/${orgId}/spaces`),
|
|
26
|
+
create: (orgId, input) => request('POST', `/orgs/${orgId}/spaces`, { body: input }),
|
|
27
|
+
createCollection: (orgId, spaceId, input) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/collections`, { body: input }),
|
|
28
|
+
createPage: (orgId, spaceId, collectionId, input) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/collections/${collectionId}/pages`, { body: input }),
|
|
29
|
+
setPageContent: (orgId, spaceId, pageId, input) => request('PUT', `/orgs/${orgId}/spaces/${spaceId}/pages/${pageId}/content`, {
|
|
30
|
+
body: input,
|
|
31
|
+
}),
|
|
32
|
+
publishPage: (orgId, spaceId, pageId) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/pages/${pageId}/publish`, { body: {} }),
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* Shapes and pieces — the surface an agent works through.
|
|
36
|
+
*
|
|
37
|
+
* `shape` says what one piece of a space is made of and hands back JSON
|
|
38
|
+
* Schema; `items` writes one, whatever a piece happens to be in that space:
|
|
39
|
+
* a documentation page, a blog post, a release, an incident. The reply says
|
|
40
|
+
* which required sections are still empty, so an agent can create a piece,
|
|
41
|
+
* ask what it forgot, and close the gap.
|
|
42
|
+
*/
|
|
43
|
+
shapes: {
|
|
44
|
+
get: (orgId, spaceId) => request('GET', `/orgs/${orgId}/spaces/${spaceId}/shape`),
|
|
45
|
+
set: (orgId, spaceId, patch) => request('PUT', `/orgs/${orgId}/spaces/${spaceId}/shape`, {
|
|
46
|
+
body: patch,
|
|
47
|
+
}),
|
|
48
|
+
applyPreset: (orgId, spaceId, name) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/shape/presets/${name}`, {
|
|
49
|
+
body: {},
|
|
50
|
+
}),
|
|
51
|
+
},
|
|
52
|
+
items: {
|
|
53
|
+
create: (orgId, spaceId, input) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/items`, {
|
|
54
|
+
body: input,
|
|
55
|
+
}),
|
|
56
|
+
/**
|
|
57
|
+
* What is in a space: how something finds a piece it has no words for.
|
|
58
|
+
*
|
|
59
|
+
* Pass `include: ['body']` to get content. Asking for `sections` ALONE is
|
|
60
|
+
* the trap: it is populated only for pieces written against a shape, so a
|
|
61
|
+
* space of free-form pages answers with empty objects and a sync carries
|
|
62
|
+
* nothing while reporting success. Every row says which field holds its
|
|
63
|
+
* content in `contentIn`.
|
|
64
|
+
*
|
|
65
|
+
* Any option here puts the response in its paged form, so `page` and
|
|
66
|
+
* `sync` come back — `page.total` is what a walk should check its own
|
|
67
|
+
* count against, and `sync.checkpoint` is what an incremental sync stores.
|
|
68
|
+
*/
|
|
69
|
+
list: (orgId, spaceId, opts = {}) => {
|
|
70
|
+
// A bare number used to be the whole signature; keep it working.
|
|
71
|
+
const o = typeof opts === 'number' ? { limit: opts } : opts;
|
|
72
|
+
const query = {};
|
|
73
|
+
if (o.limit)
|
|
74
|
+
query.limit = o.limit;
|
|
75
|
+
if (o.include?.length)
|
|
76
|
+
query.include = o.include.join(',');
|
|
77
|
+
if (o.cursor)
|
|
78
|
+
query.cursor = o.cursor;
|
|
79
|
+
if (o.updatedSince)
|
|
80
|
+
query.updated_since = o.updatedSince;
|
|
81
|
+
if (o.includeDeleted)
|
|
82
|
+
query.include_deleted = 'true';
|
|
83
|
+
if (o.fields?.length)
|
|
84
|
+
query.fields = o.fields.join(',');
|
|
85
|
+
return request('GET', `/orgs/${orgId}/spaces/${spaceId}/items`, { query });
|
|
86
|
+
},
|
|
87
|
+
get: (orgId, spaceId, itemId) => request('GET', `/orgs/${orgId}/spaces/${spaceId}/items/${itemId}`),
|
|
88
|
+
/**
|
|
89
|
+
* Make it readable. Kinds that are live on creation say so and change nothing.
|
|
90
|
+
*
|
|
91
|
+
* `conformance` describes the piece AFTER publishing — which declared slots
|
|
92
|
+
* are filled and which are still missing — so an agent that has just made
|
|
93
|
+
* its work readable can tell a complete piece from one with holes in it
|
|
94
|
+
* without fetching it again. The server has always sent it and this type
|
|
95
|
+
* omitted it, so a TypeScript caller could not see the field the docs
|
|
96
|
+
* promise (E-11).
|
|
97
|
+
*/
|
|
98
|
+
publish: (orgId, spaceId, itemId) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/items/${itemId}/publish`, { body: {} }),
|
|
99
|
+
/**
|
|
100
|
+
* Replace a piece's whole body — as markdown, or as blocks.
|
|
101
|
+
*
|
|
102
|
+
* The create door has always taken markdown and this one took blocks only,
|
|
103
|
+
* so "make it from this file" worked and "update it from the same file" did
|
|
104
|
+
* not. Both now; blocks win if a caller sends both.
|
|
105
|
+
*/
|
|
106
|
+
setContent: (orgId, spaceId, itemId, input) => request('PUT', `/orgs/${orgId}/spaces/${spaceId}/items/${itemId}/content`, { body: input }),
|
|
107
|
+
/** Replace one named section. Idempotent — sending it twice writes it once. */
|
|
108
|
+
setSection: (orgId, spaceId, itemId, key, markdown) => request('PUT', `/orgs/${orgId}/spaces/${spaceId}/items/${itemId}/sections/${key}`, { body: { markdown } }),
|
|
109
|
+
},
|
|
110
|
+
/**
|
|
111
|
+
* The addresses a space answers at.
|
|
112
|
+
*
|
|
113
|
+
* Published because the question a customer asks about a custom domain —
|
|
114
|
+
* "why is it not live yet" — is answerable only from `verified`, `active` and
|
|
115
|
+
* the setup records, and the SDK could not reach any of them (DX-2). A failed
|
|
116
|
+
* verification is a 200 with `verified: false`: read the body, not the status.
|
|
117
|
+
*/
|
|
118
|
+
domains: {
|
|
119
|
+
list: (orgId, spaceId) => request('GET', `/orgs/${orgId}/spaces/${spaceId}/domains`),
|
|
120
|
+
add: (orgId, spaceId, input) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/domains`, {
|
|
121
|
+
body: input,
|
|
122
|
+
}),
|
|
123
|
+
/** Re-run the ownership check. `activate: false` proves it without switching traffic. */
|
|
124
|
+
verify: (orgId, spaceId, domainId, activate = true) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/domains/${domainId}/verify`, { body: { activate } }),
|
|
125
|
+
/** Switch traffic to an address whose ownership is already proven. */
|
|
126
|
+
activate: (orgId, spaceId, domainId) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/domains/${domainId}/activate`, { body: {} }),
|
|
127
|
+
/** What to configure, and what is still missing. */
|
|
128
|
+
setup: (orgId, spaceId, domainId) => request('GET', `/orgs/${orgId}/spaces/${spaceId}/domains/${domainId}/setup`),
|
|
129
|
+
},
|
|
130
|
+
// ── Webhooks ───────────────────────────────────────────────────────────────
|
|
131
|
+
webhooks: {
|
|
132
|
+
list: (orgId) => request('GET', `/orgs/${orgId}/webhooks`),
|
|
133
|
+
create: (orgId, input) => request('POST', `/orgs/${orgId}/webhooks`, { body: input }),
|
|
134
|
+
/** Check a delivery's `X-Forumulate-Signature`; see {@link verifyWebhookSignature}. */
|
|
135
|
+
verifySignature: webhooks_1.verifyWebhookSignature,
|
|
136
|
+
/** Verify a delivery and parse it; throws when the signature does not hold. */
|
|
137
|
+
constructEvent: webhooks_1.constructWebhookEvent,
|
|
138
|
+
},
|
|
139
|
+
// ── Space snapshots (version history) ───────────────────────────────────────
|
|
140
|
+
snapshots: {
|
|
141
|
+
list: (orgId, spaceId) => request('GET', `/orgs/${orgId}/spaces/${spaceId}/snapshots`),
|
|
142
|
+
create: (orgId, spaceId, label) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/snapshots`, {
|
|
143
|
+
body: { label },
|
|
144
|
+
}),
|
|
145
|
+
restore: (orgId, spaceId, id) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/snapshots/${id}/restore`, { body: {} }),
|
|
146
|
+
},
|
|
147
|
+
// ── Community (forum topics) ────────────────────────────────────────────────
|
|
148
|
+
forum: {
|
|
149
|
+
listTopics: (orgId, spaceId, query = {}) => request('GET', `/orgs/${orgId}/spaces/${spaceId}/forum`, { query }),
|
|
150
|
+
createTopic: (orgId, spaceId, input) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/forum`, {
|
|
151
|
+
body: input,
|
|
152
|
+
}),
|
|
153
|
+
reply: (orgId, spaceId, topicId, content) => request('POST', `/orgs/${orgId}/spaces/${spaceId}/forum/${topicId}/replies`, { body: { content } }),
|
|
154
|
+
},
|
|
155
|
+
// ── Search & analytics (org-level) ──────────────────────────────────────────
|
|
156
|
+
search: {
|
|
157
|
+
unified: (orgId, q, opts = {}) => request('GET', `/orgs/${orgId}/search`, {
|
|
158
|
+
query: { q, ...opts },
|
|
159
|
+
}),
|
|
160
|
+
},
|
|
161
|
+
analytics: {
|
|
162
|
+
orgEvents: (orgId, days) => request('GET', `/orgs/${orgId}/analytics/events`, {
|
|
163
|
+
query: days ? { days } : {},
|
|
164
|
+
}),
|
|
165
|
+
},
|
|
166
|
+
// ── Public reader (no auth required) ────────────────────────────────────────
|
|
167
|
+
public: {
|
|
168
|
+
getPage: (orgId, spaceId, pageId) => request('GET', `/public/orgs/${orgId}/spaces/${spaceId}/pages/${pageId}`),
|
|
169
|
+
search: (orgId, spaceId, query) => request('GET', `/public/orgs/${orgId}/spaces/${spaceId}/search`, {
|
|
170
|
+
query: { q: query },
|
|
171
|
+
}),
|
|
172
|
+
listComments: (orgId, spaceId, pageId) => request('GET', `/public/orgs/${orgId}/spaces/${spaceId}/pages/${pageId}/comments`),
|
|
173
|
+
postComment: (orgId, spaceId, pageId, input) => request('POST', `/public/orgs/${orgId}/spaces/${spaceId}/pages/${pageId}/comments`, { body: input }),
|
|
174
|
+
reactToComment: (orgId, spaceId, commentId, emoji) => request('POST', `/public/orgs/${orgId}/spaces/${spaceId}/comments/${commentId}/react`, { body: { emoji } }),
|
|
175
|
+
/** Download the whole space as a PDF. Returns the raw `Response` (read `.blob()` / `.arrayBuffer()`). */
|
|
176
|
+
pdf: (orgId, spaceId) => request('GET', `/public/orgs/${orgId}/spaces/${spaceId}/pdf`, { raw: true }),
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=helpers.js.map
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place a request leaves the SDK: headers, body encoding, retries,
|
|
3
|
+
* response decoding and the error type. Every surface — the positional
|
|
4
|
+
* helpers, `typed()`, `request(operationId)` and the generated namespaces —
|
|
5
|
+
* goes through `send`, so a retry or a header rule cannot apply to one door
|
|
6
|
+
* and not another.
|
|
7
|
+
*/
|
|
8
|
+
/** Kept in step with package.json by `coverage.test.ts`. */
|
|
9
|
+
export declare const SDK_VERSION = "0.7.0";
|
|
10
|
+
/** The API's error envelope: `{ error: { code, message, details? }, requestId? }`. */
|
|
11
|
+
export interface ForumulateErrorInit {
|
|
12
|
+
/** Field-level detail, e.g. `{ title: ['Required'] }` for `validation.failed`. */
|
|
13
|
+
details?: unknown;
|
|
14
|
+
/** The id to quote in a support request; the API puts it on every error. */
|
|
15
|
+
requestId?: string;
|
|
16
|
+
/** Seconds the API asked us to wait (`Retry-After`), when it said. */
|
|
17
|
+
retryAfter?: number;
|
|
18
|
+
/** The parsed body, whole, for anything the fields above do not carry. */
|
|
19
|
+
body?: unknown;
|
|
20
|
+
/** The response headers of the failed request. */
|
|
21
|
+
headers?: Headers;
|
|
22
|
+
cause?: unknown;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Thrown for any non-2xx API response, and for a network failure that
|
|
26
|
+
* outlived its retries (`status: 0`, `code: 'network.error'`). Mirrors the
|
|
27
|
+
* API's error envelope.
|
|
28
|
+
*/
|
|
29
|
+
export declare class ForumulateError extends Error {
|
|
30
|
+
readonly status: number;
|
|
31
|
+
readonly code: string;
|
|
32
|
+
readonly details?: unknown;
|
|
33
|
+
readonly requestId?: string;
|
|
34
|
+
readonly retryAfter?: number;
|
|
35
|
+
readonly body?: unknown;
|
|
36
|
+
readonly headers?: Headers;
|
|
37
|
+
constructor(status: number, code: string, message: string, init?: ForumulateErrorInit);
|
|
38
|
+
}
|
|
39
|
+
export type Query = Record<string, string | number | boolean | readonly (string | number)[] | undefined | null>;
|
|
40
|
+
/** Per-call options every surface accepts. */
|
|
41
|
+
export interface CallOptions {
|
|
42
|
+
/** Extra headers for this request; they win over the client's. */
|
|
43
|
+
headers?: Record<string, string>;
|
|
44
|
+
/**
|
|
45
|
+
* Makes a write safe to repeat: the API runs the first request with a key
|
|
46
|
+
* and replays its answer to any repeat for 24 hours. POST and PATCH get a
|
|
47
|
+
* generated one by default (reused across this call's own retries); pass
|
|
48
|
+
* yours to dedupe across processes, e.g. a queue message id.
|
|
49
|
+
*/
|
|
50
|
+
idempotencyKey?: string;
|
|
51
|
+
signal?: AbortSignal;
|
|
52
|
+
/** Overrides the client's `maxRetries` for this call. */
|
|
53
|
+
maxRetries?: number;
|
|
54
|
+
}
|
|
55
|
+
export interface SendInit extends CallOptions {
|
|
56
|
+
method: string;
|
|
57
|
+
path: string;
|
|
58
|
+
query?: Query;
|
|
59
|
+
body?: unknown;
|
|
60
|
+
/** Return the untouched `Response` (binary downloads, streams). */
|
|
61
|
+
raw?: boolean;
|
|
62
|
+
/** How to read a 2xx body when it is not JSON. */
|
|
63
|
+
kind?: 'json' | 'text' | 'binary';
|
|
64
|
+
/** `Forumulate-Version` to send unless the caller already set one. */
|
|
65
|
+
version?: string;
|
|
66
|
+
}
|
|
67
|
+
export interface Transport {
|
|
68
|
+
baseUrl: string;
|
|
69
|
+
token?: string;
|
|
70
|
+
credentials: boolean;
|
|
71
|
+
headers: Record<string, string>;
|
|
72
|
+
fetch: typeof fetch;
|
|
73
|
+
maxRetries: number;
|
|
74
|
+
autoIdempotencyKey: boolean;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* How long to wait before retry number `attempt` (0-based), or `null` to stop.
|
|
78
|
+
*
|
|
79
|
+
* `Retry-After` wins when present — seconds or an HTTP date — because the
|
|
80
|
+
* API sends it on purpose (a 429's window, a 503's batch interruption). Without
|
|
81
|
+
* it: exponential from 0.5s, capped at 8s, with jitter so a fleet of clients
|
|
82
|
+
* that failed together does not retry together.
|
|
83
|
+
*/
|
|
84
|
+
export declare function retryDelayMs(attempt: number, retryAfter: string | null | undefined, now?: number): number | null;
|
|
85
|
+
export declare function send<T>(t: Transport, init: SendInit): Promise<T>;
|
|
86
|
+
//# sourceMappingURL=http.d.ts.map
|