10 KiB
10 KiB
Tasks — complete-storage-uri-adapters
Every checkbox MUST reflect work actually done in this change.
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin each) - 0.2 If any worktrees exist under
.claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - 0.3 Confirm the package set in
MANIFEST.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
0. Pre-flight
- 0.1 Read
openspec/AGENTS.md§0c, §13, §14 - 0.2 Read
openspec/archive/2026-05-16-add-storage-uri/proposal.md,tasks.md, andspecs/storage-uri/spec.md— confirm theUriPlugincontract and therunUriPluginConformanceharness requirements - 0.3 Read
packages/storage/storage-uri/src/types/plugin.types.ts(UriPlugin,ParsedUri,StreamingStorageExtensions,StatResult) andpackages/storage/storage-uri/src/conformance/conformance.ts(runUriPluginConformance,UriPluginFixtures) verbatim - 0.4 Read each of the four adapter packages'
index.tsto map the exported surface the plugin must wrap - 0.5 Confirm no adapter already ships a
uri-plugin(verified byfind packages/storage/storage-{file,http,cdn-bunnycdn,cdn-rackspace} -name 'uri-plugin*'returning empty) - 0.6 Build the numbered Requirement/Scenario implementation checklist (per §0c step 6) — one per adapter, plus shared scaffolding row
1. Shared scaffolding
- 1.1 Confirm
@suaveplan/storage-uriis onworkspace:^in each of the four adapter packages'devDependencies(for the conformance test only — no runtime dep) - 1.2 Add a co-located
redact-signed-url/redact-signed-url.tshelper to@suaveplan/storage-http(used by URI redaction policy) and re-use the same module frombunnycdnandrackspacevia adapter-local mirrors (do NOT cross-import — each package keeps its own redaction list per scheme)
2. @suaveplan/storage-file — fileUriPlugin
- 2.1
src/uri-plugin/uri-plugin.ts—fileUriPlugin(options)factory; scheme"file";parse(uri)with RFC 8089 authority handling, Windows drive-letter detection (/^[A-Za-z]:/after stripping leading/), and..traversal rejection againstrootDir - 2.2
src/uri-plugin/uri-plugin.ts—build(key)inverse with RFC 3986 segment-percent-encoding and Windows drive-letter preservation - 2.3
src/uri-plugin/error-codes.ts—defineCodes("STORAGE_FILE_URI", [...])with the 6 codes;StorageFileUriError extends SuaveplanError - 2.4
src/uri-plugin/uri-plugin.md(≥200 words, basic + advanced) - 2.5
src/uri-plugin/error-codes.md - 2.6
src/uri-plugin/uri-plugin.test.ts— POSIX paths, Windows drive-letter, relative-vs-absolute,..escape, permission-denied wrap, conformance viarunUriPluginConformancewithvalidUris(POSIX + Windows +file://localhost/...),invalidUris(file:,file://host/x,file:///../../etc/passwd), andbuildRoundTrippairs - 2.7 Add
"./uri-plugin/uri-plugin"subpath export +"./uri-plugin/error-codes"topackage.json - 2.8 Add named re-exports in
src/index.ts(explicit — noexport *, per CLAUDE.md rule 10) - 2.9 Update
README.md## Moduleswith the new entries
3. @suaveplan/storage-http — httpUriPlugin + httpsUriPlugin
- 3.1
src/uri-plugin/uri-plugin.ts— internalbuildHttpUriPluginbuilder;httpUriPlugin(options)andhttpsUriPlugin(options)factories differing only inscheme - 3.2
src/uri-plugin/uri-plugin.ts— full RFC 3986 parse viaparseUri, key =path + (query ? "?" + query : "")so presigned query strings survive - 3.3
src/uri-plugin/uri-plugin.ts— range parsing via?range=bytes=<offset>-<end>query parameter; on parse failure raiseSTORAGE_HTTP_URI.RANGE_INVALID; on success route toadapter.getPartial(key, { offset, length }) - 3.4
src/uri-plugin/uri-plugin.ts— redirect-loop classification (re-wrapSTORAGE_HTTP.REDIRECT_LOOPif/when adapter raises; fallback maps anyResponse.status === 310or fetch loop signal) - 3.5
src/uri-plugin/uri-plugin.ts—build(key, meta?)inverse vianew URL(key, adapter.options.baseUrl)withschemesubstitution for thehttp/httpsfactory split - 3.6
src/uri-plugin/redact-signed-url.ts— case-insensitive deny-list strip; exported for reuse and unit-tested - 3.7
src/uri-plugin/error-codes.ts—defineCodes("STORAGE_HTTP_URI", [...])with 6 codes;StorageHttpUriError - 3.8
src/uri-plugin/uri-plugin.md,redact-signed-url.md,error-codes.md - 3.9
src/uri-plugin/uri-plugin.test.ts— happyhttp://+https://round-trips, query-string preservation, range parsing success + failure, redirect loop wrap, redaction unit tests, fullrunUriPluginConformanceper factory - 3.10 Subpath exports in
package.jsonfor the three new files - 3.11 Named re-exports in
src/index.ts - 3.12 README modules update
4. @suaveplan/storage-cdn-bunnycdn — bunnyUriPlugin
- 4.1
src/uri-plugin/uri-plugin.ts—bunnyUriPlugin(options)factory; ONE plugin registered against TWO schemes (bunnycdnandbunnycdn+pull) via the same factory returning one of twoUriPlugininstances (the resolver registers each explicitly) - 4.2
src/uri-plugin/uri-plugin.ts— storage-zone parse:bunnycdn://<zone>/<path>, zone mismatch raisesSTORAGE_CDN_BUNNY_URI.ZONE_MISMATCH - 4.3
src/uri-plugin/uri-plugin.ts— pull-zone parse:bunnycdn+pull://<pull-zone>/<path>;set/deletereject withUNSUPPORTED_OPERATION - 4.4
src/uri-plugin/uri-plugin.ts— signed-URL handling: parse?token=...&expires=..., forward to existingbuildTokenAuthQueryhelper. Token bytes NEVER persisted onmeta - 4.5
src/uri-plugin/uri-plugin.ts—build(key, meta?)per surface (storagevspullbased onmeta.surface) - 4.6
src/uri-plugin/redact-signed-url.ts— stripstoken,signature,expires - 4.7
src/uri-plugin/error-codes.ts—defineCodes("STORAGE_CDN_BUNNY_URI", [...])with 6 codes - 4.8 Co-located
.mds for every implementation file - 4.9
src/uri-plugin/uri-plugin.test.ts— zone match/mismatch, pull-zone read-only enforcement, signed-URL passthrough, redaction, full conformance harness - 4.10 Subpath exports + named re-exports + README
5. @suaveplan/storage-cdn-rackspace — rackspaceUriPlugin
- 5.1
src/uri-plugin/uri-plugin.ts—rackspaceUriPlugin(options)factory; scheme"rackspace"; authority = container, path = object key - 5.2
src/uri-plugin/uri-plugin.ts— authority_or empty rebinds to configured container; mismatched container raisesCONTAINER_MISMATCH - 5.3
src/uri-plugin/uri-plugin.ts— temp-URL signed-URL verification via existingbuildTempUrlSignature, on mismatch raiseTEMP_URL_INVALID - 5.4
src/uri-plugin/uri-plugin.ts—build(key, meta?)with optional?region=<region>echo - 5.5
src/uri-plugin/redact-signed-url.ts— stripstemp_url_sig,temp_url_expires,X-Auth-Token - 5.6
src/uri-plugin/error-codes.ts—defineCodes("STORAGE_CDN_RACKSPACE_URI", [...])with 6 codes - 5.7 Co-located
.mds - 5.8
src/uri-plugin/uri-plugin.test.ts— container match/mismatch, auth-token threading, temp-URL valid/invalid, region echo, redaction, conformance - 5.9 Subpath exports + named re-exports + README
6. Cross-package conformance
- 6.1 Each adapter's
uri-plugin.test.tsMUST importrunUriPluginConformancefrom@suaveplan/storage-uri/conformance/conformanceand call it with a fixture set covering valid/invalid/round-trip - 6.2 Add a cross-scheme integration test in
packages/storage/storage-uri/tests/integration/all-four-schemes.test.tscomposing all four plugins behind one resolver and asserting end-to-end dispatch - 6.3 Tier purity:
grep -rnE "from ['\"](node:|bun:)" packages/storage/storage-cdn-*/src/uri-plugin/returns empty (CDN plugins are universal-tier);storage-file/src/uri-plugin/MAY usenode:(server-tier adapter);storage-http/src/uri-plugin/MUST be universal - 6.4 Test-runner imports — every new
.test.tsimports from@suaveplan/testing/runneronly (rule 11)
7. Gates (per adapter package)
For each of storage-file, storage-http, storage-cdn-bunnycdn,
storage-cdn-rackspace:
- 7.1
bun run check - 7.2
bunx turbo lint typecheck test --filter=@suaveplan/<pkg> - 7.3
bunx turbo test:coverage --filter=@suaveplan/<pkg>(100/100/100/100 istanbul) - 7.4 Banned-pattern scan returns zero
- 7.5 Missing-
.mdscan returns zero - 7.6 Subpath export count matches feature module count
- 7.7
bun scripts/openspec-conformance.ts @suaveplan/<pkg> - 7.8 Dependent sweep —
bunx turbo test --filter=...@suaveplan/<pkg>green - 7.9 Cross-package: full
bunx turbo test --filter=@suaveplan/storage-urigreen after the four plugins land
8. Manifest & archival
- 8.1
bun scripts/folder-reorg/generate-manifest.tsto refreshMANIFEST.md(export-graph change) - 8.2
bunx markdownlint-cli2 --fixon touched files - 8.3 Fold each
specs/<adapter>/spec.mdintoopenspec/specs/<adapter>/spec.md(one per adapter) - 8.4 Move
openspec/changes/complete-storage-uri-adapters/→openspec/archive/<yyyy-mm-dd>-complete-storage-uri-adapters/