4.7 KiB
Drive — Capability Spec
DRIVE-01 — Drive consumes AbstractStorage directly
Drive MUST accept any concrete AbstractStorage from @suaveplan/storage as its storage substrate. There MUST NOT be a separate StorageAdapter shim. Drive MUST narrow capabilities through a DriveStorage view that extends AbstractStorage with id: string and optional lifecycle hooks.
Verified by: src/drive/drive.spec-conformance.test.ts::"accepts MemoryStorage as DriveStorage"
DRIVE-02 — initialize() is idempotent and ownership-tracking
Drive.initialize() MUST be idempotent. For each registered storage, drive MUST call storage.connect?.() ONLY when storage.isConnected?.() !== true, and MUST record those storages in an internal connectedByMe: WeakSet<DriveStorage>. Connection failures MUST surface as DriveError(DRIVE.STORAGE_CONNECT_FAILED, { cause }).
Verified by: src/drive/drive.spec-conformance.test.ts::"calls connect only when not already connected" AND src/drive/drive.spec-conformance.test.ts::"surfaces connect failure as STORAGE_CONNECT_FAILED"
DRIVE-03 — dispose() is idempotent and ownership-respecting
Drive.dispose() MUST be idempotent. Drive MUST only close storages in connectedByMe; storages pre-connected by the caller MUST be left open. After dispose(), the drive transitions to a terminal disposed state.
Verified by: src/drive/drive.spec-conformance.test.ts::"closes only storages connected by drive" AND src/drive/drive.spec-conformance.test.ts::"does not close pre-connected storage"
DRIVE-04 — Operations after dispose() throw
Any file operation invoked on a disposed drive (read, write, delete, stat, list, createReadStream, createWriteStream) MUST throw DriveError(DRIVE.DISPOSED). Operations invoked before initialize() MUST throw DriveError(DRIVE.NOT_INITIALIZED).
Verified by: src/drive/drive.spec-conformance.test.ts::"read throws DISPOSED after dispose" AND src/drive/drive.spec-conformance.test.ts::"read throws NOT_INITIALIZED before initialize"
DRIVE-05 — Error-code registry is namespaced and frozen
DRIVE_CODES MUST be declared via defineCodes("DRIVE", [...]) from @suaveplan/error. Object.isFrozen(DRIVE_CODES) MUST be true. Every code value MUST start with DRIVE.. DriveError MUST extend SuaveplanError<DriveErrorCode>.
Verified by: src/error-codes.spec-conformance.test.ts::"Object.isFrozen(DRIVE_CODES) is true" AND src/error-codes.spec-conformance.test.ts::"every code value starts with DRIVE."
DRIVE-06 — Plugin middleware order
drive.use(plugin) and drive.useMiddleware(mw) MUST install in registration order. Middleware chains for read, write, delete, stat, list MUST execute outer-first on the request path and outer-last on the response path. plugin.dispose() MUST be called in reverse-registration order on Drive.dispose(). Plugin install/dispose throws MUST surface as DriveError(DRIVE.PLUGIN_FAILED, { cause }).
Verified by: src/plugins/registry.spec-conformance.test.ts::"middleware executes outer-first request, outer-last response" AND src/plugins/registry.spec-conformance.test.ts::"dispose calls plugins in reverse registration order" AND src/plugins/registry.spec-conformance.test.ts::"plugin install throw surfaces as PLUGIN_FAILED"
DRIVE-07 — Capability fallbacks for streams
drive.createReadStream(path) MUST use storage.createReadStream when storage.capabilities?.nativeStreams === true, otherwise MUST fall back to the inherited AbstractStorage.defaultCreateReadStream. Same rule for createWriteStream.
Verified by: src/file-operations/streams.spec-conformance.test.ts::"createReadStream uses native stream when nativeStreams=true" AND src/file-operations/streams.spec-conformance.test.ts::"createReadStream falls back when nativeStreams=false"
DRIVE-08 — Generic storage typing
Drive<TStorageMap> MUST narrow TStorageMap through drive.use<K, S>(id, storage) so that drive.getStorage(id) returns TStorageMap[K] exactly (no widening to the base DriveStorage). Calls with an unknown id MUST be a TypeScript error.
Verified by: src/drive/drive.spec-conformance.test.ts::"getStorage returns narrowed type" (type-level)
DRIVE-09 — Constructor overloads
new Drive(), new Drive(storage), new Drive(storage, options), and new Drive({ defaultStorage, plugins, ... }) MUST all be valid. The bare-storage form MUST be observably equivalent to passing { defaultStorage: storage } and MUST register the storage under id "default".
Verified by: src/drive/drive.spec-conformance.test.ts::"new Drive() is valid" AND src/drive/drive.spec-conformance.test.ts::"new Drive(storage) registers as default"