Include Structurizr in normalizer-aware cache invalidation and assert all shared renderer cache namespaces carry the normalizer build.
- commit
- eb348748c9c9a073e2a01309526d0ba6aab5b570
- parent
- 3f5a2986dea6329a47f2dbed6be578579ceed21d
- author
- Dami <dami.etoile@gmail.com>
@@ -57,7 +57,7 @@ function base64Url(bytes: Uint8Array): string {
5757 return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '')5858 }5959 60-async function cacheRequestFor(request: RenderRequest, build: string): Promise<Request> {60+export async function cacheRequestFor(request: RenderRequest, build: string): Promise<Request> {6161 const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(canonicalRequest(request)))6262 return new Request(`https://diagramzip-unit-cache.invalid/${CACHE_SCHEMA}/${encodeURIComponent(NORMALIZER_BUILD)}/${encodeURIComponent(request.engine)}/${encodeURIComponent(build)}/${base64Url(new Uint8Array(digest))}`)6363 }@@ -1,6 +1,7 @@
11 import { parseStructurizr, lowerStructurizr } from '../languages/structurizr'22 import { RenderError, RequestError } from '../runtime/errors'33 import { parseUnitRenderRequest } from '../runtime/validation'4+import { NORMALIZER_BUILD } from '../../../../shared/svg/index.js'45 56 const UNIT_ID = 'structurizr'67 const VERSION = 'structurizr@6.2.2/plantuml@1.2026.6-translation-1'@@ -21,11 +22,11 @@ function errorResponse(error: unknown): Response {
2122 return json({ error: { code: 'internal_error', message: 'The Structurizr diagram could not be rendered.' } }, { status: 500 })2223 }2324 24-async function cacheKey(input: unknown, build: string): Promise<Request> {25+export async function cacheKey(input: unknown, build: string): Promise<Request> {2526 const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(JSON.stringify(input)))2627 let binary = ''; for (const byte of new Uint8Array(digest)) binary += String.fromCharCode(byte)2728 const hash = btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '')28- return new Request(`https://diagramzip-structurizr-cache.invalid/${encodeURIComponent(build)}/${hash}`)29+ return new Request(`https://diagramzip-structurizr-cache.invalid/${encodeURIComponent(NORMALIZER_BUILD)}/${encodeURIComponent(build)}/${hash}`)2930 }3031 3132 export default {@@ -1,7 +1,8 @@
11 import { describe, expect, it } from 'vitest'2-import { createBrowserRendererUnit } from '../../shared/browser-unit'2+import { cacheKey, createBrowserRendererUnit } from '../../shared/browser-unit'3+import { NORMALIZER_BUILD } from '../../../shared/svg/index.js'34 4-const unit = createBrowserRendererUnit({5+const descriptor = {56 id: 'mermaid',67 kind: 'render',78 version: 'mermaid@11.17.0',@@ -9,7 +10,8 @@ const unit = createBrowserRendererUnit({
910 pipeline: ['mermaid'],1011 frame: '/index.html?v=1',1112 knownLosses: [],12-})13+} as const14+const unit = createBrowserRendererUnit(descriptor)1315 const fetchUnit = unit.fetch!1416 const context = {1517 waitUntil() {},@@ -46,6 +48,11 @@ function renderRequest(source: string): Request {
4648 }4749 4850 describe('browser-backed renderer unit', () => {51+ it('names the normalizer build in its cache namespace', async () => {52+ const key = await cacheKey({ engine: 'mermaid', source: 'graph TD', format: 'svg', options: {}, metadata: { title: '', description: '' }, presentation: { background: '', padding: 0, frame: false } }, descriptor)53+ expect(new URL(key.url).pathname).toContain(`/${NORMALIZER_BUILD}/mermaid/`)54+ })55+4956 it('serves the renderer frame outside the public API namespace', async () => {5057 const response = await fetchUnit(5158 new Request('https://mermaid.render.diagram.zip/index.html') as Parameters<typeof fetchUnit>[0],@@ -1,5 +1,7 @@
11 import { describe, expect, it } from 'vitest'22 import { lowerStructurizr, parseStructurizr } from '../src/languages/structurizr'3+import { cacheKey } from '../src/units/structurizr'4+import { NORMALIZER_BUILD } from '../../../shared/svg/index.js'35 import awsSource from './fixtures/structurizr/aws.structurizr?raw'46 import bigbankSource from './fixtures/structurizr/bigbank.structurizr?raw'57 import docsSource from './fixtures/structurizr/docs.structurizr?raw'@@ -15,6 +17,13 @@ const fixtures = {
1517 }1618 const fixture = (name: keyof typeof fixtures) => fixtures[name]1719 20+describe('Structurizr cache namespace', () => {21+ it('names the normalizer build', async () => {22+ const key = await cacheKey({ source: 'workspace "A" {}', options: {}, metadata: { title: '', description: '' }, presentation: { background: '', padding: 0, frame: false } }, 'unit-test')23+ expect(new URL(key.url).pathname).toContain(`/${NORMALIZER_BUILD}/unit-test/`)24+ })25+})26+1827 describe('bounded Structurizr lowering', () => {1928 it('covers the gettingstarted and bigbank view families', () => {2029 const gettingStarted = parseStructurizr(fixture('gettingstarted.structurizr'))@@ -1,5 +1,6 @@
11 import { describe, expect, it } from 'vitest'2-import { createRendererUnit, createRendererUnitGroup } from '../src/runtime/unit'2+import { cacheRequestFor, createRendererUnit, createRendererUnitGroup } from '../src/runtime/unit'3+import { NORMALIZER_BUILD } from '../../../shared/svg/index.js'34 import type { EngineId, RendererAdapter } from '../src/runtime/types'45 56 const adapter: RendererAdapter = {@@ -34,6 +35,11 @@ function request(body: unknown): Parameters<typeof fetchUnit>[0] {
3435 }3536 3637 describe('renderer unit protocol', () => {38+ it('names the normalizer build in its cache namespace', async () => {39+ const key = await cacheRequestFor({ engine: 'vegalite', source: '{}', format: 'svg', options: {}, metadata: { title: '', description: '' }, presentation: { background: '', padding: 0, frame: false } }, env.RENDERER_BUILD)40+ expect(new URL(key.url).pathname).toContain(`/${NORMALIZER_BUILD}/vegalite/`)41+ })42+3743 it('assigns its own engine and exposes the explicit translation pipeline', async () => {3844 const response = await fetchUnit(request({ source: '{"mark":"point"}' }), env, context)3945 expect(response.status).toBe(200)@@ -146,7 +146,7 @@ function canonicalRequest(request: RenderRequest): string {
146146 return JSON.stringify({ ...canonical, options: Object.fromEntries(Object.entries(request.options).sort(([a], [b]) => a.localeCompare(b))) })147147 }148148 149-async function cacheKey(request: RenderRequest, descriptor: BrowserUnitDescriptor): Promise<Request> {149+export async function cacheKey(request: RenderRequest, descriptor: BrowserUnitDescriptor): Promise<Request> {150150 const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(canonicalRequest(request)))151151 let binary = ''152152 for (const byte of new Uint8Array(digest)) binary += String.fromCharCode(byte)