reflet-sdk 0.6.9 → 0.7.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.
@@ -1,6 +1,5 @@
1
- import { l as sessionApiErrorSchema } from "./protocol-DyAiTtzM.js";
2
1
  import { n as teamAccessStorageKey } from "./team-activation-DCqh71bl.js";
3
- import { _ as teamTransport, a as DevtoolsBar, d as PersonIcon, f as SessionWorkspaceProvider, h as purgeAccount, i as BarButton, j as reportFailure, k as Notices, l as SESSIONS_STYLES, m as useWorkspaceLoader, n as DEVTOOLS_STYLES, p as failureMessage, r as DevtoolsSheet, s as SessionsBarButton, t as SessionsPanel, u as RecordingLayer } from "./sessions-panel-CaOJ6vWR.js";
2
+ import { A as Notices, M as reportFailure, _ as teamTransport, a as DevtoolsBar, d as PersonIcon, f as SessionWorkspaceProvider, h as purgeAccount, i as BarButton, k as sessionApiErrorSchema, l as SESSIONS_STYLES, m as useWorkspaceLoader, n as DEVTOOLS_STYLES, p as failureMessage, r as DevtoolsSheet, s as SessionsBarButton, t as SessionsPanel, u as RecordingLayer } from "./sessions-panel-DPhFYhbk.js";
4
3
  import { z } from "zod";
5
4
  import { useState } from "react";
6
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -420,4 +419,4 @@ function TeamLayer({ connection, position }) {
420
419
  //#endregion
421
420
  export { TeamLayer };
422
421
 
423
- //# sourceMappingURL=team-layer-DpM14Wwa.js.map
422
+ //# sourceMappingURL=team-layer-gMWcOdCd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"team-layer-DpM14Wwa.js","names":[],"sources":["../src/sessions/team/team-auth.ts","../src/sessions/team/team-sign-in-card.tsx","../src/sessions/team/team-layer.tsx"],"sourcesContent":["import { z } from \"zod\";\nimport { sessionApiErrorSchema } from \"../protocol\";\nimport { teamAccessStorageKey } from \"./team-activation\";\n\nconst TEAM_ACCESS_MESSAGE_TYPE = \"reflet:team-access\";\nconst POPUP_NAME = \"reflet-team-access\";\nconst POPUP_WIDTH_PX = 480;\nconst POPUP_HEIGHT_PX = 680;\nconst POPUP_CLOSED_POLL_MS = 500;\nconst SIGN_IN_TIMEOUT_MS = 5 * 60_000;\nconst PKCE_RANDOM_BYTES = 32;\nconst TRAILING_SLASHES = /\\/+$/;\nconst BASE64_PADDING = /[=]+$/;\n\nconst teamAccessSchema = z.object({\n expiresAt: z.number(),\n organizationName: z.string(),\n token: z.string().startsWith(\"fb_team_\"),\n});\n\nexport type TeamAccess = z.infer<typeof teamAccessSchema>;\n\nconst teamAccessMessageSchema = z.union([\n z.object({\n code: z.string().min(1),\n state: z.string(),\n type: z.literal(TEAM_ACCESS_MESSAGE_TYPE),\n }),\n z.object({\n error: z.literal(\"cancelled\"),\n state: z.string(),\n type: z.literal(TEAM_ACCESS_MESSAGE_TYPE),\n }),\n]);\n\nconst SIGN_IN_FAILURE_MESSAGES = {\n cancelled: \"Sign-in was cancelled.\",\n exchange_failed: \"Couldn't finish signing in to Reflet. Try again.\",\n popup_blocked:\n \"Your browser blocked the Reflet sign-in window. Allow pop-ups for this site, then try again.\",\n timeout: \"Signing in took too long. Try again.\",\n} as const;\n\nexport type TeamSignInFailure = keyof typeof SIGN_IN_FAILURE_MESSAGES;\n\nexport class TeamSignInError extends Error {\n readonly reason: TeamSignInFailure;\n\n constructor(reason: TeamSignInFailure, message?: string) {\n super(message ?? SIGN_IN_FAILURE_MESSAGES[reason]);\n this.name = \"TeamSignInError\";\n this.reason = reason;\n }\n}\n\nfunction base64Url(bytes: Uint8Array): string {\n return btoa(String.fromCharCode(...bytes))\n .replaceAll(\"+\", \"-\")\n .replaceAll(\"/\", \"_\")\n .replace(BASE64_PADDING, \"\");\n}\n\nfunction randomBase64Url(): string {\n return base64Url(crypto.getRandomValues(new Uint8Array(PKCE_RANDOM_BYTES)));\n}\n\nasync function pkceChallengeFor(codeVerifier: string): Promise<string> {\n const digest = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(codeVerifier)\n );\n return base64Url(new Uint8Array(digest));\n}\n\nfunction apiEndpoint(apiUrl: string, path: string): string {\n return `${apiUrl.replace(TRAILING_SLASHES, \"\")}${path}`;\n}\n\nfunction parseStoredJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\nfunction storedTeamAccess(publicKey: string): TeamAccess | null {\n try {\n const raw = localStorage.getItem(teamAccessStorageKey(publicKey));\n const parsed = teamAccessSchema.safeParse(\n raw === null ? null : parseStoredJson(raw)\n );\n return parsed.success ? parsed.data : null;\n } catch {\n return null;\n }\n}\n\nfunction persistTeamAccess(publicKey: string, access: TeamAccess | null): void {\n try {\n if (access) {\n localStorage.setItem(\n teamAccessStorageKey(publicKey),\n JSON.stringify(access)\n );\n } else {\n localStorage.removeItem(teamAccessStorageKey(publicKey));\n }\n } catch {\n // Storage disabled by browser policy: access then only lasts for this page.\n }\n}\n\nexport function readTeamAccess(publicKey: string): TeamAccess | null {\n const access = storedTeamAccess(publicKey);\n if (access && access.expiresAt > Date.now()) {\n return access;\n }\n persistTeamAccess(publicKey, null);\n return null;\n}\n\nfunction centeredPopupFeatures(): string {\n const left = Math.round(\n window.screenX + (window.outerWidth - POPUP_WIDTH_PX) / 2\n );\n const top = Math.round(\n window.screenY + (window.outerHeight - POPUP_HEIGHT_PX) / 2\n );\n return `popup,width=${POPUP_WIDTH_PX},height=${POPUP_HEIGHT_PX},left=${left},top=${top}`;\n}\n\nfunction waitForApprovedCode(options: {\n appOrigin: string;\n popup: Window;\n state: string;\n}): Promise<string> {\n // Executor form: the SDK targets ES2022, before `Promise.withResolvers`.\n return new Promise((resolve, reject) => {\n const listening = new AbortController();\n let closedOnLastPoll = false;\n const settle = (outcome: { code: string } | TeamSignInError) => {\n listening.abort();\n window.clearInterval(closedPoll);\n window.clearTimeout(timeout);\n if (outcome instanceof TeamSignInError) {\n reject(outcome);\n } else {\n resolve(outcome.code);\n }\n };\n const closedPoll = window.setInterval(() => {\n if (closedOnLastPoll) {\n settle(new TeamSignInError(\"cancelled\"));\n }\n closedOnLastPoll = options.popup.closed;\n }, POPUP_CLOSED_POLL_MS);\n const timeout = window.setTimeout(() => {\n options.popup.close();\n settle(new TeamSignInError(\"timeout\"));\n }, SIGN_IN_TIMEOUT_MS);\n window.addEventListener(\n \"message\",\n (event: MessageEvent) => {\n if (event.origin !== options.appOrigin) {\n return;\n }\n const message = teamAccessMessageSchema.safeParse(event.data);\n if (message.success && message.data.state === options.state) {\n settle(\n \"code\" in message.data\n ? { code: message.data.code }\n : new TeamSignInError(\"cancelled\")\n );\n }\n },\n { signal: listening.signal }\n );\n });\n}\n\nasync function exchangeCode(options: {\n apiUrl: string;\n code: string;\n codeVerifier: string;\n}): Promise<TeamAccess> {\n const response = await fetch(\n apiEndpoint(options.apiUrl, \"/api/v1/team/token\"),\n {\n body: JSON.stringify({\n code: options.code,\n codeVerifier: options.codeVerifier,\n }),\n headers: { \"Content-Type\": \"application/json\" },\n method: \"POST\",\n }\n );\n if (!response.ok) {\n const failure = sessionApiErrorSchema.safeParse(\n await response.json().catch(() => null)\n );\n throw new TeamSignInError(\n \"exchange_failed\",\n failure.success ? failure.data.error : undefined\n );\n }\n const access = teamAccessSchema.safeParse(\n await response.json().catch(() => null)\n );\n if (!access.success) {\n throw new TeamSignInError(\"exchange_failed\");\n }\n return access.data;\n}\n\n/** Call from a click handler: the popup opens before anything is awaited so browsers don't block it. */\nexport async function signInTeam(options: {\n apiUrl: string;\n appUrl: string;\n publicKey: string;\n}): Promise<TeamAccess> {\n const popup = window.open(\"about:blank\", POPUP_NAME, centeredPopupFeatures());\n if (!popup) {\n throw new TeamSignInError(\"popup_blocked\");\n }\n const codeVerifier = randomBase64Url();\n const state = randomBase64Url();\n const consentUrl = new URL(\"/auth/team\", options.appUrl);\n consentUrl.search = new URLSearchParams({\n code_challenge: await pkceChallengeFor(codeVerifier),\n origin: window.location.origin,\n public_key: options.publicKey,\n state,\n }).toString();\n popup.location.href = consentUrl.href;\n\n const code = await waitForApprovedCode({\n appOrigin: consentUrl.origin,\n popup,\n state,\n });\n const access = await exchangeCode({\n apiUrl: options.apiUrl,\n code,\n codeVerifier,\n });\n persistTeamAccess(options.publicKey, access);\n return access;\n}\n\nexport async function forgetTeamAccess(options: {\n apiUrl: string;\n publicKey: string;\n}): Promise<void> {\n const access = storedTeamAccess(options.publicKey);\n persistTeamAccess(options.publicKey, null);\n if (!access) {\n return;\n }\n await fetch(apiEndpoint(options.apiUrl, \"/api/v1/team/revoke\"), {\n headers: { Authorization: `Bearer ${access.token}` },\n method: \"POST\",\n });\n}\n","import { useState } from \"react\";\nimport { failureMessage } from \"../ui/session-workspace\";\nimport { signInTeam, type TeamAccess } from \"./team-auth\";\n\nexport interface TeamConnection {\n apiUrl: string;\n appUrl: string;\n publicKey: string;\n}\n\nfunction useTeamSignIn(\n connection: TeamConnection,\n onSignedIn: (access: TeamAccess) => void\n) {\n const [isSigningIn, setIsSigningIn] = useState(false);\n const [failure, setFailure] = useState<string | null>(null);\n\n const signIn = async () => {\n setIsSigningIn(true);\n setFailure(null);\n try {\n onSignedIn(await signInTeam(connection));\n } catch (error) {\n setFailure(failureMessage(error));\n }\n setIsSigningIn(false);\n };\n\n return { failure, isSigningIn, signIn };\n}\n\nfunction SignInActions({\n isSigningIn,\n onHide,\n onSignIn,\n}: {\n isSigningIn: boolean;\n onHide: () => void;\n onSignIn: () => void;\n}) {\n return (\n <div className=\"dt-actions\">\n <button\n className=\"dt-btn\"\n data-variant=\"primary\"\n disabled={isSigningIn}\n onClick={onSignIn}\n type=\"button\"\n >\n Sign in with Reflet\n </button>\n <button className=\"dt-btn\" onClick={onHide} type=\"button\">\n Hide\n </button>\n </div>\n );\n}\n\nexport function TeamSignInCard({\n connection,\n notice,\n onSignedIn,\n}: {\n connection: TeamConnection;\n notice: string | null;\n onSignedIn: (access: TeamAccess) => void;\n}) {\n const [isHidden, setIsHidden] = useState(false);\n const { failure, isSigningIn, signIn } = useTeamSignIn(\n connection,\n onSignedIn\n );\n\n if (isHidden) {\n return null;\n }\n return (\n <section aria-labelledby=\"rf-team-title\" className=\"dt-team-card glass\">\n <h2 className=\"dt-ses-subhead\" id=\"rf-team-title\">\n Reflet team tools\n </h2>\n <p className=\"dt-meta\">\n Sign in with your Reflet account to record a session on this app.\n </p>\n <p className=\"dt-meta\">\n Only members of the app's Reflet organization can sign in.\n </p>\n {notice && <p className=\"dt-meta\">{notice}</p>}\n {failure && (\n <p className=\"dt-error\" role=\"alert\">\n {failure}\n </p>\n )}\n <SignInActions\n isSigningIn={isSigningIn}\n onHide={() => setIsHidden(true)}\n onSignIn={signIn}\n />\n </section>\n );\n}\n","import { useState } from \"react\";\nimport {\n BarButton,\n DevtoolsBar,\n SessionsBarButton,\n} from \"../../devtools/client/devtools-bar\";\nimport { DevtoolsSheet } from \"../../devtools/client/devtools-sheet\";\nimport { DEVTOOLS_STYLES } from \"../../devtools/client/devtools-styles\";\nimport { Notices, reportFailure } from \"../../devtools/client/notices\";\nimport type { RefletFeedbackProps } from \"../../feedback/types\";\nimport { teamTransport } from \"../api/session-transports\";\nimport { purgeAccount } from \"../local/local-sessions\";\nimport { RecordingLayer } from \"../ui/recording/recording-layer\";\nimport { PersonIcon } from \"../ui/session-icons\";\nimport {\n type SessionWorkspace,\n SessionWorkspaceProvider,\n useWorkspaceLoader,\n type WorkspaceState,\n} from \"../ui/session-workspace\";\nimport { type SessionNavigation, SessionsPanel } from \"../ui/sessions-panel\";\nimport { SESSIONS_STYLES } from \"../ui/sessions-styles\";\nimport type { SessionTarget } from \"../ui/start/session-list\";\nimport { forgetTeamAccess, readTeamAccess, type TeamAccess } from \"./team-auth\";\nimport { type TeamConnection, TeamSignInCard } from \"./team-sign-in-card\";\n\nconst TEAM_TOOLS_LABEL = \"Reflet team tools\";\nconst REFUSED_SIGN_IN_COPY =\n \"Reflet no longer accepts this sign-in. Sign in again to record.\";\n\ninterface TeamPlacement {\n edge: \"bottom\" | \"top\";\n side: \"left\" | \"right\";\n}\n\nfunction AccountControls({\n access,\n onSignOut,\n workspace,\n}: {\n access: TeamAccess;\n onSignOut: () => void;\n workspace: SessionWorkspace | null;\n}) {\n const [isOpen, setIsOpen] = useState(false);\n const user = workspace?.account.userName ?? workspace?.account.userEmail;\n const label = user\n ? `Signed in as ${user} · ${access.organizationName}`\n : `Signed in · ${access.organizationName}`;\n return (\n <>\n <BarButton\n label={label}\n onClick={() => setIsOpen(!isOpen)}\n pressed={isOpen}\n >\n <PersonIcon />\n </BarButton>\n {isOpen && (\n <button className=\"dt-btn\" onClick={onSignOut} type=\"button\">\n Sign out\n </button>\n )}\n </>\n );\n}\n\nfunction SessionsBody({\n navigation,\n onStarted,\n state,\n}: {\n navigation: SessionNavigation;\n onStarted: () => void;\n state: WorkspaceState;\n}) {\n if (state.kind === \"ready\") {\n return <SessionsPanel navigation={navigation} onStarted={onStarted} />;\n }\n if (state.kind === \"failed\") {\n return (\n <div className=\"dt-empty\" role=\"alert\">\n <p>Sessions did not open: {state.message}</p>\n <button className=\"dt-btn\" onClick={state.retry} type=\"button\">\n Try again\n </button>\n </div>\n );\n }\n return <p className=\"dt-status\">Opening your sessions…</p>;\n}\n\nfunction TeamSheet({\n navigation,\n onOpenChange,\n side,\n state,\n}: {\n navigation: SessionNavigation;\n onOpenChange: (isOpen: boolean) => void;\n side: TeamPlacement[\"side\"];\n state: WorkspaceState;\n}) {\n return (\n <DevtoolsSheet\n label={TEAM_TOOLS_LABEL}\n onClose={() => onOpenChange(false)}\n onSelectTab={() => onOpenChange(true)}\n side={side}\n tab=\"sessions\"\n tabs={[\"sessions\"]}\n >\n <SessionsBody\n navigation={navigation}\n onStarted={() => onOpenChange(false)}\n state={state}\n />\n </DevtoolsSheet>\n );\n}\n\ninterface TeamSession {\n access: TeamAccess;\n apiUrl: string;\n onSignOut: (reason: string | null) => void;\n}\n\nfunction useTeamTransport(team: TeamSession) {\n const createTransport = () => ({\n apiUrl: team.apiUrl,\n transport: teamTransport({ apiUrl: team.apiUrl, token: team.access.token }),\n });\n const [current, setCurrent] = useState(createTransport);\n if (current.apiUrl !== team.apiUrl) {\n const next = createTransport();\n setCurrent(next);\n return next.transport;\n }\n return current.transport;\n}\n\nfunction useTeamWorkspace(team: TeamSession) {\n const { onSignOut } = team;\n const transport = useTeamTransport(team);\n const state = useWorkspaceLoader({\n onUnauthorized: () => onSignOut(REFUSED_SIGN_IN_COPY),\n surface: \"app\",\n transport,\n });\n const workspace = state.kind === \"ready\" ? state.workspace : null;\n\n const signOut = async () => {\n if (workspace) {\n await purgeAccount(workspace.account.key).catch((error: unknown) =>\n reportFailure(\"This device's session data was not removed\", error)\n );\n }\n onSignOut(null);\n };\n\n return { signOut, state, workspace };\n}\n\nfunction TeamTools({\n team,\n placement,\n}: {\n placement: TeamPlacement;\n team: TeamSession;\n}) {\n const { signOut, state, workspace } = useTeamWorkspace(team);\n const [isSheetOpen, setIsSheetOpen] = useState(false);\n const [open, setOpen] = useState<SessionTarget | null>(null);\n\n return (\n <SessionWorkspaceProvider workspace={workspace}>\n {isSheetOpen && (\n <TeamSheet\n navigation={{ onOpen: setOpen, open }}\n onOpenChange={setIsSheetOpen}\n side={placement.side}\n state={state}\n />\n )}\n <DevtoolsBar\n edge={placement.edge}\n label={TEAM_TOOLS_LABEL}\n side={placement.side}\n >\n <SessionsBarButton\n isOpen={isSheetOpen}\n onToggle={() => setIsSheetOpen(!isSheetOpen)}\n />\n <AccountControls\n access={team.access}\n onSignOut={signOut}\n workspace={workspace}\n />\n </DevtoolsBar>\n {workspace && (\n <RecordingLayer\n onFinished={(target) => {\n setOpen(target);\n setIsSheetOpen(true);\n }}\n />\n )}\n </SessionWorkspaceProvider>\n );\n}\n\nfunction placementFor(\n position: NonNullable<RefletFeedbackProps[\"position\"]>\n): TeamPlacement {\n return {\n edge: position.startsWith(\"top\") ? \"top\" : \"bottom\",\n side: position.endsWith(\"right\") ? \"right\" : \"left\",\n };\n}\n\nexport function TeamLayer({\n connection,\n position,\n}: {\n connection: TeamConnection;\n position: NonNullable<RefletFeedbackProps[\"position\"]>;\n}) {\n const { apiUrl, publicKey } = connection;\n const [access, setAccess] = useState(() => readTeamAccess(publicKey));\n const [notice, setNotice] = useState<string | null>(null);\n const signOut = (reason: string | null) => {\n setAccess(null);\n setNotice(reason);\n forgetTeamAccess({ apiUrl, publicKey }).catch((error: unknown) =>\n reportFailure(\"Signing out of Reflet did not finish\", error)\n );\n };\n const placement = placementFor(position);\n\n return (\n <div className=\"dt\">\n <style>{`${DEVTOOLS_STYLES}${SESSIONS_STYLES}`}</style>\n {access ? (\n <TeamTools\n placement={placement}\n team={{ access, apiUrl, onSignOut: signOut }}\n />\n ) : (\n <div\n className=\"dt-team-anchor\"\n data-edge={placement.edge}\n data-side={placement.side}\n >\n <TeamSignInCard\n connection={connection}\n notice={notice}\n onSignedIn={(signedIn) => {\n setAccess(signedIn);\n setNotice(null);\n }}\n />\n </div>\n )}\n <Notices />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"team-layer-gMWcOdCd.js","names":[],"sources":["../src/sessions/team/team-auth.ts","../src/sessions/team/team-sign-in-card.tsx","../src/sessions/team/team-layer.tsx"],"sourcesContent":["import { z } from \"zod\";\nimport { sessionApiErrorSchema } from \"../protocol\";\nimport { teamAccessStorageKey } from \"./team-activation\";\n\nconst TEAM_ACCESS_MESSAGE_TYPE = \"reflet:team-access\";\nconst POPUP_NAME = \"reflet-team-access\";\nconst POPUP_WIDTH_PX = 480;\nconst POPUP_HEIGHT_PX = 680;\nconst POPUP_CLOSED_POLL_MS = 500;\nconst SIGN_IN_TIMEOUT_MS = 5 * 60_000;\nconst PKCE_RANDOM_BYTES = 32;\nconst TRAILING_SLASHES = /\\/+$/;\nconst BASE64_PADDING = /[=]+$/;\n\nconst teamAccessSchema = z.object({\n expiresAt: z.number(),\n organizationName: z.string(),\n token: z.string().startsWith(\"fb_team_\"),\n});\n\nexport type TeamAccess = z.infer<typeof teamAccessSchema>;\n\nconst teamAccessMessageSchema = z.union([\n z.object({\n code: z.string().min(1),\n state: z.string(),\n type: z.literal(TEAM_ACCESS_MESSAGE_TYPE),\n }),\n z.object({\n error: z.literal(\"cancelled\"),\n state: z.string(),\n type: z.literal(TEAM_ACCESS_MESSAGE_TYPE),\n }),\n]);\n\nconst SIGN_IN_FAILURE_MESSAGES = {\n cancelled: \"Sign-in was cancelled.\",\n exchange_failed: \"Couldn't finish signing in to Reflet. Try again.\",\n popup_blocked:\n \"Your browser blocked the Reflet sign-in window. Allow pop-ups for this site, then try again.\",\n timeout: \"Signing in took too long. Try again.\",\n} as const;\n\nexport type TeamSignInFailure = keyof typeof SIGN_IN_FAILURE_MESSAGES;\n\nexport class TeamSignInError extends Error {\n readonly reason: TeamSignInFailure;\n\n constructor(reason: TeamSignInFailure, message?: string) {\n super(message ?? SIGN_IN_FAILURE_MESSAGES[reason]);\n this.name = \"TeamSignInError\";\n this.reason = reason;\n }\n}\n\nfunction base64Url(bytes: Uint8Array): string {\n return btoa(String.fromCharCode(...bytes))\n .replaceAll(\"+\", \"-\")\n .replaceAll(\"/\", \"_\")\n .replace(BASE64_PADDING, \"\");\n}\n\nfunction randomBase64Url(): string {\n return base64Url(crypto.getRandomValues(new Uint8Array(PKCE_RANDOM_BYTES)));\n}\n\nasync function pkceChallengeFor(codeVerifier: string): Promise<string> {\n const digest = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(codeVerifier)\n );\n return base64Url(new Uint8Array(digest));\n}\n\nfunction apiEndpoint(apiUrl: string, path: string): string {\n return `${apiUrl.replace(TRAILING_SLASHES, \"\")}${path}`;\n}\n\nfunction parseStoredJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\nfunction storedTeamAccess(publicKey: string): TeamAccess | null {\n try {\n const raw = localStorage.getItem(teamAccessStorageKey(publicKey));\n const parsed = teamAccessSchema.safeParse(\n raw === null ? null : parseStoredJson(raw)\n );\n return parsed.success ? parsed.data : null;\n } catch {\n return null;\n }\n}\n\nfunction persistTeamAccess(publicKey: string, access: TeamAccess | null): void {\n try {\n if (access) {\n localStorage.setItem(\n teamAccessStorageKey(publicKey),\n JSON.stringify(access)\n );\n } else {\n localStorage.removeItem(teamAccessStorageKey(publicKey));\n }\n } catch {\n // Storage disabled by browser policy: access then only lasts for this page.\n }\n}\n\nexport function readTeamAccess(publicKey: string): TeamAccess | null {\n const access = storedTeamAccess(publicKey);\n if (access && access.expiresAt > Date.now()) {\n return access;\n }\n persistTeamAccess(publicKey, null);\n return null;\n}\n\nfunction centeredPopupFeatures(): string {\n const left = Math.round(\n window.screenX + (window.outerWidth - POPUP_WIDTH_PX) / 2\n );\n const top = Math.round(\n window.screenY + (window.outerHeight - POPUP_HEIGHT_PX) / 2\n );\n return `popup,width=${POPUP_WIDTH_PX},height=${POPUP_HEIGHT_PX},left=${left},top=${top}`;\n}\n\nfunction waitForApprovedCode(options: {\n appOrigin: string;\n popup: Window;\n state: string;\n}): Promise<string> {\n // Executor form: the SDK targets ES2022, before `Promise.withResolvers`.\n return new Promise((resolve, reject) => {\n const listening = new AbortController();\n let closedOnLastPoll = false;\n const settle = (outcome: { code: string } | TeamSignInError) => {\n listening.abort();\n window.clearInterval(closedPoll);\n window.clearTimeout(timeout);\n if (outcome instanceof TeamSignInError) {\n reject(outcome);\n } else {\n resolve(outcome.code);\n }\n };\n const closedPoll = window.setInterval(() => {\n if (closedOnLastPoll) {\n settle(new TeamSignInError(\"cancelled\"));\n }\n closedOnLastPoll = options.popup.closed;\n }, POPUP_CLOSED_POLL_MS);\n const timeout = window.setTimeout(() => {\n options.popup.close();\n settle(new TeamSignInError(\"timeout\"));\n }, SIGN_IN_TIMEOUT_MS);\n window.addEventListener(\n \"message\",\n (event: MessageEvent) => {\n if (event.origin !== options.appOrigin) {\n return;\n }\n const message = teamAccessMessageSchema.safeParse(event.data);\n if (message.success && message.data.state === options.state) {\n settle(\n \"code\" in message.data\n ? { code: message.data.code }\n : new TeamSignInError(\"cancelled\")\n );\n }\n },\n { signal: listening.signal }\n );\n });\n}\n\nasync function exchangeCode(options: {\n apiUrl: string;\n code: string;\n codeVerifier: string;\n}): Promise<TeamAccess> {\n const response = await fetch(\n apiEndpoint(options.apiUrl, \"/api/v1/team/token\"),\n {\n body: JSON.stringify({\n code: options.code,\n codeVerifier: options.codeVerifier,\n }),\n headers: { \"Content-Type\": \"application/json\" },\n method: \"POST\",\n }\n );\n if (!response.ok) {\n const failure = sessionApiErrorSchema.safeParse(\n await response.json().catch(() => null)\n );\n throw new TeamSignInError(\n \"exchange_failed\",\n failure.success ? failure.data.error : undefined\n );\n }\n const access = teamAccessSchema.safeParse(\n await response.json().catch(() => null)\n );\n if (!access.success) {\n throw new TeamSignInError(\"exchange_failed\");\n }\n return access.data;\n}\n\n/** Call from a click handler: the popup opens before anything is awaited so browsers don't block it. */\nexport async function signInTeam(options: {\n apiUrl: string;\n appUrl: string;\n publicKey: string;\n}): Promise<TeamAccess> {\n const popup = window.open(\"about:blank\", POPUP_NAME, centeredPopupFeatures());\n if (!popup) {\n throw new TeamSignInError(\"popup_blocked\");\n }\n const codeVerifier = randomBase64Url();\n const state = randomBase64Url();\n const consentUrl = new URL(\"/auth/team\", options.appUrl);\n consentUrl.search = new URLSearchParams({\n code_challenge: await pkceChallengeFor(codeVerifier),\n origin: window.location.origin,\n public_key: options.publicKey,\n state,\n }).toString();\n popup.location.href = consentUrl.href;\n\n const code = await waitForApprovedCode({\n appOrigin: consentUrl.origin,\n popup,\n state,\n });\n const access = await exchangeCode({\n apiUrl: options.apiUrl,\n code,\n codeVerifier,\n });\n persistTeamAccess(options.publicKey, access);\n return access;\n}\n\nexport async function forgetTeamAccess(options: {\n apiUrl: string;\n publicKey: string;\n}): Promise<void> {\n const access = storedTeamAccess(options.publicKey);\n persistTeamAccess(options.publicKey, null);\n if (!access) {\n return;\n }\n await fetch(apiEndpoint(options.apiUrl, \"/api/v1/team/revoke\"), {\n headers: { Authorization: `Bearer ${access.token}` },\n method: \"POST\",\n });\n}\n","import { useState } from \"react\";\nimport { failureMessage } from \"../ui/session-workspace\";\nimport { signInTeam, type TeamAccess } from \"./team-auth\";\n\nexport interface TeamConnection {\n apiUrl: string;\n appUrl: string;\n publicKey: string;\n}\n\nfunction useTeamSignIn(\n connection: TeamConnection,\n onSignedIn: (access: TeamAccess) => void\n) {\n const [isSigningIn, setIsSigningIn] = useState(false);\n const [failure, setFailure] = useState<string | null>(null);\n\n const signIn = async () => {\n setIsSigningIn(true);\n setFailure(null);\n try {\n onSignedIn(await signInTeam(connection));\n } catch (error) {\n setFailure(failureMessage(error));\n }\n setIsSigningIn(false);\n };\n\n return { failure, isSigningIn, signIn };\n}\n\nfunction SignInActions({\n isSigningIn,\n onHide,\n onSignIn,\n}: {\n isSigningIn: boolean;\n onHide: () => void;\n onSignIn: () => void;\n}) {\n return (\n <div className=\"dt-actions\">\n <button\n className=\"dt-btn\"\n data-variant=\"primary\"\n disabled={isSigningIn}\n onClick={onSignIn}\n type=\"button\"\n >\n Sign in with Reflet\n </button>\n <button className=\"dt-btn\" onClick={onHide} type=\"button\">\n Hide\n </button>\n </div>\n );\n}\n\nexport function TeamSignInCard({\n connection,\n notice,\n onSignedIn,\n}: {\n connection: TeamConnection;\n notice: string | null;\n onSignedIn: (access: TeamAccess) => void;\n}) {\n const [isHidden, setIsHidden] = useState(false);\n const { failure, isSigningIn, signIn } = useTeamSignIn(\n connection,\n onSignedIn\n );\n\n if (isHidden) {\n return null;\n }\n return (\n <section aria-labelledby=\"rf-team-title\" className=\"dt-team-card glass\">\n <h2 className=\"dt-ses-subhead\" id=\"rf-team-title\">\n Reflet team tools\n </h2>\n <p className=\"dt-meta\">\n Sign in with your Reflet account to record a session on this app.\n </p>\n <p className=\"dt-meta\">\n Only members of the app's Reflet organization can sign in.\n </p>\n {notice && <p className=\"dt-meta\">{notice}</p>}\n {failure && (\n <p className=\"dt-error\" role=\"alert\">\n {failure}\n </p>\n )}\n <SignInActions\n isSigningIn={isSigningIn}\n onHide={() => setIsHidden(true)}\n onSignIn={signIn}\n />\n </section>\n );\n}\n","import { useState } from \"react\";\nimport {\n BarButton,\n DevtoolsBar,\n SessionsBarButton,\n} from \"../../devtools/client/devtools-bar\";\nimport { DevtoolsSheet } from \"../../devtools/client/devtools-sheet\";\nimport { DEVTOOLS_STYLES } from \"../../devtools/client/devtools-styles\";\nimport { Notices, reportFailure } from \"../../devtools/client/notices\";\nimport type { RefletFeedbackProps } from \"../../feedback/types\";\nimport { teamTransport } from \"../api/session-transports\";\nimport { purgeAccount } from \"../local/local-sessions\";\nimport { RecordingLayer } from \"../ui/recording/recording-layer\";\nimport { PersonIcon } from \"../ui/session-icons\";\nimport {\n type SessionWorkspace,\n SessionWorkspaceProvider,\n useWorkspaceLoader,\n type WorkspaceState,\n} from \"../ui/session-workspace\";\nimport { type SessionNavigation, SessionsPanel } from \"../ui/sessions-panel\";\nimport { SESSIONS_STYLES } from \"../ui/sessions-styles\";\nimport type { SessionTarget } from \"../ui/start/session-list\";\nimport { forgetTeamAccess, readTeamAccess, type TeamAccess } from \"./team-auth\";\nimport { type TeamConnection, TeamSignInCard } from \"./team-sign-in-card\";\n\nconst TEAM_TOOLS_LABEL = \"Reflet team tools\";\nconst REFUSED_SIGN_IN_COPY =\n \"Reflet no longer accepts this sign-in. Sign in again to record.\";\n\ninterface TeamPlacement {\n edge: \"bottom\" | \"top\";\n side: \"left\" | \"right\";\n}\n\nfunction AccountControls({\n access,\n onSignOut,\n workspace,\n}: {\n access: TeamAccess;\n onSignOut: () => void;\n workspace: SessionWorkspace | null;\n}) {\n const [isOpen, setIsOpen] = useState(false);\n const user = workspace?.account.userName ?? workspace?.account.userEmail;\n const label = user\n ? `Signed in as ${user} · ${access.organizationName}`\n : `Signed in · ${access.organizationName}`;\n return (\n <>\n <BarButton\n label={label}\n onClick={() => setIsOpen(!isOpen)}\n pressed={isOpen}\n >\n <PersonIcon />\n </BarButton>\n {isOpen && (\n <button className=\"dt-btn\" onClick={onSignOut} type=\"button\">\n Sign out\n </button>\n )}\n </>\n );\n}\n\nfunction SessionsBody({\n navigation,\n onStarted,\n state,\n}: {\n navigation: SessionNavigation;\n onStarted: () => void;\n state: WorkspaceState;\n}) {\n if (state.kind === \"ready\") {\n return <SessionsPanel navigation={navigation} onStarted={onStarted} />;\n }\n if (state.kind === \"failed\") {\n return (\n <div className=\"dt-empty\" role=\"alert\">\n <p>Sessions did not open: {state.message}</p>\n <button className=\"dt-btn\" onClick={state.retry} type=\"button\">\n Try again\n </button>\n </div>\n );\n }\n return <p className=\"dt-status\">Opening your sessions…</p>;\n}\n\nfunction TeamSheet({\n navigation,\n onOpenChange,\n side,\n state,\n}: {\n navigation: SessionNavigation;\n onOpenChange: (isOpen: boolean) => void;\n side: TeamPlacement[\"side\"];\n state: WorkspaceState;\n}) {\n return (\n <DevtoolsSheet\n label={TEAM_TOOLS_LABEL}\n onClose={() => onOpenChange(false)}\n onSelectTab={() => onOpenChange(true)}\n side={side}\n tab=\"sessions\"\n tabs={[\"sessions\"]}\n >\n <SessionsBody\n navigation={navigation}\n onStarted={() => onOpenChange(false)}\n state={state}\n />\n </DevtoolsSheet>\n );\n}\n\ninterface TeamSession {\n access: TeamAccess;\n apiUrl: string;\n onSignOut: (reason: string | null) => void;\n}\n\nfunction useTeamTransport(team: TeamSession) {\n const createTransport = () => ({\n apiUrl: team.apiUrl,\n transport: teamTransport({ apiUrl: team.apiUrl, token: team.access.token }),\n });\n const [current, setCurrent] = useState(createTransport);\n if (current.apiUrl !== team.apiUrl) {\n const next = createTransport();\n setCurrent(next);\n return next.transport;\n }\n return current.transport;\n}\n\nfunction useTeamWorkspace(team: TeamSession) {\n const { onSignOut } = team;\n const transport = useTeamTransport(team);\n const state = useWorkspaceLoader({\n onUnauthorized: () => onSignOut(REFUSED_SIGN_IN_COPY),\n surface: \"app\",\n transport,\n });\n const workspace = state.kind === \"ready\" ? state.workspace : null;\n\n const signOut = async () => {\n if (workspace) {\n await purgeAccount(workspace.account.key).catch((error: unknown) =>\n reportFailure(\"This device's session data was not removed\", error)\n );\n }\n onSignOut(null);\n };\n\n return { signOut, state, workspace };\n}\n\nfunction TeamTools({\n team,\n placement,\n}: {\n placement: TeamPlacement;\n team: TeamSession;\n}) {\n const { signOut, state, workspace } = useTeamWorkspace(team);\n const [isSheetOpen, setIsSheetOpen] = useState(false);\n const [open, setOpen] = useState<SessionTarget | null>(null);\n\n return (\n <SessionWorkspaceProvider workspace={workspace}>\n {isSheetOpen && (\n <TeamSheet\n navigation={{ onOpen: setOpen, open }}\n onOpenChange={setIsSheetOpen}\n side={placement.side}\n state={state}\n />\n )}\n <DevtoolsBar\n edge={placement.edge}\n label={TEAM_TOOLS_LABEL}\n side={placement.side}\n >\n <SessionsBarButton\n isOpen={isSheetOpen}\n onToggle={() => setIsSheetOpen(!isSheetOpen)}\n />\n <AccountControls\n access={team.access}\n onSignOut={signOut}\n workspace={workspace}\n />\n </DevtoolsBar>\n {workspace && (\n <RecordingLayer\n onFinished={(target) => {\n setOpen(target);\n setIsSheetOpen(true);\n }}\n />\n )}\n </SessionWorkspaceProvider>\n );\n}\n\nfunction placementFor(\n position: NonNullable<RefletFeedbackProps[\"position\"]>\n): TeamPlacement {\n return {\n edge: position.startsWith(\"top\") ? \"top\" : \"bottom\",\n side: position.endsWith(\"right\") ? \"right\" : \"left\",\n };\n}\n\nexport function TeamLayer({\n connection,\n position,\n}: {\n connection: TeamConnection;\n position: NonNullable<RefletFeedbackProps[\"position\"]>;\n}) {\n const { apiUrl, publicKey } = connection;\n const [access, setAccess] = useState(() => readTeamAccess(publicKey));\n const [notice, setNotice] = useState<string | null>(null);\n const signOut = (reason: string | null) => {\n setAccess(null);\n setNotice(reason);\n forgetTeamAccess({ apiUrl, publicKey }).catch((error: unknown) =>\n reportFailure(\"Signing out of Reflet did not finish\", error)\n );\n };\n const placement = placementFor(position);\n\n return (\n <div className=\"dt\">\n <style>{`${DEVTOOLS_STYLES}${SESSIONS_STYLES}`}</style>\n {access ? (\n <TeamTools\n placement={placement}\n team={{ access, apiUrl, onSignOut: signOut }}\n />\n ) : (\n <div\n className=\"dt-team-anchor\"\n data-edge={placement.edge}\n data-side={placement.side}\n >\n <TeamSignInCard\n connection={connection}\n notice={notice}\n onSignedIn={(signedIn) => {\n setAccess(signedIn);\n setNotice(null);\n }}\n />\n </div>\n )}\n <Notices />\n </div>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reflet-sdk",
3
- "version": "0.6.9",
3
+ "version": "0.7.0",
4
4
  "description": "Official Reflet SDK - Collect user feedback, feature requests, and bug reports from your app",
5
5
  "keywords": [
6
6
  "feedback",
@@ -1,391 +0,0 @@
1
- import { z } from "zod";
2
- //#region src/sessions/protocol.ts
3
- const SESSION_API_PATHS = [
4
- "/api/v1/sessions/analyze",
5
- "/api/v1/sessions/collection",
6
- "/api/v1/sessions/delete",
7
- "/api/v1/sessions/detail",
8
- "/api/v1/sessions/events",
9
- "/api/v1/sessions/evidence",
10
- "/api/v1/sessions/evidence/remove",
11
- "/api/v1/sessions/handoffs",
12
- "/api/v1/sessions/handoffs/delivered",
13
- "/api/v1/sessions/identity",
14
- "/api/v1/sessions/list",
15
- "/api/v1/sessions/proposals/clarify",
16
- "/api/v1/sessions/proposals/create",
17
- "/api/v1/sessions/proposals/decide",
18
- "/api/v1/sessions/proposals/edit",
19
- "/api/v1/sessions/proposals/feedback",
20
- "/api/v1/sessions/proposals/merge",
21
- "/api/v1/sessions/proposals/separate",
22
- "/api/v1/sessions/proposals/verify",
23
- "/api/v1/sessions/segments",
24
- "/api/v1/sessions/segments/correct",
25
- "/api/v1/sessions/segments/retry",
26
- "/api/v1/sessions/start",
27
- "/api/v1/sessions/update",
28
- "/api/v1/sessions/upload-url"
29
- ];
30
- const SESSION_SURFACES = ["devtools", "app"];
31
- const SESSION_GAP_REASONS = [
32
- "paused",
33
- "hidden",
34
- "interrupted",
35
- "limit"
36
- ];
37
- const SESSION_LIMIT_KINDS = [
38
- "duration",
39
- "segments",
40
- "events",
41
- "evidence",
42
- "bytes"
43
- ];
44
- const PROPOSAL_DECISIONS = [
45
- "pending",
46
- "kept",
47
- "later",
48
- "dismissed"
49
- ];
50
- const EVIDENCE_TRIGGERS = [
51
- "start",
52
- "mark",
53
- "navigation",
54
- "select",
55
- "manual"
56
- ];
57
- const sessionLimitsSchema = z.object({
58
- audioRetentionMs: z.number(),
59
- maxDurationMs: z.number(),
60
- maxEvents: z.number(),
61
- maxEventsPerBatch: z.number(),
62
- maxEvidence: z.number(),
63
- maxImageBytes: z.number(),
64
- maxLocalBufferBytes: z.number(),
65
- maxSegmentBytes: z.number(),
66
- maxSegmentMs: z.number(),
67
- maxSegments: z.number(),
68
- minAutoCaptureIntervalMs: z.number(),
69
- warnBeforeLimitMs: z.number()
70
- });
71
- const sessionIdentitySchema = z.object({
72
- analysisConfigured: z.boolean(),
73
- limits: sessionLimitsSchema,
74
- organizationId: z.string(),
75
- organizationName: z.string(),
76
- transcriptionConfigured: z.boolean(),
77
- user: z.object({
78
- email: z.string().nullable(),
79
- id: z.string(),
80
- name: z.string().nullable()
81
- })
82
- });
83
- const analysisStatusSchema = z.object({
84
- analyzedRevision: z.number().nullable(),
85
- error: z.string().nullable(),
86
- proposalCount: z.number().nullable(),
87
- state: z.enum([
88
- "idle",
89
- "waiting",
90
- "running",
91
- "succeeded",
92
- "failed"
93
- ]),
94
- transcriptRevision: z.number(),
95
- updatedAt: z.number().nullable()
96
- });
97
- const sessionSummarySchema = z.object({
98
- analysis: analysisStatusSchema,
99
- authorName: z.string().nullable(),
100
- collection: z.enum([
101
- "recording",
102
- "paused",
103
- "ended"
104
- ]),
105
- counts: z.object({
106
- evidence: z.number(),
107
- failedSegments: z.number(),
108
- handoffs: z.number(),
109
- kept: z.number(),
110
- pendingSegments: z.number(),
111
- proposals: z.number(),
112
- segments: z.number()
113
- }),
114
- durationMs: z.number(),
115
- endedAt: z.number().nullable(),
116
- id: z.string(),
117
- isMine: z.boolean(),
118
- startedAt: z.number(),
119
- surface: z.enum(SESSION_SURFACES),
120
- title: z.string(),
121
- visibility: z.enum(["private", "team"])
122
- });
123
- const rectSchema = z.object({
124
- height: z.number(),
125
- width: z.number(),
126
- x: z.number(),
127
- y: z.number()
128
- });
129
- const elementRefSchema = z.object({
130
- component: z.string().nullable(),
131
- label: z.string(),
132
- rect: rectSchema,
133
- role: z.string().nullable(),
134
- selector: z.string(),
135
- source: z.string().nullable()
136
- });
137
- const frameSchema = z.object({
138
- devicePixelRatio: z.number(),
139
- scrollX: z.number(),
140
- scrollY: z.number(),
141
- title: z.string().nullable(),
142
- url: z.string(),
143
- viewportHeight: z.number(),
144
- viewportWidth: z.number()
145
- });
146
- const pointerSchema = z.object({
147
- x: z.number(),
148
- y: z.number()
149
- });
150
- z.discriminatedUnion("kind", [
151
- z.object({
152
- atMs: z.number(),
153
- frame: frameSchema,
154
- kind: z.literal("navigation")
155
- }),
156
- z.object({
157
- atMs: z.number(),
158
- element: elementRefSchema,
159
- frame: frameSchema,
160
- kind: z.literal("click"),
161
- pointer: pointerSchema.nullable()
162
- }),
163
- z.object({
164
- atMs: z.number(),
165
- durationMs: z.number(),
166
- element: elementRefSchema,
167
- frame: frameSchema,
168
- kind: z.literal("dwell"),
169
- pointer: pointerSchema
170
- }),
171
- z.object({
172
- atMs: z.number(),
173
- kind: z.literal("scroll"),
174
- scrollX: z.number(),
175
- scrollY: z.number()
176
- }),
177
- z.object({
178
- atMs: z.number(),
179
- devicePixelRatio: z.number(),
180
- kind: z.literal("viewport"),
181
- viewportHeight: z.number(),
182
- viewportWidth: z.number()
183
- }),
184
- z.object({
185
- atMs: z.number(),
186
- element: elementRefSchema.nullable(),
187
- evidenceClientKey: z.string().nullable(),
188
- frame: frameSchema,
189
- kind: z.literal("mark"),
190
- note: z.string().nullable()
191
- }),
192
- z.object({
193
- atMs: z.number(),
194
- element: elementRefSchema,
195
- evidenceClientKey: z.string().nullable(),
196
- frame: frameSchema,
197
- kind: z.literal("select"),
198
- note: z.string().nullable()
199
- })
200
- ]);
201
- const sessionGapSchema = z.object({
202
- endMs: z.number().nullable(),
203
- reason: z.enum(SESSION_GAP_REASONS),
204
- startMs: z.number()
205
- });
206
- const segmentContextSchema = z.object({
207
- atMs: z.number(),
208
- evidenceId: z.string().nullable(),
209
- kind: z.enum([
210
- "mark",
211
- "select",
212
- "click",
213
- "dwell",
214
- "navigation"
215
- ]),
216
- label: z.string(),
217
- url: z.string().nullable()
218
- });
219
- const sessionSegmentSchema = z.object({
220
- audioExpiresAt: z.number().nullable(),
221
- context: z.array(segmentContextSchema),
222
- contextAmbiguous: z.boolean(),
223
- endMs: z.number(),
224
- hasAudio: z.boolean(),
225
- id: z.string(),
226
- language: z.string().nullable(),
227
- revision: z.number(),
228
- source: z.enum(["voice", "typed"]),
229
- startMs: z.number(),
230
- text: z.string(),
231
- textOrigin: z.enum(["transcription", "human"]),
232
- transcription: z.object({
233
- attempts: z.number(),
234
- error: z.string().nullable(),
235
- state: z.enum([
236
- "pending",
237
- "running",
238
- "done",
239
- "empty",
240
- "failed",
241
- "expired"
242
- ])
243
- })
244
- });
245
- const evidenceLinkSchema = z.object({
246
- access: z.string(),
247
- label: z.string(),
248
- url: z.string()
249
- });
250
- const sessionEvidenceSchema = z.object({
251
- atMs: z.number(),
252
- element: elementRefSchema.nullable(),
253
- frame: frameSchema.nullable(),
254
- id: z.string(),
255
- image: z.object({
256
- height: z.number(),
257
- url: z.string().nullable(),
258
- width: z.number()
259
- }).nullable(),
260
- kind: z.enum(["screenshot", "link"]),
261
- link: evidenceLinkSchema.nullable(),
262
- missingReason: z.string().nullable(),
263
- note: z.string().nullable(),
264
- status: z.enum(["available", "missing"]),
265
- trigger: z.enum(EVIDENCE_TRIGGERS)
266
- });
267
- const sessionProposalSchema = z.object({
268
- certainty: z.enum(["clear", "uncertain"]),
269
- clarifications: z.array(z.object({
270
- at: z.number(),
271
- text: z.string()
272
- })),
273
- decision: z.enum(PROPOSAL_DECISIONS),
274
- evidenceIds: z.array(z.string()),
275
- feedbackId: z.string().nullable(),
276
- handedOffRevision: z.number().nullable(),
277
- id: z.string(),
278
- mergedIntoId: z.string().nullable(),
279
- mergeSuggestion: z.object({
280
- reason: z.string(),
281
- withIds: z.array(z.string())
282
- }).nullable(),
283
- need: z.string(),
284
- needsReview: z.boolean(),
285
- observed: z.string(),
286
- origin: z.enum(["ai", "human"]),
287
- question: z.string().nullable(),
288
- refinement: z.object({
289
- error: z.string().nullable(),
290
- state: z.enum([
291
- "running",
292
- "failed",
293
- "outdated"
294
- ])
295
- }).nullable(),
296
- response: z.string().nullable(),
297
- revision: z.number(),
298
- segmentIds: z.array(z.string()),
299
- setAside: z.array(z.string()),
300
- unknowns: z.array(z.string()),
301
- updatedAt: z.number(),
302
- verification: z.object({
303
- at: z.number(),
304
- note: z.string().nullable(),
305
- status: z.enum(["works", "needs_work"])
306
- }).nullable()
307
- });
308
- const sessionHandoffSchema = z.object({
309
- brief: z.string(),
310
- createdAt: z.number(),
311
- deliveries: z.array(z.object({
312
- at: z.number(),
313
- kind: z.enum(["copied", "exported"])
314
- })),
315
- evidenceIds: z.array(z.string()),
316
- id: z.string(),
317
- proposals: z.array(z.object({
318
- id: z.string(),
319
- need: z.string(),
320
- revision: z.number()
321
- }))
322
- });
323
- const sessionMarkSchema = z.object({
324
- atMs: z.number(),
325
- kind: z.enum([
326
- "mark",
327
- "select",
328
- "navigation"
329
- ]),
330
- label: z.string(),
331
- note: z.string().nullable(),
332
- url: z.string().nullable()
333
- });
334
- const sessionDetailSchema = z.object({
335
- evidence: z.array(sessionEvidenceSchema),
336
- handoffs: z.array(sessionHandoffSchema),
337
- marks: z.array(sessionMarkSchema),
338
- proposals: z.array(sessionProposalSchema),
339
- segments: z.array(sessionSegmentSchema),
340
- session: sessionSummarySchema.extend({
341
- canEdit: z.boolean(),
342
- gaps: z.array(sessionGapSchema),
343
- startUrl: z.string().nullable()
344
- })
345
- });
346
- const handoffFileSchema = z.object({
347
- evidenceId: z.string(),
348
- name: z.string(),
349
- url: z.string()
350
- });
351
- const sessionApiErrorSchema = z.object({
352
- error: z.string(),
353
- limit: z.enum(SESSION_LIMIT_KINDS).optional()
354
- });
355
- //#endregion
356
- //#region src/devtools/protocol.ts
357
- const DEVTOOLS_ROUTE_BASE = "/api/reflet-devtools";
358
- /**
359
- * Sent on every devtools request. A custom header forces a CORS preflight, so
360
- * a page on another origin cannot drive the route from the developer's browser.
361
- */
362
- const DEVTOOLS_REQUEST_HEADER = "x-reflet-devtools";
363
- const DEVTOOLS_ENDPOINTS = {
364
- connectCallback: "/connect/callback",
365
- connectStart: "/connect/start",
366
- disconnect: "/connect/disconnect",
367
- proxy: "/proxy",
368
- search: "/search",
369
- source: "/source",
370
- status: "/status"
371
- };
372
- const PROXIED_API_PATHS = [
373
- "/api/v1/feedback/create",
374
- "/api/v1/feedback/item",
375
- "/api/v1/feedback/list",
376
- "/api/v1/feedback/screenshot/save",
377
- "/api/v1/feedback/screenshot/upload-url",
378
- ...SESSION_API_PATHS
379
- ];
380
- const EDITORS = [
381
- "cursor",
382
- "vscode",
383
- "webstorm",
384
- "windsurf",
385
- "zed"
386
- ];
387
- const DEVTOOLS_STATUS_MARKER = "reflet-devtools";
388
- //#endregion
389
- export { EDITORS as a, handoffFileSchema as c, sessionEvidenceSchema as d, sessionHandoffSchema as f, sessionSummarySchema as g, sessionSegmentSchema as h, DEVTOOLS_STATUS_MARKER as i, sessionApiErrorSchema as l, sessionProposalSchema as m, DEVTOOLS_REQUEST_HEADER as n, PROXIED_API_PATHS as o, sessionIdentitySchema as p, DEVTOOLS_ROUTE_BASE as r, analysisStatusSchema as s, DEVTOOLS_ENDPOINTS as t, sessionDetailSchema as u };
390
-
391
- //# sourceMappingURL=protocol-DyAiTtzM.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"protocol-DyAiTtzM.js","names":[],"sources":["../src/sessions/protocol.ts","../src/devtools/protocol.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const SESSION_API_PATHS = [\n \"/api/v1/sessions/analyze\",\n \"/api/v1/sessions/collection\",\n \"/api/v1/sessions/delete\",\n \"/api/v1/sessions/detail\",\n \"/api/v1/sessions/events\",\n \"/api/v1/sessions/evidence\",\n \"/api/v1/sessions/evidence/remove\",\n \"/api/v1/sessions/handoffs\",\n \"/api/v1/sessions/handoffs/delivered\",\n \"/api/v1/sessions/identity\",\n \"/api/v1/sessions/list\",\n \"/api/v1/sessions/proposals/clarify\",\n \"/api/v1/sessions/proposals/create\",\n \"/api/v1/sessions/proposals/decide\",\n \"/api/v1/sessions/proposals/edit\",\n \"/api/v1/sessions/proposals/feedback\",\n \"/api/v1/sessions/proposals/merge\",\n \"/api/v1/sessions/proposals/separate\",\n \"/api/v1/sessions/proposals/verify\",\n \"/api/v1/sessions/segments\",\n \"/api/v1/sessions/segments/correct\",\n \"/api/v1/sessions/segments/retry\",\n \"/api/v1/sessions/start\",\n \"/api/v1/sessions/update\",\n \"/api/v1/sessions/upload-url\",\n] as const;\n\nexport type SessionApiPath = (typeof SESSION_API_PATHS)[number];\n\nexport const SESSION_SURFACES = [\"devtools\", \"app\"] as const;\nexport type SessionSurface = (typeof SESSION_SURFACES)[number];\n\nexport const SESSION_GAP_REASONS = [\n \"paused\",\n \"hidden\",\n \"interrupted\",\n \"limit\",\n] as const;\nexport type SessionGapReason = (typeof SESSION_GAP_REASONS)[number];\n\nexport const SESSION_LIMIT_KINDS = [\n \"duration\",\n \"segments\",\n \"events\",\n \"evidence\",\n \"bytes\",\n] as const;\nexport type SessionLimitKind = (typeof SESSION_LIMIT_KINDS)[number];\n\nexport const PROPOSAL_DECISIONS = [\n \"pending\",\n \"kept\",\n \"later\",\n \"dismissed\",\n] as const;\nexport type ProposalDecision = (typeof PROPOSAL_DECISIONS)[number];\n\nexport const EVIDENCE_TRIGGERS = [\n \"start\",\n \"mark\",\n \"navigation\",\n \"select\",\n \"manual\",\n] as const;\nexport type EvidenceTrigger = (typeof EVIDENCE_TRIGGERS)[number];\n\nexport const sessionLimitsSchema = z.object({\n audioRetentionMs: z.number(),\n maxDurationMs: z.number(),\n maxEvents: z.number(),\n maxEventsPerBatch: z.number(),\n maxEvidence: z.number(),\n maxImageBytes: z.number(),\n maxLocalBufferBytes: z.number(),\n maxSegmentBytes: z.number(),\n maxSegmentMs: z.number(),\n maxSegments: z.number(),\n minAutoCaptureIntervalMs: z.number(),\n warnBeforeLimitMs: z.number(),\n});\nexport type SessionLimits = z.infer<typeof sessionLimitsSchema>;\n\nexport const sessionIdentitySchema = z.object({\n analysisConfigured: z.boolean(),\n limits: sessionLimitsSchema,\n organizationId: z.string(),\n organizationName: z.string(),\n transcriptionConfigured: z.boolean(),\n user: z.object({\n email: z.string().nullable(),\n id: z.string(),\n name: z.string().nullable(),\n }),\n});\nexport type SessionIdentity = z.infer<typeof sessionIdentitySchema>;\n\nexport const analysisStatusSchema = z.object({\n analyzedRevision: z.number().nullable(),\n error: z.string().nullable(),\n proposalCount: z.number().nullable(),\n state: z.enum([\"idle\", \"waiting\", \"running\", \"succeeded\", \"failed\"]),\n transcriptRevision: z.number(),\n updatedAt: z.number().nullable(),\n});\nexport type AnalysisStatus = z.infer<typeof analysisStatusSchema>;\n\nexport const sessionSummarySchema = z.object({\n analysis: analysisStatusSchema,\n authorName: z.string().nullable(),\n collection: z.enum([\"recording\", \"paused\", \"ended\"]),\n counts: z.object({\n evidence: z.number(),\n failedSegments: z.number(),\n handoffs: z.number(),\n kept: z.number(),\n pendingSegments: z.number(),\n proposals: z.number(),\n segments: z.number(),\n }),\n durationMs: z.number(),\n endedAt: z.number().nullable(),\n id: z.string(),\n isMine: z.boolean(),\n startedAt: z.number(),\n surface: z.enum(SESSION_SURFACES),\n title: z.string(),\n visibility: z.enum([\"private\", \"team\"]),\n});\nexport type SessionSummary = z.infer<typeof sessionSummarySchema>;\n\nconst rectSchema = z.object({\n height: z.number(),\n width: z.number(),\n x: z.number(),\n y: z.number(),\n});\n\nexport const elementRefSchema = z.object({\n component: z.string().nullable(),\n label: z.string(),\n rect: rectSchema,\n role: z.string().nullable(),\n selector: z.string(),\n source: z.string().nullable(),\n});\nexport type ElementRef = z.infer<typeof elementRefSchema>;\n\nexport const frameSchema = z.object({\n devicePixelRatio: z.number(),\n scrollX: z.number(),\n scrollY: z.number(),\n title: z.string().nullable(),\n url: z.string(),\n viewportHeight: z.number(),\n viewportWidth: z.number(),\n});\nexport type Frame = z.infer<typeof frameSchema>;\n\nconst pointerSchema = z.object({ x: z.number(), y: z.number() });\n\nexport const sessionEventSchema = z.discriminatedUnion(\"kind\", [\n z.object({\n atMs: z.number(),\n frame: frameSchema,\n kind: z.literal(\"navigation\"),\n }),\n z.object({\n atMs: z.number(),\n element: elementRefSchema,\n frame: frameSchema,\n kind: z.literal(\"click\"),\n pointer: pointerSchema.nullable(),\n }),\n z.object({\n atMs: z.number(),\n durationMs: z.number(),\n element: elementRefSchema,\n frame: frameSchema,\n kind: z.literal(\"dwell\"),\n pointer: pointerSchema,\n }),\n z.object({\n atMs: z.number(),\n kind: z.literal(\"scroll\"),\n scrollX: z.number(),\n scrollY: z.number(),\n }),\n z.object({\n atMs: z.number(),\n devicePixelRatio: z.number(),\n kind: z.literal(\"viewport\"),\n viewportHeight: z.number(),\n viewportWidth: z.number(),\n }),\n z.object({\n atMs: z.number(),\n element: elementRefSchema.nullable(),\n evidenceClientKey: z.string().nullable(),\n frame: frameSchema,\n kind: z.literal(\"mark\"),\n note: z.string().nullable(),\n }),\n z.object({\n atMs: z.number(),\n element: elementRefSchema,\n evidenceClientKey: z.string().nullable(),\n frame: frameSchema,\n kind: z.literal(\"select\"),\n note: z.string().nullable(),\n }),\n]);\nexport type SessionEvent = z.infer<typeof sessionEventSchema>;\n\nexport const sessionGapSchema = z.object({\n endMs: z.number().nullable(),\n reason: z.enum(SESSION_GAP_REASONS),\n startMs: z.number(),\n});\nexport type SessionGap = z.infer<typeof sessionGapSchema>;\n\nexport const segmentContextSchema = z.object({\n atMs: z.number(),\n evidenceId: z.string().nullable(),\n kind: z.enum([\"mark\", \"select\", \"click\", \"dwell\", \"navigation\"]),\n label: z.string(),\n url: z.string().nullable(),\n});\nexport type SegmentContext = z.infer<typeof segmentContextSchema>;\n\nexport const sessionSegmentSchema = z.object({\n audioExpiresAt: z.number().nullable(),\n context: z.array(segmentContextSchema),\n contextAmbiguous: z.boolean(),\n endMs: z.number(),\n hasAudio: z.boolean(),\n id: z.string(),\n language: z.string().nullable(),\n revision: z.number(),\n source: z.enum([\"voice\", \"typed\"]),\n startMs: z.number(),\n text: z.string(),\n textOrigin: z.enum([\"transcription\", \"human\"]),\n transcription: z.object({\n attempts: z.number(),\n error: z.string().nullable(),\n state: z.enum([\"pending\", \"running\", \"done\", \"empty\", \"failed\", \"expired\"]),\n }),\n});\nexport type SessionSegment = z.infer<typeof sessionSegmentSchema>;\n\nexport const evidenceLinkSchema = z.object({\n access: z.string(),\n label: z.string(),\n url: z.string(),\n});\nexport type EvidenceLink = z.infer<typeof evidenceLinkSchema>;\n\nexport const sessionEvidenceSchema = z.object({\n atMs: z.number(),\n element: elementRefSchema.nullable(),\n frame: frameSchema.nullable(),\n id: z.string(),\n image: z\n .object({\n height: z.number(),\n url: z.string().nullable(),\n width: z.number(),\n })\n .nullable(),\n kind: z.enum([\"screenshot\", \"link\"]),\n link: evidenceLinkSchema.nullable(),\n missingReason: z.string().nullable(),\n note: z.string().nullable(),\n status: z.enum([\"available\", \"missing\"]),\n trigger: z.enum(EVIDENCE_TRIGGERS),\n});\nexport type SessionEvidence = z.infer<typeof sessionEvidenceSchema>;\n\nexport const sessionProposalSchema = z.object({\n certainty: z.enum([\"clear\", \"uncertain\"]),\n clarifications: z.array(z.object({ at: z.number(), text: z.string() })),\n decision: z.enum(PROPOSAL_DECISIONS),\n evidenceIds: z.array(z.string()),\n feedbackId: z.string().nullable(),\n handedOffRevision: z.number().nullable(),\n id: z.string(),\n mergedIntoId: z.string().nullable(),\n mergeSuggestion: z\n .object({ reason: z.string(), withIds: z.array(z.string()) })\n .nullable(),\n need: z.string(),\n needsReview: z.boolean(),\n observed: z.string(),\n origin: z.enum([\"ai\", \"human\"]),\n question: z.string().nullable(),\n refinement: z\n .object({\n error: z.string().nullable(),\n state: z.enum([\"running\", \"failed\", \"outdated\"]),\n })\n .nullable(),\n response: z.string().nullable(),\n revision: z.number(),\n segmentIds: z.array(z.string()),\n setAside: z.array(z.string()),\n unknowns: z.array(z.string()),\n updatedAt: z.number(),\n verification: z\n .object({\n at: z.number(),\n note: z.string().nullable(),\n status: z.enum([\"works\", \"needs_work\"]),\n })\n .nullable(),\n});\nexport type SessionProposal = z.infer<typeof sessionProposalSchema>;\n\nexport const sessionHandoffSchema = z.object({\n brief: z.string(),\n createdAt: z.number(),\n deliveries: z.array(\n z.object({ at: z.number(), kind: z.enum([\"copied\", \"exported\"]) })\n ),\n evidenceIds: z.array(z.string()),\n id: z.string(),\n proposals: z.array(\n z.object({ id: z.string(), need: z.string(), revision: z.number() })\n ),\n});\nexport type SessionHandoff = z.infer<typeof sessionHandoffSchema>;\n\nexport const sessionMarkSchema = z.object({\n atMs: z.number(),\n kind: z.enum([\"mark\", \"select\", \"navigation\"]),\n label: z.string(),\n note: z.string().nullable(),\n url: z.string().nullable(),\n});\nexport type SessionMark = z.infer<typeof sessionMarkSchema>;\n\nexport const sessionDetailSchema = z.object({\n evidence: z.array(sessionEvidenceSchema),\n handoffs: z.array(sessionHandoffSchema),\n marks: z.array(sessionMarkSchema),\n proposals: z.array(sessionProposalSchema),\n segments: z.array(sessionSegmentSchema),\n session: sessionSummarySchema.extend({\n canEdit: z.boolean(),\n gaps: z.array(sessionGapSchema),\n startUrl: z.string().nullable(),\n }),\n});\nexport type SessionDetail = z.infer<typeof sessionDetailSchema>;\n\nexport const handoffFileSchema = z.object({\n evidenceId: z.string(),\n name: z.string(),\n url: z.string(),\n});\nexport type HandoffFile = z.infer<typeof handoffFileSchema>;\n\nexport const sessionApiErrorSchema = z.object({\n error: z.string(),\n limit: z.enum(SESSION_LIMIT_KINDS).optional(),\n});\n","import { SESSION_API_PATHS } from \"../sessions/protocol\";\n\nexport const DEVTOOLS_ROUTE_BASE = \"/api/reflet-devtools\";\n\n/**\n * Sent on every devtools request. A custom header forces a CORS preflight, so\n * a page on another origin cannot drive the route from the developer's browser.\n */\nexport const DEVTOOLS_REQUEST_HEADER = \"x-reflet-devtools\";\n\nexport const DEVTOOLS_ENDPOINTS = {\n connectCallback: \"/connect/callback\",\n connectStart: \"/connect/start\",\n disconnect: \"/connect/disconnect\",\n proxy: \"/proxy\",\n search: \"/search\",\n source: \"/source\",\n status: \"/status\",\n} as const;\n\nexport const PROXIED_API_PATHS = [\n \"/api/v1/feedback/create\",\n \"/api/v1/feedback/item\",\n \"/api/v1/feedback/list\",\n \"/api/v1/feedback/screenshot/save\",\n \"/api/v1/feedback/screenshot/upload-url\",\n ...SESSION_API_PATHS,\n] as const;\n\nexport const EDITORS = [\n \"cursor\",\n \"vscode\",\n \"webstorm\",\n \"windsurf\",\n \"zed\",\n] as const;\n\nexport type EditorId = (typeof EDITORS)[number];\n\nexport const DEVTOOLS_STATUS_MARKER = \"reflet-devtools\";\n\nexport type BoardAccess =\n | { kind: \"secretKey\" }\n | { kind: \"connected\"; organizationName: string }\n | { canConnect: boolean; kind: \"disconnected\" };\n\nexport interface DevtoolsStatus {\n board: BoardAccess;\n editor: EditorId;\n marker: typeof DEVTOOLS_STATUS_MARKER;\n}\n\nexport interface ConnectStartResponse {\n authorizeUrl: string;\n}\n\nexport interface DisconnectResponse {\n revoked: boolean;\n}\n\nexport interface LineRange {\n end: number;\n start: number;\n}\n\nexport interface SourceFile {\n absolutePath: string;\n code: string;\n /** 0-based, like React's frames; null when the map only knows the line. */\n column: number | null;\n /** Lines spanned by the JSX element the frame points at, 1-based. */\n elementLines: LineRange | null;\n line: number | null;\n /** Relative to the repository root, for display. */\n path: string;\n}\n\nexport interface CodeMatch {\n absolutePath: string;\n line: number;\n path: string;\n preview: string;\n}\n\nexport interface CodeSearchResult {\n matches: CodeMatch[];\n}\n\nexport interface DevtoolsErrorBody {\n error: string;\n}\n"],"mappings":";;AAEA,MAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAIA,MAAa,mBAAmB,CAAC,YAAY,KAAK;AAGlD,MAAa,sBAAsB;CACjC;CACA;CACA;CACA;AACF;AAGA,MAAa,sBAAsB;CACjC;CACA;CACA;CACA;CACA;AACF;AAGA,MAAa,qBAAqB;CAChC;CACA;CACA;CACA;AACF;AAGA,MAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;AACF;AAGA,MAAa,sBAAsB,EAAE,OAAO;CAC1C,kBAAkB,EAAE,OAAO;CAC3B,eAAe,EAAE,OAAO;CACxB,WAAW,EAAE,OAAO;CACpB,mBAAmB,EAAE,OAAO;CAC5B,aAAa,EAAE,OAAO;CACtB,eAAe,EAAE,OAAO;CACxB,qBAAqB,EAAE,OAAO;CAC9B,iBAAiB,EAAE,OAAO;CAC1B,cAAc,EAAE,OAAO;CACvB,aAAa,EAAE,OAAO;CACtB,0BAA0B,EAAE,OAAO;CACnC,mBAAmB,EAAE,OAAO;AAC9B,CAAC;AAGD,MAAa,wBAAwB,EAAE,OAAO;CAC5C,oBAAoB,EAAE,QAAQ;CAC9B,QAAQ;CACR,gBAAgB,EAAE,OAAO;CACzB,kBAAkB,EAAE,OAAO;CAC3B,yBAAyB,EAAE,QAAQ;CACnC,MAAM,EAAE,OAAO;EACb,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;EAC3B,IAAI,EAAE,OAAO;EACb,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAC5B,CAAC;AACH,CAAC;AAGD,MAAa,uBAAuB,EAAE,OAAO;CAC3C,kBAAkB,EAAE,OAAO,CAAC,CAAC,SAAS;CACtC,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAC3B,eAAe,EAAE,OAAO,CAAC,CAAC,SAAS;CACnC,OAAO,EAAE,KAAK;EAAC;EAAQ;EAAW;EAAW;EAAa;CAAQ,CAAC;CACnE,oBAAoB,EAAE,OAAO;CAC7B,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS;AACjC,CAAC;AAGD,MAAa,uBAAuB,EAAE,OAAO;CAC3C,UAAU;CACV,YAAY,EAAE,OAAO,CAAC,CAAC,SAAS;CAChC,YAAY,EAAE,KAAK;EAAC;EAAa;EAAU;CAAO,CAAC;CACnD,QAAQ,EAAE,OAAO;EACf,UAAU,EAAE,OAAO;EACnB,gBAAgB,EAAE,OAAO;EACzB,UAAU,EAAE,OAAO;EACnB,MAAM,EAAE,OAAO;EACf,iBAAiB,EAAE,OAAO;EAC1B,WAAW,EAAE,OAAO;EACpB,UAAU,EAAE,OAAO;CACrB,CAAC;CACD,YAAY,EAAE,OAAO;CACrB,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,IAAI,EAAE,OAAO;CACb,QAAQ,EAAE,QAAQ;CAClB,WAAW,EAAE,OAAO;CACpB,SAAS,EAAE,KAAK,gBAAgB;CAChC,OAAO,EAAE,OAAO;CAChB,YAAY,EAAE,KAAK,CAAC,WAAW,MAAM,CAAC;AACxC,CAAC;AAGD,MAAM,aAAa,EAAE,OAAO;CAC1B,QAAQ,EAAE,OAAO;CACjB,OAAO,EAAE,OAAO;CAChB,GAAG,EAAE,OAAO;CACZ,GAAG,EAAE,OAAO;AACd,CAAC;AAED,MAAa,mBAAmB,EAAE,OAAO;CACvC,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS;CAC/B,OAAO,EAAE,OAAO;CAChB,MAAM;CACN,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAC1B,UAAU,EAAE,OAAO;CACnB,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;AAC9B,CAAC;AAGD,MAAa,cAAc,EAAE,OAAO;CAClC,kBAAkB,EAAE,OAAO;CAC3B,SAAS,EAAE,OAAO;CAClB,SAAS,EAAE,OAAO;CAClB,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAC3B,KAAK,EAAE,OAAO;CACd,gBAAgB,EAAE,OAAO;CACzB,eAAe,EAAE,OAAO;AAC1B,CAAC;AAGD,MAAM,gBAAgB,EAAE,OAAO;CAAE,GAAG,EAAE,OAAO;CAAG,GAAG,EAAE,OAAO;AAAE,CAAC;AAE7B,EAAE,mBAAmB,QAAQ;CAC7D,EAAE,OAAO;EACP,MAAM,EAAE,OAAO;EACf,OAAO;EACP,MAAM,EAAE,QAAQ,YAAY;CAC9B,CAAC;CACD,EAAE,OAAO;EACP,MAAM,EAAE,OAAO;EACf,SAAS;EACT,OAAO;EACP,MAAM,EAAE,QAAQ,OAAO;EACvB,SAAS,cAAc,SAAS;CAClC,CAAC;CACD,EAAE,OAAO;EACP,MAAM,EAAE,OAAO;EACf,YAAY,EAAE,OAAO;EACrB,SAAS;EACT,OAAO;EACP,MAAM,EAAE,QAAQ,OAAO;EACvB,SAAS;CACX,CAAC;CACD,EAAE,OAAO;EACP,MAAM,EAAE,OAAO;EACf,MAAM,EAAE,QAAQ,QAAQ;EACxB,SAAS,EAAE,OAAO;EAClB,SAAS,EAAE,OAAO;CACpB,CAAC;CACD,EAAE,OAAO;EACP,MAAM,EAAE,OAAO;EACf,kBAAkB,EAAE,OAAO;EAC3B,MAAM,EAAE,QAAQ,UAAU;EAC1B,gBAAgB,EAAE,OAAO;EACzB,eAAe,EAAE,OAAO;CAC1B,CAAC;CACD,EAAE,OAAO;EACP,MAAM,EAAE,OAAO;EACf,SAAS,iBAAiB,SAAS;EACnC,mBAAmB,EAAE,OAAO,CAAC,CAAC,SAAS;EACvC,OAAO;EACP,MAAM,EAAE,QAAQ,MAAM;EACtB,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAC5B,CAAC;CACD,EAAE,OAAO;EACP,MAAM,EAAE,OAAO;EACf,SAAS;EACT,mBAAmB,EAAE,OAAO,CAAC,CAAC,SAAS;EACvC,OAAO;EACP,MAAM,EAAE,QAAQ,QAAQ;EACxB,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAC5B,CAAC;AACH,CAAC;AAGD,MAAa,mBAAmB,EAAE,OAAO;CACvC,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAC3B,QAAQ,EAAE,KAAK,mBAAmB;CAClC,SAAS,EAAE,OAAO;AACpB,CAAC;AAGD,MAAa,uBAAuB,EAAE,OAAO;CAC3C,MAAM,EAAE,OAAO;CACf,YAAY,EAAE,OAAO,CAAC,CAAC,SAAS;CAChC,MAAM,EAAE,KAAK;EAAC;EAAQ;EAAU;EAAS;EAAS;CAAY,CAAC;CAC/D,OAAO,EAAE,OAAO;CAChB,KAAK,EAAE,OAAO,CAAC,CAAC,SAAS;AAC3B,CAAC;AAGD,MAAa,uBAAuB,EAAE,OAAO;CAC3C,gBAAgB,EAAE,OAAO,CAAC,CAAC,SAAS;CACpC,SAAS,EAAE,MAAM,oBAAoB;CACrC,kBAAkB,EAAE,QAAQ;CAC5B,OAAO,EAAE,OAAO;CAChB,UAAU,EAAE,QAAQ;CACpB,IAAI,EAAE,OAAO;CACb,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CAC9B,UAAU,EAAE,OAAO;CACnB,QAAQ,EAAE,KAAK,CAAC,SAAS,OAAO,CAAC;CACjC,SAAS,EAAE,OAAO;CAClB,MAAM,EAAE,OAAO;CACf,YAAY,EAAE,KAAK,CAAC,iBAAiB,OAAO,CAAC;CAC7C,eAAe,EAAE,OAAO;EACtB,UAAU,EAAE,OAAO;EACnB,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;EAC3B,OAAO,EAAE,KAAK;GAAC;GAAW;GAAW;GAAQ;GAAS;GAAU;EAAS,CAAC;CAC5E,CAAC;AACH,CAAC;AAGD,MAAa,qBAAqB,EAAE,OAAO;CACzC,QAAQ,EAAE,OAAO;CACjB,OAAO,EAAE,OAAO;CAChB,KAAK,EAAE,OAAO;AAChB,CAAC;AAGD,MAAa,wBAAwB,EAAE,OAAO;CAC5C,MAAM,EAAE,OAAO;CACf,SAAS,iBAAiB,SAAS;CACnC,OAAO,YAAY,SAAS;CAC5B,IAAI,EAAE,OAAO;CACb,OAAO,EACJ,OAAO;EACN,QAAQ,EAAE,OAAO;EACjB,KAAK,EAAE,OAAO,CAAC,CAAC,SAAS;EACzB,OAAO,EAAE,OAAO;CAClB,CAAC,CAAC,CACD,SAAS;CACZ,MAAM,EAAE,KAAK,CAAC,cAAc,MAAM,CAAC;CACnC,MAAM,mBAAmB,SAAS;CAClC,eAAe,EAAE,OAAO,CAAC,CAAC,SAAS;CACnC,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAC1B,QAAQ,EAAE,KAAK,CAAC,aAAa,SAAS,CAAC;CACvC,SAAS,EAAE,KAAK,iBAAiB;AACnC,CAAC;AAGD,MAAa,wBAAwB,EAAE,OAAO;CAC5C,WAAW,EAAE,KAAK,CAAC,SAAS,WAAW,CAAC;CACxC,gBAAgB,EAAE,MAAM,EAAE,OAAO;EAAE,IAAI,EAAE,OAAO;EAAG,MAAM,EAAE,OAAO;CAAE,CAAC,CAAC;CACtE,UAAU,EAAE,KAAK,kBAAkB;CACnC,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC;CAC/B,YAAY,EAAE,OAAO,CAAC,CAAC,SAAS;CAChC,mBAAmB,EAAE,OAAO,CAAC,CAAC,SAAS;CACvC,IAAI,EAAE,OAAO;CACb,cAAc,EAAE,OAAO,CAAC,CAAC,SAAS;CAClC,iBAAiB,EACd,OAAO;EAAE,QAAQ,EAAE,OAAO;EAAG,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC;CAAE,CAAC,CAAC,CAC5D,SAAS;CACZ,MAAM,EAAE,OAAO;CACf,aAAa,EAAE,QAAQ;CACvB,UAAU,EAAE,OAAO;CACnB,QAAQ,EAAE,KAAK,CAAC,MAAM,OAAO,CAAC;CAC9B,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CAC9B,YAAY,EACT,OAAO;EACN,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;EAC3B,OAAO,EAAE,KAAK;GAAC;GAAW;GAAU;EAAU,CAAC;CACjD,CAAC,CAAC,CACD,SAAS;CACZ,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CAC9B,UAAU,EAAE,OAAO;CACnB,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC;CAC9B,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC;CAC5B,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC;CAC5B,WAAW,EAAE,OAAO;CACpB,cAAc,EACX,OAAO;EACN,IAAI,EAAE,OAAO;EACb,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,YAAY,CAAC;CACxC,CAAC,CAAC,CACD,SAAS;AACd,CAAC;AAGD,MAAa,uBAAuB,EAAE,OAAO;CAC3C,OAAO,EAAE,OAAO;CAChB,WAAW,EAAE,OAAO;CACpB,YAAY,EAAE,MACZ,EAAE,OAAO;EAAE,IAAI,EAAE,OAAO;EAAG,MAAM,EAAE,KAAK,CAAC,UAAU,UAAU,CAAC;CAAE,CAAC,CACnE;CACA,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC;CAC/B,IAAI,EAAE,OAAO;CACb,WAAW,EAAE,MACX,EAAE,OAAO;EAAE,IAAI,EAAE,OAAO;EAAG,MAAM,EAAE,OAAO;EAAG,UAAU,EAAE,OAAO;CAAE,CAAC,CACrE;AACF,CAAC;AAGD,MAAa,oBAAoB,EAAE,OAAO;CACxC,MAAM,EAAE,OAAO;CACf,MAAM,EAAE,KAAK;EAAC;EAAQ;EAAU;CAAY,CAAC;CAC7C,OAAO,EAAE,OAAO;CAChB,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAC1B,KAAK,EAAE,OAAO,CAAC,CAAC,SAAS;AAC3B,CAAC;AAGD,MAAa,sBAAsB,EAAE,OAAO;CAC1C,UAAU,EAAE,MAAM,qBAAqB;CACvC,UAAU,EAAE,MAAM,oBAAoB;CACtC,OAAO,EAAE,MAAM,iBAAiB;CAChC,WAAW,EAAE,MAAM,qBAAqB;CACxC,UAAU,EAAE,MAAM,oBAAoB;CACtC,SAAS,qBAAqB,OAAO;EACnC,SAAS,EAAE,QAAQ;EACnB,MAAM,EAAE,MAAM,gBAAgB;EAC9B,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CAChC,CAAC;AACH,CAAC;AAGD,MAAa,oBAAoB,EAAE,OAAO;CACxC,YAAY,EAAE,OAAO;CACrB,MAAM,EAAE,OAAO;CACf,KAAK,EAAE,OAAO;AAChB,CAAC;AAGD,MAAa,wBAAwB,EAAE,OAAO;CAC5C,OAAO,EAAE,OAAO;CAChB,OAAO,EAAE,KAAK,mBAAmB,CAAC,CAAC,SAAS;AAC9C,CAAC;;;AC7WD,MAAa,sBAAsB;;;;;AAMnC,MAAa,0BAA0B;AAEvC,MAAa,qBAAqB;CAChC,iBAAiB;CACjB,cAAc;CACd,YAAY;CACZ,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,QAAQ;AACV;AAEA,MAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;CACA,GAAG;AACL;AAEA,MAAa,UAAU;CACrB;CACA;CACA;CACA;CACA;AACF;AAIA,MAAa,yBAAyB"}