@json-to-office/jto 0.33.2 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +214 -516
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/HomePage-DgVVosCp.js +106 -0
- package/dist/client/assets/HomePage-DgVVosCp.js.map +1 -0
- package/dist/client/assets/{JsonEditorPage-GZV7oJD2.js → JsonEditorPage-13oxEHfY.js} +3 -3
- package/dist/client/assets/{JsonEditorPage-GZV7oJD2.js.map → JsonEditorPage-13oxEHfY.js.map} +1 -1
- package/dist/client/assets/{MonacoPluginProvider-btNzJ41o.js → MonacoPluginProvider-DiUyU9px.js} +3 -3
- package/dist/client/assets/{MonacoPluginProvider-btNzJ41o.js.map → MonacoPluginProvider-DiUyU9px.js.map} +1 -1
- package/dist/client/assets/editor-monaco-json-CTrPZKuA.js +24 -0
- package/dist/client/assets/{editor-monaco-json-rhvIaBDI.js.map → editor-monaco-json-CTrPZKuA.js.map} +1 -1
- package/dist/client/assets/{editor-Cugq4RzH.js → editor-ouhvdGgK.js} +2 -2
- package/dist/client/assets/{editor-Cugq4RzH.js.map → editor-ouhvdGgK.js.map} +1 -1
- package/dist/client/assets/{editor-refs-store-DeJZ8FB9.js → editor-refs-store-DNQzN0_z.js} +2 -2
- package/dist/client/assets/{editor-refs-store-DeJZ8FB9.js.map → editor-refs-store-DNQzN0_z.js.map} +1 -1
- package/dist/client/assets/index-CRuLxurM.js +5 -0
- package/dist/client/assets/index-CRuLxurM.js.map +1 -0
- package/dist/client/assets/index-CnUTzeor.css +1 -0
- package/dist/client/assets/preview-D3QYaLPp.js +4 -0
- package/dist/client/assets/preview-D3QYaLPp.js.map +1 -0
- package/dist/client/assets/{ui-vendor-Cs3OXpi7.js → ui-vendor-DyQLnWB8.js} +16 -11
- package/dist/client/assets/ui-vendor-DyQLnWB8.js.map +1 -0
- package/dist/client/index.html +3 -3
- package/dist/client/templates/Alternative deck 16_9.pptx.json +91 -49
- package/dist/client/templates/Company deck 16_9.pptx.json +134 -70
- package/dist/client/templates/Company deck 4_3.pptx.json +84 -44
- package/dist/client/templates/data-report-presentation.pptx.json +100 -52
- package/dist/client/templates/modern-annual-report-1.docx.json +207 -116
- package/dist/client/templates/modern-annual-report-2.docx.json +52 -37
- package/dist/client/templates/modern-annual-report-3.docx.json +40 -20
- package/dist/client/templates/tech-report.docx.json +96 -48
- package/dist/render-server.js +168 -106
- package/dist/render-server.js.map +1 -1
- package/package.json +7 -8
- package/dist/client/assets/HomePage-Cq6woGMG.js +0 -106
- package/dist/client/assets/HomePage-Cq6woGMG.js.map +0 -1
- package/dist/client/assets/editor-monaco-json-rhvIaBDI.js +0 -24
- package/dist/client/assets/index-C9ply7vX.css +0 -1
- package/dist/client/assets/index-C9uVroFQ.js +0 -5
- package/dist/client/assets/index-C9uVroFQ.js.map +0 -1
- package/dist/client/assets/preview-B_B09gpA.js +0 -3
- package/dist/client/assets/preview-B_B09gpA.js.map +0 -1
- package/dist/client/assets/ui-vendor-Cs3OXpi7.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/render-server.ts","../src/server/rasterize-route.ts","../src/server/lib/typebox-validator.ts","../src/server/security/outbound-source-policy.ts","../src/server/middleware/hono/rate-limit.ts","../src/server/middleware/hono/concurrency-limit.ts","../src/server/middleware/hono/auth.ts","../src/server/config/index.ts"],"sourcesContent":["/** Public front server for the chart exporter and pptx rasterizer. */\n\nimport { fileURLToPath } from 'node:url';\nimport { resolve } from 'node:path';\nimport { Hono } from 'hono';\nimport { bodyLimit } from 'hono/body-limit';\nimport { HTTPException } from 'hono/http-exception';\nimport { secureHeaders } from 'hono/secure-headers';\nimport type { StatusCode } from 'hono/utils/http-status';\nimport type {\n PptxRasterizer,\n PptxBatchRasterizer,\n} from '@json-to-office/shared';\nimport { registerRasterizeRoute } from './server/rasterize-route.js';\nimport { rateLimiter } from './server/middleware/hono/rate-limit.js';\nimport { concurrencyLimiter } from './server/middleware/hono/concurrency-limit.js';\nimport {\n createApiKeyAuthMiddleware,\n type ApiKeyAuthOptions,\n} from './server/middleware/hono/auth.js';\nimport {\n assertSafeRendererPayload,\n type OutboundSourcePolicy,\n UnsafeOutboundSourceError,\n} from './server/security/outbound-source-policy.js';\n\nconst DEFAULT_UPSTREAM = 'http://127.0.0.1:7801';\nconst DEFAULT_PORT = 10_000;\nconst DEFAULT_PROXY_TIMEOUT_MS = 30_000;\nconst DEFAULT_HEALTH_TIMEOUT_MS = 2_000;\nconst DEFAULT_EXPORT_BODY_BYTES = 4 * 1024 * 1024;\nconst DEFAULT_RASTERIZE_BODY_BYTES = 32 * 1024 * 1024;\nconst DEFAULT_RESPONSE_BYTES = 24 * 1024 * 1024;\nconst MAX_CHART_DIMENSION = 4_096;\nconst MAX_CHART_PIXELS = 16_000_000;\nconst MAX_CHART_SCALE = 4;\n\ntype FetchImplementation = typeof fetch;\n\nexport interface RenderServerOptions {\n upstreamUrl?: string;\n proxyTimeoutMs?: number;\n healthTimeoutMs?: number;\n maxExportBodyBytes?: number;\n maxRasterizeBodyBytes?: number;\n maxResponseBytes?: number;\n maxConcurrent?: number;\n exportRateLimit?: number;\n rasterizeRateLimit?: number;\n rateLimitWindowMs?: number;\n trustProxyHeaders?: boolean;\n auth?: ApiKeyAuthOptions;\n sourcePolicy?: OutboundSourcePolicy;\n fetch?: FetchImplementation;\n getRasterizer?: () => PptxRasterizer;\n getBatchRasterizer?: () => PptxBatchRasterizer;\n}\n\nclass UpstreamResponseTooLargeError extends Error {}\n\nfunction positiveInteger(value: string | undefined, fallback: number): number {\n const parsed = Number.parseInt(value ?? '', 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\n/**\n * Only the two explicitly local environments relax defaults. `staging`, `prod`,\n * or a typo must not silently downgrade auth, rate limits, or source policy.\n */\nfunction isHardenedEnv(): boolean {\n const nodeEnv = process.env.NODE_ENV;\n return (\n nodeEnv !== undefined && nodeEnv !== 'development' && nodeEnv !== 'test'\n );\n}\n\nfunction envAuthOptions(): ApiKeyAuthOptions {\n const production = isHardenedEnv();\n const requestedMode = process.env.RENDER_AUTH_MODE;\n const mode =\n requestedMode === 'auto' ||\n requestedMode === 'required' ||\n requestedMode === 'disabled'\n ? requestedMode\n : production\n ? 'required'\n : 'auto';\n return {\n mode,\n apiKey: process.env.RENDER_API_KEY || process.env.API_KEY,\n headerName: process.env.RENDER_API_KEY_HEADER || 'x-api-key',\n };\n}\n\nfunction envSourcePolicy(): OutboundSourcePolicy {\n const requestedMode = process.env.OUTBOUND_SOURCE_MODE;\n const mode =\n requestedMode === 'safe' || requestedMode === 'development'\n ? requestedMode\n : isHardenedEnv()\n ? 'safe'\n : 'development';\n return {\n mode,\n allowedHosts: (process.env.OUTBOUND_HOST_ALLOWLIST || '')\n .split(',')\n .map((host) => host.trim().toLowerCase())\n .filter(Boolean),\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Resolve one export dimension the way Highcharts does: `exporting.sourceWidth`\n * / `sourceHeight` outrank `chart.width` / `chart.height`, which in turn outrank\n * the 600x400 defaults. Checking only `chart.*` would let a nested `exporting`\n * block request an arbitrarily large raster past the pixel budget below.\n */\nfunction resolveDimension(\n exporting: Record<string, unknown> | undefined,\n chart: Record<string, unknown> | undefined,\n sourceKey: 'sourceWidth' | 'sourceHeight',\n chartKey: 'width' | 'height',\n fallback: number\n): number {\n for (const candidate of [exporting?.[sourceKey], chart?.[chartKey]]) {\n if (candidate === undefined) continue;\n if (\n typeof candidate !== 'number' ||\n !Number.isFinite(candidate) ||\n candidate <= 0 ||\n candidate > MAX_CHART_DIMENSION\n ) {\n throw new HTTPException(400, {\n message: 'Requested chart dimensions are too large',\n });\n }\n return candidate;\n }\n return fallback;\n}\n\nfunction resolveScale(value: unknown, fallback: number): number {\n if (value === undefined) return fallback;\n if (\n typeof value !== 'number' ||\n !Number.isFinite(value) ||\n value <= 0 ||\n value > MAX_CHART_SCALE\n ) {\n throw new HTTPException(400, {\n message: `scale must be between 0 and ${MAX_CHART_SCALE}`,\n });\n }\n return value;\n}\n\nfunction validateExportRequest(\n value: unknown\n): asserts value is Record<string, unknown> {\n if (!isRecord(value)) {\n throw new HTTPException(400, { message: 'Export body must be an object' });\n }\n const allowedKeys = new Set(['infile', 'type', 'b64', 'scale', 'resources']);\n const unknown = Object.keys(value).filter((key) => !allowedKeys.has(key));\n if (unknown.length > 0) {\n throw new HTTPException(400, {\n message: `Unsupported export field: ${unknown[0]}`,\n });\n }\n if (!isRecord(value.infile)) {\n throw new HTTPException(400, {\n message: 'Export infile must be a chart options object',\n });\n }\n if (value.type !== 'png' || value.b64 !== true) {\n throw new HTTPException(400, {\n message: 'Only base64 PNG exports are allowed',\n });\n }\n\n const chart = isRecord(value.infile.chart) ? value.infile.chart : undefined;\n const exporting = isRecord(value.infile.exporting)\n ? value.infile.exporting\n : undefined;\n\n // A request-level `scale` outranks `exporting.scale`; validate both so\n // neither path can smuggle an oversized multiplier past the pixel budget.\n const scale = resolveScale(value.scale, resolveScale(exporting?.scale, 1));\n const width = resolveDimension(exporting, chart, 'sourceWidth', 'width', 600);\n const height = resolveDimension(\n exporting,\n chart,\n 'sourceHeight',\n 'height',\n 400\n );\n\n if (width * height * scale * scale > MAX_CHART_PIXELS) {\n throw new HTTPException(400, {\n message: 'Requested chart dimensions are too large',\n });\n }\n}\n\nfunction isTimeout(error: unknown): boolean {\n const name = (error as { name?: string })?.name;\n return name === 'TimeoutError' || name === 'AbortError';\n}\n\nasync function readLimitedBody(\n response: Response,\n maxBytes: number\n): Promise<Uint8Array> {\n const declaredLength = Number(response.headers.get('content-length'));\n if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n await response.body?.cancel();\n throw new UpstreamResponseTooLargeError();\n }\n if (!response.body) return new Uint8Array();\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n for (;;) {\n const { value, done } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel();\n throw new UpstreamResponseTooLargeError();\n }\n chunks.push(value);\n }\n\n const output = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n output.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return output;\n}\n\nexport function createRenderServerApp(options: RenderServerOptions = {}): Hono {\n const production = isHardenedEnv();\n const upstream = (\n options.upstreamUrl ||\n process.env.HIGHCHARTS_UPSTREAM_URL ||\n DEFAULT_UPSTREAM\n ).replace(/\\/$/, '');\n const proxyTimeoutMs =\n options.proxyTimeoutMs ??\n positiveInteger(process.env.PROXY_TIMEOUT_MS, DEFAULT_PROXY_TIMEOUT_MS);\n const healthTimeoutMs =\n options.healthTimeoutMs ??\n positiveInteger(process.env.HEALTH_TIMEOUT_MS, DEFAULT_HEALTH_TIMEOUT_MS);\n const maxExportBodyBytes =\n options.maxExportBodyBytes ??\n positiveInteger(\n process.env.MAX_EXPORT_BODY_SIZE,\n DEFAULT_EXPORT_BODY_BYTES\n );\n const maxRasterizeBodyBytes =\n options.maxRasterizeBodyBytes ??\n positiveInteger(\n process.env.MAX_RASTERIZE_BODY_SIZE,\n DEFAULT_RASTERIZE_BODY_BYTES\n );\n const maxResponseBytes =\n options.maxResponseBytes ??\n positiveInteger(\n process.env.MAX_RENDER_RESPONSE_SIZE,\n DEFAULT_RESPONSE_BYTES\n );\n const maxConcurrent =\n options.maxConcurrent ??\n positiveInteger(process.env.MAX_CONCURRENT_RENDERS, production ? 4 : 16);\n const windowMs =\n options.rateLimitWindowMs ??\n positiveInteger(process.env.RENDER_RATE_LIMIT_WINDOW_MS, 15 * 60 * 1000);\n const fetchImpl = options.fetch ?? fetch;\n const sourcePolicy = options.sourcePolicy ?? envSourcePolicy();\n const trustProxyHeaders =\n options.trustProxyHeaders ?? process.env.TRUST_PROXY_HEADERS === 'true';\n\n const app = new Hono();\n const auth = createApiKeyAuthMiddleware(options.auth ?? envAuthOptions());\n const capacity = concurrencyLimiter({ limit: maxConcurrent });\n\n app.use('*', secureHeaders());\n\n app.onError((error, c) => {\n if (error instanceof HTTPException) {\n return c.json({ success: false, error: error.message }, error.status);\n }\n return c.json({ success: false, error: 'Internal server error' }, 500);\n });\n\n app.get('/health', async (c) => {\n try {\n const response = await fetchImpl(`${upstream}/health`, {\n signal: AbortSignal.timeout(healthTimeoutMs),\n });\n if (response.ok) return c.text('ok');\n return c.json({ status: 'degraded', upstream: response.status }, 503);\n } catch {\n return c.json({ status: 'degraded', upstream: 'unreachable' }, 503);\n }\n });\n\n // Admission controls precede `auth` deliberately: middleware runs in order,\n // so authenticating first would answer 401 before the limiter counted the\n // attempt, leaving API-key guessing unbounded.\n registerRasterizeRoute(app, {\n getRasterizer: options.getRasterizer,\n getBatchRasterizer: options.getBatchRasterizer,\n preMiddleware: [\n rateLimiter({\n limit:\n options.rasterizeRateLimit ??\n positiveInteger(\n process.env.RASTERIZE_RATE_LIMIT,\n production ? 30 : 1000\n ),\n window: windowMs,\n namespace: 'rasterize',\n trustProxy: trustProxyHeaders,\n }),\n capacity,\n bodyLimit({\n maxSize: maxRasterizeBodyBytes,\n onError: () => {\n throw new HTTPException(413, { message: 'Request body too large' });\n },\n }),\n auth,\n ],\n sourcePolicy,\n });\n\n app.post(\n '/export',\n rateLimiter({\n limit:\n options.exportRateLimit ??\n positiveInteger(process.env.EXPORT_RATE_LIMIT, production ? 60 : 1000),\n window: windowMs,\n namespace: 'export',\n trustProxy: trustProxyHeaders,\n }),\n capacity,\n bodyLimit({\n maxSize: maxExportBodyBytes,\n onError: () => {\n throw new HTTPException(413, { message: 'Request body too large' });\n },\n }),\n auth,\n async (c) => {\n const contentType = c.req.header('content-type');\n if (!contentType?.toLowerCase().includes('application/json')) {\n throw new HTTPException(415, {\n message: 'Content-Type must be application/json',\n });\n }\n\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n throw new HTTPException(400, { message: 'Invalid JSON body' });\n }\n validateExportRequest(body);\n try {\n assertSafeRendererPayload(body, sourcePolicy);\n } catch (error) {\n if (error instanceof UnsafeOutboundSourceError) {\n throw new HTTPException(400, { message: error.message });\n }\n throw error;\n }\n\n let response: Response;\n try {\n response = await fetchImpl(`${upstream}/export`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/plain, image/png, application/json',\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(proxyTimeoutMs),\n });\n } catch (error) {\n return isTimeout(error)\n ? c.json(\n {\n error: `Highcharts upstream timed out after ${proxyTimeoutMs}ms`,\n },\n 504\n )\n : c.json({ error: 'Highcharts upstream unreachable' }, 502);\n }\n\n let payload: Uint8Array;\n try {\n payload = await readLimitedBody(response, maxResponseBytes);\n } catch (error) {\n if (error instanceof UpstreamResponseTooLargeError) {\n return c.json({ error: 'Highcharts response too large' }, 502);\n }\n if (isTimeout(error)) {\n return c.json(\n {\n error: `Highcharts upstream timed out after ${proxyTimeoutMs}ms`,\n },\n 504\n );\n }\n return c.json({ error: 'Failed to read Highcharts response' }, 502);\n }\n\n const headers: Record<string, string> = {};\n for (const name of [\n 'content-type',\n 'content-disposition',\n 'cache-control',\n ]) {\n const value = response.headers.get(name);\n if (value) headers[name] = value;\n }\n return c.body(payload, response.status as StatusCode, headers);\n }\n );\n\n for (const route of ['/export', '/rasterize', '/rasterize/batch']) {\n app.all(route, (c) => {\n c.header('Allow', 'POST');\n return c.json({ success: false, error: 'Method not allowed' }, 405);\n });\n }\n app.notFound((c) => c.json({ success: false, error: 'Not found' }, 404));\n\n return app;\n}\n\nfunction isMainModule(): boolean {\n if (!process.argv[1]) return false;\n try {\n return resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));\n } catch {\n return false;\n }\n}\n\nif (isMainModule()) {\n const port = positiveInteger(process.env.PORT, DEFAULT_PORT);\n const app = createRenderServerApp();\n void import('@hono/node-server').then(({ serve }) => {\n serve({ fetch: app.fetch, port, hostname: '0.0.0.0' }, (info) => {\n // eslint-disable-next-line no-console\n console.log(`[jto-render-server] listening on :${info.port}`);\n });\n });\n}\n","/**\n * Shared POST /rasterize + /rasterize/batch routes — validated handlers\n * mounted on BOTH the playground format router and the standalone\n * jto-render-server, so the public and in-app rasterize surfaces can't drift\n * in validation, limits, or error mapping.\n *\n * /rasterize renders one single-slide pptx presentation to a PNG.\n * /rasterize/batch (#153) renders many independent single-slide presentations\n * in one request — one LibreOffice launch instead of one per visual — and\n * returns index-aligned per-slide results (200 with item errors rather than\n * all-or-nothing).\n */\n\nimport path from 'node:path';\nimport type { Hono, MiddlewareHandler } from 'hono';\nimport { Type } from '@sinclair/typebox';\nimport { bodyLimit } from 'hono/body-limit';\nimport { HTTPException } from 'hono/http-exception';\nimport {\n clampVisualDpi,\n DEFAULT_VISUAL_DPI,\n MIN_VISUAL_DPI,\n MAX_VISUAL_DPI,\n MAX_RASTERIZE_BATCH_SLIDES,\n type PptxRasterizer,\n type PptxBatchRasterizer,\n} from '@json-to-office/shared';\nimport {\n createLibreOfficePptxRasterizer,\n createLibreOfficePptxBatchRasterizer,\n} from '@json-to-office/jto-cli';\nimport { tbValidator, getValidated } from './lib/typebox-validator.js';\nimport {\n assertSafeOutboundSources,\n type OutboundSourcePolicy,\n UnsafeOutboundSourceError,\n} from './security/outbound-source-policy.js';\n\n/** Body for POST /rasterize: a single-slide pptx presentation + optional dpi. */\nexport const RasterizeRequestSchema = Type.Object(\n {\n presentation: Type.Object({}, { additionalProperties: true }),\n dpi: Type.Optional(\n Type.Number({ minimum: MIN_VISUAL_DPI, maximum: MAX_VISUAL_DPI })\n ),\n baseDir: Type.Optional(Type.String()),\n },\n { additionalProperties: false }\n);\n\n/** Body for POST /rasterize/batch: independent slides + shared baseDir. */\nexport const RasterizeBatchRequestSchema = Type.Object(\n {\n slides: Type.Array(\n Type.Object(\n {\n presentation: Type.Object({}, { additionalProperties: true }),\n dpi: Type.Optional(\n Type.Number({ minimum: MIN_VISUAL_DPI, maximum: MAX_VISUAL_DPI })\n ),\n },\n { additionalProperties: false }\n ),\n { minItems: 1, maxItems: MAX_RASTERIZE_BATCH_SLIDES }\n ),\n baseDir: Type.Optional(Type.String()),\n },\n { additionalProperties: false }\n);\n\n// One rasterizer per process — shares the on-disk content-addressed cache\n// across every /rasterize surface in the process. Single and batch factories\n// use the same default cache directory, so the two routes share hits too.\nlet sharedRasterizer: PptxRasterizer | undefined;\nexport function getSharedRasterizer(): PptxRasterizer {\n if (!sharedRasterizer) {\n sharedRasterizer = createLibreOfficePptxRasterizer();\n }\n return sharedRasterizer;\n}\n\nlet sharedBatchRasterizer: PptxBatchRasterizer | undefined;\nexport function getSharedBatchRasterizer(): PptxBatchRasterizer {\n if (!sharedBatchRasterizer) {\n sharedBatchRasterizer = createLibreOfficePptxBatchRasterizer();\n }\n return sharedBatchRasterizer;\n}\n\nconst jsonOnly: MiddlewareHandler = async (c, next) => {\n const contentType = c.req.header('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n throw new HTTPException(400, {\n message: 'Content-Type must be application/json',\n });\n }\n await next();\n};\n\n// Raster size budget. Slide dimensions are author-controlled inches and the\n// schema does not bound them, so an oversized canvas × dpi could demand an\n// enormous bitmap; with batching that multiplies by slide count. Budgets are\n// estimated up front from the declared slide size (default pptx 16:9 when\n// absent) — same philosophy as the /export pixel budget.\nconst DEFAULT_SLIDE_WIDTH_IN = 13.34;\nconst DEFAULT_SLIDE_HEIGHT_IN = 7.5;\nconst MAX_SLIDE_PIXELS = 64_000_000;\nconst MAX_BATCH_PIXELS = 256_000_000;\n\nfunction estimateSlidePixels(presentation: unknown, dpi: number): number {\n const props = (presentation as { props?: Record<string, unknown> } | null)\n ?.props;\n const dim = (value: unknown, fallback: number): number =>\n typeof value === 'number' && Number.isFinite(value) && value > 0\n ? value\n : fallback;\n const widthIn = dim(props?.slideWidth, DEFAULT_SLIDE_WIDTH_IN);\n const heightIn = dim(props?.slideHeight, DEFAULT_SLIDE_HEIGHT_IN);\n return widthIn * dpi * (heightIn * dpi);\n}\n\nfunction assertPixelBudget(\n slides: Array<{ presentation: unknown; dpi: number }>\n): void {\n let total = 0;\n for (const slide of slides) {\n const pixels = estimateSlidePixels(slide.presentation, slide.dpi);\n if (pixels > MAX_SLIDE_PIXELS) {\n throw new HTTPException(400, {\n message: 'Requested slide dimensions are too large',\n });\n }\n total += pixels;\n }\n if (total > MAX_BATCH_PIXELS) {\n throw new HTTPException(400, {\n message: 'Requested batch raster size is too large',\n });\n }\n}\n\n/**\n * `baseDir` selects the directory local media paths resolve against. An\n * unrestricted value would let any HTTP caller point the rasterizer at\n * arbitrary server directories and exfiltrate readable files as rendered\n * pixels, so it must stay inside the server's own working tree. Callers\n * rendering documents that live elsewhere should run the server from that\n * tree (or use an in-process rasterizer).\n */\nfunction resolveSafeBaseDir(baseDir: string | undefined): string | undefined {\n if (baseDir === undefined) return undefined;\n const resolved = path.resolve(baseDir);\n const cwd = process.cwd();\n if (resolved !== cwd && !resolved.startsWith(cwd + path.sep)) {\n throw new HTTPException(400, {\n message: 'baseDir must be inside the server working directory',\n });\n }\n return resolved;\n}\n\n/** Map rasterization failures onto stable 400/503/500 responses. */\nfunction toHttpException(error: unknown): HTTPException {\n if (error instanceof HTTPException) return error;\n if (error instanceof UnsafeOutboundSourceError) {\n return new HTTPException(400, { message: error.message });\n }\n const msg =\n error instanceof Error ? error.message.toLowerCase() : String(error);\n if (msg.includes('not found') || msg.includes('rasterization needs')) {\n return new HTTPException(503, { message: (error as Error).message });\n }\n if (msg.includes('invalid') || msg.includes('validation')) {\n return new HTTPException(400, { message: (error as Error).message });\n }\n return new HTTPException(500, {\n message: 'Internal server error during rasterization',\n });\n}\n\nexport interface RasterizeRouteOptions {\n getRasterizer?: () => PptxRasterizer;\n getBatchRasterizer?: () => PptxBatchRasterizer;\n preMiddleware?: MiddlewareHandler[];\n sourcePolicy?: OutboundSourcePolicy;\n onError?: (error: unknown) => void;\n}\n\n/**\n * Register `POST /rasterize` and `POST /rasterize/batch` on a Hono router\n * with body-size limits, content-type + schema validation (dpi clamped to\n * [MIN,MAX]_VISUAL_DPI, batch capped at MAX_RASTERIZE_BATCH_SLIDES), the\n * shared rasterizers, and structured 400/413/503/500 error mapping. Both\n * routes share `preMiddleware` instances (e.g. one rate limiter), so a batch\n * and its per-visual fallback draw from the same budget.\n *\n * @param options.preMiddleware - extra middleware (e.g. a rate limiter) run first.\n */\nexport function registerRasterizeRoute(\n router: Hono<any>,\n options: RasterizeRouteOptions = {}\n): void {\n const getRasterizer = options.getRasterizer ?? getSharedRasterizer;\n const getBatchRasterizer =\n options.getBatchRasterizer ?? getSharedBatchRasterizer;\n\n const shared: MiddlewareHandler[] = [\n ...(options.preMiddleware ?? []),\n bodyLimit({\n maxSize: 32 * 1024 * 1024,\n onError: () => {\n throw new HTTPException(413, { message: 'Request body too large' });\n },\n }),\n jsonOnly,\n ];\n\n const guard = async <T>(run: () => Promise<T>): Promise<T> => {\n try {\n return await run();\n } catch (error) {\n options.onError?.(error);\n throw toHttpException(error);\n }\n };\n\n router.post(\n '/rasterize',\n ...shared,\n tbValidator(RasterizeRequestSchema),\n async (c) => {\n const { presentation, dpi, baseDir } = getValidated<{\n presentation: unknown;\n dpi?: number;\n baseDir?: string;\n }>(c, 'json');\n const safeBaseDir = resolveSafeBaseDir(baseDir);\n\n const effectiveDpi = clampVisualDpi(dpi ?? DEFAULT_VISUAL_DPI);\n assertPixelBudget([{ presentation, dpi: effectiveDpi }]);\n\n const result = await guard(async () => {\n if (options.sourcePolicy) {\n assertSafeOutboundSources(\n presentation,\n options.sourcePolicy,\n 'presentation'\n );\n }\n return getRasterizer()({\n presentation,\n dpi: effectiveDpi,\n baseDir: safeBaseDir,\n });\n });\n return c.json(result);\n }\n );\n\n router.post(\n '/rasterize/batch',\n ...shared,\n tbValidator(RasterizeBatchRequestSchema),\n async (c) => {\n const { slides, baseDir } = getValidated<{\n slides: Array<{ presentation: unknown; dpi?: number }>;\n baseDir?: string;\n }>(c, 'json');\n const safeBaseDir = resolveSafeBaseDir(baseDir);\n const effectiveSlides = slides.map((slide) => ({\n presentation: slide.presentation,\n dpi: clampVisualDpi(slide.dpi ?? DEFAULT_VISUAL_DPI),\n }));\n assertPixelBudget(effectiveSlides);\n\n const result = await guard(async () => {\n if (options.sourcePolicy) {\n slides.forEach((slide, index) =>\n assertSafeOutboundSources(\n slide.presentation,\n options.sourcePolicy!,\n `slides[${index}].presentation`\n )\n );\n }\n return getBatchRasterizer()({\n slides: effectiveSlides,\n baseDir: safeBaseDir,\n });\n });\n\n // Per-slide errors from `build` are caused by the slide's own JSON and\n // are the caller's actionable feedback; `convert`/`rasterize` failures\n // carry raw tool output (host paths, temp dirs), which the single route\n // never exposes — log them, return a generic message (C2 parity with\n // /rasterize's 500 mapping).\n return c.json({\n results: result.results.map((slide) => {\n if (slide.ok) return slide;\n options.onError?.(new Error(slide.error));\n return slide.stage === 'build'\n ? { ok: false as const, error: slide.error, stage: slide.stage }\n : {\n ok: false as const,\n error: 'Slide rasterization failed',\n stage: slide.stage,\n };\n }),\n });\n }\n );\n}\n","import { TSchema } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport { Context, Env, ValidationTargets, MiddlewareHandler } from 'hono';\nimport { HTTPException } from 'hono/http-exception';\n\n/**\n * Auto-detect raw document JSON (has `name` + `children` but no `jsonDefinition`)\n * and wrap it so callers can POST the document tree directly.\n */\nfunction autoWrapDocumentBody(value: unknown): unknown {\n if (\n typeof value === 'object' &&\n value !== null &&\n 'name' in value &&\n 'children' in value &&\n !('jsonDefinition' in value)\n ) {\n return { jsonDefinition: value };\n }\n return value;\n}\n\nexport function tbValidator<\n T extends TSchema,\n E extends Env = Env,\n P extends string = string,\n>(schema: T) {\n return (async (c: Context<E, P>, next: () => Promise<void>) => {\n let value: unknown;\n\n try {\n value = await c.req.json();\n } catch {\n throw new HTTPException(400, {\n message: 'Invalid JSON in request body',\n });\n }\n\n value = autoWrapDocumentBody(value);\n\n const isValid = Value.Check(schema, value);\n\n if (isValid) {\n (c.req as { validatedData?: Record<string, unknown> }).validatedData = {\n json: value,\n };\n await next();\n } else {\n const errors = [...Value.Errors(schema, value)].map((error) => ({\n path: error.path,\n message: error.message,\n value: error.value,\n }));\n\n const errorMessages = errors.map((e) => `${e.path || '/'}: ${e.message}`);\n\n throw new HTTPException(400, {\n message:\n errorMessages.length === 1\n ? errorMessages[0]\n : `Validation failed:\\n${errorMessages.join('\\n')}`,\n cause: { errors },\n });\n }\n }) as MiddlewareHandler<E, P, Record<string, unknown>>;\n}\n\nexport function getValidated<T>(\n c: Context,\n target: keyof ValidationTargets\n): T {\n const validatedData = (c.req as { validatedData?: Record<string, unknown> })\n .validatedData;\n if (!validatedData || !validatedData[target]) {\n throw new Error(`No validated data found for target: ${target}`);\n }\n return validatedData[target] as T;\n}\n","import type { OutboundSourceMode } from '../config/index.js';\n\nexport interface OutboundSourcePolicy {\n mode: OutboundSourceMode;\n allowedHosts: readonly string[];\n}\n\nexport class UnsafeOutboundSourceError extends Error {\n constructor(\n readonly path: string,\n reason: string\n ) {\n super(`Unsafe outbound source at ${path}: ${reason}`);\n this.name = 'UnsafeOutboundSourceError';\n }\n}\n\ntype JsonRecord = Record<string, unknown>;\n\nconst DANGEROUS_KEYS = new Set(['__proto__', 'prototype', 'constructor']);\nconst COMPONENT_SOURCE_NAMES = new Set(['image']);\n\nfunction isRecord(value: unknown): value is JsonRecord {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction hostMatches(host: string, pattern: string): boolean {\n const normalized = pattern.toLowerCase().replace(/\\.$/, '');\n if (normalized.startsWith('*.')) {\n const suffix = normalized.slice(1);\n return host.endsWith(suffix) && host.length > suffix.length;\n }\n return host === normalized;\n}\n\nfunction isPrivateIpv4(host: string): boolean {\n const parts = host.split('.');\n if (parts.length !== 4) return false;\n const octets = parts.map(Number);\n if (\n octets.some(\n (part, index) =>\n !Number.isInteger(part) ||\n part < 0 ||\n part > 255 ||\n String(part) !== parts[index]\n )\n ) {\n return false;\n }\n const [a, b] = octets;\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 100 && b >= 64 && b <= 127) ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && (b === 0 || b === 168)) ||\n (a === 198 && (b === 18 || b === 19)) ||\n a >= 224\n );\n}\n\nfunction isPrivateHost(host: string): boolean {\n const normalized = host\n .toLowerCase()\n .replace(/^\\[|\\]$/g, '')\n .replace(/\\.$/, '');\n if (\n normalized === 'localhost' ||\n normalized.endsWith('.localhost') ||\n normalized === '::' ||\n normalized === '::1'\n ) {\n return true;\n }\n if (isPrivateIpv4(normalized)) return true;\n if (normalized.startsWith('::ffff:')) {\n const mapped = normalized.slice('::ffff:'.length);\n if (isPrivateIpv4(mapped)) return true;\n const groups = mapped.match(/^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);\n if (groups) {\n const value =\n Number.parseInt(groups[1], 16) * 0x1_0000 +\n Number.parseInt(groups[2], 16);\n return isPrivateIpv4(\n [\n value >>> 24,\n (value >>> 16) & 255,\n (value >>> 8) & 255,\n value & 255,\n ].join('.')\n );\n }\n }\n const firstIpv6Group = Number.parseInt(normalized.split(':')[0] || '0', 16);\n return (\n (firstIpv6Group >= 0xfc00 && firstIpv6Group <= 0xfdff) ||\n (firstIpv6Group >= 0xfe80 && firstIpv6Group <= 0xfebf)\n );\n}\n\nfunction assertAllowedUrl(\n source: string,\n path: string,\n allowedHosts: readonly string[]\n): void {\n if (source.startsWith('data:')) return;\n\n let url: URL;\n try {\n url = new URL(source);\n } catch {\n throw new UnsafeOutboundSourceError(\n path,\n 'local and relative file paths are disabled for HTTP requests'\n );\n }\n\n if (url.protocol !== 'https:') {\n throw new UnsafeOutboundSourceError(path, 'only HTTPS URLs are allowed');\n }\n if (url.username || url.password) {\n throw new UnsafeOutboundSourceError(\n path,\n 'URLs containing credentials are not allowed'\n );\n }\n\n const host = url.hostname.toLowerCase().replace(/\\.$/, '');\n if (isPrivateHost(host)) {\n throw new UnsafeOutboundSourceError(\n path,\n `private or local host \"${host}\" is not allowed`\n );\n }\n if (!allowedHosts.some((pattern) => hostMatches(host, pattern))) {\n throw new UnsafeOutboundSourceError(\n path,\n `host \"${host}\" is not in OUTBOUND_HOST_ALLOWLIST`\n );\n }\n}\n\nfunction extractCssReferences(input: string): string[] {\n const references: string[] = [];\n const urlPattern = /url\\(\\s*(['\"]?)([^)'\"\\s]+)\\1\\s*\\)/gi;\n const importPattern = /@import\\s+(?:url\\(\\s*)?(['\"])([^'\"]+)\\1/gi;\n for (const pattern of [urlPattern, importPattern]) {\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(input))) references.push(match[2]);\n }\n return references;\n}\n\nfunction extractAbsoluteUrls(input: string): string[] {\n return input.match(/(?:https?|file):\\/\\/[^\\s'\"<>)}\\]]+/gi) ?? [];\n}\n\nfunction assertSafeSvg(\n svg: string,\n path: string,\n allowedHosts: readonly string[]\n): void {\n if (\n /<\\s*script\\b/i.test(svg) ||\n /<\\s*(?:foreignObject|iframe|object|embed)\\b/i.test(svg) ||\n /\\bon[a-z]+\\s*=/i.test(svg)\n ) {\n throw new UnsafeOutboundSourceError(path, 'active SVG content is disabled');\n }\n if (/<!\\s*(?:DOCTYPE|ENTITY)\\b/i.test(svg)) {\n throw new UnsafeOutboundSourceError(\n path,\n 'SVG document types and entities are disabled'\n );\n }\n\n const refPattern = /\\b(?:href|src)\\s*=\\s*(['\"])(.*?)\\1/gi;\n let match: RegExpExecArray | null;\n while ((match = refPattern.exec(svg))) {\n const ref = match[2].trim();\n if (!ref || ref.startsWith('#') || ref.startsWith('data:')) continue;\n assertAllowedUrl(ref, path, allowedHosts);\n }\n for (const ref of extractCssReferences(svg)) {\n if (ref.startsWith('#') || ref.startsWith('data:')) continue;\n assertAllowedUrl(ref, path, allowedHosts);\n }\n}\n\nfunction assertSafeEncodedImage(\n source: string,\n path: string,\n allowedHosts: readonly string[]\n): void {\n let candidate = source.trim();\n let declaredSvg = false;\n\n if (candidate.startsWith('data:')) {\n const comma = candidate.indexOf(',');\n if (comma < 0) {\n throw new UnsafeOutboundSourceError(path, 'malformed image data URL');\n }\n const metadata = candidate.slice(5, comma);\n declaredSvg = metadata.split(';')[0].toLowerCase() === 'image/svg+xml';\n if (!declaredSvg) return;\n const payload = candidate.slice(comma + 1);\n try {\n candidate = /(?:^|;)base64(?:;|$)/i.test(metadata)\n ? Buffer.from(payload, 'base64').toString('utf8')\n : decodeURIComponent(payload);\n } catch {\n throw new UnsafeOutboundSourceError(path, 'malformed SVG data URL');\n }\n } else if (!/<\\s*svg\\b/i.test(candidate)) {\n // The documented `base64` shape is a data URL, but inspect raw base64 too\n // so a migration client cannot smuggle active SVG through the same field.\n candidate = Buffer.from(candidate, 'base64').toString('utf8');\n }\n\n const svgStart = candidate.search(/<\\s*svg\\b/i);\n if (svgStart < 0) {\n if (declaredSvg) {\n throw new UnsafeOutboundSourceError(path, 'malformed SVG image data');\n }\n return;\n }\n assertSafeSvg(candidate, path, allowedHosts);\n}\n\nfunction assertSafeResources(\n resources: JsonRecord,\n path: string,\n allowedHosts: readonly string[]\n): void {\n if (typeof resources.js === 'string' && resources.js.trim()) {\n throw new UnsafeOutboundSourceError(\n `${path}.js`,\n 'remote renderer JavaScript resources are disabled'\n );\n }\n if (typeof resources.css === 'string') {\n for (const ref of extractCssReferences(resources.css)) {\n if (ref.startsWith('data:')) continue;\n assertAllowedUrl(ref, `${path}.css`, allowedHosts);\n }\n }\n // Highcharts loads `resources.files` as JavaScript. Allowlisting a host for\n // fonts or stylesheets must not also grant it script execution inside the\n // renderer, so reject the field outright rather than URL-checking it.\n if (Array.isArray(resources.files) && resources.files.length > 0) {\n throw new UnsafeOutboundSourceError(\n `${path}.files`,\n 'remote renderer JavaScript resources are disabled'\n );\n }\n}\n\nfunction assertNoRemoteReferences(\n value: unknown,\n path: string,\n allowedHosts: readonly string[],\n seen: WeakSet<object>\n): void {\n if (typeof value === 'string') {\n if (\n /\\b(?:fetch|XMLHttpRequest|WebSocket|EventSource)\\s*\\(/i.test(value) ||\n /\\b(?:javascript|file):/i.test(value)\n ) {\n throw new UnsafeOutboundSourceError(\n path,\n 'network-capable JavaScript and file URLs are disabled'\n );\n }\n for (const url of [\n ...extractAbsoluteUrls(value),\n ...extractCssReferences(value),\n ]) {\n if (url.startsWith('data:')) continue;\n assertAllowedUrl(url, path, allowedHosts);\n }\n if (/^\\/\\//.test(value.trim())) {\n throw new UnsafeOutboundSourceError(\n path,\n 'protocol-relative URLs are disabled'\n );\n }\n return;\n }\n if (!value || typeof value !== 'object' || seen.has(value)) return;\n seen.add(value);\n if (Array.isArray(value)) {\n value.forEach((entry, index) =>\n assertNoRemoteReferences(entry, `${path}[${index}]`, allowedHosts, seen)\n );\n return;\n }\n for (const [key, child] of Object.entries(value as JsonRecord)) {\n assertNoRemoteReferences(child, `${path}.${key}`, allowedHosts, seen);\n }\n}\n\nfunction visit(\n value: unknown,\n path: string,\n containerKey: string | undefined,\n allowedHosts: readonly string[],\n seen: WeakSet<object>\n): void {\n if (!value || typeof value !== 'object' || seen.has(value)) return;\n seen.add(value);\n\n if (Array.isArray(value)) {\n value.forEach((entry, index) =>\n visit(entry, `${path}[${index}]`, containerKey, allowedHosts, seen)\n );\n return;\n }\n\n const record = value as JsonRecord;\n for (const key of Object.keys(record)) {\n if (DANGEROUS_KEYS.has(key)) {\n throw new UnsafeOutboundSourceError(\n `${path}.${key}`,\n 'prototype mutation keys are disabled'\n );\n }\n }\n\n const kind = typeof record.kind === 'string' ? record.kind : undefined;\n if (kind === 'file' && typeof record.path === 'string') {\n throw new UnsafeOutboundSourceError(\n `${path}.path`,\n 'local file sources are disabled for HTTP requests'\n );\n }\n if (\n (kind === 'url' || kind === 'variable') &&\n typeof record.url === 'string'\n ) {\n assertAllowedUrl(record.url, `${path}.url`, allowedHosts);\n }\n\n const componentName =\n typeof record.name === 'string' ? record.name.toLowerCase() : undefined;\n const props = isRecord(record.props) ? record.props : undefined;\n if (componentName && props) {\n if (\n COMPONENT_SOURCE_NAMES.has(componentName) &&\n typeof props.path === 'string'\n ) {\n assertAllowedUrl(props.path, `${path}.props.path`, allowedHosts);\n assertSafeEncodedImage(props.path, `${path}.props.path`, allowedHosts);\n }\n if (\n COMPONENT_SOURCE_NAMES.has(componentName) &&\n typeof props.base64 === 'string'\n ) {\n assertSafeEncodedImage(\n props.base64,\n `${path}.props.base64`,\n allowedHosts\n );\n }\n if (\n (componentName === 'highcharts' || componentName === 'visual') &&\n typeof props.serverUrl === 'string'\n ) {\n assertAllowedUrl(\n props.serverUrl,\n `${path}.props.serverUrl`,\n allowedHosts\n );\n }\n if (componentName === 'highcharts') {\n if (isRecord(props.resources)) {\n assertSafeResources(\n props.resources,\n `${path}.props.resources`,\n allowedHosts\n );\n }\n assertNoRemoteReferences(\n props.options,\n `${path}.props.options`,\n allowedHosts,\n new WeakSet()\n );\n }\n }\n\n // Slide/template/visual canvas backgrounds store an image source in a plain\n // `{ image: { path } }` object rather than an image component.\n if (containerKey === 'image' && typeof record.path === 'string') {\n assertAllowedUrl(record.path, `${path}.path`, allowedHosts);\n assertSafeEncodedImage(record.path, `${path}.path`, allowedHosts);\n }\n if (typeof record.svg === 'string') {\n assertSafeSvg(record.svg, `${path}.svg`, allowedHosts);\n }\n if (containerKey === 'resources') {\n assertSafeResources(record, path, allowedHosts);\n }\n\n for (const [key, child] of Object.entries(record)) {\n visit(child, `${path}.${key}`, key, allowedHosts, seen);\n }\n}\n\n/**\n * Reject request-controlled sources capable of making the server read local\n * files or reach arbitrary network hosts. Development mode intentionally\n * preserves local playground workflows.\n */\nexport function assertSafeOutboundSources(\n value: unknown,\n policy: OutboundSourcePolicy,\n rootPath = 'request'\n): void {\n if (policy.mode === 'development') return;\n\n let parsed = value;\n if (typeof value === 'string') {\n try {\n parsed = JSON.parse(value);\n } catch {\n // Structural validation owns malformed JSON reporting.\n return;\n }\n }\n visit(parsed, rootPath, undefined, policy.allowedHosts, new WeakSet());\n}\n\n/** Extra checks for payloads executed by the Chromium-based chart renderer. */\nexport function assertSafeRendererPayload(\n value: unknown,\n policy: OutboundSourcePolicy\n): void {\n assertSafeOutboundSources(value, policy, 'export');\n if (policy.mode === 'development' || !isRecord(value)) return;\n assertNoRemoteReferences(\n value.infile,\n 'export.infile',\n policy.allowedHosts,\n new WeakSet()\n );\n}\n","import { MiddlewareHandler } from 'hono';\nimport { HTTPException } from 'hono/http-exception';\n\nexport interface RateLimitOptions {\n limit: number;\n window: number; // milliseconds\n keyGenerator?: (c: any) => string;\n namespace?: string | ((c: any) => string);\n maxEntries?: number;\n trustProxy?: boolean;\n}\n\nfunction clientAddress(c: any, trustProxy: boolean): string {\n if (trustProxy) {\n const forwarded =\n c.req.header('X-Real-IP') ||\n c.req.header('X-Forwarded-For')?.split(',')[0]?.trim();\n if (forwarded) return forwarded;\n }\n const incoming = c.env?.incoming || c.env?.req;\n return incoming?.socket?.remoteAddress || 'anonymous';\n}\n\n/**\n * Bounded, process-local rate limiting. Distributed deployments should put a\n * shared limiter at the edge too; this remains the last line of defence for a\n * single worker and never grows without bound.\n */\nexport const rateLimiter = (options: RateLimitOptions): MiddlewareHandler => {\n const { limit, window, keyGenerator } = options;\n const maxEntries = options.maxEntries ?? 10_000;\n const rateLimitStore = new Map<\n string,\n { count: number; resetTime: number }\n >();\n let lastCleanup = 0;\n\n return async (c, next) => {\n const clientKey = keyGenerator\n ? keyGenerator(c)\n : clientAddress(c, options.trustProxy === true);\n const namespace =\n typeof options.namespace === 'function'\n ? options.namespace(c)\n : options.namespace || `${c.req.method}:${c.req.path}`;\n const key = `${namespace}:${String(clientKey).slice(0, 256)}`;\n const now = Date.now();\n\n // Amortize cleanup; scanning an attacker-filled map on every request would\n // turn the protection itself into a CPU denial of service.\n if (now - lastCleanup >= Math.min(window, 60_000)) {\n for (const [storedKey, value] of rateLimitStore.entries()) {\n if (value.resetTime <= now) rateLimitStore.delete(storedKey);\n }\n lastCleanup = now;\n }\n\n const record = rateLimitStore.get(key);\n\n if (!record) {\n if (rateLimitStore.size >= maxEntries) {\n const oldestKey = rateLimitStore.keys().next().value as\n | string\n | undefined;\n if (oldestKey) rateLimitStore.delete(oldestKey);\n }\n rateLimitStore.set(key, {\n count: 1,\n resetTime: now + window,\n });\n } else if (record.resetTime <= now) {\n // Window expired, reset\n record.count = 1;\n record.resetTime = now + window;\n } else if (record.count >= limit) {\n // Rate limit exceeded\n const retryAfter = Math.ceil((record.resetTime - now) / 1000);\n\n c.header('X-RateLimit-Limit', String(limit));\n c.header('X-RateLimit-Remaining', '0');\n c.header('X-RateLimit-Reset', String(record.resetTime));\n c.header('Retry-After', String(retryAfter));\n\n throw new HTTPException(429, {\n message: 'Too many requests, please try again later',\n });\n } else {\n // Increment count\n record.count++;\n }\n\n // Add rate limit headers\n const currentRecord = rateLimitStore.get(key)!;\n c.header('X-RateLimit-Limit', String(limit));\n c.header(\n 'X-RateLimit-Remaining',\n String(Math.max(0, limit - currentRecord.count))\n );\n c.header('X-RateLimit-Reset', String(currentRecord.resetTime));\n\n await next();\n };\n};\n","import type { MiddlewareHandler } from 'hono';\n\nexport interface ConcurrencyLimitOptions {\n limit: number;\n retryAfterSeconds?: number;\n}\n\n/**\n * Process-local admission control for expensive generation/rendering work.\n * It rejects excess work instead of queueing unbounded request bodies in RAM.\n */\nexport function concurrencyLimiter(\n options: ConcurrencyLimitOptions\n): MiddlewareHandler {\n const limit = Math.max(1, Math.floor(options.limit));\n const retryAfterSeconds = options.retryAfterSeconds ?? 1;\n let active = 0;\n\n return async (c, next) => {\n c.header('X-Concurrency-Limit', String(limit));\n if (active >= limit) {\n c.header('Retry-After', String(retryAfterSeconds));\n return c.json(\n {\n success: false,\n error: 'Server is at capacity',\n code: 'CONCURRENCY_LIMIT_EXCEEDED',\n },\n 503\n );\n }\n\n active += 1;\n c.header('X-Concurrency-Remaining', String(Math.max(0, limit - active)));\n try {\n await next();\n } finally {\n active -= 1;\n }\n };\n}\n","import { createHash, timingSafeEqual } from 'node:crypto';\nimport { MiddlewareHandler } from 'hono';\nimport { config } from '../../config';\nimport type { ApiAuthMode } from '../../config';\n\nexport interface ApiKeyAuthOptions {\n mode: ApiAuthMode;\n apiKey?: string;\n headerName?: string;\n}\n\nfunction keysEqual(received: string, expected: string): boolean {\n const receivedDigest = createHash('sha256').update(received).digest();\n const expectedDigest = createHash('sha256').update(expected).digest();\n return timingSafeEqual(receivedDigest, expectedDigest);\n}\n\nfunction readCredential(\n headers: Headers,\n headerName: string\n): string | undefined {\n const direct = headers.get(headerName)?.trim();\n if (direct) {\n if (\n headerName.toLowerCase() === 'authorization' &&\n direct.toLowerCase().startsWith('bearer ')\n ) {\n return direct.slice(7).trim() || undefined;\n }\n return direct;\n }\n\n const authorization = headers.get('authorization')?.trim();\n if (authorization?.toLowerCase().startsWith('bearer ')) {\n return authorization.slice(7).trim() || undefined;\n }\n return undefined;\n}\n\n/**\n * API Key authentication middleware for Hono\n */\nexport function createApiKeyAuthMiddleware(\n options: ApiKeyAuthOptions\n): MiddlewareHandler {\n const headerName = options.headerName || 'x-api-key';\n\n return async (c, next) => {\n if (c.req.method === 'OPTIONS' || options.mode === 'disabled') {\n return next();\n }\n\n if (!options.apiKey) {\n if (options.mode === 'auto') return next();\n return c.json(\n {\n success: false,\n error: 'API authentication is not configured',\n code: 'AUTH_CONFIGURATION_ERROR',\n },\n 503\n );\n }\n\n const apiKey = readCredential(c.req.raw.headers, headerName);\n\n if (!apiKey) {\n return c.json(\n {\n success: false,\n error: 'API key required',\n code: 'UNAUTHORIZED',\n },\n 401\n );\n }\n\n if (!keysEqual(apiKey, options.apiKey)) {\n return c.json(\n {\n success: false,\n error: 'Invalid API key',\n code: 'UNAUTHORIZED',\n },\n 401\n );\n }\n\n await next();\n };\n}\n\nexport const apiKeyAuthMiddleware = createApiKeyAuthMiddleware({\n mode: config.API_AUTH_MODE,\n apiKey: config.API_KEY,\n headerName: config.API_KEY_HEADER,\n});\n","import dotenv from 'dotenv';\n\ndotenv.config();\n\nexport type ApiAuthMode = 'auto' | 'required' | 'disabled';\nexport type OutboundSourceMode = 'development' | 'safe';\n\nfunction positiveInteger(value: string | undefined, fallback: number): number {\n const parsed = Number.parseInt(value ?? '', 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\nfunction parseAuthMode(\n value: string | undefined,\n nodeEnv: 'development' | 'production' | 'test'\n): ApiAuthMode {\n if (value === 'auto' || value === 'required' || value === 'disabled') {\n return value;\n }\n // Public production APIs must opt out explicitly. Local playgrounds remain\n // zero-config; supplying a key there preserves the historical auto-enable.\n return nodeEnv === 'production' ? 'required' : 'auto';\n}\n\nfunction parseOutboundSourceMode(\n value: string | undefined,\n nodeEnv: 'development' | 'production' | 'test'\n): OutboundSourceMode {\n if (value === 'development' || value === 'safe') return value;\n return nodeEnv === 'production' ? 'safe' : 'development';\n}\n\n/**\n * Only the two explicitly local environments keep permissive defaults. Every\n * other value — `staging`, `prod`, a typo — gets production-grade hardening, so\n * a mislabelled deployment cannot silently disable auth, rate limits, or the\n * outbound-source (SSRF) checks.\n */\nfunction normalizeNodeEnv(\n value: string | undefined\n): 'development' | 'production' | 'test' {\n return value === 'development' || value === 'test' ? value : 'production';\n}\n\nfunction parseEnv(env: NodeJS.ProcessEnv) {\n const nodeEnv = normalizeNodeEnv(env.NODE_ENV || 'development');\n return {\n NODE_ENV: nodeEnv,\n // No PORT here: the listener is opened by the dev server from the CLI\n // config (`-p` > `server.port` > `PORT` env > format default), so a second\n // copy of it in this module would be silently ignored.\n CORS_ORIGIN: env.CORS_ORIGIN || '*',\n API_KEY: env.API_KEY,\n API_KEY_HEADER: env.API_KEY_HEADER || 'x-api-key',\n API_AUTH_MODE: parseAuthMode(env.API_AUTH_MODE, nodeEnv),\n RATE_LIMIT_WINDOW_MS: positiveInteger(env.RATE_LIMIT_WINDOW_MS, 900_000),\n RATE_LIMIT_MAX: positiveInteger(\n env.RATE_LIMIT_MAX,\n nodeEnv === 'production' ? 100 : 1000\n ),\n TRUST_PROXY_HEADERS: env.TRUST_PROXY_HEADERS === 'true',\n MAX_FILE_SIZE: positiveInteger(env.MAX_FILE_SIZE, 10 * 1024 * 1024),\n MAX_REQUEST_BODY_SIZE: positiveInteger(\n env.MAX_REQUEST_BODY_SIZE,\n 32 * 1024 * 1024\n ),\n MAX_CONCURRENT_REQUESTS: positiveInteger(\n env.MAX_CONCURRENT_REQUESTS,\n nodeEnv === 'production' ? 8 : 64\n ),\n OUTBOUND_SOURCE_MODE: parseOutboundSourceMode(\n env.OUTBOUND_SOURCE_MODE,\n nodeEnv\n ),\n OUTBOUND_HOST_ALLOWLIST: (env.OUTBOUND_HOST_ALLOWLIST || '')\n .split(',')\n .map((host) => host.trim().toLowerCase())\n .filter(Boolean),\n LIBREOFFICE_PATH: env.LIBREOFFICE_PATH,\n LIBREOFFICE_TIMEOUT_MS: env.LIBREOFFICE_TIMEOUT_MS\n ? positiveInteger(env.LIBREOFFICE_TIMEOUT_MS, 30000)\n : 30000,\n LOG_LEVEL: (env.LOG_LEVEL || 'info') as 'error' | 'warn' | 'info' | 'debug',\n CACHE_ENABLED: env.CACHE_ENABLED !== 'false',\n CACHE_MAX_SIZE_MB: positiveInteger(env.CACHE_MAX_SIZE_MB, 100),\n CACHE_TTL_SECONDS: positiveInteger(env.CACHE_TTL_SECONDS, 3600),\n CACHE_MAX_ITEMS: positiveInteger(env.CACHE_MAX_ITEMS, 1000),\n };\n}\n\nconst parsedEnv = parseEnv(process.env);\n\nexport const config = {\n ...parsedEnv,\n\n isDevelopment: parsedEnv.NODE_ENV === 'development',\n isProduction: parsedEnv.NODE_ENV === 'production',\n isTest: parsedEnv.NODE_ENV === 'test',\n\n features: {\n apiKey: parsedEnv.API_AUTH_MODE !== 'disabled',\n cache: parsedEnv.CACHE_ENABLED,\n },\n\n cors: {\n origin:\n parsedEnv.CORS_ORIGIN === '*'\n ? parsedEnv.CORS_ORIGIN\n : parsedEnv.CORS_ORIGIN.split(',').map((o) => o.trim()),\n credentials: true,\n },\n\n rateLimit: {\n windowMs: parsedEnv.RATE_LIMIT_WINDOW_MS,\n max: parsedEnv.RATE_LIMIT_MAX,\n trustProxy: parsedEnv.TRUST_PROXY_HEADERS,\n },\n\n requestLimits: {\n maxBodySize: parsedEnv.MAX_REQUEST_BODY_SIZE,\n maxConcurrent: parsedEnv.MAX_CONCURRENT_REQUESTS,\n maxFileSize: parsedEnv.MAX_FILE_SIZE,\n },\n\n outboundSources: {\n mode: parsedEnv.OUTBOUND_SOURCE_MODE,\n allowedHosts: parsedEnv.OUTBOUND_HOST_ALLOWLIST,\n },\n\n cache: {\n enabled: parsedEnv.CACHE_ENABLED,\n maxSizeMB: parsedEnv.CACHE_MAX_SIZE_MB,\n ttlSeconds: parsedEnv.CACHE_TTL_SECONDS,\n maxItems: parsedEnv.CACHE_MAX_ITEMS,\n },\n} as const;\n\nexport type ServerConfig = typeof config;\n"],"mappings":"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","names":["bodyLimit","HTTPException","HTTPException","path","HTTPException","HTTPException","positiveInteger","isRecord","HTTPException","bodyLimit"]}
|
|
1
|
+
{"version":3,"sources":["../src/render-server.ts","../src/server/rasterize-route.ts","../src/server/config/index.ts","../src/server/lib/typebox-validator.ts","../src/server/security/outbound-source-policy.ts","../src/server/middleware/hono/rate-limit.ts","../src/server/middleware/hono/concurrency-limit.ts","../src/server/middleware/hono/auth.ts"],"sourcesContent":["/** Public front server for the chart exporter and pptx rasterizer. */\n\nimport { fileURLToPath } from 'node:url';\nimport { resolve } from 'node:path';\nimport { Hono } from 'hono';\nimport { bodyLimit } from 'hono/body-limit';\nimport { HTTPException } from 'hono/http-exception';\nimport { secureHeaders } from 'hono/secure-headers';\nimport type { StatusCode } from 'hono/utils/http-status';\nimport type {\n PptxRasterizer,\n PptxBatchRasterizer,\n} from '@json-to-office/shared';\nimport { registerRasterizeRoute } from './server/rasterize-route.js';\nimport { rateLimiter } from './server/middleware/hono/rate-limit.js';\nimport { concurrencyLimiter } from './server/middleware/hono/concurrency-limit.js';\nimport {\n createApiKeyAuthMiddleware,\n type ApiKeyAuthOptions,\n} from './server/middleware/hono/auth.js';\nimport {\n assertSafeRendererPayload,\n type OutboundSourcePolicy,\n UnsafeOutboundSourceError,\n} from './server/security/outbound-source-policy.js';\n\nconst DEFAULT_UPSTREAM = 'http://127.0.0.1:7801';\nconst DEFAULT_PORT = 10_000;\nconst DEFAULT_PROXY_TIMEOUT_MS = 30_000;\nconst DEFAULT_HEALTH_TIMEOUT_MS = 2_000;\nconst DEFAULT_EXPORT_BODY_BYTES = 4 * 1024 * 1024;\n// A rasterize body carries the presentation JSON and, since fonts became\n// part of the protocol, the document's base64 font faces. MAX_RASTERIZE_BODY_SIZE\n// is the operator knob when a document references many faces.\nconst DEFAULT_RASTERIZE_BODY_BYTES = 32 * 1024 * 1024;\nconst DEFAULT_RESPONSE_BYTES = 24 * 1024 * 1024;\nconst MAX_CHART_DIMENSION = 4_096;\nconst MAX_CHART_PIXELS = 16_000_000;\nconst MAX_CHART_SCALE = 4;\n\ntype FetchImplementation = typeof fetch;\n\nexport interface RenderServerOptions {\n upstreamUrl?: string;\n proxyTimeoutMs?: number;\n healthTimeoutMs?: number;\n maxExportBodyBytes?: number;\n maxRasterizeBodyBytes?: number;\n maxResponseBytes?: number;\n maxConcurrent?: number;\n exportRateLimit?: number;\n rasterizeRateLimit?: number;\n rateLimitWindowMs?: number;\n trustProxyHeaders?: boolean;\n auth?: ApiKeyAuthOptions;\n sourcePolicy?: OutboundSourcePolicy;\n fetch?: FetchImplementation;\n getRasterizer?: () => PptxRasterizer;\n getBatchRasterizer?: () => PptxBatchRasterizer;\n}\n\nclass UpstreamResponseTooLargeError extends Error {}\n\nfunction positiveInteger(value: string | undefined, fallback: number): number {\n const parsed = Number.parseInt(value ?? '', 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\n/**\n * Only the two explicitly local environments relax defaults. `staging`, `prod`,\n * or a typo must not silently downgrade auth, rate limits, or source policy.\n */\nfunction isHardenedEnv(): boolean {\n const nodeEnv = process.env.NODE_ENV;\n return (\n nodeEnv !== undefined && nodeEnv !== 'development' && nodeEnv !== 'test'\n );\n}\n\nfunction envAuthOptions(): ApiKeyAuthOptions {\n const production = isHardenedEnv();\n const requestedMode = process.env.RENDER_AUTH_MODE;\n const mode =\n requestedMode === 'auto' ||\n requestedMode === 'required' ||\n requestedMode === 'disabled'\n ? requestedMode\n : production\n ? 'required'\n : 'auto';\n return {\n mode,\n apiKey: process.env.RENDER_API_KEY || process.env.API_KEY,\n headerName: process.env.RENDER_API_KEY_HEADER || 'x-api-key',\n };\n}\n\nfunction envSourcePolicy(): OutboundSourcePolicy {\n const requestedMode = process.env.OUTBOUND_SOURCE_MODE;\n const mode =\n requestedMode === 'safe' || requestedMode === 'development'\n ? requestedMode\n : isHardenedEnv()\n ? 'safe'\n : 'development';\n return {\n mode,\n allowedHosts: (process.env.OUTBOUND_HOST_ALLOWLIST || '')\n .split(',')\n .map((host) => host.trim().toLowerCase())\n .filter(Boolean),\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Resolve one export dimension the way Highcharts does: `exporting.sourceWidth`\n * / `sourceHeight` outrank `chart.width` / `chart.height`, which in turn outrank\n * the 600x400 defaults. Checking only `chart.*` would let a nested `exporting`\n * block request an arbitrarily large raster past the pixel budget below.\n */\nfunction resolveDimension(\n exporting: Record<string, unknown> | undefined,\n chart: Record<string, unknown> | undefined,\n sourceKey: 'sourceWidth' | 'sourceHeight',\n chartKey: 'width' | 'height',\n fallback: number\n): number {\n for (const candidate of [exporting?.[sourceKey], chart?.[chartKey]]) {\n if (candidate === undefined) continue;\n if (\n typeof candidate !== 'number' ||\n !Number.isFinite(candidate) ||\n candidate <= 0 ||\n candidate > MAX_CHART_DIMENSION\n ) {\n throw new HTTPException(400, {\n message: 'Requested chart dimensions are too large',\n });\n }\n return candidate;\n }\n return fallback;\n}\n\nfunction resolveScale(value: unknown, fallback: number): number {\n if (value === undefined) return fallback;\n if (\n typeof value !== 'number' ||\n !Number.isFinite(value) ||\n value <= 0 ||\n value > MAX_CHART_SCALE\n ) {\n throw new HTTPException(400, {\n message: `scale must be between 0 and ${MAX_CHART_SCALE}`,\n });\n }\n return value;\n}\n\nfunction validateExportRequest(\n value: unknown\n): asserts value is Record<string, unknown> {\n if (!isRecord(value)) {\n throw new HTTPException(400, { message: 'Export body must be an object' });\n }\n const allowedKeys = new Set(['infile', 'type', 'b64', 'scale', 'resources']);\n const unknown = Object.keys(value).filter((key) => !allowedKeys.has(key));\n if (unknown.length > 0) {\n throw new HTTPException(400, {\n message: `Unsupported export field: ${unknown[0]}`,\n });\n }\n if (!isRecord(value.infile)) {\n throw new HTTPException(400, {\n message: 'Export infile must be a chart options object',\n });\n }\n if (value.type !== 'png' || value.b64 !== true) {\n throw new HTTPException(400, {\n message: 'Only base64 PNG exports are allowed',\n });\n }\n\n const chart = isRecord(value.infile.chart) ? value.infile.chart : undefined;\n const exporting = isRecord(value.infile.exporting)\n ? value.infile.exporting\n : undefined;\n\n // A request-level `scale` outranks `exporting.scale`; validate both so\n // neither path can smuggle an oversized multiplier past the pixel budget.\n const scale = resolveScale(value.scale, resolveScale(exporting?.scale, 1));\n const width = resolveDimension(exporting, chart, 'sourceWidth', 'width', 600);\n const height = resolveDimension(\n exporting,\n chart,\n 'sourceHeight',\n 'height',\n 400\n );\n\n if (width * height * scale * scale > MAX_CHART_PIXELS) {\n throw new HTTPException(400, {\n message: 'Requested chart dimensions are too large',\n });\n }\n}\n\nfunction isTimeout(error: unknown): boolean {\n const name = (error as { name?: string })?.name;\n return name === 'TimeoutError' || name === 'AbortError';\n}\n\nasync function readLimitedBody(\n response: Response,\n maxBytes: number\n): Promise<Uint8Array> {\n const declaredLength = Number(response.headers.get('content-length'));\n if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n await response.body?.cancel();\n throw new UpstreamResponseTooLargeError();\n }\n if (!response.body) return new Uint8Array();\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n for (;;) {\n const { value, done } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel();\n throw new UpstreamResponseTooLargeError();\n }\n chunks.push(value);\n }\n\n const output = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n output.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return output;\n}\n\nexport function createRenderServerApp(options: RenderServerOptions = {}): Hono {\n const production = isHardenedEnv();\n const upstream = (\n options.upstreamUrl ||\n process.env.HIGHCHARTS_UPSTREAM_URL ||\n DEFAULT_UPSTREAM\n ).replace(/\\/$/, '');\n const proxyTimeoutMs =\n options.proxyTimeoutMs ??\n positiveInteger(process.env.PROXY_TIMEOUT_MS, DEFAULT_PROXY_TIMEOUT_MS);\n const healthTimeoutMs =\n options.healthTimeoutMs ??\n positiveInteger(process.env.HEALTH_TIMEOUT_MS, DEFAULT_HEALTH_TIMEOUT_MS);\n const maxExportBodyBytes =\n options.maxExportBodyBytes ??\n positiveInteger(\n process.env.MAX_EXPORT_BODY_SIZE,\n DEFAULT_EXPORT_BODY_BYTES\n );\n const maxRasterizeBodyBytes =\n options.maxRasterizeBodyBytes ??\n positiveInteger(\n process.env.MAX_RASTERIZE_BODY_SIZE,\n DEFAULT_RASTERIZE_BODY_BYTES\n );\n const maxResponseBytes =\n options.maxResponseBytes ??\n positiveInteger(\n process.env.MAX_RENDER_RESPONSE_SIZE,\n DEFAULT_RESPONSE_BYTES\n );\n // Each concurrent rasterize now also writes the request's font files (and,\n // on macOS, its soffice profile trees) into its own tempDir, so this bounds\n // disk churn as well as CPU.\n const maxConcurrent =\n options.maxConcurrent ??\n positiveInteger(process.env.MAX_CONCURRENT_RENDERS, production ? 4 : 16);\n const windowMs =\n options.rateLimitWindowMs ??\n positiveInteger(process.env.RENDER_RATE_LIMIT_WINDOW_MS, 15 * 60 * 1000);\n const fetchImpl = options.fetch ?? fetch;\n const sourcePolicy = options.sourcePolicy ?? envSourcePolicy();\n const trustProxyHeaders =\n options.trustProxyHeaders ?? process.env.TRUST_PROXY_HEADERS === 'true';\n\n const app = new Hono();\n const auth = createApiKeyAuthMiddleware(options.auth ?? envAuthOptions());\n const capacity = concurrencyLimiter({ limit: maxConcurrent });\n\n app.use('*', secureHeaders());\n\n app.onError((error, c) => {\n if (error instanceof HTTPException) {\n return c.json({ success: false, error: error.message }, error.status);\n }\n return c.json({ success: false, error: 'Internal server error' }, 500);\n });\n\n app.get('/health', async (c) => {\n try {\n const response = await fetchImpl(`${upstream}/health`, {\n signal: AbortSignal.timeout(healthTimeoutMs),\n });\n if (response.ok) return c.text('ok');\n return c.json({ status: 'degraded', upstream: response.status }, 503);\n } catch {\n return c.json({ status: 'degraded', upstream: 'unreachable' }, 503);\n }\n });\n\n // Admission controls precede `auth` deliberately: middleware runs in order,\n // so authenticating first would answer 401 before the limiter counted the\n // attempt, leaving API-key guessing unbounded.\n registerRasterizeRoute(app, {\n getRasterizer: options.getRasterizer,\n getBatchRasterizer: options.getBatchRasterizer,\n preMiddleware: [\n rateLimiter({\n limit:\n options.rasterizeRateLimit ??\n positiveInteger(\n process.env.RASTERIZE_RATE_LIMIT,\n production ? 30 : 1000\n ),\n window: windowMs,\n namespace: 'rasterize',\n trustProxy: trustProxyHeaders,\n }),\n capacity,\n bodyLimit({\n maxSize: maxRasterizeBodyBytes,\n onError: () => {\n throw new HTTPException(413, { message: 'Request body too large' });\n },\n }),\n auth,\n ],\n sourcePolicy,\n });\n\n app.post(\n '/export',\n rateLimiter({\n limit:\n options.exportRateLimit ??\n positiveInteger(process.env.EXPORT_RATE_LIMIT, production ? 60 : 1000),\n window: windowMs,\n namespace: 'export',\n trustProxy: trustProxyHeaders,\n }),\n capacity,\n bodyLimit({\n maxSize: maxExportBodyBytes,\n onError: () => {\n throw new HTTPException(413, { message: 'Request body too large' });\n },\n }),\n auth,\n async (c) => {\n const contentType = c.req.header('content-type');\n if (!contentType?.toLowerCase().includes('application/json')) {\n throw new HTTPException(415, {\n message: 'Content-Type must be application/json',\n });\n }\n\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n throw new HTTPException(400, { message: 'Invalid JSON body' });\n }\n validateExportRequest(body);\n try {\n assertSafeRendererPayload(body, sourcePolicy);\n } catch (error) {\n if (error instanceof UnsafeOutboundSourceError) {\n throw new HTTPException(400, { message: error.message });\n }\n throw error;\n }\n\n let response: Response;\n try {\n response = await fetchImpl(`${upstream}/export`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/plain, image/png, application/json',\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(proxyTimeoutMs),\n });\n } catch (error) {\n return isTimeout(error)\n ? c.json(\n {\n error: `Highcharts upstream timed out after ${proxyTimeoutMs}ms`,\n },\n 504\n )\n : c.json({ error: 'Highcharts upstream unreachable' }, 502);\n }\n\n let payload: Uint8Array;\n try {\n payload = await readLimitedBody(response, maxResponseBytes);\n } catch (error) {\n if (error instanceof UpstreamResponseTooLargeError) {\n return c.json({ error: 'Highcharts response too large' }, 502);\n }\n if (isTimeout(error)) {\n return c.json(\n {\n error: `Highcharts upstream timed out after ${proxyTimeoutMs}ms`,\n },\n 504\n );\n }\n return c.json({ error: 'Failed to read Highcharts response' }, 502);\n }\n\n const headers: Record<string, string> = {};\n for (const name of [\n 'content-type',\n 'content-disposition',\n 'cache-control',\n ]) {\n const value = response.headers.get(name);\n if (value) headers[name] = value;\n }\n return c.body(payload, response.status as StatusCode, headers);\n }\n );\n\n for (const route of ['/export', '/rasterize', '/rasterize/batch']) {\n app.all(route, (c) => {\n c.header('Allow', 'POST');\n return c.json({ success: false, error: 'Method not allowed' }, 405);\n });\n }\n app.notFound((c) => c.json({ success: false, error: 'Not found' }, 404));\n\n return app;\n}\n\nfunction isMainModule(): boolean {\n if (!process.argv[1]) return false;\n try {\n return resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));\n } catch {\n return false;\n }\n}\n\nif (isMainModule()) {\n const port = positiveInteger(process.env.PORT, DEFAULT_PORT);\n const app = createRenderServerApp();\n void import('@hono/node-server').then(({ serve }) => {\n serve({ fetch: app.fetch, port, hostname: '0.0.0.0' }, (info) => {\n // eslint-disable-next-line no-console\n console.log(`[jto-render-server] listening on :${info.port}`);\n });\n });\n}\n","/**\n * Shared POST /rasterize + /rasterize/batch routes — validated handlers\n * mounted on BOTH the playground format router and the standalone\n * jto-render-server, so the public and in-app rasterize surfaces can't drift\n * in validation, limits, or error mapping.\n *\n * /rasterize renders one single-slide pptx presentation to a PNG.\n * /rasterize/batch (#153) renders many independent single-slide presentations\n * in one request — one LibreOffice launch instead of one per visual — and\n * returns index-aligned per-slide results (200 with item errors rather than\n * all-or-nothing).\n */\n\nimport path from 'node:path';\nimport type { Hono, MiddlewareHandler } from 'hono';\nimport { Type } from '@sinclair/typebox';\nimport { bodyLimit } from 'hono/body-limit';\nimport { HTTPException } from 'hono/http-exception';\nimport {\n clampVisualDpi,\n DEFAULT_VISUAL_DPI,\n MIN_VISUAL_DPI,\n MAX_VISUAL_DPI,\n MAX_RASTERIZE_BATCH_SLIDES,\n MAX_RASTERIZE_FONTS,\n MAX_RASTERIZE_FONT_BYTES,\n type PptxRasterizer,\n type PptxBatchRasterizer,\n type RasterizeFontFace,\n} from '@json-to-office/shared';\nimport {\n createLibreOfficePptxRasterizer,\n createLibreOfficePptxBatchRasterizer,\n} from '@json-to-office/jto-cli';\nimport { config } from './config/index.js';\nimport { tbValidator, getValidated } from './lib/typebox-validator.js';\nimport {\n assertSafeOutboundSources,\n type OutboundSourcePolicy,\n UnsafeOutboundSourceError,\n} from './security/outbound-source-policy.js';\n\n/**\n * Strict standard-alphabet base64: whole quadruplets with correct padding,\n * nothing else. `Buffer.from(s, 'base64')` is lenient — it silently skips\n * whitespace, newlines, invalid characters, and everything up to (and\n * including) a `data:image/...;base64,` prefix — so an unconstrained string\n * decodes to garbage that gets written to disk and handed to LibreOffice.\n * Rejecting at the boundary turns that into a 400.\n *\n * Linear-time by construction: one unambiguous fixed-width repetition\n * followed by a fixed-width optional tail, so there is no backtracking\n * blowup on a multi-megabyte string.\n */\nconst BASE64_PATTERN =\n '^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$';\n\n/**\n * One font face the caller wants staged for this render's LibreOffice\n * launch. `data` is base64 of the raw font file (no `data:` prefix).\n */\nconst RasterizeFontFaceSchema = Type.Object(\n {\n family: Type.String({ minLength: 1, maxLength: 128 }),\n weight: Type.Integer({ minimum: 1, maximum: 1000 }),\n italic: Type.Boolean(),\n data: Type.String({\n minLength: 1,\n maxLength: 4 * 1024 * 1024,\n pattern: BASE64_PATTERN,\n }),\n format: Type.Optional(\n Type.Union([\n Type.Literal('ttf'),\n Type.Literal('otf'),\n Type.Literal('woff'),\n Type.Literal('woff2'),\n ])\n ),\n },\n { additionalProperties: false }\n);\nconst FontsSchema = Type.Optional(\n Type.Array(RasterizeFontFaceSchema, { maxItems: MAX_RASTERIZE_FONTS })\n);\n\n/** Body for POST /rasterize: a single-slide pptx presentation + optional dpi. */\nexport const RasterizeRequestSchema = Type.Object(\n {\n presentation: Type.Object({}, { additionalProperties: true }),\n dpi: Type.Optional(\n Type.Number({ minimum: MIN_VISUAL_DPI, maximum: MAX_VISUAL_DPI })\n ),\n baseDir: Type.Optional(Type.String()),\n fonts: FontsSchema,\n },\n { additionalProperties: false }\n);\n\n/** Body for POST /rasterize/batch: independent slides + shared baseDir. */\nexport const RasterizeBatchRequestSchema = Type.Object(\n {\n slides: Type.Array(\n Type.Object(\n {\n presentation: Type.Object({}, { additionalProperties: true }),\n dpi: Type.Optional(\n Type.Number({ minimum: MIN_VISUAL_DPI, maximum: MAX_VISUAL_DPI })\n ),\n },\n { additionalProperties: false }\n ),\n { minItems: 1, maxItems: MAX_RASTERIZE_BATCH_SLIDES }\n ),\n baseDir: Type.Optional(Type.String()),\n // Request-level, shared by every slide, exactly like `baseDir`. NOT\n // inside the per-slide object above, whose `additionalProperties: false`\n // must keep rejecting per-slide fonts: a uniform slide shape is what\n // lets batch dedupe and the disk-cache key stay identical to the\n // single-slide path.\n fonts: FontsSchema,\n },\n { additionalProperties: false }\n);\n\n// One rasterizer per process — shares the on-disk content-addressed cache\n// across every /rasterize surface in the process. Single and batch factories\n// use the same default cache directory, so the two routes share hits too.\nlet sharedRasterizer: PptxRasterizer | undefined;\nexport function getSharedRasterizer(): PptxRasterizer {\n if (!sharedRasterizer) {\n sharedRasterizer = createLibreOfficePptxRasterizer();\n }\n return sharedRasterizer;\n}\n\nlet sharedBatchRasterizer: PptxBatchRasterizer | undefined;\nexport function getSharedBatchRasterizer(): PptxBatchRasterizer {\n if (!sharedBatchRasterizer) {\n sharedBatchRasterizer = createLibreOfficePptxBatchRasterizer();\n }\n return sharedBatchRasterizer;\n}\n\nconst jsonOnly: MiddlewareHandler = async (c, next) => {\n const contentType = c.req.header('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n throw new HTTPException(400, {\n message: 'Content-Type must be application/json',\n });\n }\n await next();\n};\n\n// Raster size budget. Slide dimensions are author-controlled inches and the\n// schema does not bound them, so an oversized canvas × dpi could demand an\n// enormous bitmap; with batching that multiplies by slide count. Budgets are\n// estimated up front from the declared slide size (default pptx 16:9 when\n// absent) — same philosophy as the /export pixel budget.\nconst DEFAULT_SLIDE_WIDTH_IN = 13.34;\nconst DEFAULT_SLIDE_HEIGHT_IN = 7.5;\nconst MAX_SLIDE_PIXELS = 64_000_000;\nconst MAX_BATCH_PIXELS = 256_000_000;\n\nfunction estimateSlidePixels(presentation: unknown, dpi: number): number {\n const props = (presentation as { props?: Record<string, unknown> } | null)\n ?.props;\n const dim = (value: unknown, fallback: number): number =>\n typeof value === 'number' && Number.isFinite(value) && value > 0\n ? value\n : fallback;\n const widthIn = dim(props?.slideWidth, DEFAULT_SLIDE_WIDTH_IN);\n const heightIn = dim(props?.slideHeight, DEFAULT_SLIDE_HEIGHT_IN);\n return widthIn * dpi * (heightIn * dpi);\n}\n\nfunction assertPixelBudget(\n slides: Array<{ presentation: unknown; dpi: number }>\n): void {\n let total = 0;\n for (const slide of slides) {\n const pixels = estimateSlidePixels(slide.presentation, slide.dpi);\n if (pixels > MAX_SLIDE_PIXELS) {\n throw new HTTPException(400, {\n message: 'Requested slide dimensions are too large',\n });\n }\n total += pixels;\n }\n if (total > MAX_BATCH_PIXELS) {\n throw new HTTPException(400, {\n message: 'Requested batch raster size is too large',\n });\n }\n}\n\n/**\n * Bound the decoded font payload. The per-face `maxLength` and\n * `MAX_RASTERIZE_FONTS` alone would admit far more than the body limit\n * allows, and staging writes every face to disk before soffice starts.\n */\nfunction assertFontBudget(fonts: RasterizeFontFace[] | undefined): void {\n if (!fonts?.length) return;\n // base64 → bytes ≈ len × 3/4. A cheap upper bound; no decode needed.\n const bytes = fonts.reduce(\n (sum, f) => sum + Math.ceil(f.data.length * 0.75),\n 0\n );\n if (bytes > MAX_RASTERIZE_FONT_BYTES) {\n throw new HTTPException(400, {\n message: 'Requested font payload is too large',\n });\n }\n}\n\n/**\n * `baseDir` selects the directory local media paths resolve against. An\n * unrestricted value would let any HTTP caller point the rasterizer at\n * arbitrary server directories and exfiltrate readable files as rendered\n * pixels, so it must stay inside the server's own working tree. Callers\n * rendering documents that live elsewhere should run the server from that\n * tree (or use an in-process rasterizer).\n */\nfunction resolveSafeBaseDir(baseDir: string | undefined): string | undefined {\n if (baseDir === undefined) return undefined;\n const resolved = path.resolve(baseDir);\n const cwd = process.cwd();\n if (resolved !== cwd && !resolved.startsWith(cwd + path.sep)) {\n throw new HTTPException(400, {\n message: 'baseDir must be inside the server working directory',\n });\n }\n return resolved;\n}\n\n/** Map rasterization failures onto stable 400/503/500 responses. */\nfunction toHttpException(error: unknown): HTTPException {\n if (error instanceof HTTPException) return error;\n if (error instanceof UnsafeOutboundSourceError) {\n return new HTTPException(400, { message: error.message });\n }\n const msg =\n error instanceof Error ? error.message.toLowerCase() : String(error);\n if (msg.includes('not found') || msg.includes('rasterization needs')) {\n return new HTTPException(503, { message: (error as Error).message });\n }\n if (msg.includes('invalid') || msg.includes('validation')) {\n return new HTTPException(400, { message: (error as Error).message });\n }\n return new HTTPException(500, {\n message: 'Internal server error during rasterization',\n });\n}\n\n/**\n * Hard ceiling for the rasterize body limit, whatever an operator configures.\n * The schema's own worst case (MAX_RASTERIZE_FONTS × 4 MiB of base64 = 128\n * MiB) must never be reachable, and a body this large is already far past the\n * MAX_RASTERIZE_FONT_BYTES working budget.\n */\nconst MAX_RASTERIZE_BODY_CEILING = 64 * 1024 * 1024;\n\n/**\n * Resolve the rasterize body limit. Precedence, most specific first:\n *\n * 1. an explicit `options.maxBodyBytes` from the mounting server,\n * 2. `MAX_RASTERIZE_BODY_SIZE` — the route's own operator knob, also read\n * by the standalone render server for its outer limit,\n * 3. `config.requestLimits.maxBodySize` (`MAX_REQUEST_BODY_SIZE`), the\n * process-wide configured ceiling,\n *\n * clamped to {@link MAX_RASTERIZE_BODY_CEILING}. Previously this was a\n * hardcoded 32 MiB, which silently discarded any configured value above it.\n * Resolved lazily (at route registration) so dotenv has certainly run.\n */\nexport function resolveMaxBodyBytes(explicit?: number): number {\n const fromEnv = Number.parseInt(\n process.env.MAX_RASTERIZE_BODY_SIZE ?? '',\n 10\n );\n const configured =\n explicit ??\n (Number.isFinite(fromEnv) && fromEnv > 0\n ? fromEnv\n : config.requestLimits.maxBodySize);\n return Math.min(configured, MAX_RASTERIZE_BODY_CEILING);\n}\n\nexport interface RasterizeRouteOptions {\n getRasterizer?: () => PptxRasterizer;\n getBatchRasterizer?: () => PptxBatchRasterizer;\n preMiddleware?: MiddlewareHandler[];\n sourcePolicy?: OutboundSourcePolicy;\n onError?: (error: unknown) => void;\n /**\n * Body-size limit for both rasterize routes. Defaults to the configured\n * server limit (see {@link resolveMaxBodyBytes}); always clamped to\n * {@link MAX_RASTERIZE_BODY_CEILING}.\n */\n maxBodyBytes?: number;\n}\n\n/**\n * Register `POST /rasterize` and `POST /rasterize/batch` on a Hono router\n * with body-size limits, content-type + schema validation (dpi clamped to\n * [MIN,MAX]_VISUAL_DPI, batch capped at MAX_RASTERIZE_BATCH_SLIDES), the\n * shared rasterizers, and structured 400/413/503/500 error mapping. Both\n * routes share `preMiddleware` instances (e.g. one rate limiter), so a batch\n * and its per-visual fallback draw from the same budget.\n *\n * @param options.preMiddleware - extra middleware (e.g. a rate limiter) run first.\n * @param options.maxBodyBytes - body-size limit; defaults to the configured\n * server limit rather than a hardcoded one (see {@link resolveMaxBodyBytes}).\n */\nexport function registerRasterizeRoute(\n router: Hono<any>,\n options: RasterizeRouteOptions = {}\n): void {\n const getRasterizer = options.getRasterizer ?? getSharedRasterizer;\n const getBatchRasterizer =\n options.getBatchRasterizer ?? getSharedBatchRasterizer;\n\n // The body now also carries base64 font faces. The schema alone would admit\n // MAX_RASTERIZE_FONTS × 4 MiB = 128 MiB; this limit is what actually bounds\n // it, and it rejects with 413 BEFORE parsing, which is the order we want.\n // MAX_RASTERIZE_FONT_BYTES (8 MiB decoded ≈ 10.7 MiB base64) is the\n // intended working ceiling for fonts within that budget.\n const maxBodyBytes = resolveMaxBodyBytes(options.maxBodyBytes);\n\n const shared: MiddlewareHandler[] = [\n ...(options.preMiddleware ?? []),\n bodyLimit({\n maxSize: maxBodyBytes,\n onError: () => {\n throw new HTTPException(413, { message: 'Request body too large' });\n },\n }),\n jsonOnly,\n ];\n\n const guard = async <T>(run: () => Promise<T>): Promise<T> => {\n try {\n return await run();\n } catch (error) {\n options.onError?.(error);\n throw toHttpException(error);\n }\n };\n\n router.post(\n '/rasterize',\n ...shared,\n tbValidator(RasterizeRequestSchema),\n async (c) => {\n const { presentation, dpi, baseDir, fonts } = getValidated<{\n presentation: unknown;\n dpi?: number;\n baseDir?: string;\n fonts?: RasterizeFontFace[];\n }>(c, 'json');\n const safeBaseDir = resolveSafeBaseDir(baseDir);\n\n const effectiveDpi = clampVisualDpi(dpi ?? DEFAULT_VISUAL_DPI);\n assertPixelBudget([{ presentation, dpi: effectiveDpi }]);\n assertFontBudget(fonts);\n\n const result = await guard(async () => {\n if (options.sourcePolicy) {\n // Scoped to `presentation` only. Font faces are inert base64 with\n // no URL-shaped keys, and walking multi-MB strings per request\n // would be pure cost.\n assertSafeOutboundSources(\n presentation,\n options.sourcePolicy,\n 'presentation'\n );\n }\n return getRasterizer()({\n presentation,\n dpi: effectiveDpi,\n baseDir: safeBaseDir,\n // Conditional so a fontless request reaches the engine with no\n // `fonts` key at all — an `undefined` would still be an absent\n // digest, but keeping the object identical to the pre-change shape\n // makes the cache-key equivalence obvious.\n ...(fonts?.length && { fonts }),\n });\n });\n return c.json(result);\n }\n );\n\n router.post(\n '/rasterize/batch',\n ...shared,\n tbValidator(RasterizeBatchRequestSchema),\n async (c) => {\n const { slides, baseDir, fonts } = getValidated<{\n slides: Array<{ presentation: unknown; dpi?: number }>;\n baseDir?: string;\n fonts?: RasterizeFontFace[];\n }>(c, 'json');\n const safeBaseDir = resolveSafeBaseDir(baseDir);\n const effectiveSlides = slides.map((slide) => ({\n presentation: slide.presentation,\n dpi: clampVisualDpi(slide.dpi ?? DEFAULT_VISUAL_DPI),\n }));\n assertPixelBudget(effectiveSlides);\n assertFontBudget(fonts);\n\n const result = await guard(async () => {\n if (options.sourcePolicy) {\n // Scoped to each slide's `presentation` only — see /rasterize.\n slides.forEach((slide, index) =>\n assertSafeOutboundSources(\n slide.presentation,\n options.sourcePolicy!,\n `slides[${index}].presentation`\n )\n );\n }\n return getBatchRasterizer()({\n slides: effectiveSlides,\n baseDir: safeBaseDir,\n ...(fonts?.length && { fonts }),\n });\n });\n\n // Per-slide errors from `build` are caused by the slide's own JSON and\n // are the caller's actionable feedback; `convert`/`rasterize` failures\n // carry raw tool output (host paths, temp dirs), which the single route\n // never exposes — log them, return a generic message (C2 parity with\n // /rasterize's 500 mapping).\n return c.json({\n results: result.results.map((slide) => {\n if (slide.ok) return slide;\n options.onError?.(new Error(slide.error));\n return slide.stage === 'build'\n ? { ok: false as const, error: slide.error, stage: slide.stage }\n : {\n ok: false as const,\n error: 'Slide rasterization failed',\n stage: slide.stage,\n };\n }),\n });\n }\n );\n}\n","import dotenv from 'dotenv';\n\ndotenv.config();\n\nexport type ApiAuthMode = 'auto' | 'required' | 'disabled';\nexport type OutboundSourceMode = 'development' | 'safe';\n\nfunction positiveInteger(value: string | undefined, fallback: number): number {\n const parsed = Number.parseInt(value ?? '', 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\nfunction parseAuthMode(\n value: string | undefined,\n nodeEnv: 'development' | 'production' | 'test'\n): ApiAuthMode {\n if (value === 'auto' || value === 'required' || value === 'disabled') {\n return value;\n }\n // Public production APIs must opt out explicitly. Local playgrounds remain\n // zero-config; supplying a key there preserves the historical auto-enable.\n return nodeEnv === 'production' ? 'required' : 'auto';\n}\n\nfunction parseOutboundSourceMode(\n value: string | undefined,\n nodeEnv: 'development' | 'production' | 'test'\n): OutboundSourceMode {\n if (value === 'development' || value === 'safe') return value;\n return nodeEnv === 'production' ? 'safe' : 'development';\n}\n\n/**\n * Only the two explicitly local environments keep permissive defaults. Every\n * other value — `staging`, `prod`, a typo — gets production-grade hardening, so\n * a mislabelled deployment cannot silently disable auth, rate limits, or the\n * outbound-source (SSRF) checks.\n */\nfunction normalizeNodeEnv(\n value: string | undefined\n): 'development' | 'production' | 'test' {\n return value === 'development' || value === 'test' ? value : 'production';\n}\n\nfunction parseEnv(env: NodeJS.ProcessEnv) {\n const nodeEnv = normalizeNodeEnv(env.NODE_ENV || 'development');\n return {\n NODE_ENV: nodeEnv,\n // No PORT here: the listener is opened by the dev server from the CLI\n // config (`-p` > `server.port` > `PORT` env > format default), so a second\n // copy of it in this module would be silently ignored.\n CORS_ORIGIN: env.CORS_ORIGIN || '*',\n API_KEY: env.API_KEY,\n API_KEY_HEADER: env.API_KEY_HEADER || 'x-api-key',\n API_AUTH_MODE: parseAuthMode(env.API_AUTH_MODE, nodeEnv),\n RATE_LIMIT_WINDOW_MS: positiveInteger(env.RATE_LIMIT_WINDOW_MS, 900_000),\n RATE_LIMIT_MAX: positiveInteger(\n env.RATE_LIMIT_MAX,\n nodeEnv === 'production' ? 100 : 1000\n ),\n TRUST_PROXY_HEADERS: env.TRUST_PROXY_HEADERS === 'true',\n MAX_FILE_SIZE: positiveInteger(env.MAX_FILE_SIZE, 10 * 1024 * 1024),\n MAX_REQUEST_BODY_SIZE: positiveInteger(\n env.MAX_REQUEST_BODY_SIZE,\n 32 * 1024 * 1024\n ),\n MAX_CONCURRENT_REQUESTS: positiveInteger(\n env.MAX_CONCURRENT_REQUESTS,\n nodeEnv === 'production' ? 8 : 64\n ),\n OUTBOUND_SOURCE_MODE: parseOutboundSourceMode(\n env.OUTBOUND_SOURCE_MODE,\n nodeEnv\n ),\n OUTBOUND_HOST_ALLOWLIST: (env.OUTBOUND_HOST_ALLOWLIST || '')\n .split(',')\n .map((host) => host.trim().toLowerCase())\n .filter(Boolean),\n LIBREOFFICE_PATH: env.LIBREOFFICE_PATH,\n LIBREOFFICE_TIMEOUT_MS: env.LIBREOFFICE_TIMEOUT_MS\n ? positiveInteger(env.LIBREOFFICE_TIMEOUT_MS, 30000)\n : 30000,\n LOG_LEVEL: (env.LOG_LEVEL || 'info') as 'error' | 'warn' | 'info' | 'debug',\n CACHE_ENABLED: env.CACHE_ENABLED !== 'false',\n CACHE_MAX_SIZE_MB: positiveInteger(env.CACHE_MAX_SIZE_MB, 100),\n CACHE_TTL_SECONDS: positiveInteger(env.CACHE_TTL_SECONDS, 3600),\n CACHE_MAX_ITEMS: positiveInteger(env.CACHE_MAX_ITEMS, 1000),\n };\n}\n\nconst parsedEnv = parseEnv(process.env);\n\nexport const config = {\n ...parsedEnv,\n\n isDevelopment: parsedEnv.NODE_ENV === 'development',\n isProduction: parsedEnv.NODE_ENV === 'production',\n isTest: parsedEnv.NODE_ENV === 'test',\n\n features: {\n apiKey: parsedEnv.API_AUTH_MODE !== 'disabled',\n cache: parsedEnv.CACHE_ENABLED,\n },\n\n cors: {\n origin:\n parsedEnv.CORS_ORIGIN === '*'\n ? parsedEnv.CORS_ORIGIN\n : parsedEnv.CORS_ORIGIN.split(',').map((o) => o.trim()),\n credentials: true,\n },\n\n rateLimit: {\n windowMs: parsedEnv.RATE_LIMIT_WINDOW_MS,\n max: parsedEnv.RATE_LIMIT_MAX,\n trustProxy: parsedEnv.TRUST_PROXY_HEADERS,\n },\n\n requestLimits: {\n maxBodySize: parsedEnv.MAX_REQUEST_BODY_SIZE,\n maxConcurrent: parsedEnv.MAX_CONCURRENT_REQUESTS,\n maxFileSize: parsedEnv.MAX_FILE_SIZE,\n },\n\n outboundSources: {\n mode: parsedEnv.OUTBOUND_SOURCE_MODE,\n allowedHosts: parsedEnv.OUTBOUND_HOST_ALLOWLIST,\n },\n\n cache: {\n enabled: parsedEnv.CACHE_ENABLED,\n maxSizeMB: parsedEnv.CACHE_MAX_SIZE_MB,\n ttlSeconds: parsedEnv.CACHE_TTL_SECONDS,\n maxItems: parsedEnv.CACHE_MAX_ITEMS,\n },\n} as const;\n\nexport type ServerConfig = typeof config;\n","import { TSchema } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport { Context, Env, ValidationTargets, MiddlewareHandler } from 'hono';\nimport { HTTPException } from 'hono/http-exception';\n\n/**\n * Auto-detect raw document JSON (has `name` + `children` but no `jsonDefinition`)\n * and wrap it so callers can POST the document tree directly.\n */\nfunction autoWrapDocumentBody(value: unknown): unknown {\n if (\n typeof value === 'object' &&\n value !== null &&\n 'name' in value &&\n 'children' in value &&\n !('jsonDefinition' in value)\n ) {\n return { jsonDefinition: value };\n }\n return value;\n}\n\nexport function tbValidator<\n T extends TSchema,\n E extends Env = Env,\n P extends string = string,\n>(schema: T) {\n return (async (c: Context<E, P>, next: () => Promise<void>) => {\n let value: unknown;\n\n try {\n value = await c.req.json();\n } catch {\n throw new HTTPException(400, {\n message: 'Invalid JSON in request body',\n });\n }\n\n value = autoWrapDocumentBody(value);\n\n const isValid = Value.Check(schema, value);\n\n if (isValid) {\n (c.req as { validatedData?: Record<string, unknown> }).validatedData = {\n json: value,\n };\n await next();\n } else {\n const errors = [...Value.Errors(schema, value)].map((error) => ({\n path: error.path,\n message: error.message,\n value: error.value,\n }));\n\n const errorMessages = errors.map((e) => `${e.path || '/'}: ${e.message}`);\n\n throw new HTTPException(400, {\n message:\n errorMessages.length === 1\n ? errorMessages[0]\n : `Validation failed:\\n${errorMessages.join('\\n')}`,\n cause: { errors },\n });\n }\n }) as MiddlewareHandler<E, P, Record<string, unknown>>;\n}\n\nexport function getValidated<T>(\n c: Context,\n target: keyof ValidationTargets\n): T {\n const validatedData = (c.req as { validatedData?: Record<string, unknown> })\n .validatedData;\n if (!validatedData || !validatedData[target]) {\n throw new Error(`No validated data found for target: ${target}`);\n }\n return validatedData[target] as T;\n}\n","import type { OutboundSourceMode } from '../config/index.js';\n\nexport interface OutboundSourcePolicy {\n mode: OutboundSourceMode;\n allowedHosts: readonly string[];\n}\n\nexport class UnsafeOutboundSourceError extends Error {\n constructor(\n readonly path: string,\n reason: string\n ) {\n super(`Unsafe outbound source at ${path}: ${reason}`);\n this.name = 'UnsafeOutboundSourceError';\n }\n}\n\ntype JsonRecord = Record<string, unknown>;\n\nconst DANGEROUS_KEYS = new Set(['__proto__', 'prototype', 'constructor']);\nconst COMPONENT_SOURCE_NAMES = new Set(['image']);\n\nfunction isRecord(value: unknown): value is JsonRecord {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction hostMatches(host: string, pattern: string): boolean {\n const normalized = pattern.toLowerCase().replace(/\\.$/, '');\n if (normalized.startsWith('*.')) {\n const suffix = normalized.slice(1);\n return host.endsWith(suffix) && host.length > suffix.length;\n }\n return host === normalized;\n}\n\nfunction isPrivateIpv4(host: string): boolean {\n const parts = host.split('.');\n if (parts.length !== 4) return false;\n const octets = parts.map(Number);\n if (\n octets.some(\n (part, index) =>\n !Number.isInteger(part) ||\n part < 0 ||\n part > 255 ||\n String(part) !== parts[index]\n )\n ) {\n return false;\n }\n const [a, b] = octets;\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 100 && b >= 64 && b <= 127) ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && (b === 0 || b === 168)) ||\n (a === 198 && (b === 18 || b === 19)) ||\n a >= 224\n );\n}\n\nfunction isPrivateHost(host: string): boolean {\n const normalized = host\n .toLowerCase()\n .replace(/^\\[|\\]$/g, '')\n .replace(/\\.$/, '');\n if (\n normalized === 'localhost' ||\n normalized.endsWith('.localhost') ||\n normalized === '::' ||\n normalized === '::1'\n ) {\n return true;\n }\n if (isPrivateIpv4(normalized)) return true;\n if (normalized.startsWith('::ffff:')) {\n const mapped = normalized.slice('::ffff:'.length);\n if (isPrivateIpv4(mapped)) return true;\n const groups = mapped.match(/^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);\n if (groups) {\n const value =\n Number.parseInt(groups[1], 16) * 0x1_0000 +\n Number.parseInt(groups[2], 16);\n return isPrivateIpv4(\n [\n value >>> 24,\n (value >>> 16) & 255,\n (value >>> 8) & 255,\n value & 255,\n ].join('.')\n );\n }\n }\n const firstIpv6Group = Number.parseInt(normalized.split(':')[0] || '0', 16);\n return (\n (firstIpv6Group >= 0xfc00 && firstIpv6Group <= 0xfdff) ||\n (firstIpv6Group >= 0xfe80 && firstIpv6Group <= 0xfebf)\n );\n}\n\nfunction assertAllowedUrl(\n source: string,\n path: string,\n allowedHosts: readonly string[]\n): void {\n if (source.startsWith('data:')) return;\n\n let url: URL;\n try {\n url = new URL(source);\n } catch {\n throw new UnsafeOutboundSourceError(\n path,\n 'local and relative file paths are disabled for HTTP requests'\n );\n }\n\n if (url.protocol !== 'https:') {\n throw new UnsafeOutboundSourceError(path, 'only HTTPS URLs are allowed');\n }\n if (url.username || url.password) {\n throw new UnsafeOutboundSourceError(\n path,\n 'URLs containing credentials are not allowed'\n );\n }\n\n const host = url.hostname.toLowerCase().replace(/\\.$/, '');\n if (isPrivateHost(host)) {\n throw new UnsafeOutboundSourceError(\n path,\n `private or local host \"${host}\" is not allowed`\n );\n }\n if (!allowedHosts.some((pattern) => hostMatches(host, pattern))) {\n throw new UnsafeOutboundSourceError(\n path,\n `host \"${host}\" is not in OUTBOUND_HOST_ALLOWLIST`\n );\n }\n}\n\nfunction extractCssReferences(input: string): string[] {\n const references: string[] = [];\n const urlPattern = /url\\(\\s*(['\"]?)([^)'\"\\s]+)\\1\\s*\\)/gi;\n const importPattern = /@import\\s+(?:url\\(\\s*)?(['\"])([^'\"]+)\\1/gi;\n for (const pattern of [urlPattern, importPattern]) {\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(input))) references.push(match[2]);\n }\n return references;\n}\n\nfunction extractAbsoluteUrls(input: string): string[] {\n return input.match(/(?:https?|file):\\/\\/[^\\s'\"<>)}\\]]+/gi) ?? [];\n}\n\nfunction assertSafeSvg(\n svg: string,\n path: string,\n allowedHosts: readonly string[]\n): void {\n if (\n /<\\s*script\\b/i.test(svg) ||\n /<\\s*(?:foreignObject|iframe|object|embed)\\b/i.test(svg) ||\n /\\bon[a-z]+\\s*=/i.test(svg)\n ) {\n throw new UnsafeOutboundSourceError(path, 'active SVG content is disabled');\n }\n if (/<!\\s*(?:DOCTYPE|ENTITY)\\b/i.test(svg)) {\n throw new UnsafeOutboundSourceError(\n path,\n 'SVG document types and entities are disabled'\n );\n }\n\n const refPattern = /\\b(?:href|src)\\s*=\\s*(['\"])(.*?)\\1/gi;\n let match: RegExpExecArray | null;\n while ((match = refPattern.exec(svg))) {\n const ref = match[2].trim();\n if (!ref || ref.startsWith('#') || ref.startsWith('data:')) continue;\n assertAllowedUrl(ref, path, allowedHosts);\n }\n for (const ref of extractCssReferences(svg)) {\n if (ref.startsWith('#') || ref.startsWith('data:')) continue;\n assertAllowedUrl(ref, path, allowedHosts);\n }\n}\n\nfunction assertSafeEncodedImage(\n source: string,\n path: string,\n allowedHosts: readonly string[]\n): void {\n let candidate = source.trim();\n let declaredSvg = false;\n\n if (candidate.startsWith('data:')) {\n const comma = candidate.indexOf(',');\n if (comma < 0) {\n throw new UnsafeOutboundSourceError(path, 'malformed image data URL');\n }\n const metadata = candidate.slice(5, comma);\n declaredSvg = metadata.split(';')[0].toLowerCase() === 'image/svg+xml';\n if (!declaredSvg) return;\n const payload = candidate.slice(comma + 1);\n try {\n candidate = /(?:^|;)base64(?:;|$)/i.test(metadata)\n ? Buffer.from(payload, 'base64').toString('utf8')\n : decodeURIComponent(payload);\n } catch {\n throw new UnsafeOutboundSourceError(path, 'malformed SVG data URL');\n }\n } else if (!/<\\s*svg\\b/i.test(candidate)) {\n // The documented `base64` shape is a data URL, but inspect raw base64 too\n // so a migration client cannot smuggle active SVG through the same field.\n candidate = Buffer.from(candidate, 'base64').toString('utf8');\n }\n\n const svgStart = candidate.search(/<\\s*svg\\b/i);\n if (svgStart < 0) {\n if (declaredSvg) {\n throw new UnsafeOutboundSourceError(path, 'malformed SVG image data');\n }\n return;\n }\n assertSafeSvg(candidate, path, allowedHosts);\n}\n\nfunction assertSafeResources(\n resources: JsonRecord,\n path: string,\n allowedHosts: readonly string[]\n): void {\n if (typeof resources.js === 'string' && resources.js.trim()) {\n throw new UnsafeOutboundSourceError(\n `${path}.js`,\n 'remote renderer JavaScript resources are disabled'\n );\n }\n if (typeof resources.css === 'string') {\n for (const ref of extractCssReferences(resources.css)) {\n if (ref.startsWith('data:')) continue;\n assertAllowedUrl(ref, `${path}.css`, allowedHosts);\n }\n }\n // Highcharts loads `resources.files` as JavaScript. Allowlisting a host for\n // fonts or stylesheets must not also grant it script execution inside the\n // renderer, so reject the field outright rather than URL-checking it.\n if (Array.isArray(resources.files) && resources.files.length > 0) {\n throw new UnsafeOutboundSourceError(\n `${path}.files`,\n 'remote renderer JavaScript resources are disabled'\n );\n }\n}\n\nfunction assertNoRemoteReferences(\n value: unknown,\n path: string,\n allowedHosts: readonly string[],\n seen: WeakSet<object>\n): void {\n if (typeof value === 'string') {\n if (\n /\\b(?:fetch|XMLHttpRequest|WebSocket|EventSource)\\s*\\(/i.test(value) ||\n /\\b(?:javascript|file):/i.test(value)\n ) {\n throw new UnsafeOutboundSourceError(\n path,\n 'network-capable JavaScript and file URLs are disabled'\n );\n }\n for (const url of [\n ...extractAbsoluteUrls(value),\n ...extractCssReferences(value),\n ]) {\n if (url.startsWith('data:')) continue;\n assertAllowedUrl(url, path, allowedHosts);\n }\n if (/^\\/\\//.test(value.trim())) {\n throw new UnsafeOutboundSourceError(\n path,\n 'protocol-relative URLs are disabled'\n );\n }\n return;\n }\n if (!value || typeof value !== 'object' || seen.has(value)) return;\n seen.add(value);\n if (Array.isArray(value)) {\n value.forEach((entry, index) =>\n assertNoRemoteReferences(entry, `${path}[${index}]`, allowedHosts, seen)\n );\n return;\n }\n for (const [key, child] of Object.entries(value as JsonRecord)) {\n assertNoRemoteReferences(child, `${path}.${key}`, allowedHosts, seen);\n }\n}\n\nfunction visit(\n value: unknown,\n path: string,\n containerKey: string | undefined,\n allowedHosts: readonly string[],\n seen: WeakSet<object>\n): void {\n if (!value || typeof value !== 'object' || seen.has(value)) return;\n seen.add(value);\n\n if (Array.isArray(value)) {\n value.forEach((entry, index) =>\n visit(entry, `${path}[${index}]`, containerKey, allowedHosts, seen)\n );\n return;\n }\n\n const record = value as JsonRecord;\n for (const key of Object.keys(record)) {\n if (DANGEROUS_KEYS.has(key)) {\n throw new UnsafeOutboundSourceError(\n `${path}.${key}`,\n 'prototype mutation keys are disabled'\n );\n }\n }\n\n const kind = typeof record.kind === 'string' ? record.kind : undefined;\n if (kind === 'file' && typeof record.path === 'string') {\n throw new UnsafeOutboundSourceError(\n `${path}.path`,\n 'local file sources are disabled for HTTP requests'\n );\n }\n if (\n (kind === 'url' || kind === 'variable') &&\n typeof record.url === 'string'\n ) {\n assertAllowedUrl(record.url, `${path}.url`, allowedHosts);\n }\n\n const componentName =\n typeof record.name === 'string' ? record.name.toLowerCase() : undefined;\n const props = isRecord(record.props) ? record.props : undefined;\n if (componentName && props) {\n if (\n COMPONENT_SOURCE_NAMES.has(componentName) &&\n typeof props.path === 'string'\n ) {\n assertAllowedUrl(props.path, `${path}.props.path`, allowedHosts);\n assertSafeEncodedImage(props.path, `${path}.props.path`, allowedHosts);\n }\n if (\n COMPONENT_SOURCE_NAMES.has(componentName) &&\n typeof props.base64 === 'string'\n ) {\n assertSafeEncodedImage(\n props.base64,\n `${path}.props.base64`,\n allowedHosts\n );\n }\n if (\n (componentName === 'highcharts' || componentName === 'visual') &&\n typeof props.serverUrl === 'string'\n ) {\n assertAllowedUrl(\n props.serverUrl,\n `${path}.props.serverUrl`,\n allowedHosts\n );\n }\n if (componentName === 'highcharts') {\n if (isRecord(props.resources)) {\n assertSafeResources(\n props.resources,\n `${path}.props.resources`,\n allowedHosts\n );\n }\n assertNoRemoteReferences(\n props.options,\n `${path}.props.options`,\n allowedHosts,\n new WeakSet()\n );\n }\n }\n\n // Slide/template/visual canvas backgrounds store an image source in a plain\n // `{ image: { path } }` object rather than an image component.\n if (containerKey === 'image' && typeof record.path === 'string') {\n assertAllowedUrl(record.path, `${path}.path`, allowedHosts);\n assertSafeEncodedImage(record.path, `${path}.path`, allowedHosts);\n }\n if (typeof record.svg === 'string') {\n assertSafeSvg(record.svg, `${path}.svg`, allowedHosts);\n }\n if (containerKey === 'resources') {\n assertSafeResources(record, path, allowedHosts);\n }\n\n for (const [key, child] of Object.entries(record)) {\n visit(child, `${path}.${key}`, key, allowedHosts, seen);\n }\n}\n\n/**\n * Reject request-controlled sources capable of making the server read local\n * files or reach arbitrary network hosts. Development mode intentionally\n * preserves local playground workflows.\n */\nexport function assertSafeOutboundSources(\n value: unknown,\n policy: OutboundSourcePolicy,\n rootPath = 'request'\n): void {\n if (policy.mode === 'development') return;\n\n let parsed = value;\n if (typeof value === 'string') {\n try {\n parsed = JSON.parse(value);\n } catch {\n // Structural validation owns malformed JSON reporting.\n return;\n }\n }\n visit(parsed, rootPath, undefined, policy.allowedHosts, new WeakSet());\n}\n\n/** Extra checks for payloads executed by the Chromium-based chart renderer. */\nexport function assertSafeRendererPayload(\n value: unknown,\n policy: OutboundSourcePolicy\n): void {\n assertSafeOutboundSources(value, policy, 'export');\n if (policy.mode === 'development' || !isRecord(value)) return;\n assertNoRemoteReferences(\n value.infile,\n 'export.infile',\n policy.allowedHosts,\n new WeakSet()\n );\n}\n","import { MiddlewareHandler } from 'hono';\nimport { HTTPException } from 'hono/http-exception';\n\nexport interface RateLimitOptions {\n limit: number;\n window: number; // milliseconds\n keyGenerator?: (c: any) => string;\n namespace?: string | ((c: any) => string);\n maxEntries?: number;\n trustProxy?: boolean;\n}\n\nfunction clientAddress(c: any, trustProxy: boolean): string {\n if (trustProxy) {\n const forwarded =\n c.req.header('X-Real-IP') ||\n c.req.header('X-Forwarded-For')?.split(',')[0]?.trim();\n if (forwarded) return forwarded;\n }\n const incoming = c.env?.incoming || c.env?.req;\n return incoming?.socket?.remoteAddress || 'anonymous';\n}\n\n/**\n * Bounded, process-local rate limiting. Distributed deployments should put a\n * shared limiter at the edge too; this remains the last line of defence for a\n * single worker and never grows without bound.\n */\nexport const rateLimiter = (options: RateLimitOptions): MiddlewareHandler => {\n const { limit, window, keyGenerator } = options;\n const maxEntries = options.maxEntries ?? 10_000;\n const rateLimitStore = new Map<\n string,\n { count: number; resetTime: number }\n >();\n let lastCleanup = 0;\n\n return async (c, next) => {\n const clientKey = keyGenerator\n ? keyGenerator(c)\n : clientAddress(c, options.trustProxy === true);\n const namespace =\n typeof options.namespace === 'function'\n ? options.namespace(c)\n : options.namespace || `${c.req.method}:${c.req.path}`;\n const key = `${namespace}:${String(clientKey).slice(0, 256)}`;\n const now = Date.now();\n\n // Amortize cleanup; scanning an attacker-filled map on every request would\n // turn the protection itself into a CPU denial of service.\n if (now - lastCleanup >= Math.min(window, 60_000)) {\n for (const [storedKey, value] of rateLimitStore.entries()) {\n if (value.resetTime <= now) rateLimitStore.delete(storedKey);\n }\n lastCleanup = now;\n }\n\n const record = rateLimitStore.get(key);\n\n if (!record) {\n if (rateLimitStore.size >= maxEntries) {\n const oldestKey = rateLimitStore.keys().next().value as\n | string\n | undefined;\n if (oldestKey) rateLimitStore.delete(oldestKey);\n }\n rateLimitStore.set(key, {\n count: 1,\n resetTime: now + window,\n });\n } else if (record.resetTime <= now) {\n // Window expired, reset\n record.count = 1;\n record.resetTime = now + window;\n } else if (record.count >= limit) {\n // Rate limit exceeded\n const retryAfter = Math.ceil((record.resetTime - now) / 1000);\n\n c.header('X-RateLimit-Limit', String(limit));\n c.header('X-RateLimit-Remaining', '0');\n c.header('X-RateLimit-Reset', String(record.resetTime));\n c.header('Retry-After', String(retryAfter));\n\n throw new HTTPException(429, {\n message: 'Too many requests, please try again later',\n });\n } else {\n // Increment count\n record.count++;\n }\n\n // Add rate limit headers\n const currentRecord = rateLimitStore.get(key)!;\n c.header('X-RateLimit-Limit', String(limit));\n c.header(\n 'X-RateLimit-Remaining',\n String(Math.max(0, limit - currentRecord.count))\n );\n c.header('X-RateLimit-Reset', String(currentRecord.resetTime));\n\n await next();\n };\n};\n","import type { MiddlewareHandler } from 'hono';\n\nexport interface ConcurrencyLimitOptions {\n limit: number;\n retryAfterSeconds?: number;\n}\n\n/**\n * Process-local admission control for expensive generation/rendering work.\n * It rejects excess work instead of queueing unbounded request bodies in RAM.\n */\nexport function concurrencyLimiter(\n options: ConcurrencyLimitOptions\n): MiddlewareHandler {\n const limit = Math.max(1, Math.floor(options.limit));\n const retryAfterSeconds = options.retryAfterSeconds ?? 1;\n let active = 0;\n\n return async (c, next) => {\n c.header('X-Concurrency-Limit', String(limit));\n if (active >= limit) {\n c.header('Retry-After', String(retryAfterSeconds));\n return c.json(\n {\n success: false,\n error: 'Server is at capacity',\n code: 'CONCURRENCY_LIMIT_EXCEEDED',\n },\n 503\n );\n }\n\n active += 1;\n c.header('X-Concurrency-Remaining', String(Math.max(0, limit - active)));\n try {\n await next();\n } finally {\n active -= 1;\n }\n };\n}\n","import { createHash, timingSafeEqual } from 'node:crypto';\nimport { MiddlewareHandler } from 'hono';\nimport { config } from '../../config';\nimport type { ApiAuthMode } from '../../config';\n\nexport interface ApiKeyAuthOptions {\n mode: ApiAuthMode;\n apiKey?: string;\n headerName?: string;\n}\n\nfunction keysEqual(received: string, expected: string): boolean {\n const receivedDigest = createHash('sha256').update(received).digest();\n const expectedDigest = createHash('sha256').update(expected).digest();\n return timingSafeEqual(receivedDigest, expectedDigest);\n}\n\nfunction readCredential(\n headers: Headers,\n headerName: string\n): string | undefined {\n const direct = headers.get(headerName)?.trim();\n if (direct) {\n if (\n headerName.toLowerCase() === 'authorization' &&\n direct.toLowerCase().startsWith('bearer ')\n ) {\n return direct.slice(7).trim() || undefined;\n }\n return direct;\n }\n\n const authorization = headers.get('authorization')?.trim();\n if (authorization?.toLowerCase().startsWith('bearer ')) {\n return authorization.slice(7).trim() || undefined;\n }\n return undefined;\n}\n\n/**\n * API Key authentication middleware for Hono\n */\nexport function createApiKeyAuthMiddleware(\n options: ApiKeyAuthOptions\n): MiddlewareHandler {\n const headerName = options.headerName || 'x-api-key';\n\n return async (c, next) => {\n if (c.req.method === 'OPTIONS' || options.mode === 'disabled') {\n return next();\n }\n\n if (!options.apiKey) {\n if (options.mode === 'auto') return next();\n return c.json(\n {\n success: false,\n error: 'API authentication is not configured',\n code: 'AUTH_CONFIGURATION_ERROR',\n },\n 503\n );\n }\n\n const apiKey = readCredential(c.req.raw.headers, headerName);\n\n if (!apiKey) {\n return c.json(\n {\n success: false,\n error: 'API key required',\n code: 'UNAUTHORIZED',\n },\n 401\n );\n }\n\n if (!keysEqual(apiKey, options.apiKey)) {\n return c.json(\n {\n success: false,\n error: 'Invalid API key',\n code: 'UNAUTHORIZED',\n },\n 401\n );\n }\n\n await next();\n };\n}\n\nexport const apiKeyAuthMiddleware = createApiKeyAuthMiddleware({\n mode: config.API_AUTH_MODE,\n apiKey: config.API_KEY,\n headerName: config.API_KEY_HEADER,\n});\n"],"mappings":"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","names":["bodyLimit","HTTPException","HTTPException","path","HTTPException","HTTPException","positiveInteger","isRecord","HTTPException","bodyLimit"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@json-to-office/jto",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "JSON to Office CLI - Unified document and presentation generation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -60,7 +60,6 @@
|
|
|
60
60
|
"idb-keyval": "^6.2.2",
|
|
61
61
|
"jsonc-parser": "^3.3.1",
|
|
62
62
|
"jsonrepair": "^3.13.3",
|
|
63
|
-
"koffi": "^2.16.1",
|
|
64
63
|
"lodash.debounce": "4.0.8",
|
|
65
64
|
"lru-cache": "10.4.3",
|
|
66
65
|
"lucide-react": "0.468.0",
|
|
@@ -85,12 +84,12 @@
|
|
|
85
84
|
"vite": "6.0.5",
|
|
86
85
|
"zod": "^4.0.0",
|
|
87
86
|
"zustand": "5.0.2",
|
|
88
|
-
"@json-to-office/core-
|
|
89
|
-
"@json-to-office/
|
|
90
|
-
"@json-to-office/
|
|
91
|
-
"@json-to-office/shared": "^0.
|
|
92
|
-
"@json-to-office/
|
|
93
|
-
"@json-to-office/shared-pptx": "^0.
|
|
87
|
+
"@json-to-office/core-docx": "^0.35.0",
|
|
88
|
+
"@json-to-office/core-pptx": "^0.35.0",
|
|
89
|
+
"@json-to-office/jto-cli": "^0.35.0",
|
|
90
|
+
"@json-to-office/shared": "^0.35.0",
|
|
91
|
+
"@json-to-office/shared-docx": "^0.35.0",
|
|
92
|
+
"@json-to-office/shared-pptx": "^0.35.0"
|
|
94
93
|
},
|
|
95
94
|
"devDependencies": {
|
|
96
95
|
"@types/lodash.debounce": "4.0.9",
|