@tutti-os/browser-node 0.0.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/BrowserNode.tsx","../src/react/useBrowserNodeController.ts","../src/core/nodeController.ts","../src/react/useBrowserNodeWebview.ts","../src/core/webviewController.ts"],"sourcesContent":["import {\n ArrowLeftIcon,\n ArrowRightIcon,\n Badge,\n Button,\n Input,\n LaunchIcon,\n LoadingIcon,\n RefreshIcon,\n WarningLinedIcon,\n ViewportMenuSurface,\n cn,\n menuItemClassName\n} from \"@tutti-os/ui-system\";\nimport { useEffect, useRef, useState } from \"react\";\nimport type { HTMLAttributes, JSX, ReactNode } from \"react\";\nimport type { BrowserNodeFeature } from \"../core/feature.ts\";\nimport type {\n BrowserNodeNavigationPolicy,\n BrowserNodeRuntimeError,\n BrowserNodeSessionMode\n} from \"../core/types.ts\";\nimport { useBrowserNodeController } from \"./useBrowserNodeController.ts\";\nimport { useBrowserNodeWebview } from \"./useBrowserNodeWebview.ts\";\n\n// Electron needs the serialized string attribute for dynamically created webviews.\nconst browserNodeAllowPopupsAttribute = \"true\" as unknown as boolean;\n\nexport interface BrowserNodeProps {\n defaultUrl: string;\n feature: BrowserNodeFeature;\n navigationPolicy?: BrowserNodeNavigationPolicy | null;\n nodeId: string;\n onFocusRequest?: () => void;\n onNavigated?: (url: string) => void;\n profileId?: string | null;\n sessionMode?: BrowserNodeSessionMode;\n sessionPartition?: string | null;\n showHeader?: boolean;\n syncDefaultUrl?: boolean;\n}\n\nexport function BrowserNode({\n defaultUrl,\n feature,\n navigationPolicy = null,\n nodeId,\n onFocusRequest,\n onNavigated,\n profileId = null,\n sessionMode = \"shared\",\n sessionPartition = null,\n showHeader = true,\n syncDefaultUrl = false\n}: BrowserNodeProps): JSX.Element {\n const { controller, state } = useBrowserNodeController({\n defaultUrl,\n feature,\n navigationPolicy,\n nodeId,\n profileId,\n sessionMode,\n sessionPartition,\n syncDefaultUrl\n });\n const runtime = state.runtime;\n const lastNavigatedUrlRef = useRef<string | null>(\n state.runtime.url?.trim() || null\n );\n const errorMessage = runtime.error\n ? formatBrowserNodeErrorMessage(feature, runtime.error)\n : null;\n const errorStatus = runtime.error\n ? formatBrowserNodeErrorStatus(feature, runtime.error)\n : null;\n const isShowingLoadError = errorMessage !== null;\n const openExternalUrl = feature.hostApi.openExternal\n ? feature.resolveAddressInput(state.displayUrl).url\n : null;\n const {\n devToolsContextMenu,\n dismissDevToolsContextMenu,\n openDevToolsFromContextMenu,\n shouldRenderWebview,\n setWebviewRef,\n webviewKey,\n webviewPartition,\n webviewSrc\n } = useBrowserNodeWebview({\n feature,\n initialUrl: state.displayUrl,\n lifecycle: runtime.lifecycle,\n navigationPolicy,\n nodeId,\n onGuestInteraction: onFocusRequest,\n profileId,\n sessionMode,\n sessionPartition\n });\n\n useEffect(() => {\n const navigatedUrl = state.runtime.url?.trim() ?? \"\";\n if (\n !onNavigated ||\n state.runtime.isLoading ||\n state.runtime.error ||\n navigatedUrl.length === 0 ||\n navigatedUrl === \"about:blank\" ||\n lastNavigatedUrlRef.current === navigatedUrl\n ) {\n return;\n }\n\n lastNavigatedUrlRef.current = navigatedUrl;\n onNavigated(navigatedUrl);\n }, [\n onNavigated,\n state.runtime.error,\n state.runtime.isLoading,\n state.runtime.url\n ]);\n\n return (\n <div className=\"flex h-full min-h-0 flex-col overflow-hidden bg-[var(--background-panel)]\">\n {showHeader ? (\n <BrowserNodeHeader\n canGoBack={runtime.canGoBack}\n canGoForward={runtime.canGoForward}\n draftUrl={state.draftUrl}\n feature={feature}\n isCold={runtime.lifecycle === \"cold\"}\n isLoading={runtime.isLoading}\n onDraftUrlChange={(nextUrl) => controller.setDraftUrl(nextUrl)}\n onFocusRequest={onFocusRequest}\n onSubmitUrl={() => {\n void controller.submitDraftUrl().catch(() => undefined);\n }}\n onOpenExternal={\n openExternalUrl\n ? () => {\n void feature.hostApi\n .openExternal?.({ url: openExternalUrl })\n .catch(() => undefined);\n }\n : undefined\n }\n onGoBack={() => {\n void controller.goBack().catch(() => undefined);\n }}\n onGoForward={() => {\n void controller.goForward().catch(() => undefined);\n }}\n onReload={() => {\n void controller.reload().catch(() => undefined);\n }}\n />\n ) : null}\n <div className=\"relative min-h-0 flex-1 overflow-hidden bg-[var(--background-panel)]\">\n {shouldRenderWebview ? (\n <webview\n allowpopups={browserNodeAllowPopupsAttribute}\n key={webviewKey}\n ref={setWebviewRef}\n className={cn(\n \"absolute inset-0 h-full w-full border-0 bg-[var(--background-panel)]\",\n isShowingLoadError ? \"hidden pointer-events-none\" : \"visible\"\n )}\n data-browser-node-webview=\"true\"\n partition={webviewPartition}\n src={webviewSrc}\n />\n ) : null}\n {errorMessage ? (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-[var(--background-panel)] px-8 py-10 text-center\">\n <div\n className=\"flex w-full max-w-[440px] flex-col items-center\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div className=\"flex size-11 items-center justify-center rounded-full border border-[color-mix(in_srgb,var(--state-danger)_22%,transparent)] bg-[color-mix(in_srgb,var(--state-danger)_8%,transparent)] text-[var(--state-danger)]\">\n <WarningLinedIcon className=\"size-5\" />\n </div>\n <div className=\"mt-4 text-lg font-semibold text-[var(--text-primary)]\">\n {feature.i18n.t(\"loadFailed\")}\n </div>\n <div className=\"mt-1 max-w-[360px] text-sm leading-5 text-[var(--text-secondary)]\">\n {errorMessage}\n </div>\n {errorStatus ? (\n <div className=\"mt-4 rounded-full border border-border bg-[var(--transparency-block)] px-2.5 py-1 text-xs font-medium text-[var(--text-secondary)]\">\n {errorStatus}\n </div>\n ) : null}\n {openExternalUrl ? (\n <div className=\"mt-5 flex flex-wrap items-center justify-center gap-2\">\n <Button\n size=\"sm\"\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n void feature.hostApi\n .openExternal?.({ url: openExternalUrl })\n .catch(() => undefined);\n }}\n >\n <LaunchIcon className=\"size-3.5\" />\n {feature.i18n.t(\"actions.openExternal\")}\n </Button>\n </div>\n ) : null}\n </div>\n </div>\n ) : null}\n {devToolsContextMenu ? (\n <ViewportMenuSurface\n open\n className=\"w-44\"\n dismissOnEscape\n dismissOnPointerDownOutside\n onDismiss={dismissDevToolsContextMenu}\n placement={{\n type: \"point\",\n point: devToolsContextMenu,\n alignX: \"start\",\n alignY: \"start\",\n estimatedSize: {\n width: 176,\n height: 40\n }\n }}\n >\n <button\n className={cn(menuItemClassName, \"w-full\")}\n type=\"button\"\n onClick={() => {\n void openDevToolsFromContextMenu().catch(() => undefined);\n }}\n >\n {feature.i18n.t(\"actions.openDevTools\")}\n </button>\n </ViewportMenuSurface>\n ) : null}\n </div>\n </div>\n );\n}\n\nexport interface BrowserNodeWorkbenchHeaderProps {\n className?: string;\n defaultActions?: ReactNode;\n defaultUrl: string;\n dragHandleProps?: HTMLAttributes<HTMLElement>;\n feature: BrowserNodeFeature;\n nodeId: string;\n onCloseRequest?: () => void;\n onFocusRequest?: () => void;\n}\n\nexport function BrowserNodeWorkbenchHeader({\n className,\n defaultActions,\n defaultUrl,\n dragHandleProps,\n feature,\n nodeId,\n onCloseRequest,\n onFocusRequest\n}: BrowserNodeWorkbenchHeaderProps): JSX.Element {\n const { controller, state } = useBrowserNodeController({\n defaultUrl,\n feature,\n nodeId\n });\n const runtime = state.runtime;\n const openExternalUrl = feature.hostApi.openExternal\n ? feature.resolveAddressInput(state.displayUrl).url\n : null;\n\n return (\n <BrowserNodeHeader\n canGoBack={runtime.canGoBack}\n canGoForward={runtime.canGoForward}\n className={className}\n defaultActions={defaultActions}\n draftUrl={state.draftUrl}\n dragHandleProps={dragHandleProps}\n feature={feature}\n isCold={runtime.lifecycle === \"cold\"}\n isLoading={runtime.isLoading}\n onCloseRequest={onCloseRequest}\n onDraftUrlChange={(nextUrl) => controller.setDraftUrl(nextUrl)}\n onFocusRequest={onFocusRequest}\n onSubmitUrl={() => {\n void controller.submitDraftUrl().catch(() => undefined);\n }}\n onOpenExternal={\n openExternalUrl\n ? () => {\n void feature.hostApi\n .openExternal?.({ url: openExternalUrl })\n .catch(() => undefined);\n }\n : undefined\n }\n onGoBack={() => {\n void controller.goBack().catch(() => undefined);\n }}\n onGoForward={() => {\n void controller.goForward().catch(() => undefined);\n }}\n onReload={() => {\n void controller.reload().catch(() => undefined);\n }}\n withBorder={false}\n />\n );\n}\n\nexport function BrowserNodeHeader({\n canGoBack,\n canGoForward,\n className,\n defaultActions,\n draftUrl,\n dragHandleProps,\n feature,\n isCold = false,\n isLoading,\n onCloseRequest,\n onDraftUrlChange,\n onFocusRequest,\n onGoBack,\n onGoForward,\n onOpenExternal,\n onReload,\n onSubmitUrl,\n withBorder = true\n}: {\n canGoBack: boolean;\n canGoForward: boolean;\n className?: string;\n defaultActions?: ReactNode;\n draftUrl: string;\n dragHandleProps?: HTMLAttributes<HTMLElement>;\n feature: BrowserNodeFeature;\n isCold?: boolean;\n isLoading: boolean;\n onCloseRequest?: () => void;\n onDraftUrlChange: (nextUrl: string) => void;\n onFocusRequest?: () => void;\n onGoBack: () => void;\n onGoForward: () => void;\n onOpenExternal?: () => void;\n onReload: () => void;\n onSubmitUrl: () => void;\n withBorder?: boolean;\n}): JSX.Element {\n const [reloadAnimationKey, setReloadAnimationKey] = useState(0);\n\n const handleReload = (): void => {\n setReloadAnimationKey((currentKey) => currentKey + 1);\n onReload();\n };\n\n return (\n <div\n className={cn(\n \"flex h-[var(--workbench-header-height,38px)] min-h-[var(--workbench-header-height,38px)] items-center gap-2 bg-[var(--background-panel)] px-2 pl-3\",\n withBorder ? \"border-b border-border\" : null,\n className\n )}\n data-browser-node-header=\"true\"\n onDoubleClick={(event) => {\n if (\n event.target instanceof Element &&\n event.target.closest(\".nodrag\")\n ) {\n return;\n }\n event.stopPropagation();\n dragHandleProps?.onDoubleClick?.(event);\n }}\n >\n <div className=\"inline-flex items-center gap-1\">\n <BrowserNodeHeaderButton\n disabled={!canGoBack}\n label={feature.i18n.t(\"actions.back\")}\n onClick={onGoBack}\n >\n <ArrowLeftIcon className=\"size-[15px]\" />\n </BrowserNodeHeaderButton>\n <BrowserNodeHeaderButton\n disabled={!canGoForward}\n label={feature.i18n.t(\"actions.forward\")}\n onClick={onGoForward}\n >\n <ArrowRightIcon className=\"size-[15px]\" />\n </BrowserNodeHeaderButton>\n <BrowserNodeHeaderButton\n label={feature.i18n.t(\"actions.reload\")}\n onClick={handleReload}\n >\n <RefreshIcon\n key={reloadAnimationKey}\n className={cn(\n \"size-[15px]\",\n reloadAnimationKey > 0 &&\n \"motion-safe:animate-[spin_520ms_cubic-bezier(0.4,0,0.2,1)_1_reverse]\"\n )}\n />\n </BrowserNodeHeaderButton>\n </div>\n <div\n {...dragHandleProps}\n className=\"h-full w-8 shrink-0 cursor-grab active:cursor-grabbing\"\n data-browser-node-drag-gutter=\"true\"\n data-node-drag-handle=\"true\"\n aria-hidden=\"true\"\n />\n <form\n className=\"nodrag relative min-w-0 flex-1\"\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n onSubmitUrl();\n }}\n >\n <Input\n aria-label={feature.i18n.t(\"addressLabel\")}\n className=\"pr-8 focus-visible:border-input focus-visible:ring-0 focus-visible:ring-offset-0\"\n placeholder={feature.i18n.t(\"addressPlaceholder\")}\n size=\"sm\"\n value={draftUrl}\n onChange={(event) => onDraftUrlChange(event.target.value)}\n onFocus={onFocusRequest}\n />\n {isLoading ? (\n <LoadingIcon className=\"pointer-events-none absolute right-2 top-1/2 z-[1] size-4 -translate-y-1/2 animate-spin text-[var(--text-tertiary)]\" />\n ) : null}\n </form>\n {onOpenExternal ? (\n <BrowserNodeHeaderButton\n label={feature.i18n.t(\"actions.openExternal\")}\n onClick={onOpenExternal}\n >\n <LaunchIcon className=\"size-[15px]\" />\n </BrowserNodeHeaderButton>\n ) : null}\n {defaultActions ? (\n <div className=\"nodrag flex shrink-0 items-center gap-1.5\">\n {isCold ? (\n <Badge\n className=\"h-[26px] min-w-7 rounded-md text-[10px] font-semibold lowercase tracking-[0.08em]\"\n aria-label={feature.i18n.t(\"coldStatus\")}\n >\n {feature.i18n.t(\"coldStatus\")}\n </Badge>\n ) : null}\n <span\n className=\"contents\"\n onClickCapture={(event) => {\n if (\n !onCloseRequest ||\n !(event.target instanceof Element) ||\n !event.target.closest('[data-workbench-action=\"close\"]')\n ) {\n return;\n }\n onCloseRequest();\n }}\n >\n {defaultActions}\n </span>\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction formatBrowserNodeErrorMessage(\n feature: BrowserNodeFeature,\n error: BrowserNodeRuntimeError\n): string {\n switch (error.code) {\n case \"invalid-url\":\n return feature.i18n.t(\"errors.invalidUrl\", error.params);\n case \"navigation-failed\":\n if (error.params && error.params.statusCode !== undefined) {\n return feature.i18n.t(\n \"errors.navigationFailedWithStatus\",\n error.params\n );\n }\n return feature.i18n.t(\"errors.navigationFailed\", error.params);\n case \"unsupported-protocol\":\n return feature.i18n.t(\"errors.unsupportedProtocol\", error.params);\n case \"unsupported-url\":\n return feature.i18n.t(\"errors.unsupportedUrl\", error.params);\n }\n}\n\nfunction formatBrowserNodeErrorStatus(\n feature: BrowserNodeFeature,\n error: BrowserNodeRuntimeError\n): string | null {\n if (error.code !== \"navigation-failed\" || !error.params) {\n return null;\n }\n\n const statusCode = error.params.statusCode;\n if (typeof statusCode === \"number\") {\n return feature.i18n.t(\"errors.statusCode\", { statusCode });\n }\n\n const errorCode = error.params.errorCode;\n if (typeof errorCode === \"number\") {\n return feature.i18n.t(\"errors.errorCode\", { errorCode });\n }\n\n return null;\n}\n\nfunction BrowserNodeHeaderButton({\n children,\n disabled,\n label,\n onClick\n}: {\n children: ReactNode;\n disabled?: boolean;\n label: string;\n onClick: () => void;\n}) {\n return (\n <Button\n aria-label={label}\n className=\"rounded-md\"\n disabled={disabled}\n size=\"icon-sm\"\n title={label}\n type=\"button\"\n variant=\"chrome\"\n onClick={onClick}\n >\n {children}\n </Button>\n );\n}\n","import { useEffect, useMemo } from \"react\";\nimport { useExternalStoreSnapshot } from \"@tutti-os/ui-react-hooks\";\nimport type { BrowserNodeFeature } from \"../core/feature.ts\";\nimport {\n acquireBrowserNodeController,\n type BrowserNodeControllerState\n} from \"../core/nodeController.ts\";\nimport type {\n BrowserNodeNavigationPolicy,\n BrowserNodeSessionMode\n} from \"../core/types.ts\";\n\nexport function useBrowserNodeController(input: {\n defaultUrl: string;\n feature: BrowserNodeFeature;\n navigationPolicy?: BrowserNodeNavigationPolicy | null;\n nodeId: string;\n profileId?: string | null;\n sessionMode?: BrowserNodeSessionMode;\n sessionPartition?: string | null;\n syncDefaultUrl?: boolean;\n}) {\n const controller = useMemo(\n () =>\n acquireBrowserNodeController({\n defaultUrl: input.defaultUrl,\n feature: input.feature,\n navigationPolicy: input.navigationPolicy,\n nodeId: input.nodeId,\n profileId: input.profileId ?? null,\n sessionMode: input.sessionMode ?? \"shared\",\n sessionPartition: input.sessionPartition,\n syncDefaultUrl: input.syncDefaultUrl ?? false\n }),\n [\n input.defaultUrl,\n input.feature,\n input.navigationPolicy,\n input.nodeId,\n input.profileId,\n input.sessionMode,\n input.sessionPartition,\n input.syncDefaultUrl\n ]\n );\n\n useEffect(() => {\n controller.retain();\n return () => {\n controller.release();\n };\n }, [controller]);\n\n useEffect(() => {\n controller.sync();\n }, [\n controller,\n input.defaultUrl,\n input.feature,\n input.navigationPolicy,\n input.nodeId,\n input.profileId,\n input.sessionMode,\n input.sessionPartition,\n input.syncDefaultUrl\n ]);\n const state = useExternalStoreSnapshot<BrowserNodeControllerState>({\n getSnapshot() {\n return controller.getState();\n },\n subscribe(listener) {\n return controller.subscribe(listener);\n }\n });\n\n return {\n controller,\n state\n };\n}\n","import type { BrowserNodeFeature } from \"./feature.ts\";\nimport {\n normalizeBrowserComparableUrl,\n normalizeHostBrowserComparableUrl\n} from \"./url.ts\";\nimport type {\n BrowserNodeNavigationPolicy,\n BrowserNodeRuntimeState,\n BrowserNodeSessionMode\n} from \"./types.ts\";\n\nexport interface BrowserNodeControllerState {\n displayUrl: string;\n draftUrl: string;\n runtime: BrowserNodeRuntimeState;\n}\n\nexport interface BrowserNodeController {\n getState(): BrowserNodeControllerState;\n goBack(): Promise<void>;\n goForward(): Promise<void>;\n reload(): Promise<void>;\n release(): void;\n retain(): void;\n setDraftUrl(nextUrl: string): void;\n sync(): void;\n subscribe(listener: () => void): () => void;\n submitDraftUrl(): Promise<void>;\n}\n\ninterface BrowserNodeControllerContext {\n defaultUrl: string;\n feature: BrowserNodeFeature;\n navigationPolicy?: BrowserNodeNavigationPolicy | null;\n nodeId: string;\n profileId: string | null;\n sessionMode: BrowserNodeSessionMode;\n sessionPartition?: string | null;\n syncDefaultUrl: boolean;\n}\n\ninterface BrowserNodeControllerEntry {\n connectedRelease: (() => void) | null;\n controller: BrowserNodeController;\n context: BrowserNodeControllerContext;\n lastColdActivationUrl: string | null;\n listeners: Set<() => void>;\n pendingColdActivationUrl: string | null;\n refCount: number;\n runtimeUnsubscribe: (() => void) | null;\n state: BrowserNodeControllerState;\n}\n\nconst controllerRegistry = new Map<string, BrowserNodeControllerEntry>();\n\nexport function acquireBrowserNodeController(input: {\n defaultUrl: string;\n feature: BrowserNodeFeature;\n navigationPolicy?: BrowserNodeNavigationPolicy | null;\n nodeId: string;\n profileId?: string | null;\n sessionMode?: BrowserNodeSessionMode;\n sessionPartition?: string | null;\n syncDefaultUrl?: boolean;\n}): BrowserNodeController {\n const existing = controllerRegistry.get(input.nodeId);\n const entry =\n existing ??\n createBrowserNodeControllerEntry({\n defaultUrl: input.defaultUrl,\n feature: input.feature,\n navigationPolicy: input.navigationPolicy,\n nodeId: input.nodeId,\n profileId: input.profileId ?? null,\n sessionMode: input.sessionMode ?? \"shared\",\n sessionPartition: input.sessionPartition,\n syncDefaultUrl: input.syncDefaultUrl ?? false\n });\n\n entry.context = {\n defaultUrl: input.defaultUrl,\n feature: input.feature,\n navigationPolicy: input.navigationPolicy,\n nodeId: input.nodeId,\n profileId: input.profileId ?? null,\n sessionMode: input.sessionMode ?? \"shared\",\n sessionPartition: input.sessionPartition,\n syncDefaultUrl: input.syncDefaultUrl ?? false\n };\n\n if (!existing) {\n controllerRegistry.set(input.nodeId, entry);\n }\n\n reconcileBrowserNodeControllerState(entry, {\n allowAutoActivate: false,\n notifyListeners: false\n });\n\n return entry.controller;\n}\n\nfunction createBrowserNodeControllerEntry(\n context: BrowserNodeControllerContext\n): BrowserNodeControllerEntry {\n const runtime = context.feature.runtimeStore.getNodeState(context.nodeId);\n const displayUrl = resolveBrowserNodeDisplayUrl(runtime, context.defaultUrl);\n const entry = {\n connectedRelease: null,\n controller: null as unknown as BrowserNodeController,\n context,\n lastColdActivationUrl: resolveInitialLastColdActivationUrl(\n runtime,\n context.defaultUrl\n ),\n listeners: new Set(),\n pendingColdActivationUrl: null,\n refCount: 0,\n runtimeUnsubscribe: null,\n state: {\n displayUrl,\n draftUrl: displayUrl,\n runtime\n }\n } as BrowserNodeControllerEntry;\n\n entry.controller = {\n getState() {\n return entry.state;\n },\n goBack() {\n return entry.context.feature.hostApi.goBack({\n nodeId: entry.context.nodeId\n });\n },\n goForward() {\n return entry.context.feature.hostApi.goForward({\n nodeId: entry.context.nodeId\n });\n },\n reload() {\n return entry.context.feature.hostApi.reload({\n nodeId: entry.context.nodeId\n });\n },\n release() {\n entry.refCount = Math.max(0, entry.refCount - 1);\n if (entry.refCount > 0) {\n return;\n }\n\n entry.connectedRelease?.();\n entry.connectedRelease = null;\n entry.runtimeUnsubscribe?.();\n entry.runtimeUnsubscribe = null;\n controllerRegistry.delete(entry.context.nodeId);\n },\n retain() {\n entry.refCount += 1;\n if (entry.refCount > 1) {\n return;\n }\n\n if (!controllerRegistry.has(entry.context.nodeId)) {\n controllerRegistry.set(entry.context.nodeId, entry);\n }\n entry.connectedRelease = entry.context.feature.connect();\n entry.runtimeUnsubscribe = entry.context.feature.runtimeStore.subscribe(\n () => {\n reconcileBrowserNodeControllerState(entry, {\n allowAutoActivate: true,\n notifyListeners: true\n });\n }\n );\n reconcileBrowserNodeControllerState(entry, {\n allowAutoActivate: true,\n notifyListeners: true\n });\n },\n setDraftUrl(nextUrl) {\n if (entry.state.draftUrl === nextUrl) {\n return;\n }\n\n entry.state = {\n ...entry.state,\n draftUrl: nextUrl\n };\n notifyBrowserNodeControllerListeners(entry);\n },\n sync() {\n reconcileBrowserNodeControllerState(entry, {\n allowAutoActivate: true,\n notifyListeners: true\n });\n },\n subscribe(listener) {\n entry.listeners.add(listener);\n return () => {\n entry.listeners.delete(listener);\n };\n },\n async submitDraftUrl() {\n const resolved = entry.context.feature.resolveAddressInput(\n entry.state.draftUrl\n );\n if (!resolved.url) {\n return;\n }\n\n if (entry.state.draftUrl !== resolved.url) {\n entry.state = {\n ...entry.state,\n draftUrl: resolved.url\n };\n notifyBrowserNodeControllerListeners(entry);\n }\n\n await entry.context.feature.hostApi.navigate({\n navigationPolicy: entry.context.navigationPolicy,\n nodeId: entry.context.nodeId,\n url: resolved.url\n });\n }\n };\n\n return entry;\n}\n\nfunction resolveInitialLastColdActivationUrl(\n runtime: BrowserNodeRuntimeState,\n defaultUrl: string\n): string | null {\n const trimmedUrl = defaultUrl.trim();\n if (\n runtime.lifecycle === \"cold\" ||\n runtime.error !== null ||\n trimmedUrl.length === 0 ||\n trimmedUrl === \"about:blank\"\n ) {\n return null;\n }\n\n const comparableDefaultUrl = normalizeBrowserComparableUrl(trimmedUrl);\n const comparableRuntimeUrl = runtime.url\n ? normalizeBrowserComparableUrl(runtime.url)\n : null;\n return comparableDefaultUrl !== null &&\n comparableDefaultUrl === comparableRuntimeUrl\n ? trimmedUrl\n : null;\n}\n\nfunction notifyBrowserNodeControllerListeners(\n entry: BrowserNodeControllerEntry\n): void {\n for (const listener of entry.listeners) {\n listener();\n }\n}\n\nfunction resolveBrowserNodeDisplayUrl(\n runtime: BrowserNodeRuntimeState,\n defaultUrl: string\n): string {\n const resolvedRuntimeUrl = runtime.url?.trim() ?? \"\";\n return resolvedRuntimeUrl.length > 0 ? resolvedRuntimeUrl : defaultUrl;\n}\n\nfunction reconcileBrowserNodeControllerState(\n entry: BrowserNodeControllerEntry,\n options: {\n allowAutoActivate: boolean;\n notifyListeners: boolean;\n }\n): void {\n const runtime = entry.context.feature.runtimeStore.getNodeState(\n entry.context.nodeId\n );\n const displayUrl = resolveBrowserNodeDisplayUrl(\n runtime,\n entry.context.defaultUrl\n );\n const nextDraftUrl =\n displayUrl !== entry.state.displayUrl ? displayUrl : entry.state.draftUrl;\n\n const changed =\n entry.state.runtime !== runtime ||\n entry.state.displayUrl !== displayUrl ||\n entry.state.draftUrl !== nextDraftUrl;\n\n if (changed) {\n entry.state = {\n displayUrl,\n draftUrl: nextDraftUrl,\n runtime\n };\n if (options.notifyListeners) {\n notifyBrowserNodeControllerListeners(entry);\n }\n }\n\n if (options.allowAutoActivate) {\n void maybeActivateBrowserNodeDefaultUrl(entry).catch(() => undefined);\n }\n}\n\nasync function maybeActivateBrowserNodeDefaultUrl(\n entry: BrowserNodeControllerEntry\n): Promise<void> {\n const {\n defaultUrl,\n feature,\n navigationPolicy,\n nodeId,\n profileId,\n sessionMode,\n sessionPartition,\n syncDefaultUrl\n } = entry.context;\n const trimmedUrl = defaultUrl.trim();\n const comparableDefaultUrl = normalizeHostBrowserComparableUrl(trimmedUrl);\n const comparableRuntimeUrl = entry.state.runtime.url\n ? normalizeHostBrowserComparableUrl(entry.state.runtime.url)\n : null;\n const shouldActivateColdNode = entry.state.runtime.lifecycle === \"cold\";\n const shouldSyncDefaultUrl =\n syncDefaultUrl &&\n entry.state.runtime.lifecycle !== \"cold\" &&\n comparableDefaultUrl !== null &&\n (comparableRuntimeUrl !== comparableDefaultUrl ||\n entry.state.runtime.error !== null) &&\n entry.lastColdActivationUrl !== trimmedUrl;\n if (\n trimmedUrl.length === 0 ||\n trimmedUrl === \"about:blank\" ||\n entry.state.runtime.isLoading ||\n entry.pendingColdActivationUrl === trimmedUrl ||\n (!shouldActivateColdNode && !shouldSyncDefaultUrl) ||\n (shouldActivateColdNode &&\n entry.state.runtime.error !== null &&\n entry.lastColdActivationUrl === trimmedUrl)\n ) {\n return;\n }\n\n entry.pendingColdActivationUrl = trimmedUrl;\n try {\n await feature.hostApi.activate({\n navigationPolicy,\n nodeId,\n profileId,\n sessionMode,\n sessionPartition,\n url: trimmedUrl\n });\n entry.lastColdActivationUrl = trimmedUrl;\n } finally {\n if (entry.pendingColdActivationUrl === trimmedUrl) {\n entry.pendingColdActivationUrl = null;\n }\n }\n}\n","import { useCallback, useEffect, useMemo } from \"react\";\nimport { useExternalStoreSnapshot } from \"@tutti-os/ui-react-hooks\";\nimport {\n acquireBrowserNodeWebviewController,\n type BrowserNodeWebviewContextMenuPoint,\n type BrowserNodeWebviewControllerState\n} from \"../core/webviewController.ts\";\nimport type { BrowserNodeFeature } from \"../core/feature.ts\";\nimport type {\n BrowserNodeLifecycle,\n BrowserNodeNavigationPolicy,\n BrowserNodeSessionMode\n} from \"../core/types.ts\";\nimport type { BrowserNodeWebviewTag } from \"./webviewTag.ts\";\n\nexport function useBrowserNodeWebview({\n feature,\n initialUrl,\n lifecycle,\n navigationPolicy,\n nodeId,\n onGuestInteraction,\n profileId,\n sessionMode,\n sessionPartition\n}: {\n feature: BrowserNodeFeature;\n initialUrl: string;\n lifecycle: BrowserNodeLifecycle;\n navigationPolicy?: BrowserNodeNavigationPolicy | null;\n nodeId: string;\n onGuestInteraction?: () => void;\n profileId: string | null;\n sessionMode: BrowserNodeSessionMode;\n sessionPartition?: string | null;\n}): {\n devToolsContextMenu: BrowserNodeWebviewContextMenuPoint | null;\n dismissDevToolsContextMenu: () => void;\n openDevToolsFromContextMenu: () => Promise<void>;\n shouldRenderWebview: boolean;\n setWebviewRef: (element: BrowserNodeWebviewTag | null) => void;\n webviewKey: string;\n webviewPartition: string;\n webviewSrc: string;\n} {\n const controller = useMemo(\n () =>\n acquireBrowserNodeWebviewController({\n feature,\n initialUrl,\n lifecycle,\n navigationPolicy,\n nodeId,\n onGuestInteraction,\n profileId,\n sessionMode,\n sessionPartition\n }),\n [\n feature,\n initialUrl,\n lifecycle,\n navigationPolicy,\n nodeId,\n onGuestInteraction,\n profileId,\n sessionMode,\n sessionPartition\n ]\n );\n\n useEffect(() => {\n controller.retain();\n return () => {\n controller.release();\n };\n }, [controller]);\n\n useEffect(() => {\n controller.sync();\n }, [\n controller,\n initialUrl,\n lifecycle,\n navigationPolicy,\n nodeId,\n onGuestInteraction,\n profileId,\n sessionMode,\n sessionPartition\n ]);\n const state = useExternalStoreSnapshot<BrowserNodeWebviewControllerState>({\n getSnapshot() {\n return controller.getState();\n },\n subscribe(listener) {\n return controller.subscribe(listener);\n }\n });\n\n const setWebviewRef = useCallback(\n (element: BrowserNodeWebviewTag | null) => {\n controller.setWebview(element);\n },\n [controller]\n );\n const dismissDevToolsContextMenu = useCallback(() => {\n controller.dismissDevToolsContextMenu();\n }, [controller]);\n const openDevToolsFromContextMenu = useCallback(() => {\n return controller.openDevToolsFromContextMenu();\n }, [controller]);\n\n return {\n devToolsContextMenu: state.devToolsContextMenu,\n dismissDevToolsContextMenu,\n openDevToolsFromContextMenu,\n shouldRenderWebview: state.shouldRenderWebview,\n setWebviewRef,\n webviewKey: state.webviewKey,\n webviewPartition: state.webviewPartition,\n webviewSrc: state.webviewSrc\n };\n}\n","import type { BrowserNodeFeature } from \"./feature.ts\";\nimport { resolveBrowserSessionPartition } from \"./session.ts\";\nimport type {\n BrowserNodeLifecycle,\n BrowserNodeNavigationPolicy,\n BrowserNodeSessionMode\n} from \"./types.ts\";\nimport type { BrowserNodeWebviewTag } from \"../react/webviewTag.ts\";\n\nconst browserGuestUnregisterGraceMs = 250;\nconst browserNodeInitialWebviewSrc = \"about:blank\";\n\nexport interface BrowserNodeWebviewControllerState {\n devToolsContextMenu: BrowserNodeWebviewContextMenuPoint | null;\n shouldRenderWebview: boolean;\n webviewKey: string;\n webviewPartition: string;\n webviewSrc: string;\n}\n\nexport interface BrowserNodeWebviewContextMenuPoint {\n x: number;\n y: number;\n}\n\nexport interface BrowserNodeWebviewController {\n dismissDevToolsContextMenu(): void;\n getState(): BrowserNodeWebviewControllerState;\n openDevToolsFromContextMenu(): Promise<void>;\n release(): void;\n retain(): void;\n setWebview(element: BrowserNodeWebviewTag | null): void;\n sync(): void;\n subscribe(listener: () => void): () => void;\n}\n\ninterface BrowserNodeWebviewControllerContext {\n feature: BrowserNodeFeature;\n initialUrl: string;\n lifecycle: BrowserNodeLifecycle;\n navigationPolicy?: BrowserNodeNavigationPolicy | null;\n nodeId: string;\n onGuestInteraction?: () => void;\n profileId: string | null;\n sessionMode: BrowserNodeSessionMode;\n sessionPartition?: string | null;\n}\n\ninterface BrowserNodeWebviewControllerEntry {\n attachedListeners: Array<{\n event: string;\n listener: EventListener;\n }>;\n context: BrowserNodeWebviewControllerContext;\n controller: BrowserNodeWebviewController;\n listeners: Set<() => void>;\n refCount: number;\n registeredGuestId: number | null;\n registeringGuestId: number | null;\n state: BrowserNodeWebviewControllerState;\n webview: BrowserNodeWebviewTag | null;\n}\n\nconst webviewControllerRegistry = new Map<\n string,\n BrowserNodeWebviewControllerEntry\n>();\nconst pendingGuestIdsByNodeId = new Map<string, number>();\nconst pendingUnregisterTimersByNodeId = new Map<\n string,\n ReturnType<typeof globalThis.setTimeout>\n>();\n\nexport function acquireBrowserNodeWebviewController(input: {\n feature: BrowserNodeFeature;\n initialUrl: string;\n lifecycle: BrowserNodeLifecycle;\n navigationPolicy?: BrowserNodeNavigationPolicy | null;\n nodeId: string;\n onGuestInteraction?: () => void;\n profileId: string | null;\n sessionMode: BrowserNodeSessionMode;\n sessionPartition?: string | null;\n}): BrowserNodeWebviewController {\n const existing = webviewControllerRegistry.get(input.nodeId);\n const entry =\n existing ??\n createBrowserNodeWebviewControllerEntry({\n feature: input.feature,\n initialUrl: input.initialUrl,\n lifecycle: input.lifecycle,\n navigationPolicy: input.navigationPolicy,\n nodeId: input.nodeId,\n onGuestInteraction: input.onGuestInteraction,\n profileId: input.profileId,\n sessionMode: input.sessionMode,\n sessionPartition: input.sessionPartition\n });\n\n entry.context = {\n feature: input.feature,\n initialUrl: input.initialUrl,\n lifecycle: input.lifecycle,\n navigationPolicy: input.navigationPolicy,\n nodeId: input.nodeId,\n onGuestInteraction: input.onGuestInteraction,\n profileId: input.profileId,\n sessionMode: input.sessionMode,\n sessionPartition: input.sessionPartition\n };\n if (!existing) {\n webviewControllerRegistry.set(input.nodeId, entry);\n }\n return entry.controller;\n}\n\nfunction createBrowserNodeWebviewControllerEntry(\n context: BrowserNodeWebviewControllerContext\n): BrowserNodeWebviewControllerEntry {\n const state = resolveBrowserNodeWebviewControllerState(context);\n const entry = {\n attachedListeners: [],\n context,\n controller: null as unknown as BrowserNodeWebviewController,\n listeners: new Set(),\n refCount: 0,\n registeredGuestId: null,\n registeringGuestId: null,\n state,\n webview: null\n } as BrowserNodeWebviewControllerEntry;\n\n entry.controller = {\n dismissDevToolsContextMenu() {\n setBrowserNodeDevToolsContextMenu(entry, null);\n },\n getState() {\n return entry.state;\n },\n async openDevToolsFromContextMenu() {\n setBrowserNodeDevToolsContextMenu(entry, null);\n try {\n await entry.context.feature.hostApi.openDevTools?.({\n nodeId: entry.context.nodeId\n });\n } catch (error) {\n reportBrowserNodeWebviewDiagnostic(\n entry,\n \"devtools.open.failed\",\n {\n error: error instanceof Error ? error.message : String(error),\n webContentsId: readBrowserNodeWebContentsId(entry.webview),\n webviewPartition: entry.state.webviewPartition\n },\n \"warn\"\n );\n throw error;\n }\n },\n release() {\n entry.refCount = Math.max(0, entry.refCount - 1);\n if (entry.refCount > 0) {\n return;\n }\n scheduleBrowserNodeGuestUnregister(entry);\n detachBrowserNodeWebview(entry);\n webviewControllerRegistry.delete(entry.context.nodeId);\n },\n retain() {\n entry.refCount += 1;\n if (entry.refCount > 1) {\n return;\n }\n reconcileBrowserNodeWebviewControllerState(entry, {\n allowHostEffects: true,\n notifyListeners: true,\n rebindWebview: true\n });\n },\n setWebview(element) {\n if (entry.webview === element) {\n return;\n }\n detachBrowserNodeWebview(entry);\n entry.webview = element;\n attachBrowserNodeWebview(entry);\n },\n sync() {\n reconcileBrowserNodeWebviewControllerState(entry, {\n allowHostEffects: true,\n notifyListeners: true,\n rebindWebview: true\n });\n },\n subscribe(listener) {\n entry.listeners.add(listener);\n return () => {\n entry.listeners.delete(listener);\n };\n }\n };\n\n return entry;\n}\n\nfunction resolveBrowserNodeWebviewControllerState(\n context: BrowserNodeWebviewControllerContext\n): BrowserNodeWebviewControllerState {\n const webviewPartition = resolveBrowserSessionPartition({\n profileId: context.profileId,\n sessionMode: context.sessionMode,\n sessionPartition: context.sessionPartition\n });\n return {\n devToolsContextMenu: null,\n shouldRenderWebview: context.lifecycle !== \"cold\",\n webviewKey: `${context.nodeId}:${webviewPartition}`,\n webviewPartition,\n webviewSrc: browserNodeInitialWebviewSrc\n };\n}\n\nfunction reconcileBrowserNodeWebviewControllerState(\n entry: BrowserNodeWebviewControllerEntry,\n options: {\n allowHostEffects: boolean;\n notifyListeners: boolean;\n rebindWebview: boolean;\n }\n): void {\n const nextState = resolveBrowserNodeWebviewControllerState(entry.context);\n const changed =\n entry.state.devToolsContextMenu !== nextState.devToolsContextMenu ||\n entry.state.shouldRenderWebview !== nextState.shouldRenderWebview ||\n entry.state.webviewKey !== nextState.webviewKey ||\n entry.state.webviewPartition !== nextState.webviewPartition ||\n entry.state.webviewSrc !== nextState.webviewSrc;\n\n if (options.allowHostEffects) {\n if (entry.context.lifecycle === \"cold\") {\n scheduleBrowserNodeGuestUnregister(entry);\n } else {\n clearPendingBrowserNodeGuestUnregister(entry.context.nodeId);\n void entry.context.feature.hostApi\n .prepareSession({\n navigationPolicy: entry.context.navigationPolicy,\n nodeId: entry.context.nodeId,\n profileId: entry.context.profileId,\n sessionMode: entry.context.sessionMode,\n sessionPartition: entry.context.sessionPartition\n })\n .catch(() => undefined);\n }\n }\n\n if (!changed) {\n if (\n options.rebindWebview &&\n entry.webview &&\n entry.attachedListeners.length === 0\n ) {\n attachBrowserNodeWebview(entry);\n }\n return;\n }\n\n entry.state = nextState;\n detachBrowserNodeWebview(entry);\n attachBrowserNodeWebview(entry);\n if (options.notifyListeners) {\n notifyBrowserNodeWebviewControllerListeners(entry);\n }\n}\n\nfunction setBrowserNodeDevToolsContextMenu(\n entry: BrowserNodeWebviewControllerEntry,\n devToolsContextMenu: BrowserNodeWebviewContextMenuPoint | null\n): void {\n if (\n entry.state.devToolsContextMenu?.x === devToolsContextMenu?.x &&\n entry.state.devToolsContextMenu?.y === devToolsContextMenu?.y\n ) {\n return;\n }\n entry.state = {\n ...entry.state,\n devToolsContextMenu\n };\n notifyBrowserNodeWebviewControllerListeners(entry);\n}\n\nfunction notifyBrowserNodeWebviewControllerListeners(\n entry: BrowserNodeWebviewControllerEntry\n): void {\n for (const listener of entry.listeners) {\n listener();\n }\n}\n\nfunction clearPendingBrowserNodeGuestUnregister(nodeId: string): void {\n const timerId = pendingUnregisterTimersByNodeId.get(nodeId);\n if (timerId !== undefined) {\n globalThis.clearTimeout(timerId);\n pendingUnregisterTimersByNodeId.delete(nodeId);\n }\n pendingGuestIdsByNodeId.delete(nodeId);\n}\n\nfunction scheduleBrowserNodeGuestUnregister(\n entry: BrowserNodeWebviewControllerEntry\n): void {\n const guestId = entry.registeredGuestId;\n const nodeId = entry.context.nodeId;\n entry.registeringGuestId = null;\n if (guestId === null) {\n clearPendingBrowserNodeGuestUnregister(nodeId);\n return;\n }\n\n entry.registeredGuestId = null;\n clearPendingBrowserNodeGuestUnregister(nodeId);\n pendingGuestIdsByNodeId.set(nodeId, guestId);\n const timerId = globalThis.setTimeout(() => {\n pendingUnregisterTimersByNodeId.delete(nodeId);\n const pendingGuestId = pendingGuestIdsByNodeId.get(nodeId);\n pendingGuestIdsByNodeId.delete(nodeId);\n if (\n typeof pendingGuestId !== \"number\" ||\n !Number.isFinite(pendingGuestId)\n ) {\n return;\n }\n\n void entry.context.feature.hostApi\n .unregisterGuest({\n nodeId: entry.context.nodeId,\n webContentsId: pendingGuestId\n })\n .catch(() => undefined);\n }, browserGuestUnregisterGraceMs);\n pendingUnregisterTimersByNodeId.set(nodeId, timerId);\n}\n\nfunction detachBrowserNodeWebview(\n entry: BrowserNodeWebviewControllerEntry\n): void {\n if (!entry.webview) {\n entry.attachedListeners = [];\n return;\n }\n for (const record of entry.attachedListeners) {\n entry.webview.removeEventListener(record.event, record.listener);\n }\n entry.attachedListeners = [];\n}\n\nfunction attachBrowserNodeWebview(\n entry: BrowserNodeWebviewControllerEntry\n): void {\n const webview = entry.webview;\n if (!webview || !entry.state.shouldRenderWebview) {\n return;\n }\n\n const registerGuest = async (): Promise<void> => {\n const guestId = webview.getWebContentsId?.();\n if (\n typeof guestId !== \"number\" ||\n !Number.isFinite(guestId) ||\n guestId <= 0 ||\n entry.registeredGuestId === guestId ||\n entry.registeringGuestId === guestId\n ) {\n return;\n }\n\n clearPendingBrowserNodeGuestUnregister(entry.context.nodeId);\n entry.registeringGuestId = guestId;\n try {\n await entry.context.feature.hostApi.registerGuest({\n navigationPolicy: entry.context.navigationPolicy,\n nodeId: entry.context.nodeId,\n profileId: entry.context.profileId,\n sessionMode: entry.context.sessionMode,\n sessionPartition: entry.context.sessionPartition,\n webContentsId: guestId\n });\n entry.registeredGuestId = guestId;\n } finally {\n if (entry.registeringGuestId === guestId) {\n entry.registeringGuestId = null;\n }\n }\n };\n\n const handleDidAttach: EventListener = () => {\n void registerGuest().catch(() => undefined);\n };\n const handleDomReady: EventListener = () => {\n void registerGuest().catch(() => undefined);\n };\n const handleGuestInteraction: EventListener = () => {\n entry.context.onGuestInteraction?.();\n };\n const handleDevToolsContextMenu: EventListener = (event) => {\n const hasNativeContextMenu =\n entry.context.feature.hostApi.showDevToolsContextMenu !== undefined;\n const hasInlineContextMenu =\n entry.context.feature.hostApi.openDevTools !== undefined;\n if (!hasNativeContextMenu && !hasInlineContextMenu) {\n return;\n }\n event.preventDefault();\n const point = resolveBrowserNodeContextMenuPoint(event, webview);\n if (hasNativeContextMenu) {\n void entry.context.feature.hostApi\n .showDevToolsContextMenu?.({\n label: entry.context.feature.i18n.t(\"actions.openDevTools\"),\n nodeId: entry.context.nodeId,\n point\n })\n .catch((error: unknown) => {\n reportBrowserNodeWebviewDiagnostic(\n entry,\n \"devtools.native-context-menu.failed\",\n {\n error: error instanceof Error ? error.message : String(error),\n webContentsId: readBrowserNodeWebContentsId(webview),\n webviewPartition: entry.state.webviewPartition\n },\n \"warn\"\n );\n });\n return;\n }\n setBrowserNodeDevToolsContextMenu(entry, point);\n };\n\n const records = [\n { event: \"did-attach\", listener: handleDidAttach },\n { event: \"dom-ready\", listener: handleDomReady },\n { event: \"context-menu\", listener: handleDevToolsContextMenu },\n { event: \"contextmenu\", listener: handleDevToolsContextMenu },\n { event: \"focus\", listener: handleGuestInteraction },\n { event: \"ipc-message\", listener: handleGuestInteraction }\n ];\n for (const record of records) {\n webview.addEventListener(record.event, record.listener);\n }\n entry.attachedListeners = records;\n}\n\nfunction resolveBrowserNodeContextMenuPoint(\n event: Event,\n webview: BrowserNodeWebviewTag\n): BrowserNodeWebviewContextMenuPoint {\n const eventWithPoint = event as Event & {\n clientX?: unknown;\n clientY?: unknown;\n params?: { x?: unknown; y?: unknown };\n };\n const clientX = readFiniteNumber(eventWithPoint.clientX);\n const clientY = readFiniteNumber(eventWithPoint.clientY);\n if (clientX !== null && clientY !== null) {\n return { x: clientX, y: clientY };\n }\n\n const paramX = readFiniteNumber(eventWithPoint.params?.x);\n const paramY = readFiniteNumber(eventWithPoint.params?.y);\n if (paramX !== null && paramY !== null) {\n return { x: paramX, y: paramY };\n }\n\n const rect = webview.getBoundingClientRect();\n return {\n x: rect.left,\n y: rect.top\n };\n}\n\nfunction readFiniteNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction readBrowserNodeWebContentsId(\n webview: BrowserNodeWebviewTag | null\n): number | null {\n const webContentsId = webview?.getWebContentsId?.();\n return typeof webContentsId === \"number\" && Number.isFinite(webContentsId)\n ? webContentsId\n : null;\n}\n\nfunction reportBrowserNodeWebviewDiagnostic(\n entry: BrowserNodeWebviewControllerEntry,\n event: string,\n details: Record<string, unknown>,\n level: \"debug\" | \"info\" | \"warn\" | \"error\" = \"info\"\n): void {\n entry.context.feature.reportDiagnostic?.({\n details: {\n ...details,\n nodeId: entry.context.nodeId\n },\n event,\n level\n });\n}\n"],"mappings":"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","names":["useEffect","useEffect","useMemo","useExternalStoreSnapshot","useMemo","useEffect","useExternalStoreSnapshot","useEffect"]}
@@ -0,0 +1,111 @@
1
+ // src/bridge/hostPolicy.ts
2
+ function normalizeHostPattern(pattern) {
3
+ const trimmed = pattern.trim().toLowerCase();
4
+ return trimmed.length > 0 ? trimmed : null;
5
+ }
6
+ function matchesHostPattern(host, pattern) {
7
+ const normalizedPattern = normalizeHostPattern(pattern);
8
+ if (!normalizedPattern) {
9
+ return false;
10
+ }
11
+ if (normalizedPattern === "*") {
12
+ return true;
13
+ }
14
+ if (normalizedPattern.startsWith("*.")) {
15
+ const suffix = normalizedPattern.slice(1);
16
+ return host.endsWith(suffix) && host.length > suffix.length;
17
+ }
18
+ if (normalizedPattern.endsWith(".*")) {
19
+ return host.startsWith(normalizedPattern.slice(0, -1));
20
+ }
21
+ return host === normalizedPattern;
22
+ }
23
+ function isBrowserNodeBridgeHostAllowed(url, hostPatterns) {
24
+ let parsed;
25
+ try {
26
+ parsed = new URL(url);
27
+ } catch {
28
+ return false;
29
+ }
30
+ const host = parsed.hostname.trim().toLowerCase();
31
+ if (host.length === 0) {
32
+ return false;
33
+ }
34
+ return hostPatterns.some((pattern) => matchesHostPattern(host, pattern));
35
+ }
36
+
37
+ // src/bridge/buildApiTree.ts
38
+ var reservedBridgeSegments = /* @__PURE__ */ new Set([
39
+ "__proto__",
40
+ "constructor",
41
+ "prototype"
42
+ ]);
43
+ var safeBridgePropertyPattern = /^[A-Za-z_$][\w$-]*$/;
44
+ function createBridgeBranch() {
45
+ return /* @__PURE__ */ Object.create(null);
46
+ }
47
+ function assertSafeBridgeSegment(segment) {
48
+ if (reservedBridgeSegments.has(segment) || !safeBridgePropertyPattern.test(segment)) {
49
+ throw new Error(`Unsafe Browser Node bridge path segment: ${segment}`);
50
+ }
51
+ }
52
+ function ensureBranch(root, keys) {
53
+ let cursor = root;
54
+ for (const key of keys) {
55
+ assertSafeBridgeSegment(key);
56
+ if (!cursor[key] || typeof cursor[key] !== "object") {
57
+ cursor[key] = createBridgeBranch();
58
+ }
59
+ cursor = cursor[key];
60
+ }
61
+ return cursor;
62
+ }
63
+ function assignMethod({
64
+ call,
65
+ method,
66
+ root,
67
+ wrapCallable
68
+ }) {
69
+ const parts = method.name.split(".").map((part) => part.trim()).filter(Boolean);
70
+ if (parts.length === 0) {
71
+ return;
72
+ }
73
+ const methodName = parts.join(".");
74
+ const leaf = parts.pop();
75
+ if (!leaf) {
76
+ return;
77
+ }
78
+ assertSafeBridgeSegment(leaf);
79
+ const branch = ensureBranch(root, parts);
80
+ const callable = async (args) => await call(methodName, args ?? null);
81
+ branch[leaf] = wrapCallable ? wrapCallable(methodName, callable) : callable;
82
+ }
83
+ function buildBrowserNodeBridgeApiTree({
84
+ call,
85
+ currentUrl,
86
+ meta,
87
+ methods,
88
+ namespace,
89
+ wrapCallable
90
+ }) {
91
+ const normalizedNamespace = namespace.trim();
92
+ if (normalizedNamespace.length === 0) {
93
+ throw new Error("Browser Node bridge namespace is required");
94
+ }
95
+ assertSafeBridgeSegment(normalizedNamespace);
96
+ const api = createBridgeBranch();
97
+ api.meta = meta ?? { runtime: "electron", version: "1" };
98
+ for (const method of methods) {
99
+ if (!isBrowserNodeBridgeHostAllowed(currentUrl, method.hostPatterns)) {
100
+ continue;
101
+ }
102
+ assignMethod({ call, method, root: api, wrapCallable });
103
+ }
104
+ return api;
105
+ }
106
+
107
+ export {
108
+ isBrowserNodeBridgeHostAllowed,
109
+ buildBrowserNodeBridgeApiTree
110
+ };
111
+ //# sourceMappingURL=chunk-IO5AJ2R5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridge/hostPolicy.ts","../src/bridge/buildApiTree.ts"],"sourcesContent":["function normalizeHostPattern(pattern: string): string | null {\n const trimmed = pattern.trim().toLowerCase();\n return trimmed.length > 0 ? trimmed : null;\n}\n\nfunction matchesHostPattern(host: string, pattern: string): boolean {\n const normalizedPattern = normalizeHostPattern(pattern);\n if (!normalizedPattern) {\n return false;\n }\n\n if (normalizedPattern === \"*\") {\n return true;\n }\n\n if (normalizedPattern.startsWith(\"*.\")) {\n const suffix = normalizedPattern.slice(1);\n return host.endsWith(suffix) && host.length > suffix.length;\n }\n\n if (normalizedPattern.endsWith(\".*\")) {\n return host.startsWith(normalizedPattern.slice(0, -1));\n }\n\n return host === normalizedPattern;\n}\n\nexport function isBrowserNodeBridgeHostAllowed(\n url: string,\n hostPatterns: readonly string[]\n): boolean {\n let parsed: URL;\n\n try {\n parsed = new URL(url);\n } catch {\n return false;\n }\n\n const host = parsed.hostname.trim().toLowerCase();\n if (host.length === 0) {\n return false;\n }\n\n return hostPatterns.some((pattern) => matchesHostPattern(host, pattern));\n}\n","import { isBrowserNodeBridgeHostAllowed } from \"./hostPolicy.ts\";\nimport type {\n BrowserNodeBridgeApiTree,\n BrowserNodeBridgeCallable,\n BrowserNodeBridgeMeta,\n BrowserNodeBridgeMethodDescriptor,\n BrowserNodeBridgeResult\n} from \"./types.ts\";\n\ntype BridgeBranch = Record<string, unknown>;\nconst reservedBridgeSegments = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\nconst safeBridgePropertyPattern = /^[A-Za-z_$][\\w$-]*$/;\n\nfunction createBridgeBranch(): BridgeBranch {\n return Object.create(null) as BridgeBranch;\n}\n\nfunction assertSafeBridgeSegment(segment: string): void {\n if (\n reservedBridgeSegments.has(segment) ||\n !safeBridgePropertyPattern.test(segment)\n ) {\n throw new Error(`Unsafe Browser Node bridge path segment: ${segment}`);\n }\n}\n\nfunction ensureBranch(root: BridgeBranch, keys: string[]): BridgeBranch {\n let cursor = root;\n for (const key of keys) {\n assertSafeBridgeSegment(key);\n if (!cursor[key] || typeof cursor[key] !== \"object\") {\n cursor[key] = createBridgeBranch();\n }\n cursor = cursor[key] as BridgeBranch;\n }\n return cursor;\n}\n\nfunction assignMethod({\n call,\n method,\n root,\n wrapCallable\n}: {\n call: (\n method: string,\n args: unknown\n ) => Promise<BrowserNodeBridgeResult<unknown>>;\n method: BrowserNodeBridgeMethodDescriptor;\n root: BridgeBranch;\n wrapCallable?: (\n methodName: string,\n callable: BrowserNodeBridgeCallable\n ) => BrowserNodeBridgeCallable;\n}): void {\n const parts = method.name\n .split(\".\")\n .map((part) => part.trim())\n .filter(Boolean);\n if (parts.length === 0) {\n return;\n }\n\n const methodName = parts.join(\".\");\n const leaf = parts.pop();\n if (!leaf) {\n return;\n }\n assertSafeBridgeSegment(leaf);\n\n const branch = ensureBranch(root, parts);\n const callable: BrowserNodeBridgeCallable = async (args?: unknown) =>\n await call(methodName, args ?? null);\n branch[leaf] = wrapCallable ? wrapCallable(methodName, callable) : callable;\n}\n\nexport function buildBrowserNodeBridgeApiTree({\n call,\n currentUrl,\n meta,\n methods,\n namespace,\n wrapCallable\n}: {\n call: (\n method: string,\n args: unknown\n ) => Promise<BrowserNodeBridgeResult<unknown>>;\n currentUrl: string;\n meta?: BrowserNodeBridgeMeta;\n methods: readonly BrowserNodeBridgeMethodDescriptor[];\n namespace: string;\n wrapCallable?: (\n methodName: string,\n callable: BrowserNodeBridgeCallable\n ) => BrowserNodeBridgeCallable;\n}): BrowserNodeBridgeApiTree {\n const normalizedNamespace = namespace.trim();\n if (normalizedNamespace.length === 0) {\n throw new Error(\"Browser Node bridge namespace is required\");\n }\n assertSafeBridgeSegment(normalizedNamespace);\n\n const api: BridgeBranch = createBridgeBranch();\n api.meta = meta ?? { runtime: \"electron\", version: \"1\" };\n\n for (const method of methods) {\n if (!isBrowserNodeBridgeHostAllowed(currentUrl, method.hostPatterns)) {\n continue;\n }\n assignMethod({ call, method, root: api, wrapCallable });\n }\n\n return api as BrowserNodeBridgeApiTree;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,93 @@
1
+ // src/i18n/browserNodeI18n.ts
2
+ import {
3
+ createI18nRuntime,
4
+ createScopedI18nRuntime,
5
+ createScopedLocaleObjectsI18nModuleManifest
6
+ } from "@tutti-os/ui-i18n-runtime";
7
+ var browserNodeI18nNamespace = "browserNode";
8
+ var browserNodeI18nModule = createScopedLocaleObjectsI18nModuleManifest({
9
+ localeObjectByLocale: {
10
+ en: "browserNodeEn",
11
+ "zh-CN": "browserNodeZhCN"
12
+ },
13
+ name: "browser-node",
14
+ namespace: "browserNode",
15
+ sourceRoot: "packages/browser/workbench-node/src"
16
+ });
17
+ var browserNodeEn = {
18
+ actions: {
19
+ back: "Back",
20
+ forward: "Forward",
21
+ openDevTools: "Open DevTools",
22
+ openExternal: "Open in browser",
23
+ reload: "Reload"
24
+ },
25
+ addressLabel: "Address",
26
+ addressPlaceholder: "Search or enter address",
27
+ coldStatus: "Sleeping",
28
+ dockLabel: "Browser",
29
+ errors: {
30
+ invalidUrl: "Enter a valid web address.",
31
+ navigationFailed: "The page could not be loaded.",
32
+ navigationFailedWithStatus: "The page could not be loaded. HTTP {{statusCode}}.",
33
+ errorCode: "Error {{errorCode}}",
34
+ statusCode: "HTTP {{statusCode}}",
35
+ unsupportedProtocol: "This address type is not supported.",
36
+ unsupportedUrl: "This page cannot be opened."
37
+ },
38
+ loadFailed: "Page load failed",
39
+ title: "Browser"
40
+ };
41
+ var browserNodeZhCN = {
42
+ actions: {
43
+ back: "\u540E\u9000",
44
+ forward: "\u524D\u8FDB",
45
+ openDevTools: "\u6253\u5F00\u5F00\u53D1\u8005\u5DE5\u5177",
46
+ openExternal: "\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00",
47
+ reload: "\u91CD\u65B0\u52A0\u8F7D"
48
+ },
49
+ addressLabel: "\u5730\u5740",
50
+ addressPlaceholder: "\u641C\u7D22\u6216\u8F93\u5165\u5730\u5740",
51
+ coldStatus: "\u4F11\u7720\u4E2D",
52
+ dockLabel: "\u6D4F\u89C8\u5668",
53
+ errors: {
54
+ invalidUrl: "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u7F51\u5740\u3002",
55
+ navigationFailed: "\u9875\u9762\u65E0\u6CD5\u52A0\u8F7D\u3002",
56
+ navigationFailedWithStatus: "\u9875\u9762\u65E0\u6CD5\u52A0\u8F7D\u3002HTTP {{statusCode}}\u3002",
57
+ errorCode: "\u9519\u8BEF {{errorCode}}",
58
+ statusCode: "HTTP {{statusCode}}",
59
+ unsupportedProtocol: "\u4E0D\u652F\u6301\u6B64\u5730\u5740\u7C7B\u578B\u3002",
60
+ unsupportedUrl: "\u65E0\u6CD5\u6253\u5F00\u6B64\u9875\u9762\u3002"
61
+ },
62
+ loadFailed: "\u9875\u9762\u52A0\u8F7D\u5931\u8D25",
63
+ title: "\u6D4F\u89C8\u5668"
64
+ };
65
+ var browserNodeDefaults = {
66
+ en: browserNodeEn,
67
+ "zh-CN": browserNodeZhCN
68
+ };
69
+ var browserNodeI18nResources = {
70
+ en: {
71
+ [browserNodeI18nNamespace]: browserNodeDefaults.en
72
+ },
73
+ "zh-CN": {
74
+ [browserNodeI18nNamespace]: browserNodeDefaults["zh-CN"]
75
+ }
76
+ };
77
+ var defaultBrowserNodeI18n = createI18nRuntime({
78
+ dictionaries: [browserNodeI18nResources.en]
79
+ });
80
+ function createBrowserNodeI18nRuntime(runtime) {
81
+ return createScopedI18nRuntime(
82
+ runtime ?? defaultBrowserNodeI18n,
83
+ browserNodeI18nNamespace
84
+ );
85
+ }
86
+
87
+ export {
88
+ browserNodeI18nNamespace,
89
+ browserNodeI18nModule,
90
+ browserNodeI18nResources,
91
+ createBrowserNodeI18nRuntime
92
+ };
93
+ //# sourceMappingURL=chunk-IS2USG4D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/i18n/browserNodeI18n.ts"],"sourcesContent":["import {\n createI18nRuntime,\n createScopedI18nRuntime,\n createScopedLocaleObjectsI18nModuleManifest,\n type I18nDictionary,\n type I18nRuntime\n} from \"@tutti-os/ui-i18n-runtime\";\n\ntype BrowserNodeI18nLocale = \"en\" | \"zh-CN\";\nexport const browserNodeI18nNamespace = \"browserNode\";\nexport const browserNodeI18nModule =\n createScopedLocaleObjectsI18nModuleManifest({\n localeObjectByLocale: {\n en: \"browserNodeEn\",\n \"zh-CN\": \"browserNodeZhCN\"\n },\n name: \"browser-node\",\n namespace: \"browserNode\",\n sourceRoot: \"packages/browser/workbench-node/src\"\n });\n\nconst browserNodeEn = {\n actions: {\n back: \"Back\",\n forward: \"Forward\",\n openDevTools: \"Open DevTools\",\n openExternal: \"Open in browser\",\n reload: \"Reload\"\n },\n addressLabel: \"Address\",\n addressPlaceholder: \"Search or enter address\",\n coldStatus: \"Sleeping\",\n dockLabel: \"Browser\",\n errors: {\n invalidUrl: \"Enter a valid web address.\",\n navigationFailed: \"The page could not be loaded.\",\n navigationFailedWithStatus:\n \"The page could not be loaded. HTTP {{statusCode}}.\",\n errorCode: \"Error {{errorCode}}\",\n statusCode: \"HTTP {{statusCode}}\",\n unsupportedProtocol: \"This address type is not supported.\",\n unsupportedUrl: \"This page cannot be opened.\"\n },\n loadFailed: \"Page load failed\",\n title: \"Browser\"\n} as const satisfies I18nDictionary;\n\nconst browserNodeZhCN = {\n actions: {\n back: \"后退\",\n forward: \"前进\",\n openDevTools: \"打开开发者工具\",\n openExternal: \"在浏览器中打开\",\n reload: \"重新加载\"\n },\n addressLabel: \"地址\",\n addressPlaceholder: \"搜索或输入地址\",\n coldStatus: \"休眠中\",\n dockLabel: \"浏览器\",\n errors: {\n invalidUrl: \"请输入有效的网址。\",\n navigationFailed: \"页面无法加载。\",\n navigationFailedWithStatus: \"页面无法加载。HTTP {{statusCode}}。\",\n errorCode: \"错误 {{errorCode}}\",\n statusCode: \"HTTP {{statusCode}}\",\n unsupportedProtocol: \"不支持此地址类型。\",\n unsupportedUrl: \"无法打开此页面。\"\n },\n loadFailed: \"页面加载失败\",\n title: \"浏览器\"\n} as const satisfies I18nDictionary;\n\nexport type BrowserNodeI18nKey =\n | \"actions.back\"\n | \"actions.forward\"\n | \"actions.openDevTools\"\n | \"actions.openExternal\"\n | \"actions.reload\"\n | \"addressLabel\"\n | \"addressPlaceholder\"\n | \"coldStatus\"\n | \"dockLabel\"\n | \"errors.invalidUrl\"\n | \"errors.errorCode\"\n | \"errors.navigationFailed\"\n | \"errors.navigationFailedWithStatus\"\n | \"errors.statusCode\"\n | \"errors.unsupportedProtocol\"\n | \"errors.unsupportedUrl\"\n | \"loadFailed\"\n | \"title\";\n\nexport type BrowserNodeI18nRuntime = I18nRuntime<BrowserNodeI18nKey>;\n\nconst browserNodeDefaults: Record<BrowserNodeI18nLocale, I18nDictionary> = {\n en: browserNodeEn,\n \"zh-CN\": browserNodeZhCN\n};\n\nexport const browserNodeI18nResources = {\n en: {\n [browserNodeI18nNamespace]: browserNodeDefaults.en\n },\n \"zh-CN\": {\n [browserNodeI18nNamespace]: browserNodeDefaults[\"zh-CN\"]\n }\n} as const satisfies Record<BrowserNodeI18nLocale, I18nDictionary>;\n\nconst defaultBrowserNodeI18n = createI18nRuntime({\n dictionaries: [browserNodeI18nResources.en]\n});\n\nexport function createBrowserNodeI18nRuntime(\n runtime: I18nRuntime<string> | undefined\n): BrowserNodeI18nRuntime {\n return createScopedI18nRuntime<BrowserNodeI18nKey>(\n runtime ?? defaultBrowserNodeI18n,\n browserNodeI18nNamespace\n );\n}\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAGA,IAAM,2BAA2B;AACjC,IAAM,wBACX,4CAA4C;AAAA,EAC1C,sBAAsB;AAAA,IACpB,IAAI;AAAA,IACJ,SAAS;AAAA,EACX;AAAA,EACA,MAAM;AAAA,EACN,WAAW;AAAA,EACX,YAAY;AACd,CAAC;AAEH,IAAM,gBAAgB;AAAA,EACpB,SAAS;AAAA,IACP,MAAM;AAAA,IACN,SAAS;AAAA,IACT,cAAc;AAAA,IACd,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AAAA,EACA,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,QAAQ;AAAA,IACN,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,4BACE;AAAA,IACF,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,qBAAqB;AAAA,IACrB,gBAAgB;AAAA,EAClB;AAAA,EACA,YAAY;AAAA,EACZ,OAAO;AACT;AAEA,IAAM,kBAAkB;AAAA,EACtB,SAAS;AAAA,IACP,MAAM;AAAA,IACN,SAAS;AAAA,IACT,cAAc;AAAA,IACd,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AAAA,EACA,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,QAAQ;AAAA,IACN,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,4BAA4B;AAAA,IAC5B,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,qBAAqB;AAAA,IACrB,gBAAgB;AAAA,EAClB;AAAA,EACA,YAAY;AAAA,EACZ,OAAO;AACT;AAwBA,IAAM,sBAAqE;AAAA,EACzE,IAAI;AAAA,EACJ,SAAS;AACX;AAEO,IAAM,2BAA2B;AAAA,EACtC,IAAI;AAAA,IACF,CAAC,wBAAwB,GAAG,oBAAoB;AAAA,EAClD;AAAA,EACA,SAAS;AAAA,IACP,CAAC,wBAAwB,GAAG,oBAAoB,OAAO;AAAA,EACzD;AACF;AAEA,IAAM,yBAAyB,kBAAkB;AAAA,EAC/C,cAAc,CAAC,yBAAyB,EAAE;AAC5C,CAAC;AAEM,SAAS,6BACd,SACwB;AACxB,SAAO;AAAA,IACL,WAAW;AAAA,IACX;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,93 @@
1
+ // src/core/url.ts
2
+ var allowedBrowserProtocols = /* @__PURE__ */ new Set(["http:", "https:"]);
3
+ var likelyHostPattern = /^(localhost|(\d{1,3}\.){3}\d{1,3}|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})(?::\d{1,5})?(?:[/?#][^\s]*)?$/i;
4
+ var explicitProtocolPattern = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//;
5
+ var loopbackHostPattern = /^(localhost|127(?:\.\d{1,3}){3})(?::\d{1,5})?(?:[/?#][^\s]*)?$/i;
6
+ function defaultSchemeForHostInput(value) {
7
+ return loopbackHostPattern.test(value) ? "http" : "https";
8
+ }
9
+ function resolveBrowserNavigationUrl(rawUrl) {
10
+ const trimmed = rawUrl.trim();
11
+ if (trimmed.length === 0) {
12
+ return { errorCode: null, url: null };
13
+ }
14
+ if (explicitProtocolPattern.test(trimmed)) {
15
+ try {
16
+ const parsed = new URL(trimmed);
17
+ if (!allowedBrowserProtocols.has(parsed.protocol)) {
18
+ return {
19
+ errorCode: "unsupported-protocol",
20
+ errorParams: { protocol: parsed.protocol },
21
+ url: null
22
+ };
23
+ }
24
+ return { errorCode: null, url: parsed.toString() };
25
+ } catch {
26
+ return { errorCode: "invalid-url", url: null };
27
+ }
28
+ }
29
+ if (!likelyHostPattern.test(trimmed)) {
30
+ return { errorCode: "invalid-url", url: null };
31
+ }
32
+ try {
33
+ const parsed = new URL(
34
+ `${defaultSchemeForHostInput(trimmed)}://${trimmed}`
35
+ );
36
+ return { errorCode: null, url: parsed.toString() };
37
+ } catch {
38
+ return { errorCode: "invalid-url", url: null };
39
+ }
40
+ }
41
+ function resolveBrowserAddressInput(rawInput, options = {}) {
42
+ const trimmed = rawInput.trim();
43
+ if (trimmed.length === 0) {
44
+ return { errorCode: null, url: null };
45
+ }
46
+ const navigation = resolveBrowserNavigationUrl(trimmed);
47
+ if (navigation.url) {
48
+ return navigation;
49
+ }
50
+ const searchUrl = options.resolveSearchUrl?.(trimmed);
51
+ if (!searchUrl) {
52
+ return navigation;
53
+ }
54
+ return resolveBrowserNavigationUrl(searchUrl);
55
+ }
56
+ function normalizeBrowserComparableUrl(rawUrl) {
57
+ const resolved = resolveBrowserNavigationUrl(rawUrl);
58
+ return resolved.errorCode === null ? resolved.url : null;
59
+ }
60
+ function resolveHostBrowserNavigationUrl(rawUrl) {
61
+ const trimmed = rawUrl.trim();
62
+ if (trimmed.length === 0) {
63
+ return { errorCode: null, url: null };
64
+ }
65
+ if (trimmed.startsWith("file://")) {
66
+ try {
67
+ return { errorCode: null, url: new URL(trimmed).toString() };
68
+ } catch {
69
+ return { errorCode: "invalid-url", url: null };
70
+ }
71
+ }
72
+ return resolveBrowserNavigationUrl(trimmed);
73
+ }
74
+ function normalizeHostBrowserComparableUrl(rawUrl) {
75
+ const trimmed = rawUrl.trim();
76
+ if (trimmed.startsWith("file://")) {
77
+ try {
78
+ return new URL(trimmed).toString();
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+ return normalizeBrowserComparableUrl(trimmed);
84
+ }
85
+
86
+ export {
87
+ resolveBrowserNavigationUrl,
88
+ resolveBrowserAddressInput,
89
+ normalizeBrowserComparableUrl,
90
+ resolveHostBrowserNavigationUrl,
91
+ normalizeHostBrowserComparableUrl
92
+ };
93
+ //# sourceMappingURL=chunk-LVVPDNEF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/url.ts"],"sourcesContent":["const allowedBrowserProtocols = new Set([\"http:\", \"https:\"]);\nconst likelyHostPattern =\n /^(localhost|(\\d{1,3}\\.){3}\\d{1,3}|(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,})(?::\\d{1,5})?(?:[/?#][^\\s]*)?$/i;\nconst explicitProtocolPattern = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:\\/\\//;\nconst loopbackHostPattern =\n /^(localhost|127(?:\\.\\d{1,3}){3})(?::\\d{1,5})?(?:[/?#][^\\s]*)?$/i;\n\nfunction defaultSchemeForHostInput(value: string): \"http\" | \"https\" {\n return loopbackHostPattern.test(value) ? \"http\" : \"https\";\n}\n\nexport type BrowserNavigationUrlErrorCode =\n | \"invalid-url\"\n | \"unsupported-protocol\";\n\nexport interface BrowserNavigationUrlResolution {\n errorCode: BrowserNavigationUrlErrorCode | null;\n errorParams?: Record<string, string>;\n url: string | null;\n}\n\nexport type BrowserSearchUrlResolver = (query: string) => string | null;\n\nexport function resolveBrowserNavigationUrl(\n rawUrl: string\n): BrowserNavigationUrlResolution {\n const trimmed = rawUrl.trim();\n if (trimmed.length === 0) {\n return { errorCode: null, url: null };\n }\n\n if (explicitProtocolPattern.test(trimmed)) {\n try {\n const parsed = new URL(trimmed);\n if (!allowedBrowserProtocols.has(parsed.protocol)) {\n return {\n errorCode: \"unsupported-protocol\",\n errorParams: { protocol: parsed.protocol },\n url: null\n };\n }\n\n return { errorCode: null, url: parsed.toString() };\n } catch {\n return { errorCode: \"invalid-url\", url: null };\n }\n }\n\n if (!likelyHostPattern.test(trimmed)) {\n return { errorCode: \"invalid-url\", url: null };\n }\n\n try {\n const parsed = new URL(\n `${defaultSchemeForHostInput(trimmed)}://${trimmed}`\n );\n return { errorCode: null, url: parsed.toString() };\n } catch {\n return { errorCode: \"invalid-url\", url: null };\n }\n}\n\nexport type BrowserAddressInputResolution = BrowserNavigationUrlResolution;\n\nexport function resolveBrowserAddressInput(\n rawInput: string,\n options: {\n resolveSearchUrl?: BrowserSearchUrlResolver;\n } = {}\n): BrowserAddressInputResolution {\n const trimmed = rawInput.trim();\n if (trimmed.length === 0) {\n return { errorCode: null, url: null };\n }\n\n const navigation = resolveBrowserNavigationUrl(trimmed);\n if (navigation.url) {\n return navigation;\n }\n\n const searchUrl = options.resolveSearchUrl?.(trimmed);\n if (!searchUrl) {\n return navigation;\n }\n\n return resolveBrowserNavigationUrl(searchUrl);\n}\n\nexport function normalizeBrowserComparableUrl(rawUrl: string): string | null {\n const resolved = resolveBrowserNavigationUrl(rawUrl);\n return resolved.errorCode === null ? resolved.url : null;\n}\n\nexport function resolveHostBrowserNavigationUrl(\n rawUrl: string\n): BrowserNavigationUrlResolution {\n const trimmed = rawUrl.trim();\n if (trimmed.length === 0) {\n return { errorCode: null, url: null };\n }\n\n if (trimmed.startsWith(\"file://\")) {\n try {\n return { errorCode: null, url: new URL(trimmed).toString() };\n } catch {\n return { errorCode: \"invalid-url\", url: null };\n }\n }\n\n return resolveBrowserNavigationUrl(trimmed);\n}\n\nexport function normalizeHostBrowserComparableUrl(\n rawUrl: string\n): string | null {\n const trimmed = rawUrl.trim();\n if (trimmed.startsWith(\"file://\")) {\n try {\n return new URL(trimmed).toString();\n } catch {\n return null;\n }\n }\n\n return normalizeBrowserComparableUrl(trimmed);\n}\n"],"mappings":";AAAA,IAAM,0BAA0B,oBAAI,IAAI,CAAC,SAAS,QAAQ,CAAC;AAC3D,IAAM,oBACJ;AACF,IAAM,0BAA0B;AAChC,IAAM,sBACJ;AAEF,SAAS,0BAA0B,OAAiC;AAClE,SAAO,oBAAoB,KAAK,KAAK,IAAI,SAAS;AACpD;AAcO,SAAS,4BACd,QACgC;AAChC,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,EAAE,WAAW,MAAM,KAAK,KAAK;AAAA,EACtC;AAEA,MAAI,wBAAwB,KAAK,OAAO,GAAG;AACzC,QAAI;AACF,YAAM,SAAS,IAAI,IAAI,OAAO;AAC9B,UAAI,CAAC,wBAAwB,IAAI,OAAO,QAAQ,GAAG;AACjD,eAAO;AAAA,UACL,WAAW;AAAA,UACX,aAAa,EAAE,UAAU,OAAO,SAAS;AAAA,UACzC,KAAK;AAAA,QACP;AAAA,MACF;AAEA,aAAO,EAAE,WAAW,MAAM,KAAK,OAAO,SAAS,EAAE;AAAA,IACnD,QAAQ;AACN,aAAO,EAAE,WAAW,eAAe,KAAK,KAAK;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI,CAAC,kBAAkB,KAAK,OAAO,GAAG;AACpC,WAAO,EAAE,WAAW,eAAe,KAAK,KAAK;AAAA,EAC/C;AAEA,MAAI;AACF,UAAM,SAAS,IAAI;AAAA,MACjB,GAAG,0BAA0B,OAAO,CAAC,MAAM,OAAO;AAAA,IACpD;AACA,WAAO,EAAE,WAAW,MAAM,KAAK,OAAO,SAAS,EAAE;AAAA,EACnD,QAAQ;AACN,WAAO,EAAE,WAAW,eAAe,KAAK,KAAK;AAAA,EAC/C;AACF;AAIO,SAAS,2BACd,UACA,UAEI,CAAC,GAC0B;AAC/B,QAAM,UAAU,SAAS,KAAK;AAC9B,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,EAAE,WAAW,MAAM,KAAK,KAAK;AAAA,EACtC;AAEA,QAAM,aAAa,4BAA4B,OAAO;AACtD,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,QAAQ,mBAAmB,OAAO;AACpD,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AAEA,SAAO,4BAA4B,SAAS;AAC9C;AAEO,SAAS,8BAA8B,QAA+B;AAC3E,QAAM,WAAW,4BAA4B,MAAM;AACnD,SAAO,SAAS,cAAc,OAAO,SAAS,MAAM;AACtD;AAEO,SAAS,gCACd,QACgC;AAChC,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,EAAE,WAAW,MAAM,KAAK,KAAK;AAAA,EACtC;AAEA,MAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,QAAI;AACF,aAAO,EAAE,WAAW,MAAM,KAAK,IAAI,IAAI,OAAO,EAAE,SAAS,EAAE;AAAA,IAC7D,QAAQ;AACN,aAAO,EAAE,WAAW,eAAe,KAAK,KAAK;AAAA,IAC/C;AAAA,EACF;AAEA,SAAO,4BAA4B,OAAO;AAC5C;AAEO,SAAS,kCACd,QACe;AACf,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,QAAI;AACF,aAAO,IAAI,IAAI,OAAO,EAAE,SAAS;AAAA,IACnC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,8BAA8B,OAAO;AAC9C;","names":[]}
@@ -0,0 +1,34 @@
1
+ // src/core/session.ts
2
+ var browserSharedPartition = "persist:browser-node-shared";
3
+ var browserIncognitoPartition = "browser-node-incognito";
4
+ var browserProfilePartitionPrefix = "persist:browser-node-profile-";
5
+ function resolveBrowserSessionPartition({
6
+ profileId,
7
+ sessionMode,
8
+ sessionPartition
9
+ }) {
10
+ const normalizedSessionPartition = sessionPartition?.trim() ?? "";
11
+ if (normalizedSessionPartition.length > 0) {
12
+ return normalizedSessionPartition;
13
+ }
14
+ if (sessionMode === "incognito") {
15
+ return browserIncognitoPartition;
16
+ }
17
+ const normalizedProfileId = profileId?.trim() ?? "";
18
+ if (sessionMode === "profile" && normalizedProfileId.length > 0) {
19
+ return `${browserProfilePartitionPrefix}${normalizedProfileId}`;
20
+ }
21
+ return browserSharedPartition;
22
+ }
23
+ function isBrowserSessionPartitionAllowed(value, options = {}) {
24
+ const partition = value?.trim() ?? "";
25
+ return partition === browserIncognitoPartition || partition === browserSharedPartition || partition.startsWith(browserProfilePartitionPrefix) || options.additionalAllowedPrefixes?.some(
26
+ (prefix) => partition.startsWith(prefix)
27
+ ) === true;
28
+ }
29
+
30
+ export {
31
+ resolveBrowserSessionPartition,
32
+ isBrowserSessionPartitionAllowed
33
+ };
34
+ //# sourceMappingURL=chunk-UTXZLRPE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/session.ts"],"sourcesContent":["import type { BrowserNodeSessionMode } from \"./types.ts\";\n\nconst browserSharedPartition = \"persist:browser-node-shared\";\nconst browserIncognitoPartition = \"browser-node-incognito\";\nconst browserProfilePartitionPrefix = \"persist:browser-node-profile-\";\n\nexport function resolveBrowserSessionPartition({\n profileId,\n sessionMode,\n sessionPartition\n}: BrowserNodeSessionPartitionInput): string {\n const normalizedSessionPartition = sessionPartition?.trim() ?? \"\";\n if (normalizedSessionPartition.length > 0) {\n return normalizedSessionPartition;\n }\n\n if (sessionMode === \"incognito\") {\n return browserIncognitoPartition;\n }\n\n const normalizedProfileId = profileId?.trim() ?? \"\";\n if (sessionMode === \"profile\" && normalizedProfileId.length > 0) {\n return `${browserProfilePartitionPrefix}${normalizedProfileId}`;\n }\n\n return browserSharedPartition;\n}\n\nexport interface BrowserNodeSessionPartitionInput {\n profileId: string | null;\n sessionMode: BrowserNodeSessionMode;\n sessionPartition?: string | null;\n}\n\nexport function isBrowserSessionPartitionAllowed(\n value: string | undefined,\n options: BrowserSessionPartitionAllowedOptions = {}\n): boolean {\n const partition = value?.trim() ?? \"\";\n return (\n partition === browserIncognitoPartition ||\n partition === browserSharedPartition ||\n partition.startsWith(browserProfilePartitionPrefix) ||\n options.additionalAllowedPrefixes?.some((prefix) =>\n partition.startsWith(prefix)\n ) === true\n );\n}\n\nexport interface BrowserSessionPartitionAllowedOptions {\n additionalAllowedPrefixes?: readonly string[];\n}\n"],"mappings":";AAEA,IAAM,yBAAyB;AAC/B,IAAM,4BAA4B;AAClC,IAAM,gCAAgC;AAE/B,SAAS,+BAA+B;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AACF,GAA6C;AAC3C,QAAM,6BAA6B,kBAAkB,KAAK,KAAK;AAC/D,MAAI,2BAA2B,SAAS,GAAG;AACzC,WAAO;AAAA,EACT;AAEA,MAAI,gBAAgB,aAAa;AAC/B,WAAO;AAAA,EACT;AAEA,QAAM,sBAAsB,WAAW,KAAK,KAAK;AACjD,MAAI,gBAAgB,aAAa,oBAAoB,SAAS,GAAG;AAC/D,WAAO,GAAG,6BAA6B,GAAG,mBAAmB;AAAA,EAC/D;AAEA,SAAO;AACT;AAQO,SAAS,iCACd,OACA,UAAiD,CAAC,GACzC;AACT,QAAM,YAAY,OAAO,KAAK,KAAK;AACnC,SACE,cAAc,6BACd,cAAc,0BACd,UAAU,WAAW,6BAA6B,KAClD,QAAQ,2BAA2B;AAAA,IAAK,CAAC,WACvC,UAAU,WAAW,MAAM;AAAA,EAC7B,MAAM;AAEV;","names":[]}