@prisma-next/sql-runtime 0.4.0-dev.9 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,223 @@
1
+ import type { Contract, PlanMeta } from '@prisma-next/contract/types';
2
+ import type { SqlStorage } from '@prisma-next/sql-contract/types';
3
+ import {
4
+ AndExpr,
5
+ BinaryExpr,
6
+ ColumnRef,
7
+ LiteralExpr,
8
+ SelectAst,
9
+ TableSource,
10
+ } from '@prisma-next/sql-relational-core/ast';
11
+ import { timeouts } from '@prisma-next/test-utils';
12
+ import { describe, expect, it, vi } from 'vitest';
13
+ import { runBeforeCompileChain } from '../src/middleware/before-compile-chain';
14
+ import type {
15
+ DraftPlan,
16
+ SqlMiddleware,
17
+ SqlMiddlewareContext,
18
+ } from '../src/middleware/sql-middleware';
19
+
20
+ function createContext(): SqlMiddlewareContext & {
21
+ log: { debug: ReturnType<typeof vi.fn> };
22
+ } {
23
+ const debug = vi.fn();
24
+ return {
25
+ contract: {} as Contract<SqlStorage>,
26
+ mode: 'strict' as const,
27
+ now: () => 0,
28
+ log: {
29
+ info: vi.fn(),
30
+ warn: vi.fn(),
31
+ error: vi.fn(),
32
+ debug,
33
+ },
34
+ };
35
+ }
36
+
37
+ const meta: PlanMeta = {
38
+ target: 'postgres',
39
+ storageHash: 'sha256:test',
40
+ lane: 'dsl',
41
+ paramDescriptors: [],
42
+ };
43
+
44
+ function createDraft(): DraftPlan {
45
+ const users = TableSource.named('users');
46
+ return {
47
+ ast: SelectAst.from(users).withProjection([]),
48
+ meta,
49
+ };
50
+ }
51
+
52
+ describe('runBeforeCompileChain', () => {
53
+ it(
54
+ 'returns the initial draft unchanged when no middleware rewrites',
55
+ async () => {
56
+ const draft = createDraft();
57
+ const ctx = createContext();
58
+ const mw: SqlMiddleware = {
59
+ name: 'noop',
60
+ familyId: 'sql',
61
+ async beforeCompile() {
62
+ return undefined;
63
+ },
64
+ };
65
+
66
+ const result = await runBeforeCompileChain([mw], draft, ctx);
67
+
68
+ expect(result).toBe(draft);
69
+ expect(ctx.log.debug).not.toHaveBeenCalled();
70
+ },
71
+ timeouts.default,
72
+ );
73
+
74
+ it(
75
+ 'treats a returned draft with same ast reference as passthrough',
76
+ async () => {
77
+ const draft = createDraft();
78
+ const ctx = createContext();
79
+ const mw: SqlMiddleware = {
80
+ name: 'sameRef',
81
+ familyId: 'sql',
82
+ async beforeCompile(d) {
83
+ return { ...d };
84
+ },
85
+ };
86
+
87
+ const result = await runBeforeCompileChain([mw], draft, ctx);
88
+
89
+ expect(result.ast).toBe(draft.ast);
90
+ expect(ctx.log.debug).not.toHaveBeenCalled();
91
+ },
92
+ timeouts.default,
93
+ );
94
+
95
+ it(
96
+ 'replaces the current draft when a middleware returns a new ast ref',
97
+ async () => {
98
+ const draft = createDraft();
99
+ const ctx = createContext();
100
+ const addWhere = BinaryExpr.eq(ColumnRef.of('users', 'deleted_at'), LiteralExpr.of(null));
101
+ const mw: SqlMiddleware = {
102
+ name: 'softDelete',
103
+ familyId: 'sql',
104
+ async beforeCompile(d) {
105
+ if (d.ast.kind !== 'select') return;
106
+ return { ...d, ast: d.ast.withWhere(addWhere) };
107
+ },
108
+ };
109
+
110
+ const result = await runBeforeCompileChain([mw], draft, ctx);
111
+
112
+ expect(result.ast).not.toBe(draft.ast);
113
+ expect(result.ast.kind).toBe('select');
114
+ expect((result.ast as SelectAst).where).toBe(addWhere);
115
+ },
116
+ timeouts.default,
117
+ );
118
+
119
+ it(
120
+ 'chains rewrites in registration order',
121
+ async () => {
122
+ const draft = createDraft();
123
+ const ctx = createContext();
124
+ const order: string[] = [];
125
+
126
+ const predA = BinaryExpr.eq(ColumnRef.of('users', 'a'), LiteralExpr.of(1));
127
+ const predB = BinaryExpr.eq(ColumnRef.of('users', 'b'), LiteralExpr.of(2));
128
+
129
+ const mwA: SqlMiddleware = {
130
+ name: 'addA',
131
+ familyId: 'sql',
132
+ async beforeCompile(d) {
133
+ order.push('A');
134
+ if (d.ast.kind !== 'select') return;
135
+ return { ...d, ast: d.ast.withWhere(predA) };
136
+ },
137
+ };
138
+ const mwB: SqlMiddleware = {
139
+ name: 'addB',
140
+ familyId: 'sql',
141
+ async beforeCompile(d) {
142
+ order.push('B');
143
+ if (d.ast.kind !== 'select') return;
144
+ const current = d.ast.where;
145
+ const combined = current ? AndExpr.of([current, predB]) : predB;
146
+ return { ...d, ast: d.ast.withWhere(combined) };
147
+ },
148
+ };
149
+
150
+ const result = await runBeforeCompileChain([mwA, mwB], draft, ctx);
151
+
152
+ expect(order).toEqual(['A', 'B']);
153
+ expect(result.ast.kind).toBe('select');
154
+ const where = (result.ast as SelectAst).where;
155
+ expect(where?.kind).toBe('and');
156
+ },
157
+ timeouts.default,
158
+ );
159
+
160
+ it(
161
+ 'emits a debug log event per rewrite with middleware name and lane',
162
+ async () => {
163
+ const draft = createDraft();
164
+ const ctx = createContext();
165
+ const pred = BinaryExpr.eq(ColumnRef.of('users', 'a'), LiteralExpr.of(1));
166
+ const mw: SqlMiddleware = {
167
+ name: 'rewriteOne',
168
+ familyId: 'sql',
169
+ async beforeCompile(d) {
170
+ if (d.ast.kind !== 'select') return;
171
+ return { ...d, ast: d.ast.withWhere(pred) };
172
+ },
173
+ };
174
+
175
+ await runBeforeCompileChain([mw, mw], draft, ctx);
176
+
177
+ expect(ctx.log.debug).toHaveBeenCalledTimes(2);
178
+ expect(ctx.log.debug).toHaveBeenCalledWith({
179
+ event: 'middleware.rewrite',
180
+ middleware: 'rewriteOne',
181
+ lane: 'dsl',
182
+ });
183
+ },
184
+ timeouts.default,
185
+ );
186
+
187
+ it(
188
+ 'skips middleware without beforeCompile',
189
+ async () => {
190
+ const draft = createDraft();
191
+ const ctx = createContext();
192
+ const observerOnly: SqlMiddleware = {
193
+ name: 'observer',
194
+ familyId: 'sql',
195
+ async beforeExecute() {},
196
+ };
197
+
198
+ const result = await runBeforeCompileChain([observerOnly], draft, ctx);
199
+
200
+ expect(result).toBe(draft);
201
+ expect(ctx.log.debug).not.toHaveBeenCalled();
202
+ },
203
+ timeouts.default,
204
+ );
205
+
206
+ it(
207
+ 'propagates errors thrown inside beforeCompile',
208
+ async () => {
209
+ const draft = createDraft();
210
+ const ctx = createContext();
211
+ const mw: SqlMiddleware = {
212
+ name: 'thrower',
213
+ familyId: 'sql',
214
+ async beforeCompile() {
215
+ throw new Error('boom');
216
+ },
217
+ };
218
+
219
+ await expect(runBeforeCompileChain([mw], draft, ctx)).rejects.toThrow('boom');
220
+ },
221
+ timeouts.default,
222
+ );
223
+ });
@@ -1,5 +1,6 @@
1
- import type { ExecutionPlan, PlanMeta } from '@prisma-next/contract/types';
2
- import type { AfterExecuteResult, MiddlewareContext } from '@prisma-next/runtime-executor';
1
+ import type { Contract, ExecutionPlan, PlanMeta } from '@prisma-next/contract/types';
2
+ import type { AfterExecuteResult } from '@prisma-next/runtime-executor';
3
+ import type { SqlStorage } from '@prisma-next/sql-contract/types';
3
4
  import {
4
5
  AggregateExpr,
5
6
  ColumnRef,
@@ -11,15 +12,14 @@ import {
11
12
  import { timeouts } from '@prisma-next/test-utils';
12
13
  import { describe, expect, it, vi } from 'vitest';
13
14
  import { budgets } from '../src/middleware/budgets';
15
+ import type { SqlMiddlewareContext } from '../src/middleware/sql-middleware';
14
16
 
15
17
  const userTable = TableSource.named('user');
16
18
  const idCol = ColumnRef.of('user', 'id');
17
19
 
18
- function createMiddlewareContext(
19
- overrides?: Partial<MiddlewareContext<unknown>>,
20
- ): MiddlewareContext<unknown> {
20
+ function createMiddlewareContext(overrides?: Partial<SqlMiddlewareContext>): SqlMiddlewareContext {
21
21
  return {
22
- contract: {},
22
+ contract: {} as Contract<SqlStorage>,
23
23
  mode: 'strict' as const,
24
24
  now: () => Date.now(),
25
25
  log: {
@@ -41,10 +41,7 @@ function createStubAdapterDescriptor(): SqlRuntimeAdapterDescriptor<'postgres'>
41
41
  readMarkerStatement: () => ({ sql: '', params: [] }),
42
42
  },
43
43
  lower() {
44
- return {
45
- profileId: 'test-profile',
46
- body: Object.freeze({ sql: '', params: [] }),
47
- };
44
+ return Object.freeze({ sql: '', params: [] });
48
45
  },
49
46
  },
50
47
  );
@@ -1,5 +1,5 @@
1
- import type { ExecutionPlan, PlanMeta } from '@prisma-next/contract/types';
2
- import type { MiddlewareContext } from '@prisma-next/runtime-executor';
1
+ import type { Contract, ExecutionPlan, PlanMeta } from '@prisma-next/contract/types';
2
+ import type { SqlStorage } from '@prisma-next/sql-contract/types';
3
3
  import {
4
4
  BinaryExpr,
5
5
  ColumnRef,
@@ -14,10 +14,11 @@ import {
14
14
  import { timeouts } from '@prisma-next/test-utils';
15
15
  import { describe, expect, it, vi } from 'vitest';
16
16
  import { lints } from '../src/middleware/lints';
17
+ import type { SqlMiddlewareContext } from '../src/middleware/sql-middleware';
17
18
 
18
- function createMiddlewareContext(): MiddlewareContext<unknown> {
19
+ function createMiddlewareContext(): SqlMiddlewareContext {
19
20
  return {
20
- contract: {},
21
+ contract: {} as Contract<SqlStorage>,
21
22
  mode: 'strict' as const,
22
23
  now: () => Date.now(),
23
24
  log: {