Initial Commit
This commit is contained in:
199
openspec/changes/complete-storage-uri-adapters/tasks.md
Normal file
199
openspec/changes/complete-storage-uri-adapters/tasks.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# 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 status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in 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.md` matches `find packages -maxdepth 3 -name package.json` count. 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`, and `specs/storage-uri/spec.md` — confirm the
|
||||
`UriPlugin` contract and the `runUriPluginConformance` harness
|
||||
requirements
|
||||
- [ ] 0.3 Read `packages/storage/storage-uri/src/types/plugin.types.ts`
|
||||
(`UriPlugin`, `ParsedUri`, `StreamingStorageExtensions`,
|
||||
`StatResult`) and
|
||||
`packages/storage/storage-uri/src/conformance/conformance.ts`
|
||||
(`runUriPluginConformance`, `UriPluginFixtures`) verbatim
|
||||
- [ ] 0.4 Read each of the four adapter packages' `index.ts` to map the
|
||||
exported surface the plugin must wrap
|
||||
- [ ] 0.5 Confirm no adapter already ships a `uri-plugin` (verified by
|
||||
`find 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-uri` is on
|
||||
`workspace:^` 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.ts`
|
||||
helper to `@suaveplan/storage-http` (used by URI redaction policy)
|
||||
and re-use the same module from `bunnycdn` and `rackspace` via
|
||||
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 against `rootDir`
|
||||
- [ ] 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 via `runUriPluginConformance`
|
||||
with `validUris` (POSIX + Windows + `file://localhost/...`),
|
||||
`invalidUris` (`file:`, `file://host/x`, `file:///../../etc/passwd`),
|
||||
and `buildRoundTrip` pairs
|
||||
- [ ] 2.7 Add `"./uri-plugin/uri-plugin"` subpath export +
|
||||
`"./uri-plugin/error-codes"` to `package.json`
|
||||
- [ ] 2.8 Add named re-exports in `src/index.ts` (explicit — no
|
||||
`export *`, per CLAUDE.md rule 10)
|
||||
- [ ] 2.9 Update `README.md` `## Modules` with the new entries
|
||||
|
||||
## 3. `@suaveplan/storage-http` — `httpUriPlugin` + `httpsUriPlugin`
|
||||
|
||||
- [ ] 3.1 `src/uri-plugin/uri-plugin.ts` — internal `buildHttpUriPlugin`
|
||||
builder; `httpUriPlugin(options)` and `httpsUriPlugin(options)`
|
||||
factories differing only in `scheme`
|
||||
- [ ] 3.2 `src/uri-plugin/uri-plugin.ts` — full RFC 3986 parse via
|
||||
`parseUri`, 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
|
||||
raise `STORAGE_HTTP_URI.RANGE_INVALID`; on success route to
|
||||
`adapter.getPartial(key, { offset, length })`
|
||||
- [ ] 3.4 `src/uri-plugin/uri-plugin.ts` — redirect-loop classification
|
||||
(re-wrap `STORAGE_HTTP.REDIRECT_LOOP` if/when adapter raises;
|
||||
fallback maps any `Response.status === 310` or fetch loop signal)
|
||||
- [ ] 3.5 `src/uri-plugin/uri-plugin.ts` — `build(key, meta?)` inverse
|
||||
via `new URL(key, adapter.options.baseUrl)` with `scheme`
|
||||
substitution for the `http`/`https` factory 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` — happy `http://` +
|
||||
`https://` round-trips, query-string preservation, range parsing
|
||||
success + failure, redirect loop wrap, redaction unit tests, full
|
||||
`runUriPluginConformance` per factory
|
||||
- [ ] 3.10 Subpath exports in `package.json` for 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
|
||||
(`bunnycdn` and `bunnycdn+pull`) via the same factory returning
|
||||
one of two `UriPlugin` instances (the resolver registers each
|
||||
explicitly)
|
||||
- [ ] 4.2 `src/uri-plugin/uri-plugin.ts` — storage-zone parse:
|
||||
`bunnycdn://<zone>/<path>`, zone mismatch raises
|
||||
`STORAGE_CDN_BUNNY_URI.ZONE_MISMATCH`
|
||||
- [ ] 4.3 `src/uri-plugin/uri-plugin.ts` — pull-zone parse:
|
||||
`bunnycdn+pull://<pull-zone>/<path>`; `set`/`delete` reject with
|
||||
`UNSUPPORTED_OPERATION`
|
||||
- [ ] 4.4 `src/uri-plugin/uri-plugin.ts` — signed-URL handling: parse
|
||||
`?token=...&expires=...`, forward to existing
|
||||
`buildTokenAuthQuery` helper. Token bytes NEVER persisted on
|
||||
`meta`
|
||||
- [ ] 4.5 `src/uri-plugin/uri-plugin.ts` — `build(key, meta?)` per
|
||||
surface (`storage` vs `pull` based on `meta.surface`)
|
||||
- [ ] 4.6 `src/uri-plugin/redact-signed-url.ts` — strips
|
||||
`token`, `signature`, `expires`
|
||||
- [ ] 4.7 `src/uri-plugin/error-codes.ts` —
|
||||
`defineCodes("STORAGE_CDN_BUNNY_URI", [...])` with 6 codes
|
||||
- [ ] 4.8 Co-located `.md`s 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 raises
|
||||
`CONTAINER_MISMATCH`
|
||||
- [ ] 5.3 `src/uri-plugin/uri-plugin.ts` — temp-URL signed-URL
|
||||
verification via existing `buildTempUrlSignature`, on mismatch
|
||||
raise `TEMP_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` — strips
|
||||
`temp_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 `.md`s
|
||||
- [ ] 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.ts` MUST import
|
||||
`runUriPluginConformance` from `@suaveplan/storage-uri/conformance/conformance`
|
||||
and 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.ts`
|
||||
composing 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 use `node:` (server-tier
|
||||
adapter); `storage-http/src/uri-plugin/` MUST be universal
|
||||
- [ ] 6.4 Test-runner imports — every new `.test.ts` imports from
|
||||
`@suaveplan/testing/runner` only (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-`.md` scan 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-uri` green after the
|
||||
four plugins land
|
||||
|
||||
## 8. Manifest & archival
|
||||
|
||||
- [ ] 8.1 `bun scripts/folder-reorg/generate-manifest.ts` to refresh
|
||||
`MANIFEST.md` (export-graph change)
|
||||
- [ ] 8.2 `bunx markdownlint-cli2 --fix` on touched files
|
||||
- [ ] 8.3 Fold each `specs/<adapter>/spec.md` into
|
||||
`openspec/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/`
|
||||
Reference in New Issue
Block a user