@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,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 {
|
|
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
|
-
|
|
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 {
|
|
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 };
|