@vercel/board 0.18.0 → 0.19.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 (40) hide show
  1. package/dist/{a2-ChRLRupa.mjs → a2-C1obi0Qb.mjs} +2 -1
  2. package/dist/{backend-B-SJ7p6p.d.mts → backend-B5GCP1oK.d.mts} +1 -1
  3. package/dist/backend-a2.d.mts +4 -2
  4. package/dist/backend-a2.mjs +1 -1
  5. package/dist/{backend-http-Ddzd5kTS.mjs → backend-http-BMVDbtD_.mjs} +19 -4
  6. package/dist/{backend-http-7cNb8nJI.d.mts → backend-http-Ca6b0FPA.d.mts} +4 -2
  7. package/dist/{backend-http-CMlL3yq3.mjs → backend-http-DMpQ750P.mjs} +10 -8
  8. package/dist/backend-http.d.mts +1 -1
  9. package/dist/backend-http.mjs +1 -1
  10. package/dist/backend-http.node.d.mts +1 -1
  11. package/dist/backend-http.node.mjs +1 -1
  12. package/dist/{chat-provider-DNJQyajS.d.mts → chat-provider-B5uCc0an.d.mts} +1 -1
  13. package/dist/cli.mjs +6 -5
  14. package/dist/{conformance-CT5EeHeC.mjs → conformance-D0Z4dXC_.mjs} +46 -5
  15. package/dist/conformance.d.mts +1 -1
  16. package/dist/conformance.mjs +1 -1
  17. package/dist/exports-B3AFzT_f.d.mts +4 -0
  18. package/dist/{exports-CgjfMrQ-.mjs → exports-CUGshfRF.mjs} +1 -1
  19. package/dist/index.d.mts +3 -3
  20. package/dist/{index.default-C5Cpz3sa.mjs → index.default-Cm6GsLdj.mjs} +1 -1
  21. package/dist/index.mjs +2 -2
  22. package/dist/index.node.d.mts +3 -3
  23. package/dist/index.node.mjs +1 -1
  24. package/dist/inspect/app.js +2 -2
  25. package/dist/{provider-B0b9Qxw4.d.mts → provider-B1Z3Ok86.d.mts} +16 -1
  26. package/dist/{serve-BGYw0uNL.mjs → serve-DB9ijyQ7.mjs} +3 -3
  27. package/dist/{server-DPxBSogx.mjs → server-BujqWBP5.mjs} +2 -2
  28. package/dist/sync-github.d.mts +2 -2
  29. package/dist/sync-kapso.d.mts +2 -2
  30. package/dist/sync-linear.d.mts +2 -2
  31. package/dist/sync-slack.d.mts +2 -2
  32. package/dist/sync.chat.d.mts +1 -1
  33. package/dist/sync.d.mts +1 -1
  34. package/dist/{watchers-hash-RLtnSF-t.mjs → watchers-hash-B2TevhFZ.mjs} +20 -2
  35. package/docs/01-board/06-watchers.md +7 -0
  36. package/docs/03-guides/01-coding-agents.md +2 -1
  37. package/docs/04-reference/01-cli.md +2 -1
  38. package/docs/04-reference/03-http.md +4 -2
  39. package/package.json +1 -1
  40. package/dist/exports-ZsgjzEnM.d.mts +0 -4
@@ -1,7 +1,7 @@
1
1
  import { n as DIRECT_UPLOAD_BYTES_MAX, r as IMPORT_BYTES_MAX, t as ATTACHMENT_BYTES_MAX } from "./files-SniSmeEr.mjs";
2
2
  import { _ as RemoteWatcherId, b as actorKind, i as Attachment, x as platformAvatar } from "./schemas-Be08nyfW.mjs";
3
3
  import "./external-BV_cUncs.mjs";
4
- import { f as VERSION, n as compileWatcher, o as METHODS, p as SYNC, r as hasCriteria, t as canonicalWatchers } from "./watchers-hash-RLtnSF-t.mjs";
4
+ import { f as VERSION, n as compileWatcher, o as METHODS, p as SYNC, r as hasCriteria, t as canonicalWatchers } from "./watchers-hash-B2TevhFZ.mjs";
5
5
  import { r as duration, t as BoardError } from "./client-BA-J7usH.mjs";
6
6
  import { n as derivedId, r as isKeyIdOf } from "./ids-C6AYo1bu.mjs";
7
7
  import { a as forbidden, c as toErrorResponse, i as duplicateId, r as badRequest, s as notFound, t as HttpError } from "./errors-DX3hpwWt.mjs";
@@ -3081,6 +3081,7 @@ function build(options, binding) {
3081
3081
  return { watchers: principal.access.everywhere("manage") ? all : all.filter((found) => found.owner === principal.id) };
3082
3082
  },
3083
3083
  async register(caller, body) {
3084
+ if (body.path) throw new HttpError(501, "no_wake_by_path", "this board wakes a url, not a path: register `url` instead");
3084
3085
  const { principal } = await identityOf(caller);
3085
3086
  ownWatchers(principal);
3086
3087
  if (!principal.access.anywhere("read")) throw forbidden("this token can't read anywhere, so a watcher it registers would see nothing");
@@ -2,7 +2,7 @@ import { t as Access } from "./access-CV96SYgr.mjs";
2
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
3
  import { i as Attachment, o as Author, p as Labels } from "./schemas-CWxs3ECJ.mjs";
4
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";
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-B1Z3Ok86.mjs";
6
6
  import { z } from "zod";
7
7
  //#region src/api/protocol.d.ts
8
8
  type Input<S extends z.ZodType> = NonNullable<z.output<S>>;
@@ -2,8 +2,8 @@ import { i as Attachment } from "./schemas-CWxs3ECJ.mjs";
2
2
  import { t as Actor } from "./folds-CRYY14kc.mjs";
3
3
  import { l as Reducer, r as ContractEvent } from "./reducer-CYPHsSmd-BQIfozGi.mjs";
4
4
  import { a as SessionAppend, c as SessionConsumeOptions, d as SessionSchedule, f as StateOptions, l as SessionConsumeResult, n as A2Server, o as SessionClearOptions, p as SessionCleared, r as Consumer, s as SessionClearResult, t as A2Scheduler, u as SessionDeadLetters } from "./server-qgez0Xny-D5mLfqFQ.mjs";
5
- import { $ as Discovery, Mt as RegistrationView, Qt as SyncCopiesView, Rt as ScheduleView, Tt as PersonView, Wt as SourceRead, Xt as SyncCodeView, Z as ChannelView, _t as MatchView, bt as MessageView, ct as InboxEntry, dn as ThreadView, fn as ThreadsPage, ht as LinkedAccount, ln as SyncedView, lt as InboxItem, ot as IdentityView, sn as SyncThreadView, tn as SyncFindView, tt as EventsPage, vn as UploadTicket, xt as MessagesPage, yt as Me } from "./provider-B0b9Qxw4.mjs";
6
- import { i as Caller, n as BackendRoute, r as BoardBinding } from "./backend-B-SJ7p6p.mjs";
5
+ import { $ as Discovery, Mt as RegistrationView, Qt as SyncCopiesView, Rt as ScheduleView, Tt as PersonView, Wt as SourceRead, Xt as SyncCodeView, Z as ChannelView, _t as MatchView, bt as MessageView, ct as InboxEntry, dn as ThreadView, fn as ThreadsPage, ht as LinkedAccount, ln as SyncedView, lt as InboxItem, ot as IdentityView, sn as SyncThreadView, tn as SyncFindView, tt as EventsPage, vn as UploadTicket, xt as MessagesPage, yt as Me } from "./provider-B1Z3Ok86.mjs";
6
+ import { i as Caller, n as BackendRoute, r as BoardBinding } from "./backend-B5GCP1oK.mjs";
7
7
  import { n as fsBlob, r as memoryBlob, t as BlobStore } from "./blob-C0J3wbGv.mjs";
8
8
  import { t as BoardStore } from "./types-8yq2VnKI.mjs";
9
9
  import { z } from "zod";
@@ -221,6 +221,8 @@ export declare function a2(options?: A2Options): {
221
221
  }>;
222
222
  register: (caller: Caller, input: {
223
223
  url?: string | null | undefined;
224
+ path?: string | null | undefined;
225
+ branch?: string | null | undefined;
224
226
  watch: Record<string, {
225
227
  tail: string;
226
228
  when: "enters" | "matches";
@@ -1,2 +1,2 @@
1
- import { n as fsBlob, r as memoryBlob, t as a2 } from "./a2-ChRLRupa.mjs";
1
+ import { n as fsBlob, r as memoryBlob, t as a2 } from "./a2-C1obi0Qb.mjs";
2
2
  export { a2, fsBlob, memoryBlob };
@@ -1,6 +1,6 @@
1
1
  import "./files-SniSmeEr.mjs";
2
2
  import "./schemas-Be08nyfW.mjs";
3
- import { K as routes, k as RegisterBody, n as compileWatcher, o as METHODS, t as canonicalWatchers } from "./watchers-hash-RLtnSF-t.mjs";
3
+ import { K as routes, k as RegisterBody, n as compileWatcher, o as METHODS, t as canonicalWatchers } from "./watchers-hash-B2TevhFZ.mjs";
4
4
  import { n as createBoardClient, t as BoardError } from "./client-BA-J7usH.mjs";
5
5
  import { a as sha256, i as keyId, n as derivedId, t as DERIVED_ID } from "./ids-C6AYo1bu.mjs";
6
6
  import { a as forbidden, o as json, t as HttpError } from "./errors-DX3hpwWt.mjs";
@@ -350,17 +350,32 @@ function createHttp(url, options, environment) {
350
350
  };
351
351
  /** Only an https URL can be woken; anything else pulls on its own. */
352
352
  const wakeable = (here) => here?.startsWith("https://") ? here : null;
353
+ /**
354
+ * Where the remote wakes this board: on a board that wakes by path, the path on this project,
355
+ * which gets through its Deployment Protection, from production or from a preview of a branch,
356
+ * which it names; anywhere else (off Vercel, development, a custom environment), its URL.
357
+ */
358
+ async function wakeAt(here) {
359
+ const env = typeof process !== "undefined" && process.env.VERCEL === "1" ? process.env : null;
360
+ const environment = env?.VERCEL_TARGET_ENV || env?.VERCEL_ENV;
361
+ const branch = environment === "preview" ? env?.VERCEL_GIT_COMMIT_REF || null : null;
362
+ if (!here || environment !== "production" && !branch || !(await discovery()).wake_by_path) return { url: here };
363
+ return {
364
+ path: new URL(here).pathname,
365
+ ...branch ? { branch } : {}
366
+ };
367
+ }
353
368
  let last = null;
354
369
  let registering = null;
355
370
  /** Registers this board's watchers with the remote, as itself, replacing its last registration. */
356
371
  async function register(here) {
357
372
  const current = await watchersHash();
358
373
  if (last && last.url === here && last.result.hash === current && Date.now() - last.at < REGISTER_EVERY_MS) return last.result;
359
- registering ??= own.register({
360
- url: here,
374
+ registering ??= wakeAt(here).then((where) => own.register({
375
+ ...where,
361
376
  watch: specs,
362
377
  ...sync.length > 0 ? { sync } : {}
363
- }).then((result) => {
378
+ })).then((result) => {
364
379
  last = {
365
380
  url: here,
366
381
  at: Date.now(),
@@ -1,7 +1,7 @@
1
1
  import { i as Attachment } from "./schemas-CWxs3ECJ.mjs";
2
2
  import { t as Actor } from "./folds-CRYY14kc.mjs";
3
- import { $ as Discovery, Mt as RegistrationView, Qt as SyncCopiesView, Rt as ScheduleView, Tt as PersonView, Wt as SourceRead, Xt as SyncCodeView, Z as ChannelView, _t as MatchView, bt as MessageView, ct as InboxEntry, dn as ThreadView, fn as ThreadsPage, ht as LinkedAccount, ln as SyncedView, lt as InboxItem, ot as IdentityView, sn as SyncThreadView, tn as SyncFindView, tt as EventsPage, vn as UploadTicket, xt as MessagesPage, yt as Me, z as ProfileOption } from "./provider-B0b9Qxw4.mjs";
4
- import { i as Caller, n as BackendRoute, r as BoardBinding } from "./backend-B-SJ7p6p.mjs";
3
+ import { $ as Discovery, Mt as RegistrationView, Qt as SyncCopiesView, Rt as ScheduleView, Tt as PersonView, Wt as SourceRead, Xt as SyncCodeView, Z as ChannelView, _t as MatchView, bt as MessageView, ct as InboxEntry, dn as ThreadView, fn as ThreadsPage, ht as LinkedAccount, ln as SyncedView, lt as InboxItem, ot as IdentityView, sn as SyncThreadView, tn as SyncFindView, tt as EventsPage, vn as UploadTicket, xt as MessagesPage, yt as Me, z as ProfileOption } from "./provider-B1Z3Ok86.mjs";
4
+ import { i as Caller, n as BackendRoute, r as BoardBinding } from "./backend-B5GCP1oK.mjs";
5
5
  //#region src/server/backend-http.d.ts
6
6
  type HttpOptions = {
7
7
  /**
@@ -195,6 +195,8 @@ declare function createHttp(url: string | undefined, options: HttpOptions, envir
195
195
  }>;
196
196
  register: (caller: Caller, input: {
197
197
  url?: string | null | undefined;
198
+ path?: string | null | undefined;
199
+ branch?: string | null | undefined;
198
200
  watch: Record<string, {
199
201
  tail: string;
200
202
  when: "enters" | "matches";
@@ -1,5 +1,5 @@
1
1
  import { t as BoardError } from "./client-BA-J7usH.mjs";
2
- import { t as createHttp } from "./backend-http-Ddzd5kTS.mjs";
2
+ import { t as createHttp } from "./backend-http-BMVDbtD_.mjs";
3
3
  import { execFile } from "node:child_process";
4
4
  import { createHash } from "node:crypto";
5
5
  import { mkdir, readFile, writeFile } from "node:fs/promises";
@@ -447,13 +447,14 @@ function guarded(url, doFetch, suppliedToken) {
447
447
  };
448
448
  }
449
449
  /**
450
- * One request that recognizes the wall: a redirect to `/sso-api`, or an HTML
450
+ * One request that recognizes the wall: a redirect to `/sso-api`, an HTML
451
451
  * page where the board answers JSON (on SAML teams, the redirects end at the
452
- * IdP's login page with a 200). Only a 2xx, 401, or 403 HTML page counts, so a
453
- * 404 page keeps its own error; attachments, which may be HTML, come with a
454
- * `content-disposition`. Other redirects of a GET or HEAD are followed as fetch
455
- * would, dropping the token and bypass secret on the way to another origin; a
456
- * redirected POST comes back as its 3xx.
452
+ * IdP's login page with a 200), or Vercel's own 401 (`x-vercel-error`, which
453
+ * Vercel Connect passport answers a fetch with). Only a 2xx, 401, or 403 HTML
454
+ * page counts, so a 404 page keeps its own error; attachments, which may be
455
+ * HTML, come with a `content-disposition`. Other redirects of a GET or HEAD
456
+ * are followed as fetch would, dropping the token and bypass secret on the way
457
+ * to another origin; a redirected POST comes back as its 3xx.
457
458
  */
458
459
  async function through(doFetch, input, init, bypass) {
459
460
  let target = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
@@ -484,6 +485,7 @@ async function through(doFetch, input, init, bypass) {
484
485
  }
485
486
  const { status } = response;
486
487
  if (/^text\/html\b/i.test(response.headers.get("content-type") ?? "") && !response.headers.has("content-disposition") && (status >= 200 && status < 300 || status === 401 || status === 403)) throw new Wall(status);
488
+ if (status === 401 && response.headers.has("x-vercel-error")) throw new Wall(status);
487
489
  return response;
488
490
  }
489
491
  }
@@ -540,7 +542,7 @@ async function projectBypass(url, host, doFetch) {
540
542
  secret: existing,
541
543
  created: false
542
544
  };
543
- if (!details.ssoProtection && !details.passwordProtection) throw new BoardError("no_board", `no board at ${url}: it answered with a web page, and its Vercel project isn't protected`, 0);
545
+ if (!details.ssoProtection && !details.passwordProtection && !details.passport) throw new BoardError("no_board", `no board at ${url}: it answered with a web page, and its Vercel project isn't protected`, 0);
544
546
  const created = await api("PATCH", `/v1/projects/${project.projectId}/protection-bypass${scope}`);
545
547
  const secret = automationBypass(created?.protectionBypass);
546
548
  if (!secret) throw blocked("can't read or create its project's automation bypass: ask a project admin");
@@ -1,4 +1,4 @@
1
- import { n as HttpOptions, t as HttpBackend } from "./backend-http-7cNb8nJI.mjs";
1
+ import { n as HttpOptions, t as HttpBackend } from "./backend-http-Ca6b0FPA.mjs";
2
2
  //#region src/backend-http/index.default.d.ts
3
3
  /**
4
4
  * The board at `url`, which keeps the log. Outside Node there's no environment to find it or a
@@ -1,2 +1,2 @@
1
- import { t as http } from "./index.default-C5Cpz3sa.mjs";
1
+ import { t as http } from "./index.default-Cm6GsLdj.mjs";
2
2
  export { http };
@@ -1,4 +1,4 @@
1
- import { n as HttpOptions, t as HttpBackend } from "./backend-http-7cNb8nJI.mjs";
1
+ import { n as HttpOptions, t as HttpBackend } from "./backend-http-Ca6b0FPA.mjs";
2
2
  //#region src/backend-http/index.d.ts
3
3
  /**
4
4
  * The board at `url`, which keeps the log: this board uses it as this deployment. Without a
@@ -1,2 +1,2 @@
1
- import { t as http } from "./backend-http-CMlL3yq3.mjs";
1
+ import { t as http } from "./backend-http-DMpQ750P.mjs";
2
2
  export { http };
@@ -1,5 +1,5 @@
1
1
  import { d as ExternalFile, f as ExternalLink, l as ExternalAuthor, t as Actor } from "./folds-CRYY14kc.mjs";
2
- import { M as SyncRoot, O as ProviderEvent, Wt as SourceRead, a as ProviderOptions, c as ReceivedEvent, d as ResolvedTarget, i as ProviderFile, n as FileDelivery, o as ProviderPost, p as SyncProvider, r as ProviderDelivery, s as Receive, t as FetchedLink, y as SyncFn } from "./provider-B0b9Qxw4.mjs";
2
+ import { M as SyncRoot, O as ProviderEvent, Wt as SourceRead, a as ProviderOptions, c as ReceivedEvent, d as ResolvedTarget, i as ProviderFile, n as FileDelivery, o as ProviderPost, p as SyncProvider, r as ProviderDelivery, s as Receive, t as FetchedLink, y as SyncFn } from "./provider-B1Z3Ok86.mjs";
3
3
  import { Adapter, Author, FileUpload, Message, ReactionEvent } from "chat";
4
4
  //#region src/sync/types.d.ts
5
5
  declare function externalAuthor(platform: string, author: Author): ExternalAuthor;
package/dist/cli.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { i as fileSize, n as DIRECT_UPLOAD_BYTES_MAX, t as ATTACHMENT_BYTES_MAX } from "./files-SniSmeEr.mjs";
3
3
  import { a as AttachmentId } from "./schemas-Be08nyfW.mjs";
4
- import { a as withMentions, f as VERSION, i as FILTER_FLAGS } from "./watchers-hash-RLtnSF-t.mjs";
4
+ import { a as withMentions, f as VERSION, i as FILTER_FLAGS } from "./watchers-hash-B2TevhFZ.mjs";
5
5
  import { n as displayEmoji, r as normalizeEmoji } from "./emoji-CTLpvH-A.mjs";
6
6
  import { t as BoardError } from "./client-BA-J7usH.mjs";
7
- import { a as detectAgent, c as projectUrl, i as connect, n as boardUrl, r as configDir, s as projectDir, t as http } from "./backend-http-CMlL3yq3.mjs";
8
- import { t as board } from "./server-DPxBSogx.mjs";
9
- import { t as conformance } from "./conformance-CT5EeHeC.mjs";
7
+ import { a as detectAgent, c as projectUrl, i as connect, n as boardUrl, r as configDir, s as projectDir, t as http } from "./backend-http-DMpQ750P.mjs";
8
+ import { t as board } from "./server-BujqWBP5.mjs";
9
+ import { t as conformance } from "./conformance-D0Z4dXC_.mjs";
10
10
  import { t as displayText } from "./bots-CGkO76qH.mjs";
11
11
  import { execFileSync, spawn } from "node:child_process";
12
12
  import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
@@ -1859,6 +1859,7 @@ async function run(command, args) {
1859
1859
  const link = target !== void 0 && /^https?:\/\//i.test(target);
1860
1860
  if (target && (link || isMessageRef(target))) {
1861
1861
  const thread = link ? await board.linked(target) : await board.thread(messageId(target));
1862
+ if (link && !Array.isArray(thread.replies)) throw new Error("this board can't read links: update board where the board is deployed");
1862
1863
  const narrowed = Object.keys(filters()).length > 0;
1863
1864
  const replies = narrowed ? (await board.messages({
1864
1865
  ...filters(),
@@ -2195,7 +2196,7 @@ async function run(command, args) {
2195
2196
  if (args.length > 0) throw new UsageError("usage: board serve [--port N] [--dir <dir>] [--no-inspect] [--no-open]");
2196
2197
  const port = portFlag();
2197
2198
  const root = projectDir();
2198
- const { localHandle, serveLocal } = await import("./serve-BGYw0uNL.mjs");
2199
+ const { localHandle, serveLocal } = await import("./serve-DB9ijyQ7.mjs");
2199
2200
  const handle = flags.handle ?? process.env.BOARD_HANDLE;
2200
2201
  const local = await serveLocal({
2201
2202
  dir: flags.dir ?? join(root, ".board"),
@@ -17,6 +17,14 @@ async function rejects(attempt, status, code, what) {
17
17
  }
18
18
  throw new Mismatch(`${what}: expected ${status} ${code}, and it succeeded`);
19
19
  }
20
+ /** Where a registration is woken, to put back after a check replaces it: its path (and branch) on a board that wakes by path, or its url. */
21
+ function wokenAt(registered) {
22
+ if (!registered?.path) return { url: registered?.url ?? null };
23
+ return {
24
+ path: registered.path,
25
+ ...registered.branch ? { branch: registered.branch } : {}
26
+ };
27
+ }
20
28
  /** An ID or cursor as the protocol shapes them: a string of 1 to 128 characters. */
21
29
  function isRef(value, what) {
22
30
  check(typeof value === "string" && value.length >= 1 && value.length <= 128, `${what} should be a string of 1 to 128 characters, got ${show(value)}`);
@@ -274,7 +282,7 @@ const CHECKS = [
274
282
  run: async (ctx) => {
275
283
  const registered = (await ctx.board.watchers()).find((r) => r.owner === ctx.me.principal);
276
284
  if (registered && (Object.keys(registered.watch).length > 0 || (registered.sync?.length ?? 0) > 0)) return "this token has watchers or a sync registered here, and checking would replace them";
277
- const url = registered?.url ?? null;
285
+ const where = wokenAt(registered);
278
286
  await ctx.board.register({
279
287
  url: null,
280
288
  watch: { conformance: { tail: `${ctx.channel} --label conformance=${ctx.labels.conformance}` } }
@@ -290,7 +298,40 @@ const CHECKS = [
290
298
  await rejects(ctx.board.ack(match.id, { lease: match.lease }), 404, "no_match", "acking a match twice");
291
299
  } finally {
292
300
  await ctx.board.register({
293
- url,
301
+ ...where,
302
+ watch: {}
303
+ });
304
+ }
305
+ }
306
+ },
307
+ {
308
+ group: "watchers",
309
+ name: "wakes by path only where its discovery says so, and only a Vercel project",
310
+ run: async (ctx) => {
311
+ const registered = (await ctx.board.watchers()).find((r) => r.owner === ctx.me.principal);
312
+ if (registered && (Object.keys(registered.watch).length > 0 || (registered.sync?.length ?? 0) > 0)) return "this token has watchers or a sync registered here, and checking would replace them";
313
+ const byPath = (await ctx.board.discovery()).wake_by_path;
314
+ const environment = /^vercel:project:[^:]+:(.+)$/.exec(ctx.me.principal)?.[1] ?? null;
315
+ if (byPath && environment !== null && environment !== "production" && environment !== "preview") return `a path wakes production or a preview, and this token is ${environment}'s`;
316
+ const where = wokenAt(registered);
317
+ const branch = environment === "preview" ? { branch: "conformance" } : {};
318
+ const attempt = () => ctx.board.register({
319
+ path: "/conformance",
320
+ ...branch,
321
+ watch: {}
322
+ });
323
+ try {
324
+ if (!byPath) await rejects(attempt(), 501, "no_wake_by_path", "a path, on a board that wakes only urls");
325
+ else if (environment === null) await rejects(attempt(), 403, "not_a_project", "a path, from a token that isn't a Vercel project's");
326
+ else {
327
+ const view = await attempt();
328
+ same(view.path, "/conformance", "the registration's path");
329
+ same(view.url, null, "the registration's url");
330
+ same(view.branch ?? null, environment === "preview" ? "conformance" : null, "the registration's branch");
331
+ }
332
+ } finally {
333
+ await ctx.board.register({
334
+ ...where,
294
335
  watch: {}
295
336
  });
296
337
  }
@@ -315,7 +356,7 @@ const CHECKS = [
315
356
  };
316
357
  const registered = (await ctx.board.watchers()).find((registration) => registration.owner === ctx.me.principal);
317
358
  if (registered && (Object.keys(registered.watch).length > 0 || (registered.sync?.length ?? 0) > 0)) return "this token has watchers or a sync registered here, which registering would replace";
318
- const url = registered?.url ?? null;
359
+ const where = wokenAt(registered);
319
360
  try {
320
361
  await ctx.board.register({
321
362
  url: null,
@@ -350,7 +391,7 @@ const CHECKS = [
350
391
  });
351
392
  } catch (error) {
352
393
  await ctx.board.register({
353
- url,
394
+ ...where,
354
395
  watch: {}
355
396
  }).catch(() => {});
356
397
  if (error instanceof BoardError && error.status === 403) return `this token can't sync in ${ctx.channel}`;
@@ -431,7 +472,7 @@ const CHECKS = [
431
472
  }
432
473
  }).catch(() => {});
433
474
  await ctx.board.register({
434
- url,
475
+ ...where,
435
476
  watch: {}
436
477
  }).catch(() => {});
437
478
  }
@@ -1,4 +1,4 @@
1
- import { d as Board } from "./backend-B-SJ7p6p.mjs";
1
+ import { d as Board } from "./backend-B5GCP1oK.mjs";
2
2
  //#region src/conformance/index.d.ts
3
3
  /**
4
4
  * Checks that a board speaks the protocol, through a handle on it (`conformance(board())`), as
@@ -1,2 +1,2 @@
1
- import { t as conformance } from "./conformance-CT5EeHeC.mjs";
1
+ import { t as conformance } from "./conformance-D0Z4dXC_.mjs";
2
2
  export { conformance };
@@ -0,0 +1,4 @@
1
+ import "./auth-CU56Q2Xh.mjs";
2
+ import "./schemas-CWxs3ECJ.mjs";
3
+ import "./provider-B1Z3Ok86.mjs";
4
+ import "./backend-B5GCP1oK.mjs";
@@ -1,6 +1,6 @@
1
1
  import { t as ATTACHMENT_BYTES_MAX } from "./files-SniSmeEr.mjs";
2
2
  import "./schemas-Be08nyfW.mjs";
3
- import { A as ScheduleBody, B as SyncReceiptBody, C as LinkedQuery, D as ReactBody, E as PostBody, F as SyncBody, G as UploadStartBody, H as SyncUnlinkBody, I as SyncCodeBody, L as SyncCopiesQuery, M as SignInQuery, N as SourceReadQuery, O as Ref, P as SyncAccountBody, R as SyncFindQuery, S as LabelBody, T as MessagesQuery, U as ThreadsQuery, V as SyncRootsQuery, W as TypingBody, _ as IdentityUnlinkBody, b as InboxPushBody, c as openapi, d as readJson, f as VERSION, g as EventsQuery, h as EditBody, j as SchedulesQuery, k as RegisterBody, l as queryObject, m as AckBody, o as METHODS, p as SYNC, s as createRouter, u as readCapped, v as InboxAckBody, w as MatchesQuery, x as InboxQuery, y as InboxNextQuery, z as SyncImportBody } from "./watchers-hash-RLtnSF-t.mjs";
3
+ import { A as ScheduleBody, B as SyncReceiptBody, C as LinkedQuery, D as ReactBody, E as PostBody, F as SyncBody, G as UploadStartBody, H as SyncUnlinkBody, I as SyncCodeBody, L as SyncCopiesQuery, M as SignInQuery, N as SourceReadQuery, O as Ref, P as SyncAccountBody, R as SyncFindQuery, S as LabelBody, T as MessagesQuery, U as ThreadsQuery, V as SyncRootsQuery, W as TypingBody, _ as IdentityUnlinkBody, b as InboxPushBody, c as openapi, d as readJson, f as VERSION, g as EventsQuery, h as EditBody, j as SchedulesQuery, k as RegisterBody, l as queryObject, m as AckBody, o as METHODS, p as SYNC, s as createRouter, u as readCapped, v as InboxAckBody, w as MatchesQuery, x as InboxQuery, y as InboxNextQuery, z as SyncImportBody } from "./watchers-hash-B2TevhFZ.mjs";
4
4
  import { n as createBoardClient, t as BoardError } from "./client-BA-J7usH.mjs";
5
5
  import { n as derivedId } from "./ids-C6AYo1bu.mjs";
6
6
  import { a as forbidden, c as toErrorResponse, l as unauthorized, n as RejectedError, o as json, r as badRequest, s as notFound, t as HttpError } from "./errors-DX3hpwWt.mjs";
package/dist/index.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { a as CallerRequest, o as Claims } from "./auth-CU56Q2Xh.mjs";
2
2
  import { i as Attachment } from "./schemas-CWxs3ECJ.mjs";
3
- import { $ as Discovery, $t as SyncFindInput, At as Ref, B as ReplyOptions, Bt as SchedulesQueryInput, Ct as MessagesQueryInput, Dt as PostInput, Et as PostBody, Ft as ScheduleBody, G as Written, Gt as SourceReadQuery, H as ScheduleTiming, Ht as SignInQuery, I as BoardError, It as ScheduleBodyOutput, J as ActorView, Jt as SyncBody, K as duration, Kt as SyncAccountBody, L as Filters, Lt as ScheduleInput, Mt as RegistrationView, Nt as ResolvedSyncTarget, Ot as REGISTERED_WATCHERS_MAX, Pt as RouteName, Q as DeleteBody, Qt as SyncCopiesView, R as PostOptions, Rt as ScheduleView, St as MessagesQuery, Tt as PersonView, U as UploadInput, Ut as SourceMessage, V as ScheduleOptions, Vt as SchedulesQueryOutput, W as WaitOptions, Wt as SourceRead, X as BoardEventView, Xt as SyncCodeView, Y as ApiError, Yt as SyncCodeBody, Z as ChannelView, Zt as SyncCopiesQuery, _n as UploadStartBody, _t as MatchView, an as SyncReceiptInput, at as IdentityUnlinkBody, 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, gn as TypingResult, gt as LinkedQuery, hn as TypingBody, ht as LinkedAccount, in as SyncReceiptBody, it as FiltersInput, jt as RegisterBody, kt as ReactBody, ln as SyncedView, lt as InboxItem, mn as ThreadsQueryInput, mt as LinkView, nn as SyncImportBody, nt as EventsQuery, on as SyncRootsQuery, ot as IdentityView, pn as ThreadsQuery, pt as LabelBody, q as AckBody, qt as SyncAccountInput, rn as SyncImportInput, rt as EventsQueryInput, sn as SyncThreadView, st as InboxAckBody, tn as SyncFindView, tt as EventsPage, un as ThreadSummaryView, ut as InboxNextQuery, vn as UploadTicket, vt as MatchesQuery, wt as ParsedFilters, xt as MessagesPage, yn as routes, yt as Me, z as ProfileOption, zt as SchedulesQuery } from "./provider-B0b9Qxw4.mjs";
4
- import { C as Protocol, S as Method, _ 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 HttpError, y as WritePatch } from "./backend-B-SJ7p6p.mjs";
5
- import "./exports-ZsgjzEnM.mjs";
3
+ import { $ as Discovery, $t as SyncFindInput, At as Ref, B as ReplyOptions, Bt as SchedulesQueryInput, Ct as MessagesQueryInput, Dt as PostInput, Et as PostBody, Ft as ScheduleBody, G as Written, Gt as SourceReadQuery, H as ScheduleTiming, Ht as SignInQuery, I as BoardError, It as ScheduleBodyOutput, J as ActorView, Jt as SyncBody, K as duration, Kt as SyncAccountBody, L as Filters, Lt as ScheduleInput, Mt as RegistrationView, Nt as ResolvedSyncTarget, Ot as REGISTERED_WATCHERS_MAX, Pt as RouteName, Q as DeleteBody, Qt as SyncCopiesView, R as PostOptions, Rt as ScheduleView, St as MessagesQuery, Tt as PersonView, U as UploadInput, Ut as SourceMessage, V as ScheduleOptions, Vt as SchedulesQueryOutput, W as WaitOptions, Wt as SourceRead, X as BoardEventView, Xt as SyncCodeView, Y as ApiError, Yt as SyncCodeBody, Z as ChannelView, Zt as SyncCopiesQuery, _n as UploadStartBody, _t as MatchView, an as SyncReceiptInput, at as IdentityUnlinkBody, 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, gn as TypingResult, gt as LinkedQuery, hn as TypingBody, ht as LinkedAccount, in as SyncReceiptBody, it as FiltersInput, jt as RegisterBody, kt as ReactBody, ln as SyncedView, lt as InboxItem, mn as ThreadsQueryInput, mt as LinkView, nn as SyncImportBody, nt as EventsQuery, on as SyncRootsQuery, ot as IdentityView, pn as ThreadsQuery, pt as LabelBody, q as AckBody, qt as SyncAccountInput, rn as SyncImportInput, rt as EventsQueryInput, sn as SyncThreadView, st as InboxAckBody, tn as SyncFindView, tt as EventsPage, un as ThreadSummaryView, ut as InboxNextQuery, vn as UploadTicket, vt as MatchesQuery, wt as ParsedFilters, xt as MessagesPage, yn as routes, yt as Me, z as ProfileOption, zt as SchedulesQuery } from "./provider-B1Z3Ok86.mjs";
4
+ import { C as Protocol, S as Method, _ 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 HttpError, y as WritePatch } from "./backend-B5GCP1oK.mjs";
5
+ import "./exports-B3AFzT_f.mjs";
6
6
  //#region src/server/index.default.d.ts
7
7
  /**
8
8
  * The handle on a board. Outside Node there's no environment to find one in, so pass its
@@ -1,4 +1,4 @@
1
- import { t as createHttp } from "./backend-http-Ddzd5kTS.mjs";
1
+ import { t as createHttp } from "./backend-http-BMVDbtD_.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
1
  import { r as duration, t as BoardError } from "./client-BA-J7usH.mjs";
2
2
  import { t as HttpError } from "./errors-DX3hpwWt.mjs";
3
- import { t as http } from "./index.default-C5Cpz3sa.mjs";
4
- import { t as createBoard } from "./exports-CgjfMrQ-.mjs";
3
+ import { t as http } from "./index.default-Cm6GsLdj.mjs";
4
+ import { t as createBoard } from "./exports-CUGshfRF.mjs";
5
5
  import { t as DroppedError } from "./middleware-CqaCwucP.mjs";
6
6
  //#region src/server/index.default.ts
7
7
  /**
@@ -1,8 +1,8 @@
1
1
  import { a as CallerRequest, o as Claims } from "./auth-CU56Q2Xh.mjs";
2
2
  import { i as Attachment } from "./schemas-CWxs3ECJ.mjs";
3
- import { $ as Discovery, $t as SyncFindInput, At as Ref, B as ReplyOptions, Bt as SchedulesQueryInput, Ct as MessagesQueryInput, Dt as PostInput, Et as PostBody, Ft as ScheduleBody, G as Written, Gt as SourceReadQuery, H as ScheduleTiming, Ht as SignInQuery, I as BoardError, It as ScheduleBodyOutput, J as ActorView, Jt as SyncBody, K as duration, Kt as SyncAccountBody, L as Filters, Lt as ScheduleInput, Mt as RegistrationView, Nt as ResolvedSyncTarget, Ot as REGISTERED_WATCHERS_MAX, Pt as RouteName, Q as DeleteBody, Qt as SyncCopiesView, R as PostOptions, Rt as ScheduleView, St as MessagesQuery, Tt as PersonView, U as UploadInput, Ut as SourceMessage, V as ScheduleOptions, Vt as SchedulesQueryOutput, W as WaitOptions, Wt as SourceRead, X as BoardEventView, Xt as SyncCodeView, Y as ApiError, Yt as SyncCodeBody, Z as ChannelView, Zt as SyncCopiesQuery, _n as UploadStartBody, _t as MatchView, an as SyncReceiptInput, at as IdentityUnlinkBody, 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, gn as TypingResult, gt as LinkedQuery, hn as TypingBody, ht as LinkedAccount, in as SyncReceiptBody, it as FiltersInput, jt as RegisterBody, kt as ReactBody, ln as SyncedView, lt as InboxItem, mn as ThreadsQueryInput, mt as LinkView, nn as SyncImportBody, nt as EventsQuery, on as SyncRootsQuery, ot as IdentityView, pn as ThreadsQuery, pt as LabelBody, q as AckBody, qt as SyncAccountInput, rn as SyncImportInput, rt as EventsQueryInput, sn as SyncThreadView, st as InboxAckBody, tn as SyncFindView, tt as EventsPage, un as ThreadSummaryView, ut as InboxNextQuery, vn as UploadTicket, vt as MatchesQuery, wt as ParsedFilters, xt as MessagesPage, yn as routes, yt as Me, z as ProfileOption, zt as SchedulesQuery } from "./provider-B0b9Qxw4.mjs";
4
- import { C as Protocol, S as Method, _ 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 HttpError, y as WritePatch } from "./backend-B-SJ7p6p.mjs";
5
- import "./exports-ZsgjzEnM.mjs";
3
+ import { $ as Discovery, $t as SyncFindInput, At as Ref, B as ReplyOptions, Bt as SchedulesQueryInput, Ct as MessagesQueryInput, Dt as PostInput, Et as PostBody, Ft as ScheduleBody, G as Written, Gt as SourceReadQuery, H as ScheduleTiming, Ht as SignInQuery, I as BoardError, It as ScheduleBodyOutput, J as ActorView, Jt as SyncBody, K as duration, Kt as SyncAccountBody, L as Filters, Lt as ScheduleInput, Mt as RegistrationView, Nt as ResolvedSyncTarget, Ot as REGISTERED_WATCHERS_MAX, Pt as RouteName, Q as DeleteBody, Qt as SyncCopiesView, R as PostOptions, Rt as ScheduleView, St as MessagesQuery, Tt as PersonView, U as UploadInput, Ut as SourceMessage, V as ScheduleOptions, Vt as SchedulesQueryOutput, W as WaitOptions, Wt as SourceRead, X as BoardEventView, Xt as SyncCodeView, Y as ApiError, Yt as SyncCodeBody, Z as ChannelView, Zt as SyncCopiesQuery, _n as UploadStartBody, _t as MatchView, an as SyncReceiptInput, at as IdentityUnlinkBody, 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, gn as TypingResult, gt as LinkedQuery, hn as TypingBody, ht as LinkedAccount, in as SyncReceiptBody, it as FiltersInput, jt as RegisterBody, kt as ReactBody, ln as SyncedView, lt as InboxItem, mn as ThreadsQueryInput, mt as LinkView, nn as SyncImportBody, nt as EventsQuery, on as SyncRootsQuery, ot as IdentityView, pn as ThreadsQuery, pt as LabelBody, q as AckBody, qt as SyncAccountInput, rn as SyncImportInput, rt as EventsQueryInput, sn as SyncThreadView, st as InboxAckBody, tn as SyncFindView, tt as EventsPage, un as ThreadSummaryView, ut as InboxNextQuery, vn as UploadTicket, vt as MatchesQuery, wt as ParsedFilters, xt as MessagesPage, yn as routes, yt as Me, z as ProfileOption, zt as SchedulesQuery } from "./provider-B1Z3Ok86.mjs";
4
+ import { C as Protocol, S as Method, _ 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 HttpError, y as WritePatch } from "./backend-B5GCP1oK.mjs";
5
+ import "./exports-B3AFzT_f.mjs";
6
6
  //#region src/server/index.d.ts
7
7
  /**
8
8
  * The handle on a board. With a `backend`, the board lives there: `a2()` keeps it in this
@@ -1,5 +1,5 @@
1
1
  import { r as duration, t as BoardError } from "./client-BA-J7usH.mjs";
2
2
  import { t as HttpError } from "./errors-DX3hpwWt.mjs";
3
3
  import { t as DroppedError } from "./middleware-CqaCwucP.mjs";
4
- import { t as board } from "./server-DPxBSogx.mjs";
4
+ import { t as board } from "./server-BujqWBP5.mjs";
5
5
  export { BoardError, DroppedError, HttpError, board, duration };