@fabricorg/assembly
v0.4.3
Published
Deterministic application assembly: versioned manifests, resolved lockfiles, and a single build gate for capabilities, component packs, and adoption bindings.
Readme
@fabricorg/assembly
Deterministic application assembly for Fabric verticals. It takes a versioned assembly manifest — the capabilities, component packs, adoption bindings, and usage contracts a vertical assembles — resolves dependency ranges deterministically, validates contracts, packs, and bindings as one build gate, and produces a locked, digest-bearing lockfile.
What it does
- Assembly manifest (
AssemblyManifest): a versioned declaration of what a vertical assembles. Capabilities and component packs carry semver ranges; bindings and generated usage-contract documents are supplied inline for validation. - Lockfile (
AssemblyLockfile): the resolved, deterministic output. Every capability and pack version is locked with its range and artifact digest, generated contracts are tied to the resolved capability manifest, every binding carries a content digest, and the whole resolved document carries a canonical SHA-256 digest. - Resolution (
resolveAssembly): resolves semver ranges against available versions, rejects unresolved or duplicate capability identities, validates usage contracts, component packs, and adoption bindings, and returns every finding rather than throwing on the first. - Build gate (
assertAssemblyResolved): throws with every finding listed, for use as a CI step. - Runtime verification (
assertAssemblyLockfile): validates a stored or transported lockfile and rejects any resolved content that no longer matches itsassemblyDigest. - Runtime compatibility (
assertAssemblyRuntimeCompatible): fails startup unless loaded capability versions and manifest digests exactly match the approved lockfile, including rejection of missing or extra modules.
Semver ranges
The resolver supports the subset of npm range syntax a deterministic lockfile
needs: ^ (caret), ~ (tilde), >=, >, <=, <, = (exact), bare exact
versions, * (wildcard), and compound ranges (>=1.0.0 <2.0.0). Prerelease
versions follow npm semantics: a prerelease only satisfies a range when a
comparator in the range declares a prerelease at the same [major, minor, patch]
tuple.
Usage
import { assertAssemblyResolved } from "@fabricorg/assembly";
const lockfile = assertAssemblyResolved({
manifest: {
formatVersion: 2,
application: "lending",
capabilities: [{ namespace: "recovery", range: "^1.0.0" }],
componentPacks: [{ pack: "lender-components", namespace: "lender", range: "^1.0.0" }],
bindings: [{ capability: "recovery", binding }],
contracts: [{ namespace: "recovery", document: usageContractDocument }],
},
capabilities: [{ namespace: "recovery", version: "1.1.0", manifestDigest: "…" }],
componentPacks: [{
pack: "lender-components",
namespace: "lender",
version: "1.1.0",
artifactDigest: "…",
}],
packManifests: [pack],
coreComponents: ["Card", "List", "DetailLayout"],
});It does not load live modules, execute handlers, or run inside
@fabricorg/platform. The assembly package depends on @fabricorg/adoption-bindings
and @fabricorg/gen-capability for contract and binding validation.
The adapter plane
An assembly manifest may declare adapters, each the output of certifyAdapter. Declaring the
field, even empty, engages the plane: every port a contract requires must be satisfied by a declared,
certified adapter, judged by validatePortRequirements with certification required, and a
certification against an older port contract is stale when portDefinitions are supplied. The
lockfile records every declared adapter, certified or not, and the assembly digest covers them.
assertAssemblyRuntimeCompatible(lockfile, capabilities, registeredAdapters) then checks that what a
runtime registered behind each port is what the assembly approved. An assembly that does not declare
adapters resolves exactly as before.
