Move docs/ package mirror into document-navigator plugin

Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 01:10:00 +01:00
parent fe3fedf42e
commit 9bf56a806b
6022 changed files with 12437 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
# `@suaveplan/drive-uri/adapter`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Defines `DriveStorageLikeAdapter`, the internal shim that exposes a `Drive`
instance as a `StorageLike` so it can be registered as the `adapter` of a
`UriPlugin` consumed by `@suaveplan/storage-uri`'s `StorageUriResolver`. The
shim is the join point between the drive-tier API (which throws on missing
files) and the storage-tier contract (which returns `undefined` for missing
keys) — every other failure path is preserved unchanged so the resolver can
attach its own `STORAGE_URI.ADAPTER_ERROR` wrap.
A second export, `isDriveNotFound`, walks the `cause` chain of a thrown value
looking for any `SuaveplanError` whose `code` ends in `.NOT_FOUND`. This is
the only place that has to know about Drive's not-found vocabulary; every
plugin defers the translation to the shim.
## Features
- Implements every required member of `StorageLike` (`get`, `set`, `delete`,
`exists`, `list`, `clear`) plus the optional `createReadStream` extension
used by `StorageUriResolver.open()`'s native-stream fast path.
- Translates `Drive.read` not-found rejections into `undefined` from `get()`
so the resolver's existing `undefined → STORAGE_URI.NOT_FOUND` mapping
applies without modification.
- Rejects `clear()` with a `DriveUriError(WRITE_UNSUPPORTED)` whose message
names `Drive.delete(path)` as the supported alternative.
- Forwards `AbortSignal` to `Drive.createReadStream` so consumers can cancel
in-flight streams.
## Usage: Basic
```ts
import { DriveStorageLikeAdapter } from "@suaveplan/drive-uri/adapter";
const adapter = new DriveStorageLikeAdapter(drive);
const bytes = await adapter.get("folder/file.bin");
if (bytes === undefined) console.log("not found");
```
## Usage: Advanced
Composing the shim with a custom `UriPlugin` (this is essentially what
`driveUriPlugin` does internally):
```ts
import { DriveStorageLikeAdapter } from "@suaveplan/drive-uri/adapter";
import type { UriPlugin } from "@suaveplan/storage-uri";
function myPlugin(drive: Drive): UriPlugin {
const adapter = new DriveStorageLikeAdapter(drive);
return {
scheme: "my",
adapter,
parse(uri: string) {
const path = uri.slice("my://".length);
return { key: path };
},
};
}
```
## API Reference
- `class DriveStorageLikeAdapter implements StorageLike` — accepts a `Drive`
in its constructor. All `StorageLike` methods delegate to the bound drive.
- `DriveStorageLikeAdapter.createReadStream(key, signal?)` — forwards
directly to `Drive.createReadStream(key, signal)`.
- `function isDriveNotFound(err: unknown): boolean` — returns `true` when
`err` (or anything in its `cause` chain, up to 8 levels deep with cycle
detection) is a `SuaveplanError` whose `code` ends in `.NOT_FOUND`.
## Implementation Notes
- The shim does NOT own any chunk codec, manifest fetching, peer discovery,
or template validation — that all happens inside `Drive` via its installed
middleware (`chunksPlugin`, `drive-mesh`, etc.).
- `list()` accepts either of two shapes Drive may return (`{ keys }` vs.
`{ entries: [{ path }] }`) and normalises to the `StorageLike` shape,
preserving `continuationToken` when present.
- `clear()` is intentionally a hard error rather than a soft no-op — wiping a
Drive through the URI surface is a footgun, and a silent success would lose
data on misuse. The supported path is `resolver.remove(uri)` per file.
- `isDriveNotFound` includes cycle detection (depth-limit + self-reference
check) so a malicious or accidental self-referential `cause` chain cannot
cause an infinite loop.

View File

@@ -0,0 +1,106 @@
# `@suaveplan/drive-uri/conformance`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Re-exports the canonical URI-plugin conformance harness from
`@suaveplan/storage-uri` (`runUriPluginConformance`, `UriPluginFixtures`,
`UriPluginConformanceFailure`). `drive-uri` does NOT author its own harness
— keeping the assertions in one place means every URI-plugin package in the
project (storage-file, storage-http, storage-cdn-*, drive-uri, …)
runs against an identical set of contract checks.
Each plugin's fixture set lives in its co-located test file; the harness
runs three classes of assertion per fixture: every `validUris` member is
accepted by `parse`, every `invalidUris` member throws
`STORAGE_URI.INVALID_URI`, and every `buildRoundTrip` entry round-trips
through `plugin.build`.
## Features
- Zero-runtime re-export — the conformance harness is a value, not a copy.
- Type-safe `UriPluginFixtures` shape.
- Strong-typed failure surface (`UriPluginConformanceFailure`).
- Compatible with both `drive`, `drive+mesh`, and `drive+template` plugins.
## Usage: Basic
```ts
import {
runUriPluginConformance,
type UriPluginFixtures,
} from "@suaveplan/drive-uri/conformance";
import { driveUriPlugin } from "@suaveplan/drive-uri";
const fixtures: UriPluginFixtures = {
validUris: ["drive://primary/a", "drive://primary/nested/file.bin"],
invalidUris: ["", "drive:", "drive://primary/has space"],
buildRoundTrip: [
{
key: "a",
meta: { driveId: "primary" },
uri: "drive://primary/a",
},
],
};
runUriPluginConformance(
driveUriPlugin({ drive, driveId: "primary" }),
fixtures,
);
```
## Usage: Advanced
Running the same fixture template across multiple driveIds parametrically:
```ts
import { runUriPluginConformance, type UriPluginFixtures } from "@suaveplan/drive-uri";
function fixturesFor(driveId: string): UriPluginFixtures {
return {
validUris: [`drive://${driveId}/a`, `drive://${driveId}/folder/b`],
invalidUris: ["", "drive:"],
buildRoundTrip: [
{
key: "a",
meta: { driveId },
uri: `drive://${driveId}/a`,
},
],
};
}
for (const id of ["primary", "secondary", "backup"]) {
runUriPluginConformance(
driveUriPlugin({ drive: drives[id], driveId: id }),
fixturesFor(id),
);
}
```
## API Reference
- `runUriPluginConformance(plugin, fixtures)` — throws
`UriPluginConformanceFailure` on the first mismatch. Re-export from
`@suaveplan/storage-uri/conformance`.
- `interface UriPluginFixtures` — `validUris`, `invalidUris`, optional
`buildRoundTrip`.
- `class UriPluginConformanceFailure extends Error` — the harness's failure
class.
## Implementation Notes
- The drive-uri tests assemble per-plugin fixture sets locally (one each for
`drive://`, `drive+mesh://`, `drive+template://`) and feed them into the
harness inside `src/conformance/conformance.test.ts`.
- The `invalidUris` set must produce `STORAGE_URI.INVALID_URI` from
`parseUri` — the drive-uri plugins authenticate the scheme themselves but
defer URI-hygiene checks (whitespace, NUL, length, scheme regex) to the
upstream `parseUri` helper.
- Round-trip fixtures only need to be supplied when the plugin implements
`build`; the harness skips that section when `buildRoundTrip` is empty
or omitted.

View File

@@ -0,0 +1,98 @@
# `@suaveplan/drive-uri/error-codes`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Declares the seven `DRIVE_URI.*` error codes raised by `@suaveplan/drive-uri`
plugins and the `DriveUriError` discriminant class used to surface them. The
codes form the public contract by which consumers programmatically distinguish
URI-parsing failures (`INVALID_URI`), authority mismatches
(`DRIVE_ID_MISMATCH`), template fetch/validation failures
(`TEMPLATE_NOT_AVAILABLE`, `TEMPLATE_INVALID`), unsupported writes
(`WRITE_UNSUPPORTED`), missing required wiring (`CAPABILITY_MISSING`), and
abort short-circuits (`ABORTED`).
Every code is produced by `@suaveplan/error`'s `defineCodes` so the literal
strings (`"DRIVE_URI.INVALID_URI"` etc.) are generated once and consumed via
the typed `DRIVE_URI_CODES` constant — never hand-typed. This keeps the
namespace honest under refactor and guarantees that `instanceof DriveUriError`
narrows the `code` discriminant to a small literal union.
## Features
- Seven namespaced codes registered via a single `defineCodes` call.
- `DriveUriError` carries optional discriminants (`uri`, `scheme`, `driveId`,
`swarmId`, `templateId`, `path`, `expected`, `actual`, `reason`) so
operators can correlate failures without parsing message strings.
- Original `cause` is preserved across re-wraps via the `SuaveplanError`
base class so `err.cause.code` still reads e.g. `DTP.TEMPLATE_EXPIRED`
after a `TEMPLATE_INVALID` wrap.
- The error class name is `"DriveUriError"` for log aggregation and
TypeScript narrowing.
## Usage: Basic
```ts
import { DRIVE_URI_CODES, DriveUriError } from "@suaveplan/drive-uri/error-codes";
throw new DriveUriError(
DRIVE_URI_CODES.DRIVE_ID_MISMATCH,
"URI driveId 'other' does not match plugin driveId 'primary'",
{ uri: "drive://other/x", expected: "primary", actual: "other" },
);
```
## Usage: Advanced
Walking the `cause` chain to surface the underlying drive failure code while
keeping the public `DRIVE_URI.*` shape:
```ts
import { DRIVE_URI_CODES, DriveUriError } from "@suaveplan/drive-uri";
try {
await drive.hydrateFrom(template);
} catch (cause) {
throw new DriveUriError(
DRIVE_URI_CODES.TEMPLATE_INVALID,
"hydrateFrom rejected for templateId 'templA'",
{ templateId: "templA", cause },
);
}
```
A consumer receives a `DriveUriError` whose `code === "DRIVE_URI.TEMPLATE_INVALID"`
and whose `cause.code === "DTP.TEMPLATE_EXPIRED"` (or whichever DTP code the
drive-template validator surfaced). Both are programmatically actionable.
## API Reference
- `DRIVE_URI_CODES` — frozen registry with seven keys: `INVALID_URI`,
`DRIVE_ID_MISMATCH`, `TEMPLATE_NOT_AVAILABLE`, `TEMPLATE_INVALID`,
`WRITE_UNSUPPORTED`, `CAPABILITY_MISSING`, `ABORTED`. Each value is the
namespaced string `DRIVE_URI.<REASON>`.
- `DriveUriErrorCode` — string-literal union over the registry values.
- `DriveUriErrorOptions` — extends `SuaveplanErrorOptions` with optional
`uri`, `scheme`, `driveId`, `swarmId`, `templateId`, `path`, `expected`,
`actual`, `reason` discriminants.
- `DriveUriError` — extends `SuaveplanError<DriveUriErrorCode>`. Constructor:
`(code, message, options?) => DriveUriError`. Exposes the discriminants as
readonly fields.
## Implementation Notes
- The registry is intentionally narrow. Codes like `STORAGE_URI.NOT_FOUND` are
emitted by the resolver, not by drive-uri — duplicating them here would
drift on rename. The codes that ARE in this registry are the ones drive-uri
raises before reaching the resolver, plus the ones it re-wraps to keep the
failure mode visible to dashboards.
- `DriveUriError` does NOT include `getMetadata`-style state — it is a plain
exception class. OpenTelemetry instrumentation lives in
`@suaveplan/drive-uri/otel`.
- The side-effect import at `src/index.ts` ensures the registry is registered
when consumers reach the package via the root entry; subpath consumers
reaching `./error-codes` directly register the codes on import via
`defineCodes`'s own side effect.

View File

@@ -0,0 +1,92 @@
# `@suaveplan/drive-uri/otel`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
OpenTelemetry instrumentation surface for `@suaveplan/drive-uri`. Exposes a
meter named `@suaveplan/drive-uri` carrying the operation-level counters and
duration histogram described in the package spec, plus a tracer of the same
name for span emission. The `redactUri` helper strips query strings and
fragments from URI attributes so peer hints, seeds, or other secret material
never reach span backends.
Counters and histograms are lazily created on first access via
`getInstruments()` so the package has zero side effects at import time, and
`setDriveUriMeterProvider` lets tests inject a stub provider for assertions.
## Features
- Lazy instrument bag keyed by the active `MeterProvider`.
- Counters: `drive_uri.reads_total`, `drive_uri.writes_total`,
`drive_uri.deletes_total`, `drive_uri.errors_total`,
`drive_uri.template_hydrations_total`.
- Histogram: `drive_uri.op_duration_ms{op, scheme, outcome}`.
- Per-op routing helper `recordOp` that increments the correct success or
error counter and the duration histogram in one call. Writes increment
`writes_total` even on rejection so operator dashboards see misuse rate.
- `redactUri(uri)` strips everything from the first `?` or `#` onwards.
## Usage: Basic
```ts
import { recordOp, recordTemplateHydration } from "@suaveplan/drive-uri/otel";
recordOp({ op: "read", scheme: "drive", outcome: "success", durationMs: 12 });
recordTemplateHydration("success");
```
## Usage: Advanced
Test harness swapping in a stub `MeterProvider`:
```ts
import {
resetInstruments,
setDriveUriMeterProvider,
} from "@suaveplan/drive-uri/otel";
class StubProvider implements MeterProvider {
public meter = new StubMeter();
public getMeter() {
return this.meter;
}
}
const provider = new StubProvider();
setDriveUriMeterProvider(provider);
resetInstruments();
// ... run code under test, then inspect provider.meter ...
setDriveUriMeterProvider(undefined);
resetInstruments();
```
## API Reference
- `setDriveUriMeterProvider(provider | undefined)` — override or revert.
- `getInstruments(force = false)` — returns the cached bag or rebuilds.
- `resetInstruments()` — clears the cache (used between provider swaps).
- `recordOp({ op, scheme, outcome, durationMs, code? })` — one-shot
per-operation observation. `op ∈ {open|read|write|stat|exists|remove|list}`.
- `recordTemplateHydration(outcome)` — increments
`drive_uri.template_hydrations_total{outcome}` by one.
- `redactUri(uri): string` — strips `?...` and `#...` suffixes.
- Types: `DriveUriOp`, `DriveUriOutcome`, `DriveUriInstruments`,
`RecordOpInput`.
## Implementation Notes
- `writes_total` carries the `outcome` attribute (`"success"` or `"error"`),
letting dashboards split successful (currently impossible) writes from
misuse rejections.
- `errors_total` is keyed by `scheme` and `code` only — operators slice it
by code to attribute failures to root causes (`DRIVE_URI.TEMPLATE_INVALID`
vs. `DRIVE_URI.WRITE_UNSUPPORTED` etc.).
- The drive-uri tracer is independent of the resolver's `@suaveplan/storage-uri`
tracer — both spans fire on a single dispatch, giving operators a per-tier
view of the operation.
- `redactUri` is content-agnostic; it does NOT validate that the input is a
syntactically correct URI. The plugin layer is responsible for rejecting
malformed URIs before they reach the span attribute.

View File

@@ -0,0 +1,91 @@
# `@suaveplan/drive-uri/plugins/drive-mesh`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`driveMeshUriPlugin(options)` is a factory returning a `UriPlugin` registered
against the `drive+mesh://` scheme. It parses `drive+mesh://<swarmId>/<path>`
URIs, captures `swarmId` for instrumentation, and forwards `path` to the
consumer-supplied mesh-configured `Drive`. The plugin does NOT use `swarmId`
to choose a `Drive` or transport — peer routing remains entirely the
responsibility of the `Drive`'s installed transports (`drive-transport-https`,
`drive-webrtc`) and its `PeerRegistry`.
The captured `swarmId` surfaces on every emitted span via the
`drive.uri.swarm_id` attribute. When a span is active at the time of
`parse(uri)`, the attribute is written there (alongside `drive.uri.scheme`
and the redacted `drive.uri`); otherwise the parse step is silent.
## Features
- `parse(uri)` enforces `scheme === "drive+mesh"`, a non-empty authority, and
a non-empty path. All other URI hygiene (whitespace, NUL, scheme regex,
4096-char limit) is delegated to `parseUri` from `@suaveplan/storage-uri`.
- `build(key, meta?)` round-trips the URI back to its source shape, defaulting
the swarmId to `options.defaultSwarmId` (or `"default"`) when no meta is
supplied.
- Adapter inherits `InstrumentedDriveAdapter` so reads, writes, and errors
are recorded under the `drive+mesh` scheme attribute.
## Usage: Basic
```ts
import { Drive } from "@suaveplan/drive";
import { chunksPlugin } from "@suaveplan/drive-plugin-chunks";
import { createStorageUriResolver } from "@suaveplan/storage-uri";
import { driveMeshUriPlugin } from "@suaveplan/drive-uri/plugins/drive-mesh";
const meshDrive = new Drive(localStorage);
await meshDrive.initialize();
await meshDrive.usePlugin(chunksPlugin({ /* ... */ }));
const resolver = createStorageUriResolver([
driveMeshUriPlugin({ drive: meshDrive }),
]);
const bytes = await resolver.read("drive+mesh://swarmA/raw/clip.mov");
```
## Usage: Advanced
Mixing mesh and template plugins on the same mesh-configured Drive so a
single resolver dispatches both shapes through one drive instance:
```ts
import {
driveMeshUriPlugin,
driveTemplateUriPlugin,
} from "@suaveplan/drive-uri";
const resolver = createStorageUriResolver([
driveMeshUriPlugin({ drive: meshDrive, defaultSwarmId: "primary" }),
driveTemplateUriPlugin({ drive: meshDrive, templateProvider }),
]);
await resolver.read("drive+mesh://primary/folder/a.bin");
await resolver.read("drive+template://templA/folder/a.bin");
```
The two plugins share the same `Drive`, so a hydration triggered by the
template plugin populates the file-location index that the mesh plugin then
uses to fetch chunks. This is the canonical composition.
## API Reference
- `driveMeshUriPlugin(options: DriveMeshUriPluginOptions): UriPlugin` —
`options.drive` is required; `options.defaultSwarmId` is optional and
defaults to `"default"` for `build` output.
## Implementation Notes
- The plugin's `parse(uri)` attaches the swarmId to the active span via
`trace.getActiveSpan()`. When no span is active (e.g. plugin used
outside the resolver, in unit tests parsing URIs directly), the
attribute write is silently skipped — never throws.
- The `drive.uri` attribute is passed through `redactUri` so query strings
carrying peer hints or seeds are stripped before reaching span backends.
- Like `driveUriPlugin`, writes are rejected with `DRIVE_URI.WRITE_UNSUPPORTED`
per the per-release scope.

View File

@@ -0,0 +1,112 @@
# `@suaveplan/drive-uri/plugins/drive-template`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`driveTemplateUriPlugin(options)` is a factory returning a `UriPlugin`
registered against the `drive+template://` scheme. It parses
`drive+template://<templateId>/<path>` URIs and, before delegating any read
to the wrapped `Drive`, fetches the matching `DriveTemplate` via
`options.templateProvider.fetchTemplate(templateId)` and feeds it to
`drive.hydrateFrom(template)`. The first read against a given `templateId`
performs both steps; concurrent reads await the same in-flight hydration;
subsequent reads after a successful hydration skip both calls.
The hydration cache is a per-plugin `Map<string, Promise<void>>` keyed by
`templateId`. After a failed hydration the entry is removed so subsequent
reads can retry — important for transient `fetchTemplate` failures.
## Features
- Construction-time validation of `options.templateProvider` — absence or a
non-function `fetchTemplate` throws `DRIVE_URI.CAPABILITY_MISSING`
synchronously, never deferred to first use.
- Per-templateId hydration memoisation with concurrent-read coalescing.
- Distinct error mappings — `fetchTemplate` rejection becomes
`DRIVE_URI.TEMPLATE_NOT_AVAILABLE`; `Drive.hydrateFrom` rejection becomes
`DRIVE_URI.TEMPLATE_INVALID`. The two failures are NEVER conflated.
- Original cause is preserved on both wrapper errors so operators see e.g.
`cause.code === "DTP.TEMPLATE_EXPIRED"` underneath a `TEMPLATE_INVALID`.
- `drive_uri.template_hydrations_total{outcome}` increments exactly once
per first-use template (success or error). Cache-hit reads do NOT
increment the counter.
## Usage: Basic
```ts
import { Drive } from "@suaveplan/drive";
import { createStorageUriResolver } from "@suaveplan/storage-uri";
import { driveTemplateUriPlugin } from "@suaveplan/drive-uri/plugins/drive-template";
const drive = new Drive(localStorage);
await drive.initialize();
const templateProvider = {
async fetchTemplate(id: string) {
return downloadTemplate(id);
},
};
const resolver = createStorageUriResolver([
driveTemplateUriPlugin({ drive, templateProvider }),
]);
const bytes = await resolver.read("drive+template://templA/manifest/clip.jpg");
```
## Usage: Advanced
Sharing the template plugin's hydration cache with a `drive+mesh://` plugin
by passing the same `Drive` to both — the file-location index populated by
hydration powers the mesh plugin's chunk fetches:
```ts
import {
driveMeshUriPlugin,
driveTemplateUriPlugin,
} from "@suaveplan/drive-uri";
const sharedDrive = await buildMeshConfiguredDrive();
const resolver = createStorageUriResolver([
driveMeshUriPlugin({ drive: sharedDrive }),
driveTemplateUriPlugin({ drive: sharedDrive, templateProvider }),
]);
// First read hydrates the template, populates the index, then reads chunks
// through the chunks plugin via the mesh transport.
await resolver.read("drive+template://templA/clip.mov");
// Subsequent reads for the same template skip hydration but still go
// through the chunks plugin (because the file-location index is now seeded).
await resolver.read("drive+template://templA/cover.jpg");
```
## API Reference
- `driveTemplateUriPlugin(options: DriveTemplateUriPluginOptions): UriPlugin` —
both `options.drive` and `options.templateProvider` are required at
construction. `templateProvider.fetchTemplate` MUST be a function.
- The returned plugin exposes `scheme === "drive+template"`, an instrumented
adapter, `parse(uri) → { key, meta: { templateId } }`, and
`build(key, meta?) → uri`.
- `interface TemplateProvider` — `{ fetchTemplate(templateId, signal?) => Promise<DriveTemplate> }`.
## Implementation Notes
- The hydration cache stores `Promise<void>` rather than the resolved
template object — concurrent reads await the same promise, and once it
settles the value is irrelevant (the template is already inside the
`Drive`'s index via `hydrateFrom`).
- Failed hydrations remove themselves from the cache so retries are
possible. A re-hydration after a previous failure is idempotent at the
`Drive` layer via `TemplateNonceLru`.
- `parse(uri)` captures the templateId in a closure-scoped variable so the
adapter's `beforeOp` hook (which fires synchronously inside the
resolver's dispatch chain) sees the right value before any `get`/`exists`/
`delete` call.
- Writes are rejected at the adapter tier with `DRIVE_URI.WRITE_UNSUPPORTED`,
per the per-release scope.

View File

@@ -0,0 +1,103 @@
# `@suaveplan/drive-uri/plugins/drive`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`driveUriPlugin(options)` is a factory returning a `UriPlugin` registered
against the `drive://` scheme. It wraps a consumer-supplied `Drive` instance
in an OTel-instrumented `DriveStorageLikeAdapter` so a single
`StorageUriResolver` can dispatch `drive://<driveId>/<path>` URIs through the
same code path as `file:///` or `s3://` URIs. The plugin enforces a 1:1 match
between the URI's authority component and the `driveId` declared in options;
a mismatch surfaces as `DRIVE_URI.DRIVE_ID_MISMATCH` before any `Drive` call.
The plugin is read-only in this release: every `adapter.set` call rejects
with `DRIVE_URI.WRITE_UNSUPPORTED` (which the resolver wraps as
`STORAGE_URI.WRITE_UNSUPPORTED` with the drive-uri error preserved on
`cause`). Writes still increment `drive_uri.writes_total{outcome:"error"}`
so operator dashboards see misuse rates.
## Features
- Parses `drive://<driveId>/<path>` URIs and rejects mismatched driveIds.
- Returns `{ key, meta: { driveId } }` so callers can re-build the URI
through `plugin.build(key, meta)`.
- Forwards `Drive.createReadStream` directly when `resolver.open()` is used
so the resolver's native streaming fast path applies — no buffered
fallback, no `storage_uri.stream_fallback_total` increment.
- Pre-aborted signals short-circuit at the adapter shim with
`DRIVE_URI.ABORTED` (no `Drive` call is made).
## Usage: Basic
```ts
import { Drive } from "@suaveplan/drive";
import { createStorageUriResolver } from "@suaveplan/storage-uri";
import { driveUriPlugin } from "@suaveplan/drive-uri/plugins/drive";
const drive = new Drive(localStorage);
await drive.initialize();
const resolver = createStorageUriResolver([
driveUriPlugin({ drive, driveId: "primary" }),
]);
const bytes = await resolver.read("drive://primary/folder/file.bin");
```
## Usage: Advanced
Composing with other plugins (file + drive in one resolver) and threading an
`AbortSignal` into the streaming open path:
```ts
import { createStorageUriResolver } from "@suaveplan/storage-uri";
import { driveUriPlugin } from "@suaveplan/drive-uri";
const resolver = createStorageUriResolver([
fileUriPlugin({ rootDir: "/var/cache" }),
driveUriPlugin({ drive, driveId: "primary" }),
]);
const controller = new AbortController();
const stream = await resolver.open(
"drive://primary/large/clip.mov",
controller.signal,
);
const reader = stream.getReader();
try {
while (true) {
const { value, done } = await reader.read();
if (done) break;
process(value);
}
} finally {
controller.abort(); // propagates to Drive.createReadStream
reader.releaseLock();
}
```
## API Reference
- `driveUriPlugin(options: DriveUriPluginOptions): UriPlugin` — `options.drive`
is the consumer-supplied `Drive`, `options.driveId` is the literal authority
the plugin will require. Both are validated synchronously; a missing or
empty value throws `DRIVE_URI.CAPABILITY_MISSING`.
- The returned plugin exposes `scheme === "drive"`, `adapter`,
`parse(uri) → { key, meta }`, and `build(key, meta?) → uri`.
## Implementation Notes
- The plugin's `parse` is purely synchronous. Async work (the actual `Drive`
call) happens inside the adapter's `get`/`createReadStream`/etc. methods
invoked by `StorageUriResolver`.
- `build(key, meta?)` defaults the driveId to the plugin's configured
`options.driveId` when no meta is supplied — useful for re-serialising
resolver-list results.
- Writes are deferred to a follow-up change (see proposal §"Out of scope").
The plugin still records the rejection so dashboards capture misuse.
- The `DriveStorageLikeAdapter` is created once per plugin instance and
shared across every URI dispatched to this plugin.

View File

@@ -0,0 +1,84 @@
# `@suaveplan/drive-uri/plugins/drive/streams`
## Purpose
`InstrumentedDriveAdapter` is the OTel-instrumented `StorageLike` shared by
all three URI plugins (`drive://`, `drive+mesh://`, `drive+template://`). It
extends `DriveStorageLikeAdapter` to:
- record `drive_uri.op_duration_ms{op, scheme, outcome}` and the per-op
counters (`reads_total`, `writes_total`, `deletes_total`, `errors_total`)
via `recordOp`;
- reject every `set` call with `DRIVE_URI.WRITE_UNSUPPORTED` (and increment
`writes_total{outcome:"error"}`);
- short-circuit `createReadStream` when the supplied `AbortSignal` is already
aborted, throwing `DRIVE_URI.ABORTED` without invoking the underlying drive;
- thread an optional `beforeOp` async hook used by the `drive+template`
plugin to interpose template hydration before every operation.
The class is exported from the package root for advanced consumers building
custom plugins; the three first-party plugins use it internally.
## Features
- Inherits `StorageLike` conformance from `DriveStorageLikeAdapter` (including
`clear() → DRIVE_URI.WRITE_UNSUPPORTED` and `list()`'s normalised shape).
- Per-op OTel instrumentation — `read`/`open`/`exists`/`list` route to
`reads_total` (on success) and `errors_total` (on failure);
`write` always routes to `writes_total`; `delete` routes to `deletes_total`.
- `not_found` outcomes are recorded as `errors_total{code:"UNKNOWN"}` for
consistency with the storage-uri resolver — operators see a clear "missing
resource" signal without the success counters being skewed.
- The `beforeOp` hook is awaited before every operation, so the
`drive+template` plugin can ensure hydration has completed.
## Usage: Basic
```ts
import { InstrumentedDriveAdapter } from "@suaveplan/drive-uri";
const adapter = new InstrumentedDriveAdapter(drive, { scheme: "drive" });
await adapter.get("folder/file.bin");
```
## Usage: Advanced
A custom plugin combining the instrumented adapter with a per-op auth check:
```ts
import { InstrumentedDriveAdapter } from "@suaveplan/drive-uri";
const adapter = new InstrumentedDriveAdapter(drive, {
scheme: "drive+custom",
beforeOp: async (op) => {
if (op === "delete" && !ctx.hasDeletePermission()) {
throw new Error("forbidden");
}
},
});
```
The hook fires in lockstep with the resolver's dispatch chain. It's run
before the wrapped `Drive` call, so failures inside the hook are recorded
under `errors_total` for the relevant op.
## API Reference
- `interface InstrumentedAdapterOptions` — `scheme: string`,
`beforeOp?: (op) => Promise<void>`.
- `class InstrumentedDriveAdapter extends DriveStorageLikeAdapter` —
overrides `get`, `set`, `delete`, `exists`, and `createReadStream`. Every
override records duration and counter samples via `recordOp`.
## Implementation Notes
- `set` always throws — the per-release scope deliberately excludes writes.
- `not_found` outcome on `get` happens when the base class translates a
drive not-found error into `undefined`; both the histogram and
`errors_total{code:"UNKNOWN"}` (per `recordOp` semantics) increment.
- `createReadStream` is the only operation that takes an `AbortSignal` at
the storage-uri resolver layer; the pre-aborted check throws `ABORTED`
before any Drive call.
- The `beforeOp` hook receives a string discriminant (`"get"|"set"|"delete"|
"exists"|"list"`) rather than the op-level `DriveUriOp` so it doesn't
conflate the resolver's `open` (which maps to `get`) with `read`.

View File

@@ -0,0 +1,95 @@
# `@suaveplan/drive-uri/types`
## Purpose
Shared option-bag interfaces consumed by the three URI plugin factories
exported from `@suaveplan/drive-uri`. Each interface follows AGENTS §11's
rule that nested object shapes must be named — every option referenced
across plugin boundaries (e.g. `TemplateProvider`) is a standalone interface,
never an inline object literal.
The types are intentionally minimal — drive-uri does not own the contract
of the `Drive` it wraps, nor the schema of the `DriveTemplate` it passes
through. Those types come from `@suaveplan/drive` and `@suaveplan/drive-template`
respectively and are referenced by name here.
## Features
- `TemplateProvider` — single-method interface that the `drive+template`
plugin consumes. Implementations may fetch templates over HTTP, gossip,
the upstream mesh, or an in-memory map (used in tests).
- `DriveUriPluginOptions` — minimum surface for the `drive` plugin:
`drive` + `driveId`.
- `DriveMeshUriPluginOptions` — `drive` + optional `defaultSwarmId`.
- `DriveTemplateUriPluginOptions` — `drive` + `templateProvider`.
## Usage: Basic
```ts
import type {
DriveUriPluginOptions,
TemplateProvider,
} from "@suaveplan/drive-uri";
const options: DriveUriPluginOptions = { drive, driveId: "primary" };
const provider: TemplateProvider = {
async fetchTemplate(templateId, signal) {
return downloadTemplate(templateId, signal);
},
};
```
## Usage: Advanced
A `TemplateProvider` backed by an HTTP fetch with caching, retry, and the
forwarded `AbortSignal`:
```ts
import type { TemplateProvider } from "@suaveplan/drive-uri";
import type { DriveTemplate } from "@suaveplan/drive-template";
const cache = new Map<string, Promise<DriveTemplate>>();
const provider: TemplateProvider = {
async fetchTemplate(templateId, signal) {
const cached = cache.get(templateId);
if (cached !== undefined) return cached;
const pending = (async () => {
const res = await fetch(`/api/templates/${templateId}`, { signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return (await res.json()) as DriveTemplate;
})();
cache.set(templateId, pending);
try {
return await pending;
} catch (err) {
cache.delete(templateId);
throw err;
}
},
};
```
## API Reference
- `interface TemplateProvider` — `fetchTemplate(templateId: string, signal?: AbortSignal): Promise<DriveTemplate>`.
- `interface DriveUriPluginOptions` — `readonly drive: Drive`, `readonly driveId: string`.
- `interface DriveMeshUriPluginOptions` — `readonly drive: Drive`,
`readonly defaultSwarmId?: string`.
- `interface DriveTemplateUriPluginOptions` — `readonly drive: Drive`,
`readonly templateProvider: TemplateProvider`.
## Implementation Notes
- Every interface is `readonly` for ergonomic safety — option bags are
one-shot construction inputs, not mutable state.
- `defaultSwarmId` on the mesh options is only used by `plugin.build(...)`
when no `meta.swarmId` is supplied — it never affects dispatch.
- `TemplateProvider.fetchTemplate` returns `DriveTemplate` (the canonical
type from `@suaveplan/drive-template`), not a serialized form. The
provider is responsible for any decoding/parsing; `drive-uri` passes the
return value straight to `Drive.hydrateFrom`.
- A future addition could be a `templateCache` option exposing the
hydration cache to the consumer, but per AGENTS §11 that requires ≥2
consumers — out of scope for the initial release.