first commit
This commit is contained in:
89
docs/persistence/storage/storage-cdn/src/guards/guards.md
Normal file
89
docs/persistence/storage/storage-cdn/src/guards/guards.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# guards
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Ships two type-guards for `CdnAdapter`: the non-throwing `isCdnAdapter`
|
||||
predicate and the throwing `assertCdnAdapter` assertion. Consumers that
|
||||
accept a structural `StorageLike` and need to opt into CDN features at
|
||||
the call site use these to narrow safely. The throwing variant raises a
|
||||
canonical `StorageCdnError` so the rejection has the same diagnostic
|
||||
surface as every other CDN error.
|
||||
|
||||
Without these helpers each consumer would re-implement
|
||||
`value instanceof CdnAdapter` checks and the assertion variant would
|
||||
either silently degrade or throw a plain `Error` — both bad outcomes for
|
||||
operator dashboards.
|
||||
|
||||
## Features
|
||||
|
||||
- Two functions, both tiny — `isCdnAdapter` and `assertCdnAdapter`.
|
||||
- Predicate form returns `value is CdnAdapter` so TypeScript narrows
|
||||
cleanly inside the `if` branch.
|
||||
- Assertion form uses TypeScript `asserts` syntax so the caller's flow
|
||||
is narrowed after the call without needing an explicit `if`.
|
||||
- Both rely on `instanceof CdnAdapter` — no structural duck typing, so
|
||||
third-party look-alikes don't sneak through.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { isCdnAdapter } from "@suaveplan/storage-cdn";
|
||||
|
||||
if (isCdnAdapter(storage)) {
|
||||
await storage.purge("a/b.bin");
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { assertCdnAdapter, STORAGE_CDN_CODES, StorageCdnError } from "@suaveplan/storage-cdn";
|
||||
|
||||
function purgeAfterWrite(storage: unknown, key: string): Promise<void> {
|
||||
assertCdnAdapter(storage); // throws if not a CdnAdapter
|
||||
return storage.purge(key);
|
||||
}
|
||||
|
||||
try {
|
||||
await purgeAfterWrite(new MemoryStorage(), "k");
|
||||
} catch (err) {
|
||||
if (err instanceof StorageCdnError && err.code === STORAGE_CDN_CODES.INVALID_OPTIONS) {
|
||||
// caller passed the wrong storage type — surface a clear message.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `isCdnAdapter(value: unknown): value is CdnAdapter`
|
||||
|
||||
Returns `true` when `value instanceof CdnAdapter`. Inside the `if`
|
||||
branch TypeScript narrows `value` to `CdnAdapter`. Safe to call with
|
||||
`null`, `undefined`, primitives, or arbitrary objects — all yield
|
||||
`false`.
|
||||
|
||||
### `assertCdnAdapter(value: unknown): asserts value is CdnAdapter`
|
||||
|
||||
Throws `StorageCdnError(STORAGE_CDN.INVALID_OPTIONS)` when `value` is
|
||||
not a `CdnAdapter`. After the call, TypeScript treats `value` as
|
||||
`CdnAdapter`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Both functions rely strictly on `instanceof`. Structural duck typing
|
||||
would let any object with `purge`, `signUrl`, `edgeUrl` methods pass —
|
||||
acceptable for many type guards, but the CdnAdapter contract includes
|
||||
the full `AbstractStorage` surface and the capabilities matrix, so
|
||||
instanceof is the right floor.
|
||||
- The assertion uses the canonical `STORAGE_CDN.INVALID_OPTIONS` code
|
||||
rather than inventing a new "not a CdnAdapter" code — the configuration
|
||||
mistake is the caller passing the wrong type, which is exactly what
|
||||
that code is for.
|
||||
- The functions intentionally don't call `Object.getPrototypeOf` or
|
||||
similar; the `instanceof` check is sufficient and avoids dragging in
|
||||
prototype-chain edge cases (cross-realm objects, etc.) which provider
|
||||
packages would have to defend against anyway.
|
||||
Reference in New Issue
Block a user