Files
suaveplan/packages/networking/drive/drive-uri/src/plugins/drive-template/drive-template.md
2026-05-27 23:47:53 +01:00

4.4 KiB

@suaveplan/drive-uri/plugins/drive-template

(no submodules)

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

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:

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.