@vercel/board 0.28.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{a2-B0_VlbtC.mjs → a2-D_28Pdip.mjs} +197 -259
- package/dist/access-CQ39gqEV.mjs +226 -0
- package/dist/access.d.mts +1 -1
- package/dist/access.mjs +3 -3
- package/dist/{actors-BLXVp4oZ.mjs → actors-xPiamLWk.mjs} +3 -3
- package/dist/{auth-lTkOgvcE.mjs → auth-BPinwfN0.mjs} +3 -226
- package/dist/backend-a2/blob-vercel.d.mts +1 -1
- package/dist/backend-a2/contract.d.mts +2 -2
- package/dist/backend-a2/contract.mjs +3 -3
- package/dist/backend-a2/scheduler-local.d.mts +1 -1
- package/dist/backend-a2/scheduler-local.mjs +1 -1
- package/dist/backend-a2/scheduler-qstash.d.mts +3 -3
- package/dist/backend-a2/scheduler-qstash.mjs +8 -8
- package/dist/backend-a2/scheduler-vercel.d.mts +3 -3
- package/dist/backend-a2/scheduler-vercel.mjs +6 -6
- package/dist/backend-a2/store-memory.d.mts +2 -2
- package/dist/backend-a2/store-memory.mjs +3 -3
- package/dist/backend-a2/store-postgres.d.mts +1 -1
- package/dist/backend-a2/store-postgres.mjs +34 -15
- package/dist/backend-a2/store-redis-http.d.mts +1 -1
- package/dist/backend-a2/store-redis-http.mjs +6 -6
- package/dist/backend-a2/store-redis.d.mts +2 -2
- package/dist/backend-a2/store-redis.mjs +5 -5
- package/dist/backend-a2/store-sqlite.d.mts +1 -1
- package/dist/backend-a2/store-sqlite.mjs +3 -3
- package/dist/backend-a2.d.mts +20 -7
- package/dist/backend-a2.mjs +2 -1
- package/dist/{backend-http-BvNFrvcY.mjs → backend-http-16SbvnAK.mjs} +2 -2
- package/dist/{backend-http-E7YU0qhn.d.mts → backend-http-3PoBYQoo.d.mts} +15 -3
- package/dist/{backend-http-CfLVYqmj.mjs → backend-http-BPaNYLWj.mjs} +4 -5
- package/dist/backend-http.d.mts +1 -1
- package/dist/backend-http.mjs +1 -1
- package/dist/backend-http.node.d.mts +1 -1
- package/dist/backend-http.node.mjs +1 -1
- package/dist/{backend-ZKYTe3xa.d.mts → backend-n3hajfrD.d.mts} +12 -6
- package/dist/{backend-vercel-D87pIp2k.mjs → backend-vercel-C4Kee2-K.mjs} +260 -30
- package/dist/{backend-vercel-DTrDiSk5.mjs → backend-vercel-Kd8nvW5A.mjs} +3 -3
- package/dist/{backend-vercel-CIRObA0t.d.mts → backend-vercel-qznuDxnN.d.mts} +20 -5
- package/dist/backend-vercel.d.mts +1 -1
- package/dist/backend-vercel.mjs +1 -1
- package/dist/backend-vercel.node.d.mts +1 -1
- package/dist/backend-vercel.node.mjs +1 -1
- package/dist/{blob-B-9AvYis.d.mts → blob-CqILvdmr.d.mts} +2 -2
- package/dist/blob-DQK5prXT.mjs +182 -0
- package/dist/{chat-provider-DnHibqnM.mjs → chat-provider-COtsU5jB.mjs} +2 -2
- package/dist/{chat-provider-DsNEBQiA.d.mts → chat-provider-D6jVT4wS.d.mts} +5 -3
- package/dist/cli-commands.d.mts +1 -1
- package/dist/cli-commands.mjs +1 -1
- package/dist/cli-embedded.d.mts +2 -2
- package/dist/cli-embedded.mjs +1 -1
- package/dist/cli.mjs +5 -5
- package/dist/{client-xLxeDz0f.d.mts → client-CjAatpV4.d.mts} +67 -10
- package/dist/{commands-BFookFtn.mjs → commands-B5DHjZKK.mjs} +54 -10
- package/dist/{commands-D6gvrOh3.d.mts → commands-BtH69yqt.d.mts} +17 -0
- package/dist/{conformance-DlnfuzEB.mjs → conformance-C7fwIvmN.mjs} +258 -18
- package/dist/conformance.d.mts +1 -1
- package/dist/conformance.mjs +1 -1
- package/dist/{engine-Bfe6pcXb.mjs → engine-DkdkXQEy.mjs} +21 -13
- package/dist/{errors-CB4tnZGz.d.mts → errors-87TVtXeb.d.mts} +1 -1
- package/dist/{client-D0Px7KzF.mjs → errors-C_5rB8Iz.mjs} +104 -4
- package/dist/{events-CewD4kw7.mjs → events-qf5XG5VQ.mjs} +2 -2
- package/dist/{exports-CCzw2Vdw.mjs → exports-CG0P8Qi4.mjs} +59 -33
- package/dist/exports-CrGA5_yN.d.mts +5 -0
- package/dist/{external-BV_cUncs.mjs → external-D3p5T57R.mjs} +4 -2
- package/dist/{folds-jS9wlh9_.d.mts → folds-8wRRXjAt.d.mts} +4 -0
- package/dist/{folds-Dyjput8M.mjs → folds-DGYkMW19.mjs} +3 -2
- package/dist/{index-C05it0mC.d.mts → index-BygAXP7t.d.mts} +2 -2
- package/dist/index.d.mts +6 -6
- package/dist/{index.default-DmKxH_E6.mjs → index.default-DhkgpfR2.mjs} +1 -1
- package/dist/index.mjs +5 -6
- package/dist/index.node.d.mts +6 -6
- package/dist/index.node.mjs +3 -4
- package/dist/inspect/app.js +29 -29
- package/dist/inspect/engine.js +5 -5
- package/dist/{internal-CvsrprnU-CRY89I-b.mjs → internal-CvsrprnU-zZMpDiRY.mjs} +1 -1
- package/dist/{local-DhkrAqPP.mjs → local-C76Fy5ad.mjs} +1 -1
- package/dist/{main-eci7w-aP.mjs → main-D2s-QBOK.mjs} +76 -26
- package/dist/{middleware-ChKM1zuu.mjs → middleware-DlJQYiwV.mjs} +2 -2
- package/dist/{node-Zd_C-gxP.mjs → node-D8Rtg1su.mjs} +1 -1
- package/dist/{platform-B4TnJtWu-CwcTyKg2.mjs → platform-B4TnJtWu-Do3agKr5.mjs} +1 -1
- package/dist/{project-DMDST9kl.mjs → project-C_2xs57L.mjs} +1 -1
- package/dist/{provider-poIkXapd.mjs → provider-BaVmi4Hi.mjs} +3 -1
- package/dist/{provider-Cdg1qadh.d.mts → provider-Bm46LAdp.d.mts} +9 -2
- package/dist/{redis-tables-DN9PfIDi.mjs → redis-tables-BWJZHUGg.mjs} +41 -25
- package/dist/{reducer-CjmvZSAb-D_OSISmj.mjs → reducer-Bswr11ar-P324o9vn.mjs} +2 -2
- package/dist/{reducer-CYPHsSmd-BQIfozGi.d.mts → reducer-CYPHsSmd-BXjGiptm.d.mts} +1 -1
- package/dist/{reducers-BcmSs4qf.mjs → reducers-8jqLewgw.mjs} +5 -5
- package/dist/{retryable-lazy-DZWmHpii-Bye-W8q7.mjs → retryable-lazy-DZWmHpii-DpTc5PIP.mjs} +1 -1
- package/dist/{scheduler-task-X1SToDjP-BVoWuInI.mjs → scheduler-task-X1SToDjP-D2Il2rln.mjs} +1 -1
- package/dist/{schemas-Be08nyfW.mjs → schemas-DBmUO6pr.mjs} +2 -1
- package/dist/{serve-B09an9DQ.mjs → serve-lPoBDEds.mjs} +7 -6
- package/dist/{server-qgez0Xny-D5mLfqFQ.d.mts → server-B9gu0qi1-DI1WvcVK.d.mts} +10 -4
- package/dist/{server-DTX0prGr.mjs → server-D82a4uMe.mjs} +4 -4
- package/dist/{server-DtFrWxi3-Boa6gdWO.mjs → server-DX7mM6nm-D7N_7bug.mjs} +32 -23
- package/dist/{serving-CvOWEym2.mjs → serving-DKny38LK.mjs} +1 -1
- package/dist/{session-clear-wHd5GCpZ-BnKSxReK.mjs → session-clear-XJYpmUpg-2q8l_FJR.mjs} +14 -2
- package/dist/{sql-U9WTxrwJ.mjs → sql-Bj0fk6dn.mjs} +2 -2
- package/dist/{sqlite-db-Ds4majtT.mjs → sqlite-db-Dc8G78XV.mjs} +1 -1
- package/dist/{store-D0MhBvg6-4Z2L0Kqd.d.mts → store-DEzcHZOA-B-5vhxef.d.mts} +12 -2
- package/dist/store-N8PXxDAS-DWWgv-Vz.mjs +5 -0
- package/dist/{store-codec-DwrWlgQx-CYxGW0aD.mjs → store-codec-pAG2oPgQ-SqDXlPBR.mjs} +4 -4
- package/dist/store-memory-4IBl0Ztu.mjs +2 -0
- package/dist/{store-memory-Bas9Uq5C.mjs → store-memory-B0PkVSuQ.mjs} +14 -7
- package/dist/{store-polling-BZt2Rf3D-BiEM6_HB.mjs → store-polling-BZt2Rf3D-Div-bJ_D.mjs} +2 -2
- package/dist/{store-presence-polling-JuAy2Ck2-CYpuK75X.mjs → store-presence-polling-JuAy2Ck2-Dh-g_KYM.mjs} +3 -3
- package/dist/{store-sqlite-CxxPCcCT.mjs → store-sqlite-D85t3tE7.mjs} +14 -7
- package/dist/store-sqlite-iBTYYKBj.mjs +2 -0
- package/dist/{stream-buffer-DrTyiIuo-BKy-vI14.mjs → stream-buffer-DrTyiIuo-Be10yOBR.mjs} +2 -2
- package/dist/sync-github.d.mts +4 -2
- package/dist/sync-github.mjs +4 -14
- package/dist/sync-kapso.d.mts +3 -2
- package/dist/sync-kapso.mjs +3 -3
- package/dist/sync-linear.d.mts +12 -2
- package/dist/sync-linear.mjs +191 -9
- package/dist/sync-slack.d.mts +41 -5
- package/dist/sync-slack.mjs +70 -11
- package/dist/sync.chat.d.mts +1 -1
- package/dist/sync.chat.mjs +1 -1
- package/dist/sync.d.mts +2 -2
- package/dist/sync.mjs +3 -3
- package/dist/tags-BpFo6q0_.mjs +14 -0
- package/dist/{targets-C-wdR5wP.mjs → targets-Cgjs29T0.mjs} +1 -1
- package/dist/{tokens-BuCU-61R.mjs → tokens-C5_yGRlL.mjs} +2 -2
- package/dist/{types-aCAf6lGU.d.mts → types-shZmGZ-q.d.mts} +2 -2
- package/dist/{watchers-hash-7DJU9b-n.mjs → watchers-hash-B_5b0-ZG.mjs} +50 -19
- package/docs/01-board/04-messages.md +93 -0
- package/docs/01-board/08-middleware.md +5 -2
- package/docs/02-sync/03-slack.md +6 -2
- package/docs/02-sync/07-linear.md +28 -6
- package/docs/04-reference/01-cli.md +27 -12
- package/docs/04-reference/02-board.md +20 -5
- package/docs/04-reference/03-http.md +17 -6
- package/docs/04-reference/05-conformance.md +14 -3
- package/package.json +2 -2
- package/dist/errors-CusN4x2l.mjs +0 -88
- package/dist/exports-D22M68rk.d.mts +0 -5
- package/dist/store-N8PXxDAS-BWBgzBHp.mjs +0 -5
- package/dist/store-memory-ZLiZbH3R.mjs +0 -2
- package/dist/store-sqlite-BmwL3pUR.mjs +0 -2
package/dist/inspect/engine.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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,k as h,l as g,m as _,n as v,o as y,p as b,r as x,s as ee,t as te,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,
|
|
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=
|
|
3
|
-
`)}function
|
|
4
|
-
`)}`:e,
|
|
5
|
-
`).filter(e=>/^📎 .* didn't come over: /u.test(e)),V=e=>JSON.stringify([e.url,e.message,e.snapshot,e.unavailable]),de=(e,t)=>e.length===t.length&&e.every((e,n)=>V(e)===V(t[n])),fe=/^\s*`?(vb-[a-z0-9]{4}-[a-z0-9]{4})`?\s*$/i,H=6e4,pe=new AbortController().signal,me=e=>{try{return JSON.parse(JSON.stringify(e??null))}catch{return null}},U=e=>typeof e==`string`?o(e):Array.isArray(e)?e.map(U):typeof e==`object`&&e?Object.fromEntries(Object.entries(e).map(([e,t])=>[o(e),U(t)])):e,W=e=>{let t=e?.data?.error,n=typeof t==`string`?t:e instanceof Error?e.message:String(e);return o(n.slice(0,500))||`unknown error`},G=e=>e instanceof m&&e.status===404;function he(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=pe,r=c){let i=o().backend;return i[e].call(i,r,t,n)}let f=t=>{let n=e[t];if(!n)throw s(`no sync entry named ${t}`);return n},p=e=>e.slice(0,e.indexOf(`:`)),g=t=>e[p(t)]!==void 0,_=e=>e.slice(e.indexOf(`:`)+1);async function C(e){try{return await l(`syncThread`,{thread:e})}catch(e){if(G(e))return null;throw e}}async function w(e){try{let{message:t,replies:n}=await l(`message`,{id:e});return[t,...n]}catch(e){if(G(e))return[];throw e}}async function T(e){return(await w(e)).find(t=>t.id===e)??null}async function E(e){return e.length===0?{}:(await l(`syncCopies`,{message:[...new Set(e)]})).copies}let D=(e,t)=>l(`syncFind`,e,t);async function O(){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 ne=(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(!d(t))return _(t);try{let n=f(e).inboxChannel(e,t);return n===null?null:_(n)}catch{return null}}async function re(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 s(`sync targets look like <key>:<channel or thread>, got: ${e}`);let n=e.slice(0,t),r=f(n);try{let i=ae.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 s(`can't sync to ${e}: ${t.message}`)}}async function V(e,t){let{event:n,raw:r,dedupe:i}=se.parse(U({...t,raw:me(t.raw)}));for(let t of[n.channel,n.thread,`subthread`in n?n.subthread:null])if(t!==null&&!t.startsWith(`${e}:`))throw s(`provider locations must belong to sync entry ${e}`);let a=ce(f(e));(!a||await a(_(n.channel),t=>A(e,t))||await he(n.thread))&&await l(`inboxPush`,{key:e,events:[{id:i,event:n,raw:r}]})}async function he(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 ge(t,n){let r=e[t];if(!r)throw y(`no sync entry named ${t}`);let i;try{i=await r.receive(t,n,e=>V(t,e))}catch(e){return console.error(`[board] intake failed`,e),new Response(`intake failed`,{status:500})}let a=_e([t]).catch(e=>console.error(`[board] couldn't pull ${t}'s inbox:`,W(e)));return x(a),i}async function _e(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=>ve(e,t))),n+=t.length}return n}async function ve(e,t){let n;try{await xe(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}:`,W(r)),n=W(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:`,W(e)))}async function ye(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:W(e)}));i.push({url:a.url,message:null,snapshot:c.snapshot,unavailable:c.snapshot?null:(c.unavailable??`unknown`).slice(0,300)})}return U(i)}async function be(e,t,i){let o=[],s=[],c=!1;for(let u of t.slice(0,10)){if(c){s.push(le(u));continue}if(u.size!==void 0&&u.size>26214400){s.push(R(u,`over ${n/1024/1024} MB`));continue}let t;try{let n=await e.fetchFile(u);if(n===null){s.push(R(u,`${e.platform} can't hand it over`));continue}t=n}catch(t){if(i<3)throw t;console.warn(`[board] couldn't fetch ${u.name??`a file`} from ${e.platform}:`,W(t)),s.push(R(u,`${e.platform} wouldn't hand it over`));continue}if(t.byteLength>26214400){s.push(R(u,`over ${n/1024/1024} MB`));continue}if(t.byteLength===0){s.push(R(u,`it was empty`));continue}let d=u.name??`${u.type}`;try{o.push((await l(`upload`,{data:t,name:d,type:u.mimeType||r(d)})).attachment)}catch(e){if(e instanceof m&&(e.code===`too_large`||e.code===`no_direct_uploads`)){s.push(R(u,`over ${a/1024/1024} MB`));continue}if(!z(e))throw e;c=!0,s.push(le(u))}}for(let e of t.slice(10))s.push(R(e,`a message carries at most 10`));return{attachments:o,notes:s}}async function xe(e,t,n){let{event:r,raw:i}=t,a=f(e),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 we(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:fe.exec(r.text)?.[1];if(i&&await Te(e,r,i))return}let l=`author`in r?r.author:{id:`${a.platform}:system`,handle:a.platform,name:a.platform,avatar:null,isBot:!0},u,d=()=>u??=P(a.event({id:n.id,key:e,event:r,raw:i})),p,m,h=!0,g={direction:`in`,key:e,channel:_(r.channel),get event(){return d()},board:o().client({author:l,key:e,...Ce(e,r)},n.id),import(r,i={}){if(!h)throw s(`the sync function has finished`);return p??=Se(e,t,n,c,r,i),p},permalink(){let e=m??a.permalink(d());return m=e,e.catch(()=>{m===e&&(m=void 0)}),e},resolve:t=>A(e,t)};try{await a.sync(g),await p}finally{h=!1}}async function Se(e,t,n,r,i,a){let o=i??(r?(await C(r))?.channel:void 0);if(o===void 0)return{root:null,message:null};try{return await Ae(e,t,n,b.parse(o),N.parse(a))}catch(e){if(!(e instanceof te))throw e;let n=(await D({thread:t.event.thread})).thread;return{root:n===null?null:(await C(n))?.root??null,message:null}}}function Ce(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(_(r),`thread`,{markdown:e})}}}async function we(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 C(e);if(!n)return!1;let r=n.targets.filter(e=>n.external[e]===t.thread),a=await w(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 l(`syncRoots`,{by:n.id,since:r-H}),o=await E(a.map(e=>e.id));for(let e of a){let n=await C(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 Te(e,t,n){try{await l(`syncAccount`,{sync:e,account:t.author,code:n,message:u(t.subthread??t.thread,t.message)})}catch(e){if(G(e))return!1;throw e}return await f(e).react(_(t.subthread??t.thread),t.message,`check`,!0).catch(t=>console.warn(`[board] couldn't confirm a linking code on ${e}:`,W(t))),!0}let Ee=e=>f(e).users!==null;async function De(e,t,n){let r=f(e);if(!r.users)throw s(`${e} can't sign people in: link it with a code`);return r.users.authorize(t,n?{callback:n}:{})}async function Oe(e,t){let n=f(e);if(!n.users)throw s(`${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 s(`${e} named an account that isn't a ${n.platform} user`);return i}async function ke(e,t,n){let r=await Oe(e,t);return r?(await l(`syncAccount`,{sync:e,account:r,code:n,method:`consent`})).link:null}async function Ae(e,t,n,r,i){let{event:a,raw:o}=t,c=Ce(e,a),u=f(e),d=(await D({thread:a.thread})).thread??void 0,p=d?await C(d):null,m=p?.root,g=e=>ee(`board-inbound`,n.id,String(e)),v={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)},b=async({attachments:t=[],...n})=>({id:await y(await l(`syncImport`,{op:`post`,from:e,source:v,...n,origin:n.origin??null,...t.length?{attachments:t.map(e=>e.id)}:{}})),re:n.re??null}),x=e=>({root:e.re??e.id,message:e.id}),S=(e={})=>{let t={...e,...i.labels};for(let e of i.unset??[])delete t[e];return t},w=e=>{if(!i.root)return e;let t=i.root(P(structuredClone(e)));if(t&&typeof t==`object`&&`then`in t)throw s(`root overrides must be synchronous`);let n=N.pick({text:!0,labels:!0}).parse(t);return{...e,text:n.text??e.text,labels:{...e.labels,...n.labels}}},E=(t,n,r)=>l(`syncImport`,{op:`labels`,author:t,from:e,origin:{thread:a.thread},set:n,unset:r}),O=async(e,t)=>{if(!i.labels&&!i.unset)return;let n=await T(t),r=Object.fromEntries(Object.entries(i.labels??{}).filter(([e,t])=>n?.labels[e]!==t)),a=(i.unset??[]).filter(e=>n?.labels[e]!==void 0);(Object.keys(r).length||a.length)&&await E(e,r,a)};switch(a.kind){case`message`:{let e=(await D({thread:a.subthread??a.thread,message:a.message})).message;if(e!==null)return{root:m??e,message:e};let{attachments:t,notes:s}=a.files?.length?await be(u,a.files,n.attempt):{attachments:[],notes:[]},c=await ye(u,a.links??[],p?.channel??r),l={...c.length?{links:c}:{},author:a.author,text:L(B(i.text??a.text,s),t.length>0),...t.length?{attachments:t}:{},origin:{channel:a.channel,thread:a.thread,subthread:a.subthread,message:a.message,context:a.context},id:await g(0)},d={...l,...i.noSync?{noSync:!0}:{}};if(m!==void 0)return await O(a.author,m),x(await b({...d,re:m}));let f=await u.fetchRoot(_(a.thread));if(f===null){let e=w({text:a.text,labels:{},author:a.author,raw:o});return x(await b({...l,channel:r,text:L(B(i.text??e.text,s),t.length>0),labels:S(e.labels)}))}let h=w({...f,raw:f.raw??null}),v=await b({author:f.author,channel:r,text:L(h.text),labels:S(h.labels),origin:{channel:a.channel,thread:a.thread,subthread:null,message:`root:${n.id}`,context:null},id:await g(1)});return x(await b({...d,re:v.id}))}case`edited`:case`deleted`:case`reaction`:{let t=(await D({thread:a.subthread??a.thread,message:a.message})).message;if(t===null)return{root:m??null,message:null};let n=`author`in a?a.author:{id:`${u.platform}:system`,handle:u.platform,name:u.platform,avatar:null,isBot:!0},r={thread:a.subthread??a.thread,message:a.message};if(m!==void 0&&await O(n,m),a.kind===`deleted`)await l(`syncImport`,{op:`delete`,author:n,from:e,origin:r});else if(a.kind===`edited`){let o=await T(t),s=L(B(i.text??a.text,o?ue(o.text):[]),(o?.attachments.length??0)>0);o&&!a.linksOnly&&o.text!==s&&await y(await l(`syncImport`,{op:`edit`,author:n,from:e,origin:r,text:s,source:v}));let c=o?await ye(u,a.links??[],o.channel,o.links):[];o&&!de(c,o.links)&&await l(`syncImport`,{op:`links`,author:n,from:e,origin:r,links:c})}else{if(u.singleReaction){let i=h(a.emoji);for(let[o,s]of Object.entries((await T(t))?.reactions??{}))s.includes(n.id)&&!(a.added&&o===i)&&await l(`syncImport`,{op:`react`,author:n,from:e,origin:r,emoji:o,remove:!0})}(!u.singleReaction||a.added)&&await l(`syncImport`,{op:`react`,author:n,from:e,origin:r,emoji:a.emoji,remove:!a.added})}return{root:m??null,message:t}}case`lifecycle`:{if(m!==void 0){let t=await T(m),r=t?.origin?.message===`lifecycle:${n.id}`,s=S((r?w({text:a.url?`${a.title}\n${a.url}`:a.title,labels:a.labels,author:a.author,raw:o}):null)?.labels??a.labels),c=e=>a.owns?.some(t=>e.startsWith(t))??!1,u=Object.fromEntries(Object.entries(s).filter(([e,n])=>t?.labels[e]!==n)),d=Object.keys(t?.labels??{}).filter(e=>!(e in s)&&(a.unset.includes(e)||i.unset?.includes(e)||c(e)));if(t&&!t.deleted&&(Object.keys(u).length||d.length)&&await E(a.author,u,d),a.retitled&&t&&!t.deleted&&t.from===e&&t.origin?.thread===a.thread){let n=L(i.text??w({text:a.url?`${a.title}\n${a.url}`:a.title,labels:t.labels,author:a.author,raw:o}).text);t.text!==n&&await y(await l(`syncImport`,{op:`edit`,author:a.author,from:e,origin:{thread:t.origin.thread,message:t.origin.message},text:n}))}return a.reply?x(await b({author:a.author,re:m,text:L(i.text??a.reply),...i.noSync?{noSync:!0}:{},id:await g(1)})):{root:m,message:r?m:null}}if(!a.createRoot)return{root:null,message:null};let t=w({text:a.url?`${a.title}\n${a.url}`:a.title,labels:a.labels,author:a.author,raw:o}),s=await b({author:a.author,channel:r,text:L(a.reply?t.text:i.text??t.text),labels:S(t.labels),origin:{channel:a.channel,thread:a.thread,subthread:null,message:`lifecycle:${n.id}`,context:null},id:await g(0)});return a.reply?x(await b({author:a.author,re:s.id,text:L(i.text??a.reply),...i.noSync?{noSync:!0}:{},id:await g(1)})):x(s)}}}function je(e,t,n,r=t.text){let i=t.mentions.map(e=>n.byId[e]).filter(e=>e!==void 0);return v(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?K(e,a.id,n)??a.name:null})}function K(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 ne(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 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:`,W(n)),null}}function Ne(e,t,n){let r=f(p(e)),i=je(r,t,n,M(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,_(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:`,W(t)),null}}function Fe(t,n,r,i,a){let o=i.byId[r.by.actor],c=o?.name??r.by.actor;r.by.profile&&o?.handle.includes(`/`)&&(c=`${o.name} via ${o.handle.split(`/`)[0]}`),r.from&&(c=`${c} (${e[r.from]?.platform??r.by.actor.slice(0,r.by.actor.indexOf(`:`))})`);let l=f(t),u=(a??(e=>l.render(e)))(P({message:{...structuredClone(r),text:M(r.text,r.by.actor)},author:o?{...o}:null,mentionOf:e=>K(l,e,i),label:c,target:n}));if(typeof u!=`string`)throw s(`a provider renderer must return markdown`);let d=je(l,r,i,u);return r.by.profile&&!r.from?{markdown:d,as:{name:c,avatar:Ie(r,o,i)}}:{markdown:d}}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(n=>`\n📎 ${n.name} (${t(n.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 E([e.replyTo]))[e.replyTo]?.[t],o=a!==void 0&&a.thread!==n?a.thread:n,c=f(p(t)),l=q(t,e,r,!1,i),u,d;if(!l.markdown.trim()&&e.attachments.length&&c.uploadsFiles)try{let n=await Be(e,t,o);d=n.message,u=n.files}catch(n){if(!c.refusesFiles(n)&&!z(n))throw n;l=q(t,e,r,!0,i),d=(await c.post(_(o),`thread`,l)).message,u=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(!l.markdown.trim())throw s(`a renderer must return text unless the message has media`);d=(await c.post(_(o),`thread`,l)).message}let m={thread:o,message:d,...u===void 0?{}:{files:u}};return await J(e,t,m),m}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(_(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: ${W(s)}`),{cause:s,data:{error:`files: ${W(s)}`}});console.warn(`[board] ${t} won't take #${e.id}'s files (${W(s)}); noting them in its text`),o=null,await a.edit(_(n.thread),n.message,q(t,e,r,!0,i)).catch(n=>console.warn(`[board] couldn't note #${e.id}'s files on ${t}:`,W(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||ie(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}`}:`,W(r)),n!==null&&await l(`syncReceipt`,{kind:`failed`,message:e,target:n,error:W(r)})}}async function Q(e,t,n,r,i){let a,o=!0,c=()=>s(`the sync function has finished`),l={direction:`out`,key:e,channel:r===null?null:j(e,r),event:P(structuredClone(t)),message:P(structuredClone(n)),target:r,deliver(e={}){if(!o)throw c();return a??=r===null?Promise.resolve():i(e.render),a},start(t){if(!o)throw c();return Ve(e,n,t)},resolve:t=>A(e,t)};try{await f(e).sync(l),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 T(t.re);!a||a.deleted||a.noSync||r!==(a.origin?a.origin.channel??S(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 T(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??(d(r)?`thread`:`channel`))===`thread`)return;let i=await C(n),[a,...o]=i?await w(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 O(),c=await E([t.id,...n.map(e=>e.id)]),l=e=>(i.push({id:t.id,target:r,error:e}),e),u=e.external[r];if(u){let e=c[t.id]?.[r];if(e)try{await Y(t,r,e,o,a)}catch(e){throw l(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 s(`a renderer must return text unless the message has media`);let c,l=!1,d=await(async()=>{if(!i){let i=await Pe(r,r,`channel`,t,o);return i?(l=!0,i):e.post(_(r),`channel`,n)}try{let i=await e.post(_(r),`channel`,{...n,files:await ze(t)});return c=i.files??[],i}catch(n){if(!e.refusesFiles(n)&&!z(n))throw n;return c=null,e.post(_(r),`channel`,q(r,t,o,!0,a))}})();u=`${p(r)}:${d.thread}`;let m={thread:u,message:d.message,...c===void 0?{}:{files:c},...l?{asUser:!0}:{}};await J(t,r,m),await Y(t,r,m,o,a)}catch(e){throw l(e)}let d=[];for(let e of n)if(!(e.deleted||e.noSync||Ge(e,r)))try{await Y(e,r,c[e.id]?.[r]??await Re(e,r,u,o,a),o,a)}catch(t){d.push({id:e.id,target:r,error:t})}if(i.push(...d),d.length)throw d[0].error}let Ge=(e,t)=>e.origin?.thread===t||e.from!==null&&p(t)===e.from;async function Ke(e,t){if(!e.message)return;if(e.message.re===null)return He(e,t);if(e.payload.noSync)return;let n=await C(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 t of n.targets){let o=n.external[t];if(g(t)&&o&&!Ge(r,t))try{await Q(p(t),e,r,t,async e=>{let n=(await E([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 qe(e){let t=(await E([e.payload.id]))[e.payload.id]??{};if(Object.keys(t).length===0)return;let n=e.type===`message.edited`?await T(e.payload.id):e.message??await T(e.payload.id);if(!n||e.type===`message.edited`&&n.deleted)return;let r=await O();for(let[i,a]of Object.entries(t))if(!(!g(i)||e.payload.from!==null&&p(i)===e.payload.from))try{await Q(p(i),e,n,i,async t=>{let o=f(p(i)),s=_(a.thread),c=a.asUser&&(e.type===`message.edited`||e.type===`message.deleted`),l=c?await Me(o,n,!1):null;if(c&&!l){console.warn(`[board] skipped ${e.type} on ${i}: #${n.id} was posted as its author, and their token is gone`);return}e.type===`message.edited`?l?await o.editAs(l,s,a.message,Ne(i,n,r)):await o.edit(s,a.message,q(i,n,r,a.files===null,t)):e.type===`message.deleted`?(l?await o.deleteAs(l,s,a.message):await o.delete(s,a.message),a.files?.length&&await o.deleteFiles(a.files)):e.type===`reaction.added`?await o.react(s,a.message,h(e.payload.emoji),!0):await o.react(s,a.message,h(e.payload.emoji),!1)})}catch(t){console.warn(`[board] skipped ${e.type} on ${i}:`,t.message)}}async function Je(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[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(_(r.thread),r.message,n)})}catch(e){console.warn(`[board] skipped labels.changed on ${t}:`,W(e))}}async function Ye(e,{attempt:t}){switch(e.type){case`thread.synced`:return Ue(e,t);case`message.posted`:return Ke(e,t);case`message.edited`:case`message.deleted`:case`reaction.added`:case`reaction.removed`:return qe(e);case`labels.changed`:return Je(e)}}async function Xe(e,t){let n=e[0]?await C(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(_(n),a,t),n}catch(e){return console.warn(`[board] skipped typing on ${n}:`,W(e)),null}}))).filter(e=>e!==null)}async function Ze(e,t){try{return`${e}:${await f(e).resolveChannel(t)}`}catch(e){throw s(`can't resolve inbox channel: ${e.message}`)}}let $=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 F?new m($(n.code),n.code,n.message):s(`can't read ${t} from ${e}: ${W(n)}`,`platform_error`)}}async function $e(e,t){let n=f(e);try{return await n.join(t)}catch(n){throw n instanceof F?new m($(n.code),n.code,n.message):s(`can't join ${t} on ${e}: ${W(n)}`,`platform_error`)}}return{entries:e,bind(e){i=e},caller:c,deliver:Ye,receive:xe,pull:_e,normalizeTarget:I,findByEmail:re,handleWebhook:ge,inboxChannel:Ze,signsIn:Ee,signIn:De,signedIn:Oe,redeemSignIn:ke,typing:Xe,readSource:Qe,joinSource:$e}}export{he 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,k as h,l as g,m as _,n as v,o as y,p as b,r as x,s as ee,t as te,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 ne(e,t){let n=t===`github:user:35613825`?A(e):null;return n?`**▲ Vercel**\n${N(n)}`:e}const re=e=>e>10;i({id:e().min(1),platform:e().min(1),key:e().min(1),...g.shape,raw:w()});const ie=i({root:T(e=>typeof e==`function`).optional(),text:e().optional(),labels:p.optional(),unset:E(_).optional(),noSync:c().optional()}).strict();function P(e){if(e&&typeof e==`object`){for(let t of Object.values(e))P(t);Object.freeze(e)}return e}const ae=i({target:e().min(1),channel:e().min(1),kind:C([`channel`,`thread`])}).strict();var F=class extends Error{code;constructor(e,t){super(t),this.code=e}};const oe=e=>e instanceof F&&e.code===`channel_not_found`,se=i({event:l,raw:w(),dedupe:e().min(1)}),I=/* @__PURE__ */ new WeakMap,ce=e=>I.get(e),L=(e,t=!1)=>{let n=o(e),r=n.trim()===``?t?``:`(no text)`:n;return r.length>1e4?`${r.slice(0,f-1).toWellFormed()}…`:r},R=(e,n,r=e.name??`a file`)=>`📎 ${r}${e.size===void 0?``:` (${t(e.size)})`} didn't come over: ${n}`,z=e=>e instanceof m&&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,fe=6e4,pe=new AbortController().signal,me=e=>{try{return JSON.parse(JSON.stringify(e??null))}catch{return null}},U=e=>typeof e==`string`?o(e):Array.isArray(e)?e.map(U):typeof e==`object`&&e?Object.fromEntries(Object.entries(e).map(([e,t])=>[o(e),U(t)])):e,W=e=>{let t=e?.data?.error,n=typeof t==`string`?t:e instanceof Error?e.message:String(e);return o(n.slice(0,500))||`unknown error`},G=e=>e instanceof m&&e.status===404;function he(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=pe,r=c){let i=o().backend;return i[e].call(i,r,t,n)}let f=t=>{let n=e[t];if(!n)throw s(`no sync entry named ${t}`);return n},p=e=>e.slice(0,e.indexOf(`:`)),g=t=>e[p(t)]!==void 0,_=e=>e.slice(e.indexOf(`:`)+1);async function C(e){try{return await l(`syncThread`,{thread:e})}catch(e){if(G(e))return null;throw e}}async function w(e){try{let{message:t,replies:n}=await l(`message`,{id:e});return[t,...n]}catch(e){if(G(e))return[];throw e}}async function T(e){return(await w(e)).find(t=>t.id===e)??null}async function E(e){return e.length===0?{}:(await l(`syncCopies`,{message:[...new Set(e)]})).copies}let D=(e,t)=>l(`syncFind`,e,t);async function O(){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 k=(e,t)=>Object.entries(e.links).filter(([,e])=>e===t).map(([e])=>e),A=/* @__PURE__ */ new Map;function j(e,t){let n=`${e}:${t}`,r=A.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&&A.get(n)===i&&A.delete(n),console.warn(`[board] sync ${e}: ${a?`skipping`:`couldn't resolve`} ${t}: ${r.message}`)}),A.set(n,i),i.id}function M(e,t){if(!d(t))return _(t);try{let n=f(e).inboxChannel(e,t);return n===null?null:_(n)}catch{return null}}async function N(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 s(`sync targets look like <key>:<channel or thread>, got: ${e}`);let n=e.slice(0,t),r=f(n);try{let i=ae.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 s(`can't sync to ${e}: ${t.message}`)}}async function H(e,t){let{event:n,raw:r,dedupe:i}=se.parse(U({...t,raw:me(t.raw)}));for(let t of[n.channel,n.thread,`subthread`in n?n.subthread:null])if(t!==null&&!t.startsWith(`${e}:`))throw s(`provider locations must belong to sync entry ${e}`);let a=ce(f(e));(!a||await a(_(n.channel),t=>j(e,t))||await he(n.thread))&&await l(`inboxPush`,{key:e,events:[{id:i,event:n,raw:r}]})}async function he(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 ge(t,n){let r=e[t];if(!r)throw y(`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})}let a=_e([t]).catch(e=>console.error(`[board] couldn't pull ${t}'s inbox:`,W(e)));return x(a),i}async function _e(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=>ve(e,t))),n+=t.length}return n}async function ve(e,t){let n;try{await xe(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}:`,W(r)),n=W(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:`,W(e)))}async function ye(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:W(e)}));i.push({url:a.url,message:null,snapshot:c.snapshot,unavailable:c.snapshot?null:(c.unavailable??`unknown`).slice(0,300)})}return U(i)}async function be(e,t,i){let o=[],s=[],c=!1;for(let u of t.slice(0,10)){if(c){s.push(B(u));continue}if(u.size!==void 0&&u.size>26214400){s.push(R(u,`over ${n/1024/1024} MB`));continue}let t;try{let n=await e.fetchFile(u);if(n===null){s.push(R(u,`${e.platform} can't hand it over`));continue}t=n}catch(t){if(i<3)throw t;console.warn(`[board] couldn't fetch ${u.name??`a file`} from ${e.platform}:`,W(t)),s.push(R(u,`${e.platform} wouldn't hand it over`));continue}if(t.byteLength>26214400){s.push(R(u,`over ${n/1024/1024} MB`));continue}if(t.byteLength===0){s.push(R(u,`it was empty`));continue}let d=u.name??`${u.type}`;try{o.push((await l(`upload`,{data:t,name:d,type:u.mimeType||r(d)})).attachment)}catch(e){if(e instanceof m&&(e.code===`too_large`||e.code===`no_direct_uploads`)){s.push(R(u,`over ${a/1024/1024} MB`));continue}if(!z(e))throw e;c=!0,s.push(B(u))}}for(let e of t.slice(10))s.push(R(e,`a message carries at most 10`));return{attachments:o,notes:s}}async function xe(e,t,n){let{event:r,raw:i}=t,a=f(e),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 we(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:de.exec(r.text)?.[1];if(i&&await Te(e,r,i))return}let l=`author`in r?r.author:{id:`${a.platform}:system`,handle:a.platform,name:a.platform,avatar:null,isBot:!0},u,d=()=>u??=P(a.event({id:n.id,key:e,event:r,raw:i})),p,m,h=!0,g={direction:`in`,key:e,channel:_(r.channel),get event(){return d()},board:o().client({author:l,key:e,...Ce(e,r)},n.id),import(r,i={}){if(!h)throw s(`the sync function has finished`);return p??=Se(e,t,n,c,r,i),p},permalink(){let e=m??a.permalink(d());return m=e,e.catch(()=>{m===e&&(m=void 0)}),e},resolve:t=>j(e,t)};try{await a.sync(g),await p}finally{h=!1}}async function Se(e,t,n,r,i,a){let o=i??(r?(await C(r))?.channel:void 0);if(o===void 0)return{root:null,message:null};try{return await Ae(e,t,n,b.parse(o),ie.parse(a))}catch(e){if(!(e instanceof te))throw e;let n=(await D({thread:t.event.thread})).thread;return{root:n===null?null:(await C(n))?.root??null,message:null}}}function Ce(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(_(r),`thread`,{markdown:e})}}}async function we(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)<fe&&!i[t]&&!e.undelivered[t];if(e!==void 0){let n=await C(e);if(!n)return!1;let r=n.targets.filter(e=>n.external[e]===t.thread),a=await w(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 l(`syncRoots`,{by:n.id,since:r-fe}),o=await E(a.map(e=>e.id));for(let e of a){let n=await C(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 Te(e,t,n){try{await l(`syncAccount`,{sync:e,account:t.author,code:n,message:u(t.subthread??t.thread,t.message)})}catch(e){if(G(e))return!1;throw e}return await f(e).react(_(t.subthread??t.thread),t.message,`check`,!0).catch(t=>console.warn(`[board] couldn't confirm a linking code on ${e}:`,W(t))),!0}let Ee=e=>f(e).users!==null;async function De(e,t,n){let r=f(e);if(!r.users)throw s(`${e} can't sign people in: link it with a code`);return r.users.authorize(t,n?{callback:n}:{})}async function Oe(e,t){let n=f(e);if(!n.users)throw s(`${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 s(`${e} named an account that isn't a ${n.platform} user`);return i}async function ke(e,t,n){let r=await Oe(e,t);return r?(await l(`syncAccount`,{sync:e,account:r,code:n,method:`consent`})).link:null}async function Ae(e,t,n,r,i){let{event:a,raw:o}=t,c=Ce(e,a),u=f(e),d=(await D({thread:a.thread})).thread??void 0,p=d?await C(d):null,m=p?.root,g=e=>ee(`board-inbound`,n.id,String(e)),v={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)},b=async({attachments:t=[],...n})=>({id:await y(await l(`syncImport`,{op:`post`,from:e,source:v,...n,origin:n.origin??null,...t.length?{attachments:t.map(e=>e.id)}:{}})),re:n.re??null}),x=e=>({root:e.re??e.id,message:e.id}),S=(e={})=>{let t={...e,...i.labels};for(let e of i.unset??[])delete t[e];return t},w=e=>{if(!i.root)return e;let t=i.root(P(structuredClone(e)));if(t&&typeof t==`object`&&`then`in t)throw s(`root overrides must be synchronous`);let n=ie.pick({text:!0,labels:!0}).parse(t);return{...e,text:n.text??e.text,labels:{...e.labels,...n.labels}}},E=(t,n,r)=>l(`syncImport`,{op:`labels`,author:t,from:e,origin:{thread:a.thread},set:n,unset:r}),O=async(e,t)=>{if(!i.labels&&!i.unset)return;let n=await T(t),r=Object.fromEntries(Object.entries(i.labels??{}).filter(([e,t])=>n?.labels[e]!==t)),a=(i.unset??[]).filter(e=>n?.labels[e]!==void 0);(Object.keys(r).length||a.length)&&await E(e,r,a)};switch(a.kind){case`message`:{let e=(await D({thread:a.subthread??a.thread,message:a.message})).message;if(e!==null)return{root:m??e,message:e};let{attachments:t,notes:s}=a.files?.length?await be(u,a.files,n.attempt):{attachments:[],notes:[]},c=await ye(u,a.links??[],p?.channel??r),l={...c.length?{links:c}:{},author:a.author,text:L(V(i.text??a.text,s),t.length>0),...t.length?{attachments:t}:{},origin:{channel:a.channel,thread:a.thread,subthread:a.subthread,message:a.message,context:a.context},id:await g(0)},d={...l,...i.noSync?{noSync:!0}:{}};if(m!==void 0)return await O(a.author,m),x(await b({...d,re:m}));let f=await u.fetchRoot(_(a.thread));if(f===null){let e=w({text:a.text,labels:{},author:a.author,raw:o});return x(await b({...l,channel:r,text:L(V(i.text??e.text,s),t.length>0),labels:S(e.labels)}))}let h=w({...f,raw:f.raw??null}),v=await b({author:f.author,channel:r,text:L(h.text),labels:S(h.labels),origin:{channel:a.channel,thread:a.thread,subthread:null,message:`root:${n.id}`,context:null},id:await g(1)});return x(await b({...d,re:v.id}))}case`edited`:case`deleted`:case`reaction`:{let t=(await D({thread:a.subthread??a.thread,message:a.message})).message;if(t===null)return{root:m??null,message:null};let n=`author`in a?a.author:{id:`${u.platform}:system`,handle:u.platform,name:u.platform,avatar:null,isBot:!0},r={thread:a.subthread??a.thread,message:a.message};if(m!==void 0&&await O(n,m),a.kind===`deleted`)await l(`syncImport`,{op:`delete`,author:n,from:e,origin:r});else if(a.kind===`edited`){let o=await T(t),s=L(V(i.text??a.text,o?le(o.text):[]),(o?.attachments.length??0)>0);o&&!a.linksOnly&&o.text!==s&&await y(await l(`syncImport`,{op:`edit`,author:n,from:e,origin:r,text:s,source:v}));let c=o?await ye(u,a.links??[],o.channel,o.links):[];o&&!ue(c,o.links)&&await l(`syncImport`,{op:`links`,author:n,from:e,origin:r,links:c})}else{if(u.singleReaction){let i=h(a.emoji);for(let[o,s]of Object.entries((await T(t))?.reactions??{}))s.includes(n.id)&&!(a.added&&o===i)&&await l(`syncImport`,{op:`react`,author:n,from:e,origin:r,emoji:o,remove:!0})}(!u.singleReaction||a.added)&&await l(`syncImport`,{op:`react`,author:n,from:e,origin:r,emoji:a.emoji,remove:!a.added})}return{root:m??null,message:t}}case`lifecycle`:{if(m!==void 0){let t=await T(m),r=t?.origin?.message===`lifecycle:${n.id}`,s=S((r?w({text:K(a),labels:a.labels,author:a.author,raw:o}):null)?.labels??a.labels),c=e=>a.owns?.some(t=>e.startsWith(t))??!1,u=Object.fromEntries(Object.entries(s).filter(([e,n])=>t?.labels[e]!==n)),d=Object.keys(t?.labels??{}).filter(e=>!(e in s)&&(a.unset.includes(e)||i.unset?.includes(e)||c(e)));if(t&&!t.deleted&&(Object.keys(u).length||d.length)&&await E(a.author,u,d),a.retitled&&t&&!t.deleted&&t.from===e&&t.origin?.thread===a.thread){let n=L(i.text??w({text:K(a),labels:t.labels,author:a.author,raw:o}).text);t.text!==n&&await y(await l(`syncImport`,{op:`edit`,author:a.author,from:e,origin:{thread:t.origin.thread,message:t.origin.message},text:n}))}return a.reply?x(await b({author:a.author,re:m,text:L(i.text??a.reply),...i.noSync?{noSync:!0}:{},id:await g(1)})):{root:m,message:r?m:null}}if(!a.createRoot)return{root:null,message:null};let t=w({text:K(a),labels:a.labels,author:a.author,raw:o}),s=await b({author:a.author,channel:r,text:L(a.reply?t.text:i.text??t.text),labels:S(t.labels),origin:{channel:a.channel,thread:a.thread,subthread:null,message:`lifecycle:${n.id}`,context:null},id:await g(0)});return a.reply?x(await b({author:a.author,re:s.id,text:L(i.text??a.reply),...i.noSync?{noSync:!0}:{},id:await g(1)})):x(s)}}}function je(e,t,n,r=t.text){let i=t.mentions.map(e=>n.byId[e]).filter(e=>e!==void 0);return v(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?Me(e,a.id,n)??a.name:null})}function Me(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 Ne(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:`,W(n)),null}}function Pe(e,t,n){let r=f(p(e)),i=je(r,t,n,ne(t.text,t.by.actor));return{markdown:t.attachments.length>0&&!r.uploadsFiles?`${i}\n${Re(t)}`:i}}async function Fe(e,t,n,r,i){let a=f(p(e));if(!r.text.trim())return null;let o=await Ne(a,r,!0);if(!o)return null;try{return await a.postAs(o,_(t),n,Pe(e,r,i))}catch(t){return console.warn(`[board] couldn't post #${r.id} to ${e} as its author; posting as the app:`,W(t)),null}}function Ie(t,n,r,i,a){let o=i.byId[r.by.actor],c=o?.name??r.by.actor;r.by.profile&&o?.handle.includes(`/`)&&(c=`${o.name} via ${o.handle.split(`/`)[0]}`),r.from&&(c=`${c} (${e[r.from]?.platform??r.by.actor.slice(0,r.by.actor.indexOf(`:`))})`);let l=f(t),u=(a??(e=>l.render(e)))(P({message:{...structuredClone(r),text:ne(r.text,r.by.actor)},author:o?{...o}:null,mentionOf:e=>Me(l,e,i),label:c,target:n}));if(typeof u!=`string`)throw s(`a provider renderer must return markdown`);let d=je(l,r,i,u);return r.by.profile&&!r.from?{markdown:d,as:{name:c,avatar:Le(r,o,i)}}:{markdown:d}}function Le(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 Re=e=>e.attachments.map(n=>`\n📎 ${n.name} (${t(n.size)}, on the board: #${e.id})`).join(``),q=(e,t,n,r=!1,i)=>{let a=p(e),{markdown:o,as:s}=Ie(a,e,t,n,i);return{markdown:t.attachments.length>0&&(r||!f(a).uploadsFiles)?`${o}\n${Re(t)}`:o,...s?{as:s}:{}}};async function ze(e,t,n,r,i){let a=e.replyTo===null?void 0:(await E([e.replyTo]))[e.replyTo]?.[t],o=a!==void 0&&a.thread!==n?a.thread:n,c=f(p(t)),l=q(t,e,r,!1,i),u,d;if(!l.markdown.trim()&&e.attachments.length&&c.uploadsFiles)try{let n=await Ve(e,t,o);d=n.message,u=n.files}catch(n){if(!c.refusesFiles(n)&&!z(n))throw n;l=q(t,e,r,!0,i),d=(await c.post(_(o),`thread`,l)).message,u=null}else{let n=await Fe(t,o,`thread`,e,r);if(n){let r={thread:o,message:n.message,asUser:!0};return await J(e,t,r),r}if(!l.markdown.trim())throw s(`a renderer must return text unless the message has media`);d=(await c.post(_(o),`thread`,l)).message}let m={thread:o,message:d,...u===void 0?{}:{files:u}};return await J(e,t,m),m}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 Be(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 Ve(e,t,n){return f(p(t)).uploadFiles(_(n),await Be(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 Ve(e,t,n.thread)).files}catch(s){if(!a.refusesFiles(s)&&!z(s))throw Object.assign(/* @__PURE__ */ Error(`files: ${W(s)}`),{cause:s,data:{error:`files: ${W(s)}`}});console.warn(`[board] ${t} won't take #${e.id}'s files (${W(s)}); noting them in its text`),o=null,await a.edit(_(n.thread),n.message,q(t,e,r,!0,i)).catch(n=>console.warn(`[board] couldn't note #${e.id}'s files on ${t}:`,W(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||re(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}`}:`,W(r)),n!==null&&await l(`syncReceipt`,{kind:`failed`,message:e,target:n,error:W(r)})}}async function Q(e,t,n,r,i){let a,o=!0,c=()=>s(`the sync function has finished`),l={direction:`out`,key:e,channel:r===null?null:M(e,r),event:P(structuredClone(t)),message:P(structuredClone(n)),target:r,deliver(e={}){if(!o)throw c();return a??=r===null?Promise.resolve():i(e.render),a},start(t){if(!o)throw c();return He(e,n,t)},resolve:t=>j(e,t)};try{await f(e).sync(l),await a}finally{o=!1}}async function He(e,t,n){let{target:r,kind:i}=await I(`${e}:${n}`),a=t.re===null?t:await T(t.re);!a||a.deleted||a.noSync||r!==(a.origin?a.origin.channel??S(a.origin.thread):null)&&await l(`syncReceipt`,{kind:`thread`,message:a.id,target:r,targetKind:i,cause:`config`})}async function Ue(t,n){let r=Object.keys(e).filter(t=>e[t].canStartThreads);if(t.payload.noSync||r.length===0)return;let i=await T(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 We(e,t){let{thread:n,target:r}=e.payload;if(!g(r)||(e.payload.kind??(d(r)?`thread`:`channel`))===`thread`)return;let i=await C(n),[a,...o]=i?await w(i.root):[];if(!i||!a||a.id!==i.root||a.deleted)return;let s=[];try{await Q(p(r),e,a,r,e=>Ge(i,a,o,r,s,e))}catch(e){X(s,a.id,r,e)}await Z(t,s)}async function Ge(e,t,n,r,i,a){let o=await O(),c=await E([t.id,...n.map(e=>e.id)]),l=e=>(i.push({id:t.id,target:r,error:e}),e),u=e.external[r];if(u){let e=c[t.id]?.[r];if(e)try{await Y(t,r,e,o,a)}catch(e){throw l(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 s(`a renderer must return text unless the message has media`);let c,l=!1,d=await(async()=>{if(!i){let i=await Fe(r,r,`channel`,t,o);if(i)return l=!0,i;if(t.attachments.length===0||!e.startsThreadsWithFiles)return e.post(_(r),`channel`,n)}try{let a=await e.post(_(r),`channel`,{...n,files:await Be(t)});return c=i?a.files??[]:a.files,a}catch(n){if(!e.refusesFiles(n)&&!z(n))throw n;return c=null,e.post(_(r),`channel`,q(r,t,o,!0,a))}})();u=`${p(r)}:${d.thread}`;let m={thread:u,message:d.message,...c===void 0?{}:{files:c},...l?{asUser:!0}:{}};await J(t,r,m),await Y(t,r,m,o,a)}catch(e){throw l(e)}let d=[];for(let e of n)if(!(e.deleted||e.noSync||Ke(e,r)))try{await Y(e,r,c[e.id]?.[r]??await ze(e,r,u,o,a),o,a)}catch(t){d.push({id:e.id,target:r,error:t})}if(i.push(...d),d.length)throw d[0].error}let Ke=(e,t)=>e.origin?.thread===t||e.from!==null&&p(t)===e.from;async function qe(e,t){if(!e.message)return;if(e.message.re===null)return Ue(e,t);if(e.payload.noSync)return;let n=await C(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 t of n.targets){let o=n.external[t];if(g(t)&&o&&!Ke(r,t))try{await Q(p(t),e,r,t,async e=>{let n=(await E([r.id]))[r.id]?.[t]??await ze(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 Je(e){let t=(await E([e.payload.id]))[e.payload.id]??{};if(Object.keys(t).length===0)return;let n=e.type===`message.edited`?await T(e.payload.id):e.message??await T(e.payload.id);if(!n||e.type===`message.edited`&&n.deleted)return;let r=await O();for(let[i,a]of Object.entries(t))if(!(!g(i)||e.payload.from!==null&&p(i)===e.payload.from))try{await Q(p(i),e,n,i,async t=>{let o=f(p(i)),s=_(a.thread),c=a.asUser&&(e.type===`message.edited`||e.type===`message.deleted`),l=c?await Ne(o,n,!1):null;if(c&&!l){console.warn(`[board] skipped ${e.type} on ${i}: #${n.id} was posted as its author, and their token is gone`);return}e.type===`message.edited`?l?await o.editAs(l,s,a.message,Pe(i,n,r)):await o.edit(s,a.message,q(i,n,r,a.files===null,t)):e.type===`message.deleted`?(l?await o.deleteAs(l,s,a.message):await o.delete(s,a.message),a.files?.length&&await o.deleteFiles(a.files)):e.type===`reaction.added`?await o.react(s,a.message,h(e.payload.emoji),!0):await o.react(s,a.message,h(e.payload.emoji),!1)})}catch(t){console.warn(`[board] skipped ${e.type} on ${i}:`,t.message)}}async function Ye(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[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(_(r.thread),r.message,n)})}catch(e){console.warn(`[board] skipped labels.changed on ${t}:`,W(e))}}async function Xe(e,{attempt:t}){switch(e.type){case`thread.synced`:return We(e,t);case`message.posted`:return qe(e,t);case`message.edited`:case`message.deleted`:case`reaction.added`:case`reaction.removed`:return Je(e);case`labels.changed`:return Ye(e)}}async function Ze(e,t){let n=e[0]?await C(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(_(n),a,t),n}catch(e){return console.warn(`[board] skipped typing on ${n}:`,W(e)),null}}))).filter(e=>e!==null)}async function Qe(e,t){try{return`${e}:${await f(e).resolveChannel(t)}`}catch(e){throw s(`can't resolve inbox channel: ${e.message}`)}}let $=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 $e(e,t,n,r){let i=f(e);try{return await i.read(t,{limit:n,after:r})}catch(n){throw n instanceof F?new m($(n.code),n.code,n.message):s(`can't read ${t} from ${e}: ${W(n)}`,`platform_error`)}}async function et(e,t){let n=f(e);try{return await n.join(t)}catch(n){throw n instanceof F?new m($(n.code),n.code,n.message):s(`can't join ${t} on ${e}: ${W(n)}`,`platform_error`)}}return{entries:e,bind(e){i=e},caller:c,deliver:Xe,receive:xe,pull:_e,normalizeTarget:I,findByEmail:N,handleWebhook:ge,inboxChannel:Qe,signsIn:Ee,signIn:De,signedIn:Oe,redeemSignIn:ke,typing:Ze,readSource:$e,joinSource:et}}function K(e){let t=e.url?`${e.title}\n${e.url}`:e.title;return e.body?`${t}\n\n${e.body}`:t}export{he as createSyncEngine};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../node_modules/.pnpm/experimental-a2@0.
|
|
1
|
+
//#region ../../node_modules/.pnpm/experimental-a2@0.24.0_@upstash+qstash@2.12.0_@vercel+queue@0.7.0_ai@7.0.130_zod@4.6.5__b1e2c243a963f2620a5d202817417c26/node_modules/experimental-a2/dist/internal-CvsrprnU.js
|
|
2
2
|
/**
|
|
3
3
|
* A null-prototype record for maps keyed by caller-controlled strings
|
|
4
4
|
* (participant ids, presence field names). On a normal object,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as deliverSchedulerClear, r as deliverSchedulerAppend } from "./server-
|
|
1
|
+
import { i as deliverSchedulerClear, r as deliverSchedulerAppend } from "./server-DX7mM6nm-D7N_7bug.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;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { i as fileSize, n as DIRECT_UPLOAD_BYTES_MAX } from "./files-SniSmeEr.mjs";
|
|
2
|
-
import { a as AttachmentId } from "./schemas-
|
|
3
|
-
import { d as VERSION, i as withMentions } from "./watchers-hash-
|
|
2
|
+
import { a as AttachmentId } from "./schemas-DBmUO6pr.mjs";
|
|
3
|
+
import { d as VERSION, i as withMentions } from "./watchers-hash-B_5b0-ZG.mjs";
|
|
4
4
|
import { n as displayEmoji, r as normalizeEmoji } from "./emoji-CTLpvH-A.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import { i as groups, n as filters$1, o as options, r as globalOptions, s as usage$1, t as commands } from "./commands-
|
|
7
|
-
import { a as handFetch, d as vercelProjectFile, n as boardUrl, o as handLogin, r as connect } from "./node-
|
|
8
|
-
import { t as http } from "./backend-http-
|
|
9
|
-
import { t as board } from "./server-
|
|
10
|
-
import { t as conformance } from "./conformance-
|
|
11
|
-
import { n as handTeam, r as vercel, t as credentialSource } from "./backend-vercel-
|
|
5
|
+
import { u as BoardError } from "./errors-C_5rB8Iz.mjs";
|
|
6
|
+
import { i as groups, n as filters$1, o as options, r as globalOptions, s as usage$1, t as commands } from "./commands-B5DHjZKK.mjs";
|
|
7
|
+
import { a as handFetch, d as vercelProjectFile, n as boardUrl, o as handLogin, r as connect } from "./node-D8Rtg1su.mjs";
|
|
8
|
+
import { t as http } from "./backend-http-16SbvnAK.mjs";
|
|
9
|
+
import { t as board } from "./server-D82a4uMe.mjs";
|
|
10
|
+
import { t as conformance } from "./conformance-C7fwIvmN.mjs";
|
|
11
|
+
import { n as handTeam, r as vercel, t as credentialSource } from "./backend-vercel-Kd8nvW5A.mjs";
|
|
12
12
|
import { t as displayText } from "./bots-CGkO76qH.mjs";
|
|
13
13
|
import { spawn } from "node:child_process";
|
|
14
14
|
import { readFile, stat, writeFile } from "node:fs/promises";
|
|
@@ -83,7 +83,7 @@ function embed(link, pad) {
|
|
|
83
83
|
/** One message as a block: header line, indented text, reactions. */
|
|
84
84
|
function formatMessage(message, options = {}) {
|
|
85
85
|
const pad = options.reply ? " " : " ";
|
|
86
|
-
const head = `${options.reply ? " ↳ " : ""}#${message.id} ${authorName(message)} · ${ago(message.createdAt)}${message.editedAt ? " (edited)" : ""}${message.schedule ? ` · scheduled
|
|
86
|
+
const head = `${options.reply ? " ↳ " : ""}#${message.id} ${authorName(message)} · ${ago(message.createdAt)}${message.editedAt ? " (edited)" : ""}${message.schedule ? ` · scheduled ${message.schedule.id}` : ""}${labels(message)}`;
|
|
87
87
|
const body = message.deleted ? `${pad}(message removed)` : indent(displayText(message.text, message.by.actor), pad);
|
|
88
88
|
const files = (message.attachments ?? []).map((a) => `${pad}file ${a.name} · ${a.type} · ${fileSize(a.size)} · ${a.id}`);
|
|
89
89
|
const links = message.deleted ? [] : (message.links ?? []).flatMap((link) => embed(link, pad));
|
|
@@ -193,7 +193,7 @@ function formatSchedule(schedule, options = {}) {
|
|
|
193
193
|
const sent = schedule.sent > 0 ? ` · ${schedule.sent} sent` : "";
|
|
194
194
|
const files = schedule.attachments.length ? ` · ${schedule.attachments.length} ${schedule.attachments.length === 1 ? "file" : "files"}` : "";
|
|
195
195
|
const text = displayText(schedule.text, schedule.by.actor);
|
|
196
|
-
return [`${pad}
|
|
196
|
+
return [`${pad}${schedule.id} ${where} · ${repeat}${stop} · ${when}${sent}${files} · by ${authorName(schedule)}`, indent(text || "(no text)", `${pad} `)].join("\n");
|
|
197
197
|
}
|
|
198
198
|
/** One line per live event, for `tail` and `wait`. */
|
|
199
199
|
function formatEvent(event) {
|
|
@@ -251,7 +251,7 @@ function formatSourceRead(key, read, now = Date.now(), cli = "vb") {
|
|
|
251
251
|
...message.files.map((file) => `${pad}file ${file.name}${file.url ? ` ${file.url}` : ""}`)
|
|
252
252
|
].join("\n");
|
|
253
253
|
});
|
|
254
|
-
const more = read.more ? [`(${read.thread ? "later replies" : "older messages"} not shown: raise --limit, up to 200)`] : [];
|
|
254
|
+
const more = read.next ? [`(more not shown: ${cli} sources ${key} read ${read.thread ?? read.channel} --after ${read.next})`] : read.more ? [`(${read.thread ? "later replies" : "older messages"} not shown: raise --limit, up to 200)`] : [];
|
|
255
255
|
return [
|
|
256
256
|
where,
|
|
257
257
|
...blocks,
|
|
@@ -859,11 +859,18 @@ const tooLarge = (path) => new Failed({
|
|
|
859
859
|
const isMessageRef = (value) => /^#[^\s/]+$/.test(value) || /^\d+$/.test(value);
|
|
860
860
|
/** A message's ID as it's passed back: bare when it's a number, `#<id>` when it could be a channel's name. */
|
|
861
861
|
const messageRef = (id) => /^\d+$/.test(id) ? id : `#${id}`;
|
|
862
|
-
|
|
862
|
+
/** `--if-revision N`, as an edit or a delete takes it. */
|
|
863
|
+
function ifRevision() {
|
|
864
|
+
const given = flags["if-revision"];
|
|
865
|
+
if (given === void 0) return {};
|
|
866
|
+
if (!/^[1-9]\d{0,15}$/.test(given)) throw usage(`--if-revision takes the message's revision, a number, and ${given} isn't one.`);
|
|
867
|
+
return { revision: Number(given) };
|
|
868
|
+
}
|
|
869
|
+
function labelPairs(values, wrong = "Labels look like key=value, and {value} isn't one.") {
|
|
863
870
|
const pairs = {};
|
|
864
871
|
for (const value of values ?? []) {
|
|
865
872
|
const at = value.indexOf("=");
|
|
866
|
-
if (at <= 0) throw usage(
|
|
873
|
+
if (at <= 0) throw usage(wrong.replace("{value}", value));
|
|
867
874
|
pairs[value.slice(0, at)] = value.slice(at + 1);
|
|
868
875
|
}
|
|
869
876
|
return pairs;
|
|
@@ -895,12 +902,11 @@ async function attachments(board) {
|
|
|
895
902
|
}));
|
|
896
903
|
return uploaded;
|
|
897
904
|
}
|
|
898
|
-
/** `s12
|
|
905
|
+
/** A schedule's ID as the board gave it: `s12`, or `sch_…` on Vercel's board. */
|
|
899
906
|
function scheduleId(value) {
|
|
900
|
-
const id = value?.replace(/^s(?=\d)/i, "") ?? "";
|
|
901
907
|
if (value === void 0) throw usage("Missing the schedule ID, like s12.", { reason: "missing_arguments" });
|
|
902
|
-
if (!/^[^\s/]+$/.test(
|
|
903
|
-
return
|
|
908
|
+
if (!/^[^\s/]+$/.test(value)) throw usage(`${value} isn't a schedule ID like s12.`);
|
|
909
|
+
return value;
|
|
904
910
|
}
|
|
905
911
|
/** A time flag: anything `Date` reads, as ISO. */
|
|
906
912
|
function instant(flag, value) {
|
|
@@ -1121,7 +1127,37 @@ function failureOf(error) {
|
|
|
1121
1127
|
details: []
|
|
1122
1128
|
} : theirs("not_found", specific("not_found"), `Can't ${doing()}.`);
|
|
1123
1129
|
}
|
|
1124
|
-
if (status === 409)
|
|
1130
|
+
if (status === 409) {
|
|
1131
|
+
const { args } = current;
|
|
1132
|
+
const newer = code === "newer_message" && flags.after !== void 0 && args[0] !== void 0;
|
|
1133
|
+
const labeled = code === "label_conflict" && (flags.if !== void 0 || flags["if-unset"] !== void 0) && args[0] !== void 0;
|
|
1134
|
+
const changed = code === "revision_conflict" && flags["if-revision"] !== void 0 && args[0] !== void 0;
|
|
1135
|
+
return theirs("conflict", specific("conflict"), `Can't ${doing()}.`, newer ? [{
|
|
1136
|
+
instruction: `Read what came after ${ref(flags.after)}`,
|
|
1137
|
+
argv: [
|
|
1138
|
+
...cli(),
|
|
1139
|
+
"read",
|
|
1140
|
+
args[0].replace(/^#/, ""),
|
|
1141
|
+
"--after",
|
|
1142
|
+
flags.after.replace(/^#/, "")
|
|
1143
|
+
]
|
|
1144
|
+
}] : labeled ? [{
|
|
1145
|
+
instruction: "See its labels now",
|
|
1146
|
+
argv: [
|
|
1147
|
+
...cli(),
|
|
1148
|
+
"read",
|
|
1149
|
+
args[0].replace(/^#/, "")
|
|
1150
|
+
]
|
|
1151
|
+
}] : changed ? [{
|
|
1152
|
+
instruction: "Read it as it is now",
|
|
1153
|
+
argv: [
|
|
1154
|
+
...cli(),
|
|
1155
|
+
"read",
|
|
1156
|
+
args[0].replace(/^#/, ""),
|
|
1157
|
+
"--json"
|
|
1158
|
+
]
|
|
1159
|
+
}] : []);
|
|
1160
|
+
}
|
|
1125
1161
|
if (status === 413) return theirs("limit", "payload_too_large", `Can't ${doing()}. It's too large for the board.`);
|
|
1126
1162
|
if (status === 429) return theirs("rate_limit", "rate_limited", "Too many requests to the board. Try again in a minute.");
|
|
1127
1163
|
if (status === 501) return theirs("platform", specific("not_supported"), `Can't ${doing()}. This board doesn't support it.`);
|
|
@@ -1506,6 +1542,7 @@ async function run(command, args) {
|
|
|
1506
1542
|
]
|
|
1507
1543
|
}] });
|
|
1508
1544
|
if (flags.sync?.length && flags["no-sync"]) throw usage("Can't use --sync with --no-sync.");
|
|
1545
|
+
if (re === void 0 && flags.after !== void 0) throw usage("Can't use --after on a new thread. It's for replies: the thread's newest message.");
|
|
1509
1546
|
const board = client();
|
|
1510
1547
|
const content = body === void 0 && flags.attach?.length ? "" : await text(body);
|
|
1511
1548
|
const files = await attachments(board);
|
|
@@ -1519,7 +1556,10 @@ async function run(command, args) {
|
|
|
1519
1556
|
const message = re === void 0 ? await board.post(target, {
|
|
1520
1557
|
...options,
|
|
1521
1558
|
...flags.sync?.length ? { sync: flags.sync } : {}
|
|
1522
|
-
}) : await board.reply(re,
|
|
1559
|
+
}) : await board.reply(re, {
|
|
1560
|
+
...options,
|
|
1561
|
+
...flags.after !== void 0 ? { after: messageId(flags.after) } : {}
|
|
1562
|
+
});
|
|
1523
1563
|
warnUnknownMentions(message.unknownMentions);
|
|
1524
1564
|
printMessage(message);
|
|
1525
1565
|
return 0;
|
|
@@ -1533,7 +1573,8 @@ async function run(command, args) {
|
|
|
1533
1573
|
text: content,
|
|
1534
1574
|
...files.length ? { attachments: files } : {},
|
|
1535
1575
|
...flags["idempotency-key"] ? { id: flags["idempotency-key"] } : {},
|
|
1536
|
-
...flags["no-sync"] ? { noSync: true } : {}
|
|
1576
|
+
...flags["no-sync"] ? { noSync: true } : {},
|
|
1577
|
+
...flags.after !== void 0 ? { after: messageId(flags.after) } : {}
|
|
1537
1578
|
});
|
|
1538
1579
|
warnUnknownMentions(message.unknownMentions);
|
|
1539
1580
|
printMessage(message);
|
|
@@ -1546,7 +1587,7 @@ async function run(command, args) {
|
|
|
1546
1587
|
schedule,
|
|
1547
1588
|
outcome: canceled ? "canceled" : "unchanged"
|
|
1548
1589
|
});
|
|
1549
|
-
else out(canceled ? `canceled
|
|
1590
|
+
else out(canceled ? `canceled ${schedule.id}` : `${schedule.id} is already ${schedule.state}`);
|
|
1550
1591
|
return 0;
|
|
1551
1592
|
}
|
|
1552
1593
|
const [target, body] = args;
|
|
@@ -1630,14 +1671,14 @@ async function run(command, args) {
|
|
|
1630
1671
|
case "hook": return 0;
|
|
1631
1672
|
case "edit": {
|
|
1632
1673
|
const id = messageId(args[0]);
|
|
1633
|
-
const { unknownMentions } = await client().edit(id, await text(args[1]));
|
|
1674
|
+
const { unknownMentions } = await client().edit(id, await text(args[1]), ifRevision());
|
|
1634
1675
|
warnUnknownMentions(unknownMentions);
|
|
1635
1676
|
printDone(id, "edited");
|
|
1636
1677
|
return 0;
|
|
1637
1678
|
}
|
|
1638
1679
|
case "delete": {
|
|
1639
1680
|
const id = messageId(args[0]);
|
|
1640
|
-
await client().delete(id);
|
|
1681
|
+
await client().delete(id, ifRevision());
|
|
1641
1682
|
printDone(id, "deleted");
|
|
1642
1683
|
return 0;
|
|
1643
1684
|
}
|
|
@@ -1659,9 +1700,15 @@ async function run(command, args) {
|
|
|
1659
1700
|
const set = labelPairs(args.slice(1));
|
|
1660
1701
|
const unset = flags.unset ?? [];
|
|
1661
1702
|
if (Object.keys(set).length === 0 && unset.length === 0) throw usage("Missing the labels: k=v to set one, --unset k to remove one.", { reason: "missing_arguments" });
|
|
1703
|
+
const expected = labelPairs(flags.if, "--if takes key=value, and {value} isn't one. For a label that mustn't be set, --if-unset key.");
|
|
1704
|
+
for (const key of flags["if-unset"] ?? []) {
|
|
1705
|
+
if (Object.hasOwn(expected, key)) throw usage(`Can't use --if ${key}=… with --if-unset ${key}.`);
|
|
1706
|
+
expected[key] = null;
|
|
1707
|
+
}
|
|
1662
1708
|
await client().label(id, {
|
|
1663
1709
|
set,
|
|
1664
|
-
unset
|
|
1710
|
+
unset,
|
|
1711
|
+
...Object.keys(expected).length ? { if: expected } : {}
|
|
1665
1712
|
});
|
|
1666
1713
|
printDone(id, "labeled");
|
|
1667
1714
|
return 0;
|
|
@@ -1743,7 +1790,10 @@ async function run(command, args) {
|
|
|
1743
1790
|
}
|
|
1744
1791
|
if (sub !== "read" && sub !== "join") throw usage(sub === void 0 ? `Missing what to do with ${key}: read or join.` : `Unknown command ${sub} for ${named()} sources.`, { reason: sub === void 0 ? "missing_arguments" : "unknown_command" });
|
|
1745
1792
|
if (!target) throw usage(`Missing the channel, thread, or link to ${sub}.`, { reason: "missing_arguments" });
|
|
1746
|
-
const read = await client().readSource(key, target,
|
|
1793
|
+
const read = await client().readSource(key, target, {
|
|
1794
|
+
...flags.limit ? { limit: Number(flags.limit) } : {},
|
|
1795
|
+
...flags.after ? { after: String(flags.after) } : {}
|
|
1796
|
+
});
|
|
1747
1797
|
if (flags.json) return result(read), 0;
|
|
1748
1798
|
return out(formatSourceRead(key, read, Date.now(), named())), 0;
|
|
1749
1799
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { m as MESSAGE_TEXT_MAX, p as Labels } from "./schemas-
|
|
2
|
-
import { t as HttpError } from "./errors-
|
|
1
|
+
import { m as MESSAGE_TEXT_MAX, p as Labels } from "./schemas-DBmUO6pr.mjs";
|
|
2
|
+
import { t as HttpError } from "./errors-C_5rB8Iz.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/server/middleware.ts
|
|
5
5
|
/** A write the middleware didn't let through: the API answers 422 with what it responded. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { u as BoardError } from "./errors-C_5rB8Iz.mjs";
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { mkdir, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../node_modules/.pnpm/experimental-a2@0.
|
|
1
|
+
//#region ../../node_modules/.pnpm/experimental-a2@0.24.0_@upstash+qstash@2.12.0_@vercel+queue@0.7.0_ai@7.0.130_zod@4.6.5__b1e2c243a963f2620a5d202817417c26/node_modules/experimental-a2/dist/platform-B4TnJtWu.js
|
|
2
2
|
const SYMBOL_FOR_REQ_CONTEXT = Symbol.for("@vercel/request-context");
|
|
3
3
|
function requestContext() {
|
|
4
4
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as setOwn, t as own } from "./own-OwgPWdmh.mjs";
|
|
2
|
-
import { n as foldMessages, r as foldSync } from "./folds-
|
|
2
|
+
import { n as foldMessages, r as foldSync } from "./folds-DGYkMW19.mjs";
|
|
3
3
|
//#region src/store/project.ts
|
|
4
4
|
/** The message an event concerns, if any. */
|
|
5
5
|
function concernedMessageId(event) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as ExternalEvent } from "./external-
|
|
1
|
+
import { n as ExternalEvent } from "./external-D3p5T57R.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/sync/fn.ts
|
|
4
4
|
const ROUTES = Symbol.for("board.syncRoutes");
|
|
@@ -120,6 +120,7 @@ var SyncProvider = class {
|
|
|
120
120
|
canStartThreads;
|
|
121
121
|
singleReaction;
|
|
122
122
|
uploadsFiles;
|
|
123
|
+
startsThreadsWithFiles;
|
|
123
124
|
fn;
|
|
124
125
|
/** People's tokens, when the adapter has them: messages they write post as them. */
|
|
125
126
|
users;
|
|
@@ -135,6 +136,7 @@ var SyncProvider = class {
|
|
|
135
136
|
this.canStartThreads = options.canStartThreads ?? false;
|
|
136
137
|
this.singleReaction = options.singleReaction ?? false;
|
|
137
138
|
this.uploadsFiles = options.uploadsFiles ?? false;
|
|
139
|
+
this.startsThreadsWithFiles = this.uploadsFiles && (options.startsThreadsWithFiles ?? false);
|
|
138
140
|
}
|
|
139
141
|
/** The platform account a person's token belongs to. */
|
|
140
142
|
async whoAmI(_token) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { d as ExternalFile, f as ExternalLink, l as ExternalAuthor, p as Link, t as Actor, u as ExternalEvent } from "./folds-
|
|
2
|
-
import {
|
|
1
|
+
import { d as ExternalFile, f as ExternalLink, l as ExternalAuthor, p as Link, t as Actor, u as ExternalEvent } from "./folds-8wRRXjAt.mjs";
|
|
2
|
+
import { H as MessageView, _ as BoardEventView, ft as SourceJoin, ht as SourceRead, t as BoardClient } from "./client-CjAatpV4.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/sync/events.d.ts
|
|
5
5
|
declare const EventBase: z.ZodObject<{
|
|
@@ -132,6 +132,7 @@ declare function envelope(platform: string, key: string, event: ExternalEvent, r
|
|
|
132
132
|
kind: "lifecycle";
|
|
133
133
|
title: string;
|
|
134
134
|
url: string | null;
|
|
135
|
+
body?: string | null | undefined;
|
|
135
136
|
labels: Record<string, string>;
|
|
136
137
|
unset: string[];
|
|
137
138
|
owns?: string[] | undefined;
|
|
@@ -275,6 +276,7 @@ declare function eventSchema<P extends string, M extends string>(platform: P, me
|
|
|
275
276
|
type: z.ZodString;
|
|
276
277
|
title: z.ZodString;
|
|
277
278
|
url: z.ZodNullable<z.ZodString>;
|
|
279
|
+
body: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
278
280
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
279
281
|
unset: z.ZodArray<z.ZodString>;
|
|
280
282
|
owns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -494,6 +496,8 @@ type ProviderOptions = {
|
|
|
494
496
|
canStartThreads?: boolean;
|
|
495
497
|
singleReaction?: boolean;
|
|
496
498
|
uploadsFiles?: boolean;
|
|
499
|
+
/** Whether a new thread's root can carry its files in the same message as its text. */
|
|
500
|
+
startsThreadsWithFiles?: boolean;
|
|
497
501
|
/** People's own tokens, to post as them and to link accounts by signing in. */
|
|
498
502
|
users?: UserTokens | null;
|
|
499
503
|
};
|
|
@@ -598,6 +602,7 @@ declare const ReceivedEvent: z.ZodObject<{
|
|
|
598
602
|
type: z.ZodString;
|
|
599
603
|
title: z.ZodString;
|
|
600
604
|
url: z.ZodNullable<z.ZodString>;
|
|
605
|
+
body: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
601
606
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
602
607
|
unset: z.ZodArray<z.ZodString>;
|
|
603
608
|
owns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -622,6 +627,7 @@ declare abstract class SyncProvider<E extends ProviderEvent = ProviderEvent> {
|
|
|
622
627
|
readonly canStartThreads: boolean;
|
|
623
628
|
readonly singleReaction: boolean;
|
|
624
629
|
readonly uploadsFiles: boolean;
|
|
630
|
+
readonly startsThreadsWithFiles: boolean;
|
|
625
631
|
private readonly fn;
|
|
626
632
|
/** People's tokens, when the adapter has them: messages they write post as them. */
|
|
627
633
|
readonly users: UserTokens | null;
|
|
@@ -662,6 +668,7 @@ declare abstract class SyncProvider<E extends ProviderEvent = ProviderEvent> {
|
|
|
662
668
|
*/
|
|
663
669
|
read(_target: string, _options: {
|
|
664
670
|
limit: number;
|
|
671
|
+
after: string | null;
|
|
665
672
|
}): Promise<SourceRead>;
|
|
666
673
|
/** Adds the board's app to a channel so `read` can read it, where the board opted in. Throws `UnreadableError` otherwise. */
|
|
667
674
|
join(_target: string): Promise<SourceJoin>;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { c as externalMessageKey } from "./external-
|
|
1
|
+
import { c as externalMessageKey } from "./external-D3p5T57R.mjs";
|
|
2
2
|
import { n as setOwn } from "./own-OwgPWdmh.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { m as nullProtoRecord, n as NOTIFY_TIMINGS, v as stateTailReaders } from "./internal-CvsrprnU-
|
|
5
|
-
import { i as presenceTombstones, n as isSerializedEvent, r as parseSerializedEvent, t as bufferedEvents } from "./stream-buffer-DrTyiIuo-
|
|
6
|
-
import { i as streamActivities, n as defaultSleep, r as pollingStream, t as createStreamActivity } from "./store-polling-BZt2Rf3D-
|
|
7
|
-
import { t as retryableLazy } from "./retryable-lazy-DZWmHpii-
|
|
8
|
-
import { i as syncReads, n as project, r as projectSync, t as concernedMessageId } from "./project-
|
|
9
|
-
import { i as idempotentReplay, n as decodeCause } from "./store-codec-
|
|
10
|
-
import { t as pollPresence } from "./store-presence-polling-JuAy2Ck2-
|
|
11
|
-
//#region ../../node_modules/.pnpm/experimental-a2@0.
|
|
3
|
+
import { a as duplicateBatchError, c as sessionClearedError, l as supersededAttemptError, n as CLEAR_EVENT_TYPE, s as lastIndexMismatchError, t as A2Error } from "./session-clear-XJYpmUpg-2q8l_FJR.mjs";
|
|
4
|
+
import { m as nullProtoRecord, n as NOTIFY_TIMINGS, v as stateTailReaders } from "./internal-CvsrprnU-zZMpDiRY.mjs";
|
|
5
|
+
import { i as presenceTombstones, n as isSerializedEvent, r as parseSerializedEvent, t as bufferedEvents } from "./stream-buffer-DrTyiIuo-Be10yOBR.mjs";
|
|
6
|
+
import { i as streamActivities, n as defaultSleep, r as pollingStream, t as createStreamActivity } from "./store-polling-BZt2Rf3D-Div-bJ_D.mjs";
|
|
7
|
+
import { t as retryableLazy } from "./retryable-lazy-DZWmHpii-DpTc5PIP.mjs";
|
|
8
|
+
import { i as syncReads, n as project, r as projectSync, t as concernedMessageId } from "./project-C_2xs57L.mjs";
|
|
9
|
+
import { i as idempotentReplay, n as decodeCause } from "./store-codec-pAG2oPgQ-SqDXlPBR.mjs";
|
|
10
|
+
import { t as pollPresence } from "./store-presence-polling-JuAy2Ck2-Dh-g_KYM.mjs";
|
|
11
|
+
//#region ../../node_modules/.pnpm/experimental-a2@0.24.0_@upstash+qstash@2.12.0_@vercel+queue@0.7.0_ai@7.0.130_zod@4.6.5__b1e2c243a963f2620a5d202817417c26/node_modules/experimental-a2/dist/store-redis-notify-ys_-VrSP.js
|
|
12
12
|
const key = ({ participant, field }) => JSON.stringify([participant, field]);
|
|
13
13
|
function presenceRecovery({ read, deliver, isActive }) {
|
|
14
14
|
const fields = /* @__PURE__ */ new Map();
|
|
@@ -645,11 +645,12 @@ end
|
|
|
645
645
|
`;
|
|
646
646
|
/**
|
|
647
647
|
* KEYS: counter.
|
|
648
|
-
* ARGV: sessionId, nowMs, n, served consumers, origin index or '',
|
|
649
|
-
* event: id, type, payloadJson, causeJson or '',
|
|
648
|
+
* ARGV: sessionId, nowMs, n, served consumers, origin index or '',
|
|
649
|
+
* ifLastIndex or '', then per event: id, type, payloadJson, causeJson or '',
|
|
650
|
+
* targets.
|
|
650
651
|
* Returns ['empty', hasPending] | ['ok', firstIndex, hasPending] |
|
|
651
|
-
* ['dup', hasPending, ...indexes] | ['partial', count] |
|
|
652
|
-
* ['superseded', position] | ['cleared', originIndex] |
|
|
652
|
+
* ['dup', hasPending, head, ...indexes] | ['partial', count, head] |
|
|
653
|
+
* ['superseded', position] | ['cleared', originIndex] | ['moved', head] |
|
|
653
654
|
* ['index', type, after].
|
|
654
655
|
*/
|
|
655
656
|
const APPEND_LUA = `${BASE_LUA}${CURSOR_LUA}
|
|
@@ -657,7 +658,7 @@ local now, n = ARGV[2], tonumber(ARGV[3])
|
|
|
657
658
|
use_served(ARGV[4])
|
|
658
659
|
if n == 0 then return { 'empty', has_pending() } end
|
|
659
660
|
local ids = {}
|
|
660
|
-
for i = 1, n do ids[i] = ARGV[
|
|
661
|
+
for i = 1, n do ids[i] = ARGV[6 + (i - 1) * 5 + 1] end
|
|
661
662
|
local owners = owners_of(read_bounds(ids))
|
|
662
663
|
local base = total
|
|
663
664
|
local dups = {}
|
|
@@ -665,18 +666,18 @@ for i = 1, n do
|
|
|
665
666
|
if owners[i] then dups[#dups + 1] = owners[i] end
|
|
666
667
|
end
|
|
667
668
|
if #dups == n then
|
|
668
|
-
local res = { 'dup', has_pending() }
|
|
669
|
+
local res = { 'dup', has_pending(), tostring(total) }
|
|
669
670
|
for i = 1, #dups do res[#res + 1] = dups[i] end
|
|
670
671
|
return res
|
|
671
672
|
end
|
|
672
|
-
if #dups > 0 then return { 'partial', tostring(#dups) } end
|
|
673
|
+
if #dups > 0 then return { 'partial', tostring(#dups), tostring(total) } end
|
|
673
674
|
local origin = ARGV[5]
|
|
674
675
|
if origin ~= '' and tonumber(origin) <= boundary then return { 'cleared', origin } end
|
|
675
676
|
-- The attempt-currency fence: a cause's head row must still be its cursor's
|
|
676
677
|
-- head, at that attempt, not dead-lettered.
|
|
677
678
|
local refs, fenced = {}, {}
|
|
678
679
|
for i = 1, n do
|
|
679
|
-
local raw = ARGV[
|
|
680
|
+
local raw = ARGV[6 + (i - 1) * 5 + 4]
|
|
680
681
|
if raw ~= '' then
|
|
681
682
|
local cause = cjson.decode(raw)
|
|
682
683
|
local index = tonumber(cause.index)
|
|
@@ -696,16 +697,18 @@ for k, cause in ipairs(fenced) do
|
|
|
696
697
|
end
|
|
697
698
|
if served_has(cause.consumer) then pending_fast = true end
|
|
698
699
|
end
|
|
700
|
+
local expected = ARGV[6]
|
|
701
|
+
if expected ~= '' and tonumber(expected) ~= total then return { 'moved', tostring(total) } end
|
|
699
702
|
local batch = {}
|
|
700
703
|
for i = 1, n do
|
|
701
|
-
local o =
|
|
704
|
+
local o = 6 + (i - 1) * 5
|
|
702
705
|
batch[i] = { idx = base + i, type = ARGV[o + 2], targets = targets_of(ARGV[o + 5]) }
|
|
703
706
|
end
|
|
704
707
|
local planned, missing, after = plan(batch, base)
|
|
705
708
|
if not planned then return { 'index', missing, tostring(after) } end
|
|
706
709
|
local marks = {}
|
|
707
710
|
for i = 1, n do
|
|
708
|
-
local o =
|
|
711
|
+
local o = 6 + (i - 1) * 5
|
|
709
712
|
local field = tostring(base + i)
|
|
710
713
|
redis.call('XADD', LOG, field .. '-0',
|
|
711
714
|
'id', ARGV[o + 1], 'type', ARGV[o + 2], 'payload', ARGV[o + 3],
|
|
@@ -2040,6 +2043,12 @@ function createRedisStoreCore(options) {
|
|
|
2040
2043
|
const suppliedIds = events.filter((e) => e.id !== void 0).map((e) => e.id);
|
|
2041
2044
|
if (new Set(suppliedIds).size !== suppliedIds.length) throw new A2Error("PARTIAL_DUPLICATE_BATCH", "batch contains the same event id more than once");
|
|
2042
2045
|
const now = clock.now().getTime();
|
|
2046
|
+
const ifLastIndex = appendOptions?.ifLastIndex;
|
|
2047
|
+
const conditionalAt = (head) => ifLastIndex === void 0 ? void 0 : {
|
|
2048
|
+
sessionId,
|
|
2049
|
+
ifLastIndex,
|
|
2050
|
+
head: Number(head)
|
|
2051
|
+
};
|
|
2043
2052
|
const withIds = events.map((e) => ({
|
|
2044
2053
|
id: e.id ?? generateId(),
|
|
2045
2054
|
type: e.type,
|
|
@@ -2054,6 +2063,7 @@ function createRedisStoreCore(options) {
|
|
|
2054
2063
|
withIds.length,
|
|
2055
2064
|
servedArg(appendOptions?.consumers),
|
|
2056
2065
|
appendOptions?.originIndex ?? "",
|
|
2066
|
+
ifLastIndex ?? "",
|
|
2057
2067
|
...withIds.flatMap((e, i) => [
|
|
2058
2068
|
e.id,
|
|
2059
2069
|
e.type,
|
|
@@ -2069,13 +2079,14 @@ function createRedisStoreCore(options) {
|
|
|
2069
2079
|
};
|
|
2070
2080
|
if (verdict === "cleared") throw sessionClearedError(`scheduled work from event ${reply[1]} was cleared in session '${sessionId}'`);
|
|
2071
2081
|
if (verdict === "superseded") throw supersededAttemptError(sessionId, withIds[Number(reply[1]) - 1].cause);
|
|
2072
|
-
if (verdict === "
|
|
2082
|
+
if (verdict === "moved") throw lastIndexMismatchError(sessionId, ifLastIndex, Number(reply[1]));
|
|
2083
|
+
if (verdict === "partial") throw duplicateBatchError(`batch mixes ${reply[1]} already-appended and ${events.length - Number(reply[1])} fresh events`, conditionalAt(reply[2]));
|
|
2073
2084
|
if (verdict === "dup") {
|
|
2074
2085
|
const hasPending = reply[1] === "1";
|
|
2075
|
-
const firstIndex = Math.min(...reply.slice(
|
|
2086
|
+
const firstIndex = Math.min(...reply.slice(3).map(Number));
|
|
2076
2087
|
const { rows: existing, clearIndex } = await retryAfterGap(() => readBoundedRange(sessionId, firstIndex - 1, firstIndex + events.length - 1));
|
|
2077
2088
|
if (firstIndex < clearIndex) throw new A2Error("STORE_UNAVAILABLE", `session '${sessionId}' was cleared while an append retry replayed; retry it`);
|
|
2078
|
-
const replayed = idempotentReplay(events, existing);
|
|
2089
|
+
const replayed = idempotentReplay(events, existing, conditionalAt(reply[2]));
|
|
2079
2090
|
await publish(notifyChannel(sessionId), wakeMessage(firstIndex + events.length - 1, now, []));
|
|
2080
2091
|
return {
|
|
2081
2092
|
events: replayed,
|
|
@@ -2674,11 +2685,16 @@ function createRedisNotifyStore(options) {
|
|
|
2674
2685
|
*/
|
|
2675
2686
|
let subscriberGate = null;
|
|
2676
2687
|
const gatedSubscriber = () => {
|
|
2677
|
-
|
|
2688
|
+
if (subscriberGate) return subscriberGate;
|
|
2689
|
+
const gate = getSubscriber().then(async (conn) => {
|
|
2678
2690
|
await conn.call("echo", "ready");
|
|
2679
2691
|
return conn;
|
|
2680
2692
|
});
|
|
2681
|
-
|
|
2693
|
+
subscriberGate = gate;
|
|
2694
|
+
gate.catch(() => {
|
|
2695
|
+
if (subscriberGate === gate) subscriberGate = null;
|
|
2696
|
+
});
|
|
2697
|
+
return gate;
|
|
2682
2698
|
};
|
|
2683
2699
|
const acquireChannel = async (channel) => {
|
|
2684
2700
|
const existing = subscriptions.get(channel);
|