@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,534 @@
1
+ import { i as Attachment, o as Author, p as Labels, r as ActorKind } from "./schemas-CWxs3ECJ.mjs";
2
+ import { z } from "zod";
3
+ //#region src/contract/events.d.ts
4
+ /** The board's one log. Every board fact is an event in session `board`. */
5
+ declare const boardEvents: {
6
+ 'message.posted': z.ZodObject<{
7
+ channel: z.ZodString;
8
+ text: z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<"">]>;
9
+ re: z.ZodNullable<z.ZodNumber>;
10
+ replyTo: z.ZodNullable<z.ZodNumber>;
11
+ thread: z.ZodString;
12
+ labels: z.ZodRecord<z.ZodString, z.ZodString>;
13
+ noSync: z.ZodBoolean;
14
+ mentions: z.ZodArray<z.ZodString>;
15
+ by: z.ZodObject<{
16
+ actor: z.ZodString;
17
+ principal: z.ZodString;
18
+ profile: z.ZodNullable<z.ZodString>;
19
+ session: z.ZodNullable<z.ZodString>;
20
+ }, z.core.$strip>;
21
+ origin: z.ZodNullable<z.ZodObject<{
22
+ channel: z.ZodOptional<z.ZodString>;
23
+ thread: z.ZodString;
24
+ subthread: z.ZodNullable<z.ZodString>;
25
+ message: z.ZodString;
26
+ context: z.ZodNullable<z.ZodString>;
27
+ }, z.core.$strip>>;
28
+ from: z.ZodNullable<z.ZodString>;
29
+ attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
30
+ id: z.ZodString;
31
+ name: z.ZodString;
32
+ type: z.ZodString;
33
+ size: z.ZodNumber;
34
+ }, z.core.$strip>>>;
35
+ links: z.ZodOptional<z.ZodArray<z.ZodObject<{
36
+ url: z.ZodString;
37
+ message: z.ZodNullable<z.ZodNumber>;
38
+ snapshot: z.ZodNullable<z.ZodObject<{
39
+ author: z.ZodString;
40
+ text: z.ZodString;
41
+ createdAt: z.ZodNullable<z.ZodString>;
42
+ files: z.ZodArray<z.ZodObject<{
43
+ name: z.ZodString;
44
+ url: z.ZodNullable<z.ZodString>;
45
+ }, z.core.$strip>>;
46
+ }, z.core.$strip>>;
47
+ unavailable: z.ZodNullable<z.ZodString>;
48
+ }, z.core.$strip>>>;
49
+ schedule: z.ZodOptional<z.ZodObject<{
50
+ id: z.ZodNumber;
51
+ n: z.ZodNumber;
52
+ }, z.core.$strip>>;
53
+ blind: z.ZodOptional<z.ZodLiteral<true>>;
54
+ importedBy: z.ZodOptional<z.ZodString>;
55
+ }, z.core.$strip>;
56
+ /** A message's links changed: an edit linked something new, or the platform sent what a link says. */
57
+ 'message.linked': z.ZodObject<{
58
+ id: z.ZodNumber;
59
+ thread: z.ZodString;
60
+ links: z.ZodArray<z.ZodObject<{
61
+ url: z.ZodString;
62
+ message: z.ZodNullable<z.ZodNumber>;
63
+ snapshot: z.ZodNullable<z.ZodObject<{
64
+ author: z.ZodString;
65
+ text: z.ZodString;
66
+ createdAt: z.ZodNullable<z.ZodString>;
67
+ files: z.ZodArray<z.ZodObject<{
68
+ name: z.ZodString;
69
+ url: z.ZodNullable<z.ZodString>;
70
+ }, z.core.$strip>>;
71
+ }, z.core.$strip>>;
72
+ unavailable: z.ZodNullable<z.ZodString>;
73
+ }, z.core.$strip>>;
74
+ by: z.ZodObject<{
75
+ actor: z.ZodString;
76
+ principal: z.ZodString;
77
+ profile: z.ZodNullable<z.ZodString>;
78
+ session: z.ZodNullable<z.ZodString>;
79
+ }, z.core.$strip>;
80
+ from: z.ZodNullable<z.ZodString>;
81
+ }, z.core.$strip>;
82
+ 'message.edited': z.ZodObject<{
83
+ id: z.ZodNumber;
84
+ thread: z.ZodString;
85
+ text: z.ZodString;
86
+ mentions: z.ZodArray<z.ZodString>;
87
+ by: z.ZodObject<{
88
+ actor: z.ZodString;
89
+ principal: z.ZodString;
90
+ profile: z.ZodNullable<z.ZodString>;
91
+ session: z.ZodNullable<z.ZodString>;
92
+ }, z.core.$strip>;
93
+ from: z.ZodNullable<z.ZodString>;
94
+ }, z.core.$strip>;
95
+ 'message.deleted': z.ZodObject<{
96
+ id: z.ZodNumber;
97
+ thread: z.ZodString;
98
+ by: z.ZodObject<{
99
+ actor: z.ZodString;
100
+ principal: z.ZodString;
101
+ profile: z.ZodNullable<z.ZodString>;
102
+ session: z.ZodNullable<z.ZodString>;
103
+ }, z.core.$strip>;
104
+ from: z.ZodNullable<z.ZodString>;
105
+ }, z.core.$strip>;
106
+ 'reaction.added': z.ZodObject<{
107
+ id: z.ZodNumber;
108
+ thread: z.ZodString;
109
+ emoji: z.ZodString;
110
+ by: z.ZodObject<{
111
+ actor: z.ZodString;
112
+ principal: z.ZodString;
113
+ profile: z.ZodNullable<z.ZodString>;
114
+ session: z.ZodNullable<z.ZodString>;
115
+ }, z.core.$strip>;
116
+ from: z.ZodNullable<z.ZodString>;
117
+ }, z.core.$strip>;
118
+ 'reaction.removed': z.ZodObject<{
119
+ id: z.ZodNumber;
120
+ thread: z.ZodString;
121
+ emoji: z.ZodString;
122
+ by: z.ZodObject<{
123
+ actor: z.ZodString;
124
+ principal: z.ZodString;
125
+ profile: z.ZodNullable<z.ZodString>;
126
+ session: z.ZodNullable<z.ZodString>;
127
+ }, z.core.$strip>;
128
+ from: z.ZodNullable<z.ZodString>;
129
+ }, z.core.$strip>;
130
+ 'labels.changed': z.ZodObject<{
131
+ id: z.ZodNumber;
132
+ thread: z.ZodString;
133
+ set: z.ZodRecord<z.ZodString, z.ZodString>;
134
+ unset: z.ZodArray<z.ZodString>;
135
+ by: z.ZodObject<{
136
+ actor: z.ZodString;
137
+ principal: z.ZodString;
138
+ profile: z.ZodNullable<z.ZodString>;
139
+ session: z.ZodNullable<z.ZodString>;
140
+ }, z.core.$strip>;
141
+ }, z.core.$strip>;
142
+ /** A thread is synced with an external channel (start a thread there) or thread (join it). */
143
+ 'thread.synced': z.ZodObject<{
144
+ thread: z.ZodString;
145
+ target: z.ZodString;
146
+ cause: z.ZodEnum<{
147
+ config: "config";
148
+ request: "request";
149
+ }>;
150
+ kind: z.ZodOptional<z.ZodEnum<{
151
+ channel: "channel";
152
+ thread: "thread";
153
+ }>>;
154
+ by: z.ZodObject<{
155
+ actor: z.ZodString;
156
+ principal: z.ZodString;
157
+ profile: z.ZodNullable<z.ZodString>;
158
+ session: z.ZodNullable<z.ZodString>;
159
+ }, z.core.$strip>;
160
+ }, z.core.$strip>;
161
+ /** An outbound delivery receipt: board message `id` exists on the platform as `external`. */
162
+ 'message.synced': z.ZodObject<{
163
+ id: z.ZodNumber;
164
+ thread: z.ZodString;
165
+ target: z.ZodString;
166
+ external: z.ZodObject<{
167
+ thread: z.ZodString;
168
+ message: z.ZodString;
169
+ asUser: z.ZodOptional<z.ZodLiteral<true>>;
170
+ }, z.core.$strip>;
171
+ syncedBy: z.ZodOptional<z.ZodString>;
172
+ }, z.core.$strip>;
173
+ /**
174
+ * Board message `id`'s files reached `target` after its text: `files` are the
175
+ * platform's IDs for them, or null when the platform refused files and the
176
+ * text got a line for each instead.
177
+ */
178
+ 'message.files_synced': z.ZodObject<{
179
+ id: z.ZodNumber;
180
+ thread: z.ZodString;
181
+ target: z.ZodString;
182
+ files: z.ZodNullable<z.ZodArray<z.ZodString>>;
183
+ syncedBy: z.ZodOptional<z.ZodString>;
184
+ }, z.core.$strip>;
185
+ /** A delivery that gave up: board message `id` didn't reach `target`, and `error` says why. */
186
+ 'message.sync_failed': z.ZodObject<{
187
+ id: z.ZodNumber;
188
+ thread: z.ZodString;
189
+ target: z.ZodString;
190
+ error: z.ZodString;
191
+ }, z.core.$strip>;
192
+ 'actor.seen': z.ZodObject<{
193
+ actor: z.ZodString;
194
+ kind: z.ZodUnion<readonly [z.ZodEnum<{
195
+ agent: "agent";
196
+ deployment: "deployment";
197
+ external: "external";
198
+ person: "person";
199
+ workflow: "workflow";
200
+ }>, z.ZodLiteral<"profile">]>;
201
+ handle: z.ZodString;
202
+ name: z.ZodString;
203
+ avatar: z.ZodNullable<z.ZodString>;
204
+ host: z.ZodOptional<z.ZodLiteral<true>>;
205
+ }, z.core.$strip>;
206
+ /**
207
+ * A platform actor (`slack:user:U0456`) is the same person as a principal
208
+ * (`vercel:user:…`): for display, `--by`, and mentions, never for permissions.
209
+ * `method` says how it was proved: a one-time code posted from that account,
210
+ * or the account's own sign-in (`consent`).
211
+ */
212
+ 'actor.linked': z.ZodObject<{
213
+ actor: z.ZodString;
214
+ to: z.ZodString;
215
+ method: z.ZodEnum<{
216
+ code: "code";
217
+ consent: "consent";
218
+ }>;
219
+ by: z.ZodObject<{
220
+ actor: z.ZodString;
221
+ principal: z.ZodString;
222
+ profile: z.ZodNullable<z.ZodString>;
223
+ session: z.ZodNullable<z.ZodString>;
224
+ }, z.core.$strip>;
225
+ }, z.core.$strip>;
226
+ 'actor.unlinked': z.ZodObject<{
227
+ actor: z.ZodString;
228
+ by: z.ZodObject<{
229
+ actor: z.ZodString;
230
+ principal: z.ZodString;
231
+ profile: z.ZodNullable<z.ZodString>;
232
+ session: z.ZodNullable<z.ZodString>;
233
+ }, z.core.$strip>;
234
+ }, z.core.$strip>;
235
+ };
236
+ declare const BOARD_SESSION = "board";
237
+ type BoardEvents = typeof boardEvents;
238
+ /** One event of the board log, of type `K`: the shape A2 stores it in (its `ContractEvent`). */
239
+ type BoardEvent<K extends keyof BoardEvents & string = keyof BoardEvents & string> = { [T in keyof BoardEvents & string]: {
240
+ id: string;
241
+ type: T;
242
+ payload: z.output<BoardEvents[T]>;
243
+ index: number;
244
+ sessionId: string;
245
+ createdAt: Date;
246
+ }; }[K];
247
+ //#endregion
248
+ //#region src/contract/external.d.ts
249
+ /** Where a message came from on a platform. `thread` is grouped (GitHub review threads fold into their PR). */
250
+ declare const Origin: z.ZodObject<{
251
+ channel: z.ZodOptional<z.ZodString>;
252
+ thread: z.ZodString;
253
+ subthread: z.ZodNullable<z.ZodString>;
254
+ message: z.ZodString;
255
+ context: z.ZodNullable<z.ZodString>;
256
+ }, z.core.$strip>;
257
+ /**
258
+ * A platform message linked from a message (a Slack permalink): the board message
259
+ * it was synced to, if any, and a snapshot of it, or why the board couldn't read it.
260
+ */
261
+ declare const Link: z.ZodObject<{
262
+ url: z.ZodString;
263
+ message: z.ZodNullable<z.ZodNumber>;
264
+ snapshot: z.ZodNullable<z.ZodObject<{
265
+ author: z.ZodString;
266
+ text: z.ZodString;
267
+ createdAt: z.ZodNullable<z.ZodString>;
268
+ files: z.ZodArray<z.ZodObject<{
269
+ name: z.ZodString;
270
+ url: z.ZodNullable<z.ZodString>;
271
+ }, z.core.$strip>>;
272
+ }, z.core.$strip>>;
273
+ unavailable: z.ZodNullable<z.ZodString>;
274
+ }, z.core.$strip>;
275
+ type Link = z.infer<typeof Link>;
276
+ type Origin = z.infer<typeof Origin>;
277
+ declare const ExternalAuthor: z.ZodObject<{
278
+ id: z.ZodString;
279
+ handle: z.ZodString;
280
+ name: z.ZodString;
281
+ avatar: z.ZodNullable<z.ZodString>;
282
+ isBot: z.ZodBoolean;
283
+ }, z.core.$strip>;
284
+ /**
285
+ * A file on a platform message, as Chat SDK describes it: enough for the
286
+ * platform's adapter to fetch it later (`rehydrateAttachment`), never the bytes.
287
+ */
288
+ declare const ExternalFile: z.ZodObject<{
289
+ type: z.ZodEnum<{
290
+ audio: "audio";
291
+ file: "file";
292
+ image: "image";
293
+ video: "video";
294
+ }>;
295
+ name: z.ZodOptional<z.ZodString>;
296
+ mimeType: z.ZodOptional<z.ZodString>;
297
+ size: z.ZodOptional<z.ZodNumber>;
298
+ url: z.ZodOptional<z.ZodString>;
299
+ fetchMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
300
+ }, z.core.$strip>;
301
+ type ExternalFile = z.infer<typeof ExternalFile>;
302
+ /**
303
+ * A platform message a platform message links to, at intake: where it lives
304
+ * (`thread`, with the sync key prefix, and `message`), and what it said when the
305
+ * platform sent that along (Slack's unfurl).
306
+ */
307
+ declare const ExternalLink: z.ZodObject<{
308
+ url: z.ZodString;
309
+ thread: z.ZodString;
310
+ message: z.ZodString;
311
+ snapshot: z.ZodNullable<z.ZodObject<{
312
+ author: z.ZodString;
313
+ text: z.ZodString;
314
+ createdAt: z.ZodNullable<z.ZodString>;
315
+ files: z.ZodArray<z.ZodObject<{
316
+ name: z.ZodString;
317
+ url: z.ZodNullable<z.ZodString>;
318
+ }, z.core.$strip>>;
319
+ }, z.core.$strip>>;
320
+ }, z.core.$strip>;
321
+ type ExternalLink = z.infer<typeof ExternalLink>;
322
+ /** A platform event, normalized at intake. Thread IDs are external IDs with the sync key prefix. */
323
+ declare const ExternalEvent: z.ZodDiscriminatedUnion<[z.ZodObject<{
324
+ channel: z.ZodString;
325
+ thread: z.ZodString;
326
+ subthread: z.ZodNullable<z.ZodString>;
327
+ kind: z.ZodLiteral<"message">;
328
+ message: z.ZodString;
329
+ isRoot: z.ZodBoolean;
330
+ text: z.ZodString;
331
+ context: z.ZodNullable<z.ZodString>;
332
+ author: z.ZodObject<{
333
+ id: z.ZodString;
334
+ handle: z.ZodString;
335
+ name: z.ZodString;
336
+ avatar: z.ZodNullable<z.ZodString>;
337
+ isBot: z.ZodBoolean;
338
+ }, z.core.$strip>;
339
+ files: z.ZodOptional<z.ZodArray<z.ZodObject<{
340
+ type: z.ZodEnum<{
341
+ audio: "audio";
342
+ file: "file";
343
+ image: "image";
344
+ video: "video";
345
+ }>;
346
+ name: z.ZodOptional<z.ZodString>;
347
+ mimeType: z.ZodOptional<z.ZodString>;
348
+ size: z.ZodOptional<z.ZodNumber>;
349
+ url: z.ZodOptional<z.ZodString>;
350
+ fetchMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
351
+ }, z.core.$strip>>>;
352
+ links: z.ZodOptional<z.ZodArray<z.ZodObject<{
353
+ url: z.ZodString;
354
+ thread: z.ZodString;
355
+ message: z.ZodString;
356
+ snapshot: z.ZodNullable<z.ZodObject<{
357
+ author: z.ZodString;
358
+ text: z.ZodString;
359
+ createdAt: z.ZodNullable<z.ZodString>;
360
+ files: z.ZodArray<z.ZodObject<{
361
+ name: z.ZodString;
362
+ url: z.ZodNullable<z.ZodString>;
363
+ }, z.core.$strip>>;
364
+ }, z.core.$strip>>;
365
+ }, z.core.$strip>>>;
366
+ }, z.core.$strip>, z.ZodObject<{
367
+ channel: z.ZodString;
368
+ thread: z.ZodString;
369
+ subthread: z.ZodNullable<z.ZodString>;
370
+ kind: z.ZodLiteral<"edited">;
371
+ message: z.ZodString;
372
+ text: z.ZodString;
373
+ author: z.ZodObject<{
374
+ id: z.ZodString;
375
+ handle: z.ZodString;
376
+ name: z.ZodString;
377
+ avatar: z.ZodNullable<z.ZodString>;
378
+ isBot: z.ZodBoolean;
379
+ }, z.core.$strip>;
380
+ links: z.ZodOptional<z.ZodArray<z.ZodObject<{
381
+ url: z.ZodString;
382
+ thread: z.ZodString;
383
+ message: z.ZodString;
384
+ snapshot: z.ZodNullable<z.ZodObject<{
385
+ author: z.ZodString;
386
+ text: z.ZodString;
387
+ createdAt: z.ZodNullable<z.ZodString>;
388
+ files: z.ZodArray<z.ZodObject<{
389
+ name: z.ZodString;
390
+ url: z.ZodNullable<z.ZodString>;
391
+ }, z.core.$strip>>;
392
+ }, z.core.$strip>>;
393
+ }, z.core.$strip>>>;
394
+ linksOnly: z.ZodOptional<z.ZodBoolean>;
395
+ }, z.core.$strip>, z.ZodObject<{
396
+ channel: z.ZodString;
397
+ thread: z.ZodString;
398
+ subthread: z.ZodNullable<z.ZodString>;
399
+ kind: z.ZodLiteral<"deleted">;
400
+ message: z.ZodString;
401
+ }, z.core.$strip>, z.ZodObject<{
402
+ channel: z.ZodString;
403
+ thread: z.ZodString;
404
+ subthread: z.ZodNullable<z.ZodString>;
405
+ kind: z.ZodLiteral<"reaction">;
406
+ message: z.ZodString;
407
+ emoji: z.ZodString;
408
+ added: z.ZodBoolean;
409
+ author: z.ZodObject<{
410
+ id: z.ZodString;
411
+ handle: z.ZodString;
412
+ name: z.ZodString;
413
+ avatar: z.ZodNullable<z.ZodString>;
414
+ isBot: z.ZodBoolean;
415
+ }, z.core.$strip>;
416
+ }, z.core.$strip>, z.ZodObject<{
417
+ channel: z.ZodString;
418
+ thread: z.ZodString;
419
+ kind: z.ZodLiteral<"lifecycle">;
420
+ type: z.ZodString;
421
+ title: z.ZodString;
422
+ url: z.ZodNullable<z.ZodString>;
423
+ labels: z.ZodRecord<z.ZodString, z.ZodString>;
424
+ unset: z.ZodArray<z.ZodString>;
425
+ owns: z.ZodOptional<z.ZodArray<z.ZodString>>;
426
+ reply: z.ZodNullable<z.ZodString>;
427
+ createRoot: z.ZodBoolean;
428
+ retitled: z.ZodOptional<z.ZodBoolean>;
429
+ author: z.ZodObject<{
430
+ id: z.ZodString;
431
+ handle: z.ZodString;
432
+ name: z.ZodString;
433
+ avatar: z.ZodNullable<z.ZodString>;
434
+ isBot: z.ZodBoolean;
435
+ }, z.core.$strip>;
436
+ }, z.core.$strip>], "kind">;
437
+ type ExternalEvent = z.infer<typeof ExternalEvent>;
438
+ type ExternalAuthor = z.infer<typeof ExternalAuthor>;
439
+ //#endregion
440
+ //#region src/contract/folds.d.ts
441
+ type Message = {
442
+ id: number;
443
+ channel: string;
444
+ text: string;
445
+ re: number | null;
446
+ replyTo: number | null;
447
+ /** The thread key: the root's event ID. */
448
+ thread: string;
449
+ labels: Labels;
450
+ reactions: Record<string, string[]>;
451
+ mentions: string[];
452
+ by: Author;
453
+ noSync: boolean;
454
+ /** Where it came from on a platform, if it did. */
455
+ origin: Origin | null;
456
+ /** The sync key it came from, or null when written on the board. */
457
+ from: string | null;
458
+ createdAt: string;
459
+ editedAt: string | null;
460
+ deleted: boolean;
461
+ /** Sync targets this message didn't reach, with why. A later delivery clears its entry. */
462
+ undelivered: Record<string, string>;
463
+ attachments: Attachment[];
464
+ /** Platform messages it links to (Slack permalinks), each with what it said. */
465
+ links: Link[];
466
+ /** Set when a schedule posted it: the schedule's ID and which send this was, from 0. */
467
+ schedule?: {
468
+ id: number;
469
+ n: number;
470
+ };
471
+ /**
472
+ * Set when its author couldn't read its channel, as `authorizeOwn` checks: where they can't read,
473
+ * it's theirs to edit and delete only then. Its event's payload says so too.
474
+ */
475
+ blind?: true;
476
+ /** The index of the last event that changed it. Folds from before it existed leave it out. */
477
+ version?: number;
478
+ };
479
+ type MessagesState = {
480
+ byId: Record<string, Message>;
481
+ /** Thread root → reply IDs, in log order. */
482
+ replies: Record<string, number[]>;
483
+ };
484
+ type Channel = {
485
+ path: string;
486
+ lastIndex: number;
487
+ messages: number;
488
+ threads: number;
489
+ };
490
+ type ChannelsState = Record<string, Channel>;
491
+ type Actor = {
492
+ id: string;
493
+ kind: ActorKind;
494
+ handle: string;
495
+ name: string;
496
+ avatar: string | null;
497
+ /** A host's principal, or one of its profiles: once it's seen as one, it stays one. Views leave it out. */
498
+ host?: true;
499
+ };
500
+ /** A platform actor proved to be a principal (`actor.linked`). */
501
+ type ActorLink = {
502
+ to: string;
503
+ method: 'code' | 'consent';
504
+ at: string;
505
+ };
506
+ type ActorsState = {
507
+ byId: Record<string, Actor>;
508
+ /** Lowercased handle → actor IDs. Handles aren't reserved, so several can share one. */
509
+ byHandle: Record<string, string[]>;
510
+ /** Platform actor → the principal it's linked to. Display and filtering only, never permissions. */
511
+ links: Record<string, ActorLink>;
512
+ };
513
+ /** A copy of a board message on a platform. */
514
+ /** `files`: the platform's IDs for the message's files, or null when it refused them; unset until they're sent. */
515
+ /** `asUser`: posted with its author's own token, so editing or deleting it needs that token too. */
516
+ /** `by`: the principal of the sync elsewhere that recorded it, whose copy it is to change; none for the board's own sync's. */
517
+ type ExternalCopy = {
518
+ thread: string;
519
+ message: string;
520
+ files?: string[] | null;
521
+ asUser?: true;
522
+ by?: string;
523
+ };
524
+ type SyncedThread = {
525
+ root: number;
526
+ channel: string;
527
+ kinds?: Record<string, 'channel' | 'thread'>;
528
+ /** External channels (start a thread there) and threads (joined), in sync order. */
529
+ targets: string[];
530
+ /** Target → the external thread it resolved to. A thread target resolves to itself. */
531
+ external: Record<string, string>;
532
+ };
533
+ //#endregion
534
+ export { BoardEvents as _, ExternalCopy as a, SyncedThread as c, ExternalFile as d, ExternalLink as f, BoardEvent as g, BOARD_SESSION as h, ChannelsState as i, ExternalAuthor as l, Origin as m, ActorsState as n, Message as o, Link as p, Channel as r, MessagesState as s, Actor as t, ExternalEvent as u };
@@ -1,9 +1,8 @@
1
1
  import { b as actorKind } from "./schemas-Be08nyfW.mjs";
2
- import { f as boardContract, p as identityContract } from "./contract-CuJmlrhw.mjs";
2
+ import { c as externalMessageKey, l as isThreadTarget } from "./external-BV_cUncs.mjs";
3
3
  import { t as canonicalEmoji } from "./emoji-CTLpvH-A.mjs";
4
4
  import { n as setOwn, t as own } from "./own-OwgPWdmh.mjs";
5
- import { reducer } from "experimental-a2";
6
- //#region src/contract/reducers.ts
5
+ //#region src/contract/folds.ts
7
6
  const iso = (date) => typeof date === "string" ? date : date.toISOString();
8
7
  function foldMessages(state, event) {
9
8
  const changed = foldMessage(state, event);
@@ -111,40 +110,6 @@ function foldMessage(state, event) {
111
110
  default: return;
112
111
  }
113
112
  }
114
- const messagesReducer = reducer({
115
- contract: boardContract,
116
- name: "board.messages.v5",
117
- initialState: {
118
- byId: {},
119
- replies: {}
120
- }
121
- }).fold(foldMessages);
122
- reducer({
123
- contract: boardContract,
124
- name: "board.posted.v1",
125
- initialState: {}
126
- }).fold((state, event) => {
127
- if (event.type === "message.posted") setOwn(state, event.id, event.index);
128
- return state;
129
- });
130
- const channelsReducer = reducer({
131
- contract: boardContract,
132
- name: "board.channels.v1",
133
- initialState: {}
134
- }).fold((state, event) => {
135
- if (event.type !== "message.posted") return state;
136
- const { channel, re } = event.payload;
137
- const entry = own(state, channel) ?? setOwn(state, channel, {
138
- path: channel,
139
- lastIndex: 0,
140
- messages: 0,
141
- threads: 0
142
- });
143
- entry.lastIndex = event.index;
144
- entry.messages += 1;
145
- if (re === null) entry.threads += 1;
146
- return state;
147
- });
148
113
  /** The principal an actor belongs to: its link, its own principal for a profile (`…+claude`), or itself. */
149
114
  function personOf(actors, actor) {
150
115
  const linked = own(actors.links, actor)?.to;
@@ -189,18 +154,6 @@ function foldActors(state, event) {
189
154
  if (!ids.includes(actor)) ids.push(actor);
190
155
  return state;
191
156
  }
192
- const actorsReducer = reducer({
193
- contract: boardContract,
194
- name: "board.actors.v2",
195
- initialState: {
196
- byId: {},
197
- byHandle: {},
198
- links: {}
199
- }
200
- }).fold(foldActors);
201
- /** `slack:C0123ABC:1727.1234` is a thread; `slack:C0123ABC` and `github:owner/repo` are channels. */
202
- const isThreadTarget = (target) => target.slice(target.indexOf(":") + 1).includes(":");
203
- const externalMessageKey = (thread, message) => `${thread}|${message}`;
204
157
  const addTarget = (thread, target) => {
205
158
  if (!thread.targets.includes(target)) thread.targets.push(target);
206
159
  };
@@ -230,6 +183,7 @@ function foldSync(state, event) {
230
183
  ...payload.importedBy ? { by: payload.importedBy } : {}
231
184
  });
232
185
  state.byExternalMessage[externalMessageKey(precise, origin.message)] = event.index;
186
+ if (precise !== origin.thread) state.byExternalMessage[externalMessageKey(origin.thread, origin.message)] = event.index;
233
187
  return state;
234
188
  }
235
189
  case "thread.synced": {
@@ -257,6 +211,8 @@ function foldSync(state, event) {
257
211
  });
258
212
  state.byExternalMessage[externalMessageKey(external.thread, external.message)] = id;
259
213
  const thread = own(state.threads, event.payload.thread);
214
+ const main = thread ? own(thread.external, target) : void 0;
215
+ if (main && main !== external.thread) state.byExternalMessage[externalMessageKey(main, external.message)] = id;
260
216
  if (thread && thread.root === id && ((thread.kinds && own(thread.kinds, target)) ?? (isThreadTarget(target) ? "thread" : "channel")) === "channel") {
261
217
  setOwn(thread.external, target, external.thread);
262
218
  setOwn(state.byExternalThread, external.thread, event.payload.thread);
@@ -266,28 +222,5 @@ function foldSync(state, event) {
266
222
  default: return state;
267
223
  }
268
224
  }
269
- const identityCodesReducer = reducer({
270
- contract: identityContract,
271
- name: "board.identity-codes.v1",
272
- initialState: {}
273
- }).fold((state, event) => {
274
- if (event.type === "identity.code_issued") {
275
- const { hash, principal, sync, expiresAt } = event.payload;
276
- state[hash] ??= {
277
- principal,
278
- sync,
279
- expiresAt,
280
- usedBy: null,
281
- usedIn: null
282
- };
283
- } else if (event.type === "identity.code_used") {
284
- const code = state[event.payload.hash];
285
- if (code && code.usedBy === null) {
286
- code.usedBy = event.payload.actor;
287
- code.usedIn = event.payload.message ?? null;
288
- }
289
- }
290
- return state;
291
- });
292
225
  //#endregion
293
- export { foldMessages as a, isThreadTarget as c, messagesReducer as d, personOf as f, foldActors as i, iso as l, channelsReducer as n, foldSync as o, externalMessageKey as r, identityCodesReducer as s, actorsReducer as t, linkedTo as u };
226
+ export { linkedTo as a, iso as i, foldMessages as n, personOf as o, foldSync as r, foldActors as t };
@@ -0,0 +1,31 @@
1
+ //#region src/client/ids.ts
2
+ const hex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
3
+ /** SHA-256 of `text`, in hex. */
4
+ async function sha256(text) {
5
+ return hex(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text))));
6
+ }
7
+ /**
8
+ * A UUIDv8 from `parts`, exactly as A2's `idempotentId` makes one: the IDs a board derives for the
9
+ * writes its own code makes, which no request may post with.
10
+ */
11
+ async function derivedId(...parts) {
12
+ const bytes = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify(parts)))).slice(0, 16);
13
+ bytes[6] = bytes[6] & 15 | 128;
14
+ bytes[8] = bytes[8] & 63 | 128;
15
+ const text = hex(bytes);
16
+ return `${text.slice(0, 8)}-${text.slice(8, 12)}-${text.slice(12, 16)}-${text.slice(16, 20)}-${text.slice(20, 32)}`;
17
+ }
18
+ /** A derived ID's shape. */
19
+ const DERIVED_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-8[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
20
+ /**
21
+ * A post's ID, from one derived for it, under `principal`: a board takes a `key:<principal>:` ID
22
+ * only from that principal, so no one else can post with it first.
23
+ */
24
+ const keyId = async (principal, derived) => `key:${principal}:${(await sha256(`board-key\u001f${derived}`)).slice(0, 32)}`;
25
+ /**
26
+ * Whether `id` is exactly the shape `keyId` makes for `principal`. A prefix check isn't enough:
27
+ * a principal's ID can be the start of another's (`oidc:gitlab.com:acme` and `oidc:gitlab.com:acme:ci`).
28
+ */
29
+ const isKeyIdOf = (id, principal) => id.startsWith(`key:${principal}:`) && /^[0-9a-f]{32}$/.test(id.slice(`key:${principal}:`.length));
30
+ //#endregion
31
+ export { sha256 as a, keyId as i, derivedId as n, isKeyIdOf as r, DERIVED_ID as t };