@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.
- package/README.md +24 -0
- package/_testable/index.js +4 -0
- package/client/appConfig.js +33 -0
- package/client/componentInteraction.js +51 -0
- package/client/componentInteraction.test.js +111 -0
- package/client/descriptorSections.js +75 -0
- package/client/index.d.ts +70 -0
- package/client/index.js +3 -0
- package/client/logging.js +38 -0
- package/client/moduleBootstrap.js +670 -0
- package/client/moduleBootstrap.test.js +403 -0
- package/client/shellBootstrap.js +233 -0
- package/client/shellBootstrap.test.js +185 -0
- package/client/shellRouting.js +321 -0
- package/client/shellRouting.test.js +113 -0
- package/client/vite/clientBootstrapPlugin.js +259 -0
- package/client/vite/clientBootstrapPlugin.test.js +563 -0
- package/client/vite/index.js +3 -0
- package/internal/node/fileSystem.js +21 -0
- package/internal/node/installedPackageDescriptor.js +104 -0
- package/package.json +43 -0
- package/server/actions/ActionRuntimeServiceProvider.js +309 -0
- package/server/actions/ActionRuntimeServiceProvider.test.js +551 -0
- package/server/actions/index.js +8 -0
- package/server/container/ContainerCoreServiceProvider.js +27 -0
- package/server/container/index.js +10 -0
- package/server/exportPolicy.test.js +68 -0
- package/server/http/HttpFastifyServiceProvider.js +25 -0
- package/server/http/_testable/index.js +2 -0
- package/server/http/index.js +1 -0
- package/server/http/lib/controller.js +183 -0
- package/server/http/lib/controller.test.js +143 -0
- package/server/http/lib/errors.js +12 -0
- package/server/http/lib/httpRuntime.js +82 -0
- package/server/http/lib/index.js +18 -0
- package/server/http/lib/kernel.js +15 -0
- package/server/http/lib/kernel.test.js +880 -0
- package/server/http/lib/middlewareRuntime.js +149 -0
- package/server/http/lib/requestActionExecutor.js +258 -0
- package/server/http/lib/requestScope.js +59 -0
- package/server/http/lib/routeRegistration.js +165 -0
- package/server/http/lib/routeSupport.js +45 -0
- package/server/http/lib/routeValidator.js +469 -0
- package/server/http/lib/routeValidator.test.js +474 -0
- package/server/http/lib/router.js +206 -0
- package/server/kernel/KernelCoreServiceProvider.js +27 -0
- package/server/kernel/index.js +10 -0
- package/server/platform/PlatformServerRuntimeServiceProvider.js +30 -0
- package/server/platform/index.js +5 -0
- package/server/platform/providerRuntime/descriptorCatalog.js +170 -0
- package/server/platform/providerRuntime/helpers.js +45 -0
- package/server/platform/providerRuntime/lockfile.js +27 -0
- package/server/platform/providerRuntime/providerLoader.js +283 -0
- package/server/platform/providerRuntime.js +142 -0
- package/server/platform/providerRuntime.test.js +217 -0
- package/server/platform/runtime.js +40 -0
- package/server/platform/surfaceRuntime.js +150 -0
- package/server/platform/surfaceRuntime.test.js +136 -0
- package/server/registries/actionSurfaceSourceRegistry.js +150 -0
- package/server/registries/bootstrapPayloadContributorRegistry.js +41 -0
- package/server/registries/domainEventListenerRegistry.js +61 -0
- package/server/registries/index.js +36 -0
- package/server/registries/primitives.js +63 -0
- package/server/registries/routeVisibilityResolverRegistry.js +87 -0
- package/server/registries/serviceRegistrationRegistry.js +431 -0
- package/server/runtime/ServerRuntimeCoreServiceProvider.js +65 -0
- package/server/runtime/ServerRuntimeCoreServiceProvider.test.js +53 -0
- package/server/runtime/apiRoutePolicyParity.test.js +109 -0
- package/server/runtime/apiRouteRegistration.js +65 -0
- package/server/runtime/bootBootstrapRoutes.js +46 -0
- package/server/runtime/bootBootstrapRoutes.test.js +79 -0
- package/server/runtime/bootstrapContributors.test.js +114 -0
- package/server/runtime/canonicalJson.js +74 -0
- package/server/runtime/composition.js +142 -0
- package/server/runtime/domainEvents.test.js +114 -0
- package/server/runtime/domainRules.js +50 -0
- package/server/runtime/domainRules.test.js +87 -0
- package/server/runtime/entityChangeEvents.js +182 -0
- package/server/runtime/entityChangeEvents.test.js +211 -0
- package/server/runtime/errors.js +68 -0
- package/server/runtime/errors.test.js +73 -0
- package/server/runtime/fastifyBootstrap.js +372 -0
- package/server/runtime/fastifyBootstrap.test.js +194 -0
- package/server/runtime/index.js +6 -0
- package/server/runtime/integers.js +13 -0
- package/server/runtime/moduleConfig.js +269 -0
- package/server/runtime/moduleConfig.test.js +141 -0
- package/server/runtime/pagination.js +13 -0
- package/server/runtime/realtimeNormalization.js +21 -0
- package/server/runtime/requestUrl.js +38 -0
- package/server/runtime/routeUtils.js +20 -0
- package/server/runtime/runtimeAssembly.js +113 -0
- package/server/runtime/runtimeKernel.js +55 -0
- package/server/runtime/securityAudit.js +269 -0
- package/server/runtime/securityAudit.test.js +41 -0
- package/server/runtime/serviceAuthorization.js +113 -0
- package/server/runtime/serviceAuthorization.test.js +100 -0
- package/server/runtime/serviceRegistration.test.js +197 -0
- package/server/support/SupportCoreServiceProvider.js +25 -0
- package/server/support/appConfig.js +37 -0
- package/server/support/appConfig.test.js +94 -0
- package/server/support/defaultMissingHandler.js +7 -0
- package/server/support/index.js +2 -0
- package/server/support/routePolicyConfig.js +51 -0
- package/server/support/symlinkSafeRequire.js +78 -0
- package/server/support/symlinkSafeRequire.test.js +27 -0
- package/server/surface/SurfaceRoutingServiceProvider.js +27 -0
- package/server/surface/index.js +19 -0
- package/shared/actions/actionContributorHelpers.js +34 -0
- package/shared/actions/actionContributorHelpers.test.js +16 -0
- package/shared/actions/actionDefinitions.js +488 -0
- package/shared/actions/actionDefinitions.test.js +212 -0
- package/shared/actions/audit.js +7 -0
- package/shared/actions/executionContext.js +97 -0
- package/shared/actions/executionContext.test.js +66 -0
- package/shared/actions/idempotency.js +62 -0
- package/shared/actions/index.js +2 -0
- package/shared/actions/observability.js +10 -0
- package/shared/actions/pipeline.js +287 -0
- package/shared/actions/policies.js +342 -0
- package/shared/actions/policies.test.js +233 -0
- package/shared/actions/registry.js +187 -0
- package/shared/actions/registry.test.js +381 -0
- package/shared/actions/requestMeta.js +36 -0
- package/shared/actions/textNormalization.js +3 -0
- package/shared/actions/withActionDefaults.js +34 -0
- package/shared/index.js +2 -0
- package/shared/runtime/application.js +323 -0
- package/shared/runtime/container.js +261 -0
- package/shared/runtime/containerErrors.js +22 -0
- package/shared/runtime/index.js +18 -0
- package/shared/runtime/kernelErrors.js +20 -0
- package/shared/runtime/serviceProvider.js +13 -0
- package/shared/support/formatDateTime.js +10 -0
- package/shared/support/formatDateTime.test.js +15 -0
- package/shared/support/index.js +14 -0
- package/shared/support/linkPath.js +67 -0
- package/shared/support/linkPath.test.js +35 -0
- package/shared/support/normalize.js +116 -0
- package/shared/support/normalize.test.js +48 -0
- package/shared/support/packageDescriptor.test.js +121 -0
- package/shared/support/permissions.js +50 -0
- package/shared/support/pickOwnProperties.js +17 -0
- package/shared/support/pickOwnProperties.test.js +25 -0
- package/shared/support/policies.js +11 -0
- package/shared/support/queryPath.js +33 -0
- package/shared/support/queryPath.test.js +19 -0
- package/shared/support/queryResilience.js +34 -0
- package/shared/support/queryResilience.test.js +33 -0
- package/shared/support/returnToPath.js +153 -0
- package/shared/support/returnToPath.test.js +123 -0
- package/shared/support/sorting.js +15 -0
- package/shared/support/tokens.js +23 -0
- package/shared/support/tokens.test.js +17 -0
- package/shared/support/visibility.js +56 -0
- package/shared/support/visibility.test.js +45 -0
- package/shared/surface/apiPaths.js +84 -0
- package/shared/surface/escapeRegExp.js +5 -0
- package/shared/surface/index.js +6 -0
- package/shared/surface/paths.js +273 -0
- package/shared/surface/registry.js +135 -0
- package/shared/surface/registry.test.js +44 -0
- package/shared/surface/runtime.js +357 -0
- package/shared/surface/runtime.test.js +319 -0
- package/shared/validators/createCursorListValidator.js +42 -0
- package/shared/validators/createCursorListValidator.test.js +34 -0
- package/shared/validators/cursorPaginationQueryValidator.js +31 -0
- package/shared/validators/cursorPaginationQueryValidator.test.js +21 -0
- package/shared/validators/index.js +12 -0
- package/shared/validators/inputNormalization.js +13 -0
- package/shared/validators/mergeObjectSchemas.js +31 -0
- package/shared/validators/mergeObjectSchemas.test.js +67 -0
- package/shared/validators/mergeValidators.js +89 -0
- package/shared/validators/mergeValidators.test.js +116 -0
- package/shared/validators/nestValidator.js +53 -0
- package/shared/validators/nestValidator.test.js +60 -0
- package/shared/validators/recordIdParamsValidator.js +36 -0
- package/shared/validators/recordIdParamsValidator.test.js +20 -0
- package/shared/validators/resourceRequiredMetadata.js +41 -0
- package/shared/validators/resourceRequiredMetadata.test.js +49 -0
- package/test/barrelExposure.test.js +106 -0
- package/test/dynamicImportPolicy.test.js +89 -0
- package/test/exportsContract.test.js +168 -0
- package/test/routeInputContractGuard.test.js +78 -0
- 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 };
|