@fabricorg/projection-host
v1.1.1
Published
Vertical-neutral query host for authorized, tenant-scoped Fabric view projection and snapshot delivery.
Readme
@fabricorg/projection-host
Vertical-neutral query execution for Fabric ViewContracts. The Host authorizes every read before
touching storage, validates declared query parameters, enforces tenant/space scope, resumes deterministic event replay from snapshots,
applies declared freshness and offline rules, and refuses to advance a cache across proven subject-event gaps or
unsupported event schema versions. Snapshot adapters use compare-and-swap expectations so concurrent
Host instances cannot regress a newer checkpoint. Durable snapshots retain per-subject sequence
high-water marks, allowing incremental replay to skip events already represented by the checkpoint.
The global ledger sequence may have legitimate gaps after filtering; gap detection requires an
explicit contiguous subjectSequence from the event source. An incremental source may claim that its stream begins after a
snapshot only when that snapshot carries a durable event cursor; the replay API otherwise fails closed.
Callers that need read-after-write behavior can add a ledger checkpoint and a bounded wait:
const result = await host.project({
...query,
parameters: { region: "east" },
minimumCheckpoint: { eventSequence: writeResult.eventSequence },
checkpointTimeoutMs: 2_000,
});Parameterized views declare a portable parameterSchema. The Host validates parameters before
authorization, passes the validated value to authorization and event-source adapters, and derives a
non-reversible SHA-256 identity from canonical JSON. That identity is part of every snapshot key, so
two parameter variants cannot share state. Supplying parameters to an unparameterized view, or
omitting parameters for a parameterized view, fails with ProjectionParameterValidationError.
Snapshot migrations are explicit: a view lists older compatibleSnapshotVersions and provides
upgradeSnapshot(data, fromVersion). The Host only resumes a declared-compatible snapshot through
that upcaster and persists the result at the current snapshotVersion; incompatible snapshots are
cold-rebuilt instead.
Checkpoint waits are authorization-first and tenant/space scoped. Event sources used with this
option must implement waitForCheckpoint; the PostgreSQL adapter polls asset_events until the
ledger reaches the requested sequence. A missing checkpoint within the bound throws the typed
ProjectionCheckpointTimeoutError with code PROJECTION_CHECKPOINT_TIMEOUT; it never returns a
projection that falsely claims the requested current position. The Host verifies the hydrated result
against the lower bound as well, so an event source that reports ledger progress before making those
events readable also fails closed.
import { createProjectionHost, PostgresProjectionEventSource, PostgresProjectionSnapshotStore } from "@fabricorg/projection-host";
const host = createProjectionHost({
views,
eventSource,
snapshotStore: new PostgresProjectionSnapshotStore(sql),
authorize: async ({ actor, scope, view }) =>
policy.canRead(actor, scope, view)
? { allowed: true, decisionId: "decision-reference" }
: { allowed: false, reason: "read denied" },
});
const result = await host.project({
view: { name: "module/summary", version: "1" },
scope: { tenantId: serverDerivedTenantId, spaceId: serverDerivedSpaceId },
actor: { id: principal.id, type: principal.type },
consistency: "current",
});PostgresProjectionEventSource reads the Platform Host event ledger using tenant, space, subject,
event-type, and cursor constraints. PostgresProjectionSnapshotStore creates a portable snapshot
table through a locked, checksummed migration ledger and performs atomic compare-and-swap updates.
CI certifies concurrent schema creation and compare-and-swap behavior against PostgreSQL 16. The
in-memory adapters remain available for tests and explicit local development.
The application supplies production event and snapshot adapters. Scope values and actor identity
must be derived from authenticated server context, never trusted from a client query. SDUI layout,
components, themes, and vertical vocabulary are deliberately outside this package. onQuery receives
both allowed and denied query evidence, and every caller receives an isolated result graph even when
concurrent reads are coalesced internally.
