@prisma-next/runtime-executor 0.3.0-dev.96 → 0.3.0-dev.98

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 CHANGED
@@ -64,7 +64,7 @@ const core = createRuntimeCore({
64
64
  driver,
65
65
  verify: { mode: 'onFirstUse', requireMarker: false },
66
66
  operationRegistry,
67
- plugins: [budgets()],
67
+ plugins: [],
68
68
  });
69
69
  ```
70
70
 
@@ -73,13 +73,10 @@ const core = createRuntimeCore({
73
73
  - `createRuntimeCore` - Create a target-neutral runtime instance
74
74
  - `RuntimeFamilyAdapter` - Interface for family runtimes
75
75
  - `MarkerReader` - Interface for marker reading
76
- - `budgets` - Target-neutral budgets plugin
77
76
  - `runtimeError` - Error envelope utilities
78
77
  - `computeSqlFingerprint` - SQL fingerprint computation
79
78
  - `parseContractMarkerRow` - Marker parsing utilities
80
79
 
81
- **Note:** The `lints` plugin has been migrated to the SQL domain. Import `lints` and `LintsOptions` from `@prisma-next/sql-runtime` instead. See [ADR 162](../../../docs/architecture docs/adrs/ADR 162 - Kysely lane emits PN SQL AST.md) for rationale.
82
-
83
80
  ## Testing
84
81
 
85
82
  Includes a mock-family smoke test (`test/mock-family.test.ts`) that proves runtime-executor can work without SQL dependencies.
package/dist/index.d.mts CHANGED
@@ -91,22 +91,6 @@ interface Plugin<TContract = unknown, TAdapter = unknown, TDriver = unknown> {
91
91
  afterExecute?(plan: ExecutionPlan, result: AfterExecuteResult, ctx: PluginContext<TContract, TAdapter, TDriver>): Promise<void>;
92
92
  }
93
93
  //#endregion
94
- //#region src/plugins/budgets.d.ts
95
- interface BudgetsOptions {
96
- readonly maxRows?: number;
97
- readonly defaultTableRows?: number;
98
- readonly tableRows?: Record<string, number>;
99
- readonly maxLatencyMs?: number;
100
- readonly severities?: {
101
- readonly rowCount?: 'warn' | 'error';
102
- readonly latency?: 'warn' | 'error';
103
- };
104
- readonly explain?: {
105
- readonly enabled?: boolean;
106
- };
107
- }
108
- declare function budgets<TContract = unknown, TAdapter = unknown, TDriver = unknown>(options?: BudgetsOptions): Plugin<TContract, TAdapter, TDriver>;
109
- //#endregion
110
94
  //#region src/runtime-spi.d.ts
111
95
  interface MarkerStatement {
112
96
  readonly sql: string;
@@ -165,5 +149,5 @@ interface RuntimeQueryable {
165
149
  }
166
150
  declare function createRuntimeCore<TContract = unknown, TAdapter = unknown, TDriver = unknown>(options: RuntimeCoreOptions<TContract, TAdapter, TDriver>): RuntimeCore<TContract, TAdapter, TDriver>;
167
151
  //#endregion
168
- export { AfterExecuteResult, AsyncIterableResult, BudgetFinding, BudgetsOptions, type ContractMarkerRecord, LintFinding, Log, MarkerReader, MarkerStatement, Plugin, PluginContext, RawGuardrailResult, RuntimeConnection, RuntimeCore, RuntimeCoreOptions, RuntimeErrorEnvelope, RuntimeFamilyAdapter, RuntimeQueryable, RuntimeTelemetryEvent, RuntimeTransaction, RuntimeVerifyOptions, Severity, TelemetryOutcome, budgets, computeSqlFingerprint, createRuntimeCore, evaluateRawGuardrails, parseContractMarkerRow, runtimeError };
152
+ export { AfterExecuteResult, AsyncIterableResult, BudgetFinding, type ContractMarkerRecord, LintFinding, Log, MarkerReader, MarkerStatement, Plugin, PluginContext, RawGuardrailResult, RuntimeConnection, RuntimeCore, RuntimeCoreOptions, RuntimeErrorEnvelope, RuntimeFamilyAdapter, RuntimeQueryable, RuntimeTelemetryEvent, RuntimeTransaction, RuntimeVerifyOptions, Severity, TelemetryOutcome, computeSqlFingerprint, createRuntimeCore, evaluateRawGuardrails, parseContractMarkerRow, runtimeError };
169
153
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/async-iterable-result.ts","../src/errors.ts","../src/fingerprint.ts","../src/guardrails/raw.ts","../src/marker.ts","../src/plugins/types.ts","../src/plugins/budgets.ts","../src/runtime-spi.ts","../src/runtime-core.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAMa,cAAA,mBAAmB,CAAA,GAAA,CAAA,YAAiB,aAAjB,CAA+B,GAA/B,CAAA,EAAqC,WAArC,CAAiD,GAAjD,EAAA,CAAA,CAAA;EAA+B,iBAAA,SAAA;EAAkB,QAAA,QAAA;EAMzC,QAAA,UAAA;EAAf,QAAA,oBAAA;EAIiB,WAAA,CAAA,SAAA,EAJjB,cAIiB,CAJF,GAIE,EAAA,IAAA,EAAA,OAAA,CAAA;EAAd,CAAzB,MAAA,CAAO,aAAA,GAAkB,EAAA,aAAA,CAAc,GAAd,CAAA;EAAzB;;;;EAwDwB,OAAA,CAAA,CAAA,EAjCd,OAiCc,CAjCN,GAiCM,EAAA,CAAA;EAAU,IAAA,CAAA,WADnB,GACmB,EAAA,EAAA,WAAA,KAAA,CAAA,CAAA,WAAA,CAAA,EAAA,CAAA,CAAA,KAAA,EAAV,GAAU,EAAA,EAAA,GAAA,QAAA,GAAW,WAAX,CAAuB,QAAvB,CAAA,CAAA,GAAA,SAAA,GAAA,IAAA,EAAA,UAAA,CAAA,EAAA,CAAA,CAAA,MAAA,EAAA,OAAA,EAAA,GACE,QADF,GACa,WADb,CACyB,QADzB,CAAA,CAAA,GAAA,SAAA,GAAA,IAAA,CAAA,EAEhC,WAFgC,CAEpB,QAFoB,GAET,QAFS,CAAA;;;;UCxEpB,oBAAA,SAA6B;;;;EDMjC,SAAA,OAAA,CAAA,ECFQ,MDEW,CAAA,MAAA,EAAA,OAAA,CAAA;;AAAiD,iBCCjE,YAAA,CDDiE,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,EAAA,OAAA,CAAA,ECIrE,MDJqE,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,ECK9E,oBDL8E;;;iBEAjE,qBAAA;;;KCJJ,YAAA;KACA,cAAA;UAEK,WAAA;EHCJ,SAAA,IAAA,EAAA,QAAmB,MAAA,EAAA;EAA+B,SAAA,QAAA,EGC1C,YHD0C;EAAkB,SAAA,OAAA,EAAA,MAAA;EAMzC,SAAA,OAAA,CAAA,EGHnB,MHGmB,CAAA,MAAA,EAAA,OAAA,CAAA;;AAIE,UGJzB,aAAA,CHIyB;EAAd,SAAA,IAAA,EAAA,UAAA,MAAA,EAAA;EAAzB,SAAO,QAAA,EGFW,cHEX;EAuBW,SAAA,OAAA,EAAA,MAAA;EAAR,SAAA,OAAA,CAAA,EGvBQ,MHuBR,CAAA,MAAA,EAAA,OAAA,CAAA;;AAiCc,UGrDV,kBAAA,CHqDU;EAAU,SAAA,OAAA,CAAA,EAAA;IAAuB,SAAA,uBAAA,CAAA,EGnDrB,cHmDqB;IAAZ,SAAA,aAAA,CAAA,EAAA,MAAA;EACT,CAAA;;AAAW,UG/CjC,kBAAA,CH+CiC;EACjC,SAAA,KAAA,EG/CC,WH+CD,EAAA;EAAW,SAAA,OAAA,EG9CR,aH8CQ,EAAA;EAAvB,SAAA,SAAA,EAAA,QAAA,GAAA,UAAA,GAAA,OAAA;;AApEgE,iBGgCrD,qBAAA,CHhCqD,IAAA,EGiC7D,aHjC6D,EAAA,MAAA,CAAA,EGkC1D,kBHlC0D,CAAA,EGmClE,kBHnCkE;;;AAUlE,iBIoCa,sBAAA,CJpCN,GAAA,EAAA,OAAA,CAAA,EIoC4C,sBJpC5C;;;KKdE,QAAA;UAEK,GAAA;;ELEJ,IAAA,CAAA,KAAA,EAAA,OAAA,CAAA,EAAmB,IAAA;EAA+B,KAAA,CAAA,KAAA,EAAA,OAAA,CAAA,EAAA,IAAA;;AAMvB,UKFvB,aLEuB,CAAA,YAAA,OAAA,EAAA,WAAA,OAAA,EAAA,UAAA,OAAA,CAAA,CAAA;EAAf,SAAA,QAAA,EKDJ,SLCI;EAIiB,SAAA,OAAA,EKJtB,QLIsB;EAAd,SAAA,MAAA,EKHT,OLGS;EAAzB,SAAO,IAAA,EAAA,QAAA,GAAA,YAAA;EAuBW,SAAA,GAAA,EAAA,GAAA,GAAA,MAAA;EAAR,SAAA,GAAA,EKvBG,GLuBH;;AAiCc,UKrDV,kBAAA,CLqDU;EAAU,SAAA,QAAA,EAAA,MAAA;EAAuB,SAAA,SAAA,EAAA,MAAA;EAAZ,SAAA,SAAA,EAAA,OAAA;;AACc,UKhD7C,MLgD6C,CAAA,YAAA,OAAA,EAAA,WAAA,OAAA,EAAA,UAAA,OAAA,CAAA,CAAA;EAAZ,SAAA,IAAA,EAAA,MAAA;EACjC,aAAA,EAAA,IAAA,EK9CP,aL8CO,EAAA,GAAA,EK7CR,aL6CQ,CK7CM,SL6CN,EK7CiB,QL6CjB,EK7C2B,OL6C3B,CAAA,CAAA,EK5CZ,OL4CY,CAAA,IAAA,CAAA;EAAW,KAAA,EAAA,GAAA,EK1CnB,ML0CmB,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,IAAA,EKzClB,aLyCkB,EAAA,GAAA,EKxCnB,aLwCmB,CKxCL,SLwCK,EKxCM,QLwCN,EKxCgB,OLwChB,CAAA,CAAA,EKvCvB,OLuCuB,CAAA,IAAA,CAAA;EAAvB,YAAA,EAAA,IAAA,EKrCK,aLqCL,EAAA,MAAA,EKpCO,kBLoCP,EAAA,GAAA,EKnCI,aLmCJ,CKnCkB,SLmClB,EKnC6B,QLmC7B,EKnCuC,OLmCvC,CAAA,CAAA,EKlCA,OLkCA,CAAA,IAAA,CAAA;;;;UMvEY,cAAA;;;ENGJ,SAAA,SAAA,CAAA,EMAU,MNAS,CAAA,MAAA,EAAA,MAAA,CAAA;EAA+B,SAAA,YAAA,CAAA,EAAA,MAAA;EAAkB,SAAA,UAAA,CAAA,EAAA;IAMzC,SAAA,QAAA,CAAA,EAAA,MAAA,GAAA,OAAA;IAAf,SAAA,OAAA,CAAA,EAAA,MAAA,GAAA,OAAA;EAIiB,CAAA;EAAd,SAAA,OAAA,CAAA,EAAA;IAAlB,SAAA,OAAA,CAAA,EAAA,OAAA;EAuBW,CAAA;;AAgCH,iBMwEF,ONxEE,CAAA,YAAA,OAAA,EAAA,WAAA,OAAA,EAAA,UAAA,OAAA,CAAA,CAAA,OAAA,CAAA,EMyEN,cNzEM,CAAA,EM0Ef,MN1Ee,CM0ER,SN1EQ,EM0EG,QN1EH,EM0Ea,ON1Eb,CAAA;;;UOrED,eAAA;;;APIjB;AAA+D,UOC9C,YAAA,CPD8C;EAAkB,mBAAA,EAAA,EOExD,ePFwD;;AAMxD,UODR,oBPCQ,CAAA,YAAA,OAAA,CAAA,CAAA;EAIiB,SAAA,QAAA,EOJrB,SPIqB;EAAd,SAAA,YAAA,EOHH,YPGG;EAAzB,YAAO,CAAA,IAAA,EOFW,aPEX,EAAA,QAAA,EOFoC,SPEpC,CAAA,EAAA,IAAA;;;;AAVqD,UQG9C,oBAAA,CRH8C;EAAkB,SAAA,IAAA,EAAA,YAAA,GAAA,SAAA,GAAA,QAAA;EAMzC,SAAA,aAAA,EAAA,OAAA;;AAIE,KQF9B,gBAAA,GRE8B,SAAA,GAAA,eAAA;AAAd,UQAX,qBAAA,CRAW;EAAzB,SAAO,IAAA,EAAA,MAAA;EAuBW,SAAA,MAAA,EAAA,MAAA;EAAR,SAAA,WAAA,EAAA,MAAA;EAgCK,SAAA,OAAA,EQnDE,gBRmDF;EACS,SAAA,UAAA,CAAA,EAAA,MAAA;;AAAiC,UQhD3C,kBRgD2C,CAAA,YAAA,OAAA,EAAA,WAAA,OAAA,EAAA,UAAA,OAAA,CAAA,CAAA;EAAZ,SAAA,aAAA,EQ/CtB,oBR+CsB,CQ/CD,SR+CC,CAAA;EACT,SAAA,MAAA,EQ/CpB,OR+CoB;EAAuB,SAAA,MAAA,EQ9C3C,oBR8C2C;EAAZ,SAAA,OAAA,CAAA,EAAA,SQ7CpB,MR6CoB,CQ7Cb,SR6Ca,EQ7CF,QR6CE,EQ7CQ,OR6CR,CAAA,EAAA;EACjC,SAAA,IAAA,CAAA,EAAA,QAAA,GAAA,YAAA;EAAW,SAAA,GAAA,CAAA,EQ5CX,GR4CW;EAAvB,SAAA,iBAAA,EQ3CyB,iBR2CzB;;AApEgE,UQ4BpD,WR5BoD,CAAA,YAAA,OAAA,EAAA,WAAA,OAAA,EAAA,UAAA,OAAA,CAAA,SQ6B3D,gBR7B2D,CAAA;EAAW,SAAA,aAAA,CAAA,EQ+BrD,SR/BqD;0BQgCtD;yBACD;gBACT,QAAQ;EPxCP,SAAA,EAAA,EOyCF,qBPrCM,GAAA,IAAA;EAGL,KAAA,EAAA,EOmCL,OPnCiB,CAAA,IAAA,CAAA;gBOoCZ;;UAGC,iBAAA,SAA0B;ENxC3B,WAAA,EAAA,EMyCC,ONzCD,CMyCS,kBNzCY,CAAA;aM0CxB;;UAGI,kBAAA,SAA2B;ELjDhC,MAAA,EAAA,EKkDA,OLlDY,CAAA,IAAA,CAAA;EACZ,QAAA,EAAA,EKkDE,OLlDY,CAAA,IAAA,CAAA;AAE1B;AAOiB,UK4CA,gBAAA,CL1CI;EAKJ,OAAA,CAAA,MKsCD,MLtCC,CAAkB,MAAA,EAAA,OAEI,CAAA,CAAA,CAAA,IAAA,EKoCQ,aLpCM,CKoCQ,GLpCR,CAAA,CAAA,EKoCe,mBLpCf,CKoCmC,GLpCnC,CAAA;AAKrD;AEhBiB,iBG2VD,iBH3Vc,CAAA,YAAA,OAAA,EAAA,WAAA,OAAA,EAAA,UAAA,OAAA,CAAA,CAAA,OAAA,EG4VnB,kBH5VmB,CG4VA,SH5VA,EG4VW,QH5VX,EG4VqB,OH5VrB,CAAA,CAAA,EG6V3B,WH7V2B,CG6Vf,SH7Ve,EG6VJ,QH7VI,EG6VM,OH7VN,CAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/async-iterable-result.ts","../src/errors.ts","../src/fingerprint.ts","../src/guardrails/raw.ts","../src/marker.ts","../src/plugins/types.ts","../src/runtime-spi.ts","../src/runtime-core.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -96,7 +96,7 @@ const READ_ONLY_INTENTS = new Set([
96
96
  ]);
97
97
  function evaluateRawGuardrails(plan, config) {
98
98
  const lints = [];
99
- const budgets$1 = [];
99
+ const budgets = [];
100
100
  const normalized = normalizeWhitespace(plan.sql);
101
101
  const statementType = classifyStatement(normalized);
102
102
  if (statementType === "select") {
@@ -104,7 +104,7 @@ function evaluateRawGuardrails(plan, config) {
104
104
  if (!LIMIT_REGEX.test(normalized)) {
105
105
  const severity = config?.budgets?.unboundedSelectSeverity ?? "error";
106
106
  lints.push(createLint("LINT.NO_LIMIT", "warn", "Raw SQL plan omits LIMIT clause", { sql: snippet(plan.sql) }));
107
- budgets$1.push(createBudget("BUDGET.ROWS_EXCEEDED", severity, "Raw SQL plan is unbounded and may exceed row budget", {
107
+ budgets.push(createBudget("BUDGET.ROWS_EXCEEDED", severity, "Raw SQL plan is unbounded and may exceed row budget", {
108
108
  sql: snippet(plan.sql),
109
109
  ...config?.budgets?.estimatedRows !== void 0 ? { estimatedRows: config.budgets.estimatedRows } : {}
110
110
  }));
@@ -118,7 +118,7 @@ function evaluateRawGuardrails(plan, config) {
118
118
  if (refs) evaluateIndexCoverage(refs, lints);
119
119
  return {
120
120
  lints,
121
- budgets: budgets$1,
121
+ budgets,
122
122
  statement: statementType
123
123
  };
124
124
  }
@@ -217,171 +217,6 @@ function parseContractMarkerRow(row) {
217
217
  };
218
218
  }
219
219
 
220
- //#endregion
221
- //#region src/plugins/budgets.ts
222
- async function computeEstimatedRows(plan, driver) {
223
- if (typeof driver.explain !== "function") return;
224
- try {
225
- return extractEstimatedRows((await driver.explain(plan.sql, [...plan.params])).rows);
226
- } catch {
227
- return;
228
- }
229
- }
230
- function extractEstimatedRows(rows) {
231
- for (const row of rows) {
232
- const estimate = findPlanRows(row);
233
- if (estimate !== void 0) return estimate;
234
- }
235
- }
236
- function findPlanRows(node) {
237
- if (!node || typeof node !== "object") return;
238
- const explainNode = node;
239
- const planRows = explainNode["Plan Rows"];
240
- if (typeof planRows === "number") return planRows;
241
- if ("Plan" in explainNode && explainNode.Plan !== void 0) {
242
- const nested = findPlanRows(explainNode.Plan);
243
- if (nested !== void 0) return nested;
244
- }
245
- if (Array.isArray(explainNode.Plans)) for (const child of explainNode.Plans) {
246
- const nested = findPlanRows(child);
247
- if (nested !== void 0) return nested;
248
- }
249
- for (const value of Object.values(node)) if (typeof value === "object" && value !== null) {
250
- const nested = findPlanRows(value);
251
- if (nested !== void 0) return nested;
252
- }
253
- }
254
- function budgetError(code, message, details) {
255
- const error = new Error(message);
256
- Object.defineProperty(error, "name", {
257
- value: "RuntimeError",
258
- configurable: true
259
- });
260
- return Object.assign(error, {
261
- code,
262
- category: "BUDGET",
263
- severity: "error",
264
- details
265
- });
266
- }
267
- function estimateRows(plan, tableRows, defaultTableRows) {
268
- const table = plan.meta.refs?.tables?.[0];
269
- if (!table) return null;
270
- const tableEstimate = tableRows[table] ?? defaultTableRows;
271
- const limit = plan.meta.annotations?.["limit"];
272
- if (typeof limit === "number") return Math.min(limit, tableEstimate);
273
- return tableEstimate;
274
- }
275
- function hasDetectableLimit(plan) {
276
- return typeof plan.meta.annotations?.["limit"] === "number";
277
- }
278
- function budgets(options) {
279
- const maxRows = options?.maxRows ?? 1e4;
280
- const defaultTableRows = options?.defaultTableRows ?? 1e4;
281
- const tableRows = options?.tableRows ?? {};
282
- const maxLatencyMs = options?.maxLatencyMs ?? 1e3;
283
- const rowSeverity = options?.severities?.rowCount ?? "error";
284
- const latencySeverity = options?.severities?.latency ?? "warn";
285
- let observedRows = 0;
286
- return Object.freeze({
287
- name: "budgets",
288
- async beforeExecute(plan, ctx) {
289
- observedRows = 0;
290
- ctx.now();
291
- const estimated = estimateRows(plan, tableRows, defaultTableRows);
292
- const isUnbounded = !hasDetectableLimit(plan);
293
- const isSelect = plan.sql.trimStart().toUpperCase().startsWith("SELECT");
294
- if (isSelect && isUnbounded) {
295
- if (estimated !== null && estimated >= maxRows) {
296
- const error$1 = budgetError("BUDGET.ROWS_EXCEEDED", "Unbounded SELECT query exceeds budget", {
297
- source: "heuristic",
298
- estimatedRows: estimated,
299
- maxRows
300
- });
301
- if (rowSeverity === "error" || ctx.mode === "strict") throw error$1;
302
- ctx.log.warn({
303
- code: error$1.code,
304
- message: error$1.message,
305
- details: error$1.details
306
- });
307
- return;
308
- }
309
- const error = budgetError("BUDGET.ROWS_EXCEEDED", "Unbounded SELECT query exceeds budget", {
310
- source: "heuristic",
311
- maxRows
312
- });
313
- if (rowSeverity === "error" || ctx.mode === "strict") throw error;
314
- ctx.log.warn({
315
- code: error.code,
316
- message: error.message,
317
- details: error.details
318
- });
319
- return;
320
- }
321
- if (estimated !== null) {
322
- if (estimated > maxRows) {
323
- const error = budgetError("BUDGET.ROWS_EXCEEDED", "Estimated row count exceeds budget", {
324
- source: "heuristic",
325
- estimatedRows: estimated,
326
- maxRows
327
- });
328
- if (rowSeverity === "error" || ctx.mode === "strict") throw error;
329
- ctx.log.warn({
330
- code: error.code,
331
- message: error.message,
332
- details: error.details
333
- });
334
- }
335
- return;
336
- }
337
- if (!plan.ast) {
338
- if (options?.explain?.enabled === true && isSelect && typeof ctx.driver === "object" && ctx.driver !== null) {
339
- const estimatedRows = await computeEstimatedRows(plan, ctx.driver);
340
- if (estimatedRows !== void 0) {
341
- if (estimatedRows > maxRows) {
342
- const error = budgetError("BUDGET.ROWS_EXCEEDED", "Estimated row count exceeds budget", {
343
- source: "explain",
344
- estimatedRows,
345
- maxRows
346
- });
347
- if (rowSeverity === "error" || ctx.mode === "strict") throw error;
348
- ctx.log.warn({
349
- code: error.code,
350
- message: error.message,
351
- details: error.details
352
- });
353
- }
354
- return;
355
- }
356
- }
357
- }
358
- },
359
- async onRow(_row, _plan, _ctx) {
360
- observedRows += 1;
361
- if (observedRows > maxRows) throw budgetError("BUDGET.ROWS_EXCEEDED", "Observed row count exceeds budget", {
362
- source: "observed",
363
- observedRows,
364
- maxRows
365
- });
366
- },
367
- async afterExecute(_plan, result, ctx) {
368
- const latencyMs = result.latencyMs;
369
- if (latencyMs > maxLatencyMs) {
370
- const error = budgetError("BUDGET.TIME_EXCEEDED", "Query latency exceeds budget", {
371
- latencyMs,
372
- maxLatencyMs
373
- });
374
- if (latencySeverity === "error" && ctx.mode === "strict") throw error;
375
- ctx.log.warn({
376
- code: error.code,
377
- message: error.message,
378
- details: error.details
379
- });
380
- }
381
- }
382
- });
383
- }
384
-
385
220
  //#endregion
386
221
  //#region src/runtime-core.ts
387
222
  var RuntimeCoreImpl = class {
@@ -551,5 +386,5 @@ function createRuntimeCore(options) {
551
386
  }
552
387
 
553
388
  //#endregion
554
- export { AsyncIterableResult, budgets, computeSqlFingerprint, createRuntimeCore, evaluateRawGuardrails, parseContractMarkerRow, runtimeError };
389
+ export { AsyncIterableResult, computeSqlFingerprint, createRuntimeCore, evaluateRawGuardrails, parseContractMarkerRow, runtimeError };
555
390
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["out: Row[]","lints: LintFinding[]","budgets: BudgetFinding[]","parsed: unknown","error","#executeWith","latencyMs"],"sources":["../src/errors.ts","../src/async-iterable-result.ts","../src/fingerprint.ts","../src/guardrails/raw.ts","../src/marker.ts","../src/plugins/budgets.ts","../src/runtime-core.ts"],"sourcesContent":["export interface RuntimeErrorEnvelope extends Error {\n readonly code: string;\n readonly category: 'PLAN' | 'CONTRACT' | 'LINT' | 'BUDGET' | 'RUNTIME';\n readonly severity: 'error';\n readonly details?: Record<string, unknown>;\n}\n\nexport function runtimeError(\n code: string,\n message: string,\n details?: Record<string, unknown>,\n): RuntimeErrorEnvelope {\n const error = new Error(message) as RuntimeErrorEnvelope;\n Object.defineProperty(error, 'name', {\n value: 'RuntimeError',\n configurable: true,\n });\n\n return Object.assign(error, {\n code,\n category: resolveCategory(code),\n severity: 'error' as const,\n message,\n details,\n });\n}\n\nfunction resolveCategory(code: string): RuntimeErrorEnvelope['category'] {\n const prefix = code.split('.')[0] ?? 'RUNTIME';\n switch (prefix) {\n case 'PLAN':\n case 'CONTRACT':\n case 'LINT':\n case 'BUDGET':\n return prefix;\n default:\n return 'RUNTIME';\n }\n}\n","import { runtimeError } from './errors';\n\n/**\n * Custom async iterable result that extends AsyncIterable with a toArray() method.\n * This provides a convenient way to collect all results from an async iterator.\n */\nexport class AsyncIterableResult<Row> implements AsyncIterable<Row>, PromiseLike<Row[]> {\n private readonly generator: AsyncGenerator<Row, void, unknown>;\n private consumed = false;\n private consumedBy: 'bufferedArray' | 'iterator' | undefined;\n private bufferedArrayPromise: Promise<Row[]> | undefined;\n\n constructor(generator: AsyncGenerator<Row, void, unknown>) {\n this.generator = generator;\n }\n\n [Symbol.asyncIterator](): AsyncIterator<Row> {\n if (this.consumed) {\n throw runtimeError(\n 'RUNTIME.ITERATOR_CONSUMED',\n `AsyncIterableResult iterator has already been consumed via ${this.consumedBy === 'bufferedArray' ? 'toArray()/then()' : 'for-await loop'}. Each AsyncIterableResult can only be iterated once.`,\n {\n consumedBy: this.consumedBy,\n suggestion:\n this.consumedBy === 'bufferedArray'\n ? 'If you need to iterate multiple times, store the results from toArray() in a variable and reuse that.'\n : 'If you need to iterate multiple times, use toArray() to collect all results first.',\n },\n );\n }\n this.consumed = true;\n this.consumedBy = 'iterator';\n return this.generator;\n }\n\n /**\n * Collects all values from the async iterator into an array.\n * Once called, the iterator is consumed and cannot be reused.\n */\n toArray(): Promise<Row[]> {\n if (this.consumedBy === 'iterator') {\n return Promise.reject(\n runtimeError(\n 'RUNTIME.ITERATOR_CONSUMED',\n 'AsyncIterableResult iterator has already been consumed via for-await loop. Each AsyncIterableResult can only be iterated once.',\n {\n consumedBy: this.consumedBy,\n suggestion:\n 'The iterator was already consumed by a for-await loop. Use toArray() or await the result before iterating.',\n },\n ),\n );\n }\n\n if (this.bufferedArrayPromise) {\n return this.bufferedArrayPromise;\n }\n\n this.consumed = true;\n this.consumedBy = 'bufferedArray';\n this.bufferedArrayPromise = (async () => {\n const out: Row[] = [];\n for await (const item of this.generator) {\n out.push(item);\n }\n return out;\n })();\n return this.bufferedArrayPromise;\n }\n\n // biome-ignore lint/suspicious/noThenProperty: PromiseLike implementation is intentional for await support.\n then<TResult1 = Row[], TResult2 = never>(\n onfulfilled?: ((value: Row[]) => TResult1 | PromiseLike<TResult1>) | undefined | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | undefined | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.toArray().then(onfulfilled, onrejected);\n }\n}\n","import { createHash } from 'node:crypto';\n\nconst STRING_LITERAL_REGEX = /'(?:''|[^'])*'/g;\nconst NUMERIC_LITERAL_REGEX = /\\b\\d+(?:\\.\\d+)?\\b/g;\nconst WHITESPACE_REGEX = /\\s+/g;\n\nexport function computeSqlFingerprint(sql: string): string {\n const withoutStrings = sql.replace(STRING_LITERAL_REGEX, '?');\n const withoutNumbers = withoutStrings.replace(NUMERIC_LITERAL_REGEX, '?');\n const normalized = withoutNumbers.replace(WHITESPACE_REGEX, ' ').trim().toLowerCase();\n\n const hash = createHash('sha256').update(normalized).digest('hex');\n return `sha256:${hash}`;\n}\n","import type { ExecutionPlan, PlanMeta, PlanRefs } from '@prisma-next/contract/types';\n\nexport type LintSeverity = 'error' | 'warn';\nexport type BudgetSeverity = 'error' | 'warn';\n\nexport interface LintFinding {\n readonly code: `LINT.${string}`;\n readonly severity: LintSeverity;\n readonly message: string;\n readonly details?: Record<string, unknown>;\n}\n\nexport interface BudgetFinding {\n readonly code: `BUDGET.${string}`;\n readonly severity: BudgetSeverity;\n readonly message: string;\n readonly details?: Record<string, unknown>;\n}\n\nexport interface RawGuardrailConfig {\n readonly budgets?: {\n readonly unboundedSelectSeverity?: BudgetSeverity;\n readonly estimatedRows?: number;\n };\n}\n\nexport interface RawGuardrailResult {\n readonly lints: LintFinding[];\n readonly budgets: BudgetFinding[];\n readonly statement: 'select' | 'mutation' | 'other';\n}\n\nconst SELECT_STAR_REGEX = /select\\s+\\*/i;\nconst LIMIT_REGEX = /\\blimit\\b/i;\nconst MUTATION_PREFIX_REGEX = /^(insert|update|delete|create|alter|drop|truncate)\\b/i;\n\nconst READ_ONLY_INTENTS = new Set(['read', 'report', 'readonly']);\n\nexport function evaluateRawGuardrails(\n plan: ExecutionPlan,\n config?: RawGuardrailConfig,\n): RawGuardrailResult {\n const lints: LintFinding[] = [];\n const budgets: BudgetFinding[] = [];\n\n const normalized = normalizeWhitespace(plan.sql);\n const statementType = classifyStatement(normalized);\n\n if (statementType === 'select') {\n if (SELECT_STAR_REGEX.test(normalized)) {\n lints.push(\n createLint('LINT.SELECT_STAR', 'error', 'Raw SQL plan selects all columns via *', {\n sql: snippet(plan.sql),\n }),\n );\n }\n\n if (!LIMIT_REGEX.test(normalized)) {\n const severity = config?.budgets?.unboundedSelectSeverity ?? 'error';\n lints.push(\n createLint('LINT.NO_LIMIT', 'warn', 'Raw SQL plan omits LIMIT clause', {\n sql: snippet(plan.sql),\n }),\n );\n\n budgets.push(\n createBudget(\n 'BUDGET.ROWS_EXCEEDED',\n severity,\n 'Raw SQL plan is unbounded and may exceed row budget',\n {\n sql: snippet(plan.sql),\n ...(config?.budgets?.estimatedRows !== undefined\n ? { estimatedRows: config.budgets.estimatedRows }\n : {}),\n },\n ),\n );\n }\n }\n\n if (isMutationStatement(statementType) && isReadOnlyIntent(plan.meta)) {\n lints.push(\n createLint(\n 'LINT.READ_ONLY_MUTATION',\n 'error',\n 'Raw SQL plan mutates data despite read-only intent',\n {\n sql: snippet(plan.sql),\n intent: plan.meta.annotations?.['intent'],\n },\n ),\n );\n }\n\n const refs = plan.meta.refs;\n if (refs) {\n evaluateIndexCoverage(refs, lints);\n }\n\n return { lints, budgets, statement: statementType };\n}\n\nfunction evaluateIndexCoverage(refs: PlanRefs, lints: LintFinding[]) {\n const predicateColumns = refs.columns ?? [];\n if (predicateColumns.length === 0) {\n return;\n }\n\n const indexes = refs.indexes ?? [];\n\n if (indexes.length === 0) {\n lints.push(\n createLint(\n 'LINT.UNINDEXED_PREDICATE',\n 'warn',\n 'Raw SQL plan predicates lack supporting indexes',\n {\n predicates: predicateColumns,\n },\n ),\n );\n return;\n }\n\n const hasSupportingIndex = predicateColumns.every((column) =>\n indexes.some(\n (index) =>\n index.table === column.table &&\n index.columns.some((col) => col.toLowerCase() === column.column.toLowerCase()),\n ),\n );\n\n if (!hasSupportingIndex) {\n lints.push(\n createLint(\n 'LINT.UNINDEXED_PREDICATE',\n 'warn',\n 'Raw SQL plan predicates lack supporting indexes',\n {\n predicates: predicateColumns,\n },\n ),\n );\n }\n}\n\nfunction classifyStatement(sql: string): 'select' | 'mutation' | 'other' {\n const trimmed = sql.trim();\n const lower = trimmed.toLowerCase();\n\n if (lower.startsWith('with')) {\n if (lower.includes('select')) {\n return 'select';\n }\n }\n\n if (lower.startsWith('select')) {\n return 'select';\n }\n\n if (MUTATION_PREFIX_REGEX.test(trimmed)) {\n return 'mutation';\n }\n\n return 'other';\n}\n\nfunction isMutationStatement(statement: 'select' | 'mutation' | 'other'): boolean {\n return statement === 'mutation';\n}\n\nfunction isReadOnlyIntent(meta: PlanMeta): boolean {\n const annotations = meta.annotations as { intent?: string } | undefined;\n const intent =\n typeof annotations?.intent === 'string' ? annotations.intent.toLowerCase() : undefined;\n return intent !== undefined && READ_ONLY_INTENTS.has(intent);\n}\n\nfunction normalizeWhitespace(value: string): string {\n return value.replace(/\\s+/g, ' ').trim();\n}\n\nfunction snippet(sql: string): string {\n return normalizeWhitespace(sql).slice(0, 200);\n}\n\nfunction createLint(\n code: LintFinding['code'],\n severity: LintFinding['severity'],\n message: string,\n details?: Record<string, unknown>,\n): LintFinding {\n return { code, severity, message, ...(details ? { details } : {}) };\n}\n\nfunction createBudget(\n code: BudgetFinding['code'],\n severity: BudgetFinding['severity'],\n message: string,\n details?: Record<string, unknown>,\n): BudgetFinding {\n return { code, severity, message, ...(details ? { details } : {}) };\n}\n","import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport { type } from 'arktype';\n\n// Re-export for backward compatibility\nexport type { ContractMarkerRecord } from '@prisma-next/contract/types';\n\nexport interface ContractMarkerRow {\n core_hash: string;\n profile_hash: string;\n contract_json: unknown | null;\n canonical_version: number | null;\n updated_at: Date;\n app_tag: string | null;\n meta: unknown | null;\n}\n\nconst MetaSchema = type({ '[string]': 'unknown' });\n\nfunction parseMeta(meta: unknown): Record<string, unknown> {\n if (meta === null || meta === undefined) {\n return {};\n }\n\n let parsed: unknown;\n if (typeof meta === 'string') {\n try {\n parsed = JSON.parse(meta);\n } catch {\n return {};\n }\n } else {\n parsed = meta;\n }\n\n const result = MetaSchema(parsed);\n if (result instanceof type.errors) {\n return {};\n }\n\n return result as Record<string, unknown>;\n}\n\nconst ContractMarkerRowSchema = type({\n core_hash: 'string',\n profile_hash: 'string',\n 'contract_json?': 'unknown | null',\n 'canonical_version?': 'number | null',\n 'updated_at?': 'Date | string',\n 'app_tag?': 'string | null',\n 'meta?': 'unknown | null',\n});\n\nexport function parseContractMarkerRow(row: unknown): ContractMarkerRecord {\n const result = ContractMarkerRowSchema(row);\n if (result instanceof type.errors) {\n const messages = result.map((p: { message: string }) => p.message).join('; ');\n throw new Error(`Invalid contract marker row: ${messages}`);\n }\n\n const validatedRow = result as {\n core_hash: string;\n profile_hash: string;\n contract_json?: unknown | null;\n canonical_version?: number | null;\n updated_at?: Date | string;\n app_tag?: string | null;\n meta?: unknown | null;\n };\n\n const updatedAt = validatedRow.updated_at\n ? validatedRow.updated_at instanceof Date\n ? validatedRow.updated_at\n : new Date(validatedRow.updated_at)\n : new Date();\n\n return {\n storageHash: validatedRow.core_hash,\n profileHash: validatedRow.profile_hash,\n contractJson: validatedRow.contract_json ?? null,\n canonicalVersion: validatedRow.canonical_version ?? null,\n updatedAt,\n appTag: validatedRow.app_tag ?? null,\n meta: parseMeta(validatedRow.meta),\n };\n}\n","import type { ExecutionPlan } from '@prisma-next/contract/types';\nimport type { AfterExecuteResult, Plugin, PluginContext } from './types';\n\nexport interface BudgetsOptions {\n readonly maxRows?: number;\n readonly defaultTableRows?: number;\n readonly tableRows?: Record<string, number>;\n readonly maxLatencyMs?: number;\n readonly severities?: {\n readonly rowCount?: 'warn' | 'error';\n readonly latency?: 'warn' | 'error';\n };\n readonly explain?: {\n readonly enabled?: boolean;\n };\n}\n\ninterface DriverWithExplain {\n explain?(\n sql: string,\n params: unknown[],\n ): Promise<{ rows: ReadonlyArray<Record<string, unknown>> }>;\n}\n\nasync function computeEstimatedRows(\n plan: ExecutionPlan,\n driver: DriverWithExplain,\n): Promise<number | undefined> {\n if (typeof driver.explain !== 'function') {\n return undefined;\n }\n\n try {\n const result = await driver.explain(plan.sql, [...plan.params]);\n return extractEstimatedRows(result.rows);\n } catch {\n return undefined;\n }\n}\n\nfunction extractEstimatedRows(rows: ReadonlyArray<Record<string, unknown>>): number | undefined {\n for (const row of rows) {\n const estimate = findPlanRows(row);\n if (estimate !== undefined) {\n return estimate;\n }\n }\n\n return undefined;\n}\n\ntype ExplainNode = {\n Plan?: unknown;\n Plans?: unknown[];\n 'Plan Rows'?: number;\n [key: string]: unknown;\n};\n\nfunction findPlanRows(node: unknown): number | undefined {\n if (!node || typeof node !== 'object') {\n return undefined;\n }\n\n const explainNode = node as ExplainNode;\n const planRows = explainNode['Plan Rows'];\n if (typeof planRows === 'number') {\n return planRows;\n }\n\n if ('Plan' in explainNode && explainNode.Plan !== undefined) {\n const nested = findPlanRows(explainNode.Plan);\n if (nested !== undefined) {\n return nested;\n }\n }\n\n if (Array.isArray(explainNode.Plans)) {\n for (const child of explainNode.Plans) {\n const nested = findPlanRows(child);\n if (nested !== undefined) {\n return nested;\n }\n }\n }\n\n for (const value of Object.values(node as Record<string, unknown>)) {\n if (typeof value === 'object' && value !== null) {\n const nested = findPlanRows(value);\n if (nested !== undefined) {\n return nested;\n }\n }\n }\n\n return undefined;\n}\n\nfunction budgetError(code: string, message: string, details?: Record<string, unknown>) {\n const error = new Error(message) as Error & {\n code: string;\n category: 'BUDGET';\n severity: 'error';\n details?: Record<string, unknown>;\n };\n Object.defineProperty(error, 'name', {\n value: 'RuntimeError',\n configurable: true,\n });\n return Object.assign(error, {\n code,\n category: 'BUDGET' as const,\n severity: 'error' as const,\n details,\n });\n}\n\nfunction estimateRows(\n plan: ExecutionPlan,\n tableRows: Record<string, number>,\n defaultTableRows: number,\n): number | null {\n const table = plan.meta.refs?.tables?.[0];\n if (!table) {\n return null;\n }\n\n const tableEstimate = tableRows[table] ?? defaultTableRows;\n\n // TODO: this is really not great. It should instead walk the AST.\n // It also doesn't check in any way that this limit correlates with the current table.\n // Move this plugin to a higher level (close to the linter) and use AST visitors.\n const limit = plan.meta.annotations?.['limit'];\n if (typeof limit === 'number') {\n return Math.min(limit, tableEstimate);\n }\n\n return tableEstimate;\n}\n\nfunction hasDetectableLimit(plan: ExecutionPlan): boolean {\n return typeof plan.meta.annotations?.['limit'] === 'number';\n}\n\nexport function budgets<TContract = unknown, TAdapter = unknown, TDriver = unknown>(\n options?: BudgetsOptions,\n): Plugin<TContract, TAdapter, TDriver> {\n const maxRows = options?.maxRows ?? 10_000;\n const defaultTableRows = options?.defaultTableRows ?? 10_000;\n const tableRows = options?.tableRows ?? {};\n const maxLatencyMs = options?.maxLatencyMs ?? 1_000;\n const rowSeverity = options?.severities?.rowCount ?? 'error';\n const latencySeverity = options?.severities?.latency ?? 'warn';\n\n let observedRows = 0;\n\n return Object.freeze({\n name: 'budgets',\n\n async beforeExecute(plan: ExecutionPlan, ctx: PluginContext<TContract, TAdapter, TDriver>) {\n observedRows = 0;\n void ctx.now();\n\n const estimated = estimateRows(plan, tableRows, defaultTableRows);\n const isUnbounded = !hasDetectableLimit(plan);\n const sqlUpper = plan.sql.trimStart().toUpperCase();\n const isSelect = sqlUpper.startsWith('SELECT');\n\n // Check for unbounded queries first - these should always error if they exceed or equal the budget\n if (isSelect && isUnbounded) {\n if (estimated !== null && estimated >= maxRows) {\n const error = budgetError(\n 'BUDGET.ROWS_EXCEEDED',\n 'Unbounded SELECT query exceeds budget',\n {\n source: 'heuristic',\n estimatedRows: estimated,\n maxRows,\n },\n );\n\n const shouldBlock = rowSeverity === 'error' || ctx.mode === 'strict';\n if (shouldBlock) {\n throw error;\n }\n ctx.log.warn({\n code: error.code,\n message: error.message,\n details: error.details,\n });\n return;\n }\n\n // Even if we can't estimate, unbounded queries should error\n const error = budgetError('BUDGET.ROWS_EXCEEDED', 'Unbounded SELECT query exceeds budget', {\n source: 'heuristic',\n maxRows,\n });\n\n const shouldBlock = rowSeverity === 'error' || ctx.mode === 'strict';\n if (shouldBlock) {\n throw error;\n }\n ctx.log.warn({\n code: error.code,\n message: error.message,\n details: error.details,\n });\n return;\n }\n\n // For bounded queries, check if estimated exceeds budget\n if (estimated !== null) {\n if (estimated > maxRows) {\n const error = budgetError('BUDGET.ROWS_EXCEEDED', 'Estimated row count exceeds budget', {\n source: 'heuristic',\n estimatedRows: estimated,\n maxRows,\n });\n\n const shouldBlock = rowSeverity === 'error' || ctx.mode === 'strict';\n if (shouldBlock) {\n throw error;\n }\n ctx.log.warn({\n code: error.code,\n message: error.message,\n details: error.details,\n });\n }\n return;\n }\n\n // Fallback: if no AST, try EXPLAIN if enabled\n if (!plan.ast) {\n const explainEnabled = options?.explain?.enabled === true;\n\n if (explainEnabled && isSelect && typeof ctx.driver === 'object' && ctx.driver !== null) {\n const estimatedRows = await computeEstimatedRows(plan, ctx.driver as DriverWithExplain);\n if (estimatedRows !== undefined) {\n if (estimatedRows > maxRows) {\n const error = budgetError(\n 'BUDGET.ROWS_EXCEEDED',\n 'Estimated row count exceeds budget',\n {\n source: 'explain',\n estimatedRows,\n maxRows,\n },\n );\n\n const shouldBlock = rowSeverity === 'error' || ctx.mode === 'strict';\n if (shouldBlock) {\n throw error;\n }\n ctx.log.warn({\n code: error.code,\n message: error.message,\n details: error.details,\n });\n }\n return;\n }\n }\n }\n },\n\n async onRow(\n _row: Record<string, unknown>,\n _plan: ExecutionPlan,\n _ctx: PluginContext<TContract, TAdapter, TDriver>,\n ) {\n void _row;\n void _plan;\n void _ctx;\n observedRows += 1;\n if (observedRows > maxRows) {\n throw budgetError('BUDGET.ROWS_EXCEEDED', 'Observed row count exceeds budget', {\n source: 'observed',\n observedRows,\n maxRows,\n });\n }\n },\n\n async afterExecute(\n _plan: ExecutionPlan,\n result: AfterExecuteResult,\n ctx: PluginContext<TContract, TAdapter, TDriver>,\n ) {\n const latencyMs = result.latencyMs;\n if (latencyMs > maxLatencyMs) {\n const error = budgetError('BUDGET.TIME_EXCEEDED', 'Query latency exceeds budget', {\n latencyMs,\n maxLatencyMs,\n });\n\n const shouldBlock = latencySeverity === 'error' && ctx.mode === 'strict';\n if (shouldBlock) {\n throw error;\n }\n ctx.log.warn({\n code: error.code,\n message: error.message,\n details: error.details,\n });\n }\n },\n });\n}\n","import type { ExecutionPlan } from '@prisma-next/contract/types';\nimport type { OperationRegistry } from '@prisma-next/operations';\nimport { AsyncIterableResult } from './async-iterable-result';\nimport { runtimeError } from './errors';\nimport { computeSqlFingerprint } from './fingerprint';\nimport { parseContractMarkerRow } from './marker';\nimport type { Log, Plugin, PluginContext } from './plugins/types';\nimport type { RuntimeFamilyAdapter } from './runtime-spi';\n\nexport interface RuntimeVerifyOptions {\n readonly mode: 'onFirstUse' | 'startup' | 'always';\n readonly requireMarker: boolean;\n}\n\nexport type TelemetryOutcome = 'success' | 'runtime-error';\n\nexport interface RuntimeTelemetryEvent {\n readonly lane: string;\n readonly target: string;\n readonly fingerprint: string;\n readonly outcome: TelemetryOutcome;\n readonly durationMs?: number;\n}\n\nexport interface RuntimeCoreOptions<TContract = unknown, TAdapter = unknown, TDriver = unknown> {\n readonly familyAdapter: RuntimeFamilyAdapter<TContract>;\n readonly driver: TDriver;\n readonly verify: RuntimeVerifyOptions;\n readonly plugins?: readonly Plugin<TContract, TAdapter, TDriver>[];\n readonly mode?: 'strict' | 'permissive';\n readonly log?: Log;\n readonly operationRegistry: OperationRegistry;\n}\n\nexport interface RuntimeCore<TContract = unknown, TAdapter = unknown, TDriver = unknown>\n extends RuntimeQueryable {\n // Type parameters are used in the implementation for type safety\n readonly _typeContract?: TContract;\n readonly _typeAdapter?: TAdapter;\n readonly _typeDriver?: TDriver;\n connection(): Promise<RuntimeConnection>;\n telemetry(): RuntimeTelemetryEvent | null;\n close(): Promise<void>;\n operations(): OperationRegistry;\n}\n\nexport interface RuntimeConnection extends RuntimeQueryable {\n transaction(): Promise<RuntimeTransaction>;\n release(): Promise<void>;\n}\n\nexport interface RuntimeTransaction extends RuntimeQueryable {\n commit(): Promise<void>;\n rollback(): Promise<void>;\n}\n\nexport interface RuntimeQueryable {\n execute<Row = Record<string, unknown>>(plan: ExecutionPlan<Row>): AsyncIterableResult<Row>;\n}\n\ninterface DriverWithQuery<_TDriver> {\n query(sql: string, params: readonly unknown[]): Promise<{ rows: ReadonlyArray<unknown> }>;\n}\n\ninterface DriverWithConnection<_TDriver> {\n acquireConnection(): Promise<DriverConnection>;\n}\n\nexport interface DriverConnection extends Queryable {\n beginTransaction(): Promise<DriverTransaction>;\n release(): Promise<void>;\n}\n\nexport interface DriverTransaction extends Queryable {\n commit(): Promise<void>;\n rollback(): Promise<void>;\n}\n\nexport interface Queryable {\n execute<Row = Record<string, unknown>>(options: {\n sql: string;\n params: readonly unknown[];\n }): AsyncIterable<Row>;\n}\n\ninterface DriverWithClose<_TDriver> {\n close(): Promise<void>;\n}\n\nclass RuntimeCoreImpl<TContract = unknown, TAdapter = unknown, TDriver = unknown>\n implements RuntimeCore<TContract, TAdapter, TDriver>\n{\n readonly _typeContract?: TContract;\n readonly _typeAdapter?: TAdapter;\n readonly _typeDriver?: TDriver;\n private readonly contract: TContract;\n private readonly familyAdapter: RuntimeFamilyAdapter<TContract>;\n private readonly driver: TDriver;\n private readonly plugins: readonly Plugin<TContract, TAdapter, TDriver>[];\n private readonly mode: 'strict' | 'permissive';\n private readonly verify: RuntimeVerifyOptions;\n private readonly operationRegistry: OperationRegistry;\n private readonly pluginContext: PluginContext<TContract, TAdapter, TDriver>;\n\n private verified: boolean;\n private startupVerified: boolean;\n private _telemetry: RuntimeTelemetryEvent | null;\n\n constructor(options: RuntimeCoreOptions<TContract, TAdapter, TDriver>) {\n const { familyAdapter, driver } = options;\n this.contract = familyAdapter.contract;\n this.familyAdapter = familyAdapter;\n this.driver = driver;\n this.plugins = options.plugins ?? [];\n this.mode = options.mode ?? 'strict';\n this.verify = options.verify;\n this.operationRegistry = options.operationRegistry;\n\n this.verified = options.verify.mode === 'startup' ? false : options.verify.mode === 'always';\n this.startupVerified = false;\n this._telemetry = null;\n\n this.pluginContext = {\n contract: this.contract,\n adapter: options.familyAdapter as unknown as TAdapter,\n driver: this.driver,\n mode: this.mode,\n now: () => Date.now(),\n log: options.log ?? {\n info: () => {\n // No-op in MVP - diagnostics stay out of runtime core\n },\n warn: () => {\n // No-op in MVP - diagnostics stay out of runtime core\n },\n error: () => {\n // No-op in MVP - diagnostics stay out of runtime core\n },\n },\n };\n }\n\n private async verifyPlanIfNeeded(_plan: ExecutionPlan): Promise<void> {\n void _plan;\n if (this.verify.mode === 'always') {\n this.verified = false;\n }\n\n if (this.verified) {\n return;\n }\n\n const readStatement = this.familyAdapter.markerReader.readMarkerStatement();\n const driver = this.driver as unknown as DriverWithQuery<TDriver>;\n const result = await driver.query(readStatement.sql, readStatement.params);\n\n if (result.rows.length === 0) {\n if (this.verify.requireMarker) {\n throw runtimeError('CONTRACT.MARKER_MISSING', 'Contract marker not found in database');\n }\n\n this.verified = true;\n return;\n }\n\n const marker = parseContractMarkerRow(result.rows[0]);\n\n const contract = this.contract as {\n storageHash: string;\n executionHash?: string | null;\n profileHash?: string | null;\n };\n if (marker.storageHash !== contract.storageHash) {\n throw runtimeError(\n 'CONTRACT.MARKER_MISMATCH',\n 'Database storage hash does not match contract',\n {\n expected: contract.storageHash,\n actual: marker.storageHash,\n },\n );\n }\n\n const expectedProfile = contract.profileHash ?? null;\n if (expectedProfile !== null && marker.profileHash !== expectedProfile) {\n throw runtimeError(\n 'CONTRACT.MARKER_MISMATCH',\n 'Database profile hash does not match contract',\n {\n expectedProfile,\n actualProfile: marker.profileHash,\n },\n );\n }\n\n this.verified = true;\n this.startupVerified = true;\n }\n\n private validatePlan(plan: ExecutionPlan): void {\n this.familyAdapter.validatePlan(plan, this.contract);\n }\n\n private recordTelemetry(\n plan: ExecutionPlan,\n outcome: TelemetryOutcome,\n durationMs?: number,\n ): void {\n const contract = this.contract as { target: string };\n this._telemetry = Object.freeze({\n lane: plan.meta.lane,\n target: contract.target,\n fingerprint: computeSqlFingerprint(plan.sql),\n outcome,\n ...(durationMs !== undefined ? { durationMs } : {}),\n });\n }\n\n execute<Row = Record<string, unknown>>(plan: ExecutionPlan<Row>): AsyncIterableResult<Row> {\n return this.#executeWith(plan, this.driver as Queryable);\n }\n\n async connection(): Promise<RuntimeConnection> {\n const driver = this.driver as unknown as DriverWithConnection<TDriver>;\n const driverConn = await driver.acquireConnection();\n const self = this;\n\n const runtimeConnection: RuntimeConnection = {\n async transaction(): Promise<RuntimeTransaction> {\n const driverTx = await driverConn.beginTransaction();\n const runtimeTx: RuntimeTransaction = {\n async commit(): Promise<void> {\n await driverTx.commit();\n },\n async rollback(): Promise<void> {\n await driverTx.rollback();\n },\n execute<Row = Record<string, unknown>>(\n plan: ExecutionPlan<Row>,\n ): AsyncIterableResult<Row> {\n return self.#executeWith(plan, driverTx);\n },\n };\n return runtimeTx;\n },\n execute<Row = Record<string, unknown>>(plan: ExecutionPlan<Row>): AsyncIterableResult<Row> {\n return self.#executeWith(plan, driverConn);\n },\n async release(): Promise<void> {\n await driverConn.release();\n },\n };\n\n return runtimeConnection;\n }\n\n telemetry(): RuntimeTelemetryEvent | null {\n return this._telemetry;\n }\n\n operations(): OperationRegistry {\n return this.operationRegistry;\n }\n\n close(): Promise<void> {\n const driver = this.driver as unknown as DriverWithClose<TDriver>;\n if (typeof driver.close === 'function') {\n return driver.close();\n }\n return Promise.resolve();\n }\n\n #executeWith<Row = Record<string, unknown>>(\n plan: ExecutionPlan<Row>,\n queryable: Queryable,\n ): AsyncIterableResult<Row> {\n this.validatePlan(plan);\n this._telemetry = null;\n\n const iterator = async function* (\n self: RuntimeCoreImpl<TContract, TAdapter, TDriver>,\n ): AsyncGenerator<Row, void, unknown> {\n const startedAt = Date.now();\n let rowCount = 0;\n let completed = false;\n\n if (!self.startupVerified && self.verify.mode === 'startup') {\n await self.verifyPlanIfNeeded(plan);\n }\n\n if (self.verify.mode === 'onFirstUse') {\n await self.verifyPlanIfNeeded(plan);\n }\n\n try {\n if (self.verify.mode === 'always') {\n await self.verifyPlanIfNeeded(plan);\n }\n\n for (const plugin of self.plugins) {\n if (plugin.beforeExecute) {\n await plugin.beforeExecute(plan, self.pluginContext);\n }\n }\n\n const encodedParams = plan.params;\n\n for await (const row of queryable.execute<Record<string, unknown>>({\n sql: plan.sql,\n params: encodedParams,\n })) {\n for (const plugin of self.plugins) {\n if (plugin.onRow) {\n await plugin.onRow(row, plan, self.pluginContext);\n }\n }\n rowCount++;\n yield row as Row;\n }\n\n completed = true;\n self.recordTelemetry(plan, 'success', Date.now() - startedAt);\n } catch (error) {\n if (self._telemetry === null) {\n self.recordTelemetry(plan, 'runtime-error', Date.now() - startedAt);\n }\n\n const latencyMs = Date.now() - startedAt;\n for (const plugin of self.plugins) {\n if (plugin.afterExecute) {\n try {\n await plugin.afterExecute(\n plan,\n { rowCount, latencyMs, completed },\n self.pluginContext,\n );\n } catch {\n // Ignore errors from afterExecute hooks\n }\n }\n }\n\n throw error;\n }\n\n const latencyMs = Date.now() - startedAt;\n for (const plugin of self.plugins) {\n if (plugin.afterExecute) {\n await plugin.afterExecute(plan, { rowCount, latencyMs, completed }, self.pluginContext);\n }\n }\n };\n\n return new AsyncIterableResult(iterator(this));\n }\n}\n\nexport function createRuntimeCore<TContract = unknown, TAdapter = unknown, TDriver = unknown>(\n options: RuntimeCoreOptions<TContract, TAdapter, TDriver>,\n): RuntimeCore<TContract, TAdapter, TDriver> {\n return new RuntimeCoreImpl(options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["out: Row[]","lints: LintFinding[]","budgets: BudgetFinding[]","parsed: unknown","#executeWith","latencyMs"],"sources":["../src/errors.ts","../src/async-iterable-result.ts","../src/fingerprint.ts","../src/guardrails/raw.ts","../src/marker.ts","../src/runtime-core.ts"],"sourcesContent":["export interface RuntimeErrorEnvelope extends Error {\n readonly code: string;\n readonly category: 'PLAN' | 'CONTRACT' | 'LINT' | 'BUDGET' | 'RUNTIME';\n readonly severity: 'error';\n readonly details?: Record<string, unknown>;\n}\n\nexport function runtimeError(\n code: string,\n message: string,\n details?: Record<string, unknown>,\n): RuntimeErrorEnvelope {\n const error = new Error(message) as RuntimeErrorEnvelope;\n Object.defineProperty(error, 'name', {\n value: 'RuntimeError',\n configurable: true,\n });\n\n return Object.assign(error, {\n code,\n category: resolveCategory(code),\n severity: 'error' as const,\n message,\n details,\n });\n}\n\nfunction resolveCategory(code: string): RuntimeErrorEnvelope['category'] {\n const prefix = code.split('.')[0] ?? 'RUNTIME';\n switch (prefix) {\n case 'PLAN':\n case 'CONTRACT':\n case 'LINT':\n case 'BUDGET':\n return prefix;\n default:\n return 'RUNTIME';\n }\n}\n","import { runtimeError } from './errors';\n\n/**\n * Custom async iterable result that extends AsyncIterable with a toArray() method.\n * This provides a convenient way to collect all results from an async iterator.\n */\nexport class AsyncIterableResult<Row> implements AsyncIterable<Row>, PromiseLike<Row[]> {\n private readonly generator: AsyncGenerator<Row, void, unknown>;\n private consumed = false;\n private consumedBy: 'bufferedArray' | 'iterator' | undefined;\n private bufferedArrayPromise: Promise<Row[]> | undefined;\n\n constructor(generator: AsyncGenerator<Row, void, unknown>) {\n this.generator = generator;\n }\n\n [Symbol.asyncIterator](): AsyncIterator<Row> {\n if (this.consumed) {\n throw runtimeError(\n 'RUNTIME.ITERATOR_CONSUMED',\n `AsyncIterableResult iterator has already been consumed via ${this.consumedBy === 'bufferedArray' ? 'toArray()/then()' : 'for-await loop'}. Each AsyncIterableResult can only be iterated once.`,\n {\n consumedBy: this.consumedBy,\n suggestion:\n this.consumedBy === 'bufferedArray'\n ? 'If you need to iterate multiple times, store the results from toArray() in a variable and reuse that.'\n : 'If you need to iterate multiple times, use toArray() to collect all results first.',\n },\n );\n }\n this.consumed = true;\n this.consumedBy = 'iterator';\n return this.generator;\n }\n\n /**\n * Collects all values from the async iterator into an array.\n * Once called, the iterator is consumed and cannot be reused.\n */\n toArray(): Promise<Row[]> {\n if (this.consumedBy === 'iterator') {\n return Promise.reject(\n runtimeError(\n 'RUNTIME.ITERATOR_CONSUMED',\n 'AsyncIterableResult iterator has already been consumed via for-await loop. Each AsyncIterableResult can only be iterated once.',\n {\n consumedBy: this.consumedBy,\n suggestion:\n 'The iterator was already consumed by a for-await loop. Use toArray() or await the result before iterating.',\n },\n ),\n );\n }\n\n if (this.bufferedArrayPromise) {\n return this.bufferedArrayPromise;\n }\n\n this.consumed = true;\n this.consumedBy = 'bufferedArray';\n this.bufferedArrayPromise = (async () => {\n const out: Row[] = [];\n for await (const item of this.generator) {\n out.push(item);\n }\n return out;\n })();\n return this.bufferedArrayPromise;\n }\n\n // biome-ignore lint/suspicious/noThenProperty: PromiseLike implementation is intentional for await support.\n then<TResult1 = Row[], TResult2 = never>(\n onfulfilled?: ((value: Row[]) => TResult1 | PromiseLike<TResult1>) | undefined | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | undefined | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.toArray().then(onfulfilled, onrejected);\n }\n}\n","import { createHash } from 'node:crypto';\n\nconst STRING_LITERAL_REGEX = /'(?:''|[^'])*'/g;\nconst NUMERIC_LITERAL_REGEX = /\\b\\d+(?:\\.\\d+)?\\b/g;\nconst WHITESPACE_REGEX = /\\s+/g;\n\nexport function computeSqlFingerprint(sql: string): string {\n const withoutStrings = sql.replace(STRING_LITERAL_REGEX, '?');\n const withoutNumbers = withoutStrings.replace(NUMERIC_LITERAL_REGEX, '?');\n const normalized = withoutNumbers.replace(WHITESPACE_REGEX, ' ').trim().toLowerCase();\n\n const hash = createHash('sha256').update(normalized).digest('hex');\n return `sha256:${hash}`;\n}\n","import type { ExecutionPlan, PlanMeta, PlanRefs } from '@prisma-next/contract/types';\n\nexport type LintSeverity = 'error' | 'warn';\nexport type BudgetSeverity = 'error' | 'warn';\n\nexport interface LintFinding {\n readonly code: `LINT.${string}`;\n readonly severity: LintSeverity;\n readonly message: string;\n readonly details?: Record<string, unknown>;\n}\n\nexport interface BudgetFinding {\n readonly code: `BUDGET.${string}`;\n readonly severity: BudgetSeverity;\n readonly message: string;\n readonly details?: Record<string, unknown>;\n}\n\nexport interface RawGuardrailConfig {\n readonly budgets?: {\n readonly unboundedSelectSeverity?: BudgetSeverity;\n readonly estimatedRows?: number;\n };\n}\n\nexport interface RawGuardrailResult {\n readonly lints: LintFinding[];\n readonly budgets: BudgetFinding[];\n readonly statement: 'select' | 'mutation' | 'other';\n}\n\nconst SELECT_STAR_REGEX = /select\\s+\\*/i;\nconst LIMIT_REGEX = /\\blimit\\b/i;\nconst MUTATION_PREFIX_REGEX = /^(insert|update|delete|create|alter|drop|truncate)\\b/i;\n\nconst READ_ONLY_INTENTS = new Set(['read', 'report', 'readonly']);\n\nexport function evaluateRawGuardrails(\n plan: ExecutionPlan,\n config?: RawGuardrailConfig,\n): RawGuardrailResult {\n const lints: LintFinding[] = [];\n const budgets: BudgetFinding[] = [];\n\n const normalized = normalizeWhitespace(plan.sql);\n const statementType = classifyStatement(normalized);\n\n if (statementType === 'select') {\n if (SELECT_STAR_REGEX.test(normalized)) {\n lints.push(\n createLint('LINT.SELECT_STAR', 'error', 'Raw SQL plan selects all columns via *', {\n sql: snippet(plan.sql),\n }),\n );\n }\n\n if (!LIMIT_REGEX.test(normalized)) {\n const severity = config?.budgets?.unboundedSelectSeverity ?? 'error';\n lints.push(\n createLint('LINT.NO_LIMIT', 'warn', 'Raw SQL plan omits LIMIT clause', {\n sql: snippet(plan.sql),\n }),\n );\n\n budgets.push(\n createBudget(\n 'BUDGET.ROWS_EXCEEDED',\n severity,\n 'Raw SQL plan is unbounded and may exceed row budget',\n {\n sql: snippet(plan.sql),\n ...(config?.budgets?.estimatedRows !== undefined\n ? { estimatedRows: config.budgets.estimatedRows }\n : {}),\n },\n ),\n );\n }\n }\n\n if (isMutationStatement(statementType) && isReadOnlyIntent(plan.meta)) {\n lints.push(\n createLint(\n 'LINT.READ_ONLY_MUTATION',\n 'error',\n 'Raw SQL plan mutates data despite read-only intent',\n {\n sql: snippet(plan.sql),\n intent: plan.meta.annotations?.['intent'],\n },\n ),\n );\n }\n\n const refs = plan.meta.refs;\n if (refs) {\n evaluateIndexCoverage(refs, lints);\n }\n\n return { lints, budgets, statement: statementType };\n}\n\nfunction evaluateIndexCoverage(refs: PlanRefs, lints: LintFinding[]) {\n const predicateColumns = refs.columns ?? [];\n if (predicateColumns.length === 0) {\n return;\n }\n\n const indexes = refs.indexes ?? [];\n\n if (indexes.length === 0) {\n lints.push(\n createLint(\n 'LINT.UNINDEXED_PREDICATE',\n 'warn',\n 'Raw SQL plan predicates lack supporting indexes',\n {\n predicates: predicateColumns,\n },\n ),\n );\n return;\n }\n\n const hasSupportingIndex = predicateColumns.every((column) =>\n indexes.some(\n (index) =>\n index.table === column.table &&\n index.columns.some((col) => col.toLowerCase() === column.column.toLowerCase()),\n ),\n );\n\n if (!hasSupportingIndex) {\n lints.push(\n createLint(\n 'LINT.UNINDEXED_PREDICATE',\n 'warn',\n 'Raw SQL plan predicates lack supporting indexes',\n {\n predicates: predicateColumns,\n },\n ),\n );\n }\n}\n\nfunction classifyStatement(sql: string): 'select' | 'mutation' | 'other' {\n const trimmed = sql.trim();\n const lower = trimmed.toLowerCase();\n\n if (lower.startsWith('with')) {\n if (lower.includes('select')) {\n return 'select';\n }\n }\n\n if (lower.startsWith('select')) {\n return 'select';\n }\n\n if (MUTATION_PREFIX_REGEX.test(trimmed)) {\n return 'mutation';\n }\n\n return 'other';\n}\n\nfunction isMutationStatement(statement: 'select' | 'mutation' | 'other'): boolean {\n return statement === 'mutation';\n}\n\nfunction isReadOnlyIntent(meta: PlanMeta): boolean {\n const annotations = meta.annotations as { intent?: string } | undefined;\n const intent =\n typeof annotations?.intent === 'string' ? annotations.intent.toLowerCase() : undefined;\n return intent !== undefined && READ_ONLY_INTENTS.has(intent);\n}\n\nfunction normalizeWhitespace(value: string): string {\n return value.replace(/\\s+/g, ' ').trim();\n}\n\nfunction snippet(sql: string): string {\n return normalizeWhitespace(sql).slice(0, 200);\n}\n\nfunction createLint(\n code: LintFinding['code'],\n severity: LintFinding['severity'],\n message: string,\n details?: Record<string, unknown>,\n): LintFinding {\n return { code, severity, message, ...(details ? { details } : {}) };\n}\n\nfunction createBudget(\n code: BudgetFinding['code'],\n severity: BudgetFinding['severity'],\n message: string,\n details?: Record<string, unknown>,\n): BudgetFinding {\n return { code, severity, message, ...(details ? { details } : {}) };\n}\n","import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport { type } from 'arktype';\n\n// Re-export for backward compatibility\nexport type { ContractMarkerRecord } from '@prisma-next/contract/types';\n\nexport interface ContractMarkerRow {\n core_hash: string;\n profile_hash: string;\n contract_json: unknown | null;\n canonical_version: number | null;\n updated_at: Date;\n app_tag: string | null;\n meta: unknown | null;\n}\n\nconst MetaSchema = type({ '[string]': 'unknown' });\n\nfunction parseMeta(meta: unknown): Record<string, unknown> {\n if (meta === null || meta === undefined) {\n return {};\n }\n\n let parsed: unknown;\n if (typeof meta === 'string') {\n try {\n parsed = JSON.parse(meta);\n } catch {\n return {};\n }\n } else {\n parsed = meta;\n }\n\n const result = MetaSchema(parsed);\n if (result instanceof type.errors) {\n return {};\n }\n\n return result as Record<string, unknown>;\n}\n\nconst ContractMarkerRowSchema = type({\n core_hash: 'string',\n profile_hash: 'string',\n 'contract_json?': 'unknown | null',\n 'canonical_version?': 'number | null',\n 'updated_at?': 'Date | string',\n 'app_tag?': 'string | null',\n 'meta?': 'unknown | null',\n});\n\nexport function parseContractMarkerRow(row: unknown): ContractMarkerRecord {\n const result = ContractMarkerRowSchema(row);\n if (result instanceof type.errors) {\n const messages = result.map((p: { message: string }) => p.message).join('; ');\n throw new Error(`Invalid contract marker row: ${messages}`);\n }\n\n const validatedRow = result as {\n core_hash: string;\n profile_hash: string;\n contract_json?: unknown | null;\n canonical_version?: number | null;\n updated_at?: Date | string;\n app_tag?: string | null;\n meta?: unknown | null;\n };\n\n const updatedAt = validatedRow.updated_at\n ? validatedRow.updated_at instanceof Date\n ? validatedRow.updated_at\n : new Date(validatedRow.updated_at)\n : new Date();\n\n return {\n storageHash: validatedRow.core_hash,\n profileHash: validatedRow.profile_hash,\n contractJson: validatedRow.contract_json ?? null,\n canonicalVersion: validatedRow.canonical_version ?? null,\n updatedAt,\n appTag: validatedRow.app_tag ?? null,\n meta: parseMeta(validatedRow.meta),\n };\n}\n","import type { ExecutionPlan } from '@prisma-next/contract/types';\nimport type { OperationRegistry } from '@prisma-next/operations';\nimport { AsyncIterableResult } from './async-iterable-result';\nimport { runtimeError } from './errors';\nimport { computeSqlFingerprint } from './fingerprint';\nimport { parseContractMarkerRow } from './marker';\nimport type { Log, Plugin, PluginContext } from './plugins/types';\nimport type { RuntimeFamilyAdapter } from './runtime-spi';\n\nexport interface RuntimeVerifyOptions {\n readonly mode: 'onFirstUse' | 'startup' | 'always';\n readonly requireMarker: boolean;\n}\n\nexport type TelemetryOutcome = 'success' | 'runtime-error';\n\nexport interface RuntimeTelemetryEvent {\n readonly lane: string;\n readonly target: string;\n readonly fingerprint: string;\n readonly outcome: TelemetryOutcome;\n readonly durationMs?: number;\n}\n\nexport interface RuntimeCoreOptions<TContract = unknown, TAdapter = unknown, TDriver = unknown> {\n readonly familyAdapter: RuntimeFamilyAdapter<TContract>;\n readonly driver: TDriver;\n readonly verify: RuntimeVerifyOptions;\n readonly plugins?: readonly Plugin<TContract, TAdapter, TDriver>[];\n readonly mode?: 'strict' | 'permissive';\n readonly log?: Log;\n readonly operationRegistry: OperationRegistry;\n}\n\nexport interface RuntimeCore<TContract = unknown, TAdapter = unknown, TDriver = unknown>\n extends RuntimeQueryable {\n // Type parameters are used in the implementation for type safety\n readonly _typeContract?: TContract;\n readonly _typeAdapter?: TAdapter;\n readonly _typeDriver?: TDriver;\n connection(): Promise<RuntimeConnection>;\n telemetry(): RuntimeTelemetryEvent | null;\n close(): Promise<void>;\n operations(): OperationRegistry;\n}\n\nexport interface RuntimeConnection extends RuntimeQueryable {\n transaction(): Promise<RuntimeTransaction>;\n release(): Promise<void>;\n}\n\nexport interface RuntimeTransaction extends RuntimeQueryable {\n commit(): Promise<void>;\n rollback(): Promise<void>;\n}\n\nexport interface RuntimeQueryable {\n execute<Row = Record<string, unknown>>(plan: ExecutionPlan<Row>): AsyncIterableResult<Row>;\n}\n\ninterface DriverWithQuery<_TDriver> {\n query(sql: string, params: readonly unknown[]): Promise<{ rows: ReadonlyArray<unknown> }>;\n}\n\ninterface DriverWithConnection<_TDriver> {\n acquireConnection(): Promise<DriverConnection>;\n}\n\nexport interface DriverConnection extends Queryable {\n beginTransaction(): Promise<DriverTransaction>;\n release(): Promise<void>;\n}\n\nexport interface DriverTransaction extends Queryable {\n commit(): Promise<void>;\n rollback(): Promise<void>;\n}\n\nexport interface Queryable {\n execute<Row = Record<string, unknown>>(options: {\n sql: string;\n params: readonly unknown[];\n }): AsyncIterable<Row>;\n}\n\ninterface DriverWithClose<_TDriver> {\n close(): Promise<void>;\n}\n\nclass RuntimeCoreImpl<TContract = unknown, TAdapter = unknown, TDriver = unknown>\n implements RuntimeCore<TContract, TAdapter, TDriver>\n{\n readonly _typeContract?: TContract;\n readonly _typeAdapter?: TAdapter;\n readonly _typeDriver?: TDriver;\n private readonly contract: TContract;\n private readonly familyAdapter: RuntimeFamilyAdapter<TContract>;\n private readonly driver: TDriver;\n private readonly plugins: readonly Plugin<TContract, TAdapter, TDriver>[];\n private readonly mode: 'strict' | 'permissive';\n private readonly verify: RuntimeVerifyOptions;\n private readonly operationRegistry: OperationRegistry;\n private readonly pluginContext: PluginContext<TContract, TAdapter, TDriver>;\n\n private verified: boolean;\n private startupVerified: boolean;\n private _telemetry: RuntimeTelemetryEvent | null;\n\n constructor(options: RuntimeCoreOptions<TContract, TAdapter, TDriver>) {\n const { familyAdapter, driver } = options;\n this.contract = familyAdapter.contract;\n this.familyAdapter = familyAdapter;\n this.driver = driver;\n this.plugins = options.plugins ?? [];\n this.mode = options.mode ?? 'strict';\n this.verify = options.verify;\n this.operationRegistry = options.operationRegistry;\n\n this.verified = options.verify.mode === 'startup' ? false : options.verify.mode === 'always';\n this.startupVerified = false;\n this._telemetry = null;\n\n this.pluginContext = {\n contract: this.contract,\n adapter: options.familyAdapter as unknown as TAdapter,\n driver: this.driver,\n mode: this.mode,\n now: () => Date.now(),\n log: options.log ?? {\n info: () => {\n // No-op in MVP - diagnostics stay out of runtime core\n },\n warn: () => {\n // No-op in MVP - diagnostics stay out of runtime core\n },\n error: () => {\n // No-op in MVP - diagnostics stay out of runtime core\n },\n },\n };\n }\n\n private async verifyPlanIfNeeded(_plan: ExecutionPlan): Promise<void> {\n void _plan;\n if (this.verify.mode === 'always') {\n this.verified = false;\n }\n\n if (this.verified) {\n return;\n }\n\n const readStatement = this.familyAdapter.markerReader.readMarkerStatement();\n const driver = this.driver as unknown as DriverWithQuery<TDriver>;\n const result = await driver.query(readStatement.sql, readStatement.params);\n\n if (result.rows.length === 0) {\n if (this.verify.requireMarker) {\n throw runtimeError('CONTRACT.MARKER_MISSING', 'Contract marker not found in database');\n }\n\n this.verified = true;\n return;\n }\n\n const marker = parseContractMarkerRow(result.rows[0]);\n\n const contract = this.contract as {\n storageHash: string;\n executionHash?: string | null;\n profileHash?: string | null;\n };\n if (marker.storageHash !== contract.storageHash) {\n throw runtimeError(\n 'CONTRACT.MARKER_MISMATCH',\n 'Database storage hash does not match contract',\n {\n expected: contract.storageHash,\n actual: marker.storageHash,\n },\n );\n }\n\n const expectedProfile = contract.profileHash ?? null;\n if (expectedProfile !== null && marker.profileHash !== expectedProfile) {\n throw runtimeError(\n 'CONTRACT.MARKER_MISMATCH',\n 'Database profile hash does not match contract',\n {\n expectedProfile,\n actualProfile: marker.profileHash,\n },\n );\n }\n\n this.verified = true;\n this.startupVerified = true;\n }\n\n private validatePlan(plan: ExecutionPlan): void {\n this.familyAdapter.validatePlan(plan, this.contract);\n }\n\n private recordTelemetry(\n plan: ExecutionPlan,\n outcome: TelemetryOutcome,\n durationMs?: number,\n ): void {\n const contract = this.contract as { target: string };\n this._telemetry = Object.freeze({\n lane: plan.meta.lane,\n target: contract.target,\n fingerprint: computeSqlFingerprint(plan.sql),\n outcome,\n ...(durationMs !== undefined ? { durationMs } : {}),\n });\n }\n\n execute<Row = Record<string, unknown>>(plan: ExecutionPlan<Row>): AsyncIterableResult<Row> {\n return this.#executeWith(plan, this.driver as Queryable);\n }\n\n async connection(): Promise<RuntimeConnection> {\n const driver = this.driver as unknown as DriverWithConnection<TDriver>;\n const driverConn = await driver.acquireConnection();\n const self = this;\n\n const runtimeConnection: RuntimeConnection = {\n async transaction(): Promise<RuntimeTransaction> {\n const driverTx = await driverConn.beginTransaction();\n const runtimeTx: RuntimeTransaction = {\n async commit(): Promise<void> {\n await driverTx.commit();\n },\n async rollback(): Promise<void> {\n await driverTx.rollback();\n },\n execute<Row = Record<string, unknown>>(\n plan: ExecutionPlan<Row>,\n ): AsyncIterableResult<Row> {\n return self.#executeWith(plan, driverTx);\n },\n };\n return runtimeTx;\n },\n execute<Row = Record<string, unknown>>(plan: ExecutionPlan<Row>): AsyncIterableResult<Row> {\n return self.#executeWith(plan, driverConn);\n },\n async release(): Promise<void> {\n await driverConn.release();\n },\n };\n\n return runtimeConnection;\n }\n\n telemetry(): RuntimeTelemetryEvent | null {\n return this._telemetry;\n }\n\n operations(): OperationRegistry {\n return this.operationRegistry;\n }\n\n close(): Promise<void> {\n const driver = this.driver as unknown as DriverWithClose<TDriver>;\n if (typeof driver.close === 'function') {\n return driver.close();\n }\n return Promise.resolve();\n }\n\n #executeWith<Row = Record<string, unknown>>(\n plan: ExecutionPlan<Row>,\n queryable: Queryable,\n ): AsyncIterableResult<Row> {\n this.validatePlan(plan);\n this._telemetry = null;\n\n const iterator = async function* (\n self: RuntimeCoreImpl<TContract, TAdapter, TDriver>,\n ): AsyncGenerator<Row, void, unknown> {\n const startedAt = Date.now();\n let rowCount = 0;\n let completed = false;\n\n if (!self.startupVerified && self.verify.mode === 'startup') {\n await self.verifyPlanIfNeeded(plan);\n }\n\n if (self.verify.mode === 'onFirstUse') {\n await self.verifyPlanIfNeeded(plan);\n }\n\n try {\n if (self.verify.mode === 'always') {\n await self.verifyPlanIfNeeded(plan);\n }\n\n for (const plugin of self.plugins) {\n if (plugin.beforeExecute) {\n await plugin.beforeExecute(plan, self.pluginContext);\n }\n }\n\n const encodedParams = plan.params;\n\n for await (const row of queryable.execute<Record<string, unknown>>({\n sql: plan.sql,\n params: encodedParams,\n })) {\n for (const plugin of self.plugins) {\n if (plugin.onRow) {\n await plugin.onRow(row, plan, self.pluginContext);\n }\n }\n rowCount++;\n yield row as Row;\n }\n\n completed = true;\n self.recordTelemetry(plan, 'success', Date.now() - startedAt);\n } catch (error) {\n if (self._telemetry === null) {\n self.recordTelemetry(plan, 'runtime-error', Date.now() - startedAt);\n }\n\n const latencyMs = Date.now() - startedAt;\n for (const plugin of self.plugins) {\n if (plugin.afterExecute) {\n try {\n await plugin.afterExecute(\n plan,\n { rowCount, latencyMs, completed },\n self.pluginContext,\n );\n } catch {\n // Ignore errors from afterExecute hooks\n }\n }\n }\n\n throw error;\n }\n\n const latencyMs = Date.now() - startedAt;\n for (const plugin of self.plugins) {\n if (plugin.afterExecute) {\n await plugin.afterExecute(plan, { rowCount, latencyMs, completed }, self.pluginContext);\n }\n }\n };\n\n return new AsyncIterableResult(iterator(this));\n }\n}\n\nexport function createRuntimeCore<TContract = unknown, TAdapter = unknown, TDriver = unknown>(\n options: RuntimeCoreOptions<TContract, TAdapter, TDriver>,\n): RuntimeCore<TContract, TAdapter, TDriver> {\n return new RuntimeCoreImpl(options);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@prisma-next/runtime-executor",
3
- "version": "0.3.0-dev.96",
3
+ "version": "0.3.0-dev.98",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Target-agnostic execution engine for Prisma Next",
7
7
  "dependencies": {
8
8
  "arktype": "^2.1.25",
9
- "@prisma-next/contract": "0.3.0-dev.96",
10
- "@prisma-next/operations": "0.3.0-dev.96"
9
+ "@prisma-next/contract": "0.3.0-dev.98",
10
+ "@prisma-next/operations": "0.3.0-dev.98"
11
11
  },
12
12
  "devDependencies": {
13
13
  "@prisma/dev": "0.19.1",
@@ -6,8 +6,6 @@ export type { BudgetFinding, LintFinding, RawGuardrailResult } from '../guardrai
6
6
  export { evaluateRawGuardrails } from '../guardrails/raw';
7
7
  export type { ContractMarkerRecord } from '../marker';
8
8
  export { parseContractMarkerRow } from '../marker';
9
- export type { BudgetsOptions } from '../plugins/budgets';
10
- export { budgets } from '../plugins/budgets';
11
9
  export type {
12
10
  AfterExecuteResult,
13
11
  Log,
@@ -1,309 +0,0 @@
1
- import type { ExecutionPlan } from '@prisma-next/contract/types';
2
- import type { AfterExecuteResult, Plugin, PluginContext } from './types';
3
-
4
- export interface BudgetsOptions {
5
- readonly maxRows?: number;
6
- readonly defaultTableRows?: number;
7
- readonly tableRows?: Record<string, number>;
8
- readonly maxLatencyMs?: number;
9
- readonly severities?: {
10
- readonly rowCount?: 'warn' | 'error';
11
- readonly latency?: 'warn' | 'error';
12
- };
13
- readonly explain?: {
14
- readonly enabled?: boolean;
15
- };
16
- }
17
-
18
- interface DriverWithExplain {
19
- explain?(
20
- sql: string,
21
- params: unknown[],
22
- ): Promise<{ rows: ReadonlyArray<Record<string, unknown>> }>;
23
- }
24
-
25
- async function computeEstimatedRows(
26
- plan: ExecutionPlan,
27
- driver: DriverWithExplain,
28
- ): Promise<number | undefined> {
29
- if (typeof driver.explain !== 'function') {
30
- return undefined;
31
- }
32
-
33
- try {
34
- const result = await driver.explain(plan.sql, [...plan.params]);
35
- return extractEstimatedRows(result.rows);
36
- } catch {
37
- return undefined;
38
- }
39
- }
40
-
41
- function extractEstimatedRows(rows: ReadonlyArray<Record<string, unknown>>): number | undefined {
42
- for (const row of rows) {
43
- const estimate = findPlanRows(row);
44
- if (estimate !== undefined) {
45
- return estimate;
46
- }
47
- }
48
-
49
- return undefined;
50
- }
51
-
52
- type ExplainNode = {
53
- Plan?: unknown;
54
- Plans?: unknown[];
55
- 'Plan Rows'?: number;
56
- [key: string]: unknown;
57
- };
58
-
59
- function findPlanRows(node: unknown): number | undefined {
60
- if (!node || typeof node !== 'object') {
61
- return undefined;
62
- }
63
-
64
- const explainNode = node as ExplainNode;
65
- const planRows = explainNode['Plan Rows'];
66
- if (typeof planRows === 'number') {
67
- return planRows;
68
- }
69
-
70
- if ('Plan' in explainNode && explainNode.Plan !== undefined) {
71
- const nested = findPlanRows(explainNode.Plan);
72
- if (nested !== undefined) {
73
- return nested;
74
- }
75
- }
76
-
77
- if (Array.isArray(explainNode.Plans)) {
78
- for (const child of explainNode.Plans) {
79
- const nested = findPlanRows(child);
80
- if (nested !== undefined) {
81
- return nested;
82
- }
83
- }
84
- }
85
-
86
- for (const value of Object.values(node as Record<string, unknown>)) {
87
- if (typeof value === 'object' && value !== null) {
88
- const nested = findPlanRows(value);
89
- if (nested !== undefined) {
90
- return nested;
91
- }
92
- }
93
- }
94
-
95
- return undefined;
96
- }
97
-
98
- function budgetError(code: string, message: string, details?: Record<string, unknown>) {
99
- const error = new Error(message) as Error & {
100
- code: string;
101
- category: 'BUDGET';
102
- severity: 'error';
103
- details?: Record<string, unknown>;
104
- };
105
- Object.defineProperty(error, 'name', {
106
- value: 'RuntimeError',
107
- configurable: true,
108
- });
109
- return Object.assign(error, {
110
- code,
111
- category: 'BUDGET' as const,
112
- severity: 'error' as const,
113
- details,
114
- });
115
- }
116
-
117
- function estimateRows(
118
- plan: ExecutionPlan,
119
- tableRows: Record<string, number>,
120
- defaultTableRows: number,
121
- ): number | null {
122
- const table = plan.meta.refs?.tables?.[0];
123
- if (!table) {
124
- return null;
125
- }
126
-
127
- const tableEstimate = tableRows[table] ?? defaultTableRows;
128
-
129
- // TODO: this is really not great. It should instead walk the AST.
130
- // It also doesn't check in any way that this limit correlates with the current table.
131
- // Move this plugin to a higher level (close to the linter) and use AST visitors.
132
- const limit = plan.meta.annotations?.['limit'];
133
- if (typeof limit === 'number') {
134
- return Math.min(limit, tableEstimate);
135
- }
136
-
137
- return tableEstimate;
138
- }
139
-
140
- function hasDetectableLimit(plan: ExecutionPlan): boolean {
141
- return typeof plan.meta.annotations?.['limit'] === 'number';
142
- }
143
-
144
- export function budgets<TContract = unknown, TAdapter = unknown, TDriver = unknown>(
145
- options?: BudgetsOptions,
146
- ): Plugin<TContract, TAdapter, TDriver> {
147
- const maxRows = options?.maxRows ?? 10_000;
148
- const defaultTableRows = options?.defaultTableRows ?? 10_000;
149
- const tableRows = options?.tableRows ?? {};
150
- const maxLatencyMs = options?.maxLatencyMs ?? 1_000;
151
- const rowSeverity = options?.severities?.rowCount ?? 'error';
152
- const latencySeverity = options?.severities?.latency ?? 'warn';
153
-
154
- let observedRows = 0;
155
-
156
- return Object.freeze({
157
- name: 'budgets',
158
-
159
- async beforeExecute(plan: ExecutionPlan, ctx: PluginContext<TContract, TAdapter, TDriver>) {
160
- observedRows = 0;
161
- void ctx.now();
162
-
163
- const estimated = estimateRows(plan, tableRows, defaultTableRows);
164
- const isUnbounded = !hasDetectableLimit(plan);
165
- const sqlUpper = plan.sql.trimStart().toUpperCase();
166
- const isSelect = sqlUpper.startsWith('SELECT');
167
-
168
- // Check for unbounded queries first - these should always error if they exceed or equal the budget
169
- if (isSelect && isUnbounded) {
170
- if (estimated !== null && estimated >= maxRows) {
171
- const error = budgetError(
172
- 'BUDGET.ROWS_EXCEEDED',
173
- 'Unbounded SELECT query exceeds budget',
174
- {
175
- source: 'heuristic',
176
- estimatedRows: estimated,
177
- maxRows,
178
- },
179
- );
180
-
181
- const shouldBlock = rowSeverity === 'error' || ctx.mode === 'strict';
182
- if (shouldBlock) {
183
- throw error;
184
- }
185
- ctx.log.warn({
186
- code: error.code,
187
- message: error.message,
188
- details: error.details,
189
- });
190
- return;
191
- }
192
-
193
- // Even if we can't estimate, unbounded queries should error
194
- const error = budgetError('BUDGET.ROWS_EXCEEDED', 'Unbounded SELECT query exceeds budget', {
195
- source: 'heuristic',
196
- maxRows,
197
- });
198
-
199
- const shouldBlock = rowSeverity === 'error' || ctx.mode === 'strict';
200
- if (shouldBlock) {
201
- throw error;
202
- }
203
- ctx.log.warn({
204
- code: error.code,
205
- message: error.message,
206
- details: error.details,
207
- });
208
- return;
209
- }
210
-
211
- // For bounded queries, check if estimated exceeds budget
212
- if (estimated !== null) {
213
- if (estimated > maxRows) {
214
- const error = budgetError('BUDGET.ROWS_EXCEEDED', 'Estimated row count exceeds budget', {
215
- source: 'heuristic',
216
- estimatedRows: estimated,
217
- maxRows,
218
- });
219
-
220
- const shouldBlock = rowSeverity === 'error' || ctx.mode === 'strict';
221
- if (shouldBlock) {
222
- throw error;
223
- }
224
- ctx.log.warn({
225
- code: error.code,
226
- message: error.message,
227
- details: error.details,
228
- });
229
- }
230
- return;
231
- }
232
-
233
- // Fallback: if no AST, try EXPLAIN if enabled
234
- if (!plan.ast) {
235
- const explainEnabled = options?.explain?.enabled === true;
236
-
237
- if (explainEnabled && isSelect && typeof ctx.driver === 'object' && ctx.driver !== null) {
238
- const estimatedRows = await computeEstimatedRows(plan, ctx.driver as DriverWithExplain);
239
- if (estimatedRows !== undefined) {
240
- if (estimatedRows > maxRows) {
241
- const error = budgetError(
242
- 'BUDGET.ROWS_EXCEEDED',
243
- 'Estimated row count exceeds budget',
244
- {
245
- source: 'explain',
246
- estimatedRows,
247
- maxRows,
248
- },
249
- );
250
-
251
- const shouldBlock = rowSeverity === 'error' || ctx.mode === 'strict';
252
- if (shouldBlock) {
253
- throw error;
254
- }
255
- ctx.log.warn({
256
- code: error.code,
257
- message: error.message,
258
- details: error.details,
259
- });
260
- }
261
- return;
262
- }
263
- }
264
- }
265
- },
266
-
267
- async onRow(
268
- _row: Record<string, unknown>,
269
- _plan: ExecutionPlan,
270
- _ctx: PluginContext<TContract, TAdapter, TDriver>,
271
- ) {
272
- void _row;
273
- void _plan;
274
- void _ctx;
275
- observedRows += 1;
276
- if (observedRows > maxRows) {
277
- throw budgetError('BUDGET.ROWS_EXCEEDED', 'Observed row count exceeds budget', {
278
- source: 'observed',
279
- observedRows,
280
- maxRows,
281
- });
282
- }
283
- },
284
-
285
- async afterExecute(
286
- _plan: ExecutionPlan,
287
- result: AfterExecuteResult,
288
- ctx: PluginContext<TContract, TAdapter, TDriver>,
289
- ) {
290
- const latencyMs = result.latencyMs;
291
- if (latencyMs > maxLatencyMs) {
292
- const error = budgetError('BUDGET.TIME_EXCEEDED', 'Query latency exceeds budget', {
293
- latencyMs,
294
- maxLatencyMs,
295
- });
296
-
297
- const shouldBlock = latencySeverity === 'error' && ctx.mode === 'strict';
298
- if (shouldBlock) {
299
- throw error;
300
- }
301
- ctx.log.warn({
302
- code: error.code,
303
- message: error.message,
304
- details: error.details,
305
- });
306
- }
307
- },
308
- });
309
- }