Restructure tree by role; rename mizan-runtime → mizan-base
packages/ flattens into: backends/ server protocol adapters (mizan-django, mizan-ts) frontends/ client kernel + framework adapters (mizan-base, mizan-react, mizan-vue, mizan-svelte) workers/ runtime workers (mizan-ssr) cores/ shared language-level primitives (empty for now; mizan-python forthcoming) The frontend kernel (was packages/mizan-runtime, now frontends/mizan-base) is renamed to reflect its role — it's the shared base that frontend adapters depend on directly. Reflects the substrate position that per-framework adapters wrap a single shared kernel; codegen targets the adapter, not the raw kernel. Path updates landed in: Makefile, two Gitea workflows, Dockerfile.test, four example/harness config files, .claude/settings.local.json, four docs (CLAUDE/ISSUES/ROADMAP/AFI_ARCHITECTURE), four codegen templates (stage1 + react/vue/svelte adapters), and three package.jsons (the mizan-base rename plus mizan-vue/svelte peerDeps). Generated files under examples/django-react-site/harness/src/api/ still reference @mizan/runtime — left as-is; they're regenerated artifacts and the harness is non-functional pending the React wrapper-layer codegen. Also folded in a pre-existing fix: the Gitea workflows had working-directory: react / django pointing at a layout that predates packages/, never updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
57
backends/mizan-ts/src/cache/keys.ts
vendored
Normal file
57
backends/mizan-ts/src/cache/keys.ts
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Cache key derivation — HMAC-SHA256 over JSON-canonical form.
|
||||
*
|
||||
* Protocol-critical: must produce identical output to Python's derive_cache_key.
|
||||
* Cross-language conformance verified by pin tests.
|
||||
*
|
||||
* Key format: "ctx:{context}:{hmac_hex}" — enables broad purge by prefix scan.
|
||||
*/
|
||||
|
||||
import { createHmac } from 'crypto'
|
||||
|
||||
const CONTEXT_KEY_PREFIX = 'ctx:'
|
||||
|
||||
/**
|
||||
* JSON.stringify with recursively sorted keys and no whitespace.
|
||||
* Equivalent to Python's json.dumps(obj, sort_keys=True, separators=(",", ":"))
|
||||
*/
|
||||
function stableStringify(obj: any): string {
|
||||
if (obj === null || obj === undefined) return 'null'
|
||||
if (typeof obj === 'string') return JSON.stringify(obj)
|
||||
if (typeof obj === 'number' || typeof obj === 'boolean') return String(obj)
|
||||
if (Array.isArray(obj)) {
|
||||
return '[' + obj.map(stableStringify).join(',') + ']'
|
||||
}
|
||||
const keys = Object.keys(obj).sort()
|
||||
const pairs = keys.map(k => JSON.stringify(k) + ':' + stableStringify(obj[k]))
|
||||
return '{' + pairs.join(',') + '}'
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a deterministic HMAC-SHA256 cache key.
|
||||
*
|
||||
* Returns "ctx:{context}:{hmac_hex}".
|
||||
*/
|
||||
export function deriveCacheKey(
|
||||
secret: string,
|
||||
context: string,
|
||||
params: Record<string, any>,
|
||||
userId?: string,
|
||||
rev: number = 0,
|
||||
): string {
|
||||
const sortedParams: Record<string, string> = {}
|
||||
for (const [k, v] of Object.entries(params).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
||||
sortedParams[k] = String(v)
|
||||
}
|
||||
|
||||
const keyData: Record<string, any> = { c: context, p: sortedParams, r: rev }
|
||||
if (userId !== undefined) {
|
||||
keyData.u = String(userId)
|
||||
}
|
||||
|
||||
const message = stableStringify(keyData)
|
||||
const hmacHex = createHmac('sha256', secret).update(message).digest('hex')
|
||||
return `${CONTEXT_KEY_PREFIX}${context}:${hmacHex}`
|
||||
}
|
||||
|
||||
export { CONTEXT_KEY_PREFIX }
|
||||
Reference in New Issue
Block a user