@kingsleyweb/bun-ui 3.0.0-rc.1 → 3.0.0-rc.2

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 (118) hide show
  1. package/dist/docs/assets/main-0rchxtv5.js +18 -0
  2. package/dist/docs/assets/{main-pgxtmtja.js.map → main-0rchxtv5.js.map} +1 -1
  3. package/dist/docs/manifest.json +5 -5
  4. package/dist/jobs/assets/AddFlowDialog-h6xkgx9b.js +6 -0
  5. package/dist/jobs/assets/{AddFlowDialog-0ec5ff4r.js.map → AddFlowDialog-h6xkgx9b.js.map} +1 -1
  6. package/dist/jobs/assets/added-cbwsxv3e.js +4 -0
  7. package/dist/jobs/assets/{added-ma7xb2dx.js.map → added-cbwsxv3e.js.map} +1 -1
  8. package/dist/jobs/assets/{channel-a5chwqet.js → channel-tgkvqm8y.js} +3 -3
  9. package/dist/jobs/assets/{channel-a5chwqet.js.map → channel-tgkvqm8y.js.map} +1 -1
  10. package/dist/jobs/assets/{channelChecks-1epbhv9a.js → channelChecks-s4yz4yhw.js} +3 -3
  11. package/dist/jobs/assets/{channelChecks-1epbhv9a.js.map → channelChecks-s4yz4yhw.js.map} +1 -1
  12. package/dist/jobs/assets/index-09z2cjft.js +4 -0
  13. package/dist/jobs/assets/{index-m7zk18cj.js.map → index-09z2cjft.js.map} +1 -1
  14. package/dist/jobs/assets/index-47pxmyxn.js +4 -0
  15. package/dist/jobs/assets/{index-c7qk888t.js.map → index-47pxmyxn.js.map} +1 -1
  16. package/dist/jobs/assets/index-4e55z986.js +4 -0
  17. package/dist/jobs/assets/{index-7kbae3pm.js.map → index-4e55z986.js.map} +1 -1
  18. package/dist/jobs/assets/index-5c933424.js +4 -0
  19. package/dist/jobs/assets/{index-xfq292gs.js.map → index-5c933424.js.map} +1 -1
  20. package/dist/jobs/assets/index-d5r5n0mm.js +4 -0
  21. package/dist/jobs/assets/{index-j3h4v03g.js.map → index-d5r5n0mm.js.map} +3 -3
  22. package/dist/jobs/assets/index-hs60b25v.js +10 -0
  23. package/dist/jobs/assets/{index-y5zw06cs.js.map → index-hs60b25v.js.map} +1 -1
  24. package/dist/jobs/assets/index-k2smrr70.js +4 -0
  25. package/dist/jobs/assets/{index-de1fgbgf.js.map → index-k2smrr70.js.map} +1 -1
  26. package/dist/jobs/assets/index-q24jw3w9.js +4 -0
  27. package/dist/jobs/assets/{index-pjtmzrcd.js.map → index-q24jw3w9.js.map} +1 -1
  28. package/dist/jobs/assets/index-rtynx6wj.js +4 -0
  29. package/dist/jobs/assets/{index-mykmd5cv.js.map → index-rtynx6wj.js.map} +1 -1
  30. package/dist/jobs/assets/{live-erz9pvnm.js → live-6k7s6358.js} +3 -3
  31. package/dist/jobs/assets/{live-erz9pvnm.js.map → live-6k7s6358.js.map} +1 -1
  32. package/dist/jobs/assets/{main-smabxbn4.js → main-0jmeh6mq.js} +4 -4
  33. package/dist/jobs/assets/{main-smabxbn4.js.map → main-0jmeh6mq.js.map} +1 -1
  34. package/dist/jobs/assets/main-3yj6bv90.js +5 -0
  35. package/dist/jobs/assets/{main-yg6scp84.js.map → main-3yj6bv90.js.map} +1 -1
  36. package/dist/jobs/assets/main-4g69fn3x.js +5 -0
  37. package/dist/jobs/assets/{main-v2f8a4g3.js.map → main-4g69fn3x.js.map} +1 -1
  38. package/dist/jobs/assets/{main-e8zsqez2.js → main-4gte3eqq.js} +4 -4
  39. package/dist/jobs/assets/{main-e8zsqez2.js.map → main-4gte3eqq.js.map} +1 -1
  40. package/dist/jobs/assets/{main-zk3n47nq.js → main-4j1fcevh.js} +3 -3
  41. package/dist/jobs/assets/{main-zk3n47nq.js.map → main-4j1fcevh.js.map} +1 -1
  42. package/dist/jobs/assets/main-5grjvsy7.js +5 -0
  43. package/dist/jobs/assets/{main-3xxhy5jh.js.map → main-5grjvsy7.js.map} +1 -1
  44. package/dist/jobs/assets/{main-gzppw8ng.js → main-5zbx60gq.js} +3 -3
  45. package/dist/jobs/assets/{main-gzppw8ng.js.map → main-5zbx60gq.js.map} +1 -1
  46. package/dist/jobs/assets/{main-zx20z2e9.js → main-9prgjhg4.js} +4 -4
  47. package/dist/jobs/assets/{main-zx20z2e9.js.map → main-9prgjhg4.js.map} +1 -1
  48. package/dist/jobs/assets/main-a250ffk7.js +17 -0
  49. package/dist/jobs/assets/{main-xf44bv26.js.map → main-a250ffk7.js.map} +4 -4
  50. package/dist/jobs/assets/{main-qahvywvw.js → main-d6ge7w08.js} +4 -4
  51. package/dist/jobs/assets/{main-qahvywvw.js.map → main-d6ge7w08.js.map} +1 -1
  52. package/dist/jobs/assets/{main-kjywj2zd.js → main-gngzht43.js} +3 -3
  53. package/dist/jobs/assets/{main-kjywj2zd.js.map → main-gngzht43.js.map} +1 -1
  54. package/dist/jobs/assets/main-hg0hrg8j.js +5 -0
  55. package/dist/jobs/assets/{main-yjykmsvp.js.map → main-hg0hrg8j.js.map} +1 -1
  56. package/dist/jobs/assets/main-hm1ng3yg.js +5 -0
  57. package/dist/jobs/assets/{main-9qd3nqs5.js.map → main-hm1ng3yg.js.map} +1 -1
  58. package/dist/jobs/assets/main-jkvd92z9.js +5 -0
  59. package/dist/jobs/assets/{main-fcq7hfk0.js.map → main-jkvd92z9.js.map} +1 -1
  60. package/dist/jobs/assets/main-jv5xqd13.js +5 -0
  61. package/dist/jobs/assets/{main-grcj8v1z.js.map → main-jv5xqd13.js.map} +7 -7
  62. package/dist/jobs/assets/main-jxre7w93.js +5 -0
  63. package/dist/jobs/assets/{main-47391fgm.js.map → main-jxre7w93.js.map} +4 -4
  64. package/dist/jobs/assets/{main-b35rfpdf.js → main-kwmqkjfx.js} +4 -4
  65. package/dist/jobs/assets/{main-b35rfpdf.js.map → main-kwmqkjfx.js.map} +1 -1
  66. package/dist/jobs/assets/main-n9cf6bxm.js +5 -0
  67. package/dist/jobs/assets/{main-vargz49c.js.map → main-n9cf6bxm.js.map} +1 -1
  68. package/dist/jobs/assets/{main-6xdkyzv1.js → main-pegp0ygb.js} +2 -2
  69. package/dist/jobs/assets/{main-2n5wnj51.js → main-qr573853.js} +3 -3
  70. package/dist/jobs/assets/{main-2n5wnj51.js.map → main-qr573853.js.map} +1 -1
  71. package/dist/jobs/assets/main-rq97rdr4.js +4 -0
  72. package/dist/jobs/assets/{main-7jzem39c.js.map → main-rq97rdr4.js.map} +1 -1
  73. package/dist/jobs/assets/main-szjdmydc.js +5 -0
  74. package/dist/jobs/assets/{main-pqke97qp.js.map → main-szjdmydc.js.map} +1 -1
  75. package/dist/jobs/assets/{main-jex4pqaa.js → main-tnbhrm9n.js} +5 -5
  76. package/dist/jobs/assets/{main-jex4pqaa.js.map → main-tnbhrm9n.js.map} +1 -1
  77. package/dist/jobs/assets/main-ts19j4n0.js +5 -0
  78. package/dist/jobs/assets/{main-eekxtm5w.js.map → main-ts19j4n0.js.map} +1 -1
  79. package/dist/jobs/assets/{main-fn8r2zyb.js → main-w93x1dzs.js} +4 -4
  80. package/dist/jobs/assets/{main-fn8r2zyb.js.map → main-w93x1dzs.js.map} +1 -1
  81. package/dist/jobs/assets/sections-0y7w4fb4.js +4 -0
  82. package/dist/jobs/assets/{sections-b570mm22.js.map → sections-0y7w4fb4.js.map} +1 -1
  83. package/dist/jobs/assets/{sse-42e8mhar.js → sse-mn0t0fh7.js} +4 -4
  84. package/dist/jobs/assets/{sse-42e8mhar.js.map → sse-mn0t0fh7.js.map} +1 -1
  85. package/dist/jobs/assets/{sseChecks-0jskzcs1.js → sseChecks-t7tcm7ra.js} +3 -3
  86. package/dist/jobs/assets/{sseChecks-0jskzcs1.js.map → sseChecks-t7tcm7ra.js.map} +1 -1
  87. package/dist/jobs/assets/validation-ysjfy072.js +4 -0
  88. package/dist/jobs/assets/{validation-199pberh.js.map → validation-ysjfy072.js.map} +1 -1
  89. package/dist/jobs/manifest.json +213 -213
  90. package/package.json +10 -6
  91. package/dist/docs/assets/main-pgxtmtja.js +0 -18
  92. package/dist/jobs/assets/AddFlowDialog-0ec5ff4r.js +0 -6
  93. package/dist/jobs/assets/added-ma7xb2dx.js +0 -4
  94. package/dist/jobs/assets/index-7kbae3pm.js +0 -4
  95. package/dist/jobs/assets/index-c7qk888t.js +0 -4
  96. package/dist/jobs/assets/index-de1fgbgf.js +0 -4
  97. package/dist/jobs/assets/index-j3h4v03g.js +0 -4
  98. package/dist/jobs/assets/index-m7zk18cj.js +0 -4
  99. package/dist/jobs/assets/index-mykmd5cv.js +0 -4
  100. package/dist/jobs/assets/index-pjtmzrcd.js +0 -4
  101. package/dist/jobs/assets/index-xfq292gs.js +0 -4
  102. package/dist/jobs/assets/index-y5zw06cs.js +0 -10
  103. package/dist/jobs/assets/main-3xxhy5jh.js +0 -5
  104. package/dist/jobs/assets/main-47391fgm.js +0 -5
  105. package/dist/jobs/assets/main-7jzem39c.js +0 -4
  106. package/dist/jobs/assets/main-9qd3nqs5.js +0 -5
  107. package/dist/jobs/assets/main-eekxtm5w.js +0 -5
  108. package/dist/jobs/assets/main-fcq7hfk0.js +0 -5
  109. package/dist/jobs/assets/main-grcj8v1z.js +0 -5
  110. package/dist/jobs/assets/main-pqke97qp.js +0 -5
  111. package/dist/jobs/assets/main-v2f8a4g3.js +0 -5
  112. package/dist/jobs/assets/main-vargz49c.js +0 -5
  113. package/dist/jobs/assets/main-xf44bv26.js +0 -17
  114. package/dist/jobs/assets/main-yg6scp84.js +0 -5
  115. package/dist/jobs/assets/main-yjykmsvp.js +0 -5
  116. package/dist/jobs/assets/sections-b570mm22.js +0 -4
  117. package/dist/jobs/assets/validation-199pberh.js +0 -4
  118. /package/dist/jobs/assets/{main-6xdkyzv1.js.map → main-pegp0ygb.js.map} +0 -0
@@ -1,26 +1,26 @@
1
1
  {
2
2
  "version": 1,
3
3
  "entry": {
4
- "js": "main-pgxtmtja.js",
4
+ "js": "main-0rchxtv5.js",
5
5
  "css": [
6
6
  "main-cqgyvs86.css"
7
7
  ]
8
8
  },
9
9
  "files": {
10
- "main-pgxtmtja.js": {
10
+ "main-0rchxtv5.js": {
11
11
  "type": "text/javascript; charset=utf-8",
12
12
  "size": 347709,
13
- "integrity": "sha384-cONQVf3hQOcpb8tDmVAAuaLeYYTRCgNEnVJszMeyUEHa9eOvui2/SiYOSfOiNmon"
13
+ "integrity": "sha384-ciO+g1eTXqjBaXy6WpJBtZie+6qCiJ78BLFHdGT/lNy4WwxY6C7RRbR93ijAgDfb"
14
14
  },
15
15
  "main-cqgyvs86.css": {
16
16
  "type": "text/css; charset=utf-8",
17
17
  "size": 32967,
18
18
  "integrity": "sha384-NHk5MkoK+kYjEVNmWAzeMnVDV53CXHU8AOd4cx6EicbB+GX56KwOuujnVYfOh9eW"
19
19
  },
20
- "main-pgxtmtja.js.map": {
20
+ "main-0rchxtv5.js.map": {
21
21
  "type": "application/json; charset=utf-8",
22
22
  "size": 1600353,
23
- "integrity": "sha384-19q1DkvprCqd9YYSYfYk/UehC2xomkkgRjuGYM3r8eqSQZtDdhT+Ou9qy1Rezpri"
23
+ "integrity": "sha384-fcy2uw8HyXP2DP/QUg0y1AwslVct2hK7sxtXCXtb+vnbiLTCSoBlbUGhfLRFiYzV"
24
24
  }
25
25
  }
26
26
  }
@@ -0,0 +1,6 @@
1
+ import{v,c,g,T,r,Ct,ht,b,n,e,t,i,yt,m,y,a,_,s,E,oe,d,Ce,l,M,S,w}from"./main-a250ffk7.js";import{rn}from"./main-5zbx60gq.js";import{We,uo,lo,on,br,Ft,sn,Ot,Nr,an,un,ln}from"./main-szjdmydc.js";import"./main-tnbhrm9n.js";import{O}from"./main-4j1fcevh.js";import{D}from"./main-hm1ng3yg.js";var ye=0;function Le(){return ye+=1,`flow-node-${ye}`}function ue(o){return{key:Le(),form:o,queue:"",ignoreFailure:!1,children:[]}}function xe(o,f){return o===""?f:`${o}.${f}`}function U(o,f){let p=[],u=(h,F,N,A,P)=>{p.push({node:h,path:F,queue:N,level:A,parent:P}),h.children.forEach((k,j)=>u(k,xe(F,`children.${j}`),k.queue===""?N:k.queue,A+1,h.key))};return u(o,"",f,1,null),p}function Ee(o){return o.children.reduce((f,p)=>f+Ee(p),1)}function ve(o,f){return[...new Set(U(o,f).map((p)=>p.queue))]}function ce(o,f){if(o.key===f)return o;for(let p of o.children){let u=ce(p,f);if(u)return u}return}function fe(o,f,p){if(o.key===f)return p(o);let u=!1,h=o.children.map((F)=>{let N=fe(F,f,p);return u||=N!==F,N});return u?{...o,children:h}:o}function ke(o,f,p){return fe(o,f,(u)=>({...u,...p}))}function Pe(o,f,p){return fe(o,f,(u)=>({...u,children:[...u.children,p]}))}function me(o,f){if(!o.children.some((p)=>p.key===f)){let p=!1,u=o.children.map((h)=>{let F=me(h,f);return p||=F!==h,F});return p?{...o,children:u}:o}return{...o,children:o.children.filter((p)=>p.key!==f)}}function pe(o,f,p){if(Ee(o)>=p.maxNodes)return`The flow holds ${p.maxNodes} jobs, the most the API accepts.`;let u=U(o,"").find((h)=>h.node.key===f);if(u&&u.level>=p.maxDepth)return`This job is at level ${u.level}; a flow nests at most ${p.maxDepth} levels.`;return null}function Fe(o){if(o==="")return;if(o.length>ht)return`At most ${ht} characters.`;return new RegExp(Ct).test(o)?void 0:"Letters, digits, “_”, “.” and “-” only, and not “.” or “..”."}function J(o,f,p,u){let h=o[f]??={};h[p]??=u}function we(o,f,p){let u={},h=U(o,f),F=new Map,N=!1,A=!1;h.forEach((P,k)=>{let{node:j,queue:I,level:H,parent:z}=P;for(let[B,G]of Object.entries(Ft(j.form)))J(u,j.key,B,G);if(z!==null){let B=Fe(j.queue);if(B)J(u,j.key,"queue",B)}else if(j.ignoreFailure)J(u,j.key,"opts.ignoreFailure","The top job has no parent to carry on without it.");if(!N&&k>=p.maxNodes)N=!0,J(u,j.key,"node",`A flow may hold at most ${p.maxNodes} jobs.`);if(!A&&H>p.maxDepth)A=!0,J(u,j.key,"node",`A flow may nest at most ${p.maxDepth} levels.`);if(j.form.jobId!==""&&br(j.form.jobId)===void 0){let B=`${I}:${j.form.jobId}`;F.set(B,[...F.get(B)??[],P])}});for(let[P,k]of F)if(k.length>1)for(let{node:j}of k)J(u,j.key,"opts.jobId",`Another job of this flow has the same queue and id (${P}).`);return u}function qe(o){return Object.values(o).some((f)=>Object.keys(f).length>0)}function De(o,f){let p=(u,h)=>{let F=sn(u.form),N=h!==null,A={...F.opts,...N&&u.ignoreFailure?{ignoreFailure:!0}:{}},P=!N?f:u.queue===""?h:u.queue;return{name:F.name,data:F.data,...Object.keys(A).length>0?{opts:A}:{},...N&&P!==h?{queue:P}:{},...u.children.length>0?{children:u.children.map((k)=>p(k,P))}:{}}};return p(o,null)}var Je=new Set(["name","data","queue","opts.jobId","opts.priority","opts.delay","opts.runAt","opts.attempts","opts.backoff","opts.timeout","opts.ignoreFailure"]);function Ne(o,f){let p=new Map(o.map((k)=>[k.path,k.node.key])),u=f===""?[]:f.split("."),h="",F=0;while(u[F]==="children"&&/^\d+$/.test(u[F+1]??""))h=xe(h,`children.${u[F+1]}`),F+=2;let N=p.get(h);if(N===void 0)return;let A=u.slice(F).join("."),P=A===""?"node":Je.has(A)?A:A.startsWith("data.")?"data":"node";return{key:N,field:P}}function Me(o,f){let p=o.context.queue;return typeof p==="string"&&p!==""?p:f}function _e(o,f){if(o.code==="QUEUE_NOT_FOUND")return`The API does not manage a queue named “${f}”.`;return o.status===401?`Sign in to add jobs to “${f}”.`:`You may not add jobs to “${f}”.`}function je(o,f,p){let u={},h=U(f,p),F=!1;if(o.code==="VALIDATION"&&o.issues.length>0){for(let N of o.issues){let A=N.target==="body"?Ne(h,N.path):void 0;if(A)J(u,A.key,A.field,N.message);else F=!0}return{errors:u,unplaced:F}}if(o.code==="NAME_NOT_ADDABLE"){let N=o.context.path,A=typeof N==="string"?Ne(h,N):void 0;if(!A)return{errors:u,unplaced:!0};return J(u,A.key,A.field,Ot(o,"add")??o.detail??o.title),{errors:u,unplaced:!1}}if(o.code==="FORBIDDEN"||o.code==="UNAUTHORIZED"||o.code==="QUEUE_NOT_FOUND"){let N=Me(o,p),A=_e(o,N),P=h.filter((k)=>k.queue===N);for(let{node:k}of P)J(u,k.key,"queue",A);return{errors:u,unplaced:P.length===0}}return{errors:u,unplaced:!0}}function Ae(o,f,p){let u=o.find((F)=>F.node.key===f);if(u===void 0)return;let{node:h}=u;switch(p){case"node":return u.path;case"queue":return u.queue;case"opts.ignoreFailure":return h.ignoreFailure;case"name":return h.form.name;case"data":return h.form.data.text;case"opts.jobId":return h.form.jobId;case"opts.priority":return h.form.priority;case"opts.delay":return`${h.form.timing}:${h.form.delay}`;case"opts.runAt":return`${h.form.timing}:${h.form.runAt}`;case"opts.attempts":return h.form.attempts;case"opts.backoff":return h.form.backoff;case"opts.timeout":return h.form.timeout}}function Re(o,f,p,u,h){return Ae(U(o,p),u,h)===Ae(U(f,p),u,h)}var he="\x00other",Ue=250,He=["opts.jobId","opts.priority","opts.delay","opts.runAt","opts.attempts","opts.backoff","opts.timeout"];function Ke(o){return o.length===0?"Top job":`Child ${o.map((f)=>f+1).join(".")}`}function Eo(o){return o.open?e(ze,{...o}):null}function ze({queue:o,onClose:f}){let p=i(),u=m(),h=y("queues.list"),F=c(),N=u.limits.maxJobDataBytes,A=u.addableNames,P=A!==null&&A.length===0,k={maxNodes:u.limits.maxFlowNodes,maxDepth:u.limits.maxFlowDepth},j=()=>on({addableNames:A,maxBytes:N}),[I,H]=r(()=>ue(j())),[z,B]=r(!1),[G,Q]=r(null),V=Ce(),C=O({mutationFn:(x)=>uo(p,o,De(x,o)),invalidate:(x,R)=>lo(ve(R,o)),toastErrors:!1}),K=U(I,o),W=K.length,X=[...new Set(K.map((x)=>x.queue))].filter((x)=>x!==o&&Fe(x)===void 0).sort().join(`
2
+ `),[Y,Z]=r("");v(()=>{let x=setTimeout(Z,Ue,X);return()=>clearTimeout(x)},[X]);let ee=Y===""?[]:Y.split(`
3
+ `),te=rn({queries:ee.map((x)=>({queryKey:_.permissions({queue:x}),queryFn:({signal:R})=>p.getPermissions({queue:x},R),retry:!1}))}),q=new Set(ee.filter((x,R)=>{let L=te[R]?.data;return L!==void 0&&!yt(L,"jobs.add")})),re=a({queryKey:_.queues(""),queryFn:({signal:x})=>p.listQueues("",x),enabled:h}),$e=h&&!re.isError?re.data?.items.map((x)=>x.name)??[]:null,Qe=z?we(I,o,k):{},se=b(C.error)?C.error:null,ne=C.variables,le=g(()=>se&&ne?je(se,ne,o):null,[se,ne,o]),ge=C.error&&(le===null||le.unplaced)?Nr(C.error,"add"):null,Te=new Set(K.map((x)=>x.node.key)),be=Object.keys(V.problems).some((x)=>Te.has(x)),Se={topQueue:o,addableNames:A,maxBytes:N,listedQueues:$e,refusedQueues:q,errorOf:(x,R)=>(R==="opts.runAt"?V.problems[x]:void 0)??Qe[x]?.[R]??(ne&&Re(I,ne,o,x,R)?le?.errors[x]?.[R]:void 0),blockedReason:(x)=>pe(I,x,k),patch:(x,R)=>H((L)=>ke(L,x,R)),addChild:(x)=>{if(pe(I,x,k)!==null)return;let R=ue(j());H((L)=>Pe(L,x,R)),Q({key:R.key,target:"first"})},remove:(x,R)=>{let L=ce(I,x);if(L)for(let ae of U(L,""))V.report(ae.node.key,void 0);H((ae)=>me(ae,x)),Q({key:R,target:"add"})},reportRunAt:V.report,focus:G,focused:()=>Q(null)},Be=(x)=>{if(x.preventDefault(),B(!0),P||be||qe(we(I,o,k)))return;C.mutate(I)},ie=()=>{if(!C.isPending)f()},de=C.isSuccess?C.data:void 0;return e(oe,{open:!0,onClose:ie,title:t(n,{children:["Add a flow to ",e("code",{children:o})]}),description:"A flow is a job and the jobs it waits on: they are added first, and it runs once they have all completed.",size:"lg",closeOnEscape:!C.isPending,closeOnBackdrop:!C.isPending,footer:de?e(s,{variant:"primary",onClick:ie,children:"Done"}):t(n,{children:[e(s,{onClick:ie,disabled:C.isPending,children:"Cancel"}),e(s,{variant:"primary",type:"submit",form:F,disabled:C.isPending||P||be,"aria-busy":C.isPending||void 0,children:C.isPending?"Adding…":"Add flow"})]}),children:de?e(Ye,{queue:o,result:de}):P?e(d,{title:"No flow can be added here",description:"This API accepts no job names: adding jobs is switched off (the jobs.add action is not enabled, or the API is read-only), or no name is allowed. Ask whoever runs it to list the names in addableNames."}):t("form",{id:F,className:"job-form flow-form",onSubmit:Be,noValidate:!0,children:[t("p",{className:"flow-count","data-testid":"flow-count","aria-live":"polite",children:[W," of ",k.maxNodes," jobs",W>=k.maxNodes?": the most one flow may hold.":`, nested at most ${k.maxDepth} levels.`]}),e("ol",{className:"flow-tree","aria-label":"Flow",children:e(Ie,{node:I,indices:[],level:1,parent:null,parentQueue:null,editor:Se})}),ge!==null&&e(D,{error:ge})]})})}function Ie({node:o,indices:f,level:p,parent:u,parentQueue:h,editor:F}){let{key:N}=o,A=Ke(f),P=u===null,k=P?F.topQueue:o.queue===""?h:o.queue,j=T(null),I=T(null),H=c(),z=c(),[B,G]=r(!1),Q=(q)=>F.errorOf(N,q),V=He.some((q)=>Q(q)),C=B||V,K=F.blockedReason(N),W=F.refusedQueues.has(k),X=Q("node")??(P?Q("opts.ignoreFailure"):void 0),Y=Q("queue")??(W?`You may not add jobs to “${k}”: the API will refuse this flow.`:void 0),{focus:Z,focused:ee}=F;v(()=>{if(Z?.key!==N)return;(Z.target==="add"&&I.current&&!I.current.disabled?I.current:j.current?.querySelector("input, select, textarea"))?.focus(),ee()},[Z,N,ee]);let te=(q)=>F.patch(N,{form:{...o.form,...q}});return t("li",{className:"flow-item",children:[t("fieldset",{ref:j,className:"flow-node","data-refused":W||void 0,"data-testid":"flow-node",children:[e("legend",{children:A}),X&&e("p",{className:"field-error job-form-error",role:"alert",children:X}),e(an,{value:o.form.name,onChange:(q)=>te({name:q}),error:Q("name"),addableNames:F.addableNames}),e(un,{value:o.form.data.text,onChange:(q)=>te({data:q}),error:Q("data"),maxBytes:F.maxBytes,rows:4}),P?t("div",{className:"flow-node-queue",children:[t("p",{className:"field-label",children:["Queue: ",e("code",{children:k})]}),Y&&e("p",{className:"field-error",children:Y})]}):e(Ve,{value:o.queue,onChange:(q)=>F.patch(N,{queue:q}),parentQueue:h,listed:F.listedQueues,error:Y}),!P&&t("div",{children:[e(w,{checked:o.ignoreFailure,onChange:(q)=>F.patch(N,{ignoreFailure:q}),label:"Parent carries on if this fails",hint:"Its failure is recorded for the parent to read, instead of failing it."}),Q("opts.ignoreFailure")&&e("p",{className:"field-error",children:Q("opts.ignoreFailure")})]}),t("div",{className:"flow-node-actions",children:[t(s,{size:"sm",variant:"ghost","aria-expanded":C,"aria-controls":C?z:void 0,onClick:()=>G(!C),children:["Options",t("span",{className:"visually-hidden",children:[" of ",A]})]}),t(s,{ref:I,size:"sm",onClick:()=>F.addChild(N),disabled:K!==null,"aria-describedby":K!==null?H:void 0,children:["Add child",t("span",{className:"visually-hidden",children:[" to ",A]})]}),u!==null&&t(s,{size:"sm",variant:"danger",onClick:()=>F.remove(N,u),children:["Remove",t("span",{className:"visually-hidden",children:[" ",A]})]}),K!==null&&e("p",{id:H,className:"field-hint flow-node-blocked",children:K})]}),C&&e("div",{id:z,children:e(ln,{form:o.form,patch:te,fieldError:(q)=>Q(q),onRunAtProblem:(q)=>F.reportRunAt(N,q)})})]}),o.children.length>0&&e("ol",{className:"flow-tree flow-children","aria-label":`Jobs ${A} waits on`,children:o.children.map((q,re)=>e(Ie,{node:q,indices:[...f,re],level:p+1,parent:N,parentQueue:k,editor:F},q.key))})]})}function Ve({value:o,onChange:f,parentQueue:p,listed:u,error:h}){let[F,N]=r(()=>o!==""&&u!==null&&!u.includes(o));if(u===null)return e(l,{label:"Queue",hint:`Leave empty for its parent's, ${p}.`,error:h,children:e(M,{value:o,onChange:f,autoComplete:"off",spellCheck:!1})});let A=F||o!==""&&!u.includes(o);return t(n,{children:[e(l,{label:"Queue",error:A?void 0:h,children:e(S,{options:[{value:"",label:`Its parent's (${p})`},...u.map((P)=>({value:P})),{value:he,label:"Another queue…"}],value:A?he:o,onChange:(P)=>{if(P===he)N(!0),f("");else N(!1),f(P)}})}),A&&e(l,{label:"Another queue",hint:"A queue need not exist yet. Leave empty for its parent's.",error:h,children:e(M,{value:o,onChange:f,autoComplete:"off",spellCheck:!1})})]})}function Oe(o){return[o,...o.children.flatMap(Oe)]}function Ye({queue:o,result:f}){let p=T(null);v(()=>p.current?.focus(),[]);let u=f.job,h=Oe(f).filter((F)=>F.added);return e("div",{ref:p,className:"flow-result",tabIndex:-1,role:"status","data-testid":"flow-result",children:f.added?t(n,{children:[t("p",{children:["Added ",h.length," ",h.length===1?"job":"jobs",". The top job,"," ",t(E,{to:We(u.queue||o,u.id),children:[u.name," (",u.id,")"]}),", runs once the jobs it waits on have completed."]}),e("ul",{className:"flow-result-list",children:h.map(({job:F})=>t("li",{children:[e(E,{to:We(F.queue,F.id),children:F.name})," ",t("span",{className:"muted",children:["in ",e("code",{children:F.queue}),", id ",e("code",{children:F.id})]})]},`${F.queue}:${F.id}`))})]}):t("p",{children:["The top job already existed, so nothing was added:"," ",t(E,{to:We(u.queue||o,u.id),children:["view ",u.name," (",u.id,")"]}),"."]})})}export{Eo as AddFlowDialog};
4
+
5
+ //# debugId=CD129209ADBCED6164756E2164756E21
6
+ //# sourceMappingURL=AddFlowDialog-h6xkgx9b.js.map
@@ -6,6 +6,6 @@
6
6
  "import type { FormEvent } from \"react\";\nimport type { AddFlowResultDto } from \"../../api/types\";\nimport type {\n FlowField,\n FlowLimits,\n FlowNode,\n FlowNodePatch,\n} from \"./addFlowForm\";\nimport type { AddJobField, AddJobForm } from \"./addJobForm\";\nimport { useQueries, useQuery } from \"@tanstack/react-query\";\nimport { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { isApiError } from \"../../api/errors\";\nimport { addFlow, flowInvalidation, jobScreenPath } from \"../../api/jobs\";\nimport { queryKeys } from \"../../api/queryKeys\";\nimport { Button } from \"../../components/Button\";\nimport { Dialog } from \"../../components/Dialog\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { Field } from \"../../components/Field\";\nimport { Checkbox, Select, TextInput } from \"../../components/inputs\";\nimport { ProblemBanner } from \"../../components/ProblemBanner\";\nimport { useApiClient } from \"../../context\";\nimport { useApiMutation } from \"../../hooks/useApiMutation\";\nimport { useFieldProblems } from \"../../hooks/useFieldProblems\";\nimport { canPerform, useCan, useMeta } from \"../../meta/hooks\";\nimport { Link } from \"../../router\";\nimport {\n addChildBlocked,\n addFlowChild,\n checkFlowQueue,\n fieldUnchangedSince,\n findFlowNode,\n flowBody,\n flowNode,\n flowProblemErrors,\n flowQueues,\n hasFlowErrors,\n placeFlow,\n removeFlowNode,\n updateFlowNode,\n validateFlow,\n} from \"./addFlowForm\";\nimport { emptyAddForm } from \"./addJobForm\";\nimport { friendlyJobError } from \"./jobErrors\";\nimport { JobDataField, JobNameField, JobOptionsFields } from \"./JobFormFields\";\nimport \"./job.css\";\n\n/** Props of {@link AddFlowDialog}: the same contract as `AddJobDialogProps`. */\nexport interface AddFlowDialogProps {\n /** The queue the top job is added to (the screen's). */\n queue: string;\n /** Whether the dialog is open. */\n open: boolean;\n /** Called to close it: Cancel, Escape, or Done once the result is shown. */\n onClose: () => void;\n}\n\n/** A select value no queue can have (queue names are `[\\w.-]+`): \"type another queue\". */\nconst OTHER_QUEUE = \"\\u0000other\";\n\n/** How long the queues typed into the flow settle before their permissions are asked, ms. */\nconst PERMISSION_CHECK_DELAY_MS = 250;\n\n/** The option fields, which open the node's options when one has an error. */\nconst OPTION_FIELDS: readonly AddJobField[] = [\n \"opts.jobId\",\n \"opts.priority\",\n \"opts.delay\",\n \"opts.runAt\",\n \"opts.attempts\",\n \"opts.backoff\",\n \"opts.timeout\",\n];\n\n/** Where focus goes after the tree changes: a node's first field, or its Add child button. */\ninterface FocusRequest {\n /** The node. */\n key: string;\n /** `\"first\"` for its first field (a new node), `\"add\"` for its Add child button (after a child went). */\n target: \"first\" | \"add\";\n}\n\n/** What every node editor shares: the tree's operations, its errors and what it may offer. */\ninterface FlowEditor {\n /** The screen's queue, which the top job goes in. */\n topQueue: string;\n /** `MetaDto.addableNames`: `null` for any name. */\n addableNames: string[] | null;\n /** `limits.maxJobDataBytes`. */\n maxBytes: number;\n /** The queues the caller may list, for a child's queue select; `null` when it may not list them. */\n listedQueues: string[] | null;\n /** Queues whose own permissions refuse `jobs.add` (the pre-check; the API decides). */\n refusedQueues: ReadonlySet<string>;\n /** The error on a node's field, if any. */\n errorOf: (key: string, field: FlowField) => string | undefined;\n /** Why a node cannot take another child, or `null`. */\n blockedReason: (key: string) => string | null;\n /** Changes a node's own fields. */\n patch: (key: string, patch: FlowNodePatch) => void;\n /** Appends an empty child to a node. */\n addChild: (key: string) => void;\n /** Removes a node and everything below it. */\n remove: (key: string, parent: string) => void;\n /** Records or clears a node's run-time input problem. */\n reportRunAt: (key: string, problem: string | undefined) => void;\n /** A pending focus move, or `null`. */\n focus: FocusRequest | null;\n /** Called once a node has taken the focus it was asked to. */\n focused: () => void;\n}\n\n/** A node's label: \"Top job\", or \"Child 1.2\" for the second child of the first child. */\nfunction nodeLabel(indices: readonly number[]): string {\n return indices.length === 0\n ? \"Top job\"\n : `Child ${indices.map((index) => index + 1).join(\".\")}`;\n}\n\n/**\n * Adds a flow (`POST /queues/:queue/flows`): a top job in the screen's queue\n * and the jobs it waits on, each in its parent's queue or one of its own.\n * Loaded on demand: the queue screen renders `AddFlowDialog` from `../lazy`.\n */\nexport function AddFlowDialog(props: AddFlowDialogProps) {\n return props.open ? <OpenAddFlowDialog {...props} /> : null;\n}\n\n/** The mounted dialog; its state lives exactly as long as one opening. */\nfunction OpenAddFlowDialog({ queue, onClose }: AddFlowDialogProps) {\n const api = useApiClient();\n const meta = useMeta();\n const canListQueues = useCan(\"queues.list\");\n const formId = useId();\n const maxBytes = meta.limits.maxJobDataBytes;\n const addableNames = meta.addableNames;\n const nothingAddable = addableNames !== null && addableNames.length === 0;\n const limits: FlowLimits = {\n maxNodes: meta.limits.maxFlowNodes,\n maxDepth: meta.limits.maxFlowDepth,\n };\n const newForm = (): AddJobForm => emptyAddForm({ addableNames, maxBytes });\n const [root, setRoot] = useState<FlowNode>(() => flowNode(newForm()));\n const [submitted, setSubmitted] = useState(false);\n const [focus, setFocus] = useState<FocusRequest | null>(null);\n const times = useFieldProblems<string>();\n\n const add = useApiMutation({\n mutationFn: (sent: FlowNode) => addFlow(api, queue, flowBody(sent, queue)),\n invalidate: (_result: AddFlowResultDto, sent: FlowNode) =>\n flowInvalidation(flowQueues(sent, queue)),\n toastErrors: false,\n });\n\n const placed = placeFlow(root, queue);\n const count = placed.length;\n\n // The pre-check: each other queue the flow writes to, asked about once\n // its name has settled, through the same targeted map a queue screen uses.\n const otherQueues = [...new Set(placed.map((entry) => entry.queue))]\n .filter((name) => name !== queue && checkFlowQueue(name) === undefined)\n .sort()\n .join(\"\\n\");\n const [checkedQueues, setCheckedQueues] = useState(\"\");\n useEffect(() => {\n const timer = setTimeout(\n setCheckedQueues,\n PERMISSION_CHECK_DELAY_MS,\n otherQueues,\n );\n return () => clearTimeout(timer);\n }, [otherQueues]);\n const toCheck = checkedQueues === \"\" ? [] : checkedQueues.split(\"\\n\");\n const permissions = useQueries({\n queries: toCheck.map((name) => ({\n queryKey: queryKeys.permissions({ queue: name }),\n queryFn: ({ signal }: { signal: AbortSignal }) =>\n api.getPermissions({ queue: name }, signal),\n retry: false,\n })),\n });\n const refusedQueues = new Set(\n toCheck.filter((_name, index) => {\n const answer = permissions[index]?.data;\n return answer !== undefined && !canPerform(answer, \"jobs.add\");\n }),\n );\n\n const listing = useQuery({\n queryKey: queryKeys.queues(\"\"),\n queryFn: ({ signal }) => api.listQueues(\"\", signal),\n enabled: canListQueues,\n });\n const listedQueues =\n canListQueues && !listing.isError\n ? (listing.data?.items.map((item) => item.name) ?? [])\n : null;\n\n const clientErrors = submitted ? validateFlow(root, queue, limits) : {};\n const apiError = isApiError(add.error) ? add.error : null;\n const sent = add.variables;\n const server = useMemo(\n () => (apiError && sent ? flowProblemErrors(apiError, sent, queue) : null),\n [apiError, sent, queue],\n );\n const bannerError =\n add.error && (server === null || server.unplaced)\n ? friendlyJobError(add.error, \"add\")\n : null;\n const liveKeys = new Set(placed.map((entry) => entry.node.key));\n const timesBlocked = Object.keys(times.problems).some((key) =>\n liveKeys.has(key),\n );\n\n const editor: FlowEditor = {\n topQueue: queue,\n addableNames,\n maxBytes,\n listedQueues,\n refusedQueues,\n errorOf: (key, field) =>\n (field === \"opts.runAt\" ? times.problems[key] : undefined) ??\n clientErrors[key]?.[field] ??\n // An API error stays only while the field holds what was sent.\n (sent && fieldUnchangedSince(root, sent, queue, key, field)\n ? server?.errors[key]?.[field]\n : undefined),\n blockedReason: (key) => addChildBlocked(root, key, limits),\n patch: (key, patch) =>\n setRoot((current) => updateFlowNode(current, key, patch)),\n addChild: (key) => {\n if (addChildBlocked(root, key, limits) !== null) {\n return;\n }\n const child = flowNode(newForm());\n setRoot((current) => addFlowChild(current, key, child));\n setFocus({ key: child.key, target: \"first\" });\n },\n remove: (key, parent) => {\n const gone = findFlowNode(root, key);\n if (gone) {\n for (const entry of placeFlow(gone, \"\")) {\n times.report(entry.node.key, undefined);\n }\n }\n setRoot((current) => removeFlowNode(current, key));\n setFocus({ key: parent, target: \"add\" });\n },\n reportRunAt: times.report,\n focus,\n focused: () => setFocus(null),\n };\n\n const onSubmit = (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setSubmitted(true);\n if (\n nothingAddable ||\n timesBlocked ||\n hasFlowErrors(validateFlow(root, queue, limits))\n ) {\n return;\n }\n add.mutate(root);\n };\n\n const close = () => {\n if (!add.isPending) {\n onClose();\n }\n };\n\n const result = add.isSuccess ? add.data : undefined;\n\n return (\n <Dialog\n open\n onClose={close}\n title={\n <>\n Add a flow to <code>{queue}</code>\n </>\n }\n description=\"A flow is a job and the jobs it waits on: they are added first, and it runs once they have all completed.\"\n size=\"lg\"\n closeOnEscape={!add.isPending}\n closeOnBackdrop={!add.isPending}\n footer={\n result ? (\n <Button\n variant=\"primary\"\n onClick={close}\n >\n Done\n </Button>\n ) : (\n <>\n <Button\n onClick={close}\n disabled={add.isPending}\n >\n Cancel\n </Button>\n <Button\n variant=\"primary\"\n type=\"submit\"\n form={formId}\n disabled={add.isPending || nothingAddable || timesBlocked}\n aria-busy={add.isPending || undefined}\n >\n {add.isPending ? \"Adding…\" : \"Add flow\"}\n </Button>\n </>\n )\n }\n >\n {result ? (\n <FlowResult\n queue={queue}\n result={result}\n />\n ) : nothingAddable ? (\n <EmptyState\n title=\"No flow can be added here\"\n description=\"This API accepts no job names: adding jobs is switched off (the jobs.add action is not enabled, or the API is read-only), or no name is allowed. Ask whoever runs it to list the names in addableNames.\"\n />\n ) : (\n <form\n id={formId}\n className=\"job-form flow-form\"\n onSubmit={onSubmit}\n noValidate\n >\n <p\n className=\"flow-count\"\n data-testid=\"flow-count\"\n aria-live=\"polite\"\n >\n {count} of {limits.maxNodes} jobs\n {count >= limits.maxNodes\n ? \": the most one flow may hold.\"\n : `, nested at most ${limits.maxDepth} levels.`}\n </p>\n <ol\n className=\"flow-tree\"\n aria-label=\"Flow\"\n >\n <FlowNodeEditor\n node={root}\n indices={[]}\n level={1}\n parent={null}\n parentQueue={null}\n editor={editor}\n />\n </ol>\n {bannerError !== null && <ProblemBanner error={bannerError} />}\n </form>\n )}\n </Dialog>\n );\n}\n\n/** Props of {@link FlowNodeEditor}. */\ninterface FlowNodeEditorProps {\n /** The node. */\n node: FlowNode;\n /** Its position under the top job, `[]` for the top job itself (see {@link nodeLabel}). */\n indices: readonly number[];\n /** Its level, the top job being 1. */\n level: number;\n /** Its parent's key; `null` for the top job. */\n parent: string | null;\n /** The queue its parent goes in; `null` for the top job. */\n parentQueue: string | null;\n /** The shared operations and state. */\n editor: FlowEditor;\n}\n\n/** One job of the flow: its fields and actions, then the jobs it waits on. */\nfunction FlowNodeEditor({\n node,\n indices,\n level,\n parent,\n parentQueue,\n editor,\n}: FlowNodeEditorProps) {\n const { key } = node;\n const label = nodeLabel(indices);\n const isTop = parent === null;\n const queue = isTop\n ? editor.topQueue\n : node.queue === \"\"\n ? parentQueue!\n : node.queue;\n const fieldsetRef = useRef<HTMLFieldSetElement>(null);\n const addRef = useRef<HTMLButtonElement>(null);\n const reasonId = useId();\n const optionsId = useId();\n const [showOptions, setShowOptions] = useState(false);\n const errorOf = (field: FlowField) => editor.errorOf(key, field);\n const optionError = OPTION_FIELDS.some((field) => errorOf(field));\n const optionsShown = showOptions || optionError;\n const blocked = editor.blockedReason(key);\n const refused = editor.refusedQueues.has(queue);\n const nodeError =\n errorOf(\"node\") ?? (isTop ? errorOf(\"opts.ignoreFailure\") : undefined);\n const queueError =\n errorOf(\"queue\") ??\n (refused\n ? `You may not add jobs to “${queue}”: the API will refuse this flow.`\n : undefined);\n\n const { focus, focused } = editor;\n useEffect(() => {\n if (focus?.key !== key) {\n return;\n }\n const target =\n focus.target === \"add\" && addRef.current && !addRef.current.disabled\n ? addRef.current\n : fieldsetRef.current?.querySelector<HTMLElement>(\n \"input, select, textarea\",\n );\n target?.focus();\n focused();\n }, [focus, key, focused]);\n\n const patchForm = (next: Partial<AddJobForm>) =>\n editor.patch(key, { form: { ...node.form, ...next } });\n\n return (\n <li className=\"flow-item\">\n <fieldset\n ref={fieldsetRef}\n className=\"flow-node\"\n data-refused={refused || undefined}\n data-testid=\"flow-node\"\n >\n <legend>{label}</legend>\n {nodeError && (\n <p\n className=\"field-error job-form-error\"\n role=\"alert\"\n >\n {nodeError}\n </p>\n )}\n <JobNameField\n value={node.form.name}\n onChange={(name) => patchForm({ name })}\n error={errorOf(\"name\")}\n addableNames={editor.addableNames}\n />\n <JobDataField\n value={node.form.data.text}\n onChange={(data) => patchForm({ data })}\n error={errorOf(\"data\")}\n maxBytes={editor.maxBytes}\n rows={4}\n />\n {isTop ? (\n <div className=\"flow-node-queue\">\n <p className=\"field-label\">\n Queue: <code>{queue}</code>\n </p>\n {queueError && <p className=\"field-error\">{queueError}</p>}\n </div>\n ) : (\n <FlowQueueField\n value={node.queue}\n onChange={(next) => editor.patch(key, { queue: next })}\n parentQueue={parentQueue!}\n listed={editor.listedQueues}\n error={queueError}\n />\n )}\n {!isTop && (\n <div>\n <Checkbox\n checked={node.ignoreFailure}\n onChange={(ignoreFailure) => editor.patch(key, { ignoreFailure })}\n label=\"Parent carries on if this fails\"\n hint=\"Its failure is recorded for the parent to read, instead of failing it.\"\n />\n {errorOf(\"opts.ignoreFailure\") && (\n <p className=\"field-error\">{errorOf(\"opts.ignoreFailure\")}</p>\n )}\n </div>\n )}\n <div className=\"flow-node-actions\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-expanded={optionsShown}\n aria-controls={optionsShown ? optionsId : undefined}\n onClick={() => setShowOptions(!optionsShown)}\n >\n Options\n <span className=\"visually-hidden\"> of {label}</span>\n </Button>\n <Button\n ref={addRef}\n size=\"sm\"\n onClick={() => editor.addChild(key)}\n disabled={blocked !== null}\n aria-describedby={blocked !== null ? reasonId : undefined}\n >\n Add child\n <span className=\"visually-hidden\"> to {label}</span>\n </Button>\n {parent !== null && (\n <Button\n size=\"sm\"\n variant=\"danger\"\n onClick={() => editor.remove(key, parent)}\n >\n Remove\n <span className=\"visually-hidden\"> {label}</span>\n </Button>\n )}\n {blocked !== null && (\n <p\n id={reasonId}\n className=\"field-hint flow-node-blocked\"\n >\n {blocked}\n </p>\n )}\n </div>\n {optionsShown && (\n <div id={optionsId}>\n <JobOptionsFields\n form={node.form}\n patch={patchForm}\n fieldError={(field) => errorOf(field)}\n onRunAtProblem={(problem) => editor.reportRunAt(key, problem)}\n />\n </div>\n )}\n </fieldset>\n {node.children.length > 0 && (\n <ol\n className=\"flow-tree flow-children\"\n aria-label={`Jobs ${label} waits on`}\n >\n {node.children.map((child, index) => (\n <FlowNodeEditor\n key={child.key}\n node={child}\n indices={[...indices, index]}\n level={level + 1}\n parent={key}\n parentQueue={queue}\n editor={editor}\n />\n ))}\n </ol>\n )}\n </li>\n );\n}\n\n/** Props of {@link FlowQueueField}. */\ninterface FlowQueueFieldProps {\n /** The child's own queue; `\"\"` for its parent's. */\n value: string;\n /** Called with a new queue (`\"\"` for its parent's). */\n onChange: (queue: string) => void;\n /** The queue its parent goes in, which `\"\"` means. */\n parentQueue: string;\n /** The queues the caller may list; `null` when it may not, and the queue is typed. */\n listed: string[] | null;\n /** The error to show under it. */\n error: string | undefined;\n}\n\n/**\n * A child's queue: its parent's by default, one the caller can list, or any\n * other name typed in (a queue need not exist before a job is added to it).\n */\nfunction FlowQueueField({\n value,\n onChange,\n parentQueue,\n listed,\n error,\n}: FlowQueueFieldProps) {\n const [typing, setTyping] = useState(\n () => value !== \"\" && listed !== null && !listed.includes(value),\n );\n if (listed === null) {\n return (\n <Field\n label=\"Queue\"\n hint={`Leave empty for its parent's, ${parentQueue}.`}\n error={error}\n >\n <TextInput\n value={value}\n onChange={onChange}\n autoComplete=\"off\"\n spellCheck={false}\n />\n </Field>\n );\n }\n const freeText = typing || (value !== \"\" && !listed.includes(value));\n return (\n <>\n <Field\n label=\"Queue\"\n error={freeText ? undefined : error}\n >\n <Select\n options={[\n { value: \"\", label: `Its parent's (${parentQueue})` },\n ...listed.map((name) => ({ value: name })),\n { value: OTHER_QUEUE, label: \"Another queue…\" },\n ]}\n value={freeText ? OTHER_QUEUE : value}\n onChange={(chosen) => {\n if (chosen === OTHER_QUEUE) {\n setTyping(true);\n onChange(\"\");\n } else {\n setTyping(false);\n onChange(chosen);\n }\n }}\n />\n </Field>\n {freeText && (\n <Field\n label=\"Another queue\"\n hint=\"A queue need not exist yet. Leave empty for its parent's.\"\n error={error}\n >\n <TextInput\n value={value}\n onChange={onChange}\n autoComplete=\"off\"\n spellCheck={false}\n />\n </Field>\n )}\n </>\n );\n}\n\n/** Props of {@link FlowResult}. */\ninterface FlowResultProps {\n /** The screen's queue, which the top job went in. */\n queue: string;\n /** What the API answered. */\n result: AddFlowResultDto;\n}\n\n/** Every job of a result in body order (each before its children). */\nfunction resultJobs(result: AddFlowResultDto): AddFlowResultDto[] {\n return [result, ...result.children.flatMap(resultJobs)];\n}\n\n/** What the flow came to: every job added, linked to its screen, or the top job that already existed. */\nfunction FlowResult({ queue, result }: FlowResultProps) {\n const ref = useRef<HTMLDivElement>(null);\n useEffect(() => ref.current?.focus(), []);\n const top = result.job;\n const jobs = resultJobs(result).filter((entry) => entry.added);\n return (\n <div\n ref={ref}\n className=\"flow-result\"\n tabIndex={-1}\n role=\"status\"\n data-testid=\"flow-result\"\n >\n {result.added ? (\n <>\n <p>\n Added {jobs.length} {jobs.length === 1 ? \"job\" : \"jobs\"}. The top\n job,{\" \"}\n <Link to={jobScreenPath(top.queue || queue, top.id)}>\n {top.name} ({top.id})\n </Link>\n , runs once the jobs it waits on have completed.\n </p>\n <ul className=\"flow-result-list\">\n {jobs.map(({ job }) => (\n <li key={`${job.queue}:${job.id}`}>\n <Link to={jobScreenPath(job.queue, job.id)}>{job.name}</Link>{\" \"}\n <span className=\"muted\">\n in <code>{job.queue}</code>, id <code>{job.id}</code>\n </span>\n </li>\n ))}\n </ul>\n </>\n ) : (\n <p>\n The top job already existed, so nothing was added:{\" \"}\n <Link to={jobScreenPath(top.queue || queue, top.id)}>\n view {top.name} ({top.id})\n </Link>\n .\n </p>\n )}\n </div>\n );\n}\n"
7
7
  ],
8
8
  "mappings": "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",
9
- "debugId": "06353D6AB441FF3964756E2164756E21",
9
+ "debugId": "CD129209ADBCED6164756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -0,0 +1,4 @@
1
+ import{Z,n,e,t,i,Y,a,H,p,o,z,Et}from"./main-a250ffk7.js";var c=20000;function f(r,m=Date.now()){return Et(r,m)}function u(r){return["addedByState",r.kind==="preset"?{seconds:r.seconds}:{from:r.from,to:r.to}]}function y(r,m,l){let{from:s,to:d}=f(m);return r.request("GET","/overview/added",{query:{from:s,to:d},signal:l})}function b({range:r}){let m=i(),l=H(c),s=a({queryKey:u(r),queryFn:({signal:d})=>y(m,r,d),refetchInterval:l,placeholderData:Y});return t(n,{children:[t("p",{className:"range-group-title",children:["Added in range, where they are now"," ",e("span",{className:"muted",children:"(still stored)"})]}),s.data?t("dl",{className:"range-cells",children:[Z.map((d)=>t("div",{className:`range-cell state-${d}`,children:[e("dt",{children:z[d]}),e("dd",{children:o(s.data.counts[d])})]},d)),t("div",{className:"range-cell range-cell-total",children:[e("dt",{children:"Total"}),e("dd",{children:o(s.data.total)})]})]}):s.isError?t("p",{className:"range-note",children:["Could not load: ",s.error.message]}):e(p,{label:"Loading jobs added in the range"}),e("p",{className:"range-note",children:"Counted by when jobs were added, not finished, so it will not match Finished in range; queues that remove finished jobs show few completed."})]})}export{b as AddedByStateGroup};
2
+
3
+ //# debugId=639A51B3BD37E91E64756E2164756E21
4
+ //# sourceMappingURL=added-cbwsxv3e.js.map
@@ -6,6 +6,6 @@
6
6
  "import type { TimeRange } from \"../../analytics/range\";\nimport { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport {\n ADDED_BY_STATE_POLL_MS,\n addedByStateKey,\n getAddedByState,\n} from \"../../api/added\";\nimport { JOB_STATES } from \"../../api/contract\";\nimport { Spinner } from \"../../components/Spinner\";\nimport { useApiClient } from \"../../context\";\nimport { formatNumber, STATE_LABELS } from \"../../format\";\nimport { usePollInterval } from \"../../live\";\n\n/** Props of {@link AddedByStateGroup}. */\nexport interface AddedByStateGroupProps {\n /** The range the tile is read over (the Jobs section's). */\n range: TimeRange;\n}\n\n/**\n * The \"Over the range\" tile's second group: of the jobs **added** in the\n * range and still stored, how many are in each state now\n * (`GET /overview/added`). Rendered only where `features.addedByState` is\n * true, and polled every {@link ADDED_BY_STATE_POLL_MS}, relaxed further\n * while live, since each read counts job records.\n */\nexport function AddedByStateGroup({ range }: AddedByStateGroupProps) {\n const api = useApiClient();\n const refetchInterval = usePollInterval(ADDED_BY_STATE_POLL_MS);\n const added = useQuery({\n queryKey: addedByStateKey(range),\n queryFn: ({ signal }) => getAddedByState(api, range, signal),\n refetchInterval,\n placeholderData: keepPreviousData,\n });\n return (\n <>\n <p className=\"range-group-title\">\n Added in range, where they are now{\" \"}\n <span className=\"muted\">(still stored)</span>\n </p>\n {added.data ? (\n <dl className=\"range-cells\">\n {JOB_STATES.map((state) => (\n <div\n key={state}\n className={`range-cell state-${state}`}\n >\n <dt>{STATE_LABELS[state]}</dt>\n <dd>{formatNumber(added.data.counts[state])}</dd>\n </div>\n ))}\n <div className=\"range-cell range-cell-total\">\n <dt>Total</dt>\n <dd>{formatNumber(added.data.total)}</dd>\n </div>\n </dl>\n ) : added.isError ? (\n <p className=\"range-note\">Could not load: {added.error.message}</p>\n ) : (\n <Spinner label=\"Loading jobs added in the range\" />\n )}\n <p className=\"range-note\">\n Counted by when jobs were added, not finished, so it will not match\n Finished in range; queues that remove finished jobs show few completed.\n </p>\n </>\n );\n}\n"
7
7
  ],
8
8
  "mappings": "yDAeO,IAAM,EAAyB,MAW/B,SAAS,CAAmB,CACjC,EACA,EAAM,KAAK,IAAI,EAMf,CACA,OAAO,GAAY,EAAO,CAAG,EAUxB,SAAS,CAAe,CAAC,EAAkB,CAChD,MAAO,CACL,eACA,EAAM,OAAS,SACX,CAAE,QAAS,EAAM,OAAQ,EACzB,CAAE,KAAM,EAAM,KAAM,GAAI,EAAM,EAAG,CACvC,EAIK,SAAS,CAAe,CAC7B,EACA,EACA,EAC0B,CAC1B,IAAQ,OAAM,MAAO,EAAoB,CAAK,EAC9C,OAAO,EAAI,QAAQ,MAAO,kBAAmB,CAC3C,MAAO,CAAE,OAAM,IAAG,EAClB,QACF,CAAC,ECtCI,SAAS,CAAiB,EAAG,SAAiC,CACnE,IAAM,EAAM,EAAa,EACnB,EAAkB,EAAgB,CAAsB,EACxD,EAAQ,EAAS,CACrB,SAAU,EAAgB,CAAK,EAC/B,QAAS,EAAG,YAAa,EAAgB,EAAK,EAAO,CAAM,EAC3D,kBACA,gBAAiB,CACnB,CAAC,EACD,OACE,cA8BE,CA7BA,EAAC,IAAD,CAAG,UAAU,oBAAb,SAGE,CAHF,qCACqC,IACnC,EAAC,OAAD,CAAM,UAAU,QAAhB,0BAAwC,GACxC,EACD,EAAM,KACL,EAAC,KAAD,CAAI,UAAU,cAAd,SAcE,CAbC,EAAW,IAAI,CAAC,IACf,EAAC,MAAD,CAEE,UAAW,oBAAoB,IAFjC,SAME,CAFA,EAAC,KAAD,UAAK,EAAa,GAAS,EAC3B,EAAC,KAAD,UAAK,EAAa,EAAM,KAAK,OAAO,EAAM,EAAI,IAJzC,CAKL,CACH,EACD,EAAC,MAAD,CAAK,UAAU,8BAAf,SAGE,CAFA,EAAC,KAAD,kBAAW,EACX,EAAC,KAAD,UAAK,EAAa,EAAM,KAAK,KAAK,EAAI,GACtC,GACF,EACA,EAAM,QACR,EAAC,IAAD,CAAG,UAAU,aAAb,SAAiE,CAAjE,mBAA2C,EAAM,MAAM,SAAU,EAEjE,EAAC,EAAD,CAAS,MAAM,kCAAkC,EAEnD,EAAC,IAAD,CAAG,UAAU,aAAb,uJAGE,GACF",
9
- "debugId": "061EE75A431A544E64756E2164756E21",
9
+ "debugId": "639A51B3BD37E91E64756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -1,4 +1,4 @@
1
- import{Q,Ge,Qn,wr,xr,Gt}from"./main-47391fgm.js";import{H}from"./main-xf44bv26.js";var k=Symbol.for("@kingsleyweb/bun-client"),v=["VALIDATION","UNKNOWN_MESSAGE","UNSUPPORTED_DATA"],S=[...v,"EXCEPTION"],T=[1007,1008,[4400,4499]],P={encode:({type:e,id:t,data:n},{idField:r})=>JSON.stringify(t===void 0?{type:e,data:n}:{type:e,[r]:t,data:n}),decode(e,{idField:t}){let n=JSON.parse(typeof e==="string"?e:new TextDecoder().decode(e));if(typeof n?.type!=="string")return;let r=n[t]??(n.type==="error"?n.id:void 0);if(n.type==="error"&&"code"in n)return{...n,id:typeof r==="string"?r:void 0};return typeof r==="string"?{type:n.type,id:r,data:n.data}:{type:n.type,data:n.data}}},O=(e)=>ArrayBuffer.isView(e)||e instanceof ArrayBuffer;function x(e,t){return e.map((n,r)=>typeof n==="string"&&(!Array.isArray(t)||t.includes(r))?Uint8Array.from(atob(n),(a)=>a.charCodeAt(0)):n)}var g=(e)=>e.length>1?e:e[0];function R(e){return{encode:({type:t,id:n,data:r})=>{let a=O(r),i=r;if(a){let d=ArrayBuffer.isView(r)?new Uint8Array(r.buffer,r.byteOffset,r.byteLength):new Uint8Array(r),s="";for(let o of d)s+=String.fromCharCode(o);i=btoa(s)}return JSON.stringify({type:a?5:2,namespace:"/",...n===void 0?{}:{id:/^\d+$/.test(n)?Number(n):n},data:r===void 0?[t]:[t,i],...a?{binary:[0]}:{}})},decode(t){let n=JSON.parse(typeof t==="string"?t:new TextDecoder().decode(t)),r=n?.type;if(r===4)return{type:"error",data:n.data};if(!Array.isArray(n?.data))return;let a=r===5||r===6?x(r===5?n.data.slice(1):n.data,n.binary):r===2?n.data.slice(1):n.data;if(r===3||r===6){let s=typeof n.id==="number"||typeof n.id==="string"?String(n.id):void 0,o=s===void 0?void 0:e(s);return o===void 0?void 0:{type:o,id:s,data:g(a)}}let i=n.data[0];if(r!==2&&r!==5||typeof i!=="string")return;let d=g(a);if(i==="exception"&&d!==void 0){let{ackId:s,...o}=d,l=typeof s==="number"||typeof s==="string"?String(s):void 0;if(typeof o.status==="number"&&v.includes(o.code))return{...o,type:"error",...l===void 0?{}:{id:l}};if(l!==void 0&&e(l)!==void 0)return{type:"error",id:l,code:"EXCEPTION",status:typeof o.status==="number"?o.status:500,...typeof o.message==="string"?{detail:o.message}:{}}}return{type:i,data:d}}}}var A,p=new Map;function M(e){if(e.mode!==void 0)return e.mode==="development";try{return!1}catch{return!0}}function m(e){return typeof e==="object"&&Object.entries(e).some(([t,n])=>t!=="onFailure"&&n!==!1&&n!==void 0)}var C=globalThis.requestAnimationFrame;function F(){return new Promise((e)=>typeof C==="function"?C(()=>e()):setTimeout(e,16))}class b{key;url;facts;options;logger;ws;state="connecting";handles=new Set;pending=new Map;resend=[];buffer=[];#d=0;#t=0;#e={reconnect:void 0,stable:void 0,watchdog:void 0,ping:void 0};codec;envelope;constructor(e,t,n,r,a){this.key=e;this.url=t;this.facts=n;this.options=r;this.logger=a;let i=r.codec??n.codec;this.codec=i==="bun-nest"?R((d)=>this.pending.get(d)?.reply):i===void 0||i==="bun-common"?P:i,this.envelope={idField:n.idField},this.#r()}#r(){let e=new WebSocket(this.url,this.facts.subprotocol===void 0?[]:[this.facts.subprotocol]);e.binaryType="arraybuffer",this.ws=e,e.onopen=()=>void this.#i(e),e.onmessage=(t)=>this.#o(e,t.data),e.onclose=(t)=>this.#a(e,t.code,t.reason)}#l(e){if(this.state===e)return;this.state=e;for(let t of this.handles)t.emit("$state",e)}async#i(e){if(e!==this.ws)return;this.#l("open"),this.#s();let t=this.options.reconnect===!1?0:this.options.reconnect?.stableAfterMs??60000;this.#e.stable=setTimeout(()=>{this.#t=0},t);for(let n of[...this.handles])try{await n.options.resubscribe?.(n.client)}catch(r){n.logger.error(`${this.facts.path}: resubscribe threw`,r)}if(e!==this.ws||e.readyState!==1)return;for(let n of this.resend.splice(0))this.pending.set(n.id,n),e.send(n.payload);for(let n of this.buffer.splice(0))e.send(n)}#s(){let{heartbeat:e,ping:t}=this.options;if(clearTimeout(this.#e.watchdog),clearTimeout(this.#e.ping),e!==void 0)this.#e.watchdog=setTimeout(()=>this.#n(),e.intervalMs*2+(e.slackMs??5000));if(t!==void 0){let n=()=>{if(this.ws?.readyState===1)this.ws.send(this.codec.encode({type:t.type},this.envelope));this.#e.ping=setTimeout(n,t.intervalMs)};this.#e.ping=setTimeout(n,t.intervalMs)}}#n(){let e=this.ws;if(e===void 0)return;e.onclose=null,e.close(4000,"heartbeat lost"),this.#a(e,4000,"heartbeat lost")}#o(e,t){if(e!==this.ws)return;this.#s();let n;try{n=this.codec.decode(t,this.envelope)}catch(i){this.logger.warn(`${this.facts.path}: a frame could not be decoded`,i);return}if(n===void 0)return;let r=typeof n.id==="string"?n.id:void 0,a=r===void 0?void 0:this.pending.get(r);if(n.type==="error"&&"code"in n){let{code:i,status:d,issues:s,detail:o}=n;if(!S.includes(i)||typeof d!=="number"||s!==void 0&&!Array.isArray(s)||o!==void 0&&typeof o!=="string"){this.logger.warn(`${this.facts.path}: an error frame does not have the error frame's shape; ignored`);return}if(a!==void 0){this.pending.delete(r),a.reject(new Qn(this.facts.path,n,a.type));return}let l=new Qn(this.facts.path,n);for(let c of this.handles)if(!c.emit("$error",l))c.logger.warn(l.message);return}if(a!==void 0&&a.reply===n.type){this.pending.delete(r),a.handle.reply(a,n.data);return}for(let i of this.handles)i.push(n)}#a(e,t,n){if(e!==this.ws)return;this.ws=void 0;for(let s of Object.values(this.#e))clearTimeout(s);for(let[s,o]of this.pending)if(this.pending.delete(s),o.retry)this.resend.push(o);else o.reject(new xr(this.facts.path,t,n));if(this.state==="closed")return;let r=(this.options.fatalCloseCodes??T).some((s)=>typeof s==="number"?s===t:t>=s[0]&&t<=s[1]);if(this.options.reconnect===!1||r){this.end(new Gt(this.facts.path,n||"the server closed the channel",t));return}let{initialMs:a=500,maxMs:i=30000}=this.options.reconnect??{},d=Math.min(i,a*2**this.#t++);this.#l(this.state==="connecting"?"connecting":"reconnecting"),this.#e.reconnect=setTimeout(()=>this.#r(),d/2+d/2*Math.random())}end(e){p.delete(this.key);for(let r of Object.values(this.#e))clearTimeout(r);let t=e??new Gt(this.facts.path,"the channel is closed");for(let r of[...this.pending.values(),...this.resend])r.reject(t);this.buffer.length=0;let n=this.ws;this.ws=void 0,n?.close(1000);for(let r of[...this.handles])r.ended(t,e!==void 0);this.state="closed"}write(e){if(this.ws?.readyState===1){this.ws.send(e);return}let t=this.options.offline??{buffer:100};if(t==="fail"||this.buffer.length>=t.buffer)throw new Gt(this.facts.path,t==="fail"?'disconnected (offline: "fail")':`the offline buffer is full (${t.buffer} messages)`);this.buffer.push(e)}request(e,t,n,r,a,i){let d=this.facts.replies[t];if(d===void 0)throw new Q(`${this.facts.path}: "${t}" has no reply declared (replies)`,{key:this.facts.path});let s=String(++this.#d),o=r.timeout??e.timeout,l,c={id:s,handle:e,type:t,reply:d,payload:this.codec.encode({type:t,id:s,data:n},this.envelope),retry:r.retry===!0,resolve:a,reject:i},h=(u)=>(E)=>{clearTimeout(l),this.pending.delete(s);let y=this.resend.indexOf(c);if(y!==-1)this.resend.splice(y,1);e.requests.delete(c),u(E)};if(c.resolve=h(a),c.reject=h(i),this.write(c.payload),this.pending.set(s,c),e.requests.add(c),o!==!1)l=setTimeout(()=>c.reject(new wr(this.facts.path,t,s,o)),o)}}var f=Symbol("dropped");class w{conn;facts;options;config;client;#d=new Map;#t=new Set;requests=new Set;#e;#r;#l=Promise.resolve();#i=0;#s=Promise.resolve();#n;timeout;logger;constructor(e,t,n,r){this.conn=e;this.facts=t;this.options=n;this.config=r;this.timeout=r.timeout??30000,this.logger=r.logger??console;let a=M(r),i=t.contract!==void 0||r.schemas!==void 0;if(r.validate!==!1&&n.validate!==!1&&(i&&(a||m(r.validate))||m(n.validate))){let s=r.schemas;this.#r=(A??=(H("1epbhv9a"),import("./channelChecks-1epbhv9a.js"))).then((o)=>o.createChannelChecks({key:t.path,development:a,levels:[r.validate,n.validate],contract:t.contract?.contract,meta:t,schemas:t.contract!==void 0||s===void 0?void 0:async()=>(typeof s==="function"?await s():s).channelSchemas?.[t.path],ValidationError:Ge})).then((o)=>this.#e=o,(o)=>{this.logger.error(`${t.path}: the channel's checks failed to load; nothing is checked`,o);return})}let d=()=>this.#n===void 0?e.state:"closed";this.client={send:(s,o,l)=>{this.#o(),this.#a(s,o,l?.validate,(c)=>e.write(e.codec.encode({type:s,data:c},e.envelope)))?.catch((c)=>this.#c(c))},request:(s,o,l={})=>new Promise((c,h)=>{this.#o(),this.#a(s,o,l.validate,(u)=>e.request(this,s,u,l,c,h))?.catch(h)}),sendWhenReady:async(s,o,l={})=>{while(this.#o()&&(e.ws?.bufferedAmount??0)>=(l.highWaterMark??1048576))await F();await this.#a(s,o,l.validate,(c)=>e.write(e.codec.encode({type:s,data:c},e.envelope)))},on:(s,o)=>{let l=this.#d.get(s);if(l===void 0)this.#d.set(s,l=new Set);return l.add(o),()=>void l.delete(o)},messages:(s)=>this.#f(s),close:()=>this.#p(),get state(){return d()},get bufferedAmount(){return e.ws?.bufferedAmount??0}}}#o(){if(this.#n!==void 0)throw this.#n;return!0}#a(e,t,n,r){if(this.#r===void 0){r(t);return}let a=this.#e;if(a!==void 0&&this.#i===0){let d=a.send(e,t,n);if(!(d instanceof Promise)){r(this.#h(a,d));return}}this.#i++;let i=this.#l.then(async()=>{let d=await this.#r;if(this.#o(),d===void 0)return r(t);r(this.#h(d,await d.send(e,t,n)))});return this.#l=i.then(()=>void this.#i--,()=>void this.#i--),i}#h(e,t){if(t.error!==void 0){let n=e.onSend;if(n==="throw")throw t.error;if(n==="warn")this.logger.warn(t.error.message);else n(t.error)}return t.value}async#u(e,t,n){let r=this.#e??await this.#r;if(r===void 0)return t;let a=await r.receive(e,t),i=a.error;if(i===void 0)return a.value;let d=r.onReceive;if(d==="warn")return this.logger.warn(i.message),a.value;if(d==="drop")return this.emit("$invalid",i),n?.(i),f;try{if(d==="throw")throw i;return d(i),a.value}catch(s){if(n===void 0)this.#c(s);else n(s);return f}}reply(e,t){this.#s=this.#s.then(async()=>{let n=await this.#u(e.reply,t,e.reject);if(n!==f)e.resolve(n)})}push(e){if(this.#n!==void 0)return;this.#s=this.#s.then(async()=>{let t=await this.#u(e.type,e.data);if(t===f||this.#n!==void 0)return;let n=e.id===void 0?{type:e.type}:{type:e.type,id:e.id};this.emit(e.type,t,n);for(let r of this.#t)if(r.type===void 0)r.push({type:e.type,data:t});else if(r.type===e.type)r.push(t)})}emit(e,...t){let n=this.#d.get(e);for(let r of n??[])try{r(...t)}catch(a){this.logger.error(`${this.facts.path}: a "${e}" listener threw`,a)}return n!==void 0&&n.size>0}#c(e){if(!this.emit("$error",e))this.logger.warn(e.message??e)}#f(e){let t=[],n=this.#n!==void 0,r,a={type:e,push:(d)=>{t.push(d),r?.()},end:()=>{n=!0,r?.()}};if(!n)this.#t.add(a);let i={[Symbol.asyncIterator]:()=>i,next:async()=>{for(;;){if(t.length>0)return{value:t.shift(),done:!1};if(n)return{value:void 0,done:!0};await new Promise((d)=>r=d),r=void 0}},return:()=>(this.#t.delete(a),a.end(),t.length=0,Promise.resolve({value:void 0,done:!0}))};return i}#p(){if(this.#n!==void 0)return;if(this.ended(new Gt(this.facts.path,"the channel is closed"),!1),this.conn.handles.size===0)this.conn.end()}ended(e,t){if(this.#n!==void 0)return;this.#n=e,this.conn.handles.delete(this);for(let n of[...this.requests])n.reject(e);for(let n of this.#t)n.end();if(this.#t.clear(),t)this.#c(e);this.emit("$state","closed")}}function q(e,t,n){let r=n??{},a=e[k]??{},i;if("spec"in t){let{meta:c,spec:h}=t;for(let u of[...Object.keys(h.receives),...Object.keys(h.sends)])if(u.startsWith("$"))throw TypeError(`channel(): the message type "${u}" starts with "$", which the client's events use`);i={path:c.path,idField:c.envelope.idField,replies:c.replies,subprotocol:c.subprotocol,contract:t,codec:void 0}}else i={path:t.path,idField:t.idField??"id",replies:t.replies??{},subprotocol:t.subprotocol,contract:void 0,codec:t.codec};let d=e.url(i.path,{params:r.params,query:r.query});if(!/^[a-z][a-z\d+.-]*:/i.test(d)){let c=globalThis.location?.href;if(!c)throw new Q(`${i.path}: "${d}" is relative, and there is no page to resolve it against; give the client an absolute baseUrl`,{key:i.path});d=new URL(d,c).href}d=d.replace(/^http(s?):/i,"ws$1:");let s=`${d} ${i.subprotocol??""}`,o=p.get(s);if(o===void 0)o=new b(s,d,i,r,a.logger??console),p.set(s,o);let l=new w(o,i,r,a);if(o.handles.add(l),o.state==="open")(async()=>r.resubscribe?.(l.client))().catch((c)=>l.logger.error(`${i.path}: resubscribe threw`,c));return l.client}export{q as channel};
1
+ import{j,Qe,jn,Cr,Lr,Qt}from"./main-jxre7w93.js";import{G}from"./main-a250ffk7.js";var k=Symbol.for("@kingsleyweb/bun-client"),v=["VALIDATION","UNKNOWN_MESSAGE","UNSUPPORTED_DATA"],S=[...v,"EXCEPTION"],T=[1007,1008,[4400,4499]],P={encode:({type:e,id:t,data:n},{idField:r})=>JSON.stringify(t===void 0?{type:e,data:n}:{type:e,[r]:t,data:n}),decode(e,{idField:t}){let n=JSON.parse(typeof e==="string"?e:new TextDecoder().decode(e));if(typeof n?.type!=="string")return;let r=n[t]??(n.type==="error"?n.id:void 0);if(n.type==="error"&&"code"in n)return{...n,id:typeof r==="string"?r:void 0};return typeof r==="string"?{type:n.type,id:r,data:n.data}:{type:n.type,data:n.data}}},O=(e)=>ArrayBuffer.isView(e)||e instanceof ArrayBuffer;function x(e,t){return e.map((n,r)=>typeof n==="string"&&(!Array.isArray(t)||t.includes(r))?Uint8Array.from(atob(n),(a)=>a.charCodeAt(0)):n)}var g=(e)=>e.length>1?e:e[0];function R(e){return{encode:({type:t,id:n,data:r})=>{let a=O(r),i=r;if(a){let d=ArrayBuffer.isView(r)?new Uint8Array(r.buffer,r.byteOffset,r.byteLength):new Uint8Array(r),s="";for(let o of d)s+=String.fromCharCode(o);i=btoa(s)}return JSON.stringify({type:a?5:2,namespace:"/",...n===void 0?{}:{id:/^\d+$/.test(n)?Number(n):n},data:r===void 0?[t]:[t,i],...a?{binary:[0]}:{}})},decode(t){let n=JSON.parse(typeof t==="string"?t:new TextDecoder().decode(t)),r=n?.type;if(r===4)return{type:"error",data:n.data};if(!Array.isArray(n?.data))return;let a=r===5||r===6?x(r===5?n.data.slice(1):n.data,n.binary):r===2?n.data.slice(1):n.data;if(r===3||r===6){let s=typeof n.id==="number"||typeof n.id==="string"?String(n.id):void 0,o=s===void 0?void 0:e(s);return o===void 0?void 0:{type:o,id:s,data:g(a)}}let i=n.data[0];if(r!==2&&r!==5||typeof i!=="string")return;let d=g(a);if(i==="exception"&&d!==void 0){let{ackId:s,...o}=d,l=typeof s==="number"||typeof s==="string"?String(s):void 0;if(typeof o.status==="number"&&v.includes(o.code))return{...o,type:"error",...l===void 0?{}:{id:l}};if(l!==void 0&&e(l)!==void 0)return{type:"error",id:l,code:"EXCEPTION",status:typeof o.status==="number"?o.status:500,...typeof o.message==="string"?{detail:o.message}:{}}}return{type:i,data:d}}}}var A,p=new Map;function M(e){if(e.mode!==void 0)return e.mode==="development";try{return!1}catch{return!0}}function m(e){return typeof e==="object"&&Object.entries(e).some(([t,n])=>t!=="onFailure"&&n!==!1&&n!==void 0)}var C=globalThis.requestAnimationFrame;function F(){return new Promise((e)=>typeof C==="function"?C(()=>e()):setTimeout(e,16))}class b{key;url;facts;options;logger;ws;state="connecting";handles=new Set;pending=new Map;resend=[];buffer=[];#d=0;#t=0;#e={reconnect:void 0,stable:void 0,watchdog:void 0,ping:void 0};codec;envelope;constructor(e,t,n,r,a){this.key=e;this.url=t;this.facts=n;this.options=r;this.logger=a;let i=r.codec??n.codec;this.codec=i==="bun-nest"?R((d)=>this.pending.get(d)?.reply):i===void 0||i==="bun-common"?P:i,this.envelope={idField:n.idField},this.#r()}#r(){let e=new WebSocket(this.url,this.facts.subprotocol===void 0?[]:[this.facts.subprotocol]);e.binaryType="arraybuffer",this.ws=e,e.onopen=()=>void this.#i(e),e.onmessage=(t)=>this.#o(e,t.data),e.onclose=(t)=>this.#a(e,t.code,t.reason)}#l(e){if(this.state===e)return;this.state=e;for(let t of this.handles)t.emit("$state",e)}async#i(e){if(e!==this.ws)return;this.#l("open"),this.#s();let t=this.options.reconnect===!1?0:this.options.reconnect?.stableAfterMs??60000;this.#e.stable=setTimeout(()=>{this.#t=0},t);for(let n of[...this.handles])try{await n.options.resubscribe?.(n.client)}catch(r){n.logger.error(`${this.facts.path}: resubscribe threw`,r)}if(e!==this.ws||e.readyState!==1)return;for(let n of this.resend.splice(0))this.pending.set(n.id,n),e.send(n.payload);for(let n of this.buffer.splice(0))e.send(n)}#s(){let{heartbeat:e,ping:t}=this.options;if(clearTimeout(this.#e.watchdog),clearTimeout(this.#e.ping),e!==void 0)this.#e.watchdog=setTimeout(()=>this.#n(),e.intervalMs*2+(e.slackMs??5000));if(t!==void 0){let n=()=>{if(this.ws?.readyState===1)this.ws.send(this.codec.encode({type:t.type},this.envelope));this.#e.ping=setTimeout(n,t.intervalMs)};this.#e.ping=setTimeout(n,t.intervalMs)}}#n(){let e=this.ws;if(e===void 0)return;e.onclose=null,e.close(4000,"heartbeat lost"),this.#a(e,4000,"heartbeat lost")}#o(e,t){if(e!==this.ws)return;this.#s();let n;try{n=this.codec.decode(t,this.envelope)}catch(i){this.logger.warn(`${this.facts.path}: a frame could not be decoded`,i);return}if(n===void 0)return;let r=typeof n.id==="string"?n.id:void 0,a=r===void 0?void 0:this.pending.get(r);if(n.type==="error"&&"code"in n){let{code:i,status:d,issues:s,detail:o}=n;if(!S.includes(i)||typeof d!=="number"||s!==void 0&&!Array.isArray(s)||o!==void 0&&typeof o!=="string"){this.logger.warn(`${this.facts.path}: an error frame does not have the error frame's shape; ignored`);return}if(a!==void 0){this.pending.delete(r),a.reject(new jn(this.facts.path,n,a.type));return}let l=new jn(this.facts.path,n);for(let c of this.handles)if(!c.emit("$error",l))c.logger.warn(l.message);return}if(a!==void 0&&a.reply===n.type){this.pending.delete(r),a.handle.reply(a,n.data);return}for(let i of this.handles)i.push(n)}#a(e,t,n){if(e!==this.ws)return;this.ws=void 0;for(let s of Object.values(this.#e))clearTimeout(s);for(let[s,o]of this.pending)if(this.pending.delete(s),o.retry)this.resend.push(o);else o.reject(new Lr(this.facts.path,t,n));if(this.state==="closed")return;let r=(this.options.fatalCloseCodes??T).some((s)=>typeof s==="number"?s===t:t>=s[0]&&t<=s[1]);if(this.options.reconnect===!1||r){this.end(new Qt(this.facts.path,n||"the server closed the channel",t));return}let{initialMs:a=500,maxMs:i=30000}=this.options.reconnect??{},d=Math.min(i,a*2**this.#t++);this.#l(this.state==="connecting"?"connecting":"reconnecting"),this.#e.reconnect=setTimeout(()=>this.#r(),d/2+d/2*Math.random())}end(e){p.delete(this.key);for(let r of Object.values(this.#e))clearTimeout(r);let t=e??new Qt(this.facts.path,"the channel is closed");for(let r of[...this.pending.values(),...this.resend])r.reject(t);this.buffer.length=0;let n=this.ws;this.ws=void 0,n?.close(1000);for(let r of[...this.handles])r.ended(t,e!==void 0);this.state="closed"}write(e){if(this.ws?.readyState===1){this.ws.send(e);return}let t=this.options.offline??{buffer:100};if(t==="fail"||this.buffer.length>=t.buffer)throw new Qt(this.facts.path,t==="fail"?'disconnected (offline: "fail")':`the offline buffer is full (${t.buffer} messages)`);this.buffer.push(e)}request(e,t,n,r,a,i){let d=this.facts.replies[t];if(d===void 0)throw new j(`${this.facts.path}: "${t}" has no reply declared (replies)`,{key:this.facts.path});let s=String(++this.#d),o=r.timeout??e.timeout,l,c={id:s,handle:e,type:t,reply:d,payload:this.codec.encode({type:t,id:s,data:n},this.envelope),retry:r.retry===!0,resolve:a,reject:i},h=(u)=>(E)=>{clearTimeout(l),this.pending.delete(s);let y=this.resend.indexOf(c);if(y!==-1)this.resend.splice(y,1);e.requests.delete(c),u(E)};if(c.resolve=h(a),c.reject=h(i),this.write(c.payload),this.pending.set(s,c),e.requests.add(c),o!==!1)l=setTimeout(()=>c.reject(new Cr(this.facts.path,t,s,o)),o)}}var f=Symbol("dropped");class w{conn;facts;options;config;client;#d=new Map;#t=new Set;requests=new Set;#e;#r;#l=Promise.resolve();#i=0;#s=Promise.resolve();#n;timeout;logger;constructor(e,t,n,r){this.conn=e;this.facts=t;this.options=n;this.config=r;this.timeout=r.timeout??30000,this.logger=r.logger??console;let a=M(r),i=t.contract!==void 0||r.schemas!==void 0;if(r.validate!==!1&&n.validate!==!1&&(i&&(a||m(r.validate))||m(n.validate))){let s=r.schemas;this.#r=(A??=(G("s4yz4yhw"),import("./channelChecks-s4yz4yhw.js"))).then((o)=>o.createChannelChecks({key:t.path,development:a,levels:[r.validate,n.validate],contract:t.contract?.contract,meta:t,schemas:t.contract!==void 0||s===void 0?void 0:async()=>(typeof s==="function"?await s():s).channelSchemas?.[t.path],ValidationError:Qe})).then((o)=>this.#e=o,(o)=>{this.logger.error(`${t.path}: the channel's checks failed to load; nothing is checked`,o);return})}let d=()=>this.#n===void 0?e.state:"closed";this.client={send:(s,o,l)=>{this.#o(),this.#a(s,o,l?.validate,(c)=>e.write(e.codec.encode({type:s,data:c},e.envelope)))?.catch((c)=>this.#c(c))},request:(s,o,l={})=>new Promise((c,h)=>{this.#o(),this.#a(s,o,l.validate,(u)=>e.request(this,s,u,l,c,h))?.catch(h)}),sendWhenReady:async(s,o,l={})=>{while(this.#o()&&(e.ws?.bufferedAmount??0)>=(l.highWaterMark??1048576))await F();await this.#a(s,o,l.validate,(c)=>e.write(e.codec.encode({type:s,data:c},e.envelope)))},on:(s,o)=>{let l=this.#d.get(s);if(l===void 0)this.#d.set(s,l=new Set);return l.add(o),()=>void l.delete(o)},messages:(s)=>this.#f(s),close:()=>this.#p(),get state(){return d()},get bufferedAmount(){return e.ws?.bufferedAmount??0}}}#o(){if(this.#n!==void 0)throw this.#n;return!0}#a(e,t,n,r){if(this.#r===void 0){r(t);return}let a=this.#e;if(a!==void 0&&this.#i===0){let d=a.send(e,t,n);if(!(d instanceof Promise)){r(this.#h(a,d));return}}this.#i++;let i=this.#l.then(async()=>{let d=await this.#r;if(this.#o(),d===void 0)return r(t);r(this.#h(d,await d.send(e,t,n)))});return this.#l=i.then(()=>void this.#i--,()=>void this.#i--),i}#h(e,t){if(t.error!==void 0){let n=e.onSend;if(n==="throw")throw t.error;if(n==="warn")this.logger.warn(t.error.message);else n(t.error)}return t.value}async#u(e,t,n){let r=this.#e??await this.#r;if(r===void 0)return t;let a=await r.receive(e,t),i=a.error;if(i===void 0)return a.value;let d=r.onReceive;if(d==="warn")return this.logger.warn(i.message),a.value;if(d==="drop")return this.emit("$invalid",i),n?.(i),f;try{if(d==="throw")throw i;return d(i),a.value}catch(s){if(n===void 0)this.#c(s);else n(s);return f}}reply(e,t){this.#s=this.#s.then(async()=>{let n=await this.#u(e.reply,t,e.reject);if(n!==f)e.resolve(n)})}push(e){if(this.#n!==void 0)return;this.#s=this.#s.then(async()=>{let t=await this.#u(e.type,e.data);if(t===f||this.#n!==void 0)return;let n=e.id===void 0?{type:e.type}:{type:e.type,id:e.id};this.emit(e.type,t,n);for(let r of this.#t)if(r.type===void 0)r.push({type:e.type,data:t});else if(r.type===e.type)r.push(t)})}emit(e,...t){let n=this.#d.get(e);for(let r of n??[])try{r(...t)}catch(a){this.logger.error(`${this.facts.path}: a "${e}" listener threw`,a)}return n!==void 0&&n.size>0}#c(e){if(!this.emit("$error",e))this.logger.warn(e.message??e)}#f(e){let t=[],n=this.#n!==void 0,r,a={type:e,push:(d)=>{t.push(d),r?.()},end:()=>{n=!0,r?.()}};if(!n)this.#t.add(a);let i={[Symbol.asyncIterator]:()=>i,next:async()=>{for(;;){if(t.length>0)return{value:t.shift(),done:!1};if(n)return{value:void 0,done:!0};await new Promise((d)=>r=d),r=void 0}},return:()=>(this.#t.delete(a),a.end(),t.length=0,Promise.resolve({value:void 0,done:!0}))};return i}#p(){if(this.#n!==void 0)return;if(this.ended(new Qt(this.facts.path,"the channel is closed"),!1),this.conn.handles.size===0)this.conn.end()}ended(e,t){if(this.#n!==void 0)return;this.#n=e,this.conn.handles.delete(this);for(let n of[...this.requests])n.reject(e);for(let n of this.#t)n.end();if(this.#t.clear(),t)this.#c(e);this.emit("$state","closed")}}function q(e,t,n){let r=n??{},a=e[k]??{},i;if("spec"in t){let{meta:c,spec:h}=t;for(let u of[...Object.keys(h.receives),...Object.keys(h.sends)])if(u.startsWith("$"))throw TypeError(`channel(): the message type "${u}" starts with "$", which the client's events use`);i={path:c.path,idField:c.envelope.idField,replies:c.replies,subprotocol:c.subprotocol,contract:t,codec:void 0}}else i={path:t.path,idField:t.idField??"id",replies:t.replies??{},subprotocol:t.subprotocol,contract:void 0,codec:t.codec};let d=e.url(i.path,{params:r.params,query:r.query});if(!/^[a-z][a-z\d+.-]*:/i.test(d)){let c=globalThis.location?.href;if(!c)throw new j(`${i.path}: "${d}" is relative, and there is no page to resolve it against; give the client an absolute baseUrl`,{key:i.path});d=new URL(d,c).href}d=d.replace(/^http(s?):/i,"ws$1:");let s=`${d} ${i.subprotocol??""}`,o=p.get(s);if(o===void 0)o=new b(s,d,i,r,a.logger??console),p.set(s,o);let l=new w(o,i,r,a);if(o.handles.add(l),o.state==="open")(async()=>r.resubscribe?.(l.client))().catch((c)=>l.logger.error(`${i.path}: resubscribe threw`,c));return l.client}export{q as channel};
2
2
 
3
- //# debugId=01A7BCB0782A1A9164756E2164756E21
4
- //# sourceMappingURL=channel-a5chwqet.js.map
3
+ //# debugId=8BB5707F752ACABE64756E2164756E21
4
+ //# sourceMappingURL=channel-tgkvqm8y.js.map
@@ -5,6 +5,6 @@
5
5
  "/**\n * `@kingsleyweb/bun-client/channel`: the typed WebSocket channel client\n * (typed-client plan, `websocket.md`, PR-W2).\n *\n * ```ts\n * import { channel } from \"@kingsleyweb/bun-client/channel\";\n * import { chat } from \"../shared/chat.contract\"; // channelContract({ … }), browser-safe\n *\n * const room = channel(api, chat, { params: { room: \"lobby\" } });\n * room.send(\"say\", { text: \"hi\" }); // typed by chat.spec.receives.say\n * const said = await room.request(\"say\", { text: \"hi\" }); // typed by sends[replies.say]\n * const off = room.on(\"said\", (m) => console.log(m.by, m.text));\n * for await (const m of room.messages(\"someoneTyping\")) … // ends on close()\n * room.close();\n * ```\n *\n * - **Types** (W-D1, §3): a `channelContract()` result is inferred (the\n * client sends each message's wire input, `WireInput`, and receives it\n * after JSON, `Jsonify`); a generated descriptor (`bun-client generate\n * --asyncapi`, `channels[\"/rooms/:room\"]`) carries the generated\n * `Channels` entry, the same shape.\n * - **One socket per URL** (§4.1): a second `channel()` on the same URL\n * shares the first one's socket and its transport options (reconnection,\n * heartbeat, ping, offline buffer, codec), refcounted; it closes when the\n * last handle does.\n * - **Requests** (§4.2): `request(type, data)` stamps the envelope's id\n * field with a counter per socket and resolves with the reply type's frame\n * carrying the same id; an error frame with that id rejects with\n * {@link ChannelError}. Default timeout: the client's `timeout` (30 s). A\n * drop rejects every waiting request with {@link DisconnectedError},\n * unless the call set `retry: true` (it is sent again after the reconnect).\n * - **Validation** (§5, TC §5.6): a send through the contract's sender guard\n * (before anything is written; a failure throws `ValidationError`), a\n * received frame through its receiver guard (a failure warns and delivers;\n * `\"drop\"` discards it and fires `$invalid`; `\"throw\"` rejects a waiting\n * request), on in development and off in production, BYO per message\n * type. The checks load lazily (`./channelChecks`); a send made before they\n * loaded (or checked by an asynchronous schema) is checked in order once\n * they have, and its failure, which can no longer throw to the caller,\n * reaches `on(\"$error\")`. The error frame is always checked for its fixed\n * shape.\n * - **Reconnection** (§7): 500 ms doubling to 30 s, half-jittered, the count\n * reset once a socket stays open 60 s; never after a close code in\n * `fatalCloseCodes` (1007, 1008, 4400–4499). A declared heartbeat arms a\n * watchdog (2 × its interval + 5 s of silence closes with 4000 and\n * reconnects); a declared ping is sent after `intervalMs` of silence.\n * `resubscribe(room)` runs after every open, before the offline buffer\n * (100 messages by default) is flushed in order.\n * - **Backpressure** (§8): `bufferedAmount`, and `sendWhenReady()`, which\n * waits (an animation frame at a time, 16 ms outside a browser) until the\n * socket's buffer is under `highWaterMark` (1 MiB).\n * - **Codecs** (W-D7, §11): `\"bun-common\"` (the default), `\"bun-nest\"` (a\n * bun-nest gateway: `request()` resolves with the gateway's ack, typed by\n * its `@ValidateMessage` `ack` schema), or a custom `{ encode, decode }`.\n *\n * Browser-safe; it imports nothing of the `.` entry's client (it reads the\n * client's configuration through the client object, as `./sse` does), so an\n * HTTP-only bundle carries none of it.\n */\nimport type {\n ChannelContract,\n ChannelErrorCode,\n ChannelErrorFrame,\n ChannelMessage,\n ChannelSpec,\n StandardSchemaV1,\n WireInput,\n} from \"@kingsleyweb/bun-contract\";\nimport type { ChannelChecks, Verdict } from \"./channelChecks\";\nimport type { Jsonify } from \"./jsonify\";\nimport type { ClientConfig, FailureMode, PartValidation } from \"./types\";\nimport type { ChannelFailureCode } from \"./errors\";\nimport {\n ChannelClosedError,\n ChannelError,\n ClientError,\n DisconnectedError,\n RequestTimeoutError,\n ValidationError,\n} from \"./errors\";\n\nexport {\n ChannelClosedError,\n ChannelError,\n DisconnectedError,\n RequestTimeoutError,\n} from \"./errors\";\n\n/* ------------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------------ */\n\n/**\n * One channel's types, the generated `Channels` map's entry shape (and what\n * a `channelContract()` spec is read as, {@link ChannelEntryOf}). The sides\n * keep the server's names: the client sends what the server `receives` and\n * receives what it `sends` (§3.3).\n */\nexport interface ChannelEntry {\n /** The path params, as the URL takes them. */\n params?: object;\n /** The upgrade's query, when the channel declares one. */\n query?: object;\n /** Client → server: each message type to its data's wire type; `undefined` for a message with no data. */\n receives: object;\n /** Server → client: each message type to its data's wire type (the client sees it after JSON). */\n sends: object;\n /** A received type to the sent type answering it, where one does. */\n replies?: object;\n}\n\n/** A message map's entry as the wire carries it: the schema's `WireInput`, `undefined` for `null`. */\ntype DataOf<D> = D extends StandardSchemaV1 ? WireInput<D> : undefined;\n\n/** A message map's types. */\ntype MessagesOf<M> = { -readonly [T in keyof M]: DataOf<M[T]> };\n\n/** A path param's name, up to the first character a name does not take. */\ntype NameOf<S extends string> =\n S extends `${infer N}${\".\" | \"-\" | \"{\" | \"}\" | \"(\" | \"?\"}${string}`\n ? NameOf<N>\n : S;\n\n/** The params of a path literal (`/rooms/:room` gives `{ room: string }`; `*rest` a `string[]`). */\ntype PathParamsOf<P extends string> =\n P extends `${string}/${infer Segment}/${infer Rest}`\n ? PathParamsOf<`/${Segment}`> & PathParamsOf<`/${Rest}`>\n : P extends `${string}/:${infer Name}`\n ? { [K in NameOf<Name>]: string }\n : P extends `${string}/*${infer Name}`\n ? { [K in NameOf<Name>]: string[] }\n : Record<never, never>;\n\n/** Flattens an intersection. */\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n/** A declared upgrade part's wire input, else `F`. */\ntype RequestPart<S, P extends string, F> = S extends {\n readonly request: { readonly [K in P]: infer X extends StandardSchemaV1 };\n}\n ? WireInput<X>\n : F;\n\n/**\n * A `channelContract()` spec read as a {@link ChannelEntry}: each message's\n * `WireInput` (`undefined` for a `null` one), the params from the upgrade's\n * `params` schema or else the path literal, the query when one is declared,\n * and the replies.\n */\nexport type ChannelEntryOf<S extends ChannelSpec> = Simplify<\n {\n params: RequestPart<S, \"params\", Simplify<PathParamsOf<S[\"path\"]>>>;\n receives: MessagesOf<S[\"receives\"]>;\n sends: MessagesOf<S[\"sends\"]>;\n replies: S extends { readonly replies: infer R }\n ? { -readonly [K in keyof R]: R[K] }\n : Record<never, never>;\n } & (S extends { readonly request: { readonly query: StandardSchemaV1 } }\n ? { query: RequestPart<S, \"query\", never> }\n : Record<never, never>)\n>;\n\n/**\n * A generated channel's runtime facts (`bun-client generate --asyncapi`\n * writes one per channel, `channels[\"/rooms/:room\"]`), typed by its\n * `Channels` entry: what {@link channel} takes in place of a contract.\n */\nexport interface ChannelDescriptor<E extends ChannelEntry = ChannelEntry> {\n /** The route path the channel is served on. */\n readonly path: string;\n /** The envelope's correlation field. Default `\"id\"`. */\n readonly idField?: string;\n /** A received type to the sent type answering it. Default: none. */\n readonly replies?: Readonly<Record<string, string>>;\n /** The subprotocol the upgrade answers with, offered on connect. Default: none. */\n readonly subprotocol?: string;\n /**\n * The envelope the server speaks, used when `channel()`'s options name\n * none: `\"bun-nest\"` for a document `gatewayAsyncApi()` wrote. Default\n * `\"bun-common\"`.\n */\n readonly codec?: \"bun-common\" | \"bun-nest\";\n /** The entry's type, carried for inference only (never set). */\n readonly \"~entry\"?: E;\n}\n\n/** A generated `channels` object's type: a {@link ChannelDescriptor} per `Channels` entry. */\nexport type ChannelDescriptors<C> = {\n readonly [K in keyof C]: ChannelDescriptor<C[K] & ChannelEntry>;\n};\n\n/** The keys of `T` whose property is required. */\ntype RequiredKeys<T> = {\n [K in keyof T]-?: Record<never, never> extends Pick<T, K> ? never : K;\n}[keyof T];\n\n/** `{ [K]: T }`: required when `T` has a required key, else optional (`Record<string, never>` for none). */\ntype Part<K extends string, T> = [keyof T] extends [never]\n ? { [P in K]?: Record<string, never> }\n : [RequiredKeys<T>] extends [never]\n ? { [P in K]?: T }\n : { [P in K]: T };\n\n/** The URL's typed parts for entry `E`. */\ntype UrlParts<E> = Part<\n \"params\",\n E extends { params: infer P } ? P : Record<never, never>\n> &\n (E extends { query: infer Q } ? Part<\"query\", Q> : { query?: object });\n\n/** `channel()`'s options: the URL's parts and the channel's own. */\ntype ChannelOpenOptions<E extends ChannelEntry> = UrlParts<E> &\n ChannelOptions<E>;\n\n/** `channel()`'s trailing argument: required when a URL part is. */\ntype OpenArgs<E extends ChannelEntry> = [RequiredKeys<UrlParts<E>>] extends [\n never,\n]\n ? [options?: ChannelOpenOptions<E>]\n : [options: ChannelOpenOptions<E>];\n\n/** What a channel's own validation level takes (§5, TC §5.6): per direction, per message type. */\nexport interface ChannelValidationConfig {\n /**\n * What the client sends: `true` runs the built-in schemas, `false` turns\n * every check off (BYO included), or one entry per message type. Default:\n * the client's `validate.request`, else on in development, off in\n * production.\n */\n send?: boolean | { readonly [type: string]: PartValidation };\n /**\n * What the client receives (pushes and replies), as `send` does. Default:\n * the client's `validate.response`, else on in development, off in\n * production.\n */\n receive?: boolean | { readonly [type: string]: PartValidation };\n /**\n * What a failure does: a send `\"throw\"`s (before anything is written),\n * `\"warn\"`s and sends, or calls a hook; a received frame `\"warn\"`s and is\n * delivered, is dropped (`\"drop\"`, firing `$invalid`), `\"throw\"`s (a\n * waiting request rejects; a push is dropped and reaches `$error`), or\n * calls a hook. Default: `{ send: \"throw\", receive: \"warn\" }`, or the\n * client's `onFailure.request` / `.response`.\n */\n onFailure?:\n | FailureMode\n | {\n /** For a message sent. Default: `\"throw\"`. */\n send?: FailureMode;\n /** For a frame received. Default: `\"warn\"`. */\n receive?: FailureMode | \"drop\";\n };\n}\n\n/**\n * A codec (W-D7): how a message is written and how a frame is read.\n * `\"bun-common\"` (the default) is `defineChannel`'s envelope, `{ type,\n * <idField>?, data }` as JSON, its error frame `{ type: \"error\", id?, code,\n * status, issues?, detail? }`. `\"bun-nest\"` is bun-nest's socket.io-style\n * packets (`BunWebSocketAdapter`'s `MessageEventTypes`): a message is an\n * `EVENT` `{ type: 2, namespace: \"/\", id?, data: [type, data] }` (a\n * `BINARY_EVENT`, its data base64, when the data is bytes), a request's\n * reply the `ACK` `{ type: 3, id, data: [ack] }` the gateway answers it\n * with.\n */\nexport interface ChannelCodec {\n /** Writes one message (its `id` set on a request) for `WebSocket.send`. */\n encode: (\n message: ChannelMessage,\n envelope: { readonly idField: string },\n ) => Payload;\n /**\n * Reads one frame: a message, an error frame (`type: \"error\"` with a\n * `code`), or `undefined` to ignore it. A throw is logged and the frame\n * ignored.\n */\n decode: (\n data: string | ArrayBuffer,\n envelope: { readonly idField: string },\n ) => ChannelMessage | CodecErrorFrame | undefined;\n}\n\n/**\n * An error frame a codec returns: the contract's {@link ChannelErrorFrame},\n * its `code` widened by `\"EXCEPTION\"` (what the `\"bun-nest\"` codec makes of a\n * failed handler's `exception` frame).\n */\nexport type CodecErrorFrame = Omit<ChannelErrorFrame, \"code\"> & {\n /** What failed. */\n code: ChannelFailureCode;\n};\n\n/**\n * A generated channel's runtime schemas: the zod module's `channelSchemas`\n * entry for its path (`bun-client generate --asyncapi … --zod`), which the\n * channel reads through the client's `schemas` source. Each message type's\n * data schema; `null` for a message with no data.\n */\nexport interface ChannelSchemas {\n /** Client → server messages. */\n readonly receives?: { readonly [type: string]: StandardSchemaV1 | null };\n /** Server → client messages. */\n readonly sends?: { readonly [type: string]: StandardSchemaV1 | null };\n}\n\n/** What `WebSocket.send` takes: what a codec encodes a message to. */\ntype Payload = Parameters<WebSocket[\"send\"]>[0];\n\n/** A close code, or an inclusive range of them. */\ntype CloseCodes = readonly (number | readonly [from: number, to: number])[];\n\n/** {@link channel}'s own options (§7, §13.3). */\nexport interface ChannelOptions<E extends ChannelEntry = ChannelEntry> {\n /**\n * Reconnection after a drop, exponential and half-jittered; `false` never\n * reconnects (any close then ends the channel). Default: `{ initialMs:\n * 500, maxMs: 30_000, stableAfterMs: 60_000 }`.\n */\n reconnect?:\n | {\n /** The first delay's ceiling, ms. Default `500`. */\n initialMs?: number;\n /** The largest delay, ms. Default `30_000`. */\n maxMs?: number;\n /** How long a socket stays open before the count starts over, ms. Default `60_000`. */\n stableAfterMs?: number;\n }\n | false;\n /**\n * Close codes the client never reconnects after (each a code or an\n * inclusive `[from, to]` range): the channel ends with\n * {@link ChannelClosedError}. Default: `1007` (bun-common's\n * `closeOnInvalid`), `1008` (policy) and `4400`–`4499`.\n */\n fatalCloseCodes?: CloseCodes;\n /**\n * The server's heartbeat: a message it sends every `intervalMs`. Silence\n * of `2 × intervalMs + slackMs` (any frame counts) closes the socket with\n * `4000` and reconnects. Default: none (a bun-common channel sends none).\n */\n heartbeat?: {\n /** The heartbeat's message type. */\n type: keyof E[\"sends\"] & string;\n /** The server's interval, ms. */\n intervalMs: number;\n /** Extra silence allowed, ms. Default `5_000`. */\n slackMs?: number;\n };\n /**\n * A ping the client sends after `intervalMs` without a frame (a message\n * with no data). Default: none.\n */\n ping?: {\n /** The ping's message type. */\n type: keyof E[\"receives\"] & string;\n /** Silence before a ping, ms. */\n intervalMs: number;\n };\n /**\n * Messages sent while disconnected: buffered (at most `buffer`) and\n * flushed in order on open, a send over the limit throwing\n * {@link ChannelClosedError}; `\"fail\"` throws at once. Default:\n * `{ buffer: 100 }`.\n */\n offline?: { buffer: number } | \"fail\";\n /**\n * Runs after every open (the first included), before the offline buffer\n * flushes: where the app re-sends its own `subscribe` messages and resume\n * cursor (§4.3, §7). Messages it sends go out at once. A throw is logged.\n * Default: none.\n */\n resubscribe?: (room: ChannelClient<E>) => void | Promise<void>;\n /**\n * The envelope: `\"bun-common\"` (a `defineChannel` channel), `\"bun-nest\"`\n * (a bun-nest gateway: socket.io-style packets, a request answered by the\n * gateway's ack), or a custom {@link ChannelCodec}. Default: a generated\n * descriptor's `codec`, else `\"bun-common\"`.\n */\n codec?: \"bun-common\" | \"bun-nest\" | ChannelCodec;\n /**\n * This channel's validation, over the client's (`createClient`'s\n * `validate`: `request` for sends, `response` for what arrives); `false`\n * turns it all off. Default: the client's.\n */\n validate?: ChannelValidationConfig | false;\n}\n\n/** A message's arguments after its type: its data (none for a message without), then the call's options. */\ntype DataArgs<D, O> = [D] extends [undefined]\n ? [data?: undefined, options?: O]\n : undefined extends D\n ? [data?: D, options?: O]\n : [data: D, options?: O];\n\n/** The types the client sends. */\ntype SendType<E extends ChannelEntry> = keyof E[\"receives\"] & string;\n\n/** The types the client receives. */\ntype PushType<E extends ChannelEntry> = keyof E[\"sends\"] & string;\n\n/** The type answering `T`, `never` when none does. */\ntype ReplyOf<E extends ChannelEntry, T> = E extends { replies: infer R }\n ? T extends keyof R\n ? R[T] & PushType<E>\n : never\n : never;\n\n/** The types `request()` takes: those `replies` maps. */\ntype RequestType<E extends ChannelEntry> = {\n [T in SendType<E>]: [ReplyOf<E, T>] extends [never] ? never : T;\n}[SendType<E>];\n\n/** A received message's data, after JSON. */\ntype Data<E extends ChannelEntry, T> = Jsonify<\n E[\"sends\"][T & keyof E[\"sends\"]]\n>;\n\n/** A push as `messages()` yields it with no type. */\ntype PushOf<E extends ChannelEntry> = {\n [T in PushType<E>]: { type: T; data: Data<E, T> };\n}[PushType<E>];\n\n/** The channel's state. */\ntype ChannelState = \"connecting\" | \"open\" | \"reconnecting\" | \"closed\";\n\n/** What `on` takes: a received type, or a client event. */\ntype EventName<E extends ChannelEntry> =\n PushType<E> | \"$state\" | \"$error\" | \"$invalid\";\n\n/** The listener for `N`. */\ntype Listener<E extends ChannelEntry, N> = N extends \"$state\"\n ? (state: ChannelState) => void\n : N extends \"$error\"\n ? (error: ChannelError | ValidationError | ChannelClosedError) => void\n : N extends \"$invalid\"\n ? (error: ValidationError) => void\n : (data: Data<E, N>, frame: { type: N; id?: string }) => void;\n\n/** `send`'s options. */\ninterface SendOptions {\n /** This message's validation, over the channel's and the client's; `false` skips it. Default: the channel's. */\n validate?: PartValidation;\n}\n\n/** `request`'s options. */\ninterface RequestOptions extends SendOptions {\n /** Milliseconds before the request rejects with {@link RequestTimeoutError}; `false` for none. Default: the client's `timeout` (30 s). */\n timeout?: number | false;\n /** Send it again after a reconnect instead of rejecting with {@link DisconnectedError} (for an idempotent request). Default `false`. */\n retry?: boolean;\n}\n\n/** `sendWhenReady`'s options. */\ninterface ReadyOptions extends SendOptions {\n /** The socket's `bufferedAmount` to wait under, bytes. Default `1_048_576` (1 MiB). */\n highWaterMark?: number;\n}\n\n/** A typed channel (§4): what {@link channel} returns. */\nexport interface ChannelClient<E extends ChannelEntry = ChannelEntry> {\n /**\n * Sends a message the server receives, checked first (a failure throws\n * `ValidationError` and writes nothing); buffered while disconnected.\n * Never waits. Throws {@link ChannelClosedError} once the channel is\n * closed, with `offline: \"fail\"` while disconnected, or when the offline\n * buffer is full.\n */\n send: <T extends SendType<E>>(\n type: T,\n ...args: DataArgs<E[\"receives\"][T], SendOptions>\n ) => void;\n /** Sends a request and resolves with its reply's data (a type `replies` maps). */\n request: <T extends RequestType<E>>(\n type: T,\n ...args: DataArgs<E[\"receives\"][T], RequestOptions>\n ) => Promise<Data<E, ReplyOf<E, T>>>;\n /** Waits until {@link bufferedAmount} is under `highWaterMark`, then sends; rejects where `send` throws. */\n sendWhenReady: <T extends SendType<E>>(\n type: T,\n ...args: DataArgs<E[\"receives\"][T], ReadyOptions>\n ) => Promise<void>;\n /**\n * Listens for a pushed message type (a frame without an id, or with one no\n * request of this client sent), or a client event: `$state` (every state\n * change), `$error` (an error frame no request waits for, a deferred\n * send's failure, a push refused under `\"throw\"`, the close that ended the\n * channel), `$invalid` (a frame `\"drop\"` discarded). Returns the\n * function that removes it. A listener that throws is logged.\n */\n on: <N extends EventName<E>>(name: N, listener: Listener<E, N>) => () => void;\n /**\n * The pushes, as an async iterator (every type as `{ type, data }`, or one\n * type's data); it ends when the channel closes. It buffers what it has\n * not taken yet.\n */\n messages: {\n (): AsyncIterableIterator<PushOf<E>>;\n <T extends PushType<E>>(type: T): AsyncIterableIterator<Data<E, T>>;\n };\n /** Closes this handle: its requests reject with {@link ChannelClosedError}, its iterators end; the socket closes with the last handle. */\n close: () => void;\n /** `\"connecting\"` until the first open, `\"open\"`, `\"reconnecting\"` after a drop, `\"closed\"`. */\n readonly state: ChannelState;\n /** The socket's `bufferedAmount`: bytes queued, not yet sent (`0` while disconnected). */\n readonly bufferedAmount: number;\n}\n\n/* ------------------------------------------------------------------------ */\n/* Runtime */\n/* ------------------------------------------------------------------------ */\n\n/** The symbol the client keeps its configuration under (`client.ts`). */\nconst CONFIG = Symbol.for(\"@kingsleyweb/bun-client\");\n\n/** The error codes an error frame may carry. */\nconst CODES: readonly ChannelErrorCode[] = [\n \"VALIDATION\",\n \"UNKNOWN_MESSAGE\",\n \"UNSUPPORTED_DATA\",\n];\n\n/** The codes the connection accepts on an error frame: {@link CODES} and `\"EXCEPTION\"`. */\nconst FRAME_CODES: readonly ChannelFailureCode[] = [...CODES, \"EXCEPTION\"];\n\n/** The default fatal close codes (§7). */\nconst FATAL: CloseCodes = [1007, 1008, [4400, 4499]];\n\n/** The `\"bun-common\"` codec: `defineChannel`'s envelope. */\nconst BUN_COMMON: ChannelCodec = {\n encode: ({ type, id, data }, { idField }) =>\n JSON.stringify(\n id === undefined ? { type, data } : { type, [idField]: id, data },\n ),\n decode(raw, { idField }) {\n const frame = JSON.parse(\n typeof raw === \"string\" ? raw : new TextDecoder().decode(raw),\n ) as Record<string, unknown> | null;\n if (typeof frame?.type !== \"string\") return undefined;\n // bun-common's error frame carries the echoed id as `id` whatever the\n // channel's idField (`channelErrorFrame`); a message under the idField.\n const id =\n frame[idField] ?? (frame.type === \"error\" ? frame.id : undefined);\n if (frame.type === \"error\" && \"code\" in frame)\n return {\n ...frame,\n id: typeof id === \"string\" ? id : undefined,\n } as ChannelErrorFrame;\n return typeof id === \"string\"\n ? { type: frame.type, id, data: frame.data }\n : { type: frame.type, data: frame.data };\n },\n};\n\n/** Whether `value` is bytes (bun-nest's adapter sends those base64, in a binary packet). */\nconst isBytes = (value: unknown): value is ArrayBufferView | ArrayBuffer =>\n ArrayBuffer.isView(value) || value instanceof ArrayBuffer;\n\n/** A binary packet's base64 arguments (those `binary` marks, every string without it) as bytes. */\nfunction unbase64(args: unknown[], marks: unknown): unknown[] {\n return args.map((arg, at) =>\n typeof arg === \"string\" && (!Array.isArray(marks) || marks.includes(at))\n ? Uint8Array.from(atob(arg), (char) => char.charCodeAt(0))\n : arg,\n );\n}\n\n/** An event's arguments as one payload, as bun-nest's adapter maps them: the one, an array of several, none. */\nconst payloadOf = (args: unknown[]): unknown =>\n args.length > 1 ? args : args[0];\n\n/**\n * The `\"bun-nest\"` codec (§11): bun-nest's packets, `MessageEventTypes`\n * (`EVENT` 2, `ACK` 3, `ERROR` 4, `BINARY_EVENT` 5, `BINARY_ACK` 6).\n *\n * - A message is an `EVENT` `[type, data]` (`[type]` with no data), a\n * request's carrying the id as a number, as socket.io's client sends it;\n * bytes are a `BINARY_EVENT`, base64.\n * - An `ACK` (or `BINARY_ACK`) is the reply of the request with its id,\n * read as that request's reply type (`replyOf`); one no request waits for\n * is ignored.\n * - An `EVENT` from the server is a push, never a reply (a server event\n * asking for an ack, `emitWithAck`, is not supported: its id is dropped).\n * `[\"exception\", { status, code, issues?, ackId? }]`, what bun-nest's\n * `ValidationExceptionFilter` emits, is an error frame. When the frame\n * carries `ackId`, the id of the event that failed, the request with that\n * id rejects at once with its {@link ChannelError} (and `$error` is not\n * emitted, as for any error frame a request waits for); without one (a\n * server before `ackId`) it reaches `$error` and the request times out.\n * Any other `exception` is a push of that type, except one with an\n * `ackId` a request waits on (a `WsException` or a thrown handler): that\n * request rejects with a `ChannelError` of code `\"EXCEPTION\"`, the\n * frame's `message` as `detail`.\n * - `ERROR` is a push of type `\"error\"`, its data the message; `CONNECT`\n * and `DISCONNECT` are ignored.\n *\n * @param replyOf The reply type of the request waiting with an id.\n */\nfunction bunNestCodec(\n replyOf: (id: string) => string | undefined,\n): ChannelCodec {\n return {\n encode: ({ type, id, data }) => {\n const binary = isBytes(data);\n let arg: unknown = data;\n if (binary) {\n const bytes = ArrayBuffer.isView(data)\n ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength)\n : new Uint8Array(data);\n let text = \"\";\n for (const byte of bytes) text += String.fromCharCode(byte);\n arg = btoa(text);\n }\n return JSON.stringify({\n type: binary ? 5 : 2,\n namespace: \"/\",\n ...(id === undefined ? {} : { id: /^\\d+$/.test(id) ? Number(id) : id }),\n data: data === undefined ? [type] : [type, arg],\n ...(binary ? { binary: [0] } : {}),\n });\n },\n decode(raw) {\n const packet = JSON.parse(\n typeof raw === \"string\" ? raw : new TextDecoder().decode(raw),\n ) as { type?: unknown; id?: unknown; data?: unknown; binary?: unknown };\n const kind = packet?.type;\n if (kind === 4) return { type: \"error\", data: packet.data };\n if (!Array.isArray(packet?.data)) return undefined;\n const args: unknown[] =\n kind === 5 || kind === 6\n ? unbase64(\n kind === 5 ? packet.data.slice(1) : packet.data,\n // An event's marks count from its first argument, after the name.\n packet.binary,\n )\n : kind === 2\n ? packet.data.slice(1)\n : packet.data;\n if (kind === 3 || kind === 6) {\n const id =\n typeof packet.id === \"number\" || typeof packet.id === \"string\"\n ? String(packet.id)\n : undefined;\n const reply = id === undefined ? undefined : replyOf(id);\n return reply === undefined\n ? undefined\n : { type: reply, id, data: payloadOf(args) };\n }\n const type: unknown = packet.data[0];\n if ((kind !== 2 && kind !== 5) || typeof type !== \"string\")\n return undefined;\n const data = payloadOf(args) as Record<string, unknown> | undefined;\n if (type === \"exception\" && data !== undefined) {\n // The id of the event that failed, when it asked for an ack (`ackId`).\n const { ackId, ...frame } = data;\n const id =\n typeof ackId === \"number\" || typeof ackId === \"string\"\n ? String(ackId)\n : undefined;\n if (\n typeof frame.status === \"number\" &&\n CODES.includes(frame.code as ChannelErrorCode)\n )\n // The filter's frame, as an error frame (`Connection` reads `code`),\n // rejecting the request whose id it carries.\n return {\n ...frame,\n type: \"error\",\n ...(id === undefined ? {} : { id }),\n } as CodecErrorFrame;\n // Any other failed handler: when a request waits on that id the ack\n // will never come, so it fails now; otherwise a push, as before.\n if (id !== undefined && replyOf(id) !== undefined)\n return {\n type: \"error\",\n id,\n code: \"EXCEPTION\",\n status: typeof frame.status === \"number\" ? frame.status : 500,\n ...(typeof frame.message === \"string\"\n ? { detail: frame.message }\n : {}),\n };\n }\n return { type, data };\n },\n };\n}\n\n/** The checks module, imported on first use and shared by every channel. */\nlet checksModule: Promise<typeof import(\"./channelChecks\")> | undefined;\n\n/** The sockets, by URL and subprotocol. */\nconst SOCKETS = new Map<string, Connection>();\n\n/**\n * The one `process` read here, `process.env.NODE_ENV`, as `client.ts`\n * reads it (a type, erased; a bundler's define replaces it).\n */\ndeclare const process: {\n /** The environment; only `NODE_ENV` is read. */\n readonly env: Readonly<Record<string, string | undefined>>;\n};\n\n/** Development unless the configuration or `NODE_ENV` says production (TC §5.5). */\nfunction isDevelopment(config: ClientConfig): boolean {\n if (config.mode !== undefined) return config.mode === \"development\";\n try {\n return process.env.NODE_ENV !== \"production\";\n } catch {\n return true;\n }\n}\n\n/** Whether a validation level could turn a check on: an object with a key but `onFailure` that is not `false`. */\nfunction mayCheck(level: object | false | undefined): boolean {\n return (\n typeof level === \"object\" &&\n Object.entries(level).some(\n ([name, value]) =>\n name !== \"onFailure\" && value !== false && value !== undefined,\n )\n );\n}\n\n/** The page's `requestAnimationFrame`, when there is one (`lib/` is checked without the DOM's). */\nconst RAF = (\n globalThis as { requestAnimationFrame?: (callback: () => void) => number }\n).requestAnimationFrame;\n\n/** Waits one animation frame, or 16 ms outside a browser. */\nfunction nextFrame(): Promise<void> {\n return new Promise((resolve) =>\n typeof RAF === \"function\" ? RAF(() => resolve()) : setTimeout(resolve, 16),\n );\n}\n\n/** What a channel is, as a contract or a descriptor gives it. */\ninterface Facts {\n /** The route path. */\n readonly path: string;\n /** The envelope's correlation field. */\n readonly idField: string;\n /** The replies map. */\n readonly replies: Readonly<Record<string, string>>;\n /** The subprotocol to offer. */\n readonly subprotocol: string | undefined;\n /** The shared contract, when one was given. */\n readonly contract: ChannelContract | undefined;\n /** A generated descriptor's codec, when it names one. */\n readonly codec: \"bun-common\" | \"bun-nest\" | undefined;\n}\n\n/** A request waiting for its reply. */\ninterface Pending {\n /** Its correlation id. */\n readonly id: string;\n /** The handle that sent it. */\n readonly handle: Handle;\n /** Its message type. */\n readonly type: string;\n /** The reply's type. */\n readonly reply: string;\n /** The encoded frame, for a resend. */\n readonly payload: Payload;\n /** Send it again after a reconnect. */\n readonly retry: boolean;\n /** Settles it with the reply's data. */\n resolve: (value: unknown) => void;\n /** Settles it with an error. */\n reject: (error: unknown) => void;\n}\n\n/** One socket, shared by every handle on its URL. */\nclass Connection {\n /** The socket, while one is open or opening. */\n ws: WebSocket | undefined;\n /** The socket's state. */\n state: ChannelState = \"connecting\";\n /** The handles on it. */\n readonly handles = new Set<Handle>();\n /** The requests waiting, by id. */\n readonly pending = new Map<string, Pending>();\n /** Requests to send again on the next open (`retry: true`). */\n readonly resend: Pending[] = [];\n /** Messages written while disconnected. */\n readonly buffer: Payload[] = [];\n /** The id counter. */\n #next = 0;\n /** Reconnect attempts since the last stable socket. */\n #attempt = 0;\n /** The reconnect, stable, watchdog and ping timers. */\n readonly #timers: Record<\n \"reconnect\" | \"stable\" | \"watchdog\" | \"ping\",\n ReturnType<typeof setTimeout> | undefined\n > = {\n reconnect: undefined,\n stable: undefined,\n watchdog: undefined,\n ping: undefined,\n };\n /** The codec. */\n readonly codec: ChannelCodec;\n /** The envelope, for the codec. */\n readonly envelope: { readonly idField: string };\n\n /**\n * @param key Its key in {@link SOCKETS}.\n * @param url The socket URL.\n * @param facts The channel's facts.\n * @param options The first handle's options (the transport's).\n * @param logger Where problems are logged.\n */\n constructor(\n readonly key: string,\n readonly url: string,\n readonly facts: Facts,\n readonly options: ChannelOptions,\n readonly logger: Pick<Console, \"warn\" | \"error\">,\n ) {\n const codec = options.codec ?? facts.codec;\n this.codec =\n codec === \"bun-nest\"\n ? bunNestCodec((id) => this.pending.get(id)?.reply)\n : codec === undefined || codec === \"bun-common\"\n ? BUN_COMMON\n : codec;\n this.envelope = { idField: facts.idField };\n this.#open();\n }\n\n /** Opens a socket. */\n #open(): void {\n const ws = new WebSocket(\n this.url,\n this.facts.subprotocol === undefined ? [] : [this.facts.subprotocol],\n );\n ws.binaryType = \"arraybuffer\";\n this.ws = ws;\n ws.onopen = () => void this.#opened(ws);\n ws.onmessage = (event) =>\n this.#frame(ws, event.data as string | ArrayBuffer);\n ws.onclose = (event) => this.#dropped(ws, event.code, event.reason);\n }\n\n /** Moves to `state`, telling every handle. */\n #state(state: ChannelState): void {\n if (this.state === state) return;\n this.state = state;\n for (const handle of this.handles) handle.emit(\"$state\", state);\n }\n\n /** The socket opened: resubscribe, resend, flush. */\n async #opened(ws: WebSocket): Promise<void> {\n if (ws !== this.ws) return;\n this.#state(\"open\");\n this.#arm();\n const stableAfter =\n this.options.reconnect === false\n ? 0\n : (this.options.reconnect?.stableAfterMs ?? 60_000);\n this.#timers.stable = setTimeout(() => {\n this.#attempt = 0;\n }, stableAfter);\n for (const handle of [...this.handles]) {\n try {\n await handle.options.resubscribe?.(handle.client as never);\n } catch (error) {\n handle.logger.error(`${this.facts.path}: resubscribe threw`, error);\n }\n }\n if (ws !== this.ws || ws.readyState !== 1) return;\n for (const pending of this.resend.splice(0)) {\n this.pending.set(pending.id, pending);\n ws.send(pending.payload);\n }\n for (const payload of this.buffer.splice(0)) ws.send(payload);\n }\n\n /** (Re)arms the heartbeat watchdog and the ping timer: any frame counts. */\n #arm(): void {\n const { heartbeat, ping } = this.options;\n clearTimeout(this.#timers.watchdog);\n clearTimeout(this.#timers.ping);\n if (heartbeat !== undefined) {\n this.#timers.watchdog = setTimeout(\n () => this.#lost(),\n heartbeat.intervalMs * 2 + (heartbeat.slackMs ?? 5_000),\n );\n }\n if (ping !== undefined) {\n const send = (): void => {\n if (this.ws?.readyState === 1)\n this.ws.send(this.codec.encode({ type: ping.type }, this.envelope));\n this.#timers.ping = setTimeout(send, ping.intervalMs);\n };\n this.#timers.ping = setTimeout(send, ping.intervalMs);\n }\n }\n\n /** The heartbeat went quiet: close with 4000 and reconnect. */\n #lost(): void {\n const ws = this.ws;\n if (ws === undefined) return;\n ws.onclose = null;\n ws.close(4000, \"heartbeat lost\");\n this.#dropped(ws, 4000, \"heartbeat lost\");\n }\n\n /** Handles one frame. */\n #frame(ws: WebSocket, raw: string | ArrayBuffer): void {\n if (ws !== this.ws) return;\n this.#arm();\n let message: ChannelMessage | CodecErrorFrame | undefined;\n try {\n message = this.codec.decode(raw, this.envelope);\n } catch (error) {\n this.logger.warn(\n `${this.facts.path}: a frame could not be decoded`,\n error,\n );\n return;\n }\n if (message === undefined) return;\n const id = typeof message.id === \"string\" ? message.id : undefined;\n const pending = id === undefined ? undefined : this.pending.get(id);\n if (message.type === \"error\" && \"code\" in message) {\n // The error frame is always checked, with its fixed shape (§5).\n const { code, status, issues, detail } = message;\n if (\n !FRAME_CODES.includes(code) ||\n typeof status !== \"number\" ||\n (issues !== undefined && !Array.isArray(issues)) ||\n (detail !== undefined && typeof detail !== \"string\")\n ) {\n this.logger.warn(\n `${this.facts.path}: an error frame does not have the error frame's shape; ignored`,\n );\n return;\n }\n if (pending !== undefined) {\n this.pending.delete(id!);\n pending.reject(\n new ChannelError(this.facts.path, message, pending.type),\n );\n return;\n }\n const error = new ChannelError(this.facts.path, message);\n // Each handle's `$error`, or its own logger when it has none.\n for (const handle of this.handles)\n if (!handle.emit(\"$error\", error)) handle.logger.warn(error.message);\n return;\n }\n if (pending !== undefined && pending.reply === message.type) {\n this.pending.delete(id!);\n pending.handle.reply(pending, (message as ChannelMessage).data);\n return;\n }\n for (const handle of this.handles) handle.push(message);\n }\n\n /** The socket closed: reject or keep the requests, then reconnect or end. */\n #dropped(ws: WebSocket, code: number, reason: string): void {\n if (ws !== this.ws) return;\n this.ws = undefined;\n for (const timer of Object.values(this.#timers)) clearTimeout(timer);\n for (const [id, pending] of this.pending) {\n this.pending.delete(id);\n if (pending.retry) this.resend.push(pending);\n else pending.reject(new DisconnectedError(this.facts.path, code, reason));\n }\n if (this.state === \"closed\") return;\n const fatal = (this.options.fatalCloseCodes ?? FATAL).some((entry) =>\n typeof entry === \"number\"\n ? entry === code\n : code >= entry[0] && code <= entry[1],\n );\n if (this.options.reconnect === false || fatal) {\n this.end(\n new ChannelClosedError(\n this.facts.path,\n reason || \"the server closed the channel\",\n code,\n ),\n );\n return;\n }\n const { initialMs = 500, maxMs = 30_000 } = this.options.reconnect ?? {};\n const ceiling = Math.min(maxMs, initialMs * 2 ** this.#attempt++);\n this.#state(this.state === \"connecting\" ? \"connecting\" : \"reconnecting\");\n this.#timers.reconnect = setTimeout(\n () => this.#open(),\n ceiling / 2 + (ceiling / 2) * Math.random(),\n );\n }\n\n /** Ends the socket for good: every handle closed with `error` (none for the last `close()`). */\n end(error?: ChannelClosedError): void {\n SOCKETS.delete(this.key);\n for (const timer of Object.values(this.#timers)) clearTimeout(timer);\n const closed =\n error ?? new ChannelClosedError(this.facts.path, \"the channel is closed\");\n for (const pending of [...this.pending.values(), ...this.resend])\n pending.reject(closed);\n this.buffer.length = 0;\n const ws = this.ws;\n this.ws = undefined;\n ws?.close(1000);\n for (const handle of [...this.handles])\n handle.ended(closed, error !== undefined);\n this.state = \"closed\";\n }\n\n /** Writes `payload`, or buffers it while disconnected; throws when it can do neither. */\n write(payload: Payload): void {\n if (this.ws?.readyState === 1) {\n this.ws.send(payload);\n return;\n }\n const offline = this.options.offline ?? { buffer: 100 };\n if (offline === \"fail\" || this.buffer.length >= offline.buffer)\n throw new ChannelClosedError(\n this.facts.path,\n offline === \"fail\"\n ? 'disconnected (offline: \"fail\")'\n : `the offline buffer is full (${offline.buffer} messages)`,\n );\n this.buffer.push(payload);\n }\n\n /** Registers a request and writes it. */\n request(\n handle: Handle,\n type: string,\n data: unknown,\n options: RequestOptions,\n resolve: (value: unknown) => void,\n reject: (error: unknown) => void,\n ): void {\n const reply = this.facts.replies[type];\n if (reply === undefined)\n throw new ClientError(\n `${this.facts.path}: \"${type}\" has no reply declared (replies)`,\n { key: this.facts.path },\n );\n const id = String(++this.#next);\n const timeout = options.timeout ?? handle.timeout;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const pending: Pending = {\n id,\n handle,\n type,\n reply,\n payload: this.codec.encode({ type, id, data }, this.envelope),\n retry: options.retry === true,\n resolve,\n reject,\n };\n // Settling (once) clears the timer and the request's every entry.\n const settle =\n <T>(fn: (value: T) => void) =>\n (value: T): void => {\n clearTimeout(timer);\n this.pending.delete(id);\n const at = this.resend.indexOf(pending);\n if (at !== -1) this.resend.splice(at, 1);\n handle.requests.delete(pending);\n fn(value);\n };\n pending.resolve = settle(resolve);\n pending.reject = settle(reject);\n this.write(pending.payload);\n this.pending.set(id, pending);\n handle.requests.add(pending);\n if (timeout !== false)\n timer = setTimeout(\n () =>\n pending.reject(\n new RequestTimeoutError(this.facts.path, type, id, timeout),\n ),\n timeout,\n );\n }\n}\n\n/** A drop of a frame `\"drop\"` asked for (or a refused push). */\nconst DROPPED = Symbol(\"dropped\");\n\n/** One iterator of `messages()`. */\ninterface Iteration {\n /** The type it yields, or every type. */\n readonly type: string | undefined;\n /** Takes a value. */\n readonly push: (value: unknown) => void;\n /** Ends it. */\n readonly end: () => void;\n}\n\n/** A handle's client as the runtime calls it (untyped; {@link ChannelClient} types it). */\ninterface RuntimeClient {\n /** `ChannelClient.send`. */\n send: (type: string, data?: unknown, options?: SendOptions) => void;\n /** `ChannelClient.request`. */\n request: (\n type: string,\n data?: unknown,\n options?: RequestOptions,\n ) => Promise<unknown>;\n /** `ChannelClient.sendWhenReady`. */\n sendWhenReady: (\n type: string,\n data?: unknown,\n options?: ReadyOptions,\n ) => Promise<void>;\n /** `ChannelClient.on`. */\n on: (name: string, listener: (...args: never[]) => void) => () => void;\n /** `ChannelClient.messages`. */\n messages: (type?: string) => AsyncIterableIterator<unknown>;\n /** `ChannelClient.close`. */\n close: () => void;\n /** `ChannelClient.state`. */\n readonly state: ChannelState;\n /** `ChannelClient.bufferedAmount`. */\n readonly bufferedAmount: number;\n}\n\n/** One `channel()` handle. */\nclass Handle {\n /** The client this handle is. */\n readonly client: RuntimeClient;\n /** The listeners, by name. */\n readonly #listeners = new Map<string, Set<(...args: never[]) => void>>();\n /** The `messages()` iterators. */\n readonly #iterations = new Set<Iteration>();\n /** Its requests waiting. */\n readonly requests = new Set<Pending>();\n /** The checks, once loaded; `undefined` while loading, or when nothing checks. */\n #checks: ChannelChecks | undefined;\n /** The checks loading; `undefined` when nothing checks. */\n readonly #loading: Promise<ChannelChecks | undefined> | undefined;\n /** Sends waiting for a check, in order. */\n #tail: Promise<void> = Promise.resolve();\n /** How many sends wait for a check. */\n #deferred = 0;\n /** Received frames, handled in order. */\n #rx: Promise<void> = Promise.resolve();\n /** Why this handle closed, once it has. */\n #closed: ChannelClosedError | undefined;\n /** The default request timeout. */\n readonly timeout: number | false;\n /**\n * Where this handle's warnings go: its own client's `logger` (a shared\n * socket logs connection problems through the first handle's).\n */\n readonly logger: Pick<Console, \"warn\" | \"error\">;\n\n /**\n * @param conn Its socket.\n * @param facts The channel's facts.\n * @param options Its options.\n * @param config The client's configuration.\n */\n constructor(\n readonly conn: Connection,\n readonly facts: Facts,\n readonly options: ChannelOptions,\n readonly config: ClientConfig,\n ) {\n this.timeout = config.timeout ?? 30_000;\n this.logger = config.logger ?? console;\n const development = isDevelopment(config);\n const hasSource =\n facts.contract !== undefined || config.schemas !== undefined;\n if (\n config.validate !== false &&\n options.validate !== false &&\n ((hasSource && (development || mayCheck(config.validate))) ||\n mayCheck(options.validate))\n ) {\n const source = config.schemas;\n this.#loading = (checksModule ??= import(\"./channelChecks\"))\n .then((module) =>\n module.createChannelChecks({\n key: facts.path,\n development,\n levels: [config.validate, options.validate],\n contract: facts.contract?.contract,\n meta: facts,\n schemas:\n facts.contract !== undefined || source === undefined\n ? undefined\n : async () =>\n (\n (typeof source === \"function\"\n ? await source()\n : source) as { channelSchemas?: object }\n ).channelSchemas?.[facts.path as never],\n ValidationError,\n }),\n )\n .then(\n (checks) => (this.#checks = checks),\n (error: unknown) => {\n this.logger.error(\n `${facts.path}: the channel's checks failed to load; nothing is checked`,\n error,\n );\n return undefined;\n },\n );\n }\n const state = (): ChannelState =>\n this.#closed === undefined ? conn.state : \"closed\";\n this.client = {\n send: (type, data, options) => {\n this.#alive();\n this.#outgoing(type, data, options?.validate, (value) =>\n conn.write(conn.codec.encode({ type, data: value }, conn.envelope)),\n )?.catch((error: unknown) => this.#report(error));\n },\n request: (type, data, options = {}) =>\n new Promise((resolve, reject) => {\n this.#alive();\n this.#outgoing(type, data, options.validate, (value) =>\n conn.request(this, type, value, options, resolve, reject),\n )?.catch(reject);\n }),\n sendWhenReady: async (type, data, options = {}) => {\n while (\n this.#alive() &&\n (conn.ws?.bufferedAmount ?? 0) >= (options.highWaterMark ?? 1 << 20)\n )\n await nextFrame();\n await this.#outgoing(type, data, options.validate, (value) =>\n conn.write(conn.codec.encode({ type, data: value }, conn.envelope)),\n );\n },\n on: (name, listener) => {\n let set = this.#listeners.get(name);\n if (set === undefined) this.#listeners.set(name, (set = new Set()));\n set.add(listener);\n return () => void set.delete(listener);\n },\n messages: (type) => this.#messages(type),\n close: () => this.#close(),\n get state() {\n return state();\n },\n get bufferedAmount() {\n return conn.ws?.bufferedAmount ?? 0;\n },\n };\n }\n\n /** Throws when this handle is closed; `true` otherwise. */\n #alive(): true {\n if (this.#closed !== undefined) throw this.#closed;\n return true;\n }\n\n /**\n * Checks one outgoing message and writes it with `write`: at once when the\n * check is synchronous and nothing waits before it (a failure under\n * `\"throw\"` throws here), else in order once its check settled (a promise\n * that rejects with the failure).\n */\n #outgoing(\n type: string,\n data: unknown,\n validate: PartValidation | undefined,\n write: (value: unknown) => void,\n ): Promise<void> | undefined {\n if (this.#loading === undefined) {\n write(data);\n return undefined;\n }\n const checks = this.#checks;\n if (checks !== undefined && this.#deferred === 0) {\n const verdict = checks.send(type, data, validate);\n if (!(verdict instanceof Promise)) {\n write(this.#sent(checks, verdict));\n return undefined;\n }\n }\n this.#deferred++;\n const run = this.#tail.then(async () => {\n const loaded = await this.#loading;\n this.#alive();\n if (loaded === undefined) return write(data);\n write(this.#sent(loaded, await loaded.send(type, data, validate)));\n });\n this.#tail = run.then(\n () => void this.#deferred--,\n () => void this.#deferred--,\n );\n return run;\n }\n\n /** A send's verdict under the failure mode: the value to write, or a throw. */\n #sent(checks: ChannelChecks, verdict: Verdict): unknown {\n if (verdict.error !== undefined) {\n const mode = checks.onSend;\n if (mode === \"throw\") throw verdict.error;\n if (mode === \"warn\") this.logger.warn(verdict.error.message);\n else mode(verdict.error);\n }\n return verdict.value;\n }\n\n /**\n * A received frame's verdict under the failure mode: the value to deliver,\n * or {@link DROPPED}; `reject` takes a `\"throw\"` (a request's), else it\n * reaches `$error`.\n */\n async #received(\n type: string,\n data: unknown,\n reject?: (error: unknown) => void,\n ): Promise<unknown> {\n const checks = this.#checks ?? (await this.#loading);\n if (checks === undefined) return data;\n const verdict = await checks.receive(type, data);\n const error = verdict.error;\n if (error === undefined) return verdict.value;\n const mode = checks.onReceive;\n if (mode === \"warn\") {\n this.logger.warn(error.message);\n return verdict.value;\n }\n if (mode === \"drop\") {\n this.emit(\"$invalid\", error);\n reject?.(error);\n return DROPPED;\n }\n try {\n if (mode === \"throw\") throw error;\n mode(error);\n return verdict.value;\n } catch (thrown) {\n if (reject === undefined) this.#report(thrown);\n else reject(thrown);\n return DROPPED;\n }\n }\n\n /** Hands a reply to its request, after the receive check (in order with the pushes). */\n reply(pending: Pending, data: unknown): void {\n this.#rx = this.#rx.then(async () => {\n const value = await this.#received(pending.reply, data, pending.reject);\n if (value !== DROPPED) pending.resolve(value);\n });\n }\n\n /** Hands a push to the listeners and iterators, after the receive check, in order. */\n push(message: ChannelMessage): void {\n if (this.#closed !== undefined) return;\n this.#rx = this.#rx.then(async () => {\n const value = await this.#received(message.type, message.data);\n if (value === DROPPED || this.#closed !== undefined) return;\n const frame =\n message.id === undefined\n ? { type: message.type }\n : { type: message.type, id: message.id };\n this.emit(message.type, value, frame);\n for (const iteration of this.#iterations) {\n if (iteration.type === undefined)\n iteration.push({ type: message.type, data: value });\n else if (iteration.type === message.type) iteration.push(value);\n }\n });\n }\n\n /** Calls the listeners of `name`; `true` when there was one. A listener that throws is logged. */\n emit(name: string, ...args: unknown[]): boolean {\n const set = this.#listeners.get(name);\n for (const listener of set ?? []) {\n try {\n (listener as (...values: unknown[]) => void)(...args);\n } catch (error) {\n this.logger.error(\n `${this.facts.path}: a \"${name}\" listener threw`,\n error,\n );\n }\n }\n return set !== undefined && set.size > 0;\n }\n\n /** A failure no caller can see: to `$error`, or the log. */\n #report(error: unknown): void {\n if (!this.emit(\"$error\", error))\n this.logger.warn((error as Error).message ?? error);\n }\n\n /** `messages()`: an iterator over the pushes. */\n #messages(type: string | undefined): AsyncIterableIterator<unknown> {\n const queue: unknown[] = [];\n let done = this.#closed !== undefined;\n let wake: (() => void) | undefined;\n const iteration: Iteration = {\n type,\n push: (value) => {\n queue.push(value);\n wake?.();\n },\n end: () => {\n done = true;\n wake?.();\n },\n };\n if (!done) this.#iterations.add(iteration);\n const iterator: AsyncIterableIterator<unknown> = {\n [Symbol.asyncIterator]: () => iterator,\n next: async () => {\n for (;;) {\n if (queue.length > 0) return { value: queue.shift(), done: false };\n if (done) return { value: undefined, done: true };\n await new Promise<void>((resolve) => (wake = resolve));\n wake = undefined;\n }\n },\n return: () => {\n this.#iterations.delete(iteration);\n iteration.end();\n queue.length = 0;\n return Promise.resolve({ value: undefined, done: true });\n },\n };\n return iterator;\n }\n\n /** This handle's `close()`. */\n #close(): void {\n if (this.#closed !== undefined) return;\n this.ended(\n new ChannelClosedError(this.facts.path, \"the channel is closed\"),\n false,\n );\n if (this.conn.handles.size === 0) this.conn.end();\n }\n\n /** Ends this handle: its requests rejected, its iterators ended, `$state` \"closed\" (and `$error` for a close the client will not retry). */\n ended(error: ChannelClosedError, report: boolean): void {\n if (this.#closed !== undefined) return;\n this.#closed = error;\n this.conn.handles.delete(this);\n for (const pending of [...this.requests]) pending.reject(error);\n for (const iteration of this.#iterations) iteration.end();\n this.#iterations.clear();\n if (report) this.#report(error);\n this.emit(\"$state\", \"closed\");\n }\n}\n\n/**\n * Opens a typed channel (§4.1) from a shared `channelContract()` result:\n * typed by its spec, checked by its own schemas.\n *\n * @param api The client (`createClient`): its `baseUrl` (`http` → `ws`),\n * `mode`, `validate`, `timeout` and `logger` apply.\n * @param contract The channel's contract, from a browser-safe module.\n * @param options The URL's `params` and `query`, and {@link ChannelOptions}.\n * @throws TypeError when a message type starts with `$` (the client's\n * events' prefix).\n */\nexport function channel<const S extends ChannelSpec>(\n api: { readonly url: (...args: never[]) => string },\n contract: ChannelContract<S>,\n ...options: OpenArgs<ChannelEntryOf<S>>\n): ChannelClient<ChannelEntryOf<S>>;\n/**\n * Opens a typed channel (§4.1) from a generated descriptor\n * (`channels[\"/rooms/:room\"]` of `bun-client generate --asyncapi`): typed by\n * its `Channels` entry, checked by the generated zod module's\n * `channelSchemas` when the client's `schemas` source has them.\n *\n * @param api The client (`createClient`).\n * @param descriptor The channel's generated descriptor.\n * @param options The URL's `params` and `query`, and {@link ChannelOptions}.\n */\nexport function channel<E extends ChannelEntry>(\n api: { readonly url: (...args: never[]) => string },\n descriptor: ChannelDescriptor<E>,\n ...options: OpenArgs<E>\n): ChannelClient<E>;\nexport function channel(\n api: { readonly url: (...args: never[]) => string },\n source: ChannelContract | ChannelDescriptor,\n options?: object,\n): ChannelClient {\n const opened = (options ?? {}) as ChannelOpenOptions<ChannelEntry>;\n const config =\n ((api as unknown as Record<symbol, unknown>)[CONFIG] as\n ClientConfig | undefined) ?? {};\n let facts: Facts;\n if (\"spec\" in source) {\n const { meta, spec } = source;\n for (const type of [\n ...Object.keys(spec.receives),\n ...Object.keys(spec.sends),\n ])\n if (type.startsWith(\"$\"))\n throw new TypeError(\n `channel(): the message type \"${type}\" starts with \"$\", which the client's events use`,\n );\n facts = {\n path: meta.path,\n idField: meta.envelope.idField,\n replies: meta.replies,\n subprotocol: meta.subprotocol,\n contract: source,\n codec: undefined,\n };\n } else {\n facts = {\n path: source.path,\n idField: source.idField ?? \"id\",\n replies: source.replies ?? {},\n subprotocol: source.subprotocol,\n contract: undefined,\n codec: source.codec,\n };\n }\n let url = (api.url as (path: string, options: object) => string)(facts.path, {\n params: opened.params,\n query: opened.query,\n });\n if (!/^[a-z][a-z\\d+.-]*:/i.test(url)) {\n const base = (globalThis as { location?: { href?: string } }).location\n ?.href;\n if (!base)\n throw new ClientError(\n `${facts.path}: \"${url}\" is relative, and there is no page to resolve it against; give the client an absolute baseUrl`,\n { key: facts.path },\n );\n url = new URL(url, base).href;\n }\n url = url.replace(/^http(s?):/i, \"ws$1:\");\n const key = `${url} ${facts.subprotocol ?? \"\"}`;\n let conn = SOCKETS.get(key);\n if (conn === undefined) {\n conn = new Connection(key, url, facts, opened, config.logger ?? console);\n SOCKETS.set(key, conn);\n }\n const handle = new Handle(conn, facts, opened, config);\n conn.handles.add(handle);\n // A handle joining an open socket resubscribes at once (the socket's\n // next open runs it with the others).\n if (conn.state === \"open\")\n void (async () => opened.resubscribe?.(handle.client as never))().catch(\n (error: unknown) =>\n handle.logger.error(`${facts.path}: resubscribe threw`, error),\n );\n return handle.client as unknown as ChannelClient;\n}\n"
6
6
  ],
7
7
  "mappings": "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",
8
- "debugId": "01A7BCB0782A1A9164756E2164756E21",
8
+ "debugId": "8BB5707F752ACABE64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,4 +1,4 @@
1
- import{Ce,lt,en,tn,Cn}from"./main-grcj8v1z.js";function p(e,n){if(n===void 0)return;if(typeof n==="boolean"){if(e.enabled=n,!n)e.custom=void 0,e.replace=!1;return}if(n.enabled!==void 0)e.enabled=n.enabled;if(n.custom!==void 0)e.custom=n.custom;if(n.replace!==void 0)e.replace=n.replace}function P(e,n){if(typeof n==="boolean"){p(e.base,n);for(let o of e.types.values())p(o,n)}else if(n!==void 0)for(let[o,u]of Object.entries(n)){let d=e.types.get(o)??{...e.base};p(d,u),e.types.set(o,d)}}function F(e,n){return e.map((o)=>({...o,target:o.target,source:n}))}function E(e,n){return e instanceof Promise?e.then(n):n(e)}async function G(e){let{development:n,key:o,meta:u}=e,d=e.contract;if(d===void 0&&e.schemas!==void 0){let a=await e.schemas();if(a!==void 0)d=Cn({path:u.path,receives:a.receives??{},sends:a.sends??{},replies:u.replies,envelope:{idField:u.idField}}).contract}let M=d===void 0?void 0:tn(d,"inbound"),R=d===void 0?void 0:en(d,"outbound"),C=()=>({base:{enabled:n,custom:void 0,replace:!1},types:new Map}),m=C(),y=C(),h="throw",b="warn",[t,s]=e.levels,g=(a,i,l,r)=>{if(a){for(let c of[m,y])p(c.base,!1),c.types.clear();return}if(P(m,i),P(y,l),typeof r==="string"||typeof r==="function")h=r,b=r;else if(r!==void 0)h=r.send??h,b=r.receive??b};if(t!==void 0){let a=t===!1?void 0:t.onFailure;g(t===!1,t===!1?void 0:t.request,t!==!1&&typeof t.response==="boolean"?t.response:void 0,typeof a==="object"?{send:a.request,receive:a.response}:a)}if(s!==void 0)g(s===!1,s===!1?void 0:s.send,s===!1?void 0:s.receive,s===!1?void 0:s.onFailure);let k=(a,i,l,r,c)=>{let D=i.enabled&&!i.replace&&a!==void 0?a({type:r,data:c}):void 0;return E(D,(v)=>{let V=[],S=c;if(v instanceof lt)V.push(...F(v.issues,"builtin"));else if(v!==void 0&&l==="receive")S=v.data;let w=(f)=>V.length===0?{value:f}:{value:c,error:new e.ValidationError(o,l,V)};if(i.custom===void 0)return w(c);return E(Ce(i.custom,S,"data",r,void 0,l==="receive"?{stripProtoKeys:"nest"}:void 0),(f)=>{if(!f.ok)V.push(...F(f.issues,"custom"));return w(f.ok?f.value:c)})})};return{send:(a,i,l)=>{let r=m.types.get(a)??m.base;if(l!==void 0)r={...r},p(r,l);return k(M,r,"send",a,i)},receive:(a,i)=>k(R,y.types.get(a)??y.base,"receive",a,i),onSend:h,onReceive:b}}export{G as createChannelChecks};
1
+ import{Pe,ut,en,tn,Pn}from"./main-jv5xqd13.js";function p(e,n){if(n===void 0)return;if(typeof n==="boolean"){if(e.enabled=n,!n)e.custom=void 0,e.replace=!1;return}if(n.enabled!==void 0)e.enabled=n.enabled;if(n.custom!==void 0)e.custom=n.custom;if(n.replace!==void 0)e.replace=n.replace}function P(e,n){if(typeof n==="boolean"){p(e.base,n);for(let o of e.types.values())p(o,n)}else if(n!==void 0)for(let[o,u]of Object.entries(n)){let d=e.types.get(o)??{...e.base};p(d,u),e.types.set(o,d)}}function F(e,n){return e.map((o)=>({...o,target:o.target,source:n}))}function E(e,n){return e instanceof Promise?e.then(n):n(e)}async function G(e){let{development:n,key:o,meta:u}=e,d=e.contract;if(d===void 0&&e.schemas!==void 0){let a=await e.schemas();if(a!==void 0)d=Pn({path:u.path,receives:a.receives??{},sends:a.sends??{},replies:u.replies,envelope:{idField:u.idField}}).contract}let M=d===void 0?void 0:tn(d,"inbound"),R=d===void 0?void 0:en(d,"outbound"),C=()=>({base:{enabled:n,custom:void 0,replace:!1},types:new Map}),m=C(),y=C(),h="throw",b="warn",[t,s]=e.levels,g=(a,i,l,r)=>{if(a){for(let c of[m,y])p(c.base,!1),c.types.clear();return}if(P(m,i),P(y,l),typeof r==="string"||typeof r==="function")h=r,b=r;else if(r!==void 0)h=r.send??h,b=r.receive??b};if(t!==void 0){let a=t===!1?void 0:t.onFailure;g(t===!1,t===!1?void 0:t.request,t!==!1&&typeof t.response==="boolean"?t.response:void 0,typeof a==="object"?{send:a.request,receive:a.response}:a)}if(s!==void 0)g(s===!1,s===!1?void 0:s.send,s===!1?void 0:s.receive,s===!1?void 0:s.onFailure);let k=(a,i,l,r,c)=>{let D=i.enabled&&!i.replace&&a!==void 0?a({type:r,data:c}):void 0;return E(D,(v)=>{let V=[],S=c;if(v instanceof ut)V.push(...F(v.issues,"builtin"));else if(v!==void 0&&l==="receive")S=v.data;let w=(f)=>V.length===0?{value:f}:{value:c,error:new e.ValidationError(o,l,V)};if(i.custom===void 0)return w(c);return E(Pe(i.custom,S,"data",r,void 0,l==="receive"?{stripProtoKeys:"nest"}:void 0),(f)=>{if(!f.ok)V.push(...F(f.issues,"custom"));return w(f.ok?f.value:c)})})};return{send:(a,i,l)=>{let r=m.types.get(a)??m.base;if(l!==void 0)r={...r},p(r,l);return k(M,r,"send",a,i)},receive:(a,i)=>k(R,y.types.get(a)??y.base,"receive",a,i),onSend:h,onReceive:b}}export{G as createChannelChecks};
2
2
 
3
- //# debugId=02B94FA12596462164756E2164756E21
4
- //# sourceMappingURL=channelChecks-1epbhv9a.js.map
3
+ //# debugId=A6A575F535E55BFA64756E2164756E21
4
+ //# sourceMappingURL=channelChecks-s4yz4yhw.js.map
@@ -5,6 +5,6 @@
5
5
  "/**\n * A channel's checks (typed-client plan, `websocket.md` §5, PR-W2): what the\n * client sends through the contract's **sender** over its `inbound` side\n * (the server's receives), what arrives through its **receiver** over\n * `outbound` (the server's sends), the very guards `defineChannel` runs\n * (D20); then a BYO schema per message type, on the same policy as HTTP\n * (TC §5.6): built-in first, BYO after on the built-in's output (or the raw\n * data when it failed), `replace` dropping the built-in, a passing BYO\n * schema's output being what is sent or delivered.\n *\n * **Loaded lazily**, by `./channel` alone; it takes bun-contract's guards\n * and `channelContract` through `./validation`, their one import site, so a\n * bundler keeps only what this module uses of it.\n */\nimport type {\n AnyContract,\n StandardOutcome,\n StandardSchemaV1,\n ValidationIssue,\n} from \"@kingsleyweb/bun-contract\";\nimport type { ChannelSchemas, ChannelValidationConfig } from \"./channel\";\nimport type { ValidationError, ValidationErrorIssue } from \"./errors\";\nimport type { FailureMode, PartValidation, ValidationConfig } from \"./types\";\nimport {\n channelContract,\n ContractError,\n createReceiver,\n createSender,\n validateStandard,\n} from \"./validation\";\n\n/** A check's outcome: what to send or deliver, and the failure, when there was one. */\nexport interface Verdict {\n /** The data as given, or a passing BYO schema's output. */\n readonly value: unknown;\n /** Every issue found, the built-in ones first; `undefined` when it passed. */\n readonly error?: ValidationError;\n}\n\n/** One channel's checks. */\nexport interface ChannelChecks {\n /** Checks a message about to be sent; synchronous when every schema it runs is. */\n readonly send: (\n type: string,\n data: unknown,\n call?: PartValidation,\n ) => Verdict | Promise<Verdict>;\n /** Checks a frame that arrived (a push or a reply); synchronous when every schema it runs is. */\n readonly receive: (type: string, data: unknown) => Verdict | Promise<Verdict>;\n /** What a send's failure does. */\n readonly onSend: FailureMode;\n /** What a received frame's failure does. */\n readonly onReceive: FailureMode | \"drop\";\n}\n\n/** What `./channel` hands the checks. */\nexport interface ChannelChecksContext {\n /** The channel's path: the errors' key. */\n readonly key: string;\n /** The client's mode is development (its defaults turn checks on). */\n readonly development: boolean;\n /** The client's validation, then the channel's. */\n readonly levels: readonly [\n ValidationConfig | false | undefined,\n ChannelValidationConfig | false | undefined,\n ];\n /** The shared contract's contract, when the channel was opened with one. */\n readonly contract: AnyContract | undefined;\n /** The channel's envelope and replies, to build a contract from generated schemas. */\n readonly meta: {\n readonly path: string;\n readonly idField: string;\n readonly replies: Readonly<Record<string, string>>;\n };\n /** The generated schemas (the client's `schemas` source's `channelSchemas` entry), when there is no contract. */\n readonly schemas: (() => Promise<ChannelSchemas | undefined>) | undefined;\n /** The error class, handed over so `./errors` stays in `./channel`'s chunk. */\n readonly ValidationError: typeof ValidationError;\n}\n\n/** One message type's validation, as the levels resolved it. */\ninterface State {\n /** Run the built-in schema. */\n enabled: boolean;\n /** The BYO schema, run after it. */\n custom: StandardSchemaV1 | undefined;\n /** Run only the BYO schema. */\n replace: boolean;\n}\n\n/** One direction's validation: every type's default, and the types with their own. */\ninterface Direction {\n /** Every type not in {@link types}. */\n base: State;\n /** Per type. */\n types: Map<string, State>;\n}\n\n/** Applies one level's entry (`true` on, `false` off, an object per key), as HTTP's parts do. */\nfunction apply(state: State, entry: PartValidation | undefined): void {\n if (entry === undefined) return;\n if (typeof entry === \"boolean\") {\n state.enabled = entry;\n if (!entry) {\n state.custom = undefined;\n state.replace = false;\n }\n return;\n }\n if (entry.enabled !== undefined) state.enabled = entry.enabled;\n if (entry.custom !== undefined) state.custom = entry.custom;\n if (entry.replace !== undefined) state.replace = entry.replace;\n}\n\n/** Applies a direction's entry at one level: a switch for every type, or per type. */\nfunction applyDirection(\n direction: Direction,\n entry: boolean | { readonly [type: string]: PartValidation } | undefined,\n): void {\n if (typeof entry === \"boolean\") {\n apply(direction.base, entry);\n for (const state of direction.types.values()) apply(state, entry);\n } else if (entry !== undefined) {\n for (const [type, part] of Object.entries(entry)) {\n const state = direction.types.get(type) ?? { ...direction.base };\n apply(state, part);\n direction.types.set(type, state);\n }\n }\n}\n\n/** `issues`, marked as `source`'s. */\nfunction mark(\n issues: readonly ValidationIssue[],\n source: \"builtin\" | \"custom\",\n): ValidationErrorIssue[] {\n return issues.map((issue) => ({\n ...issue,\n target: issue.target as ValidationErrorIssue[\"target\"],\n source,\n }));\n}\n\n/** `f(value)`, after `value` settles when it is a promise. */\nfunction then<T, R>(\n value: T | Promise<T>,\n f: (settled: T) => R | Promise<R>,\n): R | Promise<R> {\n return value instanceof Promise ? value.then(f) : f(value);\n}\n\n/**\n * Builds a channel's checks: its guards (from the shared contract, or a\n * contract built from the generated schemas, loaded now), and the levels\n * resolved over the mode's defaults (the client's `validate.request` for\n * sends and `validate.response` for what arrives, then the channel's).\n */\nexport async function createChannelChecks(\n context: ChannelChecksContext,\n): Promise<ChannelChecks> {\n const { development, key, meta } = context;\n let contract = context.contract;\n if (contract === undefined && context.schemas !== undefined) {\n const schemas = await context.schemas();\n if (schemas !== undefined)\n contract = channelContract({\n path: meta.path,\n receives: schemas.receives ?? {},\n sends: schemas.sends ?? {},\n replies: meta.replies,\n envelope: { idField: meta.idField },\n }).contract;\n }\n type Guard = (message: object) => unknown;\n const sender =\n contract === undefined\n ? undefined\n : (createSender(contract, \"inbound\") as unknown as Guard);\n const receiver =\n contract === undefined\n ? undefined\n : (createReceiver(contract, \"outbound\") as unknown as Guard);\n\n const fresh = (): Direction => ({\n base: { enabled: development, custom: undefined, replace: false },\n types: new Map(),\n });\n const send = fresh();\n const receive = fresh();\n let onSend: FailureMode = \"throw\";\n let onReceive: FailureMode | \"drop\" = \"warn\";\n const [client, channel] = context.levels;\n /** Applies one level: `false` turns everything off; else its two directions' entries and its failure modes. */\n const level = (\n off: boolean,\n sends: Parameters<typeof applyDirection>[1],\n receives: Parameters<typeof applyDirection>[1],\n failure:\n | FailureMode\n | {\n readonly send?: FailureMode;\n readonly receive?: FailureMode | \"drop\";\n }\n | undefined,\n ): void => {\n if (off) {\n for (const direction of [send, receive]) {\n apply(direction.base, false);\n direction.types.clear();\n }\n return;\n }\n applyDirection(send, sends);\n applyDirection(receive, receives);\n if (typeof failure === \"string\" || typeof failure === \"function\") {\n onSend = failure;\n onReceive = failure;\n } else if (failure !== undefined) {\n onSend = failure.send ?? onSend;\n onReceive = failure.receive ?? onReceive;\n }\n };\n // The client's level: `request` for sends, a boolean `response` for what\n // arrives (a per-status map is HTTP's alone), `onFailure` likewise.\n if (client !== undefined) {\n const failure = client === false ? undefined : client.onFailure;\n level(\n client === false,\n client === false ? undefined : client.request,\n client !== false && typeof client.response === \"boolean\"\n ? client.response\n : undefined,\n typeof failure === \"object\"\n ? { send: failure.request, receive: failure.response }\n : failure,\n );\n }\n if (channel !== undefined)\n level(\n channel === false,\n channel === false ? undefined : channel.send,\n channel === false ? undefined : channel.receive,\n channel === false ? undefined : channel.onFailure,\n );\n\n /** Runs one direction's checks on one message. */\n const run = (\n guard: Guard | undefined,\n state: State,\n direction: \"send\" | \"receive\",\n type: string,\n data: unknown,\n ): Verdict | Promise<Verdict> => {\n const builtin =\n state.enabled && !state.replace && guard !== undefined\n ? guard({ type, data })\n : undefined;\n return then(builtin, (outcome) => {\n const issues: ValidationErrorIssue[] = [];\n // A receiver's output (the built-in schema's) is what a BYO schema\n // reads; the data delivered stays as it arrived (§5.4).\n let checked = data;\n if (outcome instanceof ContractError)\n issues.push(...mark(outcome.issues, \"builtin\"));\n else if (outcome !== undefined && direction === \"receive\")\n checked = (outcome as { data?: unknown }).data;\n const verdict = (value: unknown): Verdict =>\n issues.length === 0\n ? { value }\n : {\n value: data,\n error: new context.ValidationError(key, direction, issues),\n };\n if (state.custom === undefined) return verdict(data);\n return then(\n validateStandard(\n state.custom,\n checked,\n \"data\",\n type,\n undefined,\n direction === \"receive\" ? { stripProtoKeys: \"nest\" } : undefined,\n ) as StandardOutcome<unknown> | Promise<StandardOutcome<unknown>>,\n (result) => {\n if (!result.ok) issues.push(...mark(result.issues, \"custom\"));\n return verdict(result.ok ? result.value : data);\n },\n );\n });\n };\n\n return {\n send: (type, data, call) => {\n let state = send.types.get(type) ?? send.base;\n if (call !== undefined) {\n state = { ...state };\n apply(state, call);\n }\n return run(sender, state, \"send\", type, data);\n },\n receive: (type, data) =>\n run(\n receiver,\n receive.types.get(type) ?? receive.base,\n \"receive\",\n type,\n data,\n ),\n onSend,\n onReceive,\n };\n}\n"
6
6
  ],
7
7
  "mappings": "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",
8
- "debugId": "02B94FA12596462164756E2164756E21",
8
+ "debugId": "A6A575F535E55BFA64756E2164756E21",
9
9
  "names": []
10
10
  }