@prisma-next/sql-runtime 0.3.0-pr.99.6 → 0.4.0-dev.1

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 (171) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +141 -24
  3. package/dist/exports-BO6Fl7yn.mjs +889 -0
  4. package/dist/exports-BO6Fl7yn.mjs.map +1 -0
  5. package/dist/index-n6z6trta.d.mts +186 -0
  6. package/dist/index-n6z6trta.d.mts.map +1 -0
  7. package/dist/index.d.mts +2 -0
  8. package/dist/index.mjs +3 -0
  9. package/dist/test/utils.d.mts +77 -0
  10. package/dist/test/utils.d.mts.map +1 -0
  11. package/dist/test/utils.mjs +221 -0
  12. package/dist/test/utils.mjs.map +1 -0
  13. package/package.json +26 -20
  14. package/src/codecs/decoding.ts +84 -3
  15. package/src/codecs/encoding.ts +5 -15
  16. package/src/codecs/json-schema-validation.ts +61 -0
  17. package/src/codecs/validation.ts +7 -6
  18. package/src/exports/index.ts +20 -9
  19. package/src/lower-sql-plan.ts +9 -9
  20. package/src/middleware/budgets.ts +256 -0
  21. package/src/middleware/lints.ts +192 -0
  22. package/src/middleware/sql-middleware.ts +26 -0
  23. package/src/sql-context.ts +357 -257
  24. package/src/sql-family-adapter.ts +17 -23
  25. package/src/sql-marker.ts +2 -2
  26. package/src/sql-runtime.ts +136 -61
  27. package/test/async-iterable-result.test.ts +42 -37
  28. package/test/budgets.test.ts +431 -0
  29. package/test/context.types.test-d.ts +18 -20
  30. package/test/execution-stack.test.ts +164 -0
  31. package/test/json-schema-validation.test.ts +571 -0
  32. package/test/lints.test.ts +159 -0
  33. package/test/mutation-default-generators.test.ts +254 -0
  34. package/test/parameterized-types.test.ts +181 -205
  35. package/test/sql-context.test.ts +301 -134
  36. package/test/sql-family-adapter.test.ts +37 -20
  37. package/test/sql-runtime.test.ts +261 -49
  38. package/test/utils.ts +101 -67
  39. package/dist/accelerate-EEKAFGN3-P6A6XJWJ.js +0 -137863
  40. package/dist/accelerate-EEKAFGN3-P6A6XJWJ.js.map +0 -1
  41. package/dist/amcheck-24VY6X5V.js +0 -13
  42. package/dist/amcheck-24VY6X5V.js.map +0 -1
  43. package/dist/bloom-VS74NLHT.js +0 -13
  44. package/dist/bloom-VS74NLHT.js.map +0 -1
  45. package/dist/btree_gin-WBC4EAAI.js +0 -13
  46. package/dist/btree_gin-WBC4EAAI.js.map +0 -1
  47. package/dist/btree_gist-UNC6QD3M.js +0 -13
  48. package/dist/btree_gist-UNC6QD3M.js.map +0 -1
  49. package/dist/chunk-3KTOEDFX.js +0 -49
  50. package/dist/chunk-3KTOEDFX.js.map +0 -1
  51. package/dist/chunk-47DZBRQC.js +0 -1280
  52. package/dist/chunk-47DZBRQC.js.map +0 -1
  53. package/dist/chunk-52N6AFZM.js +0 -133
  54. package/dist/chunk-52N6AFZM.js.map +0 -1
  55. package/dist/chunk-7D4SUZUM.js +0 -38
  56. package/dist/chunk-7D4SUZUM.js.map +0 -1
  57. package/dist/chunk-APA6GHYY.js +0 -537
  58. package/dist/chunk-APA6GHYY.js.map +0 -1
  59. package/dist/chunk-ECWIHLAT.js +0 -37
  60. package/dist/chunk-ECWIHLAT.js.map +0 -1
  61. package/dist/chunk-EI626SDC.js +0 -105
  62. package/dist/chunk-EI626SDC.js.map +0 -1
  63. package/dist/chunk-UKKOYUGL.js +0 -578
  64. package/dist/chunk-UKKOYUGL.js.map +0 -1
  65. package/dist/chunk-XPLNMXQV.js +0 -1537
  66. package/dist/chunk-XPLNMXQV.js.map +0 -1
  67. package/dist/citext-T7MXGUY7.js +0 -13
  68. package/dist/citext-T7MXGUY7.js.map +0 -1
  69. package/dist/client-5FENX6AW.js +0 -299
  70. package/dist/client-5FENX6AW.js.map +0 -1
  71. package/dist/cube-TFDQBZCI.js +0 -13
  72. package/dist/cube-TFDQBZCI.js.map +0 -1
  73. package/dist/dict_int-AEUOPGWP.js +0 -13
  74. package/dist/dict_int-AEUOPGWP.js.map +0 -1
  75. package/dist/dict_xsyn-DAAYX3FL.js +0 -13
  76. package/dist/dict_xsyn-DAAYX3FL.js.map +0 -1
  77. package/dist/dist-AQ3LWXOX.js +0 -570
  78. package/dist/dist-AQ3LWXOX.js.map +0 -1
  79. package/dist/dist-LBVX6BJW.js +0 -189
  80. package/dist/dist-LBVX6BJW.js.map +0 -1
  81. package/dist/dist-WLKUVDN2.js +0 -5127
  82. package/dist/dist-WLKUVDN2.js.map +0 -1
  83. package/dist/earthdistance-KIGTF4LE.js +0 -13
  84. package/dist/earthdistance-KIGTF4LE.js.map +0 -1
  85. package/dist/file_fdw-5N55UP6I.js +0 -13
  86. package/dist/file_fdw-5N55UP6I.js.map +0 -1
  87. package/dist/fuzzystrmatch-KN3YWBFP.js +0 -13
  88. package/dist/fuzzystrmatch-KN3YWBFP.js.map +0 -1
  89. package/dist/hstore-YX726NKN.js +0 -13
  90. package/dist/hstore-YX726NKN.js.map +0 -1
  91. package/dist/http-exception-FZY2H4OF.js +0 -8
  92. package/dist/http-exception-FZY2H4OF.js.map +0 -1
  93. package/dist/index.js +0 -30
  94. package/dist/index.js.map +0 -1
  95. package/dist/intarray-NKVXNO2D.js +0 -13
  96. package/dist/intarray-NKVXNO2D.js.map +0 -1
  97. package/dist/isn-FTEMJGEV.js +0 -13
  98. package/dist/isn-FTEMJGEV.js.map +0 -1
  99. package/dist/lo-DB7L4NGI.js +0 -13
  100. package/dist/lo-DB7L4NGI.js.map +0 -1
  101. package/dist/logger-WQ7SHNDD.js +0 -68
  102. package/dist/logger-WQ7SHNDD.js.map +0 -1
  103. package/dist/ltree-Z32TZT6W.js +0 -13
  104. package/dist/ltree-Z32TZT6W.js.map +0 -1
  105. package/dist/nodefs-NM46ACH7.js +0 -31
  106. package/dist/nodefs-NM46ACH7.js.map +0 -1
  107. package/dist/opfs-ahp-NJO33LVZ.js +0 -332
  108. package/dist/opfs-ahp-NJO33LVZ.js.map +0 -1
  109. package/dist/pageinspect-YP3IZR4X.js +0 -13
  110. package/dist/pageinspect-YP3IZR4X.js.map +0 -1
  111. package/dist/pg_buffercache-7TD5J2FB.js +0 -13
  112. package/dist/pg_buffercache-7TD5J2FB.js.map +0 -1
  113. package/dist/pg_dump-SG4KYBUB.js +0 -2492
  114. package/dist/pg_dump-SG4KYBUB.js.map +0 -1
  115. package/dist/pg_freespacemap-DZDNCPZK.js +0 -13
  116. package/dist/pg_freespacemap-DZDNCPZK.js.map +0 -1
  117. package/dist/pg_surgery-J2MUEWEP.js +0 -13
  118. package/dist/pg_surgery-J2MUEWEP.js.map +0 -1
  119. package/dist/pg_trgm-7VNQOYS6.js +0 -13
  120. package/dist/pg_trgm-7VNQOYS6.js.map +0 -1
  121. package/dist/pg_visibility-TTSIPHFL.js +0 -13
  122. package/dist/pg_visibility-TTSIPHFL.js.map +0 -1
  123. package/dist/pg_walinspect-KPFHSHRJ.js +0 -13
  124. package/dist/pg_walinspect-KPFHSHRJ.js.map +0 -1
  125. package/dist/proxy-signals-GUDAMDHV.js +0 -39
  126. package/dist/proxy-signals-GUDAMDHV.js.map +0 -1
  127. package/dist/seg-IYVDLE4O.js +0 -13
  128. package/dist/seg-IYVDLE4O.js.map +0 -1
  129. package/dist/src/codecs/decoding.d.ts +0 -4
  130. package/dist/src/codecs/decoding.d.ts.map +0 -1
  131. package/dist/src/codecs/encoding.d.ts +0 -5
  132. package/dist/src/codecs/encoding.d.ts.map +0 -1
  133. package/dist/src/codecs/validation.d.ts +0 -6
  134. package/dist/src/codecs/validation.d.ts.map +0 -1
  135. package/dist/src/exports/index.d.ts +0 -11
  136. package/dist/src/exports/index.d.ts.map +0 -1
  137. package/dist/src/index.d.ts +0 -2
  138. package/dist/src/index.d.ts.map +0 -1
  139. package/dist/src/lower-sql-plan.d.ts +0 -15
  140. package/dist/src/lower-sql-plan.d.ts.map +0 -1
  141. package/dist/src/sql-context.d.ts +0 -130
  142. package/dist/src/sql-context.d.ts.map +0 -1
  143. package/dist/src/sql-family-adapter.d.ts +0 -10
  144. package/dist/src/sql-family-adapter.d.ts.map +0 -1
  145. package/dist/src/sql-marker.d.ts +0 -22
  146. package/dist/src/sql-marker.d.ts.map +0 -1
  147. package/dist/src/sql-runtime.d.ts +0 -25
  148. package/dist/src/sql-runtime.d.ts.map +0 -1
  149. package/dist/tablefunc-EF4RCS7S.js +0 -13
  150. package/dist/tablefunc-EF4RCS7S.js.map +0 -1
  151. package/dist/tcn-3VT5BQYW.js +0 -13
  152. package/dist/tcn-3VT5BQYW.js.map +0 -1
  153. package/dist/test/utils.d.ts +0 -60
  154. package/dist/test/utils.d.ts.map +0 -1
  155. package/dist/test/utils.js +0 -24635
  156. package/dist/test/utils.js.map +0 -1
  157. package/dist/tiny-CW6F4GX6.js +0 -10
  158. package/dist/tiny-CW6F4GX6.js.map +0 -1
  159. package/dist/tsm_system_rows-ES7KNUQH.js +0 -13
  160. package/dist/tsm_system_rows-ES7KNUQH.js.map +0 -1
  161. package/dist/tsm_system_time-76WEIMBG.js +0 -13
  162. package/dist/tsm_system_time-76WEIMBG.js.map +0 -1
  163. package/dist/unaccent-7RYF3R64.js +0 -13
  164. package/dist/unaccent-7RYF3R64.js.map +0 -1
  165. package/dist/utility-Q5A254LJ-J4HTKZPT.js +0 -347
  166. package/dist/utility-Q5A254LJ-J4HTKZPT.js.map +0 -1
  167. package/dist/uuid_ossp-4ETE4FPE.js +0 -13
  168. package/dist/uuid_ossp-4ETE4FPE.js.map +0 -1
  169. package/dist/vector-74GPNV7V.js +0 -13
  170. package/dist/vector-74GPNV7V.js.map +0 -1
  171. package/src/index.ts +0 -1
@@ -0,0 +1,221 @@
1
+ import { a as writeContractMarker, n as ensureSchemaStatement, o as createExecutionContext, r as ensureTableStatement, s as createSqlExecutionStack } from "../exports-BO6Fl7yn.mjs";
2
+ import { codec, createCodecRegistry } from "@prisma-next/sql-relational-core/ast";
3
+ import { instantiateExecutionStack } from "@prisma-next/framework-components/execution";
4
+ import { coreHash, profileHash } from "@prisma-next/contract/types";
5
+ import { builtinGeneratorIds } from "@prisma-next/ids";
6
+ import { generateId } from "@prisma-next/ids/runtime";
7
+ import { collectAsync, collectAsync as collectAsync$1, createDevDatabase, drainAsyncIterable, teardownTestDatabase, withClient } from "@prisma-next/test-utils";
8
+
9
+ //#region test/utils.ts
10
+ function createTestMutationDefaultGenerators() {
11
+ return builtinGeneratorIds.map((id) => ({
12
+ id,
13
+ generate: (params) => generateId(params ? {
14
+ id,
15
+ params
16
+ } : { id })
17
+ }));
18
+ }
19
+ /**
20
+ * Executes a plan and collects all results into an array.
21
+ * This helper DRYs up the common pattern of executing plans in tests.
22
+ * The return type is inferred from the plan's type parameter.
23
+ */
24
+ async function executePlanAndCollect(runtime, plan) {
25
+ return collectAsync$1(runtime.execute(plan));
26
+ }
27
+ /**
28
+ * Drains a plan execution, consuming all results without collecting them.
29
+ * Useful for testing side effects without memory overhead.
30
+ */
31
+ async function drainPlanExecution(runtime, plan) {
32
+ return drainAsyncIterable(runtime.execute(plan));
33
+ }
34
+ /**
35
+ * Executes a SQL statement on a database client.
36
+ */
37
+ async function executeStatement(client, statement) {
38
+ if (statement.params.length > 0) {
39
+ await client.query(statement.sql, [...statement.params]);
40
+ return;
41
+ }
42
+ await client.query(statement.sql);
43
+ }
44
+ /**
45
+ * Sets up database schema and data, then writes the contract marker.
46
+ * This helper DRYs up the common pattern of database setup in tests.
47
+ */
48
+ async function setupTestDatabase(client, contract, setupFn) {
49
+ await client.query("drop schema if exists prisma_contract cascade");
50
+ await client.query("create schema if not exists public");
51
+ await setupFn(client);
52
+ await executeStatement(client, ensureSchemaStatement);
53
+ await executeStatement(client, ensureTableStatement);
54
+ await executeStatement(client, writeContractMarker({
55
+ storageHash: contract.storage.storageHash,
56
+ profileHash: contract.profileHash,
57
+ contractJson: contract,
58
+ canonicalVersion: 1
59
+ }).insert);
60
+ }
61
+ /**
62
+ * Writes a contract marker to the database.
63
+ * This helper DRYs up the common pattern of writing contract markers in tests.
64
+ */
65
+ async function writeTestContractMarker(client, contract) {
66
+ await executeStatement(client, writeContractMarker({
67
+ storageHash: contract.storage.storageHash,
68
+ profileHash: contract.profileHash,
69
+ contractJson: contract,
70
+ canonicalVersion: 1
71
+ }).insert);
72
+ }
73
+ /**
74
+ * Creates a test adapter descriptor from a raw adapter.
75
+ * Wraps the adapter in an SqlRuntimeAdapterDescriptor with static contributions
76
+ * derived from the adapter's codec registry.
77
+ */
78
+ function createTestAdapterDescriptor(adapter) {
79
+ const codecRegistry = adapter.profile.codecs();
80
+ return {
81
+ kind: "adapter",
82
+ id: "test-adapter",
83
+ version: "0.0.1",
84
+ familyId: "sql",
85
+ targetId: "postgres",
86
+ codecs: () => codecRegistry,
87
+ parameterizedCodecs: () => [],
88
+ mutationDefaultGenerators: createTestMutationDefaultGenerators,
89
+ create() {
90
+ return Object.assign({
91
+ familyId: "sql",
92
+ targetId: "postgres"
93
+ }, adapter);
94
+ }
95
+ };
96
+ }
97
+ /**
98
+ * Creates a test target descriptor with empty static contributions.
99
+ */
100
+ function createTestTargetDescriptor() {
101
+ return {
102
+ kind: "target",
103
+ id: "postgres",
104
+ version: "0.0.1",
105
+ familyId: "sql",
106
+ targetId: "postgres",
107
+ codecs: () => createCodecRegistry(),
108
+ parameterizedCodecs: () => [],
109
+ create() {
110
+ return {
111
+ familyId: "sql",
112
+ targetId: "postgres"
113
+ };
114
+ }
115
+ };
116
+ }
117
+ /**
118
+ * Creates an ExecutionContext for testing.
119
+ * This helper DRYs up the common pattern of context creation in tests.
120
+ *
121
+ * Accepts a raw adapter and optional extension descriptors, wrapping the
122
+ * adapter in a descriptor internally for descriptor-first context creation.
123
+ */
124
+ function createTestContext(contract, adapter, options) {
125
+ return createExecutionContext({
126
+ contract,
127
+ stack: {
128
+ target: createTestTargetDescriptor(),
129
+ adapter: createTestAdapterDescriptor(adapter),
130
+ extensionPacks: options?.extensionPacks ?? []
131
+ }
132
+ });
133
+ }
134
+ function createTestStackInstance(options) {
135
+ return instantiateExecutionStack(createSqlExecutionStack({
136
+ target: createTestTargetDescriptor(),
137
+ adapter: createTestAdapterDescriptor(createStubAdapter()),
138
+ driver: options?.driver,
139
+ extensionPacks: options?.extensionPacks ?? []
140
+ }));
141
+ }
142
+ /**
143
+ * Creates a stub adapter for testing.
144
+ * This helper DRYs up the common pattern of adapter creation in tests.
145
+ *
146
+ * The stub adapter includes simple codecs for common test types (pg/int4@1, pg/text@1, pg/timestamptz@1)
147
+ * to enable type inference in tests without requiring the postgres adapter package.
148
+ */
149
+ function createStubAdapter() {
150
+ const codecRegistry = createCodecRegistry();
151
+ codecRegistry.register(codec({
152
+ typeId: "pg/int4@1",
153
+ targetTypes: ["int4"],
154
+ encode: (value) => value,
155
+ decode: (wire) => wire
156
+ }));
157
+ codecRegistry.register(codec({
158
+ typeId: "pg/text@1",
159
+ targetTypes: ["text"],
160
+ encode: (value) => value,
161
+ decode: (wire) => wire
162
+ }));
163
+ codecRegistry.register(codec({
164
+ typeId: "pg/timestamptz@1",
165
+ targetTypes: ["timestamptz"],
166
+ encode: (value) => value instanceof Date ? value.toISOString() : value,
167
+ decode: (wire) => wire instanceof Date ? wire : new Date(wire)
168
+ }));
169
+ return {
170
+ profile: {
171
+ id: "stub-profile",
172
+ target: "postgres",
173
+ capabilities: {},
174
+ codecs() {
175
+ return codecRegistry;
176
+ },
177
+ readMarkerStatement() {
178
+ return {
179
+ sql: "select core_hash, profile_hash, contract_json, canonical_version, updated_at, app_tag, meta from prisma_contract.marker where id = $1",
180
+ params: [1]
181
+ };
182
+ }
183
+ },
184
+ lower(ast, ctx) {
185
+ const sqlText = JSON.stringify(ast);
186
+ return {
187
+ profileId: this.profile.id,
188
+ body: Object.freeze({
189
+ sql: sqlText,
190
+ params: ctx.params ? [...ctx.params] : []
191
+ })
192
+ };
193
+ }
194
+ };
195
+ }
196
+ function createTestContract(contract) {
197
+ const { execution, ...rest } = contract;
198
+ const storageHashValue = coreHash(rest["storageHash"] ?? "sha256:testcore");
199
+ return {
200
+ target: rest["target"] ?? "postgres",
201
+ targetFamily: rest["targetFamily"] ?? "sql",
202
+ storage: rest["storage"] ? {
203
+ ...rest["storage"],
204
+ storageHash: storageHashValue
205
+ } : {
206
+ storageHash: storageHashValue,
207
+ tables: {}
208
+ },
209
+ models: rest["models"] ?? {},
210
+ roots: rest["roots"] ?? {},
211
+ capabilities: rest["capabilities"] ?? {},
212
+ extensionPacks: rest["extensionPacks"] ?? {},
213
+ meta: rest["meta"] ?? {},
214
+ ...execution ? { execution } : {},
215
+ profileHash: profileHash(rest["profileHash"] ?? "sha256:testprofile")
216
+ };
217
+ }
218
+
219
+ //#endregion
220
+ export { collectAsync, createDevDatabase, createStubAdapter, createTestAdapterDescriptor, createTestContext, createTestContract, createTestStackInstance, createTestTargetDescriptor, drainPlanExecution, executePlanAndCollect, executeStatement, setupTestDatabase, teardownTestDatabase, withClient, writeTestContractMarker };
221
+ //# sourceMappingURL=utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.mjs","names":["collectAsync"],"sources":["../../test/utils.ts"],"sourcesContent":["import type { Contract, ExecutionPlan, ResultType } from '@prisma-next/contract/types';\nimport { coreHash, profileHash } from '@prisma-next/contract/types';\nimport {\n instantiateExecutionStack,\n type RuntimeDriverDescriptor,\n} from '@prisma-next/framework-components/execution';\nimport { builtinGeneratorIds } from '@prisma-next/ids';\nimport { generateId } from '@prisma-next/ids/runtime';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { Adapter, LoweredStatement, SelectAst } from '@prisma-next/sql-relational-core/ast';\nimport { codec, createCodecRegistry } from '@prisma-next/sql-relational-core/ast';\nimport type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';\nimport { collectAsync, drainAsyncIterable } from '@prisma-next/test-utils';\nimport type { Client } from 'pg';\nimport type { SqlStatement } from '../src/exports';\nimport {\n createExecutionContext,\n type createRuntime,\n createSqlExecutionStack,\n ensureSchemaStatement,\n ensureTableStatement,\n writeContractMarker,\n} from '../src/exports';\nimport type {\n ExecutionContext,\n SqlRuntimeAdapterDescriptor,\n SqlRuntimeAdapterInstance,\n SqlRuntimeDriverInstance,\n SqlRuntimeExtensionDescriptor,\n SqlRuntimeTargetDescriptor,\n} from '../src/sql-context';\n\nfunction createTestMutationDefaultGenerators() {\n return builtinGeneratorIds.map((id) => ({\n id,\n generate: (params?: Record<string, unknown>) => generateId(params ? { id, params } : { id }),\n }));\n}\n\n/**\n * Executes a plan and collects all results into an array.\n * This helper DRYs up the common pattern of executing plans in tests.\n * The return type is inferred from the plan's type parameter.\n */\nexport async function executePlanAndCollect<\n P extends ExecutionPlan<ResultType<P>> | SqlQueryPlan<ResultType<P>>,\n>(runtime: ReturnType<typeof createRuntime>, plan: P): Promise<ResultType<P>[]> {\n type Row = ResultType<P>;\n return collectAsync<Row>(runtime.execute<Row>(plan));\n}\n\n/**\n * Drains a plan execution, consuming all results without collecting them.\n * Useful for testing side effects without memory overhead.\n */\nexport async function drainPlanExecution(\n runtime: ReturnType<typeof createRuntime>,\n plan: ExecutionPlan | SqlQueryPlan<unknown>,\n): Promise<void> {\n return drainAsyncIterable(runtime.execute(plan));\n}\n\n/**\n * Executes a SQL statement on a database client.\n */\nexport async function executeStatement(client: Client, statement: SqlStatement): Promise<void> {\n if (statement.params.length > 0) {\n await client.query(statement.sql, [...statement.params]);\n return;\n }\n\n await client.query(statement.sql);\n}\n\n/**\n * Sets up database schema and data, then writes the contract marker.\n * This helper DRYs up the common pattern of database setup in tests.\n */\nexport async function setupTestDatabase(\n client: Client,\n contract: Contract<SqlStorage>,\n setupFn: (client: Client) => Promise<void>,\n): Promise<void> {\n await client.query('drop schema if exists prisma_contract cascade');\n await client.query('create schema if not exists public');\n\n await setupFn(client);\n\n await executeStatement(client, ensureSchemaStatement);\n await executeStatement(client, ensureTableStatement);\n const write = writeContractMarker({\n storageHash: contract.storage.storageHash,\n profileHash: contract.profileHash,\n contractJson: contract,\n canonicalVersion: 1,\n });\n await executeStatement(client, write.insert);\n}\n\n/**\n * Writes a contract marker to the database.\n * This helper DRYs up the common pattern of writing contract markers in tests.\n */\nexport async function writeTestContractMarker(\n client: Client,\n contract: Contract<SqlStorage>,\n): Promise<void> {\n const write = writeContractMarker({\n storageHash: contract.storage.storageHash,\n profileHash: contract.profileHash,\n contractJson: contract,\n canonicalVersion: 1,\n });\n await executeStatement(client, write.insert);\n}\n\n/**\n * Creates a test adapter descriptor from a raw adapter.\n * Wraps the adapter in an SqlRuntimeAdapterDescriptor with static contributions\n * derived from the adapter's codec registry.\n */\nexport function createTestAdapterDescriptor(\n adapter: Adapter<SelectAst, Contract<SqlStorage>, LoweredStatement>,\n): SqlRuntimeAdapterDescriptor<'postgres'> {\n const codecRegistry = adapter.profile.codecs();\n return {\n kind: 'adapter' as const,\n id: 'test-adapter',\n version: '0.0.1',\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n codecs: () => codecRegistry,\n parameterizedCodecs: () => [],\n mutationDefaultGenerators: createTestMutationDefaultGenerators,\n create(): SqlRuntimeAdapterInstance<'postgres'> {\n return Object.assign({ familyId: 'sql' as const, targetId: 'postgres' as const }, adapter);\n },\n };\n}\n\n/**\n * Creates a test target descriptor with empty static contributions.\n */\nexport function createTestTargetDescriptor(): SqlRuntimeTargetDescriptor<'postgres'> {\n return {\n kind: 'target' as const,\n id: 'postgres',\n version: '0.0.1',\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n codecs: () => createCodecRegistry(),\n parameterizedCodecs: () => [],\n create() {\n return { familyId: 'sql' as const, targetId: 'postgres' as const };\n },\n };\n}\n\n/**\n * Creates an ExecutionContext for testing.\n * This helper DRYs up the common pattern of context creation in tests.\n *\n * Accepts a raw adapter and optional extension descriptors, wrapping the\n * adapter in a descriptor internally for descriptor-first context creation.\n */\nexport function createTestContext<TContract extends Contract<SqlStorage>>(\n contract: TContract,\n adapter: Adapter<SelectAst, Contract<SqlStorage>, LoweredStatement>,\n options?: {\n extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;\n },\n): ExecutionContext<TContract> {\n return createExecutionContext({\n contract,\n stack: {\n target: createTestTargetDescriptor(),\n adapter: createTestAdapterDescriptor(adapter),\n extensionPacks: options?.extensionPacks ?? [],\n },\n });\n}\n\nexport function createTestStackInstance(options?: {\n extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;\n driver?: RuntimeDriverDescriptor<\n 'sql',\n 'postgres',\n unknown,\n SqlRuntimeDriverInstance<'postgres'>\n >;\n}) {\n const stack = createSqlExecutionStack({\n target: createTestTargetDescriptor(),\n adapter: createTestAdapterDescriptor(createStubAdapter()),\n driver: options?.driver,\n extensionPacks: options?.extensionPacks ?? [],\n });\n\n return instantiateExecutionStack(stack);\n}\n\n/**\n * Creates a stub adapter for testing.\n * This helper DRYs up the common pattern of adapter creation in tests.\n *\n * The stub adapter includes simple codecs for common test types (pg/int4@1, pg/text@1, pg/timestamptz@1)\n * to enable type inference in tests without requiring the postgres adapter package.\n */\nexport function createStubAdapter(): Adapter<SelectAst, Contract<SqlStorage>, LoweredStatement> {\n const codecRegistry = createCodecRegistry();\n\n // Register stub codecs for common test types\n // These match the codec IDs used in test contracts (pg/int4@1, pg/text@1, pg/timestamptz@1)\n // but don't require importing from the postgres adapter package\n codecRegistry.register(\n codec({\n typeId: 'pg/int4@1',\n targetTypes: ['int4'],\n encode: (value: number) => value,\n decode: (wire: number) => wire,\n }),\n );\n\n codecRegistry.register(\n codec({\n typeId: 'pg/text@1',\n targetTypes: ['text'],\n encode: (value: string) => value,\n decode: (wire: string) => wire,\n }),\n );\n\n codecRegistry.register(\n codec({\n typeId: 'pg/timestamptz@1',\n targetTypes: ['timestamptz'],\n encode: (value: string | Date) => (value instanceof Date ? value.toISOString() : value),\n decode: (wire: string | Date) => (wire instanceof Date ? wire : new Date(wire)),\n }),\n );\n\n return {\n profile: {\n id: 'stub-profile',\n target: 'postgres',\n capabilities: {},\n codecs() {\n return codecRegistry;\n },\n readMarkerStatement() {\n return {\n sql: 'select core_hash, profile_hash, contract_json, canonical_version, updated_at, app_tag, meta from prisma_contract.marker where id = $1',\n params: [1],\n };\n },\n },\n lower(ast: SelectAst, ctx: { contract: Contract<SqlStorage>; params?: readonly unknown[] }) {\n const sqlText = JSON.stringify(ast);\n return {\n profileId: this.profile.id,\n body: Object.freeze({ sql: sqlText, params: ctx.params ? [...ctx.params] : [] }),\n };\n },\n };\n}\n\nexport function createTestContract(\n contract: Partial<Omit<Contract<SqlStorage>, 'profileHash' | 'storage'>> & {\n storageHash?: string;\n profileHash?: string;\n storage?: Omit<SqlStorage, 'storageHash'>;\n },\n): Contract<SqlStorage> {\n const { execution, ...rest } = contract;\n const storageHashValue = coreHash(rest['storageHash'] ?? 'sha256:testcore');\n\n return {\n target: rest['target'] ?? 'postgres',\n targetFamily: rest['targetFamily'] ?? 'sql',\n storage: rest['storage']\n ? { ...rest['storage'], storageHash: storageHashValue }\n : { storageHash: storageHashValue, tables: {} },\n models: rest['models'] ?? {},\n roots: rest['roots'] ?? {},\n capabilities: rest['capabilities'] ?? {},\n extensionPacks: rest['extensionPacks'] ?? {},\n meta: rest['meta'] ?? {},\n ...(execution ? { execution } : {}),\n profileHash: profileHash(rest['profileHash'] ?? 'sha256:testprofile'),\n };\n}\n\n// Re-export generic utilities from test-utils\nexport {\n collectAsync,\n createDevDatabase,\n type DevDatabase,\n teardownTestDatabase,\n withClient,\n} from '@prisma-next/test-utils';\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,27 +1,30 @@
1
1
  {
2
2
  "name": "@prisma-next/sql-runtime",
3
- "version": "0.3.0-pr.99.6",
3
+ "version": "0.4.0-dev.1",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "SQL runtime implementation for Prisma Next",
7
7
  "dependencies": {
8
8
  "arktype": "^2.1.26",
9
- "@prisma-next/contract": "0.3.0-pr.99.6",
10
- "@prisma-next/core-execution-plane": "0.3.0-pr.99.6",
11
- "@prisma-next/operations": "0.3.0-pr.99.6",
12
- "@prisma-next/runtime-executor": "0.3.0-pr.99.6",
13
- "@prisma-next/sql-contract": "0.3.0-pr.99.6",
14
- "@prisma-next/sql-operations": "0.3.0-pr.99.6",
15
- "@prisma-next/sql-relational-core": "0.3.0-pr.99.6"
9
+ "@prisma-next/contract": "0.4.0-dev.1",
10
+ "@prisma-next/framework-components": "0.4.0-dev.1",
11
+ "@prisma-next/utils": "0.4.0-dev.1",
12
+ "@prisma-next/operations": "0.4.0-dev.1",
13
+ "@prisma-next/ids": "0.4.0-dev.1",
14
+ "@prisma-next/sql-contract": "0.4.0-dev.1",
15
+ "@prisma-next/runtime-executor": "0.4.0-dev.1",
16
+ "@prisma-next/sql-relational-core": "0.4.0-dev.1",
17
+ "@prisma-next/sql-operations": "0.4.0-dev.1"
16
18
  },
17
19
  "devDependencies": {
18
20
  "@types/pg": "8.16.0",
19
21
  "pg": "8.16.3",
20
- "tsup": "8.5.1",
22
+ "tsdown": "0.18.4",
21
23
  "typescript": "5.9.3",
22
- "vitest": "4.0.16",
24
+ "vitest": "4.0.17",
25
+ "@prisma-next/test-utils": "0.0.1",
23
26
  "@prisma-next/tsconfig": "0.0.0",
24
- "@prisma-next/test-utils": "0.0.1"
27
+ "@prisma-next/tsdown": "0.0.0"
25
28
  },
26
29
  "files": [
27
30
  "dist",
@@ -29,17 +32,20 @@
29
32
  "test"
30
33
  ],
31
34
  "exports": {
32
- ".": {
33
- "types": "./dist/src/index.d.ts",
34
- "import": "./dist/index.js"
35
- },
36
- "./test/utils": {
37
- "types": "./dist/test/utils.d.ts",
38
- "import": "./dist/test/utils.js"
39
- }
35
+ ".": "./dist/index.mjs",
36
+ "./test/utils": "./dist/test/utils.mjs",
37
+ "./package.json": "./package.json"
38
+ },
39
+ "main": "./dist/index.mjs",
40
+ "module": "./dist/index.mjs",
41
+ "types": "./dist/index.d.mts",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/prisma/prisma-next.git",
45
+ "directory": "packages/2-sql/5-runtime"
40
46
  },
41
47
  "scripts": {
42
- "build": "tsup --config tsup.config.ts && tsc --project tsconfig.build.json",
48
+ "build": "tsdown",
43
49
  "test": "vitest run --passWithNoTests",
44
50
  "test:coverage": "vitest run --coverage --passWithNoTests",
45
51
  "typecheck": "tsc --project tsconfig.json --noEmit",
@@ -1,5 +1,7 @@
1
1
  import type { ExecutionPlan } from '@prisma-next/contract/types';
2
2
  import type { Codec, CodecRegistry } from '@prisma-next/sql-relational-core/ast';
3
+ import type { JsonSchemaValidatorRegistry } from '@prisma-next/sql-relational-core/query-lane-context';
4
+ import { validateJsonValue } from './json-schema-validation';
3
5
 
4
6
  function resolveRowCodec(
5
7
  alias: string,
@@ -27,15 +29,69 @@ function resolveRowCodec(
27
29
  return null;
28
30
  }
29
31
 
32
+ type ColumnRefIndex = Map<string, { table: string; column: string }>;
33
+
34
+ /**
35
+ * Builds a lookup index from column name → { table, column } ref.
36
+ * Called once per decodeRow invocation to avoid O(aliases × refs) linear scans.
37
+ */
38
+ function buildColumnRefIndex(plan: ExecutionPlan): ColumnRefIndex | null {
39
+ const columns = plan.meta.refs?.columns;
40
+ if (!columns) return null;
41
+
42
+ const index: ColumnRefIndex = new Map();
43
+ for (const ref of columns) {
44
+ index.set(ref.column, ref);
45
+ }
46
+ return index;
47
+ }
48
+
49
+ function parseProjectionRef(value: string): { table: string; column: string } | null {
50
+ if (value.startsWith('include:') || value.startsWith('operation:')) {
51
+ return null;
52
+ }
53
+
54
+ const separatorIndex = value.indexOf('.');
55
+ if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
56
+ return null;
57
+ }
58
+
59
+ return {
60
+ table: value.slice(0, separatorIndex),
61
+ column: value.slice(separatorIndex + 1),
62
+ };
63
+ }
64
+
65
+ function resolveColumnRefForAlias(
66
+ alias: string,
67
+ projection: ExecutionPlan['meta']['projection'],
68
+ fallbackColumnRefIndex: ColumnRefIndex | null,
69
+ ): { table: string; column: string } | undefined {
70
+ if (projection && !Array.isArray(projection)) {
71
+ const mappedRef = (projection as Record<string, string>)[alias];
72
+ if (typeof mappedRef !== 'string') {
73
+ return undefined;
74
+ }
75
+ return parseProjectionRef(mappedRef) ?? undefined;
76
+ }
77
+
78
+ return fallbackColumnRefIndex?.get(alias);
79
+ }
80
+
30
81
  export function decodeRow(
31
82
  row: Record<string, unknown>,
32
83
  plan: ExecutionPlan,
33
84
  registry: CodecRegistry,
85
+ jsonValidators?: JsonSchemaValidatorRegistry,
34
86
  ): Record<string, unknown> {
35
87
  const decoded: Record<string, unknown> = {};
88
+ const projection = plan.meta.projection;
89
+
90
+ // Fallback for plans that do not provide projection alias -> table.column mapping.
91
+ const fallbackColumnRefIndex =
92
+ jsonValidators && (!projection || Array.isArray(projection)) ? buildColumnRefIndex(plan) : null;
36
93
 
37
94
  let aliases: readonly string[];
38
- const projection = plan.meta.projection;
39
95
  if (projection && !Array.isArray(projection)) {
40
96
  aliases = Object.keys(projection);
41
97
  } else if (projection && Array.isArray(projection)) {
@@ -47,7 +103,6 @@ export function decodeRow(
47
103
  for (const alias of aliases) {
48
104
  const wireValue = row[alias];
49
105
 
50
- const projection = plan.meta.projection;
51
106
  const projectionValue =
52
107
  projection && typeof projection === 'object' && !Array.isArray(projection)
53
108
  ? (projection as Record<string, string>)[alias]
@@ -111,8 +166,34 @@ export function decodeRow(
111
166
  }
112
167
 
113
168
  try {
114
- decoded[alias] = codec.decode(wireValue);
169
+ const decodedValue = codec.decode(wireValue);
170
+
171
+ // Validate decoded JSON value against schema
172
+ if (jsonValidators) {
173
+ const ref = resolveColumnRefForAlias(alias, projection, fallbackColumnRefIndex);
174
+ if (ref) {
175
+ validateJsonValue(
176
+ jsonValidators,
177
+ ref.table,
178
+ ref.column,
179
+ decodedValue,
180
+ 'decode',
181
+ codec.id,
182
+ );
183
+ }
184
+ }
185
+
186
+ decoded[alias] = decodedValue;
115
187
  } catch (error) {
188
+ // Re-throw JSON schema validation errors as-is
189
+ if (
190
+ error instanceof Error &&
191
+ 'code' in error &&
192
+ (error as Error & { code: string }).code === 'RUNTIME.JSON_SCHEMA_VALIDATION_FAILED'
193
+ ) {
194
+ throw error;
195
+ }
196
+
116
197
  const decodeError = new Error(
117
198
  `Failed to decode row alias '${alias}' with codec '${codec.id}': ${error instanceof Error ? error.message : String(error)}`,
118
199
  ) as Error & {
@@ -3,19 +3,8 @@ import type { Codec, CodecRegistry } from '@prisma-next/sql-relational-core/ast'
3
3
 
4
4
  function resolveParamCodec(
5
5
  paramDescriptor: ParamDescriptor,
6
- plan: ExecutionPlan,
7
6
  registry: CodecRegistry,
8
7
  ): Codec | null {
9
- const paramName = paramDescriptor.name ?? `param_${paramDescriptor.index ?? 0}`;
10
-
11
- const planCodecId = plan.meta.annotations?.codecs?.[paramName] as string | undefined;
12
- if (planCodecId) {
13
- const codec = registry.get(planCodecId);
14
- if (codec) {
15
- return codec;
16
- }
17
- }
18
-
19
8
  if (paramDescriptor.codecId) {
20
9
  const codec = registry.get(paramDescriptor.codecId);
21
10
  if (codec) {
@@ -29,14 +18,14 @@ function resolveParamCodec(
29
18
  export function encodeParam(
30
19
  value: unknown,
31
20
  paramDescriptor: ParamDescriptor,
32
- plan: ExecutionPlan,
21
+ paramIndex: number,
33
22
  registry: CodecRegistry,
34
23
  ): unknown {
35
24
  if (value === null || value === undefined) {
36
25
  return null;
37
26
  }
38
27
 
39
- const codec = resolveParamCodec(paramDescriptor, plan, registry);
28
+ const codec = resolveParamCodec(paramDescriptor, registry);
40
29
  if (!codec) {
41
30
  return value;
42
31
  }
@@ -45,8 +34,9 @@ export function encodeParam(
45
34
  try {
46
35
  return codec.encode(value);
47
36
  } catch (error) {
37
+ const label = paramDescriptor.name ?? `param[${paramIndex}]`;
48
38
  throw new Error(
49
- `Failed to encode parameter ${paramDescriptor.name ?? paramDescriptor.index}: ${error instanceof Error ? error.message : String(error)}`,
39
+ `Failed to encode parameter ${label}: ${error instanceof Error ? error.message : String(error)}`,
50
40
  );
51
41
  }
52
42
  }
@@ -66,7 +56,7 @@ export function encodeParams(plan: ExecutionPlan, registry: CodecRegistry): read
66
56
  const paramDescriptor = plan.meta.paramDescriptors[i];
67
57
 
68
58
  if (paramDescriptor) {
69
- encoded.push(encodeParam(paramValue, paramDescriptor, plan, registry));
59
+ encoded.push(encodeParam(paramValue, paramDescriptor, i, registry));
70
60
  } else {
71
61
  encoded.push(paramValue);
72
62
  }
@@ -0,0 +1,61 @@
1
+ import { runtimeError } from '@prisma-next/framework-components/runtime';
2
+ import type {
3
+ JsonSchemaValidationError,
4
+ JsonSchemaValidatorRegistry,
5
+ } from '@prisma-next/sql-relational-core/query-lane-context';
6
+
7
+ /**
8
+ * Validates a JSON value against its column's JSON Schema, if a validator exists.
9
+ *
10
+ * Throws `RUNTIME.JSON_SCHEMA_VALIDATION_FAILED` on validation failure.
11
+ * No-ops if no validator is registered for the column.
12
+ */
13
+ export function validateJsonValue(
14
+ registry: JsonSchemaValidatorRegistry,
15
+ table: string,
16
+ column: string,
17
+ value: unknown,
18
+ direction: 'encode' | 'decode',
19
+ codecId?: string,
20
+ ): void {
21
+ const key = `${table}.${column}`;
22
+ const validate = registry.get(key);
23
+ if (!validate) return;
24
+
25
+ const result = validate(value);
26
+ if (result.valid) return;
27
+
28
+ throw createJsonSchemaValidationError(table, column, direction, result.errors, codecId);
29
+ }
30
+
31
+ function createJsonSchemaValidationError(
32
+ table: string,
33
+ column: string,
34
+ direction: 'encode' | 'decode',
35
+ errors: ReadonlyArray<JsonSchemaValidationError>,
36
+ codecId?: string,
37
+ ): Error {
38
+ const summary = formatErrorSummary(errors);
39
+ return runtimeError(
40
+ 'RUNTIME.JSON_SCHEMA_VALIDATION_FAILED',
41
+ `JSON schema validation failed for column '${table}.${column}' (${direction}): ${summary}`,
42
+ {
43
+ table,
44
+ column,
45
+ codecId,
46
+ direction,
47
+ errors: [...errors],
48
+ },
49
+ );
50
+ }
51
+
52
+ function formatErrorSummary(errors: ReadonlyArray<JsonSchemaValidationError>): string {
53
+ if (errors.length === 0) return 'unknown validation error';
54
+ if (errors.length === 1) {
55
+ const err = errors[0] as JsonSchemaValidationError;
56
+ return err.path === '/' ? err.message : `${err.path}: ${err.message}`;
57
+ }
58
+ return errors
59
+ .map((err) => (err.path === '/' ? err.message : `${err.path}: ${err.message}`))
60
+ .join('; ');
61
+ }
@@ -1,8 +1,9 @@
1
- import { runtimeError } from '@prisma-next/runtime-executor';
2
- import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
1
+ import type { Contract } from '@prisma-next/contract/types';
2
+ import { runtimeError } from '@prisma-next/framework-components/runtime';
3
+ import type { SqlStorage } from '@prisma-next/sql-contract/types';
3
4
  import type { CodecRegistry } from '@prisma-next/sql-relational-core/ast';
4
5
 
5
- export function extractCodecIds(contract: SqlContract<SqlStorage>): Set<string> {
6
+ export function extractCodecIds(contract: Contract<SqlStorage>): Set<string> {
6
7
  const codecIds = new Set<string>();
7
8
 
8
9
  for (const table of Object.values(contract.storage.tables)) {
@@ -15,7 +16,7 @@ export function extractCodecIds(contract: SqlContract<SqlStorage>): Set<string>
15
16
  return codecIds;
16
17
  }
17
18
 
18
- function extractCodecIdsFromColumns(contract: SqlContract<SqlStorage>): Map<string, string> {
19
+ function extractCodecIdsFromColumns(contract: Contract<SqlStorage>): Map<string, string> {
19
20
  const codecIds = new Map<string, string>();
20
21
 
21
22
  for (const [tableName, table] of Object.entries(contract.storage.tables)) {
@@ -31,7 +32,7 @@ function extractCodecIdsFromColumns(contract: SqlContract<SqlStorage>): Map<stri
31
32
 
32
33
  export function validateContractCodecMappings(
33
34
  registry: CodecRegistry,
34
- contract: SqlContract<SqlStorage>,
35
+ contract: Contract<SqlStorage>,
35
36
  ): void {
36
37
  const codecIds = extractCodecIdsFromColumns(contract);
37
38
  const invalidCodecs: Array<{ table: string; column: string; codecId: string }> = [];
@@ -61,7 +62,7 @@ export function validateContractCodecMappings(
61
62
 
62
63
  export function validateCodecRegistryCompleteness(
63
64
  registry: CodecRegistry,
64
- contract: SqlContract<SqlStorage>,
65
+ contract: Contract<SqlStorage>,
65
66
  ): void {
66
67
  validateContractCodecMappings(registry, contract);
67
68
  }
@@ -1,28 +1,39 @@
1
1
  export type {
2
2
  AfterExecuteResult,
3
- BudgetsOptions,
4
- LintsOptions,
5
3
  Log,
6
- Plugin,
7
- PluginContext,
4
+ Middleware,
5
+ MiddlewareContext,
8
6
  } from '@prisma-next/runtime-executor';
9
- export { budgets, lints } from '@prisma-next/runtime-executor';
10
7
  export {
11
8
  extractCodecIds,
12
9
  validateCodecRegistryCompleteness,
13
10
  validateContractCodecMappings,
14
11
  } from '../codecs/validation';
15
12
  export { lowerSqlPlan } from '../lower-sql-plan';
13
+ export type { BudgetsOptions } from '../middleware/budgets';
14
+ export { budgets } from '../middleware/budgets';
15
+ export type { LintsOptions } from '../middleware/lints';
16
+ export { lints } from '../middleware/lints';
17
+ export type { SqlMiddleware, SqlMiddlewareContext } from '../middleware/sql-middleware';
16
18
  export type {
17
- CreateRuntimeContextOptions,
18
- RuntimeContext,
19
+ ExecutionContext,
20
+ RuntimeMutationDefaultGenerator,
19
21
  RuntimeParameterizedCodecDescriptor,
22
+ SqlExecutionStack,
23
+ SqlExecutionStackWithDriver,
24
+ SqlRuntimeAdapterDescriptor,
20
25
  SqlRuntimeAdapterInstance,
26
+ SqlRuntimeDriverInstance,
21
27
  SqlRuntimeExtensionDescriptor,
22
28
  SqlRuntimeExtensionInstance,
29
+ SqlRuntimeTargetDescriptor,
30
+ SqlStaticContributions,
23
31
  TypeHelperRegistry,
24
32
  } from '../sql-context';
25
- export { createRuntimeContext } from '../sql-context';
33
+ export {
34
+ createExecutionContext,
35
+ createSqlExecutionStack,
36
+ } from '../sql-context';
26
37
  export type { SqlStatement } from '../sql-marker';
27
38
  export {
28
39
  ensureSchemaStatement,
@@ -31,8 +42,8 @@ export {
31
42
  writeContractMarker,
32
43
  } from '../sql-marker';
33
44
  export type {
45
+ CreateRuntimeOptions,
34
46
  Runtime,
35
- RuntimeOptions,
36
47
  RuntimeTelemetryEvent,
37
48
  RuntimeVerifyOptions,
38
49
  TelemetryOutcome,