@knitli/openapi-mcp 0.0.0
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/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +21 -0
- package/README.md +362 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +249 -0
- package/dist/compile.d.ts +14 -0
- package/dist/compile.js +132 -0
- package/dist/compiler.d.ts +13 -0
- package/dist/compiler.js +7 -0
- package/dist/conformance/fixtures.d.ts +24 -0
- package/dist/conformance/fixtures.js +119 -0
- package/dist/conformance/index.d.ts +2 -0
- package/dist/conformance/index.js +2 -0
- package/dist/conformance/runtime-suite.d.ts +31 -0
- package/dist/conformance/runtime-suite.js +343 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/load.d.ts +31 -0
- package/dist/load.js +20 -0
- package/dist/operations.d.ts +27 -0
- package/dist/operations.js +231 -0
- package/dist/permissions.d.ts +31 -0
- package/dist/permissions.js +89 -0
- package/dist/release/compile-release.d.ts +33 -0
- package/dist/release/compile-release.js +1620 -0
- package/dist/release/load-v4.d.ts +29 -0
- package/dist/release/load-v4.js +297 -0
- package/dist/release/manifest-builder.d.ts +19 -0
- package/dist/release/manifest-builder.js +28 -0
- package/dist/release/publish.d.ts +45 -0
- package/dist/release/publish.js +346 -0
- package/dist/release/reference-map.d.ts +53 -0
- package/dist/release/reference-map.js +347 -0
- package/dist/release/schema-v4.d.ts +5 -0
- package/dist/release/schema-v4.js +91 -0
- package/dist/runtime/action-permit.d.ts +11 -0
- package/dist/runtime/action-permit.js +1 -0
- package/dist/runtime/classify.d.ts +16 -0
- package/dist/runtime/classify.js +279 -0
- package/dist/runtime/credential-binding.d.ts +3 -0
- package/dist/runtime/credential-binding.js +101 -0
- package/dist/runtime/digest.d.ts +8 -0
- package/dist/runtime/digest.js +80 -0
- package/dist/runtime/errors.d.ts +14 -0
- package/dist/runtime/errors.js +15 -0
- package/dist/runtime/index.d.ts +20 -0
- package/dist/runtime/index.js +14 -0
- package/dist/runtime/manifest.d.ts +33 -0
- package/dist/runtime/manifest.js +489 -0
- package/dist/runtime/prepared-call.d.ts +39 -0
- package/dist/runtime/prepared-call.js +566 -0
- package/dist/runtime/references.d.ts +7 -0
- package/dist/runtime/references.js +197 -0
- package/dist/runtime/runtime.d.ts +25 -0
- package/dist/runtime/runtime.js +1272 -0
- package/dist/runtime/schema-keywords.d.ts +2 -0
- package/dist/runtime/schema-keywords.js +30 -0
- package/dist/runtime/schema-resolver.d.ts +10 -0
- package/dist/runtime/schema-resolver.js +237 -0
- package/dist/runtime/serialize.d.ts +12 -0
- package/dist/runtime/serialize.js +877 -0
- package/dist/runtime/store.d.ts +52 -0
- package/dist/runtime/store.js +582 -0
- package/dist/runtime/strict-json.d.ts +19 -0
- package/dist/runtime/strict-json.js +539 -0
- package/dist/runtime/types.d.ts +457 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/verify-record.d.ts +6 -0
- package/dist/runtime/verify-record.js +492 -0
- package/dist/runtime/versions.d.ts +63 -0
- package/dist/runtime/versions.js +86 -0
- package/dist/safety.d.ts +28 -0
- package/dist/safety.js +60 -0
- package/dist/schema.d.ts +5 -0
- package/dist/schema.js +52 -0
- package/dist/schemas.d.ts +9 -0
- package/dist/schemas.js +16 -0
- package/dist/sign.d.ts +20 -0
- package/dist/sign.js +55 -0
- package/dist/sqlite/auth.d.ts +33 -0
- package/dist/sqlite/auth.js +973 -0
- package/dist/sqlite/catalog-store.d.ts +20 -0
- package/dist/sqlite/catalog-store.js +370 -0
- package/dist/sqlite/destination-guard.d.ts +24 -0
- package/dist/sqlite/destination-guard.js +140 -0
- package/dist/sqlite/generation-store.d.ts +12 -0
- package/dist/sqlite/generation-store.js +721 -0
- package/dist/sqlite/guarded-fetch.d.ts +24 -0
- package/dist/sqlite/guarded-fetch.js +626 -0
- package/dist/sqlite/index.d.ts +5 -0
- package/dist/sqlite/index.js +5 -0
- package/dist/sqlite/oauth-resource.d.ts +2 -0
- package/dist/sqlite/oauth-resource.js +15 -0
- package/dist/sqlite/release-completion.d.ts +5 -0
- package/dist/sqlite/release-completion.js +80 -0
- package/dist/stdio/action-broker.d.ts +15 -0
- package/dist/stdio/action-broker.js +136 -0
- package/dist/stdio/authorizer.d.ts +30 -0
- package/dist/stdio/authorizer.js +484 -0
- package/dist/stdio/config.d.ts +77 -0
- package/dist/stdio/config.js +144 -0
- package/dist/stdio/exact-policy.d.ts +41 -0
- package/dist/stdio/exact-policy.js +361 -0
- package/dist/stdio/index.d.ts +6 -0
- package/dist/stdio/index.js +148 -0
- package/dist/stdio/render.d.ts +8 -0
- package/dist/stdio/render.js +253 -0
- package/dist/stdio/request-state.d.ts +18 -0
- package/dist/stdio/request-state.js +79 -0
- package/dist/stdio/search.d.ts +10 -0
- package/dist/stdio/search.js +76 -0
- package/dist/stdio/server.d.ts +29 -0
- package/dist/stdio/server.js +309 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.js +1 -0
- package/package.json +131 -0
|
@@ -0,0 +1,877 @@
|
|
|
1
|
+
import { OpenApiMcpError } from "./errors.js";
|
|
2
|
+
import { canonicalJson, canonicalJsonBounded, parseJsonStrict, } from "./strict-json.js";
|
|
3
|
+
import { DEFAULT_RUNTIME_LIMITS, resolveRuntimeLimits, } from "./versions.js";
|
|
4
|
+
const forbiddenObjectKeys = new Set(["__proto__", "constructor", "prototype"]);
|
|
5
|
+
const forbiddenHeaderNames = new Set([
|
|
6
|
+
"accept",
|
|
7
|
+
"authorization",
|
|
8
|
+
"cf-access-authenticated-user-email",
|
|
9
|
+
"cf-access-client-id",
|
|
10
|
+
"cf-access-client-secret",
|
|
11
|
+
"cf-connecting-ip",
|
|
12
|
+
"cf-connecting-ipv6",
|
|
13
|
+
"cf-pseudo-ipv4",
|
|
14
|
+
"cloudfront-viewer-address",
|
|
15
|
+
"connection",
|
|
16
|
+
"content-length",
|
|
17
|
+
"content-type",
|
|
18
|
+
"cookie",
|
|
19
|
+
"fastly-client-ip",
|
|
20
|
+
"fly-client-ip",
|
|
21
|
+
"forwarded",
|
|
22
|
+
"host",
|
|
23
|
+
"keep-alive",
|
|
24
|
+
"proxy-authenticate",
|
|
25
|
+
"proxy-authorization",
|
|
26
|
+
"proxy-connection",
|
|
27
|
+
"sec-fetch-dest",
|
|
28
|
+
"sec-fetch-mode",
|
|
29
|
+
"sec-fetch-site",
|
|
30
|
+
"sec-fetch-user",
|
|
31
|
+
"set-cookie",
|
|
32
|
+
"te",
|
|
33
|
+
"trailer",
|
|
34
|
+
"transfer-encoding",
|
|
35
|
+
"true-client-ip",
|
|
36
|
+
"upgrade",
|
|
37
|
+
"via",
|
|
38
|
+
"www-authenticate",
|
|
39
|
+
"x-api-key",
|
|
40
|
+
"x-amz-security-token",
|
|
41
|
+
"x-appengine-user-ip",
|
|
42
|
+
"x-auth-request-user",
|
|
43
|
+
"x-auth-token",
|
|
44
|
+
"x-azure-clientip",
|
|
45
|
+
"x-aws-principal",
|
|
46
|
+
"x-client-id",
|
|
47
|
+
"x-client-ip",
|
|
48
|
+
"x-client-secret",
|
|
49
|
+
"x-cluster-client-ip",
|
|
50
|
+
"x-envoy-external-address",
|
|
51
|
+
"x-forwarded-for",
|
|
52
|
+
"x-forwarded-host",
|
|
53
|
+
"x-forwarded-port",
|
|
54
|
+
"x-forwarded-proto",
|
|
55
|
+
"x-forwarded-server",
|
|
56
|
+
"x-goog-authenticated-user-email",
|
|
57
|
+
"x-ms-client-principal",
|
|
58
|
+
"x-original-forwarded-for",
|
|
59
|
+
"x-provider-id",
|
|
60
|
+
"x-real-ip",
|
|
61
|
+
]);
|
|
62
|
+
const maximumCredentialSlots = 64;
|
|
63
|
+
const maximumCredentialSlotNameBytes = 256;
|
|
64
|
+
const maximumSerializerOptionsBytes = 32 * 1024;
|
|
65
|
+
// Finite IEEE-754 values stringify within exponents -324 through 308.
|
|
66
|
+
const maximumFiniteDecimalExponent = 400;
|
|
67
|
+
const supportedSchemaKeys = new Set([
|
|
68
|
+
"$anchor",
|
|
69
|
+
"$comment",
|
|
70
|
+
"$defs",
|
|
71
|
+
"$id",
|
|
72
|
+
"$ref",
|
|
73
|
+
"$schema",
|
|
74
|
+
"additionalProperties",
|
|
75
|
+
"allOf",
|
|
76
|
+
"anyOf",
|
|
77
|
+
"const",
|
|
78
|
+
"default",
|
|
79
|
+
"deprecated",
|
|
80
|
+
"description",
|
|
81
|
+
"discriminator",
|
|
82
|
+
"enum",
|
|
83
|
+
"example",
|
|
84
|
+
"examples",
|
|
85
|
+
"exclusiveMaximum",
|
|
86
|
+
"exclusiveMinimum",
|
|
87
|
+
"format",
|
|
88
|
+
"items",
|
|
89
|
+
"maxItems",
|
|
90
|
+
"maxLength",
|
|
91
|
+
"maxProperties",
|
|
92
|
+
"maximum",
|
|
93
|
+
"minItems",
|
|
94
|
+
"minLength",
|
|
95
|
+
"minProperties",
|
|
96
|
+
"minimum",
|
|
97
|
+
"multipleOf",
|
|
98
|
+
"nullable",
|
|
99
|
+
"oneOf",
|
|
100
|
+
"properties",
|
|
101
|
+
"readOnly",
|
|
102
|
+
"required",
|
|
103
|
+
"title",
|
|
104
|
+
"type",
|
|
105
|
+
"uniqueItems",
|
|
106
|
+
"writeOnly",
|
|
107
|
+
"xml",
|
|
108
|
+
]);
|
|
109
|
+
function invalid(message = "OpenAPI arguments are invalid") {
|
|
110
|
+
return new OpenApiMcpError("INPUT_INVALID", message);
|
|
111
|
+
}
|
|
112
|
+
function isObject(value) {
|
|
113
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
114
|
+
}
|
|
115
|
+
function ownValue(object, key) {
|
|
116
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
117
|
+
return descriptor !== undefined && "value" in descriptor
|
|
118
|
+
? descriptor.value
|
|
119
|
+
: undefined;
|
|
120
|
+
}
|
|
121
|
+
function hasAsciiControl(value) {
|
|
122
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
123
|
+
const code = value.charCodeAt(index);
|
|
124
|
+
if (code <= 0x1f || code === 0x7f)
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
function snapshotArguments(value, limits) {
|
|
130
|
+
try {
|
|
131
|
+
const json = canonicalJsonBounded(value, {
|
|
132
|
+
maxBytes: limits.maxArgumentsBytes,
|
|
133
|
+
maxDepth: limits.maxJsonDepth,
|
|
134
|
+
maxNodes: Math.max(1, limits.maxArgumentsBytes),
|
|
135
|
+
});
|
|
136
|
+
const detached = parseJsonStrict(json, {
|
|
137
|
+
maxBytes: limits.maxArgumentsBytes,
|
|
138
|
+
maxDepth: limits.maxJsonDepth,
|
|
139
|
+
maxKeys: limits.maxArgumentsBytes,
|
|
140
|
+
});
|
|
141
|
+
if (!isObject(detached))
|
|
142
|
+
throw new Error();
|
|
143
|
+
const allowed = new Set(["body", "headers", "path", "query"]);
|
|
144
|
+
if (Object.keys(detached).some((key) => !allowed.has(key)))
|
|
145
|
+
throw new Error();
|
|
146
|
+
for (const section of ["path", "query", "headers"]) {
|
|
147
|
+
if (Object.hasOwn(detached, section) && !isObject(detached[section]))
|
|
148
|
+
throw new Error();
|
|
149
|
+
}
|
|
150
|
+
const path = detached.path;
|
|
151
|
+
if (isObject(path) &&
|
|
152
|
+
Object.values(path).some((entry) => typeof entry !== "string" &&
|
|
153
|
+
typeof entry !== "number" &&
|
|
154
|
+
typeof entry !== "boolean"))
|
|
155
|
+
throw new Error();
|
|
156
|
+
const headers = detached.headers;
|
|
157
|
+
if (isObject(headers) &&
|
|
158
|
+
Object.values(headers).some((entry) => typeof entry !== "string"))
|
|
159
|
+
throw new Error();
|
|
160
|
+
return detached;
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
throw invalid();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function schemaFailure() {
|
|
167
|
+
throw invalid("OpenAPI argument does not satisfy its declared schema");
|
|
168
|
+
}
|
|
169
|
+
function canonicalEqual(left, right) {
|
|
170
|
+
try {
|
|
171
|
+
return canonicalJson(left) === canonicalJson(right);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function schemaArray(value) {
|
|
178
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
179
|
+
schemaFailure();
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
function finiteKeyword(schema, key) {
|
|
183
|
+
if (!Object.hasOwn(schema, key))
|
|
184
|
+
return undefined;
|
|
185
|
+
const value = schema[key];
|
|
186
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
187
|
+
schemaFailure();
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
function finiteDecimal(value) {
|
|
191
|
+
const match = /^(-?)(\d+)(?:\.(\d+))?(?:e([+-]?\d+))?$/.exec(value.toString());
|
|
192
|
+
if (match === null)
|
|
193
|
+
schemaFailure();
|
|
194
|
+
const fraction = match[3] ?? "";
|
|
195
|
+
const coefficient = BigInt(`${match[1] ?? ""}${match[2]}${fraction}`);
|
|
196
|
+
const exponent = Number(match[4] ?? "0") - fraction.length;
|
|
197
|
+
if (!Number.isSafeInteger(exponent) ||
|
|
198
|
+
Math.abs(exponent) > maximumFiniteDecimalExponent)
|
|
199
|
+
schemaFailure();
|
|
200
|
+
return { coefficient, exponent };
|
|
201
|
+
}
|
|
202
|
+
function isExactDecimalMultiple(value, multiple) {
|
|
203
|
+
const candidate = finiteDecimal(value);
|
|
204
|
+
const divisor = finiteDecimal(multiple);
|
|
205
|
+
const exponentDifference = candidate.exponent - divisor.exponent;
|
|
206
|
+
if (exponentDifference >= 0) {
|
|
207
|
+
return ((candidate.coefficient * 10n ** BigInt(exponentDifference)) %
|
|
208
|
+
divisor.coefficient ===
|
|
209
|
+
0n);
|
|
210
|
+
}
|
|
211
|
+
return (candidate.coefficient %
|
|
212
|
+
(divisor.coefficient * 10n ** BigInt(-exponentDifference)) ===
|
|
213
|
+
0n);
|
|
214
|
+
}
|
|
215
|
+
function nonNegativeIntegerKeyword(schema, key) {
|
|
216
|
+
const value = finiteKeyword(schema, key);
|
|
217
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 0))
|
|
218
|
+
schemaFailure();
|
|
219
|
+
return value;
|
|
220
|
+
}
|
|
221
|
+
function jsonType(value) {
|
|
222
|
+
if (value === null)
|
|
223
|
+
return "null";
|
|
224
|
+
if (Array.isArray(value))
|
|
225
|
+
return "array";
|
|
226
|
+
if (typeof value === "object")
|
|
227
|
+
return "object";
|
|
228
|
+
if (typeof value === "number" && Number.isInteger(value))
|
|
229
|
+
return "integer";
|
|
230
|
+
return typeof value;
|
|
231
|
+
}
|
|
232
|
+
function validationContext(limits) {
|
|
233
|
+
return {
|
|
234
|
+
activeReferences: new Map(),
|
|
235
|
+
maxDepth: limits.maxJsonDepth,
|
|
236
|
+
remainingWork: Math.max(1, Math.min(limits.maxArgumentsBytes, limits.maxSchemaClosureBytes)),
|
|
237
|
+
exhausted: false,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function consumeValidationWork(context, amount = 1) {
|
|
241
|
+
if (amount > context.remainingWork) {
|
|
242
|
+
context.exhausted = true;
|
|
243
|
+
schemaFailure();
|
|
244
|
+
}
|
|
245
|
+
context.remainingWork -= amount;
|
|
246
|
+
}
|
|
247
|
+
function canonicalEnumMatch(entries, value, context) {
|
|
248
|
+
let candidate;
|
|
249
|
+
try {
|
|
250
|
+
candidate = canonicalJson(value);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
schemaFailure();
|
|
254
|
+
}
|
|
255
|
+
const encoder = new TextEncoder();
|
|
256
|
+
const candidateBytes = encoder.encode(candidate).length;
|
|
257
|
+
consumeValidationWork(context, candidateBytes);
|
|
258
|
+
for (const entry of entries) {
|
|
259
|
+
consumeValidationWork(context);
|
|
260
|
+
let member;
|
|
261
|
+
try {
|
|
262
|
+
member = canonicalJson(entry);
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
schemaFailure();
|
|
266
|
+
}
|
|
267
|
+
consumeValidationWork(context, candidateBytes + encoder.encode(member).length);
|
|
268
|
+
if (member === candidate)
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
function validates(schema, value, closure, depth, context) {
|
|
274
|
+
try {
|
|
275
|
+
validateSchema(schema, value, closure, depth, context);
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
if (context.exhausted)
|
|
280
|
+
throw error;
|
|
281
|
+
if (error instanceof OpenApiMcpError && error.code === "INPUT_INVALID")
|
|
282
|
+
return false;
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function validateDiscriminator(schema, branches, value) {
|
|
287
|
+
if (!Object.hasOwn(schema, "discriminator"))
|
|
288
|
+
return null;
|
|
289
|
+
const discriminator = schema.discriminator;
|
|
290
|
+
if (!isObject(discriminator) || !isObject(value))
|
|
291
|
+
schemaFailure();
|
|
292
|
+
const propertyName = discriminator.propertyName;
|
|
293
|
+
if (typeof propertyName !== "string" || propertyName.length === 0)
|
|
294
|
+
schemaFailure();
|
|
295
|
+
const selectedValue = value[propertyName];
|
|
296
|
+
if (typeof selectedValue !== "string")
|
|
297
|
+
schemaFailure();
|
|
298
|
+
const mapping = discriminator.mapping;
|
|
299
|
+
if (!isObject(mapping))
|
|
300
|
+
schemaFailure();
|
|
301
|
+
const target = mapping[selectedValue];
|
|
302
|
+
if (typeof target !== "string")
|
|
303
|
+
schemaFailure();
|
|
304
|
+
const selected = branches
|
|
305
|
+
.map((branch, index) => isObject(branch) && branch.$ref === target ? index : -1)
|
|
306
|
+
.filter((index) => index >= 0);
|
|
307
|
+
if (selected.length !== 1)
|
|
308
|
+
schemaFailure();
|
|
309
|
+
return selected[0];
|
|
310
|
+
}
|
|
311
|
+
function validateReference(id, schema, value, closure, depth, context) {
|
|
312
|
+
const activeValues = context.activeReferences.get(id) ?? new Set();
|
|
313
|
+
if (activeValues.has(value))
|
|
314
|
+
schemaFailure();
|
|
315
|
+
activeValues.add(value);
|
|
316
|
+
context.activeReferences.set(id, activeValues);
|
|
317
|
+
try {
|
|
318
|
+
validateSchema(schema, value, closure, depth, context);
|
|
319
|
+
}
|
|
320
|
+
finally {
|
|
321
|
+
activeValues.delete(value);
|
|
322
|
+
if (activeValues.size === 0)
|
|
323
|
+
context.activeReferences.delete(id);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function validateSchema(schema, value, closure, depth, context) {
|
|
327
|
+
consumeValidationWork(context);
|
|
328
|
+
if (depth > context.maxDepth)
|
|
329
|
+
schemaFailure();
|
|
330
|
+
if (schema === true)
|
|
331
|
+
return;
|
|
332
|
+
if (schema === false || !isObject(schema))
|
|
333
|
+
schemaFailure();
|
|
334
|
+
for (const key of Object.keys(schema)) {
|
|
335
|
+
if (!supportedSchemaKeys.has(key))
|
|
336
|
+
schemaFailure();
|
|
337
|
+
}
|
|
338
|
+
if (Object.hasOwn(schema, "$ref")) {
|
|
339
|
+
if (typeof schema.$ref !== "string")
|
|
340
|
+
schemaFailure();
|
|
341
|
+
const id = schema.$ref;
|
|
342
|
+
const referenced = closure.get(id);
|
|
343
|
+
if (referenced === undefined)
|
|
344
|
+
schemaFailure();
|
|
345
|
+
validateReference(id, referenced.schema, value, closure, depth + 1, context);
|
|
346
|
+
}
|
|
347
|
+
if (Object.hasOwn(schema, "allOf")) {
|
|
348
|
+
for (const branch of schemaArray(schema.allOf))
|
|
349
|
+
validateSchema(branch, value, closure, depth + 1, context);
|
|
350
|
+
}
|
|
351
|
+
if (Object.hasOwn(schema, "anyOf")) {
|
|
352
|
+
const matches = schemaArray(schema.anyOf).filter((branch) => validates(branch, value, closure, depth + 1, context));
|
|
353
|
+
if (matches.length === 0)
|
|
354
|
+
schemaFailure();
|
|
355
|
+
}
|
|
356
|
+
if (Object.hasOwn(schema, "oneOf")) {
|
|
357
|
+
const branches = schemaArray(schema.oneOf);
|
|
358
|
+
const selected = validateDiscriminator(schema, branches, value);
|
|
359
|
+
const matches = branches
|
|
360
|
+
.map((branch, index) => validates(branch, value, closure, depth + 1, context) ? index : -1)
|
|
361
|
+
.filter((index) => index >= 0);
|
|
362
|
+
if (matches.length !== 1 || (selected !== null && matches[0] !== selected))
|
|
363
|
+
schemaFailure();
|
|
364
|
+
}
|
|
365
|
+
else if (Object.hasOwn(schema, "discriminator")) {
|
|
366
|
+
schemaFailure();
|
|
367
|
+
}
|
|
368
|
+
if (Object.hasOwn(schema, "nullable")) {
|
|
369
|
+
if (typeof schema.nullable !== "boolean")
|
|
370
|
+
schemaFailure();
|
|
371
|
+
if (value === null && schema.nullable)
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (Object.hasOwn(schema, "type")) {
|
|
375
|
+
const rawTypes = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
376
|
+
if (rawTypes.length === 0 ||
|
|
377
|
+
rawTypes.some((entry) => typeof entry !== "string" ||
|
|
378
|
+
![
|
|
379
|
+
"array",
|
|
380
|
+
"boolean",
|
|
381
|
+
"integer",
|
|
382
|
+
"null",
|
|
383
|
+
"number",
|
|
384
|
+
"object",
|
|
385
|
+
"string",
|
|
386
|
+
].includes(entry)))
|
|
387
|
+
schemaFailure();
|
|
388
|
+
const actual = jsonType(value);
|
|
389
|
+
if (!rawTypes.includes(actual) &&
|
|
390
|
+
!(actual === "integer" && rawTypes.includes("number")))
|
|
391
|
+
schemaFailure();
|
|
392
|
+
}
|
|
393
|
+
if (Object.hasOwn(schema, "enum") &&
|
|
394
|
+
(!Array.isArray(schema.enum) ||
|
|
395
|
+
!canonicalEnumMatch(schema.enum, value, context)))
|
|
396
|
+
schemaFailure();
|
|
397
|
+
if (Object.hasOwn(schema, "const") && !canonicalEqual(schema.const, value))
|
|
398
|
+
schemaFailure();
|
|
399
|
+
if (typeof value === "string") {
|
|
400
|
+
const length = [...value].length;
|
|
401
|
+
const minimum = nonNegativeIntegerKeyword(schema, "minLength");
|
|
402
|
+
const maximum = nonNegativeIntegerKeyword(schema, "maxLength");
|
|
403
|
+
if (minimum !== undefined && length < minimum)
|
|
404
|
+
schemaFailure();
|
|
405
|
+
if (maximum !== undefined && length > maximum)
|
|
406
|
+
schemaFailure();
|
|
407
|
+
}
|
|
408
|
+
if (typeof value === "number") {
|
|
409
|
+
if (!Number.isFinite(value))
|
|
410
|
+
schemaFailure();
|
|
411
|
+
const minimum = finiteKeyword(schema, "minimum");
|
|
412
|
+
const maximum = finiteKeyword(schema, "maximum");
|
|
413
|
+
if (minimum !== undefined && value < minimum)
|
|
414
|
+
schemaFailure();
|
|
415
|
+
if (maximum !== undefined && value > maximum)
|
|
416
|
+
schemaFailure();
|
|
417
|
+
const exclusiveMinimum = schema.exclusiveMinimum;
|
|
418
|
+
if (typeof exclusiveMinimum === "number" &&
|
|
419
|
+
(!Number.isFinite(exclusiveMinimum) || value <= exclusiveMinimum))
|
|
420
|
+
schemaFailure();
|
|
421
|
+
if (exclusiveMinimum === true && minimum !== undefined && value <= minimum)
|
|
422
|
+
schemaFailure();
|
|
423
|
+
if (exclusiveMinimum !== undefined &&
|
|
424
|
+
typeof exclusiveMinimum !== "number" &&
|
|
425
|
+
typeof exclusiveMinimum !== "boolean")
|
|
426
|
+
schemaFailure();
|
|
427
|
+
const exclusiveMaximum = schema.exclusiveMaximum;
|
|
428
|
+
if (typeof exclusiveMaximum === "number" &&
|
|
429
|
+
(!Number.isFinite(exclusiveMaximum) || value >= exclusiveMaximum))
|
|
430
|
+
schemaFailure();
|
|
431
|
+
if (exclusiveMaximum === true && maximum !== undefined && value >= maximum)
|
|
432
|
+
schemaFailure();
|
|
433
|
+
if (exclusiveMaximum !== undefined &&
|
|
434
|
+
typeof exclusiveMaximum !== "number" &&
|
|
435
|
+
typeof exclusiveMaximum !== "boolean")
|
|
436
|
+
schemaFailure();
|
|
437
|
+
const multiple = finiteKeyword(schema, "multipleOf");
|
|
438
|
+
if (multiple !== undefined) {
|
|
439
|
+
if (multiple <= 0)
|
|
440
|
+
schemaFailure();
|
|
441
|
+
if (!isExactDecimalMultiple(value, multiple))
|
|
442
|
+
schemaFailure();
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (Array.isArray(value)) {
|
|
446
|
+
const minimum = nonNegativeIntegerKeyword(schema, "minItems");
|
|
447
|
+
const maximum = nonNegativeIntegerKeyword(schema, "maxItems");
|
|
448
|
+
if (minimum !== undefined && value.length < minimum)
|
|
449
|
+
schemaFailure();
|
|
450
|
+
if (maximum !== undefined && value.length > maximum)
|
|
451
|
+
schemaFailure();
|
|
452
|
+
if (Object.hasOwn(schema, "uniqueItems")) {
|
|
453
|
+
if (typeof schema.uniqueItems !== "boolean")
|
|
454
|
+
schemaFailure();
|
|
455
|
+
if (schema.uniqueItems) {
|
|
456
|
+
const values = new Set(value.map((entry) => canonicalJson(entry)));
|
|
457
|
+
if (values.size !== value.length)
|
|
458
|
+
schemaFailure();
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
if (Object.hasOwn(schema, "items")) {
|
|
462
|
+
const items = schema.items;
|
|
463
|
+
if (typeof items !== "boolean" && !isObject(items))
|
|
464
|
+
schemaFailure();
|
|
465
|
+
for (const entry of value)
|
|
466
|
+
validateSchema(items, entry, closure, depth + 1, context);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (isObject(value)) {
|
|
470
|
+
const keys = Object.keys(value);
|
|
471
|
+
const minimum = nonNegativeIntegerKeyword(schema, "minProperties");
|
|
472
|
+
const maximum = nonNegativeIntegerKeyword(schema, "maxProperties");
|
|
473
|
+
if (minimum !== undefined && keys.length < minimum)
|
|
474
|
+
schemaFailure();
|
|
475
|
+
if (maximum !== undefined && keys.length > maximum)
|
|
476
|
+
schemaFailure();
|
|
477
|
+
const properties = Object.hasOwn(schema, "properties")
|
|
478
|
+
? schema.properties
|
|
479
|
+
: undefined;
|
|
480
|
+
if (properties !== undefined && !isObject(properties))
|
|
481
|
+
schemaFailure();
|
|
482
|
+
if (Object.hasOwn(schema, "required")) {
|
|
483
|
+
if (!Array.isArray(schema.required) ||
|
|
484
|
+
schema.required.some((key) => typeof key !== "string"))
|
|
485
|
+
schemaFailure();
|
|
486
|
+
for (const key of schema.required) {
|
|
487
|
+
if (!Object.hasOwn(value, key))
|
|
488
|
+
schemaFailure();
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
for (const key of keys) {
|
|
492
|
+
if (forbiddenObjectKeys.has(key))
|
|
493
|
+
schemaFailure();
|
|
494
|
+
if (isObject(properties) && Object.hasOwn(properties, key)) {
|
|
495
|
+
validateSchema(properties[key], value[key], closure, depth + 1, context);
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
const additional = schema.additionalProperties;
|
|
499
|
+
if (additional === false)
|
|
500
|
+
schemaFailure();
|
|
501
|
+
if (additional !== undefined && additional !== true) {
|
|
502
|
+
if (!isObject(additional))
|
|
503
|
+
schemaFailure();
|
|
504
|
+
validateSchema(additional, value[key], closure, depth + 1, context);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
function encode(value) {
|
|
510
|
+
const text = typeof value === "string" ? value : value === null ? "null" : String(value);
|
|
511
|
+
return encodeURIComponent(text).replace(/[!'()*]/g, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
512
|
+
}
|
|
513
|
+
function scalar(value) {
|
|
514
|
+
return (value === null ||
|
|
515
|
+
typeof value === "string" ||
|
|
516
|
+
typeof value === "number" ||
|
|
517
|
+
typeof value === "boolean");
|
|
518
|
+
}
|
|
519
|
+
function flatObject(value) {
|
|
520
|
+
if (!isObject(value))
|
|
521
|
+
schemaFailure();
|
|
522
|
+
const entries = Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
523
|
+
if (entries.some(([, entry]) => !scalar(entry)))
|
|
524
|
+
schemaFailure();
|
|
525
|
+
return entries;
|
|
526
|
+
}
|
|
527
|
+
function arrayScalars(value) {
|
|
528
|
+
if (!Array.isArray(value) || value.some((entry) => !scalar(entry)))
|
|
529
|
+
schemaFailure();
|
|
530
|
+
return value;
|
|
531
|
+
}
|
|
532
|
+
function pathValue(parameter, value) {
|
|
533
|
+
// OpenApiArguments intentionally permits scalar path values only.
|
|
534
|
+
if (!scalar(value) || value === null)
|
|
535
|
+
schemaFailure();
|
|
536
|
+
const encoded = encode(value);
|
|
537
|
+
if (parameter.style === "simple")
|
|
538
|
+
return encoded;
|
|
539
|
+
if (parameter.style === "label")
|
|
540
|
+
return `.${encoded}`;
|
|
541
|
+
if (parameter.style === "matrix")
|
|
542
|
+
return `;${encode(parameter.name)}=${encoded}`;
|
|
543
|
+
schemaFailure();
|
|
544
|
+
}
|
|
545
|
+
function queryValues(parameter, value) {
|
|
546
|
+
const name = encode(parameter.name);
|
|
547
|
+
if (parameter.style === "deepObject") {
|
|
548
|
+
if (!parameter.explode)
|
|
549
|
+
schemaFailure();
|
|
550
|
+
return flatObject(value).map(([key, entry]) => [
|
|
551
|
+
`${name}%5B${encode(key)}%5D`,
|
|
552
|
+
encode(entry),
|
|
553
|
+
]);
|
|
554
|
+
}
|
|
555
|
+
if (parameter.style === "spaceDelimited" ||
|
|
556
|
+
parameter.style === "pipeDelimited") {
|
|
557
|
+
if (parameter.explode)
|
|
558
|
+
schemaFailure();
|
|
559
|
+
const delimiter = parameter.style === "spaceDelimited" ? "%20" : "%7C";
|
|
560
|
+
return [[name, arrayScalars(value).map(encode).join(delimiter)]];
|
|
561
|
+
}
|
|
562
|
+
if (parameter.style !== "form")
|
|
563
|
+
schemaFailure();
|
|
564
|
+
if (scalar(value))
|
|
565
|
+
return [[name, encode(value)]];
|
|
566
|
+
if (Array.isArray(value)) {
|
|
567
|
+
const entries = arrayScalars(value).map(encode);
|
|
568
|
+
return parameter.explode
|
|
569
|
+
? entries.map((entry) => [name, entry])
|
|
570
|
+
: [[name, entries.join(",")]];
|
|
571
|
+
}
|
|
572
|
+
const entries = flatObject(value);
|
|
573
|
+
return parameter.explode
|
|
574
|
+
? entries.map(([key, entry]) => [encode(key), encode(entry)])
|
|
575
|
+
: [
|
|
576
|
+
[
|
|
577
|
+
name,
|
|
578
|
+
entries
|
|
579
|
+
.flatMap(([key, entry]) => [encode(key), encode(entry)])
|
|
580
|
+
.join(","),
|
|
581
|
+
],
|
|
582
|
+
];
|
|
583
|
+
}
|
|
584
|
+
function headerValue(parameter, value) {
|
|
585
|
+
if (parameter.style !== "simple" || typeof value !== "string")
|
|
586
|
+
schemaFailure();
|
|
587
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
588
|
+
const code = value.charCodeAt(index);
|
|
589
|
+
if ((code <= 0x1f && code !== 0x09) || code === 0x7f)
|
|
590
|
+
throw invalid("Header arguments are invalid");
|
|
591
|
+
}
|
|
592
|
+
return value;
|
|
593
|
+
}
|
|
594
|
+
function forbiddenHeader(name) {
|
|
595
|
+
const lower = name.toLowerCase();
|
|
596
|
+
return (!/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name) ||
|
|
597
|
+
forbiddenHeaderNames.has(lower));
|
|
598
|
+
}
|
|
599
|
+
function deepFreezeJson(value) {
|
|
600
|
+
if (typeof value !== "object" || value === null || Object.isFrozen(value))
|
|
601
|
+
return value;
|
|
602
|
+
if (Array.isArray(value)) {
|
|
603
|
+
for (const entry of value)
|
|
604
|
+
deepFreezeJson(entry);
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
for (const entry of Object.values(value))
|
|
608
|
+
deepFreezeJson(entry);
|
|
609
|
+
}
|
|
610
|
+
return Object.freeze(value);
|
|
611
|
+
}
|
|
612
|
+
function snapshotSerializerOptions(value) {
|
|
613
|
+
try {
|
|
614
|
+
const json = canonicalJsonBounded(value, {
|
|
615
|
+
maxBytes: maximumSerializerOptionsBytes,
|
|
616
|
+
maxDepth: 4,
|
|
617
|
+
maxNodes: 512,
|
|
618
|
+
});
|
|
619
|
+
const detached = parseJsonStrict(json, {
|
|
620
|
+
maxBytes: maximumSerializerOptionsBytes,
|
|
621
|
+
maxDepth: 4,
|
|
622
|
+
maxKeys: 256,
|
|
623
|
+
});
|
|
624
|
+
if (!isObject(detached))
|
|
625
|
+
throw new Error();
|
|
626
|
+
const optionKeys = Object.keys(detached);
|
|
627
|
+
if (optionKeys.some((key) => key !== "limits" && key !== "reservedCredentialSlots"))
|
|
628
|
+
throw new Error();
|
|
629
|
+
const rawLimits = Object.hasOwn(detached, "limits") ? detached.limits : {};
|
|
630
|
+
if (!isObject(rawLimits))
|
|
631
|
+
throw new Error();
|
|
632
|
+
const limitNames = new Set(Object.keys(DEFAULT_RUNTIME_LIMITS));
|
|
633
|
+
if (Object.keys(rawLimits).some((key) => !limitNames.has(key)))
|
|
634
|
+
throw new Error();
|
|
635
|
+
const limits = Object.freeze({ ...rawLimits });
|
|
636
|
+
const rawSlots = Object.hasOwn(detached, "reservedCredentialSlots")
|
|
637
|
+
? detached.reservedCredentialSlots
|
|
638
|
+
: [];
|
|
639
|
+
if (!Array.isArray(rawSlots) || rawSlots.length > maximumCredentialSlots)
|
|
640
|
+
throw new Error();
|
|
641
|
+
const reservedHeaderSlots = new Set();
|
|
642
|
+
const reservedQuerySlots = new Set();
|
|
643
|
+
const slots = rawSlots.map((rawSlot) => {
|
|
644
|
+
if (!isObject(rawSlot))
|
|
645
|
+
throw new Error();
|
|
646
|
+
const keys = Object.keys(rawSlot).sort();
|
|
647
|
+
if (keys.length !== 2 || keys[0] !== "name" || keys[1] !== "placement")
|
|
648
|
+
throw new Error();
|
|
649
|
+
const placement = rawSlot.placement;
|
|
650
|
+
const name = rawSlot.name;
|
|
651
|
+
if ((placement !== "header" && placement !== "query") ||
|
|
652
|
+
typeof name !== "string" ||
|
|
653
|
+
name.length === 0 ||
|
|
654
|
+
new TextEncoder().encode(name).length >
|
|
655
|
+
maximumCredentialSlotNameBytes ||
|
|
656
|
+
hasAsciiControl(name))
|
|
657
|
+
throw new Error();
|
|
658
|
+
if (placement === "header" &&
|
|
659
|
+
!/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name))
|
|
660
|
+
throw new Error();
|
|
661
|
+
const wireName = placement === "header" ? name.toLowerCase() : encode(name);
|
|
662
|
+
const target = placement === "header" ? reservedHeaderSlots : reservedQuerySlots;
|
|
663
|
+
if (target.has(wireName))
|
|
664
|
+
throw new Error();
|
|
665
|
+
target.add(wireName);
|
|
666
|
+
return Object.freeze({ placement, name });
|
|
667
|
+
});
|
|
668
|
+
Object.freeze(slots);
|
|
669
|
+
return Object.freeze({
|
|
670
|
+
limits,
|
|
671
|
+
reservedCredentialSlots: slots,
|
|
672
|
+
reservedHeaderSlots: Object.freeze(reservedHeaderSlots),
|
|
673
|
+
reservedQuerySlots: Object.freeze(reservedQuerySlots),
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
catch {
|
|
677
|
+
throw invalid("Serializer options are invalid");
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
function section(arguments_, name) {
|
|
681
|
+
const value = arguments_[name];
|
|
682
|
+
return isObject(value) ? value : {};
|
|
683
|
+
}
|
|
684
|
+
function ensurePathTemplate(path) {
|
|
685
|
+
if (!path.startsWith("/") ||
|
|
686
|
+
path.startsWith("//") ||
|
|
687
|
+
path.includes("\\") ||
|
|
688
|
+
path.includes("?") ||
|
|
689
|
+
path.includes("#") ||
|
|
690
|
+
/\r|\n/.test(path))
|
|
691
|
+
throw invalid("Operation path is invalid");
|
|
692
|
+
}
|
|
693
|
+
function chooseBodyMedia(operation) {
|
|
694
|
+
const requestBody = operation.requestBody;
|
|
695
|
+
if (requestBody === null)
|
|
696
|
+
return null;
|
|
697
|
+
const supported = requestBody.content.filter((entry) => entry.mediaType === "application/json" ||
|
|
698
|
+
entry.mediaType.endsWith("+json"));
|
|
699
|
+
if (supported.length !== 1 || requestBody.content.length !== 1)
|
|
700
|
+
throw invalid("Request body media type is unsupported");
|
|
701
|
+
if (supported[0].encoding.length !== 0)
|
|
702
|
+
throw invalid("Request body encoding is unsupported");
|
|
703
|
+
return supported[0];
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Detach, validate, and serialize one exact public argument envelope.
|
|
707
|
+
* The result is authority-free and contains no caller-selected credentials.
|
|
708
|
+
*/
|
|
709
|
+
export function serializeArguments(operation, schemas, arguments_, optionsValue = {}) {
|
|
710
|
+
const options = snapshotSerializerOptions(optionsValue);
|
|
711
|
+
let limits;
|
|
712
|
+
try {
|
|
713
|
+
limits = resolveRuntimeLimits(options.limits);
|
|
714
|
+
}
|
|
715
|
+
catch {
|
|
716
|
+
throw invalid("Serializer options are invalid");
|
|
717
|
+
}
|
|
718
|
+
const validation = validationContext(limits);
|
|
719
|
+
const input = snapshotArguments(arguments_, limits);
|
|
720
|
+
ensurePathTemplate(operation.path);
|
|
721
|
+
const pathArguments = section(input, "path");
|
|
722
|
+
const queryArguments = section(input, "query");
|
|
723
|
+
const headerArguments = section(input, "headers");
|
|
724
|
+
const consumed = {
|
|
725
|
+
path: new Set(),
|
|
726
|
+
query: new Set(),
|
|
727
|
+
headers: new Set(),
|
|
728
|
+
};
|
|
729
|
+
const normalized = {};
|
|
730
|
+
const normalizedPath = {};
|
|
731
|
+
const normalizedQuery = {};
|
|
732
|
+
const normalizedHeaders = {};
|
|
733
|
+
const outputHeaders = { accept: "application/json" };
|
|
734
|
+
if (options.reservedHeaderSlots.has("accept"))
|
|
735
|
+
throw invalid("Reserved credential slot collides with a representation header");
|
|
736
|
+
const query = [];
|
|
737
|
+
const queryWireOwners = new Map();
|
|
738
|
+
let relativeUrl = operation.path;
|
|
739
|
+
const declared = new Set();
|
|
740
|
+
for (const parameter of operation.parameters) {
|
|
741
|
+
if (parameter.in === "cookie")
|
|
742
|
+
throw invalid("Cookie parameters are unsupported");
|
|
743
|
+
if (parameter.value.kind !== "schema")
|
|
744
|
+
throw invalid("Parameter content is unsupported");
|
|
745
|
+
if (parameter.allowReserved)
|
|
746
|
+
throw invalid("Reserved parameter serialization is unsupported");
|
|
747
|
+
if (parameter.in === "header" && forbiddenHeader(parameter.name))
|
|
748
|
+
throw invalid("Credential or transport headers are forbidden");
|
|
749
|
+
if (parameter.in === "header" &&
|
|
750
|
+
options.reservedHeaderSlots.has(parameter.name.toLowerCase()))
|
|
751
|
+
throw invalid("Declared header collides with a reserved credential slot");
|
|
752
|
+
const identity = `${parameter.in}\0${parameter.in === "header" ? parameter.name.toLowerCase() : parameter.name}`;
|
|
753
|
+
if (declared.has(identity))
|
|
754
|
+
throw invalid("Parameter declaration is ambiguous");
|
|
755
|
+
declared.add(identity);
|
|
756
|
+
const source = parameter.in === "path"
|
|
757
|
+
? pathArguments
|
|
758
|
+
: parameter.in === "query"
|
|
759
|
+
? queryArguments
|
|
760
|
+
: headerArguments;
|
|
761
|
+
let sourceKey = parameter.name;
|
|
762
|
+
if (parameter.in === "header") {
|
|
763
|
+
const matches = Object.keys(source).filter((key) => key.toLowerCase() === parameter.name.toLowerCase());
|
|
764
|
+
if (matches.length > 1)
|
|
765
|
+
throw invalid("Header argument is ambiguous");
|
|
766
|
+
sourceKey = matches[0] ?? parameter.name;
|
|
767
|
+
}
|
|
768
|
+
const present = Object.hasOwn(source, sourceKey);
|
|
769
|
+
if (!present) {
|
|
770
|
+
if (parameter.required)
|
|
771
|
+
throw invalid("Required parameter is missing");
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
const value = ownValue(source, sourceKey);
|
|
775
|
+
const schema = schemas.get(parameter.value.schemaId);
|
|
776
|
+
if (schema === undefined)
|
|
777
|
+
throw invalid("Declared schema is unavailable");
|
|
778
|
+
validateReference(schema.id, schema.schema, value, schemas, 0, validation);
|
|
779
|
+
if (parameter.in === "path") {
|
|
780
|
+
const token = `{${parameter.name}}`;
|
|
781
|
+
if (!relativeUrl.includes(token))
|
|
782
|
+
throw invalid("Path parameter is not templated");
|
|
783
|
+
relativeUrl = relativeUrl.split(token).join(pathValue(parameter, value));
|
|
784
|
+
normalizedPath[parameter.name] = value;
|
|
785
|
+
consumed.path.add(sourceKey);
|
|
786
|
+
}
|
|
787
|
+
else if (parameter.in === "query") {
|
|
788
|
+
const serialized = queryValues(parameter, value);
|
|
789
|
+
const intentionalRepeat = Array.isArray(value) && parameter.style === "form" && parameter.explode;
|
|
790
|
+
const localKeys = new Set();
|
|
791
|
+
for (const [wireKey] of serialized) {
|
|
792
|
+
if (options.reservedQuerySlots.has(wireKey))
|
|
793
|
+
throw invalid("Query parameter collides with a reserved credential slot");
|
|
794
|
+
const owner = queryWireOwners.get(wireKey);
|
|
795
|
+
if ((localKeys.has(wireKey) && !intentionalRepeat) ||
|
|
796
|
+
(owner !== undefined && owner !== identity)) {
|
|
797
|
+
throw invalid("Query parameter serialization is ambiguous");
|
|
798
|
+
}
|
|
799
|
+
localKeys.add(wireKey);
|
|
800
|
+
queryWireOwners.set(wireKey, identity);
|
|
801
|
+
}
|
|
802
|
+
query.push(...serialized);
|
|
803
|
+
normalizedQuery[parameter.name] = value;
|
|
804
|
+
consumed.query.add(sourceKey);
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
const lower = parameter.name.toLowerCase();
|
|
808
|
+
outputHeaders[lower] = headerValue(parameter, value);
|
|
809
|
+
normalizedHeaders[parameter.name] = value;
|
|
810
|
+
consumed.headers.add(sourceKey);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (/\{[^{}]+\}/.test(relativeUrl))
|
|
814
|
+
throw invalid("Path parameter is missing");
|
|
815
|
+
for (const [name, values] of [
|
|
816
|
+
["path", pathArguments],
|
|
817
|
+
["query", queryArguments],
|
|
818
|
+
["headers", headerArguments],
|
|
819
|
+
]) {
|
|
820
|
+
if (Object.keys(values).some((key) => !consumed[name].has(key)))
|
|
821
|
+
throw invalid("Unknown parameter argument");
|
|
822
|
+
}
|
|
823
|
+
if (Object.keys(normalizedHeaders).length > 0)
|
|
824
|
+
normalized.headers = normalizedHeaders;
|
|
825
|
+
if (Object.keys(normalizedPath).length > 0)
|
|
826
|
+
normalized.path = normalizedPath;
|
|
827
|
+
if (Object.keys(normalizedQuery).length > 0)
|
|
828
|
+
normalized.query = normalizedQuery;
|
|
829
|
+
if (query.length > 0)
|
|
830
|
+
relativeUrl += `?${query.map(([key, value]) => `${key}=${value}`).join("&")}`;
|
|
831
|
+
const bodyMedia = chooseBodyMedia(operation);
|
|
832
|
+
const hasBody = Object.hasOwn(input, "body");
|
|
833
|
+
let body = null;
|
|
834
|
+
if (bodyMedia === null) {
|
|
835
|
+
if (hasBody)
|
|
836
|
+
throw invalid("Request body is not declared");
|
|
837
|
+
}
|
|
838
|
+
else if (!hasBody) {
|
|
839
|
+
if (operation.requestBody?.required)
|
|
840
|
+
throw invalid("Required request body is missing");
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
const bodyValue = input.body;
|
|
844
|
+
const bodySchema = schemas.get(bodyMedia.schemaId);
|
|
845
|
+
if (bodySchema === undefined)
|
|
846
|
+
throw invalid("Declared schema is unavailable");
|
|
847
|
+
validateReference(bodySchema.id, bodySchema.schema, bodyValue, schemas, 0, validation);
|
|
848
|
+
const bodyJson = canonicalJson(bodyValue);
|
|
849
|
+
body = new TextEncoder().encode(bodyJson);
|
|
850
|
+
if (options.reservedHeaderSlots.has("content-type"))
|
|
851
|
+
throw invalid("Reserved credential slot collides with a representation header");
|
|
852
|
+
outputHeaders["content-type"] = bodyMedia.mediaType;
|
|
853
|
+
normalized.body = JSON.parse(bodyJson);
|
|
854
|
+
}
|
|
855
|
+
// A second bound includes the canonical normalization performed above and
|
|
856
|
+
// guarantees no partial result escapes if normalization grows unexpectedly.
|
|
857
|
+
try {
|
|
858
|
+
canonicalJsonBounded(normalized, {
|
|
859
|
+
maxBytes: limits.maxArgumentsBytes,
|
|
860
|
+
maxDepth: limits.maxJsonDepth,
|
|
861
|
+
maxNodes: Math.max(1, limits.maxArgumentsBytes),
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
catch {
|
|
865
|
+
throw invalid();
|
|
866
|
+
}
|
|
867
|
+
const normalizedArguments = deepFreezeJson(normalized);
|
|
868
|
+
const bodySnapshot = body === null ? null : new Uint8Array(body);
|
|
869
|
+
return Object.freeze({
|
|
870
|
+
normalizedArguments,
|
|
871
|
+
relativeUrl,
|
|
872
|
+
headers: Object.freeze(outputHeaders),
|
|
873
|
+
get body() {
|
|
874
|
+
return bodySnapshot === null ? null : new Uint8Array(bodySnapshot);
|
|
875
|
+
},
|
|
876
|
+
});
|
|
877
|
+
}
|