@prisma-next/sql-runtime 0.13.0 → 0.14.0-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -43
- package/dist/{exports-DDqF-xmg.mjs → exports-BXdIMaxT.mjs} +79 -120
- package/dist/exports-BXdIMaxT.mjs.map +1 -0
- package/dist/index.d.mts +80 -2
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -2
- package/dist/{index-JOQlRa75.d.mts → prepared-statement-FQyyQnkC.d.mts} +172 -107
- package/dist/prepared-statement-FQyyQnkC.d.mts.map +1 -0
- package/dist/test/utils.d.mts +18 -7
- package/dist/test/utils.d.mts.map +1 -1
- package/dist/test/utils.mjs +23 -4
- package/dist/test/utils.mjs.map +1 -1
- package/package.json +12 -12
- package/src/codecs/ast-codec-resolver.ts +9 -53
- package/src/codecs/validation.ts +19 -30
- package/src/exports/index.ts +6 -2
- package/src/middleware/budgets.ts +7 -2
- package/src/middleware/lints.ts +3 -1
- package/src/runtime-spi.ts +1 -1
- package/src/sql-context.ts +18 -56
- package/src/sql-runtime.ts +39 -48
- package/dist/exports-DDqF-xmg.mjs.map +0 -1
- package/dist/index-JOQlRa75.d.mts.map +0 -1
package/dist/test/utils.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","names":["collectAsync","SelectAstCtor"],"sources":["../../test/test-codec.ts","../../test/utils.ts"],"sourcesContent":["/**\n * Test-only helper that constructs a SQL-family `Codec` instance from author-side encode/decode functions. Replaces the legacy public `mkCodec()` factory (deleted under TML-2357); tests that need a stub codec for behavioural assertions instantiate one through this helper rather than going through `descriptor.factory(...)`.\n *\n * The body is identical in spirit to the retired `mkCodec`: promise-lift sync author functions onto the framework-required `Promise<…>` boundary, default `encodeJson`/`decodeJson` to identity when `TInput` is JSON-safe, fail loudly otherwise.\n */\nimport type { JsonValue } from '@prisma-next/contract/types';\nimport type { CodecTrait } from '@prisma-next/framework-components/codec';\nimport type { Codec, SqlCodecCallContext } from '@prisma-next/sql-relational-core/ast';\n\ntype JsonRoundTripConfig<TInput> = [TInput] extends [JsonValue]\n ? {\n encodeJson?: (value: TInput) => JsonValue;\n decodeJson?: (json: JsonValue) => TInput;\n }\n : {\n encodeJson: (value: TInput) => JsonValue;\n decodeJson: (json: JsonValue) => TInput;\n };\n\nexport function defineTestCodec<\n Id extends string,\n const TTraits extends readonly CodecTrait[] = readonly [],\n TWire = unknown,\n TInput = unknown,\n>(\n config: {\n typeId: Id;\n targetTypes?: readonly string[];\n encode: (value: TInput, ctx: SqlCodecCallContext) => TWire | Promise<TWire>;\n decode: (wire: TWire, ctx: SqlCodecCallContext) => TInput | Promise<TInput>;\n traits?: TTraits;\n } & JsonRoundTripConfig<TInput>,\n): Codec<Id, TTraits, TWire, TInput> {\n const identity = (v: unknown) => v;\n const userEncode = config.encode;\n const userDecode = config.decode;\n const widenedConfig = config as {\n encodeJson?: (value: TInput) => JsonValue;\n decodeJson?: (json: JsonValue) => TInput;\n };\n return {\n id: config.typeId,\n encode: (value, ctx) => {\n try {\n return Promise.resolve(userEncode(value, ctx));\n } catch (error) {\n return Promise.reject(error);\n }\n },\n decode: (wire, ctx) => {\n try {\n return Promise.resolve(userDecode(wire, ctx));\n } catch (error) {\n return Promise.reject(error);\n }\n },\n encodeJson: (widenedConfig.encodeJson ?? identity) as (value: TInput) => JsonValue,\n decodeJson: (widenedConfig.decodeJson ?? identity) as (json: JsonValue) => TInput,\n } as Codec<Id, TTraits, TWire, TInput>;\n}\n","import {\n type Contract,\n type ContractModelBase,\n coreHash,\n profileHash,\n} from '@prisma-next/contract/types';\nimport type {\n CodecDescriptor,\n CodecMeta,\n CodecTrait,\n} from '@prisma-next/framework-components/codec';\nimport { APP_SPACE_ID } from '@prisma-next/framework-components/control';\nimport {\n instantiateExecutionStack,\n type RuntimeDriverDescriptor,\n} from '@prisma-next/framework-components/execution';\nimport { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type { ResultType } from '@prisma-next/framework-components/runtime';\nimport { runtimeError } from '@prisma-next/framework-components/runtime';\nimport { canonicalizeJson } from '@prisma-next/framework-components/utils';\nimport { builtinGeneratorIds } from '@prisma-next/ids';\nimport { generateId } from '@prisma-next/ids/runtime';\nimport {\n buildSqlNamespace,\n SqlStorage,\n type SqlStorageInput,\n SqlUnboundNamespace,\n type StorageTableInput,\n} from '@prisma-next/sql-contract/types';\nimport type {\n Adapter,\n AnyQueryAst,\n Codec,\n ContractCodecRegistry,\n LoweredStatement,\n SelectAst,\n} from '@prisma-next/sql-relational-core/ast';\nimport { SelectAst as SelectAstCtor, TableSource } from '@prisma-next/sql-relational-core/ast';\nimport type { SqlExecutionPlan, SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';\nimport { applicationDomainOf, collectAsync, drainAsyncIterable } from '@prisma-next/test-utils';\nimport type { Client } from 'pg';\nimport {\n createExecutionContext,\n type createRuntime,\n createSqlExecutionStack,\n} from '../src/exports';\nimport type {\n ExecutionContext,\n SqlRuntimeAdapterDescriptor,\n SqlRuntimeAdapterInstance,\n SqlRuntimeDriverInstance,\n SqlRuntimeExtensionDescriptor,\n SqlRuntimeTargetDescriptor,\n} from '../src/sql-context';\nimport { defineTestCodec } from './test-codec';\n\nfunction createTestMutationDefaultGenerators() {\n return builtinGeneratorIds.map((id) => ({\n id,\n generate: (params?: Record<string, unknown>) => generateId(params ? { id, params } : { id }),\n stability: 'field' as const,\n }));\n}\n\n/**\n * Executes a plan and collects all results into an array. This helper DRYs up the common pattern of executing plans in tests. The return type is inferred from the plan's type parameter.\n */\nexport async function executePlanAndCollect<\n P extends SqlExecutionPlan<ResultType<P>> | SqlQueryPlan<ResultType<P>>,\n>(runtime: ReturnType<typeof createRuntime>, plan: P): Promise<ResultType<P>[]> {\n type Row = ResultType<P>;\n return collectAsync<Row>(runtime.execute<Row>(plan));\n}\n\n/**\n * Drains a plan execution, consuming all results without collecting them. Useful for testing side effects without memory overhead.\n */\nexport async function drainPlanExecution(\n runtime: ReturnType<typeof createRuntime>,\n plan: SqlExecutionPlan | SqlQueryPlan<unknown>,\n): Promise<void> {\n return drainAsyncIterable(runtime.execute(plan));\n}\n\n/**\n * Sets up database schema and data, then writes the contract marker. This helper DRYs up the common pattern of database setup in tests.\n *\n * Callers must supply `bootstrapMarkerTables` (typically\n * `bootstrapPostgresSignMarkerTables` from integration `postgres-bootstrap.ts`)\n * so this package does not depend on the postgres adapter/target packs.\n */\nexport async function setupTestDatabase(\n client: Client,\n contract: Contract<SqlStorage>,\n setupFn: (client: Client) => Promise<void>,\n bootstrapMarkerTables: (client: Client) => Promise<void>,\n): Promise<void> {\n await client.query('drop schema if exists prisma_contract cascade');\n await client.query('create schema if not exists public');\n\n await setupFn(client);\n\n await bootstrapMarkerTables(client);\n await writeTestContractMarker(client, contract);\n}\n\nexport interface SeedMarkerInput {\n /** Logical space for the marker row; defaults to {@link APP_SPACE_ID}. */\n readonly space?: string;\n readonly storageHash: string;\n readonly profileHash: string;\n readonly contractJson?: unknown;\n readonly canonicalVersion?: number;\n readonly invariants?: readonly string[];\n}\n\n/**\n * Seeds a contract marker row directly via raw SQL. Test-only: the production\n * write path goes through the control adapter SPI (`initMarker`/`updateMarker`),\n * which needs a `ControlDriverInstance`; these fixtures hold a raw `pg.Client`,\n * so they perform a minimal `INSERT` over the columns the runtime reads back.\n */\nexport async function seedTestMarker(client: Client, input: SeedMarkerInput): Promise<void> {\n await client.query(\n `insert into prisma_contract.marker\n (space, core_hash, profile_hash, contract_json, canonical_version, invariants, updated_at)\n values ($1, $2, $3, $4::jsonb, $5, $6::text[], now())`,\n [\n input.space ?? APP_SPACE_ID,\n input.storageHash,\n input.profileHash,\n input.contractJson === undefined ? null : JSON.stringify(input.contractJson),\n input.canonicalVersion ?? null,\n input.invariants ?? [],\n ],\n );\n}\n\n/**\n * Seeds the app-space marker for a contract. Thin wrapper over\n * {@link seedTestMarker} for the common \"write the marker for this contract\" case.\n */\nexport async function writeTestContractMarker(\n client: Client,\n contract: Contract<SqlStorage>,\n): Promise<void> {\n await seedTestMarker(client, {\n storageHash: contract.storage.storageHash,\n profileHash: contract.profileHash,\n contractJson: contract,\n canonicalVersion: 1,\n });\n}\n\n/**\n * Creates a test adapter descriptor from a raw adapter. Wraps the adapter in an SqlRuntimeAdapterDescriptor with static contributions derived from the adapter's codec registry.\n */\n/**\n * Build a {@link ContractCodecRegistry} from a codec array for tests that exercise `encodeParam(s)` / `decodeRow` in isolation. The production runtime builds `ContractCodecRegistry` from contract walk + descriptor list and never goes through this helper; tests use it to wire a hand-built codec set into the surface those functions consume in production.\n */\nexport function buildTestContractCodecs(\n codecs: ReadonlyArray<Codec<string>>,\n): ContractCodecRegistry {\n const byId = new Map<string, Codec<string>>();\n for (const codec of codecs) {\n byId.set(codec.id, codec);\n }\n // Canonical-key cache: production `forCodecRef` memoizes per `(codecId, canonicalize(typeParams))`. Tests resolve by codecId, but key the cache on the canonical pair so callers passing distinct typeParams get distinct (still codec-id-templated) entries — and so this helper cannot silently coalesce them.\n const byCanonicalKey = new Map<string, Codec<string>>();\n return {\n forColumn: () => undefined,\n forCodecRef: (ref) => {\n const canonicalKey = canonicalizeJson({\n codecId: ref.codecId,\n ...(ref.typeParams !== undefined ? { typeParams: ref.typeParams } : {}),\n });\n const cached = byCanonicalKey.get(canonicalKey);\n if (cached) return cached;\n const template = byId.get(ref.codecId);\n if (!template) {\n throw runtimeError(\n 'RUNTIME.CODEC_DESCRIPTOR_MISSING',\n `Test ContractCodecRegistry has no codec for codecId '${ref.codecId}'.`,\n {\n codecId: ref.codecId,\n ...(ref.typeParams !== undefined ? { typeParams: ref.typeParams } : {}),\n },\n );\n }\n byCanonicalKey.set(canonicalKey, template);\n return template;\n },\n };\n}\n\n/**\n * Synthesize `CodecDescriptor`s from a codec array of non-parameterized codec instances. Test-only: the production synthesis bridge was retired under TML-2357. Lets the existing `createTestAdapterDescriptor` pattern keep wrapping a stub `Adapter` (whose `__codecs` slot still exposes the codec set) into the descriptor-list shape that `SqlStaticContributions.codecs:` now expects. The `Codec` instances carry\n * `traits`/`targetTypes`/`meta` via the SQL family extension; the structural narrow reads those fields directly.\n */\nexport function descriptorsFromCodecs(\n codecs: ReadonlyArray<Codec<string>>,\n): ReadonlyArray<CodecDescriptor> {\n // Permissive paramsSchema for synthesized test descriptors: accepts any\n // shape (incl. undefined) and passes it through. Stubs do not encode\n // parameterization, so marking them `isParameterized: true` with this\n // schema lets the runtime integrity check tolerate columns that legitimately\n // carry typeParams (e.g. `sql/char@1` length=36) without re-introducing\n // the legacy \"non-parameterized + typeParams\" silent skip.\n // Permissive schema for synthesized test descriptors. `validate()` always\n // succeeds and discards input, narrowed to `void` to match the\n // `paramsSchema: StandardSchemaV1<void, void>` slot on the descriptor.\n // The factory ignores typeParams, so typing the validated output as `void`\n // is honest about what the stub does with the value.\n const acceptAnyParamsSchema = {\n '~standard': {\n version: 1 as const,\n vendor: 'sql-runtime/test-utils',\n validate: (_value: unknown) => ({ value: undefined }),\n },\n };\n const descriptors: CodecDescriptor[] = [];\n for (const instance of codecs) {\n const legacy = instance as {\n readonly traits?: readonly CodecTrait[];\n readonly targetTypes?: readonly string[];\n readonly meta?: CodecMeta;\n };\n descriptors.push({\n codecId: instance.id,\n traits: legacy.traits ?? [],\n targetTypes: legacy.targetTypes ?? [],\n paramsSchema: acceptAnyParamsSchema,\n isParameterized: true,\n factory: () => () => instance,\n ...(legacy.meta !== undefined ? { meta: legacy.meta } : {}),\n });\n }\n return descriptors;\n}\n\nexport function createTestAdapterDescriptor(\n adapter: StubAdapter,\n): SqlRuntimeAdapterDescriptor<'postgres'> {\n const descriptors = descriptorsFromCodecs(adapter.__codecs);\n return {\n kind: 'adapter' as const,\n rawCodecInferer: { inferCodec: () => 'pg/text' },\n id: 'test-adapter',\n version: '0.0.1',\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n codecs: () => descriptors,\n mutationDefaultGenerators: createTestMutationDefaultGenerators,\n create(_stack): SqlRuntimeAdapterInstance<'postgres'> {\n return Object.assign({ familyId: 'sql' as const, targetId: 'postgres' as const }, adapter);\n },\n };\n}\n\n/**\n * Creates a test target descriptor with empty static contributions.\n */\nexport function createTestTargetDescriptor(): SqlRuntimeTargetDescriptor<'postgres'> {\n return {\n kind: 'target' as const,\n id: 'postgres',\n version: '0.0.1',\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n codecs: () => [],\n create() {\n return { familyId: 'sql' as const, targetId: 'postgres' as const };\n },\n };\n}\n\n/**\n * Creates an ExecutionContext for testing. This helper DRYs up the common pattern of context creation in tests.\n *\n * Accepts a raw adapter and optional extension descriptors, wrapping the adapter in a descriptor internally for descriptor-first context creation.\n */\nexport function createTestContext<TContract extends Contract<SqlStorage>>(\n contract: TContract,\n adapter: StubAdapter,\n options?: {\n extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;\n },\n): ExecutionContext<TContract> {\n return createExecutionContext({\n contract,\n stack: {\n target: createTestTargetDescriptor(),\n adapter: createTestAdapterDescriptor(adapter),\n extensionPacks: options?.extensionPacks ?? [],\n },\n });\n}\n\nexport function createTestStackInstance(options?: {\n extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;\n driver?: RuntimeDriverDescriptor<\n 'sql',\n 'postgres',\n unknown,\n SqlRuntimeDriverInstance<'postgres'>\n >;\n}) {\n const stack = createSqlExecutionStack({\n target: createTestTargetDescriptor(),\n adapter: createTestAdapterDescriptor(createStubAdapter()),\n driver: options?.driver,\n extensionPacks: options?.extensionPacks ?? [],\n });\n\n return instantiateExecutionStack(stack);\n}\n\n/**\n * Stub-adapter type augments the public {@link Adapter} surface with a `__codecs` slot that exposes the test stub's runtime codec set to descriptor-shaping helpers (`createTestAdapterDescriptor`). Production adapters do not declare this slot — runtime codecs flow through the descriptor list from `SqlRuntimeAdapterDescriptor.codecs()` — so the augmentation is intentionally test-only.\n */\nexport type StubAdapter = Adapter<SelectAst, Contract<SqlStorage>, LoweredStatement> & {\n readonly __codecs: ReadonlyArray<Codec<string>>;\n};\n\n/**\n * Creates a stub adapter for testing. This helper DRYs up the common pattern of adapter creation in tests.\n *\n * The stub adapter includes simple codecs for common test types (pg/int4@1, pg/text@1, pg/timestamptz@1) to enable type inference in tests without requiring the postgres adapter package.\n */\nexport function createStubAdapter(): StubAdapter {\n // Stub codecs for codec IDs that test contracts may reference. The set must\n // be complete enough to satisfy `assertColumnCodecIntegrity` against any\n // emitted test contract; the encode/decode bodies are passthrough since\n // the stub adapter never executes against a real driver.\n // The encode/decode bodies pass through; widen TInput to a JSON-safe type\n // so `defineTestCodec` does not require explicit JSON round-trip helpers.\n const passthroughCodec = (typeId: string, targetType: string): Codec<string> =>\n defineTestCodec({\n typeId,\n targetTypes: [targetType],\n encode: (value: string | number | boolean | null) => value,\n decode: (wire: string | number | boolean | null) => wire,\n });\n const codecs: ReadonlyArray<Codec<string>> = [\n passthroughCodec('pg/bit@1', 'bit'),\n passthroughCodec('pg/bool@1', 'bool'),\n passthroughCodec('pg/bytea@1', 'bytea'),\n passthroughCodec('pg/float4@1', 'float4'),\n passthroughCodec('pg/float8@1', 'float8'),\n passthroughCodec('pg/int2@1', 'int2'),\n defineTestCodec({\n typeId: 'pg/int4@1',\n targetTypes: ['int4'],\n encode: (value: number) => value,\n decode: (wire: number) => wire,\n }),\n passthroughCodec('pg/int8@1', 'int8'),\n passthroughCodec('pg/interval@1', 'interval'),\n passthroughCodec('pg/json@1', 'json'),\n passthroughCodec('pg/jsonb@1', 'jsonb'),\n passthroughCodec('pg/numeric@1', 'numeric'),\n defineTestCodec({\n typeId: 'pg/text@1',\n targetTypes: ['text'],\n encode: (value: string) => value,\n decode: (wire: string) => wire,\n }),\n passthroughCodec('pg/time@1', 'time'),\n defineTestCodec({\n typeId: 'pg/timestamp@1',\n targetTypes: ['timestamp'],\n encode: (value: Date) => value,\n decode: (wire: Date) => wire,\n encodeJson: (value: Date) => value.toISOString(),\n decodeJson: (json) => {\n if (typeof json !== 'string') throw new Error('expected ISO date string');\n return new Date(json);\n },\n }),\n defineTestCodec({\n typeId: 'pg/timestamptz@1',\n targetTypes: ['timestamptz'],\n encode: (value: Date) => value,\n decode: (wire: Date) => wire,\n // Date is not assignable to JsonValue, so the JSON round-trip pair must be supplied explicitly.\n encodeJson: (value: Date) => value.toISOString(),\n decodeJson: (json) => {\n if (typeof json !== 'string') throw new Error('expected ISO date string');\n return new Date(json);\n },\n }),\n passthroughCodec('pg/timetz@1', 'timetz'),\n passthroughCodec('pg/varbit@1', 'varbit'),\n passthroughCodec('pg/uuid@1', 'uuid'),\n passthroughCodec('sql/char@1', 'char'),\n passthroughCodec('sql/varchar@1', 'varchar'),\n ];\n\n return {\n __codecs: codecs,\n profile: {\n id: 'stub-profile',\n target: 'postgres',\n capabilities: {},\n readMarker: async () => ({ kind: 'absent' as const }),\n },\n lower(ast: SelectAst, _ctx: { contract: Contract<SqlStorage>; params?: readonly unknown[] }) {\n const sqlText = JSON.stringify(ast);\n const refs = ast.collectParamRefs();\n const params = refs.map((ref) =>\n ref.kind === 'prepared-param-ref'\n ? ({ kind: 'bind' as const, name: ref.name } as const)\n : ({ kind: 'literal' as const, value: ref.value } as const),\n );\n return Object.freeze({ sql: sqlText, params });\n },\n };\n}\n\nexport function unboundNamespaceWithTables(\n tables: Record<string, StorageTableInput>,\n): ReturnType<typeof buildSqlNamespace> {\n return buildSqlNamespace({ id: UNBOUND_NAMESPACE_ID, entries: { table: tables } });\n}\n\nexport function emptySqlTestDomain() {\n return applicationDomainOf({ models: {} });\n}\n\nexport function createTestContract(\n contract: Partial<Omit<Contract<SqlStorage>, 'profileHash' | 'storage' | 'domain'>> & {\n storageHash?: string;\n profileHash?: string;\n models?: Record<string, ContractModelBase>;\n domain?: Contract<SqlStorage>['domain'];\n storage?: Partial<Omit<SqlStorageInput, 'storageHash'>>;\n },\n): Contract<SqlStorage> {\n const { execution, ...rest } = contract;\n const storageHashValue = coreHash(rest['storageHash'] ?? 'sha256:testcore');\n\n return {\n target: rest['target'] ?? 'postgres',\n targetFamily: rest['targetFamily'] ?? 'sql',\n storage: rest['storage']\n ? new SqlStorage({\n ...rest['storage'],\n storageHash: storageHashValue,\n namespaces: rest['storage'].namespaces ?? { __unbound__: SqlUnboundNamespace.instance },\n })\n : new SqlStorage({\n storageHash: storageHashValue,\n namespaces: { __unbound__: SqlUnboundNamespace.instance },\n }),\n domain: rest['domain'] ?? applicationDomainOf({ models: rest['models'] ?? {} }),\n roots: rest['roots'] ?? {},\n capabilities: rest['capabilities'] ?? {},\n extensionPacks: rest['extensionPacks'] ?? {},\n meta: rest['meta'] ?? {},\n ...(execution ? { execution } : {}),\n profileHash: profileHash(rest['profileHash'] ?? 'sha256:testprofile'),\n };\n}\n\nexport function stubAst(): AnyQueryAst {\n return SelectAstCtor.from(TableSource.named('stub'));\n}\n\n// Re-export generic utilities from test-utils\nexport {\n collectAsync,\n createDevDatabase,\n type DevDatabase,\n teardownTestDatabase,\n withClient,\n} from '@prisma-next/test-utils';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":["collectAsync","SelectAstCtor"],"sources":["../../test/test-codec.ts","../../test/utils.ts"],"sourcesContent":["/**\n * Test-only helper that constructs a SQL-family `Codec` instance from author-side encode/decode functions. Replaces the legacy public `mkCodec()` factory (deleted under TML-2357); tests that need a stub codec for behavioural assertions instantiate one through this helper rather than going through `descriptor.factory(...)`.\n *\n * The body is identical in spirit to the retired `mkCodec`: promise-lift sync author functions onto the framework-required `Promise<…>` boundary, default `encodeJson`/`decodeJson` to identity when `TInput` is JSON-safe, fail loudly otherwise.\n */\nimport type { JsonValue } from '@prisma-next/contract/types';\nimport type { CodecTrait } from '@prisma-next/framework-components/codec';\nimport type { Codec, SqlCodecCallContext } from '@prisma-next/sql-relational-core/ast';\n\ntype JsonRoundTripConfig<TInput> = [TInput] extends [JsonValue]\n ? {\n encodeJson?: (value: TInput) => JsonValue;\n decodeJson?: (json: JsonValue) => TInput;\n }\n : {\n encodeJson: (value: TInput) => JsonValue;\n decodeJson: (json: JsonValue) => TInput;\n };\n\nexport function defineTestCodec<\n Id extends string,\n const TTraits extends readonly CodecTrait[] = readonly [],\n TWire = unknown,\n TInput = unknown,\n>(\n config: {\n typeId: Id;\n targetTypes?: readonly string[];\n encode: (value: TInput, ctx: SqlCodecCallContext) => TWire | Promise<TWire>;\n decode: (wire: TWire, ctx: SqlCodecCallContext) => TInput | Promise<TInput>;\n traits?: TTraits;\n } & JsonRoundTripConfig<TInput>,\n): Codec<Id, TTraits, TWire, TInput> {\n const identity = (v: unknown) => v;\n const userEncode = config.encode;\n const userDecode = config.decode;\n const widenedConfig = config as {\n encodeJson?: (value: TInput) => JsonValue;\n decodeJson?: (json: JsonValue) => TInput;\n };\n return {\n id: config.typeId,\n encode: (value, ctx) => {\n try {\n return Promise.resolve(userEncode(value, ctx));\n } catch (error) {\n return Promise.reject(error);\n }\n },\n decode: (wire, ctx) => {\n try {\n return Promise.resolve(userDecode(wire, ctx));\n } catch (error) {\n return Promise.reject(error);\n }\n },\n encodeJson: (widenedConfig.encodeJson ?? identity) as (value: TInput) => JsonValue,\n decodeJson: (widenedConfig.decodeJson ?? identity) as (json: JsonValue) => TInput,\n } as Codec<Id, TTraits, TWire, TInput>;\n}\n","import {\n type Contract,\n type ContractModelBase,\n coreHash,\n profileHash,\n} from '@prisma-next/contract/types';\nimport type {\n CodecDescriptor,\n CodecMeta,\n CodecTrait,\n} from '@prisma-next/framework-components/codec';\nimport { APP_SPACE_ID } from '@prisma-next/framework-components/control';\nimport {\n instantiateExecutionStack,\n type RuntimeDriverDescriptor,\n} from '@prisma-next/framework-components/execution';\nimport { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type { ResultType } from '@prisma-next/framework-components/runtime';\nimport { runtimeError } from '@prisma-next/framework-components/runtime';\nimport { canonicalizeJson } from '@prisma-next/framework-components/utils';\nimport { builtinGeneratorIds } from '@prisma-next/ids';\nimport { generateId } from '@prisma-next/ids/runtime';\nimport {\n buildSqlNamespace,\n SqlStorage,\n type SqlStorageInput,\n SqlUnboundNamespace,\n type StorageTableInput,\n} from '@prisma-next/sql-contract/types';\nimport type {\n Adapter,\n AnyQueryAst,\n Codec,\n ContractCodecRegistry,\n LoweredStatement,\n SelectAst,\n} from '@prisma-next/sql-relational-core/ast';\nimport { SelectAst as SelectAstCtor, TableSource } from '@prisma-next/sql-relational-core/ast';\nimport type { SqlExecutionPlan, SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';\nimport { applicationDomainOf, collectAsync, drainAsyncIterable } from '@prisma-next/test-utils';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { Client } from 'pg';\nimport { createExecutionContext, createSqlExecutionStack } from '../src/exports';\nimport type {\n ExecutionContext,\n SqlRuntimeAdapterDescriptor,\n SqlRuntimeAdapterInstance,\n SqlRuntimeDriverInstance,\n SqlRuntimeExtensionDescriptor,\n SqlRuntimeTargetDescriptor,\n} from '../src/sql-context';\nimport { type Runtime, type RuntimeOptions, SqlRuntimeBase } from '../src/sql-runtime';\nimport { defineTestCodec } from './test-codec';\n\nfunction createTestMutationDefaultGenerators() {\n return builtinGeneratorIds.map((id) => ({\n id,\n generate: (params?: Record<string, unknown>) => generateId(params ? { id, params } : { id }),\n stability: 'field' as const,\n }));\n}\n\nclass TestSqlRuntime extends SqlRuntimeBase {}\n\ntype CreateTestRuntimeOptions<TContract extends Contract<SqlStorage>> = Omit<\n RuntimeOptions<TContract>,\n 'adapter'\n> & {\n readonly stackInstance: { readonly adapter: RuntimeOptions<TContract>['adapter'] };\n};\n\n/**\n * Test-only concrete runtime. Unpacks `stackInstance.adapter` and forwards\n * the rest into `TestSqlRuntime`, a trivial concrete leaf of the abstract\n * `SqlRuntimeBase`.\n */\nexport function createTestRuntime<TContract extends Contract<SqlStorage>>(\n options: CreateTestRuntimeOptions<TContract>,\n): Runtime {\n const { stackInstance, context, driver, verifyMarker, middleware, mode, log } = options;\n return new TestSqlRuntime({\n context,\n adapter: stackInstance.adapter,\n driver,\n ...ifDefined('verifyMarker', verifyMarker),\n ...ifDefined('middleware', middleware),\n ...ifDefined('mode', mode),\n ...ifDefined('log', log),\n });\n}\n\n/**\n * Executes a plan and collects all results into an array. This helper DRYs up the common pattern of executing plans in tests. The return type is inferred from the plan's type parameter.\n */\nexport async function executePlanAndCollect<\n P extends SqlExecutionPlan<ResultType<P>> | SqlQueryPlan<ResultType<P>>,\n>(runtime: Runtime, plan: P): Promise<ResultType<P>[]> {\n type Row = ResultType<P>;\n return collectAsync<Row>(runtime.execute<Row>(plan));\n}\n\n/**\n * Drains a plan execution, consuming all results without collecting them. Useful for testing side effects without memory overhead.\n */\nexport async function drainPlanExecution(\n runtime: Runtime,\n plan: SqlExecutionPlan | SqlQueryPlan<unknown>,\n): Promise<void> {\n return drainAsyncIterable(runtime.execute(plan));\n}\n\n/**\n * Sets up database schema and data, then writes the contract marker. This helper DRYs up the common pattern of database setup in tests.\n *\n * Callers must supply `bootstrapMarkerTables` (typically\n * `bootstrapPostgresSignMarkerTables` from integration `postgres-bootstrap.ts`)\n * so this package does not depend on the postgres adapter/target packs.\n */\nexport async function setupTestDatabase(\n client: Client,\n contract: Contract<SqlStorage>,\n setupFn: (client: Client) => Promise<void>,\n bootstrapMarkerTables: (client: Client) => Promise<void>,\n): Promise<void> {\n await client.query('drop schema if exists prisma_contract cascade');\n await client.query('create schema if not exists public');\n\n await setupFn(client);\n\n await bootstrapMarkerTables(client);\n await writeTestContractMarker(client, contract);\n}\n\nexport interface SeedMarkerInput {\n /** Logical space for the marker row; defaults to {@link APP_SPACE_ID}. */\n readonly space?: string;\n readonly storageHash: string;\n readonly profileHash: string;\n readonly contractJson?: unknown;\n readonly canonicalVersion?: number;\n readonly invariants?: readonly string[];\n}\n\n/**\n * Seeds a contract marker row directly via raw SQL. Test-only: the production\n * write path goes through the control adapter SPI (`initMarker`/`updateMarker`),\n * which needs a `ControlDriverInstance`; these fixtures hold a raw `pg.Client`,\n * so they perform a minimal `INSERT` over the columns the runtime reads back.\n */\nexport async function seedTestMarker(client: Client, input: SeedMarkerInput): Promise<void> {\n await client.query(\n `insert into prisma_contract.marker\n (space, core_hash, profile_hash, contract_json, canonical_version, invariants, updated_at)\n values ($1, $2, $3, $4::jsonb, $5, $6::text[], now())`,\n [\n input.space ?? APP_SPACE_ID,\n input.storageHash,\n input.profileHash,\n input.contractJson === undefined ? null : JSON.stringify(input.contractJson),\n input.canonicalVersion ?? null,\n input.invariants ?? [],\n ],\n );\n}\n\n/**\n * Seeds the app-space marker for a contract. Thin wrapper over\n * {@link seedTestMarker} for the common \"write the marker for this contract\" case.\n */\nexport async function writeTestContractMarker(\n client: Client,\n contract: Contract<SqlStorage>,\n): Promise<void> {\n await seedTestMarker(client, {\n storageHash: contract.storage.storageHash,\n profileHash: contract.profileHash,\n contractJson: contract,\n canonicalVersion: 1,\n });\n}\n\n/**\n * Creates a test adapter descriptor from a raw adapter. Wraps the adapter in an SqlRuntimeAdapterDescriptor with static contributions derived from the adapter's codec registry.\n */\n/**\n * Build a {@link ContractCodecRegistry} from a codec array for tests that exercise `encodeParam(s)` / `decodeRow` in isolation. The production runtime builds `ContractCodecRegistry` from contract walk + descriptor list and never goes through this helper; tests use it to wire a hand-built codec set into the surface those functions consume in production.\n */\nexport function buildTestContractCodecs(\n codecs: ReadonlyArray<Codec<string>>,\n): ContractCodecRegistry {\n const byId = new Map<string, Codec<string>>();\n for (const codec of codecs) {\n byId.set(codec.id, codec);\n }\n // Canonical-key cache: production `forCodecRef` memoizes per `(codecId, canonicalize(typeParams))`. Tests resolve by codecId, but key the cache on the canonical pair so callers passing distinct typeParams get distinct (still codec-id-templated) entries — and so this helper cannot silently coalesce them.\n const byCanonicalKey = new Map<string, Codec<string>>();\n return {\n forColumn: () => undefined,\n forCodecRef: (ref) => {\n const canonicalKey = canonicalizeJson({\n codecId: ref.codecId,\n ...(ref.typeParams !== undefined ? { typeParams: ref.typeParams } : {}),\n });\n const cached = byCanonicalKey.get(canonicalKey);\n if (cached) return cached;\n const template = byId.get(ref.codecId);\n if (!template) {\n throw runtimeError(\n 'RUNTIME.CODEC_DESCRIPTOR_MISSING',\n `Test ContractCodecRegistry has no codec for codecId '${ref.codecId}'.`,\n {\n codecId: ref.codecId,\n ...(ref.typeParams !== undefined ? { typeParams: ref.typeParams } : {}),\n },\n );\n }\n byCanonicalKey.set(canonicalKey, template);\n return template;\n },\n };\n}\n\n/**\n * Synthesize `CodecDescriptor`s from a codec array of non-parameterized codec instances. Test-only: the production synthesis bridge was retired under TML-2357. Lets the existing `createTestAdapterDescriptor` pattern keep wrapping a stub `Adapter` (whose `__codecs` slot still exposes the codec set) into the descriptor-list shape that `SqlStaticContributions.codecs:` now expects. The `Codec` instances carry\n * `traits`/`targetTypes`/`meta` via the SQL family extension; the structural narrow reads those fields directly.\n */\nexport function descriptorsFromCodecs(\n codecs: ReadonlyArray<Codec<string>>,\n): ReadonlyArray<CodecDescriptor> {\n // Permissive paramsSchema for synthesized test descriptors: accepts any\n // shape (incl. undefined) and passes it through. Stubs do not encode\n // parameterization, so marking them `isParameterized: true` with this\n // schema lets the runtime integrity check tolerate columns that legitimately\n // carry typeParams (e.g. `sql/char@1` length=36) without re-introducing\n // the legacy \"non-parameterized + typeParams\" silent skip.\n // Permissive schema for synthesized test descriptors. `validate()` always\n // succeeds and discards input, narrowed to `void` to match the\n // `paramsSchema: StandardSchemaV1<void, void>` slot on the descriptor.\n // The factory ignores typeParams, so typing the validated output as `void`\n // is honest about what the stub does with the value.\n const acceptAnyParamsSchema = {\n '~standard': {\n version: 1 as const,\n vendor: 'sql-runtime/test-utils',\n validate: (_value: unknown) => ({ value: undefined }),\n },\n };\n const descriptors: CodecDescriptor[] = [];\n for (const instance of codecs) {\n const legacy = instance as {\n readonly traits?: readonly CodecTrait[];\n readonly targetTypes?: readonly string[];\n readonly meta?: CodecMeta;\n };\n descriptors.push({\n codecId: instance.id,\n traits: legacy.traits ?? [],\n targetTypes: legacy.targetTypes ?? [],\n paramsSchema: acceptAnyParamsSchema,\n isParameterized: true,\n factory: () => () => instance,\n ...(legacy.meta !== undefined ? { meta: legacy.meta } : {}),\n });\n }\n return descriptors;\n}\n\nexport function createTestAdapterDescriptor(\n adapter: StubAdapter,\n): SqlRuntimeAdapterDescriptor<'postgres'> {\n const descriptors = descriptorsFromCodecs(adapter.__codecs);\n return {\n kind: 'adapter' as const,\n rawCodecInferer: { inferCodec: () => 'pg/text' },\n id: 'test-adapter',\n version: '0.0.1',\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n codecs: () => descriptors,\n mutationDefaultGenerators: createTestMutationDefaultGenerators,\n create(_stack): SqlRuntimeAdapterInstance<'postgres'> {\n return Object.assign({ familyId: 'sql' as const, targetId: 'postgres' as const }, adapter);\n },\n };\n}\n\n/**\n * Creates a test target descriptor with empty static contributions.\n */\nexport function createTestTargetDescriptor(): SqlRuntimeTargetDescriptor<'postgres'> {\n return {\n kind: 'target' as const,\n id: 'postgres',\n version: '0.0.1',\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n codecs: () => [],\n create() {\n return { familyId: 'sql' as const, targetId: 'postgres' as const };\n },\n };\n}\n\n/**\n * Creates an ExecutionContext for testing. This helper DRYs up the common pattern of context creation in tests.\n *\n * Accepts a raw adapter and optional extension descriptors, wrapping the adapter in a descriptor internally for descriptor-first context creation.\n */\nexport function createTestContext<TContract extends Contract<SqlStorage>>(\n contract: TContract,\n adapter: StubAdapter,\n options?: {\n extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;\n },\n): ExecutionContext<TContract> {\n return createExecutionContext({\n contract,\n stack: {\n target: createTestTargetDescriptor(),\n adapter: createTestAdapterDescriptor(adapter),\n extensionPacks: options?.extensionPacks ?? [],\n },\n });\n}\n\nexport function createTestStackInstance(options?: {\n extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;\n driver?: RuntimeDriverDescriptor<\n 'sql',\n 'postgres',\n unknown,\n SqlRuntimeDriverInstance<'postgres'>\n >;\n}) {\n const stack = createSqlExecutionStack({\n target: createTestTargetDescriptor(),\n adapter: createTestAdapterDescriptor(createStubAdapter()),\n driver: options?.driver,\n extensionPacks: options?.extensionPacks ?? [],\n });\n\n return instantiateExecutionStack(stack);\n}\n\n/**\n * Stub-adapter type augments the public {@link Adapter} surface with a `__codecs` slot that exposes the test stub's runtime codec set to descriptor-shaping helpers (`createTestAdapterDescriptor`). Production adapters do not declare this slot — runtime codecs flow through the descriptor list from `SqlRuntimeAdapterDescriptor.codecs()` — so the augmentation is intentionally test-only.\n */\nexport type StubAdapter = Adapter<SelectAst, Contract<SqlStorage>, LoweredStatement> & {\n readonly __codecs: ReadonlyArray<Codec<string>>;\n};\n\n/**\n * Creates a stub adapter for testing. This helper DRYs up the common pattern of adapter creation in tests.\n *\n * The stub adapter includes simple codecs for common test types (pg/int4@1, pg/text@1, pg/timestamptz@1) to enable type inference in tests without requiring the postgres adapter package.\n */\nexport function createStubAdapter(): StubAdapter {\n // Stub codecs for codec IDs that test contracts may reference. The set must\n // be complete enough to satisfy `assertColumnCodecIntegrity` against any\n // emitted test contract; the encode/decode bodies are passthrough since\n // the stub adapter never executes against a real driver.\n // The encode/decode bodies pass through; widen TInput to a JSON-safe type\n // so `defineTestCodec` does not require explicit JSON round-trip helpers.\n const passthroughCodec = (typeId: string, targetType: string): Codec<string> =>\n defineTestCodec({\n typeId,\n targetTypes: [targetType],\n encode: (value: string | number | boolean | null) => value,\n decode: (wire: string | number | boolean | null) => wire,\n });\n const codecs: ReadonlyArray<Codec<string>> = [\n passthroughCodec('pg/bit@1', 'bit'),\n passthroughCodec('pg/bool@1', 'bool'),\n passthroughCodec('pg/bytea@1', 'bytea'),\n passthroughCodec('pg/float4@1', 'float4'),\n passthroughCodec('pg/float8@1', 'float8'),\n passthroughCodec('pg/int2@1', 'int2'),\n defineTestCodec({\n typeId: 'pg/int4@1',\n targetTypes: ['int4'],\n encode: (value: number) => value,\n decode: (wire: number) => wire,\n }),\n passthroughCodec('pg/int8@1', 'int8'),\n passthroughCodec('pg/interval@1', 'interval'),\n passthroughCodec('pg/json@1', 'json'),\n passthroughCodec('pg/jsonb@1', 'jsonb'),\n passthroughCodec('pg/numeric@1', 'numeric'),\n defineTestCodec({\n typeId: 'pg/text@1',\n targetTypes: ['text'],\n encode: (value: string) => value,\n decode: (wire: string) => wire,\n }),\n passthroughCodec('pg/time@1', 'time'),\n defineTestCodec({\n typeId: 'pg/timestamp@1',\n targetTypes: ['timestamp'],\n encode: (value: Date) => value,\n decode: (wire: Date) => wire,\n encodeJson: (value: Date) => value.toISOString(),\n decodeJson: (json) => {\n if (typeof json !== 'string') throw new Error('expected ISO date string');\n return new Date(json);\n },\n }),\n defineTestCodec({\n typeId: 'pg/timestamptz@1',\n targetTypes: ['timestamptz'],\n encode: (value: Date) => value,\n decode: (wire: Date) => wire,\n // Date is not assignable to JsonValue, so the JSON round-trip pair must be supplied explicitly.\n encodeJson: (value: Date) => value.toISOString(),\n decodeJson: (json) => {\n if (typeof json !== 'string') throw new Error('expected ISO date string');\n return new Date(json);\n },\n }),\n passthroughCodec('pg/timetz@1', 'timetz'),\n passthroughCodec('pg/varbit@1', 'varbit'),\n passthroughCodec('pg/uuid@1', 'uuid'),\n passthroughCodec('sql/char@1', 'char'),\n passthroughCodec('sql/varchar@1', 'varchar'),\n ];\n\n return {\n __codecs: codecs,\n profile: {\n id: 'stub-profile',\n target: 'postgres',\n capabilities: {},\n readMarker: async () => ({ kind: 'absent' as const }),\n },\n lower(ast: SelectAst, _ctx: { contract: Contract<SqlStorage>; params?: readonly unknown[] }) {\n const sqlText = JSON.stringify(ast);\n const refs = ast.collectParamRefs();\n const params = refs.map((ref) =>\n ref.kind === 'prepared-param-ref'\n ? ({ kind: 'bind' as const, name: ref.name } as const)\n : ({ kind: 'literal' as const, value: ref.value } as const),\n );\n return Object.freeze({ sql: sqlText, params });\n },\n };\n}\n\nexport function unboundNamespaceWithTables(\n tables: Record<string, StorageTableInput>,\n): ReturnType<typeof buildSqlNamespace> {\n return buildSqlNamespace({ id: UNBOUND_NAMESPACE_ID, entries: { table: tables } });\n}\n\nexport function emptySqlTestDomain() {\n return applicationDomainOf({ models: {} });\n}\n\nexport function createTestContract(\n contract: Partial<Omit<Contract<SqlStorage>, 'profileHash' | 'storage' | 'domain'>> & {\n storageHash?: string;\n profileHash?: string;\n models?: Record<string, ContractModelBase>;\n domain?: Contract<SqlStorage>['domain'];\n storage?: Partial<Omit<SqlStorageInput, 'storageHash'>>;\n },\n): Contract<SqlStorage> {\n const { execution, ...rest } = contract;\n const storageHashValue = coreHash(rest['storageHash'] ?? 'sha256:testcore');\n\n return {\n target: rest['target'] ?? 'postgres',\n targetFamily: rest['targetFamily'] ?? 'sql',\n storage: rest['storage']\n ? new SqlStorage({\n ...rest['storage'],\n storageHash: storageHashValue,\n namespaces: rest['storage'].namespaces ?? { __unbound__: SqlUnboundNamespace.instance },\n })\n : new SqlStorage({\n storageHash: storageHashValue,\n namespaces: { __unbound__: SqlUnboundNamespace.instance },\n }),\n domain: rest['domain'] ?? applicationDomainOf({ models: rest['models'] ?? {} }),\n roots: rest['roots'] ?? {},\n capabilities: rest['capabilities'] ?? {},\n extensionPacks: rest['extensionPacks'] ?? {},\n meta: rest['meta'] ?? {},\n ...(execution ? { execution } : {}),\n profileHash: profileHash(rest['profileHash'] ?? 'sha256:testprofile'),\n };\n}\n\nexport function stubAst(): AnyQueryAst {\n return SelectAstCtor.from(TableSource.named('stub'));\n}\n\n// Re-export generic utilities from test-utils\nexport {\n collectAsync,\n createDevDatabase,\n type DevDatabase,\n teardownTestDatabase,\n withClient,\n} from '@prisma-next/test-utils';\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/sql-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0-dev.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"description": "SQL runtime implementation for Prisma Next",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@prisma-next/contract": "0.
|
|
10
|
-
"@prisma-next/utils": "0.
|
|
11
|
-
"@prisma-next/framework-components": "0.
|
|
12
|
-
"@prisma-next/ids": "0.
|
|
13
|
-
"@prisma-next/operations": "0.
|
|
14
|
-
"@prisma-next/sql-contract": "0.
|
|
15
|
-
"@prisma-next/sql-operations": "0.
|
|
16
|
-
"@prisma-next/sql-relational-core": "0.
|
|
9
|
+
"@prisma-next/contract": "0.14.0-dev.2",
|
|
10
|
+
"@prisma-next/utils": "0.14.0-dev.2",
|
|
11
|
+
"@prisma-next/framework-components": "0.14.0-dev.2",
|
|
12
|
+
"@prisma-next/ids": "0.14.0-dev.2",
|
|
13
|
+
"@prisma-next/operations": "0.14.0-dev.2",
|
|
14
|
+
"@prisma-next/sql-contract": "0.14.0-dev.2",
|
|
15
|
+
"@prisma-next/sql-operations": "0.14.0-dev.2",
|
|
16
|
+
"@prisma-next/sql-relational-core": "0.14.0-dev.2",
|
|
17
17
|
"arktype": "^2.2.0"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@prisma-next/test-utils": "0.
|
|
21
|
-
"@prisma-next/tsconfig": "0.
|
|
20
|
+
"@prisma-next/test-utils": "0.14.0-dev.2",
|
|
21
|
+
"@prisma-next/tsconfig": "0.14.0-dev.2",
|
|
22
22
|
"@types/pg": "8.20.0",
|
|
23
23
|
"pg": "8.21.0",
|
|
24
|
-
"@prisma-next/tsdown": "0.
|
|
24
|
+
"@prisma-next/tsdown": "0.14.0-dev.2",
|
|
25
25
|
"tsdown": "0.22.1",
|
|
26
26
|
"typescript": "5.9.3",
|
|
27
27
|
"vitest": "4.1.8"
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { CodecRef } from '@prisma-next/framework-components/codec';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
materializeCodec,
|
|
4
|
+
resolveCodecDescriptorOrThrow,
|
|
5
|
+
} from '@prisma-next/framework-components/codec';
|
|
3
6
|
import { canonicalizeJson } from '@prisma-next/framework-components/utils';
|
|
4
7
|
import type { Codec, SqlCodecInstanceContext } from '@prisma-next/sql-relational-core/ast';
|
|
5
8
|
import type { CodecDescriptorRegistry } from '@prisma-next/sql-relational-core/query-lane-context';
|
|
@@ -37,63 +40,16 @@ export function createAstCodecResolver(
|
|
|
37
40
|
const cached = cache.get(key);
|
|
38
41
|
if (cached) return cached;
|
|
39
42
|
|
|
40
|
-
const descriptor =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
`No codec descriptor registered for codecId '${ref.codecId}'.`,
|
|
45
|
-
{ codecId: ref.codecId },
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Parameterized codecs whose paramsSchema accepts `{}` (every field
|
|
50
|
-
// optional, e.g. `pg/timestamptz@1` precision) tolerate refs that omit
|
|
51
|
-
// `typeParams` entirely. Normalize `undefined` to `{}` at the validation
|
|
52
|
-
// boundary; the integrity check upstream already rejected refs whose
|
|
53
|
-
// schemas reject the empty object.
|
|
54
|
-
const validated = validateTypeParams(
|
|
55
|
-
descriptor.paramsSchema,
|
|
56
|
-
descriptor.isParameterized && ref.typeParams === undefined
|
|
57
|
-
? { ...ref, typeParams: {} }
|
|
58
|
-
: ref,
|
|
43
|
+
const descriptor = resolveCodecDescriptorOrThrow(
|
|
44
|
+
(id) => descriptors.descriptorFor(id),
|
|
45
|
+
ref,
|
|
46
|
+
'RUNTIME.CODEC_DESCRIPTOR_MISSING',
|
|
59
47
|
);
|
|
60
48
|
const ctx = instanceContextFor(ref);
|
|
61
|
-
|
|
62
|
-
const codec = (
|
|
63
|
-
descriptor.factory as (params: unknown) => (ctx: SqlCodecInstanceContext) => Codec
|
|
64
|
-
)(validated)(ctx);
|
|
49
|
+
const codec = materializeCodec(descriptor, ref, ctx);
|
|
65
50
|
|
|
66
51
|
cache.set(key, codec);
|
|
67
52
|
return codec;
|
|
68
53
|
},
|
|
69
54
|
};
|
|
70
55
|
}
|
|
71
|
-
|
|
72
|
-
function validateTypeParams(
|
|
73
|
-
paramsSchema: { '~standard': { validate: (input: unknown) => unknown } },
|
|
74
|
-
ref: CodecRef,
|
|
75
|
-
): unknown {
|
|
76
|
-
const result = paramsSchema['~standard'].validate(ref.typeParams) as
|
|
77
|
-
| { value: unknown }
|
|
78
|
-
| { issues: ReadonlyArray<{ message: string }> }
|
|
79
|
-
| Promise<unknown>;
|
|
80
|
-
|
|
81
|
-
if (result instanceof Promise) {
|
|
82
|
-
throw runtimeError(
|
|
83
|
-
'RUNTIME.TYPE_PARAMS_INVALID',
|
|
84
|
-
`paramsSchema for codec '${ref.codecId}' returned a Promise; runtime validation requires a synchronous Standard Schema validator.`,
|
|
85
|
-
{ codecId: ref.codecId, typeParams: ref.typeParams },
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if ('issues' in result && result.issues) {
|
|
90
|
-
const messages = result.issues.map((issue) => issue.message).join('; ');
|
|
91
|
-
throw runtimeError(
|
|
92
|
-
'RUNTIME.TYPE_PARAMS_INVALID',
|
|
93
|
-
`Invalid typeParams for codec '${ref.codecId}': ${messages}`,
|
|
94
|
-
{ codecId: ref.codecId, typeParams: ref.typeParams },
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return (result as { value: unknown }).value;
|
|
99
|
-
}
|
package/src/codecs/validation.ts
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
import type { Contract } from '@prisma-next/contract/types';
|
|
2
2
|
import { runtimeError } from '@prisma-next/framework-components/runtime';
|
|
3
|
-
import type { SqlStorage
|
|
3
|
+
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
4
4
|
import type { CodecDescriptorRegistry } from '@prisma-next/sql-relational-core/query-lane-context';
|
|
5
5
|
|
|
6
|
-
// Framework `Namespace.tables` is widened to `Record<string, object>` so
|
|
7
|
-
// emitted `contract.d.ts` table literals (which omit the optional `kind`
|
|
8
|
-
// discriminator) satisfy it structurally. At runtime, every `SqlStorage`
|
|
9
|
-
// namespace holds `StorageTable` instances — the constructor enforces it.
|
|
10
|
-
// Narrowing per-iteration via a single-step cast (not `as unknown as`)
|
|
11
|
-
// keeps Pattern C walks readable without weakening the substrate type.
|
|
12
|
-
type SqlNamespaceTables = Readonly<Record<string, StorageTable>>;
|
|
13
|
-
|
|
14
6
|
export function extractCodecIds(contract: Contract<SqlStorage>): Set<string> {
|
|
15
7
|
const codecIds = new Set<string>();
|
|
16
8
|
|
|
17
9
|
for (const ns of Object.values(contract.storage.namespaces)) {
|
|
18
|
-
for (const table of Object.values(ns.entries.table
|
|
10
|
+
for (const table of Object.values(ns.entries.table ?? {})) {
|
|
19
11
|
for (const column of Object.values(table.columns)) {
|
|
20
12
|
const codecId = column.codecId;
|
|
21
13
|
codecIds.add(codecId);
|
|
@@ -26,37 +18,34 @@ export function extractCodecIds(contract: Contract<SqlStorage>): Set<string> {
|
|
|
26
18
|
return codecIds;
|
|
27
19
|
}
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
type ColumnCodecRef = {
|
|
22
|
+
readonly namespaceId: string;
|
|
23
|
+
readonly table: string;
|
|
24
|
+
readonly column: string;
|
|
25
|
+
readonly codecId: string;
|
|
26
|
+
};
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
function extractColumnCodecRefs(contract: Contract<SqlStorage>): ColumnCodecRef[] {
|
|
29
|
+
const refs: ColumnCodecRef[] = [];
|
|
30
|
+
|
|
31
|
+
for (const [namespaceId, ns] of Object.entries(contract.storage.namespaces)) {
|
|
32
|
+
for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) {
|
|
34
33
|
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
35
|
-
|
|
36
|
-
const key = `${tableName}.${columnName}`;
|
|
37
|
-
codecIds.set(key, codecId);
|
|
34
|
+
refs.push({ namespaceId, table: tableName, column: columnName, codecId: column.codecId });
|
|
38
35
|
}
|
|
39
36
|
}
|
|
40
37
|
}
|
|
41
38
|
|
|
42
|
-
return
|
|
39
|
+
return refs;
|
|
43
40
|
}
|
|
44
41
|
|
|
45
42
|
export function validateContractCodecMappings(
|
|
46
43
|
registry: CodecDescriptorRegistry,
|
|
47
44
|
contract: Contract<SqlStorage>,
|
|
48
45
|
): void {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
for (const [key, codecId] of codecIds.entries()) {
|
|
53
|
-
if (registry.descriptorFor(codecId) === undefined) {
|
|
54
|
-
const parts = key.split('.');
|
|
55
|
-
const table = parts[0] ?? '';
|
|
56
|
-
const column = parts[1] ?? '';
|
|
57
|
-
invalidCodecs.push({ table, column, codecId });
|
|
58
|
-
}
|
|
59
|
-
}
|
|
46
|
+
const invalidCodecs = extractColumnCodecRefs(contract).filter(
|
|
47
|
+
(ref) => registry.descriptorFor(ref.codecId) === undefined,
|
|
48
|
+
);
|
|
60
49
|
|
|
61
50
|
if (invalidCodecs.length > 0) {
|
|
62
51
|
const details: Record<string, unknown> = {
|
|
@@ -66,7 +55,7 @@ export function validateContractCodecMappings(
|
|
|
66
55
|
|
|
67
56
|
throw runtimeError(
|
|
68
57
|
'RUNTIME.CODEC_MISSING',
|
|
69
|
-
`Missing codec implementations for column codecIds: ${invalidCodecs.map((c) => `${c.table}.${c.column} (${c.codecId})`).join(', ')}`,
|
|
58
|
+
`Missing codec implementations for column codecIds: ${invalidCodecs.map((c) => `${c.namespaceId}.${c.table}.${c.column} (${c.codecId})`).join(', ')}`,
|
|
70
59
|
details,
|
|
71
60
|
);
|
|
72
61
|
}
|
package/src/exports/index.ts
CHANGED
|
@@ -16,6 +16,10 @@ export { budgets } from '../middleware/budgets';
|
|
|
16
16
|
export type { LintsOptions } from '../middleware/lints';
|
|
17
17
|
export { lints } from '../middleware/lints';
|
|
18
18
|
export type { SqlMiddleware, SqlMiddlewareContext } from '../middleware/sql-middleware';
|
|
19
|
+
export {
|
|
20
|
+
PreparedStatementImpl,
|
|
21
|
+
type PreparedStatementInternals,
|
|
22
|
+
} from '../prepared/prepared-statement';
|
|
19
23
|
export type {
|
|
20
24
|
BindSiteParams,
|
|
21
25
|
Declaration,
|
|
@@ -54,11 +58,11 @@ export {
|
|
|
54
58
|
createSqlExecutionStack,
|
|
55
59
|
} from '../sql-context';
|
|
56
60
|
export type {
|
|
57
|
-
|
|
61
|
+
ConnectionProvider,
|
|
58
62
|
Runtime,
|
|
59
63
|
RuntimeConnection,
|
|
60
64
|
RuntimeQueryable,
|
|
61
65
|
RuntimeTransaction,
|
|
62
66
|
TransactionContext,
|
|
63
67
|
} from '../sql-runtime';
|
|
64
|
-
export {
|
|
68
|
+
export { SqlRuntimeBase, withTransaction } from '../sql-runtime';
|
|
@@ -25,12 +25,15 @@ function hasAggregateWithoutGroupBy(ast: SelectAst): boolean {
|
|
|
25
25
|
return ast.projection.some((item) => item.expr.kind === 'aggregate');
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
function primaryTableFromAst(ast: SelectAst): string {
|
|
28
|
+
function primaryTableFromAst(ast: SelectAst): string | undefined {
|
|
29
|
+
if (ast.from === undefined) return undefined;
|
|
29
30
|
switch (ast.from.kind) {
|
|
30
31
|
case 'table-source':
|
|
31
32
|
return ast.from.name;
|
|
32
33
|
case 'derived-table-source':
|
|
33
34
|
return ast.from.alias;
|
|
35
|
+
case 'function-source':
|
|
36
|
+
return ast.from.fn;
|
|
34
37
|
// v8 ignore next 4
|
|
35
38
|
default:
|
|
36
39
|
throw new Error(
|
|
@@ -49,7 +52,9 @@ function estimateRowsFromAst(
|
|
|
49
52
|
return 1;
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
const
|
|
55
|
+
const primaryTable = primaryTableFromAst(ast);
|
|
56
|
+
const tableEstimate =
|
|
57
|
+
(primaryTable !== undefined ? tableRows[primaryTable] : undefined) ?? defaultTableRows;
|
|
53
58
|
|
|
54
59
|
if (typeof ast.limit === 'number') {
|
|
55
60
|
return Math.min(ast.limit, tableEstimate);
|
package/src/middleware/lints.ts
CHANGED
|
@@ -34,6 +34,8 @@ function getFromSourceTableDetail(source: AnyFromSource): string | undefined {
|
|
|
34
34
|
return source.name;
|
|
35
35
|
case 'derived-table-source':
|
|
36
36
|
return source.alias;
|
|
37
|
+
case 'function-source':
|
|
38
|
+
return source.fn;
|
|
37
39
|
// v8 ignore next 4
|
|
38
40
|
default:
|
|
39
41
|
throw new Error(
|
|
@@ -72,7 +74,7 @@ function evaluateAstLints(ast: AnyQueryAst): LintFinding[] {
|
|
|
72
74
|
|
|
73
75
|
case 'select':
|
|
74
76
|
if (ast.limit === undefined) {
|
|
75
|
-
const table = getFromSourceTableDetail(ast.from);
|
|
77
|
+
const table = ast.from !== undefined ? getFromSourceTableDetail(ast.from) : undefined;
|
|
76
78
|
findings.push({
|
|
77
79
|
code: 'LINT.NO_LIMIT',
|
|
78
80
|
severity: 'warn',
|
package/src/runtime-spi.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface MarkerReader {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* SQL family adapter SPI consumed by `
|
|
12
|
+
* SQL family adapter SPI consumed by `SqlRuntimeBase`. Encapsulates the
|
|
13
13
|
* runtime contract, marker reader, and plan validation logic so the
|
|
14
14
|
* runtime can be unit-tested without a concrete SQL adapter profile.
|
|
15
15
|
*
|
package/src/sql-context.ts
CHANGED
|
@@ -27,11 +27,7 @@ import {
|
|
|
27
27
|
} from '@prisma-next/framework-components/execution';
|
|
28
28
|
import { runtimeError } from '@prisma-next/framework-components/runtime';
|
|
29
29
|
import { canonicalizeJson } from '@prisma-next/framework-components/utils';
|
|
30
|
-
import {
|
|
31
|
-
isPostgresEnumStorageEntry,
|
|
32
|
-
type SqlStorage,
|
|
33
|
-
type StorageTypeInstance,
|
|
34
|
-
} from '@prisma-next/sql-contract/types';
|
|
30
|
+
import type { SqlStorage, StorageTypeInstance } from '@prisma-next/sql-contract/types';
|
|
35
31
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
36
32
|
|
|
37
33
|
function documentScopedCodecTypes(
|
|
@@ -241,30 +237,6 @@ export function assertExecutionStackContractRequirements(
|
|
|
241
237
|
}
|
|
242
238
|
}
|
|
243
239
|
|
|
244
|
-
/**
|
|
245
|
-
* Resolves codec id + typeParams for a `SqlStorage.types` entry that
|
|
246
|
-
* represents an enum. The canonical contract path always pipes raw JSON
|
|
247
|
-
* through the `SqlStorage` constructor, which rejects raw
|
|
248
|
-
* `kind: 'postgres-enum'` envelopes that bypass the per-target
|
|
249
|
-
* `ContractSerializer.deserializeContract` hydration. By the time this
|
|
250
|
-
* function runs, the entry is a live IR-class instance that
|
|
251
|
-
* structurally satisfies `PostgresEnumStorageEntry` — `codecId` and
|
|
252
|
-
* `values` are enumerable own properties on the instance. Returns
|
|
253
|
-
* `undefined` when the entry is not enum-shaped, so callers fall
|
|
254
|
-
* through to the codec-typed path.
|
|
255
|
-
*/
|
|
256
|
-
function readEnumViewIfApplicable(
|
|
257
|
-
typeInstance: unknown,
|
|
258
|
-
): { readonly codecId: string; readonly typeParams: Record<string, unknown> } | undefined {
|
|
259
|
-
if (!isPostgresEnumStorageEntry(typeInstance)) {
|
|
260
|
-
return undefined;
|
|
261
|
-
}
|
|
262
|
-
return {
|
|
263
|
-
codecId: typeInstance.codecId,
|
|
264
|
-
typeParams: { values: typeInstance.values },
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
240
|
function validateTypeParams(
|
|
269
241
|
typeParams: Record<string, unknown>,
|
|
270
242
|
descriptor: RuntimeParameterizedCodecDescriptor,
|
|
@@ -335,7 +307,7 @@ function collectTypeRefSites(
|
|
|
335
307
|
): Map<string, Array<{ readonly table: string; readonly column: string }>> {
|
|
336
308
|
const sites = new Map<string, Array<{ readonly table: string; readonly column: string }>>();
|
|
337
309
|
for (const ns of Object.values(storage.namespaces)) {
|
|
338
|
-
for (const [tableName, table] of Object.entries(ns.entries.table)) {
|
|
310
|
+
for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) {
|
|
339
311
|
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
340
312
|
if (typeof column.typeRef !== 'string') continue;
|
|
341
313
|
const list = sites.get(column.typeRef);
|
|
@@ -365,11 +337,8 @@ function initializeTypeHelpers(
|
|
|
365
337
|
const typeRefSites = collectTypeRefSites(storage);
|
|
366
338
|
|
|
367
339
|
for (const [typeName, typeInstance] of Object.entries(documentTypes)) {
|
|
368
|
-
const
|
|
369
|
-
const
|
|
370
|
-
const typeParams = enumView
|
|
371
|
-
? enumView.typeParams
|
|
372
|
-
: (typeInstance as StorageTypeInstance).typeParams;
|
|
340
|
+
const codecId = typeInstance.codecId;
|
|
341
|
+
const typeParams = typeInstance.typeParams;
|
|
373
342
|
const descriptor = codecDescriptors.get(codecId);
|
|
374
343
|
|
|
375
344
|
if (!descriptor) {
|
|
@@ -397,7 +366,7 @@ function validateColumnTypeParams(
|
|
|
397
366
|
codecDescriptors: Map<string, RuntimeParameterizedCodecDescriptor>,
|
|
398
367
|
): void {
|
|
399
368
|
for (const ns of Object.values(storage.namespaces)) {
|
|
400
|
-
for (const [tableName, table] of Object.entries(ns.entries.table)) {
|
|
369
|
+
for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) {
|
|
401
370
|
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
402
371
|
if (column.typeParams) {
|
|
403
372
|
const descriptor = codecDescriptors.get(column.codecId);
|
|
@@ -426,7 +395,7 @@ function assertColumnCodecIntegrity(
|
|
|
426
395
|
codecDescriptors: CodecDescriptorRegistry,
|
|
427
396
|
): void {
|
|
428
397
|
for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {
|
|
429
|
-
for (const [tableName, table] of Object.entries(ns.entries.table)) {
|
|
398
|
+
for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) {
|
|
430
399
|
for (const columnName of Object.keys(table.columns)) {
|
|
431
400
|
const ref = codecDescriptors.codecRefForColumn(namespaceId, tableName, columnName);
|
|
432
401
|
if (!ref) continue;
|
|
@@ -533,25 +502,15 @@ function buildContractCodecRegistry(
|
|
|
533
502
|
|
|
534
503
|
const typeRefSites = collectTypeRefSites(contract.storage);
|
|
535
504
|
for (const [typeName, typeInstance] of Object.entries(documentScopedCodecTypes(contract) ?? {})) {
|
|
536
|
-
const
|
|
537
|
-
const instanceTypeParams = enumView
|
|
538
|
-
? enumView.typeParams
|
|
539
|
-
: (typeInstance as StorageTypeInstance).typeParams;
|
|
505
|
+
const instanceTypeParams = typeInstance.typeParams;
|
|
540
506
|
const hasParamKeys =
|
|
541
507
|
instanceTypeParams !== undefined && Object.keys(instanceTypeParams).length > 0;
|
|
542
|
-
const ref: CodecRef =
|
|
543
|
-
?
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
: { codecId: enumView.codecId }
|
|
549
|
-
: hasParamKeys
|
|
550
|
-
? {
|
|
551
|
-
codecId: (typeInstance as StorageTypeInstance).codecId,
|
|
552
|
-
typeParams: instanceTypeParams as JsonValue,
|
|
553
|
-
}
|
|
554
|
-
: { codecId: (typeInstance as StorageTypeInstance).codecId };
|
|
508
|
+
const ref: CodecRef = hasParamKeys
|
|
509
|
+
? {
|
|
510
|
+
codecId: typeInstance.codecId,
|
|
511
|
+
typeParams: instanceTypeParams as JsonValue,
|
|
512
|
+
}
|
|
513
|
+
: { codecId: typeInstance.codecId };
|
|
555
514
|
const key = refKeyOf(ref);
|
|
556
515
|
const sites = typeRefSites.get(typeName) ?? [];
|
|
557
516
|
const existing = usedAtByKey.get(key);
|
|
@@ -565,7 +524,7 @@ function buildContractCodecRegistry(
|
|
|
565
524
|
}
|
|
566
525
|
|
|
567
526
|
for (const [namespaceId, ns] of Object.entries(contract.storage.namespaces)) {
|
|
568
|
-
for (const [tableName, table] of Object.entries(ns.entries.table)) {
|
|
527
|
+
for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) {
|
|
569
528
|
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
570
529
|
if (column.typeRef !== undefined) continue;
|
|
571
530
|
const ref = codecDescriptors.codecRefForColumn(namespaceId, tableName, columnName);
|
|
@@ -597,7 +556,7 @@ function buildContractCodecRegistry(
|
|
|
597
556
|
});
|
|
598
557
|
|
|
599
558
|
for (const [namespaceId, ns] of Object.entries(contract.storage.namespaces)) {
|
|
600
|
-
for (const [tableName, table] of Object.entries(ns.entries.table)) {
|
|
559
|
+
for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) {
|
|
601
560
|
for (const columnName of Object.keys(table.columns)) {
|
|
602
561
|
const ref = codecDescriptors.codecRefForColumn(namespaceId, tableName, columnName);
|
|
603
562
|
if (!ref) continue;
|
|
@@ -719,6 +678,9 @@ function applyMutationDefaults(
|
|
|
719
678
|
if (mutationDefault.ref.table !== options.table) {
|
|
720
679
|
continue;
|
|
721
680
|
}
|
|
681
|
+
if (mutationDefault.ref.namespace !== options.namespace) {
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
722
684
|
|
|
723
685
|
const defaultSpec =
|
|
724
686
|
options.op === 'create' ? mutationDefault.onCreate : mutationDefault.onUpdate;
|