@jskit-ai/kernel 0.1.4

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 (185) hide show
  1. package/README.md +24 -0
  2. package/_testable/index.js +4 -0
  3. package/client/appConfig.js +33 -0
  4. package/client/componentInteraction.js +51 -0
  5. package/client/componentInteraction.test.js +111 -0
  6. package/client/descriptorSections.js +75 -0
  7. package/client/index.d.ts +70 -0
  8. package/client/index.js +3 -0
  9. package/client/logging.js +38 -0
  10. package/client/moduleBootstrap.js +670 -0
  11. package/client/moduleBootstrap.test.js +403 -0
  12. package/client/shellBootstrap.js +233 -0
  13. package/client/shellBootstrap.test.js +185 -0
  14. package/client/shellRouting.js +321 -0
  15. package/client/shellRouting.test.js +113 -0
  16. package/client/vite/clientBootstrapPlugin.js +259 -0
  17. package/client/vite/clientBootstrapPlugin.test.js +563 -0
  18. package/client/vite/index.js +3 -0
  19. package/internal/node/fileSystem.js +21 -0
  20. package/internal/node/installedPackageDescriptor.js +104 -0
  21. package/package.json +43 -0
  22. package/server/actions/ActionRuntimeServiceProvider.js +309 -0
  23. package/server/actions/ActionRuntimeServiceProvider.test.js +551 -0
  24. package/server/actions/index.js +8 -0
  25. package/server/container/ContainerCoreServiceProvider.js +27 -0
  26. package/server/container/index.js +10 -0
  27. package/server/exportPolicy.test.js +68 -0
  28. package/server/http/HttpFastifyServiceProvider.js +25 -0
  29. package/server/http/_testable/index.js +2 -0
  30. package/server/http/index.js +1 -0
  31. package/server/http/lib/controller.js +183 -0
  32. package/server/http/lib/controller.test.js +143 -0
  33. package/server/http/lib/errors.js +12 -0
  34. package/server/http/lib/httpRuntime.js +82 -0
  35. package/server/http/lib/index.js +18 -0
  36. package/server/http/lib/kernel.js +15 -0
  37. package/server/http/lib/kernel.test.js +880 -0
  38. package/server/http/lib/middlewareRuntime.js +149 -0
  39. package/server/http/lib/requestActionExecutor.js +258 -0
  40. package/server/http/lib/requestScope.js +59 -0
  41. package/server/http/lib/routeRegistration.js +165 -0
  42. package/server/http/lib/routeSupport.js +45 -0
  43. package/server/http/lib/routeValidator.js +469 -0
  44. package/server/http/lib/routeValidator.test.js +474 -0
  45. package/server/http/lib/router.js +206 -0
  46. package/server/kernel/KernelCoreServiceProvider.js +27 -0
  47. package/server/kernel/index.js +10 -0
  48. package/server/platform/PlatformServerRuntimeServiceProvider.js +30 -0
  49. package/server/platform/index.js +5 -0
  50. package/server/platform/providerRuntime/descriptorCatalog.js +170 -0
  51. package/server/platform/providerRuntime/helpers.js +45 -0
  52. package/server/platform/providerRuntime/lockfile.js +27 -0
  53. package/server/platform/providerRuntime/providerLoader.js +283 -0
  54. package/server/platform/providerRuntime.js +142 -0
  55. package/server/platform/providerRuntime.test.js +217 -0
  56. package/server/platform/runtime.js +40 -0
  57. package/server/platform/surfaceRuntime.js +150 -0
  58. package/server/platform/surfaceRuntime.test.js +136 -0
  59. package/server/registries/actionSurfaceSourceRegistry.js +150 -0
  60. package/server/registries/bootstrapPayloadContributorRegistry.js +41 -0
  61. package/server/registries/domainEventListenerRegistry.js +61 -0
  62. package/server/registries/index.js +36 -0
  63. package/server/registries/primitives.js +63 -0
  64. package/server/registries/routeVisibilityResolverRegistry.js +87 -0
  65. package/server/registries/serviceRegistrationRegistry.js +431 -0
  66. package/server/runtime/ServerRuntimeCoreServiceProvider.js +65 -0
  67. package/server/runtime/ServerRuntimeCoreServiceProvider.test.js +53 -0
  68. package/server/runtime/apiRoutePolicyParity.test.js +109 -0
  69. package/server/runtime/apiRouteRegistration.js +65 -0
  70. package/server/runtime/bootBootstrapRoutes.js +46 -0
  71. package/server/runtime/bootBootstrapRoutes.test.js +79 -0
  72. package/server/runtime/bootstrapContributors.test.js +114 -0
  73. package/server/runtime/canonicalJson.js +74 -0
  74. package/server/runtime/composition.js +142 -0
  75. package/server/runtime/domainEvents.test.js +114 -0
  76. package/server/runtime/domainRules.js +50 -0
  77. package/server/runtime/domainRules.test.js +87 -0
  78. package/server/runtime/entityChangeEvents.js +182 -0
  79. package/server/runtime/entityChangeEvents.test.js +211 -0
  80. package/server/runtime/errors.js +68 -0
  81. package/server/runtime/errors.test.js +73 -0
  82. package/server/runtime/fastifyBootstrap.js +372 -0
  83. package/server/runtime/fastifyBootstrap.test.js +194 -0
  84. package/server/runtime/index.js +6 -0
  85. package/server/runtime/integers.js +13 -0
  86. package/server/runtime/moduleConfig.js +269 -0
  87. package/server/runtime/moduleConfig.test.js +141 -0
  88. package/server/runtime/pagination.js +13 -0
  89. package/server/runtime/realtimeNormalization.js +21 -0
  90. package/server/runtime/requestUrl.js +38 -0
  91. package/server/runtime/routeUtils.js +20 -0
  92. package/server/runtime/runtimeAssembly.js +113 -0
  93. package/server/runtime/runtimeKernel.js +55 -0
  94. package/server/runtime/securityAudit.js +269 -0
  95. package/server/runtime/securityAudit.test.js +41 -0
  96. package/server/runtime/serviceAuthorization.js +113 -0
  97. package/server/runtime/serviceAuthorization.test.js +100 -0
  98. package/server/runtime/serviceRegistration.test.js +197 -0
  99. package/server/support/SupportCoreServiceProvider.js +25 -0
  100. package/server/support/appConfig.js +37 -0
  101. package/server/support/appConfig.test.js +94 -0
  102. package/server/support/defaultMissingHandler.js +7 -0
  103. package/server/support/index.js +2 -0
  104. package/server/support/routePolicyConfig.js +51 -0
  105. package/server/support/symlinkSafeRequire.js +78 -0
  106. package/server/support/symlinkSafeRequire.test.js +27 -0
  107. package/server/surface/SurfaceRoutingServiceProvider.js +27 -0
  108. package/server/surface/index.js +19 -0
  109. package/shared/actions/actionContributorHelpers.js +34 -0
  110. package/shared/actions/actionContributorHelpers.test.js +16 -0
  111. package/shared/actions/actionDefinitions.js +488 -0
  112. package/shared/actions/actionDefinitions.test.js +212 -0
  113. package/shared/actions/audit.js +7 -0
  114. package/shared/actions/executionContext.js +97 -0
  115. package/shared/actions/executionContext.test.js +66 -0
  116. package/shared/actions/idempotency.js +62 -0
  117. package/shared/actions/index.js +2 -0
  118. package/shared/actions/observability.js +10 -0
  119. package/shared/actions/pipeline.js +287 -0
  120. package/shared/actions/policies.js +342 -0
  121. package/shared/actions/policies.test.js +233 -0
  122. package/shared/actions/registry.js +187 -0
  123. package/shared/actions/registry.test.js +381 -0
  124. package/shared/actions/requestMeta.js +36 -0
  125. package/shared/actions/textNormalization.js +3 -0
  126. package/shared/actions/withActionDefaults.js +34 -0
  127. package/shared/index.js +2 -0
  128. package/shared/runtime/application.js +323 -0
  129. package/shared/runtime/container.js +261 -0
  130. package/shared/runtime/containerErrors.js +22 -0
  131. package/shared/runtime/index.js +18 -0
  132. package/shared/runtime/kernelErrors.js +20 -0
  133. package/shared/runtime/serviceProvider.js +13 -0
  134. package/shared/support/formatDateTime.js +10 -0
  135. package/shared/support/formatDateTime.test.js +15 -0
  136. package/shared/support/index.js +14 -0
  137. package/shared/support/linkPath.js +67 -0
  138. package/shared/support/linkPath.test.js +35 -0
  139. package/shared/support/normalize.js +116 -0
  140. package/shared/support/normalize.test.js +48 -0
  141. package/shared/support/packageDescriptor.test.js +121 -0
  142. package/shared/support/permissions.js +50 -0
  143. package/shared/support/pickOwnProperties.js +17 -0
  144. package/shared/support/pickOwnProperties.test.js +25 -0
  145. package/shared/support/policies.js +11 -0
  146. package/shared/support/queryPath.js +33 -0
  147. package/shared/support/queryPath.test.js +19 -0
  148. package/shared/support/queryResilience.js +34 -0
  149. package/shared/support/queryResilience.test.js +33 -0
  150. package/shared/support/returnToPath.js +153 -0
  151. package/shared/support/returnToPath.test.js +123 -0
  152. package/shared/support/sorting.js +15 -0
  153. package/shared/support/tokens.js +23 -0
  154. package/shared/support/tokens.test.js +17 -0
  155. package/shared/support/visibility.js +56 -0
  156. package/shared/support/visibility.test.js +45 -0
  157. package/shared/surface/apiPaths.js +84 -0
  158. package/shared/surface/escapeRegExp.js +5 -0
  159. package/shared/surface/index.js +6 -0
  160. package/shared/surface/paths.js +273 -0
  161. package/shared/surface/registry.js +135 -0
  162. package/shared/surface/registry.test.js +44 -0
  163. package/shared/surface/runtime.js +357 -0
  164. package/shared/surface/runtime.test.js +319 -0
  165. package/shared/validators/createCursorListValidator.js +42 -0
  166. package/shared/validators/createCursorListValidator.test.js +34 -0
  167. package/shared/validators/cursorPaginationQueryValidator.js +31 -0
  168. package/shared/validators/cursorPaginationQueryValidator.test.js +21 -0
  169. package/shared/validators/index.js +12 -0
  170. package/shared/validators/inputNormalization.js +13 -0
  171. package/shared/validators/mergeObjectSchemas.js +31 -0
  172. package/shared/validators/mergeObjectSchemas.test.js +67 -0
  173. package/shared/validators/mergeValidators.js +89 -0
  174. package/shared/validators/mergeValidators.test.js +116 -0
  175. package/shared/validators/nestValidator.js +53 -0
  176. package/shared/validators/nestValidator.test.js +60 -0
  177. package/shared/validators/recordIdParamsValidator.js +36 -0
  178. package/shared/validators/recordIdParamsValidator.test.js +20 -0
  179. package/shared/validators/resourceRequiredMetadata.js +41 -0
  180. package/shared/validators/resourceRequiredMetadata.test.js +49 -0
  181. package/test/barrelExposure.test.js +106 -0
  182. package/test/dynamicImportPolicy.test.js +89 -0
  183. package/test/exportsContract.test.js +168 -0
  184. package/test/routeInputContractGuard.test.js +78 -0
  185. package/test/surfaceIndependence.test.js +109 -0
@@ -0,0 +1,469 @@
1
+ import { normalizeObject, normalizeText } from "../../../shared/support/normalize.js";
2
+ import { mergeValidators } from "../../../shared/validators/mergeValidators.js";
3
+ import { RouteDefinitionError } from "./errors.js";
4
+ import { resolveRouteLabel } from "./routeSupport.js";
5
+
6
+ const ROUTE_VALIDATOR_SYMBOL = Symbol.for("@jskit-ai/kernel/http/routeValidator");
7
+ const VALIDATOR_OPTION_KEYS = Object.freeze([
8
+ "meta",
9
+ "bodyValidator",
10
+ "queryValidator",
11
+ "paramsValidator",
12
+ "responseValidators",
13
+ "advanced"
14
+ ]);
15
+
16
+ function passThroughInputSection(value) {
17
+ return value;
18
+ }
19
+
20
+ function normalizeOptionalValidatorTransformer(source, normalized, { context = "route validator" } = {}) {
21
+ if (!Object.prototype.hasOwnProperty.call(source, "normalize")) {
22
+ return;
23
+ }
24
+
25
+ const normalize = source.normalize;
26
+ if (normalize != null && typeof normalize !== "function") {
27
+ throw new RouteDefinitionError(`${context}.normalize must be a function.`);
28
+ }
29
+ if (typeof normalize === "function") {
30
+ normalized.normalize = normalize;
31
+ }
32
+ }
33
+
34
+ function normalizeSingleRouteValidator(value, { context = "route validator" } = {}) {
35
+ if (value == null) {
36
+ return Object.freeze({});
37
+ }
38
+
39
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
40
+ throw new RouteDefinitionError(`${context} must be an object.`);
41
+ }
42
+
43
+ const source = normalizeObject(value);
44
+ const normalized = {};
45
+
46
+ if (Object.prototype.hasOwnProperty.call(source, "schema")) {
47
+ normalized.schema = source.schema;
48
+ }
49
+
50
+ normalizeOptionalValidatorTransformer(source, normalized, { context });
51
+
52
+ return Object.freeze(normalized);
53
+ }
54
+
55
+ function mergeNormalizedRouteValidators(validators, { context = "route validator" } = {}) {
56
+ return mergeValidators(validators, {
57
+ context,
58
+ allowAsyncNormalize: false,
59
+ createError(message) {
60
+ return new RouteDefinitionError(message);
61
+ }
62
+ });
63
+ }
64
+
65
+ function normalizeRouteValidator(value, { context = "route validator", allowArray = false } = {}) {
66
+ if (value == null) {
67
+ return Object.freeze({});
68
+ }
69
+
70
+ if (Array.isArray(value)) {
71
+ if (!allowArray) {
72
+ throw new RouteDefinitionError(`${context} does not support arrays.`);
73
+ }
74
+
75
+ if (value.length === 0) {
76
+ return Object.freeze({});
77
+ }
78
+
79
+ const validators = value.map((entry, index) => {
80
+ const validator = normalizeSingleRouteValidator(entry, {
81
+ context: `${context}[${index}]`
82
+ });
83
+
84
+ if (
85
+ !Object.prototype.hasOwnProperty.call(validator, "schema") &&
86
+ !Object.prototype.hasOwnProperty.call(validator, "normalize")
87
+ ) {
88
+ throw new RouteDefinitionError(`${context}[${index}] must define schema and/or normalize.`);
89
+ }
90
+
91
+ return validator;
92
+ });
93
+
94
+ return mergeNormalizedRouteValidators(validators, {
95
+ context
96
+ });
97
+ }
98
+
99
+ return normalizeSingleRouteValidator(value, {
100
+ context
101
+ });
102
+ }
103
+
104
+ function normalizeResponseValidatorEntry(value, { context = "route validator response entry" } = {}) {
105
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
106
+ throw new RouteDefinitionError(`${context} must be an object.`);
107
+ }
108
+
109
+ const source = normalizeObject(value);
110
+ const normalized = {};
111
+
112
+ if (!Object.prototype.hasOwnProperty.call(source, "schema")) {
113
+ throw new RouteDefinitionError(`${context}.schema is required when using a response validator object.`);
114
+ }
115
+ normalized.schema = source.schema;
116
+
117
+ normalizeOptionalValidatorTransformer(source, normalized, { context });
118
+
119
+ return Object.freeze(normalized);
120
+ }
121
+
122
+ function normalizeResponseValidatorDefinition(value, { context = "route validator.response" } = {}) {
123
+ if (value == null) {
124
+ return undefined;
125
+ }
126
+
127
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
128
+ throw new RouteDefinitionError(`${context} must be an object.`);
129
+ }
130
+
131
+ const source = normalizeObject(value);
132
+ const normalized = {};
133
+
134
+ for (const [statusCode, entry] of Object.entries(source)) {
135
+ normalized[statusCode] = normalizeResponseValidatorEntry(entry, {
136
+ context: `${context}.${statusCode}`
137
+ });
138
+ }
139
+
140
+ return Object.freeze(normalized);
141
+ }
142
+
143
+ function normalizeAdvancedFastifySchema(value, { context = "route validator" } = {}) {
144
+ if (!Object.prototype.hasOwnProperty.call(value, "fastifySchema")) {
145
+ return undefined;
146
+ }
147
+
148
+ const fastifySchema = value.fastifySchema;
149
+ if (!fastifySchema || typeof fastifySchema !== "object" || Array.isArray(fastifySchema)) {
150
+ throw new RouteDefinitionError(`${context}.advanced.fastifySchema must be an object.`);
151
+ }
152
+
153
+ return Object.freeze({
154
+ ...normalizeObject(fastifySchema)
155
+ });
156
+ }
157
+
158
+ function normalizeAdvancedJskitInput(value, { context = "route validator" } = {}) {
159
+ if (!Object.prototype.hasOwnProperty.call(value, "jskitInput")) {
160
+ return undefined;
161
+ }
162
+
163
+ const jskitInput = value.jskitInput;
164
+ if (!jskitInput || typeof jskitInput !== "object" || Array.isArray(jskitInput)) {
165
+ throw new RouteDefinitionError(`${context}.advanced.jskitInput must be an object.`);
166
+ }
167
+
168
+ const supportedKeys = new Set(["body", "query", "params"]);
169
+ for (const key of Object.keys(jskitInput)) {
170
+ if (!supportedKeys.has(key)) {
171
+ throw new RouteDefinitionError(
172
+ `${context}.advanced.jskitInput.${key} is not supported. Use body, query, or params.`
173
+ );
174
+ }
175
+ }
176
+
177
+ const normalized = {};
178
+ for (const key of ["body", "query", "params"]) {
179
+ if (!Object.prototype.hasOwnProperty.call(jskitInput, key)) {
180
+ continue;
181
+ }
182
+
183
+ const transform = jskitInput[key];
184
+ if (transform == null) {
185
+ continue;
186
+ }
187
+
188
+ if (typeof transform !== "function") {
189
+ throw new RouteDefinitionError(`${context}.advanced.jskitInput.${key} must be a function.`);
190
+ }
191
+
192
+ normalized[key] = transform;
193
+ }
194
+
195
+ return Object.freeze(normalized);
196
+ }
197
+
198
+ function normalizeRouteValidatorMeta(value, { context = "route validator" } = {}) {
199
+ if (value == null) {
200
+ return Object.freeze({});
201
+ }
202
+
203
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
204
+ throw new RouteDefinitionError(`${context}.meta must be an object.`);
205
+ }
206
+
207
+ const source = normalizeObject(value);
208
+ const normalized = {};
209
+
210
+ if (Object.prototype.hasOwnProperty.call(source, "tags")) {
211
+ if (!Array.isArray(source.tags)) {
212
+ throw new RouteDefinitionError(`${context}.meta.tags must be an array of non-empty strings.`);
213
+ }
214
+
215
+ const tags = source.tags.map((entry, index) => {
216
+ const tag = normalizeText(entry);
217
+ if (!tag) {
218
+ throw new RouteDefinitionError(`${context}.meta.tags[${index}] must be a non-empty string.`);
219
+ }
220
+ return tag;
221
+ });
222
+ normalized.tags = Object.freeze(tags);
223
+ }
224
+
225
+ if (Object.prototype.hasOwnProperty.call(source, "summary")) {
226
+ const summary = normalizeText(source.summary);
227
+ if (!summary) {
228
+ throw new RouteDefinitionError(`${context}.meta.summary must be a non-empty string.`);
229
+ }
230
+ normalized.summary = summary;
231
+ }
232
+
233
+ return Object.freeze(normalized);
234
+ }
235
+
236
+ function normalizeRouteValidatorDefinition(sourceDefinition, { context = "route validator" } = {}) {
237
+ const definition =
238
+ sourceDefinition && typeof sourceDefinition === "object" && !Array.isArray(sourceDefinition)
239
+ ? normalizeObject(sourceDefinition)
240
+ : null;
241
+
242
+ if (!definition) {
243
+ throw new RouteDefinitionError(`${context} must be an object.`);
244
+ }
245
+
246
+ if (Object.prototype.hasOwnProperty.call(definition, "body")) {
247
+ throw new RouteDefinitionError(`${context}.body is not supported. Use ${context}.bodyValidator.`);
248
+ }
249
+ if (Object.prototype.hasOwnProperty.call(definition, "query")) {
250
+ throw new RouteDefinitionError(`${context}.query is not supported. Use ${context}.queryValidator.`);
251
+ }
252
+ if (Object.prototype.hasOwnProperty.call(definition, "params")) {
253
+ throw new RouteDefinitionError(`${context}.params is not supported. Use ${context}.paramsValidator.`);
254
+ }
255
+ if (Object.prototype.hasOwnProperty.call(definition, "response")) {
256
+ throw new RouteDefinitionError(`${context}.response is not supported. Use ${context}.responseValidators.`);
257
+ }
258
+
259
+ const meta = normalizeRouteValidatorMeta(definition.meta, {
260
+ context
261
+ });
262
+ const bodyValidator = normalizeRouteValidator(definition.bodyValidator, {
263
+ context: `${context}.bodyValidator`
264
+ });
265
+ const queryValidator = normalizeRouteValidator(definition.queryValidator, {
266
+ context: `${context}.queryValidator`,
267
+ allowArray: true
268
+ });
269
+ const paramsValidator = normalizeRouteValidator(definition.paramsValidator, {
270
+ context: `${context}.paramsValidator`,
271
+ allowArray: true
272
+ });
273
+
274
+ const advancedSource =
275
+ definition.advanced && typeof definition.advanced === "object" && !Array.isArray(definition.advanced)
276
+ ? normalizeObject(definition.advanced)
277
+ : definition.advanced == null
278
+ ? {}
279
+ : null;
280
+
281
+ if (advancedSource == null) {
282
+ throw new RouteDefinitionError(`${context}.advanced must be an object.`);
283
+ }
284
+
285
+ const normalized = {
286
+ meta,
287
+ bodyValidator,
288
+ queryValidator,
289
+ paramsValidator
290
+ };
291
+
292
+ if (Object.prototype.hasOwnProperty.call(definition, "responseValidators")) {
293
+ normalized.responseValidators = normalizeResponseValidatorDefinition(definition.responseValidators, {
294
+ context: `${context}.responseValidators`
295
+ });
296
+ }
297
+
298
+ const fastifySchema = normalizeAdvancedFastifySchema(advancedSource, {
299
+ context
300
+ });
301
+ if (fastifySchema) {
302
+ normalized.fastifySchema = fastifySchema;
303
+ }
304
+
305
+ const jskitInput = normalizeAdvancedJskitInput(advancedSource, {
306
+ context
307
+ });
308
+ if (jskitInput) {
309
+ normalized.jskitInput = jskitInput;
310
+ }
311
+
312
+ return Object.freeze(normalized);
313
+ }
314
+
315
+ function compileNormalizedRouteValidator(normalizedValidator) {
316
+ const schema = {};
317
+ const input = {};
318
+
319
+ if (Array.isArray(normalizedValidator.meta?.tags) && normalizedValidator.meta.tags.length > 0) {
320
+ schema.tags = [...normalizedValidator.meta.tags];
321
+ }
322
+ if (normalizedValidator.meta?.summary) {
323
+ schema.summary = normalizedValidator.meta.summary;
324
+ }
325
+
326
+ if (Object.prototype.hasOwnProperty.call(normalizedValidator.bodyValidator, "schema")) {
327
+ schema.body = normalizedValidator.bodyValidator.schema;
328
+ input.body = typeof normalizedValidator.bodyValidator.normalize === "function"
329
+ ? normalizedValidator.bodyValidator.normalize
330
+ : passThroughInputSection;
331
+ }
332
+
333
+ if (Object.prototype.hasOwnProperty.call(normalizedValidator.queryValidator, "schema")) {
334
+ schema.querystring = normalizedValidator.queryValidator.schema;
335
+ input.query = typeof normalizedValidator.queryValidator.normalize === "function"
336
+ ? normalizedValidator.queryValidator.normalize
337
+ : passThroughInputSection;
338
+ }
339
+
340
+ if (Object.prototype.hasOwnProperty.call(normalizedValidator.paramsValidator, "schema")) {
341
+ schema.params = normalizedValidator.paramsValidator.schema;
342
+ input.params = typeof normalizedValidator.paramsValidator.normalize === "function"
343
+ ? normalizedValidator.paramsValidator.normalize
344
+ : passThroughInputSection;
345
+ }
346
+
347
+ if (Object.prototype.hasOwnProperty.call(normalizedValidator, "responseValidators")) {
348
+ const responseSchema = {};
349
+
350
+ for (const [statusCode, entry] of Object.entries(normalizedValidator.responseValidators || {})) {
351
+ responseSchema[statusCode] = entry.schema;
352
+ }
353
+
354
+ schema.response = responseSchema;
355
+ }
356
+
357
+ if (normalizedValidator.fastifySchema) {
358
+ Object.assign(schema, normalizedValidator.fastifySchema);
359
+ }
360
+
361
+ if (normalizedValidator.jskitInput) {
362
+ Object.assign(input, normalizedValidator.jskitInput);
363
+ }
364
+
365
+ const compiled = {};
366
+ if (Object.keys(schema).length > 0) {
367
+ compiled.schema = Object.freeze({
368
+ ...schema
369
+ });
370
+ }
371
+ if (Object.keys(input).length > 0) {
372
+ compiled.input = Object.freeze({
373
+ ...input
374
+ });
375
+ }
376
+
377
+ return Object.freeze(compiled);
378
+ }
379
+
380
+ function normalizeRouteValidatorSource(validator, { context = "route validator" } = {}) {
381
+ if (validator && typeof validator === "object") {
382
+ const precompiled = validator[ROUTE_VALIDATOR_SYMBOL];
383
+ if (precompiled && typeof precompiled === "object") {
384
+ return precompiled;
385
+ }
386
+ }
387
+
388
+ return normalizeRouteValidatorDefinition(validator, {
389
+ context
390
+ });
391
+ }
392
+
393
+ function compileRouteValidator(validator, { context = "route validator" } = {}) {
394
+ return compileNormalizedRouteValidator(
395
+ normalizeRouteValidatorSource(validator, {
396
+ context
397
+ })
398
+ );
399
+ }
400
+
401
+ function defineRouteValidator(definition = {}) {
402
+ const normalized = normalizeRouteValidatorDefinition(definition, {
403
+ context: "defineRouteValidator()"
404
+ });
405
+
406
+ const validator = {
407
+ toRouteOptions() {
408
+ return compileNormalizedRouteValidator(normalized);
409
+ }
410
+ };
411
+
412
+ Object.defineProperty(validator, ROUTE_VALIDATOR_SYMBOL, {
413
+ value: normalized,
414
+ enumerable: false,
415
+ configurable: false,
416
+ writable: false
417
+ });
418
+
419
+ return Object.freeze(validator);
420
+ }
421
+
422
+ function resolveRouteValidatorOptions({
423
+ method = "",
424
+ path = "",
425
+ options = {}
426
+ } = {}) {
427
+ const normalizedOptions = normalizeObject(options, {
428
+ fallback: {}
429
+ });
430
+
431
+ const routeLabel = resolveRouteLabel({
432
+ method,
433
+ path
434
+ });
435
+
436
+ const hasInlineValidatorShape = VALIDATOR_OPTION_KEYS.some((key) => Object.prototype.hasOwnProperty.call(normalizedOptions, key));
437
+
438
+ const remainingOptions = {
439
+ ...normalizedOptions
440
+ };
441
+ delete remainingOptions.schema;
442
+ delete remainingOptions.input;
443
+ delete remainingOptions.validator;
444
+
445
+ if (!hasInlineValidatorShape) {
446
+ return remainingOptions;
447
+ }
448
+
449
+ const inlineValidator = {};
450
+ for (const key of VALIDATOR_OPTION_KEYS) {
451
+ if (!Object.prototype.hasOwnProperty.call(normalizedOptions, key)) {
452
+ continue;
453
+ }
454
+
455
+ inlineValidator[key] = normalizedOptions[key];
456
+ delete remainingOptions[key];
457
+ }
458
+
459
+ const compiled = compileRouteValidator(inlineValidator, {
460
+ context: `Route ${routeLabel} validator`
461
+ });
462
+
463
+ return {
464
+ ...remainingOptions,
465
+ ...compiled
466
+ };
467
+ }
468
+
469
+ export { defineRouteValidator, compileRouteValidator, resolveRouteValidatorOptions };