@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.
|
|
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-
|
|
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
|
|
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-
|
|
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,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};
|