6.6 KiB
@suaveplan/storage-uri - Universal URI-routed dispatch over @suaveplan StorageLike adapters. One resolver, one URI string in, one stream/byte/metadata out; scheme→plugin registry, AbortSignal everywhere, full OTel surface, zero I/O of its own.
Universal URI-routed dispatch over @suaveplan StorageLike adapters.
One resolver, one URI string in, one stream/byte/metadata out.
Scheme→plugin registry; the resolver performs no I/O of its own — every
operation delegates to a registered StorageLike after URI parsing.
Documentation tree
- conformance —
runUriPluginConformance(plugin, fixtures)is the test harness adapter packages call from their own suites to prove th…- conformance —
runUriPluginConformance(plugin, fixtures)is the test harness adapter packages call from their own suites to prove th…
- conformance —
- error-codes — Defines the canonical
STORAGE_URI_CODESregistry and theStorageUriErrorclass for the@suaveplan/storage-uripac…- error-codes — Defines the canonical
STORAGE_URI_CODESregistry and theStorageUriErrorclass for the@suaveplan/storage-uripac…
- error-codes — Defines the canonical
- otel — Owns the OpenTelemetry instrument bag for
@suaveplan/storage-uri: meter, tracer, and the six counter + one histogram…- otel — Owns the OpenTelemetry instrument bag for
@suaveplan/storage-uri: meter, tracer, and the six counter + one histogram…
- otel — Owns the OpenTelemetry instrument bag for
- parse-uri —
parseUri(uri)is the RFC 3986 subset parser the resolver uses to extract a scheme, optional authority, path, and opti…- parse-uri —
parseUri(uri)is the RFC 3986 subset parser the resolver uses to extract a scheme, optional authority, path, and opti…
- parse-uri —
- resolver — The
StorageUriResolverclass is the entry point for URI-routed dispatch in@suaveplan/storage-uri. Constructed once…- resolver.list — Implements the
list(uri, options?, signal?)semantics. Treats the input URI's key portion as a listing prefix, delega… - resolver — The
StorageUriResolverclass is the entry point for URI-routed dispatch in@suaveplan/storage-uri. Constructed once… - resolver.streams — Implements the
open(uri, signal?)semantics for the resolver: forward a nativecreateReadStreamwhen the adapter ex… - resolver.writes — Implements the
write(uri, body, signal?)semantics. Accepts eitherUint8ArrayorReadableStream<Uint8Array>for t…
- resolver.list — Implements the
Install
bun add @suaveplan/storage-uri
Pair with one or more adapter packages — each ships its own uriPlugin:
bun add @suaveplan/storage-file @suaveplan/storage-http
Modules
| Subpath | What |
|---|---|
@suaveplan/storage-uri |
Full surface — re-exports everything below |
@suaveplan/storage-uri/error-codes |
STORAGE_URI_CODES, StorageUriError |
@suaveplan/storage-uri/parse-uri |
parseUri(uri) RFC 3986 subset |
@suaveplan/storage-uri/resolver |
StorageUriResolver, createStorageUriResolver |
@suaveplan/storage-uri/otel |
OTel meter+tracer instruments |
@suaveplan/storage-uri/conformance |
runUriPluginConformance for adapter authors |
@suaveplan/storage-uri/types |
UriPlugin, ParsedUri, StatResult, list types |
Quick start
import { createStorageUriResolver } from "@suaveplan/storage-uri";
import { fileUriPlugin } from "@suaveplan/storage-file";
import { httpsUriPlugin, createHttpStorage } from "@suaveplan/storage-http";
const resolver = createStorageUriResolver([
fileUriPlugin({ rootDir: "/var/cache/media" }),
httpsUriPlugin(createHttpStorage({ baseUrl: "https://cdn.example.com" })),
]);
const stream = await resolver.open("https://cdn.example.com/clip/master.m3u8");
await resolver.write("file:///cache/poster.jpg", thumbBytes);
Cross-scheme media-pipeline recipe
const resolver = createStorageUriResolver([
fileUriPlugin({ rootDir: "/var/media" }),
httpsUriPlugin(httpStorage),
]);
async function thumbnail(srcUri: string, dstUri: string): Promise<void> {
const src = await resolver.open(srcUri);
const out = await encodeJpegFromStream(src);
await resolver.write(dstUri, out);
}
A single call site composes a filesystem cache, a CDN origin, and any other adapter you register — the resolver hides which scheme served the request.
Authoring a third-party UriPlugin
import type { UriPlugin } from "@suaveplan/storage-uri";
import type { StorageLike } from "@suaveplan/types";
export function memoryUriPlugin(adapter: StorageLike): UriPlugin {
return {
scheme: "memory",
adapter,
parse(uri) {
const url = new URL(uri);
return { key: url.pathname.replace(/^\//, ""), meta: { authority: url.host } };
},
build(key, meta) {
const auth = meta?.authority ?? "default";
return `memory://${auth}/${key}`;
},
};
}
Verify your plugin against the conformance harness from your own test suite:
import { runUriPluginConformance } from "@suaveplan/storage-uri/conformance";
runUriPluginConformance(memoryUriPlugin(adapter), {
validUris: ["memory://x/a"],
invalidUris: ["bad", "memory:"],
buildRoundTrip: [{ key: "a", uri: "memory://x/a", meta: { authority: "x" } }],
});
Error codes
| Code | When |
|---|---|
STORAGE_URI.UNKNOWN_SCHEME |
URI scheme has no plugin registered |
STORAGE_URI.INVALID_URI |
parseUri rejected the input |
STORAGE_URI.SCHEME_CONFLICT |
Two distinct plugins claim the same scheme |
STORAGE_URI.NOT_FOUND |
Adapter returned undefined for get/open |
STORAGE_URI.LIST_UNSUPPORTED |
Plugin has no build() |
STORAGE_URI.WRITE_UNSUPPORTED |
Adapter threw on set (e.g. read-only HTTP) |
STORAGE_URI.ABORTED |
AbortSignal fired |
STORAGE_URI.ADAPTER_ERROR |
Adapter threw something else — cause preserved |
OpenTelemetry
Tracer + meter name: @suaveplan/storage-uri. Counters:
reads_total, writes_total, deletes_total, errors_total,
stream_fallback_total, buffered_write_total. Histogram:
op_duration_ms{op,scheme,outcome}. Spans on every public op with
storage.uri, storage.uri.scheme, storage.uri.key attributes;
storage.uri is redacted (query string + fragment stripped) so presigned
URLs cannot leak into telemetry sinks.
License
MIT