@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.
Files changed (130) hide show
  1. package/README.md +27 -27
  2. package/dist/{a2-C41hD8SF.mjs → a2-ChRLRupa.mjs} +969 -25
  3. package/dist/access.d.mts +1 -1
  4. package/dist/access.mjs +3 -3
  5. package/dist/{actors-DjMeZpnU.mjs → actors-BOamkEuz.mjs} +3 -73
  6. package/dist/attempts-CLG840Oj.mjs +20 -0
  7. package/dist/{auth-COzc09sn.mjs → auth-Drg9FiC2.mjs} +225 -2
  8. package/dist/backend-B-SJ7p6p.d.mts +680 -0
  9. package/dist/backend-a2/contract.d.mts +873 -2
  10. package/dist/backend-a2/contract.mjs +2 -2
  11. package/dist/backend-a2/scheduler-local.d.mts +1 -1
  12. package/dist/backend-a2/scheduler-local.mjs +1 -1
  13. package/dist/backend-a2/scheduler-qstash.d.mts +78 -1
  14. package/dist/backend-a2/scheduler-qstash.mjs +521 -1
  15. package/dist/backend-a2/scheduler-vercel.d.mts +37 -1
  16. package/dist/backend-a2/scheduler-vercel.mjs +203 -1
  17. package/dist/backend-a2/store-memory.d.mts +2 -2
  18. package/dist/backend-a2/store-memory.mjs +3 -3
  19. package/dist/backend-a2/store-postgres.d.mts +1 -1
  20. package/dist/backend-a2/store-postgres.mjs +2850 -2
  21. package/dist/backend-a2/store-redis-http.d.mts +1 -1
  22. package/dist/backend-a2/store-redis-http.mjs +490 -2
  23. package/dist/backend-a2/store-redis.d.mts +10 -2
  24. package/dist/backend-a2/store-redis.mjs +51 -2
  25. package/dist/backend-a2/store-sqlite.d.mts +1 -1
  26. package/dist/backend-a2/store-sqlite.mjs +3 -3
  27. package/dist/backend-a2.d.mts +2706 -3
  28. package/dist/backend-a2.mjs +1 -1
  29. package/dist/{backend-http-Bid7RIpC.d.mts → backend-http-7cNb8nJI.d.mts} +10 -3
  30. package/dist/{backend-http-0roEmjl0.mjs → backend-http-CMlL3yq3.mjs} +2 -2
  31. package/dist/{backend-http-BVRno-ca.mjs → backend-http-Ddzd5kTS.mjs} +32 -3
  32. package/dist/backend-http.d.mts +1 -1
  33. package/dist/backend-http.mjs +1 -1
  34. package/dist/backend-http.node.d.mts +1 -1
  35. package/dist/backend-http.node.mjs +1 -1
  36. package/dist/{chat-provider-XCnQlxq6.d.mts → chat-provider-DNJQyajS.d.mts} +16 -4
  37. package/dist/{chat-provider-jFSeyNmB.mjs → chat-provider-YHy-WbrB.mjs} +15 -9
  38. package/dist/cli.mjs +30 -10
  39. package/dist/{client-DGDPaN7U.mjs → client-BA-J7usH.mjs} +10 -2
  40. package/dist/{conformance-DRR23YNy.mjs → conformance-CT5EeHeC.mjs} +5 -2
  41. package/dist/conformance.d.mts +1 -1
  42. package/dist/conformance.mjs +1 -1
  43. package/dist/{engine-Bir5s8ts.mjs → engine-D0oBUBEK.mjs} +249 -163
  44. package/dist/events-CewD4kw7.mjs +75 -0
  45. package/dist/{exports-Dvc1o52I.mjs → exports-CgjfMrQ-.mjs} +35 -32
  46. package/dist/exports-ZsgjzEnM.d.mts +4 -0
  47. package/dist/external-BV_cUncs.mjs +151 -0
  48. package/dist/folds-CRYY14kc.d.mts +534 -0
  49. package/dist/{reducers-mUPFuFJV.mjs → folds-Dyjput8M.mjs} +6 -73
  50. package/dist/ids-C6AYo1bu.mjs +31 -0
  51. package/dist/index.d.mts +4 -4
  52. package/dist/{index.default-Bkg77Pxn.mjs → index.default-C5Cpz3sa.mjs} +1 -1
  53. package/dist/index.mjs +5 -5
  54. package/dist/index.node.d.mts +4 -4
  55. package/dist/index.node.mjs +3 -3
  56. package/dist/inspect/app.js +31 -31
  57. package/dist/inspect/engine.js +5 -5
  58. package/dist/internal-CvsrprnU-CRY89I-b.mjs +203 -0
  59. package/dist/{local-Cr_dSjvs.mjs → local-DhkrAqPP.mjs} +1 -1
  60. package/dist/mentions-CB6ShY4R.mjs +72 -0
  61. package/dist/{targets-yM5JBUgr.mjs → middleware-CqaCwucP.mjs} +1 -33
  62. package/dist/platform-B4TnJtWu-CwcTyKg2.mjs +32 -0
  63. package/dist/{project-CFiArraX.mjs → project-DMDST9kl.mjs} +1 -1
  64. package/dist/{provider-BGjz-mDa.d.mts → provider-B0b9Qxw4.d.mts} +170 -40
  65. package/dist/provider-B3DpYSDq.mjs +220 -0
  66. package/dist/redis-tables-DN9PfIDi.mjs +3897 -0
  67. package/dist/reducer-CYPHsSmd-BQIfozGi.d.mts +170 -0
  68. package/dist/reducer-CjmvZSAb-D_OSISmj.mjs +74 -0
  69. package/dist/{contract-CuJmlrhw.mjs → reducers-BFmIXilu.mjs} +260 -287
  70. package/dist/retryable-lazy-DZWmHpii-Bye-W8q7.mjs +19 -0
  71. package/dist/scheduler-task-X1SToDjP-BVoWuInI.mjs +69 -0
  72. package/dist/{serve-BsOOc-6_.mjs → serve-BGYw0uNL.mjs} +5 -5
  73. package/dist/{server-jwPY7_wi.mjs → server-DPxBSogx.mjs} +4 -4
  74. package/dist/server-DtFrWxi3-Boa6gdWO.mjs +3602 -0
  75. package/dist/server-qgez0Xny-D5mLfqFQ.d.mts +385 -0
  76. package/dist/{serving-BNbzc96L.mjs → serving-CNkgUPW1.mjs} +1 -1
  77. package/dist/session-clear-wHd5GCpZ-BnKSxReK.mjs +51 -0
  78. package/dist/{sql-DbiEnqM0.mjs → sql-U9WTxrwJ.mjs} +2 -2
  79. package/dist/{sqlite-db-Cbj03vmp.mjs → sqlite-db-Ds4majtT.mjs} +1 -1
  80. package/dist/store-D0MhBvg6-4Z2L0Kqd.d.mts +530 -0
  81. package/dist/store-N8PXxDAS-BWBgzBHp.mjs +5 -0
  82. package/dist/store-codec-DwrWlgQx-CYxGW0aD.mjs +61 -0
  83. package/dist/store-memory-Bas9Uq5C.mjs +872 -0
  84. package/dist/store-memory-ZLiZbH3R.mjs +2 -0
  85. package/dist/store-polling-BZt2Rf3D-BiEM6_HB.mjs +148 -0
  86. package/dist/store-presence-polling-JuAy2Ck2-CYpuK75X.mjs +109 -0
  87. package/dist/store-sqlite-BmwL3pUR.mjs +2 -0
  88. package/dist/store-sqlite-CxxPCcCT.mjs +1053 -0
  89. package/dist/stream-buffer-DrTyiIuo-BKy-vI14.mjs +53 -0
  90. package/dist/sync-github.d.mts +24 -17
  91. package/dist/sync-github.mjs +89 -22
  92. package/dist/sync-kapso.d.mts +15 -13
  93. package/dist/sync-kapso.mjs +17 -14
  94. package/dist/sync-linear.d.mts +219 -0
  95. package/dist/sync-linear.mjs +311 -0
  96. package/dist/sync-slack.d.mts +12 -17
  97. package/dist/sync-slack.mjs +54 -42
  98. package/dist/sync.chat.d.mts +1 -1
  99. package/dist/sync.chat.mjs +1 -1
  100. package/dist/sync.d.mts +3 -3
  101. package/dist/sync.mjs +5 -4
  102. package/dist/targets-C-wdR5wP.mjs +33 -0
  103. package/dist/{tokens-DKLn5P9B.mjs → tokens-DhuzKzAl.mjs} +1 -1
  104. package/dist/{types-BHCjTZdB.d.mts → types-8yq2VnKI.d.mts} +2 -2
  105. package/dist/{users-Dls7_ANV.mjs → users-yryHKFMo.mjs} +2 -2
  106. package/dist/{watch-spec-W0c7ypHJ.mjs → watchers-hash-RLtnSF-t.mjs} +233 -28
  107. package/docs/01-board/01-mounting.md +1 -1
  108. package/docs/02-sync/01-overview.md +72 -48
  109. package/docs/02-sync/02-functions.md +398 -0
  110. package/docs/02-sync/03-slack.md +30 -25
  111. package/docs/02-sync/04-github.md +62 -26
  112. package/docs/02-sync/05-whatsapp.md +23 -19
  113. package/docs/02-sync/06-providers.md +60 -27
  114. package/docs/02-sync/07-linear.md +129 -0
  115. package/docs/03-guides/04-triage.md +1 -1
  116. package/docs/04-reference/01-cli.md +26 -6
  117. package/docs/04-reference/02-board.md +18 -5
  118. package/docs/04-reference/03-http.md +5 -2
  119. package/docs/04-reference/05-conformance.md +1 -1
  120. package/docs/index.md +5 -10
  121. package/package.json +31 -6
  122. package/dist/a2-D24cvwpN.d.mts +0 -3359
  123. package/dist/access-CJU2-vaa.mjs +0 -226
  124. package/dist/attempts-Bxp9KgPf.mjs +0 -919
  125. package/dist/exports-DLiE7fLY.d.mts +0 -4
  126. package/dist/provider-CuewcDNZ.mjs +0 -205
  127. package/dist/redis-tables-B35vv4CZ.mjs +0 -842
  128. package/dist/reducers-B_vUGQRp.d.mts +0 -1172
  129. package/dist/watchers-hash-CLjsa6od.mjs +0 -225
  130. 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 SyncBody, B as UploadStartBody, C as Ref, D as SignInQuery, E as SchedulesQuery, F as SyncReceiptBody, I as SyncRootsQuery, L as SyncUnlinkBody, M as SyncCopiesQuery, N as SyncFindQuery, O as SourceReadQuery, P as SyncImportBody, R as ThreadsQuery, S as ReactBody, T as ScheduleBody, _ as InboxQuery, b as MessagesQuery, d as EditBody, f as EventsQuery, g as InboxPushBody, h as InboxNextQuery, j as SyncCodeBody, k as SyncAccountBody, m as InboxAckBody, o as derivedId, p as IdentityUnlinkBody, u as AckBody, v as LabelBody, w as RegisterBody, x as PostBody, y as MatchesQuery, z as TypingBody } from "./watch-spec-W0c7ypHJ.mjs";
4
- import { n as createBoardClient, t as BoardError } from "./client-DGDPaN7U.mjs";
5
- import { a as queryObject, c as VERSION, i as openapi, l as SYNC, n as METHODS, o as readCapped, r as createRouter, s as readJson } from "./watchers-hash-CLjsa6od.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";
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 { i as resolveActor, r as claimsOf, t as KNOWN_AGENTS } from "./actors-DjMeZpnU.mjs";
8
- import { d as issuersOf, g as verifiesTokens, l as hostPrincipal, o as createAuthority, s as fromTokens } from "./auth-COzc09sn.mjs";
9
- import { i as DroppedError, n as syncTarget, o as runMiddleware, r as withEmailMentions, t as mentionsEmails } from "./targets-yM5JBUgr.mjs";
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 and the targets its channel syncs with landing
647
- * beside it, once the caller may post there (and sync, for targets it names), through the
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) || input.channel !== void 0 && !input.noSync ? await mayPost(caller, input, signal) : null;
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(poster, {
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 sync = engine;
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 config = input.channel !== void 0 && !input.noSync ? await sync.configTargets(input.channel) : [];
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,4 @@
1
+ import "./auth-CU56Q2Xh.mjs";
2
+ import "./schemas-CWxs3ECJ.mjs";
3
+ import "./provider-B0b9Qxw4.mjs";
4
+ import "./backend-B-SJ7p6p.mjs";
@@ -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 };