@vercel/board 0.30.0 → 0.31.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 (125) hide show
  1. package/README.md +8 -17
  2. package/dist/{a2-D_28Pdip.mjs → a2-DngWY2Eb.mjs} +573 -361
  3. package/dist/{access-CQ39gqEV.mjs → access-CY5xVMIK.mjs} +19 -3
  4. package/dist/{access-CV96SYgr.d.mts → access-DPcrKIH_.d.mts} +6 -1
  5. package/dist/access.d.mts +4 -4
  6. package/dist/access.mjs +5 -4
  7. package/dist/{actors-xPiamLWk.mjs → actors-Bm-ojWWT.mjs} +2 -2
  8. package/dist/{auth-BPinwfN0.mjs → auth-CyO2aw5n.mjs} +127 -14
  9. package/dist/{auth-CU56Q2Xh.d.mts → auth-DLYSt_-A.d.mts} +32 -4
  10. package/dist/{backend-n3hajfrD.d.mts → backend-Bob-5CM7.d.mts} +86 -41
  11. package/dist/backend-a2/blob-vercel.d.mts +1 -1
  12. package/dist/backend-a2/contract.d.mts +37 -97
  13. package/dist/backend-a2/contract.mjs +1 -1
  14. package/dist/backend-a2/store-memory.d.mts +1 -1
  15. package/dist/backend-a2/store-memory.mjs +2 -2
  16. package/dist/backend-a2/store-postgres.d.mts +1 -1
  17. package/dist/backend-a2/store-postgres.mjs +1 -1
  18. package/dist/backend-a2/store-redis-http.d.mts +1 -1
  19. package/dist/backend-a2/store-redis-http.mjs +1 -1
  20. package/dist/backend-a2/store-redis.d.mts +1 -1
  21. package/dist/backend-a2/store-redis.mjs +1 -1
  22. package/dist/backend-a2/store-sqlite.d.mts +1 -1
  23. package/dist/backend-a2/store-sqlite.mjs +2 -2
  24. package/dist/backend-a2.d.mts +131 -240
  25. package/dist/backend-a2.mjs +2 -2
  26. package/dist/{backend-http-3PoBYQoo.d.mts → backend-http-BRrRbjRx.d.mts} +49 -45
  27. package/dist/{backend-http-BPaNYLWj.mjs → backend-http-CvUKOn-m.mjs} +11 -9
  28. package/dist/{backend-http-16SbvnAK.mjs → backend-http-D8mUzuKQ.mjs} +2 -2
  29. package/dist/backend-http.d.mts +1 -1
  30. package/dist/backend-http.mjs +1 -1
  31. package/dist/backend-http.node.d.mts +1 -1
  32. package/dist/backend-http.node.mjs +1 -1
  33. package/dist/{backend-vercel-Kd8nvW5A.mjs → backend-vercel-Ba8JDtqW.mjs} +3 -3
  34. package/dist/{backend-vercel-qznuDxnN.d.mts → backend-vercel-BaSEUdwg.d.mts} +49 -45
  35. package/dist/{backend-vercel-C4Kee2-K.mjs → backend-vercel-CmY9yZ9m.mjs} +80 -15
  36. package/dist/backend-vercel.d.mts +1 -1
  37. package/dist/backend-vercel.mjs +1 -1
  38. package/dist/backend-vercel.node.d.mts +1 -1
  39. package/dist/backend-vercel.node.mjs +1 -1
  40. package/dist/{blob-CqILvdmr.d.mts → blob-Bx8QPWEd.d.mts} +2 -2
  41. package/dist/{blob-DQK5prXT.mjs → blob-WbB2DJKn.mjs} +30 -11
  42. package/dist/{chat-provider-D6jVT4wS.d.mts → chat-provider-BChYrrA7.d.mts} +32 -28
  43. package/dist/{chat-provider-COtsU5jB.mjs → chat-provider-DkFp2hrh.mjs} +105 -60
  44. package/dist/cli-commands.d.mts +1 -1
  45. package/dist/cli-commands.mjs +1 -1
  46. package/dist/cli-embedded.d.mts +2 -2
  47. package/dist/cli-embedded.mjs +1 -1
  48. package/dist/cli.mjs +4 -4
  49. package/dist/{client-CjAatpV4.d.mts → client-COfYrgfV.d.mts} +184 -221
  50. package/dist/{commands-BtH69yqt.d.mts → commands-DK4W3HCJ.d.mts} +30 -10
  51. package/dist/{commands-B5DHjZKK.mjs → commands-L1JSGz2Z.mjs} +162 -45
  52. package/dist/{conformance-C7fwIvmN.mjs → conformance-DK43HiHf.mjs} +224 -20
  53. package/dist/conformance.d.mts +8 -2
  54. package/dist/conformance.mjs +1 -1
  55. package/dist/{engine-DkdkXQEy.mjs → engine-DurYIRt8.mjs} +29 -210
  56. package/dist/{errors-87TVtXeb.d.mts → errors-2vGs49Db.d.mts} +1 -1
  57. package/dist/{errors-C_5rB8Iz.mjs → errors-CJXhhzvT.mjs} +28 -23
  58. package/dist/{provider-BaVmi4Hi.mjs → events-lujPA3gQ.mjs} +80 -17
  59. package/dist/{exports-CG0P8Qi4.mjs → exports-CWIdN0Ub.mjs} +146 -80
  60. package/dist/exports-FBOQWT7O.d.mts +5 -0
  61. package/dist/{folds-DGYkMW19.mjs → folds-Cu9Q9zyQ.mjs} +2 -22
  62. package/dist/{folds-8wRRXjAt.d.mts → folds-DLUzfibJ.d.mts} +13 -40
  63. package/dist/{index-BygAXP7t.d.mts → index-Ho-ayyQL.d.mts} +2 -2
  64. package/dist/index.d.mts +7 -7
  65. package/dist/{index.default-DhkgpfR2.mjs → index.default-BUcwDaAe.mjs} +1 -1
  66. package/dist/index.mjs +5 -5
  67. package/dist/index.node.d.mts +7 -7
  68. package/dist/index.node.mjs +3 -3
  69. package/dist/inspect/app.js +62 -61
  70. package/dist/inspect/engine.js +3 -3
  71. package/dist/{main-D2s-QBOK.mjs → main-Dz-8komG.mjs} +189 -112
  72. package/dist/{middleware-DlJQYiwV.mjs → middleware-BavYSDUa.mjs} +1 -1
  73. package/dist/{node-D8Rtg1su.mjs → node-BgeGcwX0.mjs} +7 -3
  74. package/dist/{project-C_2xs57L.mjs → project-CQ4VUibA.mjs} +1 -1
  75. package/dist/{provider-Bm46LAdp.d.mts → provider-gynDUoyW.d.mts} +13 -43
  76. package/dist/{redis-tables-BWJZHUGg.mjs → redis-tables-DHf85ixI.mjs} +1 -19
  77. package/dist/{reducers-8jqLewgw.mjs → reducers-DcENwtwZ.mjs} +72 -75
  78. package/dist/{serve-lPoBDEds.mjs → serve-CTmow-YH.mjs} +6 -6
  79. package/dist/{server-D82a4uMe.mjs → server-DG8D6VV0.mjs} +4 -4
  80. package/dist/{serving-DKny38LK.mjs → serving-DBFRo6U_.mjs} +1 -1
  81. package/dist/{sql-Bj0fk6dn.mjs → sql-CT5xVuGr.mjs} +2 -8
  82. package/dist/{sqlite-db-Dc8G78XV.mjs → sqlite-db-0WeSOEZN.mjs} +1 -1
  83. package/dist/{sync-kapso.d.mts → sync-chat-sdk.d.mts} +33 -37
  84. package/dist/sync-chat-sdk.mjs +44 -0
  85. package/dist/sync-github.d.mts +5 -18
  86. package/dist/sync-github.mjs +4 -70
  87. package/dist/sync-linear.d.mts +3 -3
  88. package/dist/sync-linear.mjs +2 -3
  89. package/dist/sync-slack.d.mts +7 -7
  90. package/dist/sync-slack.mjs +33 -54
  91. package/dist/sync.d.mts +3 -3
  92. package/dist/sync.mjs +2 -4
  93. package/dist/{tokens-C5_yGRlL.mjs → tokens-Dnyd_y1n.mjs} +15 -6
  94. package/dist/{types-shZmGZ-q.d.mts → types-CmrFjpml.d.mts} +1 -3
  95. package/dist/{watchers-hash-B_5b0-ZG.mjs → watchers-hash-BjwlnGD9.mjs} +85 -105
  96. package/docs/01-board/01-mounting.md +2 -2
  97. package/docs/01-board/03-identity.md +5 -6
  98. package/docs/01-board/04-messages.md +3 -2
  99. package/docs/01-board/05-reading.md +2 -1
  100. package/docs/01-board/06-watchers.md +8 -6
  101. package/docs/01-board/08-middleware.md +1 -1
  102. package/docs/01-board/09-access.md +145 -14
  103. package/docs/02-sync/01-overview.md +19 -26
  104. package/docs/02-sync/02-functions.md +8 -9
  105. package/docs/02-sync/03-slack.md +5 -5
  106. package/docs/02-sync/04-github.md +2 -2
  107. package/docs/02-sync/06-providers.md +91 -62
  108. package/docs/03-guides/01-coding-agents.md +1 -1
  109. package/docs/03-guides/03-ci.md +7 -4
  110. package/docs/04-reference/01-cli.md +86 -41
  111. package/docs/04-reference/02-board.md +56 -23
  112. package/docs/04-reference/03-http.md +59 -32
  113. package/docs/04-reference/04-log.md +8 -11
  114. package/docs/04-reference/05-conformance.md +35 -6
  115. package/docs/index.md +2 -2
  116. package/package.json +6 -9
  117. package/sql/postgres/board.sql +1 -1
  118. package/sql/postgres/schema.sql +1 -1
  119. package/dist/events-qf5XG5VQ.mjs +0 -75
  120. package/dist/exports-CrGA5_yN.d.mts +0 -5
  121. package/dist/sync-kapso.mjs +0 -89
  122. package/dist/sync.chat.d.mts +0 -2
  123. package/dist/sync.chat.mjs +0 -2
  124. package/dist/users-yryHKFMo.mjs +0 -42
  125. package/docs/02-sync/05-whatsapp.md +0 -152
@@ -1,11 +1,12 @@
1
1
  import { t as ATTACHMENT_BYTES_MAX } from "./files-SniSmeEr.mjs";
2
2
  import "./schemas-DBmUO6pr.mjs";
3
- import { A as ScheduleBody, B as SyncImportBody, C as LinkedQuery, D as ReactBody, E as PostBody, F as SyncAccountBody, G as TypingBody, H as SyncRootsQuery, I as SyncBody, K as UploadStartBody, L as SyncCodeBody, M as SignInQuery, N as SourceJoinBody, O as Ref, P as SourceReadQuery, R as SyncCopiesQuery, S as LabelBody, T as MessagesQuery, U as SyncUnlinkBody, V as SyncReceiptBody, W as ThreadsQuery, _ as IdentityUnlinkBody, a as METHODS, b as InboxPushBody, c as queryObject, d as VERSION, f as SYNC, g as EventsQuery, h as EditBody, j as SchedulesQuery, k as RegisterBody, l as readCapped, m as DeleteBody, o as createRouter, p as AckBody, s as openapi, u as readJson, v as InboxAckBody, w as MatchesQuery, x as InboxQuery, y as InboxNextQuery, z as SyncFindQuery } from "./watchers-hash-B_5b0-ZG.mjs";
4
- import { a as forbidden, c as toErrorResponse, d as createBoardClient, l as unauthorized, n as RejectedError, o as json, r as badRequest, s as notFound, t as HttpError, u as BoardError } from "./errors-C_5rB8Iz.mjs";
3
+ import { A as RegisterBody, B as ThreadsQuery, C as LabelBody, D as PostBody, E as MessagesQuery, F as SyncBody, H as TypingBody, I as SyncCopiesQuery, L as SyncFindQuery, M as SchedulesQuery, N as SourceJoinBody, O as ReactBody, P as SourceReadQuery, R as SyncImportBody, S as InboxQuery, T as MatchesQuery, U as UploadStartBody, V as TokenBody, _ as EventsQuery, a as METHODS, b as InboxNextQuery, c as queryObject, d as VERSION, f as SYNC, g as EditBody, h as DeleteBody, j as ScheduleBody, k as Ref, l as readCapped, m as CreateChannelBody, o as createRouter, p as AckBody, s as openapi, u as readJson, v as GrantBody, w as LinkedQuery, x as InboxPushBody, y as InboxAckBody, z as SyncReceiptBody } from "./watchers-hash-BjwlnGD9.mjs";
4
+ import { a as forbidden, c as toErrorResponse, d as createBoardClient, l as unauthorized, n as RejectedError, o as json, r as badRequest, s as notFound, t as HttpError, u as BoardError } from "./errors-CJXhhzvT.mjs";
5
5
  import { r as derivedId } from "./wait-until-Dw4XrIDQ.mjs";
6
- import { i as resolveActor, r as claimsOf, t as KNOWN_AGENTS } from "./actors-xPiamLWk.mjs";
7
- import { d as issuersOf, g as verifiesTokens, l as hostPrincipal, o as createAuthority, s as fromTokens } from "./auth-BPinwfN0.mjs";
8
- import { r as runMiddleware, t as DroppedError } from "./middleware-DlJQYiwV.mjs";
6
+ import { a as scopeOf, i as parseScope, r as compileAccess } from "./access-CY5xVMIK.mjs";
7
+ import { i as resolveActor, r as claimsOf, t as KNOWN_AGENTS } from "./actors-Bm-ojWWT.mjs";
8
+ import { S as verifiesTokens, a as VERCEL_ISSUER, f as grantIssuers, h as issuersOf, l as createAuthority, p as hostPrincipal, u as fromTokens, y as tokenHash } from "./auth-CyO2aw5n.mjs";
9
+ import { r as runMiddleware, t as DroppedError } from "./middleware-BavYSDUa.mjs";
9
10
  import { n as syncTarget, r as withEmailMentions, t as mentionsEmails } from "./targets-Cgjs29T0.mjs";
10
11
  //#region src/server/policy.ts
11
12
  /**
@@ -13,10 +14,11 @@ import { n as syncTarget, r as withEmailMentions, t as mentionsEmails } from "./
13
14
  * principals its backend stores. `keeps`: the backend keeps the board, so the defaults let its
14
15
  * people and own deployments in when `access` is left out.
15
16
  */
16
- function createPolicy(options, keeps, serving) {
17
+ function createPolicy(options, keeps, serving, trust = null) {
17
18
  /** Whether requests are served at all: a board on `http()` serves none without an `access` of its own. */
18
19
  const open = keeps || options.access !== void 0;
19
20
  const access = options.access ?? [];
21
+ const grants = trust && typeof access !== "function" ? () => trust.grants() : null;
20
22
  /** The board's own Vercel project and team, from `project` or the environment, asked once. */
21
23
  let own = null;
22
24
  const project = () => (own ??= (async () => {
@@ -48,12 +50,39 @@ function createPolicy(options, keeps, serving) {
48
50
  const authority = createAuthority({
49
51
  access,
50
52
  project,
51
- hosts: options.authenticate !== void 0 && !verifiesTokens(options.authenticate)
53
+ hosts: options.authenticate !== void 0 && !verifiesTokens(options.authenticate),
54
+ grants
52
55
  });
56
+ const ruleIssuers = typeof access === "function" ? [] : issuersOf(access);
57
+ /** One for every request, so a verifier keeps what it built: its issuers change only when grants name new ones. */
53
58
  const context = {
54
59
  project,
55
- issuers: typeof access === "function" ? [] : issuersOf(access)
60
+ issuers: ruleIssuers
56
61
  };
62
+ /**
63
+ * Trusts the issuers grants name, before a token from one is verified: only for a token from an
64
+ * issuer neither Vercel, GitHub, nor a rule is, so most requests read no grants for it.
65
+ */
66
+ async function learnIssuers(credential) {
67
+ if (!grants) return;
68
+ const iss = issuerOf(credential);
69
+ if (iss === null || !iss.startsWith("https://") || iss === "https://token.actions.githubusercontent.com" || VERCEL_ISSUER.test(iss) || ruleIssuers.includes(iss)) return;
70
+ const named = grantIssuers(await grants());
71
+ const issuers = [...ruleIssuers, ...named.filter((issuer) => !ruleIssuers.includes(issuer))];
72
+ if (issuers.join("\n") !== context.issuers.join("\n")) context.issuers = issuers;
73
+ }
74
+ /** Who a board token is: the principal that minted it, narrowed, until it's revoked or expires. */
75
+ async function tokenPrincipal(secret) {
76
+ if (!trust) throw unauthorized("this board issues no tokens: send a board token to the board that minted it");
77
+ const found = await trust.token(await tokenHash(secret));
78
+ if (!found || found.expiresAt.getTime() <= Date.now()) throw unauthorized("invalid token: revoked, expired, or not this board's");
79
+ return {
80
+ ...found.minter,
81
+ expiresAt: found.expiresAt,
82
+ token: found.id,
83
+ pinned: found.pinned
84
+ };
85
+ }
57
86
  /** Verified principals by credential, until they expire, so a stream's polls don't verify from scratch. */
58
87
  const verified = /* @__PURE__ */ new Map();
59
88
  /** And by request, for one with no credential (a session cookie), for as long as it's served. */
@@ -79,7 +108,7 @@ function createPolicy(options, keeps, serving) {
79
108
  const principal = verify(request);
80
109
  perRequest.set(request, principal);
81
110
  if (key !== null) principal.then((found) => {
82
- if (found.facts?.issuer === "host" || found.expiresAt === null) return;
111
+ if (found.facts?.issuer === "host" || found.expiresAt === null || found.token !== void 0) return;
83
112
  if (verified.size >= 1e3) verified.delete(verified.keys().next().value);
84
113
  verified.set(key, {
85
114
  principal,
@@ -89,6 +118,8 @@ function createPolicy(options, keeps, serving) {
89
118
  return principal;
90
119
  }
91
120
  async function verify(request) {
121
+ if (request.credential?.startsWith("board_")) return tokenPrincipal(request.credential);
122
+ if (request.credential !== null) await learnIssuers(request.credential);
92
123
  const authenticate = await verifier();
93
124
  let result;
94
125
  try {
@@ -107,6 +138,10 @@ function createPolicy(options, keeps, serving) {
107
138
  return {
108
139
  open,
109
140
  project,
141
+ /** The OIDC issuers the rules name. */
142
+ issuers: ruleIssuers,
143
+ /** Whether grants decide anything here: not where `access` is one function, which decides alone. */
144
+ takesGrants: typeof access !== "function",
110
145
  discoveryIdentity,
111
146
  /**
112
147
  * Who a request is, and what it may do now: verified, under the rules, with the profile it
@@ -115,11 +150,13 @@ function createPolicy(options, keeps, serving) {
115
150
  */
116
151
  async identify(request) {
117
152
  const found = await principalOf(request);
118
- const principal = request.claims.scope === null ? found : {
153
+ const claims = found.pinned ? pinnedClaims(found.pinned, request.claims) : request.claims;
154
+ const scope = found.scope === void 0 ? claims.scope ?? void 0 : claims.scope === null ? found.scope : scopeOf(compileAccess(parseScope(found.scope)).narrow(parseScope(claims.scope)));
155
+ const principal = scope === void 0 ? found : {
119
156
  ...found,
120
- scope: request.claims.scope
157
+ scope
121
158
  };
122
- const { author, actor } = resolveActor(principal, request.claims, request.audience);
159
+ const { author, actor } = resolveActor(principal, claims, request.audience);
123
160
  return {
124
161
  principal: {
125
162
  ...principal,
@@ -129,10 +166,38 @@ function createPolicy(options, keeps, serving) {
129
166
  actor
130
167
  };
131
168
  },
132
- /** What a stored principal (a schedule's creator, a watcher's registrant) may do now: null once nothing. */
133
- accessNow: (principal) => authority.accessNow(principal)
169
+ /** What a stored principal (a schedule's creator, a watcher's registrant) may do now: null once nothing, its board token's revoking included. */
170
+ async accessNow(principal) {
171
+ if (principal.token !== void 0 && !await trust?.live(principal.token)) return null;
172
+ return authority.accessNow(principal);
173
+ }
134
174
  };
135
175
  }
176
+ /** A board token's pins, over what the request says: its profile and session win, and a person's name is the minter's. */
177
+ function pinnedClaims(pinned, claims) {
178
+ return {
179
+ ...claims,
180
+ ...pinned.profile !== null ? {
181
+ profile: pinned.profile,
182
+ avatar: pinned.avatar
183
+ } : {},
184
+ ...pinned.session !== null ? { session: pinned.session } : {},
185
+ handle: pinned.handle,
186
+ name: pinned.name
187
+ };
188
+ }
189
+ /** A JWT's `iss`, read but not verified: which issuer's keys would check it. Null for anything else. */
190
+ function issuerOf(credential) {
191
+ const payload = credential.split(".")[1];
192
+ if (payload === void 0) return null;
193
+ try {
194
+ const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(payload.length / 4) * 4, "="));
195
+ const { iss } = JSON.parse(json);
196
+ return typeof iss === "string" ? iss : null;
197
+ } catch {
198
+ return null;
199
+ }
200
+ }
136
201
  //#endregion
137
202
  //#region src/contract/logos.ts
138
203
  const LOGOS = {
@@ -182,6 +247,10 @@ const CREATED = /* @__PURE__ */ new Set([
182
247
  "upload",
183
248
  "uploadFinish"
184
249
  ]);
250
+ /** An email, as `--mentions` names one: sync looks its platform user up. */
251
+ const EMAIL = /^[^\s@]+@[^\s@]+$/;
252
+ /** How long a filter's email lookup holds. */
253
+ const EMAIL_TTL_MS = 3e5;
185
254
  /** Shown inline only when a browser can't run it: everything else downloads. */
186
255
  const INLINE = /^(image\/(png|jpeg|gif|webp|avif)|video\/(mp4|webm)|text\/plain)$/;
187
256
  /**
@@ -210,12 +279,15 @@ async function inputOf(name, request, url, params) {
210
279
  case "me":
211
280
  case "channels":
212
281
  case "actors":
213
- case "identity":
214
- case "watchers": return {};
282
+ case "watchers":
283
+ case "grants":
284
+ case "tokens": return {};
215
285
  case "message":
216
286
  case "posted":
217
287
  case "synced":
218
288
  case "cancelSchedule":
289
+ case "revokeGrant":
290
+ case "revokeToken":
219
291
  case "attachment":
220
292
  case "uploadFinish": return { id };
221
293
  case "messages": return MessagesQuery.parse(queryObject(url));
@@ -229,7 +301,10 @@ async function inputOf(name, request, url, params) {
229
301
  return query;
230
302
  }
231
303
  case "post": return PostBody.parse(await body());
304
+ case "createChannel": return CreateChannelBody.parse(await body());
232
305
  case "schedule": return ScheduleBody.parse(await body());
306
+ case "createGrant": return GrantBody.parse(await body());
307
+ case "createToken": return TokenBody.parse(await body());
233
308
  case "register": return RegisterBody.parse(await body());
234
309
  case "edit": return {
235
310
  id,
@@ -271,7 +346,6 @@ async function inputOf(name, request, url, params) {
271
346
  case "syncThread": return { thread: params.thread ?? "" };
272
347
  case "syncFind": return SyncFindQuery.parse(queryObject(url));
273
348
  case "syncCopies": return SyncCopiesQuery.parse(queryObject(url));
274
- case "syncRoots": return SyncRootsQuery.parse(queryObject(url));
275
349
  case "inboxPush": return {
276
350
  key,
277
351
  ...InboxPushBody.parse(await body())
@@ -289,9 +363,6 @@ async function inputOf(name, request, url, params) {
289
363
  key,
290
364
  ...InboxQuery.parse(queryObject(url))
291
365
  };
292
- case "syncCode": return SyncCodeBody.parse(await body());
293
- case "syncAccount": return SyncAccountBody.parse(await body());
294
- case "syncUnlink": return SyncUnlinkBody.parse(await body());
295
366
  case "typing": return {
296
367
  id,
297
368
  ...TypingBody.parse(await body()) ?? {}
@@ -371,17 +442,48 @@ async function createServe(context) {
371
442
  * board learns who they are; the rest stay plain text.
372
443
  */
373
444
  async function withEmails(text) {
374
- return (await withEmailMentions(text, engine ? async (email) => {
375
- const actor = await engine.findByEmail(email);
376
- if (actor) await backend.syncImport(engine.caller, {
377
- op: "seen",
378
- author: {
379
- ...actor,
380
- isBot: false
381
- }
382
- }, new AbortController().signal);
383
- return actor;
384
- } : void 0)).text;
445
+ return (await withEmailMentions(text, engine ? learnEmail : void 0)).text;
446
+ }
447
+ /**
448
+ * The synced platform's user with this email, whom the board learns: a post can mention only
449
+ * an actor it knows, and a platform mention of them resolves only once it does.
450
+ */
451
+ async function learnEmail(email) {
452
+ const actor = await engine.findByEmail(email);
453
+ if (actor) await backend.syncImport(engine.caller, {
454
+ op: "seen",
455
+ author: {
456
+ ...actor,
457
+ isBot: false
458
+ }
459
+ }, new AbortController().signal);
460
+ return actor;
461
+ }
462
+ /** Emails filters named, as sync found them, for a few minutes: `tail` and `wait` ask at every page. */
463
+ const mentioned = /* @__PURE__ */ new Map();
464
+ /**
465
+ * `--mentions ada@vercel.com`: the synced platform's user with that email, for a caller that may
466
+ * read somewhere. Anything else, and an email sync doesn't know, goes on as it came.
467
+ */
468
+ async function withMentioned(caller, query) {
469
+ const email = query.mentions?.toLowerCase();
470
+ if (!engine || email === void 0 || !EMAIL.test(email)) return query;
471
+ const access = accessOf(caller);
472
+ if (access !== null && !access.anywhere("read")) return query;
473
+ let found = mentioned.get(email);
474
+ if (!found || Date.now() - found.at > EMAIL_TTL_MS) {
475
+ if (mentioned.size >= 1e3) mentioned.clear();
476
+ found = {
477
+ at: Date.now(),
478
+ actor: learnEmail(email).catch(() => null)
479
+ };
480
+ mentioned.set(email, found);
481
+ }
482
+ const actor = await found.actor;
483
+ return actor ? {
484
+ ...query,
485
+ mentions: actor.id
486
+ } : query;
385
487
  }
386
488
  /**
387
489
  * A sync target as the board that syncs it resolves it. One for an entry this board doesn't
@@ -819,8 +921,9 @@ async function createServe(context) {
819
921
  }, signal);
820
922
  return { message: await read(message) };
821
923
  }
822
- /** The protocol method `name`, as board() serves it: what it posts and edits with the emails they mention resolved. */
924
+ /** The protocol method `name`, as board() serves it: what it posts and edits, and the filters it reads by, with their emails resolved. */
823
925
  async function invoke(name, caller, input, signal) {
926
+ if (name === "messages" || name === "threads" || name === "events") input = await withMentioned(caller, input);
824
927
  switch (name) {
825
928
  case "post": return post(caller, input, signal);
826
929
  case "edit": {
@@ -833,6 +936,11 @@ async function createServe(context) {
833
936
  });
834
937
  }
835
938
  case "syncImport": return syncImport(caller, input, signal);
939
+ case "grants":
940
+ case "createGrant":
941
+ case "revokeGrant":
942
+ if (backend.keeps && !policy.takesGrants) throw new HttpError(501, "no_grants", "this board's access is one function, which decides alone: it keeps no grants");
943
+ return backend[name].call(backend, caller, input, signal);
836
944
  case "schedule": {
837
945
  const body = input;
838
946
  const schedule = (made) => backend.sendsSchedulesHere === false ? screenSchedule(caller, made, signal) : backend.schedule(caller, made, signal);
@@ -870,35 +978,6 @@ async function createServe(context) {
870
978
  }
871
979
  }
872
980
  }
873
- /**
874
- * A sign-in that carries a code, where the entry's sync runs: a browser opens it with no token,
875
- * so the code is all it has. `signIn` sends the person to the platform as whoever the code was
876
- * issued to; `signedIn`, where the platform sends them back, links the account they approved.
877
- */
878
- async function signingIn(name, url, signal) {
879
- const { sync: key, code } = SignInQuery.parse(queryObject(url));
880
- if (!engine || !entries[key]) throw notFound(`no sync entry named ${key}`);
881
- const page = (status, text) => new Response(`${text}\n`, {
882
- status,
883
- headers: { "content-type": "text/plain; charset=utf-8" }
884
- });
885
- const { holder } = await backend.syncFind(engine.caller, { code }, signal);
886
- if (holder === null) return page(404, "That link has expired or been used. Run `vb identity link` again for a new one.");
887
- if (name === "signIn") {
888
- const back = ownUrl();
889
- const at = await engine.signIn(key, holder, back ? `${back}/identity/signed-in?${new URLSearchParams({
890
- sync: key,
891
- code
892
- })}` : void 0);
893
- return new Response(null, {
894
- status: 302,
895
- headers: { location: at }
896
- });
897
- }
898
- const linked = await engine.redeemSignIn(key, holder, code);
899
- if (!linked) return page(409, `${entries[key].platform} hasn't said you approved yet: finish signing in, then open this page again.`);
900
- return page(200, `Linked ${linked.handle ? `@${linked.handle}` : linked.actor}. You can close this tab.`);
901
- }
902
981
  /** board()'s own routes for a caller: sync's, which run where board() does. */
903
982
  async function serve(name, caller, request, url, params) {
904
983
  const signal = request.signal;
@@ -925,19 +1004,6 @@ async function createServe(context) {
925
1004
  attached: attached ?? true
926
1005
  });
927
1006
  }
928
- case "identityUnlink": {
929
- const body = IdentityUnlinkBody.parse(await readJson(request));
930
- const { links } = await backend.identity(caller, {}, signal);
931
- const wanted = body.actor.replace(/^@/, "").toLowerCase();
932
- const found = links.filter((link) => link.actor === body.actor || link.handle?.toLowerCase() === wanted).map((link) => link.actor);
933
- if (found.length === 0) throw notFound(`${body.actor} isn't linked to you`, "not_linked");
934
- if (found.length > 1) throw badRequest(`${body.actor} names several of your accounts: use ${found.join(" or ")}`, "ambiguous_actor");
935
- await backend.syncUnlink(caller, { account: found[0] }, signal);
936
- return json({
937
- ok: true,
938
- actor: found[0]
939
- });
940
- }
941
1007
  default: throw notFound(`no route: ${request.method} ${url.pathname}`);
942
1008
  }
943
1009
  }
@@ -1046,7 +1112,6 @@ async function createServe(context) {
1046
1112
  if (!engine || !entries[match.params.key ?? ""]) throw notFound(`no sync entry named ${match.params.key}`);
1047
1113
  return await engine.handleWebhook(match.params.key, request);
1048
1114
  }
1049
- if (target.kind === "board" && (target.name === "signIn" || target.name === "signedIn")) return await signingIn(target.name, url, request.signal);
1050
1115
  if (target.kind === "board" && target.name === "avatar") {
1051
1116
  const name = match.param ?? "";
1052
1117
  if (!KNOWN_AGENTS.includes(name)) throw notFound(`no avatar: ${name}`);
@@ -1068,10 +1133,11 @@ async function createServe(context) {
1068
1133
  const caller = own ?? await callerOf(asked);
1069
1134
  if (target.kind === "board") return await serve(target.name, caller, request, url, match.params);
1070
1135
  const input = await inputOf(target.name, request, url, match.params);
1071
- if (target.name === "events" && (request.headers.get("accept") ?? "").includes("text/event-stream")) return await sse(caller, input, request, asked ? () => callerOf(asked) : null);
1136
+ if (target.name === "events" && (request.headers.get("accept") ?? "").includes("text/event-stream")) return await sse(caller, await withMentioned(caller, input), request, asked ? () => callerOf(asked) : null);
1072
1137
  const output = injected && "platform" in injected ? await asPlatform(target.name, injected, input, request.signal) : await invoke(target.name, caller, input, request.signal);
1073
1138
  if (target.name === "attachment") return attachmentResponse(output);
1074
- return json(output, CREATED.has(target.name) ? 201 : 200);
1139
+ const made = CREATED.has(target.name) || target.name === "createChannel" && output.created;
1140
+ return json(output, made ? 201 : 200);
1075
1141
  } catch (error) {
1076
1142
  return toErrorResponse(error);
1077
1143
  }
@@ -1136,7 +1202,7 @@ const EVENTS_WAIT_S = 25;
1136
1202
  */
1137
1203
  function createBoard(options, runtime) {
1138
1204
  const backend = options.backend ?? runtime.http();
1139
- const policy = createPolicy(options, backend.keeps, runtime.serving);
1205
+ const policy = createPolicy(options, backend.keeps, runtime.serving, backend.trust ?? null);
1140
1206
  const watch = options.watch ?? {};
1141
1207
  const entries = options.sync ?? {};
1142
1208
  const state = { learned: null };
@@ -1244,8 +1310,7 @@ function createBoard(options, runtime) {
1244
1310
  /** This board's sync entries, as its sync and discovery see them. */
1245
1311
  const ownEntries = Object.entries(entries).map(([key, entry]) => ({
1246
1312
  key,
1247
- platform: entry.platform,
1248
- signIn: entry.users !== null
1313
+ platform: entry.platform
1249
1314
  }));
1250
1315
  const binding = {
1251
1316
  subscriptions,
@@ -1278,6 +1343,7 @@ function createBoard(options, runtime) {
1278
1343
  typing: async (messages, status) => (await server()).typing(messages, status),
1279
1344
  accessNow: (principal) => policy.accessNow(principal),
1280
1345
  project: () => policy.project(),
1346
+ issuers: () => policy.issuers,
1281
1347
  handle: (caller) => handleOf(caller, null),
1282
1348
  url: ownUrl
1283
1349
  };
@@ -0,0 +1,5 @@
1
+ import "./auth-DLYSt_-A.mjs";
2
+ import "./folds-DLUzfibJ.mjs";
3
+ import "./client-COfYrgfV.mjs";
4
+ import "./backend-Bob-5CM7.mjs";
5
+ import "./errors-2vGs49Db.mjs";
@@ -111,28 +111,8 @@ function foldMessage(state, event) {
111
111
  default: return;
112
112
  }
113
113
  }
114
- /** The principal an actor belongs to: its link, its own principal for a profile (`…+claude`), or itself. */
115
- function personOf(actors, actor) {
116
- const linked = own(actors.links, actor)?.to;
117
- if (linked) return linked;
118
- const plus = actor.indexOf("+");
119
- return plus === -1 ? actor : actor.slice(0, plus);
120
- }
121
- /** The platform actors linked to `principal`. */
122
- const linkedTo = (actors, principal) => Object.entries(actors.links).filter(([, link]) => link.to === principal).map(([actor]) => actor);
114
+ /** Who's who: `actor.seen`. */
123
115
  function foldActors(state, event) {
124
- if (event.type === "actor.linked") {
125
- setOwn(state.links, event.payload.actor, {
126
- to: event.payload.to,
127
- method: event.payload.method,
128
- at: iso(event.createdAt)
129
- });
130
- return state;
131
- }
132
- if (event.type === "actor.unlinked") {
133
- delete state.links[event.payload.actor];
134
- return state;
135
- }
136
116
  if (event.type !== "actor.seen") return state;
137
117
  const { actor, kind, handle, name, avatar, host } = event.payload;
138
118
  const previous = own(state.byId, actor);
@@ -224,4 +204,4 @@ function foldSync(state, event) {
224
204
  }
225
205
  }
226
206
  //#endregion
227
- export { linkedTo as a, iso as i, foldMessages as n, personOf as o, foldSync as r, foldActors as t };
207
+ export { iso as i, foldMessages as n, foldSync as r, foldActors as t };
@@ -101,6 +101,19 @@ declare const boardEvents: {
101
101
  blind: z.ZodOptional<z.ZodLiteral<true>>;
102
102
  importedBy: z.ZodOptional<z.ZodString>;
103
103
  }, z.core.$strip>;
104
+ /**
105
+ * A channel made before anything posted to it. A post makes one too, with no event of its own:
106
+ * this is only for one that's empty. It names no message, so it carries no `id`.
107
+ */
108
+ 'channel.created': z.ZodObject<{
109
+ channel: z.ZodString;
110
+ by: z.ZodObject<{
111
+ actor: z.ZodString;
112
+ principal: z.ZodString;
113
+ profile: z.ZodNullable<z.ZodString>;
114
+ session: z.ZodNullable<z.ZodString>;
115
+ }, z.core.$strip>;
116
+ }, z.core.$strip>;
104
117
  /** A message's links changed: an edit linked something new, or the platform sent what a link says. */
105
118
  'message.linked': z.ZodObject<{
106
119
  id: z.ZodNumber;
@@ -214,7 +227,6 @@ declare const boardEvents: {
214
227
  external: z.ZodObject<{
215
228
  thread: z.ZodString;
216
229
  message: z.ZodString;
217
- asUser: z.ZodOptional<z.ZodLiteral<true>>;
218
230
  }, z.core.$strip>;
219
231
  syncedBy: z.ZodOptional<z.ZodString>;
220
232
  }, z.core.$strip>;
@@ -251,35 +263,6 @@ declare const boardEvents: {
251
263
  avatar: z.ZodNullable<z.ZodString>;
252
264
  host: z.ZodOptional<z.ZodLiteral<true>>;
253
265
  }, z.core.$strip>;
254
- /**
255
- * A platform actor (`slack:user:U0456`) is the same person as a principal
256
- * (`vercel:user:…`): for display, `--by`, and mentions, never for permissions.
257
- * `method` says how it was proved: a one-time code posted from that account,
258
- * or the account's own sign-in (`consent`).
259
- */
260
- 'actor.linked': z.ZodObject<{
261
- actor: z.ZodString;
262
- to: z.ZodString;
263
- method: z.ZodEnum<{
264
- code: "code";
265
- consent: "consent";
266
- }>;
267
- by: z.ZodObject<{
268
- actor: z.ZodString;
269
- principal: z.ZodString;
270
- profile: z.ZodNullable<z.ZodString>;
271
- session: z.ZodNullable<z.ZodString>;
272
- }, z.core.$strip>;
273
- }, z.core.$strip>;
274
- 'actor.unlinked': z.ZodObject<{
275
- actor: z.ZodString;
276
- by: z.ZodObject<{
277
- actor: z.ZodString;
278
- principal: z.ZodString;
279
- profile: z.ZodNullable<z.ZodString>;
280
- session: z.ZodNullable<z.ZodString>;
281
- }, z.core.$strip>;
282
- }, z.core.$strip>;
283
266
  };
284
267
  declare const BOARD_SESSION = "board";
285
268
  type BoardEvents = typeof boardEvents;
@@ -548,28 +531,18 @@ type Actor = {
548
531
  /** A host's principal, or one of its profiles: once it's seen as one, it stays one. Views leave it out. */
549
532
  host?: true;
550
533
  };
551
- /** A platform actor proved to be a principal (`actor.linked`). */
552
- type ActorLink = {
553
- to: string;
554
- method: 'code' | 'consent';
555
- at: string;
556
- };
557
534
  type ActorsState = {
558
535
  byId: Record<string, Actor>;
559
536
  /** Lowercased handle → actor IDs. Handles aren't reserved, so several can share one. */
560
537
  byHandle: Record<string, string[]>;
561
- /** Platform actor → the principal it's linked to. Display and filtering only, never permissions. */
562
- links: Record<string, ActorLink>;
563
538
  };
564
539
  /** A copy of a board message on a platform. */
565
540
  /** `files`: the platform's IDs for the message's files, or null when it refused them; unset until they're sent. */
566
- /** `asUser`: posted with its author's own token, so editing or deleting it needs that token too. */
567
541
  /** `by`: the principal of the sync elsewhere that recorded it, whose copy it is to change; none for the board's own sync's. */
568
542
  type ExternalCopy = {
569
543
  thread: string;
570
544
  message: string;
571
545
  files?: string[] | null;
572
- asUser?: true;
573
546
  by?: string;
574
547
  };
575
548
  type SyncedThread = {
@@ -1,5 +1,5 @@
1
- import { d as Board, f as BoardOptions } from "./backend-n3hajfrD.mjs";
2
- import "./exports-CrGA5_yN.mjs";
1
+ import { f as Board, p as BoardOptions } from "./backend-Bob-5CM7.mjs";
2
+ import "./exports-FBOQWT7O.mjs";
3
3
  //#region src/server/index.d.ts
4
4
  /**
5
5
  * The handle on a board. With a `backend`, the board lives there: `a2()` keeps it in this
package/dist/index.d.mts CHANGED
@@ -1,9 +1,9 @@
1
- import { a as CallerRequest, o as Claims } from "./auth-CU56Q2Xh.mjs";
2
- import { x as Attachment } from "./folds-8wRRXjAt.mjs";
3
- import { $ as RegisterBody, A as InboxItem, At as SyncReceiptInput, B as MatchesQuery, Bt as TypingBody, C as EventsQuery, Ct as SyncCopiesView, D as IdentityView, Dt as SyncImportBody, E as IdentityUnlinkBody, Et as SyncFindView, F as LabelBody, Ft as ThreadSummaryView, G as MessagesQueryInput, H as MessageView, Ht as UploadStartBody, I as LinkView, It as ThreadView, J as PostBody, K as ParsedFilters, L as LinkedAccount, Lt as ThreadsPage, M as InboxPushBody, Mt as SyncThreadView, N as InboxQuery, Nt as SyncUnlinkBody, O as InboxAckBody, Ot as SyncImportInput, P as LABEL_CONDITIONS_MAX, Pt as SyncedView, Q as Ref, R as LinkedQuery, Rt as ThreadsQuery, S as EventsPage, St as SyncCopiesQuery, T as FiltersInput, Tt as SyncFindQuery, U as MessagesPage, Ut as UploadTicket, V as Me, Vt as TypingResult, W as MessagesQuery, Wt as routes, X as REGISTERED_WATCHERS_MAX, Y as PostInput, Z as ReactBody, _ as BoardEventView, _t as SyncAccountBody, a as PostOptions, at as ScheduleBodyOutput, b as Discovery, bt as SyncCodeBody, c as ScheduleOptions, ct as SchedulesQuery, d as WaitOptions, dt as SignInQuery, et as RegisterInput, f as Written, ft as SourceJoin, g as ApiError, gt as SourceReadQuery, h as ActorView, ht as SourceRead, i as Filters, it as ScheduleBody, j as InboxNextQuery, jt as SyncRootsQuery, k as InboxEntry, kt as SyncReceiptBody, l as ScheduleTiming, lt as SchedulesQueryInput, m as AckBody, mt as SourceMessage, n as BoardError, nt as ResolvedSyncTarget, o as ProfileOption, ot as ScheduleInput, p as duration, pt as SourceJoinBody, q as PersonView, rt as RouteName, s as ReplyOptions, st as ScheduleView, tt as RegistrationView, u as UploadInput, ut as SchedulesQueryOutput, v as ChannelView, vt as SyncAccountInput, w as EventsQueryInput, wt as SyncFindInput, x as EditBody, xt as SyncCodeView, y as DeleteBody, yt as SyncBody, z as MatchView, zt as ThreadsQueryInput } from "./client-CjAatpV4.mjs";
4
- import { S as Protocol, _ as MiddlewareContext, a as Identity, b as WriteSource, c as SelfCaller, d as Board, f as BoardOptions, g as Middleware, h as DroppedError, i as Caller, l as Subscription, m as WatcherContext, n as BackendRoute, o as InboxDelivery, p as Watcher, r as BoardBinding, s as ProtocolMethods, t as Backend, u as VerifiedCaller, v as Write, x as Method, y as WritePatch } from "./backend-n3hajfrD.mjs";
5
- import { t as HttpError } from "./errors-87TVtXeb.mjs";
6
- import "./exports-CrGA5_yN.mjs";
1
+ import { a as CallerRequest, o as Claims } from "./auth-DLYSt_-A.mjs";
2
+ import { x as Attachment } from "./folds-DLUzfibJ.mjs";
3
+ import { $ as RegisterBody, A as InboxAckBody, At as ThreadsPage, B as MatchView, Bt as routes, C as EventsPage, Ct as SyncImportInput, D as GrantBody, Dt as SyncedView, E as FiltersInput, Et as SyncThreadView, F as InboxQuery, Ft as TokenView, G as MessagesQuery, H as Me, I as LABEL_CONDITIONS_MAX, It as TypingBody, J as PostBody, K as MessagesQueryInput, L as LabelBody, Lt as TypingResult, M as InboxItem, Mt as ThreadsQueryInput, N as InboxNextQuery, Nt as TokenBody, O as GrantInput, Ot as ThreadSummaryView, P as InboxPushBody, Pt as TokenInput, Q as Ref, R as LinkView, Rt as UploadStartBody, S as EditBody, St as SyncImportBody, T as EventsQueryInput, Tt as SyncReceiptInput, U as MessageView, V as MatchesQuery, W as MessagesPage, X as REGISTERED_WATCHERS_MAX, Y as PostInput, Z as ReactBody, _ as BoardEventView, _t as SyncCopiesQuery, a as PostOptions, at as ScheduleBodyOutput, b as DeleteBody, bt as SyncFindQuery, c as ScheduleOptions, ct as SchedulesQuery, d as WaitOptions, dt as SourceJoin, et as RegisterInput, f as Written, ft as SourceJoinBody, g as ApiError, gt as SyncBody, h as ActorView, ht as SourceReadQuery, i as Filters, it as ScheduleBody, j as InboxEntry, jt as ThreadsQuery, k as GrantView, kt as ThreadView, l as ScheduleTiming, lt as SchedulesQueryInput, m as AckBody, mt as SourceRead, n as BoardError, nt as ResolvedSyncTarget, o as ProfileOption, ot as ScheduleInput, p as duration, pt as SourceMessage, q as ParsedFilters, rt as RouteName, s as ReplyOptions, st as ScheduleView, tt as RegistrationView, u as UploadInput, ut as SchedulesQueryOutput, v as ChannelView, vt as SyncCopiesView, w as EventsQuery, wt as SyncReceiptBody, x as Discovery, xt as SyncFindView, y as CreateChannelBody, yt as SyncFindInput, z as LinkedQuery, zt as UploadTicket } from "./client-COfYrgfV.mjs";
4
+ import { C as Protocol, S as Method, _ as Middleware, a as Identity, b as WritePatch, c as SelfCaller, d as VerifiedCaller, f as Board, g as DroppedError, h as WatcherContext, i as Caller, m as Watcher, n as BackendRoute, o as InboxDelivery, p as BoardOptions, r as BoardBinding, s as ProtocolMethods, t as Backend, u as Subscription, v as MiddlewareContext, x as WriteSource, y as Write } from "./backend-Bob-5CM7.mjs";
5
+ import { t as HttpError } from "./errors-2vGs49Db.mjs";
6
+ import "./exports-FBOQWT7O.mjs";
7
7
  //#region src/server/index.default.d.ts
8
8
  /**
9
9
  * The handle on a board. Outside Node there's no environment to find one in, so pass its
@@ -11,4 +11,4 @@ import "./exports-CrGA5_yN.mjs";
11
11
  */
12
12
  export declare function board(options?: BoardOptions): Board;
13
13
  //#endregion
14
- export { type AckBody, type ActorView, type ApiError, type Attachment, type Backend, type BackendRoute, type Board, type BoardBinding, BoardError, type BoardEventView, type BoardOptions, type Caller, type CallerRequest, type ChannelView, type Claims, type DeleteBody, type Discovery, DroppedError, type EditBody, type EventsPage, type EventsQuery, type EventsQueryInput, type Filters, type FiltersInput, HttpError, type Identity, type IdentityUnlinkBody, type IdentityView, type InboxAckBody, type InboxDelivery, type InboxEntry, type InboxItem, type InboxNextQuery, type InboxPushBody, type InboxQuery, type LABEL_CONDITIONS_MAX, type LabelBody, type LinkView, type LinkedAccount, type LinkedQuery, type MatchView, type MatchesQuery, type Me, type MessageView, type MessagesPage, type MessagesQuery, type MessagesQueryInput, type Method, type Middleware, type MiddlewareContext, type ParsedFilters, type PersonView, type PostBody, type PostInput, type PostOptions, type ProfileOption, type Protocol, type ProtocolMethods, type REGISTERED_WATCHERS_MAX, type ReactBody, type Ref, type RegisterBody, type RegisterInput, type RegistrationView, type ReplyOptions, type ResolvedSyncTarget, type RouteName, type ScheduleBody, type ScheduleBodyOutput, type ScheduleInput, type ScheduleOptions, type ScheduleTiming, type ScheduleView, type SchedulesQuery, type SchedulesQueryInput, type SchedulesQueryOutput, type SelfCaller, type SignInQuery, type SourceJoin, type SourceJoinBody, type SourceMessage, type SourceRead, type SourceReadQuery, type Subscription, type SyncAccountBody, type SyncAccountInput, type SyncBody, type SyncCodeBody, type SyncCodeView, type SyncCopiesQuery, type SyncCopiesView, type SyncFindInput, type SyncFindQuery, type SyncFindView, type SyncImportBody, type SyncImportInput, type SyncReceiptBody, type SyncReceiptInput, type SyncRootsQuery, type SyncThreadView, type SyncUnlinkBody, type SyncedView, type ThreadSummaryView, type ThreadView, type ThreadsPage, type ThreadsQuery, type ThreadsQueryInput, type TypingBody, type TypingResult, type UploadInput, type UploadStartBody, type UploadTicket, type VerifiedCaller, type WaitOptions, type Watcher, type WatcherContext, type Write, type WritePatch, type WriteSource, type Written, duration, type routes };
14
+ export { type AckBody, type ActorView, type ApiError, type Attachment, type Backend, type BackendRoute, type Board, type BoardBinding, BoardError, type BoardEventView, type BoardOptions, type Caller, type CallerRequest, type ChannelView, type Claims, type CreateChannelBody, type DeleteBody, type Discovery, DroppedError, type EditBody, type EventsPage, type EventsQuery, type EventsQueryInput, type Filters, type FiltersInput, type GrantBody, type GrantInput, type GrantView, HttpError, type Identity, type InboxAckBody, type InboxDelivery, type InboxEntry, type InboxItem, type InboxNextQuery, type InboxPushBody, type InboxQuery, type LABEL_CONDITIONS_MAX, type LabelBody, type LinkView, type LinkedQuery, type MatchView, type MatchesQuery, type Me, type MessageView, type MessagesPage, type MessagesQuery, type MessagesQueryInput, type Method, type Middleware, type MiddlewareContext, type ParsedFilters, type PostBody, type PostInput, type PostOptions, type ProfileOption, type Protocol, type ProtocolMethods, type REGISTERED_WATCHERS_MAX, type ReactBody, type Ref, type RegisterBody, type RegisterInput, type RegistrationView, type ReplyOptions, type ResolvedSyncTarget, type RouteName, type ScheduleBody, type ScheduleBodyOutput, type ScheduleInput, type ScheduleOptions, type ScheduleTiming, type ScheduleView, type SchedulesQuery, type SchedulesQueryInput, type SchedulesQueryOutput, type SelfCaller, type SourceJoin, type SourceJoinBody, type SourceMessage, type SourceRead, type SourceReadQuery, type Subscription, type SyncBody, type SyncCopiesQuery, type SyncCopiesView, type SyncFindInput, type SyncFindQuery, type SyncFindView, type SyncImportBody, type SyncImportInput, type SyncReceiptBody, type SyncReceiptInput, type SyncThreadView, type SyncedView, type ThreadSummaryView, type ThreadView, type ThreadsPage, type ThreadsQuery, type ThreadsQueryInput, type TokenBody, type TokenInput, type TokenView, type TypingBody, type TypingResult, type UploadInput, type UploadStartBody, type UploadTicket, type VerifiedCaller, type WaitOptions, type Watcher, type WatcherContext, type Write, type WritePatch, type WriteSource, type Written, duration, type routes };
@@ -1,4 +1,4 @@
1
- import { r as createHttp } from "./backend-http-BPaNYLWj.mjs";
1
+ import { r as createHttp } from "./backend-http-CvUKOn-m.mjs";
2
2
  //#region src/backend-http/index.default.ts
3
3
  /**
4
4
  * The board at `url`, which keeps the log. Outside Node there's no environment to find it or a
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { f as duration, t as HttpError, u as BoardError } from "./errors-C_5rB8Iz.mjs";
2
- import { t as http } from "./index.default-DhkgpfR2.mjs";
3
- import { t as createBoard } from "./exports-CG0P8Qi4.mjs";
4
- import { t as DroppedError } from "./middleware-DlJQYiwV.mjs";
1
+ import { f as duration, t as HttpError, u as BoardError } from "./errors-CJXhhzvT.mjs";
2
+ import { t as http } from "./index.default-BUcwDaAe.mjs";
3
+ import { t as createBoard } from "./exports-CWIdN0Ub.mjs";
4
+ import { t as DroppedError } from "./middleware-BavYSDUa.mjs";
5
5
  //#region src/server/index.default.ts
6
6
  /**
7
7
  * The handle on a board. Outside Node there's no environment to find one in, so pass its
@@ -10,7 +10,7 @@ import { t as DroppedError } from "./middleware-DlJQYiwV.mjs";
10
10
  function board(options = {}) {
11
11
  return createBoard(options, {
12
12
  http: () => http(),
13
- sync: () => import("./engine-DkdkXQEy.mjs"),
13
+ sync: () => import("./engine-DurYIRt8.mjs"),
14
14
  serving: null
15
15
  });
16
16
  }