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:
@@ -228,4 +228,50 @@ describe('Manifest', () => {
|
||||
expect(m.mutations.stripeWebhook.route).toBe('/webhooks/stripe/')
|
||||
expect(m.mutations.stripeWebhook.methods).toEqual(['POST'])
|
||||
})
|
||||
|
||||
test('rev appears in manifest', () => {
|
||||
clearRegistry()
|
||||
const Ctx = new ReactContext('data')
|
||||
client({ context: Ctx, rev: 3 }, async function versionedFn(itemId: number) {
|
||||
return { value: itemId }
|
||||
})
|
||||
|
||||
const m = generateManifest()
|
||||
const fn = m.contexts.data.functions[0]
|
||||
expect(fn.rev).toBe(3)
|
||||
})
|
||||
|
||||
test('cache TTL appears in manifest', () => {
|
||||
clearRegistry()
|
||||
const Ctx = new ReactContext('trending')
|
||||
client({ context: Ctx, cache: 60 }, async function trendingFn() {
|
||||
return { items: [] }
|
||||
})
|
||||
|
||||
const m = generateManifest()
|
||||
const fn = m.contexts.trending.functions[0]
|
||||
expect(fn.cache).toBe(60)
|
||||
})
|
||||
|
||||
test('cache=60 sets s-maxage=60', async () => {
|
||||
clearRegistry()
|
||||
const Ctx = new ReactContext('live')
|
||||
client({ context: Ctx, cache: 60 }, async function liveFn() {
|
||||
return { score: 42 }
|
||||
})
|
||||
|
||||
const r = await handleContextFetch('live', {})
|
||||
expect(r.headers['Cache-Control']).toBe('public, max-age=0, s-maxage=60')
|
||||
})
|
||||
|
||||
test('cache=false sets no-store', async () => {
|
||||
clearRegistry()
|
||||
const Ctx = new ReactContext('random')
|
||||
client({ context: Ctx, cache: false }, async function randomFn() {
|
||||
return { value: Math.random() }
|
||||
})
|
||||
|
||||
const r = await handleContextFetch('random', {})
|
||||
expect(r.headers['Cache-Control']).toBe('no-store')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user