@fabricorg/platform
v0.3.1
Published
Generic, vertical-agnostic runtime for ontology-based business applications. Provides actions, modules, events, policies, state machines, adapters, displays, projections, privacy, and evidence contracts. Zero runtime dependencies; portable across any data
Maintainers
Readme
@fabricorg/platform
A portable runtime for ontology-based, agent-native business applications. Zero dependencies. Bring your own database client.
npm install @fabricorg/platformHello, action
import { z } from "zod";
import type { ActionDefinition, ActionContext } from "@fabricorg/platform/actions";
const greet: ActionDefinition<unknown> = {
id: "demo.greet",
schema: z.object({ name: z.string() }),
handler: async (ctx: ActionContext<unknown>, params: { name: string }) => {
return { greeting: `Hello, ${params.name}!` };
},
};That's the smallest unit. From here, you compose actions into a FabricModule<TDb>, register the module at startup, and call invokeAction("demo.greet", ...) from any of three call paths (authenticated user, signed token, or system/agent). Every call passes through the same pipeline: policy → state machine → handler → adapters → events → projections.
For a complete walkthrough see https://platform.fabric.pro/docs/getting-started/quickstart.
Why use this
You're building a governed business application — not a CRUD app. You need:
- A single, auditable entry point for every mutation
- Policies that gate every change before it happens
- Events as evidence (not retrofitted logs)
- Agents and humans treated identically by policy
- A vertical-specific ontology layered on a portable runtime
That's what this package gives you. Nothing more.
What's in the box
| Subpath | What you get |
|---|---|
| @fabricorg/platform | Re-exports everything below |
| @fabricorg/platform/actions | ActionDefinition, ActionContext, SagaImplementation |
| @fabricorg/platform/policies | PolicyEvaluator, PolicyContext, code/data/hybrid evaluation |
| @fabricorg/platform/state-machines | StateMachineDefinition, validateTransition |
| @fabricorg/platform/events | AssetEventEnvelope, EventTypeRegistry |
| @fabricorg/platform/projections | Replay/snapshot framework |
| @fabricorg/platform/adapters | AdapterRegistry, retry + circuit-breaker |
| @fabricorg/platform/modules | FabricModule<TDb>, registerFabricModules |
| @fabricorg/platform/ids | createFabricId, FABRIC_ID_PREFIXES |
| @fabricorg/platform/displays | DisplayRendererRegistration |
| @fabricorg/platform/privacy | Redaction + data classification |
| @fabricorg/platform/evidence | Compliance evidence packets |
| @fabricorg/platform/objects | Shared object-type contracts |
| @fabricorg/platform/testing | In-memory adapter registry, fake ActionContext, deterministic IDs |
Composing a vertical
Verticals declare a FabricModule<TDb> that registers their actions, policies, state machines, events, and adapters with the platform:
import type { FabricModule } from "@fabricorg/platform/modules";
import { registerFabricModules } from "@fabricorg/platform/modules";
const myModule: FabricModule<MyDbClient> = {
namespace: "demo",
version: "1.0.0",
objectTypes: ["Widget"],
actions: [greet, /* ... */],
policies: [/* ... */],
stateMachines: [/* ... */],
displayRenderers: [],
};
// At app startup:
registerFabricModules([myModule]);The platform never auto-loads a module. The host app composes them explicitly — no implicit defaults, no globals, no surprises.
The action lifecycle
Every invokeAction(id, params) call goes through:
evaluatePolicies— every policy registered for the action runs (code/data/hybrid). A singleblockhalts the invocation.- State-machine validation — if the action's target entity is bound to a state machine, the requested transition is validated before the handler runs.
- Handler —
actionDef.handler(ctx, params)runs inside a singleTDbtransaction. Handler parses its own input via itsschema(single source of truth). - Adapters — for each
actionDef.adapterSteps, the registered implementation runs viaexecuteWithAdapterRetry(idempotent retry, exponential backoff, circuit breaker). - Events — domain events are appended; projections derive read models on next replay.
- Completion — invocation status is marked, telemetry emitted.
validateActionParameters is also exposed for preview/dry-run paths but is not part of the execute pipeline.
Saga actions
Multi-step orchestrations declare kind: "saga" on the ActionDefinition and ship a SagaImplementation:
import type { SagaImplementation } from "@fabricorg/platform/actions";
export const ingestSaga: SagaImplementation = async (ctx, runChild) => {
const step1 = await runChild({
suffix: "step-1",
actionId: "demo.receive_payload",
parameters: { /* ... */ },
});
// orchestrate further child actions...
return {
resultData: { /* ... */ },
event: { eventType: "...", subjectType: "...", subjectId: "...", payload: {} },
};
};Your worker maps action IDs to implementations. The platform dispatches generically — no platform code references vertical action IDs.
Portability boundary
The boundary.test.ts in this package fails CI if any of these leak in:
- Concrete database/ORM imports — the literal
Prisma, the field nameprisma:, or the type defaultTDb = any - Vertical vocabulary (
lending.,borrower,lender,Vehicle,Party,Obligation,Offer, etc.) - "Register default" hooks (
registerDefaultPolicies, etc.) — the platform never auto-loads anything
This is what keeps the platform portable across verticals and consumers. If you fork or add new patterns specific to your domain, extend the list in boundary.test.ts.
Compatibility
- Runtime: Node 20+
- Module formats: ESM and CommonJS (dual format from v0.2.0+)
- TypeScript: 5.0+,
moduleResolution: nodeornode16/nodenext
Versioning
0.x while contracts evolve. 1.0 only after at least two materially different verticals validate the API.
See CHANGELOG.md for release history.
Documentation
Full reference docs at https://platform.fabric.pro
License
MIT
