Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/santiagonieto09/portafolio/llms.txt

Use this file to discover all available pages before exploring further.

src/infrastructure/github/snapshot-cache.ts manages the dual-layer snapshot store. The module exports four functions used exclusively by github-api.server.ts.

Cache Configuration

ConstantValuePurpose
WEEK_MS7 * 24 * 60 * 60 * 1000 (604 800 000 ms)TTL for a fresh snapshot — 7 days
CACHE_URL'https://portfolio.internal/snapshot'Synthetic URL used as the Cloudflare Cache API lookup key
// In-memory store — scoped to the isolate / Node process
let memory: { at: number; value: PortfolioSnapshot } | null = null;
The in-memory store is a module-level variable. In Cloudflare Workers each isolate has its own memory, so a cold start always begins with memory = null and the module falls through to the Cloudflare Cache API. In Node.js / dev, the in-memory store persists for the lifetime of the process.

readSnapshot()

export async function readSnapshot(): Promise<PortfolioSnapshot | null>
Returns a fresh snapshot (written within the 7-day TTL) or null if no valid snapshot exists. The check order is:
  1. In-memory — if memory is set and Date.now() - memory.at < WEEK_MS, returns memory.value immediately without any I/O.
  2. Cloudflare Cache API — calls caches.default.match(CACHE_URL) and validates the x-snapshot-at response header against the same TTL.
  3. null — no fresh snapshot is available; the caller must re-fetch from GitHub.
When a hit is found in the Cloudflare layer, memory is populated so subsequent reads within the same isolate are served from memory.

readSnapshotOrStale()

export async function readSnapshotOrStale(): Promise<PortfolioSnapshot | null>
Behaves identically to readSnapshot() but also returns memory.value even if the snapshot has expired. This is the last-resort path used when fetchPortfolio() detects that the GitHub user endpoint is unreachable — it allows the site to serve potentially outdated data rather than returning an error or empty state.

writeSnapshot(value)

export async function writeSnapshot(value: PortfolioSnapshot): Promise<void>
Persists a freshly fetched snapshot to both cache layers:
  1. In-memorymemory is updated synchronously with the current timestamp.
  2. Cloudflare Cache API — if available, stores the snapshot as a Response object with the following headers:
HeaderValue
content-typeapplication/json
cache-controlpublic, max-age=604800
x-snapshot-atUnix millisecond timestamp (used by readFromCacheApi to validate TTL)
If the Cloudflare cache.put() call throws, the error is silently swallowed — the in-memory write is already committed.

invalidateSnapshot()

export function invalidateSnapshot(): void
Clears both cache layers so the next readSnapshot() call triggers a full re-fetch:
  1. Sets memory = null synchronously.
  2. Calls caches.default.delete(CACHE_URL) — this is fire-and-forget (void … .catch(() => {})); the function returns before the deletion promise resolves.
Called by invalidatePortfolioCache() in github-api.server.ts, which is itself called at the start of every POST /api/public/sync request.
In development and Node.js environments, globalThis.caches is undefined. All functions that touch the Cloudflare Cache API check for its presence via defaultCache() and silently skip the Cloudflare layer. Cache operations still work end-to-end using the in-memory store only.

Build docs developers (and LLMs) love