Runtime
한국어.
This document defines the engine API, template names and loading, scope, loops, includes, template definitions, block tags, output, limits, error behavior, browser rendering and artifact/page caching. Value rules are defined in data-model.md, expression evaluation in expressions.md, functions in functions.md, the AST in ast.md and error codes in errors.md.
API
- RT-1 Every implementation exposes the operations required by its declared support levels. Names follow the conventions of each language; implementations declaring the same level expose the same logical operations.
parse(source, name, { delimiters }) -> Template
decodeTemplate(json) -> Template
Engine(options: { loader, functions, limits, delimiters })
program.register(name, fn)
engine.render(target: name | Template, assign, { define, env }) -> string
engine.prepare(target: name | Template, assign, { define, env }) -> PreparedRender
prepared.render() -> string
Loader.load(name) -> { source | ast, version }
Compiler.compile(sourceGraph, mode: ast | gen) -> compiled artifact
ArtifactStore.loadOrRefresh(sourceGraph, refresh: dev | true | false) -> compiled artifact
Engine.prepare(compiled artifact, request) -> PreparedRender
PageCache.get(key) -> string | miss
PageCache.put(key, html, ttl: positive seconds | 0 | null)
PageCache.getOrSet(key, ttl, render)Engine receives one complete Program and delegates prepare and render without inspecting its representation. AstProgram and GeneratedProgram implement that contract independently. The compiler and artifact store select the mode and refresh policy before constructing the engine.
- RT-2
parseproduces the AST defined in the AST document without loading other templates. Includes and block tags are resolved during rendering. - RT-3
renderaccepts a template name or a parsed template. With a name, the engine loads the template through its loader. It returns the complete output as one string. - RT-4
assignis a map converted by the host binding rules, or a bound map (VAL-22) that is not bound again. A nullassign(undefinedtoo in TypeScript,nilin Go) and, in PHP, an empty array are an empty map.defineis the template definition map (RT-24).envis a map withtimezoneandnowas defined in the functions document. Each ofdefineandenvmay be omitted; an omitteddefineis an empty map. - RT-5
functionsin the engine options andregisteradd host functions as defined in the functions document. - RT-6
limitsin the engine options overrides the limit values of RT-33. An omitted limit keeps its default: the options of TypeScript, PHP, the PHP extension and Python merge the keys that they hold over the defaults, and Go and Rust start theirLimitsvalue from the defaults,DefaultLimitsandLimits::default(), so no limit is unset. - RT-61 Both
AstProgramandGeneratedProgrambindassign, resolvedefineandenv, and select the target inprepare. A bound map (VAL-22) given asassignor as thedataof a template definition is not bound again, in every implementation including the PHP extension. Rendering it produces the same bytes as rendering, at the timebindran, the host map whose entries are the entries of the bound map; for a result ofmerge, the host map with the merged entries. Only the members of a native object are read when the template reads them (VAL-19). A typed generated program still converts a boundassignto its root record and bound definitiondatato its definition data record (RT-68). The returned program-specific state exposes the samePreparedRender.renderoperation. - RT-62
PreparedRender.rendercreates only per-render scope, output and execution state. Repeated calls with unchanged input produce identical UTF-8 bytes.renderis equivalent toprepare(...).render()and remains the single-call convenience operation. - RT-70 Renders of one program may run at the same time where the host language runs code concurrently, after the host registered its functions and classes: renders share only the templates that the program loaded and parsed, and the program guards that cache. A Go program may be rendered from concurrent goroutines, and a Python program from concurrent threads, which it serializes only in its template cache. A Rust program is neither
SendnorSync, because its values and its template cache useRcandRefCell, so the compiler keeps it, and its bound maps (VAL-22), in the thread that created it; a Rust host that renders in several threads creates one program in each thread. TypeScript and PHP run one render at a time in a process or worker, and the PHP extension keeps the objects of a request in that request. A host registers functions and classes before it renders; a registration while renders of the same program run is a data race of the host. A loader of a program that concurrent renders share is called by those renders at the same time. - RT-63 Compilation mode is
astorgen.astproduces an AST artifact interpreted by the AST renderer.genproduces host-language renderer code called directly. The generated renderer receives the same normalized request (target,assignanddefineafter host binding, and resolvedenv) as the AST renderer. The mode does not select the artifact refresh policy. - RT-64 Artifact refresh is
dev,trueorfalse. Build coordination regenerates on every compiler invocation indev, after a digest change intrue, and never reads source underfalse. Missing, stale or corrupt deployed artifacts underfalseare errors. - RT-65 A page cache stores final HTML separately from compiled artifacts. A positive TTL expires after that many seconds;
0andnullmean forever. A cache hit bypasses business logic and template rendering. Its key must include every value that can change the output. - RT-66
getOrSetreturns the cached HTML on a hit without callingrender; on a miss it callsrenderonce, stores the returned HTML with the TTL, and returns it. - RT-67 The prepared render contract is declared in the single product manifest,
packages/compiler/interface.json. Interface checks fail when any required language mapping, support level or operation is missing. - RT-69
Engineowns exactly oneProgram. Generated execution never creates or retains an AST placeholder, parser or AST renderer, and AST execution never retains generated code. The five runtimes must fail the interface gate if mode selection returns to the engine.
The program diagrams are generated from the single product manifest:
flowchart LR
Source["SourceGraph<br/>entry + templates"] --> Parse["Compiler.parseSourceGraph"]
Parse --> AST["canonical AST graph"]
Types["TypeManifest<br/>entry + root + fields + records + functions + defines + templates"] --> Lower["Compiler.lowerSourceGraph"]
AST --> Lower
Lower --> Typed["TypedProgram<br/>dynamicRoot + entry + fields + records + functions + definitions + templates"]
Typed --> AstArtifact["AST artifact"]
Typed --> Backend["LanguageBackend"]
Backend --> GeneratedArtifact["generated host artifact"]
AstArtifact --> AstProgram["AstProgram"]
GeneratedArtifact --> GeneratedProgram["GeneratedProgram"]
AstProgram --> Program["Program.prepare + Program.render"]
GeneratedProgram --> Program
RuntimeBindings["RuntimeBindings"] --> AstProgram
RuntimeBindings --> GeneratedProgram
RuntimeEnvironment["RuntimeEnvironment"] --> AstProgram
RuntimeEnvironment --> GeneratedProgramclassDiagram
class Compiler {
+sourceGraph
+typeManifest
+typedProgram
+loadSourceGraph(source manifest) SourceGraph
+parseSourceGraph(SourceGraph) canonical AST graph
+lowerSourceGraph(canonical AST graph, TypeManifest) TypedProgram
+emitArtifact(TypedProgram, CompileMode, LanguageBackend) ArtifactManifest and files
}
class LanguageBackend {
+target
+emitDeclarations(TypedProgram) host declarations
+emitRuntime(TypedProgram) host runtime bindings
+emitTemplates(TypedProgram) host template functions
+emitEntry(TypedProgram) host program entry
}
class ArtifactStore {
+artifactManifest
+files
+load(ArtifactManifest) Program
+regenerate(CompileMode, LanguageBackend) ArtifactManifest
+loadOrRefresh(CompileMode, ArtifactRefresh) Program
}
class Engine {
+program
+prepare(RenderRequest) PreparedRender
+render(RenderRequest) UTF-8 string
}
class PageCache {
+html
+ttl
+get(cacheKey) UTF-8 string or miss
+put(cacheKey, html, ttl) void
+getOrSet(cacheKey, ttl, render) UTF-8 string
}
class RuntimeEnvironment {
+limits
+hostFunctions
+register(name, function) void or error
+limits() Limits
+hostFunction(name) HostFunction or missing
}
class SourceGraph {
+entry
+templates
}
class TypeManifest {
+entry
+root
+fields
+records
+functions
+defines
+templates
}
class TypedProgram {
+dynamicRoot
+entry
+fields
+records
+functions
+definitions
+templates
}
class ArtifactManifest {
+schema
+mode
+target
+entry
+delimiters
+sourceDigest
+typeDigest
+contractDigest
+compilerDigest
+files
}
class RenderRequest {
+target
+assign
+define
+env
}
class Program {
+prepare(target, assign, options) PreparedRender or error
+render(target, assign, options) UTF-8 string or error
}
class AstProgram
class GeneratedProgram
class RuntimeBindings {
+truthy(value) bool
+unary(operator, operand, frame, span) value or error
+binary(operator, left, right, frame, span) value or error
+stringify(value, frame, span) string or error
+escape(value, frame, span) string or error
+number(value, frame, span) number or error
+finite(number, frame, span) number or error
+equal(left, right, strict) bool
+compare(left, right, frame, span) ordering or error
+member(container, key, frame, span) value or error
+memberCall(container, method, args, frame, span) value or error
+classCall(className, method, args, frame, span) value or error
+index(container, key, frame, span) value or error
+depth(value, frame, span) value or error
+entries(value, frame, span) entries or error
+listSpread(value, frame, span) list or error
+mapSpread(value, frame, span) map or error
+call(name, args, frame, span) value or error
+limit(kind, count, frame, span) void or error
+error(frame, span, code, message) error
}
class RuntimeServices {
+limits() Limits
+hostFunction(name) HostFunction or missing
+classFunction(className, method) HostFunction or missing
}
Program <|-- AstProgram
Program <|-- GeneratedProgram
Compiler --> TypedProgram
Compiler --> LanguageBackend
LanguageBackend --> ArtifactManifest
ArtifactStore --> ArtifactManifest
ArtifactStore --> Program
Engine --> Program
RuntimeServices <|.. RuntimeEnvironment
AstProgram --> RuntimeEnvironment
GeneratedProgram --> RuntimeEnvironment
AstProgram --> RuntimeBindings
GeneratedProgram --> RuntimeBindingsThe compiler manifest also fixes the public Program, Engine, AstProgram and generated-program declaration structure. The interface gate reads TypeScript through oxc-parser, Go through go/parser, Rust through syn and PHP through Reflection. A missing or additional operation, changed argument count, changed owner or absent concrete AST program fails the build.
- RT-42
delimitersin the engine options and inparseselects the tag delimiters as defined in the lexical document. The default is{}. A delimiter directive in a template file overrides the option for that file.
Cross-language render contract
The executable showcase adapter boundary is part of the shared compiler interface manifest. It declares the types, field order, nullability, required fields, ownership, constructor, operations, errors, preconditions, state transitions and language name mappings. Native types express that contract in each language; they do not redefine it.
flowchart LR
Files["scenario directory"] --> Adapter["language adapter"]
Adapter --> Scenario["Scenario"]
Scenario --> Request["RenderRequest"]
Request --> Engine["Engine.render"]
Engine --> Output["UTF-8 output"]classDiagram
class Adapter {
+path root
+Engine engine
+Adapter(root) Adapter
+loadScenario() Scenario
+buildRequest(Scenario) RenderRequest
+render(RenderRequest) string
+renderTwice(RenderRequest) RepeatResult
}
class Scenario {
+string target
+JsonObject assign
+DefineRegistry define
+Environment? env
}
class RenderRequest {
+string target
+JsonObject assign
+DefineRegistry define
+Environment? env
}
class Environment {
+string? timezone
+number? now
}
class RepeatResult {
+string first
+string second
}
class DefineEntry {
+string template
+JsonObject data
+string html
}
Adapter *-- Scenario : loads
Scenario *-- RenderRequest : buildRequest transfers fields
RenderRequest *-- DefineEntry : owns define entries
RenderRequest o-- Environment : optional
DefineEntry --> DefineEntry : template data is ordered objectstateDiagram-v2
[*] --> ready : constructor
ready --> scenario-loaded : loadScenario
scenario-loaded --> request-built : buildRequest
request-built --> rendered : render
request-built --> rendered : renderTwice
failed --> request-built : recover after render error
failed --> request-built : recover after renderTwice errorsequenceDiagram
participant Caller
participant Adapter
participant Engine
Caller->>Adapter: loadScenario()
Adapter-->>Caller: Scenario
Caller->>Adapter: buildRequest(Scenario)
Adapter-->>Caller: RenderRequest
Caller->>Adapter: renderTwice(RenderRequest)
Adapter->>Engine: render(target, assign, define, env)
Engine-->>Adapter: first UTF-8 bytes
Adapter->>Engine: render(target, assign, define, env)
Engine-->>Adapter: second UTF-8 bytes
Adapter-->>Caller: RepeatResultThe logical request has this JSON shape:
{
"target": "layout",
"assign": { "title": "Mock title" },
"define": {
"layout": "layout.tpl",
"contents": "content.tpl"
},
"env": { "timezone": "Z", "now": 1789084800 }
}- RT-43 Every implementation maps the same
RenderRequestfields to its native API. A native map, object or JSON value is an adapter representation; it does not change the field names, nesting or value types. - RT-44 A JSON value is
null, boolean, number, string, array or object with string keys.assignis a root object.define.datais also an object when present. - RT-45 A scenario stores template sources as root-relative UTF-8
*.tplnames,assignindata.json, definitions indefine.json, and the optional environment inenv.json. The scenario target is passed separately astarget. - RT-46 The cross-language fixture form represents a template definition directly as a string path. An object with
template: stringand optionaldata: object, or an object withhtml: string, is reserved for definitions that need those extra values. - RT-47 An adapter may decode the shared JSON and construct the native API values, but it must not add controller data, rename fields, apply filters or otherwise reshape the request.
- RT-48 A successful parity check compares the complete UTF-8 output bytes and then renders the same request again to compare the repeated bytes.
- RT-49 The manifest is the design source for
Adapter,Scenario,RenderRequest,DefineEntry,EnvironmentandRepeatResult. Its record fields and union variants keep their declared order; ownership andfromlinks describe which values are transferred fromScenariointoRenderRequest. - RT-50 The contract generator produces the TypeScript, JavaScript, Go and Rust declarations, the PHP declarations and the Mermaid sources. A generated file that differs from the manifest fails the contract gate.
- RT-51 Language syntax may follow local conventions (
NewAdapter,Adapter::newand__constructare constructor names), while the concrete type, constructor parameter, method order, argument types, return types and request fields remain the same through the manifest mappings. - RT-52 The contract checker checks source declarations, TypeScript compilation, Go interface assignment and formatting, Rust trait compilation, PHP syntax and
ReflectionClass, generated runtime assertions, and all five adapters on every showcase scenario. - RT-53 The state proof performs
constructor -> loadScenario -> buildRequest -> renderTwice, observes an invalid-target error, renders the original request again, and compares first, second and recovered UTF-8 hashes. The request remains unchanged across the failure and recovery path. - RT-60 Native object instances may be placed directly in
assign. The runtime retains the original instance, exposes public fields through member lookup and invokes public instance methods throughobject.method(args...). Logical class functions are registered underClass::methodand invoked throughClass::method(args...).
Support levels and compiled artifacts
- RT-54 The interface manifest declares one compiler with two peer modes.
astreturns anAstArtifact;genreturns host-language source and an artifact manifest. Both produce aProgramwith the same prepared render contract. - RT-55 An implementation declares its supported levels in the manifest. Implementations declaring the same level expose the same logical types and operations, with language-specific spelling recorded only in the mapping. An unsupported operation is absent from that level; it is not an empty method or a runtime fallback.
- RT-56 TypeScript, Go, Rust, PHP and Python provide
core-runtime,artifact-runtimeand bothProgramvariants. The build compiler providessource-compilerand the five generated target backends. JavaScript ESM is emitted by the TypeScript backend and is not counted as another semantic implementation. - RT-57 An artifact manifest contains its contract format, mode, target language, entry, source digest, type digest, contract digest and output paths. The artifact is generated before service startup and is loaded or statically linked once into the process.
- RT-58 Artifact refresh is a build policy.
devregenerates on every compiler invocation,trueregenerates when a source, type or contract digest changes, andfalsereads no source and uses only the deployed artifact. A development watcher rebuilds and restarts statically linked Go and Rust processes after regeneration. - RT-59 Parsing, source discovery, artifact generation and host compilation never occur in the render request path. Rendering the same program with the same assign, define and environment produces identical output bytes and leaves the request unchanged.
- RT-68 A typed generated artifact declares its assign, presence-preserving definition data, definitions and template inputs. A generated block calls its target template function directly after applying root assign, supplied definition data fields and block scope in that order. A pre-rendered string slot is not a generated template target.
The interface declares two execution modes:
- AST mode loads the canonical AST artifact once, binds
assignanddefinefor each request, and interprets the AST. This is the complete cross-language mode. - Generated mode lowers each canonical AST node into a TypeScript, Go, Rust, PHP or Python renderer before startup, loads or links that renderer once, and calls it for each request. All five core runtimes pass the generated conformance suite of every canonical case. Generated execution does not parse or interpret template AST during a request.
Native object calls are implemented in the AST and generated runtimes. The complete mode matrix and the package install check exercise the same assigned object field, instance method and class function behavior in TypeScript, Go, Rust, PHP and Python.
flowchart LR
Source[".tpl source graph"] --> Validate["validate AST + types + scope"]
Validate --> IR["shared compiler IR"]
IR --> Compiler["Compiler.emit(compile.mode)"]
Compiler --> ASTArtifact["AstArtifact"]
Compiler --> GenArtifact["GeneratedArtifact<br/>typed host source"]
ASTArtifact --> Store["ArtifactStore.loadOrRefresh(refresh)"]
GenArtifact --> Store
Store --> Prepared["Engine.prepare(request)"]
Prepared --> Normalized["normalized request<br/>assign + define + env"]
Normalized --> ASTRender["PreparedRender.render<br/>interpret AST"]
Normalized --> GenRender["PreparedRender.render<br/>call generated code"]
ASTRender --> Bytes["same UTF-8 bytes"]
GenRender --> BytesThe generation and verification commands are:
node scripts/generate-showcase-contract.mts --check
node scripts/check-showcase-contract.mtsThe support-level graph is generated from the same manifest as the language declarations:
flowchart TB
SourceCompiler["source-compiler: parse, encodeArtifact"] --> ArtifactRuntime["artifact-runtime: loadArtifact, render"]
ArtifactRuntime --> CoreRuntime["core-runtime: render"]
GeneratedCompiler["generated-compiler: typed source + load"] --> CoreRuntimeNames and loading
- RT-7 A template name is a path relative to the loader root, with
/as separator, without a leading/. The name of a template rendered byrender(name, ...)isname. When a string target matches a template entry indefine, the entry path is rendered; this lets the host select a layout with a definition such aslayout: "layouts/page.tpl". - RT-8 A path written in an include or block tag is resolved against the directory of the template that contains the tag. A path with a leading
/is resolved against the root..and..segments are normalized. A path that leaves the root after normalization fails withE_LOAD_OUTSIDE_ROOT. - RT-9 A loader returns the source text or a parsed AST and a version for a name, reports that the name does not exist, or reports a failure through the error mechanism of its language: a thrown exception in TypeScript and PHP, a returned error in Go and Rust. A name that does not exist fails with
E_LOAD_NOT_FOUND. A failure fails withE_LOAD_FAILED, and the message of the error contains the message of the loader. The engine catches every exception that a TypeScript or PHP loader throws, as it does for a host function (FUN-46). Reason: a loader is host code that the engine calls, so its failure is a template error with a code and a position, like the failure of a host function, and not an exception that leavesrenderin some implementations only. - RT-10 A filesystem loader reads
root/nameand uses the modification time and size of the file as the version. A name does not exist when no regular file can be found atroot/name: the file system reports no metadata for the path, or the path names a directory or another file that is not a regular file. A regular file whose content cannot be read, for example because its permissions deny reading, is a failure (RT-9). The loader examines each component ofnamebelow the root without following a symbolic link, and a component that is a symbolic link, at the file or at any directory, is a failure (RT-9); the link is never followed. The loader gives one file one name: the identity of a file is its device and inode number (on Windows its volume serial number and file index), and a name whose file the loader already read under another name is a failure (RT-9), whichever spellings the two names use, including two spellings that a case-folding file system or a hard link gives one file. Reason: a followed link reads a file outside the declared tree, and two names of one file give it two cache entries and let an include of one file under a second name pass the cycle check of RT-22. A map loader holds names and sources or parsed ASTs in memory and uses a content version. A compiled artifact loader holds parsed ASTs and uses the artifact hash as the version.
Scope
- RT-11 Each rendered template file has one local scope. Blocks inside a file (
{? },{@ }) do not create scopes. - RT-12 A variable is looked up in the local scope first, then in the context data. The context data of the root template is
assign. The context data of a block is defined in RT-26. A name found in neither isnull. - RT-13 An assignment
{:x = e}writesxto the local scope. A local variable shadows a context data entry with the same name. Assignment never modifies context data. - RT-14 Loop metas (
x.index_and the others) are resolved from the loops that are active for the namex. A loop meta for a name that is not an active loop variable fails withE_RUNTIME_UNKNOWN_LOOP.
Loop
- RT-15
{@ v = e}evaluateseonce. A list iterates its elements in order with keys0,1,2, and so on. A map iterates its values in insertion order with its keys.nullproduces zero iterations. A bool, number or string fails withE_RUNTIME_TYPE. - RT-16 Each iteration binds
vin the local scope to the element and sets the loop meta ofv:index_(0-based position),key_(list index as number or map key as string),value_(the element),first_(trueon the first iteration),last_(trueon the last iteration),size_(the element count). - RT-17 After the loop ends,
vand its loop meta are restored to the values they had before the loop. A name that was unbound before the loop is unbound after it. - RT-18 A nested loop may reuse the variable name of an outer loop. The inner binding and meta apply until the inner loop ends.
- RT-19 The
{:}branch of a loop runs when the loop produced zero iterations. - RT-20 Each iteration increments the iteration counter of the render. Exceeding the iteration limit fails with
E_RUNTIME_LIMIT.
Include
- RT-21
{+ path}loads the template at the resolved path and renders its body in the local scope of the including template and in the same context data. Assignments in the included template are visible to the including template after the tag. - RT-22 A template that is already being rendered in the current chain of includes and blocks fails with
E_LOAD_CYCLE. - RT-23 Each include and each block increases the nesting depth by one. A depth that exceeds the depth limit fails with
E_RUNTIME_DEPTH.
Block
- RT-24 The
definemap maps an identifier to a template definition. A definition may be a string path, or an object withtemplate: pathand optionaldata, a map or a bound map (VAL-22); an object withhtml: stringsupplies finished HTML. Template paths in definitions given torenderare relative to the loader root. The cross-language fixture form is defined in RT-46. - RT-25
{# id}renders the definition registered underid. An identifier that is not registered fails withE_RUNTIME_BLOCK_UNDEFINED. - RT-26 A template definition is rendered in a new local scope with context data built from, in order, the root
assign, the definitiondata, and the scope arguments of the tag. A later source overrides an earlier one for the same name. Local variables and loop metas of the calling template are not visible. - RT-27 An HTML entry writes its string to the output without escaping. Scope arguments given with an HTML entry are ignored.
- RT-28
{# id path ...}registersidas a template entry with the resolved path and then renders it. Whenidis already registered with the same path, the registration is a no-op. Whenidis registered with a different path or as an HTML entry, the tag fails withE_RUNTIME_BLOCK_REDEFINED. - RT-29
{# path ...}without an identifier renders the template at the resolved path as a template entry without registering it. - RT-30 A scope argument
name:exprevaluatesexprin the calling scope and bindsnamein the context data of the block. A scope argumentnameis equivalent toname:name. - RT-31
{?# id}renders its body whenidis registered, and its{:}branch otherwise. Registrations made by earlier{# id path}tags in the same render are visible.
Output
- RT-32 The echo tag writes a safe string as is. Any other value is stringified by the data model rule and then HTML-escaped by replacing
&<>"'with&<>"'. A list or a map fails withE_RUNTIME_STRINGIFY.
Limits
- RT-33 Default limits:
| Limit | Key | Default | Error |
|---|---|---|---|
| Loop iterations per render | iterations | 1,000,000 | E_RUNTIME_LIMIT |
| Include and block nesting depth | depth | 32 | E_RUNTIME_DEPTH |
| Output size in bytes | outputBytes | 16 MiB | E_RUNTIME_LIMIT |
| Expression nesting depth | expressionDepth | 64 | E_RUNTIME_LIMIT |
TypeScript, PHP, the PHP extension and Python name each limit by its key in the limits option. Go and Rust name it by a field of their Limits type in the convention of the language: Iterations, Depth, OutputBytes and ExpressionDepth in Go, iterations, depth, output_bytes and expression_depth in Rust.
- RT-34 The expression nesting depth is checked by the parser and by the evaluator. The parser counts one level for each expression that it enters, one for each postfix expression (an operand with its accessors and calls) and one for each prefix operator
!or-: an operand alone is at level 2, a parenthesized expression or a call argument adds two levels and a prefix operator one, so 31 nested parentheses around a literal parse and 32 fail, and 62 prefix operators before a variable parse and 63 fail (tests/fixtures/expr/cases.json). - RT-35 The output size is checked as the output is written. Exceeding it fails the render.
Errors
- RT-36 An error during rendering aborts the render.
renderreturns no partial output; it reports the error object defined in the errors document. - RT-37 Every error code has one behavior in both execution modes. The selected mode changes the prebuilt renderer representation, not the error code, request shape or output contract.
Browser
- RT-38 The TypeScript package renders in a browser with a map loader that holds the template sources or their parsed ASTs.
- RT-39 A server that renders a page for later rendering in the browser embeds the assign data as
<script type="application/json">whose content is produced by{= json(assign) | raw}. The browser parses that JSON with the parser of the package, as defined in the data model document, and renders the same template name with the same assign data, define map and env to produce the same output.
Caching
- RT-40 An engine parses a template once per
name@versionand reuses the parsed template while the loader reports the same version. - RT-41 A parsed template may be stored and reloaded as its AST JSON. Rendering an AST produces the same output as rendering its source.
- RT-60 The build compiler accepts
--refresh dev|true|false.devalways writes,truereuses artifacts whose source, type and contract digests match, andfalsenever reads or parses source and never writes an artifact.