@vercel/board 0.0.0-stage → 0.15.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 +179 -2
- package/dist/a2-5EG8FkZ8.d.mts +3359 -0
- package/dist/a2-DYGYankn.mjs +3045 -0
- package/dist/access-CJU2-vaa.mjs +226 -0
- package/dist/access-CV96SYgr.d.mts +32 -0
- package/dist/access.d.mts +29 -0
- package/dist/access.mjs +4 -0
- package/dist/actors-rdN4dDTU.mjs +223 -0
- package/dist/attempts-BQUp9MKO.mjs +919 -0
- package/dist/auth-COzc09sn.mjs +276 -0
- package/dist/auth-wHFcAte3.d.mts +203 -0
- package/dist/backend-a2/blob-vercel.d.mts +8 -0
- package/dist/backend-a2/blob-vercel.mjs +81 -0
- package/dist/backend-a2/contract.d.mts +18 -0
- package/dist/backend-a2/contract.mjs +5 -0
- package/dist/backend-a2/scheduler-local.d.mts +11 -0
- package/dist/backend-a2/scheduler-local.mjs +2 -0
- package/dist/backend-a2/scheduler-qstash.d.mts +2 -0
- package/dist/backend-a2/scheduler-qstash.mjs +2 -0
- package/dist/backend-a2/scheduler-vercel.d.mts +2 -0
- package/dist/backend-a2/scheduler-vercel.mjs +2 -0
- package/dist/backend-a2/store-memory.d.mts +11 -0
- package/dist/backend-a2/store-memory.mjs +19 -0
- package/dist/backend-a2/store-postgres.d.mts +38 -0
- package/dist/backend-a2/store-postgres.mjs +110 -0
- package/dist/backend-a2/store-redis-http.d.mts +17 -0
- package/dist/backend-a2/store-redis-http.mjs +32 -0
- package/dist/backend-a2/store-redis.d.mts +23 -0
- package/dist/backend-a2/store-redis.mjs +39 -0
- package/dist/backend-a2/store-sqlite.d.mts +12 -0
- package/dist/backend-a2/store-sqlite.mjs +31 -0
- package/dist/backend-a2.d.mts +4 -0
- package/dist/backend-a2.mjs +2 -0
- package/dist/backend-http-8ex5g7Wv.d.mts +630 -0
- package/dist/backend-http-BsDSgmeo.mjs +613 -0
- package/dist/backend-http-Cv6xbEy9.mjs +469 -0
- package/dist/backend-http.d.mts +10 -0
- package/dist/backend-http.mjs +2 -0
- package/dist/backend-http.node.d.mts +10 -0
- package/dist/backend-http.node.mjs +2 -0
- package/dist/blob-C0J3wbGv.d.mts +39 -0
- package/dist/bots-CGkO76qH.mjs +67 -0
- package/dist/chat-provider-XCnQlxq6.d.mts +140 -0
- package/dist/chat-provider-jFSeyNmB.mjs +389 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +2252 -0
- package/dist/client-DGDPaN7U.mjs +380 -0
- package/dist/conformance-DRR23YNy.mjs +512 -0
- package/dist/conformance.d.mts +30 -0
- package/dist/conformance.mjs +2 -0
- package/dist/contract-CuJmlrhw.mjs +566 -0
- package/dist/emoji-CTLpvH-A.mjs +445 -0
- package/dist/engine-3EvQ8idk.mjs +1449 -0
- package/dist/errors-DX3hpwWt.mjs +64 -0
- package/dist/exports-BpgyEdh6.d.mts +4 -0
- package/dist/exports-CyY3_FjZ.mjs +1285 -0
- package/dist/files-SniSmeEr.mjs +47 -0
- package/dist/index.d.mts +13 -0
- package/dist/index.default-BZ3lItjH.mjs +12 -0
- package/dist/index.mjs +19 -0
- package/dist/index.node.d.mts +14 -0
- package/dist/index.node.mjs +5 -0
- package/dist/inspect/app.js +416 -0
- package/dist/inspect/engine.js +5 -0
- package/dist/local-Cr_dSjvs.mjs +44 -0
- package/dist/own-OwgPWdmh.mjs +15 -0
- package/dist/project-CFiArraX.mjs +124 -0
- package/dist/provider-BGjz-mDa.d.mts +2473 -0
- package/dist/provider-CuewcDNZ.mjs +205 -0
- package/dist/redis-tables-B35vv4CZ.mjs +842 -0
- package/dist/reducers-B_vUGQRp.d.mts +1172 -0
- package/dist/reducers-mUPFuFJV.mjs +293 -0
- package/dist/schemas-Be08nyfW.mjs +74 -0
- package/dist/schemas-CWxs3ECJ.d.mts +50 -0
- package/dist/serve-Ccbb2LSb.mjs +170 -0
- package/dist/server-Cxey38B-.mjs +17 -0
- package/dist/serving-BNbzc96L.mjs +2 -0
- package/dist/sql-DbiEnqM0.mjs +571 -0
- package/dist/sqlite-db-Cbj03vmp.mjs +70 -0
- package/dist/sync-github.d.mts +473 -0
- package/dist/sync-github.mjs +603 -0
- package/dist/sync-kapso.d.mts +175 -0
- package/dist/sync-kapso.mjs +86 -0
- package/dist/sync-slack.d.mts +364 -0
- package/dist/sync-slack.mjs +728 -0
- package/dist/sync.chat.d.mts +2 -0
- package/dist/sync.chat.mjs +2 -0
- package/dist/sync.d.mts +3 -0
- package/dist/sync.mjs +4 -0
- package/dist/targets-ojFEXEyN.mjs +94 -0
- package/dist/tokens-DKLn5P9B.mjs +256 -0
- package/dist/types-BHCjTZdB.d.mts +104 -0
- package/dist/users-Dls7_ANV.mjs +42 -0
- package/dist/watch-spec-W0c7ypHJ.mjs +787 -0
- package/dist/watchers-hash-D9hbHLKB.mjs +225 -0
- package/docs/01-board/01-mounting.md +506 -0
- package/docs/01-board/02-database.md +258 -0
- package/docs/01-board/03-identity.md +262 -0
- package/docs/01-board/04-messages.md +192 -0
- package/docs/01-board/05-reading.md +177 -0
- package/docs/01-board/06-watchers.md +249 -0
- package/docs/01-board/07-schedules.md +133 -0
- package/docs/01-board/08-middleware.md +139 -0
- package/docs/01-board/09-access.md +499 -0
- package/docs/01-quickstart.md +196 -0
- package/docs/02-sync/01-overview.md +290 -0
- package/docs/02-sync/02-callbacks.md +265 -0
- package/docs/02-sync/03-slack.md +248 -0
- package/docs/02-sync/04-github.md +201 -0
- package/docs/02-sync/05-whatsapp.md +148 -0
- package/docs/02-sync/06-providers.md +228 -0
- package/docs/03-guides/01-coding-agents.md +226 -0
- package/docs/03-guides/02-inspector.md +104 -0
- package/docs/03-guides/03-ci.md +251 -0
- package/docs/03-guides/04-triage.md +233 -0
- package/docs/03-guides/05-agent-factory.md +142 -0
- package/docs/04-reference/01-cli.md +606 -0
- package/docs/04-reference/02-board.md +579 -0
- package/docs/04-reference/03-http.md +489 -0
- package/docs/04-reference/04-log.md +250 -0
- package/docs/04-reference/05-conformance.md +97 -0
- package/docs/index.md +186 -0
- package/package.json +165 -3
- package/sql/postgres/board.sql +145 -0
- package/sql/postgres/notifications.sql +24 -0
- package/sql/postgres/schema.sql +451 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { t as ATTACHMENT_BYTES_MAX } from "./files-SniSmeEr.mjs";
|
|
2
|
+
import "./schemas-Be08nyfW.mjs";
|
|
3
|
+
import { V as routes } from "./watch-spec-W0c7ypHJ.mjs";
|
|
4
|
+
import { r as badRequest, t as HttpError } from "./errors-DX3hpwWt.mjs";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
//#region src/server/backend.ts
|
|
7
|
+
/** The subscription sync's outbound runs on, in `BoardBinding.subscriptions`. No watcher's ID starts with `@`. */
|
|
8
|
+
const SYNC = "@sync";
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/version.ts
|
|
11
|
+
const VERSION = "0.15.0";
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/server/routing.ts
|
|
14
|
+
/** board()'s own routes: the files it serves, and sync's, which run where board() does. */
|
|
15
|
+
const BOARD_ROUTES = [
|
|
16
|
+
"avatar",
|
|
17
|
+
"openapi",
|
|
18
|
+
"webhook",
|
|
19
|
+
"signIn",
|
|
20
|
+
"signedIn",
|
|
21
|
+
"identityUnlink",
|
|
22
|
+
"sync"
|
|
23
|
+
];
|
|
24
|
+
/** Every protocol method, by the route it's served at. */
|
|
25
|
+
const METHODS = [
|
|
26
|
+
"discovery",
|
|
27
|
+
"me",
|
|
28
|
+
"channels",
|
|
29
|
+
"actors",
|
|
30
|
+
"identity",
|
|
31
|
+
"messages",
|
|
32
|
+
"threads",
|
|
33
|
+
"message",
|
|
34
|
+
"post",
|
|
35
|
+
"posted",
|
|
36
|
+
"edit",
|
|
37
|
+
"delete",
|
|
38
|
+
"react",
|
|
39
|
+
"label",
|
|
40
|
+
"schedule",
|
|
41
|
+
"schedules",
|
|
42
|
+
"cancelSchedule",
|
|
43
|
+
"upload",
|
|
44
|
+
"attachment",
|
|
45
|
+
"uploadStart",
|
|
46
|
+
"uploadFinish",
|
|
47
|
+
"watchers",
|
|
48
|
+
"register",
|
|
49
|
+
"matches",
|
|
50
|
+
"ack",
|
|
51
|
+
"events",
|
|
52
|
+
"syncImport",
|
|
53
|
+
"syncReceipt",
|
|
54
|
+
"syncThread",
|
|
55
|
+
"syncFind",
|
|
56
|
+
"syncCopies",
|
|
57
|
+
"syncRoots",
|
|
58
|
+
"inboxPush",
|
|
59
|
+
"inboxNext",
|
|
60
|
+
"inboxAck",
|
|
61
|
+
"inbox",
|
|
62
|
+
"syncCode",
|
|
63
|
+
"syncAccount",
|
|
64
|
+
"syncUnlink",
|
|
65
|
+
"typing",
|
|
66
|
+
"readSource"
|
|
67
|
+
];
|
|
68
|
+
const escape = (literal) => literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
69
|
+
function compile(method, path, target, own) {
|
|
70
|
+
const parts = path.split("/").filter(Boolean);
|
|
71
|
+
const source = parts.map((part) => part.startsWith(":") ? "/([^/]+)" : `/${escape(part)}`).join("");
|
|
72
|
+
return {
|
|
73
|
+
method,
|
|
74
|
+
pattern: new RegExp(`${source}$`),
|
|
75
|
+
names: parts.filter((part) => part.startsWith(":")).map((part) => part.slice(1)),
|
|
76
|
+
target,
|
|
77
|
+
segments: parts.length,
|
|
78
|
+
literals: parts.filter((part) => !part.startsWith(":")).length,
|
|
79
|
+
own
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The routes a board answers, most specific first: each is matched as a suffix of the request's
|
|
84
|
+
* path, so `/api/board/messages/12` is `GET /messages/:id` under `/api/board`. A backend's own
|
|
85
|
+
* route at a protocol path takes its place. Discovery is the mount itself.
|
|
86
|
+
*/
|
|
87
|
+
function createRouter(backendRoutes, basePath) {
|
|
88
|
+
const patterns = [
|
|
89
|
+
...BOARD_ROUTES.map((name) => compile(routes[name].method, routes[name].path, {
|
|
90
|
+
kind: "board",
|
|
91
|
+
name
|
|
92
|
+
}, false)),
|
|
93
|
+
...METHODS.filter((name) => name !== "discovery").map((name) => compile(routes[name].method, routes[name].path, {
|
|
94
|
+
kind: "protocol",
|
|
95
|
+
name
|
|
96
|
+
}, false)),
|
|
97
|
+
...backendRoutes.map((route) => compile(route.method, route.path, {
|
|
98
|
+
kind: "backend",
|
|
99
|
+
route
|
|
100
|
+
}, true))
|
|
101
|
+
].sort((a, b) => b.segments - a.segments || b.literals - a.literals || Number(b.own) - Number(a.own));
|
|
102
|
+
/** A segment that isn't URI-encoded names nothing: no route. */
|
|
103
|
+
const found = (candidate, base, values) => {
|
|
104
|
+
const params = {};
|
|
105
|
+
try {
|
|
106
|
+
candidate.names.forEach((name, i) => params[name] = decodeURIComponent(values[i]));
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const first = candidate.names[0];
|
|
111
|
+
return {
|
|
112
|
+
target: candidate.target,
|
|
113
|
+
base,
|
|
114
|
+
param: first === void 0 ? null : params[first],
|
|
115
|
+
params
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
const discovery = (base) => ({
|
|
119
|
+
target: {
|
|
120
|
+
kind: "protocol",
|
|
121
|
+
name: "discovery"
|
|
122
|
+
},
|
|
123
|
+
base,
|
|
124
|
+
param: null,
|
|
125
|
+
params: {},
|
|
126
|
+
root: true
|
|
127
|
+
});
|
|
128
|
+
return function route(method, pathname) {
|
|
129
|
+
const path = pathname.length > 1 ? pathname.replace(/\/+$/, "") : pathname;
|
|
130
|
+
if (basePath !== void 0) {
|
|
131
|
+
const base = basePath.replace(/\/+$/, "");
|
|
132
|
+
if (path !== base && !path.startsWith(`${base}/`)) return null;
|
|
133
|
+
const rest = path.slice(base.length) || "/";
|
|
134
|
+
if (rest === "/") return method === "GET" ? discovery(base) : null;
|
|
135
|
+
for (const candidate of patterns) {
|
|
136
|
+
const hit = candidate.method === method ? rest.match(new RegExp(`^${candidate.pattern.source}`)) : null;
|
|
137
|
+
if (hit) return found(candidate, base, hit.slice(1));
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
for (const candidate of patterns) {
|
|
142
|
+
if (candidate.method !== method) continue;
|
|
143
|
+
const hit = path.match(candidate.pattern);
|
|
144
|
+
if (hit) return found(candidate, path.slice(0, hit.index), hit.slice(1));
|
|
145
|
+
}
|
|
146
|
+
return method === "GET" ? discovery(path === "/" ? "" : path) : null;
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function queryObject(url) {
|
|
150
|
+
const out = {};
|
|
151
|
+
for (const key of new Set(url.searchParams.keys())) {
|
|
152
|
+
const values = url.searchParams.getAll(key);
|
|
153
|
+
out[key] = values.length > 1 ? values : values[0];
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
async function readJson(request) {
|
|
158
|
+
const text = await request.text();
|
|
159
|
+
if (text.trim() === "") return void 0;
|
|
160
|
+
try {
|
|
161
|
+
return JSON.parse(text);
|
|
162
|
+
} catch {
|
|
163
|
+
throw badRequest("request body is not valid JSON");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const tooLarge = () => new HttpError(413, "too_large", `attachments are at most ${ATTACHMENT_BYTES_MAX / 1024 / 1024} MB`);
|
|
167
|
+
/** The body, refused as soon as it's past `max`: with or without a `content-length`, it never reads more. */
|
|
168
|
+
async function readCapped(request, max) {
|
|
169
|
+
if (Number(request.headers.get("content-length") ?? 0) > max) throw tooLarge();
|
|
170
|
+
const chunks = [];
|
|
171
|
+
let size = 0;
|
|
172
|
+
for await (const chunk of request.body ?? []) {
|
|
173
|
+
size += chunk.byteLength;
|
|
174
|
+
if (size > max) throw tooLarge();
|
|
175
|
+
chunks.push(chunk);
|
|
176
|
+
}
|
|
177
|
+
const body = new Uint8Array(size);
|
|
178
|
+
let at = 0;
|
|
179
|
+
for (const chunk of chunks) {
|
|
180
|
+
body.set(chunk, at);
|
|
181
|
+
at += chunk.byteLength;
|
|
182
|
+
}
|
|
183
|
+
return body;
|
|
184
|
+
}
|
|
185
|
+
function openapi() {
|
|
186
|
+
const paths = {};
|
|
187
|
+
for (const [name, definition] of Object.entries(routes)) {
|
|
188
|
+
const path = definition.path.replace(/:(\w+)/g, "{$1}");
|
|
189
|
+
const operation = { operationId: name };
|
|
190
|
+
if ("query" in definition) operation["x-query-schema"] = z.toJSONSchema(definition.query, { io: "input" });
|
|
191
|
+
if ("body" in definition) operation.requestBody = { content: { "application/json": { schema: z.toJSONSchema(definition.body, { io: "input" }) } } };
|
|
192
|
+
(paths[path] ??= {})[definition.method.toLowerCase()] = operation;
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
openapi: "3.1.0",
|
|
196
|
+
info: {
|
|
197
|
+
title: "board",
|
|
198
|
+
version: VERSION
|
|
199
|
+
},
|
|
200
|
+
paths
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/server/watchers-hash.ts
|
|
205
|
+
/** By UTF-16 code unit, as `sort()` orders strings: the same in every locale and runtime. */
|
|
206
|
+
const byCodeUnit = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
207
|
+
/**
|
|
208
|
+
* What `watchersHash` hashes, the same on both ends: a board on `http()` hashes its watchers, and
|
|
209
|
+
* whether its sync subscribes, and the board it uses hashes what it holds.
|
|
210
|
+
*/
|
|
211
|
+
function canonicalWatchers(watch, sync = []) {
|
|
212
|
+
const canonical = Object.keys(watch).sort(byCodeUnit).map((id) => [
|
|
213
|
+
id,
|
|
214
|
+
watch[id].tail,
|
|
215
|
+
watch[id].when
|
|
216
|
+
]);
|
|
217
|
+
if (sync.length > 0) canonical.push(["sync", [...sync].sort((a, b) => byCodeUnit(a.key, b.key)).map((entry) => [
|
|
218
|
+
entry.key,
|
|
219
|
+
entry.platform,
|
|
220
|
+
entry.signIn
|
|
221
|
+
])]);
|
|
222
|
+
return JSON.stringify(canonical);
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
export { queryObject as a, VERSION as c, openapi as i, SYNC as l, METHODS as n, readCapped as o, createRouter as r, readJson as s, canonicalWatchers as t };
|