docs: add a landing README and searchable reference site - #89
Conversation
3d14c88 to
09d3ee5
Compare
09d3ee5 to
4a2dc71
Compare
lan17
left a comment
There was a problem hiding this comment.
README review — prose and accuracy pass
Reviewed the README as it stands at 6c1e0bc, with attention to the latest revision ("make the README more direct and less promotional").
Direction is right. Replacing the 1000-line monolith on main with a lean landing page plus a searchable docs/ site is the change this repo wanted, and it matches the guidance in AGENTS.md. The latest pass also does what its message claims: the promotional register is gone, sentences are shorter and land on concrete subjects, and the new failure-semantics content fills a real gap.
Accuracy checks out. I verified every factual claim the README makes against the source and docs rather than reading it as prose alone. All of them hold:
| Claim | Verified against |
|---|---|
| LRU holds 10,000 entries by default | src/dialcache.ts:270 (DEFAULT_LOCAL_MAX_SIZE) |
getOrLoad() takes a zero-arg loader and a direct key |
src/dialcache.ts:404 |
Built-in stale-on-error policy accepts only FallbackTimeoutError |
src/dialcache.ts:276 |
| A failed cache write does not discard a successful loader result | putLocalFailOpen, src/dialcache.ts:1361 |
| Later invalidation does not revoke a retained snapshot | docs/stale-on-error.md:11-12, 123-134 |
Coalescing is scoped to a request or a DialCache instance |
docs/coalescing.md (Request coalescing) |
| Node engine range | package.json engines |
| Redis/telemetry clients are optional | package.json peerDependenciesMeta |
All six deep links resolve to real headings in docs/ (typed-serializer-requirement, getorload, changing-policy-on-a-running-service, keys-ids-and-extra-dimensions, independent-fence-checks, application-owned-budgets).
So everything below is about writing, not correctness.
Worth acting on, roughly in order:
- The file-level comment — the README no longer states why you'd want this, while gaining behavior spec. This is the one real decision in the review; the rest are mechanical.
README.md:204— table header no longer matches its rows.README.md:77-80—getOrLoad()lost its only example.README.md:183— one heading over two unrelated subjects.README.md:190-191— small drift fromdocs/concepts.md's list of what needs an application-owned budget.
The remaining five are line-level nits (list ambiguity, a passive sentence, a repeated paragraph opener, a doubled conjunction, one ragged wrap).
Happy to push the mechanical fixes — 2 through 5 plus the nits — as a follow-up commit, leaving 1 as a separate call since it's about what the README is for.
Generated by Claude Code
There was a problem hiding this comment.
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-53 asks for the reverse on both counts:
Keep the README focused on evaluation and getting started. Document complete feature behavior in
docs/and link it fromdocs/index.md.
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
| @@ -4,1023 +4,212 @@ | |||
| [](https://codecov.io/gh/lan17/DialCache) | |||
| [](https://scorecard.dev/viewer/?uri=github.com/lan17/DialCache) | |||
|
|
|||
| Fine-grained TypeScript caching with explicit enabled contexts, request-local memoization, process-local and Redis TTL caching, opt-in stale-on-error recovery, stable key construction, runtime rollout controls, request coalescing, adapter-based observability, and Redis watermark-based targeted invalidation. | |||
| DialCache is a TypeScript caching library for Node.js. It wraps a function and | |||
| caches its result within a request, in a process-local LRU, or in Redis or Valkey. | |||
There was a problem hiding this comment.
The trailing "or … or" muddies the list — a reader parses "in Redis or Valkey" as a fourth item before backtracking.
Suggestion: …within a request, in a process-local LRU, or in a shared Redis or Valkey cache. The added noun makes the last item read as one thing.
Generated by Claude Code
| excluded). Production deployments should use a | ||
| [currently supported LTS release](https://nodejs.org/en/about/previous-releases). | ||
| Requires Node.js `>=22.15.0 <23.0.0 || >=23.8.0`. | ||
| Redis and telemetry clients are installed separately if you use them. |
There was a problem hiding this comment.
This reads as a passive statement of fact where the previous wording ("optional dependencies you install separately") carried more information in the same space — namely that these are genuinely optional peer deps, which package.json confirms via peerDependenciesMeta.
Suggestion: Install a Redis client and a telemetry client separately if you use those layers.
Generated by Claude Code
| This prints `Loading from source: 123` twice: once for the first enabled read, | ||
| then again for the uncached call. The second enabled read reuses the value. | ||
|
|
||
| ## Metrics | ||
| This example uses only the process-local layer. A TTL with no ramp enables that |
There was a problem hiding this comment.
Nit: two consecutive paragraphs open with "This" ("This prints…", "This example uses…"). Varying the second — "The example uses only the process-local layer." — breaks the echo.
Generated by Claude Code
| For a loader defined at the call site, | ||
| [`getOrLoad()`](https://lan17.github.io/DialCache/api.html#getorload) takes a | ||
| zero-argument function and a direct key. It uses the same cache behavior without | ||
| registering a reusable reader. |
There was a problem hiding this comment.
getOrLoad() lost its only example here, leaving it described purely abstractly ("takes a zero-argument function and a direct key") in the section whose job is getting started.
The description is accurate — src/dialcache.ts:404 confirms getOrLoad<Value>(load: () => Awaitable<Value>, options) — but the deleted nine-line snippet was the one place a reader saw the inline form actually written out, including how key and defaultConfig differ from the cached() registration above it. I'd keep the snippet and drop the prose instead.
Generated by Claude Code
| depending on the active layers. | ||
| Use `coalesce: false` when callers need independent executions. The |
There was a problem hiding this comment.
Ragged wrap: line 105 ends a sentence at 31 characters and 106 starts a new one, where every other paragraph in the file flows across the ~80-column wrap. Looks like a leftover from editing — reflowing the paragraph fixes it.
(The claim itself is right: docs/coalescing.md confirms sharing is scoped to the outermost enable() scope or the DialCache instance depending on active layers.)
Generated by Claude Code
| ```bash | ||
| pnpm benchmark:stale-on-error | ||
| ``` | ||
| ## Failures and metrics |
There was a problem hiding this comment.
"Failures and metrics" bundles two unrelated subjects under one heading — error semantics and telemetry have nothing to do with each other beyond both being operational. Splitting into "Failures" and "Metrics" costs one line and makes both scannable.
Generated by Claude Code
| Redis reads and source fallbacks have separate deadlines. Providers, serializers, | ||
| writes, and the underlying clients need application-owned time limits; see |
There was a problem hiding this comment.
Small accuracy drift from the source doc. docs/concepts.md:120-121 lists what needs application-owned budgets as:
Configuration providers, serializers, Redis writes, and invalidation
This line swapped "invalidation" for "the underlying clients". Invalidation genuinely needs a finite budget, so it's the wrong item to drop — and "the underlying clients" is vague enough that a reader won't know what to bound. Suggest keeping the doc's list.
Generated by Claude Code
|
|
||
| Publishing starts by manually running the `Release` workflow from current `main`. After the package checks pass, Semantic Release selects the next version from Conventional Commits since the highest stable `vX.Y.Z` tag. While the package is pre-1.0, breaking changes bump minor — their `BREAKING CHANGE:` footers still drive full release notes without forcing 1.0.0 — `feat` bumps minor, and every other normal PR-title type (`fix`, `perf`, `docs`, `style`, `refactor`, `test`, `build`, `chore`, `ci`, and `revert`) bumps patch. The highest required bump wins. Major bumps return when 1.0.0 is cut; `release.config.mjs` implements this table and must change together with this section. | ||
| Metrics are optional. The [Prometheus and Datadog adapters](https://lan17.github.io/DialCache/observability.html) | ||
| report requests, miss reasons, errors, latency, and shadow and recovery outcomes. |
There was a problem hiding this comment.
The doubled "and" makes the list stumble on the last item.
Suggestion: report requests, miss reasons, errors, latency, and outcomes for shadow validation and stale recovery.
Worth noting the content here is an improvement on the old "feature outcomes" — docs/observability.md:117-121 confirms there really are distinct shadow and stale-recovery counters and age histograms, so the added specificity is earned.
Generated by Claude Code
| | Topic | Guide | | ||
| | --- | --- | | ||
| | Add caching to a service | [Getting started](https://lan17.github.io/DialCache/getting-started.html) | | ||
| | Understand what runs on a hit, miss, or error | [How DialCache works](https://lan17.github.io/DialCache/concepts.html) | | ||
| | Look up methods, options, and exports | [API reference](https://lan17.github.io/DialCache/api.html) | | ||
| | Set keys, layers, TTLs, and rollout policy | [Configuration](https://lan17.github.io/DialCache/configuration.html) | | ||
| | Connect Redis or Valkey; customize serialization | [Redis and Valkey](https://lan17.github.io/DialCache/redis.html) | | ||
| | Understand shared work and deadlines | [Coalescing and liveness](https://lan17.github.io/DialCache/coalescing.html) | | ||
| | Build dashboards and diagnose misses | [Observability](https://lan17.github.io/DialCache/observability.html) | | ||
| | Upgrade, validate, or contribute | [Upgrading](https://lan17.github.io/DialCache/upgrading.html) · [Maintainer guide](https://lan17.github.io/DialCache/maintainers.html) | |
There was a problem hiding this comment.
The header no longer matches its rows. Changing | I want to… | Read | to | Topic | Guide | left all eight rows phrased as reader goals — "Add caching to a service", "Understand what runs on a hit, miss, or error", "Build dashboards and diagnose misses". Those are tasks, not topics.
Either restore the task-oriented header or rewrite the rows as noun phrases. As it stands the mismatch reads like a half-finished edit, and the goal phrasing is arguably the more useful of the two for a landing page.
Generated by Claude Code
Overview
DialCache's README was serving as its full manual, making it difficult to understand the library before encountering operational detail. The README now explains what the library does and where it fits, followed by a runnable example, the three cache layers, runtime configuration, and links into a structured reference. It keeps the operational tradeoffs visible without slogans or repeated feature pitches.
Thirteen Markdown pages provide a path from getting started and the read model to feature behavior, API lookup, and operations. VitePress renders the same files as a searchable site; contributors maintain one reference that also reads on GitHub.
Documentation structure and contracts
The guides describe the current complete-frame Redis protocol: native
SET, primary-routed trackedMGET, observed-watermark conditional refills, application clocks, the one-hour tracked retention cap, retained-snapshot stale recovery, and typed miss reasons. Historical placeholder/stamp behavior appears only in migration guidance.Configuration is organized around instance resources, operation defaults, and sparse runtime overlays, with option definitions centralized in the API reference. Operational tables explain what policy changes affect: local entries keep their insertion TTL, Redis reads classify age against the invocation's policy, ramp-down bypasses existing entries, and eligible followers can join a leader admitted under earlier policy.
The coalescing guide explains how request and instance scopes compose, what followers inherit, and where deadlines begin and end. Serialization and admitted Redis refills remain on the caller's path and require application-owned settlement budgets. The reference also makes local invalidation boundaries and retained-snapshot recovery races explicit. A remote-only policy does not by itself give every caller a new invalidation check: a follower may join a pre-invalidation flight. The guide provides the full independent-check recipe, including
coalesce: false, and a deterministic regression test covers both policies without changing runtime behavior.The API and feature guides distinguish direct helper contracts from core behavior: manual key ordering and encoding, the bounded JSON type check, native JSON method returns and failures, custom miss normalization, binary frame validation order, safe timestamp checks, strict mutation reply domains, and codec Buffer ownership. Scope closure, zero-capacity local coalescing, falsy results, retry after loader rejection, native compression acceptance, and watermark repair exceptions are explicit. Observability documents fixed Prometheus bucket schemas, structured mismatch logs, and the limits of asynchronous DogStatsD error isolation.
Site and hosting
Stable VitePress 1.6.4 supplies grouped navigation, section outlines, syntax highlighting, browser-local search, and GitHub edit links.
docs/.vitepress/config.mtssets/DialCache/as the project base and participates in the existing strict typecheck.The
Documentationworkflow builds every pull request. Pushes tomain, or manual runs selected onmain, publish through GitHub Pages. Other branches and PRs cannot upload a Pages artifact or deploy. Only the deployment job receives Pages write and OIDC permissions. Actions are pinned, installs use the lockfile, and main deployments are serialized.Pages is configured to use GitHub Actions with HTTPS enforced, and the
github-pagesenvironment permitsmain. The first publication at https://lan17.github.io/DialCache/ awaits merge. This PR remains unmerged.README links use the production URL and retain a GitHub Markdown alternative. Reference pages keep relative Markdown links, with shared heading fragments that work in VitePress and GitHub. Generated files are ignored. The npm tarball excludes documentation pages and tooling; its new README arrives with a subsequent package release. The site follows
main, and readers are directed to release notes and tagged documentation for installed versions.The maintainer guide documents these commands, page maintenance, and deployment.
Maintenance and runtime scope
The README and getting-started examples run directly as
.mtsfiles withnode --experimental-strip-types. The existing packed-consumer checks extract the actual first TypeScript blocks from both pages, typecheck them against the installed tarball, execute the documented command, and verify the output. CI also runs these checks at Node.js 22.15.0. No separate copies of the examples or new test dependency are introduced.Runtime behavior, public APIs, runtime dependencies, and release behavior are unchanged. Runtime source and release configuration edits are comments only.
VitePress is a development dependency. Its scoped Vite override selects patched 6.4.x. Build and development targets use Safari 14.1 for compatibility with the repository's patched esbuild. The existing
brace-expansionoverride advances to 5.0.9. The dependency audit reports zero vulnerabilities.Validation
corepack pnpm checkpassed strict typecheck, all 660 unit tests, ESM/CJS builds and declarations, and packed consumers, including the new documentation-example checks.corepack pnpm test:package; all README code examples are unchanged, and the inline-loader link resolves directly to the getting-started example. Production documentation build passed; all 667 rendered local links/assets and README targets resolved, including anchors and the project base path. All 32 TypeScript documentation blocks parsed; the actual direct-key example also passed strict compilation and its documented runtime output check.independent fenceranks the intended invalidation section first and navigates to it. The earlierchanging policysearch check also passed. Mobile checks at a 390-pixel viewport verified topic and section navigation, exact-option search, and scrollable API tables without page overflow. The onboarding serializer link reaches its explanation and example. Development-server validation and workflowactionlintalso passed during site setup.git diff --checkpassed.Implementation references: VitePress deployment, local search, and GitHub Pages workflow setup.