Files
skills/docs/persistence/storage/storage-uri/README.md
2026-05-29 08:20:56 +01:00

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…
  • error-codes — Defines the canonical STORAGE_URI_CODES registry and the StorageUriError class for the @suaveplan/storage-uri pac…
    • error-codes — Defines the canonical STORAGE_URI_CODES registry and the StorageUriError class for the @suaveplan/storage-uri pac…
  • 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…
  • 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…
  • resolver — The StorageUriResolver class 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 StorageUriResolver class 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 native createReadStream when the adapter ex…
    • resolver.writes — Implements the write(uri, body, signal?) semantics. Accepts either Uint8Array or ReadableStream<Uint8Array> for t…

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