@prisma-next/sql-runtime 0.5.0-dev.7 → 0.5.0-dev.9

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.
Files changed (42) hide show
  1. package/README.md +29 -21
  2. package/dist/{exports-BQZSVXXt.mjs → exports-BOHa3Emo.mjs} +481 -128
  3. package/dist/exports-BOHa3Emo.mjs.map +1 -0
  4. package/dist/{index-yb51L_1h.d.mts → index-CZmC2kD3.d.mts} +53 -16
  5. package/dist/index-CZmC2kD3.d.mts.map +1 -0
  6. package/dist/index.d.mts +2 -2
  7. package/dist/index.mjs +1 -1
  8. package/dist/test/utils.d.mts +6 -5
  9. package/dist/test/utils.d.mts.map +1 -1
  10. package/dist/test/utils.mjs +7 -2
  11. package/dist/test/utils.mjs.map +1 -1
  12. package/package.json +12 -14
  13. package/src/codecs/decoding.ts +172 -116
  14. package/src/codecs/encoding.ts +59 -21
  15. package/src/exports/index.ts +10 -7
  16. package/src/fingerprint.ts +22 -0
  17. package/src/guardrails/raw.ts +214 -0
  18. package/src/lower-sql-plan.ts +3 -3
  19. package/src/marker.ts +82 -0
  20. package/src/middleware/before-compile-chain.ts +32 -1
  21. package/src/middleware/budgets.ts +14 -11
  22. package/src/middleware/lints.ts +3 -3
  23. package/src/middleware/sql-middleware.ts +6 -5
  24. package/src/runtime-spi.ts +43 -0
  25. package/src/sql-family-adapter.ts +3 -2
  26. package/src/sql-marker.ts +1 -1
  27. package/src/sql-runtime.ts +272 -110
  28. package/dist/exports-BQZSVXXt.mjs.map +0 -1
  29. package/dist/index-yb51L_1h.d.mts.map +0 -1
  30. package/test/async-iterable-result.test.ts +0 -141
  31. package/test/before-compile-chain.test.ts +0 -223
  32. package/test/budgets.test.ts +0 -431
  33. package/test/context.types.test-d.ts +0 -68
  34. package/test/execution-stack.test.ts +0 -161
  35. package/test/json-schema-validation.test.ts +0 -571
  36. package/test/lints.test.ts +0 -160
  37. package/test/mutation-default-generators.test.ts +0 -254
  38. package/test/parameterized-types.test.ts +0 -529
  39. package/test/sql-context.test.ts +0 -384
  40. package/test/sql-family-adapter.test.ts +0 -103
  41. package/test/sql-runtime.test.ts +0 -792
  42. package/test/utils.ts +0 -297
@@ -1,160 +0,0 @@
1
- import type { Contract, ExecutionPlan, PlanMeta } from '@prisma-next/contract/types';
2
- import type { SqlStorage } from '@prisma-next/sql-contract/types';
3
- import {
4
- BinaryExpr,
5
- ColumnRef,
6
- DeleteAst,
7
- DerivedTableSource,
8
- ParamRef,
9
- ProjectionItem,
10
- SelectAst,
11
- TableSource,
12
- UpdateAst,
13
- } from '@prisma-next/sql-relational-core/ast';
14
- import { timeouts } from '@prisma-next/test-utils';
15
- import { describe, expect, it, vi } from 'vitest';
16
- import { lints } from '../src/middleware/lints';
17
- import type { SqlMiddlewareContext } from '../src/middleware/sql-middleware';
18
-
19
- function createMiddlewareContext(): SqlMiddlewareContext {
20
- return {
21
- contract: {} as Contract<SqlStorage>,
22
- mode: 'strict' as const,
23
- now: () => Date.now(),
24
- log: {
25
- info: vi.fn(),
26
- warn: vi.fn(),
27
- error: vi.fn(),
28
- },
29
- };
30
- }
31
-
32
- const baseMeta: PlanMeta = {
33
- target: 'postgres',
34
- storageHash: 'sha256:test',
35
- lane: 'dsl',
36
- paramDescriptors: [],
37
- };
38
-
39
- type PlanOverrides = Partial<Omit<ExecutionPlan, 'meta'>> & { meta?: Partial<PlanMeta> };
40
-
41
- function createPlan(overrides: PlanOverrides): ExecutionPlan {
42
- const { meta: metaOverrides, ...rest } = overrides;
43
- return {
44
- sql: 'SELECT 1',
45
- params: [],
46
- meta: { ...baseMeta, ...(metaOverrides ?? {}) } as PlanMeta,
47
- ...rest,
48
- } as ExecutionPlan;
49
- }
50
-
51
- const userTable = TableSource.named('user');
52
- const idCol = ColumnRef.of('user', 'id');
53
-
54
- describe('lints middleware', () => {
55
- it(
56
- 'blocks delete without where',
57
- async () => {
58
- const plan = createPlan({ ast: DeleteAst.from(userTable) });
59
- const mw = lints();
60
- const ctx = createMiddlewareContext();
61
-
62
- await expect(mw.beforeExecute?.(plan, ctx)).rejects.toMatchObject({
63
- code: 'LINT.DELETE_WITHOUT_WHERE',
64
- details: { table: 'user' },
65
- });
66
- },
67
- timeouts.default,
68
- );
69
-
70
- it(
71
- 'blocks update without where',
72
- async () => {
73
- const plan = createPlan({
74
- ast: UpdateAst.table(userTable).withSet({
75
- email: ParamRef.of('new@example.com', { name: 'email', codecId: 'pg/text@1' }),
76
- }),
77
- });
78
- const mw = lints();
79
- const ctx = createMiddlewareContext();
80
-
81
- await expect(mw.beforeExecute?.(plan, ctx)).rejects.toMatchObject({
82
- code: 'LINT.UPDATE_WITHOUT_WHERE',
83
- details: { table: 'user' },
84
- });
85
- },
86
- timeouts.default,
87
- );
88
-
89
- it(
90
- 'warns for unbounded selects and selectAll intent',
91
- async () => {
92
- const ast = SelectAst.from(userTable)
93
- .withProjection([ProjectionItem.of('id', idCol)])
94
- .withSelectAllIntent({ table: 'user' });
95
- const plan = createPlan({ ast });
96
- const mw = lints();
97
- const ctx = createMiddlewareContext();
98
-
99
- await mw.beforeExecute?.(plan, ctx);
100
- expect(ctx.log.warn).toHaveBeenCalledWith(
101
- expect.objectContaining({ code: 'LINT.NO_LIMIT', details: { table: 'user' } }),
102
- );
103
- expect(ctx.log.warn).toHaveBeenCalledWith(
104
- expect.objectContaining({ code: 'LINT.SELECT_STAR', details: { table: 'user' } }),
105
- );
106
- },
107
- timeouts.default,
108
- );
109
-
110
- it(
111
- 'uses derived table aliases when reporting unbounded selects',
112
- async () => {
113
- const derived = DerivedTableSource.as(
114
- 'user_ids',
115
- SelectAst.from(userTable).withProjection([ProjectionItem.of('id', idCol)]),
116
- );
117
- const ast = SelectAst.from(derived).withProjection([
118
- ProjectionItem.of('id', ColumnRef.of('user_ids', 'id')),
119
- ]);
120
- const plan = createPlan({ ast });
121
- const mw = lints();
122
- const ctx = createMiddlewareContext();
123
-
124
- await mw.beforeExecute?.(plan, ctx);
125
- expect(ctx.log.warn).toHaveBeenCalledWith(
126
- expect.objectContaining({
127
- code: 'LINT.NO_LIMIT',
128
- details: { table: 'user_ids' },
129
- }),
130
- );
131
- },
132
- timeouts.default,
133
- );
134
-
135
- it(
136
- 'allows bounded selects and guarded mutations',
137
- async () => {
138
- const selectPlan = createPlan({
139
- ast: SelectAst.from(userTable)
140
- .withProjection([ProjectionItem.of('id', idCol)])
141
- .withWhere(BinaryExpr.eq(idCol, ParamRef.of(42, { codecId: 'pg/int4@1' })))
142
- .withLimit(10),
143
- });
144
- const updatePlan = createPlan({
145
- ast: UpdateAst.table(userTable)
146
- .withSet({
147
- email: ParamRef.of('new@example.com', { name: 'email', codecId: 'pg/text@1' }),
148
- })
149
- .withWhere(BinaryExpr.eq(idCol, ParamRef.of(1, { name: 'id', codecId: 'pg/int4@1' }))),
150
- });
151
- const mw = lints();
152
- const ctx = createMiddlewareContext();
153
-
154
- await mw.beforeExecute?.(selectPlan, ctx);
155
- await mw.beforeExecute?.(updatePlan, ctx);
156
- expect(ctx.log.warn).not.toHaveBeenCalled();
157
- },
158
- timeouts.default,
159
- );
160
- });
@@ -1,254 +0,0 @@
1
- import { type Contract, coreHash, executionHash, profileHash } from '@prisma-next/contract/types';
2
- import type { SqlStorage } from '@prisma-next/sql-contract/types';
3
- import { createCodecRegistry } from '@prisma-next/sql-relational-core/ast';
4
- import { describe, expect, it } from 'vitest';
5
- import {
6
- createExecutionContext,
7
- type SqlExecutionStack,
8
- type SqlRuntimeExtensionDescriptor,
9
- } from '../src/sql-context';
10
- import {
11
- createStubAdapter,
12
- createTestAdapterDescriptor,
13
- createTestTargetDescriptor,
14
- } from './utils';
15
-
16
- const testContract: Contract<SqlStorage> = {
17
- targetFamily: 'sql',
18
- target: 'postgres',
19
- profileHash: profileHash('sha256:test'),
20
- models: {},
21
- roots: {},
22
- storage: {
23
- storageHash: coreHash('sha256:test'),
24
- tables: {
25
- user: {
26
- columns: {
27
- id: { nativeType: 'text', codecId: 'pg/text@1', nullable: false },
28
- },
29
- uniques: [],
30
- indexes: [],
31
- foreignKeys: [],
32
- },
33
- },
34
- },
35
- extensionPacks: {},
36
- capabilities: {},
37
- meta: {},
38
- };
39
-
40
- function createStack(
41
- extensionPacks: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>,
42
- ): SqlExecutionStack<'postgres'> {
43
- return {
44
- target: createTestTargetDescriptor(),
45
- adapter: createTestAdapterDescriptor(createStubAdapter()),
46
- extensionPacks,
47
- };
48
- }
49
-
50
- describe('composed runtime mutation default generators', () => {
51
- it('resolves a pack-contributed generator id', () => {
52
- const extension: SqlRuntimeExtensionDescriptor<'postgres'> = {
53
- kind: 'extension',
54
- id: 'test-mutation-defaults',
55
- version: '0.0.1',
56
- familyId: 'sql',
57
- targetId: 'postgres',
58
- codecs: () => createCodecRegistry(),
59
- parameterizedCodecs: () => [],
60
- mutationDefaultGenerators: () => [
61
- {
62
- id: 'slugid',
63
- generate: () => 'slug-from-pack',
64
- },
65
- ],
66
- create() {
67
- return { familyId: 'sql', targetId: 'postgres' };
68
- },
69
- };
70
-
71
- const context = createExecutionContext({
72
- contract: {
73
- ...testContract,
74
- execution: {
75
- executionHash: executionHash('sha256:test'),
76
- mutations: {
77
- defaults: [
78
- {
79
- ref: { table: 'user', column: 'id' },
80
- onCreate: { kind: 'generator', id: 'slugid' },
81
- },
82
- ],
83
- },
84
- },
85
- },
86
- stack: createStack([extension]),
87
- });
88
-
89
- const applied = context.applyMutationDefaults({ op: 'create', table: 'user', values: {} });
90
- expect(applied).toEqual([{ column: 'id', value: 'slug-from-pack' }]);
91
- });
92
-
93
- it('skips generated default when user provides an explicit value', () => {
94
- const extension: SqlRuntimeExtensionDescriptor<'postgres'> = {
95
- kind: 'extension',
96
- id: 'test-mutation-defaults',
97
- version: '0.0.1',
98
- familyId: 'sql',
99
- targetId: 'postgres',
100
- codecs: () => createCodecRegistry(),
101
- parameterizedCodecs: () => [],
102
- mutationDefaultGenerators: () => [
103
- {
104
- id: 'slugid',
105
- generate: () => 'slug-from-pack',
106
- },
107
- ],
108
- create() {
109
- return { familyId: 'sql', targetId: 'postgres' };
110
- },
111
- };
112
-
113
- const context = createExecutionContext({
114
- contract: {
115
- ...testContract,
116
- execution: {
117
- executionHash: executionHash('sha256:test'),
118
- mutations: {
119
- defaults: [
120
- {
121
- ref: { table: 'user', column: 'id' },
122
- onCreate: { kind: 'generator', id: 'slugid' },
123
- },
124
- ],
125
- },
126
- },
127
- },
128
- stack: createStack([extension]),
129
- });
130
-
131
- const applied = context.applyMutationDefaults({
132
- op: 'create',
133
- table: 'user',
134
- values: { id: 'user-provided-value' },
135
- });
136
- expect(applied).toEqual([]);
137
- });
138
-
139
- it('throws error naming both owners when duplicate generator ids are composed', () => {
140
- const first: SqlRuntimeExtensionDescriptor<'postgres'> = {
141
- kind: 'extension',
142
- id: 'first-pack',
143
- version: '0.0.1',
144
- familyId: 'sql',
145
- targetId: 'postgres',
146
- codecs: () => createCodecRegistry(),
147
- parameterizedCodecs: () => [],
148
- mutationDefaultGenerators: () => [{ id: 'duplicate', generate: () => 'first' }],
149
- create() {
150
- return { familyId: 'sql', targetId: 'postgres' };
151
- },
152
- };
153
- const second: SqlRuntimeExtensionDescriptor<'postgres'> = {
154
- kind: 'extension',
155
- id: 'second-pack',
156
- version: '0.0.1',
157
- familyId: 'sql',
158
- targetId: 'postgres',
159
- codecs: () => createCodecRegistry(),
160
- parameterizedCodecs: () => [],
161
- mutationDefaultGenerators: () => [{ id: 'duplicate', generate: () => 'second' }],
162
- create() {
163
- return { familyId: 'sql', targetId: 'postgres' };
164
- },
165
- };
166
-
167
- expect(() =>
168
- createExecutionContext({
169
- contract: testContract,
170
- stack: createStack([first, second]),
171
- }),
172
- ).toThrow(
173
- expect.objectContaining({
174
- code: 'RUNTIME.DUPLICATE_MUTATION_DEFAULT_GENERATOR',
175
- details: expect.objectContaining({
176
- existingOwner: 'first-pack',
177
- incomingOwner: 'second-pack',
178
- }),
179
- }),
180
- );
181
- });
182
-
183
- it('throws stable error when generator id implementation is missing', () => {
184
- const context = createExecutionContext({
185
- contract: {
186
- ...testContract,
187
- execution: {
188
- executionHash: executionHash('sha256:test'),
189
- mutations: {
190
- defaults: [
191
- {
192
- ref: { table: 'user', column: 'id' },
193
- onCreate: { kind: 'generator', id: 'unknown-generator' },
194
- },
195
- ],
196
- },
197
- },
198
- },
199
- stack: createStack([]),
200
- });
201
-
202
- expect(() =>
203
- context.applyMutationDefaults({
204
- op: 'create',
205
- table: 'user',
206
- values: {},
207
- }),
208
- ).toThrow(
209
- expect.objectContaining({
210
- code: 'RUNTIME.MUTATION_DEFAULT_GENERATOR_MISSING',
211
- }),
212
- );
213
- });
214
-
215
- it('does not resolve built-in generator ids without composed contributors', () => {
216
- const adapterWithoutMutationDefaultGenerators = {
217
- ...createTestAdapterDescriptor(createStubAdapter()),
218
- mutationDefaultGenerators: () => [],
219
- };
220
- const context = createExecutionContext({
221
- contract: {
222
- ...testContract,
223
- execution: {
224
- executionHash: executionHash('sha256:test'),
225
- mutations: {
226
- defaults: [
227
- {
228
- ref: { table: 'user', column: 'id' },
229
- onCreate: { kind: 'generator', id: 'uuidv4' },
230
- },
231
- ],
232
- },
233
- },
234
- },
235
- stack: {
236
- target: createTestTargetDescriptor(),
237
- adapter: adapterWithoutMutationDefaultGenerators,
238
- extensionPacks: [],
239
- },
240
- });
241
-
242
- expect(() =>
243
- context.applyMutationDefaults({
244
- op: 'create',
245
- table: 'user',
246
- values: {},
247
- }),
248
- ).toThrow(
249
- expect.objectContaining({
250
- code: 'RUNTIME.MUTATION_DEFAULT_GENERATOR_MISSING',
251
- }),
252
- );
253
- });
254
- });