@01.works/visual-review 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -48
- package/dist/generated-source-context-Cx2Sm1Z8.js +1 -0
- package/dist/hosted-runtime-FHlAhXOq.js +13 -0
- package/dist/index.d.ts +0 -11
- package/dist/index.js +1 -1
- package/dist/install-COwLLFZK.js +1 -0
- package/dist/next-config.d.ts +4 -14
- package/dist/next-config.js +25 -211
- package/dist/react.js +1 -1
- package/dist/{source-instrumentation-transform-Cx8SIIWh.js → source-instrumentation-transform-qmiVeKpI.js} +3 -15
- package/dist/source-loader.js +2 -33
- package/dist/{src-DX1bVKG6.js → src-reBEPAL1.js} +3 -3
- package/dist/vite.d.ts +5 -13
- package/dist/vite.js +7 -89
- package/package.json +1 -1
- package/dist/generated-frame-CkUKpj5U.js +0 -1
- package/dist/generated-source-context-C6kocvPg.js +0 -1
- package/dist/hosted-runtime-1mdUKkOV.js +0 -13
- package/dist/install-C-4G_vDp.js +0 -1
- package/dist/source-index-o5fwJzns.js +0 -1400
- package/dist/{build-identity-CgoY3-jy.js → build-identity-DebYe8Qe.js} +1 -1
package/README.md
CHANGED
|
@@ -14,8 +14,8 @@ Node `24.18.0`에서 실행합니다.
|
|
|
14
14
|
### Build adapter 의존성
|
|
15
15
|
|
|
16
16
|
`@babel/core`, `@babel/parser`, JSX/TypeScript transform plugin과 `magic-string`은
|
|
17
|
-
Vite/Next
|
|
18
|
-
|
|
17
|
+
Vite/Next build에서 JSX에 source 위치를 심는 Node 전용 build adapter
|
|
18
|
+
의존성입니다. 브라우저 entry는 이 패키지들을 import하지 않으며,
|
|
19
19
|
release verifier가 dormant root의 정적 import 경계와 6 KB gzip 상한을 별도로
|
|
20
20
|
검사합니다.
|
|
21
21
|
|
|
@@ -23,7 +23,7 @@ manifest의 caret 범위는 지원 가능한 adapter patch 범위를 의도적
|
|
|
23
23
|
실제 설치 버전은 consumer lockfile이 고정하므로 애플리케이션의 lockfile을 함께
|
|
24
24
|
커밋해야 합니다. Release 검증은 빈 consumer 프로젝트에 tarball만 설치한 뒤 이
|
|
25
25
|
범위에 맞는 resolved dependency tree와 `npm audit --omit=dev
|
|
26
|
-
--audit-level=moderate`를 검사합니다. 이 의존성이나
|
|
26
|
+
--audit-level=moderate`를 검사합니다. 이 의존성이나 InstantDB
|
|
27
27
|
credential을 browser 설정으로 전달하지 않습니다.
|
|
28
28
|
|
|
29
29
|
`@instantdb/core`는 hosted realtime/auth 경계에 필요한 직접 runtime dependency이며
|
|
@@ -42,8 +42,7 @@ Vite config에 build identity plugin을 한 번 추가하면 CI deployment metad
|
|
|
42
42
|
clean Git HEAD가 widget의 source provenance에 자동으로 기록됩니다. 브라우저에
|
|
43
43
|
주입하는 build identity는 공개 build ID와 full commit SHA 두 값뿐입니다. provider deployment ID가 없는
|
|
44
44
|
same-commit rebuild에는 `git:<sha>:deploy:<public-random-variant>` 형식의 서로 다른
|
|
45
|
-
build ID를 발급합니다.
|
|
46
|
-
독립적으로 생성되므로 token 회전 시에도 현재 build identity가 바뀌지 않습니다.
|
|
45
|
+
build ID를 발급합니다.
|
|
47
46
|
|
|
48
47
|
```ts
|
|
49
48
|
// vite.config.ts
|
|
@@ -58,45 +57,11 @@ export default defineConfig({
|
|
|
58
57
|
|
|
59
58
|
Vite와 Next.js build는 upload token이 없어도 JSX element에
|
|
60
59
|
`data-review-source="src/App.tsx:42:7"`를 기본으로 심습니다. 리뷰어의 코멘트는 그대로
|
|
61
|
-
파일 위치를 가지고
|
|
62
|
-
|
|
63
|
-
source 위치가 남습니다. 공개 산출물에 marker salt와 upload token은 넣지 않습니다.
|
|
64
|
-
|
|
65
|
-
`embedSourcePath: false`는 이 경로를 감추고 per-build HMAC opaque marker와
|
|
66
|
-
generated-asset locator index를 대신 씁니다. 이 형태는 owner-only symbolication을
|
|
67
|
-
거쳐야 파일 위치가 되고, index를 쓰는 production build에서만 동작합니다. 파일 경로를
|
|
68
|
-
읽히면 안 되는 리뷰어에게만 쓰세요.
|
|
69
|
-
|
|
70
|
-
`sourceMetadata: false`는 두 형태를 모두 끄고 공개 build identity만 남깁니다. 이
|
|
71
|
-
설정들은 private source-map upload를 활성화하거나 비활성화하지 않으며 upload는 아래
|
|
72
|
-
`sourceMaps`/token gate를 그대로 따릅니다.
|
|
73
|
-
|
|
74
|
-
CI에 project-scoped `VISUAL_REVIEW_SOURCE_MAP_TOKEN`을 설정하면 같은 plugin이
|
|
75
|
-
browser build에 hidden source map을 만들고 기본 서비스
|
|
76
|
-
`https://visual-review.01.works`로 비공개 업로드합니다. 업로드가 완료되거나 실패한
|
|
77
|
-
뒤에는 배포 `outDir`의 `.map`을 모두 제거하며, 업로드 또는 정리에 실패하면 build도
|
|
78
|
-
실패합니다. token은 Vite config의 Node process에서만 읽고 browser `define`이나
|
|
79
|
-
runtime bundle에 넣지 않습니다. `inline` source map은 활성 상태에서 거부합니다.
|
|
80
|
-
|
|
81
|
-
Vite와 Next.js adapter, private upload API, 저장소, retention 경계는 package
|
|
82
|
-
verifier에서 함께 검증합니다. 다만 01.works production backend는 전용 PostgreSQL
|
|
83
|
-
runtime role, private Blob/OIDC gate가 provision되기 전까지
|
|
84
|
-
`VISUAL_REVIEW_SOURCE_MAP_BACKEND=disabled`로 명시적으로 닫아 둡니다. 해당 capability가
|
|
85
|
-
활성화되기 전에는 production consumer에 source-map token을 설정하지 마세요.
|
|
86
|
-
|
|
87
|
-
local/self-hosted upload API를 검증할 때만 Node config에서 service origin을 바꿉니다.
|
|
60
|
+
파일 위치를 가지고 저장됩니다. Vite dev server(`vite dev`)도 같은 값을 내보내므로
|
|
61
|
+
staging preview가 dev server여도 source 위치가 남습니다.
|
|
88
62
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
sourceMaps: {
|
|
92
|
-
serviceUrl: 'http://localhost:8787',
|
|
93
|
-
},
|
|
94
|
-
})
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
`sourceMaps: false`는 CI environment에 token이 있어도 이 build의 업로드를 명시적으로
|
|
98
|
-
끕니다. `sourceMaps: true`는 업로드를 명시적으로 요구하므로 token이 없으면 fail
|
|
99
|
-
closed합니다.
|
|
63
|
+
`sourceMetadata: false`는 계측을 끄고 공개 build identity만 남깁니다. 그 build의
|
|
64
|
+
capture에는 파일 위치가 남지 않습니다.
|
|
100
65
|
|
|
101
66
|
```tsx
|
|
102
67
|
import { VisualReview } from '@01.works/visual-review/react';
|
|
@@ -118,12 +83,10 @@ export function App() {
|
|
|
118
83
|
포함하므로 server layout에서 import할 수 있습니다. `next.config` wrapper는
|
|
119
84
|
object와 sync/async config factory를 보존합니다. 기존 webpack/Turbopack 설정을
|
|
120
85
|
유지하면서 실제 CLI가 선택한 bundler에 source metadata loader를 추가하고,
|
|
121
|
-
build identity로는 같은 공개 provenance 두 값만 추가합니다.
|
|
122
|
-
Vite와 같은 `embedSourcePath` 기본값을 따릅니다.
|
|
86
|
+
build identity로는 같은 공개 provenance 두 값만 추가합니다.
|
|
123
87
|
Next가 config를 다시 평가하는 compiler worker에는 main build가 확정한 공개
|
|
124
88
|
`buildId`/`gitCommit`만 process-local record로 전달합니다. 이 record는 엄격히
|
|
125
|
-
검증되며
|
|
126
|
-
않습니다.
|
|
89
|
+
검증되며 다른 값을 포함하거나 산출물에 내보내지 않습니다.
|
|
127
90
|
|
|
128
91
|
```ts
|
|
129
92
|
// next.config.mjs
|
|
@@ -171,11 +134,48 @@ dirty worktree, Git 미설치, 유효하지 않은 SHA에서는 commit을 추측
|
|
|
171
134
|
`visualReview({ buildId, gitCommit })` 또는
|
|
172
135
|
`withVisualReview(config, { buildId, gitCommit })`을 사용합니다. `gitCommit: null`은
|
|
173
136
|
자동 탐색을 명시적으로 끕니다. 명시한 `buildId`는 서로 다른 deployment output에
|
|
174
|
-
|
|
137
|
+
배포마다 고유해야 합니다.
|
|
175
138
|
commit은 full 40/64자리 SHA만 허용합니다.
|
|
176
139
|
build adapter는 staging/production mode를 추측하지 않으므로 review deployment의
|
|
177
140
|
component에는 `build={{ mode: 'preview' }}`를 명시합니다.
|
|
178
141
|
|
|
142
|
+
## Staging/Preview build
|
|
143
|
+
|
|
144
|
+
overlay flag는 browser에 공개되는 load gate입니다. production 환경에는 설정하지
|
|
145
|
+
말고, 실제로 review할 Preview/Staging build에만 정확히 `true`로 넣습니다.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Vite: .env.staging의 VITE_VISUAL_REVIEW_ENABLED=true를 build에 반영
|
|
149
|
+
npm run build -- --mode staging
|
|
150
|
+
npm run preview
|
|
151
|
+
|
|
152
|
+
# Next.js: hosting provider의 Preview/Staging environment에만 설정
|
|
153
|
+
NEXT_PUBLIC_VISUAL_REVIEW_ENABLED=true npm run build
|
|
154
|
+
npm run start
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Vite `--mode staging`은 staging 값으로 **build**한 뒤 `vite preview`로 그 결과를
|
|
158
|
+
확인하기 위한 예시입니다. `vite preview` 실행 시점에 env를 추가해도 이미 생성된
|
|
159
|
+
bundle은 바뀌지 않습니다. Next.js의 `NEXT_PUBLIC_*` 값도 build 시점에 고정되므로
|
|
160
|
+
Preview deployment별로 별도 build해야 합니다. 이 공개 flag만으로 데이터 접근
|
|
161
|
+
권한이 생기지는 않습니다.
|
|
162
|
+
|
|
163
|
+
## 실시간 협업
|
|
164
|
+
|
|
165
|
+
`collaboration`의 두 기능은 기본적으로 꺼져 있습니다.
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
<VisualReview enabled collaboration={{ cursors: true, chat: true }} />
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`cursors`는 저장하지 않는 presence만 사용하고, `chat` 메시지는 현재 review page에
|
|
172
|
+
별도 entity로 저장합니다. `chat`을 켜면 일반 입력 요소 밖에서 `/`를 눌러 바로
|
|
173
|
+
채팅을 열 수 있으며, 각 메시지에는 전송 당시의 canonical pathname이 불변
|
|
174
|
+
컨텍스트로 함께 저장되고 채팅에 표시됩니다. 따라서 커서만 켜도 채팅 기록을 읽거나
|
|
175
|
+
쓰지 않으며, 채팅만 켜면 cursor room에 참여하지 않습니다. 둘 모두 유효한
|
|
176
|
+
invitation과 같은 deny-by-default project 권한을 전제로 하며 browser flag 자체는
|
|
177
|
+
권한을 부여하지 않습니다.
|
|
178
|
+
|
|
179
179
|
## Activation
|
|
180
180
|
|
|
181
181
|
`enabled`가 생략됐거나 정확히 `true`가 아니면 DOM, storage, auth, dynamic runtime
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){return{unavailableProvenance:()=>n(e,`unavailable`),async resolve(i,a){let o=a?.signal;r(o);let s=i.closest(`[data-review-source]`),c=s?t(s):null;return r(o),{source:c,componentStack:c?[c]:[],provenance:n(e,c?`fallback`:`unavailable`)}}}}function t(e){let t=e.getAttribute(`data-review-source`)?.trim();if(!t)return null;let n=/^(.*?)(?::(\d+))?(?::(\d+))?$/u.exec(t),r=n?.[1]?.trim();return r?{filePath:r,lineNumber:n?.[2]?Number(n[2]):null,columnNumber:n?.[3]?Number(n[3]):null,componentName:e.getAttribute(`data-review-component`)?.trim()||null,origin:`app`}:null}function n(e,t){return{provider:`embedded`,build:e.build,resolvedAt:e.now?.()??Date.now(),sourceMapStatus:t}}function r(e){if(e?.aborted)throw e.reason??Error(`Source context resolution aborted`)}export{e as createGeneratedSourceContextResolver};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import{r as e}from"./install-COwLLFZK.js";import{i as t,init as n}from"@instantdb/core";const r=t.schema({entities:{$users:t.entity({email:t.string().unique().indexed()}),projects:t.entity({name:t.string(),slug:t.string().unique().indexed(),allowedOrigin:t.string().optional(),createdAt:t.number().indexed(),updatedAt:t.number()}),reviewPages:t.entity({projectId:t.string().indexed(),autoRegistrationSlot:t.string().unique().indexed().optional(),name:t.string(),pathname:t.string(),stagingOrigin:t.string(),createdAt:t.number().indexed(),updatedAt:t.number()}),reviewPageBuilds:t.entity({projectId:t.string().indexed(),pageId:t.string().unique().indexed(),buildId:t.string(),gitCommit:t.string().optional(),generatedAt:t.number().optional(),mode:t.string(),updatedAt:t.number().indexed()}),reviewers:t.entity({authUserId:t.string().indexed(),projectId:t.string().indexed(),email:t.string().indexed(),displayName:t.string(),role:t.string(),createdAt:t.number()}),reviewerIdentityClaims:t.entity({reviewerId:t.string().unique().indexed(),projectId:t.string().indexed(),authUserId:t.string().indexed(),email:t.string(),displayName:t.string(),role:t.string(),createdAt:t.number()}),invitations:t.entity({projectId:t.string().indexed(),reviewerId:t.string().indexed().optional(),email:t.string().indexed(),status:t.string().indexed(),claimRevision:t.number(),claimedByAuthUserId:t.string().indexed().optional(),expiresAt:t.number().indexed(),createdAt:t.number(),acceptedAt:t.number().optional(),revokedAt:t.number().optional()}),comments:t.entity({pageId:t.string().indexed(),authorId:t.string().indexed(),authorAuthUserId:t.string().indexed(),authorName:t.string(),body:t.string(),bodyContentMarker:t.string().optional(),anchorSelector:t.string().optional(),anchorOffsetX:t.number().optional(),anchorOffsetY:t.number().optional(),anchorViewportX:t.number().optional(),anchorViewportY:t.number().optional(),targetKind:t.string().optional(),targetPageX:t.number().optional(),targetPageY:t.number().optional(),targetWidth:t.number().optional(),targetHeight:t.number().optional(),targetViewportX:t.number().optional(),targetViewportY:t.number().optional(),targetViewportWidth:t.number().optional(),targetViewportHeight:t.number().optional(),targetPageOrigin:t.string().optional(),targetPagePathname:t.string().optional(),targetPageSearch:t.string().optional(),targetPageHash:t.string().optional(),targetPageTitle:t.string().optional(),targetCapturedAt:t.number().optional(),status:t.string().indexed(),workflowRevision:t.number(),createdAt:t.number().indexed(),updatedAt:t.number().indexed(),resolvedAt:t.number().optional(),resolvedById:t.string().optional()}),commentTargetElements:t.entity({commentId:t.string().indexed(),slotKey:t.string().unique().indexed().optional(),ordinal:t.number(),selector:t.string(),tagName:t.string(),textPreview:t.string(),pageX:t.number(),pageY:t.number(),width:t.number(),height:t.number(),viewportX:t.number(),viewportY:t.number(),viewportWidth:t.number(),viewportHeight:t.number(),authorAuthUserId:t.string().indexed()}),commentScopeEntries:t.entity({commentId:t.string().indexed(),slotKey:t.string().unique().indexed().optional(),ordinal:t.number(),key:t.string(),valueType:t.string(),stringValue:t.string().optional(),numberValue:t.number().optional(),booleanValue:t.boolean().optional(),authorAuthUserId:t.string().indexed()}),commentSourceContexts:t.entity({commentId:t.string().unique().indexed(),pageId:t.string().indexed(),authorAuthUserId:t.string().indexed(),contextJson:t.string(),createdAt:t.number().indexed()}),replies:t.entity({pageId:t.string().indexed(),commentId:t.string().indexed(),authorId:t.string().indexed(),authorAuthUserId:t.string().indexed(),authorName:t.string(),body:t.string(),bodyContentMarker:t.string().optional(),createdAt:t.number().indexed(),updatedAt:t.number().indexed()}),reviewMessages:t.entity({pageId:t.string().indexed(),pathname:t.string().indexed(),authorId:t.string().indexed(),authorAuthUserId:t.string().indexed(),authorName:t.string(),body:t.string(),bodyContentMarker:t.string(),createdAt:t.number().indexed(),updatedAt:t.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`}},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:`one`,label:`activeReviewInvitation`}},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`}},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:t.entity({reviewerId:t.string(),displayName:t.string(),color:t.string(),active:t.boolean(),cursorX:t.number().optional(),cursorY:t.number().optional(),viewportWidth:t.number().optional(),viewportHeight:t.number().optional()})}}}),i=new Uint8Array([107,167,184,17,157,173,17,209,128,180,0,192,79,212,48,200]);function a(e,t,n){return l(e,n),`${e}:${t}:${n}`}async function o(e,t,n){return c(a(e,t,n))}async function s(e){if(!e.trim())throw Error(`Comment ID is required for source context`);return c(`${e}:source-context`)}async function c(e){let t=globalThis.crypto?.subtle;if(!t)throw Error(`Web Crypto is required to persist review capture context`);let n=new TextEncoder().encode(`01works.visual-review/v1/${e}`),r=new Uint8Array(i.length+n.length);r.set(i),r.set(n,i.length);let a=new Uint8Array(await t.digest(`SHA-1`,r)).slice(0,16);a[6]=a[6]&15|80,a[8]=a[8]&63|128;let o=[...a].map(e=>e.toString(16).padStart(2,`0`)).join(``);return`${o.slice(0,8)}-${o.slice(8,12)}-${o.slice(12,16)}-${o.slice(16,20)}-${o.slice(20)}`}function l(e,t){if(!e.trim())throw Error(`Comment ID is required for capture context`);if(!Number.isSafeInteger(t)||t<0)throw Error(`Capture child ordinal must be a non-negative integer`)}const u=131072,d=32768,f=4096,p=1e3;var m=class extends Error{constructor(e,t){super(e,t),this.name=`InstantCaptureIntegrityError`}};function h(e){let t=ne(e,`comment target`,!0),n={kind:t.kind,anchor:ce(t.anchor),region:de(t.region),elements:t.elements.map(e=>({selector:e.selector,tagName:e.tagName,textPreview:e.textPreview,rect:de(e.rect),source:null,componentStack:[]})),pageUrl:t.pageUrl,pageTitle:t.pageTitle,capturedAt:t.capturedAt},r={elements:t.elements.map(e=>({selector:e.selector,source:e.source?fe(e.source):null,componentStack:e.componentStack.map(fe)})),...t.sourceProvenance?{sourceProvenance:pe(t.sourceProvenance)}:{}};A(n,u,`Public target`);let i=be(r,65536,`Source context`);return{publicTarget:n,commentFields:y(n),targetElements:n.elements.map(b),sourceContextJson:i}}function g(e,t,n){let r=T(e,n),i={selector:E(r.anchorSelector,`${n} anchor selector`,f),offsetX:k(r.anchorOffsetX,`${n} anchor offsetX`),offsetY:k(r.anchorOffsetY,`${n} anchor offsetY`),viewportX:k(r.anchorViewportX,`${n} anchor viewportX`),viewportY:k(r.anchorViewportY,`${n} anchor viewportY`)},a=ee({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:ue({origin:D(r.targetPageOrigin,`${n} page origin`,1024),pathname:D(r.targetPagePathname,`${n} page pathname`,4096),search:E(r.targetPageSearch,`${n} page search`,1024),hash:E(r.targetPageHash,`${n} page hash`,1024)}),pageTitle:r.targetPageTitle,capturedAt:r.targetCapturedAt},n),o=x(t,`${n} elements`),s=a;for(let e of o)try{s=ee({...a,elements:[...s.elements,e]},n)}catch{}return s}function _(e){let t=te(e);return t?Object.entries(t).map(([e,t],n)=>typeof t==`string`?{ordinal:n,key:e,valueType:`string`,stringValue:t}:typeof t==`number`?{ordinal:n,key:e,valueType:`number`,numberValue:t}:typeof t==`boolean`?{ordinal:n,key:e,valueType:`boolean`,booleanValue:t}:{ordinal:n,key:e,valueType:`null`}):[]}function v(e){if(!Array.isArray(e)||e.length===0)return;let t=[...e].map(e=>{try{let t=T(e,`Comment scope entry`);return{row:t,ordinal:ge(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=D(e.key,`Comment scope key`,256);if(Object.hasOwn(n,t))continue;switch(e.valueType){case`string`:n[t]=E(e.stringValue,`Comment scope ${t}`,p);break;case`number`:n[t]=O(e.numberValue,`Comment scope ${t}`);break;case`boolean`:if(typeof e.booleanValue!=`boolean`)throw j(`Comment scope ${t} is invalid`);n[t]=e.booleanValue;break;case`null`:n[t]=null;break;default:throw j(`Comment scope ${t} is invalid`)}}catch{}}if(Object.keys(n).length!==0)try{return A(n,d,`Comment scope`),n}catch{return}}function y(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:le(t.pathname),targetPageSearch:t.search.slice(1),targetPageHash:t.hash.slice(1),targetPageTitle:e.pageTitle,targetCapturedAt:e.capturedAt}}function b(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 x(e,t){if(!Array.isArray(e)||e.length===0)return[];let n=[...e].map(e=>{try{let n=T(e,t);return{row:n,ordinal:ge(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(re({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 ee(e,t){return ne(e,t,!1)}function S(e,t=`Comment body`){return D(e,t,2e4)}function C(e,t=`Comment body`){let n=S(e,t);for(let e of n)if(e.trim())return e;throw j(`${t} is empty`)}function te(e){if(e==null)return;let t=T(e,`Comment scope`);if(Object.keys(t).length>20)throw j(`Comment scope has too many fields`);let n={};for(let[e,r]of Object.entries(t)){if(!e.trim()||e.length>256)throw j(`Comment scope has an invalid key`);if(typeof r==`string`){if(r.length>p)throw j(`Comment scope ${e} is too long`);n[e]=r}else if(typeof r==`number`)n[e]=O(r,`Comment scope ${e}`);else if(typeof r==`boolean`||r===null)n[e]=r;else throw j(`Comment scope ${e} is invalid`)}return A(n,d,`Comment scope`),n}function ne(e,t,n){let r=w(e,t,[`kind`,`anchor`,`region`,`elements`,`pageUrl`,`pageTitle`,`capturedAt`,`sourceProvenance`],[`sourceProvenance`]),i=r.kind;if(i!==`element`&&i!==`region`&&i!==`viewport`)throw j(`${t} kind is invalid`);let a=me(r.elements,`${t} elements`);if(a.length>8)throw j(`${t} contains too many elements`);if(!n&&r.sourceProvenance!=null)throw j(`${t} exposes source provenance`);let o=D(r.pageUrl,`${t} pageUrl`,8192);ye(o,`${t} pageUrl`);let s={kind:i,anchor:ie(r.anchor,`${t} anchor`),region:ae(r.region,`${t} region`),elements:a.map((e,r)=>re(e,`${t} elements[${r}]`,n)),pageUrl:o,pageTitle:E(r.pageTitle,`${t} pageTitle`,p),capturedAt:_e(r.capturedAt,`${t} capturedAt`),...n&&r.sourceProvenance!=null?{sourceProvenance:se(r.sourceProvenance,`${t} provenance`)}:{}};return A(s,n?196608:u,t),s}function re(e,t,n){let r=w(e,t,[`selector`,`tagName`,`textPreview`,`rect`,`source`,`componentStack`]),i=me(r.componentStack,`${t} componentStack`);if(i.length>32)throw j(`${t} componentStack is too large`);if(!n&&(r.source!=null||i.length>0))throw j(`${t} exposes source metadata`);return{selector:D(r.selector,`${t} selector`,f),tagName:D(r.tagName,`${t} tagName`,128),textPreview:E(r.textPreview,`${t} textPreview`,p),rect:ae(r.rect,`${t} rect`),source:n&&r.source!=null?oe(r.source,`${t} source`):null,componentStack:n?i.map((e,n)=>oe(e,`${t} stack[${n}]`)):[]}}function ie(e,t){let n=w(e,t,[`selector`,`offsetX`,`offsetY`,`viewportX`,`viewportY`]);return{selector:E(n.selector,`${t} selector`,f),offsetX:k(n.offsetX,`${t} offsetX`),offsetY:k(n.offsetY,`${t} offsetY`),viewportX:k(n.viewportX,`${t} viewportX`),viewportY:k(n.viewportY,`${t} viewportY`)}}function ae(e,t){let n=w(e,t,[`pageX`,`pageY`,`width`,`height`,`viewportX`,`viewportY`,`viewportWidth`,`viewportHeight`]),r={pageX:O(n.pageX,`${t} pageX`),pageY:O(n.pageY,`${t} pageY`),width:O(n.width,`${t} width`),height:O(n.height,`${t} height`),viewportX:O(n.viewportX,`${t} viewportX`),viewportY:O(n.viewportY,`${t} viewportY`),viewportWidth:O(n.viewportWidth,`${t} viewportWidth`),viewportHeight:O(n.viewportHeight,`${t} viewportHeight`)};if(r.width<0||r.height<0||r.viewportWidth<0||r.viewportHeight<0)throw j(`${t} has a negative dimension`);return r}function oe(e,t){let n=w(e,t,[`filePath`,`lineNumber`,`columnNumber`,`componentName`,`origin`,`generated`],[`generated`]),r=n.origin;if(r!==`app`&&r!==`shared-ui`&&r!==`dependency`&&r!==`generated`&&r!==`unknown`)throw j(`${t} origin is invalid`);return{filePath:D(n.filePath,`${t} filePath`,f),lineNumber:ve(n.lineNumber,`${t} lineNumber`),columnNumber:ve(n.columnNumber,`${t} columnNumber`),componentName:n.componentName==null?null:D(n.componentName,`${t} componentName`,512),origin:r}}function se(e,t){let n=w(e,t,[`provider`,`build`,`resolvedAt`,`sourceMapStatus`]),r=n.provider;if(r!==`react-grab`&&r!==`embedded`&&r!==`custom`)throw j(`${t} provider is invalid`);let i=n.sourceMapStatus;if(i!==`resolved`&&i!==`fallback`&&i!==`unavailable`)throw j(`${t} sourceMapStatus is invalid`);let a=w(n.build,`${t} build`,[`buildId`,`gitCommit`,`generatedAt`,`mode`],[`generatedAt`]),o=a.mode;if(o!==`development`&&o!==`preview`&&o!==`production`)throw j(`${t} build mode is invalid`);return{provider:r,build:{buildId:D(a.buildId,`${t} buildId`,512),gitCommit:a.gitCommit==null?null:D(a.gitCommit,`${t} gitCommit`,256),...a.generatedAt==null?{}:{generatedAt:_e(a.generatedAt,`${t} generatedAt`)},mode:o},resolvedAt:_e(n.resolvedAt,`${t} resolvedAt`),sourceMapStatus:i}}function ce(e){return{...e}}function le(e){return e.replace(/\/+$/,``)||`/`}function ue(e){return`${e.origin}${e.pathname}${e.search?`?${e.search}`:``}${e.hash?`#${e.hash}`:``}`}function de(e){return{...e}}function fe(e){return{...e}}function pe(e){return{...e,build:{...e.build}}}function w(e,t,n,r=[]){let i=T(e,t);if(Object.keys(i).some(e=>!n.includes(e)))throw j(`${t} contains unknown fields`);for(let e of n)if(!r.includes(e)&&!Object.hasOwn(i,e))throw j(`${t} is missing ${e}`);return i}function T(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw j(`${t} is missing or invalid`);return e}function me(e,t){if(!Array.isArray(e))throw j(`${t} is missing or invalid`);return e}function E(e,t,n){if(typeof e!=`string`||he(e,n)>n)throw j(`${t} is invalid or too long`);return e}function he(e,t){let n=0;for(let r of e)if(n+=1,n>t)return n;return n}function D(e,t,n){let r=E(e,t,n);if(!r.trim())throw j(`${t} is empty`);return r}function O(e,t){if(typeof e!=`number`||!Number.isFinite(e))throw j(`${t} is invalid`);return e}function ge(e,t,n,r){if(typeof e!=`number`||!Number.isSafeInteger(e)||e<n||e>r)throw j(`${t} is invalid`);return e}function k(e,t){let n=O(e,t);if(n<0||n>1)throw j(`${t} is outside 0..1`);return n}function _e(e,t){if(typeof e!=`number`||!Number.isSafeInteger(e)||e<0||e>0xe677d21fdbff)throw j(`${t} is invalid`);return e}function ve(e,t){if(e===null)return null;if(typeof e!=`number`||!Number.isSafeInteger(e)||e<0)throw j(`${t} is invalid`);return e}function ye(e,t){let n;try{n=new URL(e)}catch(e){throw j(`${t} is invalid`,e)}if(n.protocol!==`http:`&&n.protocol!==`https:`)throw j(`${t} must use HTTP or HTTPS`)}function A(e,t,n){be(e,t,n)}function be(e,t,n){let r;try{r=JSON.stringify(e)}catch(e){throw j(`${n} cannot be serialized`,e)}if(r.length>t)throw j(`${n} is too large`);return r}function j(e,t){return new m(e,{cause:t instanceof Error?t:void 0})}function xe(e){if(!e.trim())throw Error(`InstantDB app ID is required`);return n({appId:e,schema:r})}var Se=class{db;session;#e;constructor(e,t,n={}){this.db=e,this.session=t,this.#e=n.commentTransport}async getSnapshot(e){let{data:t}=await this.db.queryOnce(Ce(e,this.session.authUserId));return we(e,t.comments,t.replies)}subscribe(e,t,n){return this.db.subscribeQuery(Ce(e,this.session.authUserId),r=>{if(r.error){n?.(je(r.error));return}if(r.data)try{t(we(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=S(n.body).trim(),i=C(r),c=F(n.createdAt,`Comment createdAt`),l=F(n.updatedAt,`Comment updatedAt`);if(l<c)throw new m(`Comment updatedAt precedes createdAt`);if(n.createdAt!==n.updatedAt)throw new m(`Comment edits must go through the review service`);if(n.status!==`open`||n.resolvedAt!==void 0||n.resolvedById!==void 0)throw new m(`New comments must start open`);let u=h(n.target);if(!Oe(n.anchor,u.publicTarget.anchor))throw new m(`Comment anchor does not match its target`);if(this.#e){await this.#e.createComment({pageId:e,commentId:n.id,body:r,target:n.target,...n.scope===void 0?{}:{scope:n.scope}});return}let d=_(n.scope),f=await Promise.all(u.targetElements.map(async e=>({element:e,id:await o(n.id,`element`,e.ordinal),slotKey:a(n.id,`element`,e.ordinal)}))),p=await Promise.all(d.map(async e=>({entry:e,id:await o(n.id,`scope`,e.ordinal),slotKey:a(n.id,`scope`,e.ordinal)}))),g=await s(n.id);await this.db.transact([this.db.tx.comments[n.id].update({pageId:e,authorId:this.session.reviewerId,authorAuthUserId:this.session.authUserId,authorName:this.session.displayName,body:r,bodyContentMarker:i,...u.commentFields,status:n.status,workflowRevision:0,createdAt:c,updatedAt:l}).link({page:e,author:this.session.reviewerId}),...f.map(({element:e,id:t,slotKey:r})=>this.db.tx.commentTargetElements[t].update({commentId:n.id,slotKey:r,authorAuthUserId:this.session.authUserId,...e}).link({comment:n.id})),...p.map(({entry:e,id:t,slotKey:r})=>this.db.tx.commentScopeEntries[t].update({commentId:n.id,slotKey:r,authorAuthUserId:this.session.authUserId,...e}).link({comment:n.id})),this.db.tx.commentSourceContexts[g].update({commentId:n.id,pageId:e,authorAuthUserId:this.session.authUserId,contextJson:u.sourceContextJson,createdAt:c}).link({comment:n.id,page:e,author:this.session.reviewerId})]);return}case`comment.delete`:throw new m(`Comment deletion must go through the review service`);case`reply.upsert`:{let n=t.reply;if(n.authorId!==this.session.reviewerId)throw Error(`Reply author mismatch`);let r=S(n.body,`Reply body`).trim(),i=C(r,`Reply body`),a=F(n.createdAt,`Reply createdAt`),o=F(n.updatedAt,`Reply updatedAt`);if(o<a)throw new m(`Reply updatedAt precedes createdAt`);if(n.createdAt!==n.updatedAt)throw new m(`Reply edits must go through the review service`);if(this.#e){await this.#e.createReply({commentId:n.commentId,replyId:n.id,body:r});return}await this.db.transact(this.db.tx.replies[n.id].update({pageId:e,commentId:n.commentId,authorId:this.session.reviewerId,authorAuthUserId:this.session.authUserId,authorName:this.session.displayName,body:r,bodyContentMarker:i,createdAt:a,updatedAt:o}).link({comment:n.commentId,author:this.session.reviewerId}));return}case`reply.delete`:throw new m(`Reply deletion must go through the review service`)}}};function Ce(e,t){return{comments:{$:{where:{pageId:e},order:{createdAt:`asc`}},targetElements:{},scopeEntries:{}},replies:{$:{where:{pageId:e},order:{createdAt:`asc`}}},$users:{$:{where:{id:t}},activeReviewInvitation:{project:{}}}}}function we(e,t,n){let r=t.map(t=>Te(t,e));Ae(r,`comment`);let i=new Set(r.map(({id:e})=>e)),a=n.map(t=>Ee(t,e));Ae(a,`reply`);for(let e of a)if(!i.has(e.commentId))throw new m(`Reply ${e.id} references a comment outside the page snapshot`);return{pageId:e,comments:r,replies:a}}function Te(e,t){let n=M(e,`comment`),r=N(n.id,`comment id`);if(N(n.pageId,`comment ${r} pageId`)!==t)throw new m(`Comment ${r} escaped its page scope`);N(n.authorAuthUserId,`comment ${r} authorAuthUserId`);let i=!(Object.hasOwn(n,`anchor`)&&Object.hasOwn(n,`target`)),a=i?g(n,n.targetElements,`comment ${r} target`):ee(n.target,`comment ${r} target`),o=i?{...a.anchor}:De(n.anchor,`comment ${r} anchor`);if(!Oe(o,a.anchor))throw new m(`Comment ${r} has inconsistent anchors`);let s=n.status;if(s!==`open`&&s!==`resolved`)throw new m(`Comment ${r} has an invalid status`);let c=F(n.createdAt,`comment ${r} createdAt`),l=F(n.updatedAt,`comment ${r} updatedAt`);if(l<c)throw new m(`Comment ${r} updatedAt precedes createdAt`);let u=Fe(n.resolvedAt,`comment ${r} resolvedAt`),d=Ie(n.resolvedById,`comment ${r} resolvedById`);if(s===`open`&&(u!==void 0||d!==void 0))throw new m(`Open comment ${r} contains resolution metadata`);if(s===`resolved`&&(u===void 0||d===void 0||u!==l))throw new m(`Resolved comment ${r} has invalid workflow metadata`);return{id:r,pageId:t,authorId:N(n.authorId,`comment ${r} authorId`),authorName:N(n.authorName,`comment ${r} authorName`),body:S(n.body,`comment ${r} body`),anchor:o,target:a,...i?ke(v(n.scopeEntries)):n.scope==null?{}:{scope:te(n.scope)},status:s,workflowRevision:Pe(n.workflowRevision,`comment ${r} workflowRevision`),createdAt:c,updatedAt:l,...u===void 0?{}:{resolvedAt:u},...d===void 0?{}:{resolvedById:d}}}function Ee(e,t){let n=M(e,`reply`),r=N(n.id,`reply id`);if(N(n.pageId,`reply ${r} pageId`)!==t)throw new m(`Reply ${r} escaped its page scope`);N(n.authorAuthUserId,`reply ${r} authorAuthUserId`);let i=F(n.createdAt,`reply ${r} createdAt`),a=F(n.updatedAt,`reply ${r} updatedAt`);if(a<i)throw new m(`Reply ${r} updatedAt precedes createdAt`);return{id:r,commentId:N(n.commentId,`reply ${r} commentId`),authorId:N(n.authorId,`reply ${r} authorId`),authorName:N(n.authorName,`reply ${r} authorName`),body:S(n.body,`reply ${r} body`),createdAt:i,updatedAt:a}}function De(e,t){let n=M(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 m(`${t} has invalid fields`);return{selector:Ne(n.selector,`${t} selector`),offsetX:P(n.offsetX,`${t} offsetX`),offsetY:P(n.offsetY,`${t} offsetY`),viewportX:P(n.viewportX,`${t} viewportX`),viewportY:P(n.viewportY,`${t} viewportY`)}}function Oe(e,t){return e.selector===t.selector&&e.offsetX===t.offsetX&&e.offsetY===t.offsetY&&e.viewportX===t.viewportX&&e.viewportY===t.viewportY}function ke(e){return e===void 0?{}:{scope:e}}function Ae(e,t){let n=new Set;for(let r of e){if(n.has(r.id))throw new m(`Duplicate ${t} ${r.id}`);n.add(r.id)}}function je(e){let t=Me(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 Me(e){return e instanceof Error?e.message:e&&typeof e==`object`&&`message`in e?String(e.message):``}function M(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw new m(`${t} is missing or invalid`);return e}function Ne(e,t){if(typeof e!=`string`)throw new m(`${t} is invalid`);return e}function N(e,t){let n=Ne(e,t);if(!n.trim())throw new m(`${t} is empty`);return n}function P(e,t){if(typeof e!=`number`||!Number.isFinite(e)||e<0||e>1)throw new m(`${t} is invalid`);return e}function F(e,t){if(!Number.isSafeInteger(e)||e<0||e>0xe677d21fdbff)throw new m(`${t} is invalid`);return e}function Pe(e,t){if(!Number.isSafeInteger(e)||e<0)throw new m(`${t} is invalid`);return e}function Fe(e,t){return e==null?void 0:F(e,t)}function Ie(e,t){return e==null?void 0:N(e,t)}Object.freeze({field:`createdAt`,direction:`desc`,tieBreaker:`id`,tieDirection:`desc`}),Object.freeze({comments:200,replies:1e3,sourceContexts:200});var Le=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=Ge(e,t.roomId),a,l,u=this.roomCredential??{id:t.roomId,expiresAt:2**53-1,refresh:async()=>void 0},d=t=>{let o=this.db.joinRoom(`visualReview`,t),s=o.subscribePresence({},e=>{if(e.error){r?.(z(e.error));return}i=Object.values(e.peers).map(e=>ze(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?.()},f=()=>{if(!this.roomCredential||o)return;let e=Math.max(1e3,u.expiresAt-Date.now()+250);l=setTimeout(()=>{p()},e)},p=async()=>{try{let e=await u.refresh();if(o||!e)return;u={...e,refresh:u.refresh},d(e.id),f()}catch(e){if(o)return;r?.(z(e)),o||(l=setTimeout(()=>{p()},5e3))}};d(u.id),f(),s.push(()=>{l&&clearTimeout(l),this.#e.delete(n),a?.()})}return t.chat&&s.push(this.db.subscribeQuery(Re(e,this.session.authUserId),t=>{if(t.error){r?.(z(t.error));return}if(t.data)try{a=t.data.reviewMessages.map(t=>Be(t,e)).sort(Ke),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(Ge(e,t));r&&r.publishPresence({reviewerId:this.session.reviewerId,displayName:this.session.displayName,color:n.color,active:n.active,...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}})}async sendMessage(e,t){if(t.pageId!==e)throw Error(`Review message page mismatch`);if(t.authorId!==this.session.reviewerId)throw Error(`Review message author mismatch`);let n=Ve(t.body),r=He(t.pathname),i=R(t.createdAt,`Review message createdAt`),a=R(t.updatedAt,`Review message updatedAt`);if(i!==a)throw Error(`New review messages must be immutable`);await this.db.transact(this.db.tx.reviewMessages[t.id].update({pageId:e,pathname:r,authorId:this.session.reviewerId,authorAuthUserId:this.session.authUserId,authorName:this.session.displayName,body:n,bodyContentMarker:C(n,`Review message body`),createdAt:i,updatedAt:a}).link({page:e,author:this.session.reviewerId}))}};function Re(e,t){return{reviewMessages:{$:{where:{pageId:e},order:{createdAt:`asc`}}},$users:{$:{where:{id:t}},activeReviewInvitation:{project:{}}}}}function ze(e){let t=Ue(e.reviewerId,128),n=Ue(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=L(e.cursorX),a=L(e.cursorY),o=We(e.viewportWidth),s=We(e.viewportHeight);return{peerId:e.peerId,reviewerId:t,displayName:n,color:r,active:e.active,...i===void 0?{}:{cursorX:i},...a===void 0?{}:{cursorY:a},...o===void 0?{}:{viewportWidth:o},...s===void 0?{}:{viewportHeight:s}}}function Be(e,t){let n=I(e.id,`Review message id`,128);if(I(e.pageId,`Review message pageId`,128)!==t)throw Error(`Review message page mismatch`);return{id:n,pageId:t,pathname:He(e.pathname),authorId:I(e.authorId,`Review message authorId`,128),authorName:I(e.authorName,`Review message authorName`,512),body:Ve(e.body),createdAt:R(e.createdAt,`Review message createdAt`),updatedAt:R(e.updatedAt,`Review message updatedAt`)}}function Ve(e){return I(e,`Review message body`,4e3)}function He(e){let t=I(e,`Review message pathname`,2e3);if(!t.startsWith(`/`))throw Error(`Review message pathname is invalid`);return t}function I(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 Ue(e,t){if(typeof e!=`string`)return null;let n=e.trim();return n&&n.length<=t?n:null}function L(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function We(e){let t=L(e);return t!==void 0&&t>0?t:void 0}function R(e,t){if(!Number.isSafeInteger(e)||e<0)throw Error(`${t} is invalid`);return e}function Ge(e,t){return JSON.stringify([e,t])}function Ke(e,t){return e.createdAt-t.createdAt||e.id.localeCompare(t.id)}function z(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`});const B={$default:`false`},V={$default:`false`},H=`$user.activeReviewInvitation.project.id`;function U(e){return[`auth.id != null`,`size(auth.ref('$user.activeReviewInvitation.id')) == 1`,`size(auth.ref('${H}')) == 1`,`size(auth.ref('$user.activeReviewInvitation.status')) == 1`,`size(auth.ref('$user.activeReviewInvitation.expiresAt')) == 1`,`size(auth.ref('$user.activeReviewInvitation.revokedAt')) == 0`,`auth.ref('$user.activeReviewInvitation.status')[0] == 'accepted'`,`request.time < timestamp(auth.ref('$user.activeReviewInvitation.expiresAt')[0])`,e].join(` && `)}function W(...e){return e.map(e=>`(${e})`).join(` && `)}function G(...e){return e.map(e=>`(${e})`).join(` || `)}function K(e,t){return`!has(${e}.${t}) || ${e}.${t} == null`}const qe=`actions.data == 'create' && actions.linkedData == 'update'`,Je=e=>`${e}.matches('[^ \\t\\n\\r\\f\\u000b\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]')`,Ye=e=>W(`${e} >= 0`,`${e} <= 253402300799999`,`${e} % 1 == 0`),Xe=e=>W(Ye(e),`(request.time - timestamp(${e})).getSeconds() <= 86400`,`(request.time - timestamp(${e})).getSeconds() >= -300`);W(`actions.data == 'update' && actions.linkedData == 'create'`,G(`auth.id != null && auth.id in data.ref('project.owners.id')`,U(`auth.ref('${H}')[0] in data.ref('project.id')`))),W(qe,G(`auth.id != null && auth.id in data.ref('page.project.owners.id')`,U(`auth.ref('${H}')[0] in data.ref('page.project.id')`)),`auth.id != null && auth.id == data.authorAuthUserId && auth.id in data.ref('author.$user.id')`,`data.pageId in data.ref('page.id')`,`data.authorId in data.ref('author.id') && data.authorAuthUserId in data.ref('author.$user.id') && data.authorName in data.ref('author.displayName')`,`data.pageId in data.ref('author.project.pages.id')`),W(qe,`data.pageId == linkedData.id`,`auth.id != null && auth.id == data.authorAuthUserId`,`data.authorId in auth.ref('$user.reviewerMemberships.id')`,`data.authorId in linkedData.ref('project.reviewers.id')`,G(`auth.id != null && auth.id in linkedData.ref('project.owners.id')`,U(`auth.ref('${H}')[0] in linkedData.ref('project.id')`)));const Ze=`auth.id != null && auth.id in data.ref('comment.page.project.owners.id')`,Qe=U(`auth.ref('${H}')[0] in data.ref('comment.page.project.id')`);U(`auth.ref('${H}')[0] in data.ref('comment.page.project.id')`),U(`auth.ref('${H}')[0] in data.ref('page.project.id')`);const $e=W(`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')`);W(`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`,$e),W(`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`,$e),W(`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')`),W(`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')`),W(`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')`),{...B},{...V},{...V},U(`data.id == auth.ref('${H}')[0]`),{...B},W(`isOwner`,G(W(K(`data`,`allowedOrigin`),K(`newData`,`allowedOrigin`)),`newData.allowedOrigin == data.allowedOrigin`)),{...V},{...B},W(`isOwner`,G(W(K(`data`,`autoRegistrationSlot`),K(`newData`,`autoRegistrationSlot`)),`newData.autoRegistrationSlot == data.autoRegistrationSlot`)),{...V},{...B},{...V},U(`auth.ref('${H}')[0] in data.ref('project.id')`),{...B},{...V},{...B},{...B},{...V},{...V},{...B},{...V},{...B},G(Ze,Qe),{...B},G(Ze,Qe),{...B},{...B},W(`data.createdAt == data.updatedAt`,Xe(`data.createdAt`),`size(data.body) > 0`,`size(data.body) <= 4000`,`size(data.bodyContentMarker) == 1`,Je(`data.bodyContentMarker`),`data.body.startsWith(data.bodyContentMarker)`,`size(data.authorName) > 0`,`size(data.authorName) <= 512`,Je(`data.authorName`),`size(data.pathname) > 0`,`size(data.pathname) <= 2000`,`request.modifiedFields.all(field, field in ['pageId', 'pathname', 'authorId', 'authorAuthUserId', 'authorName', 'body', 'bodyContentMarker', 'createdAt', 'updatedAt', 'page', 'author'])`),{...B},{...V};var et=class extends Error{code;constructor(e,t){super(t),this.code=e,this.name=`ReviewerCommentSaveError`}};function tt(e,t){let n=e.replace(/\/+$/,``),r=async(e,r)=>{let i=await t();if(!i)throw new et(`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 et(`NETWORK`,`리뷰 서버에 연결하지 못했습니다. 연결을 확인하고 다시 시도해 주세요.`)}if(!a.ok)throw new et(...await nt(a))};return{createComment:e=>r(`/v1/comments`,e),createReply:e=>r(`/v1/replies`,e)}}async function nt(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 rt=class extends Error{code;constructor(e,t){super(t),this.code=e,this.name=`ReviewBootstrapError`}};const q=new Map;function it(e,t={}){let n=e.trim();if(!n)throw Error(`InstantDB app ID is required`);return{async resolve(e){let r=(t.databaseFactory??xe)(n),i=await st(n,r,e.authHost,e.signal,e.styleNonce,t.reviewerToken);for(;;)try{await at(t.acceptInvitation,{refreshToken:i.refresh_token,invitationId:e.invitationId,suggestedDisplayName:bt(i.email),authHost:e.authHost,signal:e.signal,styleNonce:e.styleNonce});break}catch(a){if(!xt(a,`INVITATION_EMAIL_MISMATCH`))throw a;await pt(e.authHost,e.signal,e.styleNonce),i=await ct(n,r,i,e.authHost,e.signal,e.styleNonce,t.reviewerToken)}let{data:a}=await r.queryOnce({reviewers:{$:{where:{authUserId:i.id}}},reviewPages:{$:{where:{stagingOrigin:e.location.origin}}},invitations:{}}),o=ot(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 rt(o.code,Tt(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=e.collaboration?.cursors===!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,l=c&&t.resolveCollaborationRoom?{...c,refresh:async()=>{let n=await r.getAuth();if(n?.refresh_token)return t.resolveCollaborationRoom?.({refreshToken:n.refresh_token,invitationId:e.invitationId,pageId:o.page.id,scopeId:e.collaboration.scopeId,signal:e.signal})}}:void 0;return{pageId:o.page.id,session:s,repository:new Se(r,s,{...t.serviceUrl===void 0?{}:{commentTransport:tt(t.serviceUrl,async()=>(await r.getAuth())?.refresh_token??null)}}),...e.collaboration?.cursors===!0||e.collaboration?.chat===!0?{collaborationRepository:new Le(r,s,l)}:{},...e.collaboration?.cursors===!0&&l?{collaborationRoomId:l.id}:{},allowedStagingHosts:[e.location.hostname]}}}}async function at(e,t){if(!e)return;let n={refreshToken:t.refreshToken,invitationId:t.invitationId,signal:t.signal};try{await e(n);return}catch(e){if(!xt(e,`DISPLAY_NAME_REQUIRED`))throw e}let r=await ft(t.authHost,t.signal,t.suggestedDisplayName,t.styleNonce);await e({...n,displayName:r})}function ot(e,t,n,r){let i=e.find(({id:e})=>e===r.invitationId);if(!i||wt(i.email)!==wt(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=Ct(r.location.pathname),s=n.find(e=>e.projectId===i.projectId&&e.stagingOrigin===r.location.origin&&Ct(e.pathname)===o);return s?{ok:!0,invitation:i,reviewer:a,page:s}:{ok:!1,code:`PAGE_NOT_FOUND`}}async function st(e,t,n,r,i,a){let o=await J(r,()=>t.getAuth());if(o)return o;let s=await lt(e,r);try{return await J(r,()=>t.getAuth())||(a?await J(r,async()=>{await t.auth.signInWithToken(a);let e=await t.getAuth();if(!e)throw Error(`Reviewer session did not establish`);return e}):await dt(t,n,r,i))}finally{s()}}async function ct(e,t,n,r,i,a,o){Y(i);let s=await lt(e,i);try{let e=await J(i,()=>t.getAuth());return e&&e.id!==n.id?e:(e&&await J(i,async()=>{await t.auth.signOut()}),Y(i),o?await J(i,async()=>{await t.auth.signInWithToken(o);let e=await t.getAuth();if(!e)throw Error(`Reviewer session did not establish`);return e}):await dt(t,r,i,a))}finally{s()}}async function lt(e,t){Y(t);let n=q.get(e);return n||(n={held:!1,waiters:[]},q.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(Z()))};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,ut(e,n))}function ut(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(Z());continue}n.resolve(ut(e,t));return}t.held=!1,q.get(e)===t&&q.delete(e)}}}async function J(e,t){Y(e);let n=await t();return Y(e),n}function Y(e){if(e.aborted)throw Z()}function dt(e,t,n,r){try{Y(n)}catch(e){return Promise.reject(e)}let i=ht(t);i.replaceChildren(gt(i,r),_t(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,_=()=>{n.removeEventListener(`abort`,y),i.removeEventListener(`keydown`,b),a.removeEventListener(`submit`,x)},v=e=>{m||(m=!0,_(),e())},y=()=>{m||(g=!0,_(),h||v(()=>r(Z())))},b=e=>{let t=e;t.key===`Escape`?(t.preventDefault(),y()):t.key===`Tab`&&St(i,t)},x=async i=>{if(i.preventDefault(),!(m||h)){h=!0,f.textContent=``,d.disabled=!0,a.setAttribute(`aria-busy`,`true`);try{if(Y(n),!p){let t=c.value.trim();await J(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 J(n,async()=>{await e.auth.signInWithMagicCode({email:c.value.trim(),code:l.value.trim()})});let r=await J(n,()=>e.getAuth());if(!r)throw Error(`인증 세션을 확인할 수 없습니다.`);v(()=>t(r))}catch(e){if(m||n.aborted||xt(e,`AUTH_CANCELLED`))return;f.textContent=mt(e)}finally{h=!1,!m&&(g||n.aborted)?v(()=>r(Z())):m||(d.disabled=!1,a.removeAttribute(`aria-busy`))}}};n.addEventListener(`abort`,y,{once:!0}),i.addEventListener(`keydown`,b),a.addEventListener(`submit`,x)})}function ft(e,t,n=``,r){if(t.aborted)return Promise.reject(Z());let i=ht(e);i.replaceChildren(gt(i,r),vt(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(Z())),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`){St(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 pt(e,t,n){if(t.aborted)return Promise.reject(Z());let r=ht(e);r.replaceChildren(gt(r,n),yt(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(Z())),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`&&St(r,t)};t.addEventListener(`abort`,c,{once:!0}),r.addEventListener(`keydown`,u),i.addEventListener(`submit`,l)})}function mt(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 ht(e){return e.shadowRoot??e.attachShadow({mode:`open`})}function gt(e,t){let n=e.ownerDocument.createElement(`style`);return t&&(n.nonce=t),n.textContent=Et,n}function _t(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=X(t,`p`,`01.works`,`eyebrow`),i=X(t,`h2`,`Visual Review`);i.id=`visual-review-login-title`;let a=X(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=X(t,`p`,``,`error`);u.setAttribute(`role`,`alert`);let d=X(t,`button`,`코드 받기`);return d.type=`submit`,n.append(r,i,a,o,c,u,d),n}function vt(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=X(t,`p`,`01.works`,`eyebrow`),i=X(t,`h2`,`표시 이름`);i.id=`visual-review-name-title`;let a=X(t,`p`,`피드백에 이 이름이 표시됩니다.`,`description`);a.id=`visual-review-name-description`;let o=X(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=X(t,`p`,``,`error`);c.setAttribute(`role`,`alert`);let l=X(t,`button`,`리뷰 시작`);return l.type=`submit`,n.append(r,i,a,o,s,c,l),n}function yt(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=X(t,`p`,`01.works`,`eyebrow`),i=X(t,`h2`,`계정 확인`);i.id=`visual-review-account-title`;let a=X(t,`p`,`이 초대는 현재 로그인한 계정과 일치하지 않습니다.`,`description`);a.id=`visual-review-account-description`;let o=X(t,`button`,`다른 계정으로 계속`);return o.type=`submit`,n.append(r,i,a,o),n}function X(e,t,n,r){let i=e.createElement(t);return i.textContent=n,r&&(i.className=r),i}function bt(e){return e?.split(`@`)[0]?.trim().slice(0,120)??``}function xt(e,t){return!!(e&&typeof e==`object`&&`code`in e&&e.code===t)}function Z(){return new rt(`AUTH_CANCELLED`,`리뷰 로그인이 취소되었습니다.`)}function St(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 Ct(e){return e.replace(/\/+$/,``)||`/`}function wt(e){return e.trim().toLowerCase()}function Tt(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 Et=`
|
|
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 Q=class extends Error{code;status;constructor(e,t,n){super(t),this.code=e,this.status=n,this.name=`HostedBootstrapError`}};function Dt(e={}){return Ot(e)}function Ot(t={},n={}){let r=e(t.serviceUrl??`https://visual-review.01.works`),i=t.fetch??globalThis.fetch,a=n.instantRuntimeFactory??it;if(typeof i!=`function`)throw Error(`Visual Review bootstrap requires the Fetch API`);return{async resolve(e){let t=kt(e),n=await jt(r,i,t,e.signal);return a(n.instantAppId,{serviceUrl:r,...n.reviewer?{reviewerToken:n.reviewer.token}:{},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 Nt(t);if(!t.ok)throw new Q($(n,`error`)??`INVITATION_ACCEPT_FAILED`,$(n,`message`)??`리뷰 초대 수락에 실패했습니다.`,t.status)},...e.collaboration?.cursors===!0?{resolveCollaborationRoom:e=>At(r,i,e)}:{}}).resolve(e)}}}function kt(e){return{invitationId:e.invitationId,origin:e.location.origin,pathname:e.location.pathname}}async function At(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 Nt(r);if(!r.ok)throw new Q($(i,`error`)??`COLLABORATION_ROOM_FAILED`,$(i,`message`)??`실시간 협업 연결에 실패했습니다.`,r.status);if(!i||typeof i!=`object`||Array.isArray(i))throw new Q(`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 Q(`INVALID_COLLABORATION_ROOM`,`잘못된 협업 응답입니다.`,502);return{id:o,expiresAt:a.expiresAt}}async function jt(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 Nt(i);if(!i.ok)throw new Q($(a,`error`)??`BOOTSTRAP_FAILED`,$(a,`message`)??`Visual Review bootstrap에 실패했습니다.`,i.status);return Mt(a)}function Mt(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new Q(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let t=e,n=$(t,`instantAppId`);if(t.apiVersion!==1||!n||!Pt(n))throw new Q(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);let r=t.reviewer;if(r==null)return{apiVersion:1,instantAppId:n};let i=$(r,`token`),a=$(r,`email`);if(!i||!a)throw new Q(`INVALID_BOOTSTRAP_RESPONSE`,`잘못된 bootstrap 응답입니다.`,502);return{apiVersion:1,instantAppId:n,reviewer:{token:i,email:a}}}async function Nt(e){try{return await e.json()}catch{throw new Q(`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 Pt(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{Dt as createHostedReviewRuntime};
|
package/dist/index.d.ts
CHANGED
|
@@ -5,23 +5,12 @@ export interface VisualReviewBuildContext {
|
|
|
5
5
|
mode: 'development' | 'preview' | 'production';
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export interface VisualReviewGeneratedFrameLocator {
|
|
9
|
-
assetPath: string;
|
|
10
|
-
/** 1-based generated line. */
|
|
11
|
-
line1: number;
|
|
12
|
-
/** 0-based generated column. */
|
|
13
|
-
column0: number;
|
|
14
|
-
runtime: 'browser' | 'server-node' | 'server-edge' | 'unknown';
|
|
15
|
-
debugId?: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
8
|
export interface VisualReviewSourceLocation {
|
|
19
9
|
filePath: string;
|
|
20
10
|
lineNumber: number | null;
|
|
21
11
|
columnNumber: number | null;
|
|
22
12
|
componentName: string | null;
|
|
23
13
|
origin: 'app' | 'shared-ui' | 'dependency' | 'generated' | 'unknown';
|
|
24
|
-
generated?: VisualReviewGeneratedFrameLocator;
|
|
25
14
|
}
|
|
26
15
|
|
|
27
16
|
export interface VisualReviewSourceProvenance {
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./install-
|
|
1
|
+
import{t as e}from"./install-COwLLFZK.js";export{e as installVisualReview};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`https://visual-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!==!0||typeof window>`u`||typeof document>`u`)return n();let r=!1,i=0,a=null,o=null,p=null,h=null,g=document.baseURI,_=new WeakMap,S=new Map,E=W(window),A=Z(e.styleNonce,document),M=e.activationParam??`visual-review`,N=v(window.location,M),P=m(window,e=>{let t=v(e,M);if(t===N)return;let n=D(M,e.href);n&&S.set(e.origin,n),N=t,I()}),F=e=>{r||!p||e.target!==p||(N=v(window.location,M),I())};return window.addEventListener(`visual-review:reauthenticate-required`,F),{ready:I(),refresh(){return r?Promise.resolve(null):(N=v(window.location,M),I())},dispose(){r||(r=!0,i+=1,a?.abort(),a=null,p?.remove(),p=null,h?.remove(),h=null,S.clear(),P.unsubscribe(),window.removeEventListener(`visual-review:reauthenticate-required`,F))}};function I(){let t=++i;a?.abort(),p?.remove(),p=null,h?.remove();let n=new AbortController,r=document.createElement(`div`);r.dataset.agencyReviewRoot=``,a=n,h=r;let s=L(t,n,r);return o=s,s.then(r=>{r&&B(t,n)&&p===r&&e.onReady?.(r)}),s}async function L(t,n,i){try{let e=document.body??await j(document,n.signal);if(!B(t,n))return V(t);e.append(i);let r=await R(t,n,i);return B(t,n)?r:(i.remove(),V(t))}catch(a){if(!B(t,n))return i.remove(),V(t);if(d(a,`AUTH_CANCELLED`))return i.remove(),h===i&&(h=null),null;z(a,i);let o=Y(a,_.get(i)?.[2]);return J(i,o.message,A,()=>{r||h!==i||(N=v(window.location,M),I())}),e.onError?.(o),window.dispatchEvent(new CustomEvent(`visual-review:error`,{detail:o})),null}}async function R(n,r,i){let a=t(e.serviceUrl??`https://visual-review.01.works`),o=y(window.location),d=ee(a,M,o,E),f=b(S.get(o.origin)),m=x(C(a,o.origin)),D=d?.source===`url`?d:f??m??d;if(!D)return _.delete(i),i.remove(),h===i&&(h=null),null;let j=D.invitationId;_.set(i,[a,o.origin,j]),D.source===`url`?(G(a,o.origin,E),S.set(o.origin,j),w(a,o.origin,j),P.suppress(()=>O(M))):D.source===`handoff`&&S.set(o.origin,j),N=v(window.location,M);let F=await k((e.runtime??await c(a)).resolve({invitationId:j,location:o,signal:r.signal,authHost:i,styleNonce:A,collaboration:{cursors:e.collaboration?.cursors===!0,chat:e.collaboration?.chat===!0,scopeId:s(e.build,o)}}),r.signal);if(!B(n,r))return null;T(a,o.origin,j),H(a,j,o,E),S.set(o.origin,j);let I=e.sourceContextResolver??await u(e.build,g,r.signal);if(!B(n,r))return null;let{mountReviewWidget:L}=await l(r.signal);return B(n,r)?(p=L({...F,collaboration:{cursors:e.collaboration?.cursors===!0&&!!(F.collaborationRepository&&F.collaborationRoomId),chat:e.collaboration?.chat===!0&&!!F.collaborationRepository,repository:F.collaborationRepository,roomId:F.collaborationRoomId,scopeId:s(e.build,o)},developerTools:e.developerTools,sourceContextResolver:I,styleNonce:A}),i.remove(),h===i&&(h=null),window.dispatchEvent(new CustomEvent(`visual-review:ready`,{detail:p})),p):null}function z(e,t){if(!f(e))return;let n=_.get(t);if(!n)return;let[r,i,a]=n;T(r,i,a),S.get(i)===a&&S.delete(i),G(r,i,E,a)}function B(e,t){return!r&&i===e&&a===t&&!t.signal.aborted}function V(e){return!r&&i!==e?o:null}}function s(e,t){return JSON.stringify([M(e).buildId,t.pathname,t.search])}async function c(e){let{createHostedReviewRuntime:t}=await import(`./hosted-runtime-FHlAhXOq.js`);return t({serviceUrl:e})}async function l(e){return A(import(`./src-reBEPAL1.js`),e,a,`리뷰 위젯을 불러오는 시간이 초과되었습니다. 다시 시도해 주세요.`)}async function u(e,t,n){let r=M(e),{createGeneratedSourceContextResolver:i}=await A(import(`./generated-source-context-Cx2Sm1Z8.js`),n,a,`리뷰 소스 정보를 불러오는 시간이 초과되었습니다. 다시 시도해 주세요.`);return i({build:r})}function d(e,t){return e instanceof Error&&`code`in e&&e.code===t}function f(e){let t=e instanceof Error&&`code`in e?e.code:void 0;return typeof t==`string`&&/^INVITATION_(?:EXPIRED|NOT_FOUND|REQUIRED|REVOKED)$/.test(t)}const p=new WeakMap;function m(e,t){let n=p.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),h(e)},patchedReplaceState:function(t,n,r){i.call(this,t,n,r),h(e)},popstateListener:()=>h(e),subscribers:a,suppressionDepth:0},p.set(e,n),g(t,`pushState`,n.patchedPushState),g(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),_(n.history,`pushState`,n.patchedPushState,n.originalPushStateDescriptor),_(n.history,`replaceState`,n.patchedReplaceState,n.originalReplaceStateDescriptor),p.delete(e)))}}}function h(e){let t=p.get(e);if(!t||t.suppressionDepth>0)return;let n=y(e.location);[...t.subscribers].forEach(e=>e(n))}function g(e,t,n){Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:!0,value:n})}function _(e,t,n,r){if(Object.getOwnPropertyDescriptor(e,t)?.value===n){if(r){Object.defineProperty(e,t,r);return}delete e[t]}}function v(e,t){return`${e.origin}\n${e.pathname}\n${D(t,e.href)??``}`}function y(e){return new URL(e.href)}function b(e){return e?{invitationId:e,source:`memory`}:null}function x(e){return e?{invitationId:e,source:`handoff`}:null}function S(e,t){return JSON.stringify([e,t])}function C(e,t,n=Date.now()){let i=S(e,t),a=r.get(i);if(a){if(a.expiresAt>n)return a.invitationId;clearTimeout(a.timeout),r.delete(i)}}function w(e,t,n,a=Date.now()){E(a);let o=S(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 T(e,t,n){let i=S(e,t),a=r.get(i);a?.invitationId===n&&(clearTimeout(a.timeout),r.delete(i))}function E(e){for(let[t,n]of r)n.expiresAt>e||(clearTimeout(n.timeout),r.delete(t))}function ee(e,t,n,r){let i=D(t,n.href);if(i)return{invitationId:i,source:`url`};let a=U(r,K(e,n.origin));return a?{invitationId:a,source:`storage`}:q(n.hostname)?{invitationId:`local-preview`,source:`local-preview`}:null}function D(e,t){let n=new URL(t);return(n.searchParams.get(e)??n.searchParams.get(`agency-review`)??n.searchParams.get(`agency-review-invite`))?.trim()||null}function O(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)}),r&&window.history.replaceState(window.history.state,``,`${t.pathname}${t.search}${t.hash}`)}function k(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 A(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 j(e,t){return e.body?Promise.resolve(e.body):t.aborted?Promise.reject(new DOMException(`Aborted`,`AbortError`)):new Promise((n,r)=>{let i=!1,a=new MutationObserver(()=>c()),o=()=>{e.removeEventListener(`DOMContentLoaded`,c),t.removeEventListener(`abort`,l),a.disconnect()},s=e=>{i||(i=!0,o(),e())},c=()=>{let t=e.body;t&&s(()=>n(t))},l=()=>{s(()=>r(new DOMException(`Aborted`,`AbortError`)))};e.addEventListener(`DOMContentLoaded`,c),t.addEventListener(`abort`,l,{once:!0}),a.observe(e,{childList:!0,subtree:!0}),c()})}function M(e){let t=document.querySelector(`meta[name="visual-review-build"]`),n=document.querySelector(`meta[name="visual-review-commit"]`),r=V(F()),i=V(I());return{buildId:B(e?.buildId)??z(t)??r??`unversioned`,gitCommit:L(e,n,i),generatedAt:e?.generatedAt,mode:e?.mode??P(N())??(q(window.location.hostname)?`development`:`production`)}}function N(){try{return process.env.VISUAL_REVIEW_PUBLIC_BUILD_MODE}catch{return}}function P(e){return e===`development`||e===`preview`||e===`production`?e:void 0}function F(){try{return process.env.VISUAL_REVIEW_PUBLIC_BUILD_ID}catch{return}}function I(){try{return process.env.VISUAL_REVIEW_PUBLIC_GIT_COMMIT}catch{return}}function L(e,t,n){return e?.gitCommit===null?null:typeof e?.gitCommit==`string`?R(e.gitCommit):R(t?.content)??R(n)}function R(e){if(typeof e!=`string`)return null;let t=e.trim().toLowerCase();return/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(t)?t:null}function z(e){return V(e?.content)}function B(e){let t=typeof e==`string`?e.trim():``;return t.length>0?t:null}function V(e){return typeof e==`string`&&e.length>0?e:null}function H(e,t,n,r){try{r?.setItem(K(e,n.origin),t)}catch{}}function U(e,t){try{return e?.getItem(t)??null}catch{return null}}function W(e){try{return e.localStorage}catch{return null}}function G(e,t,n,r){try{if(!n)return;let i=K(e,t),a=n.getItem(i);if(a===null||r!==void 0&&a!==r)return;n.removeItem(i)}catch{}}function K(e,t){return`visual-review:${e}:${t}:invitation`}function q(e){return e===`localhost`||e===`127.0.0.1`||e.endsWith(`.test`)||e.endsWith(`.local`)}function J(e,t,n,r){let i=e.shadowRoot??e.attachShadow({mode:`open`}),a=i.ownerDocument,o=a.createElement(`style`);n&&(o.nonce=n),o.textContent=te;let s=a.createElement(`strong`);s.textContent=`Visual Review를 시작할 수 없습니다`;let c=a.createElement(`span`);c.textContent=t;let l=a.createElement(`button`);l.type=`button`,l.textContent=`다시 시도`,l.addEventListener(`click`,()=>{l.disabled=!0,r?.()},{once:!0}),i.replaceChildren(o,s,c,l)}function Y(e,t){let n=e instanceof Error?e.message:String(e),r=Error(X(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:X(n,t)}),typeof i==`number`&&Number.isFinite(i)&&Object.defineProperty(r,"status",{configurable:!0,enumerable:!0,value:i})}return r}function X(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 Z(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}const te=`:host{all:initial;position:fixed;right:18px;bottom:18px;z-index:2147483646;max-width:360px;padding:16px 18px;border:1px solid #efc4bd;border-radius:14px;background:#fff8f6;box-shadow:0 12px 40px rgba(0,0,0,.14);color:#7a271a;font:13px/1.5 Inter,ui-sans-serif,system-ui,sans-serif}strong{display:block;margin-bottom:4px;color:#53150d}span{display:block}button{margin-top:12px;padding:7px 10px;border:1px solid #d9a79f;border-radius:8px;background:#fff;color:#7a271a;font:650 12px/1 Inter,ui-sans-serif,system-ui,sans-serif;cursor:pointer}button:focus-visible{outline:2px solid #7a271a;outline-offset:2px}button:disabled{opacity:.55;cursor:wait}`;export{e as n,t as r,o as t};
|
package/dist/next-config.d.ts
CHANGED
|
@@ -9,21 +9,11 @@ export interface VisualReviewBuildIdentityOptions {
|
|
|
9
9
|
|
|
10
10
|
export type VisualReviewReleaseMode = 'development' | 'preview' | 'production';
|
|
11
11
|
|
|
12
|
-
export interface VisualReviewNextSourceMapOptions {
|
|
13
|
-
/** Enables upload explicitly. A project-scoped upload token is still required. */
|
|
14
|
-
enabled?: boolean;
|
|
15
|
-
/** Node-only build secret. Prefer VISUAL_REVIEW_SOURCE_MAP_TOKEN in CI. */
|
|
16
|
-
token?: string;
|
|
17
|
-
/** Hosted service origin. Primarily useful for local/self-hosted verification. */
|
|
18
|
-
serviceUrl?: string;
|
|
19
|
-
/** Release environment recorded with the private source-map manifest. */
|
|
20
|
-
mode?: VisualReviewReleaseMode;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
12
|
export interface VisualReviewNextOptions extends VisualReviewBuildIdentityOptions {
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Writes `data-review-source="app/page.tsx:12:5"` onto JSX elements by
|
|
15
|
+
* default. Set `false` for identity-only output.
|
|
16
|
+
*/
|
|
27
17
|
sourceMetadata?: boolean;
|
|
28
18
|
}
|
|
29
19
|
|