Add CDN-ready headers, ROADMAP, fold runtime into mizan-react
CDN headers on context GETs (Edge-ready): - Cache-Control: public, max-age=0, stale-while-revalidate=300 - Vary: Authorization, Cookie - Deterministic JSON (sorted keys) for consistent cache keys - Error responses: Cache-Control: no-store - Mutation POSTs: Cache-Control: no-store ROADMAP.md documents v1 deliverables and Mizan Cloud (Edge, Render, Deploy) as closed-source products built on the open-source protocol. mizan-runtime folded into mizan-react/src/runtime/ — framework-agnostic split deferred until a second frontend adapter exists. 268 Django + 33 React tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -496,7 +496,9 @@ def function_call_view(request: HttpRequest) -> JsonResponse:
|
||||
# Dedupe while preserving order
|
||||
response_data["invalidate"] = list(dict.fromkeys(invalidate))
|
||||
|
||||
return JsonResponse(response_data)
|
||||
response = JsonResponse(response_data)
|
||||
response["Cache-Control"] = "no-store"
|
||||
return response
|
||||
|
||||
|
||||
def execute_context(
|
||||
@@ -578,11 +580,15 @@ def context_fetch_view(request: HttpRequest, context_name: str) -> JsonResponse:
|
||||
|
||||
Endpoint: GET /api/mizan/ctx/<context_name>/?param1=val1¶m2=val2
|
||||
|
||||
Response: raw bundled data (keys are function names, values are results)
|
||||
Response: raw bundled data, CDN-cacheable.
|
||||
{
|
||||
"user_profile": { ... },
|
||||
"user_orders": [ ... ]
|
||||
}
|
||||
|
||||
Headers:
|
||||
Cache-Control: public, max-age=0, stale-while-revalidate=300
|
||||
Vary: Authorization, Cookie
|
||||
"""
|
||||
if request.method != "GET":
|
||||
return FunctionError(
|
||||
@@ -603,7 +609,18 @@ def context_fetch_view(request: HttpRequest, context_name: str) -> JsonResponse:
|
||||
ErrorCode.INTERNAL_ERROR: 500,
|
||||
ErrorCode.NOT_IMPLEMENTED: 501,
|
||||
}.get(result.code, 400)
|
||||
return result.to_response(status=status)
|
||||
error_response = result.to_response(status=status)
|
||||
error_response["Cache-Control"] = "no-store"
|
||||
return error_response
|
||||
|
||||
# Return raw bundled data (not wrapped in {"error": false, "data": ...})
|
||||
return JsonResponse(result.data)
|
||||
# Deterministic JSON (sorted keys) for consistent cache keys
|
||||
response = JsonResponse(result.data, json_dumps_params={"sort_keys": True})
|
||||
|
||||
# CDN-ready headers
|
||||
# max-age=0: browser always revalidates (mutations may have invalidated)
|
||||
# stale-while-revalidate: edge can serve stale while fetching fresh
|
||||
# Vary: different auth = different cache entry
|
||||
response["Cache-Control"] = "public, max-age=0, stale-while-revalidate=300"
|
||||
response["Vary"] = "Authorization, Cookie"
|
||||
|
||||
return response
|
||||
|
||||
@@ -797,6 +797,7 @@ class ServerDrivenInvalidationTests(TestCase):
|
||||
self.assertIn("result", data)
|
||||
self.assertIn("invalidate", data)
|
||||
self.assertEqual(data["invalidate"], ["user"])
|
||||
self.assertEqual(response["Cache-Control"], "no-store")
|
||||
|
||||
def test_mutation_without_affects_has_no_invalidate(self):
|
||||
"""Mutation without affects= returns result only."""
|
||||
@@ -846,6 +847,23 @@ class ServerDrivenInvalidationTests(TestCase):
|
||||
self.assertIn("team_info", data)
|
||||
self.assertEqual(data["team_info"]["name"], "team_3")
|
||||
|
||||
# CDN-ready headers
|
||||
self.assertIn("public", response["Cache-Control"])
|
||||
self.assertIn("stale-while-revalidate", response["Cache-Control"])
|
||||
self.assertIn("Authorization", response["Vary"])
|
||||
self.assertIn("Cookie", response["Vary"])
|
||||
|
||||
def test_context_error_not_cached(self):
|
||||
"""Context fetch errors must not be cached."""
|
||||
from mizan.client.executor import context_fetch_view
|
||||
|
||||
request = self.factory.get("/api/mizan/ctx/nonexistent/")
|
||||
request.user = AnonymousUser()
|
||||
|
||||
response = context_fetch_view(request, "nonexistent")
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertEqual(response["Cache-Control"], "no-store")
|
||||
|
||||
|
||||
class ContextFetchTests(TestCase):
|
||||
"""Tests for the bundled context fetch endpoint (execute_context)."""
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"name": "@mizan/runtime",
|
||||
"version": "0.1.0",
|
||||
"description": "Mizan client state engine — framework-agnostic context registry, invalidation, and fetch.",
|
||||
"type": "module",
|
||||
"main": "index.ts",
|
||||
"types": "index.ts",
|
||||
"exports": {
|
||||
".": "./index.ts"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
Reference in New Issue
Block a user