docs(arts): A2 ES->EN — cache (full translation)

Full Spanish -> English translation of cache/README.md (faithful; code blocks,
CACHE_* constants and scope/policy values kept verbatim). The mixed file's
Spanish sections (What It Solves, Keys, Scopes, Policies, Modes, Tags, Mutate,
Explain, Adapters, Events/Logger, Security, Roadmap) translated; the already-
English "Reacting to identity" section left as-is. Carries the A1 fixes
(defineActiveCache signature, ActiveEngine contract surface, demo URL).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
menubar-v4-safe
dev 3 months ago
parent 50dce1ea1b
commit 52c0631f97

@ -1,36 +1,36 @@
# cache
`arts/cache` es la capa de cache de datos del framework. No es un `Map` con TTL:
es un motor de coherencia para decidir si un dato se puede servir, si está fresco,
si debe revalidarse, bajo qué scope de seguridad vive y qué invalidaciones lo
afectan.
`arts/cache` is the framework's data cache layer. It is not a `Map` with a TTL:
it is a coherence engine that decides whether a datum can be served, whether it
is fresh, whether it must be revalidated, under which security scope it lives and
which invalidations affect it.
El patrón sigue el resto de artefactos:
The pattern follows the rest of the artifacts:
```ts
import { createEngineCache } from '$svrs/cache';
import { createActiveCache } from '$cache';
```
- `createEngineCache()` vive en `$svrs/cache` y es la API imperativa para server, servicios, repositorios, workers y tests.
- `createActiveCache()` vive en `$cache` y añade estado reactivo para Svelte.
- `App.cache` existe cuando la app declara `cache: defineActiveCache(...)` en
- `createEngineCache()` lives in `$svrs/cache` and is the imperative API for server, services, repositories, workers and tests.
- `createActiveCache()` lives in `$cache` and adds reactive state for Svelte.
- `App.cache` exists when the app declares `cache: defineActiveCache(...)` in
`services`.
- El core puro vive en `$libs/cache` como `createCacheRuntime()`.
- The pure core lives in `$libs/cache` as `createCacheRuntime()`.
## Qué Resuelve
## What It Solves
`cache` responde a preguntas que una cache simple no contesta:
`cache` answers questions a simple cache does not:
- Si el dato existe, si está `fresh`, `stale`, `expired` o invalidado.
- Si puede servirse stale mientras se refresca en background.
- Si puede servirse stale cuando el origen falla.
- Si pertenece a scope `public`, `tenant`, `actor`, `permission` o `custom`.
- Si un cambio de tag o key prefix invalidó la entrada.
- Si hay otra petición idéntica en vuelo y debe deduplicarse.
- Por qué tomó una decisión, vía `explain()`.
- Whether the datum exists, and whether it is `fresh`, `stale`, `expired` or invalidated.
- Whether it can be served stale while refreshing in the background.
- Whether it can be served stale when the origin fails.
- Whether it belongs to the `public`, `tenant`, `actor`, `permission` or `custom` scope.
- Whether a tag or key-prefix change invalidated the entry.
- Whether there is another identical request in flight and it must be deduplicated.
- Why it made a decision, via `explain()`.
## Uso Mínimo
## Minimal Use
```ts
import { createEngineCache, memoryCacheAdapter, CACHE_POLICY_INTERACTIVE } from '$svrs/cache';
@ -48,11 +48,11 @@ const project = await Cache.query({
});
```
La segunda lectura con la misma key/scope/policy servirá el valor cacheado si sigue válido.
A second read with the same key/scope/policy will serve the cached value if it is still valid.
## App.cache
`createActiveApp()` expone `App.cache` cuando se declara el servicio:
`createActiveApp()` exposes `App.cache` when the service is declared:
```ts
const App = createActiveApp({
@ -85,8 +85,8 @@ const App = createActiveApp({
});
```
Si una página demo o test se prerenderiza en modo producción y usa ese adapter memory de forma
intencional, configúralo de forma explícita:
If a demo or test page is prerendered in production mode and uses that memory
adapter intentionally, configure it explicitly:
```ts
const App = createActiveApp({
@ -98,11 +98,13 @@ const App = createActiveApp({
});
```
No uses esa opción para esconder una cache de producción accidental. En una app real, pasa
`cache.adapter` con el backend que quieras usar o deja el warning activo hasta decidir la estrategia.
Do not use that option to hide an accidental production cache. In a real app,
pass `cache.adapter` with the backend you want, or leave the warning active until
you decide the strategy.
Si usas `scope: 'actor'` o `scope: 'permission'`, el resolver debe aportar los valores
necesarios. Si faltan, el motor falla en vez de mezclar datos de usuarios.
If you use `scope: 'actor'` or `scope: 'permission'`, the resolver must provide
the necessary values. If they are missing, the engine fails instead of mixing
users' data.
### Reacting to identity / session changes
@ -141,7 +143,7 @@ policy out of the cache art and on the App composition.
## Keys
Las keys son arrays deterministas:
Keys are deterministic arrays:
```ts
['posts', { page: 1, filters: { status: 'published' } }][('tenant', tenantId, 'projects')][
@ -149,30 +151,30 @@ Las keys son arrays deterministas:
];
```
El normalizador:
The normalizer:
- Ordena keys de objetos.
- Omite campos `undefined` en objetos.
- Soporta `Date`, `Map`, `Set`, `URLSearchParams` y `BigInt`.
- Rechaza funciones, símbolos, números no finitos y referencias circulares.
- Sorts object keys.
- Omits `undefined` fields in objects.
- Supports `Date`, `Map`, `Set`, `URLSearchParams` and `BigInt`.
- Rejects functions, symbols, non-finite numbers and circular references.
## Scopes
Los scopes evitan fugas de datos:
Scopes prevent data leaks:
```ts
scope: 'public' // compartible
scope: 'tenant' // requiere tenantId
scope: 'actor' // requiere actorId; tenantId opcional
scope: 'permission' // requiere actorId + permissionHash
scope: 'public' // shareable
scope: 'tenant' // requires tenantId
scope: 'actor' // requires actorId; tenantId optional
scope: 'permission' // requires actorId + permissionHash
scope: { mode: 'custom', values: { tenantId, reportId } }
```
Regla de calidad: datos privados nunca deberían cachearse como `public`.
Quality rule: private data should never be cached as `public`.
## Policies
Las políticas expresan intención:
Policies express intent:
```ts
import {
@ -182,15 +184,15 @@ import {
} from '$svrs/cache';
```
Incluidas:
Included:
- `interactive`: UI normal, `stale-while-revalidate`.
- `catalog`: datos estables, ventanas largas.
- `privateSession`: datos privados, persistencia desactivada por defecto.
- `realtime`: casi sin cache.
- `immutable`: datos versionados o inmutables.
- `interactive`: normal UI, `stale-while-revalidate`.
- `catalog`: stable data, long windows.
- `privateSession`: private data, persistence off by default.
- `realtime`: almost no cache.
- `immutable`: versioned or immutable data.
Puedes definir las tuyas:
You can define your own:
```ts
import { createEngineCache } from '$svrs/cache';
@ -219,15 +221,15 @@ CACHE_READ_MODE_BYPASS_CACHE;
CACHE_READ_MODE_NO_STORE;
```
- `cache-first`: sirve caché mientras esté fresh.
- `stale-while-revalidate`: sirve stale dentro de ventana y refresca en background.
- `must-revalidate`: si no está fresh, bloquea y va al origen.
- `bypass-cache`: ignora lectura, llama al origen y escribe resultado.
- `no-store`: ignora lectura, llama al origen y no escribe.
- `cache-first`: serves cache while it is fresh.
- `stale-while-revalidate`: serves stale within the window and refreshes in the background.
- `must-revalidate`: if not fresh, blocks and goes to the origin.
- `bypass-cache`: ignores the read, calls the origin and writes the result.
- `no-store`: ignores the read, calls the origin and does not write.
## Tags Y Epochs
## Tags and Epochs
Las entradas pueden declarar tags:
Entries can declare tags:
```ts
tags: [
@ -236,7 +238,7 @@ tags: [
];
```
Invalidar un tag no escanea ni borra todas las keys. Incrementa un epoch:
Invalidating a tag neither scans nor deletes all keys. It bumps an epoch:
```ts
await Cache.invalidate({
@ -245,10 +247,10 @@ await Cache.invalidate({
});
```
Cada entry guarda los epochs con los que fue escrita. En la siguiente lectura,
si el epoch actual no coincide, la entry queda invalidada.
Each entry stores the epochs it was written with. On the next read, if the
current epoch does not match, the entry is invalidated.
También hay invalidación por key exacta y key prefix:
There is also invalidation by exact key and key prefix:
```ts
await Cache.invalidate({ key: ['project', projectId], scope: 'tenant' });
@ -257,7 +259,7 @@ await Cache.invalidate({ keyPrefix: ['projects'], scope: 'tenant' });
## ActiveCache
`createActiveCache()` expone la misma API que el engine y añade estado reactivo:
`createActiveCache()` exposes the same API as the engine and adds reactive state:
```ts
const Cache = createActiveCache();
@ -276,30 +278,31 @@ entry.error;
entry.loading;
```
`ActiveCacheEntry` no hace magia en efectos. Cargas explícitamente con `load()` o
`refresh()`, y el estado cambia sin bucles reactivos.
`ActiveCacheEntry` does no magic in effects. You load explicitly with `load()` or
`refresh()`, and the state changes without reactive loops.
`entry.set(value)` reutiliza por defecto la `policy`, `tags`, `schemaVersion`,
`persist` y `scope` definidos en la entry, y permite sobrescribirlos por llamada.
`entry.set(value)` reuses by default the `policy`, `tags`, `schemaVersion`,
`persist` and `scope` defined on the entry, and lets you override them per call.
### Superficie del contrato `ActiveEngine`
### `ActiveEngine` contract surface
Además de reflejar el engine (`get` / `set` / `invalidate` / `mutate` / `explain` /
`clear` / `entry`), `ActiveCache` implementa el contrato reactivo compartido:
Besides mirroring the engine (`get` / `set` / `invalidate` / `mutate` /
`explain` / `clear` / `entry`), `ActiveCache` implements the shared reactive
contract:
- `Cache.loading` — `true` mientras haya cargas en vuelo (getter reactivo).
- `Cache.lastError` — último error normalizado, o `null`.
- `Cache.disposed` — `true` tras `dispose()`.
- `Cache.clearError()` — limpia `lastError`.
- `Cache.snapshot()` — instantánea `{ lastEvent, eventCount, loading, lastError, disposed }`.
- `Cache.onChange(listener)` — invoca `listener(snapshot())` en cada cambio de estado
(carga, error, evento de cache, dispose); devuelve el desuscriptor. Es la base para
envolver `ActiveCache` en una vista reactiva.
- `Cache.dispose()` — idempotente; libera las entries propias y sus timers.
- `Cache.loading` — `true` while there are loads in flight (reactive getter).
- `Cache.lastError` — the last normalized error, or `null`.
- `Cache.disposed` — `true` after `dispose()`.
- `Cache.clearError()` — clears `lastError`.
- `Cache.snapshot()` — snapshot `{ lastEvent, eventCount, loading, lastError, disposed }`.
- `Cache.onChange(listener)` — invokes `listener(snapshot())` on every state change
(load, error, cache event, dispose); returns the unsubscriber. It is the basis for
wrapping `ActiveCache` in a reactive view.
- `Cache.dispose()` — idempotent; releases its own entries and their timers.
## Mutate
La mutación v1 es conservadora:
The v1 mutation is conservative:
```ts
await Cache.mutate({
@ -315,18 +318,18 @@ await Cache.mutate({
});
```
Semántica:
Semantics:
1. Ejecuta `commit()`.
2. Si falla, no toca la cache.
3. Si funciona, invalida tags/prefix/keys.
4. Aplica updates exactos.
1. Runs `commit()`.
2. If it fails, it does not touch the cache.
3. If it succeeds, it invalidates tags/prefix/keys.
4. Applies the exact updates.
Optimistic journal queda fuera de v1.
An optimistic journal is out of scope for v1.
## Explain
`explain()` existe desde v1 porque cache sin introspección se vuelve opaca:
`explain()` exists since v1 because a cache without introspection becomes opaque:
```ts
const info = await Cache.explain(['project', projectId], {
@ -335,7 +338,7 @@ const info = await Cache.explain(['project', projectId], {
});
```
Devuelve adapter, key efectiva, estado, decisión, razón, schema y epochs.
It returns the adapter, effective key, state, decision, reason, schema and epochs.
## Adapters
@ -348,11 +351,11 @@ const adapter = memoryCacheAdapter({
});
```
Soporta TTL, LRU aproximado, epochs e introspección.
Emite el warning constante `CACHE_MEMORY_ADAPTER_PRODUCTION_WARNING` si se crea
en runtime de producción. Es correcto para tests, demos, L1 por proceso y
desarrollo local; para producción multi-instancia usa un adapter intencional
o una composición tiered cuando esté disponible.
Supports TTL, approximate LRU, epochs and introspection. It emits the constant
warning `CACHE_MEMORY_ADAPTER_PRODUCTION_WARNING` if created in a production
runtime. It is correct for tests, demos, per-process L1 and local development;
for multi-instance production use an intentional adapter or a tiered composition
when available.
### Storage
@ -363,17 +366,17 @@ const adapter = storageCacheAdapter({
});
```
Útil para persistencia cliente. Respeta `persist: false`, por lo que una policy
privada puede impedir escritura persistente.
El adapter mantiene un índice interno para que `Cache.clear()` pueda borrar tanto
entries como epochs persistidos; esto es importante en logout/wipe.
Useful for client persistence. It respects `persist: false`, so a private policy
can prevent persistent writing. The adapter keeps an internal index so that
`Cache.clear()` can delete both entries and persisted epochs; this matters on
logout/wipe.
Redis, tiered cache, browser Cache API y adapters edge quedan para fases posteriores.
El contrato está preparado para que Redis se integre sin dependencia dura.
Redis, tiered cache, the browser Cache API and edge adapters are left for later
phases. The contract is ready for Redis to integrate without a hard dependency.
## Eventos Y Logger
## Events and Logger
El engine emite eventos:
The engine emits events:
```ts
Cache.on(CACHE_EVENT_ALL, (event) => {
@ -381,36 +384,38 @@ Cache.on(CACHE_EVENT_ALL, (event) => {
});
```
`arts/cache` enruta esos eventos al `EngineLogger` inyectado:
`arts/cache` routes those events to the injected `EngineLogger`:
- Hits/misses/sets/invalidation en `debug`.
- Errores de adapter, refresh y stale-if-error en `warn`.
- Hits/misses/sets/invalidation at `debug`.
- Adapter, refresh and stale-if-error errors at `warn`.
Las categorías y mensajes viven en `consts.ts`, no como strings dispersos.
Categories and messages live in `consts.ts`, not as scattered strings.
## Seguridad
## Security
Defaults y recomendaciones:
Defaults and recommendations:
- No uses `public` para datos con `Authorization`, sesión o permisos.
- Usa `tenant`, `actor` o `permission` para datos privados.
- `privateSession` no persiste por defecto.
- Cambios de permisos deben invalidar scope `permission` o cambiar `permissionHash`.
- Logout o cambio de identidad debería llamar a `Cache.clear()` / invalidar scopes privados;
use `applyStandardOrca(App)` (or the individual presets) so this happens automatically
when the session art emits `SESSION_EVENT_*`.
- El cliente cachea para UX, no para seguridad. Las decisiones autoritativas viven en servidor.
- Do not use `public` for data with `Authorization`, session or permissions.
- Use `tenant`, `actor` or `permission` for private data.
- `privateSession` does not persist by default.
- Permission changes must invalidate the `permission` scope or change
`permissionHash`.
- Logout or identity change should call `Cache.clear()` / invalidate private
scopes; use `applyStandardOrca(App)` (or the individual presets) so this
happens automatically when the session art emits `SESSION_EVENT_*`.
- The client caches for UX, not for security. Authoritative decisions live on the
server.
## Página De Prueba
## Test Page
`cache` se ejercita dentro de la demo integrada del ecosistema, en
`/active/get-started/ecosystem` (la documentación de runtime vive en `/active`).
Muestra `App.cache`, entry reactiva, invalidación por tag, scope actor/tenant,
eventos y `explain()`.
`cache` is exercised inside the integrated ecosystem demo, at
`/active/get-started/ecosystem` (the runtime documentation lives at `/active`).
It shows `App.cache`, a reactive entry, tag invalidation, actor/tenant scope,
events and `explain()`.
## Roadmap
v1 incluido:
Included in v1:
- `createEngineCache()`
- `createActiveCache()`
@ -423,16 +428,16 @@ v1 incluido:
- stale-if-error
- singleflight
- tag/prefix epochs
- mutate conservador
- conservative mutate
- explain
- logger integration
- `App.cache`
- orca presets (`applyCacheClearOnIdentityChange`, `applyCacheClearOnRevoke`)
Siguiente:
Next:
- `tieredCacheAdapter()`
- adapter Redis por interfaz, sin dependencia dura
- integración HTTP explícita
- Redis adapter by interface, without a hard dependency
- explicit HTTP integration
- optimistic journal
- browser Cache API para `Request/Response`
- browser Cache API for `Request/Response`

Loading…
Cancel
Save

Powered by TurnKey Linux.