@01.works/visual-review 0.17.1 → 0.17.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.
package/dist/cli.js CHANGED
@@ -2052,7 +2052,7 @@ Revokes the remote session before removing the local credential unless --local-o
2052
2052
  `
2053
2053
  };
2054
2054
  var CLI_COMMAND_NAMES = Object.keys(CLI_HELP_TOPICS);
2055
- var CLI_VERSION = "0.17.1";
2055
+ var CLI_VERSION = "0.17.2";
2056
2056
  var CliUsageError = class extends Error {
2057
2057
  };
2058
2058
  function parseCliCommand(args) {
@@ -0,0 +1,13 @@
1
+ import{r as e}from"./install-LaTz8Rmo.js";var t=class extends Error{code;constructor(e,t){super(t),this.code=e,this.name=`ReviewBootstrapError`}};function n(e,t,n=``,r){if(t.aborted)return Promise.reject(d());let i=o(e);i.replaceChildren(s(i,r),c(i));let a=i.querySelector(`form`),l=a.elements.namedItem(`displayName`),u=i.querySelector(`button`),p=i.querySelector(`.error`);return l.value=n.trim().slice(0,120),l.focus(),l.select(),new Promise((e,n)=>{let r=!1,o=e=>{r||(r=!0,t.removeEventListener(`abort`,s),i.removeEventListener(`keydown`,h),a.removeEventListener(`submit`,m),e())},s=()=>o(()=>n(d())),c=()=>{let t=l.value.trim();if(!t){p.textContent=`이름을 입력해 주세요.`,l.focus();return}if(t.length>120){p.textContent=`이름은 120자 이하여야 합니다.`,l.focus();return}p.textContent=``,u.disabled=!0,a.setAttribute(`aria-busy`,`true`),o(()=>e(t))},m=e=>{e.preventDefault(),c()},h=e=>{let t=e;if(t.key===`Escape`){t.preventDefault(),s();return}if(t.key===`Tab`){f(i,t);return}t.key===`Enter`&&!t.isComposing&&t.target===l&&(t.preventDefault(),c())};t.addEventListener(`abort`,s,{once:!0}),i.addEventListener(`keydown`,h),a.addEventListener(`submit`,m)})}function r(e,t,n){if(t.aborted)return Promise.reject(d());let r=o(e);r.replaceChildren(s(r,n),l(r));let i=r.querySelector(`form`),a=r.querySelector(`button`);return a.focus(),new Promise((e,n)=>{let o=!1,s=e=>{o||(o=!0,t.removeEventListener(`abort`,c),r.removeEventListener(`keydown`,u),i.removeEventListener(`submit`,l),e())},c=()=>s(()=>n(d())),l=t=>{t.preventDefault(),a.disabled=!0,i.setAttribute(`aria-busy`,`true`),s(e)},u=e=>{let t=e;t.key===`Escape`?(t.preventDefault(),c()):t.key===`Tab`&&f(r,t)};t.addEventListener(`abort`,c,{once:!0}),r.addEventListener(`keydown`,u),i.addEventListener(`submit`,l)})}function i(e,t,n,r){if(t.aborted)return Promise.reject(d());let i=o(e),a=s(i,r);return new Promise((e,r)=>{let o=!1,s=null,c=null,l=()=>{s&&c&&s.removeEventListener(`submit`,c),s=null,c=null},p=e=>{o||(o=!0,t.removeEventListener(`abort`,m),i.removeEventListener(`keydown`,h),l(),e())},m=()=>p(()=>r(d())),h=e=>{let t=e;t.key===`Escape`?(t.preventDefault(),m()):t.key===`Tab`&&f(i,t)},g=(e,t)=>{l(),s=e,c=t,e.addEventListener(`submit`,t),i.replaceChildren(a,e)},_=(t,r)=>{let a=i.ownerDocument,o=a.createElement(`form`);o.setAttribute(`role`,`dialog`),o.setAttribute(`aria-modal`,`true`);let s=u(a,`p`,`01.works`,`eyebrow`),c=u(a,`h2`,`이메일 확인`),l=u(a,`p`,`${r.emailHint}로 보낸 6자리 코드를 입력하세요.`,`description`),d=u(a,`label`,`인증 코드`),f=a.createElement(`input`);f.name=`code`,f.inputMode=`numeric`,f.autocomplete=`one-time-code`,f.pattern=`[0-9]{6}`,f.maxLength=6,f.required=!0;let m=u(a,`p`,``,`error`);m.setAttribute(`role`,`alert`);let h=u(a,`button`,`리뷰 시작`);h.type=`submit`,d.append(f),o.append(s,c,l,d,m,h),g(o,i=>{i.preventDefault();let a=f.value.trim();if(!/^\d{6}$/u.test(a)){m.textContent=`6자리 숫자를 입력해 주세요.`;return}h.disabled=!0,o.setAttribute(`aria-busy`,`true`),m.textContent=``,n.verifyCode(r.challengeId,r.attemptId,t,a).then(t=>p(()=>e(t)),e=>{h.disabled=!1,o.removeAttribute(`aria-busy`),m.textContent=e instanceof Error?e.message:`인증 코드를 확인하지 못했습니다.`,f.select()})}),f.focus()},v=i.ownerDocument,y=v.createElement(`form`);y.setAttribute(`role`,`dialog`),y.setAttribute(`aria-modal`,`true`);let b=u(v,`p`,`01.works`,`eyebrow`),x=u(v,`h2`,`리뷰 로그인`),S=u(v,`p`,`초대받은 이메일로 인증 코드를 보내드립니다.`,`description`),C=u(v,`label`,`이메일`),w=v.createElement(`input`);w.name=`email`,w.type=`email`,w.autocomplete=`email`,w.required=!0;let T=u(v,`p`,``,`error`);T.setAttribute(`role`,`alert`);let E=u(v,`button`,`인증 코드 받기`);E.type=`submit`,C.append(w),y.append(b,x,S,C,T,E),g(y,e=>{e.preventDefault();let t=w.value.trim().toLowerCase();if(t.length>320||!/^[^\s@]+@[^\s@]+\.[^\s@]+$/u.test(t)){T.textContent=`유효한 이메일을 입력해 주세요.`;return}E.disabled=!0,y.setAttribute(`aria-busy`,`true`),T.textContent=``,n.requestCode(t).then(e=>_(t,e),e=>{E.disabled=!1,y.removeAttribute(`aria-busy`),T.textContent=e instanceof Error?e.message:`인증 코드를 보내지 못했습니다.`})}),t.addEventListener(`abort`,m,{once:!0}),i.addEventListener(`keydown`,h),i.replaceChildren(a,y),w.focus()})}async function a(e,t,n,r){if(t.aborted)throw d();let i=await n.requestCode();if(t.aborted)throw d();let a=o(e),c=a.ownerDocument.createElement(`form`);c.setAttribute(`role`,`dialog`),c.setAttribute(`aria-modal`,`true`);let l=u(a.ownerDocument,`p`,`01.works`,`eyebrow`),p=u(a.ownerDocument,`h2`,`리뷰 접속 복구`),m=u(a.ownerDocument,`p`,`${i.emailHint}로 보낸 6자리 코드를 입력하세요.`,`description`),h=u(a.ownerDocument,`label`,`복구 코드`),g=a.ownerDocument.createElement(`input`);g.name=`code`,g.inputMode=`numeric`,g.autocomplete=`one-time-code`,g.pattern=`[0-9]{6}`,g.maxLength=6,g.required=!0;let _=u(a.ownerDocument,`p`,``,`error`);_.setAttribute(`role`,`alert`);let v=u(a.ownerDocument,`button`,`접속 복구`);return v.type=`submit`,h.append(g),c.append(l,p,m,h,_,v),a.replaceChildren(s(a,r),c),g.focus(),new Promise((e,r)=>{let o=!1,s=e=>{o||(o=!0,t.removeEventListener(`abort`,l),a.removeEventListener(`keydown`,u),c.removeEventListener(`submit`,p),e())},l=()=>s(()=>r(d())),u=e=>{let t=e;t.key===`Escape`?(t.preventDefault(),l()):t.key===`Tab`&&f(a,t)},p=t=>{t.preventDefault();let r=g.value.trim();if(!/^\d{6}$/.test(r)){_.textContent=`6자리 숫자를 입력해 주세요.`;return}v.disabled=!0,c.setAttribute(`aria-busy`,`true`),_.textContent=``,n.verifyCode(i.challengeId,r).then(t=>s(()=>e(t)),e=>{v.disabled=!1,c.removeAttribute(`aria-busy`),_.textContent=e instanceof Error?e.message:`복구 코드를 확인하지 못했습니다.`,g.select()})};t.addEventListener(`abort`,l,{once:!0}),a.addEventListener(`keydown`,u),c.addEventListener(`submit`,p)})}function o(e){return e.shadowRoot??e.attachShadow({mode:`open`})}function s(e,t){let n=e.ownerDocument.createElement(`style`);return t&&(n.nonce=t),n.textContent=p,n}function c(e){let t=e.ownerDocument,n=t.createElement(`form`);n.setAttribute(`role`,`dialog`),n.setAttribute(`aria-modal`,`true`),n.setAttribute(`aria-labelledby`,`visual-review-name-title`),n.setAttribute(`aria-describedby`,`visual-review-name-description`);let r=u(t,`p`,`01.works`,`eyebrow`),i=u(t,`h2`,`표시 이름`);i.id=`visual-review-name-title`;let a=u(t,`p`,`피드백에 이 이름이 표시됩니다.`,`description`);a.id=`visual-review-name-description`;let o=u(t,`label`,`이름`);o.htmlFor=`visual-review-display-name`;let s=t.createElement(`input`);s.id=`visual-review-display-name`,s.name=`displayName`,s.autocomplete=`name`,s.maxLength=120,s.required=!0;let c=u(t,`p`,``,`error`);c.setAttribute(`role`,`alert`);let l=u(t,`button`,`리뷰 시작`);return l.type=`submit`,n.append(r,i,a,o,s,c,l),n}function l(e){let t=e.ownerDocument,n=t.createElement(`form`);n.setAttribute(`role`,`dialog`),n.setAttribute(`aria-modal`,`true`),n.setAttribute(`aria-labelledby`,`visual-review-invitation-title`),n.setAttribute(`aria-describedby`,`visual-review-invitation-description`);let r=u(t,`p`,`01.works`,`eyebrow`),i=u(t,`h2`,`리뷰 초대`);i.id=`visual-review-invitation-title`;let a=u(t,`p`,`초대받은 프로젝트를 열고 화면에 피드백을 남길 수 있습니다.`,`description`);a.id=`visual-review-invitation-description`;let o=u(t,`button`,`리뷰 시작`);return o.type=`submit`,n.append(r,i,a,o),n}function u(e,t,n,r){let i=e.createElement(t);return i.textContent=n,r&&(i.className=r),i}function d(){return new t(`AUTH_CANCELLED`,`리뷰 로그인이 취소되었습니다.`)}function f(e,t){let n=Array.from(e.querySelectorAll(`input:not([disabled]),button:not([disabled])`)).filter(e=>!e.closest(`[hidden]`));if(n.length===0)return;let r=n[0],i=n.at(-1);t.shiftKey&&e.activeElement===r?(t.preventDefault(),i.focus()):!t.shiftKey&&e.activeElement===i&&(t.preventDefault(),r.focus())}const p=`
2
+ :host{all:initial;--vr-panel:#fff;--vr-text:#161616;--vr-muted:#707070;--vr-label:#3f3f3f;--vr-control:#fff;--vr-border:#d9d9d9;--vr-action:#161616;--vr-action-text:#fff;position:fixed;inset:0;z-index:2147483646;display:grid;place-items:center;overflow:auto;padding:8px;box-sizing:border-box;background:rgba(15,15,15,.34);font-family:Inter,ui-sans-serif,system-ui,sans-serif;color:var(--vr-text)}
3
+ form{box-sizing:border-box;width:min(320px,calc(100vw - 16px));padding:20px;border:0;border-radius:14px;background:var(--vr-panel);box-shadow:0 18px 56px rgba(0,0,0,.18)}
4
+ .eyebrow{margin:0 0 10px;color:var(--vr-muted);font-size:10px;font-weight:700;letter-spacing:.09em;text-transform:uppercase}
5
+ h2{margin:0 0 5px;font-size:16px;line-height:1.3;letter-spacing:-.01em}.description{margin:0 0 18px;color:var(--vr-muted);font-size:13px;line-height:1.45}
6
+ [hidden]{display:none!important}label{display:grid;gap:6px;margin-top:12px;color:var(--vr-label);font-size:12px;font-weight:600}input{box-sizing:border-box;width:100%;height:40px;margin-top:6px;padding:0 11px;border:1px solid var(--vr-border);border-radius:9px;background:var(--vr-control);color:var(--vr-text);font:400 13px/1 Inter,ui-sans-serif,system-ui,sans-serif}
7
+ label input{margin-top:0}input:focus{border-color:#999;outline:0;box-shadow:0 0 0 3px rgba(127,127,127,.16)}.error{min-height:17px;margin:8px 0 0;color:#d92d20;font-size:11px;line-height:1.45}
8
+ button{width:100%;height:40px;margin-top:7px;border:0;border-radius:9px;background:var(--vr-action);color:var(--vr-action-text);font:650 13px/1 Inter,ui-sans-serif,system-ui,sans-serif;cursor:pointer}button:focus-visible{outline:2px solid var(--vr-action);outline-offset:2px}button:disabled{opacity:.55;cursor:wait}
9
+ @media (prefers-color-scheme:dark){:host{--vr-panel:#161616;--vr-text:#fff;--vr-muted:#a7a7a7;--vr-label:#d6d6d6;--vr-control:#222;--vr-border:#3a3a3a;--vr-action:#fff;--vr-action-text:#171717}}
10
+ @media (pointer:coarse){input,button{height:44px}}
11
+ @media (max-width:480px){form{padding:18px}input{font-size:16px}}
12
+ @media (prefers-reduced-motion:reduce){*,*::before,*::after{scroll-behavior:auto!important;transition:none!important;animation:none!important}}
13
+ `;function m(e){let t=[`01works-reviewer-device-proof-v1`,e.purpose,e.deviceSessionId,e.challengeId,e.challenge,String(e.counter),String(e.expiresAt),e.invitationId,e.origin,e.pathname];return new TextEncoder().encode(t.map(e=>`${e.length}:${e}`).join(``))}const h=`reviewer-device-credentials-v1`;function g(){try{return globalThis.indexedDB?new y(globalThis.indexedDB):null}catch{return null}}async function _(e=globalThis.crypto){if(!e?.subtle||typeof e.randomUUID!=`function`)throw Error(`Visual Review device authentication requires Web Crypto`);let t=await e.subtle.generateKey({name:`ECDSA`,namedCurve:`P-256`},!1,[`sign`,`verify`]);if(!(t instanceof Object)||!(`privateKey`in t)||!(`publicKey`in t)||t.privateKey.extractable)throw Error(`Visual Review could not create a non-extractable device key`);let n=await e.subtle.exportKey(`jwk`,t.publicKey);if(n.kty!==`EC`||n.crv!==`P-256`||typeof n.x!=`string`||typeof n.y!=`string`)throw Error(`Visual Review received an invalid device public key`);return{deviceSessionId:e.randomUUID(),privateKey:t.privateKey,publicKey:{kty:`EC`,crv:`P-256`,x:n.x,y:n.y}}}async function v(e,t,n=globalThis.crypto){if(e.extractable||e.type!==`private`||e.algorithm.name!==`ECDSA`||!e.usages.includes(`sign`))throw Error(`Visual Review device key is invalid`);let r=await n.subtle.sign({name:`ECDSA`,hash:`SHA-256`},e,m(t));return C(new Uint8Array(r))}var y=class{factory;constructor(e){this.factory=e}async get(e){let t=await this.open();try{return b(await x(t.transaction(h,`readonly`).objectStore(h).get(e)))}finally{t.close()}}async set(e,t){let n=await this.open();try{await S(n.transaction(h,`readwrite`),n=>{n.put(t,e)})}finally{n.close()}}async delete(e){let t=await this.open();try{await S(t.transaction(h,`readwrite`),t=>{t.delete(e)})}finally{t.close()}}open(){return new Promise((e,t)=>{let n=this.factory.open(`01works-visual-review`,1);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains(h)||n.result.createObjectStore(h)},n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error??Error(`IndexedDB open failed`)),n.onblocked=()=>t(Error(`IndexedDB upgrade blocked`))})}};function b(e){if(!e||typeof e!=`object`||Array.isArray(e))return null;let t=e;return typeof t.deviceSessionId!=`string`||!Number.isSafeInteger(t.expiresAt)||(t.expiresAt??0)<=Date.now()||t.invitationId!==void 0&&typeof t.invitationId!=`string`||!t.privateKey||t.privateKey.type!==`private`||t.privateKey.extractable||t.privateKey.algorithm.name!==`ECDSA`||!t.privateKey.usages.includes(`sign`)?null:t}function x(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error??Error(`IndexedDB request failed`))})}function S(e,t){return new Promise((n,r)=>{e.oncomplete=()=>n(),e.onerror=()=>r(e.error??Error(`IndexedDB transaction failed`)),e.onabort=()=>r(e.error??Error(`IndexedDB transaction aborted`)),t(e.objectStore(h))})}function C(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t).replaceAll(`+`,`-`).replaceAll(`/`,`_`).replace(/=+$/u,``)}var w=class extends Error{code;status;constructor(e,t,n){super(t),this.code=e,this.status=n,this.name=`HostedBootstrapError`}};function T(e={}){return E(e)}function E(t={},n={}){let r=e(t.serviceUrl??`https://review.01.works`),i=globalThis.fetch,a=t.fetch??(typeof i==`function`?i.bind(globalThis):void 0),o=n.now??Date.now,s=new Map,c=n.reviewerDeviceStore===void 0?g():n.reviewerDeviceStore,l=n.crypto??globalThis.crypto,u=n.reviewerSessionStorage===void 0?K(de(),q()):K(n.reviewerSessionStorage);if(typeof a!=`function`)throw Error(`Visual Review bootstrap requires the Fetch API`);return{async resolve(e){let t=z(e),i=await B(r,a,t,e.signal),d=o(),f=await ee({config:i,convexRuntimeFactory:n.convexRuntimeFactory??(await import(`./convex-runtime-CpgGS5hl.js`)).createConvexReviewRuntime,fetcher:a,memorySessions:s,navigateToAccountBridge:n.navigateToAccountBridge,reviewerDeviceStore:c,cryptoProvider:l,request:e,legacySessionStorages:u,serviceUrl:r}),p=f.resolvePage;if(!p)return f;let m=async t=>{await B(r,a,{invitationId:e.invitationId,origin:t.location.origin,pathname:t.location.pathname},t.signal),d=o()};return{...f,resolvePage:async e=>(o()-d>=3e5&&await m(e),p(e))}}}}async function ee(e){let t=e.config.accessScope?.kind===`project`?e.config.accessScope:null,n=G(e.serviceUrl,e.request.location.origin,t?.locator??e.request.invitationId),i=G(e.serviceUrl,e.request.location.origin,e.request.invitationId);X(e.legacySessionStorages,n),i!==n&&X(e.legacySessionStorages,i);let a;if(t){if(a=await J(e.reviewerDeviceStore,e.memorySessions,n),a&&!a.invitationId&&(await Y(e.reviewerDeviceStore,e.memorySessions,n),a=null),!a){let n=await _(e.cryptoProvider);a=e.request.accountToken?await ne(e,t.locator,e.request.accountToken,n).catch(r=>{if(!(r instanceof w))throw r;if(r.code===`INVITATION_NOT_FOUND`&&t.mode===`email`)return D(e,t.locator,n);if(r.code===`INVITATION_TOKEN_USED`||r.code===`INVITATION_TOKEN_INVALID`||r.code===`ACCOUNT_ASSERTION_EXPIRED`||r.code===`AUTH_REQUIRED`||r.code===`INVALID_INPUT`)return O(e,t.locator);throw r}):e.request.accountBridgeAttempted?t.mode===`link`?await te(e,t.locator,n):await D(e,t.locator,n):await O(e,t.locator)}}else if(e.request.emailToken){await r(e.request.authHost,e.request.signal,e.request.styleNonce);let t=await _(e.cryptoProvider);try{a=await F(e.serviceUrl,e.fetcher,{invitationId:e.request.invitationId,emailToken:e.request.emailToken,origin:e.request.location.origin,pathname:e.request.location.pathname,signal:e.request.signal,device:t})}catch(n){if(!(n instanceof w)||n.code!==`INVITATION_TOKEN_USED`)throw n;a=await N(e,t)}}else a=await J(e.reviewerDeviceStore,e.memorySessions,n),!a&&i!==n&&(a=await J(e.reviewerDeviceStore,e.memorySessions,i),a&&await Y(e.reviewerDeviceStore,e.memorySessions,i));a||=await N(e,await _(e.cryptoProvider));let o=t?a.invitationId:e.request.invitationId;if(!o)throw new w(`INVALID_PROJECT_ACCESS_RESPONSE`,`프로젝트 리뷰 인증 응답이 올바르지 않습니다.`,502);a={...a,invitationId:o},await fe(e.reviewerDeviceStore,e.memorySessions,n,a);let{expiresAt:s}=a,c={...e.request,invitationId:o,emailToken:void 0,accountToken:void 0,accountBridgeAttempted:void 0};return{...await e.convexRuntimeFactory(e.config.convexUrl,{fetchAccessToken:t=>ie(e.serviceUrl,e.fetcher,a,t,e.cryptoProvider,()=>Y(e.reviewerDeviceStore,e.memorySessions,n))}).resolve(c),...e.config.branding===void 0?{}:{branding:e.config.branding},sessionScope:`browser`,sessionExpiresAt:s??e.config.invitationExpiresAt,clearLocalSession:()=>{Y(e.reviewerDeviceStore,e.memorySessions,n);let t=new AbortController,r=setTimeout(()=>t.abort(),5e3);ae(e.serviceUrl,e.fetcher,a,{invitationId:o,origin:e.request.location.origin,pathname:e.request.location.pathname,forceRefreshToken:!1,signal:t.signal},e.cryptoProvider).catch(()=>void 0).finally(()=>clearTimeout(r))}}}async function D(e,t,n){let r={reviewLocator:t,pathname:e.request.location.pathname,signal:e.request.signal};return i(e.request.authHost,e.request.signal,{requestCode:t=>se(e.serviceUrl,e.fetcher,{...r,email:t}),verifyCode:(t,i,a,o)=>ce(e.serviceUrl,e.fetcher,{...r,challengeId:t,attemptId:i,email:a,code:o,device:n})},e.request.styleNonce)}async function te(e,t,r){let i=await n(e.request.authHost,e.request.signal,``,e.request.styleNonce),a=await e.fetcher(`${e.serviceUrl}/v1/reviewer/link/start`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({reviewLocator:t,displayName:i,pathname:e.request.location.pathname,deviceSessionId:r.deviceSessionId,devicePublicKey:r.publicKey}),cache:`no-store`,credentials:`omit`,signal:e.request.signal}),o=await Z(a),s=Q(o,`invitationId`),c=Q(o,`deviceSessionId`),l=W($(o,`expiresAt`));if(!a.ok||c!==r.deviceSessionId||!s||l==null)throw new w(Q(o,`error`)??`LINK_ACCESS_FAILED`,Q(o,`message`)??`링크 리뷰를 시작하지 못했습니다.`,a.status);return{deviceSessionId:c,privateKey:r.privateKey,invitationId:s,expiresAt:l}}async function O(e,t){let n=A(e.cryptoProvider),r=await j(e.cryptoProvider,n),i=q(),a=k(e.serviceUrl,e.request.location.origin,t);try{if(i?.setItem(a,n),i?.getItem(a)!==n)throw Error(`storage unavailable`)}catch{throw new w(`ACCOUNT_BRIDGE_FAILED`,`이 브라우저에서는 안전한 계정 로그인을 시작할 수 없습니다.`,0)}let o=await e.fetcher(`${e.serviceUrl}/v1/reviewer/account/start`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({reviewLocator:t,codeChallenge:r,pathname:e.request.location.pathname,returnPath:`${e.request.location.pathname}${e.request.location.search??``}`,returnHash:e.request.location.hash??``}),cache:`no-store`,credentials:`omit`,signal:e.request.signal}),s=await Z(o),c=Q(s,`continueUrl`);throw!o.ok||!c||!re(c,e.serviceUrl)?(i.removeItem(a),new w(Q(s,`error`)??`ACCOUNT_BRIDGE_FAILED`,Q(s,`message`)??`로그인 상태를 확인하지 못했습니다.`,o.status)):((e.navigateToAccountBridge??(e=>{globalThis.location.assign(e)}))(c),new w(`ACCOUNT_BRIDGE_REDIRECT`,`로그인 상태를 확인하는 중입니다.`,0))}async function ne(e,t,n,r){let i=q(),a=k(e.serviceUrl,e.request.location.origin,t),o=i?.getItem(a)??``;if(!/^[A-Za-z0-9_-]{43}$/u.test(o))throw new w(`INVALID_INPUT`,`계정 인증을 시작한 브라우저 탭에서 다시 시도해 주세요.`,400);let s=await e.fetcher(`${e.serviceUrl}/v1/reviewer/account/redeem`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({assertionToken:n,codeVerifier:o,pathname:e.request.location.pathname,deviceSessionId:r.deviceSessionId,devicePublicKey:r.publicKey}),cache:`no-store`,credentials:`omit`,signal:e.request.signal}),c=await Z(s),l=Q(c,`invitationId`),u=Q(c,`deviceSessionId`),d=W($(c,`expiresAt`));if(!s.ok||u!==r.deviceSessionId||!l||d==null)throw new w(Q(c,`error`)??`ACCOUNT_BRIDGE_FAILED`,Q(c,`message`)??`로그인 계정으로 리뷰를 시작하지 못했습니다.`,s.status);return i?.removeItem(a),{deviceSessionId:u,privateKey:r.privateKey,invitationId:l,expiresAt:d}}function k(e,t,n){return`01works.visual-review.account-bridge.v1:${JSON.stringify([e,t,n])}`}function A(e){let t=new Uint8Array(32);return e.getRandomValues(t),M(t)}async function j(e,t){let n=await e.subtle.digest(`SHA-256`,new TextEncoder().encode(t));return M(new Uint8Array(n))}function M(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t).replaceAll(`+`,`-`).replaceAll(`/`,`_`).replace(/=+$/u,``)}function re(e,t){try{let n=new URL(e),r=new URL(`${t}/v1/reviewer/account/continue`);return n.origin===r.origin&&n.pathname===r.pathname&&n.searchParams.has(`state`)&&n.hash===``}catch{return!1}}async function N(e,t){let n={invitationId:e.request.invitationId,pathname:e.request.location.pathname,signal:e.request.signal};return a(e.request.authHost,e.request.signal,{requestCode:()=>oe(e.serviceUrl,e.fetcher,n),verifyCode:(r,i)=>le(e.serviceUrl,e.fetcher,{...n,challengeId:r,code:i,device:t})},e.request.styleNonce)}async function ie(e,t,n,r,i,a){let o;try{o=await P(e,t,n.deviceSessionId,r)}catch(e){throw e instanceof w&&[401,403,409,410].includes(e.status)&&await a(),e}let s=await v(n.privateKey,{purpose:`token`,deviceSessionId:n.deviceSessionId,...o,invitationId:r.invitationId,origin:r.origin,pathname:r.pathname},i),c=await t(`${e}/v1/convex/token`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({invitationId:r.invitationId,origin:r.origin,pathname:r.pathname,deviceSessionId:n.deviceSessionId,proof:{deviceSessionId:n.deviceSessionId,...o,signature:s}}),cache:`no-store`,credentials:`omit`,signal:r.signal}),l=await Z(c),u=Q(l,`token`);if(!c.ok||!u||u.length>16e3)throw[401,403,409,410].includes(c.status)&&await a(),new w(Q(l,`error`)??`CONVEX_AUTH_FAILED`,Q(l,`message`)??`리뷰 인증을 갱신하지 못했습니다.`,c.status);return u}async function P(e,t,n,r,i=`token`){let a=await t(`${e}/v1/convex/token/challenge`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({deviceSessionId:n,invitationId:r.invitationId,origin:r.origin,pathname:r.pathname,purpose:i}),cache:`no-store`,credentials:`omit`,signal:r.signal}),o=await Z(a),s=Q(o,`challengeId`),c=Q(o,`challenge`),l=$(o,`counter`),u=W($(o,`expiresAt`));if(!a.ok||!s||!c||!Number.isSafeInteger(l)||l<1||u==null)throw new w(Q(o,`error`)??`DEVICE_CHALLENGE_FAILED`,Q(o,`message`)??`리뷰 기기 인증을 시작하지 못했습니다.`,a.status);return{challengeId:s,challenge:c,counter:l,expiresAt:u}}async function ae(e,t,n,r,i){let a=await P(e,t,n.deviceSessionId,r,`revoke`),o=await v(n.privateKey,{purpose:`revoke`,deviceSessionId:n.deviceSessionId,...a,invitationId:r.invitationId,origin:r.origin,pathname:r.pathname},i);if(!(await t(`${e}/v1/convex/session/revoke`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({deviceSessionId:n.deviceSessionId,invitationId:r.invitationId,origin:r.origin,pathname:r.pathname,proof:{deviceSessionId:n.deviceSessionId,...a,signature:o}}),cache:`no-store`,credentials:`omit`,keepalive:!0,signal:r.signal})).ok)throw Error(`Reviewer device revocation failed`)}async function F(e,t,n){let r=await t(`${e}/v1/invitations/redeem-email`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({invitationId:n.invitationId,emailToken:n.emailToken,pathname:n.pathname,deviceSessionId:n.device.deviceSessionId,devicePublicKey:n.device.publicKey}),cache:`no-store`,credentials:`omit`,signal:n.signal}),i=await Z(r);if(!r.ok)throw new w(Q(i,`error`)??`INVITATION_REDEEM_FAILED`,Q(i,`message`)??`리뷰 초대를 시작하지 못했습니다.`,r.status);let a=Q(i,`deviceSessionId`);if(!a||a!==n.device.deviceSessionId)throw new w(`INVALID_INVITATION_REDEEM_RESPONSE`,`잘못된 초대 인증 응답입니다.`,502);let o=W($(i,`expiresAt`));if(o===null)throw new w(`INVALID_INVITATION_REDEEM_RESPONSE`,`잘못된 초대 인증 응답입니다.`,502);if(o===void 0)throw new w(`INVALID_INVITATION_REDEEM_RESPONSE`,`device session 만료 시각이 필요합니다.`,502);return{deviceSessionId:a,privateKey:n.device.privateKey,expiresAt:o}}async function oe(e,t,n){let r=await I(e,t,`start`,n),i=Q(r,`challengeId`),a=Q(r,`emailHint`);if(!i||!a)throw new w(`INVALID_RECOVERY_RESPONSE`,`잘못된 복구 응답입니다.`,502);return{challengeId:i,emailHint:a}}async function se(e,t,n){let r=await L(e,t,`start`,n),i=Q(r,`challengeId`),a=Q(r,`attemptId`),o=Q(r,`emailHint`);if(!i||!a||!o)throw new w(`INVALID_PROJECT_ACCESS_RESPONSE`,`프로젝트 리뷰 인증 응답이 올바르지 않습니다.`,502);return{challengeId:i,attemptId:a,emailHint:o}}async function ce(e,t,n){let r=await L(e,t,`verify`,n),i=Q(r,`deviceSessionId`),a=Q(r,`invitationId`),o=W($(r,`expiresAt`));if(i!==n.device.deviceSessionId||!a||o==null)throw new w(`INVALID_PROJECT_ACCESS_RESPONSE`,`프로젝트 리뷰 인증 응답이 올바르지 않습니다.`,502);return{deviceSessionId:i,privateKey:n.device.privateKey,invitationId:a,expiresAt:o}}async function le(e,t,n){let r=await I(e,t,`verify`,n),i=Q(r,`deviceSessionId`);if(!i||i!==n.device.deviceSessionId)throw new w(`INVALID_RECOVERY_RESPONSE`,`잘못된 복구 응답입니다.`,502);let a=W($(r,`expiresAt`));if(a===null)throw new w(`INVALID_RECOVERY_RESPONSE`,`잘못된 복구 응답입니다.`,502);if(a===void 0)throw new w(`INVALID_RECOVERY_RESPONSE`,`device session 만료 시각이 필요합니다.`,502);return{deviceSessionId:i,privateKey:n.device.privateKey,expiresAt:a}}async function I(e,t,n,r){let{signal:i,...a}=r,o=a;if(`device`in a&&R(a.device)){let{device:e,...t}=a;o={...t,deviceSessionId:e.deviceSessionId,devicePublicKey:e.publicKey}}let s=await t(`${e}/v1/invitations/recovery/${n}`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify(o),cache:`no-store`,credentials:`omit`,signal:i}),c=await Z(s);if(!s.ok)throw new w(Q(c,`error`)??`RECOVERY_FAILED`,Q(c,`message`)??`리뷰 접속을 복구하지 못했습니다.`,s.status);if(!c||typeof c!=`object`||Array.isArray(c))throw new w(`INVALID_RECOVERY_RESPONSE`,`잘못된 복구 응답입니다.`,502);return c}async function L(e,t,n,r){let{signal:i,...a}=r,o=a;if(`device`in a&&R(a.device)){let{device:e,...t}=a;o={...t,deviceSessionId:e.deviceSessionId,devicePublicKey:e.publicKey}}let s=await t(`${e}/v1/reviewer/access/${n}`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify(o),cache:`no-store`,credentials:`omit`,signal:i}),c=await Z(s);if(!s.ok)throw new w(Q(c,`error`)??`PROJECT_ACCESS_FAILED`,Q(c,`message`)??`프로젝트 리뷰에 로그인하지 못했습니다.`,s.status);if(!c||typeof c!=`object`||Array.isArray(c))throw new w(`INVALID_PROJECT_ACCESS_RESPONSE`,`프로젝트 리뷰 인증 응답이 올바르지 않습니다.`,502);return c}function R(e){return!!(e&&typeof e==`object`&&`deviceSessionId`in e&&`privateKey`in e&&`publicKey`in e)}function z(e){return{invitationId:e.invitationId,origin:e.location.origin,pathname:e.location.pathname}}async function B(e,t,n,r){let i=await t(`${e}/v1/bootstrap`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify(n),cache:`no-store`,credentials:`omit`,signal:r}),a=await Z(i);if(!i.ok)throw new w(Q(a,`error`)??`BOOTSTRAP_FAILED`,Q(a,`message`)??`Visual Review bootstrap에 실패했습니다.`,i.status);return V(a)}function V(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let t=e;if(typeof t.apiVersion==`number`&&t.apiVersion!==2)throw new w(`UNSUPPORTED_SERVICE_VERSION`,`이 Visual Review 위젯 버전은 더 이상 지원되지 않습니다. 사이트의 @01.works/visual-review를 업데이트해 주세요.`,502);if(t.apiVersion!==2)throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let n=pe(t.convexUrl),r=H(t.branding),i=W(t.invitationExpiresAt),a=ue(t.accessScope);if(t.provider!==`convex`||!n||i===null)throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);return{apiVersion:2,provider:`convex`,convexUrl:n,...r===void 0?{}:{branding:r},...i===void 0?{}:{invitationExpiresAt:i},...a===void 0?{}:{accessScope:a}}}function H(e){if(e===void 0)return;if(!e||typeof e!=`object`||Array.isArray(e))throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let t=e,n=t.projectName===void 0?void 0:Q(t,`projectName`),r=t.iconUrl===void 0?void 0:U(t.iconUrl),i=t.iconAlt===void 0?void 0:Q(t,`iconAlt`);if(n!==void 0&&(n===null||!n.trim()||n.length>200)||t.iconUrl!==void 0&&!r||i===null||i!==void 0&&i.length>200||n===void 0&&r===void 0)throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);return{...typeof n==`string`?{projectName:n.trim()}:{},...typeof r==`string`?{iconUrl:r}:{},...i===void 0?{}:{iconAlt:i}}}function U(e){if(typeof e!=`string`||e.length>2048)return null;try{let t=new URL(e);return t.protocol===`https:`&&!t.username&&!t.password&&t.href===e?t.href:null}catch{return null}}function ue(e){if(e===void 0)return;if(!e||typeof e!=`object`||Array.isArray(e))throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let t=e,n=Q(t,`locator`);if(t.kind!==`project`||!n||n.length>200||t.mode!==void 0&&t.mode!==`email`&&t.mode!==`link`)throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);return{kind:`project`,locator:n,...t.mode===void 0?{}:{mode:t.mode}}}function W(e){if(e!==void 0)return Number.isSafeInteger(e)&&e>0?e:null}function G(e,t,n){return`01works.visual-review.convex-session.v1:${JSON.stringify([e,t,n])}`}function K(...e){return e.filter(e=>e!==null)}function de(){try{return globalThis.localStorage??null}catch{return null}}function q(){try{return globalThis.sessionStorage??null}catch{return null}}async function J(e,t,n){try{let t=await e?.get(n);if(t)return t}catch{}let r=t.get(n)??null;return r&&r.expiresAt<=Date.now()?(t.delete(n),null):r}async function fe(e,t,n,r){t.set(n,r);try{await e?.set(n,r)}catch{}}async function Y(e,t,n){t.delete(n);try{await e?.delete(n)}catch{}}function X(e,t){for(let n of e)try{n.removeItem(t)}catch{}}function pe(e){if(typeof e!=`string`||!e.trim())return null;try{let t=new URL(e.trim());return t.origin!==e.trim()||t.protocol!==`https:`||!t.hostname.endsWith(`.convex.cloud`)?null:t.origin}catch{return null}}async function Z(e){try{return await e.json()}catch{throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`JSON bootstrap 응답이 필요합니다.`,502)}}function Q(e,t){let n=$(e,t);return typeof n==`string`&&n.trim()?n.trim():null}function $(e,t){if(!(!e||typeof e!=`object`||Array.isArray(e)))return e[t]}export{T as createHostedReviewRuntime};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./install-9w7UR92S.js";export{e as installVisualReview};
1
+ import{t as e}from"./install-LaTz8Rmo.js";export{e as installVisualReview};
@@ -1,2 +1,2 @@
1
1
  const e=`https://review.01.works`;function t(e){let t=new URL(e),n=t.hostname===`localhost`||t.hostname===`127.0.0.1`||t.hostname.endsWith(`.test`)||t.hostname.endsWith(`.local`);if(t.protocol!==`https:`&&!(n&&t.protocol===`http:`))throw Error(`Visual Review serviceUrl must use HTTPS outside local development`);if(t.username||t.password||t.search||t.hash)throw Error(`Visual Review serviceUrl must be a plain origin or base path`);return t.toString().replace(/\/+$/,``)}const n=()=>{let e=Promise.resolve(null);return{ready:e,refresh:()=>e,dispose(){}}},r=new Map,i=6e4,a=1e4,o=new WeakMap;function s(e={}){if(e.enabled===!1||typeof window>`u`||typeof document>`u`)return n();let r=!1,i=0,a=null,o=null,s=null,g=null,_=!1,v=!1,y=null,b=null,w=null,D=t(e.serviceUrl??`https://review.01.works`),k=e.activationParam??`visual-review`,j=le(window),N=ue(window),P=C(window.location);if(!(ae(D,k,P,N,j,e.runtime!==void 0)??re(T(D,P.origin)))){let t={reason:`missing-activation`,origin:P.origin};return M(e.build).mode===`development`&&c(document,t.origin),l(e.onInactive,t),n()}let F=new WeakMap,I=new Map,L=fe(e.styleNonce,document),R=x(window.location,k,e.pageIdentity),z=te(window,t=>{let n=x(t,k,e.pageIdentity);if(n===R)return;let r=O(k,t.href)?.invitationId;r&&(v=!1,I.set(t.origin,r)),R=n,V(t)}),B=t=>{if(!(r||!g)){if(t===`membership-revoked`){let e=C(window.location),t=I.get(e.origin);t&&(E(D,e.origin,t),I.delete(e.origin),K(D,e.origin,N,j,t));return}R=x(window.location,k,e.pageIdentity),W()}};return{ready:W(),refresh(){return r?Promise.resolve(null):(R=x(window.location,k,e.pageIdentity),V(C(window.location),!0))},dispose(){r||(r=!0,i+=1,U(),g?.remove(),g=null,w=null,b?.remove(),b=null,I.clear(),y?.dispose(),y=null,z.unsubscribe())}};function V(e,t=!1){return!g||!w||w.origin!==e.origin||O(k,e.href)?.invitationId?W():H(g,w,e,t)}function H(t,n,r,c){let l=++i;a!==o&&a?.abort();let u=new AbortController;a=u,t.beginPageTransition();let d=(async()=>{try{let i=await A(n.resolvePage({location:r,...e.pageIdentity===`url`?{routeKey:S(r)}:{},signal:u.signal,cursorChat:e.cursorChat===!0}),u.signal);return Q(l,u)?(i.commit?.(),t.setPage({pageId:i.pageId,...e.cursorChat===!0?{cursorChat:{roomId:i.cursorChatRoomId}}:{}},{reload:c}),t.isConnected||document.body?.append(t),t):$(l)}catch{return Q(l,u)?W():$(l)}})();return s=d,d}function U(){a?.abort(),o?.abort(),a=null,o=null}function W(){if(v&&!O(k,C(window.location).href))return Promise.resolve(null);let t=++i;U(),y?.suspendSession(),g?.remove(),g=null,w=null,b?.remove();let n=new AbortController,r=document.createElement(`div`);r.dataset.agencyReviewRoot=``,a=n,o=n,b=r;let c=q(t,n,r);return s=c,c.then(r=>{r&&Q(t,n)&&g===r&&e.onReady?.(r)}),c}async function q(t,n,i){try{let e=document.body??await se(document,n.signal);if(!Q(t,n))return $(t);e.append(i);let r=await J(t,n,i);return Q(t,n)?r:(i.remove(),$(t))}catch(a){if(!Q(t,n))return i.remove(),$(t);if(f(a,`AUTH_CANCELLED`)||f(a,`ACCOUNT_BRIDGE_REDIRECT`))return i.remove(),b===i&&(b=null),null;let o=F.get(i),s=o?.[3];if(Z(a,i),p(s)&&f(a,`INVITATION_EMAIL_REQUIRED`)&&!_){let e=o?.[1]??C(window.location).origin,t=o?.[2];return t&&(I.delete(e),K(D,e,N,j,t)),i.remove(),b===i&&(b=null),null}let c=de(a,o?.[2]),u=o?.[1]??C(window.location).origin,d=f(a,`ORIGIN_NOT_ALLOWED`);if(d&&l(e.onInactive,{reason:`origin-not-allowed`,origin:u}),!p(s)||_||!m(a)){let{originNotAllowedMessage:o,renderBootstrapError:s}=await import(`./bootstrap-error-B30Je5Pg.js`);if(!Q(t,n))return i.remove(),$(t);let l=h(a)?void 0:()=>{r||b!==i||(R=x(window.location,k,e.pageIdentity),W())};s(i,d?o(u):c.message,L,l)}else i.remove(),b===i&&(b=null);return e.onError?.(c),window.dispatchEvent(new CustomEvent(`visual-review:error`,{detail:c})),null}}async function J(t,n,r){let i=C(window.location),a=ae(D,k,i,N,j,e.runtime!==void 0),o=ne(I.get(i.origin)),s=re(T(D,i.origin)),c=a?.source===`url`?a:s??o??a;if(!c)return F.delete(r),r.remove(),b===r&&(b=null),null;let l=c.invitationId;F.set(r,[D,i.origin,l,c.source,c.emailToken]),c.source===`url`?(K(D,i.origin,N,j),I.set(i.origin,l),ie(D,i.origin,l,c.emailToken,c.accountToken),z.suppress(()=>oe(k))):c.source===`handoff`&&I.set(i.origin,l),R=x(window.location,k,e.pageIdentity);let f=C(window.location),p=await A((e.runtime??await u(D)).resolve({invitationId:l,emailToken:c.emailToken,accountToken:c.accountToken??void 0,accountBridgeAttempted:c.accountToken!==void 0,location:f,...e.pageIdentity===`url`?{routeKey:S(f)}:{},signal:n.signal,authHost:r,styleNonce:L,cursorChat:e.cursorChat===!0}),n.signal);if(!Q(t,n))return null;E(D,i.origin,l),ce(D,l,i,N,j,p.sessionScope??`browser`,p.sessionExpiresAt),c.source===`url`&&G(j,X(D,i.origin)),I.set(i.origin,l);let m=M(e.build),h=e.sourceContextResolver??await ee(m,n.signal);if(!Q(t,n))return null;let{createActiveReviewSessionLifecycle:te,createReviewNavigationHandler:v,mountReviewWidget:O}=await d(n.signal);return!Q(t,n)||(y??=te({window,document,storage:j,onExit:Y,onReauthenticationRequired:B}),y.bindSession(l,X(D,i.origin),p.clearLocalSession),!Q(t,n))?null:(g=O({...p,...e.cursorChat===!0&&p.cursorChatRepository?{cursorChat:{repository:p.cursorChatRepository,roomId:p.cursorChatRoomId}}:{},offset:e.offset,onNavigateToFeedback:v(window,e.navigateToFeedback),sourceContextResolver:h,styleNonce:L,position:e.position,...m.mode===`development`&&(e.developerTools?.codeContextCopy===!0||e.developerTools?.reactGrab===!0)?{codeContextCopy:{}}:{}}),y.attachWidget(g),w=p.resolvePage?{origin:i.origin,resolvePage:e=>p.resolvePage(e)}:null,r.remove(),b===r&&(b=null),_=!0,window.dispatchEvent(new CustomEvent(`visual-review:ready`,{detail:g})),g)}function Y(e){let t=C(window.location).origin;v=!0,E(D,t,e),I.delete(t),K(D,t,N,j,e),i+=1,U(),g?.remove(),g=null,w=null,b?.remove(),b=null}function Z(e,t){if(!m(e))return;let n=F.get(t);if(!n)return;let[r,i,a]=n;E(r,i,a),I.get(i)===a&&I.delete(i),K(r,i,N,j,a)}function Q(e,t){return!r&&i===e&&a===t&&!t.signal.aborted}function $(e){return!r&&i!==e?s:null}}function c(e,t){let n=o.get(e);n||(n=new Set,o.set(e,n)),!n.has(t)&&(n.add(t),console.info([`[VisualReview] Inactive: no invitation or resume marker was found.`,`Open this page using a Visual Review invitation URL.`,`Current origin: ${t}`].join(`
2
- `)))}function l(e,t){if(e)try{e(t)}catch(e){console.error(`[VisualReview] onInactive callback failed.`,e)}}async function u(e){let{createHostedReviewRuntime:t}=await import(`./hosted-runtime--Sa_y6as.js`);return t({serviceUrl:e})}async function d(e){return j(import(`./active-review-mount-CMXKUMnp.js`),e,a,`리뷰 위젯을 불러오는 시간이 초과되었습니다. 다시 시도해 주세요.`)}async function ee(e,t){let n=M(e),{createEmbeddedSourceContextResolver:r}=await j(import(`./embedded-source-context-BTB3pFqv.js`),t,a,`리뷰 소스 정보를 불러오는 시간이 초과되었습니다. 다시 시도해 주세요.`);return r({build:n})}function f(e,t){return e instanceof Error&&`code`in e&&e.code===t}function p(e){return e===`tab-storage`||e===`browser-storage`||e===`legacy-storage`}function m(e){let t=g(e);return typeof t==`string`&&/^INVITATION_(?:EXPIRED|NOT_FOUND|REVOKED)$/.test(t)}function h(e){return m(e)||g(e)===`UNSUPPORTED_SERVICE_VERSION`}function g(e){return e instanceof Error&&`code`in e?e.code:void 0}const _=new WeakMap;function te(e,t){let n=_.get(e);if(!n){let t=e.history,r=t.pushState,i=t.replaceState,a=new Set;n={history:t,originalPushStateDescriptor:Object.getOwnPropertyDescriptor(t,`pushState`),originalReplaceStateDescriptor:Object.getOwnPropertyDescriptor(t,`replaceState`),patchedPushState:function(t,n,i){r.call(this,t,n,i),v(e)},patchedReplaceState:function(t,n,r){i.call(this,t,n,r),v(e)},popstateListener:()=>v(e),subscribers:a,suppressionDepth:0},_.set(e,n),y(t,`pushState`,n.patchedPushState),y(t,`replaceState`,n.patchedReplaceState),e.addEventListener(`popstate`,n.popstateListener)}n.subscribers.add(t);let r=!0;return{suppress(e){n.suppressionDepth+=1;try{return e()}finally{--n.suppressionDepth}},unsubscribe(){r&&(r=!1,n.subscribers.delete(t),!(n.subscribers.size>0)&&(e.removeEventListener(`popstate`,n.popstateListener),b(n.history,`pushState`,n.patchedPushState,n.originalPushStateDescriptor),b(n.history,`replaceState`,n.patchedReplaceState,n.originalReplaceStateDescriptor),_.delete(e)))}}}function v(e){let t=_.get(e);if(!t||t.suppressionDepth>0)return;let n=C(e.location);[...t.subscribers].forEach(e=>e(n))}function y(e,t,n){Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:!0,value:n})}function b(e,t,n,r){if(Object.getOwnPropertyDescriptor(e,t)?.value===n){if(r){Object.defineProperty(e,t,r);return}delete e[t]}}function x(e,t,n){return`${e.origin}\n${n===`url`?S(e):e.pathname}\n${O(t,e.href)?.invitationId??``}`}function S(e){let t=new URL(e.href);return`${t.pathname}${t.search}${t.hash}`}function C(e){return new URL(e.href)}function ne(e){return e?{invitationId:e,source:`memory`}:null}function re(e){return e?{invitationId:e.invitationId,emailToken:e.emailToken,accountToken:e.accountToken,source:`handoff`}:null}function w(e,t){return JSON.stringify([e,t])}function T(e,t,n=Date.now()){let i=w(e,t),a=r.get(i);if(a){if(a.expiresAt>n)return a;clearTimeout(a.timeout),r.delete(i)}}function ie(e,t,n,a,o,s=Date.now()){D(s);let c=w(e,t),l=r.get(c);for(l&&(clearTimeout(l.timeout),r.delete(c));r.size>=32;){let e=r.keys().next().value;if(!e)break;let t=r.get(e);t&&clearTimeout(t.timeout),r.delete(e)}let u=s+i,d=setTimeout(()=>{r.get(c)?.expiresAt===u&&r.delete(c)},i);r.set(c,{invitationId:n,emailToken:a,accountToken:o,expiresAt:u,timeout:d})}function E(e,t,n){let i=w(e,t),a=r.get(i);a?.invitationId===n&&(clearTimeout(a.timeout),r.delete(i))}function D(e){for(let[t,n]of r)n.expiresAt>e||(clearTimeout(n.timeout),r.delete(t))}function ae(e,t,n,r,i,a){let o=O(t,n.href);if(o)return o;let s=U(i,X(e,n.origin)),c=U(r,J(e,n.origin));if(c&&c!==s)return{invitationId:c,source:`tab-storage`};let l=H(i,Y(e,n.origin));if(l&&l.invitationId!==s)return{invitationId:l.invitationId,source:`browser-storage`};let u=U(i,q(e,n.origin));return u&&u!==s?{invitationId:u,source:`legacy-storage`}:a&&Z(n.hostname)?{invitationId:`local-preview`,source:`local-preview`}:null}function O(e,t){let n=k(new URL(t).hash),r=(n?.get(e)??n?.get(`visual-review`)??n?.get(`agency-review`)??n?.get(`agency-review-invite`))?.trim();if(!r)return null;let i=n?.get(`visual-review-token`)?.trim(),a=n?.get(`visual-review-account-token`)?.trim();return{invitationId:r,emailToken:i&&/^[\w-]{43}$/.test(i)?i:void 0,accountToken:(a&&a.length<=4096&&/^[\w.-]+$/.test(a)?a:void 0)??(n?.get(`visual-review-account`)===`none`?null:void 0),source:`url`}}function k(e){let t=e.replace(/^#?\??/,``);return t.includes(`=`)?new URLSearchParams(t):null}function oe(e){let t=new URL(window.location.href),n=[e,`visual-review`,`agency-review`,`agency-review-invite`,`visual-review-token`,`visual-review-account-token`,`visual-review-account`,`visual-review-return-hash`];for(let e of n)t.searchParams.delete(e);let r=k(t.hash);if(r){let e=r.get(`visual-review-return-hash`);for(let e of n)r.delete(e);let i=r.toString();t.hash=e!==null&&e.length<=2e3&&(e===``||e[0]===`#`)?e:i?`#${i}`:``}window.history.replaceState(window.history.state,``,`${t.pathname}${t.search}${t.hash}`)}function A(e,t){return new Promise((n,r)=>{let i=()=>r(new DOMException(`Aborted`,`AbortError`));if(t.aborted){i();return}t.addEventListener(`abort`,i,{once:!0}),e.then(e=>{t.removeEventListener(`abort`,i),n(e)},e=>{t.removeEventListener(`abort`,i),r(e)})})}function j(e,t,n,r){return new Promise((i,a)=>{let o=!1,s=e=>{o||(o=!0,clearTimeout(l),t.removeEventListener(`abort`,c),e())},c=()=>{s(()=>a(new DOMException(`Aborted`,`AbortError`)))},l=setTimeout(()=>{s(()=>a(Error(r)))},n);if(t.aborted){c();return}t.addEventListener(`abort`,c,{once:!0}),e.then(e=>s(()=>i(e)),e=>s(()=>a(e)))})}function se(e,t){return e.body?Promise.resolve(e.body):t.aborted?Promise.reject(new DOMException(`Aborted`,`AbortError`)):new Promise((n,r)=>{let i=!1,a=new MutationObserver(()=>c()),o=()=>{e.removeEventListener(`DOMContentLoaded`,c),t.removeEventListener(`abort`,l),a.disconnect()},s=e=>{i||(i=!0,o(),e())},c=()=>{let t=e.body;t&&s(()=>n(t))},l=()=>{s(()=>r(new DOMException(`Aborted`,`AbortError`)))};e.addEventListener(`DOMContentLoaded`,c),t.addEventListener(`abort`,l,{once:!0}),a.observe(e,{childList:!0,subtree:!0}),c()})}function M(e){let t=document.querySelector(`meta[name="visual-review-build"]`),n=document.querySelector(`meta[name="visual-review-commit"]`),r=V(F()),i=V(I());return{buildId:B(e?.buildId)??z(t)??r??`unversioned`,gitCommit:L(e,n,i),generatedAt:e?.generatedAt,mode:e?.mode??P(N())??(Z(window.location.hostname)?`development`:`production`)}}function N(){try{return process.env.VISUAL_REVIEW_PUBLIC_BUILD_MODE}catch{return}}function P(e){return e===`development`||e===`preview`||e===`production`?e:void 0}function F(){try{return process.env.VISUAL_REVIEW_PUBLIC_BUILD_ID}catch{return}}function I(){try{return process.env.VISUAL_REVIEW_PUBLIC_GIT_COMMIT}catch{return}}function L(e,t,n){return e?.gitCommit===null?null:typeof e?.gitCommit==`string`?R(e.gitCommit):R(t?.content)??R(n)}function R(e){if(typeof e!=`string`)return null;let t=e.trim().toLowerCase();return/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(t)?t:null}function z(e){return V(e?.content)}function B(e){let t=typeof e==`string`?e.trim():``;return t.length>0?t:null}function V(e){return typeof e==`string`&&e.length>0?e:null}function ce(e,t,n,r,i,a,o){if(a===`tab`){try{r?.setItem(J(e,n.origin),t)}catch{}G(i,Y(e,n.origin))}else{let a=Number.isSafeInteger(o)&&o>Date.now()?o:Date.now()+864e5;try{i?.setItem(Y(e,n.origin),JSON.stringify({version:2,invitationId:t,expiresAt:a}))}catch{}G(r,J(e,n.origin))}W(e,n.origin,i,t)}function H(e,t){let n=U(e,t);if(!n)return null;let r=()=>{try{e?.removeItem?.(t)}catch{}};try{let e=JSON.parse(n),t=e.expiresAt;return e.version!==2||typeof e.invitationId!=`string`||!e.invitationId.trim()||!Number.isSafeInteger(t)||t<=Date.now()?(r(),null):{version:2,invitationId:e.invitationId,expiresAt:t}}catch{return r(),null}}function U(e,t){try{return e?.getItem(t)??null}catch{return null}}function le(e){try{return e.localStorage}catch{return null}}function ue(e){try{return e.sessionStorage}catch{return null}}function W(e,t,n,r){try{if(!n)return;let i=q(e,t),a=n.getItem(i);if(a===null||r!==void 0&&a!==r)return;n.removeItem(i)}catch{}}function G(e,t){try{e?.removeItem(t)}catch{}}function K(e,t,n,r,i){W(e,t,r,i);let a=J(e,t),o=Y(e,t);if(i===void 0){G(n,a),G(r,o);return}U(n,a)===i&&G(n,a),H(r,o)?.invitationId===i&&G(r,o)}function q(e,t){return`visual-review:${e}:${t}:invitation`}function J(e,t){return`visual-review:${e}:${t}:invitation:tab:v2`}function Y(e,t){return`visual-review:${e}:${t}:invitation:browser:v2`}function X(e,t){return`visual-review:${e}:${t}:exit:v1`}function Z(e){return e===`localhost`||e===`127.0.0.1`||e.endsWith(`.test`)||e.endsWith(`.local`)}function de(e,t){let n=e instanceof Error?e.message:String(e),r=Error(Q(n,t));if(e&&typeof e==`object`){let n=`code`in e?e.code:void 0,i=`status`in e?e.status:void 0;typeof n==`string`&&Object.defineProperty(r,"code",{configurable:!0,enumerable:!0,value:Q(n,t)}),typeof i==`number`&&Number.isFinite(i)&&Object.defineProperty(r,"status",{configurable:!0,enumerable:!0,value:i})}return r}function Q(e,t){if(!t)return e;let n=new Set([t,encodeURIComponent(t)]),r=e;for(let e of n)e&&(r=r.split(e).join(`[redacted]`));return r}function fe(e,t){if(e!==void 0)return pe(e);let n=t.currentScript,r=n?$(n):void 0;if(r)return r;try{for(let e of t.querySelectorAll(`script[nonce],style[nonce]`)){let t=$(e);if(t)return t}}catch{}}function $(e){let t=`nonce`in e?e.nonce:void 0;return typeof t==`string`?pe(t):void 0}function pe(e){return e.trim()||void 0}export{e as n,t as r,s as t};
2
+ `)))}function l(e,t){if(e)try{e(t)}catch(e){console.error(`[VisualReview] onInactive callback failed.`,e)}}async function u(e){let{createHostedReviewRuntime:t}=await import(`./hosted-runtime-CQLYCgC8.js`);return t({serviceUrl:e})}async function d(e){return j(import(`./active-review-mount-CMXKUMnp.js`),e,a,`리뷰 위젯을 불러오는 시간이 초과되었습니다. 다시 시도해 주세요.`)}async function ee(e,t){let n=M(e),{createEmbeddedSourceContextResolver:r}=await j(import(`./embedded-source-context-BTB3pFqv.js`),t,a,`리뷰 소스 정보를 불러오는 시간이 초과되었습니다. 다시 시도해 주세요.`);return r({build:n})}function f(e,t){return e instanceof Error&&`code`in e&&e.code===t}function p(e){return e===`tab-storage`||e===`browser-storage`||e===`legacy-storage`}function m(e){let t=g(e);return typeof t==`string`&&/^INVITATION_(?:EXPIRED|NOT_FOUND|REVOKED)$/.test(t)}function h(e){return m(e)||g(e)===`UNSUPPORTED_SERVICE_VERSION`}function g(e){return e instanceof Error&&`code`in e?e.code:void 0}const _=new WeakMap;function te(e,t){let n=_.get(e);if(!n){let t=e.history,r=t.pushState,i=t.replaceState,a=new Set;n={history:t,originalPushStateDescriptor:Object.getOwnPropertyDescriptor(t,`pushState`),originalReplaceStateDescriptor:Object.getOwnPropertyDescriptor(t,`replaceState`),patchedPushState:function(t,n,i){r.call(this,t,n,i),v(e)},patchedReplaceState:function(t,n,r){i.call(this,t,n,r),v(e)},popstateListener:()=>v(e),subscribers:a,suppressionDepth:0},_.set(e,n),y(t,`pushState`,n.patchedPushState),y(t,`replaceState`,n.patchedReplaceState),e.addEventListener(`popstate`,n.popstateListener)}n.subscribers.add(t);let r=!0;return{suppress(e){n.suppressionDepth+=1;try{return e()}finally{--n.suppressionDepth}},unsubscribe(){r&&(r=!1,n.subscribers.delete(t),!(n.subscribers.size>0)&&(e.removeEventListener(`popstate`,n.popstateListener),b(n.history,`pushState`,n.patchedPushState,n.originalPushStateDescriptor),b(n.history,`replaceState`,n.patchedReplaceState,n.originalReplaceStateDescriptor),_.delete(e)))}}}function v(e){let t=_.get(e);if(!t||t.suppressionDepth>0)return;let n=C(e.location);[...t.subscribers].forEach(e=>e(n))}function y(e,t,n){Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:!0,value:n})}function b(e,t,n,r){if(Object.getOwnPropertyDescriptor(e,t)?.value===n){if(r){Object.defineProperty(e,t,r);return}delete e[t]}}function x(e,t,n){return`${e.origin}\n${n===`url`?S(e):e.pathname}\n${O(t,e.href)?.invitationId??``}`}function S(e){let t=new URL(e.href);return`${t.pathname}${t.search}${t.hash}`}function C(e){return new URL(e.href)}function ne(e){return e?{invitationId:e,source:`memory`}:null}function re(e){return e?{invitationId:e.invitationId,emailToken:e.emailToken,accountToken:e.accountToken,source:`handoff`}:null}function w(e,t){return JSON.stringify([e,t])}function T(e,t,n=Date.now()){let i=w(e,t),a=r.get(i);if(a){if(a.expiresAt>n)return a;clearTimeout(a.timeout),r.delete(i)}}function ie(e,t,n,a,o,s=Date.now()){D(s);let c=w(e,t),l=r.get(c);for(l&&(clearTimeout(l.timeout),r.delete(c));r.size>=32;){let e=r.keys().next().value;if(!e)break;let t=r.get(e);t&&clearTimeout(t.timeout),r.delete(e)}let u=s+i,d=setTimeout(()=>{r.get(c)?.expiresAt===u&&r.delete(c)},i);r.set(c,{invitationId:n,emailToken:a,accountToken:o,expiresAt:u,timeout:d})}function E(e,t,n){let i=w(e,t),a=r.get(i);a?.invitationId===n&&(clearTimeout(a.timeout),r.delete(i))}function D(e){for(let[t,n]of r)n.expiresAt>e||(clearTimeout(n.timeout),r.delete(t))}function ae(e,t,n,r,i,a){let o=O(t,n.href);if(o)return o;let s=U(i,X(e,n.origin)),c=U(r,J(e,n.origin));if(c&&c!==s)return{invitationId:c,source:`tab-storage`};let l=H(i,Y(e,n.origin));if(l&&l.invitationId!==s)return{invitationId:l.invitationId,source:`browser-storage`};let u=U(i,q(e,n.origin));return u&&u!==s?{invitationId:u,source:`legacy-storage`}:a&&Z(n.hostname)?{invitationId:`local-preview`,source:`local-preview`}:null}function O(e,t){let n=k(new URL(t).hash),r=(n?.get(e)??n?.get(`visual-review`)??n?.get(`agency-review`)??n?.get(`agency-review-invite`))?.trim();if(!r)return null;let i=n?.get(`visual-review-token`)?.trim(),a=n?.get(`visual-review-account-token`)?.trim();return{invitationId:r,emailToken:i&&/^[\w-]{43}$/.test(i)?i:void 0,accountToken:(a&&a.length<=4096&&/^[\w.-]+$/.test(a)?a:void 0)??(n?.get(`visual-review-account`)===`none`?null:void 0),source:`url`}}function k(e){let t=e.replace(/^#?\??/,``);return t.includes(`=`)?new URLSearchParams(t):null}function oe(e){let t=new URL(window.location.href),n=[e,`visual-review`,`agency-review`,`agency-review-invite`,`visual-review-token`,`visual-review-account-token`,`visual-review-account`,`visual-review-return-hash`];for(let e of n)t.searchParams.delete(e);let r=k(t.hash);if(r){let e=r.get(`visual-review-return-hash`);for(let e of n)r.delete(e);let i=r.toString();t.hash=e!==null&&e.length<=2e3&&(e===``||e[0]===`#`)?e:i?`#${i}`:``}window.history.replaceState(window.history.state,``,`${t.pathname}${t.search}${t.hash}`)}function A(e,t){return new Promise((n,r)=>{let i=()=>r(new DOMException(`Aborted`,`AbortError`));if(t.aborted){i();return}t.addEventListener(`abort`,i,{once:!0}),e.then(e=>{t.removeEventListener(`abort`,i),n(e)},e=>{t.removeEventListener(`abort`,i),r(e)})})}function j(e,t,n,r){return new Promise((i,a)=>{let o=!1,s=e=>{o||(o=!0,clearTimeout(l),t.removeEventListener(`abort`,c),e())},c=()=>{s(()=>a(new DOMException(`Aborted`,`AbortError`)))},l=setTimeout(()=>{s(()=>a(Error(r)))},n);if(t.aborted){c();return}t.addEventListener(`abort`,c,{once:!0}),e.then(e=>s(()=>i(e)),e=>s(()=>a(e)))})}function se(e,t){return e.body?Promise.resolve(e.body):t.aborted?Promise.reject(new DOMException(`Aborted`,`AbortError`)):new Promise((n,r)=>{let i=!1,a=new MutationObserver(()=>c()),o=()=>{e.removeEventListener(`DOMContentLoaded`,c),t.removeEventListener(`abort`,l),a.disconnect()},s=e=>{i||(i=!0,o(),e())},c=()=>{let t=e.body;t&&s(()=>n(t))},l=()=>{s(()=>r(new DOMException(`Aborted`,`AbortError`)))};e.addEventListener(`DOMContentLoaded`,c),t.addEventListener(`abort`,l,{once:!0}),a.observe(e,{childList:!0,subtree:!0}),c()})}function M(e){let t=document.querySelector(`meta[name="visual-review-build"]`),n=document.querySelector(`meta[name="visual-review-commit"]`),r=V(F()),i=V(I());return{buildId:B(e?.buildId)??z(t)??r??`unversioned`,gitCommit:L(e,n,i),generatedAt:e?.generatedAt,mode:e?.mode??P(N())??(Z(window.location.hostname)?`development`:`production`)}}function N(){try{return process.env.VISUAL_REVIEW_PUBLIC_BUILD_MODE}catch{return}}function P(e){return e===`development`||e===`preview`||e===`production`?e:void 0}function F(){try{return process.env.VISUAL_REVIEW_PUBLIC_BUILD_ID}catch{return}}function I(){try{return process.env.VISUAL_REVIEW_PUBLIC_GIT_COMMIT}catch{return}}function L(e,t,n){return e?.gitCommit===null?null:typeof e?.gitCommit==`string`?R(e.gitCommit):R(t?.content)??R(n)}function R(e){if(typeof e!=`string`)return null;let t=e.trim().toLowerCase();return/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(t)?t:null}function z(e){return V(e?.content)}function B(e){let t=typeof e==`string`?e.trim():``;return t.length>0?t:null}function V(e){return typeof e==`string`&&e.length>0?e:null}function ce(e,t,n,r,i,a,o){if(a===`tab`){try{r?.setItem(J(e,n.origin),t)}catch{}G(i,Y(e,n.origin))}else{let a=Number.isSafeInteger(o)&&o>Date.now()?o:Date.now()+864e5;try{i?.setItem(Y(e,n.origin),JSON.stringify({version:2,invitationId:t,expiresAt:a}))}catch{}G(r,J(e,n.origin))}W(e,n.origin,i,t)}function H(e,t){let n=U(e,t);if(!n)return null;let r=()=>{try{e?.removeItem?.(t)}catch{}};try{let e=JSON.parse(n),t=e.expiresAt;return e.version!==2||typeof e.invitationId!=`string`||!e.invitationId.trim()||!Number.isSafeInteger(t)||t<=Date.now()?(r(),null):{version:2,invitationId:e.invitationId,expiresAt:t}}catch{return r(),null}}function U(e,t){try{return e?.getItem(t)??null}catch{return null}}function le(e){try{return e.localStorage}catch{return null}}function ue(e){try{return e.sessionStorage}catch{return null}}function W(e,t,n,r){try{if(!n)return;let i=q(e,t),a=n.getItem(i);if(a===null||r!==void 0&&a!==r)return;n.removeItem(i)}catch{}}function G(e,t){try{e?.removeItem(t)}catch{}}function K(e,t,n,r,i){W(e,t,r,i);let a=J(e,t),o=Y(e,t);if(i===void 0){G(n,a),G(r,o);return}U(n,a)===i&&G(n,a),H(r,o)?.invitationId===i&&G(r,o)}function q(e,t){return`visual-review:${e}:${t}:invitation`}function J(e,t){return`visual-review:${e}:${t}:invitation:tab:v2`}function Y(e,t){return`visual-review:${e}:${t}:invitation:browser:v2`}function X(e,t){return`visual-review:${e}:${t}:exit:v1`}function Z(e){return e===`localhost`||e===`127.0.0.1`||e.endsWith(`.test`)||e.endsWith(`.local`)}function de(e,t){let n=e instanceof Error?e.message:String(e),r=Error(Q(n,t));if(e&&typeof e==`object`){let n=`code`in e?e.code:void 0,i=`status`in e?e.status:void 0;typeof n==`string`&&Object.defineProperty(r,"code",{configurable:!0,enumerable:!0,value:Q(n,t)}),typeof i==`number`&&Number.isFinite(i)&&Object.defineProperty(r,"status",{configurable:!0,enumerable:!0,value:i})}return r}function Q(e,t){if(!t)return e;let n=new Set([t,encodeURIComponent(t)]),r=e;for(let e of n)e&&(r=r.split(e).join(`[redacted]`));return r}function fe(e,t){if(e!==void 0)return pe(e);let n=t.currentScript,r=n?$(n):void 0;if(r)return r;try{for(let e of t.querySelectorAll(`script[nonce],style[nonce]`)){let t=$(e);if(t)return t}}catch{}}function $(e){let t=`nonce`in e?e.nonce:void 0;return typeof t==`string`?pe(t):void 0}function pe(e){return e.trim()||void 0}export{e as n,t as r,s as t};
package/dist/react.js CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./install-9w7UR92S.js";import{useEffect as t,useRef as n}from"react";function r({activationParam:r,build:i,cursorChat:a,developerTools:o,enabled:s,navigateToFeedback:c,offset:l,onError:u,onInactive:d,onReady:f,pageIdentity:p,position:m,runtime:h,serviceUrl:g,sourceContextResolver:_,styleNonce:v}){let y=n(u),b=n(d),x=n(f),S=n(null),C=i?.buildId,w=i?.gitCommit,T=i?.generatedAt,E=i?.mode,D=o?.codeContextCopy===!0,O=o?.reactGrab===!0;return y.current=u,b.current=d,x.current=f,t(()=>{if(s===!1)return;let t=e({activationParam:r,build:C===void 0&&w===void 0&&T===void 0&&E===void 0?void 0:{buildId:C,gitCommit:w,generatedAt:T,mode:E},cursorChat:a,developerTools:D||O?{codeContextCopy:D,reactGrab:O}:void 0,enabled:s,navigateToFeedback:c,offset:l,onError:e=>y.current?.(e),onInactive:e=>{let t=`${e.reason}\n${e.origin}`;S.current!==t&&(S.current=t,b.current?.(e))},onReady:e=>{S.current=null,x.current?.(e)},pageIdentity:p,runtime:h,position:m,serviceUrl:g,sourceContextResolver:_,styleNonce:v});return()=>t.dispose()},[r,C,E,a,D,s,T,w,O,c,l?.x,l?.y,p,m,h,g,_,v]),null}r.displayName=`VisualReview`;export{r as VisualReview};
1
+ import{t as e}from"./install-LaTz8Rmo.js";import{useEffect as t,useRef as n}from"react";function r({activationParam:r,build:i,cursorChat:a,developerTools:o,enabled:s,navigateToFeedback:c,offset:l,onError:u,onInactive:d,onReady:f,pageIdentity:p,position:m,runtime:h,serviceUrl:g,sourceContextResolver:_,styleNonce:v}){let y=n(u),b=n(d),x=n(f),S=n(null),C=i?.buildId,w=i?.gitCommit,T=i?.generatedAt,E=i?.mode,D=o?.codeContextCopy===!0,O=o?.reactGrab===!0;return y.current=u,b.current=d,x.current=f,t(()=>{if(s===!1)return;let t=e({activationParam:r,build:C===void 0&&w===void 0&&T===void 0&&E===void 0?void 0:{buildId:C,gitCommit:w,generatedAt:T,mode:E},cursorChat:a,developerTools:D||O?{codeContextCopy:D,reactGrab:O}:void 0,enabled:s,navigateToFeedback:c,offset:l,onError:e=>y.current?.(e),onInactive:e=>{let t=`${e.reason}\n${e.origin}`;S.current!==t&&(S.current=t,b.current?.(e))},onReady:e=>{S.current=null,x.current?.(e)},pageIdentity:p,runtime:h,position:m,serviceUrl:g,sourceContextResolver:_,styleNonce:v});return()=>t.dispose()},[r,C,E,a,D,s,T,w,O,c,l?.x,l?.y,p,m,h,g,_,v]),null}r.displayName=`VisualReview`;export{r as VisualReview};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@01.works/visual-review",
3
- "version": "0.17.1",
3
+ "version": "0.17.2",
4
4
  "description": "Invitation-only visual feedback for staging websites, hosted by 01.works.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,13 +0,0 @@
1
- import{r as e}from"./install-9w7UR92S.js";var t=class extends Error{code;constructor(e,t){super(t),this.code=e,this.name=`ReviewBootstrapError`}};function n(e,t,n=``,r){if(t.aborted)return Promise.reject(d());let i=o(e);i.replaceChildren(s(i,r),c(i));let a=i.querySelector(`form`),l=a.elements.namedItem(`displayName`),u=i.querySelector(`button`),p=i.querySelector(`.error`);return l.value=n.trim().slice(0,120),l.focus(),l.select(),new Promise((e,n)=>{let r=!1,o=e=>{r||(r=!0,t.removeEventListener(`abort`,s),i.removeEventListener(`keydown`,h),a.removeEventListener(`submit`,m),e())},s=()=>o(()=>n(d())),c=()=>{let t=l.value.trim();if(!t){p.textContent=`이름을 입력해 주세요.`,l.focus();return}if(t.length>120){p.textContent=`이름은 120자 이하여야 합니다.`,l.focus();return}p.textContent=``,u.disabled=!0,a.setAttribute(`aria-busy`,`true`),o(()=>e(t))},m=e=>{e.preventDefault(),c()},h=e=>{let t=e;if(t.key===`Escape`){t.preventDefault(),s();return}if(t.key===`Tab`){f(i,t);return}t.key===`Enter`&&!t.isComposing&&t.target===l&&(t.preventDefault(),c())};t.addEventListener(`abort`,s,{once:!0}),i.addEventListener(`keydown`,h),a.addEventListener(`submit`,m)})}function r(e,t,n){if(t.aborted)return Promise.reject(d());let r=o(e);r.replaceChildren(s(r,n),l(r));let i=r.querySelector(`form`),a=r.querySelector(`button`);return a.focus(),new Promise((e,n)=>{let o=!1,s=e=>{o||(o=!0,t.removeEventListener(`abort`,c),r.removeEventListener(`keydown`,u),i.removeEventListener(`submit`,l),e())},c=()=>s(()=>n(d())),l=t=>{t.preventDefault(),a.disabled=!0,i.setAttribute(`aria-busy`,`true`),s(e)},u=e=>{let t=e;t.key===`Escape`?(t.preventDefault(),c()):t.key===`Tab`&&f(r,t)};t.addEventListener(`abort`,c,{once:!0}),r.addEventListener(`keydown`,u),i.addEventListener(`submit`,l)})}function i(e,t,n,r){if(t.aborted)return Promise.reject(d());let i=o(e),a=s(i,r);return new Promise((e,r)=>{let o=!1,s=null,c=null,l=()=>{s&&c&&s.removeEventListener(`submit`,c),s=null,c=null},p=e=>{o||(o=!0,t.removeEventListener(`abort`,m),i.removeEventListener(`keydown`,h),l(),e())},m=()=>p(()=>r(d())),h=e=>{let t=e;t.key===`Escape`?(t.preventDefault(),m()):t.key===`Tab`&&f(i,t)},g=(e,t)=>{l(),s=e,c=t,e.addEventListener(`submit`,t),i.replaceChildren(a,e)},_=(t,r)=>{let a=i.ownerDocument,o=a.createElement(`form`);o.setAttribute(`role`,`dialog`),o.setAttribute(`aria-modal`,`true`);let s=u(a,`p`,`01.works`,`eyebrow`),c=u(a,`h2`,`이메일 확인`),l=u(a,`p`,`${r.emailHint}로 보낸 6자리 코드를 입력하세요.`,`description`),d=u(a,`label`,`인증 코드`),f=a.createElement(`input`);f.name=`code`,f.inputMode=`numeric`,f.autocomplete=`one-time-code`,f.pattern=`[0-9]{6}`,f.maxLength=6,f.required=!0;let m=u(a,`p`,``,`error`);m.setAttribute(`role`,`alert`);let h=u(a,`button`,`리뷰 시작`);h.type=`submit`,d.append(f),o.append(s,c,l,d,m,h),g(o,i=>{i.preventDefault();let a=f.value.trim();if(!/^\d{6}$/u.test(a)){m.textContent=`6자리 숫자를 입력해 주세요.`;return}h.disabled=!0,o.setAttribute(`aria-busy`,`true`),m.textContent=``,n.verifyCode(r.challengeId,r.attemptId,t,a).then(t=>p(()=>e(t)),e=>{h.disabled=!1,o.removeAttribute(`aria-busy`),m.textContent=e instanceof Error?e.message:`인증 코드를 확인하지 못했습니다.`,f.select()})}),f.focus()},v=i.ownerDocument,y=v.createElement(`form`);y.setAttribute(`role`,`dialog`),y.setAttribute(`aria-modal`,`true`);let b=u(v,`p`,`01.works`,`eyebrow`),x=u(v,`h2`,`리뷰 로그인`),S=u(v,`p`,`초대받은 이메일로 인증 코드를 보내드립니다.`,`description`),C=u(v,`label`,`이메일`),w=v.createElement(`input`);w.name=`email`,w.type=`email`,w.autocomplete=`email`,w.required=!0;let T=u(v,`p`,``,`error`);T.setAttribute(`role`,`alert`);let E=u(v,`button`,`인증 코드 받기`);E.type=`submit`,C.append(w),y.append(b,x,S,C,T,E),g(y,e=>{e.preventDefault();let t=w.value.trim().toLowerCase();if(t.length>320||!/^[^\s@]+@[^\s@]+\.[^\s@]+$/u.test(t)){T.textContent=`유효한 이메일을 입력해 주세요.`;return}E.disabled=!0,y.setAttribute(`aria-busy`,`true`),T.textContent=``,n.requestCode(t).then(e=>_(t,e),e=>{E.disabled=!1,y.removeAttribute(`aria-busy`),T.textContent=e instanceof Error?e.message:`인증 코드를 보내지 못했습니다.`})}),t.addEventListener(`abort`,m,{once:!0}),i.addEventListener(`keydown`,h),i.replaceChildren(a,y),w.focus()})}async function a(e,t,n,r){if(t.aborted)throw d();let i=await n.requestCode();if(t.aborted)throw d();let a=o(e),c=a.ownerDocument.createElement(`form`);c.setAttribute(`role`,`dialog`),c.setAttribute(`aria-modal`,`true`);let l=u(a.ownerDocument,`p`,`01.works`,`eyebrow`),p=u(a.ownerDocument,`h2`,`리뷰 접속 복구`),m=u(a.ownerDocument,`p`,`${i.emailHint}로 보낸 6자리 코드를 입력하세요.`,`description`),h=u(a.ownerDocument,`label`,`복구 코드`),g=a.ownerDocument.createElement(`input`);g.name=`code`,g.inputMode=`numeric`,g.autocomplete=`one-time-code`,g.pattern=`[0-9]{6}`,g.maxLength=6,g.required=!0;let _=u(a.ownerDocument,`p`,``,`error`);_.setAttribute(`role`,`alert`);let v=u(a.ownerDocument,`button`,`접속 복구`);return v.type=`submit`,h.append(g),c.append(l,p,m,h,_,v),a.replaceChildren(s(a,r),c),g.focus(),new Promise((e,r)=>{let o=!1,s=e=>{o||(o=!0,t.removeEventListener(`abort`,l),a.removeEventListener(`keydown`,u),c.removeEventListener(`submit`,p),e())},l=()=>s(()=>r(d())),u=e=>{let t=e;t.key===`Escape`?(t.preventDefault(),l()):t.key===`Tab`&&f(a,t)},p=t=>{t.preventDefault();let r=g.value.trim();if(!/^\d{6}$/.test(r)){_.textContent=`6자리 숫자를 입력해 주세요.`;return}v.disabled=!0,c.setAttribute(`aria-busy`,`true`),_.textContent=``,n.verifyCode(i.challengeId,r).then(t=>s(()=>e(t)),e=>{v.disabled=!1,c.removeAttribute(`aria-busy`),_.textContent=e instanceof Error?e.message:`복구 코드를 확인하지 못했습니다.`,g.select()})};t.addEventListener(`abort`,l,{once:!0}),a.addEventListener(`keydown`,u),c.addEventListener(`submit`,p)})}function o(e){return e.shadowRoot??e.attachShadow({mode:`open`})}function s(e,t){let n=e.ownerDocument.createElement(`style`);return t&&(n.nonce=t),n.textContent=p,n}function c(e){let t=e.ownerDocument,n=t.createElement(`form`);n.setAttribute(`role`,`dialog`),n.setAttribute(`aria-modal`,`true`),n.setAttribute(`aria-labelledby`,`visual-review-name-title`),n.setAttribute(`aria-describedby`,`visual-review-name-description`);let r=u(t,`p`,`01.works`,`eyebrow`),i=u(t,`h2`,`표시 이름`);i.id=`visual-review-name-title`;let a=u(t,`p`,`피드백에 이 이름이 표시됩니다.`,`description`);a.id=`visual-review-name-description`;let o=u(t,`label`,`이름`);o.htmlFor=`visual-review-display-name`;let s=t.createElement(`input`);s.id=`visual-review-display-name`,s.name=`displayName`,s.autocomplete=`name`,s.maxLength=120,s.required=!0;let c=u(t,`p`,``,`error`);c.setAttribute(`role`,`alert`);let l=u(t,`button`,`리뷰 시작`);return l.type=`submit`,n.append(r,i,a,o,s,c,l),n}function l(e){let t=e.ownerDocument,n=t.createElement(`form`);n.setAttribute(`role`,`dialog`),n.setAttribute(`aria-modal`,`true`),n.setAttribute(`aria-labelledby`,`visual-review-invitation-title`),n.setAttribute(`aria-describedby`,`visual-review-invitation-description`);let r=u(t,`p`,`01.works`,`eyebrow`),i=u(t,`h2`,`리뷰 초대`);i.id=`visual-review-invitation-title`;let a=u(t,`p`,`초대받은 프로젝트를 열고 화면에 피드백을 남길 수 있습니다.`,`description`);a.id=`visual-review-invitation-description`;let o=u(t,`button`,`리뷰 시작`);return o.type=`submit`,n.append(r,i,a,o),n}function u(e,t,n,r){let i=e.createElement(t);return i.textContent=n,r&&(i.className=r),i}function d(){return new t(`AUTH_CANCELLED`,`리뷰 로그인이 취소되었습니다.`)}function f(e,t){let n=Array.from(e.querySelectorAll(`input:not([disabled]),button:not([disabled])`)).filter(e=>!e.closest(`[hidden]`));if(n.length===0)return;let r=n[0],i=n.at(-1);t.shiftKey&&e.activeElement===r?(t.preventDefault(),i.focus()):!t.shiftKey&&e.activeElement===i&&(t.preventDefault(),r.focus())}const p=`
2
- :host{all:initial;--vr-panel:#fff;--vr-text:#161616;--vr-muted:#707070;--vr-label:#3f3f3f;--vr-control:#fff;--vr-border:#d9d9d9;--vr-action:#161616;--vr-action-text:#fff;position:fixed;inset:0;z-index:2147483646;display:grid;place-items:center;overflow:auto;padding:8px;box-sizing:border-box;background:rgba(15,15,15,.34);font-family:Inter,ui-sans-serif,system-ui,sans-serif;color:var(--vr-text)}
3
- form{box-sizing:border-box;width:min(320px,calc(100vw - 16px));padding:20px;border:0;border-radius:14px;background:var(--vr-panel);box-shadow:0 18px 56px rgba(0,0,0,.18)}
4
- .eyebrow{margin:0 0 10px;color:var(--vr-muted);font-size:10px;font-weight:700;letter-spacing:.09em;text-transform:uppercase}
5
- h2{margin:0 0 5px;font-size:16px;line-height:1.3;letter-spacing:-.01em}.description{margin:0 0 18px;color:var(--vr-muted);font-size:13px;line-height:1.45}
6
- [hidden]{display:none!important}label{display:grid;gap:6px;margin-top:12px;color:var(--vr-label);font-size:12px;font-weight:600}input{box-sizing:border-box;width:100%;height:40px;margin-top:6px;padding:0 11px;border:1px solid var(--vr-border);border-radius:9px;background:var(--vr-control);color:var(--vr-text);font:400 13px/1 Inter,ui-sans-serif,system-ui,sans-serif}
7
- label input{margin-top:0}input:focus{border-color:#999;outline:0;box-shadow:0 0 0 3px rgba(127,127,127,.16)}.error{min-height:17px;margin:8px 0 0;color:#d92d20;font-size:11px;line-height:1.45}
8
- button{width:100%;height:40px;margin-top:7px;border:0;border-radius:9px;background:var(--vr-action);color:var(--vr-action-text);font:650 13px/1 Inter,ui-sans-serif,system-ui,sans-serif;cursor:pointer}button:focus-visible{outline:2px solid var(--vr-action);outline-offset:2px}button:disabled{opacity:.55;cursor:wait}
9
- @media (prefers-color-scheme:dark){:host{--vr-panel:#161616;--vr-text:#fff;--vr-muted:#a7a7a7;--vr-label:#d6d6d6;--vr-control:#222;--vr-border:#3a3a3a;--vr-action:#fff;--vr-action-text:#171717}}
10
- @media (pointer:coarse){input,button{height:44px}}
11
- @media (max-width:480px){form{padding:18px}input{font-size:16px}}
12
- @media (prefers-reduced-motion:reduce){*,*::before,*::after{scroll-behavior:auto!important;transition:none!important;animation:none!important}}
13
- `;function m(e){let t=[`01works-reviewer-device-proof-v1`,e.purpose,e.deviceSessionId,e.challengeId,e.challenge,String(e.counter),String(e.expiresAt),e.invitationId,e.origin,e.pathname];return new TextEncoder().encode(t.map(e=>`${e.length}:${e}`).join(``))}const h=`reviewer-device-credentials-v1`;function g(){try{return globalThis.indexedDB?new y(globalThis.indexedDB):null}catch{return null}}async function _(e=globalThis.crypto){if(!e?.subtle||typeof e.randomUUID!=`function`)throw Error(`Visual Review device authentication requires Web Crypto`);let t=await e.subtle.generateKey({name:`ECDSA`,namedCurve:`P-256`},!1,[`sign`,`verify`]);if(!(t instanceof Object)||!(`privateKey`in t)||!(`publicKey`in t)||t.privateKey.extractable)throw Error(`Visual Review could not create a non-extractable device key`);let n=await e.subtle.exportKey(`jwk`,t.publicKey);if(n.kty!==`EC`||n.crv!==`P-256`||typeof n.x!=`string`||typeof n.y!=`string`)throw Error(`Visual Review received an invalid device public key`);return{deviceSessionId:e.randomUUID(),privateKey:t.privateKey,publicKey:{kty:`EC`,crv:`P-256`,x:n.x,y:n.y}}}async function v(e,t,n=globalThis.crypto){if(e.extractable||e.type!==`private`||e.algorithm.name!==`ECDSA`||!e.usages.includes(`sign`))throw Error(`Visual Review device key is invalid`);let r=await n.subtle.sign({name:`ECDSA`,hash:`SHA-256`},e,m(t));return C(new Uint8Array(r))}var y=class{factory;constructor(e){this.factory=e}async get(e){let t=await this.open();try{return b(await x(t.transaction(h,`readonly`).objectStore(h).get(e)))}finally{t.close()}}async set(e,t){let n=await this.open();try{await S(n.transaction(h,`readwrite`),n=>{n.put(t,e)})}finally{n.close()}}async delete(e){let t=await this.open();try{await S(t.transaction(h,`readwrite`),t=>{t.delete(e)})}finally{t.close()}}open(){return new Promise((e,t)=>{let n=this.factory.open(`01works-visual-review`,1);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains(h)||n.result.createObjectStore(h)},n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error??Error(`IndexedDB open failed`)),n.onblocked=()=>t(Error(`IndexedDB upgrade blocked`))})}};function b(e){if(!e||typeof e!=`object`||Array.isArray(e))return null;let t=e;return typeof t.deviceSessionId!=`string`||!Number.isSafeInteger(t.expiresAt)||(t.expiresAt??0)<=Date.now()||t.invitationId!==void 0&&typeof t.invitationId!=`string`||!t.privateKey||t.privateKey.type!==`private`||t.privateKey.extractable||t.privateKey.algorithm.name!==`ECDSA`||!t.privateKey.usages.includes(`sign`)?null:t}function x(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error??Error(`IndexedDB request failed`))})}function S(e,t){return new Promise((n,r)=>{e.oncomplete=()=>n(),e.onerror=()=>r(e.error??Error(`IndexedDB transaction failed`)),e.onabort=()=>r(e.error??Error(`IndexedDB transaction aborted`)),t(e.objectStore(h))})}function C(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t).replaceAll(`+`,`-`).replaceAll(`/`,`_`).replace(/=+$/u,``)}var w=class extends Error{code;status;constructor(e,t,n){super(t),this.code=e,this.status=n,this.name=`HostedBootstrapError`}};function T(e={}){return E(e)}function E(t={},n={}){let r=e(t.serviceUrl??`https://review.01.works`),i=globalThis.fetch,a=t.fetch??(typeof i==`function`?i.bind(globalThis):void 0),o=n.now??Date.now,s=new Map,c=n.reviewerDeviceStore===void 0?g():n.reviewerDeviceStore,l=n.crypto??globalThis.crypto,u=n.reviewerSessionStorage===void 0?K(fe(),q()):K(n.reviewerSessionStorage);if(typeof a!=`function`)throw Error(`Visual Review bootstrap requires the Fetch API`);return{async resolve(e){let t=z(e),i=await B(r,a,t,e.signal),d=o(),f=await ee({config:i,convexRuntimeFactory:n.convexRuntimeFactory??(await import(`./convex-runtime-CpgGS5hl.js`)).createConvexReviewRuntime,fetcher:a,memorySessions:s,navigateToAccountBridge:n.navigateToAccountBridge,reviewerDeviceStore:c,cryptoProvider:l,request:e,legacySessionStorages:u,serviceUrl:r}),p=f.resolvePage;if(!p)return f;let m=async t=>{await B(r,a,{invitationId:e.invitationId,origin:t.location.origin,pathname:t.location.pathname},t.signal),d=o()};return{...f,resolvePage:async e=>(o()-d>=3e5&&await m(e),p(e))}}}}async function ee(e){let t=e.config.accessScope?.kind===`project`?e.config.accessScope:null,n=G(e.serviceUrl,e.request.location.origin,t?.locator??e.config.sessionLocator??e.request.invitationId),i=G(e.serviceUrl,e.request.location.origin,e.request.invitationId);X(e.legacySessionStorages,n),i!==n&&X(e.legacySessionStorages,i);let a;if(t){if(a=await J(e.reviewerDeviceStore,e.memorySessions,n),a&&!a.invitationId&&(await Y(e.reviewerDeviceStore,e.memorySessions,n),a=null),!a){let n=await _(e.cryptoProvider);a=e.request.accountToken?await te(e,t.locator,e.request.accountToken,n).catch(r=>{if(!(r instanceof w))throw r;if(r.code===`INVITATION_NOT_FOUND`&&t.mode===`email`)return D(e,t.locator,n);if(r.code===`INVITATION_TOKEN_USED`||r.code===`INVITATION_TOKEN_INVALID`||r.code===`ACCOUNT_ASSERTION_EXPIRED`||r.code===`AUTH_REQUIRED`||r.code===`INVALID_INPUT`)return k(e,t.locator);throw r}):e.request.accountBridgeAttempted?t.mode===`link`?await O(e,t.locator,n):await D(e,t.locator,n):await k(e,t.locator)}}else if(e.request.emailToken){await r(e.request.authHost,e.request.signal,e.request.styleNonce);let t=await _(e.cryptoProvider);try{a=await F(e.serviceUrl,e.fetcher,{invitationId:e.request.invitationId,emailToken:e.request.emailToken,origin:e.request.location.origin,pathname:e.request.location.pathname,signal:e.request.signal,device:t})}catch(n){if(!(n instanceof w)||n.code!==`INVITATION_TOKEN_USED`)throw n;a=await N(e,t)}}else a=await J(e.reviewerDeviceStore,e.memorySessions,n),!a&&i!==n&&(a=await J(e.reviewerDeviceStore,e.memorySessions,i),a&&await Y(e.reviewerDeviceStore,e.memorySessions,i));a||=await N(e,await _(e.cryptoProvider));let o=t?a.invitationId:e.request.invitationId;if(!o)throw new w(`INVALID_PROJECT_ACCESS_RESPONSE`,`프로젝트 리뷰 인증 응답이 올바르지 않습니다.`,502);a={...a,invitationId:o},await pe(e.reviewerDeviceStore,e.memorySessions,n,a);let{expiresAt:s}=a,c={...e.request,invitationId:o,emailToken:void 0,accountToken:void 0,accountBridgeAttempted:void 0};return{...await e.convexRuntimeFactory(e.config.convexUrl,{fetchAccessToken:t=>ie(e.serviceUrl,e.fetcher,a,t,e.cryptoProvider,()=>Y(e.reviewerDeviceStore,e.memorySessions,n))}).resolve(c),...e.config.branding===void 0?{}:{branding:e.config.branding},sessionScope:`browser`,sessionExpiresAt:s??e.config.invitationExpiresAt,clearLocalSession:()=>{Y(e.reviewerDeviceStore,e.memorySessions,n);let t=new AbortController,r=setTimeout(()=>t.abort(),5e3);ae(e.serviceUrl,e.fetcher,a,{invitationId:o,origin:e.request.location.origin,pathname:e.request.location.pathname,forceRefreshToken:!1,signal:t.signal},e.cryptoProvider).catch(()=>void 0).finally(()=>clearTimeout(r))}}}async function D(e,t,n){let r={reviewLocator:t,pathname:e.request.location.pathname,signal:e.request.signal};return i(e.request.authHost,e.request.signal,{requestCode:t=>se(e.serviceUrl,e.fetcher,{...r,email:t}),verifyCode:(t,i,a,o)=>ce(e.serviceUrl,e.fetcher,{...r,challengeId:t,attemptId:i,email:a,code:o,device:n})},e.request.styleNonce)}async function O(e,t,r){let i=await n(e.request.authHost,e.request.signal,``,e.request.styleNonce),a=await e.fetcher(`${e.serviceUrl}/v1/reviewer/link/start`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({reviewLocator:t,displayName:i,pathname:e.request.location.pathname,deviceSessionId:r.deviceSessionId,devicePublicKey:r.publicKey}),cache:`no-store`,credentials:`omit`,signal:e.request.signal}),o=await Z(a),s=Q(o,`invitationId`),c=Q(o,`deviceSessionId`),l=W($(o,`expiresAt`));if(!a.ok||c!==r.deviceSessionId||!s||l==null)throw new w(Q(o,`error`)??`LINK_ACCESS_FAILED`,Q(o,`message`)??`링크 리뷰를 시작하지 못했습니다.`,a.status);return{deviceSessionId:c,privateKey:r.privateKey,invitationId:s,expiresAt:l}}async function k(e,t){let n=ne(e.cryptoProvider),r=await j(e.cryptoProvider,n),i=q(),a=A(e.serviceUrl,e.request.location.origin,t);try{if(i?.setItem(a,n),i?.getItem(a)!==n)throw Error(`storage unavailable`)}catch{throw new w(`ACCOUNT_BRIDGE_FAILED`,`이 브라우저에서는 안전한 계정 로그인을 시작할 수 없습니다.`,0)}let o=await e.fetcher(`${e.serviceUrl}/v1/reviewer/account/start`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({reviewLocator:t,codeChallenge:r,pathname:e.request.location.pathname,returnPath:`${e.request.location.pathname}${e.request.location.search??``}`,returnHash:e.request.location.hash??``}),cache:`no-store`,credentials:`omit`,signal:e.request.signal}),s=await Z(o),c=Q(s,`continueUrl`);throw!o.ok||!c||!re(c,e.serviceUrl)?(i.removeItem(a),new w(Q(s,`error`)??`ACCOUNT_BRIDGE_FAILED`,Q(s,`message`)??`로그인 상태를 확인하지 못했습니다.`,o.status)):((e.navigateToAccountBridge??(e=>{globalThis.location.assign(e)}))(c),new w(`ACCOUNT_BRIDGE_REDIRECT`,`로그인 상태를 확인하는 중입니다.`,0))}async function te(e,t,n,r){let i=q(),a=A(e.serviceUrl,e.request.location.origin,t),o=i?.getItem(a)??``;if(!/^[A-Za-z0-9_-]{43}$/u.test(o))throw new w(`INVALID_INPUT`,`계정 인증을 시작한 브라우저 탭에서 다시 시도해 주세요.`,400);let s=await e.fetcher(`${e.serviceUrl}/v1/reviewer/account/redeem`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({assertionToken:n,codeVerifier:o,pathname:e.request.location.pathname,deviceSessionId:r.deviceSessionId,devicePublicKey:r.publicKey}),cache:`no-store`,credentials:`omit`,signal:e.request.signal}),c=await Z(s),l=Q(c,`invitationId`),u=Q(c,`deviceSessionId`),d=W($(c,`expiresAt`));if(!s.ok||u!==r.deviceSessionId||!l||d==null)throw new w(Q(c,`error`)??`ACCOUNT_BRIDGE_FAILED`,Q(c,`message`)??`로그인 계정으로 리뷰를 시작하지 못했습니다.`,s.status);return i?.removeItem(a),{deviceSessionId:u,privateKey:r.privateKey,invitationId:l,expiresAt:d}}function A(e,t,n){return`01works.visual-review.account-bridge.v1:${JSON.stringify([e,t,n])}`}function ne(e){let t=new Uint8Array(32);return e.getRandomValues(t),M(t)}async function j(e,t){let n=await e.subtle.digest(`SHA-256`,new TextEncoder().encode(t));return M(new Uint8Array(n))}function M(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t).replaceAll(`+`,`-`).replaceAll(`/`,`_`).replace(/=+$/u,``)}function re(e,t){try{let n=new URL(e),r=new URL(`${t}/v1/reviewer/account/continue`);return n.origin===r.origin&&n.pathname===r.pathname&&n.searchParams.has(`state`)&&n.hash===``}catch{return!1}}async function N(e,t){let n={invitationId:e.request.invitationId,pathname:e.request.location.pathname,signal:e.request.signal};return a(e.request.authHost,e.request.signal,{requestCode:()=>oe(e.serviceUrl,e.fetcher,n),verifyCode:(r,i)=>le(e.serviceUrl,e.fetcher,{...n,challengeId:r,code:i,device:t})},e.request.styleNonce)}async function ie(e,t,n,r,i,a){let o;try{o=await P(e,t,n.deviceSessionId,r)}catch(e){throw e instanceof w&&[401,403,409,410].includes(e.status)&&await a(),e}let s=await v(n.privateKey,{purpose:`token`,deviceSessionId:n.deviceSessionId,...o,invitationId:r.invitationId,origin:r.origin,pathname:r.pathname},i),c=await t(`${e}/v1/convex/token`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({invitationId:r.invitationId,origin:r.origin,pathname:r.pathname,deviceSessionId:n.deviceSessionId,proof:{deviceSessionId:n.deviceSessionId,...o,signature:s}}),cache:`no-store`,credentials:`omit`,signal:r.signal}),l=await Z(c),u=Q(l,`token`);if(!c.ok||!u||u.length>16e3)throw[401,403,409,410].includes(c.status)&&await a(),new w(Q(l,`error`)??`CONVEX_AUTH_FAILED`,Q(l,`message`)??`리뷰 인증을 갱신하지 못했습니다.`,c.status);return u}async function P(e,t,n,r,i=`token`){let a=await t(`${e}/v1/convex/token/challenge`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({deviceSessionId:n,invitationId:r.invitationId,origin:r.origin,pathname:r.pathname,purpose:i}),cache:`no-store`,credentials:`omit`,signal:r.signal}),o=await Z(a),s=Q(o,`challengeId`),c=Q(o,`challenge`),l=$(o,`counter`),u=W($(o,`expiresAt`));if(!a.ok||!s||!c||!Number.isSafeInteger(l)||l<1||u==null)throw new w(Q(o,`error`)??`DEVICE_CHALLENGE_FAILED`,Q(o,`message`)??`리뷰 기기 인증을 시작하지 못했습니다.`,a.status);return{challengeId:s,challenge:c,counter:l,expiresAt:u}}async function ae(e,t,n,r,i){let a=await P(e,t,n.deviceSessionId,r,`revoke`),o=await v(n.privateKey,{purpose:`revoke`,deviceSessionId:n.deviceSessionId,...a,invitationId:r.invitationId,origin:r.origin,pathname:r.pathname},i);if(!(await t(`${e}/v1/convex/session/revoke`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({deviceSessionId:n.deviceSessionId,invitationId:r.invitationId,origin:r.origin,pathname:r.pathname,proof:{deviceSessionId:n.deviceSessionId,...a,signature:o}}),cache:`no-store`,credentials:`omit`,keepalive:!0,signal:r.signal})).ok)throw Error(`Reviewer device revocation failed`)}async function F(e,t,n){let r=await t(`${e}/v1/invitations/redeem-email`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({invitationId:n.invitationId,emailToken:n.emailToken,pathname:n.pathname,deviceSessionId:n.device.deviceSessionId,devicePublicKey:n.device.publicKey}),cache:`no-store`,credentials:`omit`,signal:n.signal}),i=await Z(r);if(!r.ok)throw new w(Q(i,`error`)??`INVITATION_REDEEM_FAILED`,Q(i,`message`)??`리뷰 초대를 시작하지 못했습니다.`,r.status);let a=Q(i,`deviceSessionId`);if(!a||a!==n.device.deviceSessionId)throw new w(`INVALID_INVITATION_REDEEM_RESPONSE`,`잘못된 초대 인증 응답입니다.`,502);let o=W($(i,`expiresAt`));if(o===null)throw new w(`INVALID_INVITATION_REDEEM_RESPONSE`,`잘못된 초대 인증 응답입니다.`,502);if(o===void 0)throw new w(`INVALID_INVITATION_REDEEM_RESPONSE`,`device session 만료 시각이 필요합니다.`,502);return{deviceSessionId:a,privateKey:n.device.privateKey,expiresAt:o}}async function oe(e,t,n){let r=await I(e,t,`start`,n),i=Q(r,`challengeId`),a=Q(r,`emailHint`);if(!i||!a)throw new w(`INVALID_RECOVERY_RESPONSE`,`잘못된 복구 응답입니다.`,502);return{challengeId:i,emailHint:a}}async function se(e,t,n){let r=await L(e,t,`start`,n),i=Q(r,`challengeId`),a=Q(r,`attemptId`),o=Q(r,`emailHint`);if(!i||!a||!o)throw new w(`INVALID_PROJECT_ACCESS_RESPONSE`,`프로젝트 리뷰 인증 응답이 올바르지 않습니다.`,502);return{challengeId:i,attemptId:a,emailHint:o}}async function ce(e,t,n){let r=await L(e,t,`verify`,n),i=Q(r,`deviceSessionId`),a=Q(r,`invitationId`),o=W($(r,`expiresAt`));if(i!==n.device.deviceSessionId||!a||o==null)throw new w(`INVALID_PROJECT_ACCESS_RESPONSE`,`프로젝트 리뷰 인증 응답이 올바르지 않습니다.`,502);return{deviceSessionId:i,privateKey:n.device.privateKey,invitationId:a,expiresAt:o}}async function le(e,t,n){let r=await I(e,t,`verify`,n),i=Q(r,`deviceSessionId`);if(!i||i!==n.device.deviceSessionId)throw new w(`INVALID_RECOVERY_RESPONSE`,`잘못된 복구 응답입니다.`,502);let a=W($(r,`expiresAt`));if(a===null)throw new w(`INVALID_RECOVERY_RESPONSE`,`잘못된 복구 응답입니다.`,502);if(a===void 0)throw new w(`INVALID_RECOVERY_RESPONSE`,`device session 만료 시각이 필요합니다.`,502);return{deviceSessionId:i,privateKey:n.device.privateKey,expiresAt:a}}async function I(e,t,n,r){let{signal:i,...a}=r,o=a;if(`device`in a&&R(a.device)){let{device:e,...t}=a;o={...t,deviceSessionId:e.deviceSessionId,devicePublicKey:e.publicKey}}let s=await t(`${e}/v1/invitations/recovery/${n}`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify(o),cache:`no-store`,credentials:`omit`,signal:i}),c=await Z(s);if(!s.ok)throw new w(Q(c,`error`)??`RECOVERY_FAILED`,Q(c,`message`)??`리뷰 접속을 복구하지 못했습니다.`,s.status);if(!c||typeof c!=`object`||Array.isArray(c))throw new w(`INVALID_RECOVERY_RESPONSE`,`잘못된 복구 응답입니다.`,502);return c}async function L(e,t,n,r){let{signal:i,...a}=r,o=a;if(`device`in a&&R(a.device)){let{device:e,...t}=a;o={...t,deviceSessionId:e.deviceSessionId,devicePublicKey:e.publicKey}}let s=await t(`${e}/v1/reviewer/access/${n}`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify(o),cache:`no-store`,credentials:`omit`,signal:i}),c=await Z(s);if(!s.ok)throw new w(Q(c,`error`)??`PROJECT_ACCESS_FAILED`,Q(c,`message`)??`프로젝트 리뷰에 로그인하지 못했습니다.`,s.status);if(!c||typeof c!=`object`||Array.isArray(c))throw new w(`INVALID_PROJECT_ACCESS_RESPONSE`,`프로젝트 리뷰 인증 응답이 올바르지 않습니다.`,502);return c}function R(e){return!!(e&&typeof e==`object`&&`deviceSessionId`in e&&`privateKey`in e&&`publicKey`in e)}function z(e){return{invitationId:e.invitationId,origin:e.location.origin,pathname:e.location.pathname}}async function B(e,t,n,r){let i=await t(`${e}/v1/bootstrap`,{method:`POST`,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify(n),cache:`no-store`,credentials:`omit`,signal:r}),a=await Z(i);if(!i.ok)throw new w(Q(a,`error`)??`BOOTSTRAP_FAILED`,Q(a,`message`)??`Visual Review bootstrap에 실패했습니다.`,i.status);return V(a)}function V(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let t=e;if(typeof t.apiVersion==`number`&&t.apiVersion!==2)throw new w(`UNSUPPORTED_SERVICE_VERSION`,`이 Visual Review 위젯 버전은 더 이상 지원되지 않습니다. 사이트의 @01.works/visual-review를 업데이트해 주세요.`,502);if(t.apiVersion!==2)throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let n=me(t.convexUrl),r=H(t.branding),i=W(t.invitationExpiresAt),a=ue(t.accessScope),o=de(t.sessionLocator);if(t.provider!==`convex`||!n||i===null)throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);return{apiVersion:2,provider:`convex`,convexUrl:n,...r===void 0?{}:{branding:r},...i===void 0?{}:{invitationExpiresAt:i},...a===void 0?{}:{accessScope:a},...o===void 0?{}:{sessionLocator:o}}}function H(e){if(e===void 0)return;if(!e||typeof e!=`object`||Array.isArray(e))throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let t=e,n=t.projectName===void 0?void 0:Q(t,`projectName`),r=t.iconUrl===void 0?void 0:U(t.iconUrl),i=t.iconAlt===void 0?void 0:Q(t,`iconAlt`);if(n!==void 0&&(n===null||!n.trim()||n.length>200)||t.iconUrl!==void 0&&!r||i===null||i!==void 0&&i.length>200||n===void 0&&r===void 0)throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);return{...typeof n==`string`?{projectName:n.trim()}:{},...typeof r==`string`?{iconUrl:r}:{},...i===void 0?{}:{iconAlt:i}}}function U(e){if(typeof e!=`string`||e.length>2048)return null;try{let t=new URL(e);return t.protocol===`https:`&&!t.username&&!t.password&&t.href===e?t.href:null}catch{return null}}function ue(e){if(e===void 0)return;if(!e||typeof e!=`object`||Array.isArray(e))throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let t=e,n=Q(t,`locator`);if(t.kind!==`project`||!n||n.length>200||t.mode!==void 0&&t.mode!==`email`&&t.mode!==`link`)throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);return{kind:`project`,locator:n,...t.mode===void 0?{}:{mode:t.mode}}}function de(e){if(e!==void 0){if(typeof e!=`string`||!e.trim()||e.length>200)throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);return e.trim()}}function W(e){if(e!==void 0)return Number.isSafeInteger(e)&&e>0?e:null}function G(e,t,n){return`01works.visual-review.convex-session.v1:${JSON.stringify([e,t,n])}`}function K(...e){return e.filter(e=>e!==null)}function fe(){try{return globalThis.localStorage??null}catch{return null}}function q(){try{return globalThis.sessionStorage??null}catch{return null}}async function J(e,t,n){try{let t=await e?.get(n);if(t)return t}catch{}let r=t.get(n)??null;return r&&r.expiresAt<=Date.now()?(t.delete(n),null):r}async function pe(e,t,n,r){t.set(n,r);try{await e?.set(n,r)}catch{}}async function Y(e,t,n){t.delete(n);try{await e?.delete(n)}catch{}}function X(e,t){for(let n of e)try{n.removeItem(t)}catch{}}function me(e){if(typeof e!=`string`||!e.trim())return null;try{let t=new URL(e.trim());return t.origin!==e.trim()||t.protocol!==`https:`||!t.hostname.endsWith(`.convex.cloud`)?null:t.origin}catch{return null}}async function Z(e){try{return await e.json()}catch{throw new w(`INVALID_BOOTSTRAP_RESPONSE`,`JSON bootstrap 응답이 필요합니다.`,502)}}function Q(e,t){let n=$(e,t);return typeof n==`string`&&n.trim()?n.trim():null}function $(e,t){if(!(!e||typeof e!=`object`||Array.isArray(e)))return e[t]}export{T as createHostedReviewRuntime};