@amritk/lint 0.2.0 → 0.3.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/README.md +18 -0
- package/dist/core/glob.d.ts +1 -1
- package/dist/core/glob.js +89 -5
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/jsonpath.d.ts +17 -1
- package/dist/core/jsonpath.js +218 -23
- package/dist/core/lint.d.ts +15 -8
- package/dist/core/lint.js +12 -3
- package/dist/core/plugin.d.ts +6 -0
- package/dist/core/plugin.js +6 -0
- package/dist/core/pointers.js +15 -15
- package/dist/core/ruleset.js +0 -0
- package/dist/core/runner.d.ts +6 -1
- package/dist/core/runner.js +127 -43
- package/dist/core/types.d.ts +17 -2
- package/dist/core/validate-ruleset.js +16 -0
- package/dist/fix/apply.d.ts +8 -2
- package/dist/fix/apply.js +68 -18
- package/dist/functions/alphabetical.js +40 -13
- package/dist/functions/casing.js +27 -5
- package/dist/functions/enumeration.d.ts +5 -3
- package/dist/functions/enumeration.js +18 -1
- package/dist/functions/index.d.ts +1 -0
- package/dist/functions/index.js +3 -0
- package/dist/functions/length.d.ts +13 -3
- package/dist/functions/length.js +11 -3
- package/dist/functions/or.d.ts +11 -0
- package/dist/functions/or.js +25 -0
- package/dist/functions/pattern.d.ts +5 -3
- package/dist/functions/pattern.js +42 -9
- package/dist/functions/schema.d.ts +13 -0
- package/dist/functions/schema.js +95 -2
- package/dist/functions/typed-enum.js +7 -1
- package/dist/functions/unreferenced-reusable-object.d.ts +7 -1
- package/dist/functions/unreferenced-reusable-object.js +18 -3
- package/dist/functions/xor.js +8 -1
- package/dist/index.js +9 -1
- package/dist/parsers/edit-model.d.ts +15 -0
- package/dist/parsers/edit-model.js +210 -41
- package/dist/parsers/types.d.ts +14 -2
- package/dist/parsers/yaml.d.ts +10 -0
- package/dist/parsers/yaml.js +174 -26
- package/dist/rules/openapi/fixers.js +63 -4
- package/dist/rules/openapi/formats.js +11 -4
- package/dist/rules/openapi/functions/example-validation.d.ts +16 -3
- package/dist/rules/openapi/functions/example-validation.js +102 -39
- package/dist/rules/openapi/functions/helpers.d.ts +1 -0
- package/dist/rules/openapi/functions/helpers.js +5 -0
- package/dist/rules/openapi/functions/index.d.ts +3 -1
- package/dist/rules/openapi/functions/index.js +7 -1
- package/dist/rules/openapi/functions/oas-additional-operations.js +5 -5
- package/dist/rules/openapi/functions/oas-example-external-value.d.ts +11 -0
- package/dist/rules/openapi/functions/oas-example-external-value.js +23 -0
- package/dist/rules/openapi/functions/oas-no-nullable.d.ts +13 -0
- package/dist/rules/openapi/functions/oas-no-nullable.js +22 -0
- package/dist/rules/openapi/functions/oas-op-id-unique.js +4 -2
- package/dist/rules/openapi/functions/oas-op-params.d.ts +7 -1
- package/dist/rules/openapi/functions/oas-op-params.js +35 -10
- package/dist/rules/openapi/functions/oas-op-security-defined.js +2 -2
- package/dist/rules/openapi/functions/oas-op-success-response.js +6 -1
- package/dist/rules/openapi/functions/oas-path-param.d.ts +10 -1
- package/dist/rules/openapi/functions/oas-path-param.js +87 -26
- package/dist/rules/openapi/functions/oas-server-variables.d.ts +6 -1
- package/dist/rules/openapi/functions/oas-server-variables.js +31 -2
- package/dist/rules/openapi/functions/oas-unused-component.js +14 -1
- package/dist/rules/openapi/oas.js +82 -25
- package/package.json +12 -4
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// `query` is a fixed operation field from OpenAPI 3.2 onward. Including it in the
|
|
2
|
+
// operation selectors is harmless on older versions (which never carry a `query`
|
|
3
|
+
// operation) and lets the operation-scoped rules cover 3.2 `query` operations.
|
|
4
|
+
const OPERATIONS = '$.paths[*][get,put,post,delete,options,head,patch,trace,query]';
|
|
5
|
+
const WEBHOOK_OPERATIONS = '$.webhooks[*][get,put,post,delete,options,head,patch,trace,query]';
|
|
3
6
|
const OPERATION_ID_URL_SAFE = "^[A-Za-z0-9-._~:/?#\\[\\]@!\\$&'()*+,;=]*$";
|
|
7
|
+
// Matches every Schema Object that carries an inline `example` or `default` and
|
|
8
|
+
// looks like a schema (it has a JSON Schema keyword), while skipping the
|
|
9
|
+
// `properties`/`patternProperties` *maps* — whose keys can coincidentally be
|
|
10
|
+
// named like schema keywords. A single filter given (rather than a `$..example^`
|
|
11
|
+
// / `$..default^` union) keeps each schema node matched exactly once, so the
|
|
12
|
+
// `oasSchemaExample` finding for a schema is never duplicated.
|
|
13
|
+
const SCHEMA_EXAMPLE_GIVEN = "$..[?(@property !== 'properties' && @property !== 'patternProperties' && @ && (@.example !== void 0 || @.default !== void 0) && (@.type || @.enum || @.format || @.$ref || @.properties || @.items || @.allOf || @.anyOf || @.oneOf))]";
|
|
4
14
|
/** Rules shared across OpenAPI v2 and v3. */
|
|
5
15
|
const sharedRules = {
|
|
6
16
|
'contact-properties': {
|
|
@@ -56,12 +66,13 @@ const sharedRules = {
|
|
|
56
66
|
},
|
|
57
67
|
'no-eval-in-markdown': {
|
|
58
68
|
description: 'Markdown descriptions must not contain "eval(".',
|
|
59
|
-
|
|
69
|
+
// Spectral scans both `description` and `title` markdown fields.
|
|
70
|
+
given: ['$..description', '$..title'],
|
|
60
71
|
then: { function: 'pattern', functionOptions: { notMatch: 'eval\\(' } },
|
|
61
72
|
},
|
|
62
73
|
'no-script-tags-in-markdown': {
|
|
63
74
|
description: 'Markdown descriptions must not contain <script> tags.',
|
|
64
|
-
given: '$..description',
|
|
75
|
+
given: ['$..description', '$..title'],
|
|
65
76
|
then: { function: 'pattern', functionOptions: { notMatch: '<script' } },
|
|
66
77
|
},
|
|
67
78
|
'openapi-tags': {
|
|
@@ -166,7 +177,12 @@ const sharedRules = {
|
|
|
166
177
|
then: { function: 'typedEnum' },
|
|
167
178
|
},
|
|
168
179
|
'array-items': {
|
|
169
|
-
|
|
180
|
+
// Not part of `spectral:oas` — a Loupe extension. `type: array` without
|
|
181
|
+
// `items` is valid in JSON Schema 2020-12 (OpenAPI 3.1/3.2), so this is gated
|
|
182
|
+
// to the versions where `items` is genuinely required (2.0 and 3.0.x) to
|
|
183
|
+
// avoid false positives on valid 3.1+ tuple/unconstrained-array schemas.
|
|
184
|
+
description: 'Schemas of type array must define items (OpenAPI 2.0 / 3.0 only; Loupe extension).',
|
|
185
|
+
formats: ['oas2', 'oas3_0'],
|
|
170
186
|
given: "$..[?(@ && @.type === 'array')]",
|
|
171
187
|
severity: 'error',
|
|
172
188
|
then: { field: 'items', function: 'defined' },
|
|
@@ -195,8 +211,15 @@ const oas2Rules = {
|
|
|
195
211
|
'oas2-api-schemes': {
|
|
196
212
|
description: 'OpenAPI schemes must be present and non-empty.',
|
|
197
213
|
formats: ['oas2'],
|
|
198
|
-
|
|
199
|
-
|
|
214
|
+
// Target the root and the `schemes` field so a *missing* `schemes` (not just
|
|
215
|
+
// an empty array) is reported — `given: '$.schemes'` matches nothing when the
|
|
216
|
+
// key is absent, so the rule never fired for a document with no schemes.
|
|
217
|
+
given: '$',
|
|
218
|
+
then: {
|
|
219
|
+
field: 'schemes',
|
|
220
|
+
function: 'schema',
|
|
221
|
+
functionOptions: { schema: { type: 'array', minItems: 1 } },
|
|
222
|
+
},
|
|
200
223
|
},
|
|
201
224
|
'oas2-discriminator': {
|
|
202
225
|
description: 'Discriminator must reference a required property.',
|
|
@@ -250,16 +273,18 @@ const oas2Rules = {
|
|
|
250
273
|
'oas2-valid-schema-example': {
|
|
251
274
|
description: 'Schema examples must be valid against their schema.',
|
|
252
275
|
formats: ['oas2'],
|
|
253
|
-
given:
|
|
276
|
+
given: SCHEMA_EXAMPLE_GIVEN,
|
|
254
277
|
severity: 'error',
|
|
255
278
|
then: { function: 'oasSchemaExample' },
|
|
256
279
|
},
|
|
257
280
|
'oas2-valid-media-example': {
|
|
258
281
|
description: 'Media type examples must be valid against their schema.',
|
|
259
282
|
formats: ['oas2'],
|
|
260
|
-
|
|
283
|
+
// In OpenAPI 2.0 examples live on the Response Object as a MIME-type → value
|
|
284
|
+
// map alongside a sibling `schema`, so target responses that have both.
|
|
285
|
+
given: '$..responses..[?(@ && @.schema && @.examples)]',
|
|
261
286
|
severity: 'error',
|
|
262
|
-
then: { function: 'oasMediaExample' },
|
|
287
|
+
then: { function: 'oasMediaExample', functionOptions: { oasVersion: 2 } },
|
|
263
288
|
},
|
|
264
289
|
'oas2-schema': {
|
|
265
290
|
description: 'Validate structure of OpenAPI v2 specification.',
|
|
@@ -276,14 +301,26 @@ const oas3Rules = {
|
|
|
276
301
|
'oas3-api-servers': {
|
|
277
302
|
description: 'OpenAPI 3 documents must have a non-empty servers array.',
|
|
278
303
|
formats: ['oas3'],
|
|
279
|
-
|
|
280
|
-
|
|
304
|
+
// Target the root and the `servers` field so a *missing* `servers` (not just
|
|
305
|
+
// an empty array) is reported — `given: '$.servers'` matches nothing when the
|
|
306
|
+
// key is absent, so the rule never fired for a document with no servers.
|
|
307
|
+
given: '$',
|
|
308
|
+
then: {
|
|
309
|
+
field: 'servers',
|
|
310
|
+
function: 'schema',
|
|
311
|
+
functionOptions: { schema: { type: 'array', minItems: 1, items: { type: 'object' } } },
|
|
312
|
+
},
|
|
281
313
|
},
|
|
282
314
|
'oas3-examples-value-or-externalValue': {
|
|
283
315
|
description: 'Example objects must use either value or externalValue, not both.',
|
|
284
316
|
formats: ['oas3'],
|
|
285
|
-
given: [
|
|
286
|
-
|
|
317
|
+
given: [
|
|
318
|
+
'$.components.examples[*]',
|
|
319
|
+
'$..content[*].examples[*]',
|
|
320
|
+
'$..parameters[*].examples[*]',
|
|
321
|
+
'$..headers[*].examples[*]',
|
|
322
|
+
],
|
|
323
|
+
then: { function: 'oasExampleExternalValue' },
|
|
287
324
|
},
|
|
288
325
|
'oas3-operation-security-defined': {
|
|
289
326
|
description: 'Operation security must reference defined securitySchemes.',
|
|
@@ -316,7 +353,16 @@ const oas3Rules = {
|
|
|
316
353
|
'oas3-server-variables': {
|
|
317
354
|
description: 'Server variables must be defined and used.',
|
|
318
355
|
formats: ['oas3'],
|
|
319
|
-
|
|
356
|
+
// Servers can appear at the root, on a path item, on an operation, and on a
|
|
357
|
+
// Link Object, so cover every location rather than just the root servers.
|
|
358
|
+
given: [
|
|
359
|
+
'$.servers[*]',
|
|
360
|
+
'$.paths[*].servers[*]',
|
|
361
|
+
'$.paths[*][*].servers[*]',
|
|
362
|
+
'$..links[*].server',
|
|
363
|
+
'$.webhooks[*].servers[*]',
|
|
364
|
+
'$.webhooks[*][*].servers[*]',
|
|
365
|
+
],
|
|
320
366
|
severity: 'error',
|
|
321
367
|
then: { function: 'oasServerVariables' },
|
|
322
368
|
},
|
|
@@ -337,16 +383,17 @@ const oas3Rules = {
|
|
|
337
383
|
'oas3-valid-schema-example': {
|
|
338
384
|
description: 'Schema examples must be valid against their schema.',
|
|
339
385
|
formats: ['oas3'],
|
|
340
|
-
given:
|
|
386
|
+
given: SCHEMA_EXAMPLE_GIVEN,
|
|
341
387
|
severity: 'error',
|
|
342
388
|
then: { function: 'oasSchemaExample' },
|
|
343
389
|
},
|
|
344
390
|
'oas3-valid-media-example': {
|
|
345
391
|
description: 'Media type examples must be valid against their schema.',
|
|
346
392
|
formats: ['oas3'],
|
|
347
|
-
|
|
393
|
+
// Spectral validates examples on media types, parameters, and headers.
|
|
394
|
+
given: ['$..content[*]', '$..parameters[*]', '$..headers[*]'],
|
|
348
395
|
severity: 'error',
|
|
349
|
-
then: { function: 'oasMediaExample' },
|
|
396
|
+
then: { function: 'oasMediaExample', functionOptions: { oasVersion: 3 } },
|
|
350
397
|
},
|
|
351
398
|
'oas3-schema': {
|
|
352
399
|
description: 'Validate structure of OpenAPI v3.0.x specification.',
|
|
@@ -362,24 +409,29 @@ const oas3Rules = {
|
|
|
362
409
|
const oas31Rules = {
|
|
363
410
|
'oas3_1-servers-in-webhook': {
|
|
364
411
|
description: 'Webhooks must not define servers.',
|
|
365
|
-
|
|
366
|
-
|
|
412
|
+
// Servers can sit on the webhook Path Item itself (`$.webhooks[*]`) as well as
|
|
413
|
+
// on each operation, so target both. Also applies to 3.2.
|
|
414
|
+
formats: ['oas3_1', 'oas3_2'],
|
|
415
|
+
given: [WEBHOOK_OPERATIONS, '$.webhooks[*]'],
|
|
367
416
|
then: { field: 'servers', function: 'falsy' },
|
|
368
417
|
},
|
|
369
418
|
'oas3_1-callbacks-in-webhook': {
|
|
370
419
|
description: 'Webhooks must not define callbacks.',
|
|
371
|
-
formats: ['oas3_1'],
|
|
420
|
+
formats: ['oas3_1', 'oas3_2'],
|
|
372
421
|
given: WEBHOOK_OPERATIONS,
|
|
373
422
|
then: { field: 'callbacks', function: 'falsy' },
|
|
374
423
|
},
|
|
375
424
|
'oas3_1-no-nullable': {
|
|
376
425
|
// `nullable` was removed in OpenAPI 3.1 (JSON Schema 2020-12 uses a `null`
|
|
377
426
|
// type instead) and stays gone in 3.2 — mirroring the oas2-anyOf/oneOf
|
|
378
|
-
// "feature not available in this version" rules.
|
|
427
|
+
// "feature not available in this version" rules. A custom function (targeting
|
|
428
|
+
// the parent of a `nullable` key) is used instead of `$..nullable` + `falsy`
|
|
429
|
+
// so a property literally named `nullable` is not flagged and `nullable: false`
|
|
430
|
+
// still is (so the migration fixer can drop it).
|
|
379
431
|
description: 'nullable is not available in OpenAPI 3.1 or later; use a "null" type instead.',
|
|
380
432
|
formats: ['oas3_1', 'oas3_2'],
|
|
381
|
-
given: '$..nullable',
|
|
382
|
-
then: { function: '
|
|
433
|
+
given: '$..nullable^',
|
|
434
|
+
then: { function: 'oasNoNullable' },
|
|
383
435
|
},
|
|
384
436
|
'oas3_1-license-identifier': {
|
|
385
437
|
// The License Object's `identifier` (SPDX) field was added in 3.1 and is
|
|
@@ -474,7 +526,12 @@ const oas32Rules = {
|
|
|
474
526
|
// 3.x-wide oas3-examples-value-or-externalValue rule.)
|
|
475
527
|
description: 'Example object dataValue/serializedValue must not be combined with value or externalValue.',
|
|
476
528
|
formats: ['oas3_2'],
|
|
477
|
-
given: [
|
|
529
|
+
given: [
|
|
530
|
+
'$.components.examples[*]',
|
|
531
|
+
'$..content[*].examples[*]',
|
|
532
|
+
'$..parameters[*].examples[*]',
|
|
533
|
+
'$..headers[*].examples[*]',
|
|
534
|
+
],
|
|
478
535
|
severity: 'error',
|
|
479
536
|
then: { function: 'oasExampleValue' },
|
|
480
537
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amritk/lint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A fast, format-agnostic JSON/YAML style-guide linter with JSON Schema and custom rules.",
|
|
5
5
|
"module": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,9 @@
|
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "tsgo -p tsconfig.build.json && tsc-alias -p tsconfig.build.json -f && node scripts/copy-schema-json.mjs",
|
|
37
37
|
"types:check": "tsgo -p . --noEmit",
|
|
38
|
-
"test": "NODE_ENV=production vitest run --root ../.. packages/lint/"
|
|
38
|
+
"test": "NODE_ENV=production vitest run --root ../.. packages/lint/",
|
|
39
|
+
"prebench": "bun run --filter='@amritk/runtime-validators' --filter='@amritk/yaml' --filter='@amritk/resolve-refs' build",
|
|
40
|
+
"bench": "bun run ./bench/run.ts"
|
|
39
41
|
},
|
|
40
42
|
"exports": {
|
|
41
43
|
"./package.json": "./package.json",
|
|
@@ -49,8 +51,14 @@
|
|
|
49
51
|
}
|
|
50
52
|
},
|
|
51
53
|
"dependencies": {
|
|
52
|
-
"@amritk/runtime-validators": "0.
|
|
53
|
-
"@amritk/yaml": "0.
|
|
54
|
+
"@amritk/runtime-validators": "0.7.0",
|
|
55
|
+
"@amritk/yaml": "0.3.0",
|
|
54
56
|
"jsonc-parser": "^3.3.1"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@amritk/resolve-refs": "0.4.0",
|
|
60
|
+
"@stoplight/spectral-core": "^1.23.1",
|
|
61
|
+
"@stoplight/spectral-parsers": "^1.0.5",
|
|
62
|
+
"@stoplight/spectral-rulesets": "^1.22.6"
|
|
55
63
|
}
|
|
56
64
|
}
|