Initial Commit
This commit is contained in:
@@ -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,157 @@
|
||||
import { trace } from "@opentelemetry/api";
|
||||
import { createStorageUriResolver } from "@suaveplan/storage-uri";
|
||||
import { beforeEach, describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import { asDrive, FakeDrive } from "../../../tests/setup/fake-drive.js";
|
||||
import {
|
||||
DRIVE_URI_CODES,
|
||||
DriveUriError,
|
||||
} from "../../error-codes/error-codes.js";
|
||||
import { driveMeshUriPlugin } from "./drive-mesh.js";
|
||||
|
||||
interface SpanLike {
|
||||
setAttribute(name: string, value: unknown): SpanLike;
|
||||
end(): void;
|
||||
readonly attributes: Map<string, unknown>;
|
||||
}
|
||||
|
||||
function makeRecordingSpan(): SpanLike {
|
||||
const attrs = new Map<string, unknown>();
|
||||
return {
|
||||
attributes: attrs,
|
||||
setAttribute(name: string, value: unknown): SpanLike {
|
||||
attrs.set(name, value);
|
||||
return this;
|
||||
},
|
||||
end(): void {},
|
||||
};
|
||||
}
|
||||
|
||||
describe("driveMeshUriPlugin", () => {
|
||||
let fake: FakeDrive;
|
||||
|
||||
beforeEach(() => {
|
||||
fake = new FakeDrive({
|
||||
contents: new Map([["raw/clip.mov", new Uint8Array([1, 2, 3])]]),
|
||||
});
|
||||
});
|
||||
|
||||
it("requires options.drive at construction", { timeout: 5000 }, () => {
|
||||
expect(() =>
|
||||
driveMeshUriPlugin({
|
||||
drive: undefined as unknown as Parameters<
|
||||
typeof driveMeshUriPlugin
|
||||
>[0]["drive"],
|
||||
})
|
||||
).toThrow(DriveUriError);
|
||||
});
|
||||
|
||||
it("rejects non-mesh schemes", { timeout: 5000 }, () => {
|
||||
const plugin = driveMeshUriPlugin({ drive: asDrive(fake) });
|
||||
try {
|
||||
plugin.parse("drive://primary/x");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(DriveUriError);
|
||||
expect((err as DriveUriError).code).toBe(
|
||||
DRIVE_URI_CODES.INVALID_URI
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects URIs without an authority", { timeout: 5000 }, () => {
|
||||
const plugin = driveMeshUriPlugin({ drive: asDrive(fake) });
|
||||
expect(() => plugin.parse("drive+mesh:///x")).toThrow(DriveUriError);
|
||||
});
|
||||
|
||||
it("rejects URIs without a path", { timeout: 5000 }, () => {
|
||||
const plugin = driveMeshUriPlugin({ drive: asDrive(fake) });
|
||||
expect(() => plugin.parse("drive+mesh://swarmA/")).toThrow(
|
||||
DriveUriError
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects URIs with no authority component (no //)", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const plugin = driveMeshUriPlugin({ drive: asDrive(fake) });
|
||||
// `drive+mesh:x` has scheme + path but no authority — covers the
|
||||
// `parts.authority ?? ""` fallback branch.
|
||||
expect(() => plugin.parse("drive+mesh:x")).toThrow(DriveUriError);
|
||||
});
|
||||
|
||||
it("dispatches a drive+mesh:// URI to the configured Drive", {
|
||||
timeout: 5000,
|
||||
}, async () => {
|
||||
const plugin = driveMeshUriPlugin({ drive: asDrive(fake) });
|
||||
const resolver = createStorageUriResolver([plugin]);
|
||||
const bytes = await resolver.read("drive+mesh://swarmA/raw/clip.mov");
|
||||
expect(bytes).toEqual(new Uint8Array([1, 2, 3]));
|
||||
expect(fake.calls.read).toHaveLength(1);
|
||||
expect(fake.calls.read[0]?.path).toBe("raw/clip.mov");
|
||||
});
|
||||
|
||||
it("surfaces swarmId on the emitted span", { timeout: 5000 }, () => {
|
||||
const span = makeRecordingSpan();
|
||||
const plugin = driveMeshUriPlugin({ drive: asDrive(fake) });
|
||||
// Patch the OTel API's getActiveSpan to return our recording span
|
||||
// for the duration of this test. Without an SDK-installed context
|
||||
// manager, `trace.setSpan(context, span)` is a no-op (the noop
|
||||
// context manager doesn't propagate); patching at the API layer is
|
||||
// the lightest-weight test seam compatible with `@opentelemetry/api`
|
||||
// alone (no `@opentelemetry/sdk-trace-base` dev dependency needed).
|
||||
const originalGetActiveSpan = trace.getActiveSpan.bind(trace);
|
||||
trace.getActiveSpan = (() =>
|
||||
span as unknown as ReturnType<
|
||||
typeof trace.getActiveSpan
|
||||
>) as typeof trace.getActiveSpan;
|
||||
try {
|
||||
plugin.parse("drive+mesh://swarmA/raw/clip.mov?peers=secret");
|
||||
} finally {
|
||||
trace.getActiveSpan = originalGetActiveSpan;
|
||||
}
|
||||
expect(span.attributes.get("drive.uri.swarm_id")).toBe("swarmA");
|
||||
expect(span.attributes.get("drive.uri.scheme")).toBe("drive+mesh");
|
||||
expect(span.attributes.get("drive.uri")).toBe(
|
||||
"drive+mesh://swarmA/raw/clip.mov"
|
||||
);
|
||||
});
|
||||
|
||||
it("does not attempt span attribute writes when no span is active", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const plugin = driveMeshUriPlugin({ drive: asDrive(fake) });
|
||||
// No active span — must not throw.
|
||||
expect(() =>
|
||||
plugin.parse("drive+mesh://swarmA/raw/clip.mov")
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it("build round-trips swarmId and key into a drive+mesh:// URI", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const plugin = driveMeshUriPlugin({ drive: asDrive(fake) });
|
||||
expect(plugin.build?.("raw/clip.mov", { swarmId: "swarmA" })).toBe(
|
||||
"drive+mesh://swarmA/raw/clip.mov"
|
||||
);
|
||||
});
|
||||
|
||||
it("build defaults swarmId to options.defaultSwarmId when meta is omitted", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const plugin = driveMeshUriPlugin({
|
||||
drive: asDrive(fake),
|
||||
defaultSwarmId: "fallback",
|
||||
});
|
||||
expect(plugin.build?.("raw/clip.mov")).toBe(
|
||||
"drive+mesh://fallback/raw/clip.mov"
|
||||
);
|
||||
});
|
||||
|
||||
it("build uses 'default' when neither meta nor defaultSwarmId is provided", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const plugin = driveMeshUriPlugin({ drive: asDrive(fake) });
|
||||
expect(plugin.build?.("clip.mov")).toBe(
|
||||
"drive+mesh://default/clip.mov"
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import { trace } from "@opentelemetry/api";
|
||||
import type { Drive } from "@suaveplan/drive";
|
||||
import {
|
||||
type ParsedUri,
|
||||
parseUri,
|
||||
type UriPlugin,
|
||||
} from "@suaveplan/storage-uri";
|
||||
import {
|
||||
DRIVE_URI_CODES,
|
||||
DriveUriError,
|
||||
} from "../../error-codes/error-codes.js";
|
||||
import { redactUri } from "../../otel/otel.js";
|
||||
import type { DriveMeshUriPluginOptions } from "../../types/plugin-options.types.js";
|
||||
import { InstrumentedDriveAdapter } from "../drive/drive.streams.js";
|
||||
|
||||
const SCHEME = "drive+mesh";
|
||||
|
||||
/**
|
||||
* `drive+mesh://<swarmId>/<path>` URI plugin factory.
|
||||
*
|
||||
* Identical in shape to `driveUriPlugin` but captures the URI authority as
|
||||
* `swarmId` rather than enforcing a `driveId` match. The plugin does NOT use
|
||||
* `swarmId` to choose a `Drive` or transport — peer routing is the
|
||||
* caller-supplied `Drive`'s job. The swarmId surfaces on every emitted span
|
||||
* via the `drive.uri.swarm_id` attribute (set on the active span when one
|
||||
* exists; otherwise a new short-lived span is opened so the attribute is
|
||||
* not lost).
|
||||
*/
|
||||
export function driveMeshUriPlugin(
|
||||
options: DriveMeshUriPluginOptions
|
||||
): UriPlugin {
|
||||
if (options.drive === undefined || options.drive === null) {
|
||||
throw new DriveUriError(
|
||||
DRIVE_URI_CODES.CAPABILITY_MISSING,
|
||||
"driveMeshUriPlugin requires options.drive",
|
||||
{ scheme: SCHEME, reason: "drive" }
|
||||
);
|
||||
}
|
||||
const adapter = new MeshAdapter(options.drive);
|
||||
const defaultSwarmId = options.defaultSwarmId ?? "default";
|
||||
return {
|
||||
scheme: SCHEME,
|
||||
adapter,
|
||||
parse(uri: string): ParsedUri {
|
||||
const parts = parseUri(uri);
|
||||
if (parts.scheme !== SCHEME) {
|
||||
throw new DriveUriError(
|
||||
DRIVE_URI_CODES.INVALID_URI,
|
||||
`expected scheme '${SCHEME}', got '${parts.scheme}'`,
|
||||
{ uri, scheme: SCHEME }
|
||||
);
|
||||
}
|
||||
const swarmId = parts.authority ?? "";
|
||||
if (swarmId.length === 0) {
|
||||
throw new DriveUriError(
|
||||
DRIVE_URI_CODES.INVALID_URI,
|
||||
"drive+mesh:// URI is missing the swarmId authority",
|
||||
{ uri, scheme: SCHEME }
|
||||
);
|
||||
}
|
||||
const key = parts.path.replace(/^\//, "");
|
||||
if (key.length === 0) {
|
||||
throw new DriveUriError(
|
||||
DRIVE_URI_CODES.INVALID_URI,
|
||||
"drive+mesh:// URI must include a non-empty path component",
|
||||
{ uri, scheme: SCHEME, swarmId }
|
||||
);
|
||||
}
|
||||
adapter.recordSwarm(swarmId, uri);
|
||||
return { key, meta: { swarmId } };
|
||||
},
|
||||
build(key: string, meta?: Record<string, string>): string {
|
||||
const swarmId = meta?.["swarmId"] ?? defaultSwarmId;
|
||||
return `${SCHEME}://${swarmId}/${key.replace(/^\//, "")}`;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mesh-tier adapter — extends `InstrumentedDriveAdapter` and writes the
|
||||
* `drive.uri.swarm_id` and `drive.uri` attributes onto the currently active
|
||||
* span so they appear alongside the resolver's own `storage.uri.*` span.
|
||||
*
|
||||
* `recordSwarm` is invoked from the plugin's `parse` step (which the
|
||||
* resolver runs synchronously before delegating to the adapter), so by the
|
||||
* time `get`/`set`/etc. fires, the attributes are already on the active
|
||||
* span. Tests that exercise `parse(uri)` directly cover the path; tests
|
||||
* routed through `StorageUriResolver` exercise it via the dispatch flow.
|
||||
*/
|
||||
class MeshAdapter extends InstrumentedDriveAdapter {
|
||||
public constructor(drive: Drive) {
|
||||
super(drive, { scheme: SCHEME });
|
||||
}
|
||||
|
||||
public recordSwarm(swarmId: string, uri: string): void {
|
||||
const span = trace.getActiveSpan();
|
||||
if (span !== undefined) {
|
||||
span.setAttribute("drive.uri.scheme", SCHEME);
|
||||
span.setAttribute("drive.uri.swarm_id", swarmId);
|
||||
span.setAttribute("drive.uri", redactUri(uri));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { driveMeshUriPlugin } from "./drive-mesh.js";
|
||||
Reference in New Issue
Block a user