Add rev and cache parameters to @client decorator
rev=N: bumped by developer when function logic changes. Becomes part of the HMAC cache key — old cache entries are unreachable without purge. Effective rev for a context is max(rev) across all functions in it. cache=int|False|True: TTL escape hatch for unobservable mutations. cache=60 emits s-maxage=60. cache=False emits no-store. Default (True) emits s-maxage=31536000 (forever, purge on mutation). Effective cache for a context is min(TTL) across functions, with False taking precedence. Both parameters flow through: decorator → meta → manifest → cache key and Cache-Control headers. Implemented in both Python and TypeScript with 13 Python tests and 4 TypeScript tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,8 @@ export function client(optionsOrFn: ClientOptions | ClientOptions, fn?: Function
|
||||
route: options.route,
|
||||
methods: options.methods,
|
||||
auth: options.auth,
|
||||
rev: options.rev,
|
||||
cache: options.cache,
|
||||
}
|
||||
|
||||
register(entry)
|
||||
@@ -132,6 +134,8 @@ export function client(optionsOrFn: ClientOptions | ClientOptions, fn?: Function
|
||||
route: options.route,
|
||||
methods: options.methods,
|
||||
auth: options.auth,
|
||||
rev: options.rev,
|
||||
cache: options.cache,
|
||||
}
|
||||
|
||||
register(entry)
|
||||
|
||||
@@ -67,12 +67,34 @@ export async function handleContextFetch(
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve effective cache policy (minimum TTL across all functions)
|
||||
let effectiveCache: number | boolean = true
|
||||
for (const fnName of fnNames) {
|
||||
const entry = getFunction(fnName)
|
||||
if (!entry) continue
|
||||
if (entry.cache === false) { effectiveCache = false; break }
|
||||
if (typeof entry.cache === 'number') {
|
||||
effectiveCache = effectiveCache === true
|
||||
? entry.cache
|
||||
: Math.min(effectiveCache as number, entry.cache)
|
||||
}
|
||||
}
|
||||
|
||||
let cacheControl: string
|
||||
if (effectiveCache === false) {
|
||||
cacheControl = 'no-store'
|
||||
} else if (typeof effectiveCache === 'number') {
|
||||
cacheControl = `public, max-age=0, s-maxage=${effectiveCache}`
|
||||
} else {
|
||||
cacheControl = 'public, max-age=0, s-maxage=31536000'
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: results,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'public, max-age=0, s-maxage=31536000',
|
||||
'Cache-Control': cacheControl,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ export function generateManifest(baseUrl = '/api/mizan'): EdgeManifest {
|
||||
fnEntry.methods = entry.methods || ['GET']
|
||||
pageRoutes.push(entry.route)
|
||||
}
|
||||
if (entry.rev !== undefined && entry.rev !== 0) fnEntry.rev = entry.rev
|
||||
if (entry.cache !== undefined && entry.cache !== true) fnEntry.cache = entry.cache
|
||||
functions.push(fnEntry)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ export interface ClientOptions {
|
||||
route?: string
|
||||
methods?: string[]
|
||||
auth?: boolean
|
||||
rev?: number
|
||||
cache?: number | false
|
||||
}
|
||||
|
||||
export interface ParamDef {
|
||||
@@ -36,6 +38,8 @@ export interface RegistryEntry {
|
||||
route?: string
|
||||
methods?: string[]
|
||||
auth?: boolean
|
||||
rev?: number
|
||||
cache?: number | false
|
||||
}
|
||||
|
||||
export interface ManifestContext {
|
||||
|
||||
Reference in New Issue
Block a user