chromium-tabs 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -1
- package/dist/chunk-NDM7JYY6.js +40 -0
- package/dist/chunk-NDM7JYY6.js.map +1 -0
- package/dist/{chunk-2DTGNBUT.js → chunk-XUPBZES2.js} +7 -38
- package/dist/chunk-XUPBZES2.js.map +1 -0
- package/dist/command-storage-backend-BLy7EP79.d.cts +318 -0
- package/dist/command-storage-backend-CDoXkRtc.d.ts +318 -0
- package/dist/core/index.d.cts +5 -629
- package/dist/core/index.d.ts +5 -629
- package/dist/core/index.js +7 -5
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/session/index.cjs +1406 -0
- package/dist/session/index.cjs.map +1 -0
- package/dist/session/index.d.cts +479 -0
- package/dist/session/index.d.ts +479 -0
- package/dist/session/index.js +1320 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/node.cjs +76 -0
- package/dist/session/node.cjs.map +1 -0
- package/dist/session/node.d.cts +34 -0
- package/dist/session/node.d.ts +34 -0
- package/dist/session/node.js +51 -0
- package/dist/session/node.js.map +1 -0
- package/dist/tab-strip-model-DSFYavJO.d.cts +496 -0
- package/dist/tab-strip-model-JBpyUyRs.d.ts +496 -0
- package/dist/types-CYa7ouKw.d.cts +137 -0
- package/dist/types-CYa7ouKw.d.ts +137 -0
- package/package.json +12 -1
- package/dist/chunk-2DTGNBUT.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/session/index.ts","../../src/session/command-storage-manager.ts","../../src/session/backends/in-memory.ts","../../src/session/process-singleton.ts","../../src/session/backends/web-storage.ts","../../src/core/types.ts","../../src/session/session-service-commands.ts","../../src/session/session-restore.ts","../../src/session/session-types.ts","../../src/session/session-service.ts"],"sourcesContent":["export type { CommandStorageBackend, ReadCommandsResult } from './command-storage-backend'\nexport {\n CommandStorageManager,\n SAVE_DELAY_MS,\n type CommandStorageManagerDelegate,\n type CommandStorageManagerOptions,\n} from './command-storage-manager'\nexport { InMemoryStorageBackend } from './backends/in-memory'\nexport {\n WebLocksProcessSingleton,\n createDefaultProcessSingleton,\n type ProcessSingleton,\n type ProcessSingletonResult,\n type WebLocksProcessSingletonOptions,\n} from './process-singleton'\nexport { WebStorageBackend, type WebStorageBackendOptions } from './backends/web-storage'\nexport {\n SessionCommandId,\n createAddTabExtraDataCommand,\n createAddWindowExtraDataCommand,\n createLastActiveTimeCommand,\n createPinnedStateCommand,\n createSetActiveWindowCommand,\n createSetSelectedNavigationIndexCommand,\n createSetSelectedTabInWindowCommand,\n createSetTabDataCommand,\n createSetTabIndexInWindowCommand,\n createSetTabWindowCommand,\n createTabClosedCommand,\n createTabGroupCommand,\n createTabGroupMetadataUpdateCommand,\n createTabNavigationPathPrunedCommand,\n createUpdateTabNavigationCommand,\n createWindowClosedCommand,\n findClosestNavigationWithIndex,\n isClosingCommand,\n processTabNavigationPathPruned,\n restoreSessionFromCommands,\n type SessionCommand,\n} from './session-service-commands'\nexport {\n MAX_PERSISTED_NAVIGATIONS,\n SessionService,\n WRITES_PER_RESET,\n type AttachOptions,\n type RestoreIntoOptions,\n type RestoreIntoResult,\n type SessionServiceOptions,\n} from './session-service'\nexport { restoreSessionWindow, type RestoreOptions, type RestoreResult } from './session-restore'\nexport {\n DEFAULT_WINDOW_ID,\n currentNavigationEntry,\n type SerializedNavigationEntry,\n type SessionSnapshot,\n type SessionTab,\n type SessionTabGroup,\n type SessionWindow,\n type SessionWindowId,\n} from './session-types'\n","/**\n * Buffers commands and flushes them to a storage backend on a delay.\n * Ported from chromium-reference/components/sessions/core/command_storage_manager.cc.\n *\n * Chrome posts backend writes to a sequenced task runner; this port chains\n * them on a promise queue so writes land in order even when the backend is\n * asynchronous. Saves snapshot the pending buffer synchronously, exactly\n * like Chrome moving pending_commands_ into the posted task (cc:251).\n */\n\nimport type { CommandStorageBackend } from './command-storage-backend'\nimport type { SessionCommand } from './session-service-commands'\n\n/** Delay before pending commands are written. Mirrors kSaveDelay (cc:36). */\nexport const SAVE_DELAY_MS = 2500\n\n/** Mirrors CommandStorageManagerDelegate (command_storage_manager_delegate.h). */\nexport interface CommandStorageManagerDelegate {\n /** Called before each save; a chance to append more commands (cc:255). */\n onWillSaveCommands?(): void\n /** A backend write failed; schedule a full rebuild (delegate.h:29). */\n onErrorWritingSessionCommands?(): void\n}\n\nexport interface CommandStorageManagerOptions {\n backend: CommandStorageBackend\n delegate?: CommandStorageManagerDelegate\n saveDelayMs?: number\n /**\n * Awaited before the first backend write — the SessionService uses this to\n * make sure the previous session has been rotated to the \"last\" slot\n * before this session's first (truncating) write can clobber it.\n */\n ready?: Promise<unknown>\n}\n\nexport class CommandStorageManager {\n private readonly backend_: CommandStorageBackend\n private readonly delegate_: CommandStorageManagerDelegate\n private readonly saveDelayMs_: number\n\n private pendingCommands_: SessionCommand[] = []\n /** Starts true: the first save is always a complete rewrite (cc:117). */\n private pendingReset_ = true\n private commandsSinceReset_ = 0\n private saveTimer_: ReturnType<typeof setTimeout> | null = null\n /** Sequenced \"task runner\" for backend writes. */\n private queue_: Promise<void> = Promise.resolve()\n /** Backend operations started but not yet settled. */\n private inflight_ = 0\n\n constructor(options: CommandStorageManagerOptions) {\n this.backend_ = options.backend\n this.delegate_ = options.delegate ?? {}\n this.saveDelayMs_ = options.saveDelayMs ?? SAVE_DELAY_MS\n if (options.ready) {\n // The gate counts as an in-flight operation so nothing overtakes it.\n this.inflight_++\n this.queue_ = options.ready.then(\n () => {\n this.inflight_--\n },\n () => {\n this.inflight_--\n },\n )\n }\n }\n\n /**\n * Runs a backend operation. When nothing is in flight it runs inline — a\n * synchronous backend then completes before this returns, which is what\n * makes a pagehide flush reliable (the web stand-in for Chrome's\n * BLOCK_SHUTDOWN task traits). Otherwise it is sequenced behind the queue.\n */\n private enqueue_(operation: () => void | Promise<void>): Promise<void> {\n const wasIdle = this.inflight_ === 0\n this.inflight_++\n if (wasIdle) {\n let result: void | Promise<void>\n try {\n result = operation()\n } catch {\n this.inflight_--\n this.delegate_.onErrorWritingSessionCommands?.()\n return this.queue_\n }\n if (result && typeof result.then === 'function') {\n this.queue_ = result.then(\n () => {\n this.inflight_--\n },\n () => {\n this.inflight_--\n this.delegate_.onErrorWritingSessionCommands?.()\n },\n )\n } else {\n this.inflight_--\n }\n return this.queue_\n }\n this.queue_ = this.queue_\n .then(() => operation())\n .then(\n () => {\n this.inflight_--\n },\n () => {\n this.inflight_--\n this.delegate_.onErrorWritingSessionCommands?.()\n },\n )\n return this.queue_\n }\n\n get pendingReset(): boolean {\n return this.pendingReset_\n }\n\n /** Mirrors set_pending_reset (command_storage_manager.h:77). */\n setPendingReset(value: boolean): void {\n this.pendingReset_ = value\n }\n\n get commandsSinceReset(): number {\n return this.commandsSinceReset_\n }\n\n /** Buffers a command and starts the save timer. Mirrors cc:192. */\n scheduleCommand(command: SessionCommand): void {\n this.commandsSinceReset_++\n this.pendingCommands_.push(command)\n this.startSaveTimer()\n }\n\n /** Buffers rebuild commands without starting the timer. Mirrors cc:207. */\n appendRebuildCommands(commands: readonly SessionCommand[]): void {\n this.commandsSinceReset_ += commands.length\n this.pendingCommands_.push(...commands)\n }\n\n appendRebuildCommand(command: SessionCommand): void {\n this.appendRebuildCommands([command])\n }\n\n /** Removes a not-yet-saved command. Mirrors EraseCommand (cc:216). */\n eraseCommand(command: SessionCommand): void {\n const i = this.pendingCommands_.indexOf(command)\n if (i === -1) throw new Error('eraseCommand: command is not pending')\n this.pendingCommands_.splice(i, 1)\n this.commandsSinceReset_--\n }\n\n /** Replaces a not-yet-saved command in place. Mirrors SwapCommand (cc:225). */\n swapCommand(oldCommand: SessionCommand, newCommand: SessionCommand): void {\n const i = this.pendingCommands_.indexOf(oldCommand)\n if (i === -1) throw new Error('swapCommand: command is not pending')\n this.pendingCommands_[i] = newCommand\n }\n\n /** Mirrors ClearPendingCommands (cc:233) — note it does not zero the reset counter. */\n clearPendingCommands(): void {\n this.commandsSinceReset_ -= this.pendingCommands_.length\n this.pendingCommands_ = []\n }\n\n /** Read-only view for ReplacePendingCommand-style optimizations. */\n get pendingCommands(): readonly SessionCommand[] {\n return this.pendingCommands_\n }\n\n /** True between startSaveTimer() and the save it scheduled. Mirrors cc:316. */\n get hasPendingSave(): boolean {\n return this.saveTimer_ !== null\n }\n\n /** Schedules a save in saveDelayMs unless one is already pending. Mirrors cc:239. */\n startSaveTimer(): void {\n if (this.saveTimer_ !== null) return\n this.saveTimer_ = setTimeout(() => {\n this.saveTimer_ = null\n this.save()\n }, this.saveDelayMs_)\n }\n\n /**\n * Flushes pending commands to the backend. Mirrors Save (cc:251): the\n * buffer and reset flag are snapshotted synchronously; the write itself is\n * sequenced behind earlier writes. Returns once this write has settled.\n */\n save(): Promise<void> {\n if (this.saveTimer_ !== null) {\n clearTimeout(this.saveTimer_)\n this.saveTimer_ = null\n }\n\n this.delegate_.onWillSaveCommands?.()\n\n if (this.pendingCommands_.length === 0) return this.queue_\n\n const commands = this.pendingCommands_\n const truncate = this.pendingReset_\n this.pendingCommands_ = []\n if (this.pendingReset_) {\n this.commandsSinceReset_ = 0\n this.pendingReset_ = false\n }\n\n // Errors land in onErrorWritingSessionCommands (mirrors\n // OnErrorWritingToFile): surviving state is recovered by a full rebuild\n // on the next save, so the lost commands don't matter.\n return this.enqueue_(() => this.backend_.appendCommands(commands, truncate))\n }\n\n /** Cancels any timer and flushes immediately. */\n saveNow(): Promise<void> {\n return this.save()\n }\n\n /**\n * Promotes the current session to \"last\". Pending commands are flushed\n * first, mirroring MoveCurrentSessionToLastSession (cc:320). The caller is\n * expected to follow up with setPendingReset(true) plus a full re-emit of\n * live state, since the current session is now empty.\n */\n moveCurrentSessionToLastSession(): Promise<void> {\n this.save()\n return this.enqueue_(() => this.backend_.moveCurrentSessionToLastSession())\n }\n\n /** Stops the save timer without flushing. */\n dispose(): void {\n if (this.saveTimer_ !== null) {\n clearTimeout(this.saveTimer_)\n this.saveTimer_ = null\n }\n }\n}\n","/**\n * In-memory backend: no persistence beyond the object's lifetime. Useful for\n * tests, server-side rendering, and as the reference implementation of the\n * two-slot current/last contract.\n */\n\nimport type { CommandStorageBackend, ReadCommandsResult } from '../command-storage-backend'\nimport type { SessionCommand } from '../session-service-commands'\n\nexport class InMemoryStorageBackend implements CommandStorageBackend {\n private current_: SessionCommand[] | null = null\n private last_: SessionCommand[] | null = null\n\n appendCommands(commands: readonly SessionCommand[], truncate: boolean): void {\n if (truncate || this.current_ === null) this.current_ = []\n this.current_.push(...commands.map((c) => ({ ...c })))\n }\n\n readLastSessionCommands(): ReadCommandsResult {\n return { commands: (this.last_ ?? []).map((c) => ({ ...c })), errorReading: false }\n }\n\n moveCurrentSessionToLastSession(): void {\n if (this.current_ !== null) {\n this.last_ = this.current_\n this.current_ = null\n }\n }\n\n /** Test hook: the commands persisted for the current session, if any. */\n get currentSessionCommands(): readonly SessionCommand[] | null {\n return this.current_\n }\n\n /** Test hook: the commands persisted for the last session, if any. */\n get lastSessionCommands(): readonly SessionCommand[] | null {\n return this.last_\n }\n}\n","/**\n * One session writer per profile. Ported from\n * chromium-reference/chrome/browser/process_singleton.h.\n *\n * Chrome never lets two processes share a profile directory: ProcessSingleton\n * is \"named according to the user data directory, so we can be sure that no\n * more than one copy of the application can be running at once with a given\n * data directory\" (h:45). A second launch gets PROCESS_NOTIFIED and stands\n * down; only the PROCESS_NONE winner runs a SessionService over the profile.\n *\n * Two browser tabs of the same app are the web's version of two processes\n * sharing a profile. The web cannot make the second tab exit, but it can make\n * it stand down: the loser becomes a \"secondary\" SessionService with saving\n * disabled (the SetSavingEnabled(false) state, session_service_base.cc:877)\n * that neither rotates nor writes the shared log.\n *\n * The claim is made once, at startup, exactly like Chrome's — there is no\n * mid-life takeover. When the owner dies its lock evaporates with the realm\n * (the Web Lock analog of the OS reclaiming Chrome's SingletonLock), and the\n * next realm to BOOT over that profile claims it and restores the session.\n */\n\n/** 'owner' ~ PROCESS_NONE, 'secondary' ~ PROCESS_NOTIFIED (h:85). */\nexport type ProcessSingletonResult = 'owner' | 'secondary'\n\nexport interface ProcessSingleton {\n /**\n * Attempts to become the singleton for the profile. Resolves exactly once;\n * repeated calls return the same promise. Mirrors\n * NotifyOtherProcessOrCreate (h:119).\n */\n acquire(): Promise<ProcessSingletonResult>\n /** Releases the claim if held. Mirrors Cleanup (h:133). Idempotent. */\n release(): void\n}\n\nexport interface WebLocksProcessSingletonOptions {\n /** Lock name. One storage area (profile) = one name. */\n name: string\n /** Injectable for tests / non-window realms. Defaults to navigator.locks. */\n locks?: LockManager\n}\n\n/**\n * First attempt immediately, then short re-checks. Chrome's analog is\n * kRetryAttempts/kTimeoutInSeconds (process_singleton_posix.cc:137-140).\n */\nconst CLAIM_ATTEMPT_DELAYS_MS = [0, 0, 50]\n\n/**\n * ProcessSingleton over the Web Locks API. The owner holds an exclusive lock\n * named for the profile until release() or realm death; contenders ask with\n * ifAvailable and stand down instead of queueing, mirroring Chrome's\n * startup-time claim.\n */\nexport class WebLocksProcessSingleton implements ProcessSingleton {\n private readonly name_: string\n private readonly locks_: LockManager | undefined\n private acquirePromise_: Promise<ProcessSingletonResult> | null = null\n private releaseHold_: (() => void) | null = null\n private released_ = false\n\n constructor(options: WebLocksProcessSingletonOptions) {\n this.name_ = options.name\n this.locks_ =\n options.locks ?? (typeof navigator !== 'undefined' ? navigator.locks : undefined)\n }\n\n acquire(): Promise<ProcessSingletonResult> {\n if (!this.acquirePromise_) this.acquirePromise_ = this.acquireWithRetries_()\n return this.acquirePromise_\n }\n\n /**\n * Claim with bounded retries. Chrome's POSIX singleton retries its claim\n * the same way — kRetryAttempts with timeout/retry_attempts sleeps\n * (process_singleton_posix.cc:137-140, :794) — because a dying holder's\n * lock disappears asynchronously. Web Lock releases also propagate\n * asynchronously (the holder resolves its callback promise), so a realm\n * booting right after another one released (HMR, dispose-then-reconstruct)\n * needs a beat before the lock reads as free. A lock still held after the\n * final attempt belongs to a live realm: stand down.\n */\n private async acquireWithRetries_(): Promise<ProcessSingletonResult> {\n const locks = this.locks_\n if (!locks) {\n // No lock manager: behave as the sole instance, today's single-realm\n // behavior. (Chrome treats an unusable singleton as fatal — LOCK_ERROR\n // — but a library breaking persistence outright would be worse than\n // the vanishingly rare double-owner.)\n return this.released_ ? 'secondary' : 'owner'\n }\n for (const delayMs of CLAIM_ATTEMPT_DELAYS_MS) {\n if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs))\n if (this.released_) return 'secondary'\n const result = await this.tryClaim_(locks)\n if (result === 'owner') return 'owner'\n }\n return 'secondary'\n }\n\n private tryClaim_(locks: LockManager): Promise<ProcessSingletonResult> {\n return new Promise<ProcessSingletonResult>((resolve) => {\n locks\n .request(this.name_, { ifAvailable: true }, (lock) => {\n if (lock === null) {\n resolve('secondary')\n return undefined\n }\n if (this.released_) {\n // Released before the grant arrived: let the lock go right away.\n resolve('secondary')\n return undefined\n }\n return new Promise<void>((releaseHold) => {\n this.releaseHold_ = releaseHold\n resolve('owner')\n })\n })\n .catch(() => {\n // Lock manager failure: same sole-instance fallback as above.\n resolve('owner')\n })\n })\n }\n\n release(): void {\n this.released_ = true\n if (this.releaseHold_) {\n this.releaseHold_()\n this.releaseHold_ = null\n }\n }\n}\n\n/**\n * The default wiring: Web Locks keyed by the backend's profile name when the\n * platform has them, otherwise sole ownership (SSR, Node, older browsers —\n * today's behavior).\n */\nexport function createDefaultProcessSingleton(\n profileLockName: string | undefined,\n): ProcessSingleton | null {\n if (!profileLockName) return null\n if (typeof navigator === 'undefined' || !navigator.locks) return null\n return new WebLocksProcessSingleton({ name: profileLockName })\n}\n","/**\n * Web Storage backend: persists the command log to localStorage (default) or\n * sessionStorage. Both slots hold a JSON envelope `{version, commands}`; a\n * write replaces the whole value, so unlike Chrome's incremental file we\n * never need the trailing validity marker (command_storage_backend.cc writes\n * kInitialStateMarkerCommandId to detect torn writes) — setItem is atomic.\n *\n * Synchronous on purpose: a pagehide flush only works reliably when the\n * write completes before the handler returns.\n */\n\nimport type { CommandStorageBackend, ReadCommandsResult } from '../command-storage-backend'\nimport type { SessionCommand } from '../session-service-commands'\n\nconst ENVELOPE_VERSION = 1\n\nexport interface WebStorageBackendOptions {\n /** Key prefix; slots live at `<key>/current` and `<key>/last`. */\n key?: string\n /** Storage area to use. Defaults to window.localStorage. */\n storage?: Storage\n}\n\ninterface Envelope {\n version: number\n commands: SessionCommand[]\n}\n\nexport class WebStorageBackend implements CommandStorageBackend {\n /** Two realms over the same key contend for the same profile singleton. */\n readonly profileLockName: string\n\n private readonly storage_: Storage\n private readonly currentKey_: string\n private readonly lastKey_: string\n /** Parsed mirror of the current slot, so appends don't re-parse. */\n private currentCache_: SessionCommand[] | null = null\n\n constructor(options: WebStorageBackendOptions = {}) {\n const key = options.key ?? 'chromium-tabs/session'\n const storage = options.storage ?? (typeof localStorage !== 'undefined' ? localStorage : undefined)\n if (!storage) {\n throw new Error('WebStorageBackend: no Storage available; pass options.storage')\n }\n this.storage_ = storage\n this.profileLockName = `chromium-tabs-profile/${key}`\n this.currentKey_ = `${key}/current`\n this.lastKey_ = `${key}/last`\n }\n\n appendCommands(commands: readonly SessionCommand[], truncate: boolean): void {\n const existing = truncate ? [] : (this.currentCache_ ?? this.readSlot_(this.currentKey_).commands)\n existing.push(...commands)\n this.currentCache_ = existing\n this.storage_.setItem(\n this.currentKey_,\n JSON.stringify({ version: ENVELOPE_VERSION, commands: existing } satisfies Envelope),\n )\n }\n\n readLastSessionCommands(): ReadCommandsResult {\n return this.readSlot_(this.lastKey_)\n }\n\n moveCurrentSessionToLastSession(): void {\n const current = this.storage_.getItem(this.currentKey_)\n if (current !== null) {\n this.storage_.setItem(this.lastKey_, current)\n this.storage_.removeItem(this.currentKey_)\n }\n this.currentCache_ = null\n }\n\n private readSlot_(key: string): ReadCommandsResult {\n const raw = this.storage_.getItem(key)\n if (raw === null) return { commands: [], errorReading: false }\n try {\n const parsed = JSON.parse(raw) as Partial<Envelope> | null\n if (\n parsed === null ||\n parsed.version !== ENVELOPE_VERSION ||\n !Array.isArray(parsed.commands)\n ) {\n return { commands: [], errorReading: true }\n }\n return { commands: parsed.commands, errorReading: false }\n } catch {\n return { commands: [], errorReading: true }\n }\n }\n}\n","/**\n * Core types. Ported from chromium-reference/chrome/browser/ui/tabs/tab_enums.h\n * and the supporting types in tab_strip_model.h.\n *\n * Chrome's tabs carry a WebContents. This library carries a generic `data: T`\n * payload instead so it works for any application.\n */\n\n/** Sentinel for \"no tab\" — mirrors TabStripModel::kNoTab. */\nexport const NO_TAB = -1\n\nexport type TabId = string\nexport type TabGroupId = string\n\n/**\n * How a tab is being opened. Trimmed from ui::PageTransition to the two\n * qualities the TabStripModel actually branches on (tab_strip_model.cc:1731,\n * 1786): link-like openings (inherit opener, insert adjacent) and typed/manual\n * openings (append at end, transient opener at end-of-strip).\n */\nexport type TabOpenCause = 'link' | 'typed' | 'other'\n\n/** Bitmask flags used when adding tabs. Mirrors AddTabTypes (tab_enums.h:42). */\nexport const AddTabFlags = {\n NONE: 0,\n /** The tab should become the active tab. */\n ACTIVE: 1 << 0,\n /** The tab should be pinned. */\n PINNED: 1 << 1,\n /**\n * Use the caller-supplied index rather than letting the model determine\n * the position from the open cause and opener relationships.\n */\n FORCE_INDEX: 1 << 2,\n /** Set the new tab's opener to the currently active tab. */\n INHERIT_OPENER: 1 << 3,\n} as const\n\n/** Bitmask flags used when closing tabs. Mirrors TabCloseTypes (tab_enums.h:26). */\nexport const CloseTabFlags = {\n NONE: 0,\n /** The close was triggered directly by a user gesture. */\n USER_GESTURE: 1 << 0,\n} as const\n\n/**\n * Visual data attached to a tab group. Mirrors tab_groups::TabGroupVisualData.\n * Chrome cycles through 9 named colors; we keep the names so UIs can map them\n * to whatever palette they like.\n */\nexport type TabGroupColor =\n | 'grey'\n | 'blue'\n | 'red'\n | 'yellow'\n | 'green'\n | 'pink'\n | 'purple'\n | 'cyan'\n | 'orange'\n\nexport const TAB_GROUP_COLORS: readonly TabGroupColor[] = [\n 'grey',\n 'blue',\n 'red',\n 'yellow',\n 'green',\n 'pink',\n 'purple',\n 'cyan',\n 'orange',\n]\n\nexport interface TabGroupVisualData {\n title: string\n color: TabGroupColor\n isCollapsed: boolean\n}\n\n/**\n * A tab in the strip. Identity is the object (like Chrome's TabModel pointer);\n * `id` exists for React keys and serialization.\n */\nexport interface Tab<T = unknown> {\n readonly id: TabId\n /** Application payload (your \"WebContents\"). */\n data: T\n /** The tab that opened this tab, if tracked. Mirrors TabModel::opener(). */\n opener: Tab<T> | null\n /**\n * When true, the opener relationship is cleared the next time the active\n * tab changes. Set for typed new-tabs at end of strip (\"quick look-up\"\n * pattern, tab_strip_model.cc:1804-1810).\n */\n resetOpenerOnActiveTabChange: boolean\n /** Pinned tabs are locked to the left side of the strip. */\n pinned: boolean\n /** Group membership, if any. Pinned tabs are never grouped. */\n group: TabGroupId | null\n /** Blocked by a modal — selectable but flagged. Mirrors TabModel::blocked. */\n blocked: boolean\n /**\n * True when the tab's content has been dropped to save memory. The tab\n * stays in the strip (title intact via `data`); content remounts fresh\n * when the tab is next activated. Mirrors TabLifecycleUnit::is_discarded_\n * (tab_lifecycle_unit.cc:312) and WebContents::WasDiscarded.\n */\n discarded: boolean\n /**\n * Wall-clock ms of the last time this tab stopped being active, or\n * Infinity while it is active. Used for least-recently-used discard\n * ordering. Mirrors last_focused_time_ (tab_lifecycle_unit.cc:142, which\n * uses Time::Max() while focused).\n */\n lastActiveAt: number\n /**\n * Per-tab opt-out from automatic discarding. Mirrors\n * TabLifecycleUnit::auto_discardable_ (the extensions setAutoDiscardable\n * API surface).\n */\n autoDiscardable: boolean\n}\n\nexport interface TabGroup {\n readonly id: TabGroupId\n visualData: TabGroupVisualData\n}\n\n/** Options for TabStripModel.addTab / insertTabAt. */\nexport interface AddTabOptions {\n /** Target index. Omit (or pass NO_TAB) to let the model decide. */\n index?: number\n /** What kind of action opened this tab. Default 'other'. */\n cause?: TabOpenCause\n /** Bitmask of AddTabFlags. */\n flags?: number\n /** Insert directly into an existing group. */\n group?: TabGroupId\n /** Stable id; generated if omitted. */\n id?: TabId\n}\n\n/** Desired state of one tab, for TabStripModel.reconcile. */\nexport interface ReconcileTab<T> {\n id: TabId\n data: T\n /** Defaults to false. The list should be pinned-first-consistent. */\n pinned?: boolean\n}\n\nexport interface ReconcileOptions<T> {\n /** Tab to end up active. Omit to leave activation to the model. */\n activeId?: TabId | null\n /** Equality for data payloads; defaults to Object.is. */\n dataEquals?: (a: T, b: T) => boolean\n}\n\nexport interface TabStripModelOptions<T> {\n /**\n * Veto tab closes (Chrome: IsTabClosable / policy). Return false to keep\n * the tab open; observers get a tabCloseCancelled event.\n */\n canCloseTab?: (tab: Tab<T>) => boolean\n /** Disable the group feature entirely (Chrome: null TabGroupModelFactory). */\n supportsGroups?: boolean\n /** Custom id generator for tabs and groups. */\n generateId?: () => string\n}\n","/**\n * Session commands: the persisted mutation log and its rebuild algorithm.\n * Ported from chromium-reference/components/sessions/core/session_service_commands.cc.\n *\n * Chrome appends fixed-size binary payloads (pickles) to an SNSS file; this\n * port keeps the same command ids and semantics but encodes each command as a\n * JSON-serializable object. Restoring a session means replaying the log into\n * SessionTab/SessionWindow structures (RestoreSessionFromCommands, cc:1398).\n */\n\nimport { TAB_GROUP_COLORS, type TabGroupId, type TabGroupVisualData, type TabId } from '../core/types'\nimport type {\n SerializedNavigationEntry,\n SessionSnapshot,\n SessionTab,\n SessionTabGroup,\n SessionWindow,\n SessionWindowId,\n} from './session-types'\n\n/**\n * Command ids, numbered as in session_service_commands.cc:58-106. Ids that\n * only make sense for a real browser (window bounds, user agent overrides,\n * extension app ids, session storage, split tabs) are not ported; their\n * numbers stay reserved so logs remain comparable to Chrome's.\n */\nexport const SessionCommandId = {\n SET_TAB_WINDOW: 0, // cc:58\n SET_TAB_INDEX_IN_WINDOW: 2, // cc:61\n UPDATE_TAB_NAVIGATION: 6, // cc:68\n SET_SELECTED_NAVIGATION_INDEX: 7, // cc:69\n SET_SELECTED_TAB_IN_INDEX: 8, // cc:70\n SET_PINNED_STATE: 12, // cc:80\n TAB_CLOSED: 16, // cc:84\n WINDOW_CLOSED: 17, // cc:85\n SET_ACTIVE_WINDOW: 20, // cc:89\n LAST_ACTIVE_TIME: 21, // cc:90\n TAB_NAVIGATION_PATH_PRUNED: 24, // cc:94\n SET_TAB_GROUP: 25, // cc:95\n SET_TAB_GROUP_METADATA2: 27, // cc:98\n SET_TAB_DATA: 30, // cc:101\n ADD_TAB_EXTRA_DATA: 33, // cc:105\n ADD_WINDOW_EXTRA_DATA: 34, // cc:106\n} as const\n\nexport type SessionCommand =\n | { id: 0; windowId: SessionWindowId; tabId: TabId }\n | { id: 2; tabId: TabId; index: number }\n | { id: 6; tabId: TabId; navigation: SerializedNavigationEntry }\n | { id: 7; tabId: TabId; index: number }\n | { id: 8; windowId: SessionWindowId; index: number }\n | { id: 12; tabId: TabId; pinned: boolean }\n | { id: 16; tabId: TabId; closeTime: number }\n | { id: 17; windowId: SessionWindowId; closeTime: number }\n | { id: 20; windowId: SessionWindowId }\n | { id: 21; tabId: TabId; lastActiveTime: number }\n | { id: 24; tabId: TabId; index: number; count: number }\n | { id: 25; tabId: TabId; groupId: TabGroupId | null }\n | { id: 27; groupId: TabGroupId; visualData: TabGroupVisualData }\n | { id: 30; tabId: TabId; data: unknown }\n | { id: 33; tabId: TabId; key: string; value: string }\n | { id: 34; windowId: SessionWindowId; key: string; value: string }\n\n// Builders ////////////////////////////////////////////////////////////////\n// Mirrors the Create*Command helpers (session_service_commands.cc:719+).\n\nexport function createSetTabWindowCommand(windowId: SessionWindowId, tabId: TabId): SessionCommand {\n return { id: SessionCommandId.SET_TAB_WINDOW, windowId, tabId }\n}\n\nexport function createSetTabIndexInWindowCommand(tabId: TabId, index: number): SessionCommand {\n return { id: SessionCommandId.SET_TAB_INDEX_IN_WINDOW, tabId, index }\n}\n\nexport function createUpdateTabNavigationCommand(\n tabId: TabId,\n navigation: SerializedNavigationEntry,\n): SessionCommand {\n return { id: SessionCommandId.UPDATE_TAB_NAVIGATION, tabId, navigation }\n}\n\nexport function createSetSelectedNavigationIndexCommand(tabId: TabId, index: number): SessionCommand {\n return { id: SessionCommandId.SET_SELECTED_NAVIGATION_INDEX, tabId, index }\n}\n\nexport function createSetSelectedTabInWindowCommand(\n windowId: SessionWindowId,\n index: number,\n): SessionCommand {\n return { id: SessionCommandId.SET_SELECTED_TAB_IN_INDEX, windowId, index }\n}\n\nexport function createPinnedStateCommand(tabId: TabId, pinned: boolean): SessionCommand {\n return { id: SessionCommandId.SET_PINNED_STATE, tabId, pinned }\n}\n\nexport function createTabClosedCommand(tabId: TabId, closeTime = Date.now()): SessionCommand {\n return { id: SessionCommandId.TAB_CLOSED, tabId, closeTime }\n}\n\nexport function createWindowClosedCommand(\n windowId: SessionWindowId,\n closeTime = Date.now(),\n): SessionCommand {\n return { id: SessionCommandId.WINDOW_CLOSED, windowId, closeTime }\n}\n\nexport function createSetActiveWindowCommand(windowId: SessionWindowId): SessionCommand {\n return { id: SessionCommandId.SET_ACTIVE_WINDOW, windowId }\n}\n\nexport function createLastActiveTimeCommand(tabId: TabId, lastActiveTime: number): SessionCommand {\n return { id: SessionCommandId.LAST_ACTIVE_TIME, tabId, lastActiveTime }\n}\n\nexport function createTabNavigationPathPrunedCommand(\n tabId: TabId,\n index: number,\n count: number,\n): SessionCommand {\n return { id: SessionCommandId.TAB_NAVIGATION_PATH_PRUNED, tabId, index, count }\n}\n\nexport function createTabGroupCommand(tabId: TabId, groupId: TabGroupId | null): SessionCommand {\n return { id: SessionCommandId.SET_TAB_GROUP, tabId, groupId }\n}\n\nexport function createTabGroupMetadataUpdateCommand(\n groupId: TabGroupId,\n visualData: TabGroupVisualData,\n): SessionCommand {\n return { id: SessionCommandId.SET_TAB_GROUP_METADATA2, groupId, visualData: { ...visualData } }\n}\n\nexport function createSetTabDataCommand(tabId: TabId, data: unknown): SessionCommand {\n return { id: SessionCommandId.SET_TAB_DATA, tabId, data }\n}\n\nexport function createAddTabExtraDataCommand(tabId: TabId, key: string, value: string): SessionCommand {\n return { id: SessionCommandId.ADD_TAB_EXTRA_DATA, tabId, key, value }\n}\n\nexport function createAddWindowExtraDataCommand(\n windowId: SessionWindowId,\n key: string,\n value: string,\n): SessionCommand {\n return { id: SessionCommandId.ADD_WINDOW_EXTRA_DATA, windowId, key, value }\n}\n\n/**\n * True for commands recording a close. Closing commands never trigger a\n * rebuild — resetting right after a close could lose the state we want to\n * restore. Mirrors IsClosingCommand (session_service_commands.cc:1374).\n */\nexport function isClosingCommand(command: SessionCommand): boolean {\n return command.id === SessionCommandId.TAB_CLOSED || command.id === SessionCommandId.WINDOW_CLOSED\n}\n\n// Rebuild /////////////////////////////////////////////////////////////////\n\ninterface MutableSessionTab extends SessionTab {\n hasWindow: boolean\n}\n\n/** GetTab (session_service_commands.cc:240): lazily create on first mention. */\nfunction getTab(tabs: Map<TabId, MutableSessionTab>, tabId: TabId): MutableSessionTab {\n let tab = tabs.get(tabId)\n if (!tab) {\n tab = {\n tabId,\n windowId: '',\n hasWindow: false,\n visualIndex: -1,\n pinned: false,\n groupId: null,\n data: undefined,\n navigations: [],\n currentNavigationIndex: -1,\n extraData: {},\n }\n tabs.set(tabId, tab)\n }\n return tab\n}\n\n/** GetWindow (session_service_commands.cc:251). */\nfunction getWindow(windows: Map<SessionWindowId, SessionWindow>, windowId: SessionWindowId): SessionWindow {\n let window = windows.get(windowId)\n if (!window) {\n window = { windowId, selectedTabIndex: -1, tabs: [], tabGroups: [], extraData: {} }\n windows.set(windowId, window)\n }\n return window\n}\n\n/**\n * Returns the position of the first navigation whose index is >= `index`,\n * or navigations.length. Navigations are sorted ascending by index.\n * Mirrors FindClosestNavigationWithIndex (session_service_commands.cc:339).\n */\nexport function findClosestNavigationWithIndex(\n navigations: readonly SerializedNavigationEntry[],\n index: number,\n): number {\n for (let i = 0; i < navigations.length; i++) {\n if (navigations[i]!.index >= index) return i\n }\n return navigations.length\n}\n\n/**\n * Removes `count` navigation entries starting at index value `index`,\n * fixing up the selected index and renumbering survivors. Mirrors\n * ProcessTabNavigationPathPrunedCommand (session_service_commands.cc:489).\n */\nexport function processTabNavigationPathPruned(\n tab: Pick<SessionTab, 'navigations' | 'currentNavigationIndex'>,\n index: number,\n count: number,\n): void {\n if (tab.currentNavigationIndex >= index && tab.currentNavigationIndex < index + count) {\n tab.currentNavigationIndex = index - 1\n } else if (tab.currentNavigationIndex >= index + count) {\n tab.currentNavigationIndex -= count\n }\n\n const from = findClosestNavigationWithIndex(tab.navigations, index)\n const to = findClosestNavigationWithIndex(tab.navigations, index + count)\n tab.navigations.splice(from, to - from)\n\n for (const navigation of tab.navigations) {\n if (navigation.index >= index) navigation.index -= count\n }\n}\n\nconst isStr = (v: unknown): v is string => typeof v === 'string'\nconst isNum = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v)\nconst isBool = (v: unknown): v is boolean => typeof v === 'boolean'\n\nfunction isValidNavigation(nav: unknown): nav is SerializedNavigationEntry {\n if (typeof nav !== 'object' || nav === null) return false\n const n = nav as Record<string, unknown>\n return isNum(n.index) && isStr(n.url)\n}\n\nfunction isValidVisualData(v: unknown): v is TabGroupVisualData {\n if (typeof v !== 'object' || v === null) return false\n const d = v as Record<string, unknown>\n return isStr(d.title) && (TAB_GROUP_COLORS as readonly string[]).includes(d.color as string) && isBool(d.isCollapsed)\n}\n\ninterface RebuildState {\n tabs: Map<TabId, MutableSessionTab>\n tabGroups: Map<TabGroupId, SessionTabGroup>\n windows: Map<SessionWindowId, SessionWindow>\n activeWindowId: SessionWindowId | null\n}\n\n/**\n * Applies one command to the in-progress maps. Returns false on a malformed\n * or unknown command, which aborts the replay — the caller keeps whatever\n * was accumulated so far. Mirrors the switch in CreateTabsAndWindows\n * (session_service_commands.cc:521): fault-tolerant, never throws.\n */\nfunction processCommand(state: RebuildState, command: SessionCommand): boolean {\n const { tabs, tabGroups, windows } = state\n // Treat the command as untrusted input: it came from storage.\n const c = command as Record<string, unknown> & { id: number }\n switch (c.id) {\n case SessionCommandId.SET_TAB_WINDOW: {\n if (!isStr(c.tabId) || !isStr(c.windowId)) return false\n const tab = getTab(tabs, c.tabId)\n tab.windowId = c.windowId\n tab.hasWindow = true\n return true\n }\n case SessionCommandId.SET_TAB_INDEX_IN_WINDOW: {\n if (!isStr(c.tabId) || !isNum(c.index)) return false\n getTab(tabs, c.tabId).visualIndex = c.index\n return true\n }\n case SessionCommandId.UPDATE_TAB_NAVIGATION: {\n // Replace-or-insert by navigation index (cc:664-676).\n if (!isStr(c.tabId) || !isValidNavigation(c.navigation)) return false\n const tab = getTab(tabs, c.tabId)\n const navigation = { ...c.navigation }\n const i = findClosestNavigationWithIndex(tab.navigations, navigation.index)\n if (i < tab.navigations.length && tab.navigations[i]!.index === navigation.index) {\n tab.navigations[i] = navigation\n } else {\n tab.navigations.splice(i, 0, navigation)\n }\n return true\n }\n case SessionCommandId.SET_SELECTED_NAVIGATION_INDEX: {\n if (!isStr(c.tabId) || !isNum(c.index)) return false\n getTab(tabs, c.tabId).currentNavigationIndex = c.index\n return true\n }\n case SessionCommandId.SET_SELECTED_TAB_IN_INDEX: {\n if (!isStr(c.windowId) || !isNum(c.index)) return false\n getWindow(windows, c.windowId).selectedTabIndex = c.index\n return true\n }\n case SessionCommandId.SET_PINNED_STATE: {\n if (!isStr(c.tabId) || !isBool(c.pinned)) return false\n getTab(tabs, c.tabId).pinned = c.pinned\n return true\n }\n case SessionCommandId.TAB_CLOSED: {\n if (!isStr(c.tabId)) return false\n tabs.delete(c.tabId)\n return true\n }\n case SessionCommandId.WINDOW_CLOSED: {\n if (!isStr(c.windowId)) return false\n windows.delete(c.windowId)\n return true\n }\n case SessionCommandId.SET_ACTIVE_WINDOW: {\n if (!isStr(c.windowId)) return false\n state.activeWindowId = c.windowId\n return true\n }\n case SessionCommandId.LAST_ACTIVE_TIME: {\n if (!isStr(c.tabId) || !isNum(c.lastActiveTime)) return false\n getTab(tabs, c.tabId).lastActiveTime = c.lastActiveTime\n return true\n }\n case SessionCommandId.TAB_NAVIGATION_PATH_PRUNED: {\n if (!isStr(c.tabId) || !isNum(c.index) || !isNum(c.count)) return false\n if (c.index < 0 || c.count < 1) return false\n processTabNavigationPathPruned(getTab(tabs, c.tabId), c.index, c.count)\n return true\n }\n case SessionCommandId.SET_TAB_GROUP: {\n if (!isStr(c.tabId) || !(c.groupId === null || isStr(c.groupId))) return false\n getTab(tabs, c.tabId).groupId = c.groupId\n return true\n }\n case SessionCommandId.SET_TAB_GROUP_METADATA2: {\n if (!isStr(c.groupId) || !isValidVisualData(c.visualData)) return false\n tabGroups.set(c.groupId, { groupId: c.groupId, visualData: { ...c.visualData } })\n return true\n }\n case SessionCommandId.SET_TAB_DATA: {\n if (!isStr(c.tabId)) return false\n getTab(tabs, c.tabId).data = c.data\n return true\n }\n case SessionCommandId.ADD_TAB_EXTRA_DATA: {\n if (!isStr(c.tabId) || !isStr(c.key) || !isStr(c.value)) return false\n getTab(tabs, c.tabId).extraData[c.key] = c.value\n return true\n }\n case SessionCommandId.ADD_WINDOW_EXTRA_DATA: {\n if (!isStr(c.windowId) || !isStr(c.key) || !isStr(c.value)) return false\n getWindow(windows, c.windowId).extraData[c.key] = c.value\n return true\n }\n default:\n // Unknown command: stop replaying (cc:1330 default case).\n return false\n }\n}\n\n/**\n * Replays a command log into restorable windows. Port of\n * RestoreSessionFromCommands (session_service_commands.cc:1398):\n *\n * 1. CreateTabsAndWindows (cc:521) — apply each command to lazily-created\n * tab/window/group entries, stopping at the first malformed command.\n * 2. AddTabsToWindows (cc:404) — attach tabs to their windows, dropping\n * tabs that never got a window; remap each tab's selected-navigation\n * *index value* to a position in its navigations array.\n * 3. SortTabsBasedOnVisualOrderAndClear (cc:372) — order tabs by visual\n * index and drop windows with no tabs.\n * 4. UpdateSelectedTabIndex (cc:265) — remap each window's selected-tab\n * *visual index* to a position in its tabs array.\n *\n * Deviation from Chrome: tabs without navigation entries are kept when they\n * carry a data payload — this library's data-only persistence mode has no\n * navigation list, but the tab is still fully restorable from `data`.\n */\nexport function restoreSessionFromCommands(commands: readonly SessionCommand[]): SessionSnapshot {\n const state: RebuildState = {\n tabs: new Map(),\n tabGroups: new Map(),\n windows: new Map(),\n activeWindowId: null,\n }\n\n let errorReading = false\n for (const command of commands) {\n if (!processCommand(state, command)) {\n errorReading = true\n break\n }\n }\n\n // AddTabsToWindows (cc:404).\n for (const tab of state.tabs.values()) {\n if (!tab.hasWindow) continue\n if (tab.navigations.length === 0 && tab.data === undefined) continue\n const window = getWindow(state.windows, tab.windowId)\n window.tabs.push(tab)\n if (tab.navigations.length > 0) {\n const j = findClosestNavigationWithIndex(tab.navigations, tab.currentNavigationIndex)\n tab.currentNavigationIndex = j === tab.navigations.length ? tab.navigations.length - 1 : j\n } else {\n tab.currentNavigationIndex = -1\n }\n }\n\n // Collect the groups referenced by each window's tabs (cc:441-452). Groups\n // whose metadata never arrived still get an entry, with default visuals.\n for (const window of state.windows.values()) {\n const seen = new Set<TabGroupId>()\n for (const tab of window.tabs) {\n if (tab.groupId === null || seen.has(tab.groupId)) continue\n seen.add(tab.groupId)\n window.tabGroups.push(\n state.tabGroups.get(tab.groupId) ?? {\n groupId: tab.groupId,\n visualData: { title: '', color: 'grey', isCollapsed: false },\n },\n )\n }\n }\n\n // SortTabsBasedOnVisualOrderAndClear (cc:372). Array.prototype.sort is\n // stable, so tabs sharing a visual index keep command order — Chrome\n // tie-breaks on its monotonic numeric ids instead (cc:380).\n const validWindows = [...state.windows.values()].filter((w) => w.tabs.length > 0)\n for (const window of validWindows) {\n window.tabs.sort((a, b) => a.visualIndex - b.visualIndex)\n }\n validWindows.sort((a, b) => (a.windowId < b.windowId ? -1 : a.windowId > b.windowId ? 1 : 0))\n\n // UpdateSelectedTabIndex (cc:265): visual index -> array position.\n for (const window of validWindows) {\n const i = window.tabs.findIndex((t) => t.visualIndex === window.selectedTabIndex)\n window.selectedTabIndex = i === -1 ? 0 : i\n }\n\n // Strip the rebuild-only marker before handing windows out.\n for (const window of validWindows) {\n for (const tab of window.tabs) delete (tab as Partial<MutableSessionTab>).hasWindow\n }\n\n return {\n windows: validWindows as SessionWindow[],\n activeWindowId: state.activeWindowId,\n errorReading,\n }\n}\n","/**\n * Turns a restored SessionWindow back into live tabs. Ported from\n * chromium-reference/chrome/browser/sessions/session_restore.cc\n * (SessionRestoreImpl::RestoreTabsToBrowser / RestoreTab).\n *\n * Restore order mirrors Chrome: tabs are created in visual order with their\n * pinned state (AddRestoredTab), groups are formed and given their visual\n * data afterwards (RestoreTabGroupMetadata), then the selected tab is\n * activated. Group ids are re-generated by the model — exactly like Chrome,\n * which relabels every restored group via TabGroupId::GenerateNew() to avoid\n * cross-session id collisions — so callers get a groupIdMap back.\n */\n\nimport type { TabStripModel } from '../core/tab-strip-model'\nimport { AddTabFlags, type Tab, type TabGroupId, type TabId } from '../core/types'\nimport type { SessionTab, SessionWindow } from './session-types'\n\nexport interface RestoreOptions<T> {\n /**\n * Builds the live data payload for a restored tab. Defaults to the\n * persisted `data` value as-is. Tabs persisted with navigation tracking\n * only can derive their payload from currentNavigationEntry(tab).\n */\n createTabData?: (tab: SessionTab) => T\n /**\n * Keep the persisted tab ids (default). Ids already present in the model\n * fall back to generated ones. Pass false to always generate fresh ids —\n * Chrome's behavior, where restored tabs get new SessionIDs.\n */\n preserveTabIds?: boolean\n /**\n * Restore non-selected tabs in the discarded state, the analog of Chrome's\n * deferred TabLoader (background tabs exist but don't load until first\n * activation). Default false: everything restores loaded.\n */\n deferLoading?: boolean\n}\n\nexport interface RestoreResult {\n tabsRestored: number\n /** Persisted tab id -> live tab id. Identity unless ids were regenerated. */\n tabIdMap: Map<TabId, TabId>\n /** Persisted group id -> live group id (always regenerated by the model). */\n groupIdMap: Map<TabGroupId, TabGroupId>\n}\n\n/**\n * Appends a SessionWindow's tabs to `model`. The model is typically empty;\n * with existing tabs the restored ones are appended after them (Chrome\n * restores into an existing browser the same way, session_restore.cc's\n * initial_tab_count handling).\n */\nexport function restoreSessionWindow<T>(\n model: TabStripModel<T>,\n window: SessionWindow,\n options: RestoreOptions<T> = {},\n): RestoreResult {\n const createTabData = options.createTabData ?? ((tab: SessionTab) => tab.data as T)\n const preserveIds = options.preserveTabIds !== false\n\n const tabIdMap = new Map<TabId, TabId>()\n const groupIdMap = new Map<TabGroupId, TabGroupId>()\n const liveTabs: Tab<T>[] = []\n\n // RestoreTab (session_restore.cc): create tabs in stored visual order.\n // Saved order is pinned-first, so appending keeps pinned clamping happy.\n for (const sessionTab of window.tabs) {\n const id = preserveIds && model.getTabById(sessionTab.tabId) === null ? sessionTab.tabId : undefined\n const tab = model.insertTabAt(model.count, createTabData(sessionTab), {\n ...(id !== undefined ? { id } : {}),\n flags: sessionTab.pinned ? AddTabFlags.PINNED : AddTabFlags.NONE,\n })\n liveTabs.push(tab)\n tabIdMap.set(sessionTab.tabId, tab.id)\n }\n\n // RestoreTabGroupMetadata: form groups after all tabs are placed.\n if (model.supportsTabGroups) {\n for (const sessionGroup of window.tabGroups) {\n const indices: number[] = []\n window.tabs.forEach((sessionTab, i) => {\n if (sessionTab.groupId === sessionGroup.groupId) indices.push(model.indexOfTab(liveTabs[i]!))\n })\n if (indices.length === 0) continue\n const liveGroupId = model.addToNewGroup(indices, sessionGroup.visualData)\n groupIdMap.set(sessionGroup.groupId, liveGroupId)\n }\n }\n\n // ShowBrowser analog: activate the selected tab (already remapped to an\n // array position by restoreSessionFromCommands).\n if (liveTabs.length > 0) {\n const selected = Math.max(0, Math.min(window.selectedTabIndex, liveTabs.length - 1))\n model.activateTabAt(model.indexOfTab(liveTabs[selected]!))\n\n if (options.deferLoading) {\n for (let i = 0; i < liveTabs.length; i++) {\n if (i === selected) continue\n model.discardTabAt(model.indexOfTab(liveTabs[i]!))\n }\n }\n }\n\n return { tabsRestored: liveTabs.length, tabIdMap, groupIdMap }\n}\n","/**\n * Session snapshot types. Ported from\n * chromium-reference/components/sessions/core/session_types.h and\n * serialized_navigation_entry.h.\n *\n * These are the plain-data structures produced by replaying a command log\n * (restoreSessionFromCommands) and consumed by session restore. Chrome's\n * SessionTab carries WebContents-specific state (user agent overrides,\n * extension app ids, session storage ids); this port carries the library's\n * generic `data` payload instead, plus the same extra-data escape hatch.\n */\n\nimport type { TabGroupId, TabGroupVisualData, TabId } from '../core/types'\n\n/**\n * Identifies a window (one TabStripModel) within a session. Chrome uses\n * monotonic int32 SessionIDs (session_id.h); this port uses caller-supplied\n * strings so ids stay stable across process restarts.\n */\nexport type SessionWindowId = string\n\nexport const DEFAULT_WINDOW_ID: SessionWindowId = 'window-1'\n\n/**\n * One entry in a tab's navigation history. Trimmed from\n * SerializedNavigationEntry (serialized_navigation_entry.h:88): we keep the\n * fields a headless tab strip can act on. `state` is the analog of Chrome's\n * encoded_page_state — an opaque app-defined blob (scroll position, form\n * state, …) that must be JSON-serializable.\n */\nexport interface SerializedNavigationEntry {\n /**\n * Position in the tab's navigation list. Like Chrome's\n * SerializedNavigationEntry::index_, values may have gaps after pruning;\n * order is what matters.\n */\n index: number\n url: string\n title?: string\n /** Opaque app state for this entry (encoded_page_state analog). */\n state?: unknown\n /** Wall-clock ms when the entry was created/updated. */\n timestamp?: number\n}\n\n/** Mirrors SessionTab (session_types.h:34), adapted to this library. */\nexport interface SessionTab {\n tabId: TabId\n windowId: SessionWindowId\n /**\n * Visual position in the window. Mirrors tab_visual_index. May contain\n * gaps (closed tabs leave holes); restore sorts by it.\n */\n visualIndex: number\n pinned: boolean\n groupId: TabGroupId | null\n /**\n * Serialized tab data payload (the result of serializeTabData). This is\n * where your url lives if you don't use navigation tracking. Analog of\n * kCommandSetTabData's key/value map, generalized to one JSON value.\n */\n data: unknown\n /** Navigation history, sorted ascending by entry index. */\n navigations: SerializedNavigationEntry[]\n /**\n * While rebuilding this is a navigation *index value*; after\n * restoreSessionFromCommands completes it has been remapped to a position\n * in `navigations` (AddTabsToWindows, session_service_commands.cc:404), or\n * -1 when there are no navigations.\n */\n currentNavigationIndex: number\n /** Wall-clock ms the tab was last active, if recorded. */\n lastActiveTime?: number\n /** App-defined string map (kCommandAddTabExtraData). */\n extraData: Record<string, string>\n}\n\n/** Mirrors SessionTabGroup (session_types.h:103). */\nexport interface SessionTabGroup {\n groupId: TabGroupId\n visualData: TabGroupVisualData\n}\n\n/** Mirrors SessionWindow (session_types.h:120), minus desktop geometry. */\nexport interface SessionWindow {\n windowId: SessionWindowId\n /**\n * While rebuilding this is the selected tab's *visual index*; after\n * restoreSessionFromCommands completes it has been remapped to a position\n * in `tabs` (UpdateSelectedTabIndex, session_service_commands.cc:265).\n */\n selectedTabIndex: number\n /** Tabs sorted by visual order (SortTabsBasedOnVisualOrderAndClear). */\n tabs: SessionTab[]\n /** Groups referenced by this window's tabs. */\n tabGroups: SessionTabGroup[]\n /** App-defined string map (kCommandAddWindowExtraData). */\n extraData: Record<string, string>\n}\n\n/** The result of reading back a persisted session. */\nexport interface SessionSnapshot {\n /** Windows with at least one restorable tab, in stable windowId order. */\n windows: SessionWindow[]\n activeWindowId: SessionWindowId | null\n /**\n * True when the log was truncated, corrupted, or contained an unknown\n * command. Mirrors ReadCommandsResult::error_reading — whatever was\n * readable is still returned (the rebuild is fault-tolerant).\n */\n errorReading: boolean\n}\n\n/**\n * The selected navigation entry of a restored tab, or null when the tab was\n * persisted without navigation tracking. Convenience over\n * normalized_navigation_index() (session_types.h:62).\n */\nexport function currentNavigationEntry(\n tab: SessionTab,\n): SerializedNavigationEntry | null {\n if (tab.navigations.length === 0) return null\n const i = Math.max(0, Math.min(tab.currentNavigationIndex, tab.navigations.length - 1))\n return tab.navigations[i]!\n}\n","/**\n * SessionService: records TabStripModel mutations as a command log and reads\n * the previous session back. Ported from\n * chromium-reference/chrome/browser/sessions/session_service_base.cc and\n * session_service.cc.\n *\n * In Chrome the service learns about tab changes through Browser /\n * SessionTabHelper plumbing; here it is a TabStripModelObserver. Navigation\n * events have no model-level source (tabs carry opaque `data`), so — exactly\n * like SessionTabHelper driving SessionService — the embedding app reports\n * them through navigateTab / updateTabNavigation / setSelectedNavigationIndex.\n *\n * Tab `data` is persisted automatically (kCommandSetTabData analog) on\n * insert and on every data change, so apps that keep their url in `data`\n * restore correctly with zero extra wiring.\n *\n * Deviation from Chrome: on inserts, moves, and removals we re-emit\n * SetTabIndexInWindow for every tab whose index shifted, so the persisted\n * visual order is always exact. Chrome tolerates stale indices and relies on\n * periodic full rewrites; the in-buffer command swaps below keep our\n * approach cheap.\n */\n\nimport type { TabStripModelObserver, TabStripModelChange, TabStripSelectionChange } from '../core/observer'\nimport type { TabStripModel } from '../core/tab-strip-model'\nimport { NO_TAB, type Tab, type TabId } from '../core/types'\nimport type { CommandStorageBackend } from './command-storage-backend'\nimport { CommandStorageManager } from './command-storage-manager'\nimport {\n createDefaultProcessSingleton,\n type ProcessSingleton,\n type ProcessSingletonResult,\n} from './process-singleton'\nimport { restoreSessionWindow, type RestoreOptions, type RestoreResult } from './session-restore'\nimport {\n SessionCommandId,\n createAddTabExtraDataCommand,\n createAddWindowExtraDataCommand,\n createLastActiveTimeCommand,\n createPinnedStateCommand,\n createSetActiveWindowCommand,\n createSetSelectedNavigationIndexCommand,\n createSetSelectedTabInWindowCommand,\n createSetTabDataCommand,\n createSetTabIndexInWindowCommand,\n createSetTabWindowCommand,\n createTabClosedCommand,\n createTabGroupCommand,\n createTabGroupMetadataUpdateCommand,\n createTabNavigationPathPrunedCommand,\n createUpdateTabNavigationCommand,\n createWindowClosedCommand,\n findClosestNavigationWithIndex,\n isClosingCommand,\n processTabNavigationPathPruned,\n restoreSessionFromCommands,\n type SessionCommand,\n} from './session-service-commands'\nimport {\n DEFAULT_WINDOW_ID,\n type SerializedNavigationEntry,\n type SessionSnapshot,\n type SessionWindowId,\n} from './session-types'\n\n/** Commands between full log rewrites. Mirrors kWritesPerReset (session_service_base.cc:78). */\nexport const WRITES_PER_RESET = 250\n\n/** Navigation entries persisted either side of the current one. Mirrors gMaxPersistNavigationCount. */\nexport const MAX_PERSISTED_NAVIGATIONS = 6\n\nexport interface SessionServiceOptions<T> {\n storage: CommandStorageBackend\n /**\n * Converts tab data to a JSON-serializable value. Defaults to identity —\n * fine when T is already plain JSON (e.g. `{ url: string }`).\n */\n serializeTabData?: (data: T, tab: Tab<T>) => unknown\n /** Inverse of serializeTabData, used by restoreInto. Defaults to identity. */\n deserializeTabData?: (raw: unknown) => T\n /** Delay before buffered commands hit storage. Default SAVE_DELAY_MS (2500). */\n saveDelayMs?: number\n /** Commands between full rewrites. Default WRITES_PER_RESET (250). */\n writesPerReset?: number\n /** Navigation entries kept either side of current. Default 6. */\n maxPersistedNavigations?: number\n /**\n * Flush synchronously on window `pagehide` (web teardown gives timers no\n * chance to fire). Default true when a window exists. Only effective with\n * a synchronous backend such as WebStorageBackend.\n */\n flushOnPageHide?: boolean\n /** Observer-side failures are reported here instead of thrown. */\n onError?: (error: unknown) => void\n /**\n * Cross-realm coordination for the storage area — the ProcessSingleton\n * port (process_singleton.h). Exactly one realm (browser tab) owns a\n * profile; the rest become secondaries that neither rotate, write, nor\n * restore it. Omit for the default: Web Locks keyed by\n * storage.profileLockName when the platform has them, else sole ownership.\n * Pass null to force sole ownership, or your own implementation for\n * tests/custom environments.\n */\n processSingleton?: ProcessSingleton | null\n}\n\nexport interface AttachOptions {\n /** Identifies this model in the log. Default DEFAULT_WINDOW_ID. */\n windowId?: SessionWindowId\n}\n\nexport interface RestoreIntoOptions<T> extends RestoreOptions<T> {\n /** Restore (and attach as) this window. Defaults to the first saved window. */\n windowId?: SessionWindowId\n}\n\nexport interface RestoreIntoResult extends RestoreResult {\n /** False when there was no saved window to restore (e.g. first run). */\n restored: boolean\n /** The full snapshot, for callers that also restore other windows. */\n snapshot: SessionSnapshot\n /**\n * 'owner' when this realm persists the session; 'secondary' when another\n * realm already owns the storage area — this service then restored nothing\n * and will record nothing.\n */\n ownership: ProcessSingletonResult\n}\n\ninterface WindowEntry<T> {\n model: TabStripModel<T>\n unsubscribe: () => void\n /** Dedupe cache for SetSelectedTabInWindow (session_service.cc keeps the same). */\n lastSelectedIndex: number\n}\n\ninterface TrackedNavigationState {\n /** Sorted ascending by entry index. */\n navigations: SerializedNavigationEntry[]\n /** Selected navigation *index value* (not array position). */\n currentNavigationIndex: number\n}\n\nexport class SessionService<T = unknown> {\n private readonly storage_: CommandStorageBackend\n private readonly manager_: CommandStorageManager\n private readonly serializeTabData_: (data: T, tab: Tab<T>) => unknown\n private readonly deserializeTabData_: (raw: unknown) => T\n private readonly writesPerReset_: number\n private readonly maxPersistedNavigations_: number\n private readonly onError_: (error: unknown) => void\n /**\n * Resolves once the profile claim is settled and, for the owner, the\n * previous session has been rotated to the last slot.\n */\n private readonly ready_: Promise<void>\n\n private readonly windows_ = new Map<SessionWindowId, WindowEntry<T>>()\n private readonly navState_ = new Map<TabId, TrackedNavigationState>()\n private readonly tabExtraData_ = new Map<TabId, Map<string, string>>()\n private readonly windowExtraData_ = new Map<SessionWindowId, Map<string, string>>()\n private activeWindowId_: SessionWindowId | null = null\n /** Mirrors rebuild_on_next_save_ (session_service_base.cc). */\n private rebuildOnNextSave_ = false\n private pageHideListener_: (() => void) | null = null\n private disposed_ = false\n private readonly singleton_: ProcessSingleton | null\n private ownership_: 'pending' | ProcessSingletonResult = 'pending'\n /** Mirrors is_saving_enabled_ (session_service_base.h:337). */\n private savingEnabled_ = false\n\n constructor(options: SessionServiceOptions<T>) {\n this.storage_ = options.storage\n this.serializeTabData_ = options.serializeTabData ?? ((data) => data)\n this.deserializeTabData_ = options.deserializeTabData ?? ((raw) => raw as T)\n this.writesPerReset_ = options.writesPerReset ?? WRITES_PER_RESET\n this.maxPersistedNavigations_ = options.maxPersistedNavigations ?? MAX_PERSISTED_NAVIGATIONS\n this.onError_ = options.onError ?? ((error) => console.error('chromium-tabs session:', error))\n\n // Claim the profile before touching storage — the ProcessSingleton port\n // (process_singleton.h:45). Exactly one realm may rotate and write a\n // given storage area; everyone else becomes a read-nothing,\n // write-nothing secondary, the way a second Chrome launch stands down\n // when the profile is already owned.\n this.singleton_ =\n options.processSingleton !== undefined\n ? options.processSingleton\n : createDefaultProcessSingleton(this.storage_.profileLockName)\n\n let gate: Promise<void> | null = null\n if (this.singleton_ === null) {\n // Sole instance (no lock manager on this platform, in-memory backend,\n // or explicit opt-out): rotate synchronously, exactly the pre-singleton\n // behavior. Keeps the synchronous fast path (pagehide flush) intact.\n this.ownership_ = 'owner'\n gate = this.rotateNow_()\n this.ready_ = gate ?? Promise.resolve()\n } else {\n gate = this.singleton_.acquire().then(async (result) => {\n this.ownership_ = result\n if (result === 'owner' && !this.disposed_) {\n const rotation = this.rotateNow_()\n if (rotation) await rotation\n // SetSavingEnabled(true) — schedules the full snapshot of any\n // models attached while the claim was pending.\n this.setSavingEnabled_(true)\n }\n })\n this.ready_ = gate\n }\n\n this.manager_ = new CommandStorageManager({\n backend: this.storage_,\n saveDelayMs: options.saveDelayMs,\n ...(gate ? { ready: gate } : {}),\n delegate: {\n // RebuildCommandsIfRequired (session_service_base.cc:856).\n onWillSaveCommands: () => {\n if (this.rebuildOnNextSave_) this.scheduleResetCommands()\n },\n onErrorWritingSessionCommands: () => {\n this.rebuildOnNextSave_ = true\n this.manager_.startSaveTimer()\n },\n },\n })\n\n if (this.singleton_ === null) this.setSavingEnabled_(true)\n\n const flushOnPageHide = options.flushOnPageHide ?? true\n if (flushOnPageHide && typeof window !== 'undefined' && typeof window.addEventListener === 'function') {\n this.pageHideListener_ = () => {\n void this.saveNow()\n }\n window.addEventListener('pagehide', this.pageHideListener_)\n }\n }\n\n /**\n * Which side of the profile claim this service landed on: 'pending' until\n * resolved, then 'owner' (this realm persists the session) or 'secondary'\n * (another realm owns the storage area; this service records and restores\n * nothing). Settled by the time getLastSession/restoreInto resolve.\n */\n get ownership(): 'pending' | ProcessSingletonResult {\n return this.ownership_\n }\n\n /** Starts rotation; returns the pending promise when async, else null. */\n private rotateNow_(): Promise<void> | null {\n let rotation: void | Promise<void>\n try {\n rotation = this.storage_.moveCurrentSessionToLastSession()\n } catch (error) {\n this.onError_(error)\n rotation = undefined\n }\n if (rotation && typeof rotation.then === 'function') {\n return rotation.then(\n () => undefined,\n (error) => {\n this.onError_(error)\n },\n )\n }\n return null\n }\n\n /** Port of SetSavingEnabled (session_service_base.cc:877). */\n private setSavingEnabled_(enabled: boolean): void {\n if (this.savingEnabled_ === enabled) return\n this.savingEnabled_ = enabled\n if (!this.savingEnabled_) {\n this.manager_.clearPendingCommands()\n } else {\n this.scheduleResetCommands()\n }\n }\n\n // Attach / detach ////////////////////////////////////////////////////////\n\n /**\n * Starts recording a model under the given window id and schedules a full\n * snapshot of its current state (the SetSavingEnabled(true) startup path,\n * session_service_base.cc:412). Returns a detach function.\n */\n attach(model: TabStripModel<T>, options: AttachOptions = {}): () => void {\n this.checkNotDisposed_()\n const windowId = options.windowId ?? DEFAULT_WINDOW_ID\n if (this.windows_.has(windowId)) {\n throw new Error(`session: window \"${windowId}\" is already attached`)\n }\n for (const entry of this.windows_.values()) {\n if (entry.model === model) throw new Error('session: model is already attached')\n }\n\n const entry: WindowEntry<T> = { model, unsubscribe: () => {}, lastSelectedIndex: NO_TAB }\n entry.unsubscribe = model.addObserver(this.createObserver_(windowId, entry))\n this.windows_.set(windowId, entry)\n if (this.activeWindowId_ === null) this.activeWindowId_ = windowId\n this.scheduleResetCommands()\n return () => this.detach(windowId)\n }\n\n /**\n * Stops recording a window. The window's commands stay in the log until\n * the next full rewrite; use markWindowClosed first to drop it eagerly.\n */\n detach(windowId: SessionWindowId): void {\n const entry = this.windows_.get(windowId)\n if (!entry) return\n entry.unsubscribe()\n this.windows_.delete(windowId)\n for (const tab of entry.model.getTabs()) {\n this.navState_.delete(tab.id)\n this.tabExtraData_.delete(tab.id)\n }\n this.windowExtraData_.delete(windowId)\n if (this.activeWindowId_ === windowId) this.activeWindowId_ = null\n }\n\n /**\n * Records the window as closed and detaches it. Tab closes are committed\n * alongside kCommandWindowClosed, mirroring CommitPendingCloses\n * (session_service.cc) — otherwise the tabs' surviving commands would\n * resurrect the window husk during rebuild.\n */\n markWindowClosed(windowId: SessionWindowId): void {\n const entry = this.windows_.get(windowId)\n if (!entry) return\n const tabs = entry.model.getTabs()\n this.detach(windowId)\n for (const tab of tabs) this.scheduleCommand_(createTabClosedCommand(tab.id))\n this.scheduleCommand_(createWindowClosedCommand(windowId))\n }\n\n /** Records which window is active (kCommandSetActiveWindow). */\n setActiveWindow(windowId: SessionWindowId): void {\n if (!this.windows_.has(windowId)) return\n this.activeWindowId_ = windowId\n this.scheduleCommand_(createSetActiveWindowCommand(windowId))\n }\n\n // Navigation tracking (the SessionTabHelper surface) /////////////////////\n\n /**\n * Records that a tab committed a new navigation: forward history is pruned\n * and the new entry becomes current — normal browser semantics. Untracked\n * tab ids are ignored, like commands for untracked windows in Chrome.\n */\n navigateTab(tabId: TabId, entry: { url: string; title?: string; state?: unknown; timestamp?: number }): void {\n if (!this.findTab_(tabId)) return\n let nav = this.navState_.get(tabId)\n if (!nav) {\n nav = { navigations: [], currentNavigationIndex: -1 }\n this.navState_.set(tabId, nav)\n }\n\n const newIndex = nav.currentNavigationIndex + 1\n const last = nav.navigations[nav.navigations.length - 1]\n if (last && last.index >= newIndex) {\n const count = last.index - newIndex + 1\n processTabNavigationPathPruned(nav, newIndex, count)\n this.scheduleCommand_(createTabNavigationPathPrunedCommand(tabId, newIndex, count))\n }\n\n const navigation: SerializedNavigationEntry = {\n index: newIndex,\n url: entry.url,\n ...(entry.title !== undefined ? { title: entry.title } : {}),\n ...(entry.state !== undefined ? { state: entry.state } : {}),\n timestamp: entry.timestamp ?? Date.now(),\n }\n this.insertNavigation_(nav, navigation)\n nav.currentNavigationIndex = newIndex\n this.scheduleCommand_(createUpdateTabNavigationCommand(tabId, navigation))\n this.scheduleCommand_(createSetSelectedNavigationIndexCommand(tabId, newIndex))\n }\n\n /**\n * Replaces-or-inserts one navigation entry by its index — e.g. the current\n * page's title or scroll state changed in place. Mirrors\n * UpdateTabNavigation (session_service.cc).\n */\n updateTabNavigation(tabId: TabId, navigation: SerializedNavigationEntry): void {\n if (!this.findTab_(tabId)) return\n let nav = this.navState_.get(tabId)\n if (!nav) {\n nav = { navigations: [], currentNavigationIndex: -1 }\n this.navState_.set(tabId, nav)\n }\n this.insertNavigation_(nav, { ...navigation })\n this.scheduleCommand_(createUpdateTabNavigationCommand(tabId, { ...navigation }))\n }\n\n /** Records back/forward movement. Mirrors SetSelectedNavigationIndex. */\n setSelectedNavigationIndex(tabId: TabId, index: number): void {\n if (!this.findTab_(tabId)) return\n const nav = this.navState_.get(tabId)\n if (!nav || !nav.navigations.some((n) => n.index === index)) return\n nav.currentNavigationIndex = index\n this.scheduleCommand_(createSetSelectedNavigationIndexCommand(tabId, index))\n }\n\n /** Removes `count` entries from index value `index`. Mirrors TabNavigationPathPruned. */\n pruneTabNavigations(tabId: TabId, index: number, count: number): void {\n if (!this.findTab_(tabId)) return\n const nav = this.navState_.get(tabId)\n if (!nav || index < 0 || count < 1) return\n processTabNavigationPathPruned(nav, index, count)\n this.scheduleCommand_(createTabNavigationPathPrunedCommand(tabId, index, count))\n }\n\n // Extra data (kCommandAddTabExtraData / kCommandAddWindowExtraData) //////\n\n setTabExtraData(tabId: TabId, key: string, value: string): void {\n if (!this.findTab_(tabId)) return\n let map = this.tabExtraData_.get(tabId)\n if (!map) {\n map = new Map()\n this.tabExtraData_.set(tabId, map)\n }\n map.set(key, value)\n this.scheduleCommand_(createAddTabExtraDataCommand(tabId, key, value))\n }\n\n setWindowExtraData(windowId: SessionWindowId, key: string, value: string): void {\n if (!this.windows_.has(windowId)) return\n let map = this.windowExtraData_.get(windowId)\n if (!map) {\n map = new Map()\n this.windowExtraData_.set(windowId, map)\n }\n map.set(key, value)\n this.scheduleCommand_(createAddWindowExtraDataCommand(windowId, key, value))\n }\n\n // Reading and restoring //////////////////////////////////////////////////\n\n /** Replays the previous session's log. Mirrors GetLastSession. */\n async getLastSession(): Promise<SessionSnapshot> {\n this.checkNotDisposed_()\n await this.ready_\n // A secondary never reads the profile, just as a PROCESS_NOTIFIED Chrome\n // launch never opens it — restoring here would duplicate the owner's\n // live session into this realm.\n if (this.ownership_ === 'secondary') {\n return { windows: [], activeWindowId: null, errorReading: false }\n }\n let commands: readonly SessionCommand[]\n let errorReading: boolean\n try {\n const result = await this.storage_.readLastSessionCommands()\n commands = result.commands\n errorReading = result.errorReading\n } catch (error) {\n this.onError_(error)\n return { windows: [], activeWindowId: null, errorReading: true }\n }\n const snapshot = restoreSessionFromCommands(commands)\n return { ...snapshot, errorReading: snapshot.errorReading || errorReading }\n }\n\n /**\n * The one-call integration: restores the previous session's window into\n * `model` (when there is one) and attaches the model so the new session is\n * recorded. Restored navigation histories and extra data are re-adopted so\n * they survive future log rewrites.\n */\n async restoreInto(model: TabStripModel<T>, options: RestoreIntoOptions<T> = {}): Promise<RestoreIntoResult> {\n this.checkNotDisposed_()\n const snapshot = await this.getLastSession()\n const window = options.windowId\n ? snapshot.windows.find((w) => w.windowId === options.windowId)\n : snapshot.windows[0]\n const windowId = options.windowId ?? window?.windowId ?? DEFAULT_WINDOW_ID\n\n let result: RestoreResult = { tabsRestored: 0, tabIdMap: new Map(), groupIdMap: new Map() }\n if (window) {\n result = restoreSessionWindow(model, window, {\n createTabData: options.createTabData ?? ((tab) => this.deserializeTabData_(tab.data)),\n preserveTabIds: options.preserveTabIds,\n deferLoading: options.deferLoading,\n })\n // Re-adopt per-tab state under the (possibly re-generated) live ids.\n for (const sessionTab of window.tabs) {\n const liveId = result.tabIdMap.get(sessionTab.tabId)\n if (liveId === undefined) continue\n if (sessionTab.navigations.length > 0) {\n const position = Math.max(\n 0,\n Math.min(sessionTab.currentNavigationIndex, sessionTab.navigations.length - 1),\n )\n this.navState_.set(liveId, {\n navigations: sessionTab.navigations.map((n) => ({ ...n })),\n currentNavigationIndex: sessionTab.navigations[position]!.index,\n })\n }\n const extra = Object.entries(sessionTab.extraData)\n if (extra.length > 0) this.tabExtraData_.set(liveId, new Map(extra))\n }\n const windowExtra = Object.entries(window.extraData)\n if (windowExtra.length > 0) this.windowExtraData_.set(windowId, new Map(windowExtra))\n }\n\n this.attach(model, { windowId })\n return {\n ...result,\n restored: window !== undefined,\n snapshot,\n ownership: this.ownership_ === 'secondary' ? 'secondary' : 'owner',\n }\n }\n\n // Persistence control /////////////////////////////////////////////////////\n\n /** Flushes buffered commands now (e.g. before an intentional teardown). */\n saveNow(): Promise<void> {\n return this.manager_.save()\n }\n\n /** True while commands are buffered awaiting the save timer. */\n get hasPendingSave(): boolean {\n return this.manager_.hasPendingSave\n }\n\n /**\n * Discards the log and rewrites it from live state. Mirrors\n * ScheduleResetCommands (session_service_base.cc:404) +\n * BuildCommandsFromBrowsers (cc:767).\n */\n scheduleResetCommands(): void {\n // cc:397: never build commands while saving is disabled.\n if (!this.savingEnabled_) return\n this.manager_.setPendingReset(true)\n this.manager_.clearPendingCommands()\n this.rebuildOnNextSave_ = false\n for (const [windowId, entry] of this.windows_) {\n this.buildCommandsForWindow_(windowId, entry)\n }\n if (this.activeWindowId_ !== null) {\n this.manager_.appendRebuildCommand(createSetActiveWindowCommand(this.activeWindowId_))\n }\n this.manager_.startSaveTimer()\n }\n\n /** Detaches everything and stops timers. Does not flush — saveNow() first if needed. */\n dispose(): void {\n if (this.disposed_) return\n this.disposed_ = true\n for (const windowId of [...this.windows_.keys()]) this.detach(windowId)\n if (this.pageHideListener_ !== null) {\n window.removeEventListener('pagehide', this.pageHideListener_)\n this.pageHideListener_ = null\n }\n this.manager_.dispose()\n // Cleanup() port: frees the profile for the next realm to claim at boot.\n this.singleton_?.release()\n }\n\n // Internals ///////////////////////////////////////////////////////////////\n\n private checkNotDisposed_(): void {\n if (this.disposed_) throw new Error('session: service is disposed')\n }\n\n private findTab_(tabId: TabId): { windowId: SessionWindowId; tab: Tab<T>; index: number } | null {\n for (const [windowId, entry] of this.windows_) {\n const tab = entry.model.getTabById(tabId)\n if (tab) return { windowId, tab, index: entry.model.indexOfTab(tab) }\n }\n return null\n }\n\n /** Replace-or-insert by navigation index (session_service_commands.cc:664). */\n private insertNavigation_(nav: TrackedNavigationState, navigation: SerializedNavigationEntry): void {\n const i = findClosestNavigationWithIndex(nav.navigations, navigation.index)\n if (i < nav.navigations.length && nav.navigations[i]!.index === navigation.index) {\n nav.navigations[i] = navigation\n } else {\n nav.navigations.splice(i, 0, navigation)\n }\n }\n\n private serializeTab_(tab: Tab<T>): unknown {\n try {\n return this.serializeTabData_(tab.data, tab)\n } catch (error) {\n this.onError_(error)\n return undefined\n }\n }\n\n /**\n * Coalesces a new command with a buffered one when only the latest value\n * matters. Port of ReplacePendingCommand (session_service_commands.cc:1344)\n * — Chrome only handles UpdateTabNavigation and SetActiveWindow; the other\n * last-write-wins commands here are this port's extension, safe because the\n * rebuild treats each of them as a keyed assignment.\n */\n private replacePendingCommand_(command: SessionCommand): boolean {\n const pending = this.manager_.pendingCommands\n for (let i = pending.length - 1; i >= 0; i--) {\n const existing = pending[i]!\n if (existing.id !== command.id) continue\n switch (command.id) {\n case SessionCommandId.UPDATE_TAB_NAVIGATION: {\n const old = existing as Extract<SessionCommand, { id: 6 }>\n if (old.tabId === command.tabId && old.navigation.index === command.navigation.index) {\n // Mirrors cc:1351-1363: drop the stale entry, append the new one.\n this.manager_.eraseCommand(existing)\n this.manager_.appendRebuildCommand(command)\n return true\n }\n continue\n }\n case SessionCommandId.SET_ACTIVE_WINDOW:\n this.manager_.swapCommand(existing, command)\n return true\n case SessionCommandId.SET_TAB_DATA:\n case SessionCommandId.SET_TAB_INDEX_IN_WINDOW:\n case SessionCommandId.LAST_ACTIVE_TIME:\n case SessionCommandId.SET_PINNED_STATE:\n case SessionCommandId.SET_TAB_GROUP: {\n if ((existing as { tabId?: TabId }).tabId === command.tabId) {\n this.manager_.swapCommand(existing, command)\n return true\n }\n continue\n }\n case SessionCommandId.SET_SELECTED_TAB_IN_INDEX: {\n if ((existing as { windowId?: SessionWindowId }).windowId === command.windowId) {\n this.manager_.swapCommand(existing, command)\n return true\n }\n continue\n }\n case SessionCommandId.SET_TAB_GROUP_METADATA2: {\n if ((existing as { groupId?: string }).groupId === command.groupId) {\n this.manager_.swapCommand(existing, command)\n return true\n }\n continue\n }\n default:\n return false\n }\n }\n return false\n }\n\n /** Mirrors SessionServiceBase::ScheduleCommand (session_service_base.cc:788). */\n private scheduleCommand_(command: SessionCommand): void {\n // cc:790: if (!is_saving_enabled_) return.\n if (this.disposed_ || !this.savingEnabled_) return\n if (this.replacePendingCommand_(command)) return\n const closing = isClosingCommand(command)\n this.manager_.scheduleCommand(command)\n // Never reset on a closing command — we could lose the tabs we want to\n // restore if the app exits right after (cc:802-806).\n if (!this.manager_.pendingReset && this.manager_.commandsSinceReset >= this.writesPerReset_ && !closing) {\n this.scheduleResetCommands()\n }\n }\n\n /** SetTabIndexInWindow for every tab in [lo, hi] — see the header comment. */\n private emitIndexRange_(model: TabStripModel<T>, lo: number, hi: number): void {\n const tabs = model.getTabs()\n const last = Math.min(hi, tabs.length - 1)\n for (let i = Math.max(0, lo); i <= last; i++) {\n this.scheduleCommand_(createSetTabIndexInWindowCommand(tabs[i]!.id, i))\n }\n }\n\n /** Dedup-cached SetSelectedTabInWindow, like session_service.cc. */\n private maybeEmitSelectedTab_(windowId: SessionWindowId, entry: WindowEntry<T>): void {\n const index = entry.model.activeIndex\n if (index === entry.lastSelectedIndex || index === NO_TAB) return\n entry.lastSelectedIndex = index\n this.scheduleCommand_(createSetSelectedTabInWindowCommand(windowId, index))\n }\n\n /**\n * BuildCommandsForBrowser + BuildCommandsForTab\n * (session_service_base.cc:672, :592), adapted to one TabStripModel.\n */\n private buildCommandsForWindow_(windowId: SessionWindowId, entry: WindowEntry<T>): void {\n const model = entry.model\n const m = this.manager_\n\n const windowExtra = this.windowExtraData_.get(windowId)\n if (windowExtra) {\n for (const [key, value] of windowExtra) {\n m.appendRebuildCommand(createAddWindowExtraDataCommand(windowId, key, value))\n }\n }\n\n const tabs = model.getTabs()\n tabs.forEach((tab, index) => {\n m.appendRebuildCommand(createSetTabWindowCommand(windowId, tab.id))\n if (Number.isFinite(tab.lastActiveAt)) {\n m.appendRebuildCommand(createLastActiveTimeCommand(tab.id, tab.lastActiveAt))\n }\n const nav = this.navState_.get(tab.id)\n if (nav && nav.navigations.length > 0) {\n // Persist up to maxPersistedNavigations either side of current (cc:608).\n const position = findClosestNavigationWithIndex(nav.navigations, nav.currentNavigationIndex)\n const lo = Math.max(position - this.maxPersistedNavigations_, 0)\n const hi = Math.min(position + this.maxPersistedNavigations_, nav.navigations.length)\n for (let i = lo; i < hi; i++) {\n m.appendRebuildCommand(createUpdateTabNavigationCommand(tab.id, { ...nav.navigations[i]! }))\n }\n m.appendRebuildCommand(createSetSelectedNavigationIndexCommand(tab.id, nav.currentNavigationIndex))\n }\n m.appendRebuildCommand(createSetTabIndexInWindowCommand(tab.id, index))\n if (tab.pinned) m.appendRebuildCommand(createPinnedStateCommand(tab.id, true))\n if (tab.group !== null) m.appendRebuildCommand(createTabGroupCommand(tab.id, tab.group))\n m.appendRebuildCommand(createSetTabDataCommand(tab.id, this.serializeTab_(tab)))\n const extra = this.tabExtraData_.get(tab.id)\n if (extra) {\n for (const [key, value] of extra) {\n m.appendRebuildCommand(createAddTabExtraDataCommand(tab.id, key, value))\n }\n }\n })\n\n for (const group of model.getGroups()) {\n m.appendRebuildCommand(createTabGroupMetadataUpdateCommand(group.id, group.visualData))\n }\n\n if (model.activeIndex !== NO_TAB) {\n m.appendRebuildCommand(createSetSelectedTabInWindowCommand(windowId, model.activeIndex))\n }\n entry.lastSelectedIndex = model.activeIndex\n }\n\n private createObserver_(windowId: SessionWindowId, entry: WindowEntry<T>): TabStripModelObserver<T> {\n const guarded = <A extends unknown[]>(fn: (...args: A) => void) => {\n return (...args: A) => {\n try {\n fn(...args)\n } catch (error) {\n this.onError_(error)\n }\n }\n }\n\n return {\n onTabStripModelChanged: guarded(\n (change: TabStripModelChange<T>, selection: TabStripSelectionChange<T>) => {\n switch (change.type) {\n case 'inserted': {\n let minIndex = Infinity\n for (const { tab, index } of change.contents) {\n this.scheduleCommand_(createSetTabWindowCommand(windowId, tab.id))\n this.scheduleCommand_(createSetTabDataCommand(tab.id, this.serializeTab_(tab)))\n if (tab.pinned) this.scheduleCommand_(createPinnedStateCommand(tab.id, true))\n if (tab.group !== null) this.scheduleCommand_(createTabGroupCommand(tab.id, tab.group))\n minIndex = Math.min(minIndex, index)\n }\n this.emitIndexRange_(entry.model, minIndex, entry.model.count - 1)\n break\n }\n case 'removed': {\n let minIndex = Infinity\n for (const { tab, index } of change.contents) {\n this.scheduleCommand_(createTabClosedCommand(tab.id))\n this.navState_.delete(tab.id)\n this.tabExtraData_.delete(tab.id)\n minIndex = Math.min(minIndex, index)\n }\n this.emitIndexRange_(entry.model, minIndex, entry.model.count - 1)\n break\n }\n case 'moved':\n this.emitIndexRange_(\n entry.model,\n Math.min(change.fromIndex, change.toIndex),\n Math.max(change.fromIndex, change.toIndex),\n )\n break\n case 'replaced':\n this.scheduleCommand_(createSetTabDataCommand(change.tab.id, this.serializeTab_(change.tab)))\n break\n case 'selectionOnly':\n break\n }\n\n if (selection.activeTabChanged && selection.oldTab && Number.isFinite(selection.oldTab.lastActiveAt)) {\n this.scheduleCommand_(createLastActiveTimeCommand(selection.oldTab.id, selection.oldTab.lastActiveAt))\n }\n this.maybeEmitSelectedTab_(windowId, entry)\n },\n ),\n\n onTabPinnedStateChanged: guarded((tab: Tab<T>) => {\n this.scheduleCommand_(createPinnedStateCommand(tab.id, tab.pinned))\n }),\n\n onTabGroupedStateChanged: guarded((_old, _new, tab: Tab<T>) => {\n this.scheduleCommand_(createTabGroupCommand(tab.id, tab.group))\n }),\n\n onTabGroupChanged: guarded((change) => {\n if (change.type === 'created' || change.type === 'visualsChanged') {\n const visuals = entry.model.getGroupVisualData(change.groupId)\n if (visuals) {\n this.scheduleCommand_(createTabGroupMetadataUpdateCommand(change.groupId, visuals))\n }\n }\n // 'moved' is covered by the per-tab move events; 'closed' needs no\n // command — rebuild only keeps groups still referenced by live tabs.\n }),\n\n onTabChanged: guarded((tab: Tab<T>) => {\n this.scheduleCommand_(createSetTabDataCommand(tab.id, this.serializeTab_(tab)))\n }),\n }\n }\n}\n"],"mappings":"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","names":["window","window","entry","window"]}
|