@amritk/lint 0.3.2 → 0.4.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.
Files changed (119) hide show
  1. package/AI.md +55 -0
  2. package/dist/core/document.js +12 -10
  3. package/dist/core/formats.js +10 -13
  4. package/dist/core/glob.js +96 -118
  5. package/dist/core/index.d.ts +0 -1
  6. package/dist/core/index.js +30 -11
  7. package/dist/core/jsonpath.js +487 -561
  8. package/dist/core/lint.js +78 -85
  9. package/dist/core/plugin.js +21 -29
  10. package/dist/core/pointers.js +107 -151
  11. package/dist/core/ruleset.js +0 -0
  12. package/dist/core/runner.js +214 -272
  13. package/dist/core/types.js +4 -1
  14. package/dist/core/validate-ruleset.js +98 -112
  15. package/dist/fix/apply.d.ts +1 -1
  16. package/dist/fix/apply.js +58 -96
  17. package/dist/fix/index.js +7 -2
  18. package/dist/fix/plugin.js +15 -20
  19. package/dist/fix/types.d.ts +1 -1
  20. package/dist/functions/alphabetical.d.ts +1 -1
  21. package/dist/functions/alphabetical.js +39 -50
  22. package/dist/functions/casing.d.ts +1 -1
  23. package/dist/functions/casing.js +39 -45
  24. package/dist/functions/defined.d.ts +1 -1
  25. package/dist/functions/defined.js +7 -5
  26. package/dist/functions/enumeration.d.ts +1 -1
  27. package/dist/functions/enumeration.js +15 -25
  28. package/dist/functions/falsy.d.ts +1 -1
  29. package/dist/functions/falsy.js +7 -5
  30. package/dist/functions/index.d.ts +1 -1
  31. package/dist/functions/index.js +60 -44
  32. package/dist/functions/length.d.ts +1 -1
  33. package/dist/functions/length.js +22 -32
  34. package/dist/functions/or.d.ts +1 -1
  35. package/dist/functions/or.js +18 -24
  36. package/dist/functions/pattern.d.ts +1 -1
  37. package/dist/functions/pattern.js +39 -49
  38. package/dist/functions/schema.d.ts +1 -1
  39. package/dist/functions/schema.js +93 -119
  40. package/dist/functions/truthy.d.ts +1 -1
  41. package/dist/functions/truthy.js +7 -5
  42. package/dist/functions/typed-enum.d.ts +1 -1
  43. package/dist/functions/typed-enum.js +33 -36
  44. package/dist/functions/undefined.d.ts +1 -1
  45. package/dist/functions/undefined.js +7 -8
  46. package/dist/functions/unreferenced-reusable-object.d.ts +1 -1
  47. package/dist/functions/unreferenced-reusable-object.js +35 -47
  48. package/dist/functions/xor.d.ts +1 -1
  49. package/dist/functions/xor.js +13 -16
  50. package/dist/index.d.ts +17 -4
  51. package/dist/index.js +149 -164
  52. package/dist/parsers/edit-model.js +316 -444
  53. package/dist/parsers/index.d.ts +1 -1
  54. package/dist/parsers/index.js +23 -19
  55. package/dist/parsers/json.js +39 -37
  56. package/dist/parsers/lines.js +23 -26
  57. package/dist/parsers/types.js +9 -7
  58. package/dist/parsers/yaml.js +137 -204
  59. package/dist/rules/openapi/fixers.js +166 -221
  60. package/dist/rules/openapi/formats.js +26 -27
  61. package/dist/rules/openapi/functions/example-validation.d.ts +1 -1
  62. package/dist/rules/openapi/functions/example-validation.js +98 -138
  63. package/dist/rules/openapi/functions/helpers.js +8 -10
  64. package/dist/rules/openapi/functions/index.d.ts +1 -1
  65. package/dist/rules/openapi/functions/index.js +98 -72
  66. package/dist/rules/openapi/functions/oas-additional-operations.d.ts +1 -1
  67. package/dist/rules/openapi/functions/oas-additional-operations.js +16 -22
  68. package/dist/rules/openapi/functions/oas-discriminator.d.ts +1 -1
  69. package/dist/rules/openapi/functions/oas-discriminator.js +24 -22
  70. package/dist/rules/openapi/functions/oas-example-external-value.d.ts +1 -1
  71. package/dist/rules/openapi/functions/oas-example-external-value.js +13 -21
  72. package/dist/rules/openapi/functions/oas-example-value.d.ts +1 -1
  73. package/dist/rules/openapi/functions/oas-example-value.js +24 -27
  74. package/dist/rules/openapi/functions/oas-mutually-exclusive.d.ts +1 -1
  75. package/dist/rules/openapi/functions/oas-mutually-exclusive.js +15 -19
  76. package/dist/rules/openapi/functions/oas-no-nullable.d.ts +1 -1
  77. package/dist/rules/openapi/functions/oas-no-nullable.js +13 -21
  78. package/dist/rules/openapi/functions/oas-op-form-data-consume-check.d.ts +1 -1
  79. package/dist/rules/openapi/functions/oas-op-form-data-consume-check.js +21 -19
  80. package/dist/rules/openapi/functions/oas-op-id-unique.d.ts +1 -1
  81. package/dist/rules/openapi/functions/oas-op-id-unique.js +27 -27
  82. package/dist/rules/openapi/functions/oas-op-params.d.ts +1 -1
  83. package/dist/rules/openapi/functions/oas-op-params.js +36 -42
  84. package/dist/rules/openapi/functions/oas-op-security-defined.d.ts +1 -1
  85. package/dist/rules/openapi/functions/oas-op-security-defined.js +40 -39
  86. package/dist/rules/openapi/functions/oas-op-success-response.d.ts +1 -1
  87. package/dist/rules/openapi/functions/oas-op-success-response.js +11 -13
  88. package/dist/rules/openapi/functions/oas-path-param.d.ts +1 -1
  89. package/dist/rules/openapi/functions/oas-path-param.js +75 -96
  90. package/dist/rules/openapi/functions/oas-schema-example-deprecated.d.ts +1 -1
  91. package/dist/rules/openapi/functions/oas-schema-example-deprecated.js +33 -40
  92. package/dist/rules/openapi/functions/oas-schema.d.ts +1 -1
  93. package/dist/rules/openapi/functions/oas-schema.js +9 -14
  94. package/dist/rules/openapi/functions/oas-server-name-unique.d.ts +1 -1
  95. package/dist/rules/openapi/functions/oas-server-name-unique.js +21 -19
  96. package/dist/rules/openapi/functions/oas-server-variables.d.ts +1 -1
  97. package/dist/rules/openapi/functions/oas-server-variables.js +45 -49
  98. package/dist/rules/openapi/functions/oas-tag-defined.d.ts +1 -1
  99. package/dist/rules/openapi/functions/oas-tag-defined.js +20 -20
  100. package/dist/rules/openapi/functions/oas-tag-kind.d.ts +1 -1
  101. package/dist/rules/openapi/functions/oas-tag-kind.js +16 -16
  102. package/dist/rules/openapi/functions/oas-tag-parent-defined.d.ts +1 -1
  103. package/dist/rules/openapi/functions/oas-tag-parent-defined.js +40 -43
  104. package/dist/rules/openapi/functions/oas-tags-unique.d.ts +1 -1
  105. package/dist/rules/openapi/functions/oas-tags-unique.js +18 -16
  106. package/dist/rules/openapi/functions/oas-unused-component.d.ts +1 -1
  107. package/dist/rules/openapi/functions/oas-unused-component.js +48 -58
  108. package/dist/rules/openapi/functions/ref-siblings.d.ts +1 -1
  109. package/dist/rules/openapi/functions/ref-siblings.js +13 -11
  110. package/dist/rules/openapi/index.d.ts +2 -1
  111. package/dist/rules/openapi/index.js +99 -117
  112. package/dist/rules/openapi/oas.d.ts +1 -1
  113. package/dist/rules/openapi/oas.js +524 -536
  114. package/dist/rules/openapi/schemas/index.js +17 -33
  115. package/dist/rules/openapi/schemas/oas20.json +1 -1592
  116. package/dist/rules/openapi/schemas/oas30.json +1 -1651
  117. package/dist/rules/openapi/schemas/oas31.json +1 -1412
  118. package/dist/rules/openapi/schemas/oas32.json +1 -1684
  119. package/package.json +11 -6
@@ -1,129 +1,103 @@
1
- import { validate as buildValidator } from '@amritk/runtime-validators';
2
- // The rule's `options.schema` is a stable object, so cache the prepared
3
- // validator by identity. `@amritk/runtime-validators` interprets the schema at
4
- // runtime (no `new Function`), keeping this eval-free and dependency-light.
5
- const validators = new WeakMap();
6
- const getValidator = (schema) => {
7
- let validator = validators.get(schema);
8
- if (!validator) {
9
- // The `schema` built-in historically enforced string formats (ajv
10
- // `validateFormats: true`); keep that by opting into all formats here.
11
- validator = buildValidator(schema, { formats: 'all' });
12
- validators.set(schema, validator);
13
- }
14
- return validator;
1
+ import { validate as buildValidator } from "@amritk/runtime-validators";
2
+ const validators = /* @__PURE__ */ new WeakMap();
3
+ const getValidator = (schema2) => {
4
+ let validator = validators.get(schema2);
5
+ if (!validator) {
6
+ validator = buildValidator(schema2, { formats: "all" });
7
+ validators.set(schema2, validator);
8
+ }
9
+ return validator;
15
10
  };
16
- const KNOWN_TYPES = new Set(['string', 'number', 'integer', 'boolean', 'null', 'object', 'array']);
17
- // Keywords whose value is itself a schema, a list of schemas, or a map of
18
- // schemas. We recurse through them to reach every `type` a schema declares.
19
- const SCHEMA_VALUE_KEYS = ['additionalProperties', 'not', 'if', 'then', 'else', 'propertyNames', 'contains'];
20
- const SCHEMA_ITEMS_KEYS = ['items', 'additionalItems'];
21
- const SCHEMA_LIST_KEYS = ['allOf', 'anyOf', 'oneOf'];
22
- const SCHEMA_MAP_KEYS = ['properties', 'patternProperties', 'definitions', '$defs', 'dependencies'];
23
- /**
24
- * Walks a schema looking for a `type` keyword whose value is not a JSON Schema
25
- * type. `@amritk/runtime-validators` treats an unknown `type` as "always
26
- * matches" (so it never rejects data it does not model), which means a typo like
27
- * `type: "Pascal"` would silently disable the rule. Finding it up front lets us
28
- * report it instead. Returns the offending type, or `undefined` when the schema
29
- * only uses known types.
30
- */
11
+ const KNOWN_TYPES = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean", "null", "object", "array"]);
12
+ const SCHEMA_VALUE_KEYS = ["additionalProperties", "not", "if", "then", "else", "propertyNames", "contains"];
13
+ const SCHEMA_ITEMS_KEYS = ["items", "additionalItems"];
14
+ const SCHEMA_LIST_KEYS = ["allOf", "anyOf", "oneOf"];
15
+ const SCHEMA_MAP_KEYS = ["properties", "patternProperties", "definitions", "$defs", "dependencies"];
31
16
  const findInvalidType = (node) => {
32
- if (Array.isArray(node) || typeof node !== 'object' || node === null)
33
- return undefined;
34
- const schema = node;
35
- const declared = schema['type'];
36
- if (declared !== undefined) {
37
- const types = Array.isArray(declared) ? declared : [declared];
38
- for (const type of types) {
39
- if (typeof type !== 'string' || !KNOWN_TYPES.has(type)) {
40
- return typeof type === 'string' ? type : String(type);
41
- }
42
- }
43
- }
44
- for (const key of SCHEMA_VALUE_KEYS) {
45
- const found = findInvalidType(schema[key]);
46
- if (found !== undefined)
47
- return found;
17
+ if (Array.isArray(node) || typeof node !== "object" || node === null)
18
+ return void 0;
19
+ const schema2 = node;
20
+ const declared = schema2["type"];
21
+ if (declared !== void 0) {
22
+ const types = Array.isArray(declared) ? declared : [declared];
23
+ for (const type of types) {
24
+ if (typeof type !== "string" || !KNOWN_TYPES.has(type)) {
25
+ return typeof type === "string" ? type : String(type);
26
+ }
48
27
  }
49
- for (const key of SCHEMA_ITEMS_KEYS) {
50
- const value = schema[key];
51
- const list = Array.isArray(value) ? value : [value];
52
- for (const item of list) {
53
- const found = findInvalidType(item);
54
- if (found !== undefined)
55
- return found;
56
- }
28
+ }
29
+ for (const key of SCHEMA_VALUE_KEYS) {
30
+ const found = findInvalidType(schema2[key]);
31
+ if (found !== void 0)
32
+ return found;
33
+ }
34
+ for (const key of SCHEMA_ITEMS_KEYS) {
35
+ const value = schema2[key];
36
+ const list = Array.isArray(value) ? value : [value];
37
+ for (const item of list) {
38
+ const found = findInvalidType(item);
39
+ if (found !== void 0)
40
+ return found;
57
41
  }
58
- for (const key of SCHEMA_LIST_KEYS) {
59
- const value = schema[key];
60
- if (Array.isArray(value)) {
61
- for (const item of value) {
62
- const found = findInvalidType(item);
63
- if (found !== undefined)
64
- return found;
65
- }
66
- }
42
+ }
43
+ for (const key of SCHEMA_LIST_KEYS) {
44
+ const value = schema2[key];
45
+ if (Array.isArray(value)) {
46
+ for (const item of value) {
47
+ const found = findInvalidType(item);
48
+ if (found !== void 0)
49
+ return found;
50
+ }
67
51
  }
68
- for (const key of SCHEMA_MAP_KEYS) {
69
- const value = schema[key];
70
- if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
71
- for (const item of Object.values(value)) {
72
- const found = findInvalidType(item);
73
- if (found !== undefined)
74
- return found;
75
- }
76
- }
52
+ }
53
+ for (const key of SCHEMA_MAP_KEYS) {
54
+ const value = schema2[key];
55
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
56
+ for (const item of Object.values(value)) {
57
+ const found = findInvalidType(item);
58
+ if (found !== void 0)
59
+ return found;
60
+ }
77
61
  }
78
- return undefined;
62
+ }
63
+ return void 0;
79
64
  };
80
- const pointerToPath = (pointer) => pointer
81
- .split('/')
82
- .slice(1)
83
- .map((segment) => segment.replace(/~1/g, '/').replace(/~0/g, '~'))
84
- .map((segment) => (/^\d+$/.test(segment) ? Number(segment) : segment));
65
+ const pointerToPath = (pointer) => pointer.split("/").slice(1).map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~")).map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
85
66
  const formatError = (error) => {
86
- const location = error.path || 'value';
87
- return `${location} ${error.message}`.trim();
67
+ const location = error.path || "value";
68
+ return `${location} ${error.message}`.trim();
88
69
  };
89
- /** Validates a value against a JSON Schema supplied in the rule's options. */
90
- export const schema = (input, options, context) => {
91
- if (!options?.schema)
92
- return [];
93
- // A malformed schema would otherwise validate everything and silently disable
94
- // the rule. Surface it as a finding, the way Spectral reports schema compile
95
- // errors as results, so the ruleset author notices the mistake.
96
- const invalidType = findInvalidType(options.schema);
97
- if (invalidType !== undefined) {
98
- return [
99
- {
100
- message: `Invalid schema: unknown type "${invalidType}". Valid types are: ${[...KNOWN_TYPES].join(', ')}`,
101
- path: [...context.path],
102
- },
103
- ];
104
- }
105
- let result;
106
- try {
107
- result = getValidator(options.schema)(input);
108
- }
109
- catch (error) {
110
- // Preparing or running the validator can throw on a schema shape we cannot
111
- // interpret. Report it rather than letting it bubble up and crash the run.
112
- return [
113
- {
114
- message: `Invalid schema: ${error instanceof Error ? error.message : String(error)}`,
115
- path: [...context.path],
116
- },
117
- ];
118
- }
119
- if (result === true)
120
- return [];
121
- // Spectral's ajv defaults to `allErrors: false`, reporting only the first
122
- // failure. Honor the same default and only expand to the full list when the
123
- // caller opts in.
124
- const errors = options.allErrors ? result.errors : result.errors.slice(0, 1);
125
- return errors.map((error) => ({
126
- message: formatError(error),
127
- path: [...context.path, ...pointerToPath(error.path)],
128
- }));
70
+ const schema = (input, options, context) => {
71
+ if (!options?.schema)
72
+ return [];
73
+ const invalidType = findInvalidType(options.schema);
74
+ if (invalidType !== void 0) {
75
+ return [
76
+ {
77
+ message: `Invalid schema: unknown type "${invalidType}". Valid types are: ${[...KNOWN_TYPES].join(", ")}`,
78
+ path: [...context.path]
79
+ }
80
+ ];
81
+ }
82
+ let result;
83
+ try {
84
+ result = getValidator(options.schema)(input);
85
+ } catch (error) {
86
+ return [
87
+ {
88
+ message: `Invalid schema: ${error instanceof Error ? error.message : String(error)}`,
89
+ path: [...context.path]
90
+ }
91
+ ];
92
+ }
93
+ if (result === true)
94
+ return [];
95
+ const errors = options.allErrors ? result.errors : result.errors.slice(0, 1);
96
+ return errors.map((error) => ({
97
+ message: formatError(error),
98
+ path: [...context.path, ...pointerToPath(error.path)]
99
+ }));
100
+ };
101
+ export {
102
+ schema
129
103
  };
@@ -1,3 +1,3 @@
1
- import type { RulesetFunction } from '../core/index.js';
1
+ import type { RulesetFunction } from '../core/types.js';
2
2
  /** Flags a value that is not truthy. */
3
3
  export declare const truthy: RulesetFunction;
@@ -1,6 +1,8 @@
1
- /** Flags a value that is not truthy. */
2
- export const truthy = (input) => {
3
- if (!input)
4
- return [{ message: 'The value must be truthy' }];
5
- return [];
1
+ const truthy = (input) => {
2
+ if (!input)
3
+ return [{ message: "The value must be truthy" }];
4
+ return [];
5
+ };
6
+ export {
7
+ truthy
6
8
  };
@@ -1,3 +1,3 @@
1
- import type { RulesetFunction } from '../core/index.js';
1
+ import type { RulesetFunction } from '../core/types.js';
2
2
  /** Validates that each `enum` entry matches the schema's declared `type`. */
3
3
  export declare const typedEnum: RulesetFunction<Record<string, unknown>, never>;
@@ -1,40 +1,37 @@
1
1
  const JS_TYPES = {
2
- string: (v) => typeof v === 'string',
3
- number: (v) => typeof v === 'number',
4
- integer: (v) => typeof v === 'number' && Number.isInteger(v),
5
- boolean: (v) => typeof v === 'boolean',
6
- null: (v) => v === null,
7
- array: (v) => Array.isArray(v),
8
- object: (v) => typeof v === 'object' && v !== null && !Array.isArray(v),
2
+ string: (v) => typeof v === "string",
3
+ number: (v) => typeof v === "number",
4
+ integer: (v) => typeof v === "number" && Number.isInteger(v),
5
+ boolean: (v) => typeof v === "boolean",
6
+ null: (v) => v === null,
7
+ array: (v) => Array.isArray(v),
8
+ object: (v) => typeof v === "object" && v !== null && !Array.isArray(v)
9
9
  };
10
- /** Validates that each `enum` entry matches the schema's declared `type`. */
11
- export const typedEnum = (input, _options, context) => {
12
- if (typeof input !== 'object' || input === null)
13
- return [];
14
- const declaredType = input['type'];
15
- const values = input['enum'];
16
- if (declaredType === undefined || !Array.isArray(values))
17
- return [];
18
- const types = Array.isArray(declaredType) ? [...declaredType] : [declaredType];
19
- // A schema marked nullable (OpenAPI 3 `nullable` or the Swagger 2 `x-nullable`
20
- // vendor extension) is allowed to hold `null` in addition to its declared
21
- // type, so `null` must not be flagged as a type mismatch.
22
- if ((input['nullable'] === true || input['x-nullable'] === true) && !types.includes('null')) {
23
- types.push('null');
10
+ const typedEnum = (input, _options, context) => {
11
+ if (typeof input !== "object" || input === null)
12
+ return [];
13
+ const declaredType = input["type"];
14
+ const values = input["enum"];
15
+ if (declaredType === void 0 || !Array.isArray(values))
16
+ return [];
17
+ const types = Array.isArray(declaredType) ? [...declaredType] : [declaredType];
18
+ if ((input["nullable"] === true || input["x-nullable"] === true) && !types.includes("null")) {
19
+ types.push("null");
20
+ }
21
+ const checkers = types.map((type) => JS_TYPES[String(type)]).filter((fn) => Boolean(fn));
22
+ if (checkers.length === 0)
23
+ return [];
24
+ const results = [];
25
+ values.forEach((value, index) => {
26
+ if (!checkers.some((check) => check(value))) {
27
+ results.push({
28
+ message: `Enum value \`${JSON.stringify(value)}\` must be of type "${types.join(" | ")}"`,
29
+ path: [...context.path, "enum", index]
30
+ });
24
31
  }
25
- const checkers = types
26
- .map((type) => JS_TYPES[String(type)])
27
- .filter((fn) => Boolean(fn));
28
- if (checkers.length === 0)
29
- return [];
30
- const results = [];
31
- values.forEach((value, index) => {
32
- if (!checkers.some((check) => check(value))) {
33
- results.push({
34
- message: `Enum value \`${JSON.stringify(value)}\` must be of type "${types.join(' | ')}"`,
35
- path: [...context.path, 'enum', index],
36
- });
37
- }
38
- });
39
- return results;
32
+ });
33
+ return results;
34
+ };
35
+ export {
36
+ typedEnum
40
37
  };
@@ -1,4 +1,4 @@
1
- import type { RulesetFunction } from '../core/index.js';
1
+ import type { RulesetFunction } from '../core/types.js';
2
2
  /**
3
3
  * Flags a value that is defined. Exported as `undefinedFn` because `undefined`
4
4
  * is a reserved identifier; it is registered under the name `undefined`.
@@ -1,9 +1,8 @@
1
- /**
2
- * Flags a value that is defined. Exported as `undefinedFn` because `undefined`
3
- * is a reserved identifier; it is registered under the name `undefined`.
4
- */
5
- export const undefinedFn = (input) => {
6
- if (input !== undefined)
7
- return [{ message: 'The value must be undefined' }];
8
- return [];
1
+ const undefinedFn = (input) => {
2
+ if (input !== void 0)
3
+ return [{ message: "The value must be undefined" }];
4
+ return [];
5
+ };
6
+ export {
7
+ undefinedFn
9
8
  };
@@ -1,4 +1,4 @@
1
- import type { RulesetFunction } from '../core/index.js';
1
+ import type { RulesetFunction } from '../core/types.js';
2
2
  /** Options for {@link unreferencedReusableObject}. */
3
3
  export type IUnreferencedReusableObjectOptions = {
4
4
  /** JSON pointer to the map of reusable objects, e.g. "#/components/schemas". */
@@ -1,52 +1,40 @@
1
- /** Collects every `$ref` string anywhere in `node` into `into`. */
2
1
  const collectRefs = (node, into) => {
3
- if (Array.isArray(node)) {
4
- for (const item of node)
5
- collectRefs(item, into);
6
- return;
7
- }
8
- if (typeof node === 'object' && node !== null) {
9
- for (const [key, value] of Object.entries(node)) {
10
- if (key === '$ref' && typeof value === 'string')
11
- into.add(value);
12
- else
13
- collectRefs(value, into);
14
- }
2
+ if (Array.isArray(node)) {
3
+ for (const item of node)
4
+ collectRefs(item, into);
5
+ return;
6
+ }
7
+ if (typeof node === "object" && node !== null) {
8
+ for (const [key, value] of Object.entries(node)) {
9
+ if (key === "$ref" && typeof value === "string")
10
+ into.add(value);
11
+ else
12
+ collectRefs(value, into);
15
13
  }
14
+ }
16
15
  };
17
- /** Escapes a key for use in a JSON pointer segment (`~` -> `~0`, `/` -> `~1`). */
18
- const escapePointerSegment = (key) => key.replace(/~/g, '~0').replace(/\//g, '~1');
19
- /**
20
- * Flags entries in a reusable-object map that nothing `$ref`s.
21
- *
22
- * This must run against the *unresolved* document: once `$ref`s are inlined by a
23
- * resolver there are no references left to count, so every reusable object would
24
- * look orphaned.
25
- */
26
- export const unreferencedReusableObject = (input, options, context) => {
27
- if (typeof input !== 'object' || input === null)
28
- return [];
29
- const location = options?.reusableObjectsLocation;
30
- if (!location)
31
- return [];
32
- const refs = new Set();
33
- collectRefs(context.document.data, refs);
34
- const results = [];
35
- for (const key of Object.keys(input)) {
36
- // A key such as "a/b" appears in a pointer as "a~1b", so escape it before
37
- // building the expected reference. Without this a legitimately referenced
38
- // object with a special character in its name looks unreferenced.
39
- const base = `${location}/${escapePointerSegment(key)}`;
40
- // A reference can point straight at the object (`base`) or deeper into it
41
- // (e.g. `base/properties/x`); either counts as a use, so match the exact
42
- // pointer or any pointer nested beneath it.
43
- const referenced = refs.has(base) || [...refs].some((ref) => ref.startsWith(`${base}/`));
44
- if (!referenced) {
45
- results.push({
46
- message: 'This reusable object is never referenced',
47
- path: [...context.path, key],
48
- });
49
- }
16
+ const escapePointerSegment = (key) => key.replace(/~/g, "~0").replace(/\//g, "~1");
17
+ const unreferencedReusableObject = (input, options, context) => {
18
+ if (typeof input !== "object" || input === null)
19
+ return [];
20
+ const location = options?.reusableObjectsLocation;
21
+ if (!location)
22
+ return [];
23
+ const refs = /* @__PURE__ */ new Set();
24
+ collectRefs(context.document.data, refs);
25
+ const results = [];
26
+ for (const key of Object.keys(input)) {
27
+ const base = `${location}/${escapePointerSegment(key)}`;
28
+ const referenced = refs.has(base) || [...refs].some((ref) => ref.startsWith(`${base}/`));
29
+ if (!referenced) {
30
+ results.push({
31
+ message: "This reusable object is never referenced",
32
+ path: [...context.path, key]
33
+ });
50
34
  }
51
- return results;
35
+ }
36
+ return results;
37
+ };
38
+ export {
39
+ unreferencedReusableObject
52
40
  };
@@ -1,4 +1,4 @@
1
- import type { RulesetFunction } from '../core/index.js';
1
+ import type { RulesetFunction } from '../core/types.js';
2
2
  /** Options for {@link xor}. */
3
3
  export type IXorOptions = {
4
4
  properties: string[];
@@ -1,18 +1,15 @@
1
- /** Flags an object unless exactly one of the listed `properties` is present. */
2
- export const xor = (input, options) => {
3
- if (typeof input !== 'object' || input === null)
4
- return [];
5
- const properties = options?.properties;
6
- // Spectral validates the option schema (an array of at least two strings)
7
- // before the function runs and no-ops when it fails, so with fewer than two
8
- // properties there is nothing meaningful to check. We deliberately skip in
9
- // silence rather than push an error: an empty or single-element list would
10
- // otherwise flag every node with a message that names nothing useful.
11
- if (!Array.isArray(properties) || properties.length < 2)
12
- return [];
13
- const present = properties.filter((property) => property in input);
14
- if (present.length !== 1) {
15
- return [{ message: `Exactly one of ${properties.map((p) => `"${p}"`).join(', ')} must be defined` }];
16
- }
1
+ const xor = (input, options) => {
2
+ if (typeof input !== "object" || input === null)
17
3
  return [];
4
+ const properties = options?.properties;
5
+ if (!Array.isArray(properties) || properties.length < 2)
6
+ return [];
7
+ const present = properties.filter((property) => property in input);
8
+ if (present.length !== 1) {
9
+ return [{ message: `Exactly one of ${properties.map((p) => `"${p}"`).join(", ")} must be defined` }];
10
+ }
11
+ return [];
12
+ };
13
+ export {
14
+ xor
18
15
  };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { type IDiagnostic, type IDocumentOptions, type LintPlugin, type LintResolver, type ResolvedExtend, type Ruleset, type RulesetDefinition } from './core/index.js';
1
+ import { type IDocumentOptions, type LintPlugin, type LintResolver, type ResolvedExtend, type Ruleset } from './core/index.js';
2
+ import type { IDiagnostic, RulesetDefinition } from './core/types.js';
2
3
  import { type AppliedFix, type FixerRegistry } from './fix/index.js';
3
- export * from './core/index.js';
4
- export * from './fix/index.js';
5
- export * from './functions/index.js';
4
+ export { type AliasDefinition, type CompiledPath, compileQuery, createDocument, createLinter, type Document, detectFormats, type ExtendModifier, type ExtendResolver, type Format, globToRegExp, type IDocumentOptions, type IQueryMatch, type IRulesetProblem, type IRunOptions, type Linter, type LintOptions, type LintPlugin, type LintPluginContext, type LintPluginResult, type LintResolver, type LintResolverResult, type LintResult, lint, lintWithResult, matchesGlob, type PluginRunResult, pointerToPath, query, queryCompiled, queryMany, type ResolvedExtend, type Ruleset, type RulesetOptions, resolveSourceOrigin, resolveSourceOriginFromMap, resolveSourcePath, runPlugins, validateRuleset, } from './core/index.js';
5
+ export { type AppliedFix, type ApplyFixesOptions, applyFixes, createFixPlugin, type EditOp, FIX_PLUGIN_NAME, type FixContext, type Fixer, type FixerRegistry, type FixPluginData, type FixResult, type ParserFormat, } from './fix/index.js';
6
+ export { alphabetical, builtinFunctions, type CasingType, casing, defined, enumeration, falsy, type IAlphabeticalOptions, type ICasingOptions, type IOrOptions, type ISchemaOptions, type IUnreferencedReusableObjectOptions, type IXorOptions, length, or, pattern, schema, truthy, typedEnum, undefinedFn, unreferencedReusableObject, xor, } from './functions/index.js';
6
7
  export { detectFormat, parseWithPointers } from './parsers/index.js';
7
8
  /**
8
9
  * Resolves an `extends` reference to a ruleset definition. Supports:
@@ -53,6 +54,18 @@ export type ILintResult = {
53
54
  * Lints a JSON/YAML `input` end to end: parses with source maps and applies the
54
55
  * ruleset. Returns just the findings; use {@link lintDocumentWithResult} for the
55
56
  * full result.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * const ruleset = {
61
+ * rules: {
62
+ * 'require-name': { given: '$', severity: 'error', then: { field: 'name', function: 'truthy' } },
63
+ * },
64
+ * }
65
+ * const findings = await lintDocument('version: 1\n', { ruleset, source: 'service.yaml' })
66
+ * // findings[].range is ZERO-based; add 1 to line/character to print file:line:col.
67
+ * // findings[].severity is numeric (0 error, 1 warn, 2 info, 3 hint).
68
+ * ```
56
69
  */
57
70
  export declare const lintDocument: (input: string, options?: ILintOptions) => Promise<IDiagnostic[]>;
58
71
  /**