@01.works/visual-review 0.9.2 → 0.9.4
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/README.md +34 -5
- package/dist/{bootstrap-error-X9PqxFnS.js → bootstrap-error-BwIf8fUE.js} +1 -0
- package/dist/hosted-runtime-BKZERnAW.js +13 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +1 -1
- package/dist/install-DSA2Fpbw.js +3 -0
- package/dist/react.js +1 -1
- package/package.json +3 -1
- package/dist/hosted-runtime-D1xY3Jtw.js +0 -13
- package/dist/install-RHVUfLTU.js +0 -1
package/README.md
CHANGED
|
@@ -139,7 +139,32 @@ build adapter는 Vite mode와 알려진 hosting provider context로 provenance m
|
|
|
139
139
|
|
|
140
140
|
별도 enable 환경변수는 필요하지 않습니다. Launcher는 invitation fragment 또는 검증된
|
|
141
141
|
resume marker가 없으면 DOM, history listener, auth, network와 runtime import 전에
|
|
142
|
-
종료합니다.
|
|
142
|
+
종료합니다. development build에서는 설치 성공과 비활성 상태를 구분할 수 있도록 현재
|
|
143
|
+
Origin과 초대 링크로 다시 열라는 안내를 console에 한 번만 남깁니다. production의 일반
|
|
144
|
+
방문자에게는 이 안내나 UI를 노출하지 않습니다. 실제 활성화는 project의 exact origin
|
|
145
|
+
연결과 invitation 검증이 결정합니다.
|
|
146
|
+
|
|
147
|
+
초대가 있지만 bootstrap이 `ORIGIN_NOT_ALLOWED`를 반환하면 오류 surface에 현재 Origin과
|
|
148
|
+
프로젝트 설정에 그대로 추가할 값을 표시합니다. Origin은 protocol, hostname, port까지
|
|
149
|
+
정확히 일치해야 합니다. 이 검사는 invitation 기반 활성화나 서버의 exact-origin 검증을
|
|
150
|
+
우회하지 않습니다.
|
|
151
|
+
|
|
152
|
+
호스트가 자체 개발 안내나 telemetry를 연결하려면 `onInactive`를 사용할 수 있습니다.
|
|
153
|
+
초대가 없는 경우에는 어느 프로젝트에도 연결하지 않으므로 `missing-activation`만 알 수
|
|
154
|
+
있고, `origin-not-allowed`는 초대 bootstrap 응답으로 확인된 뒤에만 전달됩니다.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<VisualReview
|
|
158
|
+
onInactive={({ reason, origin }) => {
|
|
159
|
+
console.debug('Visual Review inactive', { reason, origin });
|
|
160
|
+
}}
|
|
161
|
+
/>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
현재 `reason`은 `missing-activation` 또는 `origin-not-allowed`입니다. 콜백은 opt-in
|
|
165
|
+
진단 신호일 뿐 DOM이나 network 요청을 추가하지 않으며, credential을 전달하지 않습니다.
|
|
166
|
+
콜백에서 발생한 예외는 기본 비활성 안내와 bootstrap 오류 UI를 중단시키지 않습니다.
|
|
167
|
+
React adapter는 StrictMode effect replay에서 동일한 상태를 중복 전달하지 않습니다.
|
|
143
168
|
|
|
144
169
|
```bash
|
|
145
170
|
npm run build -- --mode staging
|
|
@@ -186,13 +211,17 @@ safe-area inset은 모든 anchor에 적용됩니다. 너비 640px 이하에서
|
|
|
186
211
|
|
|
187
212
|
Invitation fragment, 검증된 resume marker 또는 명시적 local runtime이 없으면 한 번의
|
|
188
213
|
namespaced storage 확인 뒤 DOM, history listener, auth, dynamic runtime import와 network
|
|
189
|
-
bootstrap을 시작하지 않습니다.
|
|
214
|
+
bootstrap을 시작하지 않습니다. development build의 1회 console 안내와 명시적으로
|
|
215
|
+
등록한 `onInactive` callback만 이 dormant 경계에서 실행될 수 있습니다.
|
|
216
|
+
`enabled={false}`는 긴급 kill switch이며 진단도 실행하지 않습니다.
|
|
190
217
|
|
|
191
|
-
처음에는
|
|
218
|
+
처음에는 이메일의
|
|
219
|
+
`#visual-review=<invitation-id>&visual-review-token=<one-time-token>` 링크로 접근합니다. hosted service가
|
|
192
220
|
project에 연결된 exact Origin 목록과 현재 pathname을 읽기 전용 activation gate로
|
|
193
221
|
확인합니다. bootstrap 응답은 public Instant App ID만 반환하며 reviewer session이나
|
|
194
|
-
이메일을 포함하지 않습니다.
|
|
195
|
-
|
|
222
|
+
이메일을 포함하지 않습니다. Fragment는 주소창에서 즉시 제거되고, reviewer가
|
|
223
|
+
**리뷰 시작**을 누를 때 server가 1회용 token을 지정된 origin/path에서 교환해 Instant
|
|
224
|
+
session과 invitation claim을 함께 만듭니다. 인증된 `/v1/pages/resolve`가 아직 등록되지 않은
|
|
196
225
|
pathname을 자동 등록합니다. Server는
|
|
197
226
|
`db.asUser({ token })`으로 user-scoped claim을 실행하며 완료된 뒤에만 client가
|
|
198
227
|
정확한 page를 선택해 Instant subscription과 overlay를 mount합니다. `revokedAt`,
|
|
@@ -75,6 +75,7 @@ function e(e,r,i,a){let o=e.shadowRoot??e.attachShadow({mode:`open`}),s=o.ownerD
|
|
|
75
75
|
.bootstrap-error-copy > span {
|
|
76
76
|
display: block;
|
|
77
77
|
color: var(--vr-muted);
|
|
78
|
+
white-space: pre-line;
|
|
78
79
|
font: 500 11px/16px Geist, ui-sans-serif, system-ui, sans-serif;
|
|
79
80
|
}
|
|
80
81
|
.bootstrap-error button {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import{r as e}from"./install-DSA2Fpbw.js";import{t}from"./embedded-source-DSDHbbeU.js";import{i as n,init as r}from"@instantdb/core";import{makeFunctionReference as i}from"convex/server";import{ConvexClient as a}from"convex/browser";const o=131072,s=32768,c=4096,l=1e3;var u=class extends Error{constructor(e,t){super(e,t),this.name=`InstantCaptureIntegrityError`}};function d(e){let t=re(e,`comment target`,!0),n={kind:t.kind,anchor:fe(t.anchor),region:he(t.region),elements:t.elements.map(e=>({selector:e.selector,tagName:e.tagName,textPreview:e.textPreview,rect:he(e.rect),source:null,componentStack:[]})),pageUrl:t.pageUrl,pageTitle:t.pageTitle,capturedAt:t.capturedAt,...t.environment?{environment:oe(t.environment)}:{}},r={elements:t.elements.map(e=>({selector:e.selector,source:e.source?ge(e.source):null,componentStack:e.componentStack.map(ge)})),...t.sourceProvenance?{sourceProvenance:_e(t.sourceProvenance)}:{}};T(n,o,`Public target`);let i=Ce(r,65536,`Source context`);return{publicTarget:n,commentFields:m(n),targetElements:n.elements.map(h),sourceContextJson:i}}function f(e,t,n){let r=y(e,n),i={selector:b(r.anchorSelector,`${n} anchor selector`,c),offsetX:C(r.anchorOffsetX,`${n} anchor offsetX`),offsetY:C(r.anchorOffsetY,`${n} anchor offsetY`),viewportX:C(r.anchorViewportX,`${n} anchor viewportX`),viewportY:C(r.anchorViewportY,`${n} anchor viewportY`)},a=te({kind:r.targetKind,anchor:i,region:{pageX:r.targetPageX,pageY:r.targetPageY,width:r.targetWidth,height:r.targetHeight,viewportX:r.targetViewportX,viewportY:r.targetViewportY,viewportWidth:r.targetViewportWidth,viewportHeight:r.targetViewportHeight},elements:[],pageUrl:me({origin:x(r.targetPageOrigin,`${n} page origin`,1024),pathname:x(r.targetPagePathname,`${n} page pathname`,4096),search:b(r.targetPageSearch,`${n} page search`,1024),hash:b(r.targetPageHash,`${n} page hash`,1024)}),pageTitle:r.targetPageTitle,capturedAt:r.targetCapturedAt,...ae(r,n)},n),o=ee(t,`${n} elements`),s=a;for(let e of o)try{s=te({...a,elements:[...s.elements,e]},n)}catch{}return s}function p(e){if(!Array.isArray(e)||e.length===0)return;let t=[...e].map(e=>{try{let t=y(e,`Comment scope entry`);return{row:t,ordinal:be(t.ordinal,`Comment scope entry ordinal`,0,19),id:typeof t.id==`string`?t.id:``}}catch{return null}}).filter(e=>e!==null).sort((e,t)=>e.ordinal-t.ordinal||e.id.localeCompare(t.id)),n={},r=new Set;for(let{row:e,ordinal:i}of t)if(!r.has(i)){r.add(i);try{let t=x(e.key,`Comment scope key`,256);if(Object.hasOwn(n,t))continue;switch(e.valueType){case`string`:n[t]=b(e.stringValue,`Comment scope ${t}`,l);break;case`number`:n[t]=S(e.numberValue,`Comment scope ${t}`);break;case`boolean`:if(typeof e.booleanValue!=`boolean`)throw E(`Comment scope ${t} is invalid`);n[t]=e.booleanValue;break;case`null`:n[t]=null;break;default:throw E(`Comment scope ${t} is invalid`)}}catch{}}if(Object.keys(n).length!==0)try{return T(n,s,`Comment scope`),n}catch{return}}function m(e){let t=new URL(e.pageUrl);return{anchorSelector:e.anchor.selector,anchorOffsetX:e.anchor.offsetX,anchorOffsetY:e.anchor.offsetY,anchorViewportX:e.anchor.viewportX,anchorViewportY:e.anchor.viewportY,targetKind:e.kind,targetPageX:e.region.pageX,targetPageY:e.region.pageY,targetWidth:e.region.width,targetHeight:e.region.height,targetViewportX:e.region.viewportX,targetViewportY:e.region.viewportY,targetViewportWidth:e.region.viewportWidth,targetViewportHeight:e.region.viewportHeight,targetPageOrigin:t.origin,targetPagePathname:pe(t.pathname),targetPageSearch:t.search.slice(1),targetPageHash:t.hash.slice(1),targetPageTitle:e.pageTitle,targetCapturedAt:e.capturedAt,...e.environment?{environmentBrowser:e.environment.browser,environmentOperatingSystem:e.environment.operatingSystem,environmentDeviceType:e.environment.deviceType,environmentScreenWidth:e.environment.screenWidth,environmentScreenHeight:e.environment.screenHeight,environmentDevicePixelRatio:e.environment.devicePixelRatio}:{}}}function h(e,t){return{ordinal:t,selector:e.selector,tagName:e.tagName,textPreview:e.textPreview,pageX:e.rect.pageX,pageY:e.rect.pageY,width:e.rect.width,height:e.rect.height,viewportX:e.rect.viewportX,viewportY:e.rect.viewportY,viewportWidth:e.rect.viewportWidth,viewportHeight:e.rect.viewportHeight}}function ee(e,t){if(!Array.isArray(e)||e.length===0)return[];let n=[...e].map(e=>{try{let n=y(e,t);return{row:n,ordinal:be(n.ordinal,`${t} ordinal`,0,7),id:typeof n.id==`string`?n.id:``}}catch{return null}}).filter(e=>e!==null).sort((e,t)=>e.ordinal-t.ordinal||e.id.localeCompare(t.id)),r=[],i=new Set;for(let{row:e,ordinal:a}of n)if(!i.has(a)){i.add(a);try{r.push(se({selector:e.selector,tagName:e.tagName,textPreview:e.textPreview,rect:{pageX:e.pageX,pageY:e.pageY,width:e.width,height:e.height,viewportX:e.viewportX,viewportY:e.viewportY,viewportWidth:e.viewportWidth,viewportHeight:e.viewportHeight},source:null,componentStack:[]},`${t}[${a}]`,!1))}catch{}}return r}function te(e,t){return re(e,t,!1)}function g(e,t=`Comment body`){return x(e,t,2e4)}function ne(e){if(e==null)return;let t=y(e,`Comment scope`);if(Object.keys(t).length>20)throw E(`Comment scope has too many fields`);let n={};for(let[e,r]of Object.entries(t)){if(!e.trim()||e.length>256)throw E(`Comment scope has an invalid key`);if(typeof r==`string`){if(r.length>l)throw E(`Comment scope ${e} is too long`);n[e]=r}else if(typeof r==`number`)n[e]=S(r,`Comment scope ${e}`);else if(typeof r==`boolean`||r===null)n[e]=r;else throw E(`Comment scope ${e} is invalid`)}return T(n,s,`Comment scope`),n}function re(e,t,n){let r=v(e,t,[`kind`,`anchor`,`region`,`elements`,`pageUrl`,`pageTitle`,`capturedAt`,`environment`,`sourceProvenance`],[`environment`,`sourceProvenance`]),i=r.kind;if(i!==`element`&&i!==`region`&&i!==`viewport`)throw E(`${t} kind is invalid`);let a=ve(r.elements,`${t} elements`);if(a.length>8)throw E(`${t} contains too many elements`);if(!n&&r.sourceProvenance!=null)throw E(`${t} exposes source provenance`);let s=x(r.pageUrl,`${t} pageUrl`,8192);Se(s,`${t} pageUrl`);let c={kind:i,anchor:ce(r.anchor,`${t} anchor`),region:le(r.region,`${t} region`),elements:a.map((e,r)=>se(e,`${t} elements[${r}]`,n)),pageUrl:s,pageTitle:b(r.pageTitle,`${t} pageTitle`,l),capturedAt:w(r.capturedAt,`${t} capturedAt`),...r.environment==null?{}:{environment:ie(r.environment,`${t} environment`)},...n&&r.sourceProvenance!=null?{sourceProvenance:de(r.sourceProvenance,`${t} provenance`)}:{}};return T(c,n?196608:o,t),c}function ie(e,t){let n=v(e,t,[`browser`,`operatingSystem`,`deviceType`,`screenWidth`,`screenHeight`,`devicePixelRatio`]),r=n.deviceType;if(r!==`desktop`&&r!==`tablet`&&r!==`mobile`&&r!==`unknown`)throw E(`${t} deviceType is invalid`);return{browser:x(n.browser,`${t} browser`,120),operatingSystem:x(n.operatingSystem,`${t} operatingSystem`,120),deviceType:r,screenWidth:_(n.screenWidth,`${t} screenWidth`,1e5),screenHeight:_(n.screenHeight,`${t} screenHeight`,1e5),devicePixelRatio:_(n.devicePixelRatio,`${t} devicePixelRatio`,100)}}function ae(e,t){if([e.environmentBrowser,e.environmentOperatingSystem,e.environmentDeviceType,e.environmentScreenWidth,e.environmentScreenHeight,e.environmentDevicePixelRatio].every(e=>e==null))return{};try{return{environment:ie({browser:e.environmentBrowser,operatingSystem:e.environmentOperatingSystem,deviceType:e.environmentDeviceType,screenWidth:e.environmentScreenWidth,screenHeight:e.environmentScreenHeight,devicePixelRatio:e.environmentDevicePixelRatio},`${t} environment`)}}catch{return{}}}function oe(e){return{...e}}function _(e,t,n){let r=S(e,t);if(r<=0||r>n)throw E(`${t} is invalid`);return r}function se(e,t,n){let r=v(e,t,[`selector`,`tagName`,`textPreview`,`rect`,`source`,`componentStack`]),i=ve(r.componentStack,`${t} componentStack`);if(i.length>32)throw E(`${t} componentStack is too large`);if(!n&&(r.source!=null||i.length>0))throw E(`${t} exposes source metadata`);return{selector:x(r.selector,`${t} selector`,c),tagName:x(r.tagName,`${t} tagName`,128),textPreview:b(r.textPreview,`${t} textPreview`,l),rect:le(r.rect,`${t} rect`),source:n&&r.source!=null?ue(r.source,`${t} source`):null,componentStack:n?i.map((e,n)=>ue(e,`${t} stack[${n}]`)):[]}}function ce(e,t){let n=v(e,t,[`selector`,`offsetX`,`offsetY`,`viewportX`,`viewportY`]);return{selector:b(n.selector,`${t} selector`,c),offsetX:C(n.offsetX,`${t} offsetX`),offsetY:C(n.offsetY,`${t} offsetY`),viewportX:C(n.viewportX,`${t} viewportX`),viewportY:C(n.viewportY,`${t} viewportY`)}}function le(e,t){let n=v(e,t,[`pageX`,`pageY`,`width`,`height`,`viewportX`,`viewportY`,`viewportWidth`,`viewportHeight`]),r={pageX:S(n.pageX,`${t} pageX`),pageY:S(n.pageY,`${t} pageY`),width:S(n.width,`${t} width`),height:S(n.height,`${t} height`),viewportX:S(n.viewportX,`${t} viewportX`),viewportY:S(n.viewportY,`${t} viewportY`),viewportWidth:S(n.viewportWidth,`${t} viewportWidth`),viewportHeight:S(n.viewportHeight,`${t} viewportHeight`)};if(r.width<0||r.height<0||r.viewportWidth<0||r.viewportHeight<0)throw E(`${t} has a negative dimension`);return r}function ue(e,n){let r=v(e,n,[`filePath`,`lineNumber`,`columnNumber`,`componentName`,`origin`,`generated`],[`generated`]),i=r.origin;if(i!==`app`&&i!==`shared-ui`&&i!==`dependency`&&i!==`generated`&&i!==`unknown`)throw E(`${n} origin is invalid`);let a=x(r.filePath,`${n} filePath`,c);return{filePath:a,lineNumber:xe(r.lineNumber,`${n} lineNumber`),columnNumber:xe(r.columnNumber,`${n} columnNumber`),componentName:r.componentName==null?null:x(r.componentName,`${n} componentName`,512),origin:t(a)}}function de(e,t){let n=v(e,t,[`provider`,`build`,`resolvedAt`]),r=n.provider;if(r!==`react-grab`&&r!==`embedded`&&r!==`custom`)throw E(`${t} provider is invalid`);let i=v(n.build,`${t} build`,[`buildId`,`gitCommit`,`generatedAt`,`mode`],[`generatedAt`]),a=i.mode;if(a!==`development`&&a!==`preview`&&a!==`production`)throw E(`${t} build mode is invalid`);return{provider:r,build:{buildId:x(i.buildId,`${t} buildId`,512),gitCommit:i.gitCommit==null?null:x(i.gitCommit,`${t} gitCommit`,256),...i.generatedAt==null?{}:{generatedAt:w(i.generatedAt,`${t} generatedAt`)},mode:a},resolvedAt:w(n.resolvedAt,`${t} resolvedAt`)}}function fe(e){return{...e}}function pe(e){return e.replace(/\/+$/,``)||`/`}function me(e){return`${e.origin}${e.pathname}${e.search?`?${e.search}`:``}${e.hash?`#${e.hash}`:``}`}function he(e){return{...e}}function ge(e){return{...e}}function _e(e){return{...e,build:{...e.build}}}function v(e,t,n,r=[]){let i=y(e,t);if(Object.keys(i).some(e=>!n.includes(e)))throw E(`${t} contains unknown fields`);for(let e of n)if(!r.includes(e)&&!Object.hasOwn(i,e))throw E(`${t} is missing ${e}`);return i}function y(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw E(`${t} is missing or invalid`);return e}function ve(e,t){if(!Array.isArray(e))throw E(`${t} is missing or invalid`);return e}function b(e,t,n){if(typeof e!=`string`||ye(e,n)>n)throw E(`${t} is invalid or too long`);return e}function ye(e,t){let n=0;for(let r of e)if(n+=1,n>t)return n;return n}function x(e,t,n){let r=b(e,t,n);if(!r.trim())throw E(`${t} is empty`);return r}function S(e,t){if(typeof e!=`number`||!Number.isFinite(e))throw E(`${t} is invalid`);return e}function be(e,t,n,r){if(typeof e!=`number`||!Number.isSafeInteger(e)||e<n||e>r)throw E(`${t} is invalid`);return e}function C(e,t){let n=S(e,t);if(n<0||n>1)throw E(`${t} is outside 0..1`);return n}function w(e,t){if(typeof e!=`number`||!Number.isSafeInteger(e)||e<0||e>0xe677d21fdbff)throw E(`${t} is invalid`);return e}function xe(e,t){if(e===null)return null;if(typeof e!=`number`||!Number.isSafeInteger(e)||e<0)throw E(`${t} is invalid`);return e}function Se(e,t){let n;try{n=new URL(e)}catch(e){throw E(`${t} is invalid`,e)}if(n.protocol!==`http:`&&n.protocol!==`https:`)throw E(`${t} must use HTTP or HTTPS`)}function T(e,t,n){Ce(e,t,n)}function Ce(e,t,n){let r;try{r=JSON.stringify(e)}catch(e){throw E(`${n} cannot be serialized`,e)}if(r.length>t)throw E(`${n} is too large`);return r}function E(e,t){return new u(e,{cause:t instanceof Error?t:void 0})}new Uint8Array([107,167,184,17,157,173,17,209,128,180,0,192,79,212,48,200]);const we=n.schema({entities:{$files:n.entity({path:n.string().unique().indexed(),url:n.string()}),$users:n.entity({email:n.string().unique().indexed()}),projects:n.entity({name:n.string(),slug:n.string().unique().indexed(),allowedOrigin:n.string().optional(),originDisconnectedAt:n.number().optional(),originModelVersion:n.number().optional(),createdAt:n.number().indexed(),updatedAt:n.number()}),projectOrigins:n.entity({projectId:n.string().indexed(),originKey:n.string().unique().indexed(),origin:n.string().indexed(),label:n.string().optional(),createdAt:n.number().indexed(),updatedAt:n.number()}),reviewPages:n.entity({projectId:n.string().indexed(),autoRegistrationSlot:n.string().unique().indexed().optional(),name:n.string(),pathname:n.string(),stagingOrigin:n.string(),createdAt:n.number().indexed(),updatedAt:n.number()}),reviewPageBuilds:n.entity({projectId:n.string().indexed(),pageId:n.string().unique().indexed(),buildId:n.string(),gitCommit:n.string().optional(),generatedAt:n.number().optional(),mode:n.string(),updatedAt:n.number().indexed()}),reviewers:n.entity({authUserId:n.string().indexed(),projectId:n.string().indexed(),email:n.string().indexed(),displayName:n.string(),role:n.string(),createdAt:n.number()}),reviewerIdentityClaims:n.entity({reviewerId:n.string().unique().indexed(),projectId:n.string().indexed(),authUserId:n.string().indexed(),email:n.string(),displayName:n.string(),role:n.string(),createdAt:n.number()}),invitations:n.entity({projectId:n.string().indexed(),reviewerId:n.string().indexed().optional(),email:n.string().indexed(),status:n.string().indexed(),claimRevision:n.number(),claimedByAuthUserId:n.string().indexed().optional(),expiresAt:n.number().indexed(),createdAt:n.number(),acceptedAt:n.number().optional(),revokedAt:n.number().optional(),emailTokenHash:n.string().optional(),emailSentAt:n.number().optional(),reviewUrl:n.string().optional()}),comments:n.entity({pageId:n.string().indexed(),pageNumber:n.number().optional(),authorId:n.string().indexed(),authorAuthUserId:n.string().indexed(),authorName:n.string(),body:n.string(),bodyContentMarker:n.string().optional(),anchorSelector:n.string().optional(),anchorOffsetX:n.number().optional(),anchorOffsetY:n.number().optional(),anchorViewportX:n.number().optional(),anchorViewportY:n.number().optional(),targetKind:n.string().optional(),targetPageX:n.number().optional(),targetPageY:n.number().optional(),targetWidth:n.number().optional(),targetHeight:n.number().optional(),targetViewportX:n.number().optional(),targetViewportY:n.number().optional(),targetViewportWidth:n.number().optional(),targetViewportHeight:n.number().optional(),targetPageOrigin:n.string().optional(),targetPagePathname:n.string().optional(),targetPageSearch:n.string().optional(),targetPageHash:n.string().optional(),targetPageTitle:n.string().optional(),targetCapturedAt:n.number().optional(),environmentBrowser:n.string().optional(),environmentOperatingSystem:n.string().optional(),environmentDeviceType:n.string().optional(),environmentScreenWidth:n.number().optional(),environmentScreenHeight:n.number().optional(),environmentDevicePixelRatio:n.number().optional(),status:n.string().indexed(),workflowRevision:n.number(),createdAt:n.number().indexed(),updatedAt:n.number().indexed(),resolvedAt:n.number().optional(),resolvedById:n.string().optional(),deletingAt:n.number().optional()}),commentWorkflowClaims:n.entity({claimKey:n.string().unique().indexed(),commentId:n.string().indexed(),expectedWorkflowRevision:n.number(),createdAt:n.number().indexed()}),commentPageNumberClaims:n.entity({claimKey:n.string().unique().indexed(),pageId:n.string().indexed(),pageNumber:n.number(),commentId:n.string().indexed(),createdAt:n.number().indexed()}),commentTargetElements:n.entity({commentId:n.string().indexed(),slotKey:n.string().unique().indexed().optional(),ordinal:n.number(),selector:n.string(),tagName:n.string(),textPreview:n.string(),pageX:n.number(),pageY:n.number(),width:n.number(),height:n.number(),viewportX:n.number(),viewportY:n.number(),viewportWidth:n.number(),viewportHeight:n.number(),authorAuthUserId:n.string().indexed()}),commentScopeEntries:n.entity({commentId:n.string().indexed(),slotKey:n.string().unique().indexed().optional(),ordinal:n.number(),key:n.string(),valueType:n.string(),stringValue:n.string().optional(),numberValue:n.number().optional(),booleanValue:n.boolean().optional(),authorAuthUserId:n.string().indexed()}),commentSourceContexts:n.entity({commentId:n.string().unique().indexed(),pageId:n.string().indexed(),authorAuthUserId:n.string().indexed(),contextJson:n.string(),createdAt:n.number().indexed()}),replies:n.entity({pageId:n.string().indexed(),commentId:n.string().indexed(),authorId:n.string().indexed(),authorAuthUserId:n.string().indexed(),authorName:n.string(),body:n.string(),bodyContentMarker:n.string().optional(),createdAt:n.number().indexed(),updatedAt:n.number().indexed()}),reviewMessages:n.entity({pageId:n.string().indexed(),pathname:n.string().indexed(),authorId:n.string().indexed(),authorAuthUserId:n.string().indexed(),authorName:n.string(),body:n.string(),bodyContentMarker:n.string(),createdAt:n.number().indexed(),updatedAt:n.number().indexed()})},links:{projectOwners:{forward:{on:`projects`,has:`many`,label:`owners`},reverse:{on:`$users`,has:`many`,label:`ownedProjects`}},projectPages:{forward:{on:`projects`,has:`many`,label:`pages`},reverse:{on:`reviewPages`,has:`one`,label:`project`}},projectOriginProject:{forward:{on:`projects`,has:`many`,label:`origins`},reverse:{on:`projectOrigins`,has:`one`,label:`project`}},pageCurrentBuild:{forward:{on:`reviewPageBuilds`,has:`one`,label:`page`,required:!0,onDelete:`cascade`},reverse:{on:`reviewPages`,has:`one`,label:`currentBuild`}},projectReviewers:{forward:{on:`projects`,has:`many`,label:`reviewers`},reverse:{on:`reviewers`,has:`one`,label:`project`}},reviewerUser:{forward:{on:`reviewers`,has:`one`,label:`$user`},reverse:{on:`$users`,has:`many`,label:`reviewerMemberships`}},projectInvitations:{forward:{on:`projects`,has:`many`,label:`invitations`},reverse:{on:`invitations`,has:`one`,label:`project`}},invitationReviewer:{forward:{on:`invitations`,has:`one`,label:`reviewer`},reverse:{on:`reviewers`,has:`many`,label:`acceptedInvitations`}},invitationActiveUser:{forward:{on:`invitations`,has:`one`,label:`activeUser`},reverse:{on:`$users`,has:`many`,label:`activeReviewInvitations`}},pageComments:{forward:{on:`reviewPages`,has:`many`,label:`comments`},reverse:{on:`comments`,has:`one`,label:`page`}},pageReviewMessages:{forward:{on:`reviewPages`,has:`many`,label:`reviewMessages`},reverse:{on:`reviewMessages`,has:`one`,label:`page`}},commentReplies:{forward:{on:`comments`,has:`many`,label:`replies`},reverse:{on:`replies`,has:`one`,label:`comment`,required:!0,onDelete:`cascade`}},commentTargetElements:{forward:{on:`commentTargetElements`,has:`one`,label:`comment`,required:!0,onDelete:`cascade`},reverse:{on:`comments`,has:`many`,label:`targetElements`}},commentScopeEntries:{forward:{on:`commentScopeEntries`,has:`one`,label:`comment`,required:!0,onDelete:`cascade`},reverse:{on:`comments`,has:`many`,label:`scopeEntries`}},commentSourceContext:{forward:{on:`commentSourceContexts`,has:`one`,label:`comment`,required:!0,onDelete:`cascade`},reverse:{on:`comments`,has:`one`,label:`sourceContext`}},pageSourceContexts:{forward:{on:`commentSourceContexts`,has:`one`,label:`page`,required:!0},reverse:{on:`reviewPages`,has:`many`,label:`sourceContexts`}},sourceContextAuthor:{forward:{on:`commentSourceContexts`,has:`one`,label:`author`,required:!0},reverse:{on:`reviewers`,has:`many`,label:`sourceContexts`}},commentAuthor:{forward:{on:`comments`,has:`one`,label:`author`},reverse:{on:`reviewers`,has:`many`,label:`comments`}},replyAuthor:{forward:{on:`replies`,has:`one`,label:`author`},reverse:{on:`reviewers`,has:`many`,label:`replies`}},reviewMessageAuthor:{forward:{on:`reviewMessages`,has:`one`,label:`author`},reverse:{on:`reviewers`,has:`many`,label:`reviewMessages`}}},rooms:{visualReview:{presence:n.entity({reviewerId:n.string(),displayName:n.string(),color:n.string(),active:n.boolean(),pathname:n.string().optional(),cursorX:n.number().optional(),cursorY:n.number().optional(),viewportWidth:n.number().optional(),viewportHeight:n.number().optional(),cursorChat:n.string().optional()})}}}),D={$default:`false`},O={$default:`false`},k=`$user.activeReviewInvitations`,A=`${k}.project.id`;function j(e){return[`auth.id != null`,`size(auth.ref('${k}.id')) > 0`,`size(auth.ref('${A}')) == size(auth.ref('${k}.id'))`,`size(auth.ref('${k}.status')) == size(auth.ref('${k}.id'))`,`size(auth.ref('${k}.expiresAt')) == size(auth.ref('${k}.id'))`,`size(auth.ref('${k}.revokedAt')) == 0`,`auth.ref('${k}.status').all(status, status == 'accepted')`,`auth.ref('${k}.expiresAt').all(expiresAt, request.time < timestamp(expiresAt))`,e].join(` && `)}function M(...e){return e.map(e=>`(${e})`).join(` && `)}function N(...e){return e.map(e=>`(${e})`).join(` || `)}function P(e){let t=`data.ref('${e}project.origins.origin')`,n=`data.ref('${e}project.allowedOrigin')`,r=`data.ref('${e}project.originModelVersion')`;return N(`request.origin in ${t}`,M(`size(${r}) == 0`,`request.origin in ${n}`))}j(`data.ref('project.id')[0] in auth.ref('${A}')`),N(`request.origin in data.ref('project.origins.origin')`,M(`size(data.ref('project.originModelVersion')) == 0`,`request.origin in data.ref('project.allowedOrigin')`)),N(`request.origin in data.ref('origins.origin')`,M(`size(data.ref('originModelVersion')) == 0`,`request.origin in data.ref('allowedOrigin')`)),j(`data.ref('page.project.id')[0] in auth.ref('${A}')`),P(`page.`),N(`auth.id != null && auth.id in data.ref('comment.page.project.owners.id')`,M(j(`data.ref('comment.page.project.id')[0] in auth.ref('${A}')`),P(`comment.page.`))),j(`data.ref('comment.page.project.id')[0] in auth.ref('${A}')`),P(`comment.page.`),j(`data.ref('page.project.id')[0] in auth.ref('${A}')`),P(`page.`),P(``);const Te=M(`data.reviewerId != null`,`data.projectId in data.ref('project.id')`,`data.reviewerId in data.ref('reviewer.id')`,`data.projectId in data.ref('reviewer.project.id')`,`auth.id in data.ref('reviewer.$user.id')`,`auth.email in data.ref('reviewer.email')`);M(`auth.id != null`,`data.revokedAt == null`,`data.status in ['pending', 'accepted']`,`data.status == 'pending' || data.claimedByAuthUserId == auth.id`,`request.time < timestamp(data.expiresAt)`,`request.modifiedFields.all(field, field in ['status', 'claimedByAuthUserId', 'acceptedAt', 'claimRevision', 'activeUser'])`,`'status' in request.modifiedFields`,`'claimedByAuthUserId' in request.modifiedFields`,`'acceptedAt' in request.modifiedFields`,`'claimRevision' in request.modifiedFields`,`'activeUser' in request.modifiedFields`,`newData.status == 'accepted'`,`newData.claimedByAuthUserId == auth.id`,`newData.claimRevision == data.claimRevision + 1`,`math.abs((request.time - timestamp(newData.acceptedAt)).getMinutes()) <= 1`,Te),M(`actions.data == 'update'`,`actions.linkedData == 'update'`,`auth.id != null`,`auth.email != null`,`linkedData.id == auth.id`,`linkedData.email == auth.email`,`data.email == auth.email`,`data.revokedAt == null`,`data.status in ['pending', 'accepted']`,`data.status == 'pending' || data.claimedByAuthUserId == auth.id`,`request.time < timestamp(data.expiresAt)`,`newData.status == 'accepted'`,`newData.claimedByAuthUserId == auth.id`,`newData.claimRevision == data.claimRevision + 1`,`math.abs((request.time - timestamp(newData.acceptedAt)).getMinutes()) <= 1`,Te),M(`actions.data == 'update'`,`actions.linkedData == 'update'`,`auth.id != null`,`auth.email != null`,`data.id == auth.id`,`data.email == auth.email`,`linkedData.email == auth.email`,`linkedData.revokedAt == null`,`linkedData.status == 'accepted'`,`linkedData.claimedByAuthUserId == auth.id`,`request.time < timestamp(linkedData.expiresAt)`,`math.abs((request.time - timestamp(linkedData.acceptedAt)).getMinutes()) <= 1`,`linkedData.projectId in linkedData.ref('project.id')`,`linkedData.reviewerId in linkedData.ref('reviewer.id')`,`linkedData.projectId in linkedData.ref('reviewer.project.id')`,`auth.id in linkedData.ref('reviewer.$user.id')`,`auth.email in linkedData.ref('reviewer.email')`),M(`auth.id != null`,`auth.email != null`,`linkedData.id == auth.id`,`linkedData.email == auth.email`,`data.email == auth.email`,`data.claimedByAuthUserId == auth.id`,`auth.id in data.ref('activeUser.id')`),M(`auth.id != null`,`auth.email != null`,`data.id == auth.id`,`data.email == auth.email`,`linkedData.email == auth.email`,`linkedData.claimedByAuthUserId == auth.id`,`auth.id in linkedData.ref('activeUser.id')`),{...D},{...D},{...O},{...O},j(`data.id in auth.ref('${A}')`),{...D},{...O},{...D},{...D},{...O},{...D},{...O},j(`data.ref('project.id')[0] in auth.ref('${A}')`),{...D},{...O},{...D},{...D},{...O},{...O},{...D},{...O},{...D},{...D},{...D},{...D},{...D},{...D},{...D};function Ee(e){if(!e.trim())throw Error(`InstantDB app ID is required`);return r({appId:e,schema:we,devtool:!1})}var De=class{db;session;#e;attachScreenshot;constructor(e,t,n={}){this.db=e,this.session=t,this.#e=n.commentTransport,this.#e&&(this.attachScreenshot=(e,t,n)=>this.#e.uploadScreenshot({pageId:e,commentId:t,screenshot:n}))}async getSnapshot(e){let{data:t}=await this.db.queryOnce(Oe(e,this.session.authUserId));return Ae(e,t.comments,t.replies)}async getProjectSnapshot(e){this.#t(e);let{data:t}=await this.db.queryOnce(ke(e,this.session.authUserId));return je(e,t.comments,t.replies)}subscribe(e,t,n){return this.db.subscribeQuery(Oe(e,this.session.authUserId),r=>{if(r.error){n?.(Re(r.error));return}if(r.data)try{t(Ae(e,r.data.comments,r.data.replies))}catch{n?.({type:`error`,code:`data-integrity`,recovery:`retry`,clearSnapshot:!0})}})}subscribeProject(e,t,n){return this.#t(e),this.db.subscribeQuery(ke(e,this.session.authUserId),r=>{if(r.error){n?.(Re(r.error));return}if(r.data)try{t(je(e,r.data.comments,r.data.replies))}catch{n?.({type:`error`,code:`data-integrity`,recovery:`retry`,clearSnapshot:!0})}})}async mutate(e,t){switch(t.type){case`comment.upsert`:{if(t.comment.pageId!==e)throw Error(`Comment page mismatch`);let n=t.comment;if(n.authorId!==this.session.reviewerId)throw Error(`Comment author mismatch`);let r=g(n.body).trim(),i=z(n.createdAt,`Comment createdAt`);if(z(n.updatedAt,`Comment updatedAt`)<i)throw new u(`Comment updatedAt precedes createdAt`);if(n.createdAt!==n.updatedAt)throw new u(`Comment edits must go through the review service`);if(n.status!==`open`||n.resolvedAt!==void 0||n.resolvedById!==void 0)throw new u(`New comments must start open`);let a=d(n.target);if(!Ie(n.anchor,a.publicTarget.anchor))throw new u(`Comment anchor does not match its target`);if(!this.#e)throw new u(`Comment creation requires the review service transport`);await this.#e.createComment({pageId:e,commentId:n.id,body:r,target:n.target,...n.scope===void 0?{}:{scope:n.scope}});return}case`comment.delete`:throw new u(`Comment deletion must go through the review service`);case`reply.upsert`:{let e=t.reply;if(e.authorId!==this.session.reviewerId)throw Error(`Reply author mismatch`);let n=g(e.body,`Reply body`).trim(),r=z(e.createdAt,`Reply createdAt`);if(z(e.updatedAt,`Reply updatedAt`)<r)throw new u(`Reply updatedAt precedes createdAt`);if(e.createdAt!==e.updatedAt)throw new u(`Reply edits must go through the review service`);if(!this.#e)throw new u(`Reply creation requires the review service transport`);await this.#e.createReply({commentId:e.commentId,replyId:e.id,body:n});return}case`reply.delete`:throw new u(`Reply deletion must go through the review service`)}}#t(e){if(!e.trim()||e!==this.session.projectId)throw Error(`Project does not belong to the reviewer session`)}};function Oe(e,t){return{comments:{$:{where:{pageId:e},order:{createdAt:`asc`}},targetElements:{},scopeEntries:{}},replies:{$:{where:{pageId:e},order:{createdAt:`asc`}}},$users:{$:{where:{id:t}},activeReviewInvitations:{project:{}}}}}function ke(e,t){return{comments:{$:{where:{"page.project.id":e},order:{createdAt:`asc`}},page:{project:{}},targetElements:{},scopeEntries:{}},replies:{$:{where:{"comment.page.project.id":e},order:{createdAt:`asc`}},comment:{page:{project:{}}}},$users:{$:{where:{id:t}},activeReviewInvitations:{project:{}}}}}function Ae(e,t,n){let r=t.map(t=>Ne(t,e));F(r,`comment`);let i=new Set(r.map(({id:e})=>e)),a=n.map(t=>Pe(t,e));F(a,`reply`);for(let e of a)if(!i.has(e.commentId))throw new u(`Reply ${e.id} references a comment outside the page snapshot`);return{pageId:e,comments:r,replies:a}}function je(e,t,n){let r=new Map,i=t.map(t=>{let n=L(t.pageId,`comment pageId`);Me(t.page,n,e,`comment ${String(t.id)}`);let i=I(t.page,`comment ${String(t.id)} page`),a=Ne(t,n),o=`/`;try{o=new URL(a.target.pageUrl).pathname||`/`}catch{}return r.set(n,{id:n,name:typeof i.name==`string`&&i.name.trim()?i.name.trim():a.target.pageTitle.trim()||o,pathname:typeof i.pathname==`string`&&i.pathname.trim()?i.pathname.trim():o}),a});F(i,`comment`);let a=new Map(i.map(e=>[e.id,e])),o=n.map(t=>{let n=L(t.pageId,`reply pageId`),r=I(t.comment,`reply ${String(t.id)} comment`),i=L(t.commentId,`reply ${String(t.id)} commentId`);if(L(r.id,`reply ${String(t.id)} comment relation id`)!==i)throw new u(`Reply ${String(t.id)} has an inconsistent comment relation`);Me(r.page,n,e,`reply ${String(t.id)}`);let o=Pe(t,n),s=a.get(o.commentId);if(!s||s.pageId!==n)throw new u(`Reply ${o.id} references a comment outside the project snapshot`);return o});return F(o,`reply`),{projectId:e,pages:[...r.values()].sort((e,t)=>e.pathname.localeCompare(t.pathname)||e.id.localeCompare(t.id)),comments:i,replies:o}}function Me(e,t,n,r){let i=I(e,`${r} page`);if(L(i.id,`${r} page id`)!==t)throw new u(`${r} escaped its page relation`);if(L(I(i.project,`${r} project`).id,`${r} project id`)!==n)throw new u(`${r} escaped its project scope`)}function Ne(e,t){let n=I(e,`comment`),r=L(n.id,`comment id`);if(L(n.pageId,`comment ${r} pageId`)!==t)throw new u(`Comment ${r} escaped its page scope`);L(n.authorAuthUserId,`comment ${r} authorAuthUserId`);let i=!(Object.hasOwn(n,`anchor`)&&Object.hasOwn(n,`target`)),a=i?f(n,n.targetElements,`comment ${r} target`):te(n.target,`comment ${r} target`),o=i?{...a.anchor}:Fe(n.anchor,`comment ${r} anchor`);if(!Ie(o,a.anchor))throw new u(`Comment ${r} has inconsistent anchors`);let s=n.status;if(s!==`open`&&s!==`resolved`)throw new u(`Comment ${r} has an invalid status`);let c=z(n.createdAt,`comment ${r} createdAt`),l=z(n.updatedAt,`comment ${r} updatedAt`);if(l<c)throw new u(`Comment ${r} updatedAt precedes createdAt`);let d=He(n.resolvedAt,`comment ${r} resolvedAt`),m=Ue(n.resolvedById,`comment ${r} resolvedById`);if(s===`open`&&(d!==void 0||m!==void 0))throw new u(`Open comment ${r} contains resolution metadata`);if(s===`resolved`&&(d===void 0||m===void 0||d!==l))throw new u(`Resolved comment ${r} has invalid workflow metadata`);return{id:r,pageId:t,...We(n.pageNumber,`comment ${r} pageNumber`),authorId:L(n.authorId,`comment ${r} authorId`),authorName:L(n.authorName,`comment ${r} authorName`),body:g(n.body,`comment ${r} body`),anchor:o,target:a,...i?Le(p(n.scopeEntries)):n.scope==null?{}:{scope:ne(n.scope)},status:s,workflowRevision:Ve(n.workflowRevision,`comment ${r} workflowRevision`),createdAt:c,updatedAt:l,...d===void 0?{}:{resolvedAt:d},...m===void 0?{}:{resolvedById:m}}}function Pe(e,t){let n=I(e,`reply`),r=L(n.id,`reply id`);if(L(n.pageId,`reply ${r} pageId`)!==t)throw new u(`Reply ${r} escaped its page scope`);L(n.authorAuthUserId,`reply ${r} authorAuthUserId`);let i=z(n.createdAt,`reply ${r} createdAt`),a=z(n.updatedAt,`reply ${r} updatedAt`);if(a<i)throw new u(`Reply ${r} updatedAt precedes createdAt`);return{id:r,commentId:L(n.commentId,`reply ${r} commentId`),authorId:L(n.authorId,`reply ${r} authorId`),authorName:L(n.authorName,`reply ${r} authorName`),body:g(n.body,`reply ${r} body`),createdAt:i,updatedAt:a}}function Fe(e,t){let n=I(e,t);if(Object.keys(n).length!==5||!Object.hasOwn(n,`selector`)||!Object.hasOwn(n,`offsetX`)||!Object.hasOwn(n,`offsetY`)||!Object.hasOwn(n,`viewportX`)||!Object.hasOwn(n,`viewportY`))throw new u(`${t} has invalid fields`);return{selector:Be(n.selector,`${t} selector`),offsetX:R(n.offsetX,`${t} offsetX`),offsetY:R(n.offsetY,`${t} offsetY`),viewportX:R(n.viewportX,`${t} viewportX`),viewportY:R(n.viewportY,`${t} viewportY`)}}function Ie(e,t){return e.selector===t.selector&&e.offsetX===t.offsetX&&e.offsetY===t.offsetY&&e.viewportX===t.viewportX&&e.viewportY===t.viewportY}function Le(e){return e===void 0?{}:{scope:e}}function F(e,t){let n=new Set;for(let r of e){if(n.has(r.id))throw new u(`Duplicate ${t} ${r.id}`);n.add(r.id)}}function Re(e){let t=ze(e).toLowerCase();return t.includes(`expired`)||t.includes(`token`)?{type:`access-lost`,code:`session-expired`,recovery:`reauthenticate`,clearSnapshot:!0}:t.includes(`permission`)||t.includes(`forbidden`)||t.includes(`unauthorized`)?{type:`access-lost`,code:`permission-denied`,recovery:`reauthenticate`,clearSnapshot:!0}:{type:`error`,code:`transport`,recovery:`retry`,clearSnapshot:!0}}function ze(e){return e instanceof Error?e.message:e&&typeof e==`object`&&`message`in e?String(e.message):``}function I(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw new u(`${t} is missing or invalid`);return e}function Be(e,t){if(typeof e!=`string`)throw new u(`${t} is invalid`);return e}function L(e,t){let n=Be(e,t);if(!n.trim())throw new u(`${t} is empty`);return n}function R(e,t){if(typeof e!=`number`||!Number.isFinite(e)||e<0||e>1)throw new u(`${t} is invalid`);return e}function z(e,t){if(!Number.isSafeInteger(e)||e<0||e>0xe677d21fdbff)throw new u(`${t} is invalid`);return e}function Ve(e,t){if(!Number.isSafeInteger(e)||e<0)throw new u(`${t} is invalid`);return e}function He(e,t){return e==null?void 0:z(e,t)}function Ue(e,t){return e==null?void 0:L(e,t)}function We(e,t){if(e==null)return{};let n=Ve(e,t);if(n<1)throw new u(`${t} is invalid`);return{pageNumber:n}}Object.freeze({field:`createdAt`,direction:`desc`,tieBreaker:`id`,tieDirection:`desc`}),Object.freeze({comments:200,replies:1e3,sourceContexts:200});var Ge=class{db;session;roomCredential;#e=new Map;constructor(e,t,n){this.db=e,this.session=t,this.roomCredential=n}subscribe(e,t,n,r){let i=[],a=[],o=!1,s=[],c=()=>{o||n({pageId:e,peers:structuredClone(i),messages:structuredClone(a)})};if(t.cursors){let n=rt(e,t.roomId),a,l,u=this.roomCredential?.id===t.roomId?this.roomCredential:void 0,d=u??{id:t.roomId,expiresAt:2**53-1,refresh:async()=>void 0},f=t=>{let o=this.db.joinRoom(`visualReview`,t),s=o.subscribePresence({},e=>{if(e.error){r?.(at(e.error));return}i=Object.values(e.peers).map(e=>qe(e)).filter(e=>e!==null).sort((e,t)=>e.displayName.localeCompare(t.displayName)||e.peerId.localeCompare(t.peerId)),c()}),l=a;a=()=>{s(),o.leaveRoom()},this.#e.set(n,{pageId:e,roomId:t,publishPresence:e=>o.publishPresence(e)}),l?.()},p=()=>{if(!u||o)return;let e=Math.max(1e3,d.expiresAt-Date.now()+250);l=setTimeout(()=>{m()},e)},m=async()=>{try{let e=await d.refresh();if(o||!e)return;d={...e,refresh:d.refresh},u&&(u.id=e.id,u.expiresAt=e.expiresAt),f(e.id),p()}catch(e){if(o)return;r?.(at(e)),o||(l=setTimeout(()=>{m()},5e3))}};f(d.id),p(),s.push(()=>{l&&clearTimeout(l),this.#e.delete(n),a?.()})}return t.chat&&s.push(this.db.subscribeQuery(Ke(e,this.session.authUserId),t=>{if(t.error){r?.(at(t.error));return}if(t.data)try{a=t.data.reviewMessages.map(t=>Xe(t,e)).sort(it),c()}catch{r?.({type:`error`,code:`data-integrity`,recovery:`retry`,clearSnapshot:!0})}})),queueMicrotask(c),()=>{if(!o){o=!0;for(let e of s.reverse())try{e()}catch{}}}}publishPresence(e,t,n){let r=this.#e.get(rt(e,t));r&&r.publishPresence({reviewerId:this.session.reviewerId,displayName:this.session.displayName,color:n.color,active:n.active,pathname:Ye(n.pathname),...n.cursorX===void 0?{}:{cursorX:n.cursorX},...n.cursorY===void 0?{}:{cursorY:n.cursorY},...n.viewportWidth===void 0?{}:{viewportWidth:n.viewportWidth},...n.viewportHeight===void 0?{}:{viewportHeight:n.viewportHeight},cursorChat:Je(n.cursorChat)})}async sendMessage(e,t){throw Error(`Persisted review messages are no longer supported`)}};function Ke(e,t){return{reviewMessages:{$:{where:{pageId:e},order:{createdAt:`desc`},limit:200}},$users:{$:{where:{id:t}},activeReviewInvitations:{project:{}}}}}function qe(e){let t=$e(e.reviewerId,128),n=$e(e.displayName,512),r=typeof e.color==`string`&&/^#[0-9a-f]{6}$/iu.test(e.color)?e.color:null;if(!t||!n||!r||typeof e.active!=`boolean`)return null;let i=et(e.cursorX),a=et(e.cursorY),o=tt(e.viewportWidth),s=tt(e.viewportHeight),c=Je(e.cursorChat),l=Ye(e.pathname);return{peerId:e.peerId,reviewerId:t,displayName:n,color:r,active:e.active,pathname:l,...i===void 0?{}:{cursorX:i},...a===void 0?{}:{cursorY:a},...o===void 0?{}:{viewportWidth:o},...s===void 0?{}:{viewportHeight:s},...c?{cursorChat:c}:{}}}function Je(e){return typeof e==`string`?e.trim().slice(0,280):``}function Ye(e){if(typeof e!=`string`)return`/`;let t=e.trim();return t.startsWith(`/`)?t.slice(0,2e3):`/`}function Xe(e,t){let n=B(e.id,`Review message id`,128);if(B(e.pageId,`Review message pageId`,128)!==t)throw Error(`Review message page mismatch`);return{id:n,pageId:t,pathname:Qe(e.pathname),authorId:B(e.authorId,`Review message authorId`,128),authorName:B(e.authorName,`Review message authorName`,512),body:Ze(e.body),createdAt:nt(e.createdAt,`Review message createdAt`),updatedAt:nt(e.updatedAt,`Review message updatedAt`)}}function Ze(e){return B(e,`Review message body`,4e3)}function Qe(e){let t=B(e,`Review message pathname`,2e3);if(!t.startsWith(`/`))throw Error(`Review message pathname is invalid`);return t}function B(e,t,n){if(typeof e!=`string`)throw Error(`${t} is invalid`);let r=e.trim();if(!r||r.length>n)throw Error(`${t} is invalid`);return r}function $e(e,t){if(typeof e!=`string`)return null;let n=e.trim();return n&&n.length<=t?n:null}function et(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function tt(e){let t=et(e);return t!==void 0&&t>0?t:void 0}function nt(e,t){if(!Number.isSafeInteger(e)||e<0)throw Error(`${t} is invalid`);return e}function rt(e,t){return JSON.stringify([e,t])}function it(e,t){return e.createdAt-t.createdAt||e.id.localeCompare(t.id)}function at(e){let t=e&&typeof e==`object`&&`code`in e?String(e.code):``;if(/^INVITATION_(?:EXPIRED|NOT_FOUND|REQUIRED|REVOKED)$/u.test(t))return{type:`access-lost`,code:t===`INVITATION_EXPIRED`?`session-expired`:`membership-revoked`,recovery:`reauthenticate`,clearSnapshot:!0};let n=String(e instanceof Error||e&&typeof e==`object`&&`message`in e?e.message:e).toLowerCase();return n.includes(`expired`)||n.includes(`token`)?{type:`access-lost`,code:`session-expired`,recovery:`reauthenticate`,clearSnapshot:!0}:n.includes(`permission`)||n.includes(`forbidden`)||n.includes(`unauthorized`)?{type:`access-lost`,code:`permission-denied`,recovery:`reauthenticate`,clearSnapshot:!0}:{type:`error`,code:`transport`,recovery:`retry`,clearSnapshot:!0}}Object.freeze({field:`createdAt`,direction:`desc`,tieBreaker:`id`,tieDirection:`desc`});var V=class extends Error{code;constructor(e,t){super(t),this.code=e,this.name=`ReviewerCommentSaveError`}};function ot(e,t){let n=e.replace(/\/+$/,``),r=async(e,r)=>{let i=await t();if(!i)throw new V(`AUTH_REQUIRED`,`로그인 세션이 만료되었습니다. 새로고침 후 다시 시도해 주세요.`);let a;try{a=await fetch(`${n}${e}`,{method:`POST`,headers:{authorization:`Bearer ${i}`,"content-type":`application/json`},body:JSON.stringify(r)})}catch{throw new V(`NETWORK`,`리뷰 서버에 연결하지 못했습니다. 연결을 확인하고 다시 시도해 주세요.`)}if(!a.ok)throw new V(...await ct(a))};return{createComment:({target:e,...t})=>{let{environment:n,...i}=e;return r(`/v1/comments`,{...t,target:i,...n?{environment:n}:{}})},createReply:e=>r(`/v1/replies`,e),uploadScreenshot:async({pageId:e,commentId:r,screenshot:i})=>{let a=await t();if(!a)throw new V(`AUTH_REQUIRED`,`로그인 세션이 만료되어 위치 캡처를 저장하지 못했습니다.`);let o;try{o=await fetch(`${n}/v1/comments/${encodeURIComponent(r)}/screenshot`,{method:`PUT`,headers:{authorization:`Bearer ${a}`,"content-type":i.contentType,"x-review-page-id":e,"x-review-image-width":String(i.width),"x-review-image-height":String(i.height)},body:st(i.bytes)})}catch{throw new V(`NETWORK`,`리뷰 서버에 연결하지 못해 위치 캡처를 저장하지 못했습니다.`)}if(!o.ok)throw new V(...await ct(o))}}}function st(e){let t=new Uint8Array(e.byteLength);return t.set(e),t.buffer}async function ct(e){let t=e.status>=500?`리뷰 서버가 코멘트를 저장하지 못했습니다. 잠시 후 다시 시도해 주세요.`:`코멘트를 저장하지 못했습니다.`;try{let n=await e.json();if(!n||typeof n!=`object`)return[`UNKNOWN`,t];let r=n;return[typeof r.error==`string`?r.error:`UNKNOWN`,typeof r.message==`string`&&r.message.trim()?r.message:t]}catch{return[`UNKNOWN`,t]}}var H=class extends Error{code;constructor(e,t){super(t),this.code=e,this.name=`ReviewBootstrapError`}};function lt(e,t={}){let n=e.trim();if(!n)throw Error(`InstantDB app ID is required`);return{async resolve(e){let{db:r,user:i}=await ut(n,e,t);t.resolveReviewPage&&await t.resolveReviewPage({refreshToken:i.refresh_token,invitationId:e.invitationId,origin:e.location.origin,pathname:e.location.pathname,signal:e.signal});let{data:a}=await r.queryOnce({reviewers:{$:{where:{authUserId:i.id}}},reviewPages:{},invitations:{}}),o=ft(a.invitations,a.reviewers,a.reviewPages,{invitationId:e.invitationId,authUserId:i.id,email:i.email??``,location:e.location,now:Date.now()});if(!o.ok)throw new H(o.code,Dt(o.code));let s={authUserId:i.id,reviewerId:o.reviewer.id,projectId:o.reviewer.projectId,invitationId:e.invitationId,email:o.reviewer.email||i.email||``,displayName:o.reviewer.displayName||i.email||`Reviewer`,role:o.reviewer.role===`agency`?`agency`:`reviewer`},c=o.page.id,l=e.collaboration?.scopeId,u=(e.collaboration?.cursors===!0||e.collaboration?.chat===!0)&&t.resolveCollaborationRoom?await t.resolveCollaborationRoom({refreshToken:i.refresh_token,invitationId:e.invitationId,pageId:o.page.id,scopeId:e.collaboration.scopeId,signal:e.signal}):void 0,d=u&&t.resolveCollaborationRoom?{...u,refresh:async()=>{let n=await r.getAuth();if(!(!n?.refresh_token||l===void 0))return t.resolveCollaborationRoom?.({refreshToken:n.refresh_token,invitationId:e.invitationId,pageId:c,scopeId:l,signal:new AbortController().signal})}}:void 0,f=0;return{pageId:o.page.id,resolvePage:async n=>{let i=++f,a=()=>i!==f||n.signal.aborted,o=(e,t,n)=>({pageId:e,...n&&d?{collaborationRoomId:n.id}:{},commit:()=>{a()||(c=e,t!==void 0&&(l=t),!(!n||!d)&&(d.id=n.id,d.expiresAt=n.expiresAt))}});if(n.location.origin!==e.location.origin)throw new H(`PAGE_NOT_FOUND`,Dt(`PAGE_NOT_FOUND`));let{data:u}=await r.queryOnce({reviewPages:{$:{where:{projectId:s.projectId}}}});if(a())throw G();let p=Tt(n.location.pathname),m=pt(u.reviewPages,s.projectId,n.location.origin,p);if(!m&&t.resolveReviewPage){let i=await r.getAuth();if(a())throw G();if(!i?.refresh_token)throw Error(`리뷰 세션이 만료되어 페이지를 등록할 수 없습니다.`);if(await t.resolveReviewPage({refreshToken:i.refresh_token,invitationId:e.invitationId,origin:n.location.origin,pathname:p,signal:n.signal}),a()||({data:u}=await r.queryOnce({reviewPages:{$:{where:{projectId:s.projectId}}}}),a()))throw G();m=pt(u.reviewPages,s.projectId,n.location.origin,p)}if(!m)throw new H(`PAGE_NOT_FOUND`,Dt(`PAGE_NOT_FOUND`));if(n.collaboration?.cursors!==!0&&n.collaboration?.chat!==!0||d===void 0||t.resolveCollaborationRoom===void 0||!n.collaboration)return o(m.id,n.collaboration?.scopeId);let h=await r.getAuth();if(a())throw G();if(!h?.refresh_token)throw Error(`리뷰 세션이 만료되어 실시간 협업 연결을 갱신할 수 없습니다.`);let ee=await t.resolveCollaborationRoom({refreshToken:h.refresh_token,invitationId:e.invitationId,pageId:m.id,scopeId:n.collaboration.scopeId,signal:n.signal});if(a())throw G();return o(m.id,n.collaboration.scopeId,ee)},session:s,repository:new De(r,s,{...t.serviceUrl===void 0?{}:{commentTransport:ot(t.serviceUrl,async()=>(await r.getAuth())?.refresh_token??null)}}),...e.collaboration?.cursors===!0||e.collaboration?.chat===!0?{collaborationRepository:new Ge(r,s,d)}:{},...(e.collaboration?.cursors===!0||e.collaboration?.chat===!0)&&d?{collaborationRoomId:d.id}:{},allowedStagingHosts:[e.location.hostname]}}}}async function ut(e,t,n){let r=(n.databaseFactory??Ee)(e),i=!!t.emailToken,a;if(t.emailToken){if(!n.redeemEmailInvitation)throw new H(`INVITATION_EMAIL_REQUIRED`,`이 리뷰는 이메일 초대 링크에서만 시작할 수 있습니다.`);await _t(t.authHost,t.signal,t.styleNonce);let e=await n.redeemEmailInvitation({invitationId:t.invitationId,emailToken:t.emailToken,origin:t.location.origin,pathname:t.location.pathname,signal:t.signal});await U(t.signal,()=>r.getAuth())&&await U(t.signal,async()=>r.auth.signOut()),await U(t.signal,async()=>r.auth.signInWithToken(e));let i=await U(t.signal,()=>r.getAuth());if(!i)throw Error(`인증 세션을 확인할 수 없습니다.`);a=i}else a=await mt(r,t.signal);if(!i)try{await dt(n.acceptInvitation,{refreshToken:a.refresh_token,invitationId:t.invitationId,suggestedDisplayName:St(a.email),authHost:t.authHost,signal:t.signal,styleNonce:t.styleNonce})}catch(e){throw Ct(e,`INVITATION_EMAIL_MISMATCH`)?new H(`INVITATION_EMAIL_REQUIRED`,`초대받은 이메일의 링크를 다시 열어 주세요.`):e}return{db:r,user:a}}async function dt(e,t){if(!e)return;let n={refreshToken:t.refreshToken,invitationId:t.invitationId,signal:t.signal};try{await e(n);return}catch(e){if(!Ct(e,`DISPLAY_NAME_REQUIRED`))throw e}let r=await gt(t.authHost,t.signal,t.suggestedDisplayName,t.styleNonce);await e({...n,displayName:r})}function ft(e,t,n,r){let i=e.find(({id:e})=>e===r.invitationId);if(!i||Et(i.email)!==Et(r.email))return{ok:!1,code:`INVITATION_REQUIRED`};if(i.status===`revoked`)return{ok:!1,code:`INVITATION_REVOKED`};if(i.status===`expired`||i.expiresAt<=r.now)return{ok:!1,code:`INVITATION_EXPIRED`};if(i.status!==`accepted`)return{ok:!1,code:`INVITATION_NOT_ACCEPTED`};let a=t.find(e=>e.authUserId===r.authUserId&&e.projectId===i.projectId);if(!a)return{ok:!1,code:`MEMBERSHIP_REQUIRED`};let o=Tt(r.location.pathname),s=pt(n,i.projectId,r.location.origin,o);return s?{ok:!0,invitation:i,reviewer:a,page:s}:{ok:!1,code:`PAGE_NOT_FOUND`}}function pt(e,t,n,r){let i=e.filter(e=>e.projectId===t&&Tt(e.pathname)===r),a=i.filter(({stagingOrigin:e})=>e===n);return(a.length>0?a:i).sort((e,t)=>(e.createdAt??2**53-1)-(t.createdAt??2**53-1)||e.id.localeCompare(t.id))[0]}async function mt(e,t){let n=await U(t,()=>e.getAuth());if(n)return n;throw new H(`INVITATION_EMAIL_REQUIRED`,`초대받은 이메일의 링크를 열어 주세요.`)}async function U(e,t){ht(e);let n=await t();return ht(e),n}function ht(e){if(e.aborted)throw K()}function gt(e,t,n=``,r){if(t.aborted)return Promise.reject(K());let i=vt(e);i.replaceChildren(yt(i,r),bt(i));let a=i.querySelector(`form`),o=a.elements.namedItem(`displayName`),s=i.querySelector(`button`),c=i.querySelector(`.error`);return o.value=n.trim().slice(0,120),o.focus(),o.select(),new Promise((e,n)=>{let r=!1,l=e=>{r||(r=!0,t.removeEventListener(`abort`,u),i.removeEventListener(`keydown`,p),a.removeEventListener(`submit`,f),e())},u=()=>l(()=>n(K())),d=()=>{let t=o.value.trim();if(!t){c.textContent=`이름을 입력해 주세요.`,o.focus();return}if(t.length>120){c.textContent=`이름은 120자 이하여야 합니다.`,o.focus();return}c.textContent=``,s.disabled=!0,a.setAttribute(`aria-busy`,`true`),l(()=>e(t))},f=e=>{e.preventDefault(),d()},p=e=>{let t=e;if(t.key===`Escape`){t.preventDefault(),u();return}if(t.key===`Tab`){wt(i,t);return}t.key===`Enter`&&!t.isComposing&&t.target===o&&(t.preventDefault(),d())};t.addEventListener(`abort`,u,{once:!0}),i.addEventListener(`keydown`,p),a.addEventListener(`submit`,f)})}function _t(e,t,n){if(t.aborted)return Promise.reject(K());let r=vt(e);r.replaceChildren(yt(r,n),xt(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(K())),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`&&wt(r,t)};t.addEventListener(`abort`,c,{once:!0}),r.addEventListener(`keydown`,u),i.addEventListener(`submit`,l)})}function vt(e){return e.shadowRoot??e.attachShadow({mode:`open`})}function yt(e,t){let n=e.ownerDocument.createElement(`style`);return t&&(n.nonce=t),n.textContent=Ot,n}function bt(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=W(t,`p`,`01.works`,`eyebrow`),i=W(t,`h2`,`표시 이름`);i.id=`visual-review-name-title`;let a=W(t,`p`,`피드백에 이 이름이 표시됩니다.`,`description`);a.id=`visual-review-name-description`;let o=W(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=W(t,`p`,``,`error`);c.setAttribute(`role`,`alert`);let l=W(t,`button`,`리뷰 시작`);return l.type=`submit`,n.append(r,i,a,o,s,c,l),n}function xt(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=W(t,`p`,`01.works`,`eyebrow`),i=W(t,`h2`,`리뷰 초대`);i.id=`visual-review-invitation-title`;let a=W(t,`p`,`초대받은 프로젝트를 열고 화면에 피드백을 남길 수 있습니다.`,`description`);a.id=`visual-review-invitation-description`;let o=W(t,`button`,`리뷰 시작`);return o.type=`submit`,n.append(r,i,a,o),n}function W(e,t,n,r){let i=e.createElement(t);return i.textContent=n,r&&(i.className=r),i}function St(e){return e?.split(`@`)[0]?.trim().slice(0,120)??``}function Ct(e,t){return!!(e&&typeof e==`object`&&`code`in e&&e.code===t)}function G(){return new DOMException(`Aborted`,`AbortError`)}function K(){return new H(`AUTH_CANCELLED`,`리뷰 로그인이 취소되었습니다.`)}function wt(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())}function Tt(e){return e.replace(/\/+$/,``)||`/`}function Et(e){return e.trim().toLowerCase()}function Dt(e){switch(e){case`INVITATION_REQUIRED`:return`현재 계정과 일치하는 리뷰 초대를 찾을 수 없습니다.`;case`INVITATION_NOT_ACCEPTED`:return`아직 수락되지 않은 리뷰 초대입니다.`;case`INVITATION_REVOKED`:return`취소된 리뷰 초대입니다. 계속 검토하려면 담당자에게 다시 요청하세요.`;case`INVITATION_EXPIRED`:return`만료된 리뷰 초대입니다. 담당자에게 새 링크를 요청하세요.`;case`MEMBERSHIP_REQUIRED`:return`이 프로젝트의 reviewer membership이 없습니다.`;case`PAGE_NOT_FOUND`:return`이 페이지는 아직 리뷰 대상이 아닙니다. 담당자에게 문의하세요.`}}const Ot=`
|
|
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
|
+
`,q=Object.freeze({resolveSession:i(`reviews:resolveSession`),pageSnapshot:i(`reviews:pageSnapshot`),projectSnapshot:i(`reviews:projectSnapshot`),createComment:i(`reviews:createComment`),createReply:i(`reviews:createReply`),presenceList:i(`presence:list`),presenceUpdate:i(`presence:update`),presenceDisconnect:i(`presence:disconnect`),screenshotUploadUrl:i(`screenshots:generateUploadUrl`),screenshotCommit:i(`screenshots:commitUpload`),screenshotDiscard:i(`screenshots:discardUpload`)});Object.freeze({catalog:i(`owners:catalog`),listProjects:i(`owners:listProjects`),projectOverview:i(`owners:projectOverview`),createProject:i(`owners:createProject`),deleteProject:i(`owners:deleteProject`),addOrigin:i(`owners:addOrigin`),removeOrigin:i(`owners:removeOrigin`),createPage:i(`owners:createPage`),deletePage:i(`owners:deletePage`),upsertPageBuild:i(`owners:upsertPageBuild`),createInvitation:i(`owners:createInvitation`),revokeInvitation:i(`owners:revokeInvitation`),setCommentStatus:i(`owners:setCommentStatus`),createReply:i(`owners:createReply`),deleteComment:i(`owners:deleteComment`),deleteReply:i(`owners:deleteReply`),screenshotDownload:i(`screenshots:ownerDownload`)});var kt=class{client;session;#e;#t;#n;constructor(e,t,n){this.client=e,this.session=t,this.#e=Y(n.invitationId??t.invitationId,`invitationId`),this.#t=Nt(n.origin),this.#n=n.fetch??globalThis.fetch.bind(globalThis)}async attachScreenshot(e,t,n){Y(e,`pageId`);let r=Y(t,`commentId`);if(n.bytes.byteLength===0||n.bytes.byteLength>15e5)throw Error(`Screenshot size is invalid`);let i=await this.client.mutation(q.screenshotUploadUrl,{...this.#r(),commentId:r}),a=await this.#n(i,{method:`POST`,headers:{"content-type":n.contentType},body:At(n.bytes)});if(!a.ok)throw Error(`Screenshot upload failed`);let o=await a.json();if(typeof o.storageId!=`string`||!o.storageId)throw Error(`Screenshot upload returned an invalid storage ID`);try{await this.client.mutation(q.screenshotCommit,{...this.#r(),commentId:r,storageId:o.storageId,contentType:n.contentType,width:n.width,height:n.height})}catch(e){throw await this.client.mutation(q.screenshotDiscard,{...this.#r(),commentId:r,storageId:o.storageId}).catch(()=>void 0),e}}async getSnapshot(e){return Mt(await this.client.query(q.pageSnapshot,{...this.#r(),pageId:Y(e,`pageId`)}))}async getProjectSnapshot(e){return this.#i(e),structuredClone(await this.client.query(q.projectSnapshot,{...this.#r(),projectId:e}))}subscribe(e,t,n){return this.client.onUpdate(q.pageSnapshot,{...this.#r(),pageId:Y(e,`pageId`)},e=>t(Mt(e)),e=>n?.(J(e)))}subscribeProject(e,t,n){return this.#i(e),this.client.onUpdate(q.projectSnapshot,{...this.#r(),projectId:e},e=>t(structuredClone(e)),e=>n?.(J(e)))}async mutate(e,t){switch(t.type){case`comment.upsert`:{let n=t.comment;if(n.pageId!==e)throw Error(`Comment page mismatch`);if(n.authorId!==this.session.reviewerId)throw Error(`Comment author mismatch`);if(n.status!==`open`||n.resolvedAt!==void 0||n.resolvedById!==void 0||n.createdAt!==n.updatedAt)throw Error(`Only new open comments can be created by a reviewer`);let r=g(n.body).trim(),i=d(n.target),a=n.scope===void 0?void 0:ne(n.scope);await this.client.mutation(q.createComment,{...this.#r(),pageId:e,commentId:n.id,body:r,targetJson:JSON.stringify(i.publicTarget),sourceContextJson:i.sourceContextJson,...a===void 0?{}:{scopeJson:JSON.stringify(a)}});return}case`reply.upsert`:{let e=t.reply;if(e.authorId!==this.session.reviewerId)throw Error(`Reply author mismatch`);if(e.createdAt!==e.updatedAt)throw Error(`Reply edits require an owner command`);await this.client.mutation(q.createReply,{...this.#r(),commentId:e.commentId,replyId:e.id,body:g(e.body,`Reply body`).trim()});return}case`comment.delete`:throw Error(`Comment deletion requires an owner command`);case`reply.delete`:throw Error(`Reply deletion requires an owner command`)}}#r(){return{invitationId:this.#e,origin:this.#t}}#i(e){if(!e.trim()||e!==this.session.projectId)throw Error(`Project does not belong to the reviewer session`)}};function At(e){let t=new Uint8Array(e.byteLength);return t.set(e),t.buffer}function J(e){let t=jt(e);return t===`UNAUTHENTICATED`||t===`INVITATION_EXPIRED`?{type:`access-lost`,code:`session-expired`,recovery:`reauthenticate`,clearSnapshot:!0}:t===`INVITATION_REVOKED`||t===`MEMBERSHIP_REVOKED`?{type:`access-lost`,code:`membership-revoked`,recovery:`reauthenticate`,clearSnapshot:!0}:t===`ORIGIN_MISMATCH`||t===`ORIGIN_NOT_ALLOWED`||t===`INVITATION_REQUIRED`?{type:`access-lost`,code:`permission-denied`,recovery:`reauthenticate`,clearSnapshot:!0}:{type:`error`,code:e instanceof Error?`transport`:`unknown`,recovery:`retry`,clearSnapshot:!0}}function jt(e){if(!e||typeof e!=`object`)return;let t=e.data;if(typeof t==`string`)return t;if(t&&typeof t==`object`){let e=t.code;if(typeof e==`string`)return e}}function Mt(e){return structuredClone(e)}function Y(e,t){let n=e.trim();if(!n)throw Error(`${t} is required`);return n}function Nt(e){let t=Y(e,`origin`);try{let e=new URL(t);if(e.origin!==t)throw Error();return e.origin}catch{throw Error(`A valid browser origin is required`)}}var Pt=class{client;session;#e=new Map;#t;#n;#r;#i;#a;#o;constructor(e,t,n){this.client=e,this.session=t,this.#t=(n.invitationId??t.invitationId).trim(),this.#n=new URL(n.origin).origin,this.#r=Ft(n.cursorThrottleMs??80,40,1e3),this.#i=Ft(n.heartbeatMs??1e4,2e3,2e4),this.#a=n.now??Date.now,this.#o=n.sessionId??(()=>globalThis.crypto.randomUUID())}subscribe(e,t,n,r){let i=!1,a=[],o=this.#s(e,t.roomId),s=()=>{i||n({pageId:e,peers:structuredClone(a),messages:[]})},c=t.cursors||t.chat?this.client.onUpdate(q.presenceList,{...this.#d(),pageId:e,roomId:t.roomId},e=>{let t=this.#a();a=e.filter(({peerId:e,expiresAt:n})=>e!==o.sessionId&&n>t).map(({expiresAt:e,...t})=>t).sort((e,t)=>e.displayName.localeCompare(t.displayName)||e.peerId.localeCompare(t.peerId)),s()},e=>r?.(J(e))):()=>void 0;return queueMicrotask(s),()=>{i||(i=!0,c(),this.#u(o))}}publishPresence(e,t,n){let r=this.#e.get(X(e,t));!r||r.stopped||(r.latest={...n,reviewerId:this.session.reviewerId,displayName:this.session.displayName},this.#c(r))}async sendMessage(e,t){let n=[...this.#e.values()].find(t=>t.pageId===e);n?.latest&&(n.latest={...n.latest,cursorChat:t.body.slice(0,4e3)},await this.#l(n))}#s(e,t){let n=X(e,t),r=this.#e.get(n);r&&this.#u(r);let i={pageId:e,roomId:t,sessionId:this.#o(),stopped:!1,lastSentAt:-1/0,inFlight:!1};return i.heartbeatTimer=setInterval(()=>{i.latest&&this.#l(i)},this.#i),this.#e.set(n,i),i}#c(e){if(e.inFlight||e.flushTimer||e.stopped)return;let t=Math.max(0,this.#r-(this.#a()-e.lastSentAt));if(t===0){this.#l(e);return}e.flushTimer=setTimeout(()=>{e.flushTimer=void 0,this.#l(e)},t)}async#l(e){if(e.stopped||e.inFlight||!e.latest)return;e.inFlight=!0;let t=e.latest;e.latest=void 0;try{await this.client.mutation(q.presenceUpdate,{...this.#d(),pageId:e.pageId,roomId:e.roomId,sessionId:e.sessionId,color:t.color,active:t.active,...t.pathname===void 0?{}:{pathname:t.pathname},...t.cursorX===void 0?{}:{cursorX:t.cursorX},...t.cursorY===void 0?{}:{cursorY:t.cursorY},...t.viewportWidth===void 0?{}:{viewportWidth:t.viewportWidth},...t.viewportHeight===void 0?{}:{viewportHeight:t.viewportHeight},...t.cursorChat===void 0?{}:{cursorChat:t.cursorChat}}),e.lastSentAt=this.#a()}finally{e.inFlight=!1,e.latest&&this.#c(e)}}#u(e){e.stopped||(e.stopped=!0,e.flushTimer&&clearTimeout(e.flushTimer),e.heartbeatTimer&&clearInterval(e.heartbeatTimer),this.#e.delete(X(e.pageId,e.roomId)),this.client.mutation(q.presenceDisconnect,{...this.#d(),sessionId:e.sessionId}).catch(()=>void 0))}#d(){return{invitationId:this.#t,origin:this.#n}}};function X(e,t){return`${e}\u0000${t}`}function Ft(e,t,n){return Number.isFinite(e)?Math.min(n,Math.max(t,Math.round(e))):t}function It(e,t){let n=e.trim();if(!/^https:\/\/[^/]+$/.test(n))throw Error(`A valid HTTPS Convex deployment URL is required`);let r=new a(n);return r.setAuth(t),r}function Lt(e,t){let n=e.trim();if(!n)throw Error(`Convex deployment URL is required`);return{async resolve(e){let r=await t.fetchAccessToken(zt(e,!1));if(!r)throw Error(`Convex review authentication is required`);let i=r,a=(t.clientFactory??It)(n,async({forceRefreshToken:n})=>{if(!n&&i){let e=i;return i=null,e}return t.fetchAccessToken({...zt(e,n),signal:new AbortController().signal})}),o=await a.mutation(q.resolveSession,{invitationId:e.invitationId,origin:e.location.origin,pathname:e.location.pathname}),s=Vt(o.session,e.invitationId),c=o.pageId,l=e.collaboration?await Bt(t,e,c,e.collaboration.scopeId):void 0,u=0;return Rt({client:a,session:s,request:e,pageId:c,roomId:l,resolvePage:async n=>{if(n.location.origin!==e.location.origin)throw Error(`Convex review session cannot move to another origin`);let r=++u,i=await a.mutation(q.resolveSession,{invitationId:e.invitationId,origin:n.location.origin,pathname:n.location.pathname});if(n.signal.aborted||r!==u)throw Ht();let o=n.collaboration?await Bt(t,e,i.pageId,n.collaboration.scopeId,n.signal):void 0;if(n.signal.aborted||r!==u)throw Ht();return{pageId:i.pageId,...o===void 0?{}:{collaborationRoomId:o},commit:()=>{n.signal.aborted||r!==u||(c=i.pageId,l=o)}}}})}}}function Rt(e){let t=new kt(e.client,e.session,{origin:e.request.location.origin}),n=e.request.collaboration?.cursors===!0||e.request.collaboration?.chat===!0;return{pageId:e.pageId,session:e.session,repository:t,...n?{collaborationRepository:new Pt(e.client,e.session,{origin:e.request.location.origin})}:{},...e.roomId===void 0?{}:{collaborationRoomId:e.roomId},resolvePage:e.resolvePage,allowedStagingHosts:[e.request.location.hostname]}}function zt(e,t){return{invitationId:e.invitationId,origin:e.location.origin,pathname:e.location.pathname,...e.emailToken===void 0?{}:{emailToken:e.emailToken},forceRefreshToken:t,signal:e.signal}}async function Bt(e,t,n,r,i=t.signal){return e.resolveCollaborationRoom?e.resolveCollaborationRoom({invitationId:t.invitationId,pageId:n,scopeId:r,origin:t.location.origin,signal:i}):`${n}:${r}`.slice(0,512)}function Vt(e,t){if(!e||e.invitationId!==t||!e.authUserId||!e.reviewerId||!e.projectId)throw Error(`Convex returned an invalid reviewer session`);return structuredClone(e)}function Ht(){return Object.assign(Error(`Review page resolution was superseded`),{code:`PAGE_SWAP_SUPERSEDED`})}var Z=class extends Error{code;status;constructor(e,t,n){super(t),this.code=e,this.status=n,this.name=`HostedBootstrapError`}};function Ut(e={}){return Wt(e)}function Wt(t={},n={}){let r=e(t.serviceUrl??`https://review.01.works`),i=t.fetch??globalThis.fetch,a=n.instantRuntimeFactory??lt,o=n.convexRuntimeFactory??Lt,s=n.now??Date.now,c=new Map,l=n.reviewerSessionStorage===void 0?en():n.reviewerSessionStorage;if(typeof i!=`function`)throw Error(`Visual Review bootstrap requires the Fetch API`);return{async resolve(e){let t=Jt(e),u=await Zt(r,i,t,e.signal),d=s(),f={serviceUrl:r,...n.instantAuthDatabaseFactory===void 0?{}:{databaseFactory:n.instantAuthDatabaseFactory},redeemEmailInvitation:e=>qt(r,i,e),acceptInvitation:async e=>{let t=await i(`${r}/v1/invitations/accept`,{method:`POST`,headers:{accept:`application/json`,authorization:`Bearer ${e.refreshToken}`,"content-type":`application/json`},body:JSON.stringify({invitationId:e.invitationId,...e.displayName===void 0?{}:{displayName:e.displayName}}),cache:`no-store`,credentials:`omit`,signal:e.signal}),n=await Q(t);if(!t.ok)throw new Z($(n,`error`)??`INVITATION_ACCEPT_FAILED`,$(n,`message`)??`리뷰 초대 수락에 실패했습니다.`,t.status)},resolveReviewPage:e=>Xt(r,i,e),...e.collaboration?.cursors===!0||e.collaboration?.chat===!0?{resolveCollaborationRoom:e=>Yt(r,i,e)}:{}},p=u.apiVersion===2?await Gt({config:u,convexRuntimeFactory:o,fetcher:i,memorySessions:c,request:e,sessionStorage:l,serviceUrl:r}):await a(u.instantAppId,f).resolve(e),m=p.resolvePage;if(!m)return p;let h=async t=>{await Zt(r,i,{invitationId:e.invitationId,origin:t.location.origin,pathname:t.location.pathname},t.signal),d=s()};return{...p,resolvePage:async e=>(s()-d>=3e5&&await h(e),m(e))}}}}async function Gt(e){let t=$t(e.serviceUrl,e.request.location.origin,e.request.invitationId),n;if(e.request.emailToken?(await _t(e.request.authHost,e.request.signal,e.request.styleNonce),n=await qt(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}),nn(e.sessionStorage,e.memorySessions,t,n)):n=tn(e.sessionStorage,e.memorySessions,t),!n)throw new Z(`INVITATION_EMAIL_REQUIRED`,`초대받은 이메일의 링크를 다시 열어 주세요.`,401);return e.convexRuntimeFactory(e.config.convexUrl,{fetchAccessToken:r=>Kt(e.serviceUrl,e.fetcher,n,r,()=>rn(e.sessionStorage,e.memorySessions,t))}).resolve(e.request)}async function Kt(e,t,n,r,i){let a=await t(`${e}/v1/convex/token`,{method:`POST`,headers:{accept:`application/json`,authorization:`Bearer ${n}`,"content-type":`application/json`},body:JSON.stringify({invitationId:r.invitationId,origin:r.origin,pathname:r.pathname}),cache:`no-store`,credentials:`omit`,signal:r.signal}),o=await Q(a),s=$(o,`token`);if(!a.ok||!s||s.length>16e3)throw[401,403,409,410].includes(a.status)&&i(),new Z($(o,`error`)??`CONVEX_AUTH_FAILED`,$(o,`message`)??`리뷰 인증을 갱신하지 못했습니다.`,a.status);return s}async function qt(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}),cache:`no-store`,credentials:`omit`,signal:n.signal}),i=await Q(r);if(!r.ok)throw new Z($(i,`error`)??`INVITATION_REDEEM_FAILED`,$(i,`message`)??`리뷰 초대를 시작하지 못했습니다.`,r.status);let a=$(i,`refreshToken`);if(!a||a.length>4096)throw new Z(`INVALID_INVITATION_REDEEM_RESPONSE`,`잘못된 초대 인증 응답입니다.`,502);return a}function Jt(e){return{invitationId:e.invitationId,origin:e.location.origin,pathname:e.location.pathname}}async function Yt(e,t,n){let r=await t(`${e}/v1/collaboration/room`,{method:`POST`,headers:{accept:`application/json`,authorization:`Bearer ${n.refreshToken}`,"content-type":`application/json`},body:JSON.stringify({invitationId:n.invitationId,pageId:n.pageId,scopeId:n.scopeId}),cache:`no-store`,credentials:`omit`,signal:n.signal}),i=await Q(r);if(!r.ok)throw new Z($(i,`error`)??`COLLABORATION_ROOM_FAILED`,$(i,`message`)??`실시간 협업 연결에 실패했습니다.`,r.status);if(!i||typeof i!=`object`||Array.isArray(i))throw new Z(`INVALID_COLLABORATION_ROOM`,`잘못된 협업 응답입니다.`,502);let a=i,o=$(a,`id`);if(!o||!/^vr_[A-Za-z0-9_-]{43}$/u.test(o)||!Number.isSafeInteger(a.expiresAt))throw new Z(`INVALID_COLLABORATION_ROOM`,`잘못된 협업 응답입니다.`,502);return{id:o,expiresAt:a.expiresAt}}async function Xt(e,t,n){let r=await t(`${e}/v1/pages/resolve`,{method:`POST`,headers:{accept:`application/json`,authorization:`Bearer ${n.refreshToken}`,"content-type":`application/json`},body:JSON.stringify({invitationId:n.invitationId,origin:n.origin,pathname:n.pathname}),cache:`no-store`,credentials:`omit`,signal:n.signal});if(r.ok)return;let i=await Q(r);throw new Z($(i,`error`)??`PAGE_RESOLVE_FAILED`,$(i,`message`)??`리뷰 페이지를 준비하지 못했습니다.`,r.status)}async function Zt(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 Q(i);if(!i.ok)throw new Z($(a,`error`)??`BOOTSTRAP_FAILED`,$(a,`message`)??`Visual Review bootstrap에 실패했습니다.`,i.status);return Qt(a)}function Qt(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new Z(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let t=e;if(typeof t.apiVersion==`number`&&t.apiVersion!==1&&t.apiVersion!==2)throw new Z(`UNSUPPORTED_SERVICE_VERSION`,`이 Visual Review 위젯 버전은 더 이상 지원되지 않습니다. 사이트의 @01.works/visual-review를 업데이트해 주세요.`,502);if(t.apiVersion===2){let e=an(t.convexUrl);if(t.provider!==`convex`||!e)throw new Z(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);return{apiVersion:2,provider:`convex`,convexUrl:e}}let n=$(t,`instantAppId`);if(t.apiVersion!==1||!n||!on(n))throw new Z(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);return{apiVersion:1,instantAppId:n}}function $t(e,t,n){return`01works.visual-review.convex-session.v1:${JSON.stringify([e,t,n])}`}function en(){try{return globalThis.sessionStorage??null}catch{return null}}function tn(e,t,n){try{let t=e?.getItem(n);if(t)return t}catch{}return t.get(n)??null}function nn(e,t,n,r){t.set(n,r);try{e?.setItem(n,r)}catch{}}function rn(e,t,n){t.delete(n);try{e?.removeItem(n)}catch{}}function an(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 Q(e){try{return await e.json()}catch{throw new Z(`INVALID_BOOTSTRAP_RESPONSE`,`JSON bootstrap 응답이 필요합니다.`,502)}}function $(e,t){if(!e||typeof e!=`object`||Array.isArray(e))return null;let n=e[t];return typeof n==`string`&&n.trim()?n.trim():null}function on(e){return/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e)}export{Ut as createHostedReviewRuntime};
|
package/dist/index.d.ts
CHANGED
|
@@ -203,6 +203,8 @@ export interface VisualReviewCollaborationRepository {
|
|
|
203
203
|
|
|
204
204
|
export interface ReviewRuntimeRequest {
|
|
205
205
|
invitationId: string;
|
|
206
|
+
/** One-time bearer delivered in the invitation email fragment. */
|
|
207
|
+
emailToken?: string;
|
|
206
208
|
location: Location;
|
|
207
209
|
signal: AbortSignal;
|
|
208
210
|
authHost: HTMLElement;
|
|
@@ -276,6 +278,16 @@ export interface VisualReviewOffset {
|
|
|
276
278
|
y?: number;
|
|
277
279
|
}
|
|
278
280
|
|
|
281
|
+
export type VisualReviewInactiveReason =
|
|
282
|
+
| 'missing-activation'
|
|
283
|
+
| 'origin-not-allowed';
|
|
284
|
+
|
|
285
|
+
export interface VisualReviewInactiveState {
|
|
286
|
+
reason: VisualReviewInactiveReason;
|
|
287
|
+
/** The exact browser origin, including the port when one is present. */
|
|
288
|
+
origin: string;
|
|
289
|
+
}
|
|
290
|
+
|
|
279
291
|
export interface InstallVisualReviewOptions {
|
|
280
292
|
/** Override only for local development or self-hosted integration tests. */
|
|
281
293
|
serviceUrl?: string;
|
|
@@ -303,6 +315,8 @@ export interface InstallVisualReviewOptions {
|
|
|
303
315
|
/** CSP nonce for review-owned styles. Defaults to a nonce found on the host document. */
|
|
304
316
|
styleNonce?: string;
|
|
305
317
|
onError?: (error: Error) => void;
|
|
318
|
+
/** Called when the launcher can safely identify why it stayed inactive. */
|
|
319
|
+
onInactive?: (state: VisualReviewInactiveState) => void;
|
|
306
320
|
/** Called once for each successful mount owned by this installation. */
|
|
307
321
|
onReady?: (widget: HTMLElement) => void;
|
|
308
322
|
/** Optional host-router adapter for opening feedback on another page. */
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./install-
|
|
1
|
+
import{t as e}from"./install-DSA2Fpbw.js";export{e as installVisualReview};
|
|
@@ -0,0 +1,3 @@
|
|
|
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,_=null,v=!1,b=null,x=null,S=t(e.serviceUrl??`https://review.01.works`),E=e.activationParam??`visual-review`,k=ae(window),M=w(window.location);if(!(A(S,E,M,k,e.runtime!==void 0)??T(D(S,M.origin)))){let t={reason:`missing-activation`,origin:M.origin};return L(e.build).mode===`development`&&c(document,t.origin),l(e.onInactive,t),n()}let N=document.baseURI,I=new WeakMap,R=new Map,z=se(e.styleNonce,document),B=C(window.location,E),V=y(window,e=>{let t=C(e,E);if(t===B)return;let n=j(E,e.href);n&&R.set(e.origin,n),B=t,U(e)}),H=e=>{if(!(r||!_||e.target!==_)){if((e instanceof CustomEvent&&e.detail&&typeof e.detail==`object`&&`code`in e.detail?e.detail.code:void 0)===`membership-revoked`){let e=w(window.location),t=R.get(e.origin);t&&(O(S,e.origin,t),R.delete(e.origin),J(S,e.origin,k,t));return}B=C(window.location,E),K()}};return window.addEventListener(`visual-review:reauthenticate-required`,H),{ready:K(),refresh(){return r?Promise.resolve(null):(B=C(window.location,E),U(w(window.location),!0))},dispose(){r||(r=!0,i+=1,G(),_?.remove(),_=null,x=null,b?.remove(),b=null,R.clear(),V.unsubscribe(),window.removeEventListener(`visual-review:reauthenticate-required`,H))}};function U(e,t=!1){return!_||!x||x.origin!==e.origin||j(E,e.href)?K():W(_,x,e,t)}function W(t,n,r,c){let l=++i;a!==o&&a?.abort();let u=new AbortController;a=u;let f=d(e.build,r),p=(async()=>{try{let i=await F(n.resolvePage({location:r,signal:u.signal,collaboration:{cursors:e.collaboration?.cursors===!0,chat:e.collaboration?.chat===!0,scopeId:f}}),u.signal);return Z(l,u)?(i.commit?.(),t.setPage({pageId:i.pageId,collaboration:{roomId:i.collaborationRoomId,scopeId:f}},{reload:c}),t.isConnected||document.body?.append(t),t):Q(l)}catch{return Z(l,u)?K():Q(l)}})();return s=p,p}function G(){a?.abort(),o?.abort(),a=null,o=null}function K(){let t=++i;G(),_?.remove(),_=null,x=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&&Z(t,n)&&_===r&&e.onReady?.(r)}),c}async function q(t,n,i){try{let e=document.body??await re(document,n.signal);if(!Z(t,n))return Q(t);e.append(i);let r=await Y(t,n,i);return Z(t,n)?r:(i.remove(),Q(t))}catch(a){if(!Z(t,n))return i.remove(),Q(t);if(m(a,`AUTH_CANCELLED`))return i.remove(),b===i&&(b=null),null;let o=I.get(i),s=o?.[3];X(a,i);let c=oe(a,o?.[2]),d=o?.[1]??w(window.location).origin,f=m(a,`ORIGIN_NOT_ALLOWED`);if(f&&l(e.onInactive,{reason:`origin-not-allowed`,origin:d}),s!==`storage`||v||!h(a)){let{renderBootstrapError:e}=await import(`./bootstrap-error-BwIf8fUE.js`);if(!Z(t,n))return i.remove(),Q(t);let o=g(a)?void 0:()=>{r||b!==i||(B=C(window.location,E),K())};e(i,f?u(d):c.message,z,o)}else i.remove(),b===i&&(b=null);return e.onError?.(c),window.dispatchEvent(new CustomEvent(`visual-review:error`,{detail:c})),null}}async function Y(t,n,r){let i=w(window.location),a=A(S,E,i,k,e.runtime!==void 0),o=te(R.get(i.origin)),s=T(D(S,i.origin)),c=a?.source===`url`?a:s??o??a;if(!c)return I.delete(r),r.remove(),b===r&&(b=null),null;let l=c.invitationId;I.set(r,[S,i.origin,l,c.source,c.emailToken]),c.source===`url`?(J(S,i.origin,k),R.set(i.origin,l),ne(S,i.origin,l,c.emailToken),V.suppress(()=>P(E))):c.source===`handoff`&&R.set(i.origin,l),B=C(window.location,E);let u=w(window.location),m=await F((e.runtime??await f(S)).resolve({invitationId:l,emailToken:c.emailToken,location:u,signal:n.signal,authHost:r,styleNonce:z,collaboration:{cursors:e.collaboration?.cursors===!0,chat:e.collaboration?.chat===!0,scopeId:d(e.build,u)}}),n.signal);if(!Z(t,n))return null;O(S,i.origin,l),ie(S,l,i,k),R.set(i.origin,l);let h=e.sourceContextResolver??await ee(e.build,N,n.signal,z);if(!Z(t,n))return null;let{mountReviewWidget:g}=await p(n.signal);return Z(t,n)?(_=g({...m,collaboration:{cursors:e.collaboration?.cursors===!0&&!!(m.collaborationRepository&&m.collaborationRoomId),chat:e.collaboration?.chat===!0&&!!m.collaborationRepository,repository:m.collaborationRepository,roomId:m.collaborationRoomId,scopeId:d(e.build,i)},developerTools:e.developerTools?.codeContextCopy===!0||e.developerTools?.reactGrab===!0?{codeContextCopy:!0}:void 0,offset:e.offset,onNavigateToFeedback:async t=>{let n=new URL(t,window.location.href);if(n.origin!==window.location.origin)throw Error(`Visual Review route navigation must stay on the staging origin`);if(e.navigateToFeedback){await e.navigateToFeedback(n.href);return}window.location.assign(n.href)},sourceContextResolver:h,styleNonce:z,position:e.position}),x=m.resolvePage?{origin:i.origin,resolvePage:e=>m.resolvePage(e)}:null,r.remove(),b===r&&(b=null),v=!0,window.dispatchEvent(new CustomEvent(`visual-review:ready`,{detail:_})),_):null}function X(e,t){if(!h(e))return;let n=I.get(t);if(!n)return;let[r,i,a]=n;O(r,i,a),R.get(i)===a&&R.delete(i),J(r,i,k,a)}function Z(e,t){return!r&&i===e&&a===t&&!t.signal.aborted}function Q(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)}}function u(e){return[`현재 Origin이 Visual Review 프로젝트에 등록되어 있지 않아 시작할 수 없습니다.`,``,`현재 Origin:`,e,``,`프로젝트 설정에 위 값을 그대로 추가한 다음 초대 링크를 다시 여세요.`,`프로토콜, 호스트 이름, 포트까지 정확히 일치해야 합니다.`].join(`
|
|
3
|
+
`)}function d(e,t){return JSON.stringify([L(e).buildId,t.pathname,t.search])}async function f(e){let{createHostedReviewRuntime:t}=await import(`./hosted-runtime-BKZERnAW.js`);return t({serviceUrl:e})}async function p(e){return I(import(`./src-CtuDVE7s.js`),e,a,`리뷰 위젯을 불러오는 시간이 초과되었습니다. 다시 시도해 주세요.`)}async function ee(e,t,n,r){let i=L(e),{createGeneratedSourceContextResolver:o}=await I(import(`./generated-source-context-D59o5CKS.js`),n,a,`리뷰 소스 정보를 불러오는 시간이 초과되었습니다. 다시 시도해 주세요.`);return o({build:i})}function m(e,t){return e instanceof Error&&`code`in e&&e.code===t}function h(e){let t=_(e);return typeof t==`string`&&/^INVITATION_(?:EXPIRED|NOT_FOUND|REVOKED)$/.test(t)}function g(e){return h(e)||_(e)===`UNSUPPORTED_SERVICE_VERSION`}function _(e){return e instanceof Error&&`code`in e?e.code:void 0}const v=new WeakMap;function y(e,t){let n=v.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),b(e)},patchedReplaceState:function(t,n,r){i.call(this,t,n,r),b(e)},popstateListener:()=>b(e),subscribers:a,suppressionDepth:0},v.set(e,n),x(t,`pushState`,n.patchedPushState),x(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),S(n.history,`pushState`,n.patchedPushState,n.originalPushStateDescriptor),S(n.history,`replaceState`,n.patchedReplaceState,n.originalReplaceStateDescriptor),v.delete(e)))}}}function b(e){let t=v.get(e);if(!t||t.suppressionDepth>0)return;let n=w(e.location);[...t.subscribers].forEach(e=>e(n))}function x(e,t,n){Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:!0,value:n})}function S(e,t,n,r){if(Object.getOwnPropertyDescriptor(e,t)?.value===n){if(r){Object.defineProperty(e,t,r);return}delete e[t]}}function C(e,t){return`${e.origin}\n${e.pathname}\n${j(t,e.href)??``}`}function w(e){return new URL(e.href)}function te(e){return e?{invitationId:e,source:`memory`}:null}function T(e){return e?{invitationId:e.invitationId,emailToken:e.emailToken,source:`handoff`}:null}function E(e,t){return JSON.stringify([e,t])}function D(e,t,n=Date.now()){let i=E(e,t),a=r.get(i);if(a){if(a.expiresAt>n)return a;clearTimeout(a.timeout),r.delete(i)}}function ne(e,t,n,a,o=Date.now()){k(o);let s=E(e,t),c=r.get(s);for(c&&(clearTimeout(c.timeout),r.delete(s));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 l=o+i,u=setTimeout(()=>{r.get(s)?.expiresAt===l&&r.delete(s)},i);r.set(s,{invitationId:n,emailToken:a,expiresAt:l,timeout:u})}function O(e,t,n){let i=E(e,t),a=r.get(i);a?.invitationId===n&&(clearTimeout(a.timeout),r.delete(i))}function k(e){for(let[t,n]of r)n.expiresAt>e||(clearTimeout(n.timeout),r.delete(t))}function A(e,t,n,r,i){let a=j(t,n.href);if(a)return{invitationId:a,emailToken:M(n.href)??void 0,source:`url`};let o=q(r,Y(e,n.origin));return o?{invitationId:o,source:`storage`}:i&&X(n.hostname)?{invitationId:`local-preview`,source:`local-preview`}:null}function j(e,t){let n=new URL(t),r=N(n.hash);return(r?.get(e)??r?.get(`visual-review`)??r?.get(`agency-review`)??r?.get(`agency-review-invite`)??n.searchParams.get(e)??n.searchParams.get(`agency-review`)??n.searchParams.get(`agency-review-invite`))?.trim()||null}function M(e){let t=(N(new URL(e).hash)?.get(`visual-review-token`))?.trim();return t&&/^[A-Za-z0-9_-]{43}$/u.test(t)?t:null}function N(e){let t=e.startsWith(`#`)?e.slice(1):e,n=t.startsWith(`?`)?t.slice(1):t;return n.includes(`=`)?new URLSearchParams(n):null}function P(e){let t=new URL(window.location.href),n=new Set([e,`visual-review`,`agency-review`,`agency-review-invite`,`visual-review-token`]),r=!1;n.forEach(e=>{t.searchParams.has(e)&&(t.searchParams.delete(e),r=!0)});let i=N(t.hash);if(i){n.forEach(e=>{i.has(e)&&(i.delete(e),r=!0)});let e=i.toString();t.hash=e?`#${e}`:``}r&&window.history.replaceState(window.history.state,``,`${t.pathname}${t.search}${t.hash}`)}function F(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 I(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 re(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 L(e){let t=document.querySelector(`meta[name="visual-review-build"]`),n=document.querySelector(`meta[name="visual-review-commit"]`),r=K(B()),i=K(V());return{buildId:G(e?.buildId)??W(t)??r??`unversioned`,gitCommit:H(e,n,i),generatedAt:e?.generatedAt,mode:e?.mode??z(R())??(X(window.location.hostname)?`development`:`production`)}}function R(){try{return process.env.VISUAL_REVIEW_PUBLIC_BUILD_MODE}catch{return}}function z(e){return e===`development`||e===`preview`||e===`production`?e:void 0}function B(){try{return process.env.VISUAL_REVIEW_PUBLIC_BUILD_ID}catch{return}}function V(){try{return process.env.VISUAL_REVIEW_PUBLIC_GIT_COMMIT}catch{return}}function H(e,t,n){return e?.gitCommit===null?null:typeof e?.gitCommit==`string`?U(e.gitCommit):U(t?.content)??U(n)}function U(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 W(e){return K(e?.content)}function G(e){let t=typeof e==`string`?e.trim():``;return t.length>0?t:null}function K(e){return typeof e==`string`&&e.length>0?e:null}function ie(e,t,n,r){try{r?.setItem(Y(e,n.origin),t)}catch{}}function q(e,t){try{return e?.getItem(t)??null}catch{return null}}function ae(e){try{return e.localStorage}catch{return null}}function J(e,t,n,r){try{if(!n)return;let i=Y(e,t),a=n.getItem(i);if(a===null||r!==void 0&&a!==r)return;n.removeItem(i)}catch{}}function Y(e,t){return`visual-review:${e}:${t}:invitation`}function X(e){return e===`localhost`||e===`127.0.0.1`||e.endsWith(`.test`)||e.endsWith(`.local`)}function oe(e,t){let n=e instanceof Error?e.message:String(e),r=Error(Z(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:Z(n,t)}),typeof i==`number`&&Number.isFinite(i)&&Object.defineProperty(r,"status",{configurable:!0,enumerable:!0,value:i})}return r}function Z(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 se(e,t){if(e!==void 0)return $(e);let n=t.currentScript,r=n?Q(n):void 0;if(r)return r;try{for(let e of t.querySelectorAll(`script[nonce],style[nonce]`)){let t=Q(e);if(t)return t}}catch{}}function Q(e){let t=`nonce`in e?e.nonce:void 0;return typeof t==`string`?$(t):void 0}function $(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-DSA2Fpbw.js";import{useEffect as t,useRef as n}from"react";function r({activationParam:r,build:i,collaboration:a,developerTools:o,enabled:s,navigateToFeedback:c,offset:l,onError:u,onInactive:d,onReady:f,position:p,runtime:m,serviceUrl:h,sourceContextResolver:g,styleNonce:_}){let v=n(u),y=n(d),b=n(f),x=n(null),S=i?.buildId,C=i?.gitCommit,w=i?.generatedAt,T=i?.mode,E=o?.codeContextCopy===!0||o?.reactGrab===!0,D=a?.cursors===!0,O=a?.chat===!0;return v.current=u,y.current=d,b.current=f,t(()=>{if(s===!1)return;let t=e({activationParam:r,build:S===void 0&&C===void 0&&w===void 0&&T===void 0?void 0:{buildId:S,gitCommit:C,generatedAt:w,mode:T},collaboration:D||O?{cursors:D,chat:O}:void 0,developerTools:E?{codeContextCopy:!0}:void 0,enabled:s,navigateToFeedback:c,offset:l,onError:e=>v.current?.(e),onInactive:e=>{let t=`${e.reason}\n${e.origin}`;x.current!==t&&(x.current=t,y.current?.(e))},onReady:e=>{x.current=null,b.current?.(e)},runtime:m,position:p,serviceUrl:h,sourceContextResolver:g,styleNonce:_});return()=>t.dispose()},[r,S,T,O,D,E,s,w,C,c,l?.x,l?.y,p,m,h,g,_]),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.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Invitation-only visual feedback for staging websites, hosted by 01.works.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
}
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
|
+
"@agency-review/convex-adapter": "0.0.0",
|
|
73
74
|
"@agency-review/review-widget": "0.0.0",
|
|
74
75
|
"@types/react": "^19.2.0",
|
|
75
76
|
"react": "^19.2.8",
|
|
@@ -85,6 +86,7 @@
|
|
|
85
86
|
"@babel/plugin-transform-react-jsx": "^7.27.1",
|
|
86
87
|
"@babel/plugin-transform-typescript": "^7.28.5",
|
|
87
88
|
"@instantdb/core": "^1.0.52",
|
|
89
|
+
"convex": "1.45.0",
|
|
88
90
|
"magic-string": "^0.30.21"
|
|
89
91
|
}
|
|
90
92
|
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./install-RHVUfLTU.js";import{t}from"./embedded-source-DSDHbbeU.js";import{i as n,init as r}from"@instantdb/core";const i=131072,a=32768,o=4096,s=1e3;var c=class extends Error{constructor(e,t){super(e,t),this.name=`InstantCaptureIntegrityError`}};function l(e){let t=_(e,`comment target`,!0),n={kind:t.kind,anchor:le(t.anchor),region:fe(t.region),elements:t.elements.map(e=>({selector:e.selector,tagName:e.tagName,textPreview:e.textPreview,rect:fe(e.rect),source:null,componentStack:[]})),pageUrl:t.pageUrl,pageTitle:t.pageTitle,capturedAt:t.capturedAt,...t.environment?{environment:ne(t.environment)}:{}},r={elements:t.elements.map(e=>({selector:e.selector,source:e.source?pe(e.source):null,componentStack:e.componentStack.map(pe)})),...t.sourceProvenance?{sourceProvenance:me(t.sourceProvenance)}:{}};T(n,i,`Public target`);let a=xe(r,65536,`Source context`);return{publicTarget:n,commentFields:f(n),targetElements:n.elements.map(p),sourceContextJson:a}}function u(e,t,n){let r=b(e,n),i={selector:x(r.anchorSelector,`${n} anchor selector`,o),offsetX:w(r.anchorOffsetX,`${n} anchor offsetX`),offsetY:w(r.anchorOffsetY,`${n} anchor offsetY`),viewportX:w(r.anchorViewportX,`${n} anchor viewportX`),viewportY:w(r.anchorViewportY,`${n} anchor viewportY`)},a=h({kind:r.targetKind,anchor:i,region:{pageX:r.targetPageX,pageY:r.targetPageY,width:r.targetWidth,height:r.targetHeight,viewportX:r.targetViewportX,viewportY:r.targetViewportY,viewportWidth:r.targetViewportWidth,viewportHeight:r.targetViewportHeight},elements:[],pageUrl:de({origin:S(r.targetPageOrigin,`${n} page origin`,1024),pathname:S(r.targetPagePathname,`${n} page pathname`,4096),search:x(r.targetPageSearch,`${n} page search`,1024),hash:x(r.targetPageHash,`${n} page hash`,1024)}),pageTitle:r.targetPageTitle,capturedAt:r.targetCapturedAt,...te(r,n)},n),s=m(t,`${n} elements`),c=a;for(let e of s)try{c=h({...a,elements:[...c.elements,e]},n)}catch{}return c}function d(e){if(!Array.isArray(e)||e.length===0)return;let t=[...e].map(e=>{try{let t=b(e,`Comment scope entry`);return{row:t,ordinal:_e(t.ordinal,`Comment scope entry ordinal`,0,19),id:typeof t.id==`string`?t.id:``}}catch{return null}}).filter(e=>e!==null).sort((e,t)=>e.ordinal-t.ordinal||e.id.localeCompare(t.id)),n={},r=new Set;for(let{row:e,ordinal:i}of t)if(!r.has(i)){r.add(i);try{let t=S(e.key,`Comment scope key`,256);if(Object.hasOwn(n,t))continue;switch(e.valueType){case`string`:n[t]=x(e.stringValue,`Comment scope ${t}`,s);break;case`number`:n[t]=C(e.numberValue,`Comment scope ${t}`);break;case`boolean`:if(typeof e.booleanValue!=`boolean`)throw E(`Comment scope ${t} is invalid`);n[t]=e.booleanValue;break;case`null`:n[t]=null;break;default:throw E(`Comment scope ${t} is invalid`)}}catch{}}if(Object.keys(n).length!==0)try{return T(n,a,`Comment scope`),n}catch{return}}function f(e){let t=new URL(e.pageUrl);return{anchorSelector:e.anchor.selector,anchorOffsetX:e.anchor.offsetX,anchorOffsetY:e.anchor.offsetY,anchorViewportX:e.anchor.viewportX,anchorViewportY:e.anchor.viewportY,targetKind:e.kind,targetPageX:e.region.pageX,targetPageY:e.region.pageY,targetWidth:e.region.width,targetHeight:e.region.height,targetViewportX:e.region.viewportX,targetViewportY:e.region.viewportY,targetViewportWidth:e.region.viewportWidth,targetViewportHeight:e.region.viewportHeight,targetPageOrigin:t.origin,targetPagePathname:ue(t.pathname),targetPageSearch:t.search.slice(1),targetPageHash:t.hash.slice(1),targetPageTitle:e.pageTitle,targetCapturedAt:e.capturedAt,...e.environment?{environmentBrowser:e.environment.browser,environmentOperatingSystem:e.environment.operatingSystem,environmentDeviceType:e.environment.deviceType,environmentScreenWidth:e.environment.screenWidth,environmentScreenHeight:e.environment.screenHeight,environmentDevicePixelRatio:e.environment.devicePixelRatio}:{}}}function p(e,t){return{ordinal:t,selector:e.selector,tagName:e.tagName,textPreview:e.textPreview,pageX:e.rect.pageX,pageY:e.rect.pageY,width:e.rect.width,height:e.rect.height,viewportX:e.rect.viewportX,viewportY:e.rect.viewportY,viewportWidth:e.rect.viewportWidth,viewportHeight:e.rect.viewportHeight}}function m(e,t){if(!Array.isArray(e)||e.length===0)return[];let n=[...e].map(e=>{try{let n=b(e,t);return{row:n,ordinal:_e(n.ordinal,`${t} ordinal`,0,7),id:typeof n.id==`string`?n.id:``}}catch{return null}}).filter(e=>e!==null).sort((e,t)=>e.ordinal-t.ordinal||e.id.localeCompare(t.id)),r=[],i=new Set;for(let{row:e,ordinal:a}of n)if(!i.has(a)){i.add(a);try{r.push(ie({selector:e.selector,tagName:e.tagName,textPreview:e.textPreview,rect:{pageX:e.pageX,pageY:e.pageY,width:e.width,height:e.height,viewportX:e.viewportX,viewportY:e.viewportY,viewportWidth:e.viewportWidth,viewportHeight:e.viewportHeight},source:null,componentStack:[]},`${t}[${a}]`,!1))}catch{}}return r}function h(e,t){return _(e,t,!1)}function g(e,t=`Comment body`){return S(e,t,2e4)}function ee(e){if(e==null)return;let t=b(e,`Comment scope`);if(Object.keys(t).length>20)throw E(`Comment scope has too many fields`);let n={};for(let[e,r]of Object.entries(t)){if(!e.trim()||e.length>256)throw E(`Comment scope has an invalid key`);if(typeof r==`string`){if(r.length>s)throw E(`Comment scope ${e} is too long`);n[e]=r}else if(typeof r==`number`)n[e]=C(r,`Comment scope ${e}`);else if(typeof r==`boolean`||r===null)n[e]=r;else throw E(`Comment scope ${e} is invalid`)}return T(n,a,`Comment scope`),n}function _(e,t,n){let r=y(e,t,[`kind`,`anchor`,`region`,`elements`,`pageUrl`,`pageTitle`,`capturedAt`,`environment`,`sourceProvenance`],[`environment`,`sourceProvenance`]),a=r.kind;if(a!==`element`&&a!==`region`&&a!==`viewport`)throw E(`${t} kind is invalid`);let o=he(r.elements,`${t} elements`);if(o.length>8)throw E(`${t} contains too many elements`);if(!n&&r.sourceProvenance!=null)throw E(`${t} exposes source provenance`);let c=S(r.pageUrl,`${t} pageUrl`,8192);be(c,`${t} pageUrl`);let l={kind:a,anchor:ae(r.anchor,`${t} anchor`),region:oe(r.region,`${t} region`),elements:o.map((e,r)=>ie(e,`${t} elements[${r}]`,n)),pageUrl:c,pageTitle:x(r.pageTitle,`${t} pageTitle`,s),capturedAt:ve(r.capturedAt,`${t} capturedAt`),...r.environment==null?{}:{environment:v(r.environment,`${t} environment`)},...n&&r.sourceProvenance!=null?{sourceProvenance:ce(r.sourceProvenance,`${t} provenance`)}:{}};return T(l,n?196608:i,t),l}function v(e,t){let n=y(e,t,[`browser`,`operatingSystem`,`deviceType`,`screenWidth`,`screenHeight`,`devicePixelRatio`]),r=n.deviceType;if(r!==`desktop`&&r!==`tablet`&&r!==`mobile`&&r!==`unknown`)throw E(`${t} deviceType is invalid`);return{browser:S(n.browser,`${t} browser`,120),operatingSystem:S(n.operatingSystem,`${t} operatingSystem`,120),deviceType:r,screenWidth:re(n.screenWidth,`${t} screenWidth`,1e5),screenHeight:re(n.screenHeight,`${t} screenHeight`,1e5),devicePixelRatio:re(n.devicePixelRatio,`${t} devicePixelRatio`,100)}}function te(e,t){if([e.environmentBrowser,e.environmentOperatingSystem,e.environmentDeviceType,e.environmentScreenWidth,e.environmentScreenHeight,e.environmentDevicePixelRatio].every(e=>e==null))return{};try{return{environment:v({browser:e.environmentBrowser,operatingSystem:e.environmentOperatingSystem,deviceType:e.environmentDeviceType,screenWidth:e.environmentScreenWidth,screenHeight:e.environmentScreenHeight,devicePixelRatio:e.environmentDevicePixelRatio},`${t} environment`)}}catch{return{}}}function ne(e){return{...e}}function re(e,t,n){let r=C(e,t);if(r<=0||r>n)throw E(`${t} is invalid`);return r}function ie(e,t,n){let r=y(e,t,[`selector`,`tagName`,`textPreview`,`rect`,`source`,`componentStack`]),i=he(r.componentStack,`${t} componentStack`);if(i.length>32)throw E(`${t} componentStack is too large`);if(!n&&(r.source!=null||i.length>0))throw E(`${t} exposes source metadata`);return{selector:S(r.selector,`${t} selector`,o),tagName:S(r.tagName,`${t} tagName`,128),textPreview:x(r.textPreview,`${t} textPreview`,s),rect:oe(r.rect,`${t} rect`),source:n&&r.source!=null?se(r.source,`${t} source`):null,componentStack:n?i.map((e,n)=>se(e,`${t} stack[${n}]`)):[]}}function ae(e,t){let n=y(e,t,[`selector`,`offsetX`,`offsetY`,`viewportX`,`viewportY`]);return{selector:x(n.selector,`${t} selector`,o),offsetX:w(n.offsetX,`${t} offsetX`),offsetY:w(n.offsetY,`${t} offsetY`),viewportX:w(n.viewportX,`${t} viewportX`),viewportY:w(n.viewportY,`${t} viewportY`)}}function oe(e,t){let n=y(e,t,[`pageX`,`pageY`,`width`,`height`,`viewportX`,`viewportY`,`viewportWidth`,`viewportHeight`]),r={pageX:C(n.pageX,`${t} pageX`),pageY:C(n.pageY,`${t} pageY`),width:C(n.width,`${t} width`),height:C(n.height,`${t} height`),viewportX:C(n.viewportX,`${t} viewportX`),viewportY:C(n.viewportY,`${t} viewportY`),viewportWidth:C(n.viewportWidth,`${t} viewportWidth`),viewportHeight:C(n.viewportHeight,`${t} viewportHeight`)};if(r.width<0||r.height<0||r.viewportWidth<0||r.viewportHeight<0)throw E(`${t} has a negative dimension`);return r}function se(e,n){let r=y(e,n,[`filePath`,`lineNumber`,`columnNumber`,`componentName`,`origin`,`generated`],[`generated`]),i=r.origin;if(i!==`app`&&i!==`shared-ui`&&i!==`dependency`&&i!==`generated`&&i!==`unknown`)throw E(`${n} origin is invalid`);let a=S(r.filePath,`${n} filePath`,o);return{filePath:a,lineNumber:ye(r.lineNumber,`${n} lineNumber`),columnNumber:ye(r.columnNumber,`${n} columnNumber`),componentName:r.componentName==null?null:S(r.componentName,`${n} componentName`,512),origin:t(a)}}function ce(e,t){let n=y(e,t,[`provider`,`build`,`resolvedAt`]),r=n.provider;if(r!==`react-grab`&&r!==`embedded`&&r!==`custom`)throw E(`${t} provider is invalid`);let i=y(n.build,`${t} build`,[`buildId`,`gitCommit`,`generatedAt`,`mode`],[`generatedAt`]),a=i.mode;if(a!==`development`&&a!==`preview`&&a!==`production`)throw E(`${t} build mode is invalid`);return{provider:r,build:{buildId:S(i.buildId,`${t} buildId`,512),gitCommit:i.gitCommit==null?null:S(i.gitCommit,`${t} gitCommit`,256),...i.generatedAt==null?{}:{generatedAt:ve(i.generatedAt,`${t} generatedAt`)},mode:a},resolvedAt:ve(n.resolvedAt,`${t} resolvedAt`)}}function le(e){return{...e}}function ue(e){return e.replace(/\/+$/,``)||`/`}function de(e){return`${e.origin}${e.pathname}${e.search?`?${e.search}`:``}${e.hash?`#${e.hash}`:``}`}function fe(e){return{...e}}function pe(e){return{...e}}function me(e){return{...e,build:{...e.build}}}function y(e,t,n,r=[]){let i=b(e,t);if(Object.keys(i).some(e=>!n.includes(e)))throw E(`${t} contains unknown fields`);for(let e of n)if(!r.includes(e)&&!Object.hasOwn(i,e))throw E(`${t} is missing ${e}`);return i}function b(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw E(`${t} is missing or invalid`);return e}function he(e,t){if(!Array.isArray(e))throw E(`${t} is missing or invalid`);return e}function x(e,t,n){if(typeof e!=`string`||ge(e,n)>n)throw E(`${t} is invalid or too long`);return e}function ge(e,t){let n=0;for(let r of e)if(n+=1,n>t)return n;return n}function S(e,t,n){let r=x(e,t,n);if(!r.trim())throw E(`${t} is empty`);return r}function C(e,t){if(typeof e!=`number`||!Number.isFinite(e))throw E(`${t} is invalid`);return e}function _e(e,t,n,r){if(typeof e!=`number`||!Number.isSafeInteger(e)||e<n||e>r)throw E(`${t} is invalid`);return e}function w(e,t){let n=C(e,t);if(n<0||n>1)throw E(`${t} is outside 0..1`);return n}function ve(e,t){if(typeof e!=`number`||!Number.isSafeInteger(e)||e<0||e>0xe677d21fdbff)throw E(`${t} is invalid`);return e}function ye(e,t){if(e===null)return null;if(typeof e!=`number`||!Number.isSafeInteger(e)||e<0)throw E(`${t} is invalid`);return e}function be(e,t){let n;try{n=new URL(e)}catch(e){throw E(`${t} is invalid`,e)}if(n.protocol!==`http:`&&n.protocol!==`https:`)throw E(`${t} must use HTTP or HTTPS`)}function T(e,t,n){xe(e,t,n)}function xe(e,t,n){let r;try{r=JSON.stringify(e)}catch(e){throw E(`${n} cannot be serialized`,e)}if(r.length>t)throw E(`${n} is too large`);return r}function E(e,t){return new c(e,{cause:t instanceof Error?t:void 0})}new Uint8Array([107,167,184,17,157,173,17,209,128,180,0,192,79,212,48,200]);const Se=n.schema({entities:{$files:n.entity({path:n.string().unique().indexed(),url:n.string()}),$users:n.entity({email:n.string().unique().indexed()}),projects:n.entity({name:n.string(),slug:n.string().unique().indexed(),allowedOrigin:n.string().optional(),originDisconnectedAt:n.number().optional(),originModelVersion:n.number().optional(),createdAt:n.number().indexed(),updatedAt:n.number()}),projectOrigins:n.entity({projectId:n.string().indexed(),originKey:n.string().unique().indexed(),origin:n.string().indexed(),label:n.string().optional(),createdAt:n.number().indexed(),updatedAt:n.number()}),reviewPages:n.entity({projectId:n.string().indexed(),autoRegistrationSlot:n.string().unique().indexed().optional(),name:n.string(),pathname:n.string(),stagingOrigin:n.string(),createdAt:n.number().indexed(),updatedAt:n.number()}),reviewPageBuilds:n.entity({projectId:n.string().indexed(),pageId:n.string().unique().indexed(),buildId:n.string(),gitCommit:n.string().optional(),generatedAt:n.number().optional(),mode:n.string(),updatedAt:n.number().indexed()}),reviewers:n.entity({authUserId:n.string().indexed(),projectId:n.string().indexed(),email:n.string().indexed(),displayName:n.string(),role:n.string(),createdAt:n.number()}),reviewerIdentityClaims:n.entity({reviewerId:n.string().unique().indexed(),projectId:n.string().indexed(),authUserId:n.string().indexed(),email:n.string(),displayName:n.string(),role:n.string(),createdAt:n.number()}),invitations:n.entity({projectId:n.string().indexed(),reviewerId:n.string().indexed().optional(),email:n.string().indexed(),status:n.string().indexed(),claimRevision:n.number(),claimedByAuthUserId:n.string().indexed().optional(),expiresAt:n.number().indexed(),createdAt:n.number(),acceptedAt:n.number().optional(),revokedAt:n.number().optional()}),comments:n.entity({pageId:n.string().indexed(),pageNumber:n.number().optional(),authorId:n.string().indexed(),authorAuthUserId:n.string().indexed(),authorName:n.string(),body:n.string(),bodyContentMarker:n.string().optional(),anchorSelector:n.string().optional(),anchorOffsetX:n.number().optional(),anchorOffsetY:n.number().optional(),anchorViewportX:n.number().optional(),anchorViewportY:n.number().optional(),targetKind:n.string().optional(),targetPageX:n.number().optional(),targetPageY:n.number().optional(),targetWidth:n.number().optional(),targetHeight:n.number().optional(),targetViewportX:n.number().optional(),targetViewportY:n.number().optional(),targetViewportWidth:n.number().optional(),targetViewportHeight:n.number().optional(),targetPageOrigin:n.string().optional(),targetPagePathname:n.string().optional(),targetPageSearch:n.string().optional(),targetPageHash:n.string().optional(),targetPageTitle:n.string().optional(),targetCapturedAt:n.number().optional(),environmentBrowser:n.string().optional(),environmentOperatingSystem:n.string().optional(),environmentDeviceType:n.string().optional(),environmentScreenWidth:n.number().optional(),environmentScreenHeight:n.number().optional(),environmentDevicePixelRatio:n.number().optional(),status:n.string().indexed(),workflowRevision:n.number(),createdAt:n.number().indexed(),updatedAt:n.number().indexed(),resolvedAt:n.number().optional(),resolvedById:n.string().optional(),deletingAt:n.number().optional()}),commentWorkflowClaims:n.entity({claimKey:n.string().unique().indexed(),commentId:n.string().indexed(),expectedWorkflowRevision:n.number(),createdAt:n.number().indexed()}),commentPageNumberClaims:n.entity({claimKey:n.string().unique().indexed(),pageId:n.string().indexed(),pageNumber:n.number(),commentId:n.string().indexed(),createdAt:n.number().indexed()}),commentTargetElements:n.entity({commentId:n.string().indexed(),slotKey:n.string().unique().indexed().optional(),ordinal:n.number(),selector:n.string(),tagName:n.string(),textPreview:n.string(),pageX:n.number(),pageY:n.number(),width:n.number(),height:n.number(),viewportX:n.number(),viewportY:n.number(),viewportWidth:n.number(),viewportHeight:n.number(),authorAuthUserId:n.string().indexed()}),commentScopeEntries:n.entity({commentId:n.string().indexed(),slotKey:n.string().unique().indexed().optional(),ordinal:n.number(),key:n.string(),valueType:n.string(),stringValue:n.string().optional(),numberValue:n.number().optional(),booleanValue:n.boolean().optional(),authorAuthUserId:n.string().indexed()}),commentSourceContexts:n.entity({commentId:n.string().unique().indexed(),pageId:n.string().indexed(),authorAuthUserId:n.string().indexed(),contextJson:n.string(),createdAt:n.number().indexed()}),replies:n.entity({pageId:n.string().indexed(),commentId:n.string().indexed(),authorId:n.string().indexed(),authorAuthUserId:n.string().indexed(),authorName:n.string(),body:n.string(),bodyContentMarker:n.string().optional(),createdAt:n.number().indexed(),updatedAt:n.number().indexed()}),reviewMessages:n.entity({pageId:n.string().indexed(),pathname:n.string().indexed(),authorId:n.string().indexed(),authorAuthUserId:n.string().indexed(),authorName:n.string(),body:n.string(),bodyContentMarker:n.string(),createdAt:n.number().indexed(),updatedAt:n.number().indexed()})},links:{projectOwners:{forward:{on:`projects`,has:`many`,label:`owners`},reverse:{on:`$users`,has:`many`,label:`ownedProjects`}},projectPages:{forward:{on:`projects`,has:`many`,label:`pages`},reverse:{on:`reviewPages`,has:`one`,label:`project`}},projectOriginProject:{forward:{on:`projects`,has:`many`,label:`origins`},reverse:{on:`projectOrigins`,has:`one`,label:`project`}},pageCurrentBuild:{forward:{on:`reviewPageBuilds`,has:`one`,label:`page`,required:!0,onDelete:`cascade`},reverse:{on:`reviewPages`,has:`one`,label:`currentBuild`}},projectReviewers:{forward:{on:`projects`,has:`many`,label:`reviewers`},reverse:{on:`reviewers`,has:`one`,label:`project`}},reviewerUser:{forward:{on:`reviewers`,has:`one`,label:`$user`},reverse:{on:`$users`,has:`many`,label:`reviewerMemberships`}},projectInvitations:{forward:{on:`projects`,has:`many`,label:`invitations`},reverse:{on:`invitations`,has:`one`,label:`project`}},invitationReviewer:{forward:{on:`invitations`,has:`one`,label:`reviewer`},reverse:{on:`reviewers`,has:`many`,label:`acceptedInvitations`}},invitationActiveUser:{forward:{on:`invitations`,has:`one`,label:`activeUser`},reverse:{on:`$users`,has:`many`,label:`activeReviewInvitations`}},pageComments:{forward:{on:`reviewPages`,has:`many`,label:`comments`},reverse:{on:`comments`,has:`one`,label:`page`}},pageReviewMessages:{forward:{on:`reviewPages`,has:`many`,label:`reviewMessages`},reverse:{on:`reviewMessages`,has:`one`,label:`page`}},commentReplies:{forward:{on:`comments`,has:`many`,label:`replies`},reverse:{on:`replies`,has:`one`,label:`comment`,required:!0,onDelete:`cascade`}},commentTargetElements:{forward:{on:`commentTargetElements`,has:`one`,label:`comment`,required:!0,onDelete:`cascade`},reverse:{on:`comments`,has:`many`,label:`targetElements`}},commentScopeEntries:{forward:{on:`commentScopeEntries`,has:`one`,label:`comment`,required:!0,onDelete:`cascade`},reverse:{on:`comments`,has:`many`,label:`scopeEntries`}},commentSourceContext:{forward:{on:`commentSourceContexts`,has:`one`,label:`comment`,required:!0,onDelete:`cascade`},reverse:{on:`comments`,has:`one`,label:`sourceContext`}},pageSourceContexts:{forward:{on:`commentSourceContexts`,has:`one`,label:`page`,required:!0},reverse:{on:`reviewPages`,has:`many`,label:`sourceContexts`}},sourceContextAuthor:{forward:{on:`commentSourceContexts`,has:`one`,label:`author`,required:!0},reverse:{on:`reviewers`,has:`many`,label:`sourceContexts`}},commentAuthor:{forward:{on:`comments`,has:`one`,label:`author`},reverse:{on:`reviewers`,has:`many`,label:`comments`}},replyAuthor:{forward:{on:`replies`,has:`one`,label:`author`},reverse:{on:`reviewers`,has:`many`,label:`replies`}},reviewMessageAuthor:{forward:{on:`reviewMessages`,has:`one`,label:`author`},reverse:{on:`reviewers`,has:`many`,label:`reviewMessages`}}},rooms:{visualReview:{presence:n.entity({reviewerId:n.string(),displayName:n.string(),color:n.string(),active:n.boolean(),pathname:n.string().optional(),cursorX:n.number().optional(),cursorY:n.number().optional(),viewportWidth:n.number().optional(),viewportHeight:n.number().optional(),cursorChat:n.string().optional()})}}}),D={$default:`false`},O={$default:`false`},k=`$user.activeReviewInvitations`,A=`${k}.project.id`;function j(e){return[`auth.id != null`,`size(auth.ref('${k}.id')) > 0`,`size(auth.ref('${A}')) == size(auth.ref('${k}.id'))`,`size(auth.ref('${k}.status')) == size(auth.ref('${k}.id'))`,`size(auth.ref('${k}.expiresAt')) == size(auth.ref('${k}.id'))`,`size(auth.ref('${k}.revokedAt')) == 0`,`auth.ref('${k}.status').all(status, status == 'accepted')`,`auth.ref('${k}.expiresAt').all(expiresAt, request.time < timestamp(expiresAt))`,e].join(` && `)}function M(...e){return e.map(e=>`(${e})`).join(` && `)}function N(...e){return e.map(e=>`(${e})`).join(` || `)}function P(e){let t=`data.ref('${e}project.origins.origin')`,n=`data.ref('${e}project.allowedOrigin')`,r=`data.ref('${e}project.originModelVersion')`;return N(`request.origin in ${t}`,M(`size(${r}) == 0`,`request.origin in ${n}`))}j(`data.ref('project.id')[0] in auth.ref('${A}')`),N(`request.origin in data.ref('project.origins.origin')`,M(`size(data.ref('project.originModelVersion')) == 0`,`request.origin in data.ref('project.allowedOrigin')`)),N(`request.origin in data.ref('origins.origin')`,M(`size(data.ref('originModelVersion')) == 0`,`request.origin in data.ref('allowedOrigin')`)),j(`data.ref('page.project.id')[0] in auth.ref('${A}')`),P(`page.`),N(`auth.id != null && auth.id in data.ref('comment.page.project.owners.id')`,M(j(`data.ref('comment.page.project.id')[0] in auth.ref('${A}')`),P(`comment.page.`))),j(`data.ref('comment.page.project.id')[0] in auth.ref('${A}')`),P(`comment.page.`),j(`data.ref('page.project.id')[0] in auth.ref('${A}')`),P(`page.`),P(``);const Ce=M(`data.reviewerId != null`,`data.projectId in data.ref('project.id')`,`data.reviewerId in data.ref('reviewer.id')`,`data.projectId in data.ref('reviewer.project.id')`,`auth.id in data.ref('reviewer.$user.id')`,`auth.email in data.ref('reviewer.email')`);M(`auth.id != null`,`data.revokedAt == null`,`data.status in ['pending', 'accepted']`,`data.status == 'pending' || data.claimedByAuthUserId == auth.id`,`request.time < timestamp(data.expiresAt)`,`request.modifiedFields.all(field, field in ['status', 'claimedByAuthUserId', 'acceptedAt', 'claimRevision', 'activeUser'])`,`'status' in request.modifiedFields`,`'claimedByAuthUserId' in request.modifiedFields`,`'acceptedAt' in request.modifiedFields`,`'claimRevision' in request.modifiedFields`,`'activeUser' in request.modifiedFields`,`newData.status == 'accepted'`,`newData.claimedByAuthUserId == auth.id`,`newData.claimRevision == data.claimRevision + 1`,`math.abs((request.time - timestamp(newData.acceptedAt)).getMinutes()) <= 1`,Ce),M(`actions.data == 'update'`,`actions.linkedData == 'update'`,`auth.id != null`,`auth.email != null`,`linkedData.id == auth.id`,`linkedData.email == auth.email`,`data.email == auth.email`,`data.revokedAt == null`,`data.status in ['pending', 'accepted']`,`data.status == 'pending' || data.claimedByAuthUserId == auth.id`,`request.time < timestamp(data.expiresAt)`,`newData.status == 'accepted'`,`newData.claimedByAuthUserId == auth.id`,`newData.claimRevision == data.claimRevision + 1`,`math.abs((request.time - timestamp(newData.acceptedAt)).getMinutes()) <= 1`,Ce),M(`actions.data == 'update'`,`actions.linkedData == 'update'`,`auth.id != null`,`auth.email != null`,`data.id == auth.id`,`data.email == auth.email`,`linkedData.email == auth.email`,`linkedData.revokedAt == null`,`linkedData.status == 'accepted'`,`linkedData.claimedByAuthUserId == auth.id`,`request.time < timestamp(linkedData.expiresAt)`,`math.abs((request.time - timestamp(linkedData.acceptedAt)).getMinutes()) <= 1`,`linkedData.projectId in linkedData.ref('project.id')`,`linkedData.reviewerId in linkedData.ref('reviewer.id')`,`linkedData.projectId in linkedData.ref('reviewer.project.id')`,`auth.id in linkedData.ref('reviewer.$user.id')`,`auth.email in linkedData.ref('reviewer.email')`),M(`auth.id != null`,`auth.email != null`,`linkedData.id == auth.id`,`linkedData.email == auth.email`,`data.email == auth.email`,`data.claimedByAuthUserId == auth.id`,`auth.id in data.ref('activeUser.id')`),M(`auth.id != null`,`auth.email != null`,`data.id == auth.id`,`data.email == auth.email`,`linkedData.email == auth.email`,`linkedData.claimedByAuthUserId == auth.id`,`auth.id in linkedData.ref('activeUser.id')`),{...D},{...D},{...O},{...O},j(`data.id in auth.ref('${A}')`),{...D},{...O},{...D},{...D},{...O},{...D},{...O},j(`data.ref('project.id')[0] in auth.ref('${A}')`),{...D},{...O},{...D},{...D},{...O},{...O},{...D},{...O},{...D},{...D},{...D},{...D},{...D},{...D},{...D};function we(e){if(!e.trim())throw Error(`InstantDB app ID is required`);return r({appId:e,schema:Se,devtool:!1})}var Te=class{db;session;#e;attachScreenshot;constructor(e,t,n={}){this.db=e,this.session=t,this.#e=n.commentTransport,this.#e&&(this.attachScreenshot=(e,t,n)=>this.#e.uploadScreenshot({pageId:e,commentId:t,screenshot:n}))}async getSnapshot(e){let{data:t}=await this.db.queryOnce(Ee(e,this.session.authUserId));return Oe(e,t.comments,t.replies)}async getProjectSnapshot(e){this.#t(e);let{data:t}=await this.db.queryOnce(De(e,this.session.authUserId));return ke(e,t.comments,t.replies)}subscribe(e,t,n){return this.db.subscribeQuery(Ee(e,this.session.authUserId),r=>{if(r.error){n?.(Ie(r.error));return}if(r.data)try{t(Oe(e,r.data.comments,r.data.replies))}catch{n?.({type:`error`,code:`data-integrity`,recovery:`retry`,clearSnapshot:!0})}})}subscribeProject(e,t,n){return this.#t(e),this.db.subscribeQuery(De(e,this.session.authUserId),r=>{if(r.error){n?.(Ie(r.error));return}if(r.data)try{t(ke(e,r.data.comments,r.data.replies))}catch{n?.({type:`error`,code:`data-integrity`,recovery:`retry`,clearSnapshot:!0})}})}async mutate(e,t){switch(t.type){case`comment.upsert`:{if(t.comment.pageId!==e)throw Error(`Comment page mismatch`);let n=t.comment;if(n.authorId!==this.session.reviewerId)throw Error(`Comment author mismatch`);let r=g(n.body).trim(),i=z(n.createdAt,`Comment createdAt`);if(z(n.updatedAt,`Comment updatedAt`)<i)throw new c(`Comment updatedAt precedes createdAt`);if(n.createdAt!==n.updatedAt)throw new c(`Comment edits must go through the review service`);if(n.status!==`open`||n.resolvedAt!==void 0||n.resolvedById!==void 0)throw new c(`New comments must start open`);let a=l(n.target);if(!Pe(n.anchor,a.publicTarget.anchor))throw new c(`Comment anchor does not match its target`);if(!this.#e)throw new c(`Comment creation requires the review service transport`);await this.#e.createComment({pageId:e,commentId:n.id,body:r,target:n.target,...n.scope===void 0?{}:{scope:n.scope}});return}case`comment.delete`:throw new c(`Comment deletion must go through the review service`);case`reply.upsert`:{let e=t.reply;if(e.authorId!==this.session.reviewerId)throw Error(`Reply author mismatch`);let n=g(e.body,`Reply body`).trim(),r=z(e.createdAt,`Reply createdAt`);if(z(e.updatedAt,`Reply updatedAt`)<r)throw new c(`Reply updatedAt precedes createdAt`);if(e.createdAt!==e.updatedAt)throw new c(`Reply edits must go through the review service`);if(!this.#e)throw new c(`Reply creation requires the review service transport`);await this.#e.createReply({commentId:e.commentId,replyId:e.id,body:n});return}case`reply.delete`:throw new c(`Reply deletion must go through the review service`)}}#t(e){if(!e.trim()||e!==this.session.projectId)throw Error(`Project does not belong to the reviewer session`)}};function Ee(e,t){return{comments:{$:{where:{pageId:e},order:{createdAt:`asc`}},targetElements:{},scopeEntries:{}},replies:{$:{where:{pageId:e},order:{createdAt:`asc`}}},$users:{$:{where:{id:t}},activeReviewInvitations:{project:{}}}}}function De(e,t){return{comments:{$:{where:{"page.project.id":e},order:{createdAt:`asc`}},page:{project:{}},targetElements:{},scopeEntries:{}},replies:{$:{where:{"comment.page.project.id":e},order:{createdAt:`asc`}},comment:{page:{project:{}}}},$users:{$:{where:{id:t}},activeReviewInvitations:{project:{}}}}}function Oe(e,t,n){let r=t.map(t=>je(t,e));F(r,`comment`);let i=new Set(r.map(({id:e})=>e)),a=n.map(t=>Me(t,e));F(a,`reply`);for(let e of a)if(!i.has(e.commentId))throw new c(`Reply ${e.id} references a comment outside the page snapshot`);return{pageId:e,comments:r,replies:a}}function ke(e,t,n){let r=new Map,i=t.map(t=>{let n=L(t.pageId,`comment pageId`);Ae(t.page,n,e,`comment ${String(t.id)}`);let i=I(t.page,`comment ${String(t.id)} page`),a=je(t,n),o=`/`;try{o=new URL(a.target.pageUrl).pathname||`/`}catch{}return r.set(n,{id:n,name:typeof i.name==`string`&&i.name.trim()?i.name.trim():a.target.pageTitle.trim()||o,pathname:typeof i.pathname==`string`&&i.pathname.trim()?i.pathname.trim():o}),a});F(i,`comment`);let a=new Map(i.map(e=>[e.id,e])),o=n.map(t=>{let n=L(t.pageId,`reply pageId`),r=I(t.comment,`reply ${String(t.id)} comment`),i=L(t.commentId,`reply ${String(t.id)} commentId`);if(L(r.id,`reply ${String(t.id)} comment relation id`)!==i)throw new c(`Reply ${String(t.id)} has an inconsistent comment relation`);Ae(r.page,n,e,`reply ${String(t.id)}`);let o=Me(t,n),s=a.get(o.commentId);if(!s||s.pageId!==n)throw new c(`Reply ${o.id} references a comment outside the project snapshot`);return o});return F(o,`reply`),{projectId:e,pages:[...r.values()].sort((e,t)=>e.pathname.localeCompare(t.pathname)||e.id.localeCompare(t.id)),comments:i,replies:o}}function Ae(e,t,n,r){let i=I(e,`${r} page`);if(L(i.id,`${r} page id`)!==t)throw new c(`${r} escaped its page relation`);if(L(I(i.project,`${r} project`).id,`${r} project id`)!==n)throw new c(`${r} escaped its project scope`)}function je(e,t){let n=I(e,`comment`),r=L(n.id,`comment id`);if(L(n.pageId,`comment ${r} pageId`)!==t)throw new c(`Comment ${r} escaped its page scope`);L(n.authorAuthUserId,`comment ${r} authorAuthUserId`);let i=!(Object.hasOwn(n,`anchor`)&&Object.hasOwn(n,`target`)),a=i?u(n,n.targetElements,`comment ${r} target`):h(n.target,`comment ${r} target`),o=i?{...a.anchor}:Ne(n.anchor,`comment ${r} anchor`);if(!Pe(o,a.anchor))throw new c(`Comment ${r} has inconsistent anchors`);let s=n.status;if(s!==`open`&&s!==`resolved`)throw new c(`Comment ${r} has an invalid status`);let l=z(n.createdAt,`comment ${r} createdAt`),f=z(n.updatedAt,`comment ${r} updatedAt`);if(f<l)throw new c(`Comment ${r} updatedAt precedes createdAt`);let p=Be(n.resolvedAt,`comment ${r} resolvedAt`),m=Ve(n.resolvedById,`comment ${r} resolvedById`);if(s===`open`&&(p!==void 0||m!==void 0))throw new c(`Open comment ${r} contains resolution metadata`);if(s===`resolved`&&(p===void 0||m===void 0||p!==f))throw new c(`Resolved comment ${r} has invalid workflow metadata`);return{id:r,pageId:t,...He(n.pageNumber,`comment ${r} pageNumber`),authorId:L(n.authorId,`comment ${r} authorId`),authorName:L(n.authorName,`comment ${r} authorName`),body:g(n.body,`comment ${r} body`),anchor:o,target:a,...i?Fe(d(n.scopeEntries)):n.scope==null?{}:{scope:ee(n.scope)},status:s,workflowRevision:ze(n.workflowRevision,`comment ${r} workflowRevision`),createdAt:l,updatedAt:f,...p===void 0?{}:{resolvedAt:p},...m===void 0?{}:{resolvedById:m}}}function Me(e,t){let n=I(e,`reply`),r=L(n.id,`reply id`);if(L(n.pageId,`reply ${r} pageId`)!==t)throw new c(`Reply ${r} escaped its page scope`);L(n.authorAuthUserId,`reply ${r} authorAuthUserId`);let i=z(n.createdAt,`reply ${r} createdAt`),a=z(n.updatedAt,`reply ${r} updatedAt`);if(a<i)throw new c(`Reply ${r} updatedAt precedes createdAt`);return{id:r,commentId:L(n.commentId,`reply ${r} commentId`),authorId:L(n.authorId,`reply ${r} authorId`),authorName:L(n.authorName,`reply ${r} authorName`),body:g(n.body,`reply ${r} body`),createdAt:i,updatedAt:a}}function Ne(e,t){let n=I(e,t);if(Object.keys(n).length!==5||!Object.hasOwn(n,`selector`)||!Object.hasOwn(n,`offsetX`)||!Object.hasOwn(n,`offsetY`)||!Object.hasOwn(n,`viewportX`)||!Object.hasOwn(n,`viewportY`))throw new c(`${t} has invalid fields`);return{selector:Re(n.selector,`${t} selector`),offsetX:R(n.offsetX,`${t} offsetX`),offsetY:R(n.offsetY,`${t} offsetY`),viewportX:R(n.viewportX,`${t} viewportX`),viewportY:R(n.viewportY,`${t} viewportY`)}}function Pe(e,t){return e.selector===t.selector&&e.offsetX===t.offsetX&&e.offsetY===t.offsetY&&e.viewportX===t.viewportX&&e.viewportY===t.viewportY}function Fe(e){return e===void 0?{}:{scope:e}}function F(e,t){let n=new Set;for(let r of e){if(n.has(r.id))throw new c(`Duplicate ${t} ${r.id}`);n.add(r.id)}}function Ie(e){let t=Le(e).toLowerCase();return t.includes(`expired`)||t.includes(`token`)?{type:`access-lost`,code:`session-expired`,recovery:`reauthenticate`,clearSnapshot:!0}:t.includes(`permission`)||t.includes(`forbidden`)||t.includes(`unauthorized`)?{type:`access-lost`,code:`permission-denied`,recovery:`reauthenticate`,clearSnapshot:!0}:{type:`error`,code:`transport`,recovery:`retry`,clearSnapshot:!0}}function Le(e){return e instanceof Error?e.message:e&&typeof e==`object`&&`message`in e?String(e.message):``}function I(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw new c(`${t} is missing or invalid`);return e}function Re(e,t){if(typeof e!=`string`)throw new c(`${t} is invalid`);return e}function L(e,t){let n=Re(e,t);if(!n.trim())throw new c(`${t} is empty`);return n}function R(e,t){if(typeof e!=`number`||!Number.isFinite(e)||e<0||e>1)throw new c(`${t} is invalid`);return e}function z(e,t){if(!Number.isSafeInteger(e)||e<0||e>0xe677d21fdbff)throw new c(`${t} is invalid`);return e}function ze(e,t){if(!Number.isSafeInteger(e)||e<0)throw new c(`${t} is invalid`);return e}function Be(e,t){return e==null?void 0:z(e,t)}function Ve(e,t){return e==null?void 0:L(e,t)}function He(e,t){if(e==null)return{};let n=ze(e,t);if(n<1)throw new c(`${t} is invalid`);return{pageNumber:n}}Object.freeze({field:`createdAt`,direction:`desc`,tieBreaker:`id`,tieDirection:`desc`}),Object.freeze({comments:200,replies:1e3,sourceContexts:200});var Ue=class{db;session;roomCredential;#e=new Map;constructor(e,t,n){this.db=e,this.session=t,this.roomCredential=n}subscribe(e,t,n,r){let i=[],a=[],o=!1,s=[],c=()=>{o||n({pageId:e,peers:structuredClone(i),messages:structuredClone(a)})};if(t.cursors){let n=tt(e,t.roomId),a,l,u=this.roomCredential?.id===t.roomId?this.roomCredential:void 0,d=u??{id:t.roomId,expiresAt:2**53-1,refresh:async()=>void 0},f=t=>{let o=this.db.joinRoom(`visualReview`,t),s=o.subscribePresence({},e=>{if(e.error){r?.(rt(e.error));return}i=Object.values(e.peers).map(e=>Ge(e)).filter(e=>e!==null).sort((e,t)=>e.displayName.localeCompare(t.displayName)||e.peerId.localeCompare(t.peerId)),c()}),l=a;a=()=>{s(),o.leaveRoom()},this.#e.set(n,{pageId:e,roomId:t,publishPresence:e=>o.publishPresence(e)}),l?.()},p=()=>{if(!u||o)return;let e=Math.max(1e3,d.expiresAt-Date.now()+250);l=setTimeout(()=>{m()},e)},m=async()=>{try{let e=await d.refresh();if(o||!e)return;d={...e,refresh:d.refresh},u&&(u.id=e.id,u.expiresAt=e.expiresAt),f(e.id),p()}catch(e){if(o)return;r?.(rt(e)),o||(l=setTimeout(()=>{m()},5e3))}};f(d.id),p(),s.push(()=>{l&&clearTimeout(l),this.#e.delete(n),a?.()})}return t.chat&&s.push(this.db.subscribeQuery(We(e,this.session.authUserId),t=>{if(t.error){r?.(rt(t.error));return}if(t.data)try{a=t.data.reviewMessages.map(t=>Je(t,e)).sort(nt),c()}catch{r?.({type:`error`,code:`data-integrity`,recovery:`retry`,clearSnapshot:!0})}})),queueMicrotask(c),()=>{if(!o){o=!0;for(let e of s.reverse())try{e()}catch{}}}}publishPresence(e,t,n){let r=this.#e.get(tt(e,t));r&&r.publishPresence({reviewerId:this.session.reviewerId,displayName:this.session.displayName,color:n.color,active:n.active,pathname:qe(n.pathname),...n.cursorX===void 0?{}:{cursorX:n.cursorX},...n.cursorY===void 0?{}:{cursorY:n.cursorY},...n.viewportWidth===void 0?{}:{viewportWidth:n.viewportWidth},...n.viewportHeight===void 0?{}:{viewportHeight:n.viewportHeight},cursorChat:Ke(n.cursorChat)})}async sendMessage(e,t){throw Error(`Persisted review messages are no longer supported`)}};function We(e,t){return{reviewMessages:{$:{where:{pageId:e},order:{createdAt:`desc`},limit:200}},$users:{$:{where:{id:t}},activeReviewInvitations:{project:{}}}}}function Ge(e){let t=Ze(e.reviewerId,128),n=Ze(e.displayName,512),r=typeof e.color==`string`&&/^#[0-9a-f]{6}$/iu.test(e.color)?e.color:null;if(!t||!n||!r||typeof e.active!=`boolean`)return null;let i=Qe(e.cursorX),a=Qe(e.cursorY),o=$e(e.viewportWidth),s=$e(e.viewportHeight),c=Ke(e.cursorChat),l=qe(e.pathname);return{peerId:e.peerId,reviewerId:t,displayName:n,color:r,active:e.active,pathname:l,...i===void 0?{}:{cursorX:i},...a===void 0?{}:{cursorY:a},...o===void 0?{}:{viewportWidth:o},...s===void 0?{}:{viewportHeight:s},...c?{cursorChat:c}:{}}}function Ke(e){return typeof e==`string`?e.trim().slice(0,280):``}function qe(e){if(typeof e!=`string`)return`/`;let t=e.trim();return t.startsWith(`/`)?t.slice(0,2e3):`/`}function Je(e,t){let n=B(e.id,`Review message id`,128);if(B(e.pageId,`Review message pageId`,128)!==t)throw Error(`Review message page mismatch`);return{id:n,pageId:t,pathname:Xe(e.pathname),authorId:B(e.authorId,`Review message authorId`,128),authorName:B(e.authorName,`Review message authorName`,512),body:Ye(e.body),createdAt:et(e.createdAt,`Review message createdAt`),updatedAt:et(e.updatedAt,`Review message updatedAt`)}}function Ye(e){return B(e,`Review message body`,4e3)}function Xe(e){let t=B(e,`Review message pathname`,2e3);if(!t.startsWith(`/`))throw Error(`Review message pathname is invalid`);return t}function B(e,t,n){if(typeof e!=`string`)throw Error(`${t} is invalid`);let r=e.trim();if(!r||r.length>n)throw Error(`${t} is invalid`);return r}function Ze(e,t){if(typeof e!=`string`)return null;let n=e.trim();return n&&n.length<=t?n:null}function Qe(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function $e(e){let t=Qe(e);return t!==void 0&&t>0?t:void 0}function et(e,t){if(!Number.isSafeInteger(e)||e<0)throw Error(`${t} is invalid`);return e}function tt(e,t){return JSON.stringify([e,t])}function nt(e,t){return e.createdAt-t.createdAt||e.id.localeCompare(t.id)}function rt(e){let t=e&&typeof e==`object`&&`code`in e?String(e.code):``;if(/^INVITATION_(?:EXPIRED|NOT_FOUND|REQUIRED|REVOKED)$/u.test(t))return{type:`access-lost`,code:t===`INVITATION_EXPIRED`?`session-expired`:`membership-revoked`,recovery:`reauthenticate`,clearSnapshot:!0};let n=String(e instanceof Error||e&&typeof e==`object`&&`message`in e?e.message:e).toLowerCase();return n.includes(`expired`)||n.includes(`token`)?{type:`access-lost`,code:`session-expired`,recovery:`reauthenticate`,clearSnapshot:!0}:n.includes(`permission`)||n.includes(`forbidden`)||n.includes(`unauthorized`)?{type:`access-lost`,code:`permission-denied`,recovery:`reauthenticate`,clearSnapshot:!0}:{type:`error`,code:`transport`,recovery:`retry`,clearSnapshot:!0}}Object.freeze({field:`createdAt`,direction:`desc`,tieBreaker:`id`,tieDirection:`desc`});var V=class extends Error{code;constructor(e,t){super(t),this.code=e,this.name=`ReviewerCommentSaveError`}};function it(e,t){let n=e.replace(/\/+$/,``),r=async(e,r)=>{let i=await t();if(!i)throw new V(`AUTH_REQUIRED`,`로그인 세션이 만료되었습니다. 새로고침 후 다시 시도해 주세요.`);let a;try{a=await fetch(`${n}${e}`,{method:`POST`,headers:{authorization:`Bearer ${i}`,"content-type":`application/json`},body:JSON.stringify(r)})}catch{throw new V(`NETWORK`,`리뷰 서버에 연결하지 못했습니다. 연결을 확인하고 다시 시도해 주세요.`)}if(!a.ok)throw new V(...await ot(a))};return{createComment:({target:e,...t})=>{let{environment:n,...i}=e;return r(`/v1/comments`,{...t,target:i,...n?{environment:n}:{}})},createReply:e=>r(`/v1/replies`,e),uploadScreenshot:async({pageId:e,commentId:r,screenshot:i})=>{let a=await t();if(!a)throw new V(`AUTH_REQUIRED`,`로그인 세션이 만료되어 위치 캡처를 저장하지 못했습니다.`);let o;try{o=await fetch(`${n}/v1/comments/${encodeURIComponent(r)}/screenshot`,{method:`PUT`,headers:{authorization:`Bearer ${a}`,"content-type":i.contentType,"x-review-page-id":e,"x-review-image-width":String(i.width),"x-review-image-height":String(i.height)},body:at(i.bytes)})}catch{throw new V(`NETWORK`,`리뷰 서버에 연결하지 못해 위치 캡처를 저장하지 못했습니다.`)}if(!o.ok)throw new V(...await ot(o))}}}function at(e){let t=new Uint8Array(e.byteLength);return t.set(e),t.buffer}async function ot(e){let t=e.status>=500?`리뷰 서버가 코멘트를 저장하지 못했습니다. 잠시 후 다시 시도해 주세요.`:`코멘트를 저장하지 못했습니다.`;try{let n=await e.json();if(!n||typeof n!=`object`)return[`UNKNOWN`,t];let r=n;return[typeof r.error==`string`?r.error:`UNKNOWN`,typeof r.message==`string`&&r.message.trim()?r.message:t]}catch{return[`UNKNOWN`,t]}}var H=class extends Error{code;constructor(e,t){super(t),this.code=e,this.name=`ReviewBootstrapError`}};const U=new Map;function st(e,t={}){let n=e.trim();if(!n)throw Error(`InstantDB app ID is required`);return{async resolve(e){let r=(t.databaseFactory??we)(n),i=await dt(n,r,e.authHost,e.signal,e.styleNonce);for(;;)try{await ct(t.acceptInvitation,{refreshToken:i.refresh_token,invitationId:e.invitationId,suggestedDisplayName:wt(i.email),authHost:e.authHost,signal:e.signal,styleNonce:e.styleNonce});break}catch(t){if(!q(t,`INVITATION_EMAIL_MISMATCH`))throw t;await _t(e.authHost,e.signal,e.styleNonce),i=await ft(n,r,i,e.authHost,e.signal,e.styleNonce)}t.resolveReviewPage&&await t.resolveReviewPage({refreshToken:i.refresh_token,invitationId:e.invitationId,origin:e.location.origin,pathname:e.location.pathname,signal:e.signal});let{data:a}=await r.queryOnce({reviewers:{$:{where:{authUserId:i.id}}},reviewPages:{},invitations:{}}),o=lt(a.invitations,a.reviewers,a.reviewPages,{invitationId:e.invitationId,authUserId:i.id,email:i.email??``,location:e.location,now:Date.now()});if(!o.ok)throw new H(o.code,X(o.code));let s={authUserId:i.id,reviewerId:o.reviewer.id,projectId:o.reviewer.projectId,invitationId:e.invitationId,email:o.reviewer.email||i.email||``,displayName:o.reviewer.displayName||i.email||`Reviewer`,role:o.reviewer.role===`agency`?`agency`:`reviewer`},c=o.page.id,l=e.collaboration?.scopeId,u=(e.collaboration?.cursors===!0||e.collaboration?.chat===!0)&&t.resolveCollaborationRoom?await t.resolveCollaborationRoom({refreshToken:i.refresh_token,invitationId:e.invitationId,pageId:o.page.id,scopeId:e.collaboration.scopeId,signal:e.signal}):void 0,d=u&&t.resolveCollaborationRoom?{...u,refresh:async()=>{let n=await r.getAuth();if(!(!n?.refresh_token||l===void 0))return t.resolveCollaborationRoom?.({refreshToken:n.refresh_token,invitationId:e.invitationId,pageId:c,scopeId:l,signal:new AbortController().signal})}}:void 0,f=0;return{pageId:o.page.id,resolvePage:async n=>{let i=++f,a=()=>i!==f||n.signal.aborted,o=(e,t,n)=>({pageId:e,...n&&d?{collaborationRoomId:n.id}:{},commit:()=>{a()||(c=e,t!==void 0&&(l=t),!(!n||!d)&&(d.id=n.id,d.expiresAt=n.expiresAt))}});if(n.location.origin!==e.location.origin)throw new H(`PAGE_NOT_FOUND`,X(`PAGE_NOT_FOUND`));let{data:u}=await r.queryOnce({reviewPages:{$:{where:{projectId:s.projectId}}}});if(a())throw J();let p=Et(n.location.pathname),m=ut(u.reviewPages,s.projectId,n.location.origin,p);if(!m&&t.resolveReviewPage){let i=await r.getAuth();if(a())throw J();if(!i?.refresh_token)throw Error(`리뷰 세션이 만료되어 페이지를 등록할 수 없습니다.`);if(await t.resolveReviewPage({refreshToken:i.refresh_token,invitationId:e.invitationId,origin:n.location.origin,pathname:p,signal:n.signal}),a()||({data:u}=await r.queryOnce({reviewPages:{$:{where:{projectId:s.projectId}}}}),a()))throw J();m=ut(u.reviewPages,s.projectId,n.location.origin,p)}if(!m)throw new H(`PAGE_NOT_FOUND`,X(`PAGE_NOT_FOUND`));if(n.collaboration?.cursors!==!0&&n.collaboration?.chat!==!0||d===void 0||t.resolveCollaborationRoom===void 0||!n.collaboration)return o(m.id,n.collaboration?.scopeId);let h=await r.getAuth();if(a())throw J();if(!h?.refresh_token)throw Error(`리뷰 세션이 만료되어 실시간 협업 연결을 갱신할 수 없습니다.`);let g=await t.resolveCollaborationRoom({refreshToken:h.refresh_token,invitationId:e.invitationId,pageId:m.id,scopeId:n.collaboration.scopeId,signal:n.signal});if(a())throw J();return o(m.id,n.collaboration.scopeId,g)},session:s,repository:new Te(r,s,{...t.serviceUrl===void 0?{}:{commentTransport:it(t.serviceUrl,async()=>(await r.getAuth())?.refresh_token??null)}}),...e.collaboration?.cursors===!0||e.collaboration?.chat===!0?{collaborationRepository:new Ue(r,s,d)}:{},...(e.collaboration?.cursors===!0||e.collaboration?.chat===!0)&&d?{collaborationRoomId:d.id}:{},allowedStagingHosts:[e.location.hostname]}}}}async function ct(e,t){if(!e)return;let n={refreshToken:t.refreshToken,invitationId:t.invitationId,signal:t.signal};try{await e(n);return}catch(e){if(!q(e,`DISPLAY_NAME_REQUIRED`))throw e}let r=await gt(t.authHost,t.signal,t.suggestedDisplayName,t.styleNonce);await e({...n,displayName:r})}function lt(e,t,n,r){let i=e.find(({id:e})=>e===r.invitationId);if(!i||Dt(i.email)!==Dt(r.email))return{ok:!1,code:`INVITATION_REQUIRED`};if(i.status===`revoked`)return{ok:!1,code:`INVITATION_REVOKED`};if(i.status===`expired`||i.expiresAt<=r.now)return{ok:!1,code:`INVITATION_EXPIRED`};if(i.status!==`accepted`)return{ok:!1,code:`INVITATION_NOT_ACCEPTED`};let a=t.find(e=>e.authUserId===r.authUserId&&e.projectId===i.projectId);if(!a)return{ok:!1,code:`MEMBERSHIP_REQUIRED`};let o=Et(r.location.pathname),s=ut(n,i.projectId,r.location.origin,o);return s?{ok:!0,invitation:i,reviewer:a,page:s}:{ok:!1,code:`PAGE_NOT_FOUND`}}function ut(e,t,n,r){let i=e.filter(e=>e.projectId===t&&Et(e.pathname)===r),a=i.filter(({stagingOrigin:e})=>e===n);return(a.length>0?a:i).sort((e,t)=>(e.createdAt??2**53-1)-(t.createdAt??2**53-1)||e.id.localeCompare(t.id))[0]}async function dt(e,t,n,r,i){let a=await W(r,()=>t.getAuth());if(a)return a;let o=await pt(e,r);try{return await W(r,()=>t.getAuth())||await ht(t,n,r,i)}finally{o()}}async function ft(e,t,n,r,i,a){G(i);let o=await pt(e,i);try{let e=await W(i,()=>t.getAuth());return e&&e.id!==n.id?e:(e&&await W(i,async()=>{await t.auth.signOut()}),G(i),await ht(t,r,i,a))}finally{o()}}async function pt(e,t){G(t);let n=U.get(e);return n||(n={held:!1,waiters:[]},U.set(e,n)),n.held?new Promise((e,r)=>{let i,a=()=>{let e=n.waiters.indexOf(i);e<0||(n.waiters.splice(e,1),t.removeEventListener(`abort`,a),r(Y()))};i={signal:t,resolve:e,reject:r,abort:a},n.waiters.push(i),t.addEventListener(`abort`,a,{once:!0}),t.aborted&&a()}):(n.held=!0,mt(e,n))}function mt(e,t){let n=!1;return()=>{if(!n){for(n=!0;t.waiters.length>0;){let n=t.waiters.shift();if(n.signal.removeEventListener(`abort`,n.abort),n.signal.aborted){n.reject(Y());continue}n.resolve(mt(e,t));return}t.held=!1,U.get(e)===t&&U.delete(e)}}}async function W(e,t){G(e);let n=await t();return G(e),n}function G(e){if(e.aborted)throw Y()}function ht(e,t,n,r){try{G(n)}catch(e){return Promise.reject(e)}let i=yt(t);i.replaceChildren(bt(i,r),xt(i));let a=i.querySelector(`form`),o=i.querySelector(`h2`),s=i.querySelector(`.description`),c=a.elements.namedItem(`email`),l=a.elements.namedItem(`code`),u=i.querySelector(`.code`),d=i.querySelector(`button`),f=i.querySelector(`.error`),p=!1;return new Promise((t,r)=>{let m=!1,h=!1,g=!1,ee=()=>{n.removeEventListener(`abort`,v),i.removeEventListener(`keydown`,te),a.removeEventListener(`submit`,ne)},_=e=>{m||(m=!0,ee(),e())},v=()=>{m||(g=!0,ee(),h||_(()=>r(Y())))},te=e=>{let t=e;t.key===`Escape`?(t.preventDefault(),v()):t.key===`Tab`&&Tt(i,t)},ne=async i=>{if(i.preventDefault(),!(m||h)){h=!0,f.textContent=``,d.disabled=!0,a.setAttribute(`aria-busy`,`true`);try{if(G(n),!p){let t=c.value.trim();await W(n,async()=>{await e.auth.sendMagicCode({email:t})}),p=!0,c.readOnly=!0,u.hidden=!1,l.required=!0,o.textContent=`인증 코드`,s.textContent=`${t}로 보낸 코드를 입력하세요.`,d.textContent=`확인`,l.focus();return}await W(n,async()=>{await e.auth.signInWithMagicCode({email:c.value.trim(),code:l.value.trim()})});let r=await W(n,()=>e.getAuth());if(!r)throw Error(`인증 세션을 확인할 수 없습니다.`);_(()=>t(r))}catch(e){if(m||n.aborted||q(e,`AUTH_CANCELLED`))return;f.textContent=vt(e)}finally{h=!1,!m&&(g||n.aborted)?_(()=>r(Y())):m||(d.disabled=!1,a.removeAttribute(`aria-busy`))}}};n.addEventListener(`abort`,v,{once:!0}),i.addEventListener(`keydown`,te),a.addEventListener(`submit`,ne)})}function gt(e,t,n=``,r){if(t.aborted)return Promise.reject(Y());let i=yt(e);i.replaceChildren(bt(i,r),St(i));let a=i.querySelector(`form`),o=a.elements.namedItem(`displayName`),s=i.querySelector(`button`),c=i.querySelector(`.error`);return o.value=n.trim().slice(0,120),o.focus(),o.select(),new Promise((e,n)=>{let r=!1,l=e=>{r||(r=!0,t.removeEventListener(`abort`,u),i.removeEventListener(`keydown`,p),a.removeEventListener(`submit`,f),e())},u=()=>l(()=>n(Y())),d=()=>{let t=o.value.trim();if(!t){c.textContent=`이름을 입력해 주세요.`,o.focus();return}if(t.length>120){c.textContent=`이름은 120자 이하여야 합니다.`,o.focus();return}c.textContent=``,s.disabled=!0,a.setAttribute(`aria-busy`,`true`),l(()=>e(t))},f=e=>{e.preventDefault(),d()},p=e=>{let t=e;if(t.key===`Escape`){t.preventDefault(),u();return}if(t.key===`Tab`){Tt(i,t);return}t.key===`Enter`&&!t.isComposing&&t.target===o&&(t.preventDefault(),d())};t.addEventListener(`abort`,u,{once:!0}),i.addEventListener(`keydown`,p),a.addEventListener(`submit`,f)})}function _t(e,t,n){if(t.aborted)return Promise.reject(Y());let r=yt(e);r.replaceChildren(bt(r,n),Ct(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(Y())),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`&&Tt(r,t)};t.addEventListener(`abort`,c,{once:!0}),r.addEventListener(`keydown`,u),i.addEventListener(`submit`,l)})}function vt(e){if(e&&typeof e==`object`&&`body`in e){let t=e.body;if(t?.message)return t.message}return e instanceof Error?e.message:`로그인에 실패했습니다. 다시 시도해 주세요.`}function yt(e){return e.shadowRoot??e.attachShadow({mode:`open`})}function bt(e,t){let n=e.ownerDocument.createElement(`style`);return t&&(n.nonce=t),n.textContent=Ot,n}function xt(e){let t=e.ownerDocument,n=t.createElement(`form`);n.setAttribute(`role`,`dialog`),n.setAttribute(`aria-modal`,`true`),n.setAttribute(`aria-labelledby`,`visual-review-login-title`),n.setAttribute(`aria-describedby`,`visual-review-login-description`);let r=K(t,`p`,`01.works`,`eyebrow`),i=K(t,`h2`,`Visual Review`);i.id=`visual-review-login-title`;let a=K(t,`p`,`초대받은 이메일로 계속하세요.`,`description`);a.id=`visual-review-login-description`;let o=t.createElement(`label`);o.append(`이메일`);let s=t.createElement(`input`);s.name=`email`,s.type=`email`,s.autocomplete=`email`,s.required=!0,o.append(s);let c=t.createElement(`label`);c.className=`code`,c.hidden=!0,c.append(`인증 코드`);let l=t.createElement(`input`);l.name=`code`,l.inputMode=`numeric`,l.autocomplete=`one-time-code`,c.append(l);let u=K(t,`p`,``,`error`);u.setAttribute(`role`,`alert`);let d=K(t,`button`,`코드 받기`);return d.type=`submit`,n.append(r,i,a,o,c,u,d),n}function St(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=K(t,`p`,`01.works`,`eyebrow`),i=K(t,`h2`,`표시 이름`);i.id=`visual-review-name-title`;let a=K(t,`p`,`피드백에 이 이름이 표시됩니다.`,`description`);a.id=`visual-review-name-description`;let o=K(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=K(t,`p`,``,`error`);c.setAttribute(`role`,`alert`);let l=K(t,`button`,`리뷰 시작`);return l.type=`submit`,n.append(r,i,a,o,s,c,l),n}function Ct(e){let t=e.ownerDocument,n=t.createElement(`form`);n.setAttribute(`role`,`dialog`),n.setAttribute(`aria-modal`,`true`),n.setAttribute(`aria-labelledby`,`visual-review-account-title`),n.setAttribute(`aria-describedby`,`visual-review-account-description`);let r=K(t,`p`,`01.works`,`eyebrow`),i=K(t,`h2`,`계정 확인`);i.id=`visual-review-account-title`;let a=K(t,`p`,`이 초대는 현재 로그인한 계정과 일치하지 않습니다.`,`description`);a.id=`visual-review-account-description`;let o=K(t,`button`,`다른 계정으로 계속`);return o.type=`submit`,n.append(r,i,a,o),n}function K(e,t,n,r){let i=e.createElement(t);return i.textContent=n,r&&(i.className=r),i}function wt(e){return e?.split(`@`)[0]?.trim().slice(0,120)??``}function q(e,t){return!!(e&&typeof e==`object`&&`code`in e&&e.code===t)}function J(){return new DOMException(`Aborted`,`AbortError`)}function Y(){return new H(`AUTH_CANCELLED`,`리뷰 로그인이 취소되었습니다.`)}function Tt(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())}function Et(e){return e.replace(/\/+$/,``)||`/`}function Dt(e){return e.trim().toLowerCase()}function X(e){switch(e){case`INVITATION_REQUIRED`:return`현재 계정과 일치하는 리뷰 초대를 찾을 수 없습니다.`;case`INVITATION_NOT_ACCEPTED`:return`아직 수락되지 않은 리뷰 초대입니다.`;case`INVITATION_REVOKED`:return`취소된 리뷰 초대입니다. 계속 검토하려면 담당자에게 다시 요청하세요.`;case`INVITATION_EXPIRED`:return`만료된 리뷰 초대입니다. 담당자에게 새 링크를 요청하세요.`;case`MEMBERSHIP_REQUIRED`:return`이 프로젝트의 reviewer membership이 없습니다.`;case`PAGE_NOT_FOUND`:return`이 페이지는 아직 리뷰 대상이 아닙니다. 담당자에게 문의하세요.`}}const Ot=`
|
|
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
|
-
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
|
-
`;var Z=class extends Error{code;status;constructor(e,t,n){super(t),this.code=e,this.status=n,this.name=`HostedBootstrapError`}};function kt(e={}){return At(e)}function At(t={},n={}){let r=e(t.serviceUrl??`https://review.01.works`),i=t.fetch??globalThis.fetch,a=n.instantRuntimeFactory??st,o=n.now??Date.now;if(typeof i!=`function`)throw Error(`Visual Review bootstrap requires the Fetch API`);return{async resolve(e){let t=jt(e),n=await Pt(r,i,t,e.signal),s=o(),c=await a(n.instantAppId,{serviceUrl:r,acceptInvitation:async e=>{let t=await i(`${r}/v1/invitations/accept`,{method:`POST`,headers:{accept:`application/json`,authorization:`Bearer ${e.refreshToken}`,"content-type":`application/json`},body:JSON.stringify({invitationId:e.invitationId,...e.displayName===void 0?{}:{displayName:e.displayName}}),cache:`no-store`,credentials:`omit`,signal:e.signal}),n=await Q(t);if(!t.ok)throw new Z($(n,`error`)??`INVITATION_ACCEPT_FAILED`,$(n,`message`)??`리뷰 초대 수락에 실패했습니다.`,t.status)},resolveReviewPage:e=>Nt(r,i,e),...e.collaboration?.cursors===!0||e.collaboration?.chat===!0?{resolveCollaborationRoom:e=>Mt(r,i,e)}:{}}).resolve(e),l=c.resolvePage;if(!l)return c;let u=async t=>{await Pt(r,i,{invitationId:e.invitationId,origin:t.location.origin,pathname:t.location.pathname},t.signal),s=o()};return{...c,resolvePage:async e=>(o()-s>=3e5&&await u(e),l(e))}}}}function jt(e){return{invitationId:e.invitationId,origin:e.location.origin,pathname:e.location.pathname}}async function Mt(e,t,n){let r=await t(`${e}/v1/collaboration/room`,{method:`POST`,headers:{accept:`application/json`,authorization:`Bearer ${n.refreshToken}`,"content-type":`application/json`},body:JSON.stringify({invitationId:n.invitationId,pageId:n.pageId,scopeId:n.scopeId}),cache:`no-store`,credentials:`omit`,signal:n.signal}),i=await Q(r);if(!r.ok)throw new Z($(i,`error`)??`COLLABORATION_ROOM_FAILED`,$(i,`message`)??`실시간 협업 연결에 실패했습니다.`,r.status);if(!i||typeof i!=`object`||Array.isArray(i))throw new Z(`INVALID_COLLABORATION_ROOM`,`잘못된 협업 응답입니다.`,502);let a=i,o=$(a,`id`);if(!o||!/^vr_[A-Za-z0-9_-]{43}$/u.test(o)||!Number.isSafeInteger(a.expiresAt))throw new Z(`INVALID_COLLABORATION_ROOM`,`잘못된 협업 응답입니다.`,502);return{id:o,expiresAt:a.expiresAt}}async function Nt(e,t,n){let r=await t(`${e}/v1/pages/resolve`,{method:`POST`,headers:{accept:`application/json`,authorization:`Bearer ${n.refreshToken}`,"content-type":`application/json`},body:JSON.stringify({invitationId:n.invitationId,origin:n.origin,pathname:n.pathname}),cache:`no-store`,credentials:`omit`,signal:n.signal});if(r.ok)return;let i=await Q(r);throw new Z($(i,`error`)??`PAGE_RESOLVE_FAILED`,$(i,`message`)??`리뷰 페이지를 준비하지 못했습니다.`,r.status)}async function Pt(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 Q(i);if(!i.ok)throw new Z($(a,`error`)??`BOOTSTRAP_FAILED`,$(a,`message`)??`Visual Review bootstrap에 실패했습니다.`,i.status);return Ft(a)}function Ft(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new Z(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let t=e;if(typeof t.apiVersion==`number`&&t.apiVersion!==1)throw new Z(`UNSUPPORTED_SERVICE_VERSION`,`이 Visual Review 위젯 버전은 더 이상 지원되지 않습니다. 사이트의 @01.works/visual-review를 업데이트해 주세요.`,502);let n=$(t,`instantAppId`);if(t.apiVersion!==1||!n||!It(n))throw new Z(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);return{apiVersion:1,instantAppId:n}}async function Q(e){try{return await e.json()}catch{throw new Z(`INVALID_BOOTSTRAP_RESPONSE`,`JSON bootstrap 응답이 필요합니다.`,502)}}function $(e,t){if(!e||typeof e!=`object`||Array.isArray(e))return null;let n=e[t];return typeof n==`string`&&n.trim()?n.trim():null}function It(e){return/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e)}export{kt as createHostedReviewRuntime};
|
package/dist/install-RHVUfLTU.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
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;function o(e={}){if(e.enabled===!1||typeof window>`u`||typeof document>`u`)return n();let r=!1,i=0,a=null,o=null,m=null,h=null,_=!1,v=null,y=null,C=t(e.serviceUrl??`https://review.01.works`),D=e.activationParam??`visual-review`,A=K(window),N=x(window.location);if(!(O(C,D,N,A,e.runtime!==void 0)??S(w(C,N.origin))))return n();let P=document.baseURI,F=new WeakMap,I=new Map,L=ne(e.styleNonce,document),R=b(window.location,D),z=g(window,e=>{let t=b(e,D);if(t===R)return;let n=k(D,e.href);n&&I.set(e.origin,n),R=t,V(e)}),B=e=>{if(!(r||!h||e.target!==h)){if((e instanceof CustomEvent&&e.detail&&typeof e.detail==`object`&&`code`in e.detail?e.detail.code:void 0)===`membership-revoked`){let e=x(window.location),t=I.get(e.origin);t&&(E(C,e.origin,t),I.delete(e.origin),q(C,e.origin,A,t));return}R=b(window.location,D),G()}};return window.addEventListener(`visual-review:reauthenticate-required`,B),{ready:G(),refresh(){return r?Promise.resolve(null):(R=b(window.location,D),V(x(window.location),!0))},dispose(){r||(r=!0,i+=1,U(),h?.remove(),h=null,y=null,v?.remove(),v=null,I.clear(),z.unsubscribe(),window.removeEventListener(`visual-review:reauthenticate-required`,B))}};function V(e,t=!1){return!h||!y||y.origin!==e.origin||k(D,e.href)?G():H(h,y,e,t)}function H(t,n,r,c){let l=++i;a!==o&&a?.abort();let u=new AbortController;a=u;let d=s(e.build,r),f=(async()=>{try{let i=await M(n.resolvePage({location:r,signal:u.signal,collaboration:{cursors:e.collaboration?.cursors===!0,chat:e.collaboration?.chat===!0,scopeId:d}}),u.signal);return Q(l,u)?(i.commit?.(),t.setPage({pageId:i.pageId,collaboration:{roomId:i.collaborationRoomId,scopeId:d}},{reload:c}),t.isConnected||document.body?.append(t),t):$(l)}catch{return Q(l,u)?G():$(l)}})();return m=f,f}function U(){a?.abort(),o?.abort(),a=null,o=null}function G(){let t=++i;U(),h?.remove(),h=null,y=null,v?.remove();let n=new AbortController,r=document.createElement(`div`);r.dataset.agencyReviewRoot=``,a=n,o=n,v=r;let s=J(t,n,r);return m=s,s.then(r=>{r&&Q(t,n)&&h===r&&e.onReady?.(r)}),s}async function J(t,n,i){try{let e=document.body??await te(document,n.signal);if(!Q(t,n))return $(t);e.append(i);let r=await Y(t,n,i);return Q(t,n)?r:(i.remove(),$(t))}catch(a){if(!Q(t,n))return i.remove(),$(t);if(d(a,`AUTH_CANCELLED`))return i.remove(),v===i&&(v=null),null;let o=F.get(i)?.[3];Z(a,i);let s=X(a,F.get(i)?.[2]);if(o!==`storage`||_||!f(a)){let{renderBootstrapError:e}=await import(`./bootstrap-error-X9PqxFnS.js`);if(!Q(t,n))return i.remove(),$(t);let o=p(a)?void 0:()=>{r||v!==i||(R=b(window.location,D),G())};e(i,s.message,L,o)}else i.remove(),v===i&&(v=null);return e.onError?.(s),window.dispatchEvent(new CustomEvent(`visual-review:error`,{detail:s})),null}}async function Y(t,n,r){let i=x(window.location),a=O(C,D,i,A,e.runtime!==void 0),o=ee(I.get(i.origin)),d=S(w(C,i.origin)),f=a?.source===`url`?a:o??d??a;if(!f)return F.delete(r),r.remove(),v===r&&(v=null),null;let p=f.invitationId;F.set(r,[C,i.origin,p,f.source]),f.source===`url`?(q(C,i.origin,A),I.set(i.origin,p),T(C,i.origin,p),z.suppress(()=>j(D))):f.source===`handoff`&&I.set(i.origin,p),R=b(window.location,D);let m=await M((e.runtime??await c(C)).resolve({invitationId:p,location:i,signal:n.signal,authHost:r,styleNonce:L,collaboration:{cursors:e.collaboration?.cursors===!0,chat:e.collaboration?.chat===!0,scopeId:s(e.build,i)}}),n.signal);if(!Q(t,n))return null;E(C,i.origin,p),W(C,p,i,A),I.set(i.origin,p);let g=e.sourceContextResolver??await u(e.build,P,n.signal,L);if(!Q(t,n))return null;let{mountReviewWidget:k}=await l(n.signal);return Q(t,n)?(h=k({...m,collaboration:{cursors:e.collaboration?.cursors===!0&&!!(m.collaborationRepository&&m.collaborationRoomId),chat:e.collaboration?.chat===!0&&!!m.collaborationRepository,repository:m.collaborationRepository,roomId:m.collaborationRoomId,scopeId:s(e.build,i)},developerTools:e.developerTools?.codeContextCopy===!0||e.developerTools?.reactGrab===!0?{codeContextCopy:!0}:void 0,offset:e.offset,onNavigateToFeedback:async t=>{let n=new URL(t,window.location.href);if(n.origin!==window.location.origin)throw Error(`Visual Review route navigation must stay on the staging origin`);if(e.navigateToFeedback){await e.navigateToFeedback(n.href);return}window.location.assign(n.href)},sourceContextResolver:g,styleNonce:L,position:e.position}),y=m.resolvePage?{origin:i.origin,resolvePage:e=>m.resolvePage(e)}:null,r.remove(),v===r&&(v=null),_=!0,window.dispatchEvent(new CustomEvent(`visual-review:ready`,{detail:h})),h):null}function Z(e,t){if(!f(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),q(r,i,A,a)}function Q(e,t){return!r&&i===e&&a===t&&!t.signal.aborted}function $(e){return!r&&i!==e?m:null}}function s(e,t){return JSON.stringify([P(e).buildId,t.pathname,t.search])}async function c(e){let{createHostedReviewRuntime:t}=await import(`./hosted-runtime-D1xY3Jtw.js`);return t({serviceUrl:e})}async function l(e){return N(import(`./src-CtuDVE7s.js`),e,a,`리뷰 위젯을 불러오는 시간이 초과되었습니다. 다시 시도해 주세요.`)}async function u(e,t,n,r){let i=P(e),{createGeneratedSourceContextResolver:o}=await N(import(`./generated-source-context-D59o5CKS.js`),n,a,`리뷰 소스 정보를 불러오는 시간이 초과되었습니다. 다시 시도해 주세요.`);return o({build:i})}function d(e,t){return e instanceof Error&&`code`in e&&e.code===t}function f(e){let t=m(e);return typeof t==`string`&&/^INVITATION_(?:EXPIRED|NOT_FOUND|REVOKED)$/.test(t)}function p(e){return f(e)||m(e)===`UNSUPPORTED_SERVICE_VERSION`}function m(e){return e instanceof Error&&`code`in e?e.code:void 0}const h=new WeakMap;function g(e,t){let n=h.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),_(e)},patchedReplaceState:function(t,n,r){i.call(this,t,n,r),_(e)},popstateListener:()=>_(e),subscribers:a,suppressionDepth:0},h.set(e,n),v(t,`pushState`,n.patchedPushState),v(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),y(n.history,`pushState`,n.patchedPushState,n.originalPushStateDescriptor),y(n.history,`replaceState`,n.patchedReplaceState,n.originalReplaceStateDescriptor),h.delete(e)))}}}function _(e){let t=h.get(e);if(!t||t.suppressionDepth>0)return;let n=x(e.location);[...t.subscribers].forEach(e=>e(n))}function v(e,t,n){Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:!0,value:n})}function y(e,t,n,r){if(Object.getOwnPropertyDescriptor(e,t)?.value===n){if(r){Object.defineProperty(e,t,r);return}delete e[t]}}function b(e,t){return`${e.origin}\n${e.pathname}\n${k(t,e.href)??``}`}function x(e){return new URL(e.href)}function ee(e){return e?{invitationId:e,source:`memory`}:null}function S(e){return e?{invitationId:e,source:`handoff`}:null}function C(e,t){return JSON.stringify([e,t])}function w(e,t,n=Date.now()){let i=C(e,t),a=r.get(i);if(a){if(a.expiresAt>n)return a.invitationId;clearTimeout(a.timeout),r.delete(i)}}function T(e,t,n,a=Date.now()){D(a);let o=C(e,t),s=r.get(o);for(s&&(clearTimeout(s.timeout),r.delete(o));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 c=a+i,l=setTimeout(()=>{r.get(o)?.expiresAt===c&&r.delete(o)},i);r.set(o,{invitationId:n,expiresAt:c,timeout:l})}function E(e,t,n){let i=C(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 O(e,t,n,r,i){let a=k(t,n.href);if(a)return{invitationId:a,source:`url`};let o=G(r,J(e,n.origin));return o?{invitationId:o,source:`storage`}:i&&Y(n.hostname)?{invitationId:`local-preview`,source:`local-preview`}:null}function k(e,t){let n=new URL(t),r=A(n.hash);return(r?.get(e)??r?.get(`visual-review`)??r?.get(`agency-review`)??r?.get(`agency-review-invite`)??n.searchParams.get(e)??n.searchParams.get(`agency-review`)??n.searchParams.get(`agency-review-invite`))?.trim()||null}function A(e){let t=e.startsWith(`#`)?e.slice(1):e,n=t.startsWith(`?`)?t.slice(1):t;return n.includes(`=`)?new URLSearchParams(n):null}function j(e){let t=new URL(window.location.href),n=new Set([e,`visual-review`,`agency-review`,`agency-review-invite`]),r=!1;n.forEach(e=>{t.searchParams.has(e)&&(t.searchParams.delete(e),r=!0)});let i=A(t.hash);if(i){n.forEach(e=>{i.has(e)&&(i.delete(e),r=!0)});let e=i.toString();t.hash=e?`#${e}`:``}r&&window.history.replaceState(window.history.state,``,`${t.pathname}${t.search}${t.hash}`)}function M(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 N(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 te(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 P(e){let t=document.querySelector(`meta[name="visual-review-build"]`),n=document.querySelector(`meta[name="visual-review-commit"]`),r=U(L()),i=U(R());return{buildId:H(e?.buildId)??V(t)??r??`unversioned`,gitCommit:z(e,n,i),generatedAt:e?.generatedAt,mode:e?.mode??I(F())??(Y(window.location.hostname)?`development`:`production`)}}function F(){try{return process.env.VISUAL_REVIEW_PUBLIC_BUILD_MODE}catch{return}}function I(e){return e===`development`||e===`preview`||e===`production`?e:void 0}function L(){try{return process.env.VISUAL_REVIEW_PUBLIC_BUILD_ID}catch{return}}function R(){try{return process.env.VISUAL_REVIEW_PUBLIC_GIT_COMMIT}catch{return}}function z(e,t,n){return e?.gitCommit===null?null:typeof e?.gitCommit==`string`?B(e.gitCommit):B(t?.content)??B(n)}function B(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 V(e){return U(e?.content)}function H(e){let t=typeof e==`string`?e.trim():``;return t.length>0?t:null}function U(e){return typeof e==`string`&&e.length>0?e:null}function W(e,t,n,r){try{r?.setItem(J(e,n.origin),t)}catch{}}function G(e,t){try{return e?.getItem(t)??null}catch{return null}}function K(e){try{return e.localStorage}catch{return null}}function q(e,t,n,r){try{if(!n)return;let i=J(e,t),a=n.getItem(i);if(a===null||r!==void 0&&a!==r)return;n.removeItem(i)}catch{}}function J(e,t){return`visual-review:${e}:${t}:invitation`}function Y(e){return e===`localhost`||e===`127.0.0.1`||e.endsWith(`.test`)||e.endsWith(`.local`)}function X(e,t){let n=e instanceof Error?e.message:String(e),r=Error(Z(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:Z(n,t)}),typeof i==`number`&&Number.isFinite(i)&&Object.defineProperty(r,"status",{configurable:!0,enumerable:!0,value:i})}return r}function Z(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 ne(e,t){if(e!==void 0)return $(e);let n=t.currentScript,r=n?Q(n):void 0;if(r)return r;try{for(let e of t.querySelectorAll(`script[nonce],style[nonce]`)){let t=Q(e);if(t)return t}}catch{}}function Q(e){let t=`nonce`in e?e.nonce:void 0;return typeof t==`string`?$(t):void 0}function $(e){return e.trim()||void 0}export{e as n,t as r,o as t};
|