/** * Content-addressed boundary precomputation cache with a generic KV * interface -- not coupled to any specific KV provider (Cloudflare, * Deno KV, Vercel KV, etc.). * * Cache keys encode the boundary content address and the two-axis tier * result so each tier combination gets its own cached compilation output. * * @module */ import { type ContentAddress } from '@czap/core'; import type { EdgeTierResult } from './edge-tier.js'; /** * Minimal KV namespace interface -- compatible with Cloudflare Workers KV, * Deno KV, or any adapter that implements get/put with string values. * * `delete` and `list` are OPTIONAL: they power active invalidation * ({@link BoundaryCache.invalidateByPath} / {@link BoundaryCache.invalidateByTag}). * A provider that omits them still caches correctly — invalidation then degrades * to the passive TTL-orphaning the content-addressed keyspace already relies on, * with a one-time diagnostic instead of a silent no-op. */ export interface KVNamespace { get(key: string, options?: { cacheTtl?: number; }): Promise; put(key: string, value: string, options?: { expirationTtl?: number; }): Promise; /** Delete a single key. Optional — required for active invalidation. */ delete?(key: string): Promise; /** * List keys under a prefix (Cloudflare Workers KV shape, paginated). Optional — * required for {@link BoundaryCache.invalidateByPath} (prefix-scan purge). */ list?(options: { prefix: string; cursor?: string; }): Promise<{ keys: ReadonlyArray<{ name: string; }>; list_complete: boolean; cursor?: string; }>; } /** * Precompiled outputs for a single boundary at a given tier. */ export interface CompiledOutputs { readonly css: string; readonly propertyRegistrations: string; readonly containerQueries: string; /** * Authored per-state ARIA/data attributes (`@aria` blocks), keyed by state * name then attribute (`ARIACompileResult.stateAttributes`). Tier-invariant. * Absent when the boundary declares no `@aria` — most boundaries. The runtime * resolves `aria[currentState]` so authored attributes update on crossings. */ readonly aria?: Readonly>>>; /** * Compiled GLSL cast (`@glsl` blocks): the shader preamble `declarations` * the runtime prepends to a fragment shader plus the default `uniformValues` * keyed by GLSL uniform identifier (`GLSLCompileResult`). Tier-invariant. * Absent when the boundary declares no `@glsl` — most boundaries. The live * GPU runtime consumer (`runtime/gpu.ts`) is out of the D0 data-path scope; * D0 only carries this field end to end. */ readonly glsl?: CompiledGLSLOutput; /** * Compiled WGSL cast (`@wgsl` blocks): the WebGPU preamble `declarations` * (state consts + uniform struct + binding) plus the default `bindingValues` * keyed by WGSL field name (`WGSLCompileResult`). Tier-invariant. Absent when * the boundary declares no `@wgsl`. The live WebGPU runtime consumer * (`runtime/wgpu.ts`) is out of the D0 data-path scope; D0 only carries it. */ readonly wgsl?: CompiledWGSLOutput; } /** * Serialized GLSL cast artifact stored on {@link CompiledOutputs.glsl}: the * shader preamble plus default uniform values. JSON-round-trippable subset of * `@czap/compiler`'s `GLSLCompileResult` (the structured `defines`/`uniforms` * arrays re-derive from `declarations`, so only the runtime-needed fields are * stored). */ export interface CompiledGLSLOutput { /** `#define` + `uniform` shader preamble block. */ readonly declarations: string; /** Default uniform values keyed by GLSL uniform identifier (`u_*`). */ readonly uniformValues: Readonly>; /** * Per-state authored uniform values keyed by state name then `u_*` identifier. * Rides the satellite payload so the live runtime resolves * `stateUniforms[currentState]` and updates uniforms on each boundary crossing * — the GLSL analog of `CompiledOutputs.aria`. Absent when the boundary's * `@glsl` blocks authored no per-state values. */ readonly stateUniforms?: Readonly>>>; } /** JSON-safe authored WGSL vector value carried through edge cache payloads. */ type WGSLUniformVector = readonly [number, number] | readonly [number, number, number] | readonly [number, number, number, number]; /** JSON-safe authored WGSL uniform value carried through edge cache payloads. */ type WGSLUniformValue = number | WGSLUniformVector; /** * Serialized WGSL cast artifact stored on {@link CompiledOutputs.wgsl}: the * WebGPU preamble plus default binding values. JSON-round-trippable subset of * `@czap/compiler`'s `WGSLCompileResult`. */ export interface CompiledWGSLOutput { /** State consts + uniform struct + `@group/@binding` preamble block. */ readonly declarations: string; /** Default binding values keyed by WGSL struct field name. */ readonly bindingValues: Readonly>; /** * Per-state authored binding values keyed by state name then field name — the * WGSL analog of {@link CompiledGLSLOutput.stateUniforms}. Rides the satellite * payload so the runtime resolves `stateBindings[currentState]` and updates * struct fields on each crossing. Absent when no per-state values were authored. */ readonly stateBindings?: Readonly>>>; } /** * Content-addressed cache for boundary compilation results keyed by * tier combination. */ export interface BoundaryCache { /** * `qualifier` joins the key when two NAMES share one boundary * `ContentAddress` but carry different compiled CSS (the same * `Boundary.make` definition referenced by two `@quantize` blocks) — * without it, the first name's compile result would serve every name. * `themeFp` likewise segregates outputs compiled under different resolved * themes (a per-request theme is a real input to the cached CSS). */ getCompiledOutputs(boundaryId: ContentAddress, tierResult: EdgeTierResult, qualifier?: string, themeFp?: string): Promise; putCompiledOutputs(boundaryId: ContentAddress, tierResult: EdgeTierResult, outputs: CompiledOutputs, qualifier?: string, themeFp?: string, tags?: readonly string[]): Promise; /** * Active purge by content address: delete every cached tier × theme variant of * one boundary (the passive answer is to mint a new `ContentAddress` and wait * for TTL — see ADR-0017). Requires `KVNamespace.list` + `delete`; without them * it emits a diagnostic and returns 0. Resolves to the number of keys deleted. */ invalidateByPath(boundaryId: ContentAddress): Promise; /** * Active purge by tag (Astro 7 `Astro.cache` tag parity): delete every entry * stored with `tag` via {@link putCompiledOutputs}'s `tags`, across all of their * tier/theme variants. Uses per-entry tag indexes when `KVNamespace.list` is * available, with a legacy JSON-index fallback. Requires `KVNamespace.delete`; * without it emits a * diagnostic and returns 0. Resolves to the number of keys deleted. */ invalidateByTag(tag: string): Promise; } interface CacheOptions { /** * Cache entry TTL in seconds. This is an eviction/cost knob, not a * freshness knob: an entry is keyed by its boundary content address, tier, * name, and resolved-theme fingerprint, so it never goes stale for a change * in ANY of those. (A `compile` callback whose output ALSO depends on * build-time inputs outside the boundary's own content — e.g. shared layout * CSS — must additionally vary `prefix` per deploy; see {@link CacheOptions.prefix}.) * Each deploy that changes boundary content mints a new `ContentAddress`, * orphaning the old keys — and Workers KV never evicts on its own and bills * storage. Set a TTL to garbage-collect superseded builds. Omit to cache * indefinitely. */ readonly ttl?: number; /** * KV key prefix (default `czap`). Doubles as the per-deploy CONTENT VERSION * for a bundled `compile` callback: when compile's output depends on * build-time content the boundary id does not cover, set `prefix` to a hash * of that compiled output (e.g. `layout-${fnv1a(compileLayoutCss())}`) so a * content change busts the keyspace. */ readonly prefix?: string; } /** * Create a {@link BoundaryCache} backed by the provided KV namespace. * * Cache keys encode the boundary content address and the two-axis tier * result so each tier combination gets its own cached compilation output. * * @example * ```ts * import { KVCache, EdgeTier } from '@czap/edge'; * import { Boundary } from '@czap/core'; * * const kv = { get: async (k: string) => null, put: async (k: string, v: string) => {} }; * const cache = KVCache.createBoundaryCache(kv, { ttl: 3600, prefix: 'myapp' }); * * const myBoundary = Boundary.make({ * input: 'viewport.width', * at: [[0, 'compact'], [768, 'wide']], * }); * const request = new Request('https://example.com', { * headers: { 'device-memory': '8', 'sec-ch-viewport-width': '1280' }, * }); * const tierResult = EdgeTier.detectTier(request.headers); * * // Store compiled outputs * await cache.putCompiledOutputs(myBoundary.id, tierResult, { * css: '...', * propertyRegistrations: '...', * containerQueries: '...', * }); * * // Retrieve cached outputs * const cached = await cache.getCompiledOutputs(myBoundary.id, tierResult); * ``` * * @param kv - A generic KV namespace implementing get/put * @param options - Optional TTL (seconds) and key prefix configuration * @returns A {@link BoundaryCache} instance */ export declare function createBoundaryCache(kv: KVNamespace, options?: CacheOptions): BoundaryCache; /** * KV cache namespace. * * Provides a content-addressed boundary precomputation cache backed by a * generic KV interface (compatible with Cloudflare Workers KV, Deno KV, * Vercel KV, etc.). Cache keys encode the boundary content address and * the two-axis tier result (motion + design) so each tier combination * gets its own cached CSS compilation output. * * @example * ```ts * import { KVCache } from '@czap/edge'; * * const kv = { get: async (k: string) => null, put: async (k: string, v: string) => {} }; * const cache = KVCache.createBoundaryCache(kv, { ttl: 3600 }); * const outputs = await cache.getCompiledOutputs(boundaryId, tierResult); * if (!outputs) { * await cache.putCompiledOutputs(boundaryId, tierResult, compiled); * } * ``` */ export declare const KVCache: { readonly createBoundaryCache: typeof createBoundaryCache; }; export {}; //# sourceMappingURL=kv-cache.d.ts.map