@vercel/board 0.29.0 → 0.31.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 (156) hide show
  1. package/README.md +8 -17
  2. package/dist/{a2-HUbQJ0RD.mjs → a2-DngWY2Eb.mjs} +741 -415
  3. package/dist/access-CY5xVMIK.mjs +242 -0
  4. package/dist/{access-CV96SYgr.d.mts → access-DPcrKIH_.d.mts} +6 -1
  5. package/dist/access.d.mts +4 -4
  6. package/dist/access.mjs +5 -4
  7. package/dist/{actors-DcJhFbwJ.mjs → actors-Bm-ojWWT.mjs} +2 -2
  8. package/dist/{auth-JO22iIwr.mjs → auth-CyO2aw5n.mjs} +126 -236
  9. package/dist/{auth-CU56Q2Xh.d.mts → auth-DLYSt_-A.d.mts} +32 -4
  10. package/dist/{backend-CPC_vyOo.d.mts → backend-Bob-5CM7.d.mts} +93 -42
  11. package/dist/backend-a2/blob-vercel.d.mts +1 -1
  12. package/dist/backend-a2/contract.d.mts +38 -98
  13. package/dist/backend-a2/contract.mjs +1 -1
  14. package/dist/backend-a2/scheduler-local.d.mts +1 -1
  15. package/dist/backend-a2/scheduler-local.mjs +1 -1
  16. package/dist/backend-a2/scheduler-qstash.d.mts +3 -3
  17. package/dist/backend-a2/scheduler-qstash.mjs +8 -8
  18. package/dist/backend-a2/scheduler-vercel.d.mts +3 -3
  19. package/dist/backend-a2/scheduler-vercel.mjs +6 -6
  20. package/dist/backend-a2/store-memory.d.mts +2 -2
  21. package/dist/backend-a2/store-memory.mjs +3 -3
  22. package/dist/backend-a2/store-postgres.d.mts +1 -1
  23. package/dist/backend-a2/store-postgres.mjs +34 -15
  24. package/dist/backend-a2/store-redis-http.d.mts +1 -1
  25. package/dist/backend-a2/store-redis-http.mjs +6 -6
  26. package/dist/backend-a2/store-redis.d.mts +2 -2
  27. package/dist/backend-a2/store-redis.mjs +5 -5
  28. package/dist/backend-a2/store-sqlite.d.mts +1 -1
  29. package/dist/backend-a2/store-sqlite.mjs +3 -3
  30. package/dist/backend-a2.d.mts +143 -242
  31. package/dist/backend-a2.mjs +2 -2
  32. package/dist/{backend-http-CqfKhrnn.d.mts → backend-http-BRrRbjRx.d.mts} +59 -45
  33. package/dist/{backend-http-CzIJMm58.mjs → backend-http-CvUKOn-m.mjs} +11 -10
  34. package/dist/{backend-http-Bmp0IgD3.mjs → backend-http-D8mUzuKQ.mjs} +2 -2
  35. package/dist/backend-http.d.mts +1 -1
  36. package/dist/backend-http.mjs +1 -1
  37. package/dist/backend-http.node.d.mts +1 -1
  38. package/dist/backend-http.node.mjs +1 -1
  39. package/dist/{backend-vercel-DQc_TaHp.mjs → backend-vercel-Ba8JDtqW.mjs} +3 -3
  40. package/dist/{backend-vercel-4tvcPKqX.d.mts → backend-vercel-BaSEUdwg.d.mts} +62 -47
  41. package/dist/{backend-vercel-BNauAs7w.mjs → backend-vercel-CmY9yZ9m.mjs} +279 -42
  42. package/dist/backend-vercel.d.mts +1 -1
  43. package/dist/backend-vercel.mjs +1 -1
  44. package/dist/backend-vercel.node.d.mts +1 -1
  45. package/dist/backend-vercel.node.mjs +1 -1
  46. package/dist/{blob-DmiQjrYy.d.mts → blob-Bx8QPWEd.d.mts} +2 -2
  47. package/dist/{blob-DdbpJqN1.mjs → blob-WbB2DJKn.mjs} +30 -11
  48. package/dist/{chat-provider-BWHbChcQ.d.mts → chat-provider-BChYrrA7.d.mts} +32 -28
  49. package/dist/{chat-provider-COtsU5jB.mjs → chat-provider-DkFp2hrh.mjs} +105 -60
  50. package/dist/cli-commands.d.mts +1 -1
  51. package/dist/cli-commands.mjs +1 -1
  52. package/dist/cli-embedded.d.mts +2 -2
  53. package/dist/cli-embedded.mjs +1 -1
  54. package/dist/cli.mjs +4 -4
  55. package/dist/{client-BqAjk0Q4.d.mts → client-COfYrgfV.d.mts} +240 -227
  56. package/dist/{commands-D6gvrOh3.d.mts → commands-DK4W3HCJ.d.mts} +47 -10
  57. package/dist/{commands-C3R1AWgm.mjs → commands-L1JSGz2Z.mjs} +212 -54
  58. package/dist/conformance-DK43HiHf.mjs +1000 -0
  59. package/dist/conformance.d.mts +8 -2
  60. package/dist/conformance.mjs +1 -1
  61. package/dist/{engine-DQxXk7dX.mjs → engine-DurYIRt8.mjs} +29 -210
  62. package/dist/{errors-DDktVeUa.d.mts → errors-2vGs49Db.d.mts} +1 -1
  63. package/dist/{client-D0Px7KzF.mjs → errors-CJXhhzvT.mjs} +132 -27
  64. package/dist/{provider-BaVmi4Hi.mjs → events-lujPA3gQ.mjs} +80 -17
  65. package/dist/{exports-BG-0Wyn-.mjs → exports-CWIdN0Ub.mjs} +195 -103
  66. package/dist/exports-FBOQWT7O.d.mts +5 -0
  67. package/dist/{folds-BDMSxBUf.mjs → folds-Cu9Q9zyQ.mjs} +3 -22
  68. package/dist/{folds-ZkHM4M-v.d.mts → folds-DLUzfibJ.d.mts} +15 -40
  69. package/dist/{index-D3VvNfh_.d.mts → index-Ho-ayyQL.d.mts} +2 -2
  70. package/dist/index.d.mts +7 -7
  71. package/dist/{index.default-rB8sJ9RX.mjs → index.default-BUcwDaAe.mjs} +1 -1
  72. package/dist/index.mjs +5 -6
  73. package/dist/index.node.d.mts +7 -7
  74. package/dist/index.node.mjs +3 -4
  75. package/dist/inspect/app.js +62 -61
  76. package/dist/inspect/engine.js +3 -3
  77. package/dist/{internal-CvsrprnU-CRY89I-b.mjs → internal-CvsrprnU-zZMpDiRY.mjs} +1 -1
  78. package/dist/{local-DhkrAqPP.mjs → local-C76Fy5ad.mjs} +1 -1
  79. package/dist/{main-CVBqe3VO.mjs → main-Dz-8komG.mjs} +251 -127
  80. package/dist/{middleware-CXjEeef4.mjs → middleware-BavYSDUa.mjs} +1 -1
  81. package/dist/{node-Zd_C-gxP.mjs → node-BgeGcwX0.mjs} +7 -3
  82. package/dist/{platform-B4TnJtWu-CwcTyKg2.mjs → platform-B4TnJtWu-Do3agKr5.mjs} +1 -1
  83. package/dist/{project-CBXlLftU.mjs → project-CQ4VUibA.mjs} +1 -1
  84. package/dist/{provider-DmSTp9lI.d.mts → provider-gynDUoyW.d.mts} +13 -43
  85. package/dist/{redis-tables-BJQVUspu.mjs → redis-tables-DHf85ixI.mjs} +40 -42
  86. package/dist/{reducer-CjmvZSAb-D_OSISmj.mjs → reducer-Bswr11ar-P324o9vn.mjs} +2 -2
  87. package/dist/{reducer-CYPHsSmd-BQIfozGi.d.mts → reducer-CYPHsSmd-BXjGiptm.d.mts} +1 -1
  88. package/dist/{reducers-DJzVpXwM.mjs → reducers-DcENwtwZ.mjs} +74 -77
  89. package/dist/{retryable-lazy-DZWmHpii-Bye-W8q7.mjs → retryable-lazy-DZWmHpii-DpTc5PIP.mjs} +1 -1
  90. package/dist/{scheduler-task-X1SToDjP-BVoWuInI.mjs → scheduler-task-X1SToDjP-D2Il2rln.mjs} +1 -1
  91. package/dist/{serve-G9RuaDSr.mjs → serve-CTmow-YH.mjs} +7 -7
  92. package/dist/{server-qgez0Xny-D5mLfqFQ.d.mts → server-B9gu0qi1-DI1WvcVK.d.mts} +10 -4
  93. package/dist/{server-BixV_byP.mjs → server-DG8D6VV0.mjs} +4 -4
  94. package/dist/{server-DtFrWxi3-Boa6gdWO.mjs → server-DX7mM6nm-D7N_7bug.mjs} +32 -23
  95. package/dist/{serving-B75Y4BF-.mjs → serving-DBFRo6U_.mjs} +1 -1
  96. package/dist/{session-clear-wHd5GCpZ-BnKSxReK.mjs → session-clear-XJYpmUpg-2q8l_FJR.mjs} +14 -2
  97. package/dist/{sql-D4Z_efJX.mjs → sql-CT5xVuGr.mjs} +2 -8
  98. package/dist/{sqlite-db-Bz2JkfCU.mjs → sqlite-db-0WeSOEZN.mjs} +1 -1
  99. package/dist/{store-D0MhBvg6-4Z2L0Kqd.d.mts → store-DEzcHZOA-B-5vhxef.d.mts} +12 -2
  100. package/dist/store-N8PXxDAS-DWWgv-Vz.mjs +5 -0
  101. package/dist/{store-codec-DwrWlgQx-CYxGW0aD.mjs → store-codec-pAG2oPgQ-SqDXlPBR.mjs} +4 -4
  102. package/dist/store-memory-4IBl0Ztu.mjs +2 -0
  103. package/dist/{store-memory-Bas9Uq5C.mjs → store-memory-B0PkVSuQ.mjs} +14 -7
  104. package/dist/{store-polling-BZt2Rf3D-BiEM6_HB.mjs → store-polling-BZt2Rf3D-Div-bJ_D.mjs} +2 -2
  105. package/dist/{store-presence-polling-JuAy2Ck2-CYpuK75X.mjs → store-presence-polling-JuAy2Ck2-Dh-g_KYM.mjs} +3 -3
  106. package/dist/{store-sqlite-CxxPCcCT.mjs → store-sqlite-D85t3tE7.mjs} +14 -7
  107. package/dist/store-sqlite-iBTYYKBj.mjs +2 -0
  108. package/dist/{stream-buffer-DrTyiIuo-BKy-vI14.mjs → stream-buffer-DrTyiIuo-Be10yOBR.mjs} +2 -2
  109. package/dist/{sync-kapso.d.mts → sync-chat-sdk.d.mts} +33 -37
  110. package/dist/sync-chat-sdk.mjs +44 -0
  111. package/dist/sync-github.d.mts +5 -18
  112. package/dist/sync-github.mjs +4 -70
  113. package/dist/sync-linear.d.mts +3 -3
  114. package/dist/sync-linear.mjs +6 -5
  115. package/dist/sync-slack.d.mts +7 -7
  116. package/dist/sync-slack.mjs +33 -54
  117. package/dist/sync.d.mts +3 -3
  118. package/dist/sync.mjs +2 -4
  119. package/dist/{tokens-CC3izSNr.mjs → tokens-Dnyd_y1n.mjs} +15 -6
  120. package/dist/{types-No0WdhE5.d.mts → types-CmrFjpml.d.mts} +2 -4
  121. package/dist/{watchers-hash-BDhLVkV5.mjs → watchers-hash-BjwlnGD9.mjs} +124 -115
  122. package/docs/01-board/01-mounting.md +2 -2
  123. package/docs/01-board/03-identity.md +5 -6
  124. package/docs/01-board/04-messages.md +96 -2
  125. package/docs/01-board/05-reading.md +2 -1
  126. package/docs/01-board/06-watchers.md +8 -6
  127. package/docs/01-board/08-middleware.md +6 -3
  128. package/docs/01-board/09-access.md +145 -14
  129. package/docs/02-sync/01-overview.md +19 -26
  130. package/docs/02-sync/02-functions.md +8 -9
  131. package/docs/02-sync/03-slack.md +5 -5
  132. package/docs/02-sync/04-github.md +2 -2
  133. package/docs/02-sync/06-providers.md +91 -62
  134. package/docs/03-guides/01-coding-agents.md +1 -1
  135. package/docs/03-guides/03-ci.md +7 -4
  136. package/docs/04-reference/01-cli.md +109 -51
  137. package/docs/04-reference/02-board.md +76 -28
  138. package/docs/04-reference/03-http.md +76 -38
  139. package/docs/04-reference/04-log.md +8 -11
  140. package/docs/04-reference/05-conformance.md +47 -7
  141. package/docs/index.md +2 -2
  142. package/package.json +7 -10
  143. package/sql/postgres/board.sql +1 -1
  144. package/sql/postgres/schema.sql +1 -1
  145. package/dist/conformance-DlnfuzEB.mjs +0 -556
  146. package/dist/errors-CusN4x2l.mjs +0 -88
  147. package/dist/events-qf5XG5VQ.mjs +0 -75
  148. package/dist/exports-mhAYiU4i.d.mts +0 -5
  149. package/dist/store-N8PXxDAS-BWBgzBHp.mjs +0 -5
  150. package/dist/store-memory-ZLiZbH3R.mjs +0 -2
  151. package/dist/store-sqlite-BmwL3pUR.mjs +0 -2
  152. package/dist/sync-kapso.mjs +0 -89
  153. package/dist/sync.chat.d.mts +0 -2
  154. package/dist/sync.chat.mjs +0 -2
  155. package/dist/users-yryHKFMo.mjs +0 -42
  156. package/docs/02-sync/05-whatsapp.md +0 -152
@@ -1,5 +1,5 @@
1
- import { S as SyncUnit, _ as withUserTokens, d as ResolvedTarget, f as SyncContext, h as UserTokens, o as ProviderPost, r as ProviderDelivery, y as SyncFn } from "./provider-DmSTp9lI.mjs";
2
- import { n as ChatSyncProvider } from "./chat-provider-BWHbChcQ.mjs";
1
+ import { f as SyncContext, g as SyncFn, y as SyncUnit } from "./provider-gynDUoyW.mjs";
2
+ import { n as ChatSyncProvider } from "./chat-provider-BChYrrA7.mjs";
3
3
  import { z } from "zod";
4
4
  import { Adapter } from "chat";
5
5
  //#region src/sync/github.d.ts
@@ -432,9 +432,8 @@ export type GitHubRepo = SyncUnit<GitHubEvent> | {
432
432
  };
433
433
  export type GitHubOptions = {
434
434
  /**
435
- * A Vercel Connect connector (`'github/acme'`): board builds the GitHub adapter
436
- * and people's sign-in, so comments they write on the board post as them.
437
- * Or your own Chat SDK adapter, wrapped in `withUserTokens` to post as people.
435
+ * A Vercel Connect connector (`'github/acme'`): board builds the GitHub adapter.
436
+ * Or your own Chat SDK adapter.
438
437
  */
439
438
  adapter: Adapter | string;
440
439
  /**
@@ -459,18 +458,7 @@ export declare function github(app: string | GitHubOptions, sync: SyncFn<GitHubE
459
458
  */
460
459
  export declare function repos(map: Record<string, GitHubRepo>): SyncFn<GitHubEvent>;
461
460
  export declare class GitHubProvider extends ChatSyncProvider<GitHubEvent> {
462
- private readonly body;
463
461
  constructor(given: GitHubOptions, sync: SyncFn<GitHubEvent>);
464
- whoAmI(token: string): Promise<{
465
- id: string;
466
- handle: string;
467
- name: string;
468
- avatar: string | null;
469
- isBot: boolean;
470
- }>;
471
- postAs(token: string, target: string, kind: ResolvedTarget['kind'], post: ProviderPost): Promise<ProviderDelivery>;
472
- editAs(token: string, thread: string, message: string, post: ProviderPost): Promise<void>;
473
- deleteAs(token: string, thread: string, message: string): Promise<void>;
474
462
  /**
475
463
  * A thread's root is the PR or issue itself, not a comment: its copy has no comment ID
476
464
  * (`root:<event>` or `lifecycle:<event>`), so its reactions go on the PR or issue.
@@ -478,5 +466,4 @@ export declare class GitHubProvider extends ChatSyncProvider<GitHubEvent> {
478
466
  react(thread: string, message: string, emoji: string, added: boolean): Promise<void>;
479
467
  event(context: SyncContext): GitHubEvent;
480
468
  }
481
- //#endregion
482
- export { type UserTokens, withUserTokens };
469
+ //#endregion
@@ -1,7 +1,5 @@
1
- import { c as into, d as withRoutes } from "./provider-BaVmi4Hi.mjs";
2
- import { i as eventSchema, r as envelope } from "./events-qf5XG5VQ.mjs";
3
- import { r as withUserTokens, t as connectUserTokens } from "./users-yryHKFMo.mjs";
4
- import { r as markdownOf, t as ChatSyncProvider } from "./chat-provider-COtsU5jB.mjs";
1
+ import { g as withRoutes, i as eventSchema, p as into, r as envelope } from "./events-lujPA3gQ.mjs";
2
+ import { n as markdownOf, t as ChatSyncProvider } from "./chat-provider-DkFp2hrh.mjs";
5
3
  import { n as tagOf } from "./tags-BpFo6q0_.mjs";
6
4
  import { z } from "zod";
7
5
  import { connectGitHubAdapter } from "@vercel/connect/chat";
@@ -338,35 +336,6 @@ const GitHubEvent = z.discriminatedUnion("type", [
338
336
  ...prTypes.map(prLifecycle),
339
337
  ...issueTypes.map(issueLifecycle)
340
338
  ]);
341
- /** `owner/repo:12`, `owner/repo:issue:3`, or `owner/repo:12:rc:345` (a review comment thread). */
342
- function parseThread(thread) {
343
- const match = /^([^/]+)\/([^:]+):(?:issue:)?(\d+)(?::rc:(\d+))?$/.exec(thread);
344
- if (!match) throw new Error(`invalid GitHub thread: ${thread}`);
345
- return {
346
- owner: match[1],
347
- repo: match[2],
348
- number: Number(match[3]),
349
- review: match[4] ? Number(match[4]) : null
350
- };
351
- }
352
- /** GitHub's REST API with a person's own token: Chat SDK's adapter only holds the app's. */
353
- async function asPerson(token, method, path, body) {
354
- const response = await fetch(`https://api.github.com${path}`, {
355
- method,
356
- headers: {
357
- authorization: `Bearer ${token}`,
358
- accept: "application/vnd.github+json",
359
- "x-github-api-version": "2022-11-28",
360
- ...body === void 0 ? {} : { "content-type": "application/json" }
361
- },
362
- ...body === void 0 ? {} : { body: JSON.stringify(body) }
363
- });
364
- if (!response.ok) {
365
- const detail = await response.text().catch(() => "");
366
- throw Object.assign(/* @__PURE__ */ new Error(`GitHub ${method} ${path}: ${response.status} ${detail.slice(0, 200)}`), { data: { error: `github_${response.status}` } });
367
- }
368
- return response.status === 204 ? null : response.json();
369
- }
370
339
  /**
371
340
  * Syncs GitHub with the board: `app` is a Vercel Connect connector
372
341
  * (`'github/acme'`) or the options, and `sync` is what each event does, both
@@ -428,10 +397,9 @@ function fromConnector(connector, botUserId) {
428
397
  detecting.logger?.warn("Could not detect the GitHub bot user ID", { error });
429
398
  }
430
399
  };
431
- return withUserTokens(adapter, connectUserTokens(connector));
400
+ return adapter;
432
401
  }
433
402
  var GitHubProvider = class extends ChatSyncProvider {
434
- body;
435
403
  constructor(given, sync) {
436
404
  const options = {
437
405
  ...given,
@@ -590,40 +558,6 @@ var GitHubProvider = class extends ChatSyncProvider {
590
558
  };
591
559
  }
592
560
  }, sync);
593
- const converter = options.adapter.formatConverter;
594
- this.body = (post) => {
595
- try {
596
- return converter ? converter.renderPostable({ markdown: post.markdown }) : post.markdown;
597
- } catch {
598
- return post.markdown;
599
- }
600
- };
601
- }
602
- async whoAmI(token) {
603
- const user = await asPerson(token, "GET", "/user");
604
- if (!user.id) throw new Error("GitHub didn't say whose token this is");
605
- return {
606
- ...author(user),
607
- isBot: false
608
- };
609
- }
610
- async postAs(token, target, kind, post) {
611
- if (kind === "channel") throw new Error("github cannot start threads");
612
- const { owner, repo, number, review } = parseThread(target);
613
- const body = this.body(post);
614
- const comment = await (review !== null ? asPerson(token, "POST", `/repos/${owner}/${repo}/pulls/${number}/comments/${review}/replies`, { body }) : asPerson(token, "POST", `/repos/${owner}/${repo}/issues/${number}/comments`, { body }));
615
- return {
616
- thread: target,
617
- message: String(comment.id)
618
- };
619
- }
620
- async editAs(token, thread, message, post) {
621
- const { owner, repo, review } = parseThread(thread);
622
- await asPerson(token, "PATCH", `/repos/${owner}/${repo}/${review !== null ? "pulls" : "issues"}/comments/${message}`, { body: this.body(post) });
623
- }
624
- async deleteAs(token, thread, message) {
625
- const { owner, repo, review } = parseThread(thread);
626
- await asPerson(token, "DELETE", `/repos/${owner}/${repo}/${review !== null ? "pulls" : "issues"}/comments/${message}`);
627
561
  }
628
562
  /**
629
563
  * A thread's root is the PR or issue itself, not a comment: its copy has no comment ID
@@ -676,4 +610,4 @@ var GitHubProvider = class extends ChatSyncProvider {
676
610
  }
677
611
  };
678
612
  //#endregion
679
- export { GitHubEvent, GitHubPayload, GitHubProvider, github, repos, withUserTokens };
613
+ export { GitHubEvent, GitHubPayload, GitHubProvider, github, repos };
@@ -1,6 +1,6 @@
1
- import { mt as SourceRead } from "./client-BqAjk0Q4.mjs";
2
- import { S as SyncUnit, c as ReceivedEvent, d as ResolvedTarget, f as SyncContext, o as ProviderPost, r as ProviderDelivery, y as SyncFn } from "./provider-DmSTp9lI.mjs";
3
- import { n as ChatSyncProvider, t as ChatInput } from "./chat-provider-BWHbChcQ.mjs";
1
+ import { mt as SourceRead } from "./client-COfYrgfV.mjs";
2
+ import { c as ReceivedEvent, d as ResolvedTarget, f as SyncContext, g as SyncFn, o as ProviderPost, r as ProviderDelivery, y as SyncUnit } from "./provider-gynDUoyW.mjs";
3
+ import { n as ChatSyncProvider, t as ChatInput } from "./chat-provider-BChYrrA7.mjs";
4
4
  import { z } from "zod";
5
5
  import { Adapter } from "chat";
6
6
  //#region src/sync/linear.d.ts
@@ -1,6 +1,5 @@
1
- import { i as UnreadableError, s as channels } from "./provider-BaVmi4Hi.mjs";
2
- import { i as eventSchema, r as envelope } from "./events-qf5XG5VQ.mjs";
3
- import { r as markdownOf, t as ChatSyncProvider } from "./chat-provider-COtsU5jB.mjs";
1
+ import { f as channels, i as eventSchema, l as UnreadableError, r as envelope } from "./events-lujPA3gQ.mjs";
2
+ import { n as markdownOf, t as ChatSyncProvider } from "./chat-provider-DkFp2hrh.mjs";
4
3
  import { n as tagOf, t as slug } from "./tags-BpFo6q0_.mjs";
5
4
  import "zod";
6
5
  import { connectLinearAdapter } from "@vercel/connect/chat";
@@ -37,6 +36,8 @@ const bodyOf = (description) => description?.trim() || null;
37
36
  * An issue's properties as board labels: `priority=high`, `project=v0-accessibility`, a label per
38
37
  * Linear label (`tag.bug`), and its assignee (`assignee.ada`). Only what the payload says: a
39
38
  * property it carries empty is unset, and one it names without its details is left as it was.
39
+ * Linear leaves an empty assignee or project out of the payload rather than sending `null`, so
40
+ * either one missing, with no ID either, is empty.
40
41
  * `priority` and `project` are single keys, so they're unset by name; `tag.` and `assignee.` are
41
42
  * prefixes the mirror owns, so a key under them that Linear doesn't have goes.
42
43
  */
@@ -47,7 +48,7 @@ function propertiesOf(data) {
47
48
  if (data.priority === 0 || data.priorityLabel === "No priority") unset.push("priority");
48
49
  else if (data.priorityLabel && slug(data.priorityLabel)) labels.priority = slug(data.priorityLabel).slice(0, 256);
49
50
  if (data.project?.name && slug(data.project.name)) labels.project = slug(data.project.name).slice(0, 256);
50
- else if (data.project === null || data.projectId === null) unset.push("project");
51
+ else if (!data.project && !data.projectId) unset.push("project");
51
52
  if (Array.isArray(data.labels)) {
52
53
  owns.push("tag.");
53
54
  for (const label of data.labels) {
@@ -59,7 +60,7 @@ function propertiesOf(data) {
59
60
  owns.push("assignee.");
60
61
  const tag = tagOf("assignee", handleOf(data.assignee));
61
62
  if (tag) labels[tag[0]] = tag[1];
62
- } else if (data.assignee === null || data.assigneeId === null) owns.push("assignee.");
63
+ } else if (!data.assigneeId) owns.push("assignee.");
63
64
  return {
64
65
  labels,
65
66
  unset,
@@ -1,6 +1,6 @@
1
- import { f as ExternalLink } from "./folds-ZkHM4M-v.mjs";
2
- import { C as channels, _ as withUserTokens, d as ResolvedTarget, f as SyncContext, h as UserTokens, o as ProviderPost, r as ProviderDelivery, y as SyncFn } from "./provider-DmSTp9lI.mjs";
3
- import { n as ChatSyncProvider, t as ChatInput } from "./chat-provider-BWHbChcQ.mjs";
1
+ import { f as ExternalLink } from "./folds-DLUzfibJ.mjs";
2
+ import { b as channels, d as ResolvedTarget, f as SyncContext, g as SyncFn, o as ProviderPost, r as ProviderDelivery } from "./provider-gynDUoyW.mjs";
3
+ import { n as ChatSyncProvider, t as ChatInput } from "./chat-provider-BChYrrA7.mjs";
4
4
  import { z } from "zod";
5
5
  import { Adapter } from "chat";
6
6
  //#region src/sync/slack.d.ts
@@ -165,6 +165,7 @@ type SlackMessage = {
165
165
  files?: SlackFile[];
166
166
  thread_ts?: string;
167
167
  reply_count?: number;
168
+ attachments?: SlackAttachment[];
168
169
  };
169
170
  /** What Slack attaches to a message that links another: `is_msg_unfurl` marks a message link's. */
170
171
  type SlackAttachment = {
@@ -343,9 +344,8 @@ export declare const SlackEvent: z.ZodDiscriminatedUnion<[z.ZodObject<{
343
344
  export type SlackEvent = z.infer<typeof SlackEvent>;
344
345
  export type SlackOptions = {
345
346
  /**
346
- * A Vercel Connect connector (`'slack/acme'`): board builds the Slack adapter,
347
- * its Web API client, and people's sign-in, so messages they write post as them.
348
- * Or your own Chat SDK adapter, wrapped in `withUserTokens` to post as people.
347
+ * A Vercel Connect connector (`'slack/acme'`): board builds the Slack adapter
348
+ * and its Web API client. Or your own Chat SDK adapter.
349
349
  */
350
350
  adapter: Adapter | string;
351
351
  /** Not needed with a connector. */
@@ -401,4 +401,4 @@ export declare class SlackProvider extends ChatSyncProvider<SlackEvent> {
401
401
  event(context: SyncContext): SlackEvent;
402
402
  }
403
403
  //#endregion
404
- export { type UserTokens, channels, withUserTokens };
404
+ export { channels };
@@ -1,9 +1,7 @@
1
1
  import { m as MESSAGE_TEXT_MAX } from "./schemas-DBmUO6pr.mjs";
2
2
  import "./external-D3p5T57R.mjs";
3
- import { i as UnreadableError, s as channels } from "./provider-BaVmi4Hi.mjs";
4
- import { i as eventSchema, r as envelope } from "./events-qf5XG5VQ.mjs";
5
- import { r as withUserTokens, t as connectUserTokens } from "./users-yryHKFMo.mjs";
6
- import { r as markdownOf, t as ChatSyncProvider } from "./chat-provider-COtsU5jB.mjs";
3
+ import { f as channels, i as eventSchema, l as UnreadableError, r as envelope } from "./events-lujPA3gQ.mjs";
4
+ import { n as markdownOf, t as ChatSyncProvider } from "./chat-provider-DkFp2hrh.mjs";
7
5
  import { z } from "zod";
8
6
  import { stringifyMarkdown } from "chat";
9
7
  import { createSlackAdapter } from "@chat-adapter/slack";
@@ -236,13 +234,13 @@ const SLACK_CLIENT = {
236
234
  },
237
235
  timeout: 3e4
238
236
  };
239
- /** A Slack adapter, client, and people's tokens, all from one Vercel Connect connector. */
237
+ /** A Slack adapter and its client, from one Vercel Connect connector. */
240
238
  function fromConnector(connector) {
241
239
  const credentials = connectSlackAdapter(connector);
242
- const adapter = withUserTokens(createSlackAdapter({
240
+ const adapter = createSlackAdapter({
243
241
  ...credentials,
244
242
  webClientOptions: SLACK_CLIENT
245
- }), connectUserTokens(connector));
243
+ });
246
244
  const botToken = credentials.botToken;
247
245
  const client = async () => adapter.withBotToken(typeof botToken === "function" ? await botToken() : botToken, () => adapter.webClient);
248
246
  return {
@@ -263,7 +261,6 @@ var SlackProvider = class extends ChatSyncProvider {
263
261
  ...given,
264
262
  adapter: given.adapter
265
263
  };
266
- const tokens = options.adapter;
267
264
  let directClient;
268
265
  if (!options.client) try {
269
266
  directClient = options.adapter.webClient;
@@ -457,47 +454,45 @@ var SlackProvider = class extends ChatSyncProvider {
457
454
  return null;
458
455
  }
459
456
  },
460
- async fetchLink(link) {
457
+ async fetchLink(link, linker) {
461
458
  const [, channel, thread] = link.thread.split(":");
462
459
  const web = await client();
463
- if (!web?.conversations.replies || !channel || !thread) return {
460
+ const replies = web?.conversations.replies;
461
+ if (!web || !replies || !channel || !thread) return {
464
462
  snapshot: null,
465
463
  unavailable: "no Slack client to read it with"
466
464
  };
465
+ const one = async (channel, thread, ts) => (await replies({
466
+ channel,
467
+ ts: thread,
468
+ oldest: ts,
469
+ latest: ts,
470
+ inclusive: true,
471
+ limit: 1
472
+ })).messages?.find((m) => m.ts === ts);
467
473
  let found;
474
+ let unavailable = "message_not_found";
468
475
  try {
469
476
  const info = (await web.conversations.info({ channel })).channel;
470
- if (!info) return {
471
- snapshot: null,
472
- unavailable: "channel_not_found"
473
- };
474
- if (info.is_private || info.is_im || info.is_mpim) return {
475
- snapshot: null,
476
- unavailable: "private_channel"
477
- };
478
- if (!info.is_member) return {
479
- snapshot: null,
480
- unavailable: "not_in_channel"
481
- };
482
- const { messages } = await web.conversations.replies({
483
- channel,
484
- ts: thread,
485
- oldest: link.message,
486
- latest: link.message,
487
- inclusive: true,
488
- limit: 1
489
- });
490
- found = messages?.find((m) => m.ts === link.message);
477
+ if (!info) unavailable = "channel_not_found";
478
+ else if (info.is_private || info.is_im || info.is_mpim) unavailable = "private_channel";
479
+ else if (!info.is_member) unavailable = "not_in_channel";
480
+ else found = await one(channel, thread, link.message);
491
481
  } catch (error) {
492
- return {
482
+ unavailable = slackError(error);
483
+ }
484
+ if (!found) {
485
+ const [, at, root] = linker.thread.split(":");
486
+ const linking = at && root ? await one(at, root, linker.message).catch(() => void 0) : void 0;
487
+ const unfurled = linking ? slackLinks("", linking.text ?? "", linking.attachments, mrkdwn).find((known) => known.url === link.url)?.snapshot : null;
488
+ return unfurled ? {
489
+ snapshot: unfurled,
490
+ unavailable: null
491
+ } : {
493
492
  snapshot: null,
494
- unavailable: slackError(error)
493
+ unavailable
495
494
  };
496
495
  }
497
- if (!found) return {
498
- snapshot: null,
499
- unavailable: "message_not_found"
500
- };
501
496
  return {
502
497
  unavailable: null,
503
498
  snapshot: {
@@ -645,22 +640,6 @@ var SlackProvider = class extends ChatSyncProvider {
645
640
  groupThread: (thread) => thread,
646
641
  isRoot: (thread, messageId) => thread.slice(thread.lastIndexOf(":") + 1) === messageId,
647
642
  context: () => null,
648
- ...typeof tokens.withBotToken === "function" ? {
649
- runAs: (token, fn) => tokens.withBotToken(token, fn),
650
- async whoAmI(token) {
651
- const me = await tokens.withBotToken(token, () => tokens.webClient.auth.test());
652
- if (!me.user_id) throw new Error("Slack didn't say whose token this is");
653
- const user = (await (await client().catch(() => void 0))?.users?.info({ user: me.user_id }).catch(() => void 0))?.user;
654
- const handle = user?.profile?.display_name || user?.name || me.user || me.user_id;
655
- return {
656
- id: `slack:user:${me.user_id}`,
657
- handle,
658
- name: user?.profile?.real_name || user?.real_name || handle,
659
- avatar: user?.profile?.image_72 ?? user?.profile?.image_48 ?? null,
660
- isBot: false
661
- };
662
- }
663
- } : {},
664
643
  async resolveTarget(target) {
665
644
  const at = target.indexOf(":");
666
645
  const channel = await this.resolveChannel(at === -1 ? target : target.slice(0, at));
@@ -836,4 +815,4 @@ var SlackProvider = class extends ChatSyncProvider {
836
815
  }
837
816
  };
838
817
  //#endregion
839
- export { SlackEvent, SlackProvider, channels, forwardText, slack, slackFileName, slackIcon, slackLinks, withUserTokens, withoutHtml, withoutLabel };
818
+ export { SlackEvent, SlackProvider, channels, forwardText, slack, slackFileName, slackIcon, slackLinks, withoutHtml, withoutLabel };
package/dist/sync.d.mts CHANGED
@@ -1,3 +1,3 @@
1
- import { d as ExternalFile, l as ExternalAuthor, u as ExternalEvent } from "./folds-ZkHM4M-v.mjs";
2
- import { A as SyncOverrides, C as channels, D as EventBase, E as render, M as SyncRoot, N as envelope, O as ProviderEvent, P as eventSchema, S as SyncUnit, T as mirror, _ as withUserTokens, a as ProviderOptions, b as SyncIn, c as ReceivedEvent, d as ResolvedTarget, f as SyncContext, g as connectUserTokens, h as UserTokens, i as ProviderFile, j as SyncResult, k as RootOverride, l as RenderInput, m as UnreadableError, n as FileDelivery, o as ProviderPost, p as SyncProvider, r as ProviderDelivery, s as Receive, u as Renderer, v as Sync, w as into, x as SyncOut, y as SyncFn } from "./provider-DmSTp9lI.mjs";
3
- export { EventBase, type ExternalAuthor, ExternalEvent, type ExternalFile, type FileDelivery, type ProviderDelivery, type ProviderEvent, type ProviderFile, type ProviderOptions, type ProviderPost, type Receive, ReceivedEvent, type RenderInput, type Renderer, ResolvedTarget, type RootOverride, type Sync, type SyncContext, type SyncFn, type SyncIn, type SyncOut, SyncOverrides, SyncProvider, type SyncResult, type SyncRoot, type SyncUnit, UnreadableError, type UserTokens, channels, connectUserTokens, envelope, eventSchema, into, mirror, render, withUserTokens };
1
+ import { d as ExternalFile, l as ExternalAuthor, u as ExternalEvent } from "./folds-DLUzfibJ.mjs";
2
+ import { A as envelope, C as render, D as SyncOverrides, E as RootOverride, O as SyncResult, S as mirror, T as ProviderEvent, _ as SyncIn, a as ProviderOptions, b as channels, c as ReceivedEvent, d as ResolvedTarget, f as SyncContext, g as SyncFn, h as Sync, i as ProviderFile, j as eventSchema, k as SyncRoot, l as RenderInput, m as UnreadableError, n as FileDelivery, o as ProviderPost, p as SyncProvider, r as ProviderDelivery, s as Receive, u as Renderer, v as SyncOut, w as EventBase, x as into, y as SyncUnit } from "./provider-gynDUoyW.mjs";
3
+ export { EventBase, type ExternalAuthor, ExternalEvent, type ExternalFile, type FileDelivery, type ProviderDelivery, type ProviderEvent, type ProviderFile, type ProviderOptions, type ProviderPost, type Receive, ReceivedEvent, type RenderInput, type Renderer, ResolvedTarget, type RootOverride, type Sync, type SyncContext, type SyncFn, type SyncIn, type SyncOut, SyncOverrides, SyncProvider, type SyncResult, type SyncRoot, type SyncUnit, UnreadableError, channels, envelope, eventSchema, into, mirror, render };
package/dist/sync.mjs CHANGED
@@ -1,5 +1,3 @@
1
1
  import { n as ExternalEvent } from "./external-D3p5T57R.mjs";
2
- import { c as into, i as UnreadableError, l as mirror, n as ResolvedTarget, r as SyncProvider, s as channels, t as ReceivedEvent, u as render } from "./provider-BaVmi4Hi.mjs";
3
- import { i as eventSchema, n as SyncOverrides, r as envelope, t as EventBase } from "./events-qf5XG5VQ.mjs";
4
- import { r as withUserTokens, t as connectUserTokens } from "./users-yryHKFMo.mjs";
5
- export { EventBase, ExternalEvent, ReceivedEvent, ResolvedTarget, SyncOverrides, SyncProvider, UnreadableError, channels, connectUserTokens, envelope, eventSchema, into, mirror, render, withUserTokens };
2
+ import { c as SyncProvider, f as channels, h as render, i as eventSchema, l as UnreadableError, m as mirror, n as SyncOverrides, o as ReceivedEvent, p as into, r as envelope, s as ResolvedTarget, t as EventBase } from "./events-lujPA3gQ.mjs";
3
+ export { EventBase, ExternalEvent, ReceivedEvent, ResolvedTarget, SyncOverrides, SyncProvider, UnreadableError, channels, envelope, eventSchema, into, mirror, render };
@@ -1,5 +1,5 @@
1
- import { l as unauthorized } from "./errors-CusN4x2l.mjs";
2
- import { f as markVerified, m as trustedIssuers, n as TOKENS, r as VERCEL_ISSUER, u as issuerName } from "./auth-JO22iIwr.mjs";
1
+ import { l as unauthorized } from "./errors-CJXhhzvT.mjs";
2
+ import { a as VERCEL_ISSUER, b as trustedIssuers, g as markVerified, i as TOKENS, m as issuerName, v as privateHost } from "./auth-CyO2aw5n.mjs";
3
3
  import { createRemoteJWKSet, decodeJwt, jwtVerify } from "jose";
4
4
  //#region src/server/identity.ts
5
5
  let fromDisk;
@@ -95,13 +95,22 @@ function factsOf(issuer, iss, claims) {
95
95
  }
96
96
  const issuerMatches = (iss, accepted) => typeof accepted === "string" ? iss === accepted : accepted.test(iss);
97
97
  const remoteKeys = /* @__PURE__ */ new Map();
98
+ /**
99
+ * An issuer's keys, by OIDC Discovery 1.0: its document under the issuer, naming that issuer
100
+ * (§4.3), and its keys at an https `jwks_uri` (§3), which must be on the internet, as a grant's
101
+ * issuer must. Like jose's own fetch of the keys, it follows no redirect.
102
+ */
98
103
  async function keysFor(iss) {
99
104
  const cached = remoteKeys.get(iss);
100
105
  if (cached) return cached;
101
- const response = await fetch(`${iss.replace(/\/+$/, "")}/.well-known/openid-configuration`);
102
- if (!response.ok) throw unauthorized(`cannot load keys for issuer ${iss}`);
103
- const { jwks_uri } = await response.json();
104
- const keys = createRemoteJWKSet(new URL(jwks_uri));
106
+ const unknown = (why) => unauthorized(`cannot load keys for issuer ${iss}: ${why}`);
107
+ const response = await fetch(`${iss.replace(/\/+$/, "")}/.well-known/openid-configuration`, { redirect: "manual" });
108
+ if (response.status !== 200) throw unknown(`its discovery answered ${response.status}`);
109
+ const config = await response.json().catch(() => null);
110
+ if (config?.issuer !== iss) throw unknown("its discovery names another issuer");
111
+ const jwks = typeof config.jwks_uri === "string" ? URL.parse(config.jwks_uri) : null;
112
+ if (jwks?.protocol !== "https:" || privateHost(jwks.hostname)) throw unknown("its jwks_uri isn't an https URL on the internet");
113
+ const keys = createRemoteJWKSet(jwks);
105
114
  remoteKeys.set(iss, keys);
106
115
  return keys;
107
116
  }
@@ -1,5 +1,5 @@
1
- import { a as ExternalCopy, c as SyncedThread, g as BoardEvent, o as Message } from "./folds-ZkHM4M-v.mjs";
2
- import { t as A2Store } from "./store-D0MhBvg6-4Z2L0Kqd.mjs";
1
+ import { a as ExternalCopy, c as SyncedThread, g as BoardEvent, o as Message } from "./folds-DLUzfibJ.mjs";
2
+ import { t as A2Store } from "./store-DEzcHZOA-B-5vhxef.mjs";
3
3
  //#region src/store/types.d.ts
4
4
  /**
5
5
  * What a query matches, resolved by the board: handles are already actor and
@@ -84,8 +84,6 @@ interface MessageTables {
84
84
  messageOf(externalThread: string, platformMessage: string): Promise<number | null>;
85
85
  /** Every platform thread a board thread lives in. */
86
86
  externalThreads(): Promise<string[]>;
87
- /** A person's own roots posted at or after `since` (epoch ms). */
88
- recentRoots(principal: string, since: number): Promise<Message[]>;
89
87
  /** The messages that carry an attachment, by ID. A deleted message carries none. */
90
88
  withAttachment(attachment: string): Promise<Message[]>;
91
89
  close(): Promise<void>;