first commit
This commit is contained in:
159
docs/networking/drive/drive-uri/README.md
Normal file
159
docs/networking/drive/drive-uri/README.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# @suaveplan/drive-uri - URI plugins (drive://, drive+mesh://, drive+template://) for @suaveplan/storage-uri's StorageUriResolver. Wraps a caller-supplied Drive instance in a StorageLike adapter so URI-addressed drive content resolves through the same dispatcher as storage adapters.
|
||||
|
||||
URI plugins (`drive://`, `drive+mesh://`, `drive+template://`) for
|
||||
`@suaveplan/storage-uri`'s `StorageUriResolver`. Wraps a caller-supplied
|
||||
`Drive` instance in a `StorageLike` adapter so URI-addressed drive content
|
||||
resolves through the same dispatcher as `file://`, `https://`, `s3://`, and
|
||||
the other storage adapters — without re-implementing chunk decoding, peer
|
||||
discovery, or template validation. The drive itself remains the single
|
||||
source of truth for those concerns; `drive-uri` is a routing layer.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [adapter](./src/adapter/drive-storage-like.md) — Defines `DriveStorageLikeAdapter`, the internal shim that exposes a `Drive` instance as a `StorageLike` so it can be re…
|
||||
- [drive-storage-like](./src/adapter/drive-storage-like.md) — Defines `DriveStorageLikeAdapter`, the internal shim that exposes a `Drive` instance as a `StorageLike` so it can be re…
|
||||
- [conformance](./src/conformance/conformance.md) — Re-exports the canonical URI-plugin conformance harness from `@suaveplan/storage-uri` (`runUriPluginConformance`, `UriP…
|
||||
- [conformance](./src/conformance/conformance.md) — Re-exports the canonical URI-plugin conformance harness from `@suaveplan/storage-uri` (`runUriPluginConformance`, `UriP…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — Declares the seven `DRIVE_URI.*` error codes raised by `@suaveplan/drive-uri` plugins and the `DriveUriError` discrimin…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — Declares the seven `DRIVE_URI.*` error codes raised by `@suaveplan/drive-uri` plugins and the `DriveUriError` discrimin…
|
||||
- [otel](./src/otel/otel.md) — OpenTelemetry instrumentation surface for `@suaveplan/drive-uri`. Exposes a meter named `@suaveplan/drive-uri` carrying…
|
||||
- [otel](./src/otel/otel.md) — OpenTelemetry instrumentation surface for `@suaveplan/drive-uri`. Exposes a meter named `@suaveplan/drive-uri` carrying…
|
||||
- [drive](./src/plugins/drive/drive.md) — `driveUriPlugin(options)` is a factory returning a `UriPlugin` registered against the `drive://` scheme. It wraps a con…
|
||||
- [drive](./src/plugins/drive/drive.md) — `driveUriPlugin(options)` is a factory returning a `UriPlugin` registered against the `drive://` scheme. It wraps a con…
|
||||
- [drive.streams](./src/plugins/drive/drive.streams.md) — `InstrumentedDriveAdapter` is the OTel-instrumented `StorageLike` shared by all three URI plugins (`drive://`, `drive+m…
|
||||
- [drive-mesh](./src/plugins/drive-mesh/drive-mesh.md) — `driveMeshUriPlugin(options)` is a factory returning a `UriPlugin` registered against the `drive+mesh://` scheme. It pa…
|
||||
- [drive-mesh](./src/plugins/drive-mesh/drive-mesh.md) — `driveMeshUriPlugin(options)` is a factory returning a `UriPlugin` registered against the `drive+mesh://` scheme. It pa…
|
||||
- [drive-template](./src/plugins/drive-template/drive-template.md) — `driveTemplateUriPlugin(options)` is a factory returning a `UriPlugin` registered against the `drive+template://` schem…
|
||||
- [drive-template](./src/plugins/drive-template/drive-template.md) — `driveTemplateUriPlugin(options)` is a factory returning a `UriPlugin` registered against the `drive+template://` schem…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/drive-uri @suaveplan/storage-uri @suaveplan/drive
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```ts
|
||||
import { Drive } from "@suaveplan/drive";
|
||||
import { createStorageUriResolver } from "@suaveplan/storage-uri";
|
||||
import {
|
||||
driveMeshUriPlugin,
|
||||
driveTemplateUriPlugin,
|
||||
driveUriPlugin,
|
||||
} from "@suaveplan/drive-uri";
|
||||
|
||||
const localDrive = new Drive(localStorage);
|
||||
await localDrive.initialize();
|
||||
|
||||
const meshDrive = await buildMeshConfiguredDrive(); // your wiring
|
||||
|
||||
const resolver = createStorageUriResolver([
|
||||
driveUriPlugin({ drive: localDrive, driveId: "primary" }),
|
||||
driveMeshUriPlugin({ drive: meshDrive, defaultSwarmId: "swarmA" }),
|
||||
driveTemplateUriPlugin({ drive: meshDrive, templateProvider }),
|
||||
]);
|
||||
|
||||
// Three different schemes, one resolver, one dispatch flow.
|
||||
await resolver.read("drive://primary/folder/file.bin");
|
||||
await resolver.open("drive+mesh://swarmA/raw/clip.mov");
|
||||
await resolver.read("drive+template://templA/manifest/clip.jpg");
|
||||
```
|
||||
|
||||
The three plugins share zero state by default — but you can pass the same
|
||||
`Drive` to two factories so a hydration triggered by the template plugin
|
||||
populates the file-location index that the mesh plugin then uses to fetch
|
||||
chunks. That composition is the canonical pattern for "open a template,
|
||||
then stream its files".
|
||||
|
||||
## Composition with storage-uri plugins
|
||||
|
||||
`drive-uri` plugins coexist with every other URI plugin. A single
|
||||
`StorageUriResolver` can carry a mix of schemes without any code-path
|
||||
divergence — each URI dispatches to the correct adapter based on its scheme
|
||||
prefix, and no plugin observes another plugin's state.
|
||||
|
||||
```ts
|
||||
const resolver = createStorageUriResolver([
|
||||
fileUriPlugin({ rootDir: "/var/media" }),
|
||||
httpsUriPlugin(httpStorage),
|
||||
s3UriPlugin({ bucket: "uploads", region: "eu-west-1" }),
|
||||
driveUriPlugin({ drive: localDrive, driveId: "primary" }),
|
||||
driveMeshUriPlugin({ drive: meshDrive }),
|
||||
driveTemplateUriPlugin({ drive: meshDrive, templateProvider }),
|
||||
]);
|
||||
|
||||
const bytes = await resolver.read("drive://primary/folder/file.bin");
|
||||
const headers = await resolver.read("https://cdn.example/img.png");
|
||||
```
|
||||
|
||||
## Authoring a `TemplateProvider`
|
||||
|
||||
```ts
|
||||
import type { TemplateProvider } from "@suaveplan/drive-uri";
|
||||
import type { DriveTemplate } from "@suaveplan/drive-template";
|
||||
|
||||
export const httpTemplateProvider: TemplateProvider = {
|
||||
async fetchTemplate(templateId: string, signal?: AbortSignal): Promise<DriveTemplate> {
|
||||
const res = await fetch(`/api/templates/${templateId}`, { signal });
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return (await res.json()) as DriveTemplate;
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The provider is invoked exactly once per `templateId` per plugin instance —
|
||||
the hydration cache short-circuits subsequent lookups. On a transient
|
||||
network failure, the cache entry is removed so a retry is possible.
|
||||
|
||||
## Errors
|
||||
|
||||
Every drive-uri failure surfaces as `DriveUriError` with a `DRIVE_URI.*`
|
||||
code:
|
||||
|
||||
| Code | Meaning |
|
||||
|---|---|
|
||||
| `DRIVE_URI.INVALID_URI` | URI is malformed or has an unexpected scheme |
|
||||
| `DRIVE_URI.DRIVE_ID_MISMATCH` | URI's driveId differs from plugin's configured driveId |
|
||||
| `DRIVE_URI.TEMPLATE_NOT_AVAILABLE` | `templateProvider.fetchTemplate` rejected |
|
||||
| `DRIVE_URI.TEMPLATE_INVALID` | `Drive.hydrateFrom` rejected (template validation failed) |
|
||||
| `DRIVE_URI.WRITE_UNSUPPORTED` | Initial release rejects every write |
|
||||
| `DRIVE_URI.CAPABILITY_MISSING` | Required option (drive, templateProvider) missing |
|
||||
| `DRIVE_URI.ABORTED` | `AbortSignal` was already aborted at call time |
|
||||
|
||||
Wrapping rules are documented in the design doc — `templateProvider`
|
||||
rejection becomes `TEMPLATE_NOT_AVAILABLE`; `Drive.hydrateFrom` rejection
|
||||
(any `DTP_*` code) becomes `TEMPLATE_INVALID`. The original cause is
|
||||
preserved on `err.cause` so operators can drill down to the underlying drive
|
||||
failure.
|
||||
|
||||
## Observability
|
||||
|
||||
The package emits to the `@suaveplan/drive-uri` meter and tracer:
|
||||
|
||||
- `drive_uri.op_duration_ms{op, scheme, outcome}` — duration histogram
|
||||
- `drive_uri.reads_total{scheme, outcome}`,
|
||||
`drive_uri.writes_total{scheme, outcome}`,
|
||||
`drive_uri.deletes_total{scheme, outcome}` — counters
|
||||
- `drive_uri.errors_total{scheme, code}` — error attribution
|
||||
- `drive_uri.template_hydrations_total{outcome}` — increments exactly once
|
||||
per first-use template
|
||||
|
||||
Span attributes: `drive.uri` (query-stripped), `drive.uri.scheme`, and one of
|
||||
`drive.uri.drive_id` / `drive.uri.swarm_id` / `drive.uri.template_id`
|
||||
depending on the scheme.
|
||||
|
||||
## Modules
|
||||
|
||||
- [`src/error-codes/error-codes.md`](src/error-codes/error-codes.md) — `DRIVE_URI_CODES` + `DriveUriError`
|
||||
- [`src/adapter/drive-storage-like.md`](src/adapter/drive-storage-like.md) — `DriveStorageLikeAdapter` + `isDriveNotFound`
|
||||
- [`src/plugins/drive/drive.md`](src/plugins/drive/drive.md) — `driveUriPlugin`
|
||||
- [`src/plugins/drive/drive.streams.md`](src/plugins/drive/drive.streams.md) — `InstrumentedDriveAdapter`
|
||||
- [`src/plugins/drive-mesh/drive-mesh.md`](src/plugins/drive-mesh/drive-mesh.md) — `driveMeshUriPlugin`
|
||||
- [`src/plugins/drive-template/drive-template.md`](src/plugins/drive-template/drive-template.md) — `driveTemplateUriPlugin`
|
||||
- [`src/types/plugin-options.types.md`](src/types/plugin-options.types.md) — option-bag interfaces + `TemplateProvider`
|
||||
- [`src/otel/otel.md`](src/otel/otel.md) — OpenTelemetry surface
|
||||
- [`src/conformance/conformance.md`](src/conformance/conformance.md) — re-export of `runUriPluginConformance`
|
||||
|
||||
Reference in New Issue
Block a user