@prisma-next/framework-components
Internal package. This package is an implementation detail of
prisma-nextand is published only to support its runtime. Its API is unstable and may change without notice. Do not depend on this package directly; installprisma-nextinstead.
Framework component types, authoring logic, control stack assembly, and emission SPI for Prisma Next.
What this package provides
- Component types (
./components): Base descriptor and instance interfaces for framework components (family, target, adapter, driver, extension), pack refs, and type renderer system - Authoring types (
./authoring): Declarative authoring contribution types, template resolution, and validation for type constructors and field presets - Codec base interface (
./codec): The cross-familyCodecbase type that SQLCodecand MongoMongoCodecextend - Control stack (
./control): Assembly functions that combine component descriptors into a unifiedControlStackwith derived state (codec imports, renderers, authoring contributions) - Emission SPI (
./emission): Types for the emission pipeline —TargetFamilyHook,ValidationContext,GenerateContractTypesOptions,TypeRenderEntry,TypeRenderer,ParameterizedCodecDescriptor, and related types - Execution types (
./execution): Execution-plane stack and instance interfaces - Runtime SPI (
./runtime): AbstractRuntimeCore<TPlan, TExec, TMiddleware>base class,RuntimeMiddlewareinterface, and the canonicalrunWithMiddlewareorchestrator helper. Family runtimes (@prisma-next/sql-runtime,@prisma-next/mongo-runtime) extendRuntimeCoredirectly per ADR 204.
Subpath exports
import { ComponentMetadata, FamilyDescriptor, normalizeRenderer } from '@prisma-next/framework-components/components';
import { AuthoringContributions, instantiateAuthoringTypeConstructor } from '@prisma-next/framework-components/authoring';
import type { Codec } from '@prisma-next/framework-components/codec';
import { createControlStack, ControlStack } from '@prisma-next/framework-components/control';
import type { EmissionSpi } from '@prisma-next/framework-components/emission';
import { RuntimeCore, runWithMiddleware, type RuntimeMiddleware } from '@prisma-next/framework-components/runtime';
Codec interface
The base Codec interface lands on the seam between query-time methods (per-row, IO-relevant) and build-time methods (per-contract-load):
- Query-time:
encode(value): Promise<TWire>anddecode(wire): Promise<TInput>are required and Promise-returning at the public boundary. Codec authors extendCodecImpl(per ADR 208 — Higher-order codecs for parameterized types); a logically synchronous body still has to return aPromise-compatible value (mark the methodasync, or returnPromise.resolve(...)explicitly). The runtime always awaits the result. - Build-time:
encodeJson,decodeJson, and the optionalrenderOutputTypeare synchronous sofamily.deserializeContractand client construction stay synchronous.
There is no runtime / kind / equivalent async marker on the interface and no TRuntime generic. The runtime always awaits the query-time methods. See ADR 204 — Single-Path Async Codec Runtime for the full design.
Codec call context (ctx)
Codecs receive a second ctx options argument; you may ignore it. The runtime allocates one CodecCallContext per execute() call and threads the same reference to every codec dispatch site as a non-optional argument — when no signal is supplied the runtime still threads an empty {}, never undefined. The framework CodecCallContext is signal-only:
export interface CodecCallContext {
readonly signal?: AbortSignal;
}
The internal Codec interface declares the parameter as required:
encode(value: TInput, ctx: CodecCallContext): Promise<TWire>;
decode(wire: TWire, ctx: CodecCallContext): Promise<TInput>;
Codec authors who write (value) => … continue to compile via TypeScript's bivariance for trailing parameters; nothing at the author surface changes.
Family layers extend the context where they have a per-call concept that doesn't generalise. SQL declares SqlCodecCallContext extends CodecCallContext { column?: SqlColumnRef } (see @prisma-next/sql-relational-core); Mongo continues to use the framework type directly. Codec authors that take a (value, ctx) author signature can forward ctx.signal to network SDKs:
// Sketch — codec authors extend `CodecImpl`; class methods receive `(value, ctx)`.
async encode(v: string, ctx: CodecCallContext): Promise<EncryptedWire> {
return kms.encrypt({ plaintext: v }, { signal: ctx.signal });
}
Aborts surface to the caller as RUNTIME.ABORTED with details.phase ∈ { 'encode', 'decode', 'stream' }. Codec bodies that ignore the signal complete in the background (cooperative cancellation). The runtimeAborted(phase, cause?) envelope helper and the raceAgainstAbort(work, signal, phase) race helper are exported from @prisma-next/framework-components/runtime.
See ADR 207 — Codec call context: per-query AbortSignal and column metadata for the full design.
Higher-order codecs (CodecDescriptor, CodecInstanceContext)
Codec metadata, parameterized-codec registration, and runtime materialization live on a unified CodecDescriptor<P> — the only registration shape framework consumers see:
import type { CodecDescriptor, CodecInstanceContext } from '@prisma-next/framework-components/codec';
import { voidParamsSchema } from '@prisma-next/framework-components/codec';
CodecDescriptor<P = void>carriescodecId,traits,targetTypes,meta,paramsSchema: StandardSchemaV1<P>, optionalrenderOutputType, and a curriedfactory: (P) => (CodecInstanceContext) => Codec. Non-parameterized codecs useP = void(with the framework-suppliedvoidParamsSchema) and a constant factory; parameterized codecs use a non-emptyP(e.g.{ length: number }for pgvector).CodecInstanceContext(family-agnostic,{ name }only) is supplied by the runtime when materializing a per-instance codec. Pack authors close over it inside the factory; they never construct it. This is the per-materialization context, sibling to the per-callCodecCallContextdocumented above. Family-specific extensions augment it — the SQL family shipsSqlCodecInstanceContext extends CodecInstanceContextin@prisma-next/sql-relational-core/ast, addingusedAt: ReadonlyArray<{ table; column }>for SQL-domain codecs that need column-set metadata.- Contributors expose their descriptors through
ComponentMetadata.types.codecTypes.codecDescriptorsand the unifiedcodecs: () => ReadonlyArray<CodecDescriptor>slot.extractCodecLookupreadstargetTypes/meta/renderOutputTypedirectly off the descriptors — there is no parameterized vs. non-parameterized split and no synthesis bridge.
paramsSchema is typed as Standard Schema (StandardSchemaV1<P>), not arktype-specific. arktype Types satisfy the shape via their ~standard getter, so existing arktype-typed descriptors satisfy the new shape transparently while framework-components itself takes no dependency on arktype.
See ADR 208 — Higher-order codecs for parameterized types for the full design.
Why SPI types live here (dependency inversion)
This package sits in the core layer — below the tooling layer where family-specific emitters and control implementations live. SPI interfaces like EmissionSpi define the contract between framework orchestration code (control-plane emission, CLI) and family-specific implementations (SQL emitter, Mongo emitter).
By placing these interfaces in the core layer rather than alongside their implementations:
- Orchestration code (control-plane, CLI) can depend on the SPI interfaces without pulling in family-specific packages.
- Family implementations (SQL emitter, Mongo emitter) implement these interfaces and depend on this package — the dependency arrow points inward toward the core.
- The contract package (
@prisma-next/contract) remains a true leaf in thefoundationlayer with zero framework-domain dependencies.
This is the dependency inversion principle applied to package layering. The same pattern applies to component descriptors, control-plane types, and execution-plane types in this package.
See ADR 185 — SPI types live at the lowest consuming layer.
Relationship to other packages
This package is the canonical source for framework component types, assembly logic, and emission SPI types. New code should import directly from @prisma-next/framework-components.