198 lines
25 KiB
Markdown
198 lines
25 KiB
Markdown
# Complete `@suaveplan/drive-uri` — full per-scheme write + range-read parity, every operation, every option
|
|
|
|
**Package**: `@suaveplan/drive-uri` (+ MODIFIED surface on `@suaveplan/drive`)
|
|
**Status**: PROPOSED
|
|
**Type**: capability-completion (single change supersedes the two narrow follow-ups)
|
|
**Supersedes**: `openspec/changes/_superseded-narrow-deferrals-2026-05-17/add-drive-uri-writes/` AND `…/add-drive-range-reads/` — both deferrals land in this single change.
|
|
**Depends on**: `add-drive-uri` (archived `2026-05-16`), `add-storage-uri` (archived).
|
|
|
|
- **Design**: ./design.md
|
|
- **Spec deltas**:
|
|
- ./specs/drive-uri/spec.md (primary)
|
|
- ./specs/drive/spec.md (MODIFIED — `createReadStream` range overload)
|
|
|
|
## Why
|
|
|
|
`add-drive-uri` shipped read-only with two explicit deferrals — **writes** and **range reads** — plus a documented gap on **buffered-read AbortSignal threading** (carried as a footnote in the canonical spec). Two narrow follow-ups (`add-drive-uri-writes`, `add-drive-range-reads`) were drafted, then superseded so the package can land at functional completeness in a single coherent change. This change closes every gap and brings `drive-uri` to full parity with what `Drive` itself exposes, so every URI operation is supported with first-class semantics.
|
|
|
|
Concretely, the gaps closed:
|
|
|
|
| # | Gap | Source of deferral |
|
|
|---|--------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|
|
|
| 1 | `drive://` writes / removes / write-streams | `add-drive-uri` proposal § Out of scope (line 32) |
|
|
| 2 | `drive+mesh://` writes / removes / write-streams | `add-drive-uri` proposal § Out of scope (line 32) |
|
|
| 3 | `drive+template://` writes permanently locked-down with own code | `add-drive-uri-writes` proposal — `DRIVE_URI.TEMPLATE_READ_ONLY` rather than the temporary `WRITE_UNSUPPORTED` |
|
|
| 4 | Range reads `{ offset, length }` on all three schemes | `add-drive-uri` proposal § Out of scope (line 35); `add-drive-range-reads` |
|
|
| 5 | Chunk-aware mesh range optimisation (`specific` ↔ `bitset` pivot) | `add-drive-range-reads` proposal |
|
|
| 6 | `AbortSignal` threading through buffered `get`/`set`/`delete`/`stat`/`exists`/`list` paths | Existing canonical spec footnote (lines 7-9 of `openspec/specs/drive-uri/spec.md`) |
|
|
| 7 | `createWriteStream(key, body, signal?)` adapter surface | Not surfaced today — `StorageUriResolver.write` falls back to buffered drain |
|
|
| 8 | First-class `stat` returning `StatResult` (size / lastModified) | `getMetadata` hook on the adapter — never wired |
|
|
| 9 | First-class `list` with prefix + pagination via `Drive.list` | `DriveStorageLikeAdapter.list` exists but lacks resolver re-serialisation through `UriPlugin.build` |
|
|
| 10 | Cheap `exists` via `Drive.getPresence` / `getKeyStatus` (no buffered `read`) | Today's `exists` calls `Drive.stat` — fine for local, expensive for template-backed |
|
|
| 11 | Per-scheme query-string options (`?nonce=…` for templates, `?advertise=true|false` for mesh) | Never surfaced; need redaction policy |
|
|
| 12 | Mesh re-advertise opt-in via `?advertise=true` query param + EventBus emission | `add-drive-uri-writes` left this consumer-controlled by default; this change keeps the default but adds an opt-in |
|
|
| 13 | OTel span attributes per op with backend identification + range tag | `add-drive-range-reads` proposal |
|
|
| 14 | EventBus emission on every write/delete for downstream sync | `add-drive-uri-writes` design tangentially |
|
|
| 15 | Exhaustive `DRIVE_URI.*` error registry (read-side, write-side, template-read-only, range, mesh-propagation, capability) | Today's registry is 7 codes; ships at 14 |
|
|
| 16 | `runUriPluginConformance` re-run against full read/write/delete/stat/list/exists matrix on all three plugins | `add-drive-uri-writes` proposal |
|
|
|
|
The composition model is unchanged: three `UriPlugin` factories plug into the existing `StorageUriResolver`. No new resolver class. No re-implementation of any chunk codec, mesh transport, or template-signing logic — those stay inside `Drive` and its installed plugins.
|
|
|
|
## What changes
|
|
|
|
### `@suaveplan/drive` (MODIFIED)
|
|
|
|
- MODIFIED: `Drive.createReadStream(path, signalOrOptions?)` — overloaded. Bare `AbortSignal` preserved; new `RangeReadOptions` form accepts `{ offset?, length?, signal? }`. `instanceof AbortSignal` discriminator at runtime. Synchronous validation: `Number.isInteger(x) && x >= 0 && Number.isFinite(x)`; failures throw `DRIVE.RANGE_INVALID` BEFORE stream construction.
|
|
- MODIFIED: `Drive.createWriteStream(path, signal?)` — unchanged signature; reused by adapter's `createWriteStream` wrapper.
|
|
- ADDED: `RangeReadOptions` interface on `src/types/drive.types.ts`.
|
|
- ADDED: `DRIVE.RANGE_INVALID`, `DRIVE.RANGE_OUT_OF_BOUNDS` codes in `DRIVE_CODES` registry (11 → 13 codes).
|
|
- MODIFIED: `DriveStorage.capabilities?.nativeRange?: boolean` flag. `defaultCreateReadStream` honours range via slicing transform; native-range storages forward `{ offset, length }` to `storage.createReadStream`.
|
|
- MODIFIED: `DriveMiddleware` context gains `range?: { offset: number; length?: number }`. Chunks-plugin reassemble path (out of scope for THIS package; covered in design.md as a follow-up to land within the same wave) honours it.
|
|
|
|
### `@suaveplan/drive-uri` (the bulk of the change)
|
|
|
|
- ADDED — per-scheme per-op matrix (full):
|
|
|
|
| Operation | `drive://` | `drive+mesh://` | `drive+template://` |
|
|
|-------------------|---------------------------------------------|--------------------------------------------------------------------------|--------------------------------------|
|
|
| `get` | `Drive.read(key, { signal })` | `Drive.read(key, { signal })` | hydrate-then-read |
|
|
| `set` (write) | `Drive.write(key, bytes, { signal })` | `Drive.write` + optional `mesh.advertise(…)` via `?advertise=true` opt-in | `DRIVE_URI.TEMPLATE_READ_ONLY` |
|
|
| `delete` | `Drive.delete(key, signal)` | `Drive.delete` + optional `mesh.unadvertise(fileId)` on opt-in | `DRIVE_URI.TEMPLATE_READ_ONLY` |
|
|
| `stat` | `Drive.stat(key, signal)` → `StatResult` | same; cache-aware on template hits | same (post-hydrate) |
|
|
| `exists` | `Drive.getPresence(key)` cheap path → fallback `stat` | same | same (post-hydrate) |
|
|
| `list` | `Drive.list(prefix, { signal, limit, cursor })` → keys re-serialised via `UriPlugin.build` | same | same |
|
|
| `createReadStream`| `Drive.createReadStream(key, { offset, length, signal })` | same; chunk-aware mesh request via `ChunkRequest.specific` ↔ `bitset` pivot | same |
|
|
| `createWriteStream`| `Drive.createWriteStream(key, signal)` | same | `DRIVE_URI.TEMPLATE_READ_ONLY` |
|
|
| `clear` | `DRIVE_URI.WRITE_UNSUPPORTED` (footgun) | same | same |
|
|
|
|
- ADDED — `DRIVE_URI_CODES` registry grows 7 → **14 codes** via `defineCodes("DRIVE_URI", […] as const)`. New codes:
|
|
- `TEMPLATE_READ_ONLY` — permanent template-mutation rejection.
|
|
- `RANGE_INVALID` — invalid `{ offset, length }` per-scheme echo of `DRIVE.RANGE_INVALID`.
|
|
- `RANGE_OUT_OF_BOUNDS` — per-scheme echo of `DRIVE.RANGE_OUT_OF_BOUNDS` / `CHUNKS.RANGE_OUT_OF_BOUNDS`.
|
|
- `MESH_PROPAGATION_TIMEOUT` — explicit `?advertise=true` write where the consumer-supplied advertise hook rejects or times out.
|
|
- `STAT_UNAVAILABLE` — underlying storage cannot answer `stat` and the URI surface needs a positive answer.
|
|
- `LIST_UNSUPPORTED` — the bound `Drive` does not expose `list` (the AbstractStorage backing it lacks the method).
|
|
- `INVALID_QUERY` — query-string option is malformed (e.g. `?advertise=maybe`).
|
|
- **Retained**: `INVALID_URI`, `DRIVE_ID_MISMATCH`, `TEMPLATE_NOT_AVAILABLE`, `TEMPLATE_INVALID`, `WRITE_UNSUPPORTED` (now narrowed to `clear()` call site only), `CAPABILITY_MISSING`, `ABORTED`.
|
|
- ADDED — `DriveUriError` discriminants extend with `range?: { offset: number; length?: number }`, `fileId?: string`.
|
|
- ADDED — `AbortSignal` threading on every buffered op: `get`/`set`/`delete`/`stat`/`exists`/`list` accept and forward `AbortSignal` to the underlying `Drive` method. Pre-aborted signal → `DRIVE_URI.ABORTED` short-circuit before any `Drive` call.
|
|
- ADDED — `InstrumentedDriveAdapter.createWriteStream(key, body, signal?)`: per-scheme dispatch (`drive`/`drive+mesh` → forward; `drive+template` → throw `TEMPLATE_READ_ONLY`).
|
|
- ADDED — `InstrumentedDriveAdapter.getMetadata(key, signal?): Promise<StatResult | undefined>`: translates `Drive.stat` into `StatResult { size, lastModified, etag?, contentType? }` using `DriveStats.checksum` (etag) and `Drive.stat().modifiedAt` (lastModified). `StorageUriResolver.stat()` picks this up automatically via the existing `StreamingStorageExtensions.getMetadata` duck-type.
|
|
- ADDED — Per-scheme `UriPlugin.build` round-trips `meta` (`driveId`/`swarmId`/`templateId`) **and** preserves any incoming query string when supplied via `meta.query`. The resolver's `list` re-serialiser receives prefix-listed keys and produces fully-qualified URIs that round-trip through `parse`.
|
|
- ADDED — Query-string parsing on every plugin:
|
|
- `drive+mesh://swarm/path?advertise=true|false` → opt-in mesh re-advertise on write/delete.
|
|
- `drive+template://template/path?nonce=…` → forwards `nonce` to `templateProvider.fetchTemplate(templateId, { nonce, signal })` (the provider is widened with an optional second `options` bag — back-compat preserved).
|
|
- `?signal-timeout-ms=<int>` (any scheme) → upper bound on a single-op latency before raising `DRIVE_URI.ABORTED`.
|
|
- Unknown query keys → `DRIVE_URI.INVALID_QUERY` with the offending key.
|
|
- ADDED — Query-string redaction policy: every `drive.uri` span attribute, every error's `.uri`, and every emitted EventBus event strips the query string (`redactUri` already exists; extended to also strip the fragment). Sensitive keys (`nonce`, `peers`, future `auth=`) are never logged or surfaced.
|
|
- ADDED — Mesh re-advertise via `?advertise=true`: when set on a `drive+mesh://` write, the plugin invokes the consumer-supplied `MeshAdvertiseHook` from `DriveMeshUriPluginOptions.advertise?`. When the hook is absent, the plugin raises `DRIVE_URI.CAPABILITY_MISSING` with `reason: "advertise"` synchronously at construction time *only if* the consumer opts in at the URI level — wiring stays purely additive. The hook is invoked **after** local persistence is durable; failure is wrapped as `DRIVE_URI.MESH_PROPAGATION_TIMEOUT`.
|
|
- ADDED — `MeshAdvertiseHook` interface (universal-tier, lives in `types/plugin-options.types.ts`): `advertise(fileId, bitset, totalChunks, signal?): Promise<void>` + `unadvertise(fileId, signal?): Promise<void>`. The hook wraps the consumer's `mesh.advertise(...)` / `mesh.unadvertise(...)` calls; `drive-uri` never depends on `@suaveplan/drive-mesh` directly.
|
|
- ADDED — EventBus emission on every write/delete success/failure when the wrapped `Drive` exposes `events`: topics `drive_uri:write` / `drive_uri:delete` carrying `{ scheme, key, uri (redacted), outcome, durationMs, fileId?, range? }`. Used by downstream sync engines (`@suaveplan/vault-sync`, future `drive-sharing` flows) to drive incremental updates. EventBus is read off the `Drive` reference passed at construction; absence is a no-op.
|
|
- ADDED — Span event `drive_uri.mesh_write_local_only` on every `drive+mesh://` write where `?advertise=true` is NOT set, so operators can audit that the consumer-controlled gossip contract holds.
|
|
- MODIFIED — `DRIVE_URI.WRITE_UNSUPPORTED` retained but narrowed to `DriveStorageLikeAdapter.clear()` only. Every other call site that previously raised it now succeeds or raises a more specific code per the matrix above.
|
|
- MODIFIED — `DriveStorageLikeAdapter`:
|
|
- `get(key, signal?)`, `set(key, value, signal?)`, `delete(key, signal?)`, `exists(key, signal?)`, `list(options & { signal? })`, `stat(key, signal?)` all accept and forward `AbortSignal`.
|
|
- `createReadStream(key, options?: { offset?, length?, signal? })` forwards range to `Drive.createReadStream(key, options)`.
|
|
- `createWriteStream(key, body, signal?)`: consumes a `ReadableStream<Uint8Array>`, pipes into `Drive.createWriteStream(key, signal)`.
|
|
- `getMetadata(key, signal?)` → `StatResult | undefined` per `StreamingStorageExtensions`.
|
|
- `capabilities.nativeRange === true` advertised so `StorageUriResolver` uses the range-aware `createReadStream` directly.
|
|
- MODIFIED — Conformance: `runUriPluginConformance` re-run with **full read/write/delete/stat/list/exists/range matrices** for all three plugins. Fixtures:
|
|
- `drive://` — full cycle passes.
|
|
- `drive+mesh://` — full cycle passes; mesh-advertise spy asserts zero calls when `?advertise` is omitted, exactly one call when `?advertise=true`.
|
|
- `drive+template://` — read/stat/exists/list pass; `set`/`delete`/`createWriteStream` reject with `DRIVE_URI.TEMPLATE_READ_ONLY`.
|
|
- ADDED — Per-package OTel surface extensions:
|
|
- `drive_uri.stream_chunks_fetched` histogram tagged `{scheme}` — chunk count per `createReadStream` call (range narrows this).
|
|
- `drive.uri.range = "{offset}-{end}"` span attribute, omitted when no range.
|
|
- `drive.uri.has_advertise = "true"|"false"` span attribute on `drive+mesh://` write spans.
|
|
- All existing counters preserved.
|
|
|
|
## Out of scope
|
|
|
|
Per user instruction, only two carve-outs:
|
|
|
|
- **NEW URI schemes.** Adding a fourth scheme (e.g. `drive+cdn://`) is a separate change. The three existing schemes ship at full functional completeness here.
|
|
- **Templating policy.** Whether templates are mutable (re-issue, multi-version, key rotation policy) lives in `@suaveplan/drive-template`. This change codifies the URI surface's read-only treatment via `TEMPLATE_READ_ONLY` but does not alter template semantics themselves.
|
|
|
|
## Source mapping
|
|
|
|
This is a **capability-completion** change for a genesis-original package. Surface anchors:
|
|
|
|
- `packages/drive/drive-uri/src/plugins/drive/drive.streams.ts:69-84` — current unconditional `set` rejection (replaced).
|
|
- `packages/drive/drive-uri/src/adapter/drive-storage-like.ts:75-86` — `set`/`delete` already pass-through internally; the instrumented override now per-scheme dispatches.
|
|
- `packages/drive/drive-uri/src/adapter/drive-storage-like.ts:88-119` — `list` exists; resolver re-serialisation via `UriPlugin.build` newly required.
|
|
- `packages/drive/drive/src/drive/drive.ts:180-240` — `Drive.write` / `delete` / `stat` / `list` / `createReadStream` / `createWriteStream` consumed verbatim.
|
|
- `packages/drive/drive/src/drive/drive.ts:222-230` — `createReadStream` overload landing zone.
|
|
- `packages/drive/drive/src/file-operations/streams.ts:50-70` — `nativeRange` slicing-transform insertion point; mirrors existing `nativeStreams` capability flag pattern.
|
|
- `packages/drive/drive-mesh/src/mesh/drive-mesh/drive-mesh.ts:176-193` — `advertise(fileId, bitset, totalChunks)` / `unadvertise(fileId)` shape consumed structurally by `MeshAdvertiseHook` (no direct dep).
|
|
- `packages/drive/drive-mesh-protocol/src/types/protocol.types.ts:38-42` — `ChunkRequest.specific | bitset` shapes; pivot at 64 chunks per `MAX_CHUNKS_DEFAULT`.
|
|
- `packages/storage/storage-uri/src/resolver/resolver.writes.ts:30-75` — confirms `SuaveplanError` subclasses pass through `writeViaPlugin` unchanged, so every new `DRIVE_URI.*` code reaches the caller without a `STORAGE_URI.*` envelope.
|
|
- `packages/storage/storage-uri/src/resolver/resolver.streams.ts:24-54` — `openViaPlugin` already forwards to `adapter.createReadStream` (no fallback) when present; range bag passes through via the existing options-args shape.
|
|
- `packages/storage/storage-uri/src/types/plugin.types.ts:41-67` — `StreamingStorageExtensions.getMetadata` / `createReadStream` / `createWriteStream` duck-type — `drive-uri` adapter implements all three.
|
|
- `openspec/archive/2026-05-16-add-drive-uri/proposal.md` lines 30-38 — original deferral text.
|
|
- `openspec/changes/_superseded-narrow-deferrals-2026-05-17/add-drive-uri-writes/proposal.md` — superseded research, reused.
|
|
- `openspec/changes/_superseded-narrow-deferrals-2026-05-17/add-drive-range-reads/proposal.md` — superseded research, reused.
|
|
|
|
### Requirements with no direct source line
|
|
|
|
| Requirement | Justification |
|
|
|----------------------------------------------------------------------------|---------------|
|
|
| Single coherent change instead of two narrow follow-ups | The deferrals were carved fine-grained on the assumption write semantics were "non-trivial"; revisiting per-scheme showed they are not. Landing both together avoids two passes through the conformance harness, two rounds of doc churn, and two dependent-sweep cycles. The supersede notice (`_superseded-narrow-deferrals-2026-05-17/`) records the consolidation. |
|
|
| `?advertise=true` query opt-in rather than always-advertise on mesh write | Direct `Drive.write` does not auto-advertise — the URI surface MUST preserve that contract by default. The opt-in gives the URI an explicit knob without bending the default. |
|
|
| `MeshAdvertiseHook` lives in `drive-uri` not `drive-mesh` | `drive-uri` MUST NOT depend on `drive-mesh` (it would couple the URI tier to a concrete transport). The structural hook lets consumers wire their `mesh.advertise(...)` without `drive-uri` ever importing `@suaveplan/drive-mesh`. |
|
|
| EventBus emission off the wrapped `Drive`'s `events` rather than a separate bus | The `Drive` already carries `events: EventBusLike | undefined`(`drive.types.ts:73`); reusing it keeps consumer wiring minimal and matches the precedent set by`key:grant`(`Drive.hydrateFrom`). |
|
|
| `TEMPLATE_READ_ONLY` distinct from `WRITE_UNSUPPORTED` rather than reusing | `WRITE_UNSUPPORTED` was a *temporary* state code from `add-drive-uri`; templates are *permanently* read-only via an architectural constraint (authority re-signing). Distinct codes let consumers branch correctly. |
|
|
| Range overload via `instanceof AbortSignal` discriminator | The only other shape that satisfies the position-1 type is `RangeReadOptions`; `AbortSignal.prototype` is a stable cross-runtime brand. Duck-typing on `signal?.aborted` would falsely match `RangeReadOptions`. |
|
|
| Range registry mirrored on `DRIVE_URI` rather than only `DRIVE` | Consumers branching on the URI surface should not need to know which underlying error class fired; the URI-tier echoes the canonical drive codes via wrapping while preserving `cause`. |
|
|
| Cheap `exists` via `Drive.getPresence` over `Drive.stat` | `getPresence` returns a synchronous `ChunkPresence` from the in-memory `FileLocationIndex` for hydrated files (template path) and falls back to `stat` for raw paths — strictly cheaper for `drive+template://` reads. |
|
|
| Query-string redaction always-on | A future opt-in `?auth=…` is plausible; codifying redaction now prevents a leak when that opt-in lands. |
|
|
|
|
## Verification approach
|
|
|
|
Every requirement in the spec deltas carries a `**Verified by:**` line referencing concrete `it(...)` titles in planned test files. The pre-archive gate (`§13` of `AGENTS.md`) re-runs the full conformance matrix against all three plugins. Integration tests (no mocks per CLAUDE.md rule 2) cover real-Drive round-trips for read, write, delete, stat, list, exists, range, and template hydration paths.
|
|
|
|
## Migration
|
|
|
|
| Old | New |
|
|
|------------------------------------------------------------------|--------------------------------------------------------------|
|
|
| `resolver.write("drive://…", bytes)` → `DRIVE_URI.WRITE_UNSUPPORTED` | succeeds; returns `void` |
|
|
| `resolver.write("drive+mesh://…", bytes)` → `DRIVE_URI.WRITE_UNSUPPORTED` | succeeds; mesh advertise opt-in via `?advertise=true` |
|
|
| `resolver.write("drive+template://…", bytes)` → `DRIVE_URI.WRITE_UNSUPPORTED` | `DRIVE_URI.TEMPLATE_READ_ONLY` |
|
|
| `resolver.remove("drive+template://…")` → silent footgun (forwarded to `Drive.delete`) | `DRIVE_URI.TEMPLATE_READ_ONLY` |
|
|
| `resolver.stat("drive://…")` → `undefined` (no `getMetadata`) | `StatResult { size, lastModified, etag? }` |
|
|
| `resolver.open("drive+mesh://…/clip.mov", signal)` → whole-file fetch | same; `open(..., { offset, length, signal })` fetches only in-range chunks |
|
|
| `DRIVE_URI.WRITE_UNSUPPORTED` raised for `set`/`delete` | only raised for `DriveStorageLikeAdapter.clear()` |
|
|
|
|
Consumer search: `grep -rln "DRIVE_URI.WRITE_UNSUPPORTED" packages/ | grep -v "/dist/\|/coverage/\|/drive-uri/"`. Branch new code on `err.code === "DRIVE_URI.TEMPLATE_READ_ONLY"` for the immutable template surface; on `DRIVE_URI.RANGE_INVALID` / `RANGE_OUT_OF_BOUNDS` for range errors; on `DRIVE_URI.MESH_PROPAGATION_TIMEOUT` for opt-in advertise failures.
|
|
|
|
## Impact
|
|
|
|
### Affected dependents
|
|
|
|
Run `bunx turbo ls --filter=...@suaveplan/drive-uri` to enumerate. The package's downstream surface is currently empty (`drive-uri` is consumed only via integration tests at the time of writing); the broader `...@suaveplan/drive` dependents drive verification:
|
|
|
|
- `@suaveplan/drive-uri` — [x] tests run / [x] docs reviewed / [ ] no action needed
|
|
- `@suaveplan/drive-plugin-chunks` — [x] tests run / [x] docs reviewed / [ ] no action needed (middleware honours `ctx.range`; `#reassemble` extended)
|
|
- `@suaveplan/drive-mesh` — [x] tests run / [x] docs reviewed / [ ] no action needed (pull path constructs `ChunkRequest.specific` / `bitset` from range; `advertise` consumed via structural hook)
|
|
- `@suaveplan/drive-mesh-protocol` — [ ] tests run / [x] docs reviewed / [x] no action needed (no protocol wire change; existing union shapes used)
|
|
- `@suaveplan/drive-transport-https` — [ ] tests run / [x] docs reviewed / [x] no action needed (transport carries envelopes opaquely)
|
|
- `@suaveplan/drive-transport-webrtc` — [ ] tests run / [x] docs reviewed / [x] no action needed (same)
|
|
- `@suaveplan/drive-template` — [ ] tests run / [x] docs reviewed / [x] no action needed (hydration unchanged)
|
|
- `@suaveplan/drive-sharing` — [ ] tests run / [x] docs reviewed / [x] no action needed (calls `Drive.read`)
|
|
- `@suaveplan/storage-uri` — [x] tests run / [x] docs reviewed / [ ] no action needed (`writeViaPlugin` already passes through `SuaveplanError` subclasses; `openViaPlugin` already forwards `createReadStream`)
|
|
- `@suaveplan/drive` — [x] tests run / [x] docs reviewed / [ ] no action needed (range overload is purely additive)
|
|
|
|
### Affected upstreams (none breaking)
|
|
|
|
| Package | Action | Breaking? |
|
|
|-----------------------------------|-----------------------------------------------------------------------------------------|-----------|
|
|
| `@suaveplan/drive` | ADD `RangeReadOptions`, `createReadStream` overload, `RANGE_*` codes, `nativeRange` cap | no |
|
|
| `@suaveplan/storage-uri` | None — `StreamingStorageExtensions` already specifies `createWriteStream` + `getMetadata`| no |
|
|
| `@suaveplan/drive-mesh` | None — `advertise`/`unadvertise` consumed via structural hook only | no |
|
|
| `@suaveplan/drive-template` | None — only `DriveTemplate` type consumed | no |
|
|
| `@suaveplan/drive-plugin-chunks` | Honour `ctx.range` (delivered as a sibling change inside the same wave) | no |
|
|
| `@suaveplan/types` | None — `DriveLike` hoist tracked separately | no |
|
|
|
|
### Ordering
|
|
|
|
Unblocked. All packages exist on genesis today; the change is additive at every boundary.
|