@rockhall/electron-offline-content 0.4.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/CHANGELOG.md +384 -0
- package/LICENSE +21 -0
- package/README.md +794 -0
- package/dist/internal/asset-file-name.cjs +13 -0
- package/dist/internal/asset-file-name.cjs.map +1 -0
- package/dist/internal/asset-file-name.d.cts +6 -0
- package/dist/internal/asset-file-name.d.cts.map +1 -0
- package/dist/internal/asset-file-name.d.ts +6 -0
- package/dist/internal/asset-file-name.d.ts.map +1 -0
- package/dist/internal/asset-file-name.js +12 -0
- package/dist/internal/asset-file-name.js.map +1 -0
- package/dist/internal/asset-key.cjs +30 -0
- package/dist/internal/asset-key.cjs.map +1 -0
- package/dist/internal/asset-key.d.cts +19 -0
- package/dist/internal/asset-key.d.cts.map +1 -0
- package/dist/internal/asset-key.d.ts +19 -0
- package/dist/internal/asset-key.d.ts.map +1 -0
- package/dist/internal/asset-key.js +27 -0
- package/dist/internal/asset-key.js.map +1 -0
- package/dist/internal/log-format.cjs +98 -0
- package/dist/internal/log-format.cjs.map +1 -0
- package/dist/internal/log-format.d.cts +10 -0
- package/dist/internal/log-format.d.cts.map +1 -0
- package/dist/internal/log-format.d.ts +10 -0
- package/dist/internal/log-format.d.ts.map +1 -0
- package/dist/internal/log-format.js +97 -0
- package/dist/internal/log-format.js.map +1 -0
- package/dist/internal/media-kind.cjs +46 -0
- package/dist/internal/media-kind.cjs.map +1 -0
- package/dist/internal/media-kind.d.cts +20 -0
- package/dist/internal/media-kind.d.cts.map +1 -0
- package/dist/internal/media-kind.d.ts +20 -0
- package/dist/internal/media-kind.d.ts.map +1 -0
- package/dist/internal/media-kind.js +45 -0
- package/dist/internal/media-kind.js.map +1 -0
- package/dist/internal/url-warn.cjs +14 -0
- package/dist/internal/url-warn.cjs.map +1 -0
- package/dist/internal/url-warn.d.cts +10 -0
- package/dist/internal/url-warn.d.cts.map +1 -0
- package/dist/internal/url-warn.d.ts +10 -0
- package/dist/internal/url-warn.d.ts.map +1 -0
- package/dist/internal/url-warn.js +13 -0
- package/dist/internal/url-warn.js.map +1 -0
- package/dist/internal/validation.cjs +222 -0
- package/dist/internal/validation.cjs.map +1 -0
- package/dist/internal/validation.d.cts +78 -0
- package/dist/internal/validation.d.cts.map +1 -0
- package/dist/internal/validation.d.ts +78 -0
- package/dist/internal/validation.d.ts.map +1 -0
- package/dist/internal/validation.js +196 -0
- package/dist/internal/validation.js.map +1 -0
- package/dist/main/asset-download.cjs +265 -0
- package/dist/main/asset-download.cjs.map +1 -0
- package/dist/main/asset-download.d.cts +12 -0
- package/dist/main/asset-download.d.cts.map +1 -0
- package/dist/main/asset-download.d.ts +12 -0
- package/dist/main/asset-download.d.ts.map +1 -0
- package/dist/main/asset-download.js +263 -0
- package/dist/main/asset-download.js.map +1 -0
- package/dist/main/database.cjs +473 -0
- package/dist/main/database.cjs.map +1 -0
- package/dist/main/database.d.cts +81 -0
- package/dist/main/database.d.cts.map +1 -0
- package/dist/main/database.d.ts +81 -0
- package/dist/main/database.d.ts.map +1 -0
- package/dist/main/database.js +472 -0
- package/dist/main/database.js.map +1 -0
- package/dist/main/index.cjs +22 -0
- package/dist/main/index.d.cts +7 -0
- package/dist/main/index.d.ts +7 -0
- package/dist/main/index.js +7 -0
- package/dist/main/media-cache.cjs +862 -0
- package/dist/main/media-cache.cjs.map +1 -0
- package/dist/main/media-cache.d.cts +134 -0
- package/dist/main/media-cache.d.cts.map +1 -0
- package/dist/main/media-cache.d.ts +134 -0
- package/dist/main/media-cache.d.ts.map +1 -0
- package/dist/main/media-cache.js +854 -0
- package/dist/main/media-cache.js.map +1 -0
- package/dist/main/storage-root-lock.cjs +124 -0
- package/dist/main/storage-root-lock.cjs.map +1 -0
- package/dist/main/storage-root-lock.d.cts +11 -0
- package/dist/main/storage-root-lock.d.cts.map +1 -0
- package/dist/main/storage-root-lock.d.ts +11 -0
- package/dist/main/storage-root-lock.d.ts.map +1 -0
- package/dist/main/storage-root-lock.js +120 -0
- package/dist/main/storage-root-lock.js.map +1 -0
- package/dist/main/store.cjs +197 -0
- package/dist/main/store.cjs.map +1 -0
- package/dist/main/store.d.cts +83 -0
- package/dist/main/store.d.cts.map +1 -0
- package/dist/main/store.d.ts +83 -0
- package/dist/main/store.d.ts.map +1 -0
- package/dist/main/store.js +195 -0
- package/dist/main/store.js.map +1 -0
- package/dist/preload/index.cjs +36 -0
- package/dist/preload/index.cjs.map +1 -0
- package/dist/preload/index.d.cts +14 -0
- package/dist/preload/index.d.cts.map +1 -0
- package/dist/preload/index.d.ts +14 -0
- package/dist/preload/index.d.ts.map +1 -0
- package/dist/preload/index.js +34 -0
- package/dist/preload/index.js.map +1 -0
- package/dist/react/index.cjs +199 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +50 -0
- package/dist/react/index.d.cts.map +1 -0
- package/dist/react/index.d.ts +50 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +191 -0
- package/dist/react/index.js.map +1 -0
- package/dist/renderer/helpers.cjs +36 -0
- package/dist/renderer/helpers.cjs.map +1 -0
- package/dist/renderer/helpers.d.cts +11 -0
- package/dist/renderer/helpers.d.cts.map +1 -0
- package/dist/renderer/helpers.d.ts +11 -0
- package/dist/renderer/helpers.d.ts.map +1 -0
- package/dist/renderer/helpers.js +35 -0
- package/dist/renderer/helpers.js.map +1 -0
- package/dist/renderer/index.cjs +20 -0
- package/dist/renderer/index.cjs.map +1 -0
- package/dist/renderer/index.d.cts +14 -0
- package/dist/renderer/index.d.cts.map +1 -0
- package/dist/renderer/index.d.ts +14 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/renderer/index.js +14 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/renderer/runtime.cjs +278 -0
- package/dist/renderer/runtime.cjs.map +1 -0
- package/dist/renderer/runtime.d.cts +35 -0
- package/dist/renderer/runtime.d.cts.map +1 -0
- package/dist/renderer/runtime.d.ts +35 -0
- package/dist/renderer/runtime.d.ts.map +1 -0
- package/dist/renderer/runtime.js +273 -0
- package/dist/renderer/runtime.js.map +1 -0
- package/dist/renderer/window-globals.d.cts +9 -0
- package/dist/renderer/window-globals.d.cts.map +1 -0
- package/dist/renderer/window-globals.d.ts +9 -0
- package/dist/renderer/window-globals.d.ts.map +1 -0
- package/dist/shared/errors.cjs +102 -0
- package/dist/shared/errors.cjs.map +1 -0
- package/dist/shared/errors.d.cts +45 -0
- package/dist/shared/errors.d.cts.map +1 -0
- package/dist/shared/errors.d.ts +45 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +93 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/ipc.cjs +14 -0
- package/dist/shared/ipc.cjs.map +1 -0
- package/dist/shared/ipc.d.cts +12 -0
- package/dist/shared/ipc.d.cts.map +1 -0
- package/dist/shared/ipc.d.ts +12 -0
- package/dist/shared/ipc.d.ts.map +1 -0
- package/dist/shared/ipc.js +13 -0
- package/dist/shared/ipc.js.map +1 -0
- package/dist/shared/normalize.cjs +19 -0
- package/dist/shared/normalize.cjs.map +1 -0
- package/dist/shared/normalize.d.cts +11 -0
- package/dist/shared/normalize.d.cts.map +1 -0
- package/dist/shared/normalize.d.ts +11 -0
- package/dist/shared/normalize.d.ts.map +1 -0
- package/dist/shared/normalize.js +18 -0
- package/dist/shared/normalize.js.map +1 -0
- package/dist/shared/pagination.cjs +32 -0
- package/dist/shared/pagination.cjs.map +1 -0
- package/dist/shared/pagination.d.cts +14 -0
- package/dist/shared/pagination.d.cts.map +1 -0
- package/dist/shared/pagination.d.ts +14 -0
- package/dist/shared/pagination.d.ts.map +1 -0
- package/dist/shared/pagination.js +28 -0
- package/dist/shared/pagination.js.map +1 -0
- package/dist/shared/stem.cjs +16 -0
- package/dist/shared/stem.cjs.map +1 -0
- package/dist/shared/stem.d.cts +6 -0
- package/dist/shared/stem.d.cts.map +1 -0
- package/dist/shared/stem.d.ts +6 -0
- package/dist/shared/stem.d.ts.map +1 -0
- package/dist/shared/stem.js +14 -0
- package/dist/shared/stem.js.map +1 -0
- package/dist/shared/types.cjs +15 -0
- package/dist/shared/types.cjs.map +1 -0
- package/dist/shared/types.d.cts +234 -0
- package/dist/shared/types.d.cts.map +1 -0
- package/dist/shared/types.d.ts +234 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +14 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +120 -0
- package/skills/authenticated-downloads/SKILL.md +203 -0
- package/skills/cache-configuration/SKILL.md +357 -0
- package/skills/cache-configuration/references/options.md +356 -0
- package/skills/getting-started/SKILL.md +407 -0
- package/skills/production-checklist/SKILL.md +397 -0
- package/skills/react-rendering/SKILL.md +424 -0
- package/skills/react-rendering/references/hooks.md +443 -0
- package/skills/store-authoring/SKILL.md +369 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-cache.cjs","names":["resetStorageRootLocksForTests","disableStorageRootLockForTests","enableStorageRootLockForTests","EventEmitter","sleep","statfs","hashKey","parseWithSchema","stringInputSchema","optionalPaginationInputSchema","normalizeStem","MEDIA_CACHE_IPC","acquireStorageRootLock","MediaCacheDatabase","DataValidationError","validateFlatManifest","StoreValidationError","toSerializedError","StoreExpiredError","StorageLimitError","effectiveReserveFreeBytes","AssetDownloader","formatMediaCacheConsoleLine","mediaCacheStoragePathSchema","Readable"],"sources":["../../src/main/media-cache.ts"],"sourcesContent":["import { EventEmitter } from \"node:events\";\nimport { createReadStream, existsSync, mkdirSync, readdirSync, rmSync, statSync } from \"node:fs\";\nimport { statfs } from \"node:fs/promises\";\nimport { Readable } from \"node:stream\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport type { IpcMain, Session } from \"electron\";\nimport { MEDIA_CACHE_IPC } from \"../shared/ipc.js\";\nimport { validateFlatManifest } from \"../shared/normalize.js\";\nimport { normalizeStem } from \"../shared/stem.js\";\nimport {\n DataValidationError,\n StoreExpiredError,\n StoreValidationError,\n StorageLimitError,\n toSerializedError,\n} from \"../shared/errors.js\";\nimport type {\n AssetKeyInput,\n FlatManifest,\n FileStemMatch,\n JsonValue,\n MediaCacheAppPath,\n MediaCacheLogEvent,\n MediaCacheLogFormat,\n MediaCacheLogHandler,\n MediaCacheLogLevel,\n MediaCacheOptions,\n MediaCacheStatus,\n PaginationInput,\n PaginationResult,\n ResolvedMediaAsset,\n SyncProgress,\n SyncRunStats,\n} from \"../shared/types.js\";\nimport { formatMediaCacheConsoleLine } from \"../internal/log-format.js\";\nimport {\n mediaCacheStoragePathSchema,\n optionalPaginationInputSchema,\n parseWithSchema,\n stringInputSchema,\n} from \"../internal/validation.js\";\nimport { MediaCacheDatabase } from \"./database.js\";\nimport { consoleWarnResolveAssetBaseUrlFallback } from \"../internal/url-warn.js\";\nimport { hashKey } from \"../internal/asset-key.js\";\nimport type { StorageRootLockHandle } from \"./storage-root-lock.js\";\nimport {\n acquireStorageRootLock,\n disableStorageRootLockForTests,\n enableStorageRootLockForTests,\n resetStorageRootLocksForTests,\n} from \"./storage-root-lock.js\";\nimport {\n AssetDownloader,\n effectiveReserveFreeBytes,\n type AssetDownloadTarget,\n type QueuedAssetDownloadTarget,\n} from \"./asset-download.js\";\n\nexport { DEFAULT_RESERVE_FREE_BYTES, effectiveReserveFreeBytes } from \"./asset-download.js\";\n\nconst requireElectron = createRequire(import.meta.url);\n\nconst DEFAULT_STALE_DELETE_MS = 7 * 24 * 60 * 60 * 1000;\nconst DEFAULT_SYNC_HISTORY_LIMIT = 50;\ntype StatFsResult = Awaited<ReturnType<typeof statfs>>;\nconst LOG_LEVEL_WEIGHT: Record<MediaCacheLogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nlet mediaCacheProtocolSchemesPrivileged = false;\n\nfunction isModuleNotFoundError(error: unknown): boolean {\n return (\n error instanceof Error &&\n \"code\" in error &&\n (error as NodeJS.ErrnoException).code === \"MODULE_NOT_FOUND\"\n );\n}\n\n/** True when not a production build; Electron `forge start` often leaves `NODE_ENV` unset. */\nfunction isNonProductionNodeEnv(): boolean {\n return process.env.NODE_ENV !== \"production\";\n}\n\n/**\n * Registers the privileged `media:` scheme once per process. Call happens when constructing\n * {@link MediaCache} in offline mode so consumers do not need a separate bootstrap step.\n *\n * No-ops when `electron` cannot be loaded (e.g. unit tests outside Electron). When Electron is\n * available, failures from `protocol.registerSchemesAsPrivileged` propagate (including calling\n * too late after `app.ready`).\n */\nfunction ensureMediaCacheProtocolSchemesPrivileged(): void {\n if (mediaCacheProtocolSchemesPrivileged) {\n return;\n }\n\n let protocol: import(\"electron\").Protocol;\n try {\n ({ protocol } = requireElectron(\"electron\") as typeof import(\"electron\"));\n } catch (error) {\n if (isModuleNotFoundError(error)) {\n return;\n }\n throw error;\n }\n\n if (protocol == null || typeof protocol.registerSchemesAsPrivileged !== \"function\") {\n return;\n }\n\n protocol.registerSchemesAsPrivileged([\n {\n scheme: \"media\",\n privileges: {\n standard: true,\n secure: true,\n supportFetchAPI: true,\n stream: true,\n },\n },\n ]);\n mediaCacheProtocolSchemesPrivileged = true;\n}\n\n/**\n * Clears the internal `media:` scheme registration flag so subsequent {@link MediaCache}\n * construction runs registration again. **Unit tests only**; do not use in application code.\n * @internal\n */\nexport function resetMediaCacheProtocolRegistrationStateForTests(): void {\n mediaCacheProtocolSchemesPrivileged = false;\n}\n\n/**\n * Clears any held storage-root locks so tests can run multiple scenarios in one process.\n * **Unit tests only**; do not use in application code.\n * @internal\n */\nexport const resetMediaCacheStorageRootLocksForTests = resetStorageRootLocksForTests;\n\n/**\n * Disables storage-root exclusivity checks so tests can exercise multiple cache instances freely.\n * **Unit tests only**; do not use in application code.\n * @internal\n */\nexport const disableMediaCacheStorageRootLockForTests = disableStorageRootLockForTests;\n\n/**\n * Re-enables storage-root exclusivity checks after test-only overrides.\n * **Unit tests only**; do not use in application code.\n * @internal\n */\nexport const enableMediaCacheStorageRootLockForTests = enableStorageRootLockForTests;\n\ninterface RuntimeDependencies {\n fetchImpl: typeof globalThis.fetch;\n now: () => number;\n sleep: (delayMs: number) => Promise<void>;\n resolveAppPath: (name: MediaCacheAppPath) => Promise<string>;\n statfs: (path: string) => Promise<StatFsResult>;\n}\n\n/** Options for {@link MediaCacheMain.registerProtocol}. */\ninterface RegisterProtocolOptions {\n /** Electron session to register the `media:` handler on (defaults to `defaultSession`). */\n session?: Session;\n /** Custom file-serving handler; receives the protocol request and resolved local path. */\n fetchFile?: (request: Request, filePath: string) => Promise<Response>;\n}\n\n/** Options for {@link MediaCacheMain.attachIpc}. */\ninterface AttachIpcOptions {\n /** Custom `ipcMain` instance (defaults to `electron.ipcMain`). */\n ipcMain?: IpcMain;\n}\n\n/**\n * Main-process controller: syncs the store, stores blobs, serves `media:` URLs, and can expose\n * the same operations to renderers via IPC. In offline mode (default), construct the cache before\n * `app.whenReady()` so the privileged `media:` scheme can be registered, then after ready call\n * {@link MediaCacheMain.start} for the one-call happy path.\n *\n * `MediaCache` requires exclusive ownership of its resolved `storageRoot`. The first process that\n * acquires that root remains the owner for the process lifetime. A second process (or cache\n * instance) targeting the same root throws {@link import(\"../shared/errors.js\").StorageOwnershipError}.\n */\nexport interface MediaCacheMain {\n /**\n * One-call setup: register protocol, attach IPC, initialize storage, then run initial sync.\n * Cache-root ownership is enforced during initialization, before SQLite or blob writes begin.\n */\n start(): Promise<void>;\n /** Runs or joins the current sync; concurrent callers share one run. */\n syncNow(): Promise<void>;\n /** Latest status snapshot (phase, progress, last run, error). */\n getStatus(): Promise<MediaCacheStatus>;\n /** Single asset by key, or null if missing. */\n getAsset(key: string): Promise<ResolvedMediaAsset | null>;\n /** Assets matching a secondary index value, paginated. */\n listByIndex(\n indexName: string,\n value: string,\n pagination?: PaginationInput,\n ): Promise<PaginationResult<ResolvedMediaAsset>>;\n /** Search by normalized file name stem. */\n findByFileStem(\n stem: string,\n pagination?: PaginationInput,\n ): Promise<PaginationResult<FileStemMatch>>;\n /** Register the `media:` protocol handler for the given session (default: `defaultSession`). */\n registerProtocol(options?: RegisterProtocolOptions): Promise<void>;\n /** Wire `ipcMain` handlers and broadcast status to all browser windows. */\n attachIpc(options?: AttachIpcOptions): Promise<void>;\n}\n\n/** Constructs a {@link MediaCacheMain} instance with the given options (does not start sync until `start` or `syncNow`). */\nexport function createMediaCache(options: MediaCacheOptions): MediaCacheMain {\n return new MediaCache(options);\n}\n\nexport class MediaCache implements MediaCacheMain {\n private readonly events = new EventEmitter();\n private readonly deps: RuntimeDependencies;\n /** When no custom handler is configured, log to the main-process console in development. */\n private readonly defaultDevelopmentConsole: boolean;\n /** Custom structured log sink when configured via `options.logging.onLog`. */\n private readonly logHandler: MediaCacheLogHandler | null;\n /** Built-in console line shape when {@link defaultDevelopmentConsole} is active. */\n private readonly logFormat: MediaCacheLogFormat;\n /** Minimum severity emitted for the currently active log sink. */\n private readonly effectiveLogLevel: MediaCacheLogLevel;\n private readonly devPassthrough: boolean;\n private readonly assetBaseUrlOrigin: string | null;\n private storageRootLock: StorageRootLockHandle | null = null;\n private db: MediaCacheDatabase | null = null;\n private storageRoot: string | null = null;\n private status: MediaCacheStatus;\n private syncPromise: Promise<void> | null = null;\n private ipcAttached = false;\n private protocolRegistered = false;\n\n constructor(\n private readonly options: MediaCacheOptions,\n deps?: Partial<RuntimeDependencies>,\n ) {\n this.deps = {\n fetchImpl: deps?.fetchImpl ?? globalThis.fetch.bind(globalThis),\n now: deps?.now ?? Date.now,\n sleep: deps?.sleep ?? sleep,\n resolveAppPath: deps?.resolveAppPath ?? resolveElectronAppPath,\n statfs: deps?.statfs ?? statfs,\n };\n const logging = normalizeLoggingOptions(options.logging);\n this.logHandler = logging.onLog;\n this.defaultDevelopmentConsole =\n this.logHandler == null && isNonProductionNodeEnv() && process.env.VITEST !== \"true\";\n this.logFormat = logging.format;\n this.effectiveLogLevel =\n logging.level ??\n (this.logHandler == null && this.defaultDevelopmentConsole ? \"debug\" : \"info\");\n this.devPassthrough = options.devPassthrough ?? process.env.NODE_ENV === \"development\";\n if (this.devPassthrough) {\n this.assetBaseUrlOrigin = normalizeAssetBaseUrl(options.assetBaseUrl);\n } else {\n if (options.assetBaseUrl) {\n throw new Error(\n \"assetBaseUrl has no effect when devPassthrough is false. \" +\n \"Set devPassthrough: true or remove assetBaseUrl.\",\n );\n }\n this.assetBaseUrlOrigin = null;\n }\n if (this.devPassthrough) {\n this.emitLog(\"info\", \"dev_passthrough_active\", {\n source: options.devPassthrough === true ? \"option\" : \"node_env\",\n node_env: process.env.NODE_ENV ?? null,\n });\n }\n if (\n this.devPassthrough &&\n this.options.onSyncFailure &&\n this.options.onSyncFailure !== \"throw\"\n ) {\n this.emitLog(\"warn\", \"dev_passthrough_ignores_sync_failure_mode\", {\n configured_mode: this.options.onSyncFailure,\n });\n }\n this.status = {\n phase: \"idle\",\n storageRoot: null,\n activeGenerationId: null,\n progress: null,\n lastRun: null,\n error: null,\n updatedAt: this.deps.now(),\n };\n\n if (!this.devPassthrough) {\n ensureMediaCacheProtocolSchemesPrivileged();\n }\n }\n\n async start(): Promise<void> {\n await this.registerProtocol();\n await this.attachIpc();\n await this.syncNow();\n }\n\n async syncNow(): Promise<void> {\n await this.ensureInitialized();\n if (this.syncPromise) {\n this.emitLog(\"debug\", \"sync_reused\", {\n phase: this.status.phase,\n active_generation_id: this.status.activeGenerationId,\n });\n return this.syncPromise;\n }\n\n this.syncPromise = this.runSync().finally(() => {\n this.syncPromise = null;\n });\n return this.syncPromise;\n }\n\n async getStatus(): Promise<MediaCacheStatus> {\n await this.ensureInitialized();\n return this.status;\n }\n\n async getAsset(key: AssetKeyInput): Promise<ResolvedMediaAsset | null> {\n const hashedKey = hashKey(key);\n await this.ensureInitialized();\n return this.db!.getAsset(hashedKey);\n }\n\n async listByIndex(\n indexName: string,\n value: string,\n pagination?: PaginationInput,\n ): Promise<PaginationResult<ResolvedMediaAsset>> {\n const validatedIndexName = parseWithSchema(stringInputSchema, indexName, \"index name\");\n const validatedValue = parseWithSchema(stringInputSchema, value, \"index value\");\n const validatedPagination = parseWithSchema(\n optionalPaginationInputSchema,\n pagination,\n \"index pagination input\",\n );\n await this.ensureInitialized();\n return this.db!.listByIndex(validatedIndexName, validatedValue, validatedPagination);\n }\n\n async findByFileStem(\n stem: string,\n pagination?: PaginationInput,\n ): Promise<PaginationResult<FileStemMatch>> {\n const validatedStem = parseWithSchema(stringInputSchema, stem, \"file stem\");\n const validatedPagination = parseWithSchema(\n optionalPaginationInputSchema,\n pagination,\n \"file stem pagination input\",\n );\n await this.ensureInitialized();\n return this.db!.findByFileStem(normalizeStem(validatedStem), validatedPagination);\n }\n\n async registerProtocol(options?: RegisterProtocolOptions): Promise<void> {\n await this.ensureInitialized();\n if (this.devPassthrough) {\n this.protocolRegistered = true;\n this.emitLog(\"debug\", \"protocol_registration_skipped\", { reason: \"dev_passthrough\" });\n return;\n }\n if (this.protocolRegistered) {\n this.emitLog(\"debug\", \"protocol_registration_skipped\", { reason: \"already_registered\" });\n return;\n }\n\n const electron = options?.session ? null : await import(\"electron\");\n const session = options?.session ?? electron?.session?.defaultSession;\n if (!session || typeof session.protocol?.handle !== \"function\") {\n this.emitLog(\"debug\", \"protocol_registration_skipped\", {\n reason: \"session_unavailable\",\n });\n return;\n }\n\n const fetchFile =\n options?.fetchFile ??\n (async (request: Request, filePath: string) => createFileResponse(filePath, request));\n\n session.protocol.handle(\"media\", async (request) => {\n const parsed = new URL(request.url);\n const parts = parsed.pathname.split(\"/\").filter(Boolean);\n\n if (parsed.hostname !== \"asset\" || parts.length !== 1) {\n return new Response(\"Not found\", { status: 404 });\n }\n\n let assetKey: string;\n try {\n assetKey = decodeURIComponent(parts[0]);\n } catch {\n return new Response(\"Not found\", { status: 404 });\n }\n\n const target = this.db!.getProtocolAssetTarget(assetKey);\n\n if (!target) {\n this.emitLog(\"debug\", \"protocol_request_not_found\", {\n asset_key: assetKey,\n method: request.method,\n });\n return new Response(\"Not found\", { status: 404 });\n }\n\n if (!target.absolutePath || !existsSync(target.absolutePath)) {\n this.emitLog(\"debug\", \"protocol_request_file_missing\", {\n asset_key: assetKey,\n method: request.method,\n });\n return new Response(\"Not found\", { status: 404 });\n }\n\n this.emitLog(\"debug\", \"protocol_request_local_resolved\", {\n asset_key: assetKey,\n method: request.method,\n range: request.headers.get(\"range\"),\n });\n return fetchFile(request, target.absolutePath);\n });\n\n this.protocolRegistered = true;\n this.emitLog(\"info\", \"protocol_registered\", {});\n }\n\n async attachIpc(options?: AttachIpcOptions): Promise<void> {\n await this.ensureInitialized();\n if (this.ipcAttached) {\n this.emitLog(\"debug\", \"ipc_attach_skipped\", { reason: \"already_attached\" });\n return;\n }\n\n const electron = options?.ipcMain ? null : await import(\"electron\");\n const ipcMain = options?.ipcMain ?? electron?.ipcMain;\n if (!ipcMain || typeof ipcMain.handle !== \"function\") {\n this.emitLog(\"debug\", \"ipc_attach_skipped\", {\n reason: \"ipc_main_unavailable\",\n });\n return;\n }\n\n ipcMain.handle(MEDIA_CACHE_IPC.getStatus, async () => this.getStatus());\n ipcMain.handle(MEDIA_CACHE_IPC.syncNow, async () => this.syncNow());\n ipcMain.handle(MEDIA_CACHE_IPC.getAsset, async (_event, key: AssetKeyInput) =>\n this.getAsset(key),\n );\n ipcMain.handle(\n MEDIA_CACHE_IPC.listByIndex,\n async (_event, indexName: string, value: string, pagination?: PaginationInput) =>\n this.listByIndex(indexName, value, pagination),\n );\n ipcMain.handle(\n MEDIA_CACHE_IPC.findByFileStem,\n async (_event, stem: string, pagination?: PaginationInput) =>\n this.findByFileStem(stem, pagination),\n );\n\n if (electron) {\n this.events.on(MEDIA_CACHE_IPC.statusChanged, (status: MediaCacheStatus) => {\n for (const window of electron.BrowserWindow.getAllWindows()) {\n window.webContents.send(MEDIA_CACHE_IPC.statusChanged, status);\n }\n });\n }\n\n this.ipcAttached = true;\n this.emitLog(\"info\", \"ipc_attached\", {});\n }\n\n private async ensureInitialized(): Promise<void> {\n if (this.db) {\n return;\n }\n\n this.storageRoot = await resolveStorageRoot(this.options.storagePath, this.deps.resolveAppPath);\n mkdirSync(this.storageRoot, { recursive: true });\n this.storageRootLock ??= acquireStorageRootLock(this.storageRoot, this);\n mkdirSync(join(this.storageRoot, \"temp\"), { recursive: true });\n mkdirSync(join(this.storageRoot, \"blobs\"), { recursive: true });\n\n if (!this.devPassthrough) {\n this.emitLog(\"info\", \"cache_storage_location\", {\n storage_root: this.storageRoot,\n });\n }\n\n this.db = new MediaCacheDatabase(this.storageRoot, {\n devPassthrough: this.devPassthrough,\n assetBaseUrlOrigin: this.assetBaseUrlOrigin,\n onWarn: (contextLabel, err) => {\n if (this.logHandler != null || this.defaultDevelopmentConsole) {\n this.emitLog(\"warn\", \"resolve_asset_base_url_fallback\", {\n context_label: contextLabel,\n error: err != null ? String(err) : undefined,\n });\n } else {\n consoleWarnResolveAssetBaseUrlFallback(contextLabel, err);\n }\n },\n });\n if (this.devPassthrough) {\n this.prepareDevRuntimeState();\n }\n let storedStatus: MediaCacheStatus | null = null;\n let activeGenerationId: number | null = null;\n if (!this.devPassthrough) {\n activeGenerationId = this.reconcileOrphanedStagedGenerations();\n try {\n storedStatus = this.db.loadStatus();\n } catch (error) {\n if (!(error instanceof DataValidationError)) {\n throw error;\n }\n\n this.emitLog(\"warn\", \"status_snapshot_invalid\", {\n error_code: error.code,\n error_message: error.message,\n });\n }\n if (storedStatus) {\n this.status = storedStatus;\n } else if (activeGenerationId !== null) {\n this.status = {\n ...this.status,\n phase: \"ready\",\n activeGenerationId,\n progress: null,\n error: null,\n };\n }\n this.status.activeGenerationId = activeGenerationId;\n }\n this.status = {\n ...this.status,\n storageRoot: this.storageRoot,\n };\n this.emitLog(\"info\", \"cache_initialized\", {\n storage_root: this.storageRoot,\n active_generation_id: this.status.activeGenerationId,\n dev_passthrough_enabled: this.devPassthrough,\n });\n }\n\n private reconcileOrphanedStagedGenerations(): number | null {\n const activeGenerationId = this.db!.getActiveGenerationId();\n const stagedGenerationIds = this.db!.listStagedGenerationIds().filter(\n (generationId) => generationId !== activeGenerationId,\n );\n if (stagedGenerationIds.length === 0) {\n return activeGenerationId;\n }\n\n for (const stagedGenerationId of stagedGenerationIds) {\n this.cleanupStagedGenerationFiles(stagedGenerationId, activeGenerationId);\n this.db!.deleteGeneration(stagedGenerationId);\n }\n\n this.emitLog(\"warn\", \"orphaned_staged_generations_removed\", {\n active_generation_id: activeGenerationId,\n removed_generation_ids: stagedGenerationIds,\n removed_generation_count: stagedGenerationIds.length,\n });\n return activeGenerationId;\n }\n\n private prepareDevRuntimeState(): void {\n // Wipe happens before resolveStore; if store resolution later throws, blobs are\n // already gone. Deferring the wipe until after staging would require a broader restructure.\n this.emitLog(\"warn\", \"dev_passthrough_clearing_state\", {\n storage_root: this.storageRoot,\n reason: \"devPassthrough=true clears all local state on startup\",\n });\n this.db!.clearAllState();\n rmSync(join(this.storageRoot!, \"blobs\"), { recursive: true, force: true });\n rmSync(join(this.storageRoot!, \"temp\"), { recursive: true, force: true });\n mkdirSync(join(this.storageRoot!, \"blobs\"), { recursive: true });\n mkdirSync(join(this.storageRoot!, \"temp\"), { recursive: true });\n this.status = {\n ...this.status,\n phase: \"idle\",\n activeGenerationId: null,\n progress: null,\n lastRun: null,\n error: null,\n updatedAt: this.deps.now(),\n };\n }\n\n private async runSync(): Promise<void> {\n const now = this.deps.now();\n const runId = this.db!.createSyncRun(now);\n const stats: SyncRunStats = {\n totalAssets: 0,\n downloadedAssets: 0,\n skippedAssets: 0,\n bytesDownloaded: 0,\n };\n\n this.updateStatus({\n phase: \"syncing\",\n error: null,\n progress: {\n runId,\n phase: \"resolving-store\",\n totalAssets: 0,\n completedAssets: 0,\n downloadedAssets: 0,\n skippedAssets: 0,\n bytesDownloaded: 0,\n },\n });\n this.emitLog(\"info\", \"sync_started\", {\n run_id: runId,\n active_generation_id: this.status.activeGenerationId,\n });\n\n let stagedGenerationId: number | null = null;\n\n try {\n const store = await this.options.resolveStore();\n const manifest = validateFlatManifest(store._serialize());\n this.assertStoreNotExpired(manifest, runId);\n stagedGenerationId = this.db!.createStagedGeneration(manifest, now);\n this.emitLog(\"info\", \"store_resolved\", {\n run_id: runId,\n staged_generation_id: stagedGenerationId,\n index_count: manifest.indexDefinitions.length,\n asset_count: manifest.assets.length,\n });\n\n const currentGenerationId = this.db!.getActiveGenerationId();\n const currentAssets = currentGenerationId\n ? this.db!.getGenerationAssets(currentGenerationId)\n : [];\n const stagedAssets = this.db!.getGenerationAssets(stagedGenerationId);\n stats.totalAssets = stagedAssets.length;\n\n this.updateProgress((progress) => ({\n ...progress,\n phase: \"diffing\",\n totalAssets: stagedAssets.length,\n }));\n\n const manifestAssetMap = new Map(manifest.assets.map((asset) => [asset.key, asset]));\n const currentMap = new Map(currentAssets.map((row) => [row.assetKey, row]));\n const downloads: QueuedAssetDownloadTarget[] = [];\n\n for (const row of stagedAssets) {\n const manifestAsset = manifestAssetMap.get(row.assetKey);\n if (!manifestAsset) {\n throw new StoreValidationError(`Asset \"${row.assetKey}\" not found in serialized store.`);\n }\n\n const activeRow = currentMap.get(row.assetKey);\n const activeRelativePath = activeRow?.relativePath ?? null;\n const normalizedActiveRelativePath =\n activeRelativePath === null ? null : normalizeStoredRelativePath(activeRelativePath);\n const nextVersion = manifestAsset.version;\n const canReuseActiveBlob =\n normalizedActiveRelativePath !== null &&\n existsSync(join(this.storageRoot!, normalizedActiveRelativePath));\n\n if (this.devPassthrough) {\n stats.skippedAssets += 1;\n continue;\n }\n\n if (canReuseActiveBlob) {\n const currentVersion = getResolvedVersionFromPath(normalizedActiveRelativePath);\n if (currentVersion === nextVersion) {\n this.db!.setAssetDownloadState(\n stagedGenerationId,\n row.assetKey,\n normalizedActiveRelativePath,\n activeRow?.mimeType ?? null,\n );\n stats.skippedAssets += 1;\n continue;\n }\n }\n\n downloads.push({\n assetKey: row.assetKey,\n version: manifestAsset.version,\n fileName: manifestAsset.fileName,\n byteLength: manifestAsset.byteLength,\n });\n }\n\n this.emitLog(\"info\", \"sync_diffed\", {\n run_id: runId,\n total_assets: stagedAssets.length,\n download_count: downloads.length,\n skipped_assets: stats.skippedAssets,\n dev_passthrough_enabled: this.devPassthrough,\n });\n\n await this.pruneExpiredDeletions();\n this.cleanupObsoletePartialDownloads(downloads);\n if (!this.devPassthrough) {\n await this.enforceStorageLimits(downloads);\n\n this.updateProgress((progress) => ({\n ...progress,\n phase: \"downloading\",\n totalAssets: stagedAssets.length,\n completedAssets: stats.skippedAssets,\n skippedAssets: stats.skippedAssets,\n }));\n\n for (const queuedDownload of downloads) {\n this.assertStoreNotExpired(manifest, runId, queuedDownload);\n const download: AssetDownloadTarget = {\n ...queuedDownload,\n request: { url: manifestAssetMap.get(queuedDownload.assetKey)!.url },\n };\n this.emitLog(\"debug\", \"asset_download_started\", {\n run_id: runId,\n asset_key: download.assetKey,\n version: download.version,\n url: download.request.url,\n });\n const { relativePath, fallbackMimeType } =\n await this.createAssetDownloader().downloadAsset(download, (chunkBytes) => {\n stats.bytesDownloaded += chunkBytes;\n this.updateProgress((progress) => ({\n ...progress,\n bytesDownloaded: stats.bytesDownloaded,\n }));\n });\n this.db!.setAssetDownloadState(\n stagedGenerationId,\n download.assetKey,\n relativePath,\n fallbackMimeType,\n );\n stats.downloadedAssets += 1;\n this.emitLog(\"debug\", \"asset_download_completed\", {\n run_id: runId,\n asset_key: download.assetKey,\n relative_path: relativePath,\n });\n this.updateProgress((progress) => ({\n ...progress,\n completedAssets: stats.downloadedAssets + stats.skippedAssets,\n downloadedAssets: stats.downloadedAssets,\n skippedAssets: stats.skippedAssets,\n bytesDownloaded: stats.bytesDownloaded,\n }));\n }\n } else {\n this.updateProgress((progress) => ({\n ...progress,\n completedAssets: stagedAssets.length,\n skippedAssets: stagedAssets.length,\n }));\n }\n\n this.updateProgress((progress) => ({\n ...progress,\n phase: \"committing\",\n }));\n\n const previousGenerationId = this.db!.activateGeneration(stagedGenerationId, this.deps.now());\n this.db!.clearPendingDeletionsForGeneration(stagedGenerationId);\n if (previousGenerationId) {\n this.markRemovedAssetsForDeletion(previousGenerationId, stagedGenerationId);\n }\n this.emitLog(\"info\", \"generation_committed\", {\n run_id: runId,\n previous_generation_id: previousGenerationId,\n active_generation_id: stagedGenerationId,\n });\n\n this.updateProgress((progress) => ({\n ...progress,\n phase: \"pruning\",\n }));\n await this.pruneExpiredDeletions();\n\n const summary = this.db!.completeSyncRun(runId, \"success\", this.deps.now(), stats);\n this.db!.pruneSyncHistory(this.options.syncHistoryLimit ?? DEFAULT_SYNC_HISTORY_LIMIT);\n this.emitLog(\"info\", \"sync_completed\", {\n run_id: runId,\n active_generation_id: stagedGenerationId,\n total_assets: summary.stats.totalAssets,\n downloaded_assets: summary.stats.downloadedAssets,\n skipped_assets: summary.stats.skippedAssets,\n bytes_downloaded: summary.stats.bytesDownloaded,\n });\n this.updateStatus({\n phase: \"ready\",\n activeGenerationId: stagedGenerationId,\n progress: null,\n lastRun: summary,\n error: null,\n });\n } catch (error) {\n if (stagedGenerationId !== null) {\n this.cleanupStagedGenerationFiles(stagedGenerationId, this.db!.getActiveGenerationId());\n this.db!.deleteGeneration(stagedGenerationId);\n }\n\n const serialized = toSerializedError(error);\n const summary = this.db!.completeSyncRun(\n runId,\n \"error\",\n this.deps.now(),\n stats,\n serialized.code,\n serialized.message,\n );\n this.emitLog(\"error\", \"sync_failed\", {\n run_id: runId,\n active_generation_id: this.db!.getActiveGenerationId(),\n error_code: serialized.code,\n error_message: serialized.message,\n total_assets: summary.stats.totalAssets,\n downloaded_assets: summary.stats.downloadedAssets,\n skipped_assets: summary.stats.skippedAssets,\n bytes_downloaded: summary.stats.bytesDownloaded,\n });\n\n this.updateStatus({\n phase:\n this.devPassthrough || this.options.onSyncFailure === \"throw\"\n ? \"error\"\n : this.db!.getActiveGenerationId()\n ? \"ready\"\n : \"error\",\n activeGenerationId: this.db!.getActiveGenerationId(),\n progress: null,\n lastRun: summary,\n error: serialized,\n });\n\n if (this.devPassthrough || this.options.onSyncFailure === \"throw\") {\n throw error;\n }\n }\n }\n\n private assertStoreNotExpired(\n manifest: FlatManifest,\n runId: number,\n download?: Pick<QueuedAssetDownloadTarget, \"assetKey\">,\n ): void {\n if (!manifest.expiresAt) {\n return;\n }\n\n const expiresAtMs = Date.parse(manifest.expiresAt);\n const now = this.deps.now();\n if (Number.isNaN(expiresAtMs) || now < expiresAtMs) {\n return;\n }\n\n this.emitLog(\"warn\", \"store_expired\", {\n run_id: runId,\n expires_at: manifest.expiresAt,\n now_ms: now,\n asset_key: download?.assetKey,\n });\n\n const assetLabel = download ? ` before downloading ${download.assetKey}` : \"\";\n throw new StoreExpiredError(\n `Store URLs expired at ${manifest.expiresAt}${assetLabel}. Refresh the store and retry sync.`,\n );\n }\n\n private async enforceStorageLimits(downloads: QueuedAssetDownloadTarget[]): Promise<void> {\n const estimatedBlobBytes = downloads.reduce(\n (sum, download) => sum + (download.byteLength ?? 0),\n 0,\n );\n const estimatedRemainingDownloadBytes = downloads.reduce(\n (sum, download) => sum + this.createAssetDownloader().remainingDownloadBytes(download),\n 0,\n );\n\n if (this.options.maxCacheBytes !== undefined) {\n const currentBytes = this.currentBytesOnDisk(join(this.storageRoot!, \"blobs\"));\n if (currentBytes + estimatedBlobBytes > this.options.maxCacheBytes) {\n this.emitLog(\"warn\", \"storage_limit_exceeded\", {\n current_bytes: currentBytes,\n estimated_download_bytes: estimatedBlobBytes,\n max_cache_bytes: this.options.maxCacheBytes,\n });\n throw new StorageLimitError(\n `Estimated cache size ${currentBytes + estimatedBlobBytes} exceeds maxCacheBytes ${this.options.maxCacheBytes}.`,\n );\n }\n }\n\n const stats = await this.deps.statfs(this.storageRoot!);\n const availableBytes = Number(stats.bavail) * Number(stats.bsize);\n const reserve = effectiveReserveFreeBytes(this.options.reserveFreeBytes);\n if (availableBytes - estimatedRemainingDownloadBytes < reserve) {\n this.emitLog(\"warn\", \"storage_reserve_violation\", {\n available_bytes: availableBytes,\n estimated_download_bytes: estimatedRemainingDownloadBytes,\n reserve_free_bytes: reserve,\n });\n throw new StorageLimitError(\n `Estimated download size ${estimatedRemainingDownloadBytes} leaves less than reserveFreeBytes ${reserve}.`,\n );\n }\n }\n\n private async ensureFileSpaceCommit(): Promise<void> {\n await this.createAssetDownloader().ensureFileSpaceCommit();\n }\n\n private cleanupObsoletePartialDownloads(downloads: QueuedAssetDownloadTarget[]): void {\n this.createAssetDownloader().cleanupObsoletePartialDownloads(downloads);\n }\n\n private createAssetDownloader(): AssetDownloader {\n return new AssetDownloader(this.storageRoot!, this.deps, {\n reserveFreeBytes: this.options.reserveFreeBytes,\n emitLog: (level, event, fields = {}) => this.emitLog(level, event, fields),\n });\n }\n\n private cleanupStagedGenerationFiles(\n stagedGenerationId: number,\n activeGenerationId: number | null,\n ): void {\n const activePaths = new Set(\n activeGenerationId\n ? this.db!.getGenerationAssets(activeGenerationId).flatMap((row) =>\n row.relativePath ? [normalizeStoredRelativePath(row.relativePath)] : [],\n )\n : [],\n );\n\n for (const row of this.db!.getGenerationAssets(stagedGenerationId)) {\n if (!row.relativePath) {\n continue;\n }\n\n const normalizedRelativePath = normalizeStoredRelativePath(row.relativePath);\n if (activePaths.has(normalizedRelativePath)) {\n continue;\n }\n\n const absolutePath = join(this.storageRoot!, normalizedRelativePath);\n rmSync(absolutePath, { force: true });\n pruneEmptyParents(absolutePath, this.storageRoot!);\n }\n }\n\n private markRemovedAssetsForDeletion(\n previousGenerationId: number,\n stagedGenerationId: number,\n ): void {\n const previousAssets = this.db!.getGenerationAssets(previousGenerationId);\n const nextAssets = new Map(\n this.db!.getGenerationAssets(stagedGenerationId).map((row) => [\n row.assetKey,\n row.relativePath,\n ]),\n );\n const deleteAfterMs =\n this.deps.now() + (this.options.staleDeleteAfterMs ?? DEFAULT_STALE_DELETE_MS);\n\n let markedCount = 0;\n for (const row of previousAssets) {\n const nextRelativePath = nextAssets.get(row.assetKey);\n if (row.relativePath && nextRelativePath !== row.relativePath) {\n this.db!.markPendingDeletion(\n row.assetKey,\n row.relativePath,\n previousGenerationId,\n createPendingDeletionKey(row.assetKey, row.relativePath),\n deleteAfterMs,\n );\n markedCount += 1;\n }\n }\n this.emitLog(\"debug\", \"assets_marked_for_deletion\", {\n previous_generation_id: previousGenerationId,\n active_generation_id: stagedGenerationId,\n marked_count: markedCount,\n delete_after_ms: deleteAfterMs,\n });\n }\n\n private async pruneExpiredDeletions(): Promise<void> {\n const expired = this.db!.getExpiredPendingDeletions(this.deps.now());\n if (expired.length === 0) {\n this.emitLog(\"debug\", \"deletion_prune_skipped\", { expired_count: 0 });\n return;\n }\n\n for (const deletion of expired) {\n const absolutePath = join(\n this.storageRoot!,\n normalizeStoredRelativePath(deletion.relativePath),\n );\n rmSync(absolutePath, { force: true });\n pruneEmptyParents(absolutePath, this.storageRoot!);\n }\n\n this.db!.deletePendingDeletions(expired.map((item) => item.deletionKey));\n this.emitLog(\"debug\", \"assets_pruned\", { pruned_count: expired.length });\n }\n\n private currentBytesOnDisk(directory: string): number {\n if (!existsSync(directory)) {\n return 0;\n }\n\n const stats = statSync(directory);\n if (stats.isFile()) {\n return stats.size;\n }\n\n return readdirSync(directory).reduce(\n (sum, entry) => sum + this.currentBytesOnDisk(join(directory, entry)),\n 0,\n );\n }\n\n private updateProgress(transform: (progress: SyncProgress) => SyncProgress): void {\n if (!this.status.progress) {\n return;\n }\n this.updateStatus({\n progress: transform(this.status.progress),\n });\n }\n\n private updateStatus(partial: Partial<MediaCacheStatus>): void {\n this.status = {\n ...this.status,\n ...partial,\n updatedAt: this.deps.now(),\n };\n this.db?.saveStatus(this.status, this.status.updatedAt);\n this.events.emit(MEDIA_CACHE_IPC.statusChanged, this.status);\n }\n\n private emitLog(\n level: MediaCacheLogLevel,\n event: string,\n fields: Record<string, ReturnType<typeof normalizeLogValue>> = {},\n ): void {\n if (this.logHandler == null && !this.defaultDevelopmentConsole) {\n return;\n }\n\n const threshold = LOG_LEVEL_WEIGHT[this.effectiveLogLevel];\n if (LOG_LEVEL_WEIGHT[level] < threshold) {\n return;\n }\n\n const entry: MediaCacheLogEvent = {\n timestamp: new Date(this.deps.now()).toISOString(),\n level,\n event,\n service: \"rockhall-electron-offline-content\",\n component: \"media-cache\",\n ...fields,\n };\n\n if (this.logHandler != null) {\n try {\n this.logHandler(entry);\n } catch {\n // Consumer loggers must not break cache behavior.\n }\n return;\n }\n\n writeDefaultDevelopmentConsoleLog(level, entry, this.logFormat);\n }\n}\n\nfunction normalizeLoggingOptions(logging: MediaCacheOptions[\"logging\"]): {\n onLog: MediaCacheLogHandler | null;\n level: MediaCacheLogLevel | undefined;\n format: MediaCacheLogFormat;\n} {\n if (logging?.onLog != null && logging.format !== undefined) {\n throw new Error(\n \"MediaCacheOptions.logging.format cannot be set when logging.onLog is provided.\",\n );\n }\n\n const format = logging?.format;\n if (format !== undefined && format !== \"english\" && format !== \"json\") {\n throw new Error(\n `Invalid MediaCacheOptions.logging.format: expected \"english\" | \"json\", received ${JSON.stringify(format)}`,\n );\n }\n\n return {\n onLog: logging?.onLog ?? null,\n level: logging?.level,\n format: format ?? \"english\",\n };\n}\n\nfunction writeDefaultDevelopmentConsoleLog(\n level: MediaCacheLogLevel,\n entry: MediaCacheLogEvent,\n format: MediaCacheLogFormat,\n): void {\n const line = formatMediaCacheConsoleLine(entry, format);\n switch (level) {\n case \"debug\":\n console.debug(line);\n break;\n case \"info\":\n console.log(line);\n break;\n case \"warn\":\n console.warn(line);\n break;\n case \"error\":\n console.error(line);\n break;\n default:\n console.log(line);\n }\n}\n\nfunction createPendingDeletionKey(assetKey: string, relativePath: string): string {\n return JSON.stringify([assetKey, relativePath]);\n}\n\nfunction normalizeAssetBaseUrl(assetBaseUrl: string | null | undefined): string | null {\n if (!assetBaseUrl) {\n return null;\n }\n\n let parsed: URL;\n try {\n parsed = new URL(assetBaseUrl);\n } catch {\n throw new Error(`assetBaseUrl is not a valid URL: \"${assetBaseUrl}\"`);\n }\n if (parsed.username || parsed.password) {\n throw new Error(\"assetBaseUrl must not include credentials.\");\n }\n if (parsed.search || parsed.hash) {\n throw new Error(\"assetBaseUrl must not include a query string or hash fragment.\");\n }\n if (parsed.pathname !== \"/\" && parsed.pathname !== \"\") {\n throw new Error(\"assetBaseUrl must be an origin without a path.\");\n }\n\n return parsed.origin;\n}\n\nfunction pruneEmptyParents(pathToFile: string, storageRoot: string): void {\n let current = dirname(pathToFile);\n while (current.startsWith(storageRoot) && current !== storageRoot) {\n if (existsSync(current) && readdirSync(current).length === 0) {\n rmSync(current, { recursive: true, force: true });\n current = dirname(current);\n continue;\n }\n break;\n }\n}\n\nfunction getResolvedVersionFromPath(relativePath: string): string | null {\n const parts = relativePath.split(/[\\\\/]/);\n return parts.length >= 4 ? decodeURIComponent(parts.at(-2)!) : null;\n}\n\nfunction normalizeStoredRelativePath(relativePath: string): string {\n return relativePath.split(/[\\\\/]/).join(\"/\");\n}\n\nasync function resolveElectronAppPath(name: MediaCacheAppPath): Promise<string> {\n const electron = await import(\"electron\");\n return electron.app.getPath(name);\n}\n\nasync function resolveStorageRoot(\n input: MediaCacheOptions[\"storagePath\"],\n resolveAppPath: RuntimeDependencies[\"resolveAppPath\"],\n): Promise<string> {\n const storagePath = parseWithSchema(mediaCacheStoragePathSchema, input, \"storage path\");\n const root = await resolveAppPath(storagePath.appPath);\n return join(root, ...(storagePath.segments ?? []));\n}\n\nfunction createFileResponse(filePath: string, request: Request): Response {\n const stats = statSync(filePath);\n const size = stats.size;\n const rangeHeader = request.headers.get(\"range\");\n const mimeType = inferMimeType(filePath);\n const baseHeaders = new Headers({\n \"accept-ranges\": \"bytes\",\n \"content-type\": mimeType,\n });\n\n if (request.method === \"HEAD\") {\n baseHeaders.set(\"content-length\", String(size));\n return new Response(null, {\n status: 200,\n headers: baseHeaders,\n });\n }\n\n if (!rangeHeader) {\n baseHeaders.set(\"content-length\", String(size));\n return new Response(Readable.toWeb(createReadStream(filePath)) as BodyInit, {\n status: 200,\n headers: baseHeaders,\n });\n }\n\n const parsedRange = parseByteRange(rangeHeader, size);\n if (!parsedRange) {\n baseHeaders.set(\"content-range\", `bytes */${size}`);\n return new Response(null, {\n status: 416,\n headers: baseHeaders,\n });\n }\n\n const { start, end } = parsedRange;\n const chunkLength = end - start + 1;\n baseHeaders.set(\"content-length\", String(chunkLength));\n baseHeaders.set(\"content-range\", `bytes ${start}-${end}/${size}`);\n return new Response(Readable.toWeb(createReadStream(filePath, { start, end })) as BodyInit, {\n status: 206,\n headers: baseHeaders,\n });\n}\n\nfunction parseByteRange(rangeHeader: string, size: number): { start: number; end: number } | null {\n if (!rangeHeader.startsWith(\"bytes=\")) {\n return null;\n }\n\n const value = rangeHeader.slice(\"bytes=\".length).trim();\n if (value.length === 0 || value.includes(\",\")) {\n return null;\n }\n\n const [startText, endText] = value.split(\"-\", 2);\n if (startText === undefined || endText === undefined) {\n return null;\n }\n\n if (startText === \"\") {\n const suffixLength = Number.parseInt(endText, 10);\n if (!Number.isFinite(suffixLength) || suffixLength <= 0) {\n return null;\n }\n const start = Math.max(size - suffixLength, 0);\n const end = size - 1;\n return start <= end ? { start, end } : null;\n }\n\n const start = Number.parseInt(startText, 10);\n const end = endText === \"\" ? size - 1 : Number.parseInt(endText, 10);\n if (!Number.isFinite(start) || !Number.isFinite(end)) {\n return null;\n }\n\n if (start < 0 || end < start || start >= size) {\n return null;\n }\n\n return {\n start,\n end: Math.min(end, size - 1),\n };\n}\n\nfunction inferMimeType(filePath: string): string {\n const lower = filePath.toLowerCase();\n if (lower.endsWith(\".mp4\")) {\n return \"video/mp4\";\n }\n if (lower.endsWith(\".webm\")) {\n return \"video/webm\";\n }\n if (lower.endsWith(\".mov\")) {\n return \"video/quicktime\";\n }\n if (lower.endsWith(\".jpg\") || lower.endsWith(\".jpeg\")) {\n return \"image/jpeg\";\n }\n if (lower.endsWith(\".png\")) {\n return \"image/png\";\n }\n if (lower.endsWith(\".gif\")) {\n return \"image/gif\";\n }\n if (lower.endsWith(\".webp\")) {\n return \"image/webp\";\n }\n if (lower.endsWith(\".vtt\")) {\n return \"text/vtt\";\n }\n if (lower.endsWith(\".srt\")) {\n return \"application/x-subrip\";\n }\n if (lower.endsWith(\".mp3\")) {\n return \"audio/mpeg\";\n }\n if (lower.endsWith(\".wav\")) {\n return \"audio/wav\";\n }\n if (lower.endsWith(\".html\")) {\n return \"text/html; charset=utf-8\";\n }\n if (lower.endsWith(\".txt\")) {\n return \"text/plain; charset=utf-8\";\n }\n if (lower.endsWith(\".json\")) {\n return \"application/json; charset=utf-8\";\n }\n if (lower.endsWith(\".pdf\")) {\n return \"application/pdf\";\n }\n return \"application/octet-stream\";\n}\n\nfunction normalizeLogValue(value: unknown): JsonValue | undefined {\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n\n if (value === undefined) {\n return undefined;\n }\n\n if (Array.isArray(value)) {\n return value\n .map((entry) => normalizeLogValue(entry))\n .filter((entry): entry is JsonValue => entry !== undefined);\n }\n\n if (value instanceof Error) {\n return {\n name: value.name,\n message: value.message,\n };\n }\n\n if (typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value)\n .map(([key, entry]) => [key, normalizeLogValue(entry)])\n .filter(([, entry]) => entry !== undefined),\n ) as JsonValue;\n }\n\n return String(value);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA8DA,MAAM,mBAAA,GAAA,YAAA,eAAA,QAAA,MAAA,CAAA,cAAA,WAAA,CAAA,KAAgD;AAEtD,MAAM,0BAA0B,QAAc,KAAK;AACnD,MAAM,6BAA6B;AAEnC,MAAM,mBAAuD;CAC3D,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACR;AAED,IAAI,sCAAsC;AAE1C,SAAS,sBAAsB,OAAyB;CACtD,OACE,iBAAiB,SACjB,UAAU,SACT,MAAgC,SAAS;;;AAK9C,SAAS,yBAAkC;CACzC,OAAO,QAAQ,IAAI,aAAa;;;;;;;;;;AAWlC,SAAS,4CAAkD;CACzD,IAAI,qCACF;CAGF,IAAI;CACJ,IAAI;EACF,CAAC,CAAE,YAAa,gBAAgB,WAAW;UACpC,OAAO;EACd,IAAI,sBAAsB,MAAM,EAC9B;EAEF,MAAM;;CAGR,IAAI,YAAY,QAAQ,OAAO,SAAS,gCAAgC,YACtE;CAGF,SAAS,4BAA4B,CACnC;EACE,QAAQ;EACR,YAAY;GACV,UAAU;GACV,QAAQ;GACR,iBAAiB;GACjB,QAAQ;GACT;EACF,CACF,CAAC;CACF,sCAAsC;;;;;;;AAQxC,SAAgB,mDAAyD;CACvE,sCAAsC;;;;;;;AAQxC,MAAa,0CAA0CA,+BAAAA;;;;;;AAOvD,MAAa,2CAA2CC,+BAAAA;;;;;;AAOxD,MAAa,0CAA0CC,+BAAAA;;AAgEvD,SAAgB,iBAAiB,SAA4C;CAC3E,OAAO,IAAI,WAAW,QAAQ;;AAGhC,IAAa,aAAb,MAAkD;CAsB7B;CArBnB,SAA0B,IAAIC,YAAAA,cAAc;CAC5C;;CAEA;;CAEA;;CAEA;;CAEA;CACA;CACA;CACA,kBAAwD;CACxD,KAAwC;CACxC,cAAqC;CACrC;CACA,cAA4C;CAC5C,cAAsB;CACtB,qBAA6B;CAE7B,YACE,SACA,MACA;EAFiB,KAAA,UAAA;EAGjB,KAAK,OAAO;GACV,WAAW,MAAM,aAAa,WAAW,MAAM,KAAK,WAAW;GAC/D,KAAK,MAAM,OAAO,KAAK;GACvB,OAAO,MAAM,SAASC,qBAAAA;GACtB,gBAAgB,MAAM,kBAAkB;GACxC,QAAQ,MAAM,UAAUC,iBAAAA;GACzB;EACD,MAAM,UAAU,wBAAwB,QAAQ,QAAQ;EACxD,KAAK,aAAa,QAAQ;EAC1B,KAAK,4BACH,KAAK,cAAc,QAAQ,wBAAwB,IAAI,QAAQ,IAAI,WAAW;EAChF,KAAK,YAAY,QAAQ;EACzB,KAAK,oBACH,QAAQ,UACP,KAAK,cAAc,QAAQ,KAAK,4BAA4B,UAAU;EACzE,KAAK,iBAAiB,QAAQ,kBAAkB,QAAQ,IAAI,aAAa;EACzE,IAAI,KAAK,gBACP,KAAK,qBAAqB,sBAAsB,QAAQ,aAAa;OAChE;GACL,IAAI,QAAQ,cACV,MAAM,IAAI,MACR,4GAED;GAEH,KAAK,qBAAqB;;EAE5B,IAAI,KAAK,gBACP,KAAK,QAAQ,QAAQ,0BAA0B;GAC7C,QAAQ,QAAQ,mBAAmB,OAAO,WAAW;GACrD,UAAU,QAAQ,IAAI,YAAY;GACnC,CAAC;EAEJ,IACE,KAAK,kBACL,KAAK,QAAQ,iBACb,KAAK,QAAQ,kBAAkB,SAE/B,KAAK,QAAQ,QAAQ,6CAA6C,EAChE,iBAAiB,KAAK,QAAQ,eAC/B,CAAC;EAEJ,KAAK,SAAS;GACZ,OAAO;GACP,aAAa;GACb,oBAAoB;GACpB,UAAU;GACV,SAAS;GACT,OAAO;GACP,WAAW,KAAK,KAAK,KAAK;GAC3B;EAED,IAAI,CAAC,KAAK,gBACR,2CAA2C;;CAI/C,MAAM,QAAuB;EAC3B,MAAM,KAAK,kBAAkB;EAC7B,MAAM,KAAK,WAAW;EACtB,MAAM,KAAK,SAAS;;CAGtB,MAAM,UAAyB;EAC7B,MAAM,KAAK,mBAAmB;EAC9B,IAAI,KAAK,aAAa;GACpB,KAAK,QAAQ,SAAS,eAAe;IACnC,OAAO,KAAK,OAAO;IACnB,sBAAsB,KAAK,OAAO;IACnC,CAAC;GACF,OAAO,KAAK;;EAGd,KAAK,cAAc,KAAK,SAAS,CAAC,cAAc;GAC9C,KAAK,cAAc;IACnB;EACF,OAAO,KAAK;;CAGd,MAAM,YAAuC;EAC3C,MAAM,KAAK,mBAAmB;EAC9B,OAAO,KAAK;;CAGd,MAAM,SAAS,KAAwD;EACrE,MAAM,YAAYC,2BAAAA,QAAQ,IAAI;EAC9B,MAAM,KAAK,mBAAmB;EAC9B,OAAO,KAAK,GAAI,SAAS,UAAU;;CAGrC,MAAM,YACJ,WACA,OACA,YAC+C;EAC/C,MAAM,qBAAqBC,4BAAAA,gBAAgBC,4BAAAA,mBAAmB,WAAW,aAAa;EACtF,MAAM,iBAAiBD,4BAAAA,gBAAgBC,4BAAAA,mBAAmB,OAAO,cAAc;EAC/E,MAAM,sBAAsBD,4BAAAA,gBAC1BE,4BAAAA,+BACA,YACA,yBACD;EACD,MAAM,KAAK,mBAAmB;EAC9B,OAAO,KAAK,GAAI,YAAY,oBAAoB,gBAAgB,oBAAoB;;CAGtF,MAAM,eACJ,MACA,YAC0C;EAC1C,MAAM,gBAAgBF,4BAAAA,gBAAgBC,4BAAAA,mBAAmB,MAAM,YAAY;EAC3E,MAAM,sBAAsBD,4BAAAA,gBAC1BE,4BAAAA,+BACA,YACA,6BACD;EACD,MAAM,KAAK,mBAAmB;EAC9B,OAAO,KAAK,GAAI,eAAeC,oBAAAA,cAAc,cAAc,EAAE,oBAAoB;;CAGnF,MAAM,iBAAiB,SAAkD;EACvE,MAAM,KAAK,mBAAmB;EAC9B,IAAI,KAAK,gBAAgB;GACvB,KAAK,qBAAqB;GAC1B,KAAK,QAAQ,SAAS,iCAAiC,EAAE,QAAQ,mBAAmB,CAAC;GACrF;;EAEF,IAAI,KAAK,oBAAoB;GAC3B,KAAK,QAAQ,SAAS,iCAAiC,EAAE,QAAQ,sBAAsB,CAAC;GACxF;;EAGF,MAAM,WAAW,SAAS,UAAU,OAAO,MAAM,OAAO;EACxD,MAAM,UAAU,SAAS,WAAW,UAAU,SAAS;EACvD,IAAI,CAAC,WAAW,OAAO,QAAQ,UAAU,WAAW,YAAY;GAC9D,KAAK,QAAQ,SAAS,iCAAiC,EACrD,QAAQ,uBACT,CAAC;GACF;;EAGF,MAAM,YACJ,SAAS,cACR,OAAO,SAAkB,aAAqB,mBAAmB,UAAU,QAAQ;EAEtF,QAAQ,SAAS,OAAO,SAAS,OAAO,YAAY;GAClD,MAAM,SAAS,IAAI,IAAI,QAAQ,IAAI;GACnC,MAAM,QAAQ,OAAO,SAAS,MAAM,IAAI,CAAC,OAAO,QAAQ;GAExD,IAAI,OAAO,aAAa,WAAW,MAAM,WAAW,GAClD,OAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;GAGnD,IAAI;GACJ,IAAI;IACF,WAAW,mBAAmB,MAAM,GAAG;WACjC;IACN,OAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;;GAGnD,MAAM,SAAS,KAAK,GAAI,uBAAuB,SAAS;GAExD,IAAI,CAAC,QAAQ;IACX,KAAK,QAAQ,SAAS,8BAA8B;KAClD,WAAW;KACX,QAAQ,QAAQ;KACjB,CAAC;IACF,OAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;;GAGnD,IAAI,CAAC,OAAO,gBAAgB,EAAA,GAAA,QAAA,YAAY,OAAO,aAAa,EAAE;IAC5D,KAAK,QAAQ,SAAS,iCAAiC;KACrD,WAAW;KACX,QAAQ,QAAQ;KACjB,CAAC;IACF,OAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;;GAGnD,KAAK,QAAQ,SAAS,mCAAmC;IACvD,WAAW;IACX,QAAQ,QAAQ;IAChB,OAAO,QAAQ,QAAQ,IAAI,QAAQ;IACpC,CAAC;GACF,OAAO,UAAU,SAAS,OAAO,aAAa;IAC9C;EAEF,KAAK,qBAAqB;EAC1B,KAAK,QAAQ,QAAQ,uBAAuB,EAAE,CAAC;;CAGjD,MAAM,UAAU,SAA2C;EACzD,MAAM,KAAK,mBAAmB;EAC9B,IAAI,KAAK,aAAa;GACpB,KAAK,QAAQ,SAAS,sBAAsB,EAAE,QAAQ,oBAAoB,CAAC;GAC3E;;EAGF,MAAM,WAAW,SAAS,UAAU,OAAO,MAAM,OAAO;EACxD,MAAM,UAAU,SAAS,WAAW,UAAU;EAC9C,IAAI,CAAC,WAAW,OAAO,QAAQ,WAAW,YAAY;GACpD,KAAK,QAAQ,SAAS,sBAAsB,EAC1C,QAAQ,wBACT,CAAC;GACF;;EAGF,QAAQ,OAAOC,mBAAAA,gBAAgB,WAAW,YAAY,KAAK,WAAW,CAAC;EACvE,QAAQ,OAAOA,mBAAAA,gBAAgB,SAAS,YAAY,KAAK,SAAS,CAAC;EACnE,QAAQ,OAAOA,mBAAAA,gBAAgB,UAAU,OAAO,QAAQ,QACtD,KAAK,SAAS,IAAI,CACnB;EACD,QAAQ,OACNA,mBAAAA,gBAAgB,aAChB,OAAO,QAAQ,WAAmB,OAAe,eAC/C,KAAK,YAAY,WAAW,OAAO,WAAW,CACjD;EACD,QAAQ,OACNA,mBAAAA,gBAAgB,gBAChB,OAAO,QAAQ,MAAc,eAC3B,KAAK,eAAe,MAAM,WAAW,CACxC;EAED,IAAI,UACF,KAAK,OAAO,GAAGA,mBAAAA,gBAAgB,gBAAgB,WAA6B;GAC1E,KAAK,MAAM,UAAU,SAAS,cAAc,eAAe,EACzD,OAAO,YAAY,KAAKA,mBAAAA,gBAAgB,eAAe,OAAO;IAEhE;EAGJ,KAAK,cAAc;EACnB,KAAK,QAAQ,QAAQ,gBAAgB,EAAE,CAAC;;CAG1C,MAAc,oBAAmC;EAC/C,IAAI,KAAK,IACP;EAGF,KAAK,cAAc,MAAM,mBAAmB,KAAK,QAAQ,aAAa,KAAK,KAAK,eAAe;EAC/F,CAAA,GAAA,QAAA,WAAU,KAAK,aAAa,EAAE,WAAW,MAAM,CAAC;EAChD,KAAK,oBAAoBC,+BAAAA,uBAAuB,KAAK,aAAa,KAAK;EACvE,CAAA,GAAA,QAAA,YAAA,GAAA,UAAA,MAAe,KAAK,aAAa,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;EAC9D,CAAA,GAAA,QAAA,YAAA,GAAA,UAAA,MAAe,KAAK,aAAa,QAAQ,EAAE,EAAE,WAAW,MAAM,CAAC;EAE/D,IAAI,CAAC,KAAK,gBACR,KAAK,QAAQ,QAAQ,0BAA0B,EAC7C,cAAc,KAAK,aACpB,CAAC;EAGJ,KAAK,KAAK,IAAIC,sBAAAA,mBAAmB,KAAK,aAAa;GACjD,gBAAgB,KAAK;GACrB,oBAAoB,KAAK;GACzB,SAAS,cAAc,QAAQ;IAC7B,IAAI,KAAK,cAAc,QAAQ,KAAK,2BAClC,KAAK,QAAQ,QAAQ,mCAAmC;KACtD,eAAe;KACf,OAAO,OAAO,OAAO,OAAO,IAAI,GAAG,KAAA;KACpC,CAAC;SAEF,0BAAA,uCAAuC,cAAc,IAAI;;GAG9D,CAAC;EACF,IAAI,KAAK,gBACP,KAAK,wBAAwB;EAE/B,IAAI,eAAwC;EAC5C,IAAI,qBAAoC;EACxC,IAAI,CAAC,KAAK,gBAAgB;GACxB,qBAAqB,KAAK,oCAAoC;GAC9D,IAAI;IACF,eAAe,KAAK,GAAG,YAAY;YAC5B,OAAO;IACd,IAAI,EAAE,iBAAiBC,sBAAAA,sBACrB,MAAM;IAGR,KAAK,QAAQ,QAAQ,2BAA2B;KAC9C,YAAY,MAAM;KAClB,eAAe,MAAM;KACtB,CAAC;;GAEJ,IAAI,cACF,KAAK,SAAS;QACT,IAAI,uBAAuB,MAChC,KAAK,SAAS;IACZ,GAAG,KAAK;IACR,OAAO;IACP;IACA,UAAU;IACV,OAAO;IACR;GAEH,KAAK,OAAO,qBAAqB;;EAEnC,KAAK,SAAS;GACZ,GAAG,KAAK;GACR,aAAa,KAAK;GACnB;EACD,KAAK,QAAQ,QAAQ,qBAAqB;GACxC,cAAc,KAAK;GACnB,sBAAsB,KAAK,OAAO;GAClC,yBAAyB,KAAK;GAC/B,CAAC;;CAGJ,qCAA4D;EAC1D,MAAM,qBAAqB,KAAK,GAAI,uBAAuB;EAC3D,MAAM,sBAAsB,KAAK,GAAI,yBAAyB,CAAC,QAC5D,iBAAiB,iBAAiB,mBACpC;EACD,IAAI,oBAAoB,WAAW,GACjC,OAAO;EAGT,KAAK,MAAM,sBAAsB,qBAAqB;GACpD,KAAK,6BAA6B,oBAAoB,mBAAmB;GACzE,KAAK,GAAI,iBAAiB,mBAAmB;;EAG/C,KAAK,QAAQ,QAAQ,uCAAuC;GAC1D,sBAAsB;GACtB,wBAAwB;GACxB,0BAA0B,oBAAoB;GAC/C,CAAC;EACF,OAAO;;CAGT,yBAAuC;EAGrC,KAAK,QAAQ,QAAQ,kCAAkC;GACrD,cAAc,KAAK;GACnB,QAAQ;GACT,CAAC;EACF,KAAK,GAAI,eAAe;EACxB,CAAA,GAAA,QAAA,SAAA,GAAA,UAAA,MAAY,KAAK,aAAc,QAAQ,EAAE;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;EAC1E,CAAA,GAAA,QAAA,SAAA,GAAA,UAAA,MAAY,KAAK,aAAc,OAAO,EAAE;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;EACzE,CAAA,GAAA,QAAA,YAAA,GAAA,UAAA,MAAe,KAAK,aAAc,QAAQ,EAAE,EAAE,WAAW,MAAM,CAAC;EAChE,CAAA,GAAA,QAAA,YAAA,GAAA,UAAA,MAAe,KAAK,aAAc,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;EAC/D,KAAK,SAAS;GACZ,GAAG,KAAK;GACR,OAAO;GACP,oBAAoB;GACpB,UAAU;GACV,SAAS;GACT,OAAO;GACP,WAAW,KAAK,KAAK,KAAK;GAC3B;;CAGH,MAAc,UAAyB;EACrC,MAAM,MAAM,KAAK,KAAK,KAAK;EAC3B,MAAM,QAAQ,KAAK,GAAI,cAAc,IAAI;EACzC,MAAM,QAAsB;GAC1B,aAAa;GACb,kBAAkB;GAClB,eAAe;GACf,iBAAiB;GAClB;EAED,KAAK,aAAa;GAChB,OAAO;GACP,OAAO;GACP,UAAU;IACR;IACA,OAAO;IACP,aAAa;IACb,iBAAiB;IACjB,kBAAkB;IAClB,eAAe;IACf,iBAAiB;IAClB;GACF,CAAC;EACF,KAAK,QAAQ,QAAQ,gBAAgB;GACnC,QAAQ;GACR,sBAAsB,KAAK,OAAO;GACnC,CAAC;EAEF,IAAI,qBAAoC;EAExC,IAAI;GAEF,MAAM,WAAWC,yBAAAA,sBAAqB,MADlB,KAAK,QAAQ,cAAc,EACH,YAAY,CAAC;GACzD,KAAK,sBAAsB,UAAU,MAAM;GAC3C,qBAAqB,KAAK,GAAI,uBAAuB,UAAU,IAAI;GACnE,KAAK,QAAQ,QAAQ,kBAAkB;IACrC,QAAQ;IACR,sBAAsB;IACtB,aAAa,SAAS,iBAAiB;IACvC,aAAa,SAAS,OAAO;IAC9B,CAAC;GAEF,MAAM,sBAAsB,KAAK,GAAI,uBAAuB;GAC5D,MAAM,gBAAgB,sBAClB,KAAK,GAAI,oBAAoB,oBAAoB,GACjD,EAAE;GACN,MAAM,eAAe,KAAK,GAAI,oBAAoB,mBAAmB;GACrE,MAAM,cAAc,aAAa;GAEjC,KAAK,gBAAgB,cAAc;IACjC,GAAG;IACH,OAAO;IACP,aAAa,aAAa;IAC3B,EAAE;GAEH,MAAM,mBAAmB,IAAI,IAAI,SAAS,OAAO,KAAK,UAAU,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;GACpF,MAAM,aAAa,IAAI,IAAI,cAAc,KAAK,QAAQ,CAAC,IAAI,UAAU,IAAI,CAAC,CAAC;GAC3E,MAAM,YAAyC,EAAE;GAEjD,KAAK,MAAM,OAAO,cAAc;IAC9B,MAAM,gBAAgB,iBAAiB,IAAI,IAAI,SAAS;IACxD,IAAI,CAAC,eACH,MAAM,IAAIC,sBAAAA,qBAAqB,UAAU,IAAI,SAAS,kCAAkC;IAG1F,MAAM,YAAY,WAAW,IAAI,IAAI,SAAS;IAC9C,MAAM,qBAAqB,WAAW,gBAAgB;IACtD,MAAM,+BACJ,uBAAuB,OAAO,OAAO,4BAA4B,mBAAmB;IACtF,MAAM,cAAc,cAAc;IAClC,MAAM,qBACJ,iCAAiC,SAAA,GAAA,QAAA,aAAA,GAAA,UAAA,MACjB,KAAK,aAAc,6BAA6B,CAAC;IAEnE,IAAI,KAAK,gBAAgB;KACvB,MAAM,iBAAiB;KACvB;;IAGF,IAAI;SACqB,2BAA2B,6BAChC,KAAK,aAAa;MAClC,KAAK,GAAI,sBACP,oBACA,IAAI,UACJ,8BACA,WAAW,YAAY,KACxB;MACD,MAAM,iBAAiB;MACvB;;;IAIJ,UAAU,KAAK;KACb,UAAU,IAAI;KACd,SAAS,cAAc;KACvB,UAAU,cAAc;KACxB,YAAY,cAAc;KAC3B,CAAC;;GAGJ,KAAK,QAAQ,QAAQ,eAAe;IAClC,QAAQ;IACR,cAAc,aAAa;IAC3B,gBAAgB,UAAU;IAC1B,gBAAgB,MAAM;IACtB,yBAAyB,KAAK;IAC/B,CAAC;GAEF,MAAM,KAAK,uBAAuB;GAClC,KAAK,gCAAgC,UAAU;GAC/C,IAAI,CAAC,KAAK,gBAAgB;IACxB,MAAM,KAAK,qBAAqB,UAAU;IAE1C,KAAK,gBAAgB,cAAc;KACjC,GAAG;KACH,OAAO;KACP,aAAa,aAAa;KAC1B,iBAAiB,MAAM;KACvB,eAAe,MAAM;KACtB,EAAE;IAEH,KAAK,MAAM,kBAAkB,WAAW;KACtC,KAAK,sBAAsB,UAAU,OAAO,eAAe;KAC3D,MAAM,WAAgC;MACpC,GAAG;MACH,SAAS,EAAE,KAAK,iBAAiB,IAAI,eAAe,SAAS,CAAE,KAAK;MACrE;KACD,KAAK,QAAQ,SAAS,0BAA0B;MAC9C,QAAQ;MACR,WAAW,SAAS;MACpB,SAAS,SAAS;MAClB,KAAK,SAAS,QAAQ;MACvB,CAAC;KACF,MAAM,EAAE,cAAc,qBACpB,MAAM,KAAK,uBAAuB,CAAC,cAAc,WAAW,eAAe;MACzE,MAAM,mBAAmB;MACzB,KAAK,gBAAgB,cAAc;OACjC,GAAG;OACH,iBAAiB,MAAM;OACxB,EAAE;OACH;KACJ,KAAK,GAAI,sBACP,oBACA,SAAS,UACT,cACA,iBACD;KACD,MAAM,oBAAoB;KAC1B,KAAK,QAAQ,SAAS,4BAA4B;MAChD,QAAQ;MACR,WAAW,SAAS;MACpB,eAAe;MAChB,CAAC;KACF,KAAK,gBAAgB,cAAc;MACjC,GAAG;MACH,iBAAiB,MAAM,mBAAmB,MAAM;MAChD,kBAAkB,MAAM;MACxB,eAAe,MAAM;MACrB,iBAAiB,MAAM;MACxB,EAAE;;UAGL,KAAK,gBAAgB,cAAc;IACjC,GAAG;IACH,iBAAiB,aAAa;IAC9B,eAAe,aAAa;IAC7B,EAAE;GAGL,KAAK,gBAAgB,cAAc;IACjC,GAAG;IACH,OAAO;IACR,EAAE;GAEH,MAAM,uBAAuB,KAAK,GAAI,mBAAmB,oBAAoB,KAAK,KAAK,KAAK,CAAC;GAC7F,KAAK,GAAI,mCAAmC,mBAAmB;GAC/D,IAAI,sBACF,KAAK,6BAA6B,sBAAsB,mBAAmB;GAE7E,KAAK,QAAQ,QAAQ,wBAAwB;IAC3C,QAAQ;IACR,wBAAwB;IACxB,sBAAsB;IACvB,CAAC;GAEF,KAAK,gBAAgB,cAAc;IACjC,GAAG;IACH,OAAO;IACR,EAAE;GACH,MAAM,KAAK,uBAAuB;GAElC,MAAM,UAAU,KAAK,GAAI,gBAAgB,OAAO,WAAW,KAAK,KAAK,KAAK,EAAE,MAAM;GAClF,KAAK,GAAI,iBAAiB,KAAK,QAAQ,oBAAoB,2BAA2B;GACtF,KAAK,QAAQ,QAAQ,kBAAkB;IACrC,QAAQ;IACR,sBAAsB;IACtB,cAAc,QAAQ,MAAM;IAC5B,mBAAmB,QAAQ,MAAM;IACjC,gBAAgB,QAAQ,MAAM;IAC9B,kBAAkB,QAAQ,MAAM;IACjC,CAAC;GACF,KAAK,aAAa;IAChB,OAAO;IACP,oBAAoB;IACpB,UAAU;IACV,SAAS;IACT,OAAO;IACR,CAAC;WACK,OAAO;GACd,IAAI,uBAAuB,MAAM;IAC/B,KAAK,6BAA6B,oBAAoB,KAAK,GAAI,uBAAuB,CAAC;IACvF,KAAK,GAAI,iBAAiB,mBAAmB;;GAG/C,MAAM,aAAaC,sBAAAA,kBAAkB,MAAM;GAC3C,MAAM,UAAU,KAAK,GAAI,gBACvB,OACA,SACA,KAAK,KAAK,KAAK,EACf,OACA,WAAW,MACX,WAAW,QACZ;GACD,KAAK,QAAQ,SAAS,eAAe;IACnC,QAAQ;IACR,sBAAsB,KAAK,GAAI,uBAAuB;IACtD,YAAY,WAAW;IACvB,eAAe,WAAW;IAC1B,cAAc,QAAQ,MAAM;IAC5B,mBAAmB,QAAQ,MAAM;IACjC,gBAAgB,QAAQ,MAAM;IAC9B,kBAAkB,QAAQ,MAAM;IACjC,CAAC;GAEF,KAAK,aAAa;IAChB,OACE,KAAK,kBAAkB,KAAK,QAAQ,kBAAkB,UAClD,UACA,KAAK,GAAI,uBAAuB,GAC9B,UACA;IACR,oBAAoB,KAAK,GAAI,uBAAuB;IACpD,UAAU;IACV,SAAS;IACT,OAAO;IACR,CAAC;GAEF,IAAI,KAAK,kBAAkB,KAAK,QAAQ,kBAAkB,SACxD,MAAM;;;CAKZ,sBACE,UACA,OACA,UACM;EACN,IAAI,CAAC,SAAS,WACZ;EAGF,MAAM,cAAc,KAAK,MAAM,SAAS,UAAU;EAClD,MAAM,MAAM,KAAK,KAAK,KAAK;EAC3B,IAAI,OAAO,MAAM,YAAY,IAAI,MAAM,aACrC;EAGF,KAAK,QAAQ,QAAQ,iBAAiB;GACpC,QAAQ;GACR,YAAY,SAAS;GACrB,QAAQ;GACR,WAAW,UAAU;GACtB,CAAC;EAEF,MAAM,aAAa,WAAW,uBAAuB,SAAS,aAAa;EAC3E,MAAM,IAAIC,sBAAAA,kBACR,yBAAyB,SAAS,YAAY,WAAW,qCAC1D;;CAGH,MAAc,qBAAqB,WAAuD;EACxF,MAAM,qBAAqB,UAAU,QAClC,KAAK,aAAa,OAAO,SAAS,cAAc,IACjD,EACD;EACD,MAAM,kCAAkC,UAAU,QAC/C,KAAK,aAAa,MAAM,KAAK,uBAAuB,CAAC,uBAAuB,SAAS,EACtF,EACD;EAED,IAAI,KAAK,QAAQ,kBAAkB,KAAA,GAAW;GAC5C,MAAM,eAAe,KAAK,oBAAA,GAAA,UAAA,MAAwB,KAAK,aAAc,QAAQ,CAAC;GAC9E,IAAI,eAAe,qBAAqB,KAAK,QAAQ,eAAe;IAClE,KAAK,QAAQ,QAAQ,0BAA0B;KAC7C,eAAe;KACf,0BAA0B;KAC1B,iBAAiB,KAAK,QAAQ;KAC/B,CAAC;IACF,MAAM,IAAIC,sBAAAA,kBACR,wBAAwB,eAAe,mBAAmB,yBAAyB,KAAK,QAAQ,cAAc,GAC/G;;;EAIL,MAAM,QAAQ,MAAM,KAAK,KAAK,OAAO,KAAK,YAAa;EACvD,MAAM,iBAAiB,OAAO,MAAM,OAAO,GAAG,OAAO,MAAM,MAAM;EACjE,MAAM,UAAUC,uBAAAA,0BAA0B,KAAK,QAAQ,iBAAiB;EACxE,IAAI,iBAAiB,kCAAkC,SAAS;GAC9D,KAAK,QAAQ,QAAQ,6BAA6B;IAChD,iBAAiB;IACjB,0BAA0B;IAC1B,oBAAoB;IACrB,CAAC;GACF,MAAM,IAAID,sBAAAA,kBACR,2BAA2B,gCAAgC,qCAAqC,QAAQ,GACzG;;;CAIL,MAAc,wBAAuC;EACnD,MAAM,KAAK,uBAAuB,CAAC,uBAAuB;;CAG5D,gCAAwC,WAA8C;EACpF,KAAK,uBAAuB,CAAC,gCAAgC,UAAU;;CAGzE,wBAAiD;EAC/C,OAAO,IAAIE,uBAAAA,gBAAgB,KAAK,aAAc,KAAK,MAAM;GACvD,kBAAkB,KAAK,QAAQ;GAC/B,UAAU,OAAO,OAAO,SAAS,EAAE,KAAK,KAAK,QAAQ,OAAO,OAAO,OAAO;GAC3E,CAAC;;CAGJ,6BACE,oBACA,oBACM;EACN,MAAM,cAAc,IAAI,IACtB,qBACI,KAAK,GAAI,oBAAoB,mBAAmB,CAAC,SAAS,QACxD,IAAI,eAAe,CAAC,4BAA4B,IAAI,aAAa,CAAC,GAAG,EAAE,CACxE,GACD,EAAE,CACP;EAED,KAAK,MAAM,OAAO,KAAK,GAAI,oBAAoB,mBAAmB,EAAE;GAClE,IAAI,CAAC,IAAI,cACP;GAGF,MAAM,yBAAyB,4BAA4B,IAAI,aAAa;GAC5E,IAAI,YAAY,IAAI,uBAAuB,EACzC;GAGF,MAAM,gBAAA,GAAA,UAAA,MAAoB,KAAK,aAAc,uBAAuB;GACpE,CAAA,GAAA,QAAA,QAAO,cAAc,EAAE,OAAO,MAAM,CAAC;GACrC,kBAAkB,cAAc,KAAK,YAAa;;;CAItD,6BACE,sBACA,oBACM;EACN,MAAM,iBAAiB,KAAK,GAAI,oBAAoB,qBAAqB;EACzE,MAAM,aAAa,IAAI,IACrB,KAAK,GAAI,oBAAoB,mBAAmB,CAAC,KAAK,QAAQ,CAC5D,IAAI,UACJ,IAAI,aACL,CAAC,CACH;EACD,MAAM,gBACJ,KAAK,KAAK,KAAK,IAAI,KAAK,QAAQ,sBAAsB;EAExD,IAAI,cAAc;EAClB,KAAK,MAAM,OAAO,gBAAgB;GAChC,MAAM,mBAAmB,WAAW,IAAI,IAAI,SAAS;GACrD,IAAI,IAAI,gBAAgB,qBAAqB,IAAI,cAAc;IAC7D,KAAK,GAAI,oBACP,IAAI,UACJ,IAAI,cACJ,sBACA,yBAAyB,IAAI,UAAU,IAAI,aAAa,EACxD,cACD;IACD,eAAe;;;EAGnB,KAAK,QAAQ,SAAS,8BAA8B;GAClD,wBAAwB;GACxB,sBAAsB;GACtB,cAAc;GACd,iBAAiB;GAClB,CAAC;;CAGJ,MAAc,wBAAuC;EACnD,MAAM,UAAU,KAAK,GAAI,2BAA2B,KAAK,KAAK,KAAK,CAAC;EACpE,IAAI,QAAQ,WAAW,GAAG;GACxB,KAAK,QAAQ,SAAS,0BAA0B,EAAE,eAAe,GAAG,CAAC;GACrE;;EAGF,KAAK,MAAM,YAAY,SAAS;GAC9B,MAAM,gBAAA,GAAA,UAAA,MACJ,KAAK,aACL,4BAA4B,SAAS,aAAa,CACnD;GACD,CAAA,GAAA,QAAA,QAAO,cAAc,EAAE,OAAO,MAAM,CAAC;GACrC,kBAAkB,cAAc,KAAK,YAAa;;EAGpD,KAAK,GAAI,uBAAuB,QAAQ,KAAK,SAAS,KAAK,YAAY,CAAC;EACxE,KAAK,QAAQ,SAAS,iBAAiB,EAAE,cAAc,QAAQ,QAAQ,CAAC;;CAG1E,mBAA2B,WAA2B;EACpD,IAAI,EAAA,GAAA,QAAA,YAAY,UAAU,EACxB,OAAO;EAGT,MAAM,SAAA,GAAA,QAAA,UAAiB,UAAU;EACjC,IAAI,MAAM,QAAQ,EAChB,OAAO,MAAM;EAGf,QAAA,GAAA,QAAA,aAAmB,UAAU,CAAC,QAC3B,KAAK,UAAU,MAAM,KAAK,oBAAA,GAAA,UAAA,MAAwB,WAAW,MAAM,CAAC,EACrE,EACD;;CAGH,eAAuB,WAA2D;EAChF,IAAI,CAAC,KAAK,OAAO,UACf;EAEF,KAAK,aAAa,EAChB,UAAU,UAAU,KAAK,OAAO,SAAS,EAC1C,CAAC;;CAGJ,aAAqB,SAA0C;EAC7D,KAAK,SAAS;GACZ,GAAG,KAAK;GACR,GAAG;GACH,WAAW,KAAK,KAAK,KAAK;GAC3B;EACD,KAAK,IAAI,WAAW,KAAK,QAAQ,KAAK,OAAO,UAAU;EACvD,KAAK,OAAO,KAAKV,mBAAAA,gBAAgB,eAAe,KAAK,OAAO;;CAG9D,QACE,OACA,OACA,SAA+D,EAAE,EAC3D;EACN,IAAI,KAAK,cAAc,QAAQ,CAAC,KAAK,2BACnC;EAGF,MAAM,YAAY,iBAAiB,KAAK;EACxC,IAAI,iBAAiB,SAAS,WAC5B;EAGF,MAAM,QAA4B;GAChC,WAAW,IAAI,KAAK,KAAK,KAAK,KAAK,CAAC,CAAC,aAAa;GAClD;GACA;GACA,SAAS;GACT,WAAW;GACX,GAAG;GACJ;EAED,IAAI,KAAK,cAAc,MAAM;GAC3B,IAAI;IACF,KAAK,WAAW,MAAM;WAChB;GAGR;;EAGF,kCAAkC,OAAO,OAAO,KAAK,UAAU;;;AAInE,SAAS,wBAAwB,SAI/B;CACA,IAAI,SAAS,SAAS,QAAQ,QAAQ,WAAW,KAAA,GAC/C,MAAM,IAAI,MACR,iFACD;CAGH,MAAM,SAAS,SAAS;CACxB,IAAI,WAAW,KAAA,KAAa,WAAW,aAAa,WAAW,QAC7D,MAAM,IAAI,MACR,mFAAmF,KAAK,UAAU,OAAO,GAC1G;CAGH,OAAO;EACL,OAAO,SAAS,SAAS;EACzB,OAAO,SAAS;EAChB,QAAQ,UAAU;EACnB;;AAGH,SAAS,kCACP,OACA,OACA,QACM;CACN,MAAM,OAAOW,4BAAAA,4BAA4B,OAAO,OAAO;CACvD,QAAQ,OAAR;EACE,KAAK;GACH,QAAQ,MAAM,KAAK;GACnB;EACF,KAAK;GACH,QAAQ,IAAI,KAAK;GACjB;EACF,KAAK;GACH,QAAQ,KAAK,KAAK;GAClB;EACF,KAAK;GACH,QAAQ,MAAM,KAAK;GACnB;EACF,SACE,QAAQ,IAAI,KAAK;;;AAIvB,SAAS,yBAAyB,UAAkB,cAA8B;CAChF,OAAO,KAAK,UAAU,CAAC,UAAU,aAAa,CAAC;;AAGjD,SAAS,sBAAsB,cAAwD;CACrF,IAAI,CAAC,cACH,OAAO;CAGT,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,aAAa;SACxB;EACN,MAAM,IAAI,MAAM,qCAAqC,aAAa,GAAG;;CAEvE,IAAI,OAAO,YAAY,OAAO,UAC5B,MAAM,IAAI,MAAM,6CAA6C;CAE/D,IAAI,OAAO,UAAU,OAAO,MAC1B,MAAM,IAAI,MAAM,iEAAiE;CAEnF,IAAI,OAAO,aAAa,OAAO,OAAO,aAAa,IACjD,MAAM,IAAI,MAAM,iDAAiD;CAGnE,OAAO,OAAO;;AAGhB,SAAS,kBAAkB,YAAoB,aAA2B;CACxE,IAAI,WAAA,GAAA,UAAA,SAAkB,WAAW;CACjC,OAAO,QAAQ,WAAW,YAAY,IAAI,YAAY,aAAa;EACjE,KAAA,GAAA,QAAA,YAAe,QAAQ,KAAA,GAAA,QAAA,aAAgB,QAAQ,CAAC,WAAW,GAAG;GAC5D,CAAA,GAAA,QAAA,QAAO,SAAS;IAAE,WAAW;IAAM,OAAO;IAAM,CAAC;GACjD,WAAA,GAAA,UAAA,SAAkB,QAAQ;GAC1B;;EAEF;;;AAIJ,SAAS,2BAA2B,cAAqC;CACvE,MAAM,QAAQ,aAAa,MAAM,QAAQ;CACzC,OAAO,MAAM,UAAU,IAAI,mBAAmB,MAAM,GAAG,GAAG,CAAE,GAAG;;AAGjE,SAAS,4BAA4B,cAA8B;CACjE,OAAO,aAAa,MAAM,QAAQ,CAAC,KAAK,IAAI;;AAG9C,eAAe,uBAAuB,MAA0C;CAE9E,QAAO,MADgB,OAAO,aACd,IAAI,QAAQ,KAAK;;AAGnC,eAAe,mBACb,OACA,gBACiB;CACjB,MAAM,cAAcf,4BAAAA,gBAAgBgB,4BAAAA,6BAA6B,OAAO,eAAe;CAEvF,QAAA,GAAA,UAAA,MAAY,MADO,eAAe,YAAY,QAAQ,EACpC,GAAI,YAAY,YAAY,EAAE,CAAE;;AAGpD,SAAS,mBAAmB,UAAkB,SAA4B;CAExE,MAAM,QAAA,GAAA,QAAA,UADiB,SACL,CAAC;CACnB,MAAM,cAAc,QAAQ,QAAQ,IAAI,QAAQ;CAChD,MAAM,WAAW,cAAc,SAAS;CACxC,MAAM,cAAc,IAAI,QAAQ;EAC9B,iBAAiB;EACjB,gBAAgB;EACjB,CAAC;CAEF,IAAI,QAAQ,WAAW,QAAQ;EAC7B,YAAY,IAAI,kBAAkB,OAAO,KAAK,CAAC;EAC/C,OAAO,IAAI,SAAS,MAAM;GACxB,QAAQ;GACR,SAAS;GACV,CAAC;;CAGJ,IAAI,CAAC,aAAa;EAChB,YAAY,IAAI,kBAAkB,OAAO,KAAK,CAAC;EAC/C,OAAO,IAAI,SAASC,YAAAA,SAAS,OAAA,GAAA,QAAA,kBAAuB,SAAS,CAAC,EAAc;GAC1E,QAAQ;GACR,SAAS;GACV,CAAC;;CAGJ,MAAM,cAAc,eAAe,aAAa,KAAK;CACrD,IAAI,CAAC,aAAa;EAChB,YAAY,IAAI,iBAAiB,WAAW,OAAO;EACnD,OAAO,IAAI,SAAS,MAAM;GACxB,QAAQ;GACR,SAAS;GACV,CAAC;;CAGJ,MAAM,EAAE,OAAO,QAAQ;CACvB,MAAM,cAAc,MAAM,QAAQ;CAClC,YAAY,IAAI,kBAAkB,OAAO,YAAY,CAAC;CACtD,YAAY,IAAI,iBAAiB,SAAS,MAAM,GAAG,IAAI,GAAG,OAAO;CACjE,OAAO,IAAI,SAASA,YAAAA,SAAS,OAAA,GAAA,QAAA,kBAAuB,UAAU;EAAE;EAAO;EAAK,CAAC,CAAC,EAAc;EAC1F,QAAQ;EACR,SAAS;EACV,CAAC;;AAGJ,SAAS,eAAe,aAAqB,MAAqD;CAChG,IAAI,CAAC,YAAY,WAAW,SAAS,EACnC,OAAO;CAGT,MAAM,QAAQ,YAAY,MAAM,EAAgB,CAAC,MAAM;CACvD,IAAI,MAAM,WAAW,KAAK,MAAM,SAAS,IAAI,EAC3C,OAAO;CAGT,MAAM,CAAC,WAAW,WAAW,MAAM,MAAM,KAAK,EAAE;CAChD,IAAI,cAAc,KAAA,KAAa,YAAY,KAAA,GACzC,OAAO;CAGT,IAAI,cAAc,IAAI;EACpB,MAAM,eAAe,OAAO,SAAS,SAAS,GAAG;EACjD,IAAI,CAAC,OAAO,SAAS,aAAa,IAAI,gBAAgB,GACpD,OAAO;EAET,MAAM,QAAQ,KAAK,IAAI,OAAO,cAAc,EAAE;EAC9C,MAAM,MAAM,OAAO;EACnB,OAAO,SAAS,MAAM;GAAE;GAAO;GAAK,GAAG;;CAGzC,MAAM,QAAQ,OAAO,SAAS,WAAW,GAAG;CAC5C,MAAM,MAAM,YAAY,KAAK,OAAO,IAAI,OAAO,SAAS,SAAS,GAAG;CACpE,IAAI,CAAC,OAAO,SAAS,MAAM,IAAI,CAAC,OAAO,SAAS,IAAI,EAClD,OAAO;CAGT,IAAI,QAAQ,KAAK,MAAM,SAAS,SAAS,MACvC,OAAO;CAGT,OAAO;EACL;EACA,KAAK,KAAK,IAAI,KAAK,OAAO,EAAE;EAC7B;;AAGH,SAAS,cAAc,UAA0B;CAC/C,MAAM,QAAQ,SAAS,aAAa;CACpC,IAAI,MAAM,SAAS,OAAO,EACxB,OAAO;CAET,IAAI,MAAM,SAAS,QAAQ,EACzB,OAAO;CAET,IAAI,MAAM,SAAS,OAAO,EACxB,OAAO;CAET,IAAI,MAAM,SAAS,OAAO,IAAI,MAAM,SAAS,QAAQ,EACnD,OAAO;CAET,IAAI,MAAM,SAAS,OAAO,EACxB,OAAO;CAET,IAAI,MAAM,SAAS,OAAO,EACxB,OAAO;CAET,IAAI,MAAM,SAAS,QAAQ,EACzB,OAAO;CAET,IAAI,MAAM,SAAS,OAAO,EACxB,OAAO;CAET,IAAI,MAAM,SAAS,OAAO,EACxB,OAAO;CAET,IAAI,MAAM,SAAS,OAAO,EACxB,OAAO;CAET,IAAI,MAAM,SAAS,OAAO,EACxB,OAAO;CAET,IAAI,MAAM,SAAS,QAAQ,EACzB,OAAO;CAET,IAAI,MAAM,SAAS,OAAO,EACxB,OAAO;CAET,IAAI,MAAM,SAAS,QAAQ,EACzB,OAAO;CAET,IAAI,MAAM,SAAS,OAAO,EACxB,OAAO;CAET,OAAO"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { AssetKeyInput, FileStemMatch, MediaCacheAppPath, MediaCacheOptions, MediaCacheStatus, PaginationInput, PaginationResult, ResolvedMediaAsset } from "../shared/types.cjs";
|
|
2
|
+
import { disableStorageRootLockForTests, enableStorageRootLockForTests, resetStorageRootLocksForTests } from "./storage-root-lock.cjs";
|
|
3
|
+
import { DEFAULT_RESERVE_FREE_BYTES, effectiveReserveFreeBytes } from "./asset-download.cjs";
|
|
4
|
+
import { statfs } from "node:fs/promises";
|
|
5
|
+
import { IpcMain, Session } from "electron";
|
|
6
|
+
|
|
7
|
+
//#region src/main/media-cache.d.ts
|
|
8
|
+
type StatFsResult = Awaited<ReturnType<typeof statfs>>;
|
|
9
|
+
/**
|
|
10
|
+
* Clears the internal `media:` scheme registration flag so subsequent {@link MediaCache}
|
|
11
|
+
* construction runs registration again. **Unit tests only**; do not use in application code.
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
declare function resetMediaCacheProtocolRegistrationStateForTests(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Clears any held storage-root locks so tests can run multiple scenarios in one process.
|
|
17
|
+
* **Unit tests only**; do not use in application code.
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
declare const resetMediaCacheStorageRootLocksForTests: typeof resetStorageRootLocksForTests;
|
|
21
|
+
/**
|
|
22
|
+
* Disables storage-root exclusivity checks so tests can exercise multiple cache instances freely.
|
|
23
|
+
* **Unit tests only**; do not use in application code.
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
declare const disableMediaCacheStorageRootLockForTests: typeof disableStorageRootLockForTests;
|
|
27
|
+
/**
|
|
28
|
+
* Re-enables storage-root exclusivity checks after test-only overrides.
|
|
29
|
+
* **Unit tests only**; do not use in application code.
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
declare const enableMediaCacheStorageRootLockForTests: typeof enableStorageRootLockForTests;
|
|
33
|
+
interface RuntimeDependencies {
|
|
34
|
+
fetchImpl: typeof globalThis.fetch;
|
|
35
|
+
now: () => number;
|
|
36
|
+
sleep: (delayMs: number) => Promise<void>;
|
|
37
|
+
resolveAppPath: (name: MediaCacheAppPath) => Promise<string>;
|
|
38
|
+
statfs: (path: string) => Promise<StatFsResult>;
|
|
39
|
+
}
|
|
40
|
+
/** Options for {@link MediaCacheMain.registerProtocol}. */
|
|
41
|
+
interface RegisterProtocolOptions {
|
|
42
|
+
/** Electron session to register the `media:` handler on (defaults to `defaultSession`). */
|
|
43
|
+
session?: Session;
|
|
44
|
+
/** Custom file-serving handler; receives the protocol request and resolved local path. */
|
|
45
|
+
fetchFile?: (request: Request, filePath: string) => Promise<Response>;
|
|
46
|
+
}
|
|
47
|
+
/** Options for {@link MediaCacheMain.attachIpc}. */
|
|
48
|
+
interface AttachIpcOptions {
|
|
49
|
+
/** Custom `ipcMain` instance (defaults to `electron.ipcMain`). */
|
|
50
|
+
ipcMain?: IpcMain;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Main-process controller: syncs the store, stores blobs, serves `media:` URLs, and can expose
|
|
54
|
+
* the same operations to renderers via IPC. In offline mode (default), construct the cache before
|
|
55
|
+
* `app.whenReady()` so the privileged `media:` scheme can be registered, then after ready call
|
|
56
|
+
* {@link MediaCacheMain.start} for the one-call happy path.
|
|
57
|
+
*
|
|
58
|
+
* `MediaCache` requires exclusive ownership of its resolved `storageRoot`. The first process that
|
|
59
|
+
* acquires that root remains the owner for the process lifetime. A second process (or cache
|
|
60
|
+
* instance) targeting the same root throws {@link import("../shared/errors.js").StorageOwnershipError}.
|
|
61
|
+
*/
|
|
62
|
+
interface MediaCacheMain {
|
|
63
|
+
/**
|
|
64
|
+
* One-call setup: register protocol, attach IPC, initialize storage, then run initial sync.
|
|
65
|
+
* Cache-root ownership is enforced during initialization, before SQLite or blob writes begin.
|
|
66
|
+
*/
|
|
67
|
+
start(): Promise<void>;
|
|
68
|
+
/** Runs or joins the current sync; concurrent callers share one run. */
|
|
69
|
+
syncNow(): Promise<void>;
|
|
70
|
+
/** Latest status snapshot (phase, progress, last run, error). */
|
|
71
|
+
getStatus(): Promise<MediaCacheStatus>;
|
|
72
|
+
/** Single asset by key, or null if missing. */
|
|
73
|
+
getAsset(key: string): Promise<ResolvedMediaAsset | null>;
|
|
74
|
+
/** Assets matching a secondary index value, paginated. */
|
|
75
|
+
listByIndex(indexName: string, value: string, pagination?: PaginationInput): Promise<PaginationResult<ResolvedMediaAsset>>;
|
|
76
|
+
/** Search by normalized file name stem. */
|
|
77
|
+
findByFileStem(stem: string, pagination?: PaginationInput): Promise<PaginationResult<FileStemMatch>>;
|
|
78
|
+
/** Register the `media:` protocol handler for the given session (default: `defaultSession`). */
|
|
79
|
+
registerProtocol(options?: RegisterProtocolOptions): Promise<void>;
|
|
80
|
+
/** Wire `ipcMain` handlers and broadcast status to all browser windows. */
|
|
81
|
+
attachIpc(options?: AttachIpcOptions): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
/** Constructs a {@link MediaCacheMain} instance with the given options (does not start sync until `start` or `syncNow`). */
|
|
84
|
+
declare function createMediaCache(options: MediaCacheOptions): MediaCacheMain;
|
|
85
|
+
declare class MediaCache implements MediaCacheMain {
|
|
86
|
+
private readonly options;
|
|
87
|
+
private readonly events;
|
|
88
|
+
private readonly deps;
|
|
89
|
+
/** When no custom handler is configured, log to the main-process console in development. */
|
|
90
|
+
private readonly defaultDevelopmentConsole;
|
|
91
|
+
/** Custom structured log sink when configured via `options.logging.onLog`. */
|
|
92
|
+
private readonly logHandler;
|
|
93
|
+
/** Built-in console line shape when {@link defaultDevelopmentConsole} is active. */
|
|
94
|
+
private readonly logFormat;
|
|
95
|
+
/** Minimum severity emitted for the currently active log sink. */
|
|
96
|
+
private readonly effectiveLogLevel;
|
|
97
|
+
private readonly devPassthrough;
|
|
98
|
+
private readonly assetBaseUrlOrigin;
|
|
99
|
+
private storageRootLock;
|
|
100
|
+
private db;
|
|
101
|
+
private storageRoot;
|
|
102
|
+
private status;
|
|
103
|
+
private syncPromise;
|
|
104
|
+
private ipcAttached;
|
|
105
|
+
private protocolRegistered;
|
|
106
|
+
constructor(options: MediaCacheOptions, deps?: Partial<RuntimeDependencies>);
|
|
107
|
+
start(): Promise<void>;
|
|
108
|
+
syncNow(): Promise<void>;
|
|
109
|
+
getStatus(): Promise<MediaCacheStatus>;
|
|
110
|
+
getAsset(key: AssetKeyInput): Promise<ResolvedMediaAsset | null>;
|
|
111
|
+
listByIndex(indexName: string, value: string, pagination?: PaginationInput): Promise<PaginationResult<ResolvedMediaAsset>>;
|
|
112
|
+
findByFileStem(stem: string, pagination?: PaginationInput): Promise<PaginationResult<FileStemMatch>>;
|
|
113
|
+
registerProtocol(options?: RegisterProtocolOptions): Promise<void>;
|
|
114
|
+
attachIpc(options?: AttachIpcOptions): Promise<void>;
|
|
115
|
+
private ensureInitialized;
|
|
116
|
+
private reconcileOrphanedStagedGenerations;
|
|
117
|
+
private prepareDevRuntimeState;
|
|
118
|
+
private runSync;
|
|
119
|
+
private assertStoreNotExpired;
|
|
120
|
+
private enforceStorageLimits;
|
|
121
|
+
private ensureFileSpaceCommit;
|
|
122
|
+
private cleanupObsoletePartialDownloads;
|
|
123
|
+
private createAssetDownloader;
|
|
124
|
+
private cleanupStagedGenerationFiles;
|
|
125
|
+
private markRemovedAssetsForDeletion;
|
|
126
|
+
private pruneExpiredDeletions;
|
|
127
|
+
private currentBytesOnDisk;
|
|
128
|
+
private updateProgress;
|
|
129
|
+
private updateStatus;
|
|
130
|
+
private emitLog;
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { DEFAULT_RESERVE_FREE_BYTES, MediaCache, MediaCacheMain, createMediaCache, disableMediaCacheStorageRootLockForTests, effectiveReserveFreeBytes, enableMediaCacheStorageRootLockForTests, resetMediaCacheProtocolRegistrationStateForTests, resetMediaCacheStorageRootLocksForTests };
|
|
134
|
+
//# sourceMappingURL=media-cache.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-cache.d.cts","names":[],"sources":["../../src/main/media-cache.ts"],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { AssetKeyInput, FileStemMatch, MediaCacheAppPath, MediaCacheOptions, MediaCacheStatus, PaginationInput, PaginationResult, ResolvedMediaAsset } from "../shared/types.js";
|
|
2
|
+
import { disableStorageRootLockForTests, enableStorageRootLockForTests, resetStorageRootLocksForTests } from "./storage-root-lock.js";
|
|
3
|
+
import { DEFAULT_RESERVE_FREE_BYTES, effectiveReserveFreeBytes } from "./asset-download.js";
|
|
4
|
+
import { statfs } from "node:fs/promises";
|
|
5
|
+
import { IpcMain, Session } from "electron";
|
|
6
|
+
|
|
7
|
+
//#region src/main/media-cache.d.ts
|
|
8
|
+
type StatFsResult = Awaited<ReturnType<typeof statfs>>;
|
|
9
|
+
/**
|
|
10
|
+
* Clears the internal `media:` scheme registration flag so subsequent {@link MediaCache}
|
|
11
|
+
* construction runs registration again. **Unit tests only**; do not use in application code.
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
declare function resetMediaCacheProtocolRegistrationStateForTests(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Clears any held storage-root locks so tests can run multiple scenarios in one process.
|
|
17
|
+
* **Unit tests only**; do not use in application code.
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
declare const resetMediaCacheStorageRootLocksForTests: typeof resetStorageRootLocksForTests;
|
|
21
|
+
/**
|
|
22
|
+
* Disables storage-root exclusivity checks so tests can exercise multiple cache instances freely.
|
|
23
|
+
* **Unit tests only**; do not use in application code.
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
declare const disableMediaCacheStorageRootLockForTests: typeof disableStorageRootLockForTests;
|
|
27
|
+
/**
|
|
28
|
+
* Re-enables storage-root exclusivity checks after test-only overrides.
|
|
29
|
+
* **Unit tests only**; do not use in application code.
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
declare const enableMediaCacheStorageRootLockForTests: typeof enableStorageRootLockForTests;
|
|
33
|
+
interface RuntimeDependencies {
|
|
34
|
+
fetchImpl: typeof globalThis.fetch;
|
|
35
|
+
now: () => number;
|
|
36
|
+
sleep: (delayMs: number) => Promise<void>;
|
|
37
|
+
resolveAppPath: (name: MediaCacheAppPath) => Promise<string>;
|
|
38
|
+
statfs: (path: string) => Promise<StatFsResult>;
|
|
39
|
+
}
|
|
40
|
+
/** Options for {@link MediaCacheMain.registerProtocol}. */
|
|
41
|
+
interface RegisterProtocolOptions {
|
|
42
|
+
/** Electron session to register the `media:` handler on (defaults to `defaultSession`). */
|
|
43
|
+
session?: Session;
|
|
44
|
+
/** Custom file-serving handler; receives the protocol request and resolved local path. */
|
|
45
|
+
fetchFile?: (request: Request, filePath: string) => Promise<Response>;
|
|
46
|
+
}
|
|
47
|
+
/** Options for {@link MediaCacheMain.attachIpc}. */
|
|
48
|
+
interface AttachIpcOptions {
|
|
49
|
+
/** Custom `ipcMain` instance (defaults to `electron.ipcMain`). */
|
|
50
|
+
ipcMain?: IpcMain;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Main-process controller: syncs the store, stores blobs, serves `media:` URLs, and can expose
|
|
54
|
+
* the same operations to renderers via IPC. In offline mode (default), construct the cache before
|
|
55
|
+
* `app.whenReady()` so the privileged `media:` scheme can be registered, then after ready call
|
|
56
|
+
* {@link MediaCacheMain.start} for the one-call happy path.
|
|
57
|
+
*
|
|
58
|
+
* `MediaCache` requires exclusive ownership of its resolved `storageRoot`. The first process that
|
|
59
|
+
* acquires that root remains the owner for the process lifetime. A second process (or cache
|
|
60
|
+
* instance) targeting the same root throws {@link import("../shared/errors.js").StorageOwnershipError}.
|
|
61
|
+
*/
|
|
62
|
+
interface MediaCacheMain {
|
|
63
|
+
/**
|
|
64
|
+
* One-call setup: register protocol, attach IPC, initialize storage, then run initial sync.
|
|
65
|
+
* Cache-root ownership is enforced during initialization, before SQLite or blob writes begin.
|
|
66
|
+
*/
|
|
67
|
+
start(): Promise<void>;
|
|
68
|
+
/** Runs or joins the current sync; concurrent callers share one run. */
|
|
69
|
+
syncNow(): Promise<void>;
|
|
70
|
+
/** Latest status snapshot (phase, progress, last run, error). */
|
|
71
|
+
getStatus(): Promise<MediaCacheStatus>;
|
|
72
|
+
/** Single asset by key, or null if missing. */
|
|
73
|
+
getAsset(key: string): Promise<ResolvedMediaAsset | null>;
|
|
74
|
+
/** Assets matching a secondary index value, paginated. */
|
|
75
|
+
listByIndex(indexName: string, value: string, pagination?: PaginationInput): Promise<PaginationResult<ResolvedMediaAsset>>;
|
|
76
|
+
/** Search by normalized file name stem. */
|
|
77
|
+
findByFileStem(stem: string, pagination?: PaginationInput): Promise<PaginationResult<FileStemMatch>>;
|
|
78
|
+
/** Register the `media:` protocol handler for the given session (default: `defaultSession`). */
|
|
79
|
+
registerProtocol(options?: RegisterProtocolOptions): Promise<void>;
|
|
80
|
+
/** Wire `ipcMain` handlers and broadcast status to all browser windows. */
|
|
81
|
+
attachIpc(options?: AttachIpcOptions): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
/** Constructs a {@link MediaCacheMain} instance with the given options (does not start sync until `start` or `syncNow`). */
|
|
84
|
+
declare function createMediaCache(options: MediaCacheOptions): MediaCacheMain;
|
|
85
|
+
declare class MediaCache implements MediaCacheMain {
|
|
86
|
+
private readonly options;
|
|
87
|
+
private readonly events;
|
|
88
|
+
private readonly deps;
|
|
89
|
+
/** When no custom handler is configured, log to the main-process console in development. */
|
|
90
|
+
private readonly defaultDevelopmentConsole;
|
|
91
|
+
/** Custom structured log sink when configured via `options.logging.onLog`. */
|
|
92
|
+
private readonly logHandler;
|
|
93
|
+
/** Built-in console line shape when {@link defaultDevelopmentConsole} is active. */
|
|
94
|
+
private readonly logFormat;
|
|
95
|
+
/** Minimum severity emitted for the currently active log sink. */
|
|
96
|
+
private readonly effectiveLogLevel;
|
|
97
|
+
private readonly devPassthrough;
|
|
98
|
+
private readonly assetBaseUrlOrigin;
|
|
99
|
+
private storageRootLock;
|
|
100
|
+
private db;
|
|
101
|
+
private storageRoot;
|
|
102
|
+
private status;
|
|
103
|
+
private syncPromise;
|
|
104
|
+
private ipcAttached;
|
|
105
|
+
private protocolRegistered;
|
|
106
|
+
constructor(options: MediaCacheOptions, deps?: Partial<RuntimeDependencies>);
|
|
107
|
+
start(): Promise<void>;
|
|
108
|
+
syncNow(): Promise<void>;
|
|
109
|
+
getStatus(): Promise<MediaCacheStatus>;
|
|
110
|
+
getAsset(key: AssetKeyInput): Promise<ResolvedMediaAsset | null>;
|
|
111
|
+
listByIndex(indexName: string, value: string, pagination?: PaginationInput): Promise<PaginationResult<ResolvedMediaAsset>>;
|
|
112
|
+
findByFileStem(stem: string, pagination?: PaginationInput): Promise<PaginationResult<FileStemMatch>>;
|
|
113
|
+
registerProtocol(options?: RegisterProtocolOptions): Promise<void>;
|
|
114
|
+
attachIpc(options?: AttachIpcOptions): Promise<void>;
|
|
115
|
+
private ensureInitialized;
|
|
116
|
+
private reconcileOrphanedStagedGenerations;
|
|
117
|
+
private prepareDevRuntimeState;
|
|
118
|
+
private runSync;
|
|
119
|
+
private assertStoreNotExpired;
|
|
120
|
+
private enforceStorageLimits;
|
|
121
|
+
private ensureFileSpaceCommit;
|
|
122
|
+
private cleanupObsoletePartialDownloads;
|
|
123
|
+
private createAssetDownloader;
|
|
124
|
+
private cleanupStagedGenerationFiles;
|
|
125
|
+
private markRemovedAssetsForDeletion;
|
|
126
|
+
private pruneExpiredDeletions;
|
|
127
|
+
private currentBytesOnDisk;
|
|
128
|
+
private updateProgress;
|
|
129
|
+
private updateStatus;
|
|
130
|
+
private emitLog;
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { DEFAULT_RESERVE_FREE_BYTES, MediaCache, MediaCacheMain, createMediaCache, disableMediaCacheStorageRootLockForTests, effectiveReserveFreeBytes, enableMediaCacheStorageRootLockForTests, resetMediaCacheProtocolRegistrationStateForTests, resetMediaCacheStorageRootLocksForTests };
|
|
134
|
+
//# sourceMappingURL=media-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-cache.d.ts","names":[],"sources":["../../src/main/media-cache.ts"],"mappings":"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"}
|