@prisma-next/sql-runtime 0.5.0-dev.4 → 0.5.0-dev.40

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 (45) hide show
  1. package/README.md +29 -21
  2. package/dist/exports-CrHMfIKo.mjs +1564 -0
  3. package/dist/exports-CrHMfIKo.mjs.map +1 -0
  4. package/dist/{index-yb51L_1h.d.mts → index-_dXSGeho.d.mts} +78 -25
  5. package/dist/index-_dXSGeho.d.mts.map +1 -0
  6. package/dist/index.d.mts +2 -2
  7. package/dist/index.mjs +2 -2
  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 +11 -5
  11. package/dist/test/utils.mjs.map +1 -1
  12. package/package.json +10 -12
  13. package/src/codecs/decoding.ts +294 -173
  14. package/src/codecs/encoding.ts +162 -37
  15. package/src/codecs/validation.ts +22 -3
  16. package/src/exports/index.ts +11 -7
  17. package/src/fingerprint.ts +22 -0
  18. package/src/guardrails/raw.ts +165 -0
  19. package/src/lower-sql-plan.ts +3 -3
  20. package/src/marker.ts +75 -0
  21. package/src/middleware/before-compile-chain.ts +1 -0
  22. package/src/middleware/budgets.ts +26 -96
  23. package/src/middleware/lints.ts +3 -3
  24. package/src/middleware/sql-middleware.ts +6 -5
  25. package/src/runtime-spi.ts +44 -0
  26. package/src/sql-context.ts +332 -78
  27. package/src/sql-family-adapter.ts +3 -2
  28. package/src/sql-marker.ts +62 -47
  29. package/src/sql-runtime.ts +332 -113
  30. package/dist/exports-BQZSVXXt.mjs +0 -981
  31. package/dist/exports-BQZSVXXt.mjs.map +0 -1
  32. package/dist/index-yb51L_1h.d.mts.map +0 -1
  33. package/test/async-iterable-result.test.ts +0 -141
  34. package/test/before-compile-chain.test.ts +0 -223
  35. package/test/budgets.test.ts +0 -431
  36. package/test/context.types.test-d.ts +0 -68
  37. package/test/execution-stack.test.ts +0 -161
  38. package/test/json-schema-validation.test.ts +0 -571
  39. package/test/lints.test.ts +0 -160
  40. package/test/mutation-default-generators.test.ts +0 -254
  41. package/test/parameterized-types.test.ts +0 -529
  42. package/test/sql-context.test.ts +0 -384
  43. package/test/sql-family-adapter.test.ts +0 -103
  44. package/test/sql-runtime.test.ts +0 -792
  45. 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
- });