@frank/ad-template-renderer (0.2.0)

Published 2026-10-04 23:32:01 +11:00 by steve

Installation

@frank:registry=
npm install @frank/ad-template-renderer@0.2.0
"@frank/ad-template-renderer": "0.2.0"

About this package

@frank/ad-template-renderer

The canonical deterministic renderer for final ad templates, ported from packages/ad-template-renderer in the Blockwise repo; its only divergence is the narrow font-cache identity fix described below. It renders an AdTemplate from @frank/ad-template into feed (1080x1350) and story (1080x1920) PNGs, fails closed when authored text cannot fit its box, and produces a render audit receipt (pixel gates, perceptual duplicate-image checks, text diagnostics) for the tool-run quality gate.

Public API (src/index.ts)

  • renderPlacement(input, placement), renderBoth(input) - render one or both placements. RenderInput carries template, imageValues, textValues, colourMap, optional fontValues (font file -> bytes).
  • TextPreflightError / TEXT_PREFLIGHT_ERROR_CODE - thrown when text cannot fit at the readability floor.
  • auditTemplateArtifact(input) - full audit; AuditArtifactInput carries artifactBytes, assetsDir, optional sourceBytes, runId, iteration.
  • Subpath @frank/ad-template-renderer/text-layout: segmentGraphemes, measureTrackedTextWidth, wrapText, prepareTextLayout.
  • Subpath @frank/ad-template-renderer/layer-geometry (browser-safe, no canvas): resolveGeometry, effectiveTextFontSize, imageMaskRadius and the Fabric helpers. The renderer uses the same functions, so an editor that imports them places layers exactly where the server renders them.
  • Bins: ad-template-render (CLI render), ad-template-audit (CLI audit).

Config

No environment reads. Fonts the template declares by bare file name resolve to fonts/adbuilder/ inside this package; callers override by passing fontValues bytes. Asset files resolve against the caller-provided assetsDir.

Usage

import { renderBoth } from "@frank/ad-template-renderer";
const [feed, story] = await renderBoth({ template, imageValues, textValues, colourMap });

Colour resolution

The colour map a render receives is partial by contract: exactly the document's persisted resolvedColourMap plus any explicit colourOverrides (overrides win), and nothing else. Template semanticColours are never merged in. A role the document does not persist (and no override supplies) stays omitted, and the canonical per-layer fallback applies: text layers render #000000, plate layers #FFFFFF. Apps that want template defaults must resolve them into document.resolvedColourMap before enqueuing the request; existing persisted empty, partial and full maps keep their original canonical render behavior.

Shared tool execution boundary

validateTemplateRenderInput(input) parses unknown input. executeTemplateRender(input, ports, signal?) implements the shared layered render tool using the existing renderPlacement engine. It validates all references before reading bytes, preserves separate placement crop overrides, computes both PNGs before writes and checks the abort signal around each port operation. Rejected port operations retain their original error unless the lease was aborted. Native Vue Fabric documents receive CREATIVE_NATIVE_EDITOR_UNSUPPORTED; native edits must use their existing export/save adapter, never a silent layered fallback.

The ports are readAsset(ref, signal?) -> Promise<Buffer> and writeArtifact({ scope, objectKey, mimeType, bytes }, signal?) -> Promise<ScopedAssetRef>. The result is { scope, requestId, feed, story, outputs: { feed: { objectKey, mimeType }, story: { objectKey, mimeType } } }. Stored refs are validated for full shape, both scope components and PNG MIME.

import { executeTemplateRender } from "@frank/ad-template-renderer";

const result = await executeTemplateRender(payload, {
  readAsset: readAuthorizedImmutableAsset,
  writeArtifact: storeProjectArtifact,
}, context.signal); // Existing @frank/jobs handler lease signal.

Adapters must authorize project access independently, verify the job workspace fence, and preserve immutable asset versions. Store create-only artifacts; on retry, an existing object is success only after checking its content hash. Publish result pointers only after both outputs persist. Computing both first prevents deterministic render errors from writing half a pair; storage writes are not universally atomic, so a failed write can leave an unpublished orphan.

Imported font aliases include a SHA-256 of their bytes. Different versions under one filename, concurrent projects, and bundled fonts therefore retain their own glyphs in the process-wide font registry. Bundled font aliases remain unchanged. One registration is retained per distinct imported content identity; worker process lifetime still bounds registry memory.

App adapter proposals and survivor

  • Blockwise src/lib/adbuilder/save-ad.ts: replace layered render input/asset assembly with an authorized scoped request and this job handler. Preserve its revision-save and content-verified upload transaction behavior. Native saves in src/lib/adbuilder/vue-native-save.ts and the pinned Vue Fabric editor stay on their existing validated export path.
  • Frank apps/frank/lib/runs/tools.ts: retain Hermes template authoring. A validated generated pack can enqueue createTemplateRenderJob(request); use this same worker boundary for project previews/results.

The duplicate candidate is app-specific layered render glue; the survivor is this boundary plus the canonical renderer and shared geometry helpers. Fixture tests prove direct-engine PNG parity with real image/text/font inputs, different feed/story crops, and equal pixels through two differently scoped adapter fakes. No app glue or engine was deleted; remove it only after real consumer integration retains these checks and product save semantics. UI, customer permissions, pricing and publishing remain app-owned.

Dependencies

Dependencies

ID Version
@frank/ad-template 0.2.0
@napi-rs/canvas ^1.0.3

Development dependencies

ID Version
@types/node ^22.0.0
fabric ^7.4.0
Details
npm
2026-10-04 23:32:01 +11:00
78
latest
2.2 MiB
Assets (1)
Versions (3) View all
0.2.0 2026-10-04
0.1.1 2026-09-26
0.1.0 2026-09-26