@vercel/board 0.0.0 → 0.16.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.
Files changed (126) hide show
  1. package/README.md +180 -0
  2. package/dist/a2-C41hD8SF.mjs +3045 -0
  3. package/dist/a2-D24cvwpN.d.mts +3359 -0
  4. package/dist/access-CJU2-vaa.mjs +226 -0
  5. package/dist/access-CV96SYgr.d.mts +32 -0
  6. package/dist/access.d.mts +30 -0
  7. package/dist/access.mjs +4 -0
  8. package/dist/actors-DjMeZpnU.mjs +224 -0
  9. package/dist/attempts-Bxp9KgPf.mjs +919 -0
  10. package/dist/auth-COzc09sn.mjs +276 -0
  11. package/dist/auth-CU56Q2Xh.d.mts +204 -0
  12. package/dist/backend-a2/blob-vercel.d.mts +8 -0
  13. package/dist/backend-a2/blob-vercel.mjs +81 -0
  14. package/dist/backend-a2/contract.d.mts +18 -0
  15. package/dist/backend-a2/contract.mjs +5 -0
  16. package/dist/backend-a2/scheduler-local.d.mts +11 -0
  17. package/dist/backend-a2/scheduler-local.mjs +2 -0
  18. package/dist/backend-a2/scheduler-qstash.d.mts +2 -0
  19. package/dist/backend-a2/scheduler-qstash.mjs +2 -0
  20. package/dist/backend-a2/scheduler-vercel.d.mts +2 -0
  21. package/dist/backend-a2/scheduler-vercel.mjs +2 -0
  22. package/dist/backend-a2/store-memory.d.mts +11 -0
  23. package/dist/backend-a2/store-memory.mjs +19 -0
  24. package/dist/backend-a2/store-postgres.d.mts +38 -0
  25. package/dist/backend-a2/store-postgres.mjs +110 -0
  26. package/dist/backend-a2/store-redis-http.d.mts +17 -0
  27. package/dist/backend-a2/store-redis-http.mjs +32 -0
  28. package/dist/backend-a2/store-redis.d.mts +23 -0
  29. package/dist/backend-a2/store-redis.mjs +39 -0
  30. package/dist/backend-a2/store-sqlite.d.mts +12 -0
  31. package/dist/backend-a2/store-sqlite.mjs +31 -0
  32. package/dist/backend-a2.d.mts +4 -0
  33. package/dist/backend-a2.mjs +2 -0
  34. package/dist/backend-http-0roEmjl0.mjs +613 -0
  35. package/dist/backend-http-BVRno-ca.mjs +469 -0
  36. package/dist/backend-http-Bid7RIpC.d.mts +630 -0
  37. package/dist/backend-http.d.mts +10 -0
  38. package/dist/backend-http.mjs +2 -0
  39. package/dist/backend-http.node.d.mts +10 -0
  40. package/dist/backend-http.node.mjs +2 -0
  41. package/dist/blob-C0J3wbGv.d.mts +39 -0
  42. package/dist/bots-CGkO76qH.mjs +67 -0
  43. package/dist/chat-provider-XCnQlxq6.d.mts +140 -0
  44. package/dist/chat-provider-jFSeyNmB.mjs +389 -0
  45. package/dist/cli.d.mts +1 -0
  46. package/dist/cli.mjs +2252 -0
  47. package/dist/client-DGDPaN7U.mjs +380 -0
  48. package/dist/conformance-DRR23YNy.mjs +512 -0
  49. package/dist/conformance.d.mts +30 -0
  50. package/dist/conformance.mjs +2 -0
  51. package/dist/contract-CuJmlrhw.mjs +566 -0
  52. package/dist/emoji-CTLpvH-A.mjs +445 -0
  53. package/dist/engine-Bir5s8ts.mjs +1449 -0
  54. package/dist/errors-DX3hpwWt.mjs +64 -0
  55. package/dist/exports-DLiE7fLY.d.mts +4 -0
  56. package/dist/exports-Dvc1o52I.mjs +1285 -0
  57. package/dist/files-SniSmeEr.mjs +47 -0
  58. package/dist/index.d.mts +13 -0
  59. package/dist/index.default-Bkg77Pxn.mjs +12 -0
  60. package/dist/index.mjs +19 -0
  61. package/dist/index.node.d.mts +14 -0
  62. package/dist/index.node.mjs +5 -0
  63. package/dist/inspect/app.js +416 -0
  64. package/dist/inspect/engine.js +5 -0
  65. package/dist/local-Cr_dSjvs.mjs +44 -0
  66. package/dist/own-OwgPWdmh.mjs +15 -0
  67. package/dist/project-CFiArraX.mjs +124 -0
  68. package/dist/provider-BGjz-mDa.d.mts +2473 -0
  69. package/dist/provider-CuewcDNZ.mjs +205 -0
  70. package/dist/redis-tables-B35vv4CZ.mjs +842 -0
  71. package/dist/reducers-B_vUGQRp.d.mts +1172 -0
  72. package/dist/reducers-mUPFuFJV.mjs +293 -0
  73. package/dist/schemas-Be08nyfW.mjs +74 -0
  74. package/dist/schemas-CWxs3ECJ.d.mts +50 -0
  75. package/dist/serve-BsOOc-6_.mjs +170 -0
  76. package/dist/server-jwPY7_wi.mjs +17 -0
  77. package/dist/serving-BNbzc96L.mjs +2 -0
  78. package/dist/sql-DbiEnqM0.mjs +571 -0
  79. package/dist/sqlite-db-Cbj03vmp.mjs +70 -0
  80. package/dist/sync-github.d.mts +473 -0
  81. package/dist/sync-github.mjs +603 -0
  82. package/dist/sync-kapso.d.mts +175 -0
  83. package/dist/sync-kapso.mjs +86 -0
  84. package/dist/sync-slack.d.mts +364 -0
  85. package/dist/sync-slack.mjs +728 -0
  86. package/dist/sync.chat.d.mts +2 -0
  87. package/dist/sync.chat.mjs +2 -0
  88. package/dist/sync.d.mts +3 -0
  89. package/dist/sync.mjs +4 -0
  90. package/dist/targets-yM5JBUgr.mjs +94 -0
  91. package/dist/tokens-DKLn5P9B.mjs +256 -0
  92. package/dist/types-BHCjTZdB.d.mts +104 -0
  93. package/dist/users-Dls7_ANV.mjs +42 -0
  94. package/dist/watch-spec-W0c7ypHJ.mjs +787 -0
  95. package/dist/watchers-hash-CLjsa6od.mjs +225 -0
  96. package/docs/01-board/01-mounting.md +506 -0
  97. package/docs/01-board/02-database.md +258 -0
  98. package/docs/01-board/03-identity.md +264 -0
  99. package/docs/01-board/04-messages.md +192 -0
  100. package/docs/01-board/05-reading.md +177 -0
  101. package/docs/01-board/06-watchers.md +249 -0
  102. package/docs/01-board/07-schedules.md +133 -0
  103. package/docs/01-board/08-middleware.md +139 -0
  104. package/docs/01-board/09-access.md +501 -0
  105. package/docs/01-quickstart.md +196 -0
  106. package/docs/02-sync/01-overview.md +290 -0
  107. package/docs/02-sync/02-callbacks.md +265 -0
  108. package/docs/02-sync/03-slack.md +248 -0
  109. package/docs/02-sync/04-github.md +201 -0
  110. package/docs/02-sync/05-whatsapp.md +148 -0
  111. package/docs/02-sync/06-providers.md +228 -0
  112. package/docs/03-guides/01-coding-agents.md +226 -0
  113. package/docs/03-guides/02-inspector.md +104 -0
  114. package/docs/03-guides/03-ci.md +251 -0
  115. package/docs/03-guides/04-triage.md +233 -0
  116. package/docs/03-guides/05-agent-factory.md +142 -0
  117. package/docs/04-reference/01-cli.md +606 -0
  118. package/docs/04-reference/02-board.md +579 -0
  119. package/docs/04-reference/03-http.md +489 -0
  120. package/docs/04-reference/04-log.md +250 -0
  121. package/docs/04-reference/05-conformance.md +97 -0
  122. package/docs/index.md +186 -0
  123. package/package.json +168 -1
  124. package/sql/postgres/board.sql +145 -0
  125. package/sql/postgres/notifications.sql +24 -0
  126. 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.16.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 };