@bendyline/docblocks 2.1.0 → 2.1.2

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.
Files changed (72) hide show
  1. package/README.md +2 -2
  2. package/dist/{chunk-WJKYY5QR.js → chunk-4437YWLE.js} +5 -6
  3. package/dist/{chunk-XUML3MKV.js → chunk-45T5AFCN.js} +5 -6
  4. package/dist/{chunk-COYQXQK3.js → chunk-6L3JRDE7.js} +4 -5
  5. package/dist/{chunk-BNKV7MTO.js → chunk-6XHOOBYF.js} +0 -1
  6. package/dist/{chunk-U722UUFN.js → chunk-73OOROXH.js} +0 -1
  7. package/dist/{chunk-JS6XFWQK.js → chunk-AYWKIZLD.js} +0 -1
  8. package/dist/{chunk-VTCSF7VS.js → chunk-BVY7GGNV.js} +4 -5
  9. package/dist/{chunk-B56KWCSE.js → chunk-DOV5VRTD.js} +61 -10
  10. package/dist/{chunk-AEHCVWU5.js → chunk-DZB666V7.js} +0 -1
  11. package/dist/{chunk-GM5YXU4L.js → chunk-KQ6756AJ.js} +1 -2
  12. package/dist/{chunk-BXMF5YQJ.js → chunk-LMMOFRGQ.js} +0 -1
  13. package/dist/{chunk-FNW556FN.js → chunk-M2PUNKHS.js} +0 -1
  14. package/dist/{chunk-FA7UNOXK.js → chunk-M5ZNQGR5.js} +1 -2
  15. package/dist/{chunk-P4WATND4.js → chunk-M62ZGJ5I.js} +0 -1
  16. package/dist/{chunk-RVPGQBWR.js → chunk-OQCPBOQA.js} +0 -1
  17. package/dist/{chunk-MNSZ7EZN.js → chunk-QI6YKTQV.js} +2 -3
  18. package/dist/{chunk-P3DVZQ6S.js → chunk-SJBB5OD7.js} +2 -3
  19. package/dist/{chunk-EZKJZUNJ.js → chunk-XAMILHQP.js} +0 -1
  20. package/dist/{chunk-6IYMTYMQ.js → chunk-YM7O3PWO.js} +7 -8
  21. package/dist/{chunk-DN7A33LL.js → chunk-ZO7G53QP.js} +4 -5
  22. package/dist/document/index.js +5 -6
  23. package/dist/filesystem/electron.js +6 -7
  24. package/dist/filesystem/index.js +16 -17
  25. package/dist/filesystem/indexeddb.js +8 -9
  26. package/dist/filesystem/memory.js +5 -6
  27. package/dist/filesystem/native.js +6 -7
  28. package/dist/host/index.js +2 -3
  29. package/dist/index.js +19 -20
  30. package/dist/mcp/index.d.ts +6 -3
  31. package/dist/mcp/index.js +4 -3
  32. package/dist/mcp/zod.d.ts +1859 -648
  33. package/dist/mcp/zod.js +78 -7
  34. package/dist/share/index.js +1 -2
  35. package/dist/{types-CX893hgH.d.ts → types-B7BBB5az.d.ts} +63 -2
  36. package/dist/vscode/index.d.ts +5 -0
  37. package/dist/vscode/index.js +11 -5
  38. package/dist/workspace/index.js +3 -4
  39. package/package.json +3 -2
  40. package/dist/chunk-6IYMTYMQ.js.map +0 -1
  41. package/dist/chunk-AEHCVWU5.js.map +0 -1
  42. package/dist/chunk-B56KWCSE.js.map +0 -1
  43. package/dist/chunk-BNKV7MTO.js.map +0 -1
  44. package/dist/chunk-BXMF5YQJ.js.map +0 -1
  45. package/dist/chunk-COYQXQK3.js.map +0 -1
  46. package/dist/chunk-DN7A33LL.js.map +0 -1
  47. package/dist/chunk-EZKJZUNJ.js.map +0 -1
  48. package/dist/chunk-FA7UNOXK.js.map +0 -1
  49. package/dist/chunk-FNW556FN.js.map +0 -1
  50. package/dist/chunk-GM5YXU4L.js.map +0 -1
  51. package/dist/chunk-JS6XFWQK.js.map +0 -1
  52. package/dist/chunk-MNSZ7EZN.js.map +0 -1
  53. package/dist/chunk-P3DVZQ6S.js.map +0 -1
  54. package/dist/chunk-P4WATND4.js.map +0 -1
  55. package/dist/chunk-RVPGQBWR.js.map +0 -1
  56. package/dist/chunk-U722UUFN.js.map +0 -1
  57. package/dist/chunk-VTCSF7VS.js.map +0 -1
  58. package/dist/chunk-WJKYY5QR.js.map +0 -1
  59. package/dist/chunk-XUML3MKV.js.map +0 -1
  60. package/dist/document/index.js.map +0 -1
  61. package/dist/filesystem/electron.js.map +0 -1
  62. package/dist/filesystem/index.js.map +0 -1
  63. package/dist/filesystem/indexeddb.js.map +0 -1
  64. package/dist/filesystem/memory.js.map +0 -1
  65. package/dist/filesystem/native.js.map +0 -1
  66. package/dist/host/index.js.map +0 -1
  67. package/dist/index.js.map +0 -1
  68. package/dist/mcp/index.js.map +0 -1
  69. package/dist/mcp/zod.js.map +0 -1
  70. package/dist/share/index.js.map +0 -1
  71. package/dist/vscode/index.js.map +0 -1
  72. package/dist/workspace/index.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/document/document-session.ts","../src/document/filesystem-target.ts","../src/document/recovery-journal.ts"],"sourcesContent":["import type {\n DocumentCommitTarget,\n DocumentConflictStrategy,\n DocumentExternalChangeResult,\n DocumentExternalSnapshot,\n DocumentSaveReason,\n DocumentSessionConflict,\n DocumentSessionEditScope,\n DocumentSessionLifecycle,\n DocumentSessionOptions,\n DocumentSessionSnapshot,\n DocumentSessionStatus,\n DocumentSessionTransition,\n} from './types.js';\nimport { isFileSystemMoveStateError } from '../filesystem/move-error.js';\nimport type { DocumentRecoveryJournal } from './recovery-journal.js';\n\ntype SessionListener = () => void;\n\nconst DEFAULT_AUTO_SAVE_RETRY_DELAYS_MS = [1_000, 3_000, 10_000] as const;\n\n/**\n * Raised by a commit target when optimistic external-change verification\n * fails. The session converts it into durable conflict state.\n */\nexport class DocumentCommitConflictError extends Error {\n public readonly externalContent: string | null;\n public readonly externalVersion: string | number | null;\n\n public constructor(\n message: string,\n externalContent: string | null,\n externalVersion: string | number | null = null,\n ) {\n super(message);\n this.name = 'DocumentCommitConflictError';\n this.externalContent = externalContent;\n this.externalVersion = externalVersion;\n }\n}\n\nexport class DocumentSessionConflictError extends Error {\n public readonly conflict: DocumentSessionConflict;\n\n public constructor(conflict: DocumentSessionConflict) {\n super('The document changed outside DocBlocks while local edits were pending.');\n this.name = 'DocumentSessionConflictError';\n this.conflict = conflict;\n }\n}\n\n/**\n * A revisioned, serialized persistence boundary for one active document.\n *\n * Invariants:\n * - only drain() calls target.commit();\n * - at most one commit is in flight;\n * - revisions never decrease, including across target transitions;\n * - destructive operations wait for an in-flight commit and run last;\n * - a target is never changed by a React effect cleanup.\n */\nexport class DocumentSession {\n private readonly autoSaveDelayMs: number;\n private readonly autoSaveRetryDelaysMs: readonly number[];\n private readonly recoveryJournal: DocumentRecoveryJournal | null;\n private readonly listeners = new Set<SessionListener>();\n private autoSaveEnabled: boolean;\n\n private target: DocumentCommitTarget | null = null;\n private content = '';\n private persistedContent: string | null = null;\n private revision = 0;\n private persistedRevision = 0;\n private savingRevision: number | null = null;\n private savingContent: string | null = null;\n private generation = 0;\n private externalSequence = -1;\n private lifecycle: DocumentSessionLifecycle = 'open';\n private frozen = false;\n private error: Error | null = null;\n private conflict: DocumentSessionConflict | null = null;\n\n private autoSaveTimer: ReturnType<typeof setTimeout> | null = null;\n private autoSaveRetryAttempt = 0;\n private drainPromise: Promise<void> | null = null;\n private haltDrain = false;\n private operationTail: Promise<void> = Promise.resolve();\n private snapshot: DocumentSessionSnapshot;\n\n public constructor(options: DocumentSessionOptions = {}) {\n this.autoSaveDelayMs = Math.max(0, options.autoSaveDelayMs ?? 500);\n this.autoSaveEnabled = options.autoSaveEnabled ?? true;\n this.autoSaveRetryDelaysMs = normalizeRetryDelays(\n options.autoSaveRetryDelaysMs ?? DEFAULT_AUTO_SAVE_RETRY_DELAYS_MS,\n );\n this.recoveryJournal = options.recoveryJournal ?? null;\n this.snapshot = this.createSnapshot();\n }\n\n public readonly subscribe = (listener: SessionListener): (() => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n public readonly getSnapshot = (): DocumentSessionSnapshot => this.snapshot;\n\n /** Enable or disable automatic persistence without weakening explicit or lifecycle flushes. */\n public setAutoSaveEnabled(enabled: boolean): void {\n if (this.autoSaveEnabled === enabled) return;\n this.autoSaveEnabled = enabled;\n if (!enabled) {\n this.clearAutoSaveTimer();\n return;\n }\n if (\n this.target &&\n this.lifecycle === 'open' &&\n !this.frozen &&\n !this.conflict &&\n this.persistedRevision < this.revision\n ) {\n this.scheduleAutoSave();\n }\n }\n\n /**\n * Record a complete local snapshot and schedule its persistence.\n * Returns the assigned monotonic revision.\n */\n public edit(content: string, scope: DocumentSessionEditScope): number {\n if (!this.target || this.lifecycle !== 'open' || this.frozen) {\n throw new Error('Cannot edit a document session that is not open.');\n }\n if (scope.targetKey !== this.target.key || scope.generation !== this.generation) {\n throw new Error('Cannot apply an edit from an obsolete document instance.');\n }\n if (content === this.content) return this.revision;\n\n this.content = content;\n this.revision += 1;\n this.error = null;\n this.autoSaveRetryAttempt = 0;\n if (this.conflict) {\n this.conflict = {\n ...this.conflict,\n localContent: content,\n localRevision: this.revision,\n };\n }\n this.writeRecoverySnapshot();\n if (!this.conflict) this.scheduleAutoSave();\n this.emit();\n return this.revision;\n }\n\n /**\n * Persist the revision current when flush() is called. A later edit may\n * advance the session again, but commits remain strictly serialized.\n */\n public async flush(reason: DocumentSaveReason = 'manual'): Promise<DocumentSessionSnapshot> {\n this.clearAutoSaveTimer();\n if (!this.target) return this.snapshot;\n if (this.conflict) throw new DocumentSessionConflictError(this.conflict);\n\n const requiredRevision = this.revision;\n while (this.target && this.persistedRevision < requiredRevision) {\n if (this.conflict) throw new DocumentSessionConflictError(this.conflict);\n if (this.haltDrain) {\n throw new Error('Document persistence was cancelled before the requested revision saved.');\n }\n try {\n await this.ensureDrain(reason);\n } catch (error: unknown) {\n if (this.conflict) throw new DocumentSessionConflictError(this.conflict);\n throw error;\n }\n }\n\n if (this.conflict) throw new DocumentSessionConflictError(this.conflict);\n if (this.persistedRevision < requiredRevision) {\n throw new Error('The document target closed before the requested revision saved.');\n }\n return this.snapshot;\n }\n\n /**\n * Flush the old document, then replace it with a new clean baseline.\n * UI state should change only after this promise resolves.\n */\n public transitionTo(\n target: DocumentCommitTarget | null,\n content: string,\n ): Promise<DocumentSessionSnapshot> {\n return this.enqueueOperation(() =>\n this.performTransition(async () => ({ target, content })).then((snapshot) => {\n if (!snapshot) throw new Error('A fixed document transition cannot be cancelled.');\n return snapshot;\n }),\n );\n }\n\n /**\n * Freeze and flush the current document before reading the next one. The\n * loader may return null when a navigation request was superseded, leaving\n * the current target active and clean.\n */\n public transitionWithLoad(\n load: () => Promise<DocumentSessionTransition | null>,\n ): Promise<DocumentSessionSnapshot | null> {\n return this.enqueueOperation(() => this.performTransition(load));\n }\n\n /**\n * Flush the current path, run the storage move, and only then swap the\n * commit target. No edit can enter between those steps.\n */\n public retarget(\n nextTarget: DocumentCommitTarget,\n moveStorage: () => Promise<void>,\n ): Promise<DocumentSessionSnapshot> {\n return this.enqueueOperation(async () => {\n this.assertNotPreparingClose();\n if (!this.target) throw new Error('Cannot retarget a session without a document.');\n\n this.lifecycle = 'transitioning';\n this.frozen = true;\n this.emit();\n try {\n this.haltDrain = false;\n await this.flush('transition');\n await moveStorage();\n this.generation += 1;\n this.externalSequence = -1;\n this.target = nextTarget;\n this.error = null;\n this.conflict = null;\n this.lifecycle = 'open';\n this.frozen = false;\n this.emit();\n return this.snapshot;\n } catch (error: unknown) {\n if (isFileSystemMoveStateError(error)) {\n if (error.documentLocation === 'destination') {\n this.generation += 1;\n this.externalSequence = -1;\n this.target = nextTarget;\n }\n this.haltDrain = true;\n this.error = error;\n this.lifecycle = 'open';\n // The provider could not establish one authoritative compound move.\n // Keep editing frozen until navigation/recovery chooses a real path.\n this.frozen = true;\n this.emit();\n throw error;\n }\n this.lifecycle = 'open';\n this.frozen = false;\n this.emit();\n throw error;\n }\n });\n }\n\n /**\n * Cancel queued writes, wait for an already-running write, then delete.\n * The deletion is therefore the final storage operation for this target.\n */\n public delete(deleteStorage: () => Promise<void>): Promise<DocumentSessionSnapshot> {\n return this.enqueueOperation(async () => {\n this.assertNotPreparingClose();\n this.lifecycle = 'transitioning';\n this.frozen = true;\n this.haltDrain = true;\n this.clearAutoSaveTimer();\n this.emit();\n\n await this.awaitActiveDrain();\n try {\n await deleteStorage();\n } catch (error: unknown) {\n this.haltDrain = false;\n this.lifecycle = 'open';\n this.frozen = false;\n if (this.target && this.persistedRevision < this.revision && !this.conflict) {\n this.scheduleAutoSave();\n }\n this.emit();\n throw error;\n }\n\n this.discardRecoverySnapshot();\n this.detach();\n return this.snapshot;\n });\n }\n\n /**\n * Discard queued changes and detach without mutating storage. This is used\n * for explicitly abandoned sessions, never as an implicit effect cleanup.\n */\n public cancel(): Promise<DocumentSessionSnapshot> {\n return this.enqueueOperation(async () => {\n this.lifecycle = 'transitioning';\n this.frozen = true;\n this.haltDrain = true;\n this.clearAutoSaveTimer();\n this.emit();\n await this.awaitActiveDrain();\n this.discardRecoverySnapshot();\n this.detach();\n return this.snapshot;\n });\n }\n\n /**\n * Observe a filesystem/host change captured for targetKey. Clean sessions\n * adopt it. Dirty or saving sessions preserve local text and enter conflict.\n */\n public observeExternal(change: DocumentExternalSnapshot): DocumentExternalChangeResult {\n if (!this.target || change.targetKey !== this.target.key || this.lifecycle === 'closed') {\n return 'ignored';\n }\n if (change.sequence !== undefined) {\n if (!Number.isSafeInteger(change.sequence) || change.sequence <= this.externalSequence) {\n return 'ignored';\n }\n this.externalSequence = change.sequence;\n }\n\n // While conflicted, a current observation equal to the latest local\n // snapshot proves that revision durable without requiring another write.\n // Never acknowledge while a commit is still running; its eventual result\n // remains authoritative for the serialized drain.\n if (\n this.conflict &&\n change.content === this.content &&\n this.savingRevision === null &&\n this.lifecycle === 'open'\n ) {\n this.clearAutoSaveTimer();\n this.autoSaveRetryAttempt = 0;\n this.persistedRevision = this.revision;\n this.persistedContent = this.content;\n this.haltDrain = false;\n this.error = null;\n this.conflict = null;\n this.discardRecoverySnapshot();\n this.emit();\n return 'applied';\n }\n\n // Once a conflict exists, every later accepted observation refreshes its\n // external branch before ordinary duplicate suppression. In particular,\n // A -> local C -> external B -> external A must offer A, not stale B, when\n // the user chooses Reload external.\n if (this.conflict) {\n const externalVersion = change.version ?? null;\n if (\n this.conflict.localContent === this.content &&\n this.conflict.localRevision === this.revision &&\n this.conflict.externalContent === change.content &&\n this.conflict.externalVersion === externalVersion\n ) {\n return 'ignored';\n }\n\n this.clearAutoSaveTimer();\n this.haltDrain = true;\n this.conflict = {\n targetKey: change.targetKey,\n localContent: this.content,\n localRevision: this.revision,\n externalContent: change.content,\n externalVersion,\n };\n this.error = null;\n this.writeRecoverySnapshot();\n this.emit();\n return 'conflict';\n }\n\n if (\n change.content === this.content ||\n change.content === this.persistedContent ||\n (this.savingRevision !== null && change.content === this.savingContent)\n ) {\n return 'ignored';\n }\n\n const isClean =\n this.persistedRevision === this.revision &&\n this.savingRevision === null &&\n this.error === null &&\n this.conflict === null;\n\n if (isClean && change.content !== null) {\n this.content = change.content;\n this.revision += 1;\n this.persistedRevision = this.revision;\n this.persistedContent = change.content;\n this.generation += 1;\n this.discardRecoverySnapshot();\n this.emit();\n return 'applied';\n }\n\n this.clearAutoSaveTimer();\n this.haltDrain = true;\n this.conflict = {\n targetKey: change.targetKey,\n localContent: this.content,\n localRevision: this.revision,\n externalContent: change.content,\n externalVersion: change.version ?? null,\n };\n this.error = null;\n this.writeRecoverySnapshot();\n this.emit();\n return 'conflict';\n }\n\n public resolveConflict(strategy: DocumentConflictStrategy): Promise<DocumentSessionSnapshot> {\n return this.enqueueOperation(async () => {\n if (!this.conflict) return this.snapshot;\n\n this.clearAutoSaveTimer();\n this.haltDrain = true;\n await this.awaitActiveDrain();\n // A watcher may refresh or converge the conflict while an in-flight\n // commit settles. Resolve the branch that is current now, never the\n // snapshot captured before the await.\n const currentConflict = this.conflict;\n if (!currentConflict) return this.snapshot;\n\n if (strategy === 'use-external') {\n this.discardRecoverySnapshot();\n this.generation += 1;\n this.revision += 1;\n this.conflict = null;\n this.error = null;\n this.haltDrain = false;\n\n if (currentConflict.externalContent === null) {\n this.target = null;\n this.content = '';\n this.persistedContent = null;\n this.persistedRevision = this.revision;\n this.lifecycle = 'closed';\n this.frozen = true;\n } else {\n this.content = currentConflict.externalContent;\n this.persistedContent = currentConflict.externalContent;\n this.persistedRevision = this.revision;\n this.lifecycle = 'open';\n this.frozen = false;\n }\n this.emit();\n return this.snapshot;\n }\n\n // Keep the local branch, but make the observed external snapshot the\n // optimistic baseline for the next conditional commit.\n this.persistedContent = currentConflict.externalContent;\n // A clean external deletion/conflict has no unsaved local revision.\n // Keeping local still requires a fresh commit that recreates/overwrites\n // the target, so make that intent explicit with a new revision.\n this.revision += 1;\n this.conflict = null;\n this.error = null;\n this.haltDrain = false;\n this.lifecycle = 'open';\n this.frozen = false;\n this.writeRecoverySnapshot();\n this.emit();\n return this.flush('conflict');\n });\n }\n\n /**\n * Freeze edits and flush through the latest revision. A successful call\n * remains frozen until cancelClose(); the owning host may now destroy it.\n */\n public prepareClose(): Promise<DocumentSessionSnapshot> {\n return this.enqueueOperation(async () => {\n if (this.lifecycle === 'closing') return this.snapshot;\n this.lifecycle = 'closing';\n this.frozen = true;\n this.haltDrain = false;\n this.emit();\n try {\n await this.flush('close');\n return this.snapshot;\n } catch (error: unknown) {\n this.lifecycle = 'open';\n this.frozen = false;\n this.emit();\n throw error;\n }\n });\n }\n\n /** Resume editing when a host cancels a prepared close. */\n public cancelClose(): void {\n if (this.lifecycle !== 'closing') return;\n this.lifecycle = 'open';\n this.frozen = false;\n this.haltDrain = false;\n if (this.target && this.persistedRevision < this.revision && !this.conflict) {\n this.scheduleAutoSave();\n }\n this.emit();\n }\n\n private async performTransition(\n load: () => Promise<DocumentSessionTransition | null>,\n ): Promise<DocumentSessionSnapshot | null> {\n this.assertNotPreparingClose();\n this.lifecycle = 'transitioning';\n this.frozen = true;\n this.emit();\n\n try {\n this.haltDrain = false;\n await this.flush('transition');\n this.clearAutoSaveTimer();\n const next = await load();\n if (!next) {\n this.lifecycle = 'open';\n this.frozen = false;\n this.emit();\n return null;\n }\n\n this.generation += 1;\n this.externalSequence = -1;\n this.target = next.target;\n this.content = next.content;\n this.revision += 1;\n this.persistedRevision = this.revision;\n this.persistedContent = next.target ? next.content : null;\n this.savingRevision = null;\n this.savingContent = null;\n this.error = null;\n this.conflict = null;\n this.haltDrain = false;\n this.lifecycle = 'open';\n this.frozen = false;\n const recoveryState = this.restoreRecoverySnapshot();\n if (recoveryState === 'dirty') this.scheduleAutoSave();\n this.emit();\n return this.snapshot;\n } catch (error: unknown) {\n this.lifecycle = 'open';\n this.frozen = false;\n this.emit();\n throw error;\n }\n }\n\n private scheduleAutoSave(delayMs = this.autoSaveDelayMs): void {\n this.clearAutoSaveTimer();\n if (!this.autoSaveEnabled) return;\n this.autoSaveTimer = setTimeout(() => {\n this.autoSaveTimer = null;\n const target = this.target;\n const generation = this.generation;\n void this.flush('autosave').then(\n () => {\n this.autoSaveRetryAttempt = 0;\n },\n () => {\n // Preserve honest error/conflict state while making ordinary I/O\n // failures recoverable. A newer edit already owns a fresh debounce\n // timer, and lifecycle operations cancel this retry path.\n if (\n this.autoSaveTimer !== null ||\n !target ||\n target !== this.target ||\n generation !== this.generation ||\n this.lifecycle !== 'open' ||\n this.frozen ||\n this.haltDrain ||\n this.conflict ||\n this.persistedRevision >= this.revision\n ) {\n return;\n }\n const retryDelay = this.autoSaveRetryDelaysMs[this.autoSaveRetryAttempt];\n if (retryDelay === undefined) return;\n this.autoSaveRetryAttempt += 1;\n this.scheduleAutoSave(retryDelay);\n },\n );\n }, delayMs);\n }\n\n private clearAutoSaveTimer(): void {\n if (this.autoSaveTimer === null) return;\n clearTimeout(this.autoSaveTimer);\n this.autoSaveTimer = null;\n }\n\n private ensureDrain(reason: DocumentSaveReason): Promise<void> {\n if (this.drainPromise) return this.drainPromise;\n const running = this.drain(reason);\n this.drainPromise = running;\n const clear = () => {\n if (this.drainPromise === running) this.drainPromise = null;\n };\n void running.then(clear, clear);\n return running;\n }\n\n private async drain(reason: DocumentSaveReason): Promise<void> {\n while (\n this.target &&\n !this.haltDrain &&\n !this.conflict &&\n this.persistedRevision < this.revision\n ) {\n const target = this.target;\n const generation = this.generation;\n const saveRevision = this.revision;\n const saveContent = this.content;\n const previousPersistedRevision = this.persistedRevision;\n const previousPersistedContent = this.persistedContent;\n\n this.savingRevision = saveRevision;\n this.savingContent = saveContent;\n this.error = null;\n this.emit();\n\n try {\n await target.commit({\n targetKey: target.key,\n content: saveContent,\n revision: saveRevision,\n persistedRevision: previousPersistedRevision,\n persistedContent: previousPersistedContent,\n reason,\n });\n } catch (error: unknown) {\n this.savingRevision = null;\n this.savingContent = null;\n if (error instanceof DocumentCommitConflictError) {\n this.haltDrain = true;\n this.conflict = {\n targetKey: target.key,\n localContent: this.content,\n localRevision: this.revision,\n externalContent: error.externalContent,\n externalVersion: error.externalVersion,\n };\n this.error = null;\n } else {\n this.error = toError(error);\n }\n this.emit();\n throw error;\n }\n\n if (generation !== this.generation || target !== this.target) return;\n this.persistedRevision = Math.max(this.persistedRevision, saveRevision);\n this.persistedContent = saveContent;\n this.autoSaveRetryAttempt = 0;\n this.savingRevision = null;\n this.savingContent = null;\n this.error = null;\n const activeConflict = this.getSnapshot().conflict;\n if (activeConflict?.externalContent === saveContent && this.content === saveContent) {\n // An observation that matched the in-flight local snapshot could not\n // be acknowledged until the commit settled. Its successful return now\n // proves both branches converged on the same durable revision.\n this.conflict = null;\n this.haltDrain = false;\n }\n this.recoveryJournal?.acknowledge({\n targetKey: target.key,\n generation,\n persistedRevision: this.persistedRevision,\n });\n this.emit();\n }\n }\n\n private async awaitActiveDrain(): Promise<void> {\n const active = this.drainPromise;\n if (!active) return;\n try {\n await active;\n } catch {\n // A destructive cancellation still waits for the operation to settle,\n // but a failed obsolete write must not prevent the requested deletion.\n }\n }\n\n /**\n * Rehydrate a synchronous crash snapshot after the durable baseline has\n * been read. A matching baseline is safe to resume as dirty; a changed\n * baseline becomes an explicit conflict and is never auto-overwritten.\n */\n private restoreRecoverySnapshot(): 'none' | 'dirty' | 'conflict' {\n if (!this.target || !this.recoveryJournal) return 'none';\n const recovered = this.recoveryJournal.lookup(this.target.key);\n if (!recovered) return 'none';\n\n if (recovered.content === this.content) {\n this.recoveryJournal.acknowledge({\n targetKey: recovered.targetKey,\n generation: recovered.generation,\n persistedRevision: recovered.revision,\n });\n return 'none';\n }\n\n const externalContent = this.content;\n this.generation = Math.max(this.generation, recovered.generation) + 1;\n this.revision = Math.max(this.revision, recovered.revision) + 1;\n this.content = recovered.content;\n\n if (recovered.persistedContent === externalContent) {\n this.writeRecoverySnapshot();\n return 'dirty';\n }\n\n // Preserve the journal's prior baseline so a second crash continues to\n // reopen as a conflict until the user explicitly chooses a branch.\n this.persistedContent = recovered.persistedContent;\n this.haltDrain = true;\n this.conflict = {\n targetKey: this.target.key,\n localContent: recovered.content,\n localRevision: this.revision,\n externalContent,\n externalVersion: null,\n };\n this.writeRecoverySnapshot();\n return 'conflict';\n }\n\n private writeRecoverySnapshot(): void {\n if (!this.target || !this.recoveryJournal) return;\n if (this.persistedRevision >= this.revision && !this.conflict) return;\n this.recoveryJournal.write({\n targetKey: this.target.key,\n generation: this.generation,\n revision: this.revision,\n content: this.content,\n persistedContent: this.persistedContent,\n });\n }\n\n private discardRecoverySnapshot(): void {\n if (!this.target) return;\n this.recoveryJournal?.discard(this.target.key);\n }\n\n private detach(): void {\n this.clearAutoSaveTimer();\n this.autoSaveRetryAttempt = 0;\n this.generation += 1;\n this.externalSequence = -1;\n this.target = null;\n this.persistedContent = null;\n this.savingRevision = null;\n this.savingContent = null;\n this.error = null;\n this.conflict = null;\n this.haltDrain = false;\n this.lifecycle = 'closed';\n this.frozen = true;\n this.emit();\n }\n\n private assertNotPreparingClose(): void {\n if (this.lifecycle === 'closing') {\n throw new Error('The document is already prepared for close.');\n }\n }\n\n private enqueueOperation<T>(operation: () => Promise<T>): Promise<T> {\n const run = this.operationTail.then(operation, operation);\n this.operationTail = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n\n private createSnapshot(): DocumentSessionSnapshot {\n return Object.freeze({\n targetKey: this.target?.key ?? null,\n content: this.content,\n revision: this.revision,\n persistedRevision: this.persistedRevision,\n persistedContent: this.persistedContent,\n savingRevision: this.savingRevision,\n generation: this.generation,\n status: this.computeStatus(),\n lifecycle: this.lifecycle,\n frozen: this.frozen,\n error: this.error,\n conflict: this.conflict,\n });\n }\n\n private computeStatus(): DocumentSessionStatus {\n if (this.lifecycle === 'closed') return 'closed';\n if (!this.target) return 'idle';\n if (this.conflict) return 'conflict';\n if (this.error) return 'error';\n if (this.savingRevision !== null) return 'saving';\n return this.persistedRevision < this.revision ? 'dirty' : 'saved';\n }\n\n private emit(): void {\n this.snapshot = this.createSnapshot();\n for (const listener of [...this.listeners]) listener();\n }\n}\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction normalizeRetryDelays(delays: readonly number[]): readonly number[] {\n return delays.map((delay) => {\n if (!Number.isFinite(delay) || delay < 0) {\n throw new TypeError('Autosave retry delays must be finite, non-negative numbers.');\n }\n return Math.floor(delay);\n });\n}\n","import { getFileSystemProviderV2, type FileSystemProvider } from '../filesystem/types.js';\nimport { FsError } from '../filesystem/fs-error.js';\nimport { parseWorkspacePath } from '../filesystem/workspace-path.js';\nimport { decodeUtf8Text } from '../filesystem/utf8.js';\nimport { DocumentCommitConflictError } from './document-session.js';\nimport type { DocumentCommitTarget, DocumentExternalVersion } from './types.js';\n\nexport interface FileSystemDocumentTargetOptions {\n /**\n * Compare the current file with the session's persisted baseline before\n * replacing it. Enabled by default.\n */\n verifyExternalChanges?: boolean;\n onCommitted?: (content: string, revision: number) => void;\n}\n\n/**\n * Adapt a FileSystemProvider path to the commit protocol used by\n * DocumentSession. The optimistic read catches external edits/deletions\n * before a local write recreates or silently replaces them.\n */\nexport function createFileSystemDocumentTarget(\n provider: FileSystemProvider,\n path: string,\n options: FileSystemDocumentTargetOptions = {},\n): DocumentCommitTarget {\n const verifyExternalChanges = options.verifyExternalChanges ?? true;\n const canonicalPath = parseWorkspacePath(path);\n const providerV2 = getFileSystemProviderV2(provider);\n return {\n key: provider.id + ':' + canonicalPath,\n async commit(request) {\n if (providerV2 && providerV2.capabilities.conditionalWrite !== 'none') {\n const current = await providerV2.readFile(canonicalPath);\n const currentContent = current\n ? decodeUtf8Text(current.data, {\n label: 'The external document',\n path: canonicalPath,\n })\n : null;\n if (currentContent !== request.persistedContent && currentContent !== request.content) {\n throw new DocumentCommitConflictError(\n 'The document changed outside DocBlocks: ' + path,\n currentContent,\n current?.entry.version ?? null,\n );\n }\n if (currentContent === request.content) {\n options.onCommitted?.(request.content, request.revision);\n return { version: current?.entry.version ?? null };\n }\n\n try {\n const committed = await providerV2.writeFile(\n canonicalPath,\n new TextEncoder().encode(request.content),\n {\n mode: current ? 'replace' : 'create',\n createParents: true,\n expectedVersion: current?.entry.version ?? null,\n },\n );\n options.onCommitted?.(request.content, request.revision);\n return { version: committed.version };\n } catch (error: unknown) {\n if (\n error instanceof FsError &&\n (error.code === 'conflict' ||\n error.code === 'already-exists' ||\n error.code === 'not-found')\n ) {\n const external = await providerV2.readFile(canonicalPath);\n throw new DocumentCommitConflictError(\n 'The document changed outside DocBlocks: ' + path,\n external\n ? decodeUtf8Text(external.data, {\n label: 'The external document',\n path: canonicalPath,\n })\n : null,\n external?.entry.version ?? null,\n );\n }\n throw error;\n }\n }\n\n if (provider.commitFile) {\n const result = await provider.commitFile(path, request.content, request.persistedContent);\n if (result.status === 'conflict') {\n throw new DocumentCommitConflictError(\n 'The document changed outside DocBlocks: ' + path,\n result.content,\n result.version,\n );\n }\n options.onCommitted?.(request.content, request.revision);\n return { version: result.version };\n }\n\n if (verifyExternalChanges) {\n const current = await provider.readFile(path);\n if (current !== request.persistedContent && current !== request.content) {\n const meta = await provider.stat(path).catch(() => null);\n const version: DocumentExternalVersion = meta?.lastModified ?? null;\n throw new DocumentCommitConflictError(\n 'The document changed outside DocBlocks: ' + path,\n current,\n version,\n );\n }\n }\n\n await provider.writeFile(path, request.content);\n options.onCommitted?.(request.content, request.revision);\n const meta = await provider.stat(path).catch(() => null);\n return { version: meta?.lastModified ?? null };\n },\n };\n}\n","/**\n * Synchronous crash-recovery journal for dirty document snapshots.\n *\n * The journal deliberately depends on a tiny injected Storage-like contract\n * instead of reading `window.localStorage` at module load time. This keeps the\n * document package usable in Node, Electron main, VS Code extension hosts, and\n * tests while allowing browser renderers to use localStorage for a best-effort\n * write that completes before the current JavaScript turn ends.\n */\n\nexport const DOCUMENT_RECOVERY_JOURNAL_STORAGE_KEY = 'docblocks:document-recovery:v1';\nexport const DOCUMENT_RECOVERY_JOURNAL_SCHEMA_VERSION = 1;\n\nexport interface DocumentRecoveryStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nexport interface DocumentRecoveryRecord {\n /** Stable commit-target key, including the workspace/provider scope. */\n targetKey: string;\n /** DocumentSession generation that produced this snapshot. */\n generation: number;\n /** Monotonic DocumentSession revision represented by `content`. */\n revision: number;\n /** Complete markdown snapshot. */\n content: string;\n /**\n * Content last acknowledged by the commit target when this draft was made.\n * Recovery may be applied automatically only when storage still matches this\n * baseline; otherwise the caller must surface an external-change conflict.\n */\n persistedContent: string | null;\n /** Time the dirty record for this target was first created. */\n createdAt: number;\n /** Time the record was most recently replaced. */\n updatedAt: number;\n}\n\nexport interface DocumentRecoveryWrite {\n targetKey: string;\n generation: number;\n revision: number;\n content: string;\n persistedContent: string | null;\n}\n\nexport interface DocumentRecoveryAcknowledgement {\n targetKey: string;\n generation: number;\n /** Highest revision durably acknowledged by the commit target. */\n persistedRevision: number;\n}\n\nexport type DocumentRecoveryWriteFailure =\n | 'invalid-record'\n | 'record-too-large'\n | 'storage-unavailable'\n | 'quota-exceeded';\n\nexport type DocumentRecoveryWriteResult =\n | { status: 'stored'; evicted: number }\n | { status: 'unchanged'; evicted: 0 }\n | { status: 'ignored-stale'; evicted: 0 }\n | { status: 'rejected'; reason: DocumentRecoveryWriteFailure; evicted: number };\n\nexport interface DocumentRecoveryJournalOptions {\n storageKey?: string;\n /** Maximum number of document records retained. Defaults to 20. */\n maxEntries?: number;\n /** Conservative UTF-16 estimate for the whole serialized journal. */\n maxStorageBytes?: number;\n /** Conservative UTF-16 estimate for one serialized record. */\n maxRecordBytes?: number;\n /** Records older than this are discarded while reading. Defaults to 7 days. */\n maxAgeMs?: number;\n /** Tolerated clock skew for records dated in the future. Defaults to 5 minutes. */\n maxFutureSkewMs?: number;\n /** Injected for deterministic tests. */\n now?: () => number;\n}\n\ninterface DocumentRecoveryEnvelope {\n schemaVersion: typeof DOCUMENT_RECOVERY_JOURNAL_SCHEMA_VERSION;\n records: DocumentRecoveryRecord[];\n}\n\ninterface LoadedRecords {\n records: DocumentRecoveryRecord[];\n needsRepair: boolean;\n}\n\ninterface PersistResult {\n stored: boolean;\n evicted: number;\n reason?: 'storage-unavailable' | 'quota-exceeded';\n}\n\nconst DEFAULT_MAX_ENTRIES = 20;\nconst DEFAULT_MAX_STORAGE_BYTES = 2_000_000;\nconst DEFAULT_MAX_RECORD_BYTES = 1_000_000;\nconst DEFAULT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;\nconst DEFAULT_MAX_FUTURE_SKEW_MS = 5 * 60 * 1000;\nconst MAX_TARGET_KEY_LENGTH = 4096;\n\n/**\n * Best-effort, synchronous recovery storage.\n *\n * Normal save acknowledgement is the only automatic path that removes the\n * current record. Explicit discard, age expiry, corruption repair, and bounded\n * eviction are intentional exceptions: callers can discard after the user\n * rejects recovery, and the journal must never grow without limit.\n */\nexport class DocumentRecoveryJournal {\n private readonly storage: DocumentRecoveryStorage | null;\n private readonly storageKey: string;\n private readonly maxEntries: number;\n private readonly maxStorageBytes: number;\n private readonly maxRecordBytes: number;\n private readonly maxAgeMs: number;\n private readonly maxFutureSkewMs: number;\n private readonly now: () => number;\n\n public constructor(\n storage: DocumentRecoveryStorage | null,\n options: DocumentRecoveryJournalOptions = {},\n ) {\n this.storage = storage;\n this.storageKey = options.storageKey?.trim() || DOCUMENT_RECOVERY_JOURNAL_STORAGE_KEY;\n this.maxEntries = positiveInteger(options.maxEntries, DEFAULT_MAX_ENTRIES);\n this.maxStorageBytes = positiveInteger(options.maxStorageBytes, DEFAULT_MAX_STORAGE_BYTES);\n this.maxRecordBytes = Math.min(\n positiveInteger(options.maxRecordBytes, DEFAULT_MAX_RECORD_BYTES),\n this.maxStorageBytes,\n );\n this.maxAgeMs = nonNegativeFinite(options.maxAgeMs, DEFAULT_MAX_AGE_MS);\n this.maxFutureSkewMs = nonNegativeFinite(options.maxFutureSkewMs, DEFAULT_MAX_FUTURE_SKEW_MS);\n this.now = options.now ?? Date.now;\n }\n\n /**\n * Synchronously persist the latest dirty snapshot for a target.\n * Storage/quota failures are returned, never thrown into an editor onChange.\n */\n public write(input: DocumentRecoveryWrite): DocumentRecoveryWriteResult {\n const timestamp = this.now();\n if (!isValidWrite(input) || !Number.isFinite(timestamp) || timestamp < 0) {\n return { status: 'rejected', reason: 'invalid-record', evicted: 0 };\n }\n if (!this.storage) {\n return { status: 'rejected', reason: 'storage-unavailable', evicted: 0 };\n }\n if (\n estimatedBytes(input.content) > this.maxRecordBytes ||\n (input.persistedContent !== null &&\n estimatedBytes(input.persistedContent) > this.maxRecordBytes)\n ) {\n return { status: 'rejected', reason: 'record-too-large', evicted: 0 };\n }\n\n const loaded = this.load(timestamp);\n const existing = loaded.records.find((record) => record.targetKey === input.targetKey);\n if (existing) {\n const ordering = compareGenerationRevision(input, existing);\n if (ordering < 0) return { status: 'ignored-stale', evicted: 0 };\n if (ordering === 0) {\n if (\n existing.content === input.content &&\n existing.persistedContent === input.persistedContent\n ) {\n return { status: 'unchanged', evicted: 0 };\n }\n // A revision is an immutable content snapshot. Reusing it for different\n // content would make acknowledgement and recovery ambiguous.\n return { status: 'rejected', reason: 'invalid-record', evicted: 0 };\n }\n }\n\n const candidate: DocumentRecoveryRecord = {\n targetKey: input.targetKey,\n generation: input.generation,\n revision: input.revision,\n content: input.content,\n persistedContent: input.persistedContent,\n createdAt: existing?.createdAt ?? timestamp,\n updatedAt: timestamp,\n };\n if (estimatedBytes(JSON.stringify(candidate)) > this.maxRecordBytes) {\n return { status: 'rejected', reason: 'record-too-large', evicted: 0 };\n }\n if (estimatedBytes(serializeEnvelope([candidate])) > this.maxStorageBytes) {\n return { status: 'rejected', reason: 'record-too-large', evicted: 0 };\n }\n\n const next = loaded.records.filter((record) => record.targetKey !== input.targetKey);\n next.push(candidate);\n const persisted = this.persistBounded(next, input.targetKey);\n if (!persisted.stored) {\n return {\n status: 'rejected',\n reason: persisted.reason ?? 'quota-exceeded',\n evicted: persisted.evicted,\n };\n }\n return { status: 'stored', evicted: persisted.evicted };\n }\n\n /** Return the recoverable snapshot for one target, if present and valid. */\n public lookup(targetKey: string, generation?: number): DocumentRecoveryRecord | null {\n if (!isValidTargetKey(targetKey) || !isOptionalGeneration(generation)) return null;\n const record = this.load(this.now()).records.find(\n (candidate) =>\n candidate.targetKey === targetKey &&\n (generation === undefined || candidate.generation === generation),\n );\n return record ? cloneRecord(record) : null;\n }\n\n /** List valid records, newest first. Optionally restrict to one target. */\n public list(targetKey?: string): DocumentRecoveryRecord[] {\n if (targetKey !== undefined && !isValidTargetKey(targetKey)) return [];\n return this.load(this.now())\n .records.filter((record) => targetKey === undefined || record.targetKey === targetKey)\n .sort(compareNewestFirst)\n .map(cloneRecord);\n }\n\n /**\n * Clear a record only when the same generation has durably acknowledged at\n * least the journaled revision. A stale acknowledgement cannot erase a newer\n * crash-recovery snapshot.\n */\n public acknowledge(input: DocumentRecoveryAcknowledgement): boolean {\n if (!isValidAcknowledgement(input) || !this.storage) return false;\n const records = this.load(this.now()).records;\n const record = records.find((candidate) => candidate.targetKey === input.targetKey);\n if (\n !record ||\n record.generation !== input.generation ||\n record.revision > input.persistedRevision\n ) {\n return false;\n }\n\n return this.persistExact(records.filter((candidate) => candidate !== record));\n }\n\n /** Explicitly discard recovery data after a user or lifecycle decision. */\n public discard(targetKey: string, generation?: number): boolean {\n if (!isValidTargetKey(targetKey) || !isOptionalGeneration(generation) || !this.storage) {\n return false;\n }\n const records = this.load(this.now()).records;\n const next = records.filter(\n (record) =>\n record.targetKey !== targetKey ||\n (generation !== undefined && record.generation !== generation),\n );\n if (next.length === records.length) return false;\n return this.persistExact(next);\n }\n\n private load(timestamp: number): LoadedRecords {\n if (!this.storage) return { records: [], needsRepair: false };\n\n let raw: string | null;\n try {\n raw = this.storage.getItem(this.storageKey);\n } catch {\n return { records: [], needsRepair: false };\n }\n if (raw === null) return { records: [], needsRepair: false };\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw) as unknown;\n } catch {\n this.tryRemove();\n return { records: [], needsRepair: true };\n }\n if (!isEnvelope(parsed)) {\n this.tryRemove();\n return { records: [], needsRepair: true };\n }\n\n const byTarget = new Map<string, DocumentRecoveryRecord>();\n let needsRepair = estimatedBytes(raw) > this.maxStorageBytes;\n for (const candidate of parsed.records) {\n if (!isValidStoredRecord(candidate, timestamp, this.maxAgeMs, this.maxFutureSkewMs)) {\n needsRepair = true;\n continue;\n }\n if (estimatedBytes(JSON.stringify(candidate)) > this.maxRecordBytes) {\n needsRepair = true;\n continue;\n }\n const current = byTarget.get(candidate.targetKey);\n if (!current || compareStoredRecords(candidate, current) > 0) {\n if (current) needsRepair = true;\n byTarget.set(candidate.targetKey, cloneRecord(candidate));\n } else {\n needsRepair = true;\n }\n }\n\n const records = [...byTarget.values()].sort(compareNewestFirst);\n while (records.length > this.maxEntries) {\n records.pop();\n needsRepair = true;\n }\n while (\n records.length > 0 &&\n estimatedBytes(serializeEnvelope(records)) > this.maxStorageBytes\n ) {\n records.pop();\n needsRepair = true;\n }\n if (needsRepair) {\n // Best effort only. Read APIs must still return the validated in-memory\n // records even when storage is temporarily unavailable.\n this.persistBounded(records);\n }\n return { records, needsRepair };\n }\n\n private persistBounded(\n records: DocumentRecoveryRecord[],\n protectedTarget?: string,\n ): PersistResult {\n if (!this.storage) {\n return { stored: false, evicted: 0, reason: 'storage-unavailable' };\n }\n\n const next = [...records].sort(compareNewestFirst);\n let evicted = 0;\n while (next.length > this.maxEntries) {\n if (!evictOldest(next, protectedTarget)) break;\n evicted += 1;\n }\n\n let serialized = serializeEnvelope(next);\n while (estimatedBytes(serialized) > this.maxStorageBytes) {\n if (!evictOldest(next, protectedTarget)) {\n return { stored: false, evicted, reason: 'quota-exceeded' };\n }\n evicted += 1;\n serialized = serializeEnvelope(next);\n }\n\n for (;;) {\n try {\n this.storage.setItem(this.storageKey, serialized);\n return { stored: true, evicted };\n } catch {\n if (!evictOldest(next, protectedTarget)) {\n return { stored: false, evicted, reason: 'quota-exceeded' };\n }\n evicted += 1;\n serialized = serializeEnvelope(next);\n }\n }\n }\n\n private persistExact(records: DocumentRecoveryRecord[]): boolean {\n if (!this.storage) return false;\n if (records.length === 0) {\n try {\n this.storage.removeItem(this.storageKey);\n return true;\n } catch {\n return false;\n }\n }\n try {\n this.storage.setItem(this.storageKey, serializeEnvelope(records));\n return true;\n } catch {\n return false;\n }\n }\n\n private tryRemove(): void {\n try {\n this.storage?.removeItem(this.storageKey);\n } catch {\n // Recovery is best effort; a denied cleanup must not fail application startup.\n }\n }\n}\n\n/**\n * Resolve browser localStorage on demand. Importing this module never touches\n * browser globals, and access errors (sandboxing/security settings) yield null.\n */\nexport function getDefaultDocumentRecoveryStorage(): DocumentRecoveryStorage | null {\n try {\n const candidate = Reflect.get(globalThis, 'localStorage') as unknown;\n if (!isStorage(candidate)) return null;\n return candidate;\n } catch {\n return null;\n }\n}\n\nfunction serializeEnvelope(records: DocumentRecoveryRecord[]): string {\n const envelope: DocumentRecoveryEnvelope = {\n schemaVersion: DOCUMENT_RECOVERY_JOURNAL_SCHEMA_VERSION,\n records,\n };\n return JSON.stringify(envelope);\n}\n\n/** localStorage quotas are commonly measured from UTF-16 storage usage. */\nfunction estimatedBytes(value: string): number {\n return value.length * 2;\n}\n\nfunction positiveInteger(value: number | undefined, fallback: number): number {\n return Number.isSafeInteger(value) && (value ?? 0) > 0 ? (value as number) : fallback;\n}\n\nfunction nonNegativeFinite(value: number | undefined, fallback: number): number {\n return Number.isFinite(value) && (value ?? -1) >= 0 ? (value as number) : fallback;\n}\n\nfunction isEnvelope(value: unknown): value is DocumentRecoveryEnvelope {\n if (!isObject(value)) return false;\n return (\n value.schemaVersion === DOCUMENT_RECOVERY_JOURNAL_SCHEMA_VERSION && Array.isArray(value.records)\n );\n}\n\nfunction isValidStoredRecord(\n value: unknown,\n timestamp: number,\n maxAgeMs: number,\n maxFutureSkewMs: number,\n): value is DocumentRecoveryRecord {\n if (!isObject(value)) return false;\n if (\n !isValidTargetKey(value.targetKey) ||\n !isNonNegativeSafeInteger(value.generation) ||\n !isNonNegativeSafeInteger(value.revision) ||\n typeof value.content !== 'string' ||\n (value.persistedContent !== null && typeof value.persistedContent !== 'string') ||\n !isNonNegativeFiniteNumber(value.createdAt) ||\n !isNonNegativeFiniteNumber(value.updatedAt) ||\n value.createdAt > value.updatedAt\n ) {\n return false;\n }\n if (!Number.isFinite(timestamp) || timestamp < 0) return false;\n if (value.updatedAt > timestamp + maxFutureSkewMs) return false;\n return timestamp - value.updatedAt <= maxAgeMs;\n}\n\nfunction isValidWrite(value: DocumentRecoveryWrite): boolean {\n return (\n isValidTargetKey(value.targetKey) &&\n isNonNegativeSafeInteger(value.generation) &&\n isNonNegativeSafeInteger(value.revision) &&\n typeof value.content === 'string' &&\n (value.persistedContent === null || typeof value.persistedContent === 'string')\n );\n}\n\nfunction isValidAcknowledgement(value: DocumentRecoveryAcknowledgement): boolean {\n return (\n isValidTargetKey(value.targetKey) &&\n isNonNegativeSafeInteger(value.generation) &&\n isNonNegativeSafeInteger(value.persistedRevision)\n );\n}\n\nfunction isValidTargetKey(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0 && value.length <= MAX_TARGET_KEY_LENGTH;\n}\n\nfunction isOptionalGeneration(value: number | undefined): boolean {\n return value === undefined || isNonNegativeSafeInteger(value);\n}\n\nfunction isNonNegativeSafeInteger(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction isNonNegativeFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isStorage(value: unknown): value is DocumentRecoveryStorage {\n if (!isObject(value)) return false;\n return (\n typeof value.getItem === 'function' &&\n typeof value.setItem === 'function' &&\n typeof value.removeItem === 'function'\n );\n}\n\nfunction compareGenerationRevision(\n left: Pick<DocumentRecoveryRecord, 'generation' | 'revision'>,\n right: Pick<DocumentRecoveryRecord, 'generation' | 'revision'>,\n): number {\n if (left.generation !== right.generation) return left.generation - right.generation;\n return left.revision - right.revision;\n}\n\nfunction compareStoredRecords(left: DocumentRecoveryRecord, right: DocumentRecoveryRecord): number {\n return compareGenerationRevision(left, right) || left.updatedAt - right.updatedAt;\n}\n\nfunction compareNewestFirst(left: DocumentRecoveryRecord, right: DocumentRecoveryRecord): number {\n return right.updatedAt - left.updatedAt || right.revision - left.revision;\n}\n\nfunction cloneRecord(record: DocumentRecoveryRecord): DocumentRecoveryRecord {\n return { ...record };\n}\n\nfunction evictOldest(records: DocumentRecoveryRecord[], protectedTarget?: string): boolean {\n let oldestIndex = -1;\n for (let index = 0; index < records.length; index += 1) {\n if (records[index].targetKey === protectedTarget) continue;\n if (oldestIndex < 0 || compareNewestFirst(records[index], records[oldestIndex]) > 0) {\n oldestIndex = index;\n }\n }\n if (oldestIndex < 0) return false;\n records.splice(oldestIndex, 1);\n return true;\n}\n"],"mappings":"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","names":["meta"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/filesystem/memory-provider-v2.ts","../src/filesystem/memory-provider.ts"],"sourcesContent":["import { FsError, type FsOperation } from './fs-error.js';\nimport type { FileCommitResult } from './types.js';\nimport {\n parseFileSystemVersion,\n type FileSystemCreateDirectoryOptions,\n type FileSystemDirectorySnapshot,\n type FileSystemEntrySnapshot,\n type FileSystemFileRead,\n type FileSystemFileSnapshot,\n type FileSystemMoveOptions,\n type FileSystemProviderCapabilities,\n type FileSystemProviderV2,\n type FileSystemRemoveOptions,\n type FileSystemRemoveResult,\n type FileSystemSnapshot,\n type FileSystemSnapshotEntry,\n type FileSystemVersion,\n type FileSystemWatchEvent,\n type FileSystemWatchListener,\n type FileSystemWatchOptions,\n type FileSystemWatchSubscription,\n type FileSystemWriteOptions,\n} from './v2.js';\nimport {\n WORKSPACE_ROOT,\n parseWorkspacePath,\n workspacePathBasename,\n workspacePathContains,\n workspacePathDirname,\n type WorkspacePath,\n} from './workspace-path.js';\nimport { copyBytes } from './internal/bytes.js';\nimport { compareSnapshotsByName, compareText } from './internal/entry-order.js';\nimport { parentChainWithRoot } from './internal/parent-chain.js';\nimport { decodeUtf8Text } from './utf8.js';\n\ninterface MemoryEntryBase {\n readonly version: FileSystemVersion;\n readonly lastModified: string;\n}\n\ninterface MemoryFileEntry extends MemoryEntryBase {\n readonly kind: 'file';\n readonly data: ArrayBuffer;\n /** Compatibility metadata only; v2 always treats the payload as bytes. */\n readonly payloadKind: MemoryFilePayloadKind;\n}\n\ninterface MemoryDirectoryEntry extends MemoryEntryBase {\n readonly kind: 'directory';\n}\n\ntype MemoryEntry = MemoryFileEntry | MemoryDirectoryEntry;\n\nexport type MemoryFilePayloadKind = 'text' | 'binary';\n\nexport interface MemoryFileSystemV2StateFile {\n readonly path: WorkspacePath;\n readonly data: ArrayBuffer;\n readonly payloadKind: MemoryFilePayloadKind;\n}\n\nexport interface MemoryFileSystemV2CompatibilityFile {\n readonly data: ArrayBuffer;\n readonly payloadKind: MemoryFilePayloadKind;\n}\n\nexport interface MemoryFileSystemV2State {\n readonly revision: number;\n readonly files: readonly MemoryFileSystemV2StateFile[];\n readonly directories: readonly WorkspacePath[];\n}\n\nexport interface MemoryFileSystemV2ReplacementFile {\n readonly path: WorkspacePath;\n readonly data: ArrayBuffer | Uint8Array;\n readonly payloadKind: MemoryFilePayloadKind;\n}\n\nexport interface MemoryFileSystemV2Replacement {\n readonly files: readonly MemoryFileSystemV2ReplacementFile[];\n readonly directories?: readonly WorkspacePath[];\n}\n\ninterface MemoryWatcher {\n readonly listener: FileSystemWatchListener;\n readonly onError: FileSystemWatchOptions['onError'];\n}\n\ntype MemoryWatchEventInput = FileSystemWatchEvent extends infer Event\n ? Event extends FileSystemWatchEvent\n ? Omit<Event, 'sequence' | 'origin'>\n : never\n : never;\n\nconst MEMORY_CAPABILITIES: FileSystemProviderCapabilities = Object.freeze({\n writeAtomicity: 'process',\n moveAtomicity: 'process',\n snapshotAtomicity: 'process',\n conditionalWrite: 'process',\n recursiveRemove: true,\n watch: true,\n caseSensitivity: 'sensitive',\n symlinkPolicy: 'unsupported',\n durability: 'volatile',\n});\n\n/**\n * Correctness reference for FileSystemProviderV2.\n *\n * Every mutation validates first, stages a fresh map, swaps it once, and only\n * then emits notifications. File buffers are owned by the provider and every\n * read/snapshot returns a defensive copy.\n */\nexport class MemoryFileSystemProviderV2 implements FileSystemProviderV2 {\n public readonly id: string;\n public readonly label: string;\n public readonly capabilities = MEMORY_CAPABILITIES;\n\n private entries: Map<WorkspacePath, MemoryEntry>;\n private readonly watchers = new Set<MemoryWatcher>();\n private readonly subscriptions = new Set<FileSystemWatchSubscription>();\n private readonly pendingEvents: FileSystemWatchEvent[] = [];\n private emittingEvents = false;\n private mutationCounter = 0;\n private eventSequence = 0;\n private treeVersion: FileSystemVersion;\n private disposed = false;\n private disposePromise: Promise<void> | null = null;\n\n public constructor(id: string, label: string) {\n this.id = id;\n this.label = label;\n this.treeVersion = parseFileSystemVersion(`${id}:0`);\n const now = new Date().toISOString();\n this.entries = new Map([\n [WORKSPACE_ROOT, { kind: 'directory', version: this.treeVersion, lastModified: now }],\n ]);\n }\n\n /** Monotonic in-process revision used by synchronous transient-workspace staging. */\n public get mutationRevision(): number {\n return this.mutationCounter;\n }\n\n /** @internal Atomically couple owned bytes to their v1 compatibility kind. */\n public readCompatibilityFile(path: WorkspacePath): MemoryFileSystemV2CompatibilityFile | null {\n this.assertOpen('read');\n const canonical = this.canonical(path);\n const entry = this.entries.get(canonical);\n if (!entry) return null;\n if (entry.kind !== 'file') {\n throw this.error(\n 'type-mismatch',\n 'read',\n canonical,\n 'Expected a file but found a directory.',\n );\n }\n return Object.freeze({ data: entry.data.slice(0), payloadKind: entry.payloadKind });\n }\n\n /** @internal Capture an owned synchronous copy for DBK whole-tree staging. */\n public captureState(): MemoryFileSystemV2State {\n this.assertOpen('snapshot');\n const files: MemoryFileSystemV2StateFile[] = [];\n const directories: WorkspacePath[] = [];\n for (const [path, entry] of [...this.entries].sort(([left], [right]) =>\n compareText(left, right),\n )) {\n if (!path) continue;\n if (entry.kind === 'directory') directories.push(path);\n else {\n files.push(\n Object.freeze({ path, data: entry.data.slice(0), payloadKind: entry.payloadKind }),\n );\n }\n }\n return Object.freeze({\n revision: this.mutationCounter,\n files: Object.freeze(files),\n directories: Object.freeze(directories),\n });\n }\n\n /** @internal Atomically replace a transient workspace after complete validation. */\n public replaceState(replacement: MemoryFileSystemV2Replacement, expectedRevision?: number): void {\n this.assertOpen('write');\n const stagedFiles = new Map<\n WorkspacePath,\n { readonly data: ArrayBuffer; readonly payloadKind: MemoryFilePayloadKind }\n >();\n const stagedDirectories = new Set<WorkspacePath>();\n const explicitDirectories = new Set<WorkspacePath>();\n\n for (const rawDirectory of replacement.directories ?? []) {\n const directory = this.canonicalNonRoot(rawDirectory, 'create-directory');\n if (explicitDirectories.has(directory)) {\n throw this.error(\n 'already-exists',\n 'create-directory',\n directory,\n 'Duplicate replacement directory.',\n );\n }\n explicitDirectories.add(directory);\n stagedDirectories.add(directory);\n addParentPaths(stagedDirectories, directory);\n }\n for (const rawFile of replacement.files) {\n const path = this.canonicalNonRoot(rawFile.path, 'write');\n if (stagedFiles.has(path)) {\n throw this.error('already-exists', 'write', path, 'Duplicate replacement file.');\n }\n if (!isMemoryFilePayloadKind(rawFile.payloadKind)) {\n throw this.error('type-mismatch', 'write', path, 'Invalid replacement payload kind.');\n }\n stagedFiles.set(path, {\n data: copyBytes(rawFile.data),\n payloadKind: rawFile.payloadKind,\n });\n addParentPaths(stagedDirectories, path);\n }\n for (const path of stagedFiles.keys()) {\n if (stagedDirectories.has(path)) {\n throw this.error(\n 'type-mismatch',\n 'write',\n path,\n 'Replacement path has conflicting file and directory kinds.',\n );\n }\n for (const parent of parentChainWithRoot(path)) {\n if (parent && stagedFiles.has(parent)) {\n throw this.error('type-mismatch', 'write', parent, 'A replacement parent is a file.');\n }\n }\n }\n if (expectedRevision !== undefined && expectedRevision !== this.mutationCounter) {\n throw this.error(\n 'conflict',\n 'write',\n WORKSPACE_ROOT,\n 'Memory filesystem changed while replacement contents were staged.',\n );\n }\n\n const version = this.nextVersion();\n const now = new Date().toISOString();\n const next = new Map<WorkspacePath, MemoryEntry>([\n [WORKSPACE_ROOT, { kind: 'directory', version, lastModified: now }],\n ]);\n this.addDirectories(next, [...stagedDirectories], version, now);\n for (const [path, file] of stagedFiles) {\n next.set(path, {\n kind: 'file',\n data: file.data,\n payloadKind: file.payloadKind,\n version,\n lastModified: now,\n });\n }\n this.entries = next;\n this.emit({\n type: 'overflow',\n kind: null,\n path: WORKSPACE_ROOT,\n destinationPath: null,\n version: null,\n previousVersion: null,\n });\n }\n\n /** @internal Synchronous setup hook used before a transient provider is published. */\n public seedText(path: WorkspacePath, content: string): void {\n this.writeFileNow(path, new TextEncoder().encode(content), { createParents: true }, 'text');\n }\n\n /** @internal Legacy text write that preserves text/binary compatibility metadata. */\n public async writeText(\n path: WorkspacePath,\n content: string,\n options: FileSystemWriteOptions = {},\n ): Promise<FileSystemFileSnapshot> {\n return this.writeFileNow(path, new TextEncoder().encode(content), options, 'text');\n }\n\n /** @internal Legacy binary write that explicitly changes compatibility payload kind. */\n public async writeBinary(\n path: WorkspacePath,\n data: ArrayBuffer | Uint8Array,\n options: FileSystemWriteOptions = {},\n ): Promise<FileSystemFileSnapshot> {\n return this.writeFileNow(path, data, options, 'binary');\n }\n\n /** @internal Atomic legacy compare-and-write over the authoritative v2 entry. */\n public async commitText(\n path: WorkspacePath,\n content: string,\n expectedContent: string | null,\n ): Promise<FileCommitResult> {\n this.assertOpen('write');\n const canonical = this.canonicalNonRoot(path, 'write');\n const existing = this.entries.get(canonical);\n if (existing?.kind === 'directory') {\n throw this.error('type-mismatch', 'write', canonical, 'Cannot replace a directory.');\n }\n // Every file is bytes, so the baseline is decided by the bytes, not by the\n // compatibility kind. Gating on `payloadKind === 'binary'` reported\n // `conflict` with a null `content`, which callers read as \"deleted\n // externally\" — a phantom deletion for a file that is plainly still there.\n // Undecodable bytes are a real fault and are raised as one, matching the\n // IndexedDB facade and the v2 commit target.\n const currentContent =\n existing?.kind === 'file'\n ? decodeUtf8Text(existing.data, {\n label: 'The stored file',\n operation: 'write',\n path: canonical,\n })\n : null;\n if (currentContent !== expectedContent && currentContent !== content) {\n return {\n status: 'conflict',\n content: currentContent,\n version: existing?.version ?? null,\n };\n }\n const written = this.writeFileNow(\n canonical,\n new TextEncoder().encode(content),\n { createParents: true, expectedVersion: existing?.version ?? null },\n 'text',\n );\n return { status: 'committed', version: written.version };\n }\n\n public async stat(path: WorkspacePath): Promise<FileSystemEntrySnapshot | null> {\n this.assertOpen('stat');\n const canonical = this.canonical(path);\n const entry = this.entries.get(canonical);\n return entry ? this.toEntrySnapshot(canonical, entry) : null;\n }\n\n public async readFile(path: WorkspacePath): Promise<FileSystemFileRead | null> {\n this.assertOpen('read');\n const canonical = this.canonical(path);\n const entry = this.entries.get(canonical);\n if (!entry) return null;\n if (entry.kind !== 'file') {\n throw this.error(\n 'type-mismatch',\n 'read',\n canonical,\n 'Expected a file but found a directory.',\n );\n }\n return Object.freeze({\n entry: this.toFileSnapshot(canonical, entry),\n data: entry.data.slice(0),\n });\n }\n\n public async readDirectory(path: WorkspacePath): Promise<readonly FileSystemEntrySnapshot[]> {\n this.assertOpen('list');\n const canonical = this.canonical(path);\n const directory = this.entries.get(canonical);\n if (!directory) {\n throw this.error('not-found', 'list', canonical, 'Directory does not exist.');\n }\n if (directory.kind !== 'directory') {\n throw this.error(\n 'type-mismatch',\n 'list',\n canonical,\n 'Expected a directory but found a file.',\n );\n }\n\n const children: FileSystemEntrySnapshot[] = [];\n for (const [entryPath, entry] of this.entries) {\n if (entryPath && workspacePathDirname(entryPath) === canonical) {\n children.push(this.toEntrySnapshot(entryPath, entry));\n }\n }\n children.sort(compareSnapshotsByName);\n return Object.freeze(children);\n }\n\n public async writeFile(\n path: WorkspacePath,\n data: ArrayBuffer | Uint8Array,\n options: FileSystemWriteOptions = {},\n ): Promise<FileSystemFileSnapshot> {\n return this.writeFileNow(path, data, options);\n }\n\n private writeFileNow(\n path: WorkspacePath,\n data: ArrayBuffer | Uint8Array,\n options: FileSystemWriteOptions,\n payloadKind?: MemoryFilePayloadKind,\n ): FileSystemFileSnapshot {\n this.assertOpen('write');\n const canonical = this.canonicalNonRoot(path, 'write');\n const existing = this.entries.get(canonical);\n if (existing?.kind === 'directory') {\n throw this.error(\n 'type-mismatch',\n 'write',\n canonical,\n 'Cannot replace a directory with a file.',\n );\n }\n\n const mode = options.mode ?? 'upsert';\n if (mode === 'create' && existing) {\n throw this.error('already-exists', 'write', canonical, 'File already exists.');\n }\n if (mode === 'replace' && !existing) {\n throw this.error('not-found', 'write', canonical, 'File does not exist.');\n }\n this.assertExpectedVersion(existing, options.expectedVersion, 'write', canonical);\n\n const missingParents = this.planParents(canonical, options.createParents ?? false, 'write');\n const ownedData = copyBytes(data);\n const version = this.nextVersion();\n const now = new Date().toISOString();\n const next = new Map(this.entries);\n this.addDirectories(next, missingParents, version, now);\n next.set(canonical, {\n kind: 'file',\n data: ownedData,\n payloadKind: payloadKind ?? (existing?.kind === 'file' ? existing.payloadKind : 'binary'),\n version,\n lastModified: now,\n });\n this.touchParents(next, canonical, version, now);\n this.entries = next;\n\n for (const directoryPath of missingParents) {\n this.emit({\n type: 'created',\n kind: 'directory',\n path: directoryPath,\n destinationPath: null,\n version,\n previousVersion: null,\n });\n }\n this.emit({\n type: existing ? 'modified' : 'created',\n kind: 'file',\n path: canonical,\n destinationPath: null,\n version,\n previousVersion: existing?.version ?? null,\n });\n return this.toFileSnapshot(canonical, next.get(canonical) as MemoryFileEntry);\n }\n\n public async createDirectory(\n path: WorkspacePath,\n options: FileSystemCreateDirectoryOptions = {},\n ): Promise<FileSystemDirectorySnapshot> {\n this.assertOpen('create-directory');\n const canonical = this.canonical(path);\n const existing = this.entries.get(canonical);\n if (existing) {\n if (existing.kind !== 'directory') {\n throw this.error(\n 'type-mismatch',\n 'create-directory',\n canonical,\n 'Cannot replace a file with a directory.',\n );\n }\n if ((options.mode ?? 'ensure') === 'create') {\n throw this.error(\n 'already-exists',\n 'create-directory',\n canonical,\n 'Directory already exists.',\n );\n }\n return this.toDirectorySnapshot(canonical, existing);\n }\n\n const missingParents = this.planParents(\n canonical,\n options.createParents ?? false,\n 'create-directory',\n );\n const version = this.nextVersion();\n const now = new Date().toISOString();\n const next = new Map(this.entries);\n const created = [...missingParents, canonical];\n this.addDirectories(next, created, version, now);\n this.touchParents(next, canonical, version, now);\n this.entries = next;\n\n for (const directoryPath of created) {\n this.emit({\n type: 'created',\n kind: 'directory',\n path: directoryPath,\n destinationPath: null,\n version,\n previousVersion: null,\n });\n }\n return this.toDirectorySnapshot(canonical, next.get(canonical) as MemoryDirectoryEntry);\n }\n\n public async remove(\n path: WorkspacePath,\n options: FileSystemRemoveOptions = {},\n ): Promise<FileSystemRemoveResult> {\n this.assertOpen('remove');\n const canonical = this.canonicalNonRoot(path, 'remove');\n const existing = this.entries.get(canonical);\n if (!existing) {\n if ((options.missing ?? 'error') === 'ignore') {\n return Object.freeze({ removed: false, version: this.treeVersion });\n }\n throw this.error('not-found', 'remove', canonical, 'Entry does not exist.');\n }\n this.assertExpectedVersion(existing, options.expectedVersion, 'remove', canonical);\n\n const descendants = [...this.entries.keys()].filter((candidate) =>\n workspacePathContains(canonical, candidate, false),\n );\n if (existing.kind === 'directory' && descendants.length > 0 && !options.recursive) {\n throw this.error('not-empty', 'remove', canonical, 'Directory is not empty.');\n }\n\n const version = this.nextVersion();\n const now = new Date().toISOString();\n const next = new Map(this.entries);\n next.delete(canonical);\n if (existing.kind === 'directory') {\n for (const descendant of descendants) next.delete(descendant);\n }\n this.touchParents(next, canonical, version, now);\n this.entries = next;\n\n this.emit({\n type: 'removed',\n kind: existing.kind,\n path: canonical,\n destinationPath: null,\n version: null,\n previousVersion: existing.version,\n });\n return Object.freeze({ removed: true, version });\n }\n\n public async move(\n oldPath: WorkspacePath,\n newPath: WorkspacePath,\n options: FileSystemMoveOptions = {},\n ): Promise<FileSystemEntrySnapshot> {\n this.assertOpen('move');\n const sourcePath = this.canonicalNonRoot(oldPath, 'move');\n const destinationPath = this.canonicalNonRoot(newPath, 'move');\n const source = this.entries.get(sourcePath);\n if (!source) throw this.error('not-found', 'move', sourcePath, 'Source does not exist.');\n this.assertExpectedVersion(source, options.expectedVersion, 'move', sourcePath);\n if (sourcePath === destinationPath) return this.toEntrySnapshot(sourcePath, source);\n if (this.entries.has(destinationPath)) {\n throw new FsError('already-exists', 'Destination already exists.', {\n operation: 'move',\n path: sourcePath,\n destinationPath,\n });\n }\n if (source.kind === 'directory' && workspacePathContains(sourcePath, destinationPath, false)) {\n throw new FsError('invalid-path', 'Cannot move a directory into itself.', {\n operation: 'move',\n path: sourcePath,\n destinationPath,\n });\n }\n\n const missingParents = this.planParents(\n destinationPath,\n options.createParents ?? false,\n 'move',\n );\n const sourcePaths = [...this.entries.keys()].filter((candidate) =>\n workspacePathContains(sourcePath, candidate),\n );\n const sourceSet = new Set(sourcePaths);\n const remapped = sourcePaths.map((currentPath) => {\n const suffix = currentPath.slice(sourcePath.length);\n const target = parseWorkspacePath(`${destinationPath}${suffix}`);\n if (this.entries.has(target) && !sourceSet.has(target)) {\n throw new FsError('already-exists', 'A destination descendant already exists.', {\n operation: 'move',\n path: currentPath,\n destinationPath: target,\n });\n }\n return [currentPath, target] as const;\n });\n\n const version = this.nextVersion();\n const now = new Date().toISOString();\n const next = new Map(this.entries);\n for (const currentPath of sourcePaths) next.delete(currentPath);\n this.addDirectories(next, missingParents, version, now);\n for (const [currentPath, targetPath] of remapped) {\n const current = this.entries.get(currentPath)!;\n next.set(targetPath, this.withVersion(current, version, now));\n }\n this.touchParents(next, sourcePath, version, now);\n this.touchParents(next, destinationPath, version, now);\n this.entries = next;\n\n for (const directoryPath of missingParents) {\n this.emit({\n type: 'created',\n kind: 'directory',\n path: directoryPath,\n destinationPath: null,\n version,\n previousVersion: null,\n });\n }\n this.emit({\n type: 'moved',\n kind: source.kind,\n path: sourcePath,\n destinationPath,\n version,\n previousVersion: source.version,\n });\n return this.toEntrySnapshot(destinationPath, next.get(destinationPath)!);\n }\n\n public async snapshot(): Promise<FileSystemSnapshot> {\n this.assertOpen('snapshot');\n const entries: FileSystemSnapshotEntry[] = [...this.entries.entries()]\n .sort(([left], [right]) => compareText(left, right))\n .map(([path, entry]) => {\n if (entry.kind === 'file') {\n return Object.freeze({ ...this.toFileSnapshot(path, entry), data: entry.data.slice(0) });\n }\n return this.toDirectorySnapshot(path, entry);\n });\n return Object.freeze({ version: this.treeVersion, entries: Object.freeze(entries) });\n }\n\n public watch(\n listener: FileSystemWatchListener,\n options: FileSystemWatchOptions = {},\n ): FileSystemWatchSubscription {\n this.assertOpen('watch');\n const watcher: MemoryWatcher = { listener, onError: options.onError };\n this.watchers.add(watcher);\n let closed = false;\n let disposePromise: Promise<void> | null = null;\n const subscription: FileSystemWatchSubscription = {\n get closed() {\n return closed;\n },\n ready: Promise.resolve(),\n dispose: () => {\n if (disposePromise) return disposePromise;\n closed = true;\n this.watchers.delete(watcher);\n this.subscriptions.delete(subscription);\n disposePromise = Promise.resolve();\n return disposePromise;\n },\n };\n this.subscriptions.add(subscription);\n return subscription;\n }\n\n public dispose(): Promise<void> {\n if (this.disposePromise) return this.disposePromise;\n this.disposed = true;\n this.disposePromise = Promise.all(\n [...this.subscriptions].map((subscription) => subscription.dispose()),\n ).then(() => undefined);\n return this.disposePromise;\n }\n\n private canonical(path: WorkspacePath): WorkspacePath {\n return parseWorkspacePath(path);\n }\n\n private canonicalNonRoot(path: WorkspacePath, operation: FsOperation): WorkspacePath {\n const canonical = this.canonical(path);\n if (!canonical) {\n throw this.error(\n 'invalid-path',\n operation,\n canonical,\n 'The workspace root is not a file entry.',\n );\n }\n return canonical;\n }\n\n private assertOpen(operation: FsOperation): void {\n if (!this.disposed) return;\n throw new FsError('disposed', 'Filesystem provider is disposed.', { operation });\n }\n\n private assertExpectedVersion(\n entry: MemoryEntry | undefined,\n expected: FileSystemVersion | null | undefined,\n operation: FsOperation,\n path: WorkspacePath,\n ): void {\n if (expected === undefined) return;\n if (expected === null ? !entry : entry?.version === expected) return;\n throw this.error(\n 'conflict',\n operation,\n path,\n 'Entry version does not match the expected version.',\n );\n }\n\n private planParents(\n path: WorkspacePath,\n createParents: boolean,\n operation: FsOperation,\n ): WorkspacePath[] {\n const missing: WorkspacePath[] = [];\n for (const parent of parentChainWithRoot(path)) {\n if (!parent) continue;\n const entry = this.entries.get(parent);\n if (entry?.kind === 'file') {\n throw this.error(\n 'type-mismatch',\n operation,\n parent,\n 'A parent path is a file, not a directory.',\n );\n }\n if (!entry) {\n if (!createParents) {\n throw this.error('not-found', operation, parent, 'Parent directory does not exist.');\n }\n missing.push(parent);\n }\n }\n return missing;\n }\n\n private addDirectories(\n target: Map<WorkspacePath, MemoryEntry>,\n paths: readonly WorkspacePath[],\n version: FileSystemVersion,\n lastModified: string,\n ): void {\n for (const path of paths) {\n target.set(path, { kind: 'directory', version, lastModified });\n }\n }\n\n private touchParents(\n target: Map<WorkspacePath, MemoryEntry>,\n path: WorkspacePath,\n version: FileSystemVersion,\n lastModified: string,\n ): void {\n for (const parent of parentChainWithRoot(path)) {\n const entry = target.get(parent);\n if (entry?.kind === 'directory') {\n target.set(parent, { kind: 'directory', version, lastModified });\n }\n }\n }\n\n private withVersion(\n entry: MemoryEntry,\n version: FileSystemVersion,\n lastModified: string,\n ): MemoryEntry {\n return entry.kind === 'file'\n ? {\n kind: 'file',\n data: entry.data,\n payloadKind: entry.payloadKind,\n version,\n lastModified,\n }\n : { kind: 'directory', version, lastModified };\n }\n\n private nextVersion(): FileSystemVersion {\n this.mutationCounter += 1;\n this.treeVersion = parseFileSystemVersion(`${this.id}:${this.mutationCounter}`);\n return this.treeVersion;\n }\n\n private toEntrySnapshot(path: WorkspacePath, entry: MemoryEntry): FileSystemEntrySnapshot {\n return entry.kind === 'file'\n ? this.toFileSnapshot(path, entry)\n : this.toDirectorySnapshot(path, entry);\n }\n\n private toFileSnapshot(path: WorkspacePath, entry: MemoryFileEntry): FileSystemFileSnapshot {\n return Object.freeze({\n kind: 'file',\n path,\n name: workspacePathBasename(path),\n size: entry.data.byteLength,\n version: entry.version,\n lastModified: entry.lastModified,\n });\n }\n\n private toDirectorySnapshot(\n path: WorkspacePath,\n entry: MemoryDirectoryEntry,\n ): FileSystemDirectorySnapshot {\n return Object.freeze({\n kind: 'directory',\n path,\n name: workspacePathBasename(path),\n size: null,\n version: entry.version,\n lastModified: entry.lastModified,\n });\n }\n\n private emit(event: MemoryWatchEventInput): void {\n const complete: FileSystemWatchEvent = Object.freeze({\n ...event,\n sequence: ++this.eventSequence,\n origin: 'local',\n });\n this.pendingEvents.push(complete);\n if (this.emittingEvents) return;\n\n this.emittingEvents = true;\n try {\n while (this.pendingEvents.length > 0) {\n const pending = this.pendingEvents.shift();\n if (!pending) continue;\n for (const watcher of [...this.watchers]) {\n try {\n watcher.listener(pending);\n } catch {\n // A watcher is observational; it cannot roll back a completed mutation.\n }\n }\n }\n } finally {\n this.emittingEvents = false;\n }\n }\n\n private error(\n code: ConstructorParameters<typeof FsError>[0],\n operation: FsOperation,\n path: WorkspacePath,\n message: string,\n ): FsError {\n return new FsError(code, message, { operation, path });\n }\n}\n\nfunction addParentPaths(target: Set<WorkspacePath>, path: WorkspacePath): void {\n for (const parent of parentChainWithRoot(path)) {\n if (parent) target.add(parent);\n }\n}\n\nfunction isMemoryFilePayloadKind(value: unknown): value is MemoryFilePayloadKind {\n return value === 'text' || value === 'binary';\n}\n","/**\n * In-memory filesystem used by transient loose-file and DBK workspaces.\n *\n * MemoryFileSystemProviderV2 is authoritative. This class is the temporary\n * text-oriented v1 facade retained while callers migrate to the byte contract.\n */\n\nimport { FsError, type FsOperation } from './fs-error.js';\nimport {\n MemoryFileSystemProviderV2,\n type MemoryFileSystemV2ReplacementFile,\n} from './memory-provider-v2.js';\nimport type { FileCommitResult, FileSystemEntry, FileSystemProvider, FileMeta } from './types.js';\nimport { parseWorkspacePath, type WorkspacePath } from './workspace-path.js';\nimport { decodeUtf8Text } from './utf8.js';\n\nexport type MemoryFileSystemSnapshotFile =\n | {\n readonly kind: 'text';\n readonly path: string;\n readonly content: string;\n }\n | {\n readonly kind: 'binary';\n readonly path: string;\n readonly data: ArrayBuffer | Uint8Array;\n };\n\n/** Complete provider-relative state for a transient memory workspace. */\nexport interface MemoryFileSystemSnapshot {\n readonly files: readonly MemoryFileSystemSnapshotFile[];\n readonly directories?: readonly string[];\n}\n\nfunction canonicalPath(path: string): WorkspacePath {\n return parseWorkspacePath(path);\n}\n\n/**\n * Canonicalize and reject the root eagerly.\n *\n * Only the synchronous staging entry points need this: they must validate every\n * input before `replaceState` swaps the tree, so they cannot rely on the v2\n * authority to reject the root mid-swap. The async provider methods pass paths\n * straight through, letting v2 state root policy once for every facade.\n */\nfunction entryPath(path: string, operation: FsOperation): WorkspacePath {\n const canonical = canonicalPath(path);\n if (!canonical) {\n throw new FsError('invalid-path', 'The workspace root is not a file entry.', {\n operation,\n path,\n });\n }\n return canonical;\n}\n\n/**\n * Byte-authoritative entries may hold payloads that are not valid UTF-8. A\n * lossy decode would hand the caller replacement characters that a later save\n * writes back over the user's original bytes, so surface the fault instead.\n */\nfunction decode(data: ArrayBuffer, path?: WorkspacePath): string {\n return decodeUtf8Text(data, { label: 'The file', operation: 'read', path });\n}\n\n/**\n * Legacy facade over the correctness-first memory provider.\n *\n * Synchronous seed/snapshot replacement methods remain because DBK import\n * fully stages external bytes before publishing one atomic in-memory swap.\n */\nexport class MemoryFileSystemProvider implements FileSystemProvider {\n public readonly id: string;\n public readonly label: string;\n public readonly v2: MemoryFileSystemProviderV2;\n\n public constructor(id: string, label: string) {\n this.id = id;\n this.label = label;\n this.v2 = new MemoryFileSystemProviderV2(id, label);\n }\n\n /** Monotonic version of the complete in-memory tree. */\n public get treeVersion(): number {\n return this.v2.mutationRevision;\n }\n\n /** Capture an owned deterministic copy of the complete transient tree. */\n public captureContents(): MemoryFileSystemSnapshot {\n const state = this.v2.captureState();\n return {\n files: state.files.map((file) =>\n file.payloadKind === 'text'\n ? { kind: 'text' as const, path: file.path, content: decode(file.data, file.path) }\n : { kind: 'binary' as const, path: file.path, data: file.data.slice(0) },\n ),\n directories: [...state.directories],\n };\n }\n\n /** Atomically replace the complete tree after validating and owning all input. */\n public replaceContents(snapshot: MemoryFileSystemSnapshot, expectedTreeVersion?: number): void {\n const files: MemoryFileSystemV2ReplacementFile[] = snapshot.files.map((file) => ({\n path: entryPath(file.path, 'write'),\n payloadKind: file.kind,\n data: file.kind === 'text' ? new TextEncoder().encode(file.content) : file.data,\n }));\n const directories = (snapshot.directories ?? []).map((path) =>\n entryPath(path, 'create-directory'),\n );\n this.v2.replaceState({ files, directories }, expectedTreeVersion);\n }\n\n /** Seed synchronously before publishing a transient provider to consumers. */\n public seedText(path: string, content: string): void {\n this.v2.seedText(entryPath(path, 'write'), content);\n }\n\n public async readFile(path: string): Promise<string | null> {\n const canonical = canonicalPath(path);\n const file = await this.v2.readFile(canonical);\n return file ? decode(file.data, canonical) : null;\n }\n\n public async writeFile(path: string, content: string): Promise<void> {\n await this.v2.writeText(canonicalPath(path), content, { createParents: true });\n }\n\n public commitFile(\n path: string,\n content: string,\n expectedContent: string | null,\n ): Promise<FileCommitResult> {\n return this.v2.commitText(canonicalPath(path), content, expectedContent);\n }\n\n public async delete(path: string): Promise<void> {\n await this.v2.remove(canonicalPath(path), { recursive: true, missing: 'ignore' });\n }\n\n public async rename(oldPath: string, newPath: string): Promise<void> {\n await this.v2.move(canonicalPath(oldPath), canonicalPath(newPath), {\n createParents: true,\n });\n }\n\n public async readDirectory(path: string): Promise<FileSystemEntry[]> {\n const canonical = canonicalPath(path);\n try {\n const entries = await this.v2.readDirectory(canonical);\n return entries.map((entry) => ({\n kind: entry.kind,\n name: entry.name,\n path: entry.path,\n }));\n } catch (error: unknown) {\n // Preserve the v1 missing-directory behavior only on the compatibility facade.\n if (error instanceof FsError && error.code === 'not-found') return [];\n throw error;\n }\n }\n\n public async exists(path: string): Promise<boolean> {\n return (await this.v2.stat(canonicalPath(path))) !== null;\n }\n\n public async createDirectory(path: string): Promise<void> {\n await this.v2.createDirectory(canonicalPath(path), { createParents: true });\n }\n\n public async stat(path: string): Promise<FileMeta | null> {\n const entry = await this.v2.stat(canonicalPath(path));\n if (!entry || entry.kind !== 'file') return null;\n return {\n name: entry.name,\n path: entry.path,\n size: entry.size,\n lastModified: entry.lastModified,\n };\n }\n\n /**\n * Every stored file is bytes, so every stored file is readable as binary.\n * `payloadKind` records how a file was written for DBK export fidelity; it is\n * not a filter. Gating on it made `readBinary` report an existing text file as\n * missing, which no other provider does and which callers read as \"no file\".\n */\n public async readBinary(path: string): Promise<ArrayBuffer | null> {\n return this.v2.readCompatibilityFile(canonicalPath(path))?.data ?? null;\n }\n\n public async writeBinary(path: string, data: ArrayBuffer | Uint8Array): Promise<void> {\n await this.v2.writeBinary(canonicalPath(path), data, { createParents: true });\n }\n}\n"],"mappings":"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","names":["entryPath"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/filesystem/types.ts"],"sourcesContent":["/**\n * FileSystemProvider — abstract interface for a virtual filesystem.\n *\n * Legacy text-oriented facade retained while consumers migrate to\n * FileSystemProviderV2. New persistence code must use the provider's `v2`\n * contract so paths, errors, operation modes, and capabilities are explicit.\n */\n\n// ── Entry types ────────────────────────────────────────────────────\n\nimport type { FileSystemProviderV2 } from './v2.js';\n\nexport interface FileEntry {\n kind: 'file';\n name: string;\n path: string;\n}\n\nexport interface FolderEntry {\n kind: 'directory';\n name: string;\n path: string;\n}\n\nexport type FileSystemEntry = FileEntry | FolderEntry;\n\nexport interface FileMeta {\n name: string;\n path: string;\n size: number;\n lastModified: string;\n}\n\nexport type FileCommitResult =\n | { status: 'committed'; version: string | null }\n | { status: 'conflict'; content: string | null; version: string | null };\n\n// ── Provider interface ─────────────────────────────────────────────\n\n/**\n * @deprecated Compatibility facade for external providers and older callers.\n * Implement new backends with `FileSystemProviderV2`; built-in facades expose\n * their authoritative implementation through `v2`.\n */\nexport interface FileSystemProvider {\n /** Unique identifier for this provider instance. */\n readonly id: string;\n\n /** Human-readable label (e.g., folder name or \"Browser Storage\"). */\n readonly label: string;\n\n /** Optional correctness-first implementation exposed during the v1-to-v2 migration. */\n readonly v2?: FileSystemProviderV2;\n\n /** Read the text content of a file. Returns null if the file doesn't exist. */\n readFile(path: string): Promise<string | null>;\n\n /** Write text content to a file, creating it (and parent dirs) if needed. */\n writeFile(path: string, content: string): Promise<void>;\n\n /**\n * Conditionally replace a text file when it still matches the caller's\n * persisted baseline. Production providers implement this at their\n * strongest available serialization boundary.\n */\n commitFile?(\n path: string,\n content: string,\n expectedContent: string | null,\n ): Promise<FileCommitResult>;\n\n /**\n * Legacy recursive removal of a file or directory subtree. Workspace root\n * deletion is forbidden. Prefer v2 `remove({ recursive })`.\n */\n delete(path: string): Promise<void>;\n\n /** Rename or move an entry. Rejects missing sources and existing destinations. */\n rename(oldPath: string, newPath: string): Promise<void>;\n\n /** List immediate children of a directory. */\n readDirectory(path: string): Promise<FileSystemEntry[]>;\n\n /** Check whether a path exists. */\n exists(path: string): Promise<boolean>;\n\n /** Create a directory (and parents if needed). */\n createDirectory(path: string): Promise<void>;\n\n /** Get metadata for a file. Returns null if not found. */\n stat(path: string): Promise<FileMeta | null>;\n\n /** Read raw binary content. Returns null if not found. */\n readBinary(path: string): Promise<ArrayBuffer | null>;\n\n /** Write raw binary content. */\n writeBinary(path: string, data: ArrayBuffer | Uint8Array): Promise<void>;\n}\n\nexport type FileSystemProviderWithV2 = FileSystemProvider & {\n readonly v2: FileSystemProviderV2;\n};\n\n/** Discover a provider-owned v2 implementation without coupling consumers to a concrete class. */\nexport function hasFileSystemProviderV2(\n provider: FileSystemProvider,\n): provider is FileSystemProviderWithV2 {\n return provider.v2 !== undefined && provider.v2 !== null;\n}\n\n/** Return the provider-owned v2 implementation when migration support is available. */\nexport function getFileSystemProviderV2(provider: FileSystemProvider): FileSystemProviderV2 | null {\n return provider.v2 ?? null;\n}\n"],"mappings":";AAwGO,SAAS,wBACd,UACsC;AACtC,SAAO,SAAS,OAAO,UAAa,SAAS,OAAO;AACtD;AAGO,SAAS,wBAAwB,UAA2D;AACjG,SAAO,SAAS,MAAM;AACxB;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/filesystem/utf8.ts"],"sourcesContent":["import { FsError, type FsOperation } from './fs-error.js';\n\nexport interface DecodeUtf8TextOptions {\n /** User-facing description of the payload being decoded. */\n readonly label?: string;\n /** Filesystem operation that produced the bytes. */\n readonly operation?: FsOperation;\n /** Canonical or display path associated with the payload. */\n readonly path?: string;\n}\n\n/**\n * Decode byte-authoritative text without silently replacing malformed input.\n * Replacement characters are unsafe at persistence boundaries because a later\n * save can permanently overwrite the original bytes with altered content.\n */\nexport function decodeUtf8Text(\n data: ArrayBuffer | ArrayBufferView,\n options: DecodeUtf8TextOptions = {},\n): string {\n try {\n return new TextDecoder('utf-8', { fatal: true }).decode(data);\n } catch {\n throw new FsError('corrupt', `${options.label ?? 'Text content'} is not valid UTF-8.`, {\n operation: options.operation ?? 'read',\n path: options.path,\n });\n }\n}\n"],"mappings":";;;;;AAgBO,SAAS,eACd,MACA,UAAiC,CAAC,GAC1B;AACR,MAAI;AACF,WAAO,IAAI,YAAY,SAAS,EAAE,OAAO,KAAK,CAAC,EAAE,OAAO,IAAI;AAAA,EAC9D,QAAQ;AACN,UAAM,IAAI,QAAQ,WAAW,GAAG,QAAQ,SAAS,cAAc,wBAAwB;AAAA,MACrF,WAAW,QAAQ,aAAa;AAAA,MAChC,MAAM,QAAQ;AAAA,IAChB,CAAC;AAAA,EACH;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/host/wire-policy.ts"],"sourcesContent":["import type { OpenRequest } from './types.js';\n\n/** Quantitative limits shared by privileged host boundaries. */\nexport const HOST_WIRE_LIMITS = Object.freeze({\n identifierCharacters: 256,\n labelCharacters: 1_024,\n pathCharacters: 4_096,\n urlCharacters: 8_192,\n messageCharacters: 2_000,\n documentCharacters: 20 * 1024 * 1024,\n binaryBytes: 100 * 1024 * 1024,\n base64Characters: 140 * 1024 * 1024,\n arrayEntries: 10_000,\n});\n\nfunction hasExactKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {\n const actual = Object.keys(value);\n return (\n actual.length === keys.length &&\n keys.every((key) => Object.prototype.hasOwnProperty.call(value, key))\n );\n}\n\nexport function isBoundedString(\n value: unknown,\n maximumCharacters: number,\n minimumCharacters = 0,\n): value is string {\n return (\n typeof value === 'string' &&\n value.length >= minimumCharacters &&\n value.length <= maximumCharacters &&\n !value.includes('\\0')\n );\n}\n\nexport function isBoundedBytePayload(\n value: unknown,\n maximumBytes = HOST_WIRE_LIMITS.binaryBytes,\n): value is ArrayBuffer | Uint8Array {\n return (\n (value instanceof ArrayBuffer || value instanceof Uint8Array) &&\n value.byteLength <= maximumBytes\n );\n}\n\n/** Parse one exact, bounded main-to-renderer open request. */\nexport function parseOpenRequest(value: unknown): OpenRequest | null {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return null;\n const record = value as Record<string, unknown>;\n\n if (record.kind === 'workspace-file') {\n if (!hasExactKeys(record, ['kind', 'workspaceId', 'path'])) return null;\n if (!isBoundedString(record.workspaceId, HOST_WIRE_LIMITS.identifierCharacters, 1)) return null;\n if (!isBoundedString(record.path, HOST_WIRE_LIMITS.pathCharacters, 1)) return null;\n return { kind: 'workspace-file', workspaceId: record.workspaceId, path: record.path };\n }\n\n if (record.kind === 'external-file' || record.kind === 'external-bundle') {\n if (!hasExactKeys(record, ['kind', 'resourceId', 'name'])) return null;\n if (!isBoundedString(record.resourceId, HOST_WIRE_LIMITS.identifierCharacters, 1)) return null;\n if (!isBoundedString(record.name, HOST_WIRE_LIMITS.labelCharacters, 1)) return null;\n return { kind: record.kind, resourceId: record.resourceId, name: record.name };\n }\n\n return null;\n}\n\n/** Return one canonical HTTP(S) URL, or null when external navigation is unsafe. */\nexport function parseExternalHttpUrl(value: unknown): string | null {\n if (!isBoundedString(value, HOST_WIRE_LIMITS.urlCharacters, 1)) return null;\n try {\n const url = new URL(value);\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return null;\n if (url.username || url.password) return null;\n const canonical = url.href;\n return canonical.length <= HOST_WIRE_LIMITS.urlCharacters ? canonical : null;\n } catch {\n return null;\n }\n}\n\n/** True only for the packaged renderer origin or the exact configured dev origin. */\nexport function isTrustedRendererUrl(value: unknown, developmentOrigin?: string): boolean {\n if (!isBoundedString(value, HOST_WIRE_LIMITS.urlCharacters, 1)) return false;\n try {\n const url = new URL(value);\n if (\n url.protocol === 'app:' &&\n url.hostname === 'docblocks' &&\n url.port === '' &&\n url.username === '' &&\n url.password === ''\n ) {\n return true;\n }\n if (!developmentOrigin) return false;\n const trustedDevelopmentOrigin = new URL(developmentOrigin);\n return url.origin === trustedDevelopmentOrigin.origin;\n } catch {\n return false;\n }\n}\n"],"mappings":";AAGO,IAAM,mBAAmB,OAAO,OAAO;AAAA,EAC5C,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,oBAAoB,KAAK,OAAO;AAAA,EAChC,aAAa,MAAM,OAAO;AAAA,EAC1B,kBAAkB,MAAM,OAAO;AAAA,EAC/B,cAAc;AAChB,CAAC;AAED,SAAS,aAAa,OAAgC,MAAkC;AACtF,QAAM,SAAS,OAAO,KAAK,KAAK;AAChC,SACE,OAAO,WAAW,KAAK,UACvB,KAAK,MAAM,CAAC,QAAQ,OAAO,UAAU,eAAe,KAAK,OAAO,GAAG,CAAC;AAExE;AAEO,SAAS,gBACd,OACA,mBACA,oBAAoB,GACH;AACjB,SACE,OAAO,UAAU,YACjB,MAAM,UAAU,qBAChB,MAAM,UAAU,qBAChB,CAAC,MAAM,SAAS,IAAI;AAExB;AAEO,SAAS,qBACd,OACA,eAAe,iBAAiB,aACG;AACnC,UACG,iBAAiB,eAAe,iBAAiB,eAClD,MAAM,cAAc;AAExB;AAGO,SAAS,iBAAiB,OAAoC;AACnE,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,EAAG,QAAO;AAChF,QAAM,SAAS;AAEf,MAAI,OAAO,SAAS,kBAAkB;AACpC,QAAI,CAAC,aAAa,QAAQ,CAAC,QAAQ,eAAe,MAAM,CAAC,EAAG,QAAO;AACnE,QAAI,CAAC,gBAAgB,OAAO,aAAa,iBAAiB,sBAAsB,CAAC,EAAG,QAAO;AAC3F,QAAI,CAAC,gBAAgB,OAAO,MAAM,iBAAiB,gBAAgB,CAAC,EAAG,QAAO;AAC9E,WAAO,EAAE,MAAM,kBAAkB,aAAa,OAAO,aAAa,MAAM,OAAO,KAAK;AAAA,EACtF;AAEA,MAAI,OAAO,SAAS,mBAAmB,OAAO,SAAS,mBAAmB;AACxE,QAAI,CAAC,aAAa,QAAQ,CAAC,QAAQ,cAAc,MAAM,CAAC,EAAG,QAAO;AAClE,QAAI,CAAC,gBAAgB,OAAO,YAAY,iBAAiB,sBAAsB,CAAC,EAAG,QAAO;AAC1F,QAAI,CAAC,gBAAgB,OAAO,MAAM,iBAAiB,iBAAiB,CAAC,EAAG,QAAO;AAC/E,WAAO,EAAE,MAAM,OAAO,MAAM,YAAY,OAAO,YAAY,MAAM,OAAO,KAAK;AAAA,EAC/E;AAEA,SAAO;AACT;AAGO,SAAS,qBAAqB,OAA+B;AAClE,MAAI,CAAC,gBAAgB,OAAO,iBAAiB,eAAe,CAAC,EAAG,QAAO;AACvE,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,KAAK;AACzB,QAAI,IAAI,aAAa,WAAW,IAAI,aAAa,SAAU,QAAO;AAClE,QAAI,IAAI,YAAY,IAAI,SAAU,QAAO;AACzC,UAAM,YAAY,IAAI;AACtB,WAAO,UAAU,UAAU,iBAAiB,gBAAgB,YAAY;AAAA,EAC1E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,qBAAqB,OAAgB,mBAAqC;AACxF,MAAI,CAAC,gBAAgB,OAAO,iBAAiB,eAAe,CAAC,EAAG,QAAO;AACvE,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,KAAK;AACzB,QACE,IAAI,aAAa,UACjB,IAAI,aAAa,eACjB,IAAI,SAAS,MACb,IAAI,aAAa,MACjB,IAAI,aAAa,IACjB;AACA,aAAO;AAAA,IACT;AACA,QAAI,CAAC,kBAAmB,QAAO;AAC/B,UAAM,2BAA2B,IAAI,IAAI,iBAAiB;AAC1D,WAAO,IAAI,WAAW,yBAAyB;AAAA,EACjD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/filesystem/internal/parent-chain.ts"],"sourcesContent":["/**\n * Ancestor enumeration shared by every filesystem provider.\n *\n * Two variants exist because backends genuinely disagree about whether the\n * workspace root is an *entry*, and that difference is real rather than\n * accidental:\n *\n * - IndexedDB and memory store the root as a first-class directory record\n * (IndexedDB initialization fails with `corrupt` if the root record is\n * missing). Their parent walks must visit the root so it is version-bumped\n * and kind-checked like any other directory.\n * - Native (File System Access) has no root record — the root *is* the\n * directory handle the user granted, always present and never created.\n * Visiting it would be a guaranteed no-op.\n *\n * Both orderings are root-most first, so callers that create missing parents\n * create them top-down.\n */\n\nimport { workspacePathDirname, type WorkspacePath } from '../workspace-path.js';\n\n/**\n * Strict ancestors of `path`, excluding the workspace root.\n *\n * Use when the root is implicit and always exists.\n */\nexport function parentChain(path: WorkspacePath): WorkspacePath[] {\n const parents: WorkspacePath[] = [];\n let current = workspacePathDirname(path);\n while (current) {\n parents.unshift(current);\n current = workspacePathDirname(current);\n }\n return parents;\n}\n\n/**\n * Strict ancestors of `path`, including the workspace root as the first entry.\n *\n * Use when the root is a stored entry that participates in kind checks and\n * version bumps.\n */\nexport function parentChainWithRoot(path: WorkspacePath): WorkspacePath[] {\n const parents: WorkspacePath[] = [];\n let current = workspacePathDirname(path);\n while (true) {\n parents.unshift(current);\n if (!current) return parents;\n current = workspacePathDirname(current);\n }\n}\n"],"mappings":";;;;;AA0BO,SAAS,YAAY,MAAsC;AAChE,QAAM,UAA2B,CAAC;AAClC,MAAI,UAAU,qBAAqB,IAAI;AACvC,SAAO,SAAS;AACd,YAAQ,QAAQ,OAAO;AACvB,cAAU,qBAAqB,OAAO;AAAA,EACxC;AACA,SAAO;AACT;AAQO,SAAS,oBAAoB,MAAsC;AACxE,QAAM,UAA2B,CAAC;AAClC,MAAI,UAAU,qBAAqB,IAAI;AACvC,SAAO,MAAM;AACX,YAAQ,QAAQ,OAAO;AACvB,QAAI,CAAC,QAAS,QAAO;AACrB,cAAU,qBAAqB,OAAO;AAAA,EACxC;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/filesystem/fs-error.ts","../src/filesystem/workspace-path.ts"],"sourcesContent":["/** Stable error codes shared by every filesystem transport and backend. */\nexport type FsErrorCode =\n | 'invalid-path'\n | 'path-escape'\n | 'not-found'\n | 'already-exists'\n | 'type-mismatch'\n | 'not-empty'\n | 'permission-denied'\n | 'conflict'\n | 'corrupt'\n | 'quota-exceeded'\n | 'not-supported'\n | 'busy'\n | 'disposed'\n | 'closed'\n | 'aborted'\n | 'io';\n\nexport type FsOperation =\n | 'parse-path'\n | 'read'\n | 'stat'\n | 'list'\n | 'write'\n | 'create-directory'\n | 'remove'\n | 'move'\n | 'snapshot'\n | 'watch'\n | 'dispose';\n\n/** Plain-data form safe to send over IPC, postMessage, or JSON. */\nexport interface SerializedFsError {\n readonly name: 'FsError';\n readonly code: FsErrorCode;\n readonly message: string;\n readonly operation: FsOperation | null;\n readonly path: string | null;\n readonly destinationPath: string | null;\n readonly retryable: boolean;\n}\n\nexport interface FsErrorContext {\n readonly operation?: FsOperation;\n readonly path?: string;\n readonly destinationPath?: string;\n readonly retryable?: boolean;\n readonly defaultCode?: FsErrorCode;\n}\n\nconst FS_ERROR_CODES: ReadonlySet<string> = new Set<FsErrorCode>([\n 'invalid-path',\n 'path-escape',\n 'not-found',\n 'already-exists',\n 'type-mismatch',\n 'not-empty',\n 'permission-denied',\n 'conflict',\n 'corrupt',\n 'quota-exceeded',\n 'not-supported',\n 'busy',\n 'disposed',\n 'closed',\n 'aborted',\n 'io',\n]);\n\nconst FS_OPERATIONS: ReadonlySet<string> = new Set<FsOperation>([\n 'parse-path',\n 'read',\n 'stat',\n 'list',\n 'write',\n 'create-directory',\n 'remove',\n 'move',\n 'snapshot',\n 'watch',\n 'dispose',\n]);\n\n/** Error with a stable code and a deliberately serializable public shape. */\nexport class FsError extends Error {\n public readonly code: FsErrorCode;\n public readonly operation: FsOperation | null;\n public readonly path: string | null;\n public readonly destinationPath: string | null;\n public readonly retryable: boolean;\n\n public constructor(code: FsErrorCode, message: string, context: FsErrorContext = {}) {\n super(message);\n this.name = 'FsError';\n this.code = code;\n this.operation = context.operation ?? null;\n this.path = context.path ?? null;\n this.destinationPath = context.destinationPath ?? null;\n this.retryable = context.retryable ?? (code === 'io' || code === 'aborted' || code === 'busy');\n }\n\n public toJSON(): SerializedFsError {\n return {\n name: 'FsError',\n code: this.code,\n message: this.message,\n operation: this.operation,\n path: this.path,\n destinationPath: this.destinationPath,\n retryable: this.retryable,\n };\n }\n}\n\nexport function mapDomExceptionToFsErrorCode(name: string): FsErrorCode {\n switch (name) {\n case 'NotFoundError':\n return 'not-found';\n case 'ConstraintError':\n return 'already-exists';\n case 'TypeMismatchError':\n return 'type-mismatch';\n case 'InvalidModificationError':\n return 'not-empty';\n case 'NotAllowedError':\n case 'SecurityError':\n case 'NoModificationAllowedError':\n return 'permission-denied';\n case 'QuotaExceededError':\n return 'quota-exceeded';\n case 'NotSupportedError':\n return 'not-supported';\n case 'InvalidStateError':\n return 'disposed';\n case 'AbortError':\n return 'aborted';\n default:\n return 'io';\n }\n}\n\nexport function mapNodeErrorCodeToFsErrorCode(code: string): FsErrorCode {\n switch (code) {\n case 'ENOENT':\n return 'not-found';\n case 'ENOTDIR':\n return 'type-mismatch';\n case 'EEXIST':\n return 'already-exists';\n case 'EISDIR':\n return 'type-mismatch';\n case 'ENOTEMPTY':\n return 'not-empty';\n case 'EACCES':\n case 'EPERM':\n return 'permission-denied';\n case 'ENOSPC':\n case 'EDQUOT':\n return 'quota-exceeded';\n case 'EBUSY':\n return 'busy';\n case 'EXDEV':\n case 'ENOTSUP':\n case 'EOPNOTSUPP':\n return 'not-supported';\n default:\n return 'io';\n }\n}\n\nexport function isSerializedFsError(value: unknown): value is SerializedFsError {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Partial<Record<keyof SerializedFsError, unknown>>;\n return (\n candidate.name === 'FsError' &&\n typeof candidate.code === 'string' &&\n FS_ERROR_CODES.has(candidate.code) &&\n typeof candidate.message === 'string' &&\n (candidate.operation === null ||\n (typeof candidate.operation === 'string' && FS_OPERATIONS.has(candidate.operation))) &&\n (candidate.path === null || typeof candidate.path === 'string') &&\n (candidate.destinationPath === null || typeof candidate.destinationPath === 'string') &&\n typeof candidate.retryable === 'boolean'\n );\n}\n\nexport function deserializeFsError(value: SerializedFsError): FsError {\n return new FsError(value.code, value.message, {\n operation: value.operation ?? undefined,\n path: value.path ?? undefined,\n destinationPath: value.destinationPath ?? undefined,\n retryable: value.retryable,\n });\n}\n\nexport function fsErrorFromUnknown(error: unknown, context: FsErrorContext = {}): FsError {\n if (error instanceof FsError) return error;\n if (isSerializedFsError(error)) return deserializeFsError(error);\n\n const name = getErrorStringField(error, 'name');\n const nodeCode = getErrorStringField(error, 'code');\n const message = getErrorStringField(error, 'message') ?? String(error);\n const code =\n context.defaultCode ??\n (nodeCode\n ? mapNodeErrorCodeToFsErrorCode(nodeCode)\n : name\n ? mapDomExceptionToFsErrorCode(name)\n : 'io');\n return new FsError(code, message, context);\n}\n\nexport function serializeFsError(error: unknown, context: FsErrorContext = {}): SerializedFsError {\n return fsErrorFromUnknown(error, context).toJSON();\n}\n\n/**\n * True when a storage backend rejected an operation because the origin or\n * device is out of space. Covers typed `FsError`s (`'quota-exceeded'`, from\n * DOMException `QuotaExceededError` or Node `ENOSPC`/`EDQUOT`), serialized\n * transport copies, and raw DOMExceptions from backends that do not wrap\n * their errors (e.g. the File System Access API) — including cross-realm\n * instances where `instanceof DOMException` would fail.\n */\nexport function isQuotaExceededError(error: unknown): boolean {\n if (error instanceof FsError) return error.code === 'quota-exceeded';\n if (isSerializedFsError(error)) return error.code === 'quota-exceeded';\n return getErrorStringField(error, 'name') === 'QuotaExceededError';\n}\n\nfunction getErrorStringField(error: unknown, key: 'name' | 'message' | 'code'): string | null {\n if (!error || typeof error !== 'object') return null;\n const value = (error as Record<string, unknown>)[key];\n return typeof value === 'string' ? value : null;\n}\n","import { FsError } from './fs-error.js';\n\ndeclare const workspacePathBrand: unique symbol;\n\n/** Canonical, root-relative, forward-slash path within one workspace. */\nexport type WorkspacePath = string & { readonly [workspacePathBrand]: 'WorkspacePath' };\n\nexport const WORKSPACE_ROOT = '' as WorkspacePath;\n\n/**\n * Parse a legacy/user path into its one canonical workspace representation.\n * Leading/trailing and repeated separators are normalized; traversal and NUL\n * segments are rejected rather than resolved.\n */\nexport function parseWorkspacePath(input: string): WorkspacePath {\n if (\n [...input].some((character) => {\n const codePoint = character.codePointAt(0) ?? 0;\n return codePoint <= 0x1f || codePoint === 0x7f;\n })\n ) {\n throw invalidPath(input, 'Workspace paths must not contain NUL or other control characters.');\n }\n if (/^[a-zA-Z]:/u.test(input)) {\n throw new FsError('path-escape', 'Workspace paths must not be OS drive-qualified paths.', {\n operation: 'parse-path',\n path: input,\n });\n }\n\n const normalized = input\n .replace(/\\\\/g, '/')\n .replace(/\\/+/g, '/')\n .replace(/^\\/|\\/$/g, '');\n if (!normalized) return WORKSPACE_ROOT;\n\n const segments = normalized.split('/');\n if (segments.some((segment) => segment === '.' || segment === '..')) {\n throw new FsError('path-escape', 'Workspace paths must not contain \".\" or \"..\" segments.', {\n operation: 'parse-path',\n path: input,\n });\n }\n return normalized as WorkspacePath;\n}\n\nexport function tryParseWorkspacePath(input: unknown): WorkspacePath | null {\n if (typeof input !== 'string') return null;\n try {\n return parseWorkspacePath(input);\n } catch {\n return null;\n }\n}\n\n/** Convert a canonical path to the slash-prefixed form accepted by v1 providers. */\nexport function workspacePathToLegacy(path: WorkspacePath): string {\n return path ? `/${path}` : '/';\n}\n\nexport function workspacePathJoin(parent: WorkspacePath, child: string): WorkspacePath {\n const childPath = parseWorkspacePath(child);\n if (!parent) return childPath;\n if (!childPath) return parent;\n return parseWorkspacePath(`${parent}/${childPath}`);\n}\n\nexport function workspacePathDirname(path: WorkspacePath): WorkspacePath {\n if (!path) return WORKSPACE_ROOT;\n const slash = path.lastIndexOf('/');\n return slash < 0 ? WORKSPACE_ROOT : (path.slice(0, slash) as WorkspacePath);\n}\n\nexport function workspacePathBasename(path: WorkspacePath): string {\n if (!path) return '';\n const slash = path.lastIndexOf('/');\n return slash < 0 ? path : path.slice(slash + 1);\n}\n\nexport function workspacePathContains(\n parent: WorkspacePath,\n candidate: WorkspacePath,\n inclusive = true,\n): boolean {\n if (parent === candidate) return inclusive;\n if (!parent) return candidate !== WORKSPACE_ROOT;\n return candidate.startsWith(`${parent}/`);\n}\n\nfunction invalidPath(path: string, message: string): FsError {\n return new FsError('invalid-path', message, { operation: 'parse-path', path });\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/filesystem/move-error.ts"],"sourcesContent":["import { getFileSystemProviderV2, type FileSystemProvider } from './types.js';\nimport { parseWorkspacePath } from './workspace-path.js';\n\nexport type FileSystemPathPresence = 'present' | 'missing' | 'unknown';\n\nexport interface FileSystemPartialMoveState {\n readonly source: FileSystemPathPresence;\n readonly destination: FileSystemPathPresence;\n readonly companionSource: FileSystemPathPresence;\n readonly companionDestination: FileSystemPathPresence;\n}\n\nexport type FileSystemMoveLocation = 'source' | 'destination' | 'both' | 'neither' | 'unknown';\n\nexport interface FileSystemEntryMoveState {\n readonly source: FileSystemPathPresence;\n readonly destination: FileSystemPathPresence;\n}\n\nfunction locationFromPresence(\n source: FileSystemPathPresence,\n destination: FileSystemPathPresence,\n): FileSystemMoveLocation {\n if (source === 'unknown' || destination === 'unknown') return 'unknown';\n if (source === 'present' && destination === 'present') return 'both';\n if (source === 'present') return 'source';\n if (destination === 'present') return 'destination';\n return 'neither';\n}\n\n/**\n * One provider move failed without restoring a provable source-only state.\n * This is backend-neutral: callers must use `location`, never infer from the\n * original exception or a provider's advertised move atomicity.\n */\nexport class FileSystemMoveRecoveryError extends Error {\n public readonly code = 'partial-move' as const;\n\n public constructor(\n public readonly sourcePath: string,\n public readonly destinationPath: string,\n public readonly state: FileSystemEntryMoveState,\n public readonly moveError: unknown,\n ) {\n super(\n `The move did not restore one authoritative path. Entry location: ${locationFromPresence(\n state.source,\n state.destination,\n )}.`,\n );\n this.name = 'FileSystemMoveRecoveryError';\n }\n\n public get location(): FileSystemMoveLocation {\n return locationFromPresence(this.state.source, this.state.destination);\n }\n\n /** Alias used by DocumentSession's document-retarget recovery protocol. */\n public get documentLocation(): FileSystemMoveLocation {\n return this.location;\n }\n}\n\n/**\n * A compound document/companion move failed and the document rollback also\n * failed. Callers must use the recorded post-state instead of assuming the\n * original path is still authoritative.\n */\nexport class FileSystemPartialMoveError extends Error {\n readonly code = 'partial-move' as const;\n\n constructor(\n readonly sourcePath: string,\n readonly destinationPath: string,\n readonly companionSourcePath: string,\n readonly companionDestinationPath: string,\n readonly state: FileSystemPartialMoveState,\n readonly moveError: unknown,\n readonly rollbackError: unknown,\n ) {\n super(\n `The document move could not be rolled back. Document location: ${locationFromPresence(\n state.source,\n state.destination,\n )}; companion location: ${locationFromPresence(\n state.companionSource,\n state.companionDestination,\n )}.`,\n );\n this.name = 'FileSystemPartialMoveError';\n }\n\n get documentLocation(): FileSystemMoveLocation {\n return locationFromPresence(this.state.source, this.state.destination);\n }\n\n get companionLocation(): FileSystemMoveLocation {\n return locationFromPresence(this.state.companionSource, this.state.companionDestination);\n }\n}\n\nexport function isFileSystemPartialMoveError(error: unknown): error is FileSystemPartialMoveError {\n return error instanceof FileSystemPartialMoveError;\n}\n\nexport function isFileSystemMoveRecoveryError(\n error: unknown,\n): error is FileSystemMoveRecoveryError {\n return error instanceof FileSystemMoveRecoveryError;\n}\n\nexport function isFileSystemMoveStateError(\n error: unknown,\n): error is FileSystemMoveRecoveryError | FileSystemPartialMoveError {\n return isFileSystemMoveRecoveryError(error) || isFileSystemPartialMoveError(error);\n}\n\nasync function probePath(\n provider: FileSystemProvider,\n path: string,\n): Promise<FileSystemPathPresence> {\n try {\n const providerV2 = getFileSystemProviderV2(provider);\n const present = providerV2\n ? (await providerV2.stat(parseWorkspacePath(path))) !== null\n : await provider.exists(path);\n return present ? 'present' : 'missing';\n } catch {\n return 'unknown';\n }\n}\n\nexport async function describePartialMove(\n provider: FileSystemProvider,\n sourcePath: string,\n destinationPath: string,\n companionSourcePath: string,\n companionDestinationPath: string,\n): Promise<FileSystemPartialMoveState> {\n const [source, destination, companionSource, companionDestination] = await Promise.all([\n probePath(provider, sourcePath),\n probePath(provider, destinationPath),\n probePath(provider, companionSourcePath),\n probePath(provider, companionDestinationPath),\n ]);\n return { source, destination, companionSource, companionDestination };\n}\n\nexport async function describeEntryMove(\n provider: FileSystemProvider,\n sourcePath: string,\n destinationPath: string,\n): Promise<FileSystemEntryMoveState> {\n const [source, destination] = await Promise.all([\n probePath(provider, sourcePath),\n probePath(provider, destinationPath),\n ]);\n return { source, destination };\n}\n"],"mappings":";;;;;;;;AAmBA,SAAS,qBACP,QACA,aACwB;AACxB,MAAI,WAAW,aAAa,gBAAgB,UAAW,QAAO;AAC9D,MAAI,WAAW,aAAa,gBAAgB,UAAW,QAAO;AAC9D,MAAI,WAAW,UAAW,QAAO;AACjC,MAAI,gBAAgB,UAAW,QAAO;AACtC,SAAO;AACT;AAOO,IAAM,8BAAN,cAA0C,MAAM;AAAA,EAG9C,YACW,YACA,iBACA,OACA,WAChB;AACA;AAAA,MACE,oEAAoE;AAAA,QAClE,MAAM;AAAA,QACN,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAVgB;AACA;AACA;AACA;AANlB,SAAgB,OAAO;AAcrB,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,IAAW,WAAmC;AAC5C,WAAO,qBAAqB,KAAK,MAAM,QAAQ,KAAK,MAAM,WAAW;AAAA,EACvE;AAAA;AAAA,EAGA,IAAW,mBAA2C;AACpD,WAAO,KAAK;AAAA,EACd;AACF;AAOO,IAAM,6BAAN,cAAyC,MAAM;AAAA,EAGpD,YACW,YACA,iBACA,qBACA,0BACA,OACA,WACA,eACT;AACA;AAAA,MACE,kEAAkE;AAAA,QAChE,MAAM;AAAA,QACN,MAAM;AAAA,MACR,CAAC,yBAAyB;AAAA,QACxB,MAAM;AAAA,QACN,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAhBS;AACA;AACA;AACA;AACA;AACA;AACA;AATX,SAAS,OAAO;AAoBd,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,IAAI,mBAA2C;AAC7C,WAAO,qBAAqB,KAAK,MAAM,QAAQ,KAAK,MAAM,WAAW;AAAA,EACvE;AAAA,EAEA,IAAI,oBAA4C;AAC9C,WAAO,qBAAqB,KAAK,MAAM,iBAAiB,KAAK,MAAM,oBAAoB;AAAA,EACzF;AACF;AAEO,SAAS,6BAA6B,OAAqD;AAChG,SAAO,iBAAiB;AAC1B;AAEO,SAAS,8BACd,OACsC;AACtC,SAAO,iBAAiB;AAC1B;AAEO,SAAS,2BACd,OACmE;AACnE,SAAO,8BAA8B,KAAK,KAAK,6BAA6B,KAAK;AACnF;AAEA,eAAe,UACb,UACA,MACiC;AACjC,MAAI;AACF,UAAM,aAAa,wBAAwB,QAAQ;AACnD,UAAM,UAAU,aACX,MAAM,WAAW,KAAK,mBAAmB,IAAI,CAAC,MAAO,OACtD,MAAM,SAAS,OAAO,IAAI;AAC9B,WAAO,UAAU,YAAY;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,oBACpB,UACA,YACA,iBACA,qBACA,0BACqC;AACrC,QAAM,CAAC,QAAQ,aAAa,iBAAiB,oBAAoB,IAAI,MAAM,QAAQ,IAAI;AAAA,IACrF,UAAU,UAAU,UAAU;AAAA,IAC9B,UAAU,UAAU,eAAe;AAAA,IACnC,UAAU,UAAU,mBAAmB;AAAA,IACvC,UAAU,UAAU,wBAAwB;AAAA,EAC9C,CAAC;AACD,SAAO,EAAE,QAAQ,aAAa,iBAAiB,qBAAqB;AACtE;AAEA,eAAsB,kBACpB,UACA,YACA,iBACmC;AACnC,QAAM,CAAC,QAAQ,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC9C,UAAU,UAAU,UAAU;AAAA,IAC9B,UAAU,UAAU,eAAe;AAAA,EACrC,CAAC;AACD,SAAO,EAAE,QAAQ,YAAY;AAC/B;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/workspace/workspace-schema.ts","../src/workspace/workspace-manager.ts","../src/workspace/reconcile-electron.ts"],"sourcesContent":["import type { WorkspaceDescriptor } from './types.js';\n\nconst MAX_IDENTIFIER_CHARACTERS = 256;\nconst MAX_LABEL_CHARACTERS = 1_024;\nconst MAX_PATH_CHARACTERS = 4_096;\nconst MAX_WORKSPACES = 10_000;\n\n/** Parse the complete persisted workspace list before it reaches startup code. */\nexport function parsePersistedWorkspaceList(value: unknown): WorkspaceDescriptor[] {\n if (!Array.isArray(value)) throw new Error('Persisted workspaces must be an array.');\n if (value.length > MAX_WORKSPACES) throw new Error('Persisted workspace list is too large.');\n const parsed = value.map(parsePersistedWorkspace);\n if (new Set(parsed.map((workspace) => workspace.id)).size !== parsed.length) {\n throw new Error('Persisted workspace IDs must be unique.');\n }\n return parsed;\n}\n\nfunction parsePersistedWorkspace(value: unknown, index: number): WorkspaceDescriptor {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error(`Persisted workspace ${index} must be an object.`);\n }\n const record = value as Record<string, unknown>;\n const allowed = ['id', 'name', 'type', 'lastOpened', 'rootPath', 'versioningOverride'];\n const unknownKey = Object.keys(record).find((key) => !allowed.includes(key));\n if (unknownKey) throw new Error(`Persisted workspace ${index} has unknown field ${unknownKey}.`);\n\n const id = boundedString(record.id, MAX_IDENTIFIER_CHARACTERS, `workspace ${index} id`);\n const name = boundedString(record.name, MAX_LABEL_CHARACTERS, `workspace ${index} name`);\n if (\n record.type !== 'indexeddb' &&\n record.type !== 'native' &&\n record.type !== 'electron-native'\n ) {\n throw new Error(`Persisted workspace ${index} has an invalid provider type.`);\n }\n const lastOpened = boundedString(\n record.lastOpened,\n MAX_IDENTIFIER_CHARACTERS,\n `workspace ${index} lastOpened`,\n );\n if (\n !Number.isFinite(Date.parse(lastOpened)) ||\n new Date(lastOpened).toISOString() !== lastOpened\n ) {\n throw new Error(`Persisted workspace ${index} has an invalid lastOpened timestamp.`);\n }\n\n const descriptor: WorkspaceDescriptor = { id, name, type: record.type, lastOpened };\n if (record.rootPath !== undefined) {\n descriptor.rootPath = boundedString(\n record.rootPath,\n MAX_PATH_CHARACTERS,\n `workspace ${index} rootPath`,\n );\n }\n if (record.type === 'electron-native' && descriptor.rootPath === undefined) {\n throw new Error(`Persisted Electron workspace ${index} is missing rootPath.`);\n }\n if (record.type !== 'electron-native' && descriptor.rootPath !== undefined) {\n throw new Error(`Persisted workspace ${index} must not contain rootPath.`);\n }\n if (record.versioningOverride !== undefined) {\n if (\n record.versioningOverride !== 'inherit' &&\n record.versioningOverride !== 'on' &&\n record.versioningOverride !== 'off'\n ) {\n throw new Error(`Persisted workspace ${index} has an invalid versioning override.`);\n }\n descriptor.versioningOverride = record.versioningOverride;\n }\n return descriptor;\n}\n\nfunction boundedString(value: unknown, maximum: number, label: string): string {\n if (\n typeof value !== 'string' ||\n value.length < 1 ||\n value.length > maximum ||\n value.includes('\\0')\n ) {\n throw new Error(`Persisted ${label} must be a bounded non-empty string.`);\n }\n return value;\n}\n","/**\n * WorkspaceManager — manages the list of known workspaces in IndexedDB\n * and provides helpers for creating / switching / removing them.\n *\n * The actual FileSystemProvider instances are created by the caller\n * (since native providers need a DirectoryHandle from user interaction).\n */\n\nimport {\n RawIndexedDBFileSystemStore,\n type IndexedDBFileSystemStore,\n type IndexedDBFileSystemTransaction,\n} from '../filesystem/indexeddb-store.js';\nimport { getFileSystemProviderV2, type FileSystemProvider } from '../filesystem/types.js';\nimport type { WorkspaceDescriptor } from './types.js';\nimport { parsePersistedWorkspaceList } from './workspace-schema.js';\n\nconst DB_NAME = 'docblocks-workspaces';\nconst STORE_NAME = 'workspaces';\nconst LIST_KEY = 'workspace-list';\nconst DEFAULT_WORKSPACE_ID = 'default';\n\n/**\n * The registry deliberately uses the error-propagating store rather than the\n * Squisq LocalForage adapter: that adapter converts a failed read into `null`,\n * which this module cannot tell apart from \"no workspaces yet\". A single\n * transient IndexedDB failure therefore read as an empty registry, and the next\n * mutation persisted that emptiness over every workspace the user had.\n *\n * The database and object store names are unchanged, and the raw store uses the\n * same out-of-line keys LocalForage did, so existing registries load as-is.\n */\nlet registryStore: IndexedDBFileSystemStore | null = null;\n\nfunction store(): IndexedDBFileSystemStore {\n registryStore ??= new RawIndexedDBFileSystemStore(DB_NAME, STORE_NAME);\n return registryStore;\n}\n\n/**\n * Replace the registry's storage backend, or restore the default with `null`.\n * Mirrors the `store` option the IndexedDB providers accept; tests inject an\n * in-memory store through it and production code never calls it.\n */\nexport function setWorkspaceRegistryStore(next: IndexedDBFileSystemStore | null): void {\n registryStore = next;\n}\n\n/**\n * Session-only transient workspaces (a loose file/`.dbk` opened from the OS,\n * or a document copied from a shared URL). Kept purely in memory — never\n * written to the persisted list — with their pre-built provider registered\n * alongside the descriptor. Lost on reload, which is the intended\n * \"transient for the app session\" behaviour.\n */\ninterface TransientEntry {\n descriptor: WorkspaceDescriptor;\n provider: FileSystemProvider;\n}\nconst transientWorkspaces = new Map<string, TransientEntry>();\nlet persistedMutationTail: Promise<void> = Promise.resolve();\n\n/** Register a transient workspace and its (already-built) provider. */\nexport function registerTransientWorkspace(\n descriptor: WorkspaceDescriptor,\n provider: FileSystemProvider,\n): void {\n const previous = transientWorkspaces.get(descriptor.id);\n if (previous && previous.provider !== provider) {\n void getFileSystemProviderV2(previous.provider)?.dispose();\n }\n transientWorkspaces.set(descriptor.id, { descriptor, provider });\n}\n\n/** Look up a transient workspace (descriptor + provider) by id, if any. */\nexport function getTransientWorkspace(\n id: string,\n): { descriptor: WorkspaceDescriptor; provider: FileSystemProvider } | null {\n return transientWorkspaces.get(id) ?? null;\n}\n\n/** Forget a transient workspace (e.g. when it is closed). */\nexport async function unregisterTransientWorkspace(id: string): Promise<void> {\n const entry = transientWorkspaces.get(id);\n if (!entry) return;\n transientWorkspaces.delete(id);\n await getFileSystemProviderV2(entry.provider)?.dispose();\n}\n\n/**\n * The persisted (on-disk) workspace list only — excludes transient ones.\n *\n * An unreadable registry throws. Only a store that positively reports \"no such\n * key\" is an empty registry; anything else is an unknown registry, and callers\n * must not act as though the user has no workspaces.\n */\nasync function readPersistedIn(\n transaction: IndexedDBFileSystemTransaction,\n): Promise<WorkspaceDescriptor[]> {\n const list = await transaction.get<unknown>(LIST_KEY);\n return list == null ? [] : parsePersistedWorkspaceList(list);\n}\n\nasync function readPersisted(): Promise<WorkspaceDescriptor[]> {\n return store().transaction('readonly', readPersistedIn);\n}\n\nasync function listPersisted(): Promise<WorkspaceDescriptor[]> {\n await persistedMutationTail;\n return readPersisted();\n}\n\nfunction mutatePersisted(\n mutation: (workspaces: WorkspaceDescriptor[]) => WorkspaceDescriptor[],\n): Promise<void> {\n const operation = persistedMutationTail.then(async () => {\n // Read and write in one transaction. A failed read aborts the mutation\n // instead of rebuilding the registry from a phantom empty list, and no\n // concurrent writer (another tab) can land between the read and the write\n // only to be clobbered by this mutation's stale copy.\n await store().transaction('readwrite', async (transaction) => {\n const current = await readPersistedIn(transaction);\n const next = parsePersistedWorkspaceList(mutation([...current]));\n await transaction.put(LIST_KEY, next);\n });\n });\n persistedMutationTail = operation.then(\n () => undefined,\n () => undefined,\n );\n return operation;\n}\n\n/**\n * Get all known workspace descriptors — persisted plus any session-only\n * transient ones (so they show in the picker and resolve for the session).\n */\nexport async function listWorkspaces(): Promise<WorkspaceDescriptor[]> {\n const persisted = await listPersisted();\n const transient = [...transientWorkspaces.values()].map((t) => t.descriptor);\n return [...persisted, ...transient];\n}\n\n/**\n * Get a specific workspace descriptor by id (transient or persisted).\n */\nexport async function getWorkspace(id: string): Promise<WorkspaceDescriptor | null> {\n const transient = transientWorkspaces.get(id);\n if (transient) return transient.descriptor;\n const list = await listPersisted();\n return list.find((w) => w.id === id) ?? null;\n}\n\n/**\n * Add or update a workspace descriptor. If a workspace with the same id\n * already exists, it is replaced. Transient workspaces are never persisted.\n */\nexport async function saveWorkspace(workspace: WorkspaceDescriptor): Promise<void> {\n if (workspace.type === 'transient') return;\n const persisted = parsePersistedWorkspaceList([workspace])[0];\n await mutatePersisted((list) => {\n const idx = list.findIndex((candidate) => candidate.id === persisted.id);\n if (idx >= 0) list[idx] = persisted;\n else list.push(persisted);\n return list;\n });\n}\n\n/**\n * Remove a workspace descriptor by id (transient or persisted).\n */\nexport async function removeWorkspace(id: string): Promise<void> {\n if (transientWorkspaces.has(id)) {\n await unregisterTransientWorkspace(id);\n return;\n }\n await mutatePersisted((list) => list.filter((workspace) => workspace.id !== id));\n}\n\n/**\n * Touch the lastOpened timestamp on a workspace. For transient workspaces this\n * updates the in-memory descriptor only (nothing is persisted).\n */\nexport async function touchWorkspace(id: string): Promise<void> {\n const transient = transientWorkspaces.get(id);\n if (transient) {\n transient.descriptor.lastOpened = new Date().toISOString();\n return;\n }\n await mutatePersisted((list) => {\n const workspace = list.find((candidate) => candidate.id === id);\n if (workspace) workspace.lastOpened = new Date().toISOString();\n return list;\n });\n}\n\n/**\n * Ensure a default IndexedDB workspace exists. Called on app startup.\n * Returns the descriptor.\n */\nexport async function ensureDefaultWorkspace(): Promise<WorkspaceDescriptor> {\n const existing = await getWorkspace(DEFAULT_WORKSPACE_ID);\n if (existing) return existing;\n\n const descriptor: WorkspaceDescriptor = {\n id: DEFAULT_WORKSPACE_ID,\n name: 'My Documents',\n type: 'indexeddb',\n lastOpened: new Date().toISOString(),\n };\n await saveWorkspace(descriptor);\n return descriptor;\n}\n","import type { ElectronWorkspaceInfo, HostEnvironment } from '../host/types.js';\nimport type { WorkspaceDescriptor } from './types.js';\n\nexport interface ElectronWorkspaceReconciliation {\n /** Obsolete renderer descriptors to remove before attempting restoration. */\n readonly removeIds: readonly string[];\n /** Canonical main-authorized descriptors to insert or replace. */\n readonly upsert: readonly WorkspaceDescriptor[];\n /** Safe navigation migration for a local descriptor matched by root. */\n readonly idRemap: Readonly<Record<string, string>>;\n}\n\nfunction comparableRoot(rootPath: string, platform: HostEnvironment['platform']): string {\n const normalized = rootPath.replace(/\\\\/gu, '/').replace(/\\/+$/gu, '');\n return platform === 'win32' ? normalized.toLowerCase() : normalized;\n}\n\n/**\n * Reconcile renderer IndexedDB metadata with the main process authority list.\n * `hostWorkspaces` is authoritative: local Electron descriptors absent from\n * it cannot be opened safely and are removed. Root-path matching carries user\n * metadata across an id migration without treating that display path as\n * authority.\n */\nexport function reconcileElectronWorkspaceDescriptors(\n local: readonly WorkspaceDescriptor[],\n hostWorkspaces: readonly ElectronWorkspaceInfo[],\n platform: HostEnvironment['platform'],\n now: string,\n): ElectronWorkspaceReconciliation {\n const electron = local.filter((workspace) => workspace.type === 'electron-native');\n const matchedLocalIds = new Set<string>();\n const idRemap: Record<string, string> = {};\n const upsert = hostWorkspaces.map((hostWorkspace): WorkspaceDescriptor => {\n const root = comparableRoot(hostWorkspace.rootPath, platform);\n const candidates = electron.filter(\n (workspace) => workspace.rootPath && comparableRoot(workspace.rootPath, platform) === root,\n );\n const matching = candidates.sort((left, right) =>\n right.lastOpened.localeCompare(left.lastOpened),\n )[0];\n if (matching) {\n matchedLocalIds.add(matching.id);\n if (matching.id !== hostWorkspace.id) idRemap[matching.id] = hostWorkspace.id;\n }\n return {\n id: hostWorkspace.id,\n name: matching?.name ?? hostWorkspace.name,\n type: 'electron-native',\n rootPath: hostWorkspace.rootPath,\n lastOpened: matching?.lastOpened ?? now,\n ...(matching?.versioningOverride ? { versioningOverride: matching.versioningOverride } : {}),\n };\n });\n const canonicalIds = new Set(hostWorkspaces.map((workspace) => workspace.id));\n const removeIds = electron\n .filter(\n (workspace) =>\n !canonicalIds.has(workspace.id) ||\n (matchedLocalIds.has(workspace.id) &&\n !upsert.some((candidate) => candidate.id === workspace.id)),\n )\n .map((workspace) => workspace.id);\n return { removeIds, upsert, idRemap };\n}\n"],"mappings":";;;;;;;;AAEA,IAAM,4BAA4B;AAClC,IAAM,uBAAuB;AAC7B,IAAM,sBAAsB;AAC5B,IAAM,iBAAiB;AAGhB,SAAS,4BAA4B,OAAuC;AACjF,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,OAAM,IAAI,MAAM,wCAAwC;AACnF,MAAI,MAAM,SAAS,eAAgB,OAAM,IAAI,MAAM,wCAAwC;AAC3F,QAAM,SAAS,MAAM,IAAI,uBAAuB;AAChD,MAAI,IAAI,IAAI,OAAO,IAAI,CAAC,cAAc,UAAU,EAAE,CAAC,EAAE,SAAS,OAAO,QAAQ;AAC3E,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AACA,SAAO;AACT;AAEA,SAAS,wBAAwB,OAAgB,OAAoC;AACnF,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,UAAM,IAAI,MAAM,uBAAuB,KAAK,qBAAqB;AAAA,EACnE;AACA,QAAM,SAAS;AACf,QAAM,UAAU,CAAC,MAAM,QAAQ,QAAQ,cAAc,YAAY,oBAAoB;AACrF,QAAM,aAAa,OAAO,KAAK,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,QAAQ,SAAS,GAAG,CAAC;AAC3E,MAAI,WAAY,OAAM,IAAI,MAAM,uBAAuB,KAAK,sBAAsB,UAAU,GAAG;AAE/F,QAAM,KAAK,cAAc,OAAO,IAAI,2BAA2B,aAAa,KAAK,KAAK;AACtF,QAAM,OAAO,cAAc,OAAO,MAAM,sBAAsB,aAAa,KAAK,OAAO;AACvF,MACE,OAAO,SAAS,eAChB,OAAO,SAAS,YAChB,OAAO,SAAS,mBAChB;AACA,UAAM,IAAI,MAAM,uBAAuB,KAAK,gCAAgC;AAAA,EAC9E;AACA,QAAM,aAAa;AAAA,IACjB,OAAO;AAAA,IACP;AAAA,IACA,aAAa,KAAK;AAAA,EACpB;AACA,MACE,CAAC,OAAO,SAAS,KAAK,MAAM,UAAU,CAAC,KACvC,IAAI,KAAK,UAAU,EAAE,YAAY,MAAM,YACvC;AACA,UAAM,IAAI,MAAM,uBAAuB,KAAK,uCAAuC;AAAA,EACrF;AAEA,QAAM,aAAkC,EAAE,IAAI,MAAM,MAAM,OAAO,MAAM,WAAW;AAClF,MAAI,OAAO,aAAa,QAAW;AACjC,eAAW,WAAW;AAAA,MACpB,OAAO;AAAA,MACP;AAAA,MACA,aAAa,KAAK;AAAA,IACpB;AAAA,EACF;AACA,MAAI,OAAO,SAAS,qBAAqB,WAAW,aAAa,QAAW;AAC1E,UAAM,IAAI,MAAM,gCAAgC,KAAK,uBAAuB;AAAA,EAC9E;AACA,MAAI,OAAO,SAAS,qBAAqB,WAAW,aAAa,QAAW;AAC1E,UAAM,IAAI,MAAM,uBAAuB,KAAK,6BAA6B;AAAA,EAC3E;AACA,MAAI,OAAO,uBAAuB,QAAW;AAC3C,QACE,OAAO,uBAAuB,aAC9B,OAAO,uBAAuB,QAC9B,OAAO,uBAAuB,OAC9B;AACA,YAAM,IAAI,MAAM,uBAAuB,KAAK,sCAAsC;AAAA,IACpF;AACA,eAAW,qBAAqB,OAAO;AAAA,EACzC;AACA,SAAO;AACT;AAEA,SAAS,cAAc,OAAgB,SAAiB,OAAuB;AAC7E,MACE,OAAO,UAAU,YACjB,MAAM,SAAS,KACf,MAAM,SAAS,WACf,MAAM,SAAS,IAAI,GACnB;AACA,UAAM,IAAI,MAAM,aAAa,KAAK,sCAAsC;AAAA,EAC1E;AACA,SAAO;AACT;;;ACpEA,IAAM,UAAU;AAChB,IAAM,aAAa;AACnB,IAAM,WAAW;AACjB,IAAM,uBAAuB;AAY7B,IAAI,gBAAiD;AAErD,SAAS,QAAkC;AACzC,oCAAkB,IAAI,4BAA4B,SAAS,UAAU;AACrE,SAAO;AACT;AAsBA,IAAM,sBAAsB,oBAAI,IAA4B;AAC5D,IAAI,wBAAuC,QAAQ,QAAQ;AAGpD,SAAS,2BACd,YACA,UACM;AACN,QAAM,WAAW,oBAAoB,IAAI,WAAW,EAAE;AACtD,MAAI,YAAY,SAAS,aAAa,UAAU;AAC9C,SAAK,wBAAwB,SAAS,QAAQ,GAAG,QAAQ;AAAA,EAC3D;AACA,sBAAoB,IAAI,WAAW,IAAI,EAAE,YAAY,SAAS,CAAC;AACjE;AAGO,SAAS,sBACd,IAC0E;AAC1E,SAAO,oBAAoB,IAAI,EAAE,KAAK;AACxC;AAGA,eAAsB,6BAA6B,IAA2B;AAC5E,QAAM,QAAQ,oBAAoB,IAAI,EAAE;AACxC,MAAI,CAAC,MAAO;AACZ,sBAAoB,OAAO,EAAE;AAC7B,QAAM,wBAAwB,MAAM,QAAQ,GAAG,QAAQ;AACzD;AASA,eAAe,gBACb,aACgC;AAChC,QAAM,OAAO,MAAM,YAAY,IAAa,QAAQ;AACpD,SAAO,QAAQ,OAAO,CAAC,IAAI,4BAA4B,IAAI;AAC7D;AAEA,eAAe,gBAAgD;AAC7D,SAAO,MAAM,EAAE,YAAY,YAAY,eAAe;AACxD;AAEA,eAAe,gBAAgD;AAC7D,QAAM;AACN,SAAO,cAAc;AACvB;AAEA,SAAS,gBACP,UACe;AACf,QAAM,YAAY,sBAAsB,KAAK,YAAY;AAKvD,UAAM,MAAM,EAAE,YAAY,aAAa,OAAO,gBAAgB;AAC5D,YAAM,UAAU,MAAM,gBAAgB,WAAW;AACjD,YAAM,OAAO,4BAA4B,SAAS,CAAC,GAAG,OAAO,CAAC,CAAC;AAC/D,YAAM,YAAY,IAAI,UAAU,IAAI;AAAA,IACtC,CAAC;AAAA,EACH,CAAC;AACD,0BAAwB,UAAU;AAAA,IAChC,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AACA,SAAO;AACT;AAMA,eAAsB,iBAAiD;AACrE,QAAM,YAAY,MAAM,cAAc;AACtC,QAAM,YAAY,CAAC,GAAG,oBAAoB,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU;AAC3E,SAAO,CAAC,GAAG,WAAW,GAAG,SAAS;AACpC;AAKA,eAAsB,aAAa,IAAiD;AAClF,QAAM,YAAY,oBAAoB,IAAI,EAAE;AAC5C,MAAI,UAAW,QAAO,UAAU;AAChC,QAAM,OAAO,MAAM,cAAc;AACjC,SAAO,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK;AAC1C;AAMA,eAAsB,cAAc,WAA+C;AACjF,MAAI,UAAU,SAAS,YAAa;AACpC,QAAM,YAAY,4BAA4B,CAAC,SAAS,CAAC,EAAE,CAAC;AAC5D,QAAM,gBAAgB,CAAC,SAAS;AAC9B,UAAM,MAAM,KAAK,UAAU,CAAC,cAAc,UAAU,OAAO,UAAU,EAAE;AACvE,QAAI,OAAO,EAAG,MAAK,GAAG,IAAI;AAAA,QACrB,MAAK,KAAK,SAAS;AACxB,WAAO;AAAA,EACT,CAAC;AACH;AAKA,eAAsB,gBAAgB,IAA2B;AAC/D,MAAI,oBAAoB,IAAI,EAAE,GAAG;AAC/B,UAAM,6BAA6B,EAAE;AACrC;AAAA,EACF;AACA,QAAM,gBAAgB,CAAC,SAAS,KAAK,OAAO,CAAC,cAAc,UAAU,OAAO,EAAE,CAAC;AACjF;AAMA,eAAsB,eAAe,IAA2B;AAC9D,QAAM,YAAY,oBAAoB,IAAI,EAAE;AAC5C,MAAI,WAAW;AACb,cAAU,WAAW,cAAa,oBAAI,KAAK,GAAE,YAAY;AACzD;AAAA,EACF;AACA,QAAM,gBAAgB,CAAC,SAAS;AAC9B,UAAM,YAAY,KAAK,KAAK,CAAC,cAAc,UAAU,OAAO,EAAE;AAC9D,QAAI,UAAW,WAAU,cAAa,oBAAI,KAAK,GAAE,YAAY;AAC7D,WAAO;AAAA,EACT,CAAC;AACH;AAMA,eAAsB,yBAAuD;AAC3E,QAAM,WAAW,MAAM,aAAa,oBAAoB;AACxD,MAAI,SAAU,QAAO;AAErB,QAAM,aAAkC;AAAA,IACtC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,EACrC;AACA,QAAM,cAAc,UAAU;AAC9B,SAAO;AACT;;;ACxMA,SAAS,eAAe,UAAkB,UAA+C;AACvF,QAAM,aAAa,SAAS,QAAQ,QAAQ,GAAG,EAAE,QAAQ,UAAU,EAAE;AACrE,SAAO,aAAa,UAAU,WAAW,YAAY,IAAI;AAC3D;AASO,SAAS,sCACd,OACA,gBACA,UACA,KACiC;AACjC,QAAM,WAAW,MAAM,OAAO,CAAC,cAAc,UAAU,SAAS,iBAAiB;AACjF,QAAM,kBAAkB,oBAAI,IAAY;AACxC,QAAM,UAAkC,CAAC;AACzC,QAAM,SAAS,eAAe,IAAI,CAAC,kBAAuC;AACxE,UAAM,OAAO,eAAe,cAAc,UAAU,QAAQ;AAC5D,UAAM,aAAa,SAAS;AAAA,MAC1B,CAAC,cAAc,UAAU,YAAY,eAAe,UAAU,UAAU,QAAQ,MAAM;AAAA,IACxF;AACA,UAAM,WAAW,WAAW;AAAA,MAAK,CAAC,MAAM,UACtC,MAAM,WAAW,cAAc,KAAK,UAAU;AAAA,IAChD,EAAE,CAAC;AACH,QAAI,UAAU;AACZ,sBAAgB,IAAI,SAAS,EAAE;AAC/B,UAAI,SAAS,OAAO,cAAc,GAAI,SAAQ,SAAS,EAAE,IAAI,cAAc;AAAA,IAC7E;AACA,WAAO;AAAA,MACL,IAAI,cAAc;AAAA,MAClB,MAAM,UAAU,QAAQ,cAAc;AAAA,MACtC,MAAM;AAAA,MACN,UAAU,cAAc;AAAA,MACxB,YAAY,UAAU,cAAc;AAAA,MACpC,GAAI,UAAU,qBAAqB,EAAE,oBAAoB,SAAS,mBAAmB,IAAI,CAAC;AAAA,IAC5F;AAAA,EACF,CAAC;AACD,QAAM,eAAe,IAAI,IAAI,eAAe,IAAI,CAAC,cAAc,UAAU,EAAE,CAAC;AAC5E,QAAM,YAAY,SACf;AAAA,IACC,CAAC,cACC,CAAC,aAAa,IAAI,UAAU,EAAE,KAC7B,gBAAgB,IAAI,UAAU,EAAE,KAC/B,CAAC,OAAO,KAAK,CAAC,cAAc,UAAU,OAAO,UAAU,EAAE;AAAA,EAC/D,EACC,IAAI,CAAC,cAAc,UAAU,EAAE;AAClC,SAAO,EAAE,WAAW,QAAQ,QAAQ;AACtC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/filesystem/v2.ts","../src/filesystem/internal/bytes.ts","../src/filesystem/internal/entry-order.ts"],"sourcesContent":["import type { FsError } from './fs-error.js';\nimport type { WorkspacePath } from './workspace-path.js';\n\n/**\n * Opaque provider-issued token representing one observed entry/tree state.\n * Tokens support equality only within the issuing provider; callers must not\n * parse them or assume a move or byte-identical rewrite changes the token.\n * A token need not be a content hash, but it must change whenever the issuing\n * provider observes a potentially different kind, payload, or descendant\n * state. Detection of non-cooperating external changes is bounded by the\n * backend's declared capabilities and metadata precision.\n */\ndeclare const fileSystemVersionBrand: unique symbol;\nexport type FileSystemVersion = string & {\n readonly [fileSystemVersionBrand]: 'FileSystemVersion';\n};\n\n/** Validate/rebrand a version received from a provider transport. */\nexport function parseFileSystemVersion(value: unknown): FileSystemVersion {\n if (typeof value !== 'string' || !value || value.length > 1024 || value.includes('\\0')) {\n throw new TypeError('Filesystem version tokens must be non-empty bounded strings.');\n }\n return value as FileSystemVersion;\n}\n\nexport function tryParseFileSystemVersion(value: unknown): FileSystemVersion | null {\n try {\n return parseFileSystemVersion(value);\n } catch {\n return null;\n }\n}\n\n/** `process` excludes this provider's local peers; `cross-context` also excludes cooperating tabs/processes. */\nexport type FileSystemAtomicity = 'none' | 'process' | 'cross-context';\nexport type FileSystemCaseSensitivity = 'sensitive' | 'insensitive' | 'platform';\nexport type FileSystemDurability = 'volatile' | 'best-effort' | 'durable';\nexport type FileSystemConditionalWriteStrength =\n | 'none'\n | 'process'\n | 'cross-context'\n | 'storage-atomic';\nexport type FileSystemSymlinkPolicy = 'unsupported' | 'reject' | 'follow-contained' | 'preserve';\n\n/** Serializable feature declaration; consumers never need provider instanceof checks. */\nexport interface FileSystemProviderCapabilities {\n /** Isolation of one successful file replacement from cooperating writers. */\n readonly writeAtomicity: FileSystemAtomicity;\n /** Isolation of one successful source-to-destination move. */\n readonly moveAtomicity: FileSystemAtomicity;\n /** Writers excluded while one complete snapshot is captured. */\n readonly snapshotAtomicity: FileSystemAtomicity;\n /** Strongest boundary at which expectedVersion is checked with mutation. */\n readonly conditionalWrite: FileSystemConditionalWriteStrength;\n readonly recursiveRemove: boolean;\n /** True only when watch has a real ready barrier and sequenced events. */\n readonly watch: boolean;\n readonly caseSensitivity: FileSystemCaseSensitivity;\n readonly symlinkPolicy: FileSystemSymlinkPolicy;\n readonly durability: FileSystemDurability;\n}\n\ninterface FileSystemEntrySnapshotBase {\n readonly path: WorkspacePath;\n readonly name: string;\n readonly version: FileSystemVersion;\n /** ISO-8601 timestamp supplied by the backend; versions remain authoritative. */\n readonly lastModified: string;\n}\n\nexport interface FileSystemFileSnapshot extends FileSystemEntrySnapshotBase {\n readonly kind: 'file';\n readonly size: number;\n}\n\nexport interface FileSystemDirectorySnapshot extends FileSystemEntrySnapshotBase {\n readonly kind: 'directory';\n readonly size: null;\n}\n\nexport type FileSystemEntrySnapshot = FileSystemFileSnapshot | FileSystemDirectorySnapshot;\n\n/** A read always couples owned bytes to the exact metadata version they represent. */\nexport interface FileSystemFileRead {\n readonly entry: FileSystemFileSnapshot;\n readonly data: ArrayBuffer;\n}\n\nexport interface FileSystemSnapshotFile extends FileSystemFileSnapshot {\n readonly data: ArrayBuffer;\n}\n\nexport type FileSystemSnapshotEntry = FileSystemSnapshotFile | FileSystemDirectorySnapshot;\n\n/**\n * Complete best-available tree traversal. `snapshotAtomicity` declares\n * whether concurrent local/cross-context mutations are excluded while it is\n * captured. Implementations must never expose aliased buffers.\n */\nexport interface FileSystemSnapshot {\n readonly version: FileSystemVersion;\n readonly entries: readonly FileSystemSnapshotEntry[];\n}\n\nexport type FileSystemWriteMode = 'upsert' | 'create' | 'replace';\n\nexport interface FileSystemWriteOptions {\n /** Defaults to upsert. */\n readonly mode?: FileSystemWriteMode;\n /** Defaults to false so structure creation is never implicit. */\n readonly createParents?: boolean;\n /** null means the entry must not exist; a token requires an exact match. */\n readonly expectedVersion?: FileSystemVersion | null;\n}\n\nexport type FileSystemCreateDirectoryMode = 'ensure' | 'create';\n\nexport interface FileSystemCreateDirectoryOptions {\n /** Defaults to ensure (idempotently return an existing directory). */\n readonly mode?: FileSystemCreateDirectoryMode;\n /** Defaults to false. */\n readonly createParents?: boolean;\n}\n\nexport interface FileSystemRemoveOptions {\n /** Defaults to false; non-empty directories otherwise fail with not-empty. */\n readonly recursive?: boolean;\n /** Defaults to error. */\n readonly missing?: 'ignore' | 'error';\n readonly expectedVersion?: FileSystemVersion;\n}\n\nexport interface FileSystemRemoveResult {\n readonly removed: boolean;\n /** Version of the complete tree after the removal attempt. */\n readonly version: FileSystemVersion;\n}\n\nexport interface FileSystemMoveOptions {\n /** Defaults to false. Destinations are never overwritten. */\n readonly createParents?: boolean;\n readonly expectedVersion?: FileSystemVersion;\n}\n\nexport type FileSystemWatchEventType = 'created' | 'modified' | 'removed' | 'moved' | 'overflow';\nexport type FileSystemWatchEventOrigin = 'local' | 'external' | 'unknown';\n\ninterface FileSystemWatchEventBase {\n readonly sequence: number;\n readonly origin: FileSystemWatchEventOrigin;\n readonly path: WorkspacePath;\n}\n\nexport interface FileSystemEntryWatchEvent extends FileSystemWatchEventBase {\n readonly type: Exclude<FileSystemWatchEventType, 'overflow'>;\n readonly kind: 'file' | 'directory';\n readonly destinationPath: WorkspacePath | null;\n readonly version: FileSystemVersion | null;\n readonly previousVersion: FileSystemVersion | null;\n}\n\nexport interface FileSystemOverflowWatchEvent extends FileSystemWatchEventBase {\n readonly type: 'overflow';\n readonly kind: null;\n readonly destinationPath: null;\n readonly version: null;\n readonly previousVersion: null;\n}\n\nexport type FileSystemWatchEvent = FileSystemEntryWatchEvent | FileSystemOverflowWatchEvent;\n\nexport type FileSystemWatchListener = (event: FileSystemWatchEvent) => void;\nexport type FileSystemWatchErrorListener = (error: FsError) => void;\n\nexport interface FileSystemWatchOptions {\n readonly onError?: FileSystemWatchErrorListener;\n}\n\nexport interface FileSystemWatchSubscription {\n readonly closed: boolean;\n /** Resolves after the backend watch is installed and no initial event can be missed. */\n readonly ready: Promise<void>;\n /** Idempotent and awaitable so resource release can join provider shutdown. */\n dispose(): Promise<void>;\n}\n\n/**\n * Correctness-first filesystem contract. Every logical path has exactly one\n * kind and one byte payload; conditional writes and lifecycle disposal are\n * mandatory rather than optional provider extensions.\n */\nexport interface FileSystemProviderV2 {\n readonly id: string;\n readonly label: string;\n readonly capabilities: FileSystemProviderCapabilities;\n\n /** Absence is represented only by null; permission/I/O/wrong-kind errors throw FsError. */\n stat(path: WorkspacePath): Promise<FileSystemEntrySnapshot | null>;\n /** Absence is represented only by null; a directory at path is type-mismatch. */\n readFile(path: WorkspacePath): Promise<FileSystemFileRead | null>;\n /** Missing paths and file paths throw; results are canonical and deterministic. */\n readDirectory(path: WorkspacePath): Promise<readonly FileSystemEntrySnapshot[]>;\n\n writeFile(\n path: WorkspacePath,\n data: ArrayBuffer | Uint8Array,\n options?: FileSystemWriteOptions,\n ): Promise<FileSystemFileSnapshot>;\n createDirectory(\n path: WorkspacePath,\n options?: FileSystemCreateDirectoryOptions,\n ): Promise<FileSystemDirectorySnapshot>;\n remove(path: WorkspacePath, options?: FileSystemRemoveOptions): Promise<FileSystemRemoveResult>;\n move(\n oldPath: WorkspacePath,\n newPath: WorkspacePath,\n options?: FileSystemMoveOptions,\n ): Promise<FileSystemEntrySnapshot>;\n\n snapshot(): Promise<FileSystemSnapshot>;\n /**\n * Always returns a disposable subscription. When `capabilities.watch` is\n * false, `ready` rejects with `FsError('not-supported')` and `onError`\n * observes the same failure; callers can therefore use one cleanup path.\n */\n watch(\n listener: FileSystemWatchListener,\n options?: FileSystemWatchOptions,\n ): FileSystemWatchSubscription;\n dispose(): Promise<void>;\n}\n","/**\n * Byte helpers shared by every filesystem provider.\n *\n * Providers own their stored buffers, so every value that crosses the provider\n * boundary is copied rather than aliased. Keeping one implementation here stops\n * the copy semantics (notably: honouring a view's byteOffset/byteLength instead\n * of its whole backing buffer) from drifting between backends.\n */\n\n/** Copy only the visible bytes of a buffer or view into a fresh ArrayBuffer. */\nexport function copyBytes(data: ArrayBuffer | Uint8Array): ArrayBuffer {\n const source = ArrayBuffer.isView(data)\n ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength)\n : new Uint8Array(data);\n const copy = new Uint8Array(source.byteLength);\n copy.set(source);\n return copy.buffer as ArrayBuffer;\n}\n\n/** Compare the visible bytes of two buffers or views for exact equality. */\nexport function bytesEqual(\n left: ArrayBuffer | Uint8Array,\n right: ArrayBuffer | Uint8Array,\n): boolean {\n const leftBytes = ArrayBuffer.isView(left)\n ? new Uint8Array(left.buffer, left.byteOffset, left.byteLength)\n : new Uint8Array(left);\n const rightBytes = ArrayBuffer.isView(right)\n ? new Uint8Array(right.buffer, right.byteOffset, right.byteLength)\n : new Uint8Array(right);\n if (leftBytes.byteLength !== rightBytes.byteLength) return false;\n return leftBytes.every((byte, index) => byte === rightBytes[index]);\n}\n","/**\n * Deterministic listing order shared by every filesystem provider.\n *\n * Providers must return byte-stable orderings so snapshots can be compared\n * across backends and across runs. `compareText` is deliberately a raw code\n * unit comparison rather than `localeCompare`, which is locale-dependent.\n */\n\nimport type { FileSystemEntrySnapshot } from '../v2.js';\n\n/** Locale-independent, deterministic string ordering. */\nexport function compareText(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\n/** Directory-first, then by entry name. Used for sibling listings. */\nexport function compareSnapshotsByName(\n left: FileSystemEntrySnapshot,\n right: FileSystemEntrySnapshot,\n): number {\n if (left.kind !== right.kind) return left.kind === 'directory' ? -1 : 1;\n return compareText(left.name, right.name);\n}\n\n/**\n * Directory-first, then by full path.\n *\n * Equivalent to `compareSnapshotsByName` for siblings, and used where entries\n * may not share a parent — the Electron transport re-sorts host-supplied\n * listings defensively and cannot assume a single directory's children.\n */\nexport function compareSnapshotsByPath(\n left: FileSystemEntrySnapshot,\n right: FileSystemEntrySnapshot,\n): number {\n if (left.kind !== right.kind) return left.kind === 'directory' ? -1 : 1;\n return compareText(left.path, right.path);\n}\n"],"mappings":";AAkBO,SAAS,uBAAuB,OAAmC;AACxE,MAAI,OAAO,UAAU,YAAY,CAAC,SAAS,MAAM,SAAS,QAAQ,MAAM,SAAS,IAAI,GAAG;AACtF,UAAM,IAAI,UAAU,8DAA8D;AAAA,EACpF;AACA,SAAO;AACT;AAEO,SAAS,0BAA0B,OAA0C;AAClF,MAAI;AACF,WAAO,uBAAuB,KAAK;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACrBO,SAAS,UAAU,MAA6C;AACrE,QAAM,SAAS,YAAY,OAAO,IAAI,IAClC,IAAI,WAAW,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,IAC5D,IAAI,WAAW,IAAI;AACvB,QAAM,OAAO,IAAI,WAAW,OAAO,UAAU;AAC7C,OAAK,IAAI,MAAM;AACf,SAAO,KAAK;AACd;AAGO,SAAS,WACd,MACA,OACS;AACT,QAAM,YAAY,YAAY,OAAO,IAAI,IACrC,IAAI,WAAW,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,IAC5D,IAAI,WAAW,IAAI;AACvB,QAAM,aAAa,YAAY,OAAO,KAAK,IACvC,IAAI,WAAW,MAAM,QAAQ,MAAM,YAAY,MAAM,UAAU,IAC/D,IAAI,WAAW,KAAK;AACxB,MAAI,UAAU,eAAe,WAAW,WAAY,QAAO;AAC3D,SAAO,UAAU,MAAM,CAAC,MAAM,UAAU,SAAS,WAAW,KAAK,CAAC;AACpE;;;ACrBO,SAAS,YAAY,MAAc,OAAuB;AAC/D,SAAO,OAAO,QAAQ,KAAK,OAAO,QAAQ,IAAI;AAChD;AAGO,SAAS,uBACd,MACA,OACQ;AACR,MAAI,KAAK,SAAS,MAAM,KAAM,QAAO,KAAK,SAAS,cAAc,KAAK;AACtE,SAAO,YAAY,KAAK,MAAM,MAAM,IAAI;AAC1C;AASO,SAAS,uBACd,MACA,OACQ;AACR,MAAI,KAAK,SAAS,MAAM,KAAM,QAAO,KAAK,SAAS,cAAc,KAAK;AACtE,SAAO,YAAY,KAAK,MAAM,MAAM,IAAI;AAC1C;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/filesystem/indexeddb-store.ts"],"sourcesContent":["/**\n * Minimal, error-propagating IndexedDB store used by the virtual filesystem.\n *\n * The previous Squisq LocalForage adapter intentionally converted failed\n * reads/enumerations/removals into null/empty/success. Those semantics are\n * unsuitable for filesystem mutations: an unavailable database must never be\n * interpreted as an absent file. This layer opens the old database and object\n * store directly so existing data can be migrated in one transaction.\n */\n\nexport type IndexedDBFileSystemTransactionMode = 'readonly' | 'readwrite';\n\n/** The database holding one browser-local workspace's files. */\nexport function indexedDBWorkspaceDatabaseName(workspaceId: string): string {\n return `docblocks-fs-${workspaceId}`;\n}\n\n/**\n * Delete a database and everything in it.\n *\n * Open connections do not have to be closed first: deletion fires\n * `versionchange` on them, and this module's connections close themselves in\n * response, after which the deletion proceeds. A `blocked` event is therefore\n * transient (an in-flight transaction) and is deliberately not treated as a\n * failure — the deletion still completes once that transaction finishes.\n */\nexport function deleteIndexedDBDatabase(databaseName: string): Promise<void> {\n if (typeof indexedDB === 'undefined') {\n return Promise.reject(new Error('IndexedDB is not available in this environment.'));\n }\n return new Promise((resolve, reject) => {\n const request = indexedDB.deleteDatabase(databaseName);\n request.onsuccess = () => resolve();\n request.onerror = () =>\n reject(request.error ?? new Error(`Failed to delete the \"${databaseName}\" database.`));\n });\n}\n\n/**\n * Delete a browser-local workspace's stored files.\n *\n * Removing a workspace descriptor alone leaves these bytes behind: invisible to\n * the user, still counted against the origin's quota, and ready to reappear\n * under any later workspace that reuses the same id.\n */\nexport function deleteIndexedDBWorkspaceData(workspaceId: string): Promise<void> {\n return deleteIndexedDBDatabase(indexedDBWorkspaceDatabaseName(workspaceId));\n}\n\nexport interface IndexedDBFileSystemTransaction {\n get<T>(key: string): Promise<T | null>;\n put<T>(key: string, value: T): Promise<void>;\n delete(key: string): Promise<void>;\n keys(): Promise<string[]>;\n /** O(log n) namespace probe in the concrete IndexedDB implementation. */\n hasKeyWithPrefix(prefix: string): Promise<boolean>;\n}\n\nexport interface IndexedDBFileSystemStore {\n transaction<T>(\n mode: IndexedDBFileSystemTransactionMode,\n operation: (transaction: IndexedDBFileSystemTransaction) => Promise<T>,\n ): Promise<T>;\n close?(): void;\n}\n\nexport class RawIndexedDBFileSystemStore implements IndexedDBFileSystemStore {\n private database: IDBDatabase | null = null;\n private opening: Promise<IDBDatabase> | null = null;\n private closed = false;\n\n public constructor(\n private readonly databaseName: string,\n private readonly storeName: string,\n ) {}\n\n public async transaction<T>(\n mode: IndexedDBFileSystemTransactionMode,\n operation: (transaction: IndexedDBFileSystemTransaction) => Promise<T>,\n ): Promise<T> {\n const database = await this.getDatabase();\n const nativeTransaction = database.transaction(this.storeName, mode);\n const completion = waitForTransaction(nativeTransaction);\n const transaction = new RawIndexedDBFileSystemTransaction(\n nativeTransaction.objectStore(this.storeName),\n );\n\n let result: T;\n try {\n result = await operation(transaction);\n } catch (error: unknown) {\n try {\n nativeTransaction.abort();\n } catch {\n // The request failure may already have aborted the transaction.\n }\n await completion.catch(() => undefined);\n throw error;\n }\n\n await completion;\n return result;\n }\n\n public close(): void {\n if (this.closed) return;\n this.closed = true;\n this.database?.close();\n this.database = null;\n this.opening = null;\n }\n\n private getDatabase(): Promise<IDBDatabase> {\n if (this.closed) {\n return Promise.reject(new Error('The IndexedDB filesystem store is closed.'));\n }\n if (this.database) return Promise.resolve(this.database);\n if (this.opening) return this.opening;\n if (typeof indexedDB === 'undefined') {\n return Promise.reject(new Error('IndexedDB is not available in this environment.'));\n }\n\n this.opening = openDatabaseWithStore(this.databaseName, this.storeName)\n .then((database) => {\n if (this.closed) {\n database.close();\n throw new Error('The IndexedDB filesystem store is closed.');\n }\n this.database = database;\n this.opening = null;\n database.onversionchange = () => {\n database.close();\n if (this.database === database) this.database = null;\n };\n return database;\n })\n .catch((error: unknown) => {\n this.opening = null;\n throw error;\n });\n return this.opening;\n }\n}\n\nclass RawIndexedDBFileSystemTransaction implements IndexedDBFileSystemTransaction {\n public constructor(private readonly store: IDBObjectStore) {}\n\n public async get<T>(key: string): Promise<T | null> {\n const value = await requestResult(this.store.get(key));\n return value === undefined ? null : (value as T);\n }\n\n public async put<T>(key: string, value: T): Promise<void> {\n await requestResult(this.store.put(value, key));\n }\n\n public async delete(key: string): Promise<void> {\n await requestResult(this.store.delete(key));\n }\n\n public async keys(): Promise<string[]> {\n const keys = await requestResult(this.store.getAllKeys());\n return keys.map((key) => {\n if (typeof key !== 'string') {\n throw new Error('The DocBlocks filesystem store contains a non-string key.');\n }\n return key;\n });\n }\n\n public async hasKeyWithPrefix(prefix: string): Promise<boolean> {\n const cursor = await requestResult(this.store.openKeyCursor(IDBKeyRange.lowerBound(prefix)));\n return cursor !== null && typeof cursor.key === 'string' && cursor.key.startsWith(prefix);\n }\n}\n\nasync function openDatabaseWithStore(\n databaseName: string,\n storeName: string,\n): Promise<IDBDatabase> {\n const database = await openDatabase(databaseName, undefined, (upgradeDatabase) => {\n if (!upgradeDatabase.objectStoreNames.contains(storeName)) {\n upgradeDatabase.createObjectStore(storeName);\n }\n });\n if (database.objectStoreNames.contains(storeName)) return database;\n\n const nextVersion = database.version + 1;\n database.close();\n return openDatabase(databaseName, nextVersion, (upgradeDatabase) => {\n if (!upgradeDatabase.objectStoreNames.contains(storeName)) {\n upgradeDatabase.createObjectStore(storeName);\n }\n });\n}\n\nfunction openDatabase(\n databaseName: string,\n version: number | undefined,\n upgrade: (database: IDBDatabase) => void,\n): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request =\n version === undefined ? indexedDB.open(databaseName) : indexedDB.open(databaseName, version);\n let settled = false;\n\n request.onupgradeneeded = () => upgrade(request.result);\n request.onsuccess = () => {\n if (settled) {\n request.result.close();\n return;\n }\n settled = true;\n resolve(request.result);\n };\n request.onerror = () => {\n if (settled) return;\n settled = true;\n reject(request.error ?? new Error(`Could not open IndexedDB database ${databaseName}.`));\n };\n request.onblocked = () => {\n if (settled) return;\n settled = true;\n reject(new Error(`IndexedDB database ${databaseName} is blocked by another open page.`));\n };\n });\n}\n\nfunction requestResult<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error('IndexedDB request failed.'));\n });\n}\n\nfunction waitForTransaction(transaction: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n transaction.oncomplete = () => resolve();\n transaction.onabort = () =>\n reject(transaction.error ?? new Error('IndexedDB transaction was aborted.'));\n transaction.onerror = () =>\n reject(transaction.error ?? new Error('IndexedDB transaction failed.'));\n });\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/filesystem/internal/records.ts"],"sourcesContent":["/**\n * Structural guard shared by the filesystem providers that validate untrusted\n * payloads — stored IndexedDB records and Electron transport messages.\n */\n\n/** True for any non-null object, including arrays and class instances. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n"],"mappings":";AAMO,SAAS,SAAS,OAAkD;AACzE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;","names":[]}