@blamejs/core 0.14.18 → 0.14.19
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/CHANGELOG.md +2 -0
- package/README.md +1 -1
- package/lib/archive.js +206 -52
- package/lib/auth/oauth.js +58 -0
- package/lib/auth/oid4vci.js +84 -27
- package/lib/mail-auth.js +554 -55
- package/lib/middleware/scim-server.js +294 -10
- package/lib/openapi-paths-builder.js +105 -29
- package/lib/openapi.js +225 -100
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/openapi.js
CHANGED
|
@@ -5,24 +5,32 @@
|
|
|
5
5
|
* @title Openapi
|
|
6
6
|
*
|
|
7
7
|
* @intro
|
|
8
|
-
* OpenAPI 3.1 emitter from declarative route declarations +
|
|
9
|
-
* (composable with `b.safeSchema`); JSON / YAML output.
|
|
10
|
-
* describe their public HTTP surface as an OpenAPI
|
|
11
|
-
* framework serves at `/openapi.json` (or any path) for
|
|
12
|
-
* tooling: API consumers, Postman, code-generators,
|
|
13
|
-
* rigs.
|
|
8
|
+
* OpenAPI 3.1 / 3.2 emitter from declarative route declarations +
|
|
9
|
+
* schemas (composable with `b.safeSchema`); JSON / YAML output.
|
|
10
|
+
* Operators describe their public HTTP surface as an OpenAPI document
|
|
11
|
+
* the framework serves at `/openapi.json` (or any path) for
|
|
12
|
+
* downstream tooling: API consumers, Postman, code-generators,
|
|
13
|
+
* contract-test rigs.
|
|
14
14
|
*
|
|
15
|
-
* The builder is FRAMEWORK-FACING: it produces a valid OpenAPI
|
|
15
|
+
* The builder is FRAMEWORK-FACING: it produces a valid OpenAPI
|
|
16
16
|
* document, but the operator's hand-written contract is the source
|
|
17
17
|
* of truth — it does NOT auto-walk `b.router` routes (operators
|
|
18
18
|
* frequently want a smaller / different surface published than what
|
|
19
19
|
* the router exposes internally).
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* `3.1.0` is the default emitted version. Pass
|
|
22
|
+
* `create({ openapi: "3.2.0", ... })` to opt into OpenAPI 3.2; both
|
|
23
|
+
* 3.1.x and 3.2.x parse and emit. The 3.2 additions wired here are
|
|
24
|
+
* the top-level `webhooks` map (named out-of-band Path Item Objects
|
|
25
|
+
* the API initiates — OpenAPI 3.2 §4.8.2) and the `jsonSchemaDialect`
|
|
26
|
+
* field (declares the default JSON Schema dialect for the document —
|
|
27
|
+
* OpenAPI 3.2 §4.8.1).
|
|
28
|
+
*
|
|
29
|
+
* The builder fluent surface is `path()` / `webhook()` / `schema()` /
|
|
30
|
+
* `response()` / `parameter()` / `requestBody()` / `header()` /
|
|
31
|
+
* `example()` / `security.add()` / `security.require()` / `tag()` /
|
|
32
|
+
* `server()`, each returning the builder for chaining. Terminal calls
|
|
33
|
+
* are `toJson()` (JSON document with referential integrity checked
|
|
26
34
|
* — every security-scheme reference must resolve), `toJsonString()`,
|
|
27
35
|
* `toYaml()`, and `middleware(opts)` which mounts the cached
|
|
28
36
|
* document at request-time. Security-scheme builders for bearer /
|
|
@@ -30,7 +38,7 @@
|
|
|
30
38
|
* `b.openapi.security`.
|
|
31
39
|
*
|
|
32
40
|
* @card
|
|
33
|
-
* OpenAPI 3.1 emitter from declarative route declarations + schemas (composable with `b.safeSchema`); JSON / YAML output.
|
|
41
|
+
* OpenAPI 3.1 / 3.2 emitter from declarative route declarations + schemas (composable with `b.safeSchema`); JSON / YAML output.
|
|
34
42
|
*/
|
|
35
43
|
|
|
36
44
|
var validateOpts = require("./validate-opts");
|
|
@@ -44,7 +52,33 @@ var audit = lazyRequire(function () { return require("./audit");
|
|
|
44
52
|
|
|
45
53
|
var OpenApiError = defineClass("OpenApiError", { alwaysPermanent: true });
|
|
46
54
|
|
|
47
|
-
|
|
55
|
+
// Default emitted version. `create({ openapi: "3.2.0" })` opts into 3.2.
|
|
56
|
+
// Both 3.1.x and 3.2.x are accepted by create() and parse() (OpenAPI
|
|
57
|
+
// 3.2 is a backward-compatible superset of 3.1).
|
|
58
|
+
var OPENAPI_VERSION = "3.1.0";
|
|
59
|
+
var SUPPORTED_MAJOR_MINOR = ["3.1", "3.2"];
|
|
60
|
+
|
|
61
|
+
// _resolveVersion — validate an operator-supplied `opts.openapi` version
|
|
62
|
+
// string against the accepted major.minor set and return it; default to
|
|
63
|
+
// OPENAPI_VERSION when omitted. THROWS on an unsupported version
|
|
64
|
+
// (config-time / entry-point tier — operator catches the typo at boot).
|
|
65
|
+
function _resolveVersion(version, label) {
|
|
66
|
+
if (version === undefined || version === null) return OPENAPI_VERSION;
|
|
67
|
+
if (typeof version !== "string" || version.length === 0) {
|
|
68
|
+
throw new OpenApiError("openapi/bad-version",
|
|
69
|
+
label + ": openapi must be a version string (e.g. \"3.1.0\" or \"3.2.0\")");
|
|
70
|
+
}
|
|
71
|
+
for (var i = 0; i < SUPPORTED_MAJOR_MINOR.length; i += 1) {
|
|
72
|
+
if (version.indexOf(SUPPORTED_MAJOR_MINOR[i] + ".") === 0 ||
|
|
73
|
+
version === SUPPORTED_MAJOR_MINOR[i]) {
|
|
74
|
+
return version;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
throw new OpenApiError("openapi/bad-version",
|
|
78
|
+
label + ": openapi version must be one of " +
|
|
79
|
+
SUPPORTED_MAJOR_MINOR.map(function (v) { return v + ".x"; }).join(" / ") +
|
|
80
|
+
" — got " + JSON.stringify(version));
|
|
81
|
+
}
|
|
48
82
|
|
|
49
83
|
/**
|
|
50
84
|
* @primitive b.openapi.create
|
|
@@ -52,23 +86,33 @@ var OPENAPI_VERSION = "3.1.0";
|
|
|
52
86
|
* @since 0.6.30
|
|
53
87
|
* @related b.openapi.parse, b.asyncapi.create, b.safeSchema
|
|
54
88
|
*
|
|
55
|
-
* Build a fluent OpenAPI 3.1 document builder. `opts.info` is
|
|
56
|
-
* (`title` + `version`). Returns a chainable builder; terminal
|
|
57
|
-
* are `toJson()`, `toJsonString(indent)`, `toYaml()`, and
|
|
89
|
+
* Build a fluent OpenAPI 3.1 / 3.2 document builder. `opts.info` is
|
|
90
|
+
* required (`title` + `version`). Returns a chainable builder; terminal
|
|
91
|
+
* calls are `toJson()`, `toJsonString(indent)`, `toYaml()`, and
|
|
58
92
|
* `middleware(opts)`. `toJson()` cross-checks every doc-level and
|
|
59
93
|
* per-operation security requirement against
|
|
60
94
|
* `components.securitySchemes` and throws
|
|
61
95
|
* `OpenApiError("openapi/dangling-security")` on a missing scheme.
|
|
62
96
|
*
|
|
97
|
+
* `3.1.0` is emitted by default. Pass `openapi: "3.2.0"` to opt into
|
|
98
|
+
* OpenAPI 3.2; an unsupported version (e.g. `"4.0.0"`) throws
|
|
99
|
+
* `OpenApiError("openapi/bad-version")`. `webhook(name, method, opts)`
|
|
100
|
+
* registers a top-level webhook (OpenAPI 3.2 §4.8.2) and
|
|
101
|
+
* `jsonSchemaDialect` declares the document's default JSON Schema
|
|
102
|
+
* dialect (OpenAPI 3.2 §4.8.1) — both valid in 3.1.x and 3.2.x.
|
|
103
|
+
*
|
|
63
104
|
* @opts
|
|
64
|
-
* info:
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
105
|
+
* info: { title, version, description?, contact?, license? }, // REQUIRED — title + version are non-empty strings
|
|
106
|
+
* openapi: string, // emitted version — "3.1.x" (default) or "3.2.x"
|
|
107
|
+
* jsonSchemaDialect: string, // default JSON Schema dialect URI for the document
|
|
108
|
+
* servers: array, // [{ url, description?, variables? }, ...]
|
|
109
|
+
* externalDocs: { url, description? },
|
|
110
|
+
* tags: array, // [{ name, description? }, ...] — seed; builder.tag() appends more
|
|
111
|
+
* security: array, // doc-level security requirements [{ schemeName: ["scope"] }, ...]
|
|
69
112
|
*
|
|
70
113
|
* @example
|
|
71
114
|
* var doc = b.openapi.create({
|
|
115
|
+
* openapi: "3.2.0",
|
|
72
116
|
* info: { title: "Acme API", version: "1.0.0" },
|
|
73
117
|
* servers: [{ url: "https://api.acme.example.com" }],
|
|
74
118
|
* });
|
|
@@ -79,14 +123,19 @@ var OPENAPI_VERSION = "3.1.0";
|
|
|
79
123
|
* responses: { "200": { description: "ok" }, "404": { description: "not found" } },
|
|
80
124
|
* security: [{ bearerAuth: [] }],
|
|
81
125
|
* });
|
|
126
|
+
* doc.webhook("newPet", "post", {
|
|
127
|
+
* requestBody: { content: { "application/json": { schema: { type: "object" } } } },
|
|
128
|
+
* responses: { "200": { description: "ack" } },
|
|
129
|
+
* });
|
|
82
130
|
* var json = doc.toJson();
|
|
83
|
-
* json.openapi; // → "3.
|
|
84
|
-
* json.
|
|
131
|
+
* json.openapi; // → "3.2.0"
|
|
132
|
+
* json.webhooks.newPet.post.responses["200"].description; // → "ack"
|
|
85
133
|
*/
|
|
86
134
|
function create(opts) {
|
|
87
135
|
opts = opts || {};
|
|
88
136
|
validateOpts(opts, [
|
|
89
|
-
"info", "
|
|
137
|
+
"info", "openapi", "jsonSchemaDialect",
|
|
138
|
+
"servers", "externalDocs", "tags", "security",
|
|
90
139
|
], "openapi.create");
|
|
91
140
|
if (!opts.info || typeof opts.info !== "object") {
|
|
92
141
|
throw new OpenApiError("openapi/bad-info",
|
|
@@ -97,7 +146,13 @@ function create(opts) {
|
|
|
97
146
|
validateOpts.requireNonEmptyString(opts.info.version,
|
|
98
147
|
"openapi.create: info.version", OpenApiError, "openapi/bad-info");
|
|
99
148
|
|
|
149
|
+
var openapiVersion = _resolveVersion(opts.openapi, "openapi.create");
|
|
150
|
+
var jsonSchemaDialect = validateOpts.optionalNonEmptyString(
|
|
151
|
+
opts.jsonSchemaDialect, "openapi.create: jsonSchemaDialect",
|
|
152
|
+
OpenApiError, "openapi/bad-json-schema-dialect");
|
|
153
|
+
|
|
100
154
|
var paths = new pathsBuilderMod.PathsBuilder();
|
|
155
|
+
var webhooks = new pathsBuilderMod.WebhooksBuilder();
|
|
101
156
|
var components = {
|
|
102
157
|
schemas: {},
|
|
103
158
|
responses: {},
|
|
@@ -124,6 +179,32 @@ function create(opts) {
|
|
|
124
179
|
_validateServerEntry(docServers[s], "openapi.create: servers[" + s + "]");
|
|
125
180
|
}
|
|
126
181
|
|
|
182
|
+
// _checkOperationSecurity — every per-operation security requirement
|
|
183
|
+
// key must resolve to a registered security scheme. `labelPrefix`
|
|
184
|
+
// distinguishes path operations ("") from webhook operations
|
|
185
|
+
// ("webhook ") in the error message.
|
|
186
|
+
function _checkOperationSecurity(itemMap, labelPrefix) {
|
|
187
|
+
for (var itemKey in itemMap) {
|
|
188
|
+
if (!Object.prototype.hasOwnProperty.call(itemMap, itemKey)) continue;
|
|
189
|
+
var item = itemMap[itemKey];
|
|
190
|
+
for (var methodKey in item) {
|
|
191
|
+
if (!Object.prototype.hasOwnProperty.call(item, methodKey)) continue;
|
|
192
|
+
var op = item[methodKey];
|
|
193
|
+
if (!Array.isArray(op.security)) continue;
|
|
194
|
+
for (var os = 0; os < op.security.length; os += 1) {
|
|
195
|
+
for (var sn in op.security[os]) {
|
|
196
|
+
if (!Object.prototype.hasOwnProperty.call(op.security[os], sn)) continue;
|
|
197
|
+
if (!components.securitySchemes[sn]) {
|
|
198
|
+
throw new OpenApiError("openapi/dangling-security",
|
|
199
|
+
"toJson: " + labelPrefix + methodKey.toUpperCase() + " " + itemKey +
|
|
200
|
+
" references undefined security scheme " + JSON.stringify(sn));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
127
208
|
var builder = {
|
|
128
209
|
info: Object.assign({}, opts.info),
|
|
129
210
|
|
|
@@ -132,6 +213,11 @@ function create(opts) {
|
|
|
132
213
|
return builder;
|
|
133
214
|
},
|
|
134
215
|
|
|
216
|
+
webhook: function (name, method, webhookOpts) {
|
|
217
|
+
webhooks.add(name, method, webhookOpts || {});
|
|
218
|
+
return builder;
|
|
219
|
+
},
|
|
220
|
+
|
|
135
221
|
schema: function (name, schemaSpec) {
|
|
136
222
|
validateOpts.requireNonEmptyString(name, "schema: name",
|
|
137
223
|
OpenApiError, "openapi/bad-component");
|
|
@@ -235,11 +321,13 @@ function create(opts) {
|
|
|
235
321
|
|
|
236
322
|
toJson: function () {
|
|
237
323
|
var doc = {
|
|
238
|
-
openapi:
|
|
324
|
+
openapi: openapiVersion,
|
|
239
325
|
info: builder.info,
|
|
240
326
|
};
|
|
327
|
+
if (jsonSchemaDialect) doc.jsonSchemaDialect = jsonSchemaDialect;
|
|
241
328
|
if (docServers.length > 0) doc.servers = docServers.slice();
|
|
242
329
|
doc.paths = paths.toMap();
|
|
330
|
+
if (webhooks.count() > 0) doc.webhooks = webhooks.toMap();
|
|
243
331
|
var anyComponent = false;
|
|
244
332
|
var componentsOut = {};
|
|
245
333
|
var keys = ["schemas", "responses", "parameters", "requestBodies",
|
|
@@ -267,27 +355,10 @@ function create(opts) {
|
|
|
267
355
|
}
|
|
268
356
|
}
|
|
269
357
|
}
|
|
270
|
-
// Same check on per-operation security
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
for (var methodKey in pathItem) {
|
|
275
|
-
if (!Object.prototype.hasOwnProperty.call(pathItem, methodKey)) continue;
|
|
276
|
-
var op = pathItem[methodKey];
|
|
277
|
-
if (Array.isArray(op.security)) {
|
|
278
|
-
for (var os = 0; os < op.security.length; os += 1) {
|
|
279
|
-
for (var sn in op.security[os]) {
|
|
280
|
-
if (!Object.prototype.hasOwnProperty.call(op.security[os], sn)) continue;
|
|
281
|
-
if (!components.securitySchemes[sn]) {
|
|
282
|
-
throw new OpenApiError("openapi/dangling-security",
|
|
283
|
-
"toJson: " + methodKey.toUpperCase() + " " + pathKey +
|
|
284
|
-
" references undefined security scheme " + JSON.stringify(sn));
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
358
|
+
// Same check on per-operation security — for both `paths` and
|
|
359
|
+
// `webhooks` operations (webhook operations carry `security` too).
|
|
360
|
+
_checkOperationSecurity(doc.paths, "");
|
|
361
|
+
if (doc.webhooks) _checkOperationSecurity(doc.webhooks, "webhook ");
|
|
291
362
|
try {
|
|
292
363
|
audit().safeEmit({
|
|
293
364
|
action: "openapi.document.built",
|
|
@@ -347,25 +418,100 @@ function create(opts) {
|
|
|
347
418
|
return builder;
|
|
348
419
|
}
|
|
349
420
|
|
|
421
|
+
var PARSE_METHODS = ["get", "put", "post", "delete",
|
|
422
|
+
"options", "head", "patch", "trace"];
|
|
423
|
+
|
|
424
|
+
// _validateItemOperations — validate the Operation Objects inside a
|
|
425
|
+
// single Path Item (used for both `paths` entries and `webhooks`
|
|
426
|
+
// entries). `label` is the operator-facing prefix (a path key like
|
|
427
|
+
// "/x" or a webhook label like "webhook newPet"). Pushes shape errors
|
|
428
|
+
// into `errors`; non-method fields (parameters / summary / $ref) are
|
|
429
|
+
// skipped exactly as the paths loop did before this was extracted.
|
|
430
|
+
function _validateItemOperations(item, label, errors, securitySchemes) {
|
|
431
|
+
for (var methodKey in item) {
|
|
432
|
+
if (!Object.prototype.hasOwnProperty.call(item, methodKey)) continue;
|
|
433
|
+
if (PARSE_METHODS.indexOf(methodKey) === -1) continue; // allow non-method fields like 'parameters', 'summary', '$ref'
|
|
434
|
+
var op = item[methodKey];
|
|
435
|
+
if (!op || typeof op !== "object") {
|
|
436
|
+
errors.push(methodKey.toUpperCase() + " " + label + ": operation must be an object");
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if (!op.responses || typeof op.responses !== "object" ||
|
|
440
|
+
Object.keys(op.responses).length === 0) {
|
|
441
|
+
errors.push(methodKey.toUpperCase() + " " + label +
|
|
442
|
+
": responses object required (per OpenAPI 3.1 §4.8.5)");
|
|
443
|
+
} else {
|
|
444
|
+
for (var statusKey in op.responses) {
|
|
445
|
+
if (!Object.prototype.hasOwnProperty.call(op.responses, statusKey)) continue;
|
|
446
|
+
var resp = op.responses[statusKey];
|
|
447
|
+
if (!resp || typeof resp !== "object") {
|
|
448
|
+
errors.push(methodKey.toUpperCase() + " " + label +
|
|
449
|
+
" response " + statusKey + ": must be an object");
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
if (resp["$ref"]) continue; // $ref short-circuit
|
|
453
|
+
if (typeof resp.description !== "string" || resp.description.length === 0) {
|
|
454
|
+
errors.push(methodKey.toUpperCase() + " " + label +
|
|
455
|
+
" response " + statusKey +
|
|
456
|
+
": description is required (per OpenAPI 3.1 §4.8.16)");
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (Array.isArray(op.parameters)) {
|
|
461
|
+
for (var pi = 0; pi < op.parameters.length; pi += 1) {
|
|
462
|
+
var p = op.parameters[pi];
|
|
463
|
+
if (!p || typeof p !== "object") continue;
|
|
464
|
+
if (p["$ref"]) continue;
|
|
465
|
+
if (p.in === "path" && p.required !== true) {
|
|
466
|
+
errors.push(methodKey.toUpperCase() + " " + label +
|
|
467
|
+
" parameters[" + pi + "]: path parameter " +
|
|
468
|
+
JSON.stringify(p.name) + " must have required=true");
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// Operation-level security requirement keys must resolve to a
|
|
473
|
+
// registered scheme — the builder's toJson() enforces this, so parse()
|
|
474
|
+
// must too, for both path and webhook operations.
|
|
475
|
+
if (securitySchemes && Array.isArray(op.security)) {
|
|
476
|
+
for (var rqi = 0; rqi < op.security.length; rqi += 1) {
|
|
477
|
+
var req = op.security[rqi];
|
|
478
|
+
if (!req || typeof req !== "object") continue;
|
|
479
|
+
for (var schemeKey in req) {
|
|
480
|
+
if (!Object.prototype.hasOwnProperty.call(req, schemeKey)) continue;
|
|
481
|
+
if (!securitySchemes[schemeKey]) {
|
|
482
|
+
errors.push(methodKey.toUpperCase() + " " + label +
|
|
483
|
+
": security references undefined scheme " +
|
|
484
|
+
JSON.stringify(schemeKey));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
350
492
|
/**
|
|
351
493
|
* @primitive b.openapi.parse
|
|
352
494
|
* @signature b.openapi.parse(jsonStringOrObject)
|
|
353
495
|
* @since 0.6.30
|
|
354
496
|
* @related b.openapi.create
|
|
355
497
|
*
|
|
356
|
-
* Parse + validate an external OpenAPI 3.1 document. Operators
|
|
357
|
-
* doc that arrived from a downstream integration (consumer hand-
|
|
498
|
+
* Parse + validate an external OpenAPI 3.1 / 3.2 document. Operators
|
|
499
|
+
* hand a doc that arrived from a downstream integration (consumer hand-
|
|
358
500
|
* edited, contract-test fixture, third-party publish) and want the
|
|
359
501
|
* framework's gate to enforce the same shape rules `toJson()`
|
|
360
502
|
* enforces on builder output. Throws on invalid JSON or non-object
|
|
361
503
|
* input; otherwise returns `{ doc, errors, valid }`. `errors` is an
|
|
362
|
-
* array of strings — empty on a valid document.
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
* doc-level security must
|
|
504
|
+
* array of strings — empty on a valid document. The `openapi` version
|
|
505
|
+
* must be `3.1.x` or `3.2.x`. Path keys must start with `/`, every
|
|
506
|
+
* operation must declare `responses` with a `description`, path
|
|
507
|
+
* parameters must carry `required: true`, and doc-level security must
|
|
508
|
+
* reference declared schemes. Top-level `webhooks` (OpenAPI 3.2
|
|
509
|
+
* §4.8.2) are validated with the same operation rules but free-form
|
|
510
|
+
* names instead of `/`-prefixed URL keys; `jsonSchemaDialect` (OpenAPI
|
|
511
|
+
* 3.2 §4.8.1) must be a string when present.
|
|
366
512
|
*
|
|
367
513
|
* @example
|
|
368
|
-
* var result = b.openapi.parse('{"openapi":"3.
|
|
514
|
+
* var result = b.openapi.parse('{"openapi":"3.2.0","info":{"title":"x","version":"1.0.0"}}');
|
|
369
515
|
* result.valid; // → true
|
|
370
516
|
* result.errors; // → []
|
|
371
517
|
*
|
|
@@ -389,9 +535,12 @@ function parse(jsonStringOrObject) {
|
|
|
389
535
|
}
|
|
390
536
|
var errors = [];
|
|
391
537
|
if (typeof doc.openapi !== "string") {
|
|
392
|
-
errors.push("missing or non-string `openapi` version field (must be 3.1.x)");
|
|
393
|
-
} else if (doc.openapi.indexOf("3.1") !== 0) {
|
|
394
|
-
errors.push("`openapi` version must be 3.1.x — got " + JSON.stringify(doc.openapi));
|
|
538
|
+
errors.push("missing or non-string `openapi` version field (must be 3.1.x or 3.2.x)");
|
|
539
|
+
} else if (doc.openapi.indexOf("3.1") !== 0 && doc.openapi.indexOf("3.2") !== 0) {
|
|
540
|
+
errors.push("`openapi` version must be 3.1.x or 3.2.x — got " + JSON.stringify(doc.openapi));
|
|
541
|
+
}
|
|
542
|
+
if (doc.jsonSchemaDialect != null && typeof doc.jsonSchemaDialect !== "string") {
|
|
543
|
+
errors.push("`jsonSchemaDialect` must be a string when present (per OpenAPI 3.2 §4.8.1)");
|
|
395
544
|
}
|
|
396
545
|
if (!doc.info || typeof doc.info !== "object") {
|
|
397
546
|
errors.push("missing or non-object `info`");
|
|
@@ -403,6 +552,9 @@ function parse(jsonStringOrObject) {
|
|
|
403
552
|
errors.push("info.version must be a non-empty string");
|
|
404
553
|
}
|
|
405
554
|
}
|
|
555
|
+
// Resolved once so operation-level security requirements on both paths
|
|
556
|
+
// and webhooks can be checked against it during shape validation.
|
|
557
|
+
var securitySchemes = (doc.components && doc.components.securitySchemes) || {};
|
|
406
558
|
if (doc.paths != null && typeof doc.paths !== "object") {
|
|
407
559
|
errors.push("`paths` must be an object when present");
|
|
408
560
|
} else if (doc.paths) {
|
|
@@ -416,54 +568,27 @@ function parse(jsonStringOrObject) {
|
|
|
416
568
|
errors.push("paths[" + JSON.stringify(pathKey) + "] must be an object");
|
|
417
569
|
continue;
|
|
418
570
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if (!Object.prototype.hasOwnProperty.call(op.responses, statusKey)) continue;
|
|
435
|
-
var resp = op.responses[statusKey];
|
|
436
|
-
if (!resp || typeof resp !== "object") {
|
|
437
|
-
errors.push(methodKey.toUpperCase() + " " + pathKey +
|
|
438
|
-
" response " + statusKey + ": must be an object");
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
if (resp["$ref"]) continue; // $ref short-circuit
|
|
442
|
-
if (typeof resp.description !== "string" || resp.description.length === 0) {
|
|
443
|
-
errors.push(methodKey.toUpperCase() + " " + pathKey +
|
|
444
|
-
" response " + statusKey +
|
|
445
|
-
": description is required (per OpenAPI 3.1 §4.8.16)");
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
if (Array.isArray(op.parameters)) {
|
|
450
|
-
for (var pi = 0; pi < op.parameters.length; pi += 1) {
|
|
451
|
-
var p = op.parameters[pi];
|
|
452
|
-
if (!p || typeof p !== "object") continue;
|
|
453
|
-
if (p["$ref"]) continue;
|
|
454
|
-
if (p.in === "path" && p.required !== true) {
|
|
455
|
-
errors.push(methodKey.toUpperCase() + " " + pathKey +
|
|
456
|
-
" parameters[" + pi + "]: path parameter " +
|
|
457
|
-
JSON.stringify(p.name) + " must have required=true");
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
571
|
+
_validateItemOperations(pathItem, pathKey, errors, securitySchemes);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
// Webhooks — same operation rules as paths, but keys are free-form
|
|
575
|
+
// webhook names (not `/`-prefixed URL templates), per OpenAPI 3.2
|
|
576
|
+
// §4.8.2.
|
|
577
|
+
if (doc.webhooks != null && typeof doc.webhooks !== "object") {
|
|
578
|
+
errors.push("`webhooks` must be an object when present");
|
|
579
|
+
} else if (doc.webhooks) {
|
|
580
|
+
for (var webhookKey in doc.webhooks) {
|
|
581
|
+
if (!Object.prototype.hasOwnProperty.call(doc.webhooks, webhookKey)) continue;
|
|
582
|
+
var webhookItem = doc.webhooks[webhookKey];
|
|
583
|
+
if (!webhookItem || typeof webhookItem !== "object") {
|
|
584
|
+
errors.push("webhooks[" + JSON.stringify(webhookKey) + "] must be an object");
|
|
585
|
+
continue;
|
|
461
586
|
}
|
|
587
|
+
_validateItemOperations(webhookItem, "webhook " + webhookKey, errors, securitySchemes);
|
|
462
588
|
}
|
|
463
589
|
}
|
|
464
590
|
// Dangling security references — every requirement key must resolve
|
|
465
591
|
// to a registered security scheme.
|
|
466
|
-
var securitySchemes = (doc.components && doc.components.securitySchemes) || {};
|
|
467
592
|
if (Array.isArray(doc.security)) {
|
|
468
593
|
for (var s = 0; s < doc.security.length; s += 1) {
|
|
469
594
|
for (var schemeName in doc.security[s]) {
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:c7c9f488-0e96-4295-af9d-359b64bbfce6",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-06-
|
|
8
|
+
"timestamp": "2026-06-02T19:28:34.781Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/core@0.14.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.14.19",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.14.
|
|
25
|
+
"version": "0.14.19",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "The Node framework that owns its stack.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/core@0.14.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.14.19",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/core@0.14.
|
|
57
|
+
"ref": "@blamejs/core@0.14.19",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|