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:
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user