@spencerboucher/switchyard 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +5 -4
  2. package/dist/bridge/switchyard-terminal.js +527 -0
  3. package/dist/switchyard.mjs +3471 -1846
  4. package/dist/terminal/holder.js +305 -0
  5. package/package.json +6 -3
  6. package/web-dist/assets/{AutomationView-BhEfjAtI.js → AutomationView-CFohp0SQ.js} +2 -2
  7. package/web-dist/assets/{AutomationView-BhEfjAtI.js.map → AutomationView-CFohp0SQ.js.map} +1 -1
  8. package/web-dist/assets/{InteractionCard-DxEZUNrD.js → InteractionCard-B-xmpTNG.js} +2 -2
  9. package/web-dist/assets/{InteractionCard-DxEZUNrD.js.map → InteractionCard-B-xmpTNG.js.map} +1 -1
  10. package/web-dist/assets/{Overlay-C-GqmwUn.js → Overlay--v0ur2lj.js} +2 -2
  11. package/web-dist/assets/{Overlay-C-GqmwUn.js.map → Overlay--v0ur2lj.js.map} +1 -1
  12. package/web-dist/assets/{attention-Dzqdk9Gz.js → attention-q6_xlnT4.js} +2 -2
  13. package/web-dist/assets/{attention-Dzqdk9Gz.js.map → attention-q6_xlnT4.js.map} +1 -1
  14. package/web-dist/assets/{automations-DzTTwxBl.js → automations-CyA83OAD.js} +2 -2
  15. package/web-dist/assets/{automations-DzTTwxBl.js.map → automations-CyA83OAD.js.map} +1 -1
  16. package/web-dist/assets/{board-C5mG_8rg.js → board-BFvNcFhk.js} +2 -2
  17. package/web-dist/assets/{board-C5mG_8rg.js.map → board-BFvNcFhk.js.map} +1 -1
  18. package/web-dist/assets/{code-9shyh1Gw.js → code-DavnmlQN.js} +2 -2
  19. package/web-dist/assets/{code-9shyh1Gw.js.map → code-DavnmlQN.js.map} +1 -1
  20. package/web-dist/assets/{describe-BbCZVvJP.js → describe-BirsjlUl.js} +2 -2
  21. package/web-dist/assets/{describe-BbCZVvJP.js.map → describe-BirsjlUl.js.map} +1 -1
  22. package/web-dist/assets/dist-C-h8FC-n.js +56 -0
  23. package/web-dist/assets/dist-C-h8FC-n.js.map +1 -0
  24. package/web-dist/assets/{enroll-DWJANJtw.js → enroll-C7UBC20s.js} +2 -2
  25. package/web-dist/assets/{enroll-DWJANJtw.js.map → enroll-C7UBC20s.js.map} +1 -1
  26. package/web-dist/assets/{index-afOJ8NRR.js → index-IdzOP9GE.js} +3 -3
  27. package/web-dist/assets/{index-afOJ8NRR.js.map → index-IdzOP9GE.js.map} +1 -1
  28. package/web-dist/assets/{more-B6qxC6M-.js → more-syTnSpW3.js} +2 -2
  29. package/web-dist/assets/{more-B6qxC6M-.js.map → more-syTnSpW3.js.map} +1 -1
  30. package/web-dist/assets/{now-BOWxgbt3.js → now-BqsLS-tK.js} +2 -2
  31. package/web-dist/assets/{now-BOWxgbt3.js.map → now-BqsLS-tK.js.map} +1 -1
  32. package/web-dist/assets/{settings-BpkmIv_4.js → settings-DRj7UPtP.js} +2 -2
  33. package/web-dist/assets/{settings-BpkmIv_4.js.map → settings-DRj7UPtP.js.map} +1 -1
  34. package/web-dist/assets/{useNow-D9Zxr5Jb.js → useNow-BQpDYijR.js} +2 -2
  35. package/web-dist/assets/{useNow-D9Zxr5Jb.js.map → useNow-BQpDYijR.js.map} +1 -1
  36. package/web-dist/index.html +1 -1
  37. package/web-dist/sw.js +1 -1
  38. package/web-dist/assets/dist-PaQekTP8.js +0 -56
  39. package/web-dist/assets/dist-PaQekTP8.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{_ as e,a as t,f as n,m as r,n as i,t as a}from"./jsx-runtime-D13aM9HY.js";import{G as o,U as s,V as c,g as l,l as u,n as d,o as f}from"./states-DrTQuYXw.js";import{t as p}from"./key-round-VDbE9E4X.js";import{H as m,R as h,V as g}from"./index-afOJ8NRR.js";import{n as _}from"./TextArea-BsQHOQMe.js";import{a as v,h as y,i as b,n as x,o as S,r as C,s as w,t as T,u as E}from"./logic-D_Eq9DMW.js";var D={name:`log-in`,size:24,node:[[`path`,{d:`m10 17 5-5-5-5`,key:`1bsop3`}],[`path`,{d:`M15 12H3`,key:`6jk70r`}],[`path`,{d:`M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4`,key:`u53s6r`}]]};D.node;var O=i(D),k=e(r(),1),A=a();function j(){let e=new URLSearchParams(location.hash.replace(/^#/,``)),t=new URLSearchParams(location.search);return e.get(`token`)??t.get(`token`)}function M(){let e=n(),[r]=t(),i=r.get(`next`),a=(0,k.useMemo)(j,[]),c=m(e=>e.status),l=m(e=>e.signedOutReason),[d,v]=(0,k.useState)(()=>C()),[y,b]=(0,k.useState)(!1),[x,S]=(0,k.useState)(null);return(0,k.useEffect)(()=>{a&&history.replaceState(history.state,``,location.pathname+location.search)},[a]),(0,k.useEffect)(()=>{!a&&c===`authenticated`&&e(g(i),{replace:!0})},[a,c,i,e]),!a&&c===`checking`?(0,A.jsx)(f,{label:`Checking this device…`}):(0,A.jsx)(`div`,{className:`flex min-h-dvh items-start justify-center px-4 py-10 safe-pt safe-pb sm:items-center`,children:(0,A.jsxs)(`div`,{className:`w-full max-w-sm rounded-xl border border-border bg-surface p-5 shadow-sm`,children:[(0,A.jsxs)(`div`,{className:`mb-4 flex items-center gap-3`,children:[(0,A.jsx)(`img`,{src:`/icon.svg`,alt:``,className:`size-10 rounded-lg`}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h1`,{className:`text-lg font-semibold`,children:`Switchyard`}),(0,A.jsx)(`p`,{className:`text-sm text-muted`,children:a?`Enroll this device`:`Sign in`})]})]}),l&&(0,A.jsx)(`p`,{role:`status`,className:`mb-3 rounded-md bg-warning-soft px-3 py-2 text-sm`,children:l}),a?(0,A.jsxs)(`form`,{onSubmit:async t=>{if(t.preventDefault(),a&&d.trim()){b(!0),S(null);try{await s(`/api/enroll`,{token:a,name:d.trim().slice(0,80),kind:E()?`pwa`:`browser`}),await h()===`authenticated`?e(g(i),{replace:!0}):S({code:`internal`,message:`Enrolled, but the coordinator did not accept the new credential. Try again.`})}catch(e){let t=o(e)?e.code:`internal`;S({code:t,message:T[t]??(e instanceof Error?e.message:String(e))})}finally{b(!1)}}},className:`flex flex-col gap-3`,noValidate:!0,children:[(0,A.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,A.jsx)(`label`,{htmlFor:`device-name`,className:`text-sm font-medium`,children:`Device name`}),(0,A.jsx)(_,{id:`device-name`,value:d,maxLength:80,autoComplete:`off`,onChange:e=>v(e.target.value),"aria-describedby":`device-name-hint`}),(0,A.jsx)(`p`,{id:`device-name-hint`,className:`text-xs text-muted`,children:`Shown in Settings › Devices, where you can revoke it later.`})]}),x&&(0,A.jsxs)(`div`,{role:`alert`,className:`rounded-md border border-border bg-danger-soft px-3 py-2 text-sm`,children:[(0,A.jsx)(`div`,{className:`font-medium`,children:x.code===`enrollment_expired`?`Enrollment link expired`:x.code===`enrollment_used`?`Enrollment link already used`:`Enrollment failed`}),(0,A.jsx)(`div`,{className:`text-muted`,children:x.message})]}),(0,A.jsx)(u,{type:`submit`,variant:`primary`,busy:y,disabled:!d.trim()||x?.code===`enrollment_expired`||x?.code===`enrollment_used`,icon:(0,A.jsx)(p,{className:`size-4`}),children:`Enroll device`})]}):(0,A.jsx)(F,{name:d,onName:v,next:i})]})})}var N=()=>E()?`pwa`:`browser`;function P({title:e,children:t}){return(0,A.jsxs)(`div`,{role:`alert`,className:`rounded-md border border-border bg-danger-soft px-3 py-2 text-sm`,children:[(0,A.jsxs)(`div`,{className:`flex items-center gap-1.5 font-medium`,children:[(0,A.jsx)(l,{"aria-hidden":!0,className:`size-4 shrink-0`}),e]}),(0,A.jsx)(`div`,{className:`break-words text-muted`,children:t})]})}function F({name:e,onName:r,next:i}){let a=n(),[l,m]=t(),[v,x]=(0,k.useState)(null),[S,C]=(0,k.useState)(null),[w,T]=(0,k.useState)(0),[D,j]=(0,k.useState)(null),[M,F]=(0,k.useState)(()=>{let e=l.get(`error`);return e?{code:e,message:b(e,l.get(`detail`))}:null}),[L,R]=(0,k.useState)(null),z=(0,k.useRef)(null);(0,k.useEffect)(()=>{if(!l.has(`error`)&&!l.has(`detail`))return;let e=new URLSearchParams(l);e.delete(`error`),e.delete(`detail`),m(e,{replace:!0})},[]),(0,k.useEffect)(()=>{let e=new AbortController;return C(null),c(`/api/setup`,void 0,e.signal).then(t=>!e.signal.aborted&&x(t)).catch(t=>!e.signal.aborted&&C(t)),()=>e.abort()},[w]);let B=e=>{let t=o(e)?e.code:`internal`;e instanceof Error&&e.name===`NotAllowedError`?F({code:t,message:`Passkey sign-in was cancelled or timed out.`}):F({code:t,message:o(e)?b(t,e.message):e instanceof Error?e.message:String(e)})},V=(0,k.useCallback)(async()=>{await h()===`authenticated`?a(g(i),{replace:!0}):F({code:`internal`,message:`Signed in, but the coordinator did not accept the new credential. Try again.`})},[a,i]),H=e.trim().slice(0,80),U=async()=>{if(H){j(`passkey`),F(null);try{let{challengeId:e,options:t}=await s(`/api/auth/passkey/options`),n=await y({optionsJSON:t});await s(`/api/auth/passkey/verify`,{challengeId:e,response:n,deviceName:H,kind:N()}),await V()}catch(e){B(e)}finally{j(null)}}},W=async()=>{if(!H)return;j(`oidc`),F(null);let e=E();try{let t=await s(`/api/auth/oidc/begin`,{deviceName:H,kind:N(),next:g(i),handoff:e});if(e&&t.flowId&&t.pollToken)z.current=t.authorizeUrl,R({flowId:t.flowId,pollToken:t.pollToken}),window.open(t.authorizeUrl,`_blank`);else{location.assign(t.authorizeUrl);return}}catch(e){B(e)}j(null)};if(S)return(0,A.jsx)(d,{title:`Cannot reach Switchyard`,error:S,onRetry:()=>T(e=>e+1)});if(!v)return(0,A.jsx)(f,{label:`Checking this server…`});if(!v.initialized)return(0,A.jsxs)(`div`,{"data-testid":`setup-not-initialized`,className:`flex flex-col gap-2 text-sm`,children:[(0,A.jsx)(`p`,{className:`font-medium`,children:`Switchyard isn't set up yet`}),(0,A.jsxs)(`p`,{className:`text-muted`,children:[`Run `,(0,A.jsx)(`code`,{children:`switchyard init`}),` on the server to create the first sign-in link. With Docker: `,(0,A.jsx)(`code`,{children:`docker exec <container> switchyard init`}),`.`]})]});if(L)return(0,A.jsx)(I,{flowId:L.flowId,pollToken:L.pollToken,authorizeUrl:z.current,onDone:V,onExit:()=>R(null)});let G=v.passkey.registered&&v.passkey.available;return(0,A.jsxs)(`div`,{className:`flex flex-col gap-4 text-sm`,children:[(0,A.jsx)(`p`,{className:`text-base font-medium`,children:`Sign in to Switchyard`}),M&&(0,A.jsx)(P,{title:`Sign-in failed`,children:M.message}),(0,A.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,A.jsx)(`label`,{htmlFor:`device-name`,className:`text-sm font-medium`,children:`Device name`}),(0,A.jsx)(_,{id:`device-name`,value:e,maxLength:80,autoComplete:`off`,onChange:e=>r(e.target.value),"aria-describedby":`device-name-hint`}),(0,A.jsx)(`p`,{id:`device-name-hint`,className:`text-xs text-muted`,children:`Shown in Settings › Devices, where you can revoke it later.`})]}),(0,A.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[G&&(0,A.jsx)(u,{"data-testid":`signin-passkey`,variant:`primary`,busy:D===`passkey`,disabled:!H||D!==null,icon:(0,A.jsx)(p,{className:`size-4`}),onClick:U,children:`Sign in with a passkey`}),v.oidc&&(0,A.jsx)(u,{"data-testid":`signin-oidc`,variant:G?`secondary`:`primary`,busy:D===`oidc`,disabled:!H||D!==null,icon:(0,A.jsx)(O,{className:`size-4`}),onClick:W,children:v.oidc.label}),v.passkey.registered&&!v.passkey.available&&v.passkey.reason&&(0,A.jsxs)(`p`,{className:`text-xs text-muted`,children:[`Passkeys are unavailable: `,v.passkey.reason,`.`]}),!H&&(G||v.oidc)&&(0,A.jsx)(`p`,{className:`text-xs text-muted`,children:`Enter a device name to sign in.`})]}),(0,A.jsx)(`p`,{className:`text-muted`,children:`Or open a pairing link from a device you already use: Settings › Devices › Add device.`}),(0,A.jsxs)(`p`,{className:`text-xs text-muted`,children:[`Lost every device? Run `,(0,A.jsx)(`code`,{children:`switchyard pair`}),` on the server.`]})]})}function I({flowId:e,pollToken:t,authorizeUrl:n,onDone:r,onExit:i}){let[a,c]=(0,k.useState)(()=>w(Date.now())),l=(0,k.useRef)(a);l.current=a;let d=(0,k.useRef)(!1),f=(0,k.useRef)(r);f.current=r;let p=(0,k.useCallback)(e=>{let t=v(l.current,e);return l.current=t,c(t),t},[]),m=(0,k.useCallback)(async()=>{if(!(d.current||l.current.phase!==`waiting`)){d.current=!0;try{let n=await s(`/api/auth/oidc/claim`,{flowId:e,pollToken:t});p({type:`claim`,result:n,now:Date.now()}).phase===`done`&&await f.current()}catch(e){p({type:`claim_error`,code:o(e)?e.code:`network`,message:`This sign-in expired or was already used. Start again.`,now:Date.now()})}finally{d.current=!1}}},[e,t,p]);return(0,k.useEffect)(()=>{let e=()=>document.visibilityState===`visible`,t=setInterval(()=>{p({type:`tick`,now:Date.now()}),S(l.current,e())&&m()},x),n=()=>{S(l.current,e())&&m()};return document.addEventListener(`visibilitychange`,n),()=>{clearInterval(t),document.removeEventListener(`visibilitychange`,n)}},[m,p]),a.phase===`timeout`||a.phase===`failed`||a.phase===`cancelled`?(0,A.jsxs)(`div`,{"data-testid":`signin-handoff`,className:`flex flex-col gap-3 text-sm`,children:[(0,A.jsx)(P,{title:a.phase===`timeout`?`Sign-in timed out`:`Sign-in stopped`,children:a.phase===`timeout`?`The sign-in was not finished in time. Start again.`:a.message??`Start again.`}),(0,A.jsx)(u,{onClick:i,children:`Back to sign-in`})]}):(0,A.jsxs)(`div`,{"data-testid":`signin-handoff`,role:`status`,className:`flex flex-col gap-3 text-sm`,children:[(0,A.jsx)(`p`,{className:`font-medium`,children:`Finish signing in, then come back here`}),(0,A.jsx)(`p`,{className:`text-muted`,children:`Complete single sign-on in the page that opened, then return to this app. This screen continues on its own.`}),n&&(0,A.jsx)(`a`,{href:n,target:`_blank`,rel:`noreferrer`,className:`font-medium text-accent underline`,children:`Open the sign-in page again`}),(0,A.jsx)(u,{onClick:()=>{p({type:`cancel`}),i()},children:`Cancel`})]})}export{M as default,C as defaultDeviceName};
2
- //# sourceMappingURL=enroll-DWJANJtw.js.map
1
+ import{_ as e,a as t,f as n,m as r,n as i,t as a}from"./jsx-runtime-D13aM9HY.js";import{G as o,U as s,V as c,g as l,l as u,n as d,o as f}from"./states-DrTQuYXw.js";import{t as p}from"./key-round-VDbE9E4X.js";import{H as m,R as h,V as g}from"./index-IdzOP9GE.js";import{n as _}from"./TextArea-BsQHOQMe.js";import{a as v,h as y,i as b,n as x,o as S,r as C,s as w,t as T,u as E}from"./logic-D_Eq9DMW.js";var D={name:`log-in`,size:24,node:[[`path`,{d:`m10 17 5-5-5-5`,key:`1bsop3`}],[`path`,{d:`M15 12H3`,key:`6jk70r`}],[`path`,{d:`M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4`,key:`u53s6r`}]]};D.node;var O=i(D),k=e(r(),1),A=a();function j(){let e=new URLSearchParams(location.hash.replace(/^#/,``)),t=new URLSearchParams(location.search);return e.get(`token`)??t.get(`token`)}function M(){let e=n(),[r]=t(),i=r.get(`next`),a=(0,k.useMemo)(j,[]),c=m(e=>e.status),l=m(e=>e.signedOutReason),[d,v]=(0,k.useState)(()=>C()),[y,b]=(0,k.useState)(!1),[x,S]=(0,k.useState)(null);return(0,k.useEffect)(()=>{a&&history.replaceState(history.state,``,location.pathname+location.search)},[a]),(0,k.useEffect)(()=>{!a&&c===`authenticated`&&e(g(i),{replace:!0})},[a,c,i,e]),!a&&c===`checking`?(0,A.jsx)(f,{label:`Checking this device…`}):(0,A.jsx)(`div`,{className:`flex min-h-dvh items-start justify-center px-4 py-10 safe-pt safe-pb sm:items-center`,children:(0,A.jsxs)(`div`,{className:`w-full max-w-sm rounded-xl border border-border bg-surface p-5 shadow-sm`,children:[(0,A.jsxs)(`div`,{className:`mb-4 flex items-center gap-3`,children:[(0,A.jsx)(`img`,{src:`/icon.svg`,alt:``,className:`size-10 rounded-lg`}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h1`,{className:`text-lg font-semibold`,children:`Switchyard`}),(0,A.jsx)(`p`,{className:`text-sm text-muted`,children:a?`Enroll this device`:`Sign in`})]})]}),l&&(0,A.jsx)(`p`,{role:`status`,className:`mb-3 rounded-md bg-warning-soft px-3 py-2 text-sm`,children:l}),a?(0,A.jsxs)(`form`,{onSubmit:async t=>{if(t.preventDefault(),a&&d.trim()){b(!0),S(null);try{await s(`/api/enroll`,{token:a,name:d.trim().slice(0,80),kind:E()?`pwa`:`browser`}),await h()===`authenticated`?e(g(i),{replace:!0}):S({code:`internal`,message:`Enrolled, but the coordinator did not accept the new credential. Try again.`})}catch(e){let t=o(e)?e.code:`internal`;S({code:t,message:T[t]??(e instanceof Error?e.message:String(e))})}finally{b(!1)}}},className:`flex flex-col gap-3`,noValidate:!0,children:[(0,A.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,A.jsx)(`label`,{htmlFor:`device-name`,className:`text-sm font-medium`,children:`Device name`}),(0,A.jsx)(_,{id:`device-name`,value:d,maxLength:80,autoComplete:`off`,onChange:e=>v(e.target.value),"aria-describedby":`device-name-hint`}),(0,A.jsx)(`p`,{id:`device-name-hint`,className:`text-xs text-muted`,children:`Shown in Settings › Devices, where you can revoke it later.`})]}),x&&(0,A.jsxs)(`div`,{role:`alert`,className:`rounded-md border border-border bg-danger-soft px-3 py-2 text-sm`,children:[(0,A.jsx)(`div`,{className:`font-medium`,children:x.code===`enrollment_expired`?`Enrollment link expired`:x.code===`enrollment_used`?`Enrollment link already used`:`Enrollment failed`}),(0,A.jsx)(`div`,{className:`text-muted`,children:x.message})]}),(0,A.jsx)(u,{type:`submit`,variant:`primary`,busy:y,disabled:!d.trim()||x?.code===`enrollment_expired`||x?.code===`enrollment_used`,icon:(0,A.jsx)(p,{className:`size-4`}),children:`Enroll device`})]}):(0,A.jsx)(F,{name:d,onName:v,next:i})]})})}var N=()=>E()?`pwa`:`browser`;function P({title:e,children:t}){return(0,A.jsxs)(`div`,{role:`alert`,className:`rounded-md border border-border bg-danger-soft px-3 py-2 text-sm`,children:[(0,A.jsxs)(`div`,{className:`flex items-center gap-1.5 font-medium`,children:[(0,A.jsx)(l,{"aria-hidden":!0,className:`size-4 shrink-0`}),e]}),(0,A.jsx)(`div`,{className:`break-words text-muted`,children:t})]})}function F({name:e,onName:r,next:i}){let a=n(),[l,m]=t(),[v,x]=(0,k.useState)(null),[S,C]=(0,k.useState)(null),[w,T]=(0,k.useState)(0),[D,j]=(0,k.useState)(null),[M,F]=(0,k.useState)(()=>{let e=l.get(`error`);return e?{code:e,message:b(e,l.get(`detail`))}:null}),[L,R]=(0,k.useState)(null),z=(0,k.useRef)(null);(0,k.useEffect)(()=>{if(!l.has(`error`)&&!l.has(`detail`))return;let e=new URLSearchParams(l);e.delete(`error`),e.delete(`detail`),m(e,{replace:!0})},[]),(0,k.useEffect)(()=>{let e=new AbortController;return C(null),c(`/api/setup`,void 0,e.signal).then(t=>!e.signal.aborted&&x(t)).catch(t=>!e.signal.aborted&&C(t)),()=>e.abort()},[w]);let B=e=>{let t=o(e)?e.code:`internal`;e instanceof Error&&e.name===`NotAllowedError`?F({code:t,message:`Passkey sign-in was cancelled or timed out.`}):F({code:t,message:o(e)?b(t,e.message):e instanceof Error?e.message:String(e)})},V=(0,k.useCallback)(async()=>{await h()===`authenticated`?a(g(i),{replace:!0}):F({code:`internal`,message:`Signed in, but the coordinator did not accept the new credential. Try again.`})},[a,i]),H=e.trim().slice(0,80),U=async()=>{if(H){j(`passkey`),F(null);try{let{challengeId:e,options:t}=await s(`/api/auth/passkey/options`),n=await y({optionsJSON:t});await s(`/api/auth/passkey/verify`,{challengeId:e,response:n,deviceName:H,kind:N()}),await V()}catch(e){B(e)}finally{j(null)}}},W=async()=>{if(!H)return;j(`oidc`),F(null);let e=E();try{let t=await s(`/api/auth/oidc/begin`,{deviceName:H,kind:N(),next:g(i),handoff:e});if(e&&t.flowId&&t.pollToken)z.current=t.authorizeUrl,R({flowId:t.flowId,pollToken:t.pollToken}),window.open(t.authorizeUrl,`_blank`);else{location.assign(t.authorizeUrl);return}}catch(e){B(e)}j(null)};if(S)return(0,A.jsx)(d,{title:`Cannot reach Switchyard`,error:S,onRetry:()=>T(e=>e+1)});if(!v)return(0,A.jsx)(f,{label:`Checking this server…`});if(!v.initialized)return(0,A.jsxs)(`div`,{"data-testid":`setup-not-initialized`,className:`flex flex-col gap-2 text-sm`,children:[(0,A.jsx)(`p`,{className:`font-medium`,children:`Switchyard isn't set up yet`}),(0,A.jsxs)(`p`,{className:`text-muted`,children:[`Run `,(0,A.jsx)(`code`,{children:`switchyard init`}),` on the server to create the first sign-in link. With Docker: `,(0,A.jsx)(`code`,{children:`docker exec <container> switchyard init`}),`.`]})]});if(L)return(0,A.jsx)(I,{flowId:L.flowId,pollToken:L.pollToken,authorizeUrl:z.current,onDone:V,onExit:()=>R(null)});let G=v.passkey.registered&&v.passkey.available;return(0,A.jsxs)(`div`,{className:`flex flex-col gap-4 text-sm`,children:[(0,A.jsx)(`p`,{className:`text-base font-medium`,children:`Sign in to Switchyard`}),M&&(0,A.jsx)(P,{title:`Sign-in failed`,children:M.message}),(0,A.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,A.jsx)(`label`,{htmlFor:`device-name`,className:`text-sm font-medium`,children:`Device name`}),(0,A.jsx)(_,{id:`device-name`,value:e,maxLength:80,autoComplete:`off`,onChange:e=>r(e.target.value),"aria-describedby":`device-name-hint`}),(0,A.jsx)(`p`,{id:`device-name-hint`,className:`text-xs text-muted`,children:`Shown in Settings › Devices, where you can revoke it later.`})]}),(0,A.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[G&&(0,A.jsx)(u,{"data-testid":`signin-passkey`,variant:`primary`,busy:D===`passkey`,disabled:!H||D!==null,icon:(0,A.jsx)(p,{className:`size-4`}),onClick:U,children:`Sign in with a passkey`}),v.oidc&&(0,A.jsx)(u,{"data-testid":`signin-oidc`,variant:G?`secondary`:`primary`,busy:D===`oidc`,disabled:!H||D!==null,icon:(0,A.jsx)(O,{className:`size-4`}),onClick:W,children:v.oidc.label}),v.passkey.registered&&!v.passkey.available&&v.passkey.reason&&(0,A.jsxs)(`p`,{className:`text-xs text-muted`,children:[`Passkeys are unavailable: `,v.passkey.reason,`.`]}),!H&&(G||v.oidc)&&(0,A.jsx)(`p`,{className:`text-xs text-muted`,children:`Enter a device name to sign in.`})]}),(0,A.jsx)(`p`,{className:`text-muted`,children:`Or open a pairing link from a device you already use: Settings › Devices › Add device.`}),(0,A.jsxs)(`p`,{className:`text-xs text-muted`,children:[`Lost every device? Run `,(0,A.jsx)(`code`,{children:`switchyard pair`}),` on the server.`]})]})}function I({flowId:e,pollToken:t,authorizeUrl:n,onDone:r,onExit:i}){let[a,c]=(0,k.useState)(()=>w(Date.now())),l=(0,k.useRef)(a);l.current=a;let d=(0,k.useRef)(!1),f=(0,k.useRef)(r);f.current=r;let p=(0,k.useCallback)(e=>{let t=v(l.current,e);return l.current=t,c(t),t},[]),m=(0,k.useCallback)(async()=>{if(!(d.current||l.current.phase!==`waiting`)){d.current=!0;try{let n=await s(`/api/auth/oidc/claim`,{flowId:e,pollToken:t});p({type:`claim`,result:n,now:Date.now()}).phase===`done`&&await f.current()}catch(e){p({type:`claim_error`,code:o(e)?e.code:`network`,message:`This sign-in expired or was already used. Start again.`,now:Date.now()})}finally{d.current=!1}}},[e,t,p]);return(0,k.useEffect)(()=>{let e=()=>document.visibilityState===`visible`,t=setInterval(()=>{p({type:`tick`,now:Date.now()}),S(l.current,e())&&m()},x),n=()=>{S(l.current,e())&&m()};return document.addEventListener(`visibilitychange`,n),()=>{clearInterval(t),document.removeEventListener(`visibilitychange`,n)}},[m,p]),a.phase===`timeout`||a.phase===`failed`||a.phase===`cancelled`?(0,A.jsxs)(`div`,{"data-testid":`signin-handoff`,className:`flex flex-col gap-3 text-sm`,children:[(0,A.jsx)(P,{title:a.phase===`timeout`?`Sign-in timed out`:`Sign-in stopped`,children:a.phase===`timeout`?`The sign-in was not finished in time. Start again.`:a.message??`Start again.`}),(0,A.jsx)(u,{onClick:i,children:`Back to sign-in`})]}):(0,A.jsxs)(`div`,{"data-testid":`signin-handoff`,role:`status`,className:`flex flex-col gap-3 text-sm`,children:[(0,A.jsx)(`p`,{className:`font-medium`,children:`Finish signing in, then come back here`}),(0,A.jsx)(`p`,{className:`text-muted`,children:`Complete single sign-on in the page that opened, then return to this app. This screen continues on its own.`}),n&&(0,A.jsx)(`a`,{href:n,target:`_blank`,rel:`noreferrer`,className:`font-medium text-accent underline`,children:`Open the sign-in page again`}),(0,A.jsx)(u,{onClick:()=>{p({type:`cancel`}),i()},children:`Cancel`})]})}export{M as default,C as defaultDeviceName};
2
+ //# sourceMappingURL=enroll-C7UBC20s.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"enroll-DWJANJtw.js","names":["useMemo","useState","useRef","useCallback","__toESM","useSearchParams","useNavigate","require_react","createLucideIcon","require_jsx_runtime","isApiError","apiPost","apiGet","TriangleAlert","Button","ErrorState","LoadingState","KeyRound","useAuth","checkAuth","takeReturnTo","TextInput","handoffReduce","startAuthentication","enrollErrorText","HANDOFF_POLL_MS","shouldPoll","defaultDeviceName","startHandoff","ERROR_TEXT","isStandalone","__iconData","LogIn","import_react","import_jsx_runtime","readToken","hash","query","EnrollView","navigate","params","next","token","status","s","signedOutReason","name","setName","busy","setBusy","error","setError","e","err","code","SignInPanel","kindOf","AlertBox","title","setParams","setup","setSetup","loadError","setLoadError","nonce","setNonce","notice","setNotice","handoff","setHandoff","authorizeUrlRef","rest","ac","v","fail","finish","deviceName","signInWithPasskey","response","options","signInWithOidc","standalone","res","n","HandoffScreen","passkeyReady","onName","state","setState","stateRef","inFlight","doneRef","onDone","apply","event","poll","result","flowId","pollToken","visible","timer","onVisible","onExit","authorizeUrl"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/log-in.mjs","../../src/features/enroll/index.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"log-in\",\n size: 24,\n node: [\n [\"path\", { d: \"m10 17 5-5-5-5\", key: \"1bsop3\" }],\n [\"path\", { d: \"M15 12H3\", key: \"6jk70r\" }],\n [\"path\", { d: \"M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4\", key: \"u53s6r\" }]\n ]\n};\n__iconData.node;\nconst LogIn = createLucideIcon(__iconData);\n\nexport { __iconData, LogIn as default };\n//# sourceMappingURL=log-in.mjs.map\n","import { startAuthentication } from \"@simplewebauthn/browser\";\nimport type { OidcBeginResponse, OidcClaimResponse, SetupState } from \"@switchyard/protocol\";\nimport { KeyRound, LogIn, TriangleAlert } from \"lucide-react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useNavigate, useSearchParams } from \"react-router\";\nimport { checkAuth, takeReturnTo, useAuth } from \"../../app/auth\";\nimport { Button } from \"../../components/ui/Button\";\nimport { ErrorState, LoadingState } from \"../../components/ui/states\";\nimport { TextInput } from \"../../components/ui/TextArea\";\nimport { isStandalone } from \"../../push\";\nimport { apiGet, apiPost, isApiError } from \"../../sync/api\";\nimport {\n defaultDeviceName,\n ERROR_TEXT,\n enrollErrorText,\n HANDOFF_POLL_MS,\n type HandoffState,\n handoffReduce,\n shouldPoll,\n startHandoff,\n} from \"./logic\";\n\nexport { defaultDeviceName };\n\nfunction readToken(): string | null {\n const hash = new URLSearchParams(location.hash.replace(/^#/, \"\"));\n const query = new URLSearchParams(location.search);\n return hash.get(\"token\") ?? query.get(\"token\");\n}\n\n/**\n * /enroll: redeem a single-use enrollment token from `#token=`, or (without\n * one) sign in with a passkey or single sign-on, name this device, then\n * return to the deep link that required authentication.\n */\nexport default function EnrollView() {\n const navigate = useNavigate();\n const [params] = useSearchParams();\n const next = params.get(\"next\");\n const token = useMemo(readToken, []);\n const status = useAuth((s) => s.status);\n const signedOutReason = useAuth((s) => s.signedOutReason);\n const [name, setName] = useState(() => defaultDeviceName());\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<{ code: string; message: string } | null>(null);\n\n // Keep the token out of the address bar and history.\n useEffect(() => {\n if (token) history.replaceState(history.state, \"\", location.pathname + location.search);\n }, [token]);\n\n // Already enrolled and no token: go back to the deep link.\n useEffect(() => {\n if (!token && status === \"authenticated\") navigate(takeReturnTo(next), { replace: true });\n }, [token, status, next, navigate]);\n\n const submit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!token || !name.trim()) return;\n setBusy(true);\n setError(null);\n try {\n await apiPost(\"/api/enroll\", {\n token,\n name: name.trim().slice(0, 80),\n kind: isStandalone() ? \"pwa\" : \"browser\",\n });\n const result = await checkAuth();\n if (result === \"authenticated\") navigate(takeReturnTo(next), { replace: true });\n else\n setError({\n code: \"internal\",\n message: \"Enrolled, but the coordinator did not accept the new credential. Try again.\",\n });\n } catch (err) {\n const code = isApiError(err) ? err.code : \"internal\";\n setError({\n code,\n message: ERROR_TEXT[code] ?? (err instanceof Error ? err.message : String(err)),\n });\n } finally {\n setBusy(false);\n }\n };\n\n if (!token && status === \"checking\") return <LoadingState label=\"Checking this device…\" />;\n\n return (\n <div className=\"flex min-h-dvh items-start justify-center px-4 py-10 safe-pt safe-pb sm:items-center\">\n <div className=\"w-full max-w-sm rounded-xl border border-border bg-surface p-5 shadow-sm\">\n <div className=\"mb-4 flex items-center gap-3\">\n <img src=\"/icon.svg\" alt=\"\" className=\"size-10 rounded-lg\" />\n <div>\n <h1 className=\"text-lg font-semibold\">Switchyard</h1>\n <p className=\"text-sm text-muted\">{token ? \"Enroll this device\" : \"Sign in\"}</p>\n </div>\n </div>\n {signedOutReason && (\n <p role=\"status\" className=\"mb-3 rounded-md bg-warning-soft px-3 py-2 text-sm\">\n {signedOutReason}\n </p>\n )}\n {token ? (\n <form onSubmit={submit} className=\"flex flex-col gap-3\" noValidate>\n <div className=\"flex flex-col gap-1.5\">\n <label htmlFor=\"device-name\" className=\"text-sm font-medium\">\n Device name\n </label>\n <TextInput\n id=\"device-name\"\n value={name}\n maxLength={80}\n autoComplete=\"off\"\n onChange={(e) => setName(e.target.value)}\n aria-describedby=\"device-name-hint\"\n />\n <p id=\"device-name-hint\" className=\"text-xs text-muted\">\n Shown in Settings › Devices, where you can revoke it later.\n </p>\n </div>\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-border bg-danger-soft px-3 py-2 text-sm\"\n >\n <div className=\"font-medium\">\n {error.code === \"enrollment_expired\"\n ? \"Enrollment link expired\"\n : error.code === \"enrollment_used\"\n ? \"Enrollment link already used\"\n : \"Enrollment failed\"}\n </div>\n <div className=\"text-muted\">{error.message}</div>\n </div>\n )}\n <Button\n type=\"submit\"\n variant=\"primary\"\n busy={busy}\n disabled={\n !name.trim() ||\n error?.code === \"enrollment_expired\" ||\n error?.code === \"enrollment_used\"\n }\n icon={<KeyRound className=\"size-4\" />}\n >\n Enroll device\n </Button>\n </form>\n ) : (\n <SignInPanel name={name} onName={setName} next={next} />\n )}\n </div>\n </div>\n );\n}\n\ninterface ChallengeResponse {\n challengeId: string;\n options: unknown;\n}\n\nconst kindOf = () => (isStandalone() ? \"pwa\" : \"browser\");\n\nfunction AlertBox({ title, children }: { title: string; children: React.ReactNode }) {\n return (\n <div role=\"alert\" className=\"rounded-md border border-border bg-danger-soft px-3 py-2 text-sm\">\n <div className=\"flex items-center gap-1.5 font-medium\">\n <TriangleAlert aria-hidden className=\"size-4 shrink-0\" />\n {title}\n </div>\n <div className=\"break-words text-muted\">{children}</div>\n </div>\n );\n}\n\n/** The no-token screen: not-initialized notice, or the sign-in options. */\nfunction SignInPanel({\n name,\n onName,\n next,\n}: {\n name: string;\n onName: (v: string) => void;\n next: string | null;\n}) {\n const navigate = useNavigate();\n const [params, setParams] = useSearchParams();\n const [setup, setSetup] = useState<SetupState | null>(null);\n const [loadError, setLoadError] = useState<unknown>(null);\n const [nonce, setNonce] = useState(0);\n const [busy, setBusy] = useState<\"passkey\" | \"oidc\" | null>(null);\n const [notice, setNotice] = useState<{ code: string; message: string } | null>(() => {\n const code = params.get(\"error\");\n return code ? { code, message: enrollErrorText(code, params.get(\"detail\")) } : null;\n });\n const [handoff, setHandoff] = useState<{ flowId: string; pollToken: string } | null>(null);\n const authorizeUrlRef = useRef<string | null>(null);\n\n // Show a callback error once, then drop it from the address bar.\n // biome-ignore lint/correctness/useExhaustiveDependencies: runs once on mount\n useEffect(() => {\n if (!params.has(\"error\") && !params.has(\"detail\")) return;\n const rest = new URLSearchParams(params);\n rest.delete(\"error\");\n rest.delete(\"detail\");\n setParams(rest, { replace: true });\n }, []);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: `nonce` reloads\n useEffect(() => {\n const ac = new AbortController();\n setLoadError(null);\n apiGet<SetupState>(\"/api/setup\", undefined, ac.signal)\n .then((v) => !ac.signal.aborted && setSetup(v))\n .catch((e: unknown) => !ac.signal.aborted && setLoadError(e));\n return () => ac.abort();\n }, [nonce]);\n\n const fail = (err: unknown) => {\n const code = isApiError(err) ? err.code : \"internal\";\n // A cancelled or dismissed WebAuthn prompt is not an error worth shouting about.\n if (err instanceof Error && err.name === \"NotAllowedError\") {\n setNotice({ code, message: \"Passkey sign-in was cancelled or timed out.\" });\n return;\n }\n setNotice({\n code,\n message: isApiError(err)\n ? enrollErrorText(code, err.message)\n : err instanceof Error\n ? err.message\n : String(err),\n });\n };\n\n const finish = useCallback(async () => {\n const result = await checkAuth();\n if (result === \"authenticated\") navigate(takeReturnTo(next), { replace: true });\n else\n setNotice({\n code: \"internal\",\n message: \"Signed in, but the coordinator did not accept the new credential. Try again.\",\n });\n }, [navigate, next]);\n\n const deviceName = name.trim().slice(0, 80);\n\n const signInWithPasskey = async () => {\n if (!deviceName) return;\n setBusy(\"passkey\");\n setNotice(null);\n try {\n const { challengeId, options } = await apiPost<ChallengeResponse>(\n \"/api/auth/passkey/options\",\n );\n const response = await startAuthentication({\n optionsJSON: options as Parameters<typeof startAuthentication>[0][\"optionsJSON\"],\n });\n await apiPost(\"/api/auth/passkey/verify\", {\n challengeId,\n response,\n deviceName,\n kind: kindOf(),\n });\n await finish();\n } catch (err) {\n fail(err);\n } finally {\n setBusy(null);\n }\n };\n\n const signInWithOidc = async () => {\n if (!deviceName) return;\n setBusy(\"oidc\");\n setNotice(null);\n const standalone = isStandalone();\n try {\n const res = await apiPost<OidcBeginResponse>(\"/api/auth/oidc/begin\", {\n deviceName,\n kind: kindOf(),\n next: takeReturnTo(next),\n handoff: standalone,\n });\n if (standalone && res.flowId && res.pollToken) {\n // Home Screen apps have their own cookie jar: finish in the browser, claim here.\n authorizeUrlRef.current = res.authorizeUrl;\n setHandoff({ flowId: res.flowId, pollToken: res.pollToken });\n window.open(res.authorizeUrl, \"_blank\");\n } else {\n location.assign(res.authorizeUrl);\n return; // leaving the page; keep the button busy\n }\n } catch (err) {\n fail(err);\n }\n setBusy(null);\n };\n\n if (loadError) {\n return (\n <ErrorState\n title=\"Cannot reach Switchyard\"\n error={loadError}\n onRetry={() => setNonce((n) => n + 1)}\n />\n );\n }\n if (!setup) return <LoadingState label=\"Checking this server…\" />;\n\n if (!setup.initialized) {\n return (\n <div data-testid=\"setup-not-initialized\" className=\"flex flex-col gap-2 text-sm\">\n <p className=\"font-medium\">Switchyard isn't set up yet</p>\n <p className=\"text-muted\">\n Run <code>switchyard init</code> on the server to create the first sign-in link. With\n Docker: <code>docker exec &lt;container&gt; switchyard init</code>.\n </p>\n </div>\n );\n }\n\n if (handoff) {\n return (\n <HandoffScreen\n flowId={handoff.flowId}\n pollToken={handoff.pollToken}\n authorizeUrl={authorizeUrlRef.current}\n onDone={finish}\n onExit={() => setHandoff(null)}\n />\n );\n }\n\n const passkeyReady = setup.passkey.registered && setup.passkey.available;\n return (\n <div className=\"flex flex-col gap-4 text-sm\">\n <p className=\"text-base font-medium\">Sign in to Switchyard</p>\n {notice && <AlertBox title=\"Sign-in failed\">{notice.message}</AlertBox>}\n <div className=\"flex flex-col gap-1.5\">\n <label htmlFor=\"device-name\" className=\"text-sm font-medium\">\n Device name\n </label>\n <TextInput\n id=\"device-name\"\n value={name}\n maxLength={80}\n autoComplete=\"off\"\n onChange={(e) => onName(e.target.value)}\n aria-describedby=\"device-name-hint\"\n />\n <p id=\"device-name-hint\" className=\"text-xs text-muted\">\n Shown in Settings › Devices, where you can revoke it later.\n </p>\n </div>\n <div className=\"flex flex-col gap-2\">\n {passkeyReady && (\n <Button\n data-testid=\"signin-passkey\"\n variant=\"primary\"\n busy={busy === \"passkey\"}\n disabled={!deviceName || busy !== null}\n icon={<KeyRound className=\"size-4\" />}\n onClick={signInWithPasskey}\n >\n Sign in with a passkey\n </Button>\n )}\n {setup.oidc && (\n <Button\n data-testid=\"signin-oidc\"\n variant={passkeyReady ? \"secondary\" : \"primary\"}\n busy={busy === \"oidc\"}\n disabled={!deviceName || busy !== null}\n icon={<LogIn className=\"size-4\" />}\n onClick={signInWithOidc}\n >\n {setup.oidc.label}\n </Button>\n )}\n {setup.passkey.registered && !setup.passkey.available && setup.passkey.reason && (\n <p className=\"text-xs text-muted\">Passkeys are unavailable: {setup.passkey.reason}.</p>\n )}\n {!deviceName && (passkeyReady || setup.oidc) && (\n <p className=\"text-xs text-muted\">Enter a device name to sign in.</p>\n )}\n </div>\n <p className=\"text-muted\">\n Or open a pairing link from a device you already use: Settings › Devices › Add device.\n </p>\n <p className=\"text-xs text-muted\">\n Lost every device? Run <code>switchyard pair</code> on the server.\n </p>\n </div>\n );\n}\n\n/** PWA single sign-on handoff: finish in the browser, claim the credential here. */\nfunction HandoffScreen({\n flowId,\n pollToken,\n authorizeUrl,\n onDone,\n onExit,\n}: {\n flowId: string;\n pollToken: string;\n authorizeUrl: string | null;\n onDone: () => Promise<void>;\n onExit: () => void;\n}) {\n const [state, setState] = useState<HandoffState>(() => startHandoff(Date.now()));\n const stateRef = useRef(state);\n stateRef.current = state;\n const inFlight = useRef(false);\n const doneRef = useRef(onDone);\n doneRef.current = onDone;\n\n const apply = useCallback((event: Parameters<typeof handoffReduce>[1]) => {\n const next = handoffReduce(stateRef.current, event);\n stateRef.current = next;\n setState(next);\n return next;\n }, []);\n\n const poll = useCallback(async () => {\n if (inFlight.current || stateRef.current.phase !== \"waiting\") return;\n inFlight.current = true;\n try {\n const result = await apiPost<OidcClaimResponse>(\"/api/auth/oidc/claim\", {\n flowId,\n pollToken,\n });\n const next = apply({ type: \"claim\", result, now: Date.now() });\n if (next.phase === \"done\") await doneRef.current();\n } catch (err) {\n apply({\n type: \"claim_error\",\n code: isApiError(err) ? err.code : \"network\",\n message: \"This sign-in expired or was already used. Start again.\",\n now: Date.now(),\n });\n } finally {\n inFlight.current = false;\n }\n }, [flowId, pollToken, apply]);\n\n useEffect(() => {\n const visible = () => document.visibilityState === \"visible\";\n const timer = setInterval(() => {\n apply({ type: \"tick\", now: Date.now() });\n if (shouldPoll(stateRef.current, visible())) void poll();\n }, HANDOFF_POLL_MS);\n const onVisible = () => {\n if (shouldPoll(stateRef.current, visible())) void poll();\n };\n document.addEventListener(\"visibilitychange\", onVisible);\n return () => {\n clearInterval(timer);\n document.removeEventListener(\"visibilitychange\", onVisible);\n };\n }, [poll, apply]);\n\n if (state.phase === \"timeout\" || state.phase === \"failed\" || state.phase === \"cancelled\") {\n return (\n <div data-testid=\"signin-handoff\" className=\"flex flex-col gap-3 text-sm\">\n <AlertBox title={state.phase === \"timeout\" ? \"Sign-in timed out\" : \"Sign-in stopped\"}>\n {state.phase === \"timeout\"\n ? \"The sign-in was not finished in time. Start again.\"\n : (state.message ?? \"Start again.\")}\n </AlertBox>\n <Button onClick={onExit}>Back to sign-in</Button>\n </div>\n );\n }\n return (\n <div data-testid=\"signin-handoff\" role=\"status\" className=\"flex flex-col gap-3 text-sm\">\n <p className=\"font-medium\">Finish signing in, then come back here</p>\n <p className=\"text-muted\">\n Complete single sign-on in the page that opened, then return to this app. This screen\n continues on its own.\n </p>\n {authorizeUrl && (\n <a\n href={authorizeUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-medium text-accent underline\"\n >\n Open the sign-in page again\n </a>\n )}\n <Button\n onClick={() => {\n apply({ type: \"cancel\" });\n onExit();\n }}\n >\n Cancel\n </Button>\n </div>\n );\n}\n"],"ignoreList":[0],"mappings":"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"}
1
+ {"version":3,"file":"enroll-C7UBC20s.js","names":["useMemo","useState","useRef","useCallback","__toESM","useSearchParams","useNavigate","require_react","createLucideIcon","require_jsx_runtime","isApiError","apiPost","apiGet","TriangleAlert","Button","ErrorState","LoadingState","KeyRound","useAuth","checkAuth","takeReturnTo","TextInput","handoffReduce","startAuthentication","enrollErrorText","HANDOFF_POLL_MS","shouldPoll","defaultDeviceName","startHandoff","ERROR_TEXT","isStandalone","__iconData","LogIn","import_react","import_jsx_runtime","readToken","hash","query","EnrollView","navigate","params","next","token","status","s","signedOutReason","name","setName","busy","setBusy","error","setError","e","err","code","SignInPanel","kindOf","AlertBox","title","setParams","setup","setSetup","loadError","setLoadError","nonce","setNonce","notice","setNotice","handoff","setHandoff","authorizeUrlRef","rest","ac","v","fail","finish","deviceName","signInWithPasskey","response","options","signInWithOidc","standalone","res","n","HandoffScreen","passkeyReady","onName","state","setState","stateRef","inFlight","doneRef","onDone","apply","event","poll","result","flowId","pollToken","visible","timer","onVisible","onExit","authorizeUrl"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/log-in.mjs","../../src/features/enroll/index.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"log-in\",\n size: 24,\n node: [\n [\"path\", { d: \"m10 17 5-5-5-5\", key: \"1bsop3\" }],\n [\"path\", { d: \"M15 12H3\", key: \"6jk70r\" }],\n [\"path\", { d: \"M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4\", key: \"u53s6r\" }]\n ]\n};\n__iconData.node;\nconst LogIn = createLucideIcon(__iconData);\n\nexport { __iconData, LogIn as default };\n//# sourceMappingURL=log-in.mjs.map\n","import { startAuthentication } from \"@simplewebauthn/browser\";\nimport type { OidcBeginResponse, OidcClaimResponse, SetupState } from \"@switchyard/protocol\";\nimport { KeyRound, LogIn, TriangleAlert } from \"lucide-react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useNavigate, useSearchParams } from \"react-router\";\nimport { checkAuth, takeReturnTo, useAuth } from \"../../app/auth\";\nimport { Button } from \"../../components/ui/Button\";\nimport { ErrorState, LoadingState } from \"../../components/ui/states\";\nimport { TextInput } from \"../../components/ui/TextArea\";\nimport { isStandalone } from \"../../push\";\nimport { apiGet, apiPost, isApiError } from \"../../sync/api\";\nimport {\n defaultDeviceName,\n ERROR_TEXT,\n enrollErrorText,\n HANDOFF_POLL_MS,\n type HandoffState,\n handoffReduce,\n shouldPoll,\n startHandoff,\n} from \"./logic\";\n\nexport { defaultDeviceName };\n\nfunction readToken(): string | null {\n const hash = new URLSearchParams(location.hash.replace(/^#/, \"\"));\n const query = new URLSearchParams(location.search);\n return hash.get(\"token\") ?? query.get(\"token\");\n}\n\n/**\n * /enroll: redeem a single-use enrollment token from `#token=`, or (without\n * one) sign in with a passkey or single sign-on, name this device, then\n * return to the deep link that required authentication.\n */\nexport default function EnrollView() {\n const navigate = useNavigate();\n const [params] = useSearchParams();\n const next = params.get(\"next\");\n const token = useMemo(readToken, []);\n const status = useAuth((s) => s.status);\n const signedOutReason = useAuth((s) => s.signedOutReason);\n const [name, setName] = useState(() => defaultDeviceName());\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<{ code: string; message: string } | null>(null);\n\n // Keep the token out of the address bar and history.\n useEffect(() => {\n if (token) history.replaceState(history.state, \"\", location.pathname + location.search);\n }, [token]);\n\n // Already enrolled and no token: go back to the deep link.\n useEffect(() => {\n if (!token && status === \"authenticated\") navigate(takeReturnTo(next), { replace: true });\n }, [token, status, next, navigate]);\n\n const submit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!token || !name.trim()) return;\n setBusy(true);\n setError(null);\n try {\n await apiPost(\"/api/enroll\", {\n token,\n name: name.trim().slice(0, 80),\n kind: isStandalone() ? \"pwa\" : \"browser\",\n });\n const result = await checkAuth();\n if (result === \"authenticated\") navigate(takeReturnTo(next), { replace: true });\n else\n setError({\n code: \"internal\",\n message: \"Enrolled, but the coordinator did not accept the new credential. Try again.\",\n });\n } catch (err) {\n const code = isApiError(err) ? err.code : \"internal\";\n setError({\n code,\n message: ERROR_TEXT[code] ?? (err instanceof Error ? err.message : String(err)),\n });\n } finally {\n setBusy(false);\n }\n };\n\n if (!token && status === \"checking\") return <LoadingState label=\"Checking this device…\" />;\n\n return (\n <div className=\"flex min-h-dvh items-start justify-center px-4 py-10 safe-pt safe-pb sm:items-center\">\n <div className=\"w-full max-w-sm rounded-xl border border-border bg-surface p-5 shadow-sm\">\n <div className=\"mb-4 flex items-center gap-3\">\n <img src=\"/icon.svg\" alt=\"\" className=\"size-10 rounded-lg\" />\n <div>\n <h1 className=\"text-lg font-semibold\">Switchyard</h1>\n <p className=\"text-sm text-muted\">{token ? \"Enroll this device\" : \"Sign in\"}</p>\n </div>\n </div>\n {signedOutReason && (\n <p role=\"status\" className=\"mb-3 rounded-md bg-warning-soft px-3 py-2 text-sm\">\n {signedOutReason}\n </p>\n )}\n {token ? (\n <form onSubmit={submit} className=\"flex flex-col gap-3\" noValidate>\n <div className=\"flex flex-col gap-1.5\">\n <label htmlFor=\"device-name\" className=\"text-sm font-medium\">\n Device name\n </label>\n <TextInput\n id=\"device-name\"\n value={name}\n maxLength={80}\n autoComplete=\"off\"\n onChange={(e) => setName(e.target.value)}\n aria-describedby=\"device-name-hint\"\n />\n <p id=\"device-name-hint\" className=\"text-xs text-muted\">\n Shown in Settings › Devices, where you can revoke it later.\n </p>\n </div>\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-border bg-danger-soft px-3 py-2 text-sm\"\n >\n <div className=\"font-medium\">\n {error.code === \"enrollment_expired\"\n ? \"Enrollment link expired\"\n : error.code === \"enrollment_used\"\n ? \"Enrollment link already used\"\n : \"Enrollment failed\"}\n </div>\n <div className=\"text-muted\">{error.message}</div>\n </div>\n )}\n <Button\n type=\"submit\"\n variant=\"primary\"\n busy={busy}\n disabled={\n !name.trim() ||\n error?.code === \"enrollment_expired\" ||\n error?.code === \"enrollment_used\"\n }\n icon={<KeyRound className=\"size-4\" />}\n >\n Enroll device\n </Button>\n </form>\n ) : (\n <SignInPanel name={name} onName={setName} next={next} />\n )}\n </div>\n </div>\n );\n}\n\ninterface ChallengeResponse {\n challengeId: string;\n options: unknown;\n}\n\nconst kindOf = () => (isStandalone() ? \"pwa\" : \"browser\");\n\nfunction AlertBox({ title, children }: { title: string; children: React.ReactNode }) {\n return (\n <div role=\"alert\" className=\"rounded-md border border-border bg-danger-soft px-3 py-2 text-sm\">\n <div className=\"flex items-center gap-1.5 font-medium\">\n <TriangleAlert aria-hidden className=\"size-4 shrink-0\" />\n {title}\n </div>\n <div className=\"break-words text-muted\">{children}</div>\n </div>\n );\n}\n\n/** The no-token screen: not-initialized notice, or the sign-in options. */\nfunction SignInPanel({\n name,\n onName,\n next,\n}: {\n name: string;\n onName: (v: string) => void;\n next: string | null;\n}) {\n const navigate = useNavigate();\n const [params, setParams] = useSearchParams();\n const [setup, setSetup] = useState<SetupState | null>(null);\n const [loadError, setLoadError] = useState<unknown>(null);\n const [nonce, setNonce] = useState(0);\n const [busy, setBusy] = useState<\"passkey\" | \"oidc\" | null>(null);\n const [notice, setNotice] = useState<{ code: string; message: string } | null>(() => {\n const code = params.get(\"error\");\n return code ? { code, message: enrollErrorText(code, params.get(\"detail\")) } : null;\n });\n const [handoff, setHandoff] = useState<{ flowId: string; pollToken: string } | null>(null);\n const authorizeUrlRef = useRef<string | null>(null);\n\n // Show a callback error once, then drop it from the address bar.\n // biome-ignore lint/correctness/useExhaustiveDependencies: runs once on mount\n useEffect(() => {\n if (!params.has(\"error\") && !params.has(\"detail\")) return;\n const rest = new URLSearchParams(params);\n rest.delete(\"error\");\n rest.delete(\"detail\");\n setParams(rest, { replace: true });\n }, []);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: `nonce` reloads\n useEffect(() => {\n const ac = new AbortController();\n setLoadError(null);\n apiGet<SetupState>(\"/api/setup\", undefined, ac.signal)\n .then((v) => !ac.signal.aborted && setSetup(v))\n .catch((e: unknown) => !ac.signal.aborted && setLoadError(e));\n return () => ac.abort();\n }, [nonce]);\n\n const fail = (err: unknown) => {\n const code = isApiError(err) ? err.code : \"internal\";\n // A cancelled or dismissed WebAuthn prompt is not an error worth shouting about.\n if (err instanceof Error && err.name === \"NotAllowedError\") {\n setNotice({ code, message: \"Passkey sign-in was cancelled or timed out.\" });\n return;\n }\n setNotice({\n code,\n message: isApiError(err)\n ? enrollErrorText(code, err.message)\n : err instanceof Error\n ? err.message\n : String(err),\n });\n };\n\n const finish = useCallback(async () => {\n const result = await checkAuth();\n if (result === \"authenticated\") navigate(takeReturnTo(next), { replace: true });\n else\n setNotice({\n code: \"internal\",\n message: \"Signed in, but the coordinator did not accept the new credential. Try again.\",\n });\n }, [navigate, next]);\n\n const deviceName = name.trim().slice(0, 80);\n\n const signInWithPasskey = async () => {\n if (!deviceName) return;\n setBusy(\"passkey\");\n setNotice(null);\n try {\n const { challengeId, options } = await apiPost<ChallengeResponse>(\n \"/api/auth/passkey/options\",\n );\n const response = await startAuthentication({\n optionsJSON: options as Parameters<typeof startAuthentication>[0][\"optionsJSON\"],\n });\n await apiPost(\"/api/auth/passkey/verify\", {\n challengeId,\n response,\n deviceName,\n kind: kindOf(),\n });\n await finish();\n } catch (err) {\n fail(err);\n } finally {\n setBusy(null);\n }\n };\n\n const signInWithOidc = async () => {\n if (!deviceName) return;\n setBusy(\"oidc\");\n setNotice(null);\n const standalone = isStandalone();\n try {\n const res = await apiPost<OidcBeginResponse>(\"/api/auth/oidc/begin\", {\n deviceName,\n kind: kindOf(),\n next: takeReturnTo(next),\n handoff: standalone,\n });\n if (standalone && res.flowId && res.pollToken) {\n // Home Screen apps have their own cookie jar: finish in the browser, claim here.\n authorizeUrlRef.current = res.authorizeUrl;\n setHandoff({ flowId: res.flowId, pollToken: res.pollToken });\n window.open(res.authorizeUrl, \"_blank\");\n } else {\n location.assign(res.authorizeUrl);\n return; // leaving the page; keep the button busy\n }\n } catch (err) {\n fail(err);\n }\n setBusy(null);\n };\n\n if (loadError) {\n return (\n <ErrorState\n title=\"Cannot reach Switchyard\"\n error={loadError}\n onRetry={() => setNonce((n) => n + 1)}\n />\n );\n }\n if (!setup) return <LoadingState label=\"Checking this server…\" />;\n\n if (!setup.initialized) {\n return (\n <div data-testid=\"setup-not-initialized\" className=\"flex flex-col gap-2 text-sm\">\n <p className=\"font-medium\">Switchyard isn't set up yet</p>\n <p className=\"text-muted\">\n Run <code>switchyard init</code> on the server to create the first sign-in link. With\n Docker: <code>docker exec &lt;container&gt; switchyard init</code>.\n </p>\n </div>\n );\n }\n\n if (handoff) {\n return (\n <HandoffScreen\n flowId={handoff.flowId}\n pollToken={handoff.pollToken}\n authorizeUrl={authorizeUrlRef.current}\n onDone={finish}\n onExit={() => setHandoff(null)}\n />\n );\n }\n\n const passkeyReady = setup.passkey.registered && setup.passkey.available;\n return (\n <div className=\"flex flex-col gap-4 text-sm\">\n <p className=\"text-base font-medium\">Sign in to Switchyard</p>\n {notice && <AlertBox title=\"Sign-in failed\">{notice.message}</AlertBox>}\n <div className=\"flex flex-col gap-1.5\">\n <label htmlFor=\"device-name\" className=\"text-sm font-medium\">\n Device name\n </label>\n <TextInput\n id=\"device-name\"\n value={name}\n maxLength={80}\n autoComplete=\"off\"\n onChange={(e) => onName(e.target.value)}\n aria-describedby=\"device-name-hint\"\n />\n <p id=\"device-name-hint\" className=\"text-xs text-muted\">\n Shown in Settings › Devices, where you can revoke it later.\n </p>\n </div>\n <div className=\"flex flex-col gap-2\">\n {passkeyReady && (\n <Button\n data-testid=\"signin-passkey\"\n variant=\"primary\"\n busy={busy === \"passkey\"}\n disabled={!deviceName || busy !== null}\n icon={<KeyRound className=\"size-4\" />}\n onClick={signInWithPasskey}\n >\n Sign in with a passkey\n </Button>\n )}\n {setup.oidc && (\n <Button\n data-testid=\"signin-oidc\"\n variant={passkeyReady ? \"secondary\" : \"primary\"}\n busy={busy === \"oidc\"}\n disabled={!deviceName || busy !== null}\n icon={<LogIn className=\"size-4\" />}\n onClick={signInWithOidc}\n >\n {setup.oidc.label}\n </Button>\n )}\n {setup.passkey.registered && !setup.passkey.available && setup.passkey.reason && (\n <p className=\"text-xs text-muted\">Passkeys are unavailable: {setup.passkey.reason}.</p>\n )}\n {!deviceName && (passkeyReady || setup.oidc) && (\n <p className=\"text-xs text-muted\">Enter a device name to sign in.</p>\n )}\n </div>\n <p className=\"text-muted\">\n Or open a pairing link from a device you already use: Settings › Devices › Add device.\n </p>\n <p className=\"text-xs text-muted\">\n Lost every device? Run <code>switchyard pair</code> on the server.\n </p>\n </div>\n );\n}\n\n/** PWA single sign-on handoff: finish in the browser, claim the credential here. */\nfunction HandoffScreen({\n flowId,\n pollToken,\n authorizeUrl,\n onDone,\n onExit,\n}: {\n flowId: string;\n pollToken: string;\n authorizeUrl: string | null;\n onDone: () => Promise<void>;\n onExit: () => void;\n}) {\n const [state, setState] = useState<HandoffState>(() => startHandoff(Date.now()));\n const stateRef = useRef(state);\n stateRef.current = state;\n const inFlight = useRef(false);\n const doneRef = useRef(onDone);\n doneRef.current = onDone;\n\n const apply = useCallback((event: Parameters<typeof handoffReduce>[1]) => {\n const next = handoffReduce(stateRef.current, event);\n stateRef.current = next;\n setState(next);\n return next;\n }, []);\n\n const poll = useCallback(async () => {\n if (inFlight.current || stateRef.current.phase !== \"waiting\") return;\n inFlight.current = true;\n try {\n const result = await apiPost<OidcClaimResponse>(\"/api/auth/oidc/claim\", {\n flowId,\n pollToken,\n });\n const next = apply({ type: \"claim\", result, now: Date.now() });\n if (next.phase === \"done\") await doneRef.current();\n } catch (err) {\n apply({\n type: \"claim_error\",\n code: isApiError(err) ? err.code : \"network\",\n message: \"This sign-in expired or was already used. Start again.\",\n now: Date.now(),\n });\n } finally {\n inFlight.current = false;\n }\n }, [flowId, pollToken, apply]);\n\n useEffect(() => {\n const visible = () => document.visibilityState === \"visible\";\n const timer = setInterval(() => {\n apply({ type: \"tick\", now: Date.now() });\n if (shouldPoll(stateRef.current, visible())) void poll();\n }, HANDOFF_POLL_MS);\n const onVisible = () => {\n if (shouldPoll(stateRef.current, visible())) void poll();\n };\n document.addEventListener(\"visibilitychange\", onVisible);\n return () => {\n clearInterval(timer);\n document.removeEventListener(\"visibilitychange\", onVisible);\n };\n }, [poll, apply]);\n\n if (state.phase === \"timeout\" || state.phase === \"failed\" || state.phase === \"cancelled\") {\n return (\n <div data-testid=\"signin-handoff\" className=\"flex flex-col gap-3 text-sm\">\n <AlertBox title={state.phase === \"timeout\" ? \"Sign-in timed out\" : \"Sign-in stopped\"}>\n {state.phase === \"timeout\"\n ? \"The sign-in was not finished in time. Start again.\"\n : (state.message ?? \"Start again.\")}\n </AlertBox>\n <Button onClick={onExit}>Back to sign-in</Button>\n </div>\n );\n }\n return (\n <div data-testid=\"signin-handoff\" role=\"status\" className=\"flex flex-col gap-3 text-sm\">\n <p className=\"font-medium\">Finish signing in, then come back here</p>\n <p className=\"text-muted\">\n Complete single sign-on in the page that opened, then return to this app. This screen\n continues on its own.\n </p>\n {authorizeUrl && (\n <a\n href={authorizeUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-medium text-accent underline\"\n >\n Open the sign-in page again\n </a>\n )}\n <Button\n onClick={() => {\n apply({ type: \"cancel\" });\n onExit();\n }}\n >\n Cancel\n </Button>\n </div>\n );\n}\n"],"ignoreList":[0],"mappings":"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"}