@prisma-next/runtime-executor 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.
@@ -0,0 +1,42 @@
1
+ import type { ExecutionPlan } from '@prisma-next/contract/types';
2
+
3
+ export type Severity = 'error' | 'warn' | 'info';
4
+
5
+ export interface Log {
6
+ info(event: unknown): void;
7
+ warn(event: unknown): void;
8
+ error(event: unknown): void;
9
+ }
10
+
11
+ export interface PluginContext<TContract = unknown, TAdapter = unknown, TDriver = unknown> {
12
+ readonly contract: TContract;
13
+ readonly adapter: TAdapter;
14
+ readonly driver: TDriver;
15
+ readonly mode: 'strict' | 'permissive';
16
+ readonly now: () => number;
17
+ readonly log: Log;
18
+ }
19
+
20
+ export interface AfterExecuteResult {
21
+ readonly rowCount: number;
22
+ readonly latencyMs: number;
23
+ readonly completed: boolean;
24
+ }
25
+
26
+ export interface Plugin<TContract = unknown, TAdapter = unknown, TDriver = unknown> {
27
+ readonly name: string;
28
+ beforeExecute?(
29
+ plan: ExecutionPlan,
30
+ ctx: PluginContext<TContract, TAdapter, TDriver>,
31
+ ): Promise<void>;
32
+ onRow?(
33
+ row: Record<string, unknown>,
34
+ plan: ExecutionPlan,
35
+ ctx: PluginContext<TContract, TAdapter, TDriver>,
36
+ ): Promise<void>;
37
+ afterExecute?(
38
+ plan: ExecutionPlan,
39
+ result: AfterExecuteResult,
40
+ ctx: PluginContext<TContract, TAdapter, TDriver>,
41
+ ): Promise<void>;
42
+ }
@@ -0,0 +1,285 @@
1
+ import type { ExecutionPlan } from '@prisma-next/contract/types';
2
+ import type { OperationRegistry } from '@prisma-next/operations';
3
+ import { AsyncIterableResult } from './async-iterable-result';
4
+ import { runtimeError } from './errors';
5
+ import { computeSqlFingerprint } from './fingerprint';
6
+ import { parseContractMarkerRow } from './marker';
7
+ import type { Log, Plugin, PluginContext } from './plugins/types';
8
+ import type { RuntimeFamilyAdapter } from './runtime-spi';
9
+
10
+ export interface RuntimeVerifyOptions {
11
+ readonly mode: 'onFirstUse' | 'startup' | 'always';
12
+ readonly requireMarker: boolean;
13
+ }
14
+
15
+ export type TelemetryOutcome = 'success' | 'runtime-error';
16
+
17
+ export interface RuntimeTelemetryEvent {
18
+ readonly lane: string;
19
+ readonly target: string;
20
+ readonly fingerprint: string;
21
+ readonly outcome: TelemetryOutcome;
22
+ readonly durationMs?: number;
23
+ }
24
+
25
+ export interface RuntimeCoreOptions<TContract = unknown, TAdapter = unknown, TDriver = unknown> {
26
+ readonly familyAdapter: RuntimeFamilyAdapter<TContract>;
27
+ readonly driver: TDriver;
28
+ readonly verify: RuntimeVerifyOptions;
29
+ readonly plugins?: readonly Plugin<TContract, TAdapter, TDriver>[];
30
+ readonly mode?: 'strict' | 'permissive';
31
+ readonly log?: Log;
32
+ readonly operationRegistry: OperationRegistry;
33
+ }
34
+
35
+ export interface RuntimeCore<TContract = unknown, TAdapter = unknown, TDriver = unknown> {
36
+ // Type parameters are used in the implementation for type safety
37
+ readonly _typeContract?: TContract;
38
+ readonly _typeAdapter?: TAdapter;
39
+ readonly _typeDriver?: TDriver;
40
+ execute<Row = Record<string, unknown>>(plan: ExecutionPlan<Row>): AsyncIterableResult<Row>;
41
+ telemetry(): RuntimeTelemetryEvent | null;
42
+ close(): Promise<void>;
43
+ operations(): OperationRegistry;
44
+ }
45
+
46
+ interface DriverWithQuery<_TDriver> {
47
+ query(sql: string, params: readonly unknown[]): Promise<{ rows: ReadonlyArray<unknown> }>;
48
+ }
49
+
50
+ interface DriverWithExecute<_TDriver> {
51
+ execute<Row = Record<string, unknown>>(options: {
52
+ sql: string;
53
+ params: readonly unknown[];
54
+ }): AsyncIterable<Row>;
55
+ }
56
+
57
+ interface DriverWithClose<_TDriver> {
58
+ close(): Promise<void>;
59
+ }
60
+
61
+ class RuntimeCoreImpl<TContract = unknown, TAdapter = unknown, TDriver = unknown>
62
+ implements RuntimeCore<TContract, TAdapter, TDriver>
63
+ {
64
+ readonly _typeContract?: TContract;
65
+ readonly _typeAdapter?: TAdapter;
66
+ readonly _typeDriver?: TDriver;
67
+ private readonly contract: TContract;
68
+ private readonly familyAdapter: RuntimeFamilyAdapter<TContract>;
69
+ private readonly driver: TDriver;
70
+ private readonly plugins: readonly Plugin<TContract, TAdapter, TDriver>[];
71
+ private readonly mode: 'strict' | 'permissive';
72
+ private readonly verify: RuntimeVerifyOptions;
73
+ private readonly operationRegistry: OperationRegistry;
74
+ private readonly pluginContext: PluginContext<TContract, TAdapter, TDriver>;
75
+
76
+ private verified: boolean;
77
+ private startupVerified: boolean;
78
+ private _telemetry: RuntimeTelemetryEvent | null;
79
+
80
+ constructor(options: RuntimeCoreOptions<TContract, TAdapter, TDriver>) {
81
+ const { familyAdapter, driver } = options;
82
+ this.contract = familyAdapter.contract;
83
+ this.familyAdapter = familyAdapter;
84
+ this.driver = driver;
85
+ this.plugins = options.plugins ?? [];
86
+ this.mode = options.mode ?? 'strict';
87
+ this.verify = options.verify;
88
+ this.operationRegistry = options.operationRegistry;
89
+
90
+ this.verified = options.verify.mode === 'startup' ? false : options.verify.mode === 'always';
91
+ this.startupVerified = false;
92
+ this._telemetry = null;
93
+
94
+ this.pluginContext = {
95
+ contract: this.contract,
96
+ adapter: options.familyAdapter as unknown as TAdapter,
97
+ driver: this.driver,
98
+ mode: this.mode,
99
+ now: () => Date.now(),
100
+ log: options.log ?? {
101
+ info: () => {
102
+ // No-op in MVP - diagnostics stay out of runtime core
103
+ },
104
+ warn: () => {
105
+ // No-op in MVP - diagnostics stay out of runtime core
106
+ },
107
+ error: () => {
108
+ // No-op in MVP - diagnostics stay out of runtime core
109
+ },
110
+ },
111
+ };
112
+ }
113
+
114
+ private async verifyPlanIfNeeded(_plan: ExecutionPlan): Promise<void> {
115
+ void _plan;
116
+ if (this.verify.mode === 'always') {
117
+ this.verified = false;
118
+ }
119
+
120
+ if (this.verified) {
121
+ return;
122
+ }
123
+
124
+ const readStatement = this.familyAdapter.markerReader.readMarkerStatement();
125
+ const driver = this.driver as unknown as DriverWithQuery<TDriver>;
126
+ const result = await driver.query(readStatement.sql, readStatement.params);
127
+
128
+ if (result.rows.length === 0) {
129
+ if (this.verify.requireMarker) {
130
+ throw runtimeError('CONTRACT.MARKER_MISSING', 'Contract marker not found in database');
131
+ }
132
+
133
+ this.verified = true;
134
+ return;
135
+ }
136
+
137
+ const marker = parseContractMarkerRow(result.rows[0]);
138
+
139
+ const contract = this.contract as { coreHash: string; profileHash?: string | null };
140
+ if (marker.coreHash !== contract.coreHash) {
141
+ throw runtimeError('CONTRACT.MARKER_MISMATCH', 'Database core hash does not match contract', {
142
+ expected: contract.coreHash,
143
+ actual: marker.coreHash,
144
+ });
145
+ }
146
+
147
+ const expectedProfile = contract.profileHash ?? null;
148
+ if (expectedProfile !== null && marker.profileHash !== expectedProfile) {
149
+ throw runtimeError(
150
+ 'CONTRACT.MARKER_MISMATCH',
151
+ 'Database profile hash does not match contract',
152
+ {
153
+ expectedProfile,
154
+ actualProfile: marker.profileHash,
155
+ },
156
+ );
157
+ }
158
+
159
+ this.verified = true;
160
+ this.startupVerified = true;
161
+ }
162
+
163
+ private validatePlan(plan: ExecutionPlan): void {
164
+ this.familyAdapter.validatePlan(plan, this.contract);
165
+ }
166
+
167
+ private recordTelemetry(
168
+ plan: ExecutionPlan,
169
+ outcome: TelemetryOutcome,
170
+ durationMs?: number,
171
+ ): void {
172
+ const contract = this.contract as { target: string };
173
+ this._telemetry = Object.freeze({
174
+ lane: plan.meta.lane,
175
+ target: contract.target,
176
+ fingerprint: computeSqlFingerprint(plan.sql),
177
+ outcome,
178
+ ...(durationMs !== undefined ? { durationMs } : {}),
179
+ });
180
+ }
181
+
182
+ execute<Row = Record<string, unknown>>(plan: ExecutionPlan<Row>): AsyncIterableResult<Row> {
183
+ this.validatePlan(plan);
184
+ this._telemetry = null;
185
+
186
+ const iterator = async function* (
187
+ self: RuntimeCoreImpl<TContract, TAdapter, TDriver>,
188
+ ): AsyncGenerator<Row, void, unknown> {
189
+ const startedAt = Date.now();
190
+ let rowCount = 0;
191
+ let completed = false;
192
+
193
+ if (!self.startupVerified && self.verify.mode === 'startup') {
194
+ await self.verifyPlanIfNeeded(plan);
195
+ }
196
+
197
+ if (self.verify.mode === 'onFirstUse') {
198
+ await self.verifyPlanIfNeeded(plan);
199
+ }
200
+
201
+ try {
202
+ if (self.verify.mode === 'always') {
203
+ await self.verifyPlanIfNeeded(plan);
204
+ }
205
+
206
+ for (const plugin of self.plugins) {
207
+ if (plugin.beforeExecute) {
208
+ await plugin.beforeExecute(plan, self.pluginContext);
209
+ }
210
+ }
211
+
212
+ const driver = self.driver as unknown as DriverWithExecute<TDriver>;
213
+ const encodedParams = plan.params as readonly unknown[];
214
+
215
+ for await (const row of driver.execute<Record<string, unknown>>({
216
+ sql: plan.sql,
217
+ params: encodedParams,
218
+ })) {
219
+ for (const plugin of self.plugins) {
220
+ if (plugin.onRow) {
221
+ await plugin.onRow(row, plan, self.pluginContext);
222
+ }
223
+ }
224
+ rowCount++;
225
+ yield row as Row;
226
+ }
227
+
228
+ completed = true;
229
+ self.recordTelemetry(plan, 'success', Date.now() - startedAt);
230
+ } catch (error) {
231
+ if (self._telemetry === null) {
232
+ self.recordTelemetry(plan, 'runtime-error', Date.now() - startedAt);
233
+ }
234
+
235
+ const latencyMs = Date.now() - startedAt;
236
+ for (const plugin of self.plugins) {
237
+ if (plugin.afterExecute) {
238
+ try {
239
+ await plugin.afterExecute(
240
+ plan,
241
+ { rowCount, latencyMs, completed },
242
+ self.pluginContext,
243
+ );
244
+ } catch {
245
+ // Ignore errors from afterExecute hooks
246
+ }
247
+ }
248
+ }
249
+
250
+ throw error;
251
+ }
252
+
253
+ const latencyMs = Date.now() - startedAt;
254
+ for (const plugin of self.plugins) {
255
+ if (plugin.afterExecute) {
256
+ await plugin.afterExecute(plan, { rowCount, latencyMs, completed }, self.pluginContext);
257
+ }
258
+ }
259
+ };
260
+
261
+ return new AsyncIterableResult(iterator(this));
262
+ }
263
+
264
+ telemetry(): RuntimeTelemetryEvent | null {
265
+ return this._telemetry;
266
+ }
267
+
268
+ operations(): OperationRegistry {
269
+ return this.operationRegistry;
270
+ }
271
+
272
+ close(): Promise<void> {
273
+ const driver = this.driver as unknown as DriverWithClose<TDriver>;
274
+ if (typeof driver.close === 'function') {
275
+ return driver.close();
276
+ }
277
+ return Promise.resolve();
278
+ }
279
+ }
280
+
281
+ export function createRuntimeCore<TContract = unknown, TAdapter = unknown, TDriver = unknown>(
282
+ options: RuntimeCoreOptions<TContract, TAdapter, TDriver>,
283
+ ): RuntimeCore<TContract, TAdapter, TDriver> {
284
+ return new RuntimeCoreImpl(options);
285
+ }
@@ -0,0 +1,16 @@
1
+ import type { ExecutionPlan } from '@prisma-next/contract/types';
2
+
3
+ export interface MarkerStatement {
4
+ readonly sql: string;
5
+ readonly params: readonly unknown[];
6
+ }
7
+
8
+ export interface MarkerReader {
9
+ readMarkerStatement(): MarkerStatement;
10
+ }
11
+
12
+ export interface RuntimeFamilyAdapter<TContract = unknown> {
13
+ readonly contract: TContract;
14
+ readonly markerReader: MarkerReader;
15
+ validatePlan(plan: ExecutionPlan, contract: TContract): void;
16
+ }