@vercel/board 0.16.0 → 0.17.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 +27 -27
- package/dist/{a2-C41hD8SF.mjs → a2-Bck5x1Kh.mjs} +969 -25
- package/dist/access.d.mts +1 -1
- package/dist/access.mjs +3 -3
- package/dist/{actors-DjMeZpnU.mjs → actors-BOamkEuz.mjs} +3 -73
- package/dist/attempts-CLG840Oj.mjs +20 -0
- package/dist/{auth-COzc09sn.mjs → auth-Drg9FiC2.mjs} +225 -2
- package/dist/backend-B-SJ7p6p.d.mts +680 -0
- package/dist/backend-a2/contract.d.mts +873 -2
- package/dist/backend-a2/contract.mjs +2 -2
- package/dist/backend-a2/scheduler-local.d.mts +1 -1
- package/dist/backend-a2/scheduler-local.mjs +1 -1
- package/dist/backend-a2/scheduler-qstash.d.mts +78 -1
- package/dist/backend-a2/scheduler-qstash.mjs +521 -1
- package/dist/backend-a2/scheduler-vercel.d.mts +37 -1
- package/dist/backend-a2/scheduler-vercel.mjs +203 -1
- package/dist/backend-a2/store-memory.d.mts +2 -2
- package/dist/backend-a2/store-memory.mjs +3 -3
- package/dist/backend-a2/store-postgres.d.mts +1 -1
- package/dist/backend-a2/store-postgres.mjs +2850 -2
- package/dist/backend-a2/store-redis-http.d.mts +1 -1
- package/dist/backend-a2/store-redis-http.mjs +490 -2
- package/dist/backend-a2/store-redis.d.mts +10 -2
- package/dist/backend-a2/store-redis.mjs +51 -2
- package/dist/backend-a2/store-sqlite.d.mts +1 -1
- package/dist/backend-a2/store-sqlite.mjs +3 -3
- package/dist/backend-a2.d.mts +2706 -3
- package/dist/backend-a2.mjs +1 -1
- package/dist/{backend-http-Bid7RIpC.d.mts → backend-http-7cNb8nJI.d.mts} +10 -3
- package/dist/{backend-http-0roEmjl0.mjs → backend-http-C5I7Wfdw.mjs} +2 -2
- package/dist/{backend-http-BVRno-ca.mjs → backend-http-CuUIACCs.mjs} +32 -3
- package/dist/backend-http.d.mts +1 -1
- package/dist/backend-http.mjs +1 -1
- package/dist/backend-http.node.d.mts +1 -1
- package/dist/backend-http.node.mjs +1 -1
- package/dist/{chat-provider-XCnQlxq6.d.mts → chat-provider-DNJQyajS.d.mts} +16 -4
- package/dist/{chat-provider-jFSeyNmB.mjs → chat-provider-YHy-WbrB.mjs} +15 -9
- package/dist/cli.mjs +30 -10
- package/dist/{client-DGDPaN7U.mjs → client-BA-J7usH.mjs} +10 -2
- package/dist/{conformance-DRR23YNy.mjs → conformance-CT5EeHeC.mjs} +5 -2
- package/dist/conformance.d.mts +1 -1
- package/dist/conformance.mjs +1 -1
- package/dist/{engine-Bir5s8ts.mjs → engine-D0oBUBEK.mjs} +249 -163
- package/dist/events-CewD4kw7.mjs +75 -0
- package/dist/{exports-Dvc1o52I.mjs → exports-DOdA7KhF.mjs} +35 -32
- package/dist/exports-ZsgjzEnM.d.mts +4 -0
- package/dist/external-BV_cUncs.mjs +151 -0
- package/dist/folds-CRYY14kc.d.mts +534 -0
- package/dist/{reducers-mUPFuFJV.mjs → folds-Dyjput8M.mjs} +6 -73
- package/dist/ids-C6AYo1bu.mjs +31 -0
- package/dist/index.d.mts +4 -4
- package/dist/{index.default-Bkg77Pxn.mjs → index.default-n4qKN7EL.mjs} +1 -1
- package/dist/index.mjs +5 -5
- package/dist/index.node.d.mts +4 -4
- package/dist/index.node.mjs +3 -3
- package/dist/inspect/app.js +31 -31
- package/dist/inspect/engine.js +5 -5
- package/dist/internal-CvsrprnU-CRY89I-b.mjs +203 -0
- package/dist/{local-Cr_dSjvs.mjs → local-DhkrAqPP.mjs} +1 -1
- package/dist/mentions-CB6ShY4R.mjs +72 -0
- package/dist/{targets-yM5JBUgr.mjs → middleware-CqaCwucP.mjs} +1 -33
- package/dist/platform-B4TnJtWu-CwcTyKg2.mjs +32 -0
- package/dist/{project-CFiArraX.mjs → project-DMDST9kl.mjs} +1 -1
- package/dist/{provider-BGjz-mDa.d.mts → provider-B0b9Qxw4.d.mts} +170 -40
- package/dist/provider-B3DpYSDq.mjs +220 -0
- package/dist/redis-tables-DN9PfIDi.mjs +3897 -0
- package/dist/reducer-CYPHsSmd-BQIfozGi.d.mts +170 -0
- package/dist/reducer-CjmvZSAb-D_OSISmj.mjs +74 -0
- package/dist/{contract-CuJmlrhw.mjs → reducers-BFmIXilu.mjs} +260 -287
- package/dist/retryable-lazy-DZWmHpii-Bye-W8q7.mjs +19 -0
- package/dist/scheduler-task-X1SToDjP-BVoWuInI.mjs +69 -0
- package/dist/{serve-BsOOc-6_.mjs → serve-CNYvA4pT.mjs} +5 -5
- package/dist/server-DtFrWxi3-Boa6gdWO.mjs +3602 -0
- package/dist/server-qgez0Xny-D5mLfqFQ.d.mts +385 -0
- package/dist/{server-jwPY7_wi.mjs → server-snYFkITb.mjs} +4 -4
- package/dist/{serving-BNbzc96L.mjs → serving-CNkgUPW1.mjs} +1 -1
- package/dist/session-clear-wHd5GCpZ-BnKSxReK.mjs +51 -0
- package/dist/{sql-DbiEnqM0.mjs → sql-U9WTxrwJ.mjs} +2 -2
- package/dist/{sqlite-db-Cbj03vmp.mjs → sqlite-db-Ds4majtT.mjs} +1 -1
- package/dist/store-D0MhBvg6-4Z2L0Kqd.d.mts +530 -0
- package/dist/store-N8PXxDAS-BWBgzBHp.mjs +5 -0
- package/dist/store-codec-DwrWlgQx-CYxGW0aD.mjs +61 -0
- package/dist/store-memory-Bas9Uq5C.mjs +872 -0
- package/dist/store-memory-ZLiZbH3R.mjs +2 -0
- package/dist/store-polling-BZt2Rf3D-BiEM6_HB.mjs +148 -0
- package/dist/store-presence-polling-JuAy2Ck2-CYpuK75X.mjs +109 -0
- package/dist/store-sqlite-BmwL3pUR.mjs +2 -0
- package/dist/store-sqlite-CxxPCcCT.mjs +1053 -0
- package/dist/stream-buffer-DrTyiIuo-BKy-vI14.mjs +53 -0
- package/dist/sync-github.d.mts +24 -17
- package/dist/sync-github.mjs +89 -22
- package/dist/sync-kapso.d.mts +15 -13
- package/dist/sync-kapso.mjs +17 -14
- package/dist/sync-slack.d.mts +12 -17
- package/dist/sync-slack.mjs +54 -42
- package/dist/sync.chat.d.mts +1 -1
- package/dist/sync.chat.mjs +1 -1
- package/dist/sync.d.mts +3 -3
- package/dist/sync.mjs +5 -4
- package/dist/targets-C-wdR5wP.mjs +33 -0
- package/dist/{tokens-DKLn5P9B.mjs → tokens-DhuzKzAl.mjs} +1 -1
- package/dist/{types-BHCjTZdB.d.mts → types-8yq2VnKI.d.mts} +2 -2
- package/dist/{users-Dls7_ANV.mjs → users-yryHKFMo.mjs} +2 -2
- package/dist/{watch-spec-W0c7ypHJ.mjs → watchers-hash-DojIuoFg.mjs} +233 -28
- package/docs/01-board/01-mounting.md +1 -1
- package/docs/02-sync/01-overview.md +69 -47
- package/docs/02-sync/02-functions.md +395 -0
- package/docs/02-sync/03-slack.md +30 -25
- package/docs/02-sync/04-github.md +62 -26
- package/docs/02-sync/05-whatsapp.md +23 -19
- package/docs/02-sync/06-providers.md +58 -26
- package/docs/03-guides/04-triage.md +1 -1
- package/docs/04-reference/01-cli.md +26 -6
- package/docs/04-reference/02-board.md +18 -5
- package/docs/04-reference/03-http.md +5 -2
- package/docs/04-reference/05-conformance.md +1 -1
- package/docs/index.md +5 -10
- package/package.json +22 -6
- package/dist/a2-D24cvwpN.d.mts +0 -3359
- package/dist/access-CJU2-vaa.mjs +0 -226
- package/dist/attempts-Bxp9KgPf.mjs +0 -919
- package/dist/exports-DLiE7fLY.d.mts +0 -4
- package/dist/provider-CuewcDNZ.mjs +0 -205
- package/dist/redis-tables-B35vv4CZ.mjs +0 -842
- package/dist/reducers-B_vUGQRp.d.mts +0 -1172
- package/dist/watchers-hash-CLjsa6od.mjs +0 -225
- package/docs/02-sync/02-callbacks.md +0 -265
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
import { t as Access } from "./access-CV96SYgr.mjs";
|
|
2
|
+
import { m as RuleFunction, n as AccessRule, r as Authenticate, s as Granted, t as AccessFunction, u as Known } from "./auth-CU56Q2Xh.mjs";
|
|
3
|
+
import { i as Attachment, o as Author, p as Labels } from "./schemas-CWxs3ECJ.mjs";
|
|
4
|
+
import { t as Actor, u as ExternalEvent } from "./folds-CRYY14kc.mjs";
|
|
5
|
+
import { $ as Discovery, Et as PostBody, F as BoardClient, Ft as ScheduleBody, Gt as SourceReadQuery, Kt as SyncAccountBody, Mt as RegistrationView, Qt as SyncCopiesView, Rt as ScheduleView, St as MessagesQuery, Tt as PersonView, Wt as SourceRead, X as BoardEventView, Xt as SyncCodeView, Yt as SyncCodeBody, Z as ChannelView, Zt as SyncCopiesQuery, _n as UploadStartBody, _t as MatchView, bt as MessageView, cn as SyncUnlinkBody, ct as InboxEntry, dn as ThreadView, dt as InboxPushBody, en as SyncFindQuery, et as EditBody, fn as ThreadsPage, ft as InboxQuery, gt as LinkedQuery, hn as TypingBody, ht as LinkedAccount, in as SyncReceiptBody, jt as RegisterBody, kt as ReactBody, ln as SyncedView, lt as InboxItem, nn as SyncImportBody, nt as EventsQuery, on as SyncRootsQuery, ot as IdentityView, p as SyncProvider, pn as ThreadsQuery, pt as LabelBody, q as AckBody, sn as SyncThreadView, st as InboxAckBody, tn as SyncFindView, tt as EventsPage, ut as InboxNextQuery, vn as UploadTicket, vt as MatchesQuery, xt as MessagesPage, yt as Me, z as ProfileOption, zt as SchedulesQuery } from "./provider-B0b9Qxw4.mjs";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
//#region src/api/protocol.d.ts
|
|
8
|
+
type Input<S extends z.ZodType> = NonNullable<z.output<S>>;
|
|
9
|
+
type Id = {
|
|
10
|
+
id: string;
|
|
11
|
+
};
|
|
12
|
+
type Key = {
|
|
13
|
+
key: string;
|
|
14
|
+
};
|
|
15
|
+
type Nothing = Record<string, never>;
|
|
16
|
+
/**
|
|
17
|
+
* The protocol, method by method: what each takes and what it answers, as the wire carries them.
|
|
18
|
+
* board() serves each at its route in `routes` and calls the backend's method of the same name.
|
|
19
|
+
*/
|
|
20
|
+
type Protocol = {
|
|
21
|
+
/** Where the board is and what it syncs, for a client to mint a token from: it needs none itself. */
|
|
22
|
+
discovery: {
|
|
23
|
+
input: Nothing;
|
|
24
|
+
output: Discovery;
|
|
25
|
+
};
|
|
26
|
+
me: {
|
|
27
|
+
input: Nothing;
|
|
28
|
+
output: Me;
|
|
29
|
+
};
|
|
30
|
+
channels: {
|
|
31
|
+
input: Nothing;
|
|
32
|
+
output: {
|
|
33
|
+
channels: ChannelView[];
|
|
34
|
+
cursor: string;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
actors: {
|
|
38
|
+
input: Nothing;
|
|
39
|
+
output: {
|
|
40
|
+
actors: (Actor & {
|
|
41
|
+
person?: PersonView;
|
|
42
|
+
})[];
|
|
43
|
+
cursor: string;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
identity: {
|
|
47
|
+
input: Nothing;
|
|
48
|
+
output: IdentityView;
|
|
49
|
+
};
|
|
50
|
+
messages: {
|
|
51
|
+
input: Input<typeof MessagesQuery>;
|
|
52
|
+
output: MessagesPage;
|
|
53
|
+
};
|
|
54
|
+
threads: {
|
|
55
|
+
input: Input<typeof ThreadsQuery>;
|
|
56
|
+
output: ThreadsPage;
|
|
57
|
+
};
|
|
58
|
+
message: {
|
|
59
|
+
input: Id;
|
|
60
|
+
output: ThreadView;
|
|
61
|
+
};
|
|
62
|
+
post: {
|
|
63
|
+
input: Input<typeof PostBody>;
|
|
64
|
+
output: {
|
|
65
|
+
message: MessageView;
|
|
66
|
+
unknownMentions?: string[];
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
/** The caller's own post with client ID `id`, as it landed: 404 `post_not_found` when there's none. */
|
|
70
|
+
posted: {
|
|
71
|
+
input: Id;
|
|
72
|
+
output: {
|
|
73
|
+
message: MessageView;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
edit: {
|
|
77
|
+
input: Id & Input<typeof EditBody>;
|
|
78
|
+
output: {
|
|
79
|
+
ok: true;
|
|
80
|
+
unknownMentions?: string[];
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
delete: {
|
|
84
|
+
input: Id;
|
|
85
|
+
output: {
|
|
86
|
+
ok: true;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
react: {
|
|
90
|
+
input: Id & Input<typeof ReactBody>;
|
|
91
|
+
output: {
|
|
92
|
+
ok: true;
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
label: {
|
|
96
|
+
input: Id & Input<typeof LabelBody>;
|
|
97
|
+
output: {
|
|
98
|
+
ok: true;
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
schedule: {
|
|
102
|
+
input: Input<typeof ScheduleBody>;
|
|
103
|
+
output: {
|
|
104
|
+
schedule: ScheduleView;
|
|
105
|
+
unknownMentions?: string[];
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
schedules: {
|
|
109
|
+
input: Input<typeof SchedulesQuery>;
|
|
110
|
+
output: {
|
|
111
|
+
schedules: ScheduleView[];
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
cancelSchedule: {
|
|
115
|
+
input: Id;
|
|
116
|
+
output: {
|
|
117
|
+
schedule: ScheduleView;
|
|
118
|
+
canceled: boolean;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
upload: {
|
|
122
|
+
input: {
|
|
123
|
+
data: Uint8Array;
|
|
124
|
+
name: string | null;
|
|
125
|
+
type: string | null;
|
|
126
|
+
};
|
|
127
|
+
output: {
|
|
128
|
+
attachment: Attachment;
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
attachment: {
|
|
132
|
+
input: Id;
|
|
133
|
+
output: {
|
|
134
|
+
attachment: Attachment;
|
|
135
|
+
data: Uint8Array | ReadableStream<Uint8Array>;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
/** A direct upload's ticket: where to `PUT` a file too big for `upload`. 501 `no_direct_uploads` when the blob store can't sign one. */
|
|
139
|
+
uploadStart: {
|
|
140
|
+
input: Input<typeof UploadStartBody>;
|
|
141
|
+
output: UploadTicket;
|
|
142
|
+
};
|
|
143
|
+
/** The attachment, once the bytes are in the store: 409 `not_uploaded` before. Finishing one twice answers the same. */
|
|
144
|
+
uploadFinish: {
|
|
145
|
+
input: Id;
|
|
146
|
+
output: {
|
|
147
|
+
attachment: Attachment;
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
watchers: {
|
|
151
|
+
input: Nothing;
|
|
152
|
+
output: {
|
|
153
|
+
watchers: RegistrationView[];
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
register: {
|
|
157
|
+
input: Input<typeof RegisterBody>;
|
|
158
|
+
output: RegistrationView;
|
|
159
|
+
};
|
|
160
|
+
matches: {
|
|
161
|
+
input: Input<typeof MatchesQuery>;
|
|
162
|
+
output: {
|
|
163
|
+
matches: MatchView[];
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
ack: {
|
|
167
|
+
input: Id & Input<typeof AckBody>;
|
|
168
|
+
output: {
|
|
169
|
+
ok: true;
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
/** Long-polls with `wait`. board() serves SSE from it, so a backend has one way to read events. */
|
|
173
|
+
events: {
|
|
174
|
+
input: Input<typeof EventsQuery>;
|
|
175
|
+
output: EventsPage;
|
|
176
|
+
};
|
|
177
|
+
/** Typing in the platform threads the message's thread is synced to: needs `post` there. Answers the threads that took it. */
|
|
178
|
+
typing: {
|
|
179
|
+
input: Id & Input<typeof TypingBody>;
|
|
180
|
+
output: {
|
|
181
|
+
targets: string[];
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
/** A platform channel or thread, read with sync entry `key`'s app where that sync runs: needs `manage` everywhere. */
|
|
185
|
+
readSource: {
|
|
186
|
+
input: Key & Input<typeof SourceReadQuery>;
|
|
187
|
+
output: SourceRead;
|
|
188
|
+
};
|
|
189
|
+
/** Where a message's thread is synced, and its copy on each target, from sync's records: needs `read` where it is. */
|
|
190
|
+
synced: {
|
|
191
|
+
input: Id;
|
|
192
|
+
output: SyncedView;
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* The thread of the board message a platform link names, from sync's records: the platform is
|
|
196
|
+
* never asked. 404 `message_not_found` for a link the board didn't sync, or one the caller can't read.
|
|
197
|
+
*/
|
|
198
|
+
linked: {
|
|
199
|
+
input: Input<typeof LinkedQuery>;
|
|
200
|
+
output: ThreadView;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* A platform's write as its platform author, idempotent per platform event. `dropped`: the
|
|
204
|
+
* middleware kept a post or an edit off the board, and what it responded, for sync to tell the
|
|
205
|
+
* author on the platform.
|
|
206
|
+
*/
|
|
207
|
+
syncImport: {
|
|
208
|
+
input: Input<typeof SyncImportBody>;
|
|
209
|
+
output: {
|
|
210
|
+
message: string | null;
|
|
211
|
+
dropped?: string[];
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
/** A delivery, its files, a failure, or a thread's new target. `attached` is false for a target the thread had. */
|
|
215
|
+
syncReceipt: {
|
|
216
|
+
input: Input<typeof SyncReceiptBody>;
|
|
217
|
+
output: {
|
|
218
|
+
ok: true;
|
|
219
|
+
attached?: boolean;
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
syncThread: {
|
|
223
|
+
input: {
|
|
224
|
+
thread: string;
|
|
225
|
+
};
|
|
226
|
+
output: SyncThreadView;
|
|
227
|
+
};
|
|
228
|
+
syncFind: {
|
|
229
|
+
input: Input<typeof SyncFindQuery>;
|
|
230
|
+
output: SyncFindView;
|
|
231
|
+
};
|
|
232
|
+
/** Messages' copies on platforms that the caller's sync recorded, by target: another sync's never show. */
|
|
233
|
+
syncCopies: {
|
|
234
|
+
input: Input<typeof SyncCopiesQuery>;
|
|
235
|
+
output: SyncCopiesView;
|
|
236
|
+
};
|
|
237
|
+
syncRoots: {
|
|
238
|
+
input: Input<typeof SyncRootsQuery>;
|
|
239
|
+
output: {
|
|
240
|
+
roots: MessageView[];
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
/** Platform events into the caller's inbox, deduplicated by their platform IDs. */
|
|
244
|
+
inboxPush: {
|
|
245
|
+
input: Key & Input<typeof InboxPushBody>;
|
|
246
|
+
output: {
|
|
247
|
+
ok: true;
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
/** The caller's next inbox events, leased, one platform thread at a time and in order. */
|
|
251
|
+
inboxNext: {
|
|
252
|
+
input: Key & Input<typeof InboxNextQuery>;
|
|
253
|
+
output: {
|
|
254
|
+
items: InboxItem[];
|
|
255
|
+
};
|
|
256
|
+
};
|
|
257
|
+
inboxAck: {
|
|
258
|
+
input: Key & Id & Input<typeof InboxAckBody>;
|
|
259
|
+
output: {
|
|
260
|
+
ok: true;
|
|
261
|
+
};
|
|
262
|
+
};
|
|
263
|
+
/** Raw platform events, newest first, for debugging sync. */
|
|
264
|
+
inbox: {
|
|
265
|
+
input: Key & Input<typeof InboxQuery>;
|
|
266
|
+
output: {
|
|
267
|
+
events: InboxEntry[];
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
/** A code for linking the caller's account, and the sign-in that carries it when the entry signs people in. */
|
|
271
|
+
syncCode: {
|
|
272
|
+
input: Input<typeof SyncCodeBody>;
|
|
273
|
+
output: SyncCodeView;
|
|
274
|
+
};
|
|
275
|
+
/** 404 `bad_code` for a code that isn't live. */
|
|
276
|
+
syncAccount: {
|
|
277
|
+
input: Input<typeof SyncAccountBody>;
|
|
278
|
+
output: {
|
|
279
|
+
person: string;
|
|
280
|
+
link: LinkedAccount;
|
|
281
|
+
};
|
|
282
|
+
};
|
|
283
|
+
syncUnlink: {
|
|
284
|
+
input: Input<typeof SyncUnlinkBody>;
|
|
285
|
+
output: {
|
|
286
|
+
ok: true;
|
|
287
|
+
};
|
|
288
|
+
};
|
|
289
|
+
};
|
|
290
|
+
type Method = keyof Protocol;
|
|
291
|
+
//#endregion
|
|
292
|
+
//#region src/server/errors.d.ts
|
|
293
|
+
declare class HttpError extends Error {
|
|
294
|
+
readonly status: number;
|
|
295
|
+
readonly code: string;
|
|
296
|
+
constructor(status: number, code: string, message: string);
|
|
297
|
+
}
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/server/middleware.d.ts
|
|
300
|
+
/** Where a write came from, as the middleware sees it. */
|
|
301
|
+
type WriteSource =
|
|
302
|
+
/** The HTTP API: the CLI, the SDK, scripts, CI. */
|
|
303
|
+
{
|
|
304
|
+
kind: 'api';
|
|
305
|
+
} |
|
|
306
|
+
/** A synced platform's event, or a sync callback's own writes. `thread` is the platform thread. */
|
|
307
|
+
{
|
|
308
|
+
kind: 'sync';
|
|
309
|
+
key: string;
|
|
310
|
+
platform: string;
|
|
311
|
+
channel: string;
|
|
312
|
+
thread: string;
|
|
313
|
+
} |
|
|
314
|
+
/** The board's handle in this deployment: `team.post(…)`, or `team.as(name).post(…)`. `name` is null for the handle itself. */
|
|
315
|
+
{
|
|
316
|
+
kind: 'client';
|
|
317
|
+
name: string | null;
|
|
318
|
+
} |
|
|
319
|
+
/** A watcher's in-process client. */
|
|
320
|
+
{
|
|
321
|
+
kind: 'watcher';
|
|
322
|
+
id: string;
|
|
323
|
+
} |
|
|
324
|
+
/** A scheduled message's send. */
|
|
325
|
+
{
|
|
326
|
+
kind: 'schedule';
|
|
327
|
+
id: number;
|
|
328
|
+
};
|
|
329
|
+
/** The message a write would put on the board. `re` is its thread's root, and IDs are as clients see them. */
|
|
330
|
+
type Write = {
|
|
331
|
+
kind: 'post';
|
|
332
|
+
channel: string;
|
|
333
|
+
re: string | null;
|
|
334
|
+
text: string;
|
|
335
|
+
labels: Labels;
|
|
336
|
+
attachments: readonly Attachment[];
|
|
337
|
+
} | {
|
|
338
|
+
kind: 'edit';
|
|
339
|
+
id: string;
|
|
340
|
+
channel: string;
|
|
341
|
+
re: string | null;
|
|
342
|
+
text: string;
|
|
343
|
+
};
|
|
344
|
+
/** What `next` may change before the write lands. Labels apply to posts only. */
|
|
345
|
+
type WritePatch = {
|
|
346
|
+
text?: string;
|
|
347
|
+
labels?: Labels;
|
|
348
|
+
};
|
|
349
|
+
type MiddlewareContext = {
|
|
350
|
+
write: Readonly<Write>;
|
|
351
|
+
/** Who's writing. */
|
|
352
|
+
author: Readonly<Actor>;
|
|
353
|
+
source: Readonly<WriteSource>;
|
|
354
|
+
/**
|
|
355
|
+
* What to tell whoever wrote it when the write is dropped, where they wrote it from: a reply in
|
|
356
|
+
* their platform thread for sync, the error the API answers with otherwise.
|
|
357
|
+
*/
|
|
358
|
+
respond(text: string): Promise<void>;
|
|
359
|
+
/** The board, acting as `board:middleware`. Its writes skip the middleware. */
|
|
360
|
+
board: Board;
|
|
361
|
+
/**
|
|
362
|
+
* Hands the write to the next middleware, and after the last one, to the board.
|
|
363
|
+
* Not calling it drops the write, and so does emptying a write that has no files.
|
|
364
|
+
*/
|
|
365
|
+
next(patch?: WritePatch): Promise<void>;
|
|
366
|
+
};
|
|
367
|
+
type Middleware = (context: MiddlewareContext) => unknown;
|
|
368
|
+
/** A write the middleware didn't let through: the API answers 422 with what it responded. */
|
|
369
|
+
declare class DroppedError extends HttpError {
|
|
370
|
+
/** What the middleware responded, in order: none when it dropped the write without a word. */
|
|
371
|
+
readonly responses: readonly string[];
|
|
372
|
+
constructor(responses: readonly string[]);
|
|
373
|
+
}
|
|
374
|
+
//#endregion
|
|
375
|
+
//#region src/server/policy.d.ts
|
|
376
|
+
/** Who may use a board, and what they may do: `board()`'s options for it. */
|
|
377
|
+
type PolicyOptions = {
|
|
378
|
+
/**
|
|
379
|
+
* Who a request is: `oidcTokens()` from `@vercel/board/access` by default, which checks its bearer token
|
|
380
|
+
* for the board's URL (pass it `local` for `vb serve`'s, or test issuers). Or a host's own
|
|
381
|
+
* verifier, `(request) => ({ id, kind, handle })`: who the request is in the board's ID scheme,
|
|
382
|
+
* from its credential or its headers (a session cookie). It replaces the built-in one, so no
|
|
383
|
+
* token's audience or the board's own project counts then, unless it calls `oidcTokens()` itself.
|
|
384
|
+
* Null when it recognizes nothing; to reject, return or throw a `Response`.
|
|
385
|
+
*/
|
|
386
|
+
authenticate?: Authenticate;
|
|
387
|
+
/**
|
|
388
|
+
* Who besides the board's people and its own deployments in the environment it runs in may use
|
|
389
|
+
* it, and what they may do in each channel: `vercelProject()`, `githubActions()`, `oidc()` from
|
|
390
|
+
* `@vercel/board/access`, and functions of your own, `(principal) => can`, for principals your
|
|
391
|
+
* `authenticate` returns. The first rule that matches decides; a function's undefined leaves it to
|
|
392
|
+
* the next. A production board's previews need a rule, as
|
|
393
|
+
* `vercelProject({ project: 'self', environments: ['preview'], can })`.
|
|
394
|
+
*
|
|
395
|
+
* Or a host's own model: one function of the principal its `authenticate` returned, giving what it
|
|
396
|
+
* may do in each channel (`'all'`, `['read']`, `{ 'dev/': ['post'] }`), or null for nothing.
|
|
397
|
+
* Nothing else is trusted then, not the board's own people or deployments.
|
|
398
|
+
*
|
|
399
|
+
* On `http()`, a board with none serves no requests but wake-ups and webhooks; with one, it serves
|
|
400
|
+
* its callers within it, as its project on the board it uses.
|
|
401
|
+
*/
|
|
402
|
+
access?: readonly (AccessRule | RuleFunction)[] | AccessFunction;
|
|
403
|
+
/**
|
|
404
|
+
* The board's Vercel project: `self` in the rules, whose people's development tokens it takes,
|
|
405
|
+
* and what discovery names for clients to mint them. Defaults to `VERCEL_PROJECT_ID` or
|
|
406
|
+
* `.vercel/project.json`.
|
|
407
|
+
*/
|
|
408
|
+
project?: {
|
|
409
|
+
id: string;
|
|
410
|
+
teamId?: string;
|
|
411
|
+
};
|
|
412
|
+
};
|
|
413
|
+
//#endregion
|
|
414
|
+
//#region src/server/origin.d.ts
|
|
415
|
+
/**
|
|
416
|
+
* Where a write came from: `middleware` is the middleware's own client, which skips it, and
|
|
417
|
+
* `engine` is sync's own lookups and receipts.
|
|
418
|
+
*/
|
|
419
|
+
type Via = WriteSource | {
|
|
420
|
+
kind: 'middleware';
|
|
421
|
+
} | {
|
|
422
|
+
kind: 'engine';
|
|
423
|
+
};
|
|
424
|
+
/**
|
|
425
|
+
* How a write reaches the middleware: where it's from, and how to answer there. `schedule` marks a
|
|
426
|
+
* schedule's send, which a backend's own schedules set in-process: no request carries it.
|
|
427
|
+
*/
|
|
428
|
+
type WriteOrigin = {
|
|
429
|
+
via?: Via;
|
|
430
|
+
respond?: (text: string) => Promise<void>;
|
|
431
|
+
schedule?: {
|
|
432
|
+
id: number;
|
|
433
|
+
n: number;
|
|
434
|
+
};
|
|
435
|
+
};
|
|
436
|
+
//#endregion
|
|
437
|
+
//#region src/server/watcher.d.ts
|
|
438
|
+
/** What a watcher's `run` gets. */
|
|
439
|
+
type WatcherContext = {
|
|
440
|
+
/** Exactly what `vb tail --json` prints. */
|
|
441
|
+
event: BoardEventView;
|
|
442
|
+
/** The board, acting as this watcher. */
|
|
443
|
+
board: Board;
|
|
444
|
+
};
|
|
445
|
+
/** A `vb tail` the board runs for you: every event the tail would print, handed to `run`. */
|
|
446
|
+
type Watcher = {
|
|
447
|
+
/**
|
|
448
|
+
* A `vb tail` expression without `vb tail`: `firehose --label status=needs-agent`. `--all` adds
|
|
449
|
+
* delivery receipts, as in `vb tail`: `message.synced` says where a message landed on a platform.
|
|
450
|
+
*/
|
|
451
|
+
tail: string;
|
|
452
|
+
/**
|
|
453
|
+
* `matches` (the default) runs on every event the tail prints. `enters` runs only when a
|
|
454
|
+
* message enters the view: it didn't match before the event and does after it.
|
|
455
|
+
*/
|
|
456
|
+
when?: 'matches' | 'enters';
|
|
457
|
+
run: (context: WatcherContext) => unknown;
|
|
458
|
+
};
|
|
459
|
+
//#endregion
|
|
460
|
+
//#region src/server/board.d.ts
|
|
461
|
+
/**
|
|
462
|
+
* `board()`'s options: where the board lives, who may use it and what its writes go through, and
|
|
463
|
+
* what runs here. The backend keeps or reaches the log; the policy is the board's.
|
|
464
|
+
*/
|
|
465
|
+
type BoardOptions = PolicyOptions & {
|
|
466
|
+
/**
|
|
467
|
+
* Where the board lives: `a2({ … })` from `@vercel/board/backend-a2` keeps it here, `http(url)` from
|
|
468
|
+
* `@vercel/board/backend-http` uses another, or your own. By default, `http()`: in Node, the board the
|
|
469
|
+
* environment points at (`BOARD_URL`, the repo's link, or `vb use`'s).
|
|
470
|
+
*/
|
|
471
|
+
backend?: Backend;
|
|
472
|
+
/**
|
|
473
|
+
* Runs on every post and edit before it reaches the backend, whatever it came from: requests (a
|
|
474
|
+
* board on `http()` using this one included), watchers, schedules, the handle, and sync's imports.
|
|
475
|
+
* Each calls `next()` to pass the write on; one that doesn't drops it.
|
|
476
|
+
*/
|
|
477
|
+
middleware?: readonly Middleware[];
|
|
478
|
+
/** Tails the board runs for you, keyed by an id you choose: `{ triage: { tail: 'firehose', run } }`. */
|
|
479
|
+
watch?: Record<string, Watcher>;
|
|
480
|
+
/**
|
|
481
|
+
* Two-way sync, keyed by a name you choose: `{ slack: slack('slack/acme', channels({...})) }`. It
|
|
482
|
+
* runs here, wherever the board lives: webhooks land at `<board>/webhooks/<key>`, and what the
|
|
483
|
+
* board's channels say goes out to the platforms. On `http()`, the board it uses must let this
|
|
484
|
+
* deployment `sync` in those channels.
|
|
485
|
+
*/
|
|
486
|
+
sync?: Record<string, SyncProvider>;
|
|
487
|
+
/** The board's public URL, which every token's audience must equal. Inferred from requests otherwise. */
|
|
488
|
+
url?: string;
|
|
489
|
+
/** The mount path, e.g. `/api/board`. Inferred from requests otherwise: the shallowest one they show. */
|
|
490
|
+
basePath?: string;
|
|
491
|
+
/** SSE streams close after this long, before the platform's limit; clients reconnect. */
|
|
492
|
+
streamMaxMs?: number;
|
|
493
|
+
};
|
|
494
|
+
/**
|
|
495
|
+
* The handle on a board: its methods act as the board's caller, through board(), which calls the
|
|
496
|
+
* backend's methods, and `fetch` serves it. Over `http()` the caller is whoever its `fetch`'s
|
|
497
|
+
* credentials say, with the profile `BOARD_AS` or the agent it runs in picks; on a board it keeps
|
|
498
|
+
* (`a2()`), its own code, as `board:client`.
|
|
499
|
+
*/
|
|
500
|
+
type Board = BoardClient & {
|
|
501
|
+
/**
|
|
502
|
+
* The board's HTTP handler, for a route file (`export const GET = team.fetch, POST = team.fetch`)
|
|
503
|
+
* or any runtime that takes one function.
|
|
504
|
+
*/
|
|
505
|
+
fetch(request: Request): Promise<Response>;
|
|
506
|
+
/**
|
|
507
|
+
* The handle, acting with `profile`: `<principal>+<profile>` over HTTP, `board:client:<profile>`
|
|
508
|
+
* on `a2()`. `null` acts as yourself, outside any agent session.
|
|
509
|
+
*/
|
|
510
|
+
as(profile: ProfileOption): Board;
|
|
511
|
+
};
|
|
512
|
+
//#endregion
|
|
513
|
+
//#region src/server/backend.d.ts
|
|
514
|
+
/**
|
|
515
|
+
* Who a verified caller is: its principal, with what board()'s `access` lets it do now (its scope
|
|
516
|
+
* included, which only narrows), the author record its writes carry, and the actor it writes as.
|
|
517
|
+
* `records: false`: its writes leave the board's record of its actor (name, avatar) as it is, for
|
|
518
|
+
* a platform user a sync that can't sync everywhere writes as.
|
|
519
|
+
*/
|
|
520
|
+
type Identity = {
|
|
521
|
+
principal: Granted;
|
|
522
|
+
author: Author;
|
|
523
|
+
actor: Actor;
|
|
524
|
+
records?: false;
|
|
525
|
+
};
|
|
526
|
+
/**
|
|
527
|
+
* A caller board() verified, and what its policy lets it do: a request's, or for work a backend does
|
|
528
|
+
* outside requests, the one it does it for (a schedule's creator, at its send). A backend trusts it
|
|
529
|
+
* as it is: only board() makes one, in process.
|
|
530
|
+
*/
|
|
531
|
+
type VerifiedCaller = {
|
|
532
|
+
identity: Identity;
|
|
533
|
+
origin: WriteOrigin;
|
|
534
|
+
};
|
|
535
|
+
/** This deployment's own code, as it calls the backend through board(). */
|
|
536
|
+
type SelfCaller = {
|
|
537
|
+
/**
|
|
538
|
+
* A handle (`via: client`, with the profile from `as(profile)`, or null for the board's own), a
|
|
539
|
+
* watcher, the middleware's handle (`via: middleware`), or sync (`via: engine`). Each backend
|
|
540
|
+
* decides who that is on its board: `board:client` and `board:client:<profile>` on a2(), the
|
|
541
|
+
* token's holder with that profile elsewhere.
|
|
542
|
+
*/
|
|
543
|
+
self: string | null;
|
|
544
|
+
/**
|
|
545
|
+
* What `as(profile)` asked for, its avatar included, or null for yourself. Absent on the
|
|
546
|
+
* handle itself, whose caller is whoever the environment picks: a backend elsewhere sends
|
|
547
|
+
* the profile `BOARD_AS` or the agent it runs in names.
|
|
548
|
+
*/
|
|
549
|
+
profile?: ProfileOption;
|
|
550
|
+
origin: WriteOrigin;
|
|
551
|
+
};
|
|
552
|
+
/** Who's calling a backend's method: one board() verified, or this deployment's own code. Every method honors what it may do. */
|
|
553
|
+
type Caller = VerifiedCaller | SelfCaller;
|
|
554
|
+
/** One method per protocol route: the caller, the parsed input, and the request's signal. */
|
|
555
|
+
type ProtocolMethods = { [M in keyof Protocol]: (caller: Caller, input: Protocol[M]['input'], signal: AbortSignal) => Promise<Protocol[M]['output']>; };
|
|
556
|
+
/** A route of a backend's own, beside the protocol: http()'s wake-ups. */
|
|
557
|
+
type BackendRoute = {
|
|
558
|
+
method: 'GET' | 'POST';
|
|
559
|
+
/** Under the board's mount, with `:param` segments, e.g. `/wake`. One at a protocol route's path takes its place. */
|
|
560
|
+
path: string;
|
|
561
|
+
/** `caller` is this deployment's own code, or null for a request, which board() doesn't verify for it: the route checks what it needs. */
|
|
562
|
+
handle(request: Request, context: {
|
|
563
|
+
caller: Caller | null;
|
|
564
|
+
param: string | null;
|
|
565
|
+
}): Promise<Response>;
|
|
566
|
+
};
|
|
567
|
+
/** What runs on a board's events. */
|
|
568
|
+
type Subscription =
|
|
569
|
+
/** A watcher: every event its tail prints. */
|
|
570
|
+
{
|
|
571
|
+
kind: 'tail';
|
|
572
|
+
tail: string;
|
|
573
|
+
when: 'matches' | 'enters';
|
|
574
|
+
} |
|
|
575
|
+
/** Sync's outbound: every post, change, and thread sync in every channel, as its own code sees them. */
|
|
576
|
+
{
|
|
577
|
+
kind: 'sync';
|
|
578
|
+
};
|
|
579
|
+
/** One of board()'s sync entries: its key, its platform, and whether it can sign people in. */
|
|
580
|
+
type SyncEntry = {
|
|
581
|
+
key: string;
|
|
582
|
+
platform: string;
|
|
583
|
+
signIn: boolean;
|
|
584
|
+
};
|
|
585
|
+
/** A platform event sync pushed to an inbox, as the backend delivers it back. */
|
|
586
|
+
type InboxDelivery = {
|
|
587
|
+
event: ExternalEvent;
|
|
588
|
+
raw: unknown;
|
|
589
|
+
};
|
|
590
|
+
/** What a backend gets from the board it serves: once, in memory, when board() is created. */
|
|
591
|
+
type BoardBinding = {
|
|
592
|
+
/** What runs on this board's events, by ID: this project's watchers, and sync's outbound at `SYNC` when it syncs. */
|
|
593
|
+
subscriptions: Record<string, Subscription>;
|
|
594
|
+
/**
|
|
595
|
+
* Runs one subscription on an event. A throw is the backend's to retry; `attempt` counts tries
|
|
596
|
+
* from 1, and sync gives up on a delivery, and says so, before A2 would dead-letter it.
|
|
597
|
+
*/
|
|
598
|
+
run(id: string, event: WatcherContext['event'], meta: {
|
|
599
|
+
trigger: string;
|
|
600
|
+
attempt: number;
|
|
601
|
+
}): Promise<void>;
|
|
602
|
+
/**
|
|
603
|
+
* Runs one platform event this board's sync pushed to the backend's inbox, for a backend that
|
|
604
|
+
* delivers them in-process: one platform thread at a time, in order. `id` is the event's in the
|
|
605
|
+
* inbox, which the writes it makes derive their IDs from. A throw is the backend's to retry.
|
|
606
|
+
*/
|
|
607
|
+
receive(key: string, item: InboxDelivery, meta: {
|
|
608
|
+
id: string;
|
|
609
|
+
attempt: number;
|
|
610
|
+
signal: AbortSignal;
|
|
611
|
+
}): Promise<void>;
|
|
612
|
+
/** Pulls and runs what this board's sync queued in a backend's inbox, for one that queues them: http()'s, when it's woken. */
|
|
613
|
+
pull(): Promise<number>;
|
|
614
|
+
/** board()'s sync entries: what its sync imports through, and what discovery lists. */
|
|
615
|
+
sync: readonly SyncEntry[];
|
|
616
|
+
/**
|
|
617
|
+
* Reads a platform channel or thread with sync entry `key`'s app, which only board() has: what
|
|
618
|
+
* `readSource` answers. 404 `not_synced_here` for an entry it doesn't run.
|
|
619
|
+
*/
|
|
620
|
+
readSource(key: string, target: string, limit: number): Promise<SourceRead>;
|
|
621
|
+
/**
|
|
622
|
+
* The message a platform link names, as each platform this board syncs reads it: the platform,
|
|
623
|
+
* and its own thread and message IDs, the message null for a link to a thread. Any entry of that
|
|
624
|
+
* platform may have synced it, so a lookup tries each. Empty when no entry here reads it. It asks
|
|
625
|
+
* no platform anything.
|
|
626
|
+
*/
|
|
627
|
+
locate(url: string): {
|
|
628
|
+
platform: string;
|
|
629
|
+
thread: string;
|
|
630
|
+
message: string | null;
|
|
631
|
+
}[];
|
|
632
|
+
/** The platform's link to a copy sync recorded, by entry `key` here: null when none runs it, or its platform has none. */
|
|
633
|
+
linkOf(key: string, thread: string, message: string): Promise<string | null>;
|
|
634
|
+
/**
|
|
635
|
+
* Shows typing in the platform threads a board thread (`messages`, its root first) is synced to,
|
|
636
|
+
* through board()'s sync: what `typing` answers. Answers the threads that took it.
|
|
637
|
+
*/
|
|
638
|
+
typing(messages: readonly MessageView[], status: string | undefined): Promise<string[]>;
|
|
639
|
+
/**
|
|
640
|
+
* What a principal the backend stored may do now, under board()'s `access`: a schedule's creator
|
|
641
|
+
* at each send, a registrant at each event and pull. Null once it may do nothing.
|
|
642
|
+
*/
|
|
643
|
+
accessNow(principal: Known): Promise<Access | null>;
|
|
644
|
+
/** The board's own Vercel project and team, as board() knows them: what discovery names, for clients to mint tokens with. */
|
|
645
|
+
project(): Promise<{
|
|
646
|
+
id: string | null;
|
|
647
|
+
teamId: string | null;
|
|
648
|
+
}>;
|
|
649
|
+
/**
|
|
650
|
+
* A handle through board(), as `caller`: what a backend writes for someone outside a request
|
|
651
|
+
* goes through it, so board()'s middleware sees it, a schedule's send as its creator, say. A
|
|
652
|
+
* verified caller's posts keep the IDs they're given.
|
|
653
|
+
*/
|
|
654
|
+
handle(caller: Caller): Board;
|
|
655
|
+
/** The board's own URL, where a board elsewhere can wake it, once it's known. */
|
|
656
|
+
url(): string | null;
|
|
657
|
+
};
|
|
658
|
+
/**
|
|
659
|
+
* Where a board lives. board() serves the protocol over HTTP: it routes, validates, verifies each
|
|
660
|
+
* caller, applies `access`, runs the middleware, watchers, and sync, and calls these with the caller
|
|
661
|
+
* it verified. A backend stores and answers, honoring what each caller may do.
|
|
662
|
+
*/
|
|
663
|
+
interface Backend extends ProtocolMethods {
|
|
664
|
+
/** Routes of its own, beside the protocol: one at a protocol route's path takes its place. */
|
|
665
|
+
readonly routes: readonly BackendRoute[];
|
|
666
|
+
/**
|
|
667
|
+
* Whether the board lives here. One that keeps it (`a2()`) lets in, when board()'s `access` is left
|
|
668
|
+
* out, its people and the board's own deployments; one that uses another board (`http()`) serves no
|
|
669
|
+
* requests then but wake-ups and webhooks, and with an `access`, serves its callers as that board's
|
|
670
|
+
* project.
|
|
671
|
+
*/
|
|
672
|
+
readonly keeps: boolean;
|
|
673
|
+
/**
|
|
674
|
+
* board() calls it once, when it's created, before any request: what background work needs
|
|
675
|
+
* from the board (its subscriptions, its sync, its policy, a handle through it). It does no I/O.
|
|
676
|
+
*/
|
|
677
|
+
bind(board: BoardBinding): void;
|
|
678
|
+
}
|
|
679
|
+
//#endregion
|
|
680
|
+
export { Protocol as C, Method as S, MiddlewareContext as _, Identity as a, WriteSource as b, SelfCaller as c, Board as d, BoardOptions as f, Middleware as g, DroppedError as h, Caller as i, Subscription as l, WatcherContext as m, BackendRoute as n, InboxDelivery as o, Watcher as p, BoardBinding as r, ProtocolMethods as s, Backend as t, VerifiedCaller as u, Write as v, HttpError as x, WritePatch as y };
|