@vercel/board 0.16.0 → 0.18.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/README.md +27 -27
- package/dist/{a2-C41hD8SF.mjs → a2-ChRLRupa.mjs} +969 -25
- package/dist/access.d.mts +1 -1
- package/dist/access.mjs +3 -3
- package/dist/{actors-DjMeZpnU.mjs → actors-BOamkEuz.mjs} +3 -73
- package/dist/attempts-CLG840Oj.mjs +20 -0
- package/dist/{auth-COzc09sn.mjs → auth-Drg9FiC2.mjs} +225 -2
- package/dist/backend-B-SJ7p6p.d.mts +680 -0
- package/dist/backend-a2/contract.d.mts +873 -2
- package/dist/backend-a2/contract.mjs +2 -2
- package/dist/backend-a2/scheduler-local.d.mts +1 -1
- package/dist/backend-a2/scheduler-local.mjs +1 -1
- package/dist/backend-a2/scheduler-qstash.d.mts +78 -1
- package/dist/backend-a2/scheduler-qstash.mjs +521 -1
- package/dist/backend-a2/scheduler-vercel.d.mts +37 -1
- package/dist/backend-a2/scheduler-vercel.mjs +203 -1
- package/dist/backend-a2/store-memory.d.mts +2 -2
- package/dist/backend-a2/store-memory.mjs +3 -3
- package/dist/backend-a2/store-postgres.d.mts +1 -1
- package/dist/backend-a2/store-postgres.mjs +2850 -2
- package/dist/backend-a2/store-redis-http.d.mts +1 -1
- package/dist/backend-a2/store-redis-http.mjs +490 -2
- package/dist/backend-a2/store-redis.d.mts +10 -2
- package/dist/backend-a2/store-redis.mjs +51 -2
- package/dist/backend-a2/store-sqlite.d.mts +1 -1
- package/dist/backend-a2/store-sqlite.mjs +3 -3
- package/dist/backend-a2.d.mts +2706 -3
- package/dist/backend-a2.mjs +1 -1
- package/dist/{backend-http-Bid7RIpC.d.mts → backend-http-7cNb8nJI.d.mts} +10 -3
- package/dist/{backend-http-0roEmjl0.mjs → backend-http-CMlL3yq3.mjs} +2 -2
- package/dist/{backend-http-BVRno-ca.mjs → backend-http-Ddzd5kTS.mjs} +32 -3
- 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-XCnQlxq6.d.mts → chat-provider-DNJQyajS.d.mts} +16 -4
- package/dist/{chat-provider-jFSeyNmB.mjs → chat-provider-YHy-WbrB.mjs} +15 -9
- package/dist/cli.mjs +30 -10
- package/dist/{client-DGDPaN7U.mjs → client-BA-J7usH.mjs} +10 -2
- package/dist/{conformance-DRR23YNy.mjs → conformance-CT5EeHeC.mjs} +5 -2
- package/dist/conformance.d.mts +1 -1
- package/dist/conformance.mjs +1 -1
- package/dist/{engine-Bir5s8ts.mjs → engine-D0oBUBEK.mjs} +249 -163
- package/dist/events-CewD4kw7.mjs +75 -0
- package/dist/{exports-Dvc1o52I.mjs → exports-CgjfMrQ-.mjs} +35 -32
- package/dist/exports-ZsgjzEnM.d.mts +4 -0
- package/dist/external-BV_cUncs.mjs +151 -0
- package/dist/folds-CRYY14kc.d.mts +534 -0
- package/dist/{reducers-mUPFuFJV.mjs → folds-Dyjput8M.mjs} +6 -73
- package/dist/ids-C6AYo1bu.mjs +31 -0
- package/dist/index.d.mts +4 -4
- package/dist/{index.default-Bkg77Pxn.mjs → index.default-C5Cpz3sa.mjs} +1 -1
- package/dist/index.mjs +5 -5
- package/dist/index.node.d.mts +4 -4
- package/dist/index.node.mjs +3 -3
- package/dist/inspect/app.js +31 -31
- package/dist/inspect/engine.js +5 -5
- package/dist/internal-CvsrprnU-CRY89I-b.mjs +203 -0
- package/dist/{local-Cr_dSjvs.mjs → local-DhkrAqPP.mjs} +1 -1
- package/dist/mentions-CB6ShY4R.mjs +72 -0
- package/dist/{targets-yM5JBUgr.mjs → middleware-CqaCwucP.mjs} +1 -33
- package/dist/platform-B4TnJtWu-CwcTyKg2.mjs +32 -0
- package/dist/{project-CFiArraX.mjs → project-DMDST9kl.mjs} +1 -1
- package/dist/{provider-BGjz-mDa.d.mts → provider-B0b9Qxw4.d.mts} +170 -40
- package/dist/provider-B3DpYSDq.mjs +220 -0
- package/dist/redis-tables-DN9PfIDi.mjs +3897 -0
- package/dist/reducer-CYPHsSmd-BQIfozGi.d.mts +170 -0
- package/dist/reducer-CjmvZSAb-D_OSISmj.mjs +74 -0
- package/dist/{contract-CuJmlrhw.mjs → reducers-BFmIXilu.mjs} +260 -287
- package/dist/retryable-lazy-DZWmHpii-Bye-W8q7.mjs +19 -0
- package/dist/scheduler-task-X1SToDjP-BVoWuInI.mjs +69 -0
- package/dist/{serve-BsOOc-6_.mjs → serve-BGYw0uNL.mjs} +5 -5
- package/dist/{server-jwPY7_wi.mjs → server-DPxBSogx.mjs} +4 -4
- package/dist/server-DtFrWxi3-Boa6gdWO.mjs +3602 -0
- package/dist/server-qgez0Xny-D5mLfqFQ.d.mts +385 -0
- package/dist/{serving-BNbzc96L.mjs → serving-CNkgUPW1.mjs} +1 -1
- package/dist/session-clear-wHd5GCpZ-BnKSxReK.mjs +51 -0
- package/dist/{sql-DbiEnqM0.mjs → sql-U9WTxrwJ.mjs} +2 -2
- package/dist/{sqlite-db-Cbj03vmp.mjs → sqlite-db-Ds4majtT.mjs} +1 -1
- package/dist/store-D0MhBvg6-4Z2L0Kqd.d.mts +530 -0
- package/dist/store-N8PXxDAS-BWBgzBHp.mjs +5 -0
- package/dist/store-codec-DwrWlgQx-CYxGW0aD.mjs +61 -0
- package/dist/store-memory-Bas9Uq5C.mjs +872 -0
- package/dist/store-memory-ZLiZbH3R.mjs +2 -0
- package/dist/store-polling-BZt2Rf3D-BiEM6_HB.mjs +148 -0
- package/dist/store-presence-polling-JuAy2Ck2-CYpuK75X.mjs +109 -0
- package/dist/store-sqlite-BmwL3pUR.mjs +2 -0
- package/dist/store-sqlite-CxxPCcCT.mjs +1053 -0
- package/dist/stream-buffer-DrTyiIuo-BKy-vI14.mjs +53 -0
- package/dist/sync-github.d.mts +24 -17
- package/dist/sync-github.mjs +89 -22
- package/dist/sync-kapso.d.mts +15 -13
- package/dist/sync-kapso.mjs +17 -14
- package/dist/sync-linear.d.mts +219 -0
- package/dist/sync-linear.mjs +311 -0
- package/dist/sync-slack.d.mts +12 -17
- package/dist/sync-slack.mjs +54 -42
- package/dist/sync.chat.d.mts +1 -1
- package/dist/sync.chat.mjs +1 -1
- package/dist/sync.d.mts +3 -3
- package/dist/sync.mjs +5 -4
- package/dist/targets-C-wdR5wP.mjs +33 -0
- package/dist/{tokens-DKLn5P9B.mjs → tokens-DhuzKzAl.mjs} +1 -1
- package/dist/{types-BHCjTZdB.d.mts → types-8yq2VnKI.d.mts} +2 -2
- package/dist/{users-Dls7_ANV.mjs → users-yryHKFMo.mjs} +2 -2
- package/dist/{watch-spec-W0c7ypHJ.mjs → watchers-hash-RLtnSF-t.mjs} +233 -28
- package/docs/01-board/01-mounting.md +1 -1
- package/docs/02-sync/01-overview.md +72 -48
- package/docs/02-sync/02-functions.md +398 -0
- package/docs/02-sync/03-slack.md +30 -25
- package/docs/02-sync/04-github.md +62 -26
- package/docs/02-sync/05-whatsapp.md +23 -19
- package/docs/02-sync/06-providers.md +60 -27
- package/docs/02-sync/07-linear.md +129 -0
- package/docs/03-guides/04-triage.md +1 -1
- package/docs/04-reference/01-cli.md +26 -6
- package/docs/04-reference/02-board.md +18 -5
- package/docs/04-reference/03-http.md +5 -2
- package/docs/04-reference/05-conformance.md +1 -1
- package/docs/index.md +5 -10
- package/package.json +31 -6
- package/dist/a2-D24cvwpN.d.mts +0 -3359
- package/dist/access-CJU2-vaa.mjs +0 -226
- package/dist/attempts-Bxp9KgPf.mjs +0 -919
- package/dist/exports-DLiE7fLY.d.mts +0 -4
- package/dist/provider-CuewcDNZ.mjs +0 -205
- package/dist/redis-tables-B35vv4CZ.mjs +0 -842
- package/dist/reducers-B_vUGQRp.d.mts +0 -1172
- package/dist/watchers-hash-CLjsa6od.mjs +0 -225
- package/docs/02-sync/02-callbacks.md +0 -265
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { d as LabelKey, p as Labels } from "./schemas-Be08nyfW.mjs";
|
|
2
|
+
import { n as ExternalEvent, r as ExternalLocation } from "./external-BV_cUncs.mjs";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
//#region src/sync/events.ts
|
|
5
|
+
const EventBase = z.object({
|
|
6
|
+
id: z.string().min(1),
|
|
7
|
+
platform: z.string().min(1),
|
|
8
|
+
key: z.string().min(1),
|
|
9
|
+
...ExternalLocation.shape,
|
|
10
|
+
raw: z.unknown()
|
|
11
|
+
});
|
|
12
|
+
const eventTypes = {
|
|
13
|
+
message: "message",
|
|
14
|
+
edited: "message.edited",
|
|
15
|
+
deleted: "message.deleted",
|
|
16
|
+
reaction: "reaction.added",
|
|
17
|
+
lifecycle: "lifecycle"
|
|
18
|
+
};
|
|
19
|
+
function envelope(platform, key, event, raw, id) {
|
|
20
|
+
return {
|
|
21
|
+
...event,
|
|
22
|
+
...EventBase.parse({
|
|
23
|
+
id,
|
|
24
|
+
platform,
|
|
25
|
+
key,
|
|
26
|
+
channel: event.channel,
|
|
27
|
+
thread: event.thread,
|
|
28
|
+
subthread: "subthread" in event ? event.subthread : null,
|
|
29
|
+
raw
|
|
30
|
+
}),
|
|
31
|
+
type: event.kind === "lifecycle" ? event.type : event.kind === "reaction" && !event.added ? "reaction.removed" : eventTypes[event.kind]
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function eventSchema(platform, messageType) {
|
|
35
|
+
const [message, edited, deleted, reaction, lifecycle] = ExternalEvent.options;
|
|
36
|
+
const common = {
|
|
37
|
+
...EventBase.shape,
|
|
38
|
+
platform: z.literal(platform)
|
|
39
|
+
};
|
|
40
|
+
return z.discriminatedUnion("kind", [
|
|
41
|
+
message.extend({
|
|
42
|
+
...common,
|
|
43
|
+
type: z.literal(messageType)
|
|
44
|
+
}),
|
|
45
|
+
edited.extend({
|
|
46
|
+
...common,
|
|
47
|
+
type: z.literal("message.edited")
|
|
48
|
+
}),
|
|
49
|
+
deleted.extend({
|
|
50
|
+
...common,
|
|
51
|
+
type: z.literal("message.deleted")
|
|
52
|
+
}),
|
|
53
|
+
reaction.extend({
|
|
54
|
+
...common,
|
|
55
|
+
type: z.enum(["reaction.added", "reaction.removed"])
|
|
56
|
+
}),
|
|
57
|
+
lifecycle.extend(common)
|
|
58
|
+
]);
|
|
59
|
+
}
|
|
60
|
+
const SyncOverrides = z.object({
|
|
61
|
+
root: z.custom((value) => typeof value === "function").optional(),
|
|
62
|
+
text: z.string().optional(),
|
|
63
|
+
labels: Labels.optional(),
|
|
64
|
+
unset: z.array(LabelKey).optional(),
|
|
65
|
+
noSync: z.boolean().optional()
|
|
66
|
+
}).strict();
|
|
67
|
+
function immutable(value) {
|
|
68
|
+
if (value && typeof value === "object") {
|
|
69
|
+
for (const item of Object.values(value)) immutable(item);
|
|
70
|
+
Object.freeze(value);
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { immutable as a, eventSchema as i, SyncOverrides as n, envelope as r, EventBase as t };
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { t as ATTACHMENT_BYTES_MAX } from "./files-SniSmeEr.mjs";
|
|
2
2
|
import "./schemas-Be08nyfW.mjs";
|
|
3
|
-
import { A as
|
|
4
|
-
import { n as createBoardClient, t as BoardError } from "./client-
|
|
5
|
-
import {
|
|
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";
|
|
4
|
+
import { n as createBoardClient, t as BoardError } from "./client-BA-J7usH.mjs";
|
|
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";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { d as issuersOf, g as verifiesTokens, l as hostPrincipal, o as createAuthority, s as fromTokens } from "./auth-Drg9FiC2.mjs";
|
|
8
|
+
import { i as resolveActor, r as claimsOf, t as KNOWN_AGENTS } from "./actors-BOamkEuz.mjs";
|
|
9
|
+
import { r as runMiddleware, t as DroppedError } from "./middleware-CqaCwucP.mjs";
|
|
10
|
+
import { n as syncTarget, r as withEmailMentions, t as mentionsEmails } from "./targets-C-wdR5wP.mjs";
|
|
10
11
|
//#region src/server/policy.ts
|
|
11
12
|
/**
|
|
12
13
|
* The policy a board's options say: verifying requests, and `access` over them and over the
|
|
@@ -214,6 +215,7 @@ async function inputOf(name, request, url, params) {
|
|
|
214
215
|
case "watchers": return {};
|
|
215
216
|
case "message":
|
|
216
217
|
case "posted":
|
|
218
|
+
case "synced":
|
|
217
219
|
case "delete":
|
|
218
220
|
case "cancelSchedule":
|
|
219
221
|
case "attachment":
|
|
@@ -296,6 +298,7 @@ async function inputOf(name, request, url, params) {
|
|
|
296
298
|
key,
|
|
297
299
|
...SourceReadQuery.parse(queryObject(url))
|
|
298
300
|
};
|
|
301
|
+
case "linked": return LinkedQuery.parse(queryObject(url));
|
|
299
302
|
}
|
|
300
303
|
}
|
|
301
304
|
/** A platform user, as the middleware is told who's writing. */
|
|
@@ -643,9 +646,8 @@ async function createServe(context) {
|
|
|
643
646
|
}
|
|
644
647
|
}
|
|
645
648
|
/**
|
|
646
|
-
* A post, with the emails it mentions resolved
|
|
647
|
-
*
|
|
648
|
-
* middleware.
|
|
649
|
+
* A post, with the emails it mentions resolved once the caller may post there (and sync, for
|
|
650
|
+
* targets it names), through the middleware.
|
|
649
651
|
*/
|
|
650
652
|
async function post(caller, input, signal) {
|
|
651
653
|
const send = (poster, body) => screenPost(poster, body, signal, (patched) => backend.post(poster, patched, signal));
|
|
@@ -653,26 +655,15 @@ async function createServe(context) {
|
|
|
653
655
|
...input,
|
|
654
656
|
sync: await Promise.all(input.sync.map(resolveTarget))
|
|
655
657
|
} : input);
|
|
656
|
-
const allowed = (input.sync?.length ?? 0) > 0 || mentionsEmails(input.text)
|
|
658
|
+
const allowed = (input.sync?.length ?? 0) > 0 || mentionsEmails(input.text) ? await mayPost(caller, input, signal) : null;
|
|
657
659
|
if (!allowed) return send(caller, input);
|
|
658
660
|
const { access, channel } = allowed;
|
|
659
661
|
const body = input.re === void 0 && (input.sync?.length ?? 0) > 0 && (access === null || access.can("sync", channel)) ? {
|
|
660
662
|
...input,
|
|
661
663
|
sync: await Promise.all(input.sync.map(resolveTarget))
|
|
662
664
|
} : input;
|
|
663
|
-
let poster = caller;
|
|
664
|
-
if (body.channel !== void 0 && !body.noSync) {
|
|
665
|
-
const config = await engine.configTargets(body.channel);
|
|
666
|
-
if (config.length > 0) poster = {
|
|
667
|
-
...caller,
|
|
668
|
-
origin: {
|
|
669
|
-
...caller.origin,
|
|
670
|
-
config
|
|
671
|
-
}
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
665
|
const text = await withEmails(body.text);
|
|
675
|
-
const answer = await send(
|
|
666
|
+
const answer = await send(caller, {
|
|
676
667
|
...body,
|
|
677
668
|
text
|
|
678
669
|
});
|
|
@@ -757,8 +748,7 @@ async function createServe(context) {
|
|
|
757
748
|
}
|
|
758
749
|
/** A sync callback's post, as an import by its platform user: once per client ID, which the backend sees to. */
|
|
759
750
|
async function importPost(caller, input, signal) {
|
|
760
|
-
const
|
|
761
|
-
const me = sync.caller;
|
|
751
|
+
const me = engine.caller;
|
|
762
752
|
const { author, key } = caller.platform;
|
|
763
753
|
const { via } = caller.origin;
|
|
764
754
|
const read = async (id) => {
|
|
@@ -771,14 +761,7 @@ async function createServe(context) {
|
|
|
771
761
|
if (found !== null) return { message: await read(found) };
|
|
772
762
|
}
|
|
773
763
|
const targets = await Promise.all((input.sync ?? []).map(resolveTarget));
|
|
774
|
-
const
|
|
775
|
-
const message = await landed(caller, await syncImport(config.length > 0 ? {
|
|
776
|
-
...me,
|
|
777
|
-
origin: {
|
|
778
|
-
...me.origin,
|
|
779
|
-
config
|
|
780
|
-
}
|
|
781
|
-
} : me, {
|
|
764
|
+
const message = await landed(caller, await syncImport(me, {
|
|
782
765
|
op: "post",
|
|
783
766
|
author,
|
|
784
767
|
from: null,
|
|
@@ -1237,6 +1220,26 @@ function createBoard(options, runtime) {
|
|
|
1237
1220
|
pull: async () => ownEntries.length > 0 ? (await server()).pull() : 0,
|
|
1238
1221
|
sync: ownEntries,
|
|
1239
1222
|
readSource: async (key, target, limit) => (await server()).readSource(key, target, limit),
|
|
1223
|
+
locate: (url) => {
|
|
1224
|
+
const read = /* @__PURE__ */ new Map();
|
|
1225
|
+
for (const entry of Object.values(entries)) {
|
|
1226
|
+
const found = entry.locate(url);
|
|
1227
|
+
if (found) read.set(JSON.stringify([
|
|
1228
|
+
entry.platform,
|
|
1229
|
+
found.thread,
|
|
1230
|
+
found.message
|
|
1231
|
+
]), {
|
|
1232
|
+
platform: entry.platform,
|
|
1233
|
+
...found
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
return [...read.values()];
|
|
1237
|
+
},
|
|
1238
|
+
linkOf: async (key, thread, message) => {
|
|
1239
|
+
const entry = Object.hasOwn(entries, key) ? entries[key] : void 0;
|
|
1240
|
+
if (!entry || !thread.startsWith(`${key}:`)) return null;
|
|
1241
|
+
return entry.url(thread.slice(key.length + 1), message).catch(() => null);
|
|
1242
|
+
},
|
|
1240
1243
|
typing: async (messages, status) => (await server()).typing(messages, status),
|
|
1241
1244
|
accessNow: (principal) => policy.accessNow(principal),
|
|
1242
1245
|
project: () => policy.project(),
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { h as MessageId, m as MESSAGE_TEXT_MAX } from "./schemas-Be08nyfW.mjs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/contract/external.ts
|
|
4
|
+
/**
|
|
5
|
+
* An external thread or channel, prefixed with its sync key:
|
|
6
|
+
* `slack:C0123ABC` (a channel), `slack:C0123ABC:1727.1234` (a thread),
|
|
7
|
+
* `github:basehub-ai/www:12` (a PR thread).
|
|
8
|
+
*/
|
|
9
|
+
const ExternalTarget = z.string().min(3).max(300);
|
|
10
|
+
/** Where a message came from on a platform. `thread` is grouped (GitHub review threads fold into their PR). */
|
|
11
|
+
const Origin = z.object({
|
|
12
|
+
channel: ExternalTarget.optional(),
|
|
13
|
+
thread: ExternalTarget,
|
|
14
|
+
/** The exact platform thread, when it differs from `thread`. */
|
|
15
|
+
subthread: ExternalTarget.nullable(),
|
|
16
|
+
message: z.string().min(1).max(300),
|
|
17
|
+
context: z.string().max(300).nullable()
|
|
18
|
+
});
|
|
19
|
+
/** What a linked message said, as the board saw it when the link arrived. */
|
|
20
|
+
const LinkSnapshot = z.object({
|
|
21
|
+
author: z.string().min(1).max(256),
|
|
22
|
+
text: z.string().max(MESSAGE_TEXT_MAX),
|
|
23
|
+
/** When it was written, if the platform said. */
|
|
24
|
+
createdAt: z.string().max(64).nullable(),
|
|
25
|
+
/** Its files, linked rather than copied. */
|
|
26
|
+
files: z.array(z.object({
|
|
27
|
+
name: z.string().min(1).max(1e3),
|
|
28
|
+
url: z.string().max(4e3).nullable()
|
|
29
|
+
})).max(20)
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* A platform message linked from a message (a Slack permalink): the board message
|
|
33
|
+
* it was synced to, if any, and a snapshot of it, or why the board couldn't read it.
|
|
34
|
+
*/
|
|
35
|
+
const Link = z.object({
|
|
36
|
+
url: z.string().min(1).max(4e3),
|
|
37
|
+
message: MessageId.nullable(),
|
|
38
|
+
snapshot: LinkSnapshot.nullable(),
|
|
39
|
+
/** Why there's no snapshot: `not_in_channel`, `channel_not_found`. */
|
|
40
|
+
unavailable: z.string().max(300).nullable()
|
|
41
|
+
});
|
|
42
|
+
const ExternalAuthor = z.object({
|
|
43
|
+
/** The platform principal, e.g. `slack:user:U0456`. */
|
|
44
|
+
id: z.string().min(1),
|
|
45
|
+
handle: z.string().min(1),
|
|
46
|
+
name: z.string().min(1),
|
|
47
|
+
avatar: z.string().nullable(),
|
|
48
|
+
isBot: z.boolean()
|
|
49
|
+
});
|
|
50
|
+
/**
|
|
51
|
+
* A file on a platform message, as Chat SDK describes it: enough for the
|
|
52
|
+
* platform's adapter to fetch it later (`rehydrateAttachment`), never the bytes.
|
|
53
|
+
*/
|
|
54
|
+
const ExternalFile = z.object({
|
|
55
|
+
type: z.enum([
|
|
56
|
+
"image",
|
|
57
|
+
"file",
|
|
58
|
+
"video",
|
|
59
|
+
"audio"
|
|
60
|
+
]),
|
|
61
|
+
name: z.string().max(1e3).optional(),
|
|
62
|
+
mimeType: z.string().max(255).optional(),
|
|
63
|
+
size: z.number().int().nonnegative().optional(),
|
|
64
|
+
url: z.string().max(4e3).optional(),
|
|
65
|
+
fetchMetadata: z.record(z.string(), z.string().max(4e3)).optional()
|
|
66
|
+
});
|
|
67
|
+
/**
|
|
68
|
+
* A platform message a platform message links to, at intake: where it lives
|
|
69
|
+
* (`thread`, with the sync key prefix, and `message`), and what it said when the
|
|
70
|
+
* platform sent that along (Slack's unfurl).
|
|
71
|
+
*/
|
|
72
|
+
const ExternalLink = z.object({
|
|
73
|
+
url: z.string().min(1).max(4e3),
|
|
74
|
+
thread: ExternalTarget,
|
|
75
|
+
message: z.string().min(1).max(300),
|
|
76
|
+
snapshot: LinkSnapshot.nullable()
|
|
77
|
+
});
|
|
78
|
+
const ExternalLocation = z.object({
|
|
79
|
+
channel: ExternalTarget,
|
|
80
|
+
thread: ExternalTarget,
|
|
81
|
+
subthread: ExternalTarget.nullable()
|
|
82
|
+
});
|
|
83
|
+
/** A platform event, normalized at intake. Thread IDs are external IDs with the sync key prefix. */
|
|
84
|
+
const ExternalEvent = z.discriminatedUnion("kind", [
|
|
85
|
+
z.object({
|
|
86
|
+
kind: z.literal("message"),
|
|
87
|
+
...ExternalLocation.shape,
|
|
88
|
+
message: z.string(),
|
|
89
|
+
isRoot: z.boolean(),
|
|
90
|
+
text: z.string(),
|
|
91
|
+
context: z.string().nullable(),
|
|
92
|
+
author: ExternalAuthor,
|
|
93
|
+
/** Files on the message, fetched when it lands on the board. Older events have none. */
|
|
94
|
+
files: z.array(ExternalFile).max(20).optional(),
|
|
95
|
+
/** Platform messages it links to. Older events have none. */
|
|
96
|
+
links: z.array(ExternalLink).max(10).optional()
|
|
97
|
+
}),
|
|
98
|
+
z.object({
|
|
99
|
+
kind: z.literal("edited"),
|
|
100
|
+
...ExternalLocation.shape,
|
|
101
|
+
message: z.string(),
|
|
102
|
+
text: z.string(),
|
|
103
|
+
author: ExternalAuthor,
|
|
104
|
+
links: z.array(ExternalLink).max(10).optional(),
|
|
105
|
+
/** Only the links changed (the platform sent what they say), so `text` isn't news. */
|
|
106
|
+
linksOnly: z.boolean().optional()
|
|
107
|
+
}),
|
|
108
|
+
z.object({
|
|
109
|
+
kind: z.literal("deleted"),
|
|
110
|
+
...ExternalLocation.shape,
|
|
111
|
+
message: z.string()
|
|
112
|
+
}),
|
|
113
|
+
z.object({
|
|
114
|
+
kind: z.literal("reaction"),
|
|
115
|
+
...ExternalLocation.shape,
|
|
116
|
+
message: z.string(),
|
|
117
|
+
emoji: z.string(),
|
|
118
|
+
added: z.boolean(),
|
|
119
|
+
author: ExternalAuthor
|
|
120
|
+
}),
|
|
121
|
+
z.object({
|
|
122
|
+
/** A platform lifecycle event: a PR opening, closing, being reviewed. */
|
|
123
|
+
kind: z.literal("lifecycle"),
|
|
124
|
+
...ExternalLocation.omit({ subthread: true }).shape,
|
|
125
|
+
type: z.string(),
|
|
126
|
+
title: z.string(),
|
|
127
|
+
url: z.string().nullable(),
|
|
128
|
+
labels: z.record(z.string(), z.string()),
|
|
129
|
+
unset: z.array(z.string()),
|
|
130
|
+
/** Key prefixes the platform owns on the root, e.g. `tag.`: keys under them that aren't in `labels` are unset. */
|
|
131
|
+
owns: z.array(z.string()).optional(),
|
|
132
|
+
/** A reply to add to the thread, e.g. "merged by julian". */
|
|
133
|
+
reply: z.string().nullable(),
|
|
134
|
+
/** Create the root if the board hasn't seen this thread. */
|
|
135
|
+
createRoot: z.boolean(),
|
|
136
|
+
/** The thread's title changed on the platform: a root that came from it says the new one. */
|
|
137
|
+
retitled: z.boolean().optional(),
|
|
138
|
+
author: ExternalAuthor
|
|
139
|
+
})
|
|
140
|
+
]);
|
|
141
|
+
/** `slack:C0123ABC:1727.1234` is a thread; `slack:C0123ABC` and `github:owner/repo` are channels. */
|
|
142
|
+
const isThreadTarget = (target) => target.slice(target.indexOf(":") + 1).includes(":");
|
|
143
|
+
/** `slack:C0123ABC:1727.1` → `slack:C0123ABC`; `github:o/r:12` → `github:o/r`. */
|
|
144
|
+
const channelOf = (externalThread) => {
|
|
145
|
+
const at = externalThread.indexOf(":");
|
|
146
|
+
const colon = externalThread.slice(at + 1).indexOf(":");
|
|
147
|
+
return colon === -1 ? externalThread : externalThread.slice(0, at + 1 + colon);
|
|
148
|
+
};
|
|
149
|
+
const externalMessageKey = (thread, message) => `${thread}|${message}`;
|
|
150
|
+
//#endregion
|
|
151
|
+
export { Link as a, externalMessageKey as c, ExternalTarget as i, isThreadTarget as l, ExternalEvent as n, Origin as o, ExternalLocation as r, channelOf as s, ExternalAuthor as t };
|