@prisma-next/sql-runtime 0.3.0-dev.3 → 0.3.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/README.md +1 -2
- package/dist/{accelerate-EEKAFGN3-SHR4XFVV.js → accelerate-EEKAFGN3-P6A6XJWJ.js} +28 -28
- package/dist/{accelerate-EEKAFGN3-SHR4XFVV.js.map → accelerate-EEKAFGN3-P6A6XJWJ.js.map} +1 -1
- package/dist/{chunk-C6I3V3DM.js → chunk-APA6GHYY.js} +84 -2
- package/dist/chunk-APA6GHYY.js.map +1 -0
- package/dist/{dist-LCVVJCGI.js → dist-AQ3LWXOX.js} +13 -13
- package/dist/{dist-LCVVJCGI.js.map → dist-AQ3LWXOX.js.map} +1 -1
- package/dist/index.js +1 -1
- package/dist/src/codecs/decoding.d.ts +4 -0
- package/dist/src/codecs/decoding.d.ts.map +1 -0
- package/dist/src/codecs/encoding.d.ts +5 -0
- package/dist/src/codecs/encoding.d.ts.map +1 -0
- package/dist/src/codecs/validation.d.ts +6 -0
- package/dist/src/codecs/validation.d.ts.map +1 -0
- package/dist/src/exports/index.d.ts +11 -0
- package/dist/src/exports/index.d.ts.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/lower-sql-plan.d.ts +15 -0
- package/dist/src/lower-sql-plan.d.ts.map +1 -0
- package/dist/src/sql-context.d.ts +130 -0
- package/dist/src/sql-context.d.ts.map +1 -0
- package/dist/src/sql-family-adapter.d.ts +10 -0
- package/dist/src/sql-family-adapter.d.ts.map +1 -0
- package/dist/src/sql-marker.d.ts +22 -0
- package/dist/src/sql-marker.d.ts.map +1 -0
- package/dist/src/sql-runtime.d.ts +25 -0
- package/dist/src/sql-runtime.d.ts.map +1 -0
- package/dist/test/utils.d.ts +20 -24
- package/dist/test/utils.d.ts.map +1 -0
- package/dist/test/utils.js +26 -26
- package/dist/test/utils.js.map +1 -1
- package/package.json +25 -22
- package/src/codecs/decoding.ts +140 -0
- package/src/codecs/encoding.ts +76 -0
- package/src/codecs/validation.ts +67 -0
- package/src/exports/index.ts +40 -0
- package/src/index.ts +1 -0
- package/src/lower-sql-plan.ts +32 -0
- package/src/sql-context.ts +402 -0
- package/src/sql-family-adapter.ts +43 -0
- package/src/sql-marker.ts +105 -0
- package/src/sql-runtime.ts +166 -0
- package/test/async-iterable-result.test.ts +136 -0
- package/test/context.types.test-d.ts +70 -0
- package/test/parameterized-types.test.ts +553 -0
- package/test/sql-context.test.ts +217 -0
- package/test/sql-family-adapter.test.ts +86 -0
- package/test/sql-runtime.test.ts +155 -0
- package/test/utils.ts +266 -0
- package/dist/chunk-C6I3V3DM.js.map +0 -1
- package/dist/index.d.ts +0 -29
- package/dist/sql-runtime-DgEbg2OP.d.ts +0 -109
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { ExecutionPlan } from '@prisma-next/contract/types';
|
|
2
|
+
import type { OperationRegistry } from '@prisma-next/operations';
|
|
3
|
+
import type {
|
|
4
|
+
Log,
|
|
5
|
+
Plugin,
|
|
6
|
+
RuntimeCore,
|
|
7
|
+
RuntimeCoreOptions,
|
|
8
|
+
RuntimeTelemetryEvent,
|
|
9
|
+
RuntimeVerifyOptions,
|
|
10
|
+
TelemetryOutcome,
|
|
11
|
+
} from '@prisma-next/runtime-executor';
|
|
12
|
+
import { AsyncIterableResult, createRuntimeCore } from '@prisma-next/runtime-executor';
|
|
13
|
+
import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
|
|
14
|
+
import type {
|
|
15
|
+
Adapter,
|
|
16
|
+
CodecRegistry,
|
|
17
|
+
LoweredStatement,
|
|
18
|
+
SelectAst,
|
|
19
|
+
SqlDriver,
|
|
20
|
+
} from '@prisma-next/sql-relational-core/ast';
|
|
21
|
+
import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
|
|
22
|
+
import { decodeRow } from './codecs/decoding';
|
|
23
|
+
import { encodeParams } from './codecs/encoding';
|
|
24
|
+
import { validateCodecRegistryCompleteness } from './codecs/validation';
|
|
25
|
+
import { lowerSqlPlan } from './lower-sql-plan';
|
|
26
|
+
import type { RuntimeContext } from './sql-context';
|
|
27
|
+
import { SqlFamilyAdapter } from './sql-family-adapter';
|
|
28
|
+
|
|
29
|
+
export interface RuntimeOptions<
|
|
30
|
+
TContract extends SqlContract<SqlStorage> = SqlContract<SqlStorage>,
|
|
31
|
+
> {
|
|
32
|
+
readonly driver: SqlDriver;
|
|
33
|
+
readonly verify: RuntimeVerifyOptions;
|
|
34
|
+
readonly context: RuntimeContext<TContract>;
|
|
35
|
+
readonly plugins?: readonly Plugin<
|
|
36
|
+
TContract,
|
|
37
|
+
Adapter<SelectAst, SqlContract<SqlStorage>, LoweredStatement>,
|
|
38
|
+
SqlDriver
|
|
39
|
+
>[];
|
|
40
|
+
readonly mode?: 'strict' | 'permissive';
|
|
41
|
+
readonly log?: Log;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface Runtime {
|
|
45
|
+
execute<Row = Record<string, unknown>>(
|
|
46
|
+
plan: ExecutionPlan<Row> | SqlQueryPlan<Row>,
|
|
47
|
+
): AsyncIterableResult<Row>;
|
|
48
|
+
telemetry(): RuntimeTelemetryEvent | null;
|
|
49
|
+
close(): Promise<void>;
|
|
50
|
+
operations(): OperationRegistry;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type { RuntimeTelemetryEvent, RuntimeVerifyOptions, TelemetryOutcome };
|
|
54
|
+
|
|
55
|
+
class SqlRuntimeImpl<TContract extends SqlContract<SqlStorage> = SqlContract<SqlStorage>>
|
|
56
|
+
implements Runtime
|
|
57
|
+
{
|
|
58
|
+
private readonly core: RuntimeCore<
|
|
59
|
+
TContract,
|
|
60
|
+
Adapter<SelectAst, SqlContract<SqlStorage>, LoweredStatement>,
|
|
61
|
+
SqlDriver
|
|
62
|
+
>;
|
|
63
|
+
private readonly contract: TContract;
|
|
64
|
+
private readonly context: RuntimeContext<TContract>;
|
|
65
|
+
private readonly codecRegistry: CodecRegistry;
|
|
66
|
+
private codecRegistryValidated: boolean;
|
|
67
|
+
|
|
68
|
+
constructor(options: RuntimeOptions<TContract>) {
|
|
69
|
+
const { context, driver, verify, plugins, mode, log } = options;
|
|
70
|
+
this.contract = context.contract;
|
|
71
|
+
this.context = context;
|
|
72
|
+
this.codecRegistry = context.codecs;
|
|
73
|
+
this.codecRegistryValidated = false;
|
|
74
|
+
|
|
75
|
+
const familyAdapter = new SqlFamilyAdapter(context.contract);
|
|
76
|
+
|
|
77
|
+
const coreOptions: RuntimeCoreOptions<
|
|
78
|
+
TContract,
|
|
79
|
+
Adapter<SelectAst, SqlContract<SqlStorage>, LoweredStatement>,
|
|
80
|
+
SqlDriver
|
|
81
|
+
> = {
|
|
82
|
+
familyAdapter,
|
|
83
|
+
driver,
|
|
84
|
+
verify,
|
|
85
|
+
plugins: plugins as readonly Plugin<
|
|
86
|
+
TContract,
|
|
87
|
+
Adapter<SelectAst, SqlContract<SqlStorage>, LoweredStatement>,
|
|
88
|
+
SqlDriver
|
|
89
|
+
>[],
|
|
90
|
+
...(mode !== undefined ? { mode } : {}),
|
|
91
|
+
...(log !== undefined ? { log } : {}),
|
|
92
|
+
operationRegistry: context.operations,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
this.core = createRuntimeCore(coreOptions);
|
|
96
|
+
|
|
97
|
+
if (verify.mode === 'startup') {
|
|
98
|
+
validateCodecRegistryCompleteness(this.codecRegistry, context.contract);
|
|
99
|
+
this.codecRegistryValidated = true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private ensureCodecRegistryValidated(contract: SqlContract<SqlStorage>): void {
|
|
104
|
+
if (!this.codecRegistryValidated) {
|
|
105
|
+
validateCodecRegistryCompleteness(this.codecRegistry, contract);
|
|
106
|
+
this.codecRegistryValidated = true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
execute<Row = Record<string, unknown>>(
|
|
111
|
+
plan: ExecutionPlan<Row> | SqlQueryPlan<Row>,
|
|
112
|
+
): AsyncIterableResult<Row> {
|
|
113
|
+
this.ensureCodecRegistryValidated(this.contract);
|
|
114
|
+
|
|
115
|
+
// Check if plan is SqlQueryPlan (has ast but no sql)
|
|
116
|
+
const isSqlQueryPlan = (p: ExecutionPlan<Row> | SqlQueryPlan<Row>): p is SqlQueryPlan<Row> => {
|
|
117
|
+
return 'ast' in p && !('sql' in p);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Lower SqlQueryPlan to Plan if needed
|
|
121
|
+
const executablePlan: ExecutionPlan<Row> = isSqlQueryPlan(plan)
|
|
122
|
+
? lowerSqlPlan(this.context, plan)
|
|
123
|
+
: plan;
|
|
124
|
+
|
|
125
|
+
const iterator = async function* (
|
|
126
|
+
self: SqlRuntimeImpl<TContract>,
|
|
127
|
+
): AsyncGenerator<Row, void, unknown> {
|
|
128
|
+
const encodedParams = encodeParams(executablePlan, self.codecRegistry);
|
|
129
|
+
const planWithEncodedParams: ExecutionPlan<Row> = {
|
|
130
|
+
...executablePlan,
|
|
131
|
+
params: encodedParams,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const coreIterator = self.core.execute(planWithEncodedParams);
|
|
135
|
+
|
|
136
|
+
for await (const rawRow of coreIterator) {
|
|
137
|
+
const decodedRow = decodeRow(
|
|
138
|
+
rawRow as Record<string, unknown>,
|
|
139
|
+
executablePlan,
|
|
140
|
+
self.codecRegistry,
|
|
141
|
+
);
|
|
142
|
+
yield decodedRow as Row;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return new AsyncIterableResult(iterator(this));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
telemetry(): RuntimeTelemetryEvent | null {
|
|
150
|
+
return this.core.telemetry();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
operations(): OperationRegistry {
|
|
154
|
+
return this.core.operations();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
close(): Promise<void> {
|
|
158
|
+
return this.core.close();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function createRuntime<TContract extends SqlContract<SqlStorage>>(
|
|
163
|
+
options: RuntimeOptions<TContract>,
|
|
164
|
+
): Runtime {
|
|
165
|
+
return new SqlRuntimeImpl(options);
|
|
166
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { ExecutionPlan } from '@prisma-next/contract/types';
|
|
2
|
+
import type { AsyncIterableResult } from '@prisma-next/runtime-executor';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { createRuntime } from '../src/exports';
|
|
5
|
+
import { createStubAdapter, createTestContext, createTestContract } from './utils';
|
|
6
|
+
|
|
7
|
+
// Mock driver that implements SqlDriver interface
|
|
8
|
+
class MockDriver {
|
|
9
|
+
private rows: ReadonlyArray<Record<string, unknown>> = [];
|
|
10
|
+
|
|
11
|
+
setRows(rows: ReadonlyArray<Record<string, unknown>>): void {
|
|
12
|
+
this.rows = rows;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async query(
|
|
16
|
+
_sql: string,
|
|
17
|
+
_params: readonly unknown[],
|
|
18
|
+
): Promise<{ rows: ReadonlyArray<unknown> }> {
|
|
19
|
+
// Return empty marker result for contract verification
|
|
20
|
+
return { rows: [] };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async *execute<Row = Record<string, unknown>>(_options: {
|
|
24
|
+
sql: string;
|
|
25
|
+
params: readonly unknown[];
|
|
26
|
+
}): AsyncIterable<Row> {
|
|
27
|
+
for (const row of this.rows) {
|
|
28
|
+
yield row as Row;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async connect(): Promise<void> {
|
|
33
|
+
// No-op
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async close(): Promise<void> {
|
|
37
|
+
// No-op
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const fixtureContract = createTestContract({
|
|
42
|
+
schemaVersion: '1',
|
|
43
|
+
targetFamily: 'sql',
|
|
44
|
+
target: 'postgres',
|
|
45
|
+
coreHash: 'test-hash',
|
|
46
|
+
profileHash: 'test-profile-hash',
|
|
47
|
+
storage: {
|
|
48
|
+
tables: {
|
|
49
|
+
user: {
|
|
50
|
+
columns: {
|
|
51
|
+
id: { nativeType: 'int4', codecId: 'pg/int4@1', nullable: false },
|
|
52
|
+
email: { nativeType: 'text', codecId: 'pg/text@1', nullable: false },
|
|
53
|
+
},
|
|
54
|
+
uniques: [],
|
|
55
|
+
indexes: [],
|
|
56
|
+
foreignKeys: [],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
models: {},
|
|
61
|
+
relations: {},
|
|
62
|
+
mappings: { codecTypes: {}, operationTypes: {} },
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('SqlRuntime AsyncIterableResult integration', () => {
|
|
66
|
+
it('returns AsyncIterableResult from execute', async () => {
|
|
67
|
+
const adapter = createStubAdapter();
|
|
68
|
+
const driver = new MockDriver();
|
|
69
|
+
driver.setRows([
|
|
70
|
+
{ id: 1, email: 'test1@example.com' },
|
|
71
|
+
{ id: 2, email: 'test2@example.com' },
|
|
72
|
+
]);
|
|
73
|
+
const context = createTestContext(fixtureContract, adapter);
|
|
74
|
+
const runtime = createRuntime({
|
|
75
|
+
driver: driver as unknown as Parameters<typeof createRuntime>[0]['driver'],
|
|
76
|
+
context,
|
|
77
|
+
verify: { mode: 'onFirstUse', requireMarker: false },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const plan: ExecutionPlan<{ id: number; email: string }> = {
|
|
81
|
+
sql: 'SELECT id, email FROM "user" ORDER BY id',
|
|
82
|
+
params: [],
|
|
83
|
+
meta: {
|
|
84
|
+
target: 'postgres',
|
|
85
|
+
targetFamily: 'sql',
|
|
86
|
+
coreHash: 'test-hash',
|
|
87
|
+
lane: 'sql',
|
|
88
|
+
paramDescriptors: [],
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const result = runtime.execute(plan);
|
|
93
|
+
|
|
94
|
+
// Verify it's an AsyncIterableResult
|
|
95
|
+
expect(result).toBeInstanceOf(Object);
|
|
96
|
+
expect(typeof result.toArray).toBe('function');
|
|
97
|
+
expect(typeof result[Symbol.asyncIterator]).toBe('function');
|
|
98
|
+
|
|
99
|
+
await runtime.close();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('preserves type information', async () => {
|
|
103
|
+
const adapter = createStubAdapter();
|
|
104
|
+
const driver = new MockDriver();
|
|
105
|
+
driver.setRows([{ id: 1, email: 'test@example.com' }]);
|
|
106
|
+
const context = createTestContext(fixtureContract, adapter);
|
|
107
|
+
const runtime = createRuntime({
|
|
108
|
+
driver: driver as unknown as Parameters<typeof createRuntime>[0]['driver'],
|
|
109
|
+
context,
|
|
110
|
+
verify: { mode: 'onFirstUse', requireMarker: false },
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const plan: ExecutionPlan<{ id: number; email: string }> = {
|
|
114
|
+
sql: 'SELECT id, email FROM "user" LIMIT 1',
|
|
115
|
+
params: [],
|
|
116
|
+
meta: {
|
|
117
|
+
target: 'postgres',
|
|
118
|
+
targetFamily: 'sql',
|
|
119
|
+
coreHash: 'test-hash',
|
|
120
|
+
lane: 'sql',
|
|
121
|
+
paramDescriptors: [],
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const result: AsyncIterableResult<{ id: number; email: string }> = runtime.execute(plan);
|
|
126
|
+
const rows = await result.toArray();
|
|
127
|
+
|
|
128
|
+
expect(rows.length).toBe(1);
|
|
129
|
+
if (rows[0]) {
|
|
130
|
+
expect(typeof rows[0].id).toBe('number');
|
|
131
|
+
expect(typeof rows[0].email).toBe('string');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
await runtime.close();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { SqlContract, SqlMappings, SqlStorage } from '@prisma-next/sql-contract/types';
|
|
2
|
+
import { expectTypeOf, test } from 'vitest';
|
|
3
|
+
import type { RuntimeContext, TypeHelperRegistry } from '../src/sql-context';
|
|
4
|
+
|
|
5
|
+
// Contract type with storage.types using literal types (matching emission output)
|
|
6
|
+
type TestContract = SqlContract<
|
|
7
|
+
{
|
|
8
|
+
readonly tables: {
|
|
9
|
+
readonly document: {
|
|
10
|
+
readonly columns: {
|
|
11
|
+
readonly id: {
|
|
12
|
+
readonly nativeType: 'int4';
|
|
13
|
+
readonly codecId: 'pg/int4@1';
|
|
14
|
+
nullable: false;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
readonly primaryKey: { readonly columns: readonly ['id'] };
|
|
18
|
+
readonly uniques: readonly [];
|
|
19
|
+
readonly indexes: readonly [];
|
|
20
|
+
readonly foreignKeys: readonly [];
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
readonly types: {
|
|
24
|
+
readonly Vector1536: {
|
|
25
|
+
readonly codecId: 'pg/vector@1';
|
|
26
|
+
readonly nativeType: 'vector';
|
|
27
|
+
readonly typeParams: { readonly length: 1536 };
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
Record<string, never>,
|
|
32
|
+
Record<string, never>,
|
|
33
|
+
SqlMappings
|
|
34
|
+
>;
|
|
35
|
+
|
|
36
|
+
test('RuntimeContext.types is TypeHelperRegistry', () => {
|
|
37
|
+
// RuntimeContext.types is intentionally loose (Record<string, unknown>)
|
|
38
|
+
// The strong typing comes from schema(context).types via ExtractSchemaTypes
|
|
39
|
+
expectTypeOf<RuntimeContext<TestContract>['types']>().toEqualTypeOf<
|
|
40
|
+
TypeHelperRegistry | undefined
|
|
41
|
+
>();
|
|
42
|
+
|
|
43
|
+
// TypeHelperRegistry allows any values - the actual type depends on init hooks
|
|
44
|
+
expectTypeOf<TypeHelperRegistry>().toEqualTypeOf<Record<string, unknown>>();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('RuntimeContext preserves contract type parameter', () => {
|
|
48
|
+
// Verify the contract type is preserved in RuntimeContext
|
|
49
|
+
expectTypeOf<RuntimeContext<TestContract>['contract']>().toEqualTypeOf<TestContract>();
|
|
50
|
+
|
|
51
|
+
// Verify we can access storage.types through the context's contract
|
|
52
|
+
type ContractStorageTypes = RuntimeContext<TestContract>['contract']['storage']['types'];
|
|
53
|
+
expectTypeOf<ContractStorageTypes>().toExtend<
|
|
54
|
+
| {
|
|
55
|
+
readonly Vector1536: {
|
|
56
|
+
readonly codecId: 'pg/vector@1';
|
|
57
|
+
readonly nativeType: 'vector';
|
|
58
|
+
readonly typeParams: { readonly length: 1536 };
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
| undefined
|
|
62
|
+
>();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('RuntimeContext accepts generic SqlContract', () => {
|
|
66
|
+
// Verify RuntimeContext defaults work
|
|
67
|
+
type DefaultContext = RuntimeContext;
|
|
68
|
+
expectTypeOf<DefaultContext['contract']>().toExtend<SqlContract<SqlStorage>>();
|
|
69
|
+
expectTypeOf<DefaultContext['types']>().toEqualTypeOf<TypeHelperRegistry | undefined>();
|
|
70
|
+
});
|