@prisma-next/sql-runtime 0.13.0-dev.3 → 0.13.0-dev.31

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/package.json CHANGED
@@ -1,27 +1,27 @@
1
1
  {
2
2
  "name": "@prisma-next/sql-runtime",
3
- "version": "0.13.0-dev.3",
3
+ "version": "0.13.0-dev.31",
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.13.0-dev.3",
10
- "@prisma-next/utils": "0.13.0-dev.3",
11
- "@prisma-next/framework-components": "0.13.0-dev.3",
12
- "@prisma-next/ids": "0.13.0-dev.3",
13
- "@prisma-next/operations": "0.13.0-dev.3",
14
- "@prisma-next/sql-contract": "0.13.0-dev.3",
15
- "@prisma-next/sql-operations": "0.13.0-dev.3",
16
- "@prisma-next/sql-relational-core": "0.13.0-dev.3",
9
+ "@prisma-next/contract": "0.13.0-dev.31",
10
+ "@prisma-next/utils": "0.13.0-dev.31",
11
+ "@prisma-next/framework-components": "0.13.0-dev.31",
12
+ "@prisma-next/ids": "0.13.0-dev.31",
13
+ "@prisma-next/operations": "0.13.0-dev.31",
14
+ "@prisma-next/sql-contract": "0.13.0-dev.31",
15
+ "@prisma-next/sql-operations": "0.13.0-dev.31",
16
+ "@prisma-next/sql-relational-core": "0.13.0-dev.31",
17
17
  "arktype": "^2.2.0"
18
18
  },
19
19
  "devDependencies": {
20
- "@prisma-next/test-utils": "0.13.0-dev.3",
21
- "@prisma-next/tsconfig": "0.13.0-dev.3",
20
+ "@prisma-next/test-utils": "0.13.0-dev.31",
21
+ "@prisma-next/tsconfig": "0.13.0-dev.31",
22
22
  "@types/pg": "8.20.0",
23
23
  "pg": "8.21.0",
24
- "@prisma-next/tsdown": "0.13.0-dev.3",
24
+ "@prisma-next/tsdown": "0.13.0-dev.31",
25
25
  "tsdown": "0.22.1",
26
26
  "typescript": "5.9.3",
27
27
  "vitest": "4.1.8"
@@ -1,4 +1,5 @@
1
1
  import type { CodecRef } from '@prisma-next/framework-components/codec';
2
+ import { materializeCodec } from '@prisma-next/framework-components/codec';
2
3
  import { runtimeError } from '@prisma-next/framework-components/runtime';
3
4
  import { canonicalizeJson } from '@prisma-next/framework-components/utils';
4
5
  import type { Codec, SqlCodecInstanceContext } from '@prisma-next/sql-relational-core/ast';
@@ -46,54 +47,11 @@ export function createAstCodecResolver(
46
47
  );
47
48
  }
48
49
 
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,
59
- );
60
50
  const ctx = instanceContextFor(ref);
61
- // The descriptor's `factory` is typed against its own `P`; the registry erases `P` to `unknown`, so callers narrow per codec id at the dispatch boundary. The descriptor's `paramsSchema` validates the input above before we forward it, so this narrow is safe by construction.
62
- const codec = (
63
- descriptor.factory as (params: unknown) => (ctx: SqlCodecInstanceContext) => Codec
64
- )(validated)(ctx);
51
+ const codec = materializeCodec(descriptor, ref, ctx);
65
52
 
66
53
  cache.set(key, codec);
67
54
  return codec;
68
55
  },
69
56
  };
70
57
  }
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
- }
@@ -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, StorageTable } from '@prisma-next/sql-contract/types';
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 as SqlNamespaceTables)) {
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
- function extractCodecIdsFromColumns(contract: Contract<SqlStorage>): Map<string, string> {
30
- const codecIds = new Map<string, string>();
21
+ type ColumnCodecRef = {
22
+ readonly namespaceId: string;
23
+ readonly table: string;
24
+ readonly column: string;
25
+ readonly codecId: string;
26
+ };
31
27
 
32
- for (const ns of Object.values(contract.storage.namespaces)) {
33
- for (const [tableName, table] of Object.entries(ns.entries.table as SqlNamespaceTables)) {
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
- const codecId = column.codecId;
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 codecIds;
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 codecIds = extractCodecIdsFromColumns(contract);
50
- const invalidCodecs: Array<{ table: string; column: string; codecId: string }> = [];
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
  }
@@ -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
- CreateRuntimeOptions,
61
+ ConnectionProvider,
58
62
  Runtime,
59
63
  RuntimeConnection,
60
64
  RuntimeQueryable,
61
65
  RuntimeTransaction,
62
66
  TransactionContext,
63
67
  } from '../sql-runtime';
64
- export { createRuntime, withTransaction } from '../sql-runtime';
68
+ export { SqlRuntimeBase, withTransaction } from '../sql-runtime';
@@ -9,7 +9,7 @@ export interface MarkerReader {
9
9
  }
10
10
 
11
11
  /**
12
- * SQL family adapter SPI consumed by `SqlRuntime`. Encapsulates the
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
  *
@@ -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 enumView = readEnumViewIfApplicable(typeInstance);
369
- const codecId = enumView ? enumView.codecId : (typeInstance as StorageTypeInstance).codecId;
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 enumView = readEnumViewIfApplicable(typeInstance);
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 = enumView
543
- ? hasParamKeys
544
- ? {
545
- codecId: enumView.codecId,
546
- typeParams: instanceTypeParams as unknown as JsonValue,
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;
@@ -1,8 +1,4 @@
1
1
  import type { Contract } from '@prisma-next/contract/types';
2
- import type {
3
- ExecutionStackInstance,
4
- RuntimeDriverInstance,
5
- } from '@prisma-next/framework-components/execution';
6
2
  import {
7
3
  AsyncIterableResult,
8
4
  checkAborted,
@@ -23,6 +19,7 @@ import type {
23
19
  LoweredStatement,
24
20
  PreparedExecuteRequest,
25
21
  SqlCodecCallContext,
22
+ SqlConnection,
26
23
  SqlDriver,
27
24
  SqlQueryable,
28
25
  SqlTransaction,
@@ -64,11 +61,7 @@ import type {
64
61
  TelemetryOutcome,
65
62
  VerifyMarkerOption,
66
63
  } from './runtime-spi';
67
- import type {
68
- ExecutionContext,
69
- SqlRuntimeAdapterInstance,
70
- SqlRuntimeExtensionInstance,
71
- } from './sql-context';
64
+ import type { ExecutionContext } from './sql-context';
72
65
  import { SqlFamilyAdapter } from './sql-family-adapter';
73
66
 
74
67
  export type Log = RuntimeLog;
@@ -83,25 +76,10 @@ export interface RuntimeOptions<TContract extends Contract<SqlStorage> = Contrac
83
76
  readonly log?: Log;
84
77
  }
85
78
 
86
- export interface CreateRuntimeOptions<
87
- TContract extends Contract<SqlStorage> = Contract<SqlStorage>,
88
- TTargetId extends string = string,
89
- > {
90
- readonly stackInstance: ExecutionStackInstance<
91
- 'sql',
92
- TTargetId,
93
- SqlRuntimeAdapterInstance<TTargetId>,
94
- RuntimeDriverInstance<'sql', TTargetId>,
95
- SqlRuntimeExtensionInstance<TTargetId>
96
- >;
97
- readonly context: ExecutionContext<TContract>;
98
- readonly driver: SqlDriver<unknown>;
99
- readonly verifyMarker?: VerifyMarkerOption;
100
- readonly middleware?: readonly SqlMiddleware[];
101
- readonly mode?: 'strict' | 'permissive';
102
- readonly log?: Log;
103
- }
104
-
79
+ /**
80
+ * SQL-family runtime interface. Named `Runtime` (not `SqlRuntime`) by deliberate exception
81
+ * to avoid a repo-wide rename; see ADR 230 (runtime target layer) for the recorded decision.
82
+ */
105
83
  export interface Runtime extends RuntimeQueryable {
106
84
  connection(): Promise<RuntimeConnection>;
107
85
  telemetry(): RuntimeTelemetryEvent | null;
@@ -167,7 +145,12 @@ function isExecutionPlan(plan: SqlExecutionPlan | SqlQueryPlan): plan is SqlExec
167
145
  const noopLogSink = (): void => {};
168
146
  const noopLog: Log = { info: noopLogSink, warn: noopLogSink, error: noopLogSink };
169
147
 
170
- class SqlRuntimeImpl<TContract extends Contract<SqlStorage> = Contract<SqlStorage>>
148
+ /**
149
+ * Abstract family-layer base for SQL runtimes. Subclass to build a target runtime
150
+ * (e.g. `PostgresRuntimeImpl`); app code should consume the `Runtime` interface returned
151
+ * by the target factories, never this class directly.
152
+ */
153
+ export abstract class SqlRuntimeBase<TContract extends Contract<SqlStorage> = Contract<SqlStorage>>
171
154
  extends RuntimeCore<SqlQueryPlan, SqlExecutionPlan, SqlMiddleware>
172
155
  implements Runtime
173
156
  {
@@ -325,6 +308,16 @@ class SqlRuntimeImpl<TContract extends Contract<SqlStorage> = Contract<SqlStorag
325
308
  );
326
309
  }
327
310
 
311
+ /**
312
+ * Returns the raw driver connection. The connection is a `SqlQueryable` — SQL
313
+ * issued on it runs below the middleware/codec/telemetry pipeline. It carries
314
+ * its own lifecycle (`release`/`destroy`/`beginTransaction`); the caller owns
315
+ * disposal.
316
+ */
317
+ protected acquireRawConnection(): Promise<SqlConnection> {
318
+ return this.driver.acquireConnection();
319
+ }
320
+
328
321
  private async *streamRows<Row>(
329
322
  exec: SqlExecutionPlan,
330
323
  decodeContext: DecodeContext,
@@ -385,7 +378,12 @@ class SqlRuntimeImpl<TContract extends Contract<SqlStorage> = Contract<SqlStorag
385
378
  }
386
379
  }
387
380
 
388
- private executeAgainstQueryable<Row>(
381
+ /**
382
+ * Execute a plan against a caller-supplied queryable, running the full
383
+ * middleware/codec/telemetry pipeline. Use `acquireRawConnection` to obtain a
384
+ * queryable that subclasses can bind typed plans to.
385
+ */
386
+ protected executeAgainstQueryable<Row>(
389
387
  plan: SqlExecutionPlan<unknown> | SqlQueryPlan<unknown>,
390
388
  queryable: SqlQueryable,
391
389
  options?: RuntimeExecuteOptions,
@@ -526,7 +524,11 @@ class SqlRuntimeImpl<TContract extends Contract<SqlStorage> = Contract<SqlStorag
526
524
  return new PreparedStatementImpl<ParamsFromDeclaration<D, CT>, Row>(internals);
527
525
  }
528
526
 
529
- private executePreparedAgainstQueryable<P, Row>(
527
+ /**
528
+ * Execute a prepared statement against a caller-supplied queryable, running
529
+ * the full middleware/codec/telemetry pipeline.
530
+ */
531
+ protected executePreparedAgainstQueryable<P, Row>(
530
532
  ps: PreparedStatementImpl<P, Row>,
531
533
  userParams: Record<string, unknown>,
532
534
  queryable: SqlQueryable,
@@ -753,8 +755,13 @@ function transactionClosedError(): Error {
753
755
  );
754
756
  }
755
757
 
758
+ /** Minimal structural type `withTransaction` depends on — anything that can open a connection. */
759
+ export interface ConnectionProvider {
760
+ connection(): Promise<RuntimeConnection>;
761
+ }
762
+
756
763
  export async function withTransaction<R>(
757
- runtime: Runtime,
764
+ runtime: ConnectionProvider,
758
765
  fn: (tx: TransactionContext) => PromiseLike<R>,
759
766
  ): Promise<R> {
760
767
  const connection = await runtime.connection();
@@ -859,19 +866,3 @@ export async function withTransaction<R>(
859
866
  }
860
867
  }
861
868
  }
862
-
863
- export function createRuntime<TContract extends Contract<SqlStorage>, TTargetId extends string>(
864
- options: CreateRuntimeOptions<TContract, TTargetId>,
865
- ): Runtime {
866
- const { stackInstance, context, driver, verifyMarker, middleware, mode, log } = options;
867
-
868
- return new SqlRuntimeImpl({
869
- context,
870
- adapter: stackInstance.adapter,
871
- driver,
872
- ...ifDefined('verifyMarker', verifyMarker),
873
- ...ifDefined('middleware', middleware),
874
- ...ifDefined('mode', mode),
875
- ...ifDefined('log', log),
876
- });
877
- }