@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,6 +1,6 @@
1
1
  import { f as ExternalLink } from "./folds-CRYY14kc.mjs";
2
- import { C as channels, _ as withUserTokens, d as ResolvedTarget, f as SyncContext, h as UserTokens, o as ProviderPost, r as ProviderDelivery, y as SyncFn } from "./provider-B0b9Qxw4.mjs";
3
- import { n as ChatSyncProvider, t as ChatInput } from "./chat-provider-DNJQyajS.mjs";
2
+ import { C as channels, _ as withUserTokens, d as ResolvedTarget, f as SyncContext, h as UserTokens, o as ProviderPost, r as ProviderDelivery, y as SyncFn } from "./provider-B1Z3Ok86.mjs";
3
+ import { n as ChatSyncProvider, t as ChatInput } from "./chat-provider-B5uCc0an.mjs";
4
4
  import { z } from "zod";
5
5
  import { Adapter } from "chat";
6
6
  //#region src/sync/slack.d.ts
@@ -1,2 +1,2 @@
1
- import { i as externalAuthor, n as ChatSyncProvider, r as ChatTransport, t as ChatInput } from "./chat-provider-DNJQyajS.mjs";
1
+ import { i as externalAuthor, n as ChatSyncProvider, r as ChatTransport, t as ChatInput } from "./chat-provider-B5uCc0an.mjs";
2
2
  export { ChatInput, ChatSyncProvider, ChatTransport, externalAuthor };
package/dist/sync.d.mts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { d as ExternalFile, l as ExternalAuthor, u as ExternalEvent } from "./folds-CRYY14kc.mjs";
2
- import { A as SyncOverrides, C as channels, D as EventBase, E as render, M as SyncRoot, N as envelope, O as ProviderEvent, P as eventSchema, S as SyncUnit, T as mirror, _ as withUserTokens, a as ProviderOptions, b as SyncIn, c as ReceivedEvent, d as ResolvedTarget, f as SyncContext, g as connectUserTokens, h as UserTokens, i as ProviderFile, j as SyncResult, k as RootOverride, l as RenderInput, m as UnreadableError, n as FileDelivery, o as ProviderPost, p as SyncProvider, r as ProviderDelivery, s as Receive, u as Renderer, v as Sync, w as into, x as SyncOut, y as SyncFn } from "./provider-B0b9Qxw4.mjs";
2
+ import { A as SyncOverrides, C as channels, D as EventBase, E as render, M as SyncRoot, N as envelope, O as ProviderEvent, P as eventSchema, S as SyncUnit, T as mirror, _ as withUserTokens, a as ProviderOptions, b as SyncIn, c as ReceivedEvent, d as ResolvedTarget, f as SyncContext, g as connectUserTokens, h as UserTokens, i as ProviderFile, j as SyncResult, k as RootOverride, l as RenderInput, m as UnreadableError, n as FileDelivery, o as ProviderPost, p as SyncProvider, r as ProviderDelivery, s as Receive, u as Renderer, v as Sync, w as into, x as SyncOut, y as SyncFn } from "./provider-B1Z3Ok86.mjs";
3
3
  export { EventBase, type ExternalAuthor, ExternalEvent, type ExternalFile, type FileDelivery, type ProviderDelivery, type ProviderEvent, type ProviderFile, type ProviderOptions, type ProviderPost, type Receive, ReceivedEvent, type RenderInput, type Renderer, ResolvedTarget, type RootOverride, type Sync, type SyncContext, type SyncFn, type SyncIn, type SyncOut, SyncOverrides, SyncProvider, type SyncResult, type SyncRoot, type SyncUnit, UnreadableError, type UserTokens, channels, connectUserTokens, envelope, eventSchema, into, mirror, render, withUserTokens };
@@ -116,8 +116,20 @@ const InboxQuery = z.object({
116
116
  });
117
117
  /** What a board on `http()` registers with the board it uses: its watchers' tails, and where to wake it. */
118
118
  const RegisterBody = z.object({
119
- /** Without one, the board pulls on its own instead of being woken. */
119
+ /** Without one, or a `path`, the board pulls on its own instead of being woken. */
120
120
  url: z.string().url().max(500).refine((url) => /^https:\/\//.test(url), "wake-ups go over https").transform((url) => url.replace(/\/+$/, "")).nullable().optional(),
121
+ /**
122
+ * Where to wake the caller on its own Vercel project, on a board whose discovery says
123
+ * `wake_by_path`. The board takes the project and environment from the caller's token and
124
+ * finds the deployment, so a project can only register itself, and anyone else gets 403
125
+ * `not_a_project`.
126
+ */
127
+ path: z.string().max(300).regex(/^\/[^?#]*$/, "a path starts with / and has no query or fragment").transform((path) => path.replace(/(.)\/+$/, "$1")).nullable().optional(),
128
+ /**
129
+ * With a `path`, from a preview: the branch whose latest ready preview is woken, since a
130
+ * token says which project and environment called but not which deployment. Production names none.
131
+ */
132
+ branch: z.string().min(1).max(255).nullable().optional(),
121
133
  watch: z.record(z.string().max(100), z.object({
122
134
  tail: z.string().max(1e3),
123
135
  when: z.enum(["matches", "enters"]).default("matches")
@@ -131,6 +143,12 @@ const RegisterBody = z.object({
131
143
  platform: z.string().min(1).max(64),
132
144
  signIn: z.boolean().default(false)
133
145
  })).max(32).optional()
146
+ }).refine((body) => !(body.url && body.path), {
147
+ message: "register a url or a path, not both",
148
+ path: ["path"]
149
+ }).refine((body) => !body.branch || body.path, {
150
+ message: "a branch goes with a path",
151
+ path: ["branch"]
134
152
  });
135
153
  /** A pull of the caller's matches: at most `limit`, waiting up to `wait` seconds for one. */
136
154
  const MatchesQuery = z.object({
@@ -631,7 +649,7 @@ const routes = {
631
649
  const SYNC = "@sync";
632
650
  //#endregion
633
651
  //#region src/version.ts
634
- const VERSION = "0.18.0";
652
+ const VERSION = "0.19.0";
635
653
  //#endregion
636
654
  //#region src/server/routing.ts
637
655
  /** board()'s own routes: the files it serves, and sync's, which run where board() does. */
@@ -236,6 +236,13 @@ export const GET = team.fetch, POST = team.fetch
236
236
  makes this board pull with its own credentials. One that Deployment
237
237
  Protection stops answers 401, and the other board tries less often until
238
238
  this board pulls or registers.
239
+ - **On a board whose discovery says `wake_by_path`,** this board registers
240
+ its mount path on its own project instead of its URL, and its wake-ups get
241
+ through Deployment Protection with nothing to set up. Only on Vercel, from
242
+ production or a preview of a branch, which it names: the other board finds
243
+ the deployment from this project's token and that branch. Anywhere else
244
+ (development, a custom environment, a preview made with `vercel deploy`), it
245
+ registers its URL.
239
246
  - **Retries need the other board's scheduler,** as its own watchers do.
240
247
  Without one there, nothing waits on a clock: a retry, a lease that ran out,
241
248
  and a wake-up that didn't get there (once its back-off is over) each wait
@@ -54,7 +54,8 @@ token expires in 11h 42m
54
54
 
55
55
  ### Boards behind Deployment Protection
56
56
 
57
- Many teams protect production with Vercel Authentication. The CLI and
57
+ Many teams protect production with Vercel Authentication or Vercel Connect
58
+ passport. The CLI and
58
59
  `board()` in Node get through the way `vercel curl` does: with your
59
60
  `vercel login`, they find the board's project from its host, read its
60
61
  Protection Bypass for Automation (creating one if the project has none, and
@@ -533,11 +533,12 @@ core
533
533
 
534
534
  watchers
535
535
  passed matches a registered tail, hands the match to a pull, and takes its ack
536
+ passed wakes by path only where its discovery says so, and only a Vercel project
536
537
 
537
538
  sync
538
539
  passed keeps sync's records: an import as a platform author, its lookups by platform ID, a receipt, and where it's synced
539
540
 
540
- 18 passed
541
+ 19 passed
541
542
  ```
542
543
 
543
544
  A board that keeps no schedules or no files answers that check with a 501, and
@@ -94,7 +94,7 @@ a refused write it can read answers 403. `GET /me` returns the caller's
94
94
 
95
95
  | Method | Path | Purpose |
96
96
  | --- | --- | --- |
97
- | GET | `/` | Discovery (no token): `{ version, url, audience, team_id, project_id, sync }`, where `sync` lists each entry's `key` and `platform`. A board on `http()` with no `access` of its own has none: it answers only wake-ups, webhooks, and its sync's sign-ins |
97
+ | GET | `/` | Discovery (no token): `{ version, url, audience, team_id, project_id, sync, wake_by_path? }`, where `sync` lists each entry's `key` and `platform`, and `wake_by_path` says `POST /watchers` takes a `path`. A board on `http()` with no `access` of its own has none: it answers only wake-ups, webhooks, and its sync's sign-ins |
98
98
  | GET | `/me` | Who this token and profile write as: `{ actor, principal, kind, handle, name, avatar, profile, session, expiresAt, can }`, where `can` maps each channel pattern to its permissions |
99
99
  | GET | `/channels` | `{ channels, cursor }`: each `{ path, lastIndex, messages, threads }`, sorted by path |
100
100
  | GET | `/actors` | `{ actors, cursor }`: each `{ id, kind, handle, name, avatar }`, sorted by handle |
@@ -118,7 +118,7 @@ a refused write it can read answers 403. `GET /me` returns the caller's
118
118
  | GET | `/schedules` | `{ schedules }`. Query: `re`, `channel`, `by`, `all` |
119
119
  | POST | `/schedules/:id/cancel` | Cancel one (`12` or `s12`). Its creator, or `manage` in its channel. Answers `{ schedule, canceled }` |
120
120
  | GET | `/events` | The event feed. See [Event feed](#event-feed) |
121
- | POST | `/watchers` | `{ url?, watch, sync? }`: registers the caller's watchers (at most 32, each `{ tail, when? }`), its sync entries, and where to wake it (`https` only), replacing its last registration. Needs `read` somewhere, and `sync` somewhere for sync entries. Answers the registration, `{ owner, url, watch, sync, hash }`. On a board on `http()`, it takes no token and registers that board's own |
121
+ | POST | `/watchers` | `{ url?, path?, branch?, watch, sync? }`: registers the caller's watchers (at most 32, each `{ tail, when? }`), its sync entries, and where to wake it, replacing its last registration: a `url` (`https` only), or on a board whose discovery says `wake_by_path`, a `path` on the caller's own Vercel project, which the board finds from the caller's token and wakes through its Deployment Protection: production's best domain, or with `branch`, a preview's latest ready deployment of that branch (403 `not_a_project` for a caller that isn't a Vercel project, 501 `no_wake_by_path` on a board that can't). Needs `read` somewhere, and `sync` somewhere for sync entries. Answers the registration, `{ owner, url, path?, branch?, watch, sync, hash }`. On a board on `http()`, it takes no token and registers that board's own |
122
122
  | GET | `/watchers` | `{ watchers }`: the caller's registration, or every one for `manage` in every channel |
123
123
  | GET | `/watchers/matches` | `{ matches }`: the caller's matches it can run now, one thread at a time and in order, each `{ id, watcher, thread, lease, attempt, event }` and leased to it for 5 minutes. Query: `limit` (10 by default, at most 50), `wait` (seconds to hold for one, at most 25). 404 `no_registration` without a registration |
124
124
  | POST | `/watchers/matches/:id/ack` | `{ lease, error?, skip? }`: done, an error to try again after a wait (after 5 tries it's `watch.failed`), or `skip` to drop it. 404 `no_match`, 409 `stale_lease` for a lease that ran out |
@@ -451,6 +451,7 @@ Every failure is JSON with a matching status:
451
451
  | 401 | `unauthorized` | a missing, malformed, or expired token, an unknown issuer, or the wrong audience |
452
452
  | 403 | `forbidden` | a caller no rule in `access` matches, a write its access doesn't allow where it can read (the message names the permission and channel), an edit or delete by someone else, someone else's schedule without `manage`, or a source read without `manage` in every channel, or the inbox without `sync` in every channel, or a code for anyone but a person, or watchers or sync's records for a caller narrowed with `x-board-scope` |
453
453
  | 403 | `private_channel`, `shared_channel`, `not_in_channel` | a source read the board won't do |
454
+ | 403 | `not_a_project` | a `path` in `POST /watchers` from a caller that isn't a Vercel project, on a board that wakes by path: a path is on the caller's own project |
454
455
  | 404 | `not_found` | no such route, a request a board on `http()` with no `access` doesn't serve, or a webhook, sign-in, or code for a sync entry the board doesn't have |
455
456
  | 404 | `message_not_found`, `schedule_not_found`, `attachment_not_found`, `post_not_found` | no such thing, or one the caller can't read |
456
457
  | 404 | `thread_not_found` | `GET /sync/threads/:thread` for a thread that isn't there, or that the caller can't sync |
@@ -473,6 +474,7 @@ Every failure is JSON with a matching status:
473
474
  | 501 | `no_files` | an upload, a download, or a write that carries files, on a board that keeps no files: `` this board keeps no files: pass `blob` to `a2()` `` |
474
475
  | 501 | `no_direct_uploads` | a direct upload on a board whose blob store can't sign upload URLs |
475
476
  | 501 | `no_verifier` | a request to a board on board's default build, outside Node, with no `authenticate` |
477
+ | 501 | `no_wake_by_path` | a `path` in `POST /watchers` on a board that wakes only a `url`: `` this board wakes a url, not a path: register `url` instead `` |
476
478
  | 502 | `no_client`, `list_failed`, … | the platform failed a source read |
477
479
  | 502 | `unreachable`, `remote_error` | on a board on `http()`: the board it uses didn't answer, or answered with something that isn't its JSON error |
478
480
  | 503 | `unavailable` | the store is down, or isn't set up for this release: the message says what to apply, or, on Redis, to give the store a new `keyPrefix` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/board",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "A message board you mount in one route: one durable log, verified authors, live tail, and two-way sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,4 +0,0 @@
1
- import "./auth-CU56Q2Xh.mjs";
2
- import "./schemas-CWxs3ECJ.mjs";
3
- import "./provider-B0b9Qxw4.mjs";
4
- import "./backend-B-SJ7p6p.mjs";