@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
@@ -1,5 +1,5 @@
1
- import{C as e,D as t,E as n,S as r,T as i,_ as a,a as o,b as s,c,d as l,f as u,g as d,h as f,i as p,l as m,m as h,n as g,o as _,p as v,r as y,s as b,t as x,u as S,v as C,w,x as T,y as E}from"./app.js";const D=/^\[vc\]: #\S*\r?\n/,O=/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g,k=/!\[([^\]]+)\]\(https:\/\/vercel\.com\/static\/status\/[^)]+\)\s*(?:\[[^\]]*\]\((https?:\/\/[^)\s]+)\))?/;function A(e){if(!D.test(e))return null;let t=e.search(/<details><summary>\d+ Skipped Deployments?<\/summary>/),n=[],r=0;for(let i of e.split(/\r?\n/)){let a=r;if(r=e.indexOf(`
2
- `,r)+1||e.length,!i.startsWith(`|`)||/^\|\s*(Project|Name)\s*\|/.test(i)||/^\|\s*:?-/.test(i))continue;let o=i.split(`|`).slice(1,-1).map(e=>e.trim());if(o.length<2)continue;let s=k.exec(o[1]);if(!s)continue;let c=[...o[0].matchAll(O)].at(-1),l=c?.[1]??o[0].replace(/<[^>]*>/g,``).trim();if(!l)continue;let u=o[2]?[...o[2].matchAll(O)].find(e=>/preview/i.test(e[1])):void 0,d=o[3]?.match(/<relative-time[^>]*>([^<]+)<\/relative-time>/)?.[1]?.trim()??(o[3]?.replace(/<[^>]*>/g,``).trim()||null);n.push({project:l,projectUrl:c?.[2]??null,status:s[1].trim(),deploymentUrl:s[2]??null,previewUrl:u?.[2]??null,updated:d,skipped:t!==-1&&a>t})}return n.length>0?n:null}const j={ready:`✅`,building:`⏳`,queued:`⏳`,initializing:`⏳`,error:`❌`,failed:`❌`,canceled:`⚪`,ignored:`⚪`},M=(e,t)=>t?`[${e}](${t})`:e;function N(e){return e.map(e=>`- ${[`${j[e.status.toLowerCase()]??`▲`} **${M(e.project,e.projectUrl)}**`,`${M(e.status,e.deploymentUrl)}${e.skipped?` (skipped)`:``}`,...e.previewUrl?[M(`Preview`,e.previewUrl)]:[],...e.updated?[e.updated]:[]].join(` · `)}`).join(`
3
- `)}function P(e,t){let n=t===`github:user:35613825`?A(e):null;return n?`**▲ Vercel**\n${N(n)}`:e}const ee=e=>e>10,te=e=>{let t=e.indexOf(`:`),n=e.slice(t+1).indexOf(`:`);return n===-1?e:e.slice(0,t+1+n)};function ne(e){try{Reflect.get(globalThis,Symbol.for(`@vercel/request-context`))?.get?.()?.waitUntil?.(e)}catch{}}s({id:T().min(1),platform:T().min(1),key:T().min(1),...S.shape,raw:r()});const re=s({root:E(e=>typeof e==`function`).optional(),text:T().optional(),labels:v.optional(),unset:a(u).optional(),noSync:C().optional()}).strict();function F(e){if(e&&typeof e==`object`){for(let t of Object.values(e))F(t);Object.freeze(e)}return e}const ie=s({target:T().min(1),channel:T().min(1),kind:d([`channel`,`thread`])}).strict();var ae=class extends Error{code;constructor(e,t){super(t),this.code=e}};const oe=s({event:m,raw:r(),dedupe:T().min(1)}),I=(e,t=!1)=>{let n=f(e),r=n.trim()===``?t?``:`(no text)`:n;return r.length>1e4?`${r.slice(0,h-1).toWellFormed()}…`:r},L=(e,t,n=e.name??`a file`)=>`📎 ${n}${e.size===void 0?``:` (${i(e.size)})`} didn't come over: ${t}`,R=e=>e instanceof o&&e.code===`no_files`,z=e=>{let t=e.name??`a file`;if(!e.url||!/^https?:\/\//.test(e.url))return L(e,`this board keeps no files`);let n=t.replace(/[[\]\\]/g,e=>`\\${e}`),r=e.url.replace(/[ ()]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`);return L(e,`this board keeps no files`,`[${n}](${r})`)},B=(e,t)=>t.length?`${e.trim()?`${e}\n\n`:``}${t.join(`
4
- `)}`:e,se=e=>e.split(`
5
- `).filter(e=>/^📎 .* didn't come over: /u.test(e)),V=e=>JSON.stringify([e.url,e.message,e.snapshot,e.unavailable]),ce=(e,t)=>e.length===t.length&&e.every((e,n)=>V(e)===V(t[n])),le=/^\s*`?(vb-[a-z0-9]{4}-[a-z0-9]{4})`?\s*$/i,H=6e4,U=new AbortController().signal,ue=e=>{try{return JSON.parse(JSON.stringify(e??null))}catch{return null}},W=e=>typeof e==`string`?f(e):Array.isArray(e)?e.map(W):typeof e==`object`&&e?Object.fromEntries(Object.entries(e).map(([e,t])=>[f(e),W(t)])):e,G=e=>{let t=e?.data?.error,n=typeof t==`string`?t:e instanceof Error?e.message:String(e);return f(n.slice(0,500))||`unknown error`},K=e=>e instanceof o&&e.status===404;function q(r){let a=null,s=()=>{if(!a)throw Error(`sync used before board() bound it`);return a},u={self:`sync`,origin:{via:{kind:`engine`}}};function d(e,t,n=U,r=u){let i=s().backend;return i[e].call(i,r,t,n)}let f=e=>{let t=r[e];if(!t)throw _(`no sync entry named ${e}`);return t},m=e=>e.slice(0,e.indexOf(`:`)),h=e=>r[m(e)]!==void 0,v=e=>e.slice(e.indexOf(`:`)+1);async function S(e){try{return await d(`syncThread`,{thread:e})}catch(e){if(K(e))return null;throw e}}async function C(e){try{let{message:t,replies:n}=await d(`message`,{id:e});return[t,...n]}catch(e){if(K(e))return[];throw e}}async function T(e){return(await C(e)).find(t=>t.id===e)??null}async function E(e){return e.length===0?{}:(await d(`syncCopies`,{message:[...new Set(e)]})).copies}let D=(e,t)=>d(`syncFind`,e,t);async function O(){let{actors:e}=await d(`actors`,{}),t={byId:{},links:{}};for(let{person:n,...r}of e)t.byId[r.id]={id:r.id,kind:r.kind,handle:r.handle,name:r.name,avatar:r.avatar},n&&(t.links[r.id]=n.id);return t}let k=(e,t)=>Object.entries(e.links).filter(([,e])=>e===t).map(([e])=>e),A=/* @__PURE__ */ new Map,j=e=>{let t=A.get(e);if(t&&(t.complete||Date.now()-t.at<6e4))return t.map;let n=f(e),r=Object.entries(n.mappings),i={complete:!0,at:Date.now(),map:Promise.resolve(/* @__PURE__ */ new Map)};return i.map=Promise.allSettled(r.map(([e])=>n.resolveChannel(e))).then(t=>{let n=/* @__PURE__ */ new Map;return t.forEach((t,a)=>{let[o,s]=r[a];t.status===`fulfilled`?n.set(`${e}:${t.value}`,s):(i.complete=!1,console.warn(`[board] sync ${e}: skipping ${o}: ${t.reason.message}`))}),n}),A.set(e,i),i.map};async function M(e){for(let t of Object.values(r)){let n=await t.findByEmail(e).catch(()=>null);if(n)return{id:n.id,kind:`external`,handle:n.handle,name:n.name,avatar:n.avatar}}return null}async function N(e){let t=[];for(let[n,i]of Object.entries(r))if(i.canStartThreads)for(let[r,i]of await j(n))i.channel===e&&t.push({target:r,kind:`channel`});return t}async function V(e){let t=e.indexOf(`:`);if(t<=0)throw _(`sync targets look like <key>:<channel or thread>, got: ${e}`);let n=e.slice(0,t),r=f(n);try{let i=ie.parse(await r.resolveTarget(e.slice(t+1)));if(i.kind===`channel`&&!r.canStartThreads)throw Error(`${r.platform} cannot start threads; use an existing thread`);return{target:`${n}:${i.target}`,kind:i.kind}}catch(t){throw _(`can't sync to ${e}: ${t.message}`)}}async function q(e,t){let{event:n,raw:r,dedupe:i}=oe.parse(W({...t,raw:ue(t.raw)}));for(let t of[n.channel,n.thread,`subthread`in n?n.subthread:null])if(t!==null&&!t.startsWith(`${e}:`))throw _(`provider locations must belong to sync entry ${e}`);((await j(e)).has(n.channel)||await de(n.thread))&&await d(`inboxPush`,{key:e,events:[{id:i,event:n,raw:r}]})}async function de(e){try{return(await D({thread:e},AbortSignal.timeout(2e3))).thread!==null}catch(e){let t=e?.name;if(t===`TimeoutError`||t===`AbortError`)return!0;throw e}}async function fe(e,t){let n=r[e];if(!n)throw b(`no sync entry named ${e}`);let i;try{i=await n.receive(e,t,t=>q(e,t))}catch(e){return console.error(`[board] intake failed`,e),new Response(`intake failed`,{status:500})}return ne(J([e]).catch(t=>console.error(`[board] couldn't pull ${e}'s inbox:`,G(t)))),i}async function J(e=Object.keys(r)){let t=0,n=Date.now()+24e4;for(let r of e)for(;Date.now()<n;){let{items:e}=await d(`inboxNext`,{key:r,limit:10});if(e.length===0)break;await Promise.all(e.map(e=>pe(r,e))),t+=e.length}return t}async function pe(e,t){let n;try{await ge(e,{event:t.event,raw:t.raw},{id:t.id,attempt:t.attempt+1})}catch(r){console.error(`[board] sync ${e} couldn't import ${t.id}:`,G(r)),n=G(r)}await d(`inboxAck`,{key:e,id:t.id,lease:t.lease,...n===void 0?{}:{error:n}}).catch(e=>console.error(`[board] couldn't ack an inbox event:`,G(e)))}async function me(e,t,n,r=[]){if(t.length===0)return[];let i=[];for(let a of t){if(i.some(e=>e.url===a.url))continue;let t=(await D({thread:a.thread,message:a.message})).message,o=t===null?null:await T(t);if(o&&!o.deleted&&o.channel===n){i.push({url:a.url,message:o.id,unavailable:null,snapshot:{author:o.author?.name??o.by.actor,text:o.text,createdAt:o.createdAt,files:o.attachments.map(e=>({name:e.name,url:null}))}});continue}if(a.snapshot){i.push({url:a.url,message:null,snapshot:a.snapshot,unavailable:null});continue}let s=r.find(e=>e.url===a.url&&e.snapshot&&e.message===null);if(s){i.push(s);continue}let c=await e.fetchLink(a).catch(e=>({snapshot:null,unavailable:G(e)}));i.push({url:a.url,message:null,snapshot:c.snapshot,unavailable:c.snapshot?null:(c.unavailable??`unknown`).slice(0,300)})}return W(i)}async function he(t,r,i){let a=[],s=[],c=!1;for(let l of r.slice(0,10)){if(c){s.push(z(l));continue}if(l.size!==void 0&&l.size>26214400){s.push(L(l,`over ${w/1024/1024} MB`));continue}let r;try{let e=await t.fetchFile(l);if(e===null){s.push(L(l,`${t.platform} can't hand it over`));continue}r=e}catch(e){if(i<3)throw e;console.warn(`[board] couldn't fetch ${l.name??`a file`} from ${t.platform}:`,G(e)),s.push(L(l,`${t.platform} wouldn't hand it over`));continue}if(r.byteLength>26214400){s.push(L(l,`over ${w/1024/1024} MB`));continue}if(r.byteLength===0){s.push(L(l,`it was empty`));continue}let u=l.name??`${l.type}`;try{a.push((await d(`upload`,{data:r,name:u,type:l.mimeType||n(u)})).attachment)}catch(t){if(t instanceof o&&(t.code===`too_large`||t.code===`no_direct_uploads`)){s.push(L(l,`over ${e/1024/1024} MB`));continue}if(!R(t))throw t;c=!0,s.push(z(l))}}for(let e of r.slice(10))s.push(L(e,`a message carries at most 10`));return{attachments:a,notes:s}}async function ge(e,t,n){let{event:r,raw:i}=t,a=f(e),o=(await j(e)).get(r.channel),c=(await D({thread:r.thread})).thread??void 0;if(r.kind===`message`){let t=(await D({thread:r.subthread??r.thread,message:r.message})).message;if(t!==null){let e=(await T(t))?.origin;if(!e||e.message!==r.message||(e.subthread??e.thread)!==(r.subthread??r.thread))return}else if(a.users&&n.attempt<6&&await ve(c,r))throw Error(`holding ${r.message} until ${r.author.id}'s board message there has its receipt`);let i=r.author.isBot?void 0:le.exec(r.text)?.[1];if(i&&await ye(e,r,i))return}let u=`author`in r?r.author:{id:`${a.platform}:system`,handle:a.platform,name:a.platform,avatar:null,isBot:!0},d=s().client({author:u,key:e,..._e(e,r)},n.id),p,m=!0,h={id:n.id,key:e,event:r,raw:i,board:d,apply(i,a={}){if(!m)throw _(`the sync callback has finished`);if(p)throw _(`an event can only be imported once per callback`);return p=we(e,t,n,l.parse(i),re.parse(a)).catch(async e=>{if(!(e instanceof y))throw e;let t=(await D({thread:r.thread})).thread;return{root:t===null?null:(await S(t))?.root??null,message:null}}),p},client(e){async function t(t,n,r){if(!m)throw _(`the sync callback has finished`);if(typeof n==`string`)return d.sync(t,n);if(typeof t!=`string`||n!==e)throw _(`sync only accepts the current verified event`);return a.sync(h,t,r)}return{...d,sync:t}}};try{if(o?.run){await o.run(h);return}let e=o?.channel??(c?(await S(c))?.channel:void 0);e&&await a.sync(h,e)}finally{m=!1}}function _e(e,t){let n=f(e),r=(`subthread`in t?t.subthread:null)??t.thread;return{via:{kind:`sync`,key:e,platform:n.platform,channel:t.channel,thread:r},respond:async e=>{await n.post(v(r),`thread`,{markdown:e})}}}async function ve(e,t){let n=(await D({account:t.author.id})).person;if(!n||n.method!==`consent`)return!1;let r=Date.now(),i=(e,t,i)=>e.by.principal===n.id&&e.by.profile===null&&e.from===null&&!e.deleted&&!e.noSync&&r-Date.parse(e.createdAt)<H&&!i[t]&&!e.undelivered[t];if(e!==void 0){let n=await S(e);if(!n)return!1;let r=n.targets.filter(e=>n.external[e]===t.thread),a=await C(n.root),o=await E(a.map(e=>e.id));return a.some(e=>r.some(t=>i(e,t,o[e.id]??{})))}let{roots:a}=await d(`syncRoots`,{by:n.id,since:r-H}),o=await E(a.map(e=>e.id));for(let e of a){let n=await S(e.thread);if(n?.targets.includes(t.channel)&&!n.external[t.channel]&&i(e,t.channel,o[e.id]??{}))return!0}return!1}async function ye(e,t,n){try{await d(`syncAccount`,{sync:e,account:t.author,code:n,message:x(t.subthread??t.thread,t.message)})}catch(e){if(K(e))return!1;throw e}return await f(e).react(v(t.subthread??t.thread),t.message,`check`,!0).catch(t=>console.warn(`[board] couldn't confirm a linking code on ${e}:`,G(t))),!0}let be=e=>f(e).users!==null;async function xe(e,t,n){let r=f(e);if(!r.users)throw _(`${e} can't sign people in: link it with a code`);return r.users.authorize(t,n?{callback:n}:{})}async function Se(e,t){let n=f(e);if(!n.users)throw _(`${e} can't sign people in: link it with a code`);let r=await n.users.token(t);if(!r)return null;let i=await n.whoAmI(r);if(!i.id.startsWith(`${n.platform}:user:`))throw _(`${e} named an account that isn't a ${n.platform} user`);return i}async function Ce(e,t,n){let r=await Se(e,t);return r?(await d(`syncAccount`,{sync:e,account:r,code:n,method:`consent`})).link:null}async function we(e,n,r,i,a){let{event:o,raw:s}=n,l=_e(e,o),p=f(e),m=(await D({thread:o.thread})).thread??void 0,h=m?await S(m):null,g=h?.root,b=e=>c(`board-inbound`,r.id,String(e)),x={key:e,channel:l.via.channel,thread:l.via.thread},C=async e=>{if(!e.dropped)return e.message;for(let t of e.dropped)await l.respond(t);throw new y(e.dropped)},w=async({attachments:t=[],...n})=>{let r=n.channel??h?.channel??i,a=n.re===void 0?{...u,origin:{...u.origin,config:await N(r)}}:u;return{id:await C(await d(`syncImport`,{op:`post`,from:e,source:x,...n,origin:n.origin??null,...t.length?{attachments:t.map(e=>e.id)}:{}},U,a)),re:n.re??null}},E=e=>({root:e.re??e.id,message:e.id}),O=(e={})=>{let t={...e,...a.labels};for(let e of a.unset??[])delete t[e];return t},k=e=>{if(!a.root)return e;let t=a.root(F(structuredClone(e)));if(t&&typeof t==`object`&&`then`in t)throw _(`root overrides must be synchronous`);let n=re.pick({text:!0,labels:!0}).parse(t);return{...e,text:n.text??e.text,labels:{...e.labels,...n.labels}}},A=(t,n,r)=>d(`syncImport`,{op:`labels`,author:t,from:e,origin:{thread:o.thread},set:n,unset:r}),j=async(e,t)=>{if(!a.labels&&!a.unset)return;let n=await T(t),r=Object.fromEntries(Object.entries(a.labels??{}).filter(([e,t])=>n?.labels[e]!==t)),i=(a.unset??[]).filter(e=>n?.labels[e]!==void 0);(Object.keys(r).length||i.length)&&await A(e,r,i)};switch(o.kind){case`message`:{let e=(await D({thread:o.subthread??o.thread,message:o.message})).message;if(e!==null)return{root:g??e,message:e};let{attachments:t,notes:n}=o.files?.length?await he(p,o.files,r.attempt):{attachments:[],notes:[]},c=await me(p,o.links??[],h?.channel??i),l={...c.length?{links:c}:{},author:o.author,text:I(B(a.text??o.text,n),t.length>0),...t.length?{attachments:t}:{},origin:{channel:o.channel,thread:o.thread,subthread:o.subthread,message:o.message,context:o.context},id:await b(0)},u={...l,...a.noSync?{noSync:!0}:{}};if(g!==void 0)return await j(o.author,g),E(await w({...u,re:g}));let d=await p.fetchRoot(v(o.thread));if(d===null){let e=k({text:o.text,labels:{},author:o.author,raw:s});return E(await w({...l,channel:i,text:I(B(a.text??e.text,n),t.length>0),labels:O(e.labels)}))}let f=k({...d,raw:d.raw??null}),m=await w({author:d.author,channel:i,text:I(f.text),labels:O(f.labels),origin:{channel:o.channel,thread:o.thread,subthread:null,message:`root:${r.id}`,context:null},id:await b(1)});return E(await w({...u,re:m.id}))}case`edited`:case`deleted`:case`reaction`:{let n=(await D({thread:o.subthread??o.thread,message:o.message})).message;if(n===null)return{root:g??null,message:null};let r=`author`in o?o.author:{id:`${p.platform}:system`,handle:p.platform,name:p.platform,avatar:null,isBot:!0},i={thread:o.subthread??o.thread,message:o.message};if(g!==void 0&&await j(r,g),o.kind===`deleted`)await d(`syncImport`,{op:`delete`,author:r,from:e,origin:i});else if(o.kind===`edited`){let t=await T(n),s=I(B(a.text??o.text,t?se(t.text):[]),(t?.attachments.length??0)>0);t&&!o.linksOnly&&t.text!==s&&await C(await d(`syncImport`,{op:`edit`,author:r,from:e,origin:i,text:s,source:x}));let c=t?await me(p,o.links??[],t.channel,t.links):[];t&&!ce(c,t.links)&&await d(`syncImport`,{op:`links`,author:r,from:e,origin:i,links:c})}else{if(p.singleReaction){let a=t(o.emoji);for(let[t,s]of Object.entries((await T(n))?.reactions??{}))s.includes(r.id)&&!(o.added&&t===a)&&await d(`syncImport`,{op:`react`,author:r,from:e,origin:i,emoji:t,remove:!0})}(!p.singleReaction||o.added)&&await d(`syncImport`,{op:`react`,author:r,from:e,origin:i,emoji:o.emoji,remove:!o.added})}return{root:g??null,message:n}}case`lifecycle`:{if(g!==void 0){let e=await T(g),t=e?.origin?.message===`lifecycle:${r.id}`,n=O((t?k({text:o.url?`${o.title}\n${o.url}`:o.title,labels:o.labels,author:o.author,raw:s}):null)?.labels??o.labels),i=e=>o.owns?.some(t=>e.startsWith(t))??!1,c=Object.fromEntries(Object.entries(n).filter(([t,n])=>e?.labels[t]!==n)),l=Object.keys(e?.labels??{}).filter(e=>!(e in n)&&(o.unset.includes(e)||a.unset?.includes(e)||i(e)));return e&&!e.deleted&&(Object.keys(c).length||l.length)&&await A(o.author,c,l),o.reply?E(await w({author:o.author,re:g,text:I(a.text??o.reply),...a.noSync?{noSync:!0}:{},id:await b(1)})):{root:g,message:t?g:null}}if(!o.createRoot)return{root:null,message:null};let e=k({text:o.url?`${o.title}\n${o.url}`:o.title,labels:o.labels,author:o.author,raw:s}),t=await w({author:o.author,channel:i,text:I(o.reply?e.text:a.text??e.text),labels:O(e.labels),origin:{channel:o.channel,thread:o.thread,subthread:null,message:`lifecycle:${r.id}`,context:null},id:await b(0)});return o.reply?E(await w({author:o.author,re:t.id,text:I(a.text??o.reply),...a.noSync?{noSync:!0}:{},id:await b(1)})):E(t)}}}function Te(e,t,n,r=t.text){let i=t.mentions.map(e=>n.byId[e]).filter(e=>e!==void 0);return p(r,(t,r)=>{let a=t.kind===`actor`?i.find(e=>e.id===t.actor):i.find(e=>e.handle.toLowerCase()===t.handle);return a?Ee(e,a.id,n)??a.name:null})}function Ee(e,t,n){let r=t=>{let r=n.byId[t];return r?e.mention(r):null},i=n.links[t]??t,a=r(t)??(i===t?null:r(i));if(a)return a;for(let e of k(n,i)){let t=r(e);if(t)return t}return null}async function De(e,t,n){if(!e.users||t.from!==null||t.by.profile!==null||!t.by.principal.startsWith(`vercel:user:`))return null;if(n){let{accounts:n}=await D({person:t.by.principal});if(!n.some(t=>t.method===`consent`&&t.id.startsWith(`${e.platform}:user:`)))return null}try{return await e.users.token(t.by.principal)}catch(n){return console.warn(`[board] no ${e.platform} token for ${t.by.principal}; posting #${t.id} as the app:`,G(n)),null}}function Oe(e,t,n){let r=f(m(e)),i=Te(r,t,n,P(t.text,t.by.actor));return{markdown:t.attachments.length>0&&!r.uploadsFiles?`${i}\n${Me(t)}`:i}}async function ke(e,t,n,r,i){let a=f(m(e));if(!r.text.trim())return null;let o=await De(a,r,!0);if(!o)return null;try{return await a.postAs(o,v(t),n,Oe(e,r,i))}catch(t){return console.warn(`[board] couldn't post #${r.id} to ${e} as its author; posting as the app:`,G(t)),null}}function Ae(e,t,n,i){let a=i.byId[n.by.actor],o=a?.name??n.by.actor;n.by.profile&&a?.handle.includes(`/`)&&(o=`${a.name} via ${a.handle.split(`/`)[0]}`),n.from&&(o=`${o} (${r[n.from]?.platform??n.by.actor.slice(0,n.by.actor.indexOf(`:`))})`);let s=f(e),c=s.render(F({message:{...structuredClone(n),text:P(n.text,n.by.actor)},author:a?{...a}:null,mentionOf:e=>Ee(s,e,i),label:o,target:t}));if(typeof c!=`string`)throw _(`a provider renderer must return markdown`);let l=Te(s,n,i,c);return n.by.profile&&!n.from?{markdown:l,as:{name:o,avatar:je(n,a,i)}}:{markdown:l}}function je(e,t,n){let r=e=>!e||/\/avatars\/[a-z0-9-]+$/.test(e),i=t?.avatar??null;if(!r(i))return i;let a=n.byId[e.by.principal]?.avatar;if(!r(a))return a;let o=t?.handle.split(`/`)[0]??``;return!e.by.principal.startsWith(`vercel:user:`)||!o||/^user-/.test(o)?i:`https://vercel.com/api/www/avatar?u=${encodeURIComponent(o)}&s=64&format=png`}let Me=e=>e.attachments.map(t=>`\n📎 ${t.name} (${i(t.size)}, on the board: #${e.id})`).join(``),Y=(e,t,n,r=!1)=>{let i=m(e),{markdown:a,as:o}=Ae(i,e,t,n);return{markdown:t.attachments.length>0&&(r||!f(i).uploadsFiles)?`${a}\n${Me(t)}`:a,...o?{as:o}:{}}};async function Ne(e,t,n,r){let i=e.replyTo===null?void 0:(await E([e.replyTo]))[e.replyTo]?.[t],a=i!==void 0&&i.thread!==n?i.thread:n,o=f(m(t)),s=Y(t,e,r),c,l;if(!s.markdown.trim()&&e.attachments.length&&o.uploadsFiles)try{let n=await Fe(e,t,a);l=n.message,c=n.files}catch(n){if(!o.refusesFiles(n)&&!R(n))throw n;s=Y(t,e,r,!0),l=(await o.post(v(a),`thread`,s)).message,c=null}else{let n=await ke(t,a,`thread`,e,r);if(n){let r={thread:a,message:n.message,asUser:!0};return await X(e,t,r),r}if(!s.markdown.trim())throw _(`a renderer must return text unless the message has media`);l=(await o.post(v(a),`thread`,s)).message}let u={thread:a,message:l,...c===void 0?{}:{files:c}};return await X(e,t,u),u}async function X(e,t,n){await d(`syncReceipt`,{kind:`message`,message:e.id,target:t,external:{thread:n.thread,message:n.message,...n.asUser?{asUser:!0}:{}},...n.files===void 0?{}:{files:n.files}})}async function Pe(e){return Promise.all(e.attachments.map(async e=>{let{data:t}=await d(`attachment`,{id:e.id});return{data:t instanceof Uint8Array?t:new Uint8Array(await new Response(t).arrayBuffer()),filename:e.name,mimeType:e.type}}))}async function Fe(e,t,n){return f(m(t)).uploadFiles(v(n),await Pe(e))}async function Z(e,t,n,r){let i=f(m(t));if(!i.uploadsFiles||e.attachments.length===0||n.files!==void 0)return;let a;try{a=(await Fe(e,t,n.thread)).files}catch(o){if(!i.refusesFiles(o)&&!R(o))throw Object.assign(/* @__PURE__ */ Error(`files: ${G(o)}`),{cause:o,data:{error:`files: ${G(o)}`}});console.warn(`[board] ${t} won't take #${e.id}'s files (${G(o)}); noting them in its text`),a=null,await i.edit(v(n.thread),n.message,Y(t,e,r,!0)).catch(n=>console.warn(`[board] couldn't note #${e.id}'s files on ${t}:`,G(n)))}await d(`syncReceipt`,{kind:`files`,message:e.id,target:t,files:a})}async function Q(e,t){if(t.length!==0){if(e<5||ee(e))throw t[0].error;for(let{id:e,target:n,error:r}of t)console.error(`[board] gave up delivering #${e} to ${n}:`,G(r)),await d(`syncReceipt`,{kind:`failed`,message:e,target:n,error:G(r)})}}async function Ie(e){if(e.re!==null||e.noSync)return;let t=e.origin?e.origin.channel??te(e.origin.thread):null;for(let n of await N(e.channel))n.target!==t&&await d(`syncReceipt`,{kind:`thread`,message:e.id,target:n.target,targetKind:n.kind,cause:`config`})}async function Le(e,t){let{thread:n,target:r}=e.payload;if(!h(r)||(e.payload.kind??(g(r)?`thread`:`channel`))===`thread`)return;let i=await S(n),[a,...o]=i?await C(i.root):[];if(!i||!a||a.id!==i.root||a.deleted)return;let s=await O(),c=await E([a.id,...o.map(e=>e.id)]),l=i.external[r];if(l){let e=c[a.id]?.[r];if(e)try{await Z(a,r,e,s)}catch(e){return Q(t,[{id:a.id,target:r,error:e}])}}else try{let e=f(m(r)),t=Y(r,a,s),n=!t.markdown.trim()&&a.attachments.length>0&&e.uploadsFiles;if(!t.markdown.trim()&&!n)throw _(`a renderer must return text unless the message has media`);let i,o=!1,c=await(async()=>{if(!n){let n=await ke(r,r,`channel`,a,s);return n?(o=!0,n):e.post(v(r),`channel`,t)}try{let n=await e.post(v(r),`channel`,{...t,files:await Pe(a)});return i=n.files??[],n}catch(t){if(!e.refusesFiles(t)&&!R(t))throw t;return i=null,e.post(v(r),`channel`,Y(r,a,s,!0))}})();l=`${m(r)}:${c.thread}`;let u={thread:l,message:c.message,...i===void 0?{}:{files:i},...o?{asUser:!0}:{}};await X(a,r,u),await Z(a,r,u,s)}catch(e){return Q(t,[{id:a.id,target:r,error:e}])}let u=[];for(let e of o)if(!(e.deleted||e.noSync||$(e,r)))try{await Z(e,r,c[e.id]?.[r]??await Ne(e,r,l,s),s)}catch(t){u.push({id:e.id,target:r,error:t})}await Q(t,u)}let $=(e,t)=>e.origin?.thread===t||e.from!==null&&m(t)===e.from;async function Re(e,t){if(!e.message)return;if(e.message.re===null)return Ie(e.message);if(e.payload.noSync)return;let n=await S(e.payload.thread);if(!n||n.targets.length===0)return;let r=await T(e.message.id);if(!r||r.deleted)return;let i=await O(),a=[];for(let e of n.targets){let t=n.external[e];if(h(e)&&t&&!$(r,e))try{await Z(r,e,(await E([r.id]))[r.id]?.[e]??await Ne(r,e,t,i),i)}catch(t){a.push({id:r.id,target:e,error:t})}}await Q(t,a)}async function ze(e){let n=(await E([e.payload.id]))[e.payload.id]??{};if(Object.keys(n).length===0)return;let r=e.type===`message.edited`?await T(e.payload.id):e.message;if(e.type===`message.edited`&&(!r||r.deleted))return;let i=await O();for(let[a,o]of Object.entries(n))if(!(!h(a)||e.payload.from!==null&&m(a)===e.payload.from))try{let n=f(m(a)),s=v(o.thread),c=o.asUser&&r&&(e.type===`message.edited`||e.type===`message.deleted`),l=c?await De(n,r,!1):null;if(c&&!l){console.warn(`[board] skipped ${e.type} on ${a}: #${r.id} was posted as its author, and their token is gone`);continue}e.type===`message.edited`&&r?l?await n.editAs(l,s,o.message,Oe(a,r,i)):await n.edit(s,o.message,Y(a,r,i,o.files===null)):e.type===`message.deleted`?(l?await n.deleteAs(l,s,o.message):await n.delete(s,o.message),o.files?.length&&await n.deleteFiles(o.files)):e.type===`reaction.added`?await n.react(s,o.message,t(e.payload.emoji),!0):e.type===`reaction.removed`&&await n.react(s,o.message,t(e.payload.emoji),!1)}catch(t){console.warn(`[board] skipped ${e.type} on ${a}:`,t.message)}}async function Be(e){let{id:t}=e.payload,n=(await E([t]))[t]??{},r=e.message;if(!r||Object.keys(n).length===0)return;let i=await T(t);if(!i||i.deleted)return;let a=await O(),o={...i,labels:r.labels},s={...i,labels:e.before?.labels??{}};for(let[e,t]of Object.entries(n))if(h(e))try{let n=Y(e,o,a,t.files===null);if(n.markdown===Y(e,s,a,t.files===null).markdown)continue;await f(m(e)).edit(v(t.thread),t.message,n)}catch(t){console.warn(`[board] skipped labels.changed on ${e}:`,G(t))}}async function Ve(e,{attempt:t}){switch(e.type){case`thread.synced`:return Le(e,t);case`message.posted`:return Re(e,t);case`message.edited`:case`message.deleted`:case`reaction.added`:case`reaction.removed`:return ze(e);case`labels.changed`:return Be(e)}}async function He(e,t){let n=e[0]?await S(e[0].thread):null;if(!n)return[];let r=[...new Set(Object.values(n.external))].filter(h);return(await Promise.all(r.map(async n=>{let r=m(n);try{let i=f(r),a=null;for(let t=e.length-1;t>=0&&a===null;t--){let r=e[t];!r.deleted&&r.origin?.thread===n&&(a=r.origin.message)}return await i.startTyping(v(n),a,t),n}catch(e){return console.warn(`[board] skipped typing on ${n}:`,G(e)),null}}))).filter(e=>e!==null)}async function Ue(e,t){try{return`${e}:${await f(e).resolveChannel(t)}`}catch(e){throw _(`can't resolve inbox channel: ${e.message}`)}}let We=e=>e===`unsupported`||e===`invalid_target`?400:e===`channel_not_found`?404:e===`ratelimited`?429:[`no_client`,`list_failed`,`internal_error`,`fatal_error`,`service_unavailable`,`request_timeout`].includes(e)?502:403;async function Ge(e,t,n){let r=f(e);try{return await r.read(t,{limit:n})}catch(n){throw n instanceof ae?new o(We(n.code),n.code,n.message):_(`can't read ${t} from ${e}: ${G(n)}`,`platform_error`)}}return{entries:r,bind(e){a=e},caller:u,deliver:Ve,receive:ge,pull:J,configTargets:N,normalizeTarget:V,findByEmail:M,handleWebhook:fe,inboxChannel:Ue,signsIn:be,signIn:xe,signedIn:Se,redeemSignIn:Ce,typing:He,readSource:Ge}}export{q as createSyncEngine};
1
+ import{C as e,D as t,E as n,O as r,S as i,T as a,_ as o,a as s,b as c,c as l,d as u,f as d,g as f,h as p,i as m,l as h,m as g,n as _,o as ee,p as v,r as y,s as b,t as te,u as x,v as S,w as C,x as w,y as T}from"./app.js";const E=/^\[vc\]: #\S*\r?\n/,D=/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g,O=/!\[([^\]]+)\]\(https:\/\/vercel\.com\/static\/status\/[^)]+\)\s*(?:\[[^\]]*\]\((https?:\/\/[^)\s]+)\))?/;function k(e){if(!E.test(e))return null;let t=e.search(/<details><summary>\d+ Skipped Deployments?<\/summary>/),n=[],r=0;for(let i of e.split(/\r?\n/)){let a=r;if(r=e.indexOf(`
2
+ `,r)+1||e.length,!i.startsWith(`|`)||/^\|\s*(Project|Name)\s*\|/.test(i)||/^\|\s*:?-/.test(i))continue;let o=i.split(`|`).slice(1,-1).map(e=>e.trim());if(o.length<2)continue;let s=O.exec(o[1]);if(!s)continue;let c=[...o[0].matchAll(D)].at(-1),l=c?.[1]??o[0].replace(/<[^>]*>/g,``).trim();if(!l)continue;let u=o[2]?[...o[2].matchAll(D)].find(e=>/preview/i.test(e[1])):void 0,d=o[3]?.match(/<relative-time[^>]*>([^<]+)<\/relative-time>/)?.[1]?.trim()??(o[3]?.replace(/<[^>]*>/g,``).trim()||null);n.push({project:l,projectUrl:c?.[2]??null,status:s[1].trim(),deploymentUrl:s[2]??null,previewUrl:u?.[2]??null,updated:d,skipped:t!==-1&&a>t})}return n.length>0?n:null}const A={ready:`✅`,building:`⏳`,queued:`⏳`,initializing:`⏳`,error:`❌`,failed:`❌`,canceled:`⚪`,ignored:`⚪`},j=(e,t)=>t?`[${e}](${t})`:e;function M(e){return e.map(e=>`- ${[`${A[e.status.toLowerCase()]??`▲`} **${j(e.project,e.projectUrl)}**`,`${j(e.status,e.deploymentUrl)}${e.skipped?` (skipped)`:``}`,...e.previewUrl?[j(`Preview`,e.previewUrl)]:[],...e.updated?[e.updated]:[]].join(` · `)}`).join(`
3
+ `)}function N(e,t){let n=t===`github:user:35613825`?k(e):null;return n?`**▲ Vercel**\n${M(n)}`:e}const ne=e=>e>10;function re(e){try{Reflect.get(globalThis,Symbol.for(`@vercel/request-context`))?.get?.()?.waitUntil?.(e)}catch{}}w({id:i().min(1),platform:i().min(1),key:i().min(1),...l.shape,raw:e()});const P=w({root:c(e=>typeof e==`function`).optional(),text:i().optional(),labels:g.optional(),unset:S(v).optional(),noSync:T().optional()}).strict();function F(e){if(e&&typeof e==`object`){for(let t of Object.values(e))F(t);Object.freeze(e)}return e}const ie=w({target:i().min(1),channel:i().min(1),kind:o([`channel`,`thread`])}).strict();var ae=class extends Error{code;constructor(e,t){super(t),this.code=e}};const oe=e=>e instanceof ae&&e.code===`channel_not_found`,se=w({event:b,raw:e(),dedupe:i().min(1)}),I=/* @__PURE__ */ new WeakMap,ce=e=>I.get(e),L=(e,t=!1)=>{let n=f(e),r=n.trim()===``?t?``:`(no text)`:n;return r.length>1e4?`${r.slice(0,p-1).toWellFormed()}…`:r},R=(e,t,r=e.name??`a file`)=>`📎 ${r}${e.size===void 0?``:` (${n(e.size)})`} didn't come over: ${t}`,z=e=>e instanceof y&&e.code===`no_files`,B=e=>{let t=e.name??`a file`;if(!e.url||!/^https?:\/\//.test(e.url))return R(e,`this board keeps no files`);let n=t.replace(/[[\]\\]/g,e=>`\\${e}`),r=e.url.replace(/[ ()]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`);return R(e,`this board keeps no files`,`[${n}](${r})`)},V=(e,t)=>t.length?`${e.trim()?`${e}\n\n`:``}${t.join(`
4
+ `)}`:e,le=e=>e.split(`
5
+ `).filter(e=>/^📎 .* didn't come over: /u.test(e)),H=e=>JSON.stringify([e.url,e.message,e.snapshot,e.unavailable]),ue=(e,t)=>e.length===t.length&&e.every((e,n)=>H(e)===H(t[n])),de=/^\s*`?(vb-[a-z0-9]{4}-[a-z0-9]{4})`?\s*$/i,U=6e4,fe=new AbortController().signal,pe=e=>{try{return JSON.parse(JSON.stringify(e??null))}catch{return null}},W=e=>typeof e==`string`?f(e):Array.isArray(e)?e.map(W):typeof e==`object`&&e?Object.fromEntries(Object.entries(e).map(([e,t])=>[f(e),W(t)])):e,G=e=>{let t=e?.data?.error,n=typeof t==`string`?t:e instanceof Error?e.message:String(e);return f(n.slice(0,500))||`unknown error`},K=e=>e instanceof y&&e.status===404;function me(e){let i=null,o=()=>{if(!i)throw Error(`sync used before board() bound it`);return i},c={self:`sync`,origin:{via:{kind:`engine`}}};function l(e,t,n=fe,r=c){let i=o().backend;return i[e].call(i,r,t,n)}let f=t=>{let n=e[t];if(!n)throw m(`no sync entry named ${t}`);return n},p=e=>e.slice(0,e.indexOf(`:`)),g=t=>e[p(t)]!==void 0,v=e=>e.slice(e.indexOf(`:`)+1);async function b(e){try{return await l(`syncThread`,{thread:e})}catch(e){if(K(e))return null;throw e}}async function S(e){try{let{message:t,replies:n}=await l(`message`,{id:e});return[t,...n]}catch(e){if(K(e))return[];throw e}}async function w(e){return(await S(e)).find(t=>t.id===e)??null}async function T(e){return e.length===0?{}:(await l(`syncCopies`,{message:[...new Set(e)]})).copies}let E=(e,t)=>l(`syncFind`,e,t);async function D(){let{actors:e}=await l(`actors`,{}),t={byId:{},links:{}};for(let{person:n,...r}of e)t.byId[r.id]={id:r.id,kind:r.kind,handle:r.handle,name:r.name,avatar:r.avatar},n&&(t.links[r.id]=n.id);return t}let O=(e,t)=>Object.entries(e.links).filter(([,e])=>e===t).map(([e])=>e),k=/* @__PURE__ */ new Map;function A(e,t){let n=`${e}:${t}`,r=k.get(n);if(r&&(!r.failed||Date.now()-r.at<6e4))return r.id;let i={id:f(e).resolveChannel(t),failed:!1,at:Date.now()};return i.id.catch(r=>{i.failed=!0;let a=oe(r);!a&&k.get(n)===i&&k.delete(n),console.warn(`[board] sync ${e}: ${a?`skipping`:`couldn't resolve`} ${t}: ${r.message}`)}),k.set(n,i),i.id}function j(e,t){if(!u(t))return v(t);try{let n=f(e).inboxChannel(e,t);return n===null?null:v(n)}catch{return null}}async function M(t){for(let n of Object.values(e)){let e=await n.findByEmail(t).catch(()=>null);if(e)return{id:e.id,kind:`external`,handle:e.handle,name:e.name,avatar:e.avatar}}return null}async function I(e){let t=e.indexOf(`:`);if(t<=0)throw m(`sync targets look like <key>:<channel or thread>, got: ${e}`);let n=e.slice(0,t),r=f(n);try{let i=ie.parse(await r.resolveTarget(e.slice(t+1)));if(i.kind===`channel`&&!r.canStartThreads)throw Error(`${r.platform} cannot start threads; use an existing thread`);return{target:`${n}:${i.target}`,kind:i.kind}}catch(t){throw m(`can't sync to ${e}: ${t.message}`)}}async function H(e,t){let{event:n,raw:r,dedupe:i}=se.parse(W({...t,raw:pe(t.raw)}));for(let t of[n.channel,n.thread,`subthread`in n?n.subthread:null])if(t!==null&&!t.startsWith(`${e}:`))throw m(`provider locations must belong to sync entry ${e}`);let a=ce(f(e));(!a||await a(v(n.channel),t=>A(e,t))||await me(n.thread))&&await l(`inboxPush`,{key:e,events:[{id:i,event:n,raw:r}]})}async function me(e){try{return(await E({thread:e},AbortSignal.timeout(2e3))).thread!==null}catch(e){let t=e?.name;if(t===`TimeoutError`||t===`AbortError`)return!0;throw e}}async function he(t,n){let r=e[t];if(!r)throw s(`no sync entry named ${t}`);let i;try{i=await r.receive(t,n,e=>H(t,e))}catch(e){return console.error(`[board] intake failed`,e),new Response(`intake failed`,{status:500})}return re(ge([t]).catch(e=>console.error(`[board] couldn't pull ${t}'s inbox:`,G(e)))),i}async function ge(t=Object.keys(e)){let n=0,r=Date.now()+24e4;for(let e of t)for(;Date.now()<r;){let{items:t}=await l(`inboxNext`,{key:e,limit:10});if(t.length===0)break;await Promise.all(t.map(t=>_e(e,t))),n+=t.length}return n}async function _e(e,t){let n;try{await be(e,{event:t.event,raw:t.raw},{id:t.id,attempt:t.attempt+1})}catch(r){console.error(`[board] sync ${e} couldn't import ${t.id}:`,G(r)),n=G(r)}await l(`inboxAck`,{key:e,id:t.id,lease:t.lease,...n===void 0?{}:{error:n}}).catch(e=>console.error(`[board] couldn't ack an inbox event:`,G(e)))}async function ve(e,t,n,r=[]){if(t.length===0)return[];let i=[];for(let a of t){if(i.some(e=>e.url===a.url))continue;let t=(await E({thread:a.thread,message:a.message})).message,o=t===null?null:await w(t);if(o&&!o.deleted&&o.channel===n){i.push({url:a.url,message:o.id,unavailable:null,snapshot:{author:o.author?.name??o.by.actor,text:o.text,createdAt:o.createdAt,files:o.attachments.map(e=>({name:e.name,url:null}))}});continue}if(a.snapshot){i.push({url:a.url,message:null,snapshot:a.snapshot,unavailable:null});continue}let s=r.find(e=>e.url===a.url&&e.snapshot&&e.message===null);if(s){i.push(s);continue}let c=await e.fetchLink(a).catch(e=>({snapshot:null,unavailable:G(e)}));i.push({url:a.url,message:null,snapshot:c.snapshot,unavailable:c.snapshot?null:(c.unavailable??`unknown`).slice(0,300)})}return W(i)}async function ye(e,n,r){let i=[],o=[],s=!1;for(let c of n.slice(0,10)){if(s){o.push(B(c));continue}if(c.size!==void 0&&c.size>26214400){o.push(R(c,`over ${a/1024/1024} MB`));continue}let n;try{let t=await e.fetchFile(c);if(t===null){o.push(R(c,`${e.platform} can't hand it over`));continue}n=t}catch(t){if(r<3)throw t;console.warn(`[board] couldn't fetch ${c.name??`a file`} from ${e.platform}:`,G(t)),o.push(R(c,`${e.platform} wouldn't hand it over`));continue}if(n.byteLength>26214400){o.push(R(c,`over ${a/1024/1024} MB`));continue}if(n.byteLength===0){o.push(R(c,`it was empty`));continue}let u=c.name??`${c.type}`;try{i.push((await l(`upload`,{data:n,name:u,type:c.mimeType||t(u)})).attachment)}catch(e){if(e instanceof y&&(e.code===`too_large`||e.code===`no_direct_uploads`)){o.push(R(c,`over ${C/1024/1024} MB`));continue}if(!z(e))throw e;s=!0,o.push(B(c))}}for(let e of n.slice(10))o.push(R(e,`a message carries at most 10`));return{attachments:i,notes:o}}async function be(e,t,n){let{event:r,raw:i}=t,a=f(e),s=(await E({thread:r.thread})).thread??void 0;if(r.kind===`message`){let t=(await E({thread:r.subthread??r.thread,message:r.message})).message;if(t!==null){let e=(await w(t))?.origin;if(!e||e.message!==r.message||(e.subthread??e.thread)!==(r.subthread??r.thread))return}else if(a.users&&n.attempt<6&&await Ce(s,r))throw Error(`holding ${r.message} until ${r.author.id}'s board message there has its receipt`);let i=r.author.isBot?void 0:de.exec(r.text)?.[1];if(i&&await we(e,r,i))return}let c=`author`in r?r.author:{id:`${a.platform}:system`,handle:a.platform,name:a.platform,avatar:null,isBot:!0},l,u=()=>l??=F(a.event({id:n.id,key:e,event:r,raw:i})),d,p,h=!0,g={direction:`in`,key:e,channel:v(r.channel),get event(){return u()},board:o().client({author:c,key:e,...Se(e,r)},n.id),import(r,i={}){if(!h)throw m(`the sync function has finished`);return d??=xe(e,t,n,s,r,i),d},permalink(){let e=p??a.permalink(u());return p=e,e.catch(()=>{p===e&&(p=void 0)}),e},resolve:t=>A(e,t)};try{await a.sync(g),await d}finally{h=!1}}async function xe(e,t,n,r,i,a){let o=i??(r?(await b(r))?.channel:void 0);if(o===void 0)return{root:null,message:null};try{return await ke(e,t,n,d.parse(o),P.parse(a))}catch(e){if(!(e instanceof te))throw e;let n=(await E({thread:t.event.thread})).thread;return{root:n===null?null:(await b(n))?.root??null,message:null}}}function Se(e,t){let n=f(e),r=(`subthread`in t?t.subthread:null)??t.thread;return{via:{kind:`sync`,key:e,platform:n.platform,channel:t.channel,thread:r},respond:async e=>{await n.post(v(r),`thread`,{markdown:e})}}}async function Ce(e,t){let n=(await E({account:t.author.id})).person;if(!n||n.method!==`consent`)return!1;let r=Date.now(),i=(e,t,i)=>e.by.principal===n.id&&e.by.profile===null&&e.from===null&&!e.deleted&&!e.noSync&&r-Date.parse(e.createdAt)<U&&!i[t]&&!e.undelivered[t];if(e!==void 0){let n=await b(e);if(!n)return!1;let r=n.targets.filter(e=>n.external[e]===t.thread),a=await S(n.root),o=await T(a.map(e=>e.id));return a.some(e=>r.some(t=>i(e,t,o[e.id]??{})))}let{roots:a}=await l(`syncRoots`,{by:n.id,since:r-U}),o=await T(a.map(e=>e.id));for(let e of a){let n=await b(e.thread);if(n?.targets.includes(t.channel)&&!n.external[t.channel]&&i(e,t.channel,o[e.id]??{}))return!0}return!1}async function we(e,t,n){try{await l(`syncAccount`,{sync:e,account:t.author,code:n,message:x(t.subthread??t.thread,t.message)})}catch(e){if(K(e))return!1;throw e}return await f(e).react(v(t.subthread??t.thread),t.message,`check`,!0).catch(t=>console.warn(`[board] couldn't confirm a linking code on ${e}:`,G(t))),!0}let Te=e=>f(e).users!==null;async function Ee(e,t,n){let r=f(e);if(!r.users)throw m(`${e} can't sign people in: link it with a code`);return r.users.authorize(t,n?{callback:n}:{})}async function De(e,t){let n=f(e);if(!n.users)throw m(`${e} can't sign people in: link it with a code`);let r=await n.users.token(t);if(!r)return null;let i=await n.whoAmI(r);if(!i.id.startsWith(`${n.platform}:user:`))throw m(`${e} named an account that isn't a ${n.platform} user`);return i}async function Oe(e,t,n){let r=await De(e,t);return r?(await l(`syncAccount`,{sync:e,account:r,code:n,method:`consent`})).link:null}async function ke(e,t,n,i,a){let{event:o,raw:s}=t,c=Se(e,o),u=f(e),d=(await E({thread:o.thread})).thread??void 0,p=d?await b(d):null,h=p?.root,g=e=>ee(`board-inbound`,n.id,String(e)),_={key:e,channel:c.via.channel,thread:c.via.thread},y=async e=>{if(!e.dropped)return e.message;for(let t of e.dropped)await c.respond(t);throw new te(e.dropped)},x=async({attachments:t=[],...n})=>({id:await y(await l(`syncImport`,{op:`post`,from:e,source:_,...n,origin:n.origin??null,...t.length?{attachments:t.map(e=>e.id)}:{}})),re:n.re??null}),S=e=>({root:e.re??e.id,message:e.id}),C=(e={})=>{let t={...e,...a.labels};for(let e of a.unset??[])delete t[e];return t},T=e=>{if(!a.root)return e;let t=a.root(F(structuredClone(e)));if(t&&typeof t==`object`&&`then`in t)throw m(`root overrides must be synchronous`);let n=P.pick({text:!0,labels:!0}).parse(t);return{...e,text:n.text??e.text,labels:{...e.labels,...n.labels}}},D=(t,n,r)=>l(`syncImport`,{op:`labels`,author:t,from:e,origin:{thread:o.thread},set:n,unset:r}),O=async(e,t)=>{if(!a.labels&&!a.unset)return;let n=await w(t),r=Object.fromEntries(Object.entries(a.labels??{}).filter(([e,t])=>n?.labels[e]!==t)),i=(a.unset??[]).filter(e=>n?.labels[e]!==void 0);(Object.keys(r).length||i.length)&&await D(e,r,i)};switch(o.kind){case`message`:{let e=(await E({thread:o.subthread??o.thread,message:o.message})).message;if(e!==null)return{root:h??e,message:e};let{attachments:t,notes:r}=o.files?.length?await ye(u,o.files,n.attempt):{attachments:[],notes:[]},c=await ve(u,o.links??[],p?.channel??i),l={...c.length?{links:c}:{},author:o.author,text:L(V(a.text??o.text,r),t.length>0),...t.length?{attachments:t}:{},origin:{channel:o.channel,thread:o.thread,subthread:o.subthread,message:o.message,context:o.context},id:await g(0)},d={...l,...a.noSync?{noSync:!0}:{}};if(h!==void 0)return await O(o.author,h),S(await x({...d,re:h}));let f=await u.fetchRoot(v(o.thread));if(f===null){let e=T({text:o.text,labels:{},author:o.author,raw:s});return S(await x({...l,channel:i,text:L(V(a.text??e.text,r),t.length>0),labels:C(e.labels)}))}let m=T({...f,raw:f.raw??null}),_=await x({author:f.author,channel:i,text:L(m.text),labels:C(m.labels),origin:{channel:o.channel,thread:o.thread,subthread:null,message:`root:${n.id}`,context:null},id:await g(1)});return S(await x({...d,re:_.id}))}case`edited`:case`deleted`:case`reaction`:{let t=(await E({thread:o.subthread??o.thread,message:o.message})).message;if(t===null)return{root:h??null,message:null};let n=`author`in o?o.author:{id:`${u.platform}:system`,handle:u.platform,name:u.platform,avatar:null,isBot:!0},i={thread:o.subthread??o.thread,message:o.message};if(h!==void 0&&await O(n,h),o.kind===`deleted`)await l(`syncImport`,{op:`delete`,author:n,from:e,origin:i});else if(o.kind===`edited`){let r=await w(t),s=L(V(a.text??o.text,r?le(r.text):[]),(r?.attachments.length??0)>0);r&&!o.linksOnly&&r.text!==s&&await y(await l(`syncImport`,{op:`edit`,author:n,from:e,origin:i,text:s,source:_}));let c=r?await ve(u,o.links??[],r.channel,r.links):[];r&&!ue(c,r.links)&&await l(`syncImport`,{op:`links`,author:n,from:e,origin:i,links:c})}else{if(u.singleReaction){let a=r(o.emoji);for(let[r,s]of Object.entries((await w(t))?.reactions??{}))s.includes(n.id)&&!(o.added&&r===a)&&await l(`syncImport`,{op:`react`,author:n,from:e,origin:i,emoji:r,remove:!0})}(!u.singleReaction||o.added)&&await l(`syncImport`,{op:`react`,author:n,from:e,origin:i,emoji:o.emoji,remove:!o.added})}return{root:h??null,message:t}}case`lifecycle`:{if(h!==void 0){let t=await w(h),r=t?.origin?.message===`lifecycle:${n.id}`,i=C((r?T({text:o.url?`${o.title}\n${o.url}`:o.title,labels:o.labels,author:o.author,raw:s}):null)?.labels??o.labels),c=e=>o.owns?.some(t=>e.startsWith(t))??!1,u=Object.fromEntries(Object.entries(i).filter(([e,n])=>t?.labels[e]!==n)),d=Object.keys(t?.labels??{}).filter(e=>!(e in i)&&(o.unset.includes(e)||a.unset?.includes(e)||c(e)));if(t&&!t.deleted&&(Object.keys(u).length||d.length)&&await D(o.author,u,d),o.retitled&&t&&!t.deleted&&t.from===e&&t.origin?.thread===o.thread){let n=L(a.text??T({text:o.url?`${o.title}\n${o.url}`:o.title,labels:t.labels,author:o.author,raw:s}).text);t.text!==n&&await y(await l(`syncImport`,{op:`edit`,author:o.author,from:e,origin:{thread:t.origin.thread,message:t.origin.message},text:n}))}return o.reply?S(await x({author:o.author,re:h,text:L(a.text??o.reply),...a.noSync?{noSync:!0}:{},id:await g(1)})):{root:h,message:r?h:null}}if(!o.createRoot)return{root:null,message:null};let t=T({text:o.url?`${o.title}\n${o.url}`:o.title,labels:o.labels,author:o.author,raw:s}),r=await x({author:o.author,channel:i,text:L(o.reply?t.text:a.text??t.text),labels:C(t.labels),origin:{channel:o.channel,thread:o.thread,subthread:null,message:`lifecycle:${n.id}`,context:null},id:await g(0)});return o.reply?S(await x({author:o.author,re:r.id,text:L(a.text??o.reply),...a.noSync?{noSync:!0}:{},id:await g(1)})):S(r)}}}function Ae(e,t,n,r=t.text){let i=t.mentions.map(e=>n.byId[e]).filter(e=>e!==void 0);return _(r,(t,r)=>{let a=t.kind===`actor`?i.find(e=>e.id===t.actor):i.find(e=>e.handle.toLowerCase()===t.handle);return a?je(e,a.id,n)??a.name:null})}function je(e,t,n){let r=t=>{let r=n.byId[t];return r?e.mention(r):null},i=n.links[t]??t,a=r(t)??(i===t?null:r(i));if(a)return a;for(let e of O(n,i)){let t=r(e);if(t)return t}return null}async function Me(e,t,n){if(!e.users||t.from!==null||t.by.profile!==null||!t.by.principal.startsWith(`vercel:user:`))return null;if(n){let{accounts:n}=await E({person:t.by.principal});if(!n.some(t=>t.method===`consent`&&t.id.startsWith(`${e.platform}:user:`)))return null}try{return await e.users.token(t.by.principal)}catch(n){return console.warn(`[board] no ${e.platform} token for ${t.by.principal}; posting #${t.id} as the app:`,G(n)),null}}function Ne(e,t,n){let r=f(p(e)),i=Ae(r,t,n,N(t.text,t.by.actor));return{markdown:t.attachments.length>0&&!r.uploadsFiles?`${i}\n${Le(t)}`:i}}async function Pe(e,t,n,r,i){let a=f(p(e));if(!r.text.trim())return null;let o=await Me(a,r,!0);if(!o)return null;try{return await a.postAs(o,v(t),n,Ne(e,r,i))}catch(t){return console.warn(`[board] couldn't post #${r.id} to ${e} as its author; posting as the app:`,G(t)),null}}function Fe(t,n,r,i,a){let o=i.byId[r.by.actor],s=o?.name??r.by.actor;r.by.profile&&o?.handle.includes(`/`)&&(s=`${o.name} via ${o.handle.split(`/`)[0]}`),r.from&&(s=`${s} (${e[r.from]?.platform??r.by.actor.slice(0,r.by.actor.indexOf(`:`))})`);let c=f(t),l=(a??(e=>c.render(e)))(F({message:{...structuredClone(r),text:N(r.text,r.by.actor)},author:o?{...o}:null,mentionOf:e=>je(c,e,i),label:s,target:n}));if(typeof l!=`string`)throw m(`a provider renderer must return markdown`);let u=Ae(c,r,i,l);return r.by.profile&&!r.from?{markdown:u,as:{name:s,avatar:Ie(r,o,i)}}:{markdown:u}}function Ie(e,t,n){let r=e=>!e||/\/avatars\/[a-z0-9-]+$/.test(e),i=t?.avatar??null;if(!r(i))return i;let a=n.byId[e.by.principal]?.avatar;if(!r(a))return a;let o=t?.handle.split(`/`)[0]??``;return!e.by.principal.startsWith(`vercel:user:`)||!o||/^user-/.test(o)?i:`https://vercel.com/api/www/avatar?u=${encodeURIComponent(o)}&s=64&format=png`}let Le=e=>e.attachments.map(t=>`\n📎 ${t.name} (${n(t.size)}, on the board: #${e.id})`).join(``),q=(e,t,n,r=!1,i)=>{let a=p(e),{markdown:o,as:s}=Fe(a,e,t,n,i);return{markdown:t.attachments.length>0&&(r||!f(a).uploadsFiles)?`${o}\n${Le(t)}`:o,...s?{as:s}:{}}};async function Re(e,t,n,r,i){let a=e.replyTo===null?void 0:(await T([e.replyTo]))[e.replyTo]?.[t],o=a!==void 0&&a.thread!==n?a.thread:n,s=f(p(t)),c=q(t,e,r,!1,i),l,u;if(!c.markdown.trim()&&e.attachments.length&&s.uploadsFiles)try{let n=await Be(e,t,o);u=n.message,l=n.files}catch(n){if(!s.refusesFiles(n)&&!z(n))throw n;c=q(t,e,r,!0,i),u=(await s.post(v(o),`thread`,c)).message,l=null}else{let n=await Pe(t,o,`thread`,e,r);if(n){let r={thread:o,message:n.message,asUser:!0};return await J(e,t,r),r}if(!c.markdown.trim())throw m(`a renderer must return text unless the message has media`);u=(await s.post(v(o),`thread`,c)).message}let d={thread:o,message:u,...l===void 0?{}:{files:l}};return await J(e,t,d),d}async function J(e,t,n){await l(`syncReceipt`,{kind:`message`,message:e.id,target:t,external:{thread:n.thread,message:n.message,...n.asUser?{asUser:!0}:{}},...n.files===void 0?{}:{files:n.files}})}async function ze(e){return Promise.all(e.attachments.map(async e=>{let{data:t}=await l(`attachment`,{id:e.id});return{data:t instanceof Uint8Array?t:new Uint8Array(await new Response(t).arrayBuffer()),filename:e.name,mimeType:e.type}}))}async function Be(e,t,n){return f(p(t)).uploadFiles(v(n),await ze(e))}async function Y(e,t,n,r,i){let a=f(p(t));if(!a.uploadsFiles||e.attachments.length===0||n.files!==void 0)return;let o;try{o=(await Be(e,t,n.thread)).files}catch(s){if(!a.refusesFiles(s)&&!z(s))throw Object.assign(/* @__PURE__ */ Error(`files: ${G(s)}`),{cause:s,data:{error:`files: ${G(s)}`}});console.warn(`[board] ${t} won't take #${e.id}'s files (${G(s)}); noting them in its text`),o=null,await a.edit(v(n.thread),n.message,q(t,e,r,!0,i)).catch(n=>console.warn(`[board] couldn't note #${e.id}'s files on ${t}:`,G(n)))}await l(`syncReceipt`,{kind:`files`,message:e.id,target:t,files:o})}let X=(e,t,n,r)=>{e.some(e=>e.error===r)||e.push({id:t,target:n,error:r})};async function Z(e,t){if(t.length!==0){if(e<5||ne(e))throw t[0].error;for(let{id:e,target:n,error:r}of t)console.error(`[board] gave up ${n===null?`syncing #${e}`:`delivering #${e} to ${n}`}:`,G(r)),n!==null&&await l(`syncReceipt`,{kind:`failed`,message:e,target:n,error:G(r)})}}async function Q(e,t,n,r,i){let a,o=!0,s=()=>m(`the sync function has finished`),c={direction:`out`,key:e,channel:r===null?null:j(e,r),event:F(structuredClone(t)),message:F(structuredClone(n)),target:r,deliver(e={}){if(!o)throw s();return a??=r===null?Promise.resolve():i(e.render),a},start(t){if(!o)throw s();return Ve(e,n,t)},resolve:t=>A(e,t)};try{await f(e).sync(c),await a}finally{o=!1}}async function Ve(e,t,n){let{target:r,kind:i}=await I(`${e}:${n}`),a=t.re===null?t:await w(t.re);!a||a.deleted||a.noSync||r!==(a.origin?a.origin.channel??h(a.origin.thread):null)&&await l(`syncReceipt`,{kind:`thread`,message:a.id,target:r,targetKind:i,cause:`config`})}async function He(t,n){let r=Object.keys(e).filter(t=>e[t].canStartThreads);if(t.payload.noSync||r.length===0)return;let i=await w(t.message.id);if(!i||i.deleted)return;let a=[];for(let e of r)try{await Q(e,t,i,null,async()=>{})}catch(e){X(a,i.id,null,e)}await Z(n,a)}async function Ue(e,t){let{thread:n,target:r}=e.payload;if(!g(r)||(e.payload.kind??(u(r)?`thread`:`channel`))===`thread`)return;let i=await b(n),[a,...o]=i?await S(i.root):[];if(!i||!a||a.id!==i.root||a.deleted)return;let s=[];try{await Q(p(r),e,a,r,e=>We(i,a,o,r,s,e))}catch(e){X(s,a.id,r,e)}await Z(t,s)}async function We(e,t,n,r,i,a){let o=await D(),s=await T([t.id,...n.map(e=>e.id)]),c=e=>(i.push({id:t.id,target:r,error:e}),e),l=e.external[r];if(l){let e=s[t.id]?.[r];if(e)try{await Y(t,r,e,o,a)}catch(e){throw c(e)}}else try{let e=f(p(r)),n=q(r,t,o,!1,a),i=!n.markdown.trim()&&t.attachments.length>0&&e.uploadsFiles;if(!n.markdown.trim()&&!i)throw m(`a renderer must return text unless the message has media`);let s,c=!1,u=await(async()=>{if(!i){let i=await Pe(r,r,`channel`,t,o);return i?(c=!0,i):e.post(v(r),`channel`,n)}try{let i=await e.post(v(r),`channel`,{...n,files:await ze(t)});return s=i.files??[],i}catch(n){if(!e.refusesFiles(n)&&!z(n))throw n;return s=null,e.post(v(r),`channel`,q(r,t,o,!0,a))}})();l=`${p(r)}:${u.thread}`;let d={thread:l,message:u.message,...s===void 0?{}:{files:s},...c?{asUser:!0}:{}};await J(t,r,d),await Y(t,r,d,o,a)}catch(e){throw c(e)}let u=[];for(let e of n)if(!(e.deleted||e.noSync||$(e,r)))try{await Y(e,r,s[e.id]?.[r]??await Re(e,r,l,o,a),o,a)}catch(t){u.push({id:e.id,target:r,error:t})}if(i.push(...u),u.length)throw u[0].error}let $=(e,t)=>e.origin?.thread===t||e.from!==null&&p(t)===e.from;async function Ge(e,t){if(!e.message)return;if(e.message.re===null)return He(e,t);if(e.payload.noSync)return;let n=await b(e.payload.thread);if(!n||n.targets.length===0)return;let r=await w(e.message.id);if(!r||r.deleted)return;let i=await D(),a=[];for(let t of n.targets){let o=n.external[t];if(g(t)&&o&&!$(r,t))try{await Q(p(t),e,r,t,async e=>{let n=(await T([r.id]))[r.id]?.[t]??await Re(r,t,o,i,e);await Y(r,t,n,i,e)})}catch(e){X(a,r.id,t,e)}}await Z(t,a)}async function Ke(e){let t=(await T([e.payload.id]))[e.payload.id]??{};if(Object.keys(t).length===0)return;let n=e.type===`message.edited`?await w(e.payload.id):e.message??await w(e.payload.id);if(!n||e.type===`message.edited`&&n.deleted)return;let i=await D();for(let[a,o]of Object.entries(t))if(!(!g(a)||e.payload.from!==null&&p(a)===e.payload.from))try{await Q(p(a),e,n,a,async t=>{let s=f(p(a)),c=v(o.thread),l=o.asUser&&(e.type===`message.edited`||e.type===`message.deleted`),u=l?await Me(s,n,!1):null;if(l&&!u){console.warn(`[board] skipped ${e.type} on ${a}: #${n.id} was posted as its author, and their token is gone`);return}e.type===`message.edited`?u?await s.editAs(u,c,o.message,Ne(a,n,i)):await s.edit(c,o.message,q(a,n,i,o.files===null,t)):e.type===`message.deleted`?(u?await s.deleteAs(u,c,o.message):await s.delete(c,o.message),o.files?.length&&await s.deleteFiles(o.files)):e.type===`reaction.added`?await s.react(c,o.message,r(e.payload.emoji),!0):await s.react(c,o.message,r(e.payload.emoji),!1)})}catch(t){console.warn(`[board] skipped ${e.type} on ${a}:`,t.message)}}async function qe(e){let{id:t}=e.payload,n=(await T([t]))[t]??{},r=e.message;if(!r||Object.keys(n).length===0)return;let i=await w(t);if(!i||i.deleted)return;let a=await D(),o={...i,labels:r.labels},s={...i,labels:e.before?.labels??{}};for(let[t,r]of Object.entries(n))if(g(t))try{await Q(p(t),e,o,t,async e=>{let n=q(t,o,a,r.files===null,e);n.markdown!==q(t,s,a,r.files===null,e).markdown&&await f(p(t)).edit(v(r.thread),r.message,n)})}catch(e){console.warn(`[board] skipped labels.changed on ${t}:`,G(e))}}async function Je(e,{attempt:t}){switch(e.type){case`thread.synced`:return Ue(e,t);case`message.posted`:return Ge(e,t);case`message.edited`:case`message.deleted`:case`reaction.added`:case`reaction.removed`:return Ke(e);case`labels.changed`:return qe(e)}}async function Ye(e,t){let n=e[0]?await b(e[0].thread):null;if(!n)return[];let r=[...new Set(Object.values(n.external))].filter(g);return(await Promise.all(r.map(async n=>{let r=p(n);try{let i=f(r),a=null;for(let t=e.length-1;t>=0&&a===null;t--){let r=e[t];!r.deleted&&r.origin?.thread===n&&(a=r.origin.message)}return await i.startTyping(v(n),a,t),n}catch(e){return console.warn(`[board] skipped typing on ${n}:`,G(e)),null}}))).filter(e=>e!==null)}async function Xe(e,t){try{return`${e}:${await f(e).resolveChannel(t)}`}catch(e){throw m(`can't resolve inbox channel: ${e.message}`)}}let Ze=e=>e===`unsupported`||e===`invalid_target`?400:e===`channel_not_found`?404:e===`ratelimited`?429:[`no_client`,`list_failed`,`internal_error`,`fatal_error`,`service_unavailable`,`request_timeout`].includes(e)?502:403;async function Qe(e,t,n){let r=f(e);try{return await r.read(t,{limit:n})}catch(n){throw n instanceof ae?new y(Ze(n.code),n.code,n.message):m(`can't read ${t} from ${e}: ${G(n)}`,`platform_error`)}}return{entries:e,bind(e){i=e},caller:c,deliver:Je,receive:be,pull:ge,normalizeTarget:I,findByEmail:M,handleWebhook:he,inboxChannel:Xe,signsIn:Te,signIn:Ee,signedIn:De,redeemSignIn:Oe,typing:Ye,readSource:Qe}}export{me as createSyncEngine};
@@ -0,0 +1,203 @@
1
+ //#region ../../node_modules/.pnpm/experimental-a2@0.22.0_@upstash+qstash@2.12.0_@vercel+queue@0.7.0_ai@7.0.130_zod@4.6.5__52060f00b21aecfab049c27fa3abd2f9/node_modules/experimental-a2/dist/internal-CvsrprnU.js
2
+ /**
3
+ * A null-prototype record for maps keyed by caller-controlled strings
4
+ * (participant ids, presence field names). On a normal object,
5
+ * assigning to '__proto__' rewrites the prototype instead of creating
6
+ * an own property — the prototype-pollution class. Null-prototype
7
+ * objects still satisfy Record types and JSON.stringify cleanly.
8
+ */
9
+ function nullProtoRecord() {
10
+ return Object.create(null);
11
+ }
12
+ /**
13
+ * Participant ids that collide with Object.prototype plumbing.
14
+ * Null-prototype construction already defuses them, but they are
15
+ * rejected at both seams (`parsePushBody`, `setPresence`) so a hostile
16
+ * id never propagates into userland maps either.
17
+ */
18
+ const RESERVED_PARTICIPANT_IDS = /* @__PURE__ */ new Set([
19
+ "__proto__",
20
+ "constructor",
21
+ "prototype"
22
+ ]);
23
+ const serverInternals = /* @__PURE__ */ new WeakMap();
24
+ /**
25
+ * Consumers A2's own runtimes define: named under `a2.`, with child ids that
26
+ * leave the name out, since their types are disjoint. Defined through
27
+ * `runtimeConsumer` (runtime-consumer.ts), which the server module installs.
28
+ */
29
+ const runtimeConsumers = /* @__PURE__ */ new WeakSet();
30
+ const runtimeConsumerFactory = {};
31
+ /**
32
+ * Consumer contexts whose previous attempt ended in a failure the consumer
33
+ * threw, as opposed to a claim that lapsed (a crash, a timeout, a frozen
34
+ * process). Only the attempt's own outcome is known, not how far it got.
35
+ */
36
+ const failedPreviousAttempt = /* @__PURE__ */ new WeakSet();
37
+ const objectLike = (value) => typeof value === "object" && value !== null || typeof value === "function";
38
+ const SCHEDULER_SEND_FAILURE = Symbol.for("experimental-a2.scheduler.send-failure");
39
+ const schedulerSendFailureMarker = (value) => {
40
+ if (!objectLike(value)) return void 0;
41
+ const marker = Reflect.get(value, SCHEDULER_SEND_FAILURE);
42
+ if (!objectLike(marker) || Reflect.get(marker, "version") !== 1 || !Array.isArray(Reflect.get(marker, "pending")) || !Array.isArray(Reflect.get(marker, "active"))) return;
43
+ return marker;
44
+ };
45
+ const schedulerFailureObject = (error) => {
46
+ if (objectLike(error) && (schedulerSendFailureMarker(error) !== void 0 || Object.isExtensible(error))) return error;
47
+ return new Error(String(error), { cause: error });
48
+ };
49
+ const schedulerSendFailureState = (error) => {
50
+ const existing = schedulerSendFailureMarker(error);
51
+ if (existing !== void 0) return existing;
52
+ const marker = {
53
+ version: 1,
54
+ pending: [],
55
+ active: []
56
+ };
57
+ Object.defineProperty(error, SCHEDULER_SEND_FAILURE, {
58
+ value: marker,
59
+ enumerable: false,
60
+ configurable: true,
61
+ writable: false
62
+ });
63
+ return marker;
64
+ };
65
+ /**
66
+ * Classify an adapter error before throwing it. Unclassified adapter failures
67
+ * are retryable because the caller cannot know whether a remote send landed.
68
+ */
69
+ function classifySchedulerSendFailure(error, disposition) {
70
+ const classified = schedulerFailureObject(error);
71
+ schedulerSendFailureState(classified).pending.push(disposition);
72
+ return classified;
73
+ }
74
+ /** Consume a pending adapter classification and count this rejected send. */
75
+ function markSchedulerSendFailure(error) {
76
+ const marked = schedulerFailureObject(error);
77
+ const marker = schedulerSendFailureState(marked);
78
+ marker.active.push(marker.pending.pop() ?? "retryable");
79
+ return marked;
80
+ }
81
+ const isDrainableServer = (value) => {
82
+ if (value === null || typeof value !== "object") return false;
83
+ const contract = Reflect.get(value, "contract");
84
+ return contract !== null && typeof contract === "object" && typeof Reflect.get(contract, "name") === "string" && typeof Reflect.get(value, "drain") === "function";
85
+ };
86
+ /**
87
+ * Validate the server list captured by a scheduler route at construction time.
88
+ * The seam stays structural so custom scheduler integrations can supply their
89
+ * own drainable targets.
90
+ */
91
+ function assertSchedulerTargets(servers) {
92
+ const contracts = /* @__PURE__ */ new Set();
93
+ for (const target of servers) {
94
+ if (!isDrainableServer(target)) throw new TypeError("a2 scheduler: expected a server from createServer()");
95
+ const name = target.contract.name;
96
+ if (contracts.has(name)) throw new TypeError(`a2 scheduler: duplicate server contract '${name}' in one handler`);
97
+ contracts.add(name);
98
+ }
99
+ }
100
+ /**
101
+ * Drain timing knobs. Mutable only as a white-box test seam. Claim
102
+ * heartbeats run on real timers, so tests shrink these to keep suites
103
+ * fast. Production always uses the defaults.
104
+ */
105
+ const DRAIN_TIMINGS = {
106
+ claimTtlMs: 5e3,
107
+ claimHeartbeatMs: 2e3,
108
+ renewRetryMs: 400,
109
+ watchdogGraceMs: 1e3,
110
+ schedulerArmTimeoutMs: 2e3,
111
+ failureRetryBaseMs: 1e3,
112
+ failureRetryMaxMs: 3e4
113
+ };
114
+ /**
115
+ * Per server: called when a drain of a session ends with nothing left to run
116
+ * for it in this process, so the layer above can let go of what it kept for
117
+ * that session's next step.
118
+ */
119
+ const drainSettledHooks = /* @__PURE__ */ new WeakMap();
120
+ /**
121
+ * Stream liveness knobs — the server-side SSE heartbeat and the
122
+ * client-side stall watchdog. Paired by design: the client declares a
123
+ * connection dead after roughly two missed heartbeats. Mutable only as
124
+ * a white-box test seam; production always uses the defaults.
125
+ */
126
+ /**
127
+ * Poll cadence for the poll-based store streams (sqlite, postgres) —
128
+ * owned, not configurable: adaptive polling removed the operator's
129
+ * reason to tune it (the floor keeps active streams smooth; the
130
+ * ceiling bounds idle cost). Mutable only as a white-box test seam so
131
+ * suites don't wait out real idle gaps.
132
+ */
133
+ const POLL_TIMINGS = {
134
+ activeFloorMs: 25,
135
+ idleCeilingMs: 2e3
136
+ };
137
+ /**
138
+ * Timings for the notify-driven redis live stream. A parked feed re-reads
139
+ * the store every `safetyReadMs` even without a pub/sub wake-up, so a lost
140
+ * notification (subscriber reconnect gap, dropped PUBLISH) costs latency,
141
+ * never an event. Writers publish after their scripts, so wake-ups from
142
+ * concurrent writers can arrive out of order: a feed holds a gap for
143
+ * `reorderMs` before catching up with a read, and a write waits at most
144
+ * `publishMs` for its PUBLISH. Mutable only as a white-box test seam.
145
+ */
146
+ const NOTIFY_TIMINGS = {
147
+ safetyReadMs: 1e4,
148
+ reorderMs: 50,
149
+ publishMs: 1e3
150
+ };
151
+ /**
152
+ * Bounds for each server instance's in-memory reducer-state cache. Sizes are
153
+ * encoded JSON lengths, re-measured every `measureEvery` folded events, so
154
+ * the byte budget is approximate. Mutable only as a white-box test seam.
155
+ */
156
+ const STATE_CACHE = {
157
+ maxEntries: 1e3,
158
+ maxBytes: 33554432,
159
+ snapshotEvery: 128,
160
+ maxTail: 255,
161
+ measureEvery: 16
162
+ };
163
+ /**
164
+ * Warm state-read tails for stores that can serve one in a single request:
165
+ * events from `fromIndex` (inclusive) through `throughIndex` or the newest
166
+ * event, or null when that needs more than one request. Like every store
167
+ * read, a tail starts no lower than the latest clear marker. Keyed by the exact
168
+ * store object, so a wrapper built by spreading or proxying a store reads
169
+ * through its own `readHistory` instead.
170
+ */
171
+ const stateTailReaders = /* @__PURE__ */ new WeakMap();
172
+ const STREAM_TIMINGS = {
173
+ sseHeartbeatMs: 15e3,
174
+ sseDeadlineGraceMs: 1e3,
175
+ stallTimeoutMs: 35e3,
176
+ reconnectBaseMs: 500,
177
+ reconnectMaxMs: 5e3
178
+ };
179
+ /**
180
+ * WebSocket down-pump backpressure — `sessionsSocket` parks its pull
181
+ * loop while the socket reports more buffered bytes than the
182
+ * high-water mark and re-checks on the poll cadence, so a slow reader
183
+ * applies backpressure to the store subscription instead of buffering
184
+ * without bound in the socket. Acks and heartbeats stay exempt: they
185
+ * are small and correctness-relevant. Mutable only as a white-box test
186
+ * seam.
187
+ */
188
+ const SOCKET_TIMINGS = {
189
+ highWaterMarkBytes: 1048576,
190
+ resumePollMs: 25,
191
+ maxInFlightMessages: 32,
192
+ disconnectWaterMarkBytes: 8388608
193
+ };
194
+ /**
195
+ * Presence timings. Owned, not configurable (decision #36's rule).
196
+ * Mutable only as a white-box test seam.
197
+ */
198
+ const PRESENCE_TIMINGS = {
199
+ sendIntervalMs: 66,
200
+ tombstoneMs: 6e4
201
+ };
202
+ //#endregion
203
+ export { serverInternals as _, RESERVED_PARTICIPANT_IDS as a, STREAM_TIMINGS as c, drainSettledHooks as d, failedPreviousAttempt as f, runtimeConsumers as g, runtimeConsumerFactory as h, PRESENCE_TIMINGS as i, assertSchedulerTargets as l, nullProtoRecord as m, NOTIFY_TIMINGS as n, SOCKET_TIMINGS as o, markSchedulerSendFailure as p, POLL_TIMINGS as r, STATE_CACHE as s, DRAIN_TIMINGS as t, classifySchedulerSendFailure as u, stateTailReaders as v };
@@ -1,4 +1,4 @@
1
- import { deliverSchedulerAppend, deliverSchedulerClear } from "experimental-a2/server";
1
+ import { i as deliverSchedulerClear, r as deliverSchedulerAppend } from "./server-DtFrWxi3-Boa6gdWO.mjs";
2
2
  //#region src/scheduler/local.ts
3
3
  /** The longest delay a timer takes: past it, `setTimeout` fires at once. */
4
4
  const MAX_DELAY = 2 ** 31 - 1;
@@ -0,0 +1,72 @@
1
+ //#region src/contract/mentions.ts
2
+ const FENCED_CODE = /```[\s\S]*?```/g;
3
+ const INLINE_CODE = /`[^`\n]*`/g;
4
+ const NAME = "[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?";
5
+ const MENTION = new RegExp(`(?<![\\w@.-])@(${NAME}(?:/${NAME})?)(?!:|[a-z0-9_/-]|\\.(?=[a-z0-9]))`, "gi");
6
+ const EXACT = /(?<![\w@.-])@\[([^\]\s]+)\]/g;
7
+ /** Blanks out code so mentions inside it never count. */
8
+ const proseOnly = (text) => text.replace(FENCED_CODE, (match) => " ".repeat(match.length)).replace(INLINE_CODE, (match) => " ".repeat(match.length));
9
+ /** Mentions in prose order, deduplicated. Handles are lowercased. */
10
+ function extractMentions(text) {
11
+ const prose = proseOnly(text);
12
+ const found = [];
13
+ for (const match of prose.matchAll(EXACT)) {
14
+ const actor = match[1];
15
+ if (actor !== void 0) found.push({
16
+ index: match.index,
17
+ token: {
18
+ kind: "actor",
19
+ actor
20
+ },
21
+ key: `actor:${actor}`
22
+ });
23
+ }
24
+ for (const match of prose.replace(EXACT, (m) => " ".repeat(m.length)).matchAll(MENTION)) {
25
+ const handle = match[1]?.toLowerCase();
26
+ if (handle !== void 0) found.push({
27
+ index: match.index,
28
+ token: {
29
+ kind: "handle",
30
+ handle
31
+ },
32
+ key: `handle:${handle}`
33
+ });
34
+ }
35
+ const seen = /* @__PURE__ */ new Set();
36
+ return found.sort((a, b) => a.index - b.index).filter(({ key }) => !seen.has(key) && seen.add(key)).map(({ token }) => token);
37
+ }
38
+ /**
39
+ * Rewrites each mention in prose with `replace`, which also gets the mention as
40
+ * written; one it returns null for stays as written.
41
+ * Code is left alone, as in `extractMentions`.
42
+ */
43
+ function replaceMentions(text, replace) {
44
+ const prose = proseOnly(text);
45
+ const found = [];
46
+ for (const match of prose.matchAll(EXACT)) found.push({
47
+ index: match.index,
48
+ length: match[0].length,
49
+ token: {
50
+ kind: "actor",
51
+ actor: match[1]
52
+ }
53
+ });
54
+ for (const match of prose.replace(EXACT, (m) => " ".repeat(m.length)).matchAll(MENTION)) found.push({
55
+ index: match.index,
56
+ length: match[0].length,
57
+ token: {
58
+ kind: "handle",
59
+ handle: match[1].toLowerCase()
60
+ }
61
+ });
62
+ let out = "";
63
+ let at = 0;
64
+ for (const { index, length, token } of found.sort((a, b) => a.index - b.index)) {
65
+ const written = text.slice(index, index + length);
66
+ out += text.slice(at, index) + (replace(token, written) ?? written);
67
+ at = index + length;
68
+ }
69
+ return out + text.slice(at);
70
+ }
71
+ //#endregion
72
+ export { replaceMentions as n, extractMentions as t };
@@ -1,7 +1,5 @@
1
1
  import { m as MESSAGE_TEXT_MAX, p as Labels } from "./schemas-Be08nyfW.mjs";
2
2
  import { t as HttpError } from "./errors-DX3hpwWt.mjs";
3
- import { o as extractMentions, s as replaceMentions } from "./actors-DjMeZpnU.mjs";
4
- import { c as isThreadTarget } from "./reducers-mUPFuFJV.mjs";
5
3
  import { z } from "zod";
6
4
  //#region src/server/middleware.ts
7
5
  /** A write the middleware didn't let through: the API answers 422 with what it responded. */
@@ -61,34 +59,4 @@ async function runMiddleware(middleware, input, commit) {
61
59
  return committed.value;
62
60
  }
63
61
  //#endregion
64
- //#region src/server/targets.ts
65
- /** A target as a post or a receipt names it, resolved: a plain one's kind goes by its shape. */
66
- const syncTarget = (target) => typeof target === "string" ? {
67
- target,
68
- kind: isThreadTarget(target) ? "thread" : "channel"
69
- } : target;
70
- /** Whether `text` mentions anyone by email, `@[ada@vercel.com]`, which sync looks up. */
71
- const mentionsEmails = (text) => extractMentions(text).some((token) => token.kind === "actor" && token.actor.includes("@"));
72
- /**
73
- * `@[ada@vercel.com]`: each email `find` knows (a synced platform's user) becomes that user's
74
- * exact mention, `@[slack:user:U0456]`, so it reads natively there; the rest stay plain text.
75
- */
76
- async function withEmailMentions(text, find) {
77
- const emails = extractMentions(text).flatMap((token) => token.kind === "actor" && token.actor.includes("@") ? [token.actor] : []);
78
- if (emails.length === 0) return {
79
- text,
80
- found: []
81
- };
82
- const byEmail = /* @__PURE__ */ new Map();
83
- for (const email of emails) byEmail.set(email, await find?.(email).catch(() => null) ?? null);
84
- return {
85
- text: replaceMentions(text, (token) => {
86
- if (token.kind !== "actor" || !byEmail.has(token.actor)) return null;
87
- const actor = byEmail.get(token.actor);
88
- return actor ? `@[${actor.id}]` : token.actor;
89
- }),
90
- found: [...byEmail.values()].filter((actor) => actor !== null)
91
- };
92
- }
93
- //#endregion
94
- export { MIDDLEWARE_ACTOR as a, DroppedError as i, syncTarget as n, runMiddleware as o, withEmailMentions as r, mentionsEmails as t };
62
+ export { MIDDLEWARE_ACTOR as n, runMiddleware as r, DroppedError as t };
@@ -0,0 +1,32 @@
1
+ //#region ../../node_modules/.pnpm/experimental-a2@0.22.0_@upstash+qstash@2.12.0_@vercel+queue@0.7.0_ai@7.0.130_zod@4.6.5__52060f00b21aecfab049c27fa3abd2f9/node_modules/experimental-a2/dist/platform-B4TnJtWu.js
2
+ const SYMBOL_FOR_REQ_CONTEXT = Symbol.for("@vercel/request-context");
3
+ function requestContext() {
4
+ try {
5
+ return Reflect.get(globalThis, SYMBOL_FOR_REQ_CONTEXT)?.get?.() ?? {};
6
+ } catch {
7
+ return {};
8
+ }
9
+ }
10
+ /** The current invocation's Vercel deployment identity, when available. */
11
+ function platformVercelOidcToken() {
12
+ try {
13
+ const token = requestContext().headers?.["x-vercel-oidc-token"];
14
+ return typeof token === "string" && token.length > 0 ? token : null;
15
+ } catch {
16
+ return null;
17
+ }
18
+ }
19
+ function platformWaitUntil(promise) {
20
+ try {
21
+ requestContext().waitUntil?.(promise);
22
+ } catch {}
23
+ }
24
+ /** The invocation's termination time in epoch ms, or null if unknowable. */
25
+ function invocationDeadlineMs() {
26
+ const deadline = requestContext().deadline;
27
+ if (deadline === void 0 || deadline === null) return null;
28
+ const ms = new Date(deadline).getTime();
29
+ return Number.isNaN(ms) ? null : ms;
30
+ }
31
+ //#endregion
32
+ export { platformVercelOidcToken as n, platformWaitUntil as r, invocationDeadlineMs as t };
@@ -1,5 +1,5 @@
1
1
  import { n as setOwn, t as own } from "./own-OwgPWdmh.mjs";
2
- import { a as foldMessages, o as foldSync } from "./reducers-mUPFuFJV.mjs";
2
+ import { n as foldMessages, r as foldSync } from "./folds-Dyjput8M.mjs";
3
3
  //#region src/store/project.ts
4
4
  /** The message an event concerns, if any. */
5
5
  function concernedMessageId(event) {