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-functionfetchTemplatethrowsDRIVE_URI.CAPABILITY_MISSINGsynchronously, never deferred to first use. - Per-templateId hydration memoisation with concurrent-read coalescing.
- Distinct error mappings —
fetchTemplaterejection becomesDRIVE_URI.TEMPLATE_NOT_AVAILABLE;Drive.hydrateFromrejection becomesDRIVE_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 aTEMPLATE_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— bothoptions.driveandoptions.templateProviderare required at construction.templateProvider.fetchTemplateMUST be a function.- The returned plugin exposes
scheme === "drive+template", an instrumented adapter,parse(uri) → { key, meta: { templateId } }, andbuild(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 theDrive's index viahydrateFrom). - Failed hydrations remove themselves from the cache so retries are
possible. A re-hydration after a previous failure is idempotent at the
Drivelayer viaTemplateNonceLru. parse(uri)captures the templateId in a closure-scoped variable so the adapter'sbeforeOphook (which fires synchronously inside the resolver's dispatch chain) sees the right value before anyget/exists/deletecall.- Writes are rejected at the adapter tier with
DRIVE_URI.WRITE_UNSUPPORTED, per the per-release scope.