CEM-ML CLI Transform Config Schema (https://cem.dev/ns/cli/transform-config/1)
Status: reserved runtime surface with active parser/lowering validation.
This is the source-of-truth schema artifact for the CEM-native CLI transform
graph config. It is separate from the CEM core document schema and separate
from CEM-native template schemas. The graph config describes import,
typed conversion, transformation, and export wiring; it does not define template execution
semantics. The CEM-native template declaration schema is
https://cem.dev/ns/template/cem-native/1 and lives at
packages/cem_ml/schema-packages/cem-native-template/v1/.
Schema URI: https://cem.dev/ns/cli/transform-config/1
Namespace URI: https://cem.dev/ns/cli/transform-config/1
Element Vocabulary
| Element | Required attributes | Optional attributes | Child elements |
|---|---|---|---|
run | none | none | import |
import | src | id, content-type, contentType, schema, opaque, root | join, convert, transform, rewrite-importmap, export |
join | mode | id, input, by, with:* | convert, transform, rewrite-importmap, export |
convert | none | id, input, content-type, contentType, schema, converter | join, convert, transform, rewrite-importmap, export |
transform | src | id, input, with:*, content-type, contentType, schema, entrypoint, template-content-type, templateContentType, template-schema, templateSchema | param, join, convert, transform, rewrite-importmap, export |
rewrite-importmap | target-map | id, input, source-map, sourceMap, authored-map, targetMap, mode, missing | export |
param | name, value | none | none |
export | out | id, content-type, contentType, schema, style-policy, stylePolicy | none |
Graph Semantics
- A document must contain exactly one top-level
runelement. runcontains one or more import branches.importcreates a source node from@src.import @root="../content"declares a folder base for source-derived{path}and{dir}bindings.@srcstill selects the files;@rootdoes not perform implicit discovery. Both resolve relative to the configuration. Every selected file must be inside that folder at a segment boundary.{src}and the retained input URI preserve the original resource identity; names and paths are metadata. Without@root, the existing configuration-relative binding behavior applies.**/matches zero or more directories, including files directly in the root. Files sort by resolved URI. One-to-one operations retain their bindings;join @mode=collectexposes each typed input and its folder metadata throughitems[*].bindings, without serializing/reparsing the input AST. This is a file-tree input: empty directories are not artifacts.import @opaque=truecreates a byte-preserving source for static publication without parsing or schema validation.@opaqueis a stricttrue/falseboolean. Unrooted opaque imports require@content-type; rooted opaque imports can omit it for mixed static trees. Each file then uses its declared resource/resolver content type, native extension inference, orapplication/octet-streamfor unknown extensions. An explicit import content type overrides that choice for all selected files. Opaque imports forbid@schema, and must feed one or more directexportnodes only. Those exports cannot declare@schema; an explicit export content type must have the same media type as the import. Omitting the export content type preserves the source media type, including when the output filename has another extension. Resource schema/namespace metadata is not carried as a validation claim for opaque bytes. Static outputs retain whole-resource lineage, without invented parsed character source maps.- Folder roots must be concrete and nonempty. Out-of-root resources, unsafe path bindings (including encoded separators/dot segments), unmatched globs and duplicate resolved destinations fail before publication. Imports allow at most 1,024 files. Native filesystem glob traversal also stops at depth 64 or 16,384 visited entries, checks cancellation and skips symlink entries. Browser builds match the admitted concrete resource manifest using the same native selector; they never discover files from the network or local filesystem.
join @mode="collect"creates one collection artifact from all artifacts in its primary input.join @mode="group-by" @by="NAME"creates one collection artifact for each distinct source binding value named by@by.join @mode="match-by" @by="NAME" @with:LABEL="NODE"creates one collection artifact for each primary input key and attaches same-key named secondary input artifacts.join @mode="zip" @with:LABEL="NODE"creates one collection artifact for each positional tuple across primary and named secondary input artifacts. Unequal input counts are a schema validation failure.convertfollows one registered, schema-package-owned converter edge from the typed input identity to the target@content-typeand/or@schema.convert @converterselects that exact registered edge and validates that its declared source and target identities match the graph artifacts.- Conversion produces a typed graph artifact, so later
transform,rewrite-importmap, andexportnodes consume the target identity rather than reparsing an implicit JSON or text intermediary. - Executable graph conversions include the Markdown package's
markdown-to-html-rustedge and the HTML package'shtml-to-cem-dom-projection-rustrecovery edge. The latter retains the package-ownedHtmlDocumentAstas a native DOM projection whose borrowed hierarchical query view can feed CEMT directly; it does not serialize the DOM through JSON, raw HTML, or a replacement tree. transformcreates a template application node from@src.transform @content-type @schemadeclares its result identity independently of its template identity and final export formats. An explicit result takes precedence over export inference; omitted result attributes preserve existing inference. A CEM-native stage producingapplication/cemwithhttps://cem.dev/ns/cem-ml/1retains a typed CEM tree for subsequent stages.transform @entrypointselects a public CEM-native template entrypoint.transformchildparam @name @valuerecords provide caller params for that transform stage.- Graph params and entrypoints are stage-local; top-level CLI
--paramand--template-entrypointcannot be combined with--config. rewrite-importmapmutates the<script type="importmap">JSON in a text HTML input artifact and leaves the rest of the HTML unchanged.rewrite-importmap @source-mapoptionally validates the sourceimportsentries before rewriting. Legacy browser import-map JSON remains validation only.rewrite-importmap @authored-mapoptionally separates the exact HTML source URL expectations from deployment asset selection. This document must declare exactly the target import keys; its raw paths are checked against authored HTML without resolving or loading them.source-mapstill selects and hashes the deployment bytes, and the paired-map identity checks remain mandatory. Omittingauthored-mappreserves the original source-map validation behavior.- When both maps declare
$schemaas module-map v1, v2, or v3, they use the dedicatedapplication/vnd.cem.module-map+jsoncontract and must declare matching keys. V1 retains string JavaScriptimports; v2 adds typed deployment-only JavaScript/CSS/WASMresources; both remain byte-preserving. - V3 uses typed JavaScript/JSON
importsand permits JavaScript imports or resources to declare exactmoduleImportsedges. Lowering rewrites only declared static import, export-from, and quoted dynamic-import bare specifiers to relative deployed URLs. Undeclared/unused edges, mismatched map pairs, and unsafe destinations are errors; no dependency is discovered or copied from JavaScript source. - Destination
importspaths are written into the HTML browser import map; resources remain deployment-only. Every declared asset lowers through an ordinary typed graph import/export beside each HTML export. - Lowering also produces a deterministic module-asset manifest with resolved
source and destination provenance plus source/output byte lengths and SHA-256
digests. Byte-identical v1/v2 manifests retain contract/hash version 1; a v3
rewrite selects contract/hash version 2. The common transform-graph response
and CLI reports retain the full manifest. CLI
--module-asset-cache-keyprints only the aggregatesha256:<digest>and stops before execution/publication for use as an Nx runtime input. rewrite-importmap @target-mappoints to either a legacy browser import-map JSON file or the paired destination CEM-ML module map.rewrite-importmap @modesupportsreplace-importsby default, plusmergeandreplace-script.rewrite-importmap @missingsupportserrorby default, plusignoreandinsert.exportcreates a sink node from@out.export @style-policycontrols HTML style handling for mixed HTML/CSS exports.autokeeps inline styles unless a sibling CSS export for the same graph artifact exists, then links it;inlinekeeps inline<style>blocks;linkrequires a sibling CSS export and replaces inline styles with a stylesheet link;omitremoves inline styles without adding a link.- Nested operation nodes create parent graph edges.
@inputcreates an explicit primary input edge to an existing graph node.@with:*creates named side-input edges to existing graph nodes.joinis the only supported cardinality-changing operation in this slice. Supported join modes arecollect, source-bindinggroup-by, and same-bindingmatch-by, and positionalzip.- Graph node IDs are explicit
@idvalues or parser-generated fallback IDs. - Duplicate IDs, unknown references, cycles, missing required attributes, and duplicate output destinations are schema validation failures.
@outpatterns use named path placeholders such as{stem}. Bare*wildcards are rejected.
Template Identity
Transform nodes must declare or imply a supported template identity through
@template-content-type, @template-schema, or a recognized @src
extension. The current parser classifies XSLT and CEM-native templates; the
runtime executes supported CEM-native templates and bounded XSLT 1.0 parity
templates through registered transform-template adapters.
Template Entrypoints And Params
transform @entrypoint selects a template entrypoint for adapters that expose
named execution. Child param @name @value records provide caller params for
that transform stage. @value is a string-first CLI/config value: source
bindings such as {stem} are expanded by the CLI host before the engine
request is built. CEM-native module execution then coerces the string according
to the selected entrypoint's param declaration. XSLT parity passes the expanded
string as an xsl:with-param value for named template entrypoints. Plain
cem-ql-fragment execution still uses the implicit entrypoint with no params.
Example
For site metadata, opt into native YAML front matter on the import with
front-matter=yaml in its Markdown content type. The recovered DOM provides
input.metadata as common CEM data nodes and input.sourceUri alongside
input.children. Add site-wide data/navigation as a separate import and bind
it with transform @with:site=site; reusable partials remain declared CEMT
module imports. Metadata does not trigger resource discovery, select templates
or rewrite destinations. The full contract and bounds are in the
Markdown package.
The layout in this example receives the native DOM projection as its implicit
input value. A complete layout can recursively emit input.children with the
CEM transform vocabulary while adding shared navigation and page structure.