@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.
- package/dist/{a2-ChRLRupa.mjs → a2-C1obi0Qb.mjs} +2 -1
- package/dist/{backend-B-SJ7p6p.d.mts → backend-B5GCP1oK.d.mts} +1 -1
- package/dist/backend-a2.d.mts +4 -2
- package/dist/backend-a2.mjs +1 -1
- package/dist/{backend-http-Ddzd5kTS.mjs → backend-http-BMVDbtD_.mjs} +19 -4
- package/dist/{backend-http-7cNb8nJI.d.mts → backend-http-Ca6b0FPA.d.mts} +4 -2
- package/dist/{backend-http-CMlL3yq3.mjs → backend-http-DMpQ750P.mjs} +10 -8
- package/dist/backend-http.d.mts +1 -1
- package/dist/backend-http.mjs +1 -1
- package/dist/backend-http.node.d.mts +1 -1
- package/dist/backend-http.node.mjs +1 -1
- package/dist/{chat-provider-DNJQyajS.d.mts → chat-provider-B5uCc0an.d.mts} +1 -1
- package/dist/cli.mjs +6 -5
- package/dist/{conformance-CT5EeHeC.mjs → conformance-D0Z4dXC_.mjs} +46 -5
- package/dist/conformance.d.mts +1 -1
- package/dist/conformance.mjs +1 -1
- package/dist/exports-B3AFzT_f.d.mts +4 -0
- package/dist/{exports-CgjfMrQ-.mjs → exports-CUGshfRF.mjs} +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/{index.default-C5Cpz3sa.mjs → index.default-Cm6GsLdj.mjs} +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.node.d.mts +3 -3
- package/dist/index.node.mjs +1 -1
- package/dist/inspect/app.js +2 -2
- package/dist/{provider-B0b9Qxw4.d.mts → provider-B1Z3Ok86.d.mts} +16 -1
- package/dist/{serve-BGYw0uNL.mjs → serve-DB9ijyQ7.mjs} +3 -3
- package/dist/{server-DPxBSogx.mjs → server-BujqWBP5.mjs} +2 -2
- package/dist/sync-github.d.mts +2 -2
- package/dist/sync-kapso.d.mts +2 -2
- package/dist/sync-linear.d.mts +2 -2
- package/dist/sync-slack.d.mts +2 -2
- package/dist/sync.chat.d.mts +1 -1
- package/dist/sync.d.mts +1 -1
- package/dist/{watchers-hash-RLtnSF-t.mjs → watchers-hash-B2TevhFZ.mjs} +20 -2
- package/docs/01-board/06-watchers.md +7 -0
- package/docs/03-guides/01-coding-agents.md +2 -1
- package/docs/04-reference/01-cli.md +2 -1
- package/docs/04-reference/03-http.md +4 -2
- package/package.json +1 -1
- package/dist/exports-ZsgjzEnM.d.mts +0 -4
package/dist/sync-slack.d.mts
CHANGED
|
@@ -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-
|
|
3
|
-
import { n as ChatSyncProvider, t as ChatInput } from "./chat-provider-
|
|
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
|
package/dist/sync.chat.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as externalAuthor, n as ChatSyncProvider, r as ChatTransport, t as ChatInput } from "./chat-provider-
|
|
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-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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),
|
|
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