forked from rungalileo/gcache
-
Notifications
You must be signed in to change notification settings - Fork 1
docs: add a landing README and searchable reference site #89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
lan17
wants to merge
35
commits into
main
Choose a base branch
from
docs/readme-safety-adoption
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
4a2dc71
docs: focus README on safe cache rollouts
lan17 183395b
docs: improve onboarding and reference coverage
lan17 0a833b2
docs: refine rollout safety guidance
lan17 6502c7c
docs: make rollout examples fail safe
lan17 e639a05
docs: clarify read-through cache positioning
lan17 b44c9de
docs: make README opening easier to scan
lan17 8bb7309
docs: refresh guides for v0.13.0
lan17 a50a0e8
docs: refresh guides for v0.14.1
lan17 0d30d6a
Merge origin/main into docs/readme-safety-adoption
lan17 e6ec849
docs: refresh guides for v0.19.0
lan17 671ca4f
docs: rebuild landing README and reference on current main
lan17 d8e268f
docs: host searchable reference with VitePress and GitHub Pages
lan17 bcec11b
docs: clarify runtime contracts and verify onboarding examples
lan17 a22c854
docs: distinguish operation budgets from the read path
lan17 9c3d694
docs: correct invalidation freshness and shared anchors
lan17 fa83520
docs: specify the legacy binary escape prefix
lan17 d31acd0
docs: surface serializer requirements during onboarding
lan17 9f7c9dc
docs: complete public behavior and helper contracts
lan17 f3d1648
docs: clarify serializer limits and edge-case telemetry
lan17 5273ab7
docs: qualify native JSON serialization failures
lan17 6c1e0bc
docs: make the README more direct and less promotional
lan17 fc2fb95
docs: address README review feedback
lan17 f60494a
docs: define terms and add scenarios to the README
lan17 6e3566a
docs: align page titles and fix reference nits
lan17 e0b1dfc
docs: open the README with a capability list and show both entry points
lan17 3632204
docs: trim the README to the example and its comments
lan17 ecd3a90
docs: give the README opening more energy
lan17 27c40dc
docs: rewrite the README opening paragraph
lan17 e935b1e
docs: write the README in the third person
lan17 dafd724
docs: reflow the stale-on-error paragraph
lan17 8a72292
docs: reframe the README opening around use cases
lan17 cc361ea
docs: drop the entry-point sentence from the README opening
lan17 f6aaf96
docs: shorten the multi-layer bullet to the layer chain
lan17 9070df4
docs: restore the README install section
lan17 24a3c53
docs: shape the README as a landing page
lan17 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| name: Documentation | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: docs-${{ github.event.pull_request.number || github.ref }} | ||
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | ||
|
|
||
| jobs: | ||
| build: | ||
| name: Build documentation | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | ||
| - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0 | ||
| with: | ||
| version: 10.33.0 | ||
| - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0 | ||
| with: | ||
| node-version: "24" | ||
| cache: "pnpm" | ||
| - run: pnpm install --frozen-lockfile | ||
| - run: pnpm docs:build | ||
| - uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 | ||
| if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' | ||
| with: | ||
| path: docs/.vitepress/dist | ||
|
|
||
| deploy: | ||
| name: Deploy documentation | ||
| if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' | ||
| needs: build | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| pages: write | ||
| id-token: write | ||
| environment: | ||
| name: github-pages | ||
| url: ${{ steps.deployment.outputs.page_url }} | ||
| steps: | ||
| - uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 | ||
| - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 | ||
| id: deployment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| node_modules/ | ||
| dist/ | ||
| coverage/ | ||
| docs/.vitepress/cache/ | ||
| *.tgz | ||
| .env | ||
| .env.* | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { defineConfig } from "vitepress"; | ||
|
|
||
| // Preserve Vite 6's targets except Safari 14, whose destructuring target is | ||
| // unsupported by the repository's patched esbuild. Apply to build and dev. | ||
| const browserTargets = ["es2020", "chrome87", "edge88", "firefox78", "safari14.1"]; | ||
|
|
||
| export default defineConfig({ | ||
| title: "DialCache", | ||
| description: | ||
| "DialCache organizes caching into use cases, with runtime control and observability for each one. Reference for the read path, runtime policies, and the API.", | ||
| lang: "en-US", | ||
| base: "/DialCache/", | ||
| vite: { | ||
| build: { target: browserTargets }, | ||
| optimizeDeps: { esbuildOptions: { target: browserTargets } }, | ||
| }, | ||
| themeConfig: { | ||
| nav: [ | ||
| { text: "Documentation", link: "/" }, | ||
| { text: "API reference", link: "/api" }, | ||
| { text: "npm", link: "https://www.npmjs.com/package/dialcache" }, | ||
| ], | ||
| sidebar: [ | ||
| { | ||
| text: "Start here", | ||
| items: [ | ||
| { text: "Overview", link: "/" }, | ||
| { text: "Getting started", link: "/getting-started" }, | ||
| { text: "How DialCache works", link: "/concepts" }, | ||
| ], | ||
| }, | ||
| { | ||
| text: "Features and behavior", | ||
| items: [ | ||
| { text: "Configuration", link: "/configuration" }, | ||
| { text: "Redis and Valkey", link: "/redis" }, | ||
| { text: "Targeted invalidation", link: "/invalidation" }, | ||
| { text: "Stale-on-error", link: "/stale-on-error" }, | ||
| { text: "Shadow validation", link: "/shadow-validation" }, | ||
| { text: "Coalescing and liveness", link: "/coalescing" }, | ||
| { text: "Observability", link: "/observability" }, | ||
| ], | ||
| }, | ||
| { | ||
| text: "Reference and operations", | ||
| items: [ | ||
| { text: "API reference", link: "/api" }, | ||
| { text: "Upgrading", link: "/upgrading" }, | ||
| { text: "Maintainer guide", link: "/maintainers" }, | ||
| ], | ||
| }, | ||
| ], | ||
| outline: [2, 3], | ||
| search: { provider: "local" }, | ||
| socialLinks: [ | ||
| { icon: "github", link: "https://github.com/lan17/DialCache" }, | ||
| ], | ||
| editLink: { | ||
| pattern: "https://github.com/lan17/DialCache/edit/main/docs/:path", | ||
| }, | ||
| }, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,248 @@ | ||
| # API reference | ||
|
|
||
| [Documentation](index.md) | ||
|
|
||
| Start with the `DialCache` instance, define a cache operation, then choose its | ||
| policy. The feature guides explain the behavior behind these tables; the | ||
| package's declarations provide the full generic signatures. | ||
|
|
||
| ## Imports | ||
|
|
||
| | Entry point | Public surface | Guide | | ||
| | --- | --- | --- | | ||
| | `dialcache` | `DialCache`, configuration, keys, serializers, core errors, semantic Redis types, and metric types | This page | | ||
| | `dialcache/node-redis` | `createNodeRedisDialCacheClient` | [Redis](redis.md#node-redis) | | ||
| | `dialcache/valkey-glide` | `createValkeyGlideDialCacheClient`, `ValkeyGlideRuntime`, `ValkeyGlideScriptingClient` | [Valkey GLIDE](redis.md#valkey-glide) | | ||
| | `dialcache/prometheus` | `createPrometheusDialCacheMetrics`, `PrometheusDialCacheMetrics`, `PrometheusMetricsOptions` | [Prometheus](observability.md#prometheus) | | ||
| | `dialcache/datadog` | `createDatadogDialCacheMetrics`, `DatadogDialCacheMetrics`, `DatadogMetricsOptions`, `DatadogDogStatsDClient`, `DatadogObservationMetricType` | [Datadog](observability.md#datadog) | | ||
| | `dialcache/redis-protocol` | Frame codecs, semantic miss types and guard, invalidation Lua, and reply/TTL validators | [Wire protocol](redis.md#advanced-wire-protocol) | | ||
|
|
||
| Optional integrations use their own import paths. The application installs and | ||
| owns the corresponding client or metrics registry. | ||
|
|
||
| ## Constructor | ||
|
|
||
| `new DialCache(options?)` constructs one instance for each intended local-cache | ||
| and coalescing boundary. | ||
| With no options, it supports in-memory caching and uses a disabled baseline. | ||
| Enable a scope **and** configure at least one layer to store values. | ||
|
|
||
| | Option | Default | Contract | | ||
| | --- | --- | --- | | ||
| | `namespace` | `"urn"` | Cache identity and metric namespace label; no `{` or `}` | | ||
| | `localMaxSize` | `10_000` | Nonnegative safe-integer LRU entry cap across all use cases; `0` disables storage | | ||
| | `redis` | Absent | `RedisConfig`: connected semantic client and optional read timeout, serializer, and compression policy | | ||
| | `cacheConfigProvider` | No overrides | `(key: DialCacheKey) => DialCacheKeyConfig \| null`, synchronously or via a Promise | | ||
| | `shouldAttemptStaleRecovery` | Accepts only `FallbackTimeoutError` | Synchronous `(error: unknown) => boolean`; an operation override replaces it | | ||
| | `shadowMaxInFlight` | `1` | Positive safe-integer cap on admitted shadow jobs; no queue | | ||
| | `metrics` | Absent | `DialCacheMetricsAdapter` | | ||
| | `logger` | `console` | `Logger`, the `debug`, `warn`, and `error` methods | | ||
|
|
||
| See [Configuration](configuration.md) for validation and lifetime rules. | ||
|
|
||
| ### `RedisConfig` | ||
|
|
||
| Pass this object as the constructor's `redis` option: | ||
|
|
||
| | Field | Default | Contract | | ||
| | --- | --- | --- | | ||
| | `client` | Required | Connected `DialCacheRedisClient`; the application owns connection and shutdown | | ||
| | `readTimeoutMs` | `50` | Positive safe integer up to `2_147_483_647` ms; a use case's `remoteReadTimeoutMs` takes precedence | | ||
| | `serializer` | `JsonSerializer` | Instance-level Redis serializer; an operation's serializer takes precedence | | ||
| | `compression` | `{ thresholdBytes: 4_096, level: 3 }` | `CompressionConfig` or `false`; threshold is a positive safe integer, level is an integer from `1` through `22` | | ||
|
|
||
| Providing a client makes the remote layer available; each operation still needs | ||
| a remote TTL and an enabled scope. See [client setup](redis.md), | ||
| [serialization](redis.md#serialization), and [compression](redis.md#compression). | ||
|
|
||
| ## Scope methods | ||
|
|
||
| | Method | Return | Behavior | | ||
| | --- | --- | --- | | ||
| | `enable(fn)` | `Promise<T>` | Enables caching while the callback and its awaited work run | | ||
| | `disable(fn)` | `Promise<T>` | Runs a nested region uncached; does not evict values | | ||
| | `withEnabled(fn)` | `Promise<T>` | Alias for `enable` | | ||
| | `withDisabled(fn)` | `Promise<T>` | Alias for `disable` | | ||
| | `isEnabled()` | `boolean` | Whether the current asynchronous chain has a live enabled scope | | ||
|
|
||
| Callbacks may return synchronously or asynchronously. Scope state is per | ||
| instance and asynchronous call chain. Nested scopes restore prior state. The | ||
| outermost `enable()` owns request-local state. New invocations become pass-through | ||
| after it closes; already admitted work can finish. An invocation still awaiting | ||
| its configuration provider bypasses caching after closure but retains its enabled | ||
| fallback deadline. See [Scope lifetime](configuration.md#enable-and-disable-scopes). | ||
|
|
||
| `DialCacheContext` is the lower-level root export with `enable`, `disable`, and | ||
| `isEnabled`. A separately constructed context does not enable another | ||
| `DialCache` instance or attach a cache to it. | ||
|
|
||
| ## `cached` | ||
|
|
||
| `cached(fn, options)` returns a `CachedFn<Fn>`: the same parameter types with a | ||
| `Promise` of the resolved return value. Register each `useCase` once per instance. | ||
| Wrap a bound method or closure when the loader needs a receiver. | ||
|
|
||
| ```ts | ||
| const getUser = dialcache.cached(fetchUser, { | ||
| keyType: "user_id", | ||
| useCase: "GetUser", | ||
| cacheKey: (userId) => userId, | ||
| defaultConfig: new DialCacheKeyConfig({ requestLocal: true }), | ||
| }); | ||
| ``` | ||
|
|
||
| ## `getOrLoad` | ||
|
|
||
| `getOrLoad(load, options)` returns `Promise<Value>`. Its zero-argument loader may | ||
| be synchronous or asynchronous. Supply a direct `key` instead of `cacheKey`. | ||
| The method does not register a use case, so stable names can be reused at a call | ||
| site. All calls sharing an identity must agree on value meaning and serializer. | ||
|
|
||
| ```ts | ||
| const value = await dialcache.getOrLoad(() => fetchUser(userId), { | ||
| keyType: "user_id", | ||
| useCase: "InlineGetUser", | ||
| key: userId, | ||
| defaultConfig: new DialCacheKeyConfig({ requestLocal: true }), | ||
| }); | ||
| ``` | ||
|
|
||
| This snippet assumes an enclosing enabled scope; without one the loader runs | ||
| uncached. | ||
|
|
||
| ### Operation options | ||
|
|
||
| `CachedOptions<Fn>` and `GetOrLoadOptions<Value>` share these fields: | ||
|
|
||
| | Option | Default | Contract | | ||
| | --- | --- | --- | | ||
| | `keyType` | Required | Entity kind; combines with id and namespace for tracked invalidation | | ||
| | `useCase` | Required | Stable operation name, cache-key component, and metric label; `"watermark"` is reserved | | ||
| | `cacheKey` / `key` | Required | Selector for `cached`, direct value for `getOrLoad`; returns/accepts `CacheKeySpec` | | ||
| | `defaultConfig` | Absent | Baseline `DialCacheKeyConfig`, snapshotted at registration or inline invocation | | ||
| | `serializer` | Effective JSON default | Typed `Serializer<Value>` required when the [JSON type guard](redis.md#typed-serializer-requirement) cannot establish compatibility, even for local-only declarations | | ||
| | `trackForInvalidation` | `false` | Use watermark-aware Redis reads for this operation | | ||
| | `fallbackTimeoutMs` | `60_000` | Positive safe integer up to `2_147_483_647` ms; `null` disables the source deadline | | ||
| | `shadowComparator` | Node strict deep equality | Synchronous, bounded `(cached, source) => boolean`; must not mutate its inputs | | ||
| | `shouldAttemptStaleRecovery` | Instance policy | Synchronous error classifier; replaces the lower-precedence policy | | ||
|
|
||
| `CacheKeySpec` is a string, number, or bigint id, or `{ id, args? }`. Argument | ||
| values are string, number, bigint, boolean, `null`, or `undefined`; undefined | ||
| arguments are omitted. See [Key design](configuration.md#keys-ids-and-extra-dimensions). | ||
|
|
||
| Static defaults, fallback timeout, and stale-recovery classifier are validated | ||
| and captured when registering `cached()` or invoking `getOrLoad()`. | ||
| The comparator is captured then; its execution and synchronous boolean result | ||
| are checked only when shadow comparison runs, with failures reported as | ||
| `comparison_error`. | ||
| Runtime policy is resolved per enabled invocation. These guarantees do not make | ||
| the entire caller-owned options object deeply immutable; keep definitions stable. | ||
|
|
||
| ## `DialCacheKeyConfig` | ||
|
|
||
| `new DialCacheKeyConfig({...})` describes the baseline or a sparse runtime | ||
| overlay. Omission inherits; it does not turn an inherited field off. | ||
|
|
||
| | Field | Effective default | Values | | ||
| | --- | --- | --- | | ||
| | `ttlSec.local`, `ttlSec.remote` | No TTL: layer off | Positive safe-integer seconds, at most `31_536_000` (365 days) | | ||
| | `ramp.local`, `ramp.remote` | `100` when a TTL exists | Key-selection threshold from `0` through `100`, not a share of traffic; `0` bypasses serving | | ||
| | `requestLocal` | `false` | Boolean; no TTL or ramp | | ||
| | `coalesce` | `true` | Boolean; affects request-local and process flights | | ||
| | `remoteReadTimeoutMs` | Instance setting, then `50` | Positive safe-integer milliseconds, at most `2_147_483_647`; cannot be unbounded | | ||
| | `staleOnErrorMaxAgeSec` | Off | Nonnegative safe-integer seconds; `0` disables; positive age must exceed remote TTL and be at most `31_536_000` | | ||
| | `shadow.ramp` | Off | Independent finite percentage from `0` through `100` | | ||
| | `shadow.logMismatches` | `false` | Boolean; controls diagnostic warning output | | ||
|
|
||
| Use `CacheLayer.LOCAL` (`"local"`) and `CacheLayer.REMOTE` (`"remote"`) as map | ||
| keys. `LayerConfig` is a partial map; `ShadowConfig` describes the shadow group. | ||
| Tracked Redis physical retention has a separate one-hour cap. | ||
|
|
||
| | Helper | Result | | ||
| | --- | --- | | ||
| | `DialCacheKeyConfig.enabled(ttlSec)` | Sets local and remote TTLs to the supplied value and both ramps to `100`; leaves request-local, shadow, and recovery unselected | | ||
| | `DialCacheKeyConfig.disabled()` | Disables request-local and recovery, sets both serving ramps and shadow ramp to `0`, and disables mismatch logging | | ||
|
|
||
| The enabled helper does not create a Redis connection. The disabled helper is | ||
| an invocation policy, not cancellation or eviction. See | ||
| [overlay precedence](configuration.md#baseline-and-overlay-precedence). | ||
|
|
||
| ## `invalidateRemote` | ||
|
|
||
| `invalidateRemote(keyType, id, futureBufferMs = 0): Promise<void>` advances the | ||
| entity's Redis watermark. Call it after the source mutation commits. The id is | ||
| stringified and the buffer is a nonnegative safe integer, at most | ||
| `31_536_000_000` milliseconds. | ||
|
|
||
| It affects tracked Redis entries across use cases and argument variants in the | ||
| same namespace. It does not evict in-memory or untracked Redis values, revoke | ||
| acquired snapshots, or clear in-flight work. See | ||
| [Independent fence checks](invalidation.md#independent-fence-checks) when each | ||
| invocation must observe invalidation separately. Missing | ||
| Redis configuration and mutation failures reject; the method works outside an | ||
| enabled scope. Choose the buffer from the | ||
| [clock and in-flight-work contract](invalidation.md#choosing-futurebufferms). | ||
|
|
||
| ## `getCoalescingState` | ||
|
|
||
| `getCoalescingState(): CoalescingState` returns a point-in-time process-flight | ||
| snapshot for this instance: | ||
|
|
||
| ```ts | ||
| const { process } = dialcache.getCoalescingState(); | ||
| process.activeLeaders; // Number of distinct in-flight keys. | ||
| process.activeFollowers; // Callers waiting on those leaders. | ||
| process.oldestLeaderAgeMs; // Monotonic age, or null when idle. | ||
| ``` | ||
|
|
||
| The nested shape is `ProcessCoalescingState`. Request-local flights are excluded. | ||
| There is no method to clear a cache, cancel in-flight loads, cap coalesced | ||
| flights, or shut an instance down. | ||
| See [Coalescing state](coalescing.md#inspecting-process-scoped-flights). | ||
|
|
||
| ## Keys and serializers | ||
|
|
||
| | Export | Purpose | | ||
| | --- | --- | | ||
| | `DialCacheKey`, `DialCacheKeyInit` | Construct an identity from string components and ordered string argument pairs; `toString()` returns its precomputed `urn` | | ||
| | `normalizeArgs(record)` | Drop undefined arguments, stringify scalar values, and sort names | | ||
| | `invalidationPrefix(namespace, keyType, id)` | Build an encoded tracked-entity prefix without braces | | ||
| | `redisClusterHashTag(value)` | Reject embedded braces and wrap the value in braces without encoding | | ||
| | `Serializer<T>` | `dump(value)` returns `string \| Buffer`; `load(payload)` returns `T`; either may return a Promise | | ||
| | `JsonSerializer<T>` | Default JSON codec, including top-level undefined support; both methods return Promises | | ||
|
|
||
| `CachedValue<Fn>` exposes a function's resolved result type. `ShadowComparator<T>` | ||
| and `StaleRecoveryPredicate` name the corresponding synchronous callbacks. | ||
| See [Direct key construction](configuration.md#constructing-keys-directly) for | ||
| defaults, encoding, and validation, and [Serialization](redis.md#serialization) | ||
| for direct codec behavior, the compile-time guard, and round-trip limitations. | ||
|
|
||
| ## Errors | ||
|
|
||
| | Root export | When it matters | | ||
| | --- | --- | | ||
| | `DialCacheError` | Base class of the four core errors below | | ||
| | `UseCaseIsAlreadyRegisteredError` | Duplicate `cached()` registration on an instance | | ||
| | `UseCaseNameIsReservedError` | Either operation API uses `"watermark"` | | ||
| | `FallbackTimeoutError` | Enabled source deadline; exposes `useCase` and `timeoutMs` | | ||
| | `RedisReadTimeoutError` | Remote wait deadline; exposes `useCase` and `timeoutMs`; serving reads log/count it before fallback, while shadow reads report a job outcome | | ||
| | `DialCacheRedisPayloadError` | Invalid raw Redis reply shape | | ||
| | `DialCacheRedisPayloadEncodingError` | Unsupported payload encoding in a frame | | ||
| | `DialCacheRedisProtocolError` | Invalid semantic mutation reply | | ||
|
|
||
| The three Redis error classes extend `Error` directly. Core cache operations | ||
| usually absorb cache-path errors; direct adapter calls and explicit maintenance | ||
| can surface them. Invalid static options may throw `TypeError` or `RangeError`. | ||
| Source errors retain their original rejection value if recovery does not serve. | ||
|
|
||
| ## Custom integrations | ||
|
|
||
| `RedisConfig`, `CompressionConfig`, `DialCacheRedisClient`, `RedisReadRequest`, | ||
| `RedisReadContext`, `RedisReadResult`, `RedisReadMiss`, `DecodedRedisFrame`, | ||
| `RedisWriteRequest`, `RedisInvalidationRequest`, and `RedisCachePayload` are root | ||
| types. Use `isRedisReadMiss` to discriminate reads. The complete semantic and | ||
| binary contracts are in [Redis and Valkey](redis.md#custom-client-contract). | ||
|
|
||
| `DialCacheMetricsAdapter` and its label/outcome types are root exports. | ||
| [Observability](observability.md#custom-adapters) lists required and optional | ||
| hooks, bounded labels, and the effects of omitting optional hooks. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The README no longer says why you'd want this.
Splitting the 1000-line monolith into
docs/plus a lean landing page is clearly right. But in trimming the promotional tone, the latest revision also dropped the evaluation content: the "Why DialCache?" framing and the sentence "It fits database lookups, service reads, and reusable computations whose results can be cached." What's left opens with a category label — "a TypeScript caching library for Node.js" — and goes straight to mechanics.Meanwhile the new "Failures and metrics" section moves in the opposite direction, adding behavior specification to the README.
AGENTS.md:52-53asks for the reverse on both counts:The bulleted pitch doesn't need to come back. One or two direct sentences on what problem this solves and where it fits would restore the evaluation half without reintroducing the sales register.
Generated by Claude Code