@amritk/lint 0.5.3 → 0.6.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/AI.md +3 -3
- package/dist/rules/asyncapi/asyncapi.js +55 -1
- package/dist/rules/asyncapi/functions/asyncapi-headers-object.js +12 -9
- package/dist/rules/asyncapi/functions/asyncapi-message-examples.d.ts +10 -1
- package/dist/rules/asyncapi/functions/asyncapi-message-examples.js +15 -7
- package/dist/rules/asyncapi/functions/asyncapi-payload.d.ts +11 -1
- package/dist/rules/asyncapi/functions/asyncapi-payload.js +7 -5
- package/dist/rules/asyncapi/functions/asyncapi-schema-validation.d.ts +14 -3
- package/dist/rules/asyncapi/functions/asyncapi-schema-validation.js +11 -3
- package/dist/rules/asyncapi/functions/index.d.ts +3 -2
- package/dist/rules/asyncapi/functions/index.js +3 -1
- package/dist/rules/asyncapi/functions/multi-format-schema.d.ts +25 -0
- package/dist/rules/asyncapi/functions/multi-format-schema.js +5 -0
- package/package.json +2 -2
package/AI.md
CHANGED
|
@@ -30,7 +30,7 @@ import { lintDocument } from '@amritk/lint'
|
|
|
30
30
|
import { createAsyncApiRuleset } from '@amritk/lint/rules/asyncapi'
|
|
31
31
|
import { resolveRefs } from '@amritk/resolve-refs' // any resolver will do
|
|
32
32
|
|
|
33
|
-
// Recommended rules only:
|
|
33
|
+
// Recommended rules only: 54 of 62. For all 62, pass the `all` modifier:
|
|
34
34
|
// createAsyncApiRuleset({ extends: [['asyncapi', 'all']] })
|
|
35
35
|
const ruleset = createAsyncApiRuleset()
|
|
36
36
|
|
|
@@ -91,7 +91,7 @@ createAsyncApiRuleset({
|
|
|
91
91
|
7. **`createRuleset` is memoized** per `(definition object, basePath,
|
|
92
92
|
restrictTo)`. Mutating a definition you already passed in will not rebuild the
|
|
93
93
|
ruleset — pass a fresh object instead.
|
|
94
|
-
8. **Most AsyncAPI rules are gated per major** (11 of the
|
|
94
|
+
8. **Most AsyncAPI rules are gated per major** (11 of the 62 apply to both).
|
|
95
95
|
The 3.x-only rules are named with an `asyncapi-3-` prefix
|
|
96
96
|
(`asyncapi-3-operation-description`) and the 2.x ones are not, because 3.0
|
|
97
97
|
moved operations to the top level and tags under `info`. Re-severitying
|
|
@@ -133,6 +133,6 @@ createAsyncApiRuleset({
|
|
|
133
133
|
|---|---|
|
|
134
134
|
| `@amritk/lint` | core engine, `lintDocument`/`fixDocument`, built-in functions |
|
|
135
135
|
| `@amritk/lint/rules/openapi` | OpenAPI 2.0/3.0/3.1/3.2 preset — 66 rules, plus `oasFixers` |
|
|
136
|
-
| `@amritk/lint/rules/asyncapi` | AsyncAPI 2.0–2.6 / 3.0 preset —
|
|
136
|
+
| `@amritk/lint/rules/asyncapi` | AsyncAPI 2.0–2.6 / 3.0 preset — 62 rules, no fixers |
|
|
137
137
|
|
|
138
138
|
Install: `bun add @amritk/lint`.
|
|
@@ -369,6 +369,7 @@ const V3_MESSAGE_TRAITS = [
|
|
|
369
369
|
"$.components.messageTraits[*]"
|
|
370
370
|
];
|
|
371
371
|
const V3_ALL_MESSAGES = [...V3_MESSAGES, ...V3_MESSAGE_TRAITS];
|
|
372
|
+
const V3_PAYLOADS = V3_ALL_MESSAGES.map((given) => `${given}.payload`);
|
|
372
373
|
const v3Rules = {
|
|
373
374
|
"asyncapi-3-channel-no-empty-parameter": {
|
|
374
375
|
description: "Channel address must not have an empty parameter substitution pattern.",
|
|
@@ -424,6 +425,16 @@ const v3Rules = {
|
|
|
424
425
|
severity: "error",
|
|
425
426
|
then: { function: "asyncApiHeadersObject", functionOptions: { multiFormat: true } }
|
|
426
427
|
},
|
|
428
|
+
"asyncapi-3-message-examples": {
|
|
429
|
+
description: "Message examples must be valid against the payload and headers schemas.",
|
|
430
|
+
formats: ["aas3"],
|
|
431
|
+
// Messages only, for the same reason as the 2.x twin: the pass folds traits
|
|
432
|
+
// in and reports against whichever array the merge took, so matching each
|
|
433
|
+
// trait location as well printed the identical finding twice.
|
|
434
|
+
given: V3_MESSAGES,
|
|
435
|
+
severity: "error",
|
|
436
|
+
then: { function: "asyncApiMessageExamples", functionOptions: { multiFormat: true } }
|
|
437
|
+
},
|
|
427
438
|
"asyncapi-3-operation-description": {
|
|
428
439
|
description: "Operation must have a description.",
|
|
429
440
|
formats: ["aas3"],
|
|
@@ -438,10 +449,33 @@ const v3Rules = {
|
|
|
438
449
|
severity: "error",
|
|
439
450
|
then: { function: "asyncApiSecurity", functionOptions: { objectType: "Operation" } }
|
|
440
451
|
},
|
|
452
|
+
"asyncapi-3-payload": {
|
|
453
|
+
description: "Payloads must be valid against the AsyncAPI Schema object.",
|
|
454
|
+
formats: ["aas3"],
|
|
455
|
+
// Given the message rather than the payload, exactly as the 2.x twin is, so
|
|
456
|
+
// traits are folded in before the payload is read.
|
|
457
|
+
given: V3_ALL_MESSAGES,
|
|
458
|
+
severity: "error",
|
|
459
|
+
then: { function: "asyncApiPayload", functionOptions: { multiFormat: true } }
|
|
460
|
+
},
|
|
461
|
+
"asyncapi-3-payload-default": {
|
|
462
|
+
description: "Payload default must be valid against its schema.",
|
|
463
|
+
formats: ["aas3"],
|
|
464
|
+
given: V3_PAYLOADS,
|
|
465
|
+
severity: "error",
|
|
466
|
+
then: { function: "asyncApiSchemaValidation", functionOptions: { type: "default", multiFormat: true } }
|
|
467
|
+
},
|
|
468
|
+
"asyncapi-3-payload-examples": {
|
|
469
|
+
description: "Payload examples must be valid against their schema.",
|
|
470
|
+
formats: ["aas3"],
|
|
471
|
+
given: V3_PAYLOADS,
|
|
472
|
+
severity: "error",
|
|
473
|
+
then: { function: "asyncApiSchemaValidation", functionOptions: { type: "examples", multiFormat: true } }
|
|
474
|
+
},
|
|
441
475
|
"asyncapi-3-payload-unsupported-schemaFormat": {
|
|
442
476
|
description: "Message payload validation is only supported with an unspecified schemaFormat.",
|
|
443
477
|
formats: ["aas3"],
|
|
444
|
-
given:
|
|
478
|
+
given: V3_PAYLOADS,
|
|
445
479
|
severity: "info",
|
|
446
480
|
resolved: false,
|
|
447
481
|
then: {
|
|
@@ -450,6 +484,26 @@ const v3Rules = {
|
|
|
450
484
|
functionOptions: { match: "^application/vnd\\.aai\\.asyncapi([+;])" }
|
|
451
485
|
}
|
|
452
486
|
},
|
|
487
|
+
"asyncapi-3-schema-default": {
|
|
488
|
+
description: "Schema default must be valid against its schema.",
|
|
489
|
+
formats: ["aas3"],
|
|
490
|
+
// No parameter locations here: a 3.0 Channel Parameter Object has no
|
|
491
|
+
// `schema` at all — it carries `enum`, `default` and `examples` as plain
|
|
492
|
+
// strings — so `components.schemas` is the only place a reusable Schema
|
|
493
|
+
// Object is written. Each entry is a Multi Format Schema Object or a bare
|
|
494
|
+
// Schema Object, which is why this unwraps rather than reading `default`
|
|
495
|
+
// straight off the node.
|
|
496
|
+
given: "$.components.schemas[*]",
|
|
497
|
+
severity: "error",
|
|
498
|
+
then: { function: "asyncApiSchemaValidation", functionOptions: { type: "default", multiFormat: true } }
|
|
499
|
+
},
|
|
500
|
+
"asyncapi-3-schema-examples": {
|
|
501
|
+
description: "Schema examples must be valid against their schema.",
|
|
502
|
+
formats: ["aas3"],
|
|
503
|
+
given: "$.components.schemas[*]",
|
|
504
|
+
severity: "error",
|
|
505
|
+
then: { function: "asyncApiSchemaValidation", functionOptions: { type: "examples", multiFormat: true } }
|
|
506
|
+
},
|
|
453
507
|
"asyncapi-3-server-security": {
|
|
454
508
|
// 3.0 keeps `security` on the Server Object, in the same
|
|
455
509
|
// Reference-or-inline-scheme shape `asyncapi-3-operation-security` checks.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isObject } from "./helpers.js";
|
|
2
|
+
import { splitMultiFormatSchema } from "./multi-format-schema.js";
|
|
2
3
|
import { isAsyncApiSchemaFormat } from "./schema-format.js";
|
|
3
4
|
const MESSAGE = 'Headers schema type must be "object"';
|
|
4
5
|
const asyncApiHeadersObject = (headers, options, context) => {
|
|
@@ -8,15 +9,17 @@ const asyncApiHeadersObject = (headers, options, context) => {
|
|
|
8
9
|
return [];
|
|
9
10
|
if (typeof headers["$ref"] === "string")
|
|
10
11
|
return [];
|
|
11
|
-
if (options?.multiFormat === true
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
if (options?.multiFormat === true) {
|
|
13
|
+
const { schemaFormat, schema: inner, path } = splitMultiFormatSchema(headers);
|
|
14
|
+
if (path.length > 0) {
|
|
15
|
+
if (!isAsyncApiSchemaFormat(schemaFormat))
|
|
16
|
+
return [];
|
|
17
|
+
if (!isObject(inner))
|
|
18
|
+
return [{ message: MESSAGE, path: [...context.path, ...path] }];
|
|
19
|
+
if (typeof inner["$ref"] === "string" || inner["type"] === "object")
|
|
20
|
+
return [];
|
|
21
|
+
return [{ message: MESSAGE, path: [...context.path, ...path] }];
|
|
22
|
+
}
|
|
20
23
|
}
|
|
21
24
|
return headers["type"] === "object" ? [] : [{ message: MESSAGE, path: [...context.path] }];
|
|
22
25
|
};
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import type { RulesetFunction } from '../../../core/types.js';
|
|
2
|
+
/** Options for {@link asyncApiMessageExamples}. */
|
|
3
|
+
export type IAsyncApiMessageExamplesOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* Whether `payload` and `headers` may each be a Multi Format Schema Object
|
|
6
|
+
* (`{ schemaFormat, schema }`). That shape is 3.0 only, and it is also where
|
|
7
|
+
* that major states the schema language — 2.x states it once, on the message.
|
|
8
|
+
*/
|
|
9
|
+
multiFormat?: boolean;
|
|
10
|
+
};
|
|
2
11
|
/**
|
|
3
12
|
* Checks every entry of a Message Object's `examples` against the message's own
|
|
4
13
|
* `payload` and `headers` schemas. Traits are folded in first, so an example is
|
|
5
14
|
* judged against the message a tool would actually assemble rather than against
|
|
6
15
|
* the half of it written inline.
|
|
7
16
|
*/
|
|
8
|
-
export declare const asyncApiMessageExamples: RulesetFunction
|
|
17
|
+
export declare const asyncApiMessageExamples: RulesetFunction<unknown, IAsyncApiMessageExamplesOptions | undefined>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { schema as schemaFunction } from "../../../functions/index.js";
|
|
2
2
|
import { isObject, mergeTraits } from "./helpers.js";
|
|
3
|
+
import { splitMultiFormatSchema } from "./multi-format-schema.js";
|
|
3
4
|
import { isAsyncApiSchemaFormat } from "./schema-format.js";
|
|
4
5
|
const examplesOrigin = (message) => {
|
|
5
6
|
const traits = message["traits"];
|
|
@@ -12,7 +13,8 @@ const examplesOrigin = (message) => {
|
|
|
12
13
|
}
|
|
13
14
|
return ["examples"];
|
|
14
15
|
};
|
|
15
|
-
const
|
|
16
|
+
const PARTS = ["payload", "headers"];
|
|
17
|
+
const asyncApiMessageExamples = (input, options, context) => {
|
|
16
18
|
if (!isObject(input))
|
|
17
19
|
return [];
|
|
18
20
|
const message = mergeTraits(input);
|
|
@@ -20,17 +22,23 @@ const asyncApiMessageExamples = (input, _options, context) => {
|
|
|
20
22
|
if (!Array.isArray(examples))
|
|
21
23
|
return [];
|
|
22
24
|
const origin = examplesOrigin(input);
|
|
23
|
-
const
|
|
25
|
+
const schemaOf = (part) => {
|
|
26
|
+
if (options?.multiFormat !== true) {
|
|
27
|
+
return part === "headers" || isAsyncApiSchemaFormat(message["schemaFormat"]) ? message[part] : void 0;
|
|
28
|
+
}
|
|
29
|
+
const { schemaFormat, schema } = splitMultiFormatSchema(message[part]);
|
|
30
|
+
return isAsyncApiSchemaFormat(schemaFormat) ? schema : void 0;
|
|
31
|
+
};
|
|
32
|
+
const schemas = { payload: schemaOf("payload"), headers: schemaOf("headers") };
|
|
24
33
|
const results = [];
|
|
25
34
|
examples.forEach((example, index) => {
|
|
26
35
|
if (!isObject(example))
|
|
27
36
|
return;
|
|
28
|
-
for (const part of
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (part === "payload" && !payloadIsSchema)
|
|
37
|
+
for (const part of PARTS) {
|
|
38
|
+
const partSchema = schemas[part];
|
|
39
|
+
if (example[part] === void 0 || partSchema === void 0)
|
|
32
40
|
continue;
|
|
33
|
-
const findings = schemaFunction(example[part], { schema: isObject(
|
|
41
|
+
const findings = schemaFunction(example[part], { schema: isObject(partSchema) ? partSchema : {}, allErrors: true, skipUnusableSchema: true }, { ...context, path: [...context.path, ...origin, index, part] });
|
|
34
42
|
if (findings)
|
|
35
43
|
results.push(...findings);
|
|
36
44
|
}
|
|
@@ -11,4 +11,14 @@ import type { RulesetFunction } from '../../../core/types.js';
|
|
|
11
11
|
* payload came to be judged as JSON Schema and reported at error severity.
|
|
12
12
|
* `asyncapi-payload-unsupported-schemaFormat` reports those separately.
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
/** Options for {@link asyncApiPayload}. */
|
|
15
|
+
export type IAsyncApiPayloadOptions = {
|
|
16
|
+
/**
|
|
17
|
+
* Whether the payload may be a Multi Format Schema Object (`{ schemaFormat,
|
|
18
|
+
* schema }`). That shape is 3.0 only: the format that used to sit on the
|
|
19
|
+
* message now sits on the payload, so the gate reads a different place — and
|
|
20
|
+
* the schema to judge lives one level further down.
|
|
21
|
+
*/
|
|
22
|
+
multiFormat?: boolean;
|
|
23
|
+
};
|
|
24
|
+
export declare const asyncApiPayload: RulesetFunction<unknown, IAsyncApiPayloadOptions | undefined>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { schema as schemaFunction } from "../../../functions/index.js";
|
|
2
2
|
import { asyncApiSchemaVersion, loadAsyncApiSchema } from "../schemas/index.js";
|
|
3
3
|
import { isObject, mergeTraits } from "./helpers.js";
|
|
4
|
+
import { splitMultiFormatSchema } from "./multi-format-schema.js";
|
|
4
5
|
import { isAsyncApiSchemaFormat } from "./schema-format.js";
|
|
5
6
|
const payloadSchemaId = (version) => `http://asyncapi.com/definitions/${version}.0/schema.json`;
|
|
6
7
|
const payloadSchemas = /* @__PURE__ */ new Map();
|
|
@@ -13,19 +14,20 @@ const payloadSchema = (version) => {
|
|
|
13
14
|
}
|
|
14
15
|
return wrapper;
|
|
15
16
|
};
|
|
16
|
-
const asyncApiPayload = (message,
|
|
17
|
+
const asyncApiPayload = (message, options, context) => {
|
|
17
18
|
if (!isObject(message))
|
|
18
19
|
return [];
|
|
19
20
|
const merged = mergeTraits(message);
|
|
20
|
-
|
|
21
|
+
const written = merged["payload"];
|
|
22
|
+
if (written === void 0)
|
|
21
23
|
return [];
|
|
22
|
-
const payload = merged["
|
|
23
|
-
if (payload
|
|
24
|
+
const payload = options?.multiFormat === true ? splitMultiFormatSchema(written) : { schemaFormat: merged["schemaFormat"], schema: written, path: [] };
|
|
25
|
+
if (!isAsyncApiSchemaFormat(payload.schemaFormat))
|
|
24
26
|
return [];
|
|
25
27
|
const version = asyncApiSchemaVersion(isObject(context.document.data) ? context.document.data["asyncapi"] : void 0);
|
|
26
28
|
if (version === void 0)
|
|
27
29
|
return [];
|
|
28
|
-
return schemaFunction(payload, { schema: payloadSchema(version), allErrors: true }, { ...context, path: [...context.path, "payload"] }) ?? [];
|
|
30
|
+
return schemaFunction(payload.schema, { schema: payloadSchema(version), allErrors: true }, { ...context, path: [...context.path, "payload", ...payload.path] }) ?? [];
|
|
29
31
|
};
|
|
30
32
|
export {
|
|
31
33
|
asyncApiPayload
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import type { RulesetFunction } from '../../../core/types.js';
|
|
2
|
-
/** Options for {@link asyncApiSchemaValidation}: which sibling of the schema to check. */
|
|
2
|
+
/** Options for {@link asyncApiSchemaValidation}: which sibling of the schema to check, and how the schema is wrapped. */
|
|
3
3
|
export type IAsyncApiSchemaValidationOptions = {
|
|
4
4
|
type: 'default' | 'examples';
|
|
5
|
+
/**
|
|
6
|
+
* Whether the matched node may be a Multi Format Schema Object (`{
|
|
7
|
+
* schemaFormat, schema }`) rather than a bare Schema Object. That shape is 3.0
|
|
8
|
+
* only, where a payload — and a `components.schemas` entry — can be written in
|
|
9
|
+
* Avro or Protobuf. A `default` under one of those is not JSON Schema data and
|
|
10
|
+
* cannot be judged, so it is left to
|
|
11
|
+
* `asyncapi-3-payload-unsupported-schemaFormat` to mention the format at all.
|
|
12
|
+
*/
|
|
13
|
+
multiFormat?: boolean;
|
|
5
14
|
};
|
|
6
15
|
/**
|
|
7
16
|
* Validates a Schema Object's own `default` or `examples` against that same
|
|
8
|
-
* schema.
|
|
9
|
-
*
|
|
17
|
+
* schema. In 2.x the rule targets the schema through a `^` parent selector, so
|
|
18
|
+
* the input is the schema and the values under test sit inside it; in 3.0 the
|
|
19
|
+
* rule targets the payload (or `components.schemas` entry) directly, because the
|
|
20
|
+
* wrapper has to be unwrapped before either can be found.
|
|
10
21
|
*/
|
|
11
22
|
export declare const asyncApiSchemaValidation: RulesetFunction<unknown, IAsyncApiSchemaValidationOptions>;
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { schema as schemaFunction } from "../../../functions/index.js";
|
|
2
2
|
import { isObject } from "./helpers.js";
|
|
3
|
+
import { splitMultiFormatSchema } from "./multi-format-schema.js";
|
|
4
|
+
import { isAsyncApiSchemaFormat } from "./schema-format.js";
|
|
3
5
|
const asyncApiSchemaValidation = (input, options, context) => {
|
|
4
|
-
if (
|
|
6
|
+
if (options?.type === void 0)
|
|
5
7
|
return [];
|
|
6
|
-
const
|
|
8
|
+
const source = options.multiFormat === true ? splitMultiFormatSchema(input) : { schemaFormat: void 0, schema: input, path: [] };
|
|
9
|
+
if (!isAsyncApiSchemaFormat(source.schemaFormat))
|
|
10
|
+
return [];
|
|
11
|
+
const schema = source.schema;
|
|
12
|
+
if (!isObject(schema))
|
|
13
|
+
return [];
|
|
14
|
+
const targets = options.type === "default" ? Object.hasOwn(schema, "default") ? [{ path: ["default"], value: schema["default"] }] : [] : Array.isArray(schema["examples"]) ? schema["examples"].map((value, index) => ({ path: ["examples", index], value })) : [];
|
|
7
15
|
const results = [];
|
|
8
16
|
for (const target of targets) {
|
|
9
|
-
const findings = schemaFunction(target.value, { schema
|
|
17
|
+
const findings = schemaFunction(target.value, { schema, allErrors: true, skipUnusableSchema: true }, { ...context, path: [...context.path, ...source.path, ...target.path] });
|
|
10
18
|
if (findings)
|
|
11
19
|
results.push(...findings);
|
|
12
20
|
}
|
|
@@ -5,11 +5,12 @@ export { asyncApiChannelParameters } from './asyncapi-channel-parameters.js';
|
|
|
5
5
|
export { asyncApiChannelServers } from './asyncapi-channel-servers.js';
|
|
6
6
|
export { asyncApiDocumentSchema } from './asyncapi-document-schema.js';
|
|
7
7
|
export { asyncApiHeadersObject, type IAsyncApiHeadersOptions } from './asyncapi-headers-object.js';
|
|
8
|
-
export { asyncApiMessageExamples } from './asyncapi-message-examples.js';
|
|
8
|
+
export { asyncApiMessageExamples, type IAsyncApiMessageExamplesOptions } from './asyncapi-message-examples.js';
|
|
9
9
|
export { asyncApiMessageIdUnique } from './asyncapi-message-id-unique.js';
|
|
10
10
|
export { asyncApiOperationIdUnique } from './asyncapi-operation-id-unique.js';
|
|
11
|
-
export { asyncApiPayload } from './asyncapi-payload.js';
|
|
11
|
+
export { asyncApiPayload, type IAsyncApiPayloadOptions } from './asyncapi-payload.js';
|
|
12
12
|
export { asyncApiSchemaValidation, type IAsyncApiSchemaValidationOptions } from './asyncapi-schema-validation.js';
|
|
13
13
|
export { asyncApiSecurity, type IAsyncApiSecurityOptions } from './asyncapi-security.js';
|
|
14
|
+
export { type MultiFormatSchema, splitMultiFormatSchema } from './multi-format-schema.js';
|
|
14
15
|
/** The AsyncAPI-specific custom functions, keyed by name for ruleset `then` references. */
|
|
15
16
|
export declare const aasFunctions: FunctionRegistry;
|
|
@@ -22,6 +22,7 @@ import { asyncApiOperationIdUnique as asyncApiOperationIdUnique2 } from "./async
|
|
|
22
22
|
import { asyncApiPayload as asyncApiPayload2 } from "./asyncapi-payload.js";
|
|
23
23
|
import { asyncApiSchemaValidation as asyncApiSchemaValidation2 } from "./asyncapi-schema-validation.js";
|
|
24
24
|
import { asyncApiSecurity as asyncApiSecurity2 } from "./asyncapi-security.js";
|
|
25
|
+
import { splitMultiFormatSchema } from "./multi-format-schema.js";
|
|
25
26
|
const aasFunctions = {
|
|
26
27
|
aasServerVariables,
|
|
27
28
|
aasTagsUnique,
|
|
@@ -49,5 +50,6 @@ export {
|
|
|
49
50
|
asyncApiOperationIdUnique2 as asyncApiOperationIdUnique,
|
|
50
51
|
asyncApiPayload2 as asyncApiPayload,
|
|
51
52
|
asyncApiSchemaValidation2 as asyncApiSchemaValidation,
|
|
52
|
-
asyncApiSecurity2 as asyncApiSecurity
|
|
53
|
+
asyncApiSecurity2 as asyncApiSecurity,
|
|
54
|
+
splitMultiFormatSchema
|
|
53
55
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { JsonPath } from '../../../core/types.js';
|
|
2
|
+
/** An AsyncAPI 3.0 payload or headers node, split into the schema it holds and the language that schema is written in. */
|
|
3
|
+
export type MultiFormatSchema = {
|
|
4
|
+
/** The wrapper's `schemaFormat`, or `undefined` when the node is a bare Schema Object. */
|
|
5
|
+
schemaFormat: unknown;
|
|
6
|
+
/** The Schema Object itself: the wrapper's `schema`, or the whole node when it is bare. */
|
|
7
|
+
schema: unknown;
|
|
8
|
+
/** How to get from the matched node down to `schema` — `['schema']` when wrapped, empty when bare. */
|
|
9
|
+
path: JsonPath;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Splits an AsyncAPI 3.0 Multi Format Schema Object (`{ schemaFormat, schema }`)
|
|
13
|
+
* into its parts, passing a bare Schema Object through untouched.
|
|
14
|
+
*
|
|
15
|
+
* 3.0 moved `schemaFormat` off the message and onto the payload/headers wrapper,
|
|
16
|
+
* so this is where "is this even an AsyncAPI Schema Object?" gets answered for
|
|
17
|
+
* that major — the question 2.x answers once, on the message.
|
|
18
|
+
*
|
|
19
|
+
* An own `schema` key is what makes it a wrapper, which is exactly how the
|
|
20
|
+
* bundled 3.0 meta-schema decides (`anySchema.json`: `if: { required: ['schema']
|
|
21
|
+
* }`). `schemaFormat` is optional on the wrapper and defaults to the AsyncAPI
|
|
22
|
+
* dialect, so demanding it too would leave a plain `{ schema: … }` judged as a
|
|
23
|
+
* schema whose only keyword is one no dialect defines.
|
|
24
|
+
*/
|
|
25
|
+
export declare const splitMultiFormatSchema: (node: unknown) => MultiFormatSchema;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { isObject } from "./helpers.js";
|
|
2
|
+
const splitMultiFormatSchema = (node) => isObject(node) && Object.hasOwn(node, "schema") ? { schemaFormat: node["schemaFormat"], schema: node["schema"], path: ["schema"] } : { schemaFormat: void 0, schema: node, path: [] };
|
|
3
|
+
export {
|
|
4
|
+
splitMultiFormatSchema
|
|
5
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amritk/lint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "A fast, format-agnostic JSON/YAML style-guide linter with JSON Schema and custom rules.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@amritk/runtime-validators": "^0.
|
|
72
|
+
"@amritk/runtime-validators": "^0.14.0",
|
|
73
73
|
"@amritk/yaml": "^0.7.2",
|
|
74
74
|
"jsonc-parser": "^3.3.1"
|
|
75
75
|
},
|