@opsydyn/elysia-spectral 0.2.4
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 +47 -0
- package/README.md +637 -0
- package/dist/core/index.d.mts +2 -0
- package/dist/core/index.mjs +2 -0
- package/dist/core-Czin3kvK.mjs +1099 -0
- package/dist/index-BrFQCFDI.d.mts +172 -0
- package/dist/index.d.mts +36 -0
- package/dist/index.mjs +85 -0
- package/package.json +86 -0
- package/spectral.yaml +25 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { ISpectralDiagnostic, RulesetDefinition } from "@stoplight/spectral-core";
|
|
2
|
+
import { AnyElysia } from "elysia";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
type SeverityThreshold = 'error' | 'warn' | 'info' | 'hint' | 'never';
|
|
6
|
+
type LintSeverity = 'error' | 'warn' | 'info' | 'hint';
|
|
7
|
+
type StartupLintMode = 'enforce' | 'report' | 'off';
|
|
8
|
+
type OpenApiLintRuntimeStatus = 'idle' | 'running' | 'passed' | 'failed';
|
|
9
|
+
type ArtifactWriteFailureMode = 'warn' | 'error';
|
|
10
|
+
type SpectralLogger = {
|
|
11
|
+
info: (message: string) => void;
|
|
12
|
+
warn: (message: string) => void;
|
|
13
|
+
error: (message: string) => void;
|
|
14
|
+
};
|
|
15
|
+
type OpenApiLintArtifacts = {
|
|
16
|
+
jsonReportPath?: string;
|
|
17
|
+
junitReportPath?: string;
|
|
18
|
+
sarifReportPath?: string;
|
|
19
|
+
specSnapshotPath?: string;
|
|
20
|
+
};
|
|
21
|
+
type OpenApiLintSinkContext = {
|
|
22
|
+
spec: Record<string, unknown>;
|
|
23
|
+
logger: SpectralLogger;
|
|
24
|
+
};
|
|
25
|
+
type OpenApiLintSink = {
|
|
26
|
+
name: string;
|
|
27
|
+
write: (result: LintRunResult, context: OpenApiLintSinkContext) => undefined | Partial<OpenApiLintArtifacts> | Promise<undefined | Partial<OpenApiLintArtifacts>>;
|
|
28
|
+
};
|
|
29
|
+
type SpectralPluginOptions = {
|
|
30
|
+
ruleset?: string | RulesetDefinition | Record<string, unknown>;
|
|
31
|
+
failOn?: SeverityThreshold;
|
|
32
|
+
healthcheck?: false | {
|
|
33
|
+
path?: string;
|
|
34
|
+
};
|
|
35
|
+
output?: {
|
|
36
|
+
console?: boolean;
|
|
37
|
+
jsonReportPath?: string;
|
|
38
|
+
junitReportPath?: string;
|
|
39
|
+
sarifReportPath?: string;
|
|
40
|
+
specSnapshotPath?: string | true;
|
|
41
|
+
pretty?: boolean;
|
|
42
|
+
artifactWriteFailures?: ArtifactWriteFailureMode;
|
|
43
|
+
sinks?: OpenApiLintSink[];
|
|
44
|
+
};
|
|
45
|
+
source?: {
|
|
46
|
+
specPath?: string;
|
|
47
|
+
baseUrl?: string;
|
|
48
|
+
};
|
|
49
|
+
enabled?: boolean | ((env: Record<string, string | undefined>) => boolean);
|
|
50
|
+
startup?: {
|
|
51
|
+
mode?: StartupLintMode;
|
|
52
|
+
};
|
|
53
|
+
logger?: SpectralLogger;
|
|
54
|
+
};
|
|
55
|
+
type LintFinding = {
|
|
56
|
+
code: string;
|
|
57
|
+
message: string;
|
|
58
|
+
severity: LintSeverity;
|
|
59
|
+
path: Array<string | number>;
|
|
60
|
+
documentPointer?: string;
|
|
61
|
+
recommendation?: string;
|
|
62
|
+
source?: string;
|
|
63
|
+
range?: {
|
|
64
|
+
start?: {
|
|
65
|
+
line: number;
|
|
66
|
+
character: number;
|
|
67
|
+
};
|
|
68
|
+
end?: {
|
|
69
|
+
line: number;
|
|
70
|
+
character: number;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
operation?: {
|
|
74
|
+
method?: string;
|
|
75
|
+
path?: string;
|
|
76
|
+
operationId?: string;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
type LintRunResult = {
|
|
80
|
+
ok: boolean;
|
|
81
|
+
generatedAt: string;
|
|
82
|
+
summary: {
|
|
83
|
+
error: number;
|
|
84
|
+
warn: number;
|
|
85
|
+
info: number;
|
|
86
|
+
hint: number;
|
|
87
|
+
total: number;
|
|
88
|
+
};
|
|
89
|
+
artifacts?: OpenApiLintArtifacts;
|
|
90
|
+
findings: LintFinding[];
|
|
91
|
+
};
|
|
92
|
+
interface SpecProvider {
|
|
93
|
+
getSpec(): Promise<unknown>;
|
|
94
|
+
}
|
|
95
|
+
type OpenApiLintRuntimeFailure = {
|
|
96
|
+
name: string;
|
|
97
|
+
message: string;
|
|
98
|
+
generatedAt: string;
|
|
99
|
+
};
|
|
100
|
+
type OpenApiLintRuntime = {
|
|
101
|
+
status: OpenApiLintRuntimeStatus;
|
|
102
|
+
startedAt: string | null;
|
|
103
|
+
completedAt: string | null;
|
|
104
|
+
durationMs: number | null;
|
|
105
|
+
latest: LintRunResult | null;
|
|
106
|
+
lastSuccess: LintRunResult | null;
|
|
107
|
+
lastFailure: OpenApiLintRuntimeFailure | null;
|
|
108
|
+
running: boolean;
|
|
109
|
+
run: (app: AnyElysia) => Promise<LintRunResult>;
|
|
110
|
+
};
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/core/lint-openapi.d.ts
|
|
113
|
+
declare const lintOpenApi: (spec: Record<string, unknown>, ruleset: RulesetDefinition) => Promise<LintRunResult>;
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/core/load-ruleset.d.ts
|
|
116
|
+
type LoadedRuleset = {
|
|
117
|
+
ruleset: RulesetDefinition;
|
|
118
|
+
source?: {
|
|
119
|
+
path: string;
|
|
120
|
+
autodiscovered: boolean;
|
|
121
|
+
mergedWithDefault: boolean;
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
type ResolvedRulesetCandidate = {
|
|
125
|
+
ruleset: unknown;
|
|
126
|
+
source?: LoadedRuleset['source'];
|
|
127
|
+
};
|
|
128
|
+
type RulesetResolverInput = string | RulesetDefinition | Record<string, unknown> | undefined;
|
|
129
|
+
type RulesetResolverContext = {
|
|
130
|
+
baseDir: string;
|
|
131
|
+
defaultRuleset: RulesetDefinition;
|
|
132
|
+
mergeAutodiscoveredWithDefault: boolean;
|
|
133
|
+
};
|
|
134
|
+
type RulesetResolver = (input: RulesetResolverInput, context: RulesetResolverContext) => Promise<ResolvedRulesetCandidate | undefined>;
|
|
135
|
+
type LoadResolvedRulesetOptions = {
|
|
136
|
+
baseDir?: string;
|
|
137
|
+
resolvers?: RulesetResolver[];
|
|
138
|
+
mergeAutodiscoveredWithDefault?: boolean;
|
|
139
|
+
};
|
|
140
|
+
declare class RulesetLoadError extends Error {
|
|
141
|
+
constructor(message: string, options?: {
|
|
142
|
+
cause?: unknown;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
declare const loadRuleset: (input?: RulesetResolverInput, baseDirOrOptions?: string | LoadResolvedRulesetOptions) => Promise<RulesetDefinition>;
|
|
146
|
+
declare const loadResolvedRuleset: (input?: RulesetResolverInput, baseDirOrOptions?: string | LoadResolvedRulesetOptions) => Promise<LoadedRuleset>;
|
|
147
|
+
declare const defaultRulesetResolvers: RulesetResolver[];
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/core/normalize-findings.d.ts
|
|
150
|
+
declare const normalizeFindings: (diagnostics: ISpectralDiagnostic[], spec: unknown) => LintRunResult;
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/core/runtime.d.ts
|
|
153
|
+
declare const createOpenApiLintRuntime: (options?: SpectralPluginOptions) => OpenApiLintRuntime;
|
|
154
|
+
declare class OpenApiLintArtifactWriteError extends Error {
|
|
155
|
+
readonly artifact: string;
|
|
156
|
+
readonly cause: unknown;
|
|
157
|
+
constructor(artifact: string, cause: unknown);
|
|
158
|
+
}
|
|
159
|
+
declare const isEnabled: (options?: SpectralPluginOptions) => boolean;
|
|
160
|
+
declare const resolveStartupMode: (options?: SpectralPluginOptions) => StartupLintMode;
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/core/thresholds.d.ts
|
|
163
|
+
declare class OpenApiLintThresholdError extends Error {
|
|
164
|
+
readonly threshold: SeverityThreshold;
|
|
165
|
+
readonly result: LintRunResult;
|
|
166
|
+
constructor(threshold: SeverityThreshold, result: LintRunResult);
|
|
167
|
+
}
|
|
168
|
+
declare const exceedsThreshold: (severity: LintSeverity, threshold: SeverityThreshold) => boolean;
|
|
169
|
+
declare const shouldFail: (result: LintRunResult, threshold: SeverityThreshold) => boolean;
|
|
170
|
+
declare const enforceThreshold: (result: LintRunResult, threshold: SeverityThreshold) => void;
|
|
171
|
+
//#endregion
|
|
172
|
+
export { OpenApiLintSinkContext as A, LintRunResult as C, OpenApiLintRuntimeFailure as D, OpenApiLintRuntime as E, StartupLintMode as F, SpecProvider as M, SpectralLogger as N, OpenApiLintRuntimeStatus as O, SpectralPluginOptions as P, LintFinding as S, OpenApiLintArtifacts as T, defaultRulesetResolvers as _, OpenApiLintArtifactWriteError as a, lintOpenApi as b, resolveStartupMode as c, LoadedRuleset as d, ResolvedRulesetCandidate as f, RulesetResolverInput as g, RulesetResolverContext as h, shouldFail as i, SeverityThreshold as j, OpenApiLintSink as k, normalizeFindings as l, RulesetResolver as m, enforceThreshold as n, createOpenApiLintRuntime as o, RulesetLoadError as p, exceedsThreshold as r, isEnabled as s, OpenApiLintThresholdError as t, LoadResolvedRulesetOptions as u, loadResolvedRuleset as v, LintSeverity as w, ArtifactWriteFailureMode as x, loadRuleset as y };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { A as OpenApiLintSinkContext, C as LintRunResult, D as OpenApiLintRuntimeFailure, E as OpenApiLintRuntime, F as StartupLintMode, M as SpecProvider, N as SpectralLogger, O as OpenApiLintRuntimeStatus, P as SpectralPluginOptions, S as LintFinding, T as OpenApiLintArtifacts, _ as defaultRulesetResolvers, a as OpenApiLintArtifactWriteError, b as lintOpenApi, c as resolveStartupMode, d as LoadedRuleset, f as ResolvedRulesetCandidate, g as RulesetResolverInput, h as RulesetResolverContext, i as shouldFail, j as SeverityThreshold, k as OpenApiLintSink, l as normalizeFindings, m as RulesetResolver, n as enforceThreshold, o as createOpenApiLintRuntime, p as RulesetLoadError, r as exceedsThreshold, s as isEnabled, t as OpenApiLintThresholdError, u as LoadResolvedRulesetOptions, v as loadResolvedRuleset, w as LintSeverity, x as ArtifactWriteFailureMode, y as loadRuleset } from "./index-BrFQCFDI.mjs";
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
|
|
4
|
+
//#region src/plugin.d.ts
|
|
5
|
+
declare const spectralPlugin: (options?: SpectralPluginOptions) => Elysia<"", {
|
|
6
|
+
decorator: {};
|
|
7
|
+
store: {
|
|
8
|
+
openApiLint: OpenApiLintRuntime;
|
|
9
|
+
};
|
|
10
|
+
derive: {};
|
|
11
|
+
resolve: {};
|
|
12
|
+
}, {
|
|
13
|
+
typebox: {};
|
|
14
|
+
error: {};
|
|
15
|
+
}, {
|
|
16
|
+
schema: {};
|
|
17
|
+
standaloneSchema: {};
|
|
18
|
+
macro: {};
|
|
19
|
+
macroFn: {};
|
|
20
|
+
parser: {};
|
|
21
|
+
response: {};
|
|
22
|
+
}, {}, {
|
|
23
|
+
derive: {};
|
|
24
|
+
resolve: {};
|
|
25
|
+
schema: {};
|
|
26
|
+
standaloneSchema: {};
|
|
27
|
+
response: {};
|
|
28
|
+
}, {
|
|
29
|
+
derive: {};
|
|
30
|
+
resolve: {};
|
|
31
|
+
schema: {};
|
|
32
|
+
standaloneSchema: {};
|
|
33
|
+
response: {};
|
|
34
|
+
}>;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { ArtifactWriteFailureMode, LintFinding, LintRunResult, LintSeverity, LoadResolvedRulesetOptions, LoadedRuleset, OpenApiLintArtifactWriteError, OpenApiLintArtifacts, OpenApiLintRuntime, OpenApiLintRuntimeFailure, OpenApiLintRuntimeStatus, OpenApiLintSink, OpenApiLintSinkContext, OpenApiLintThresholdError, ResolvedRulesetCandidate, RulesetLoadError, RulesetResolver, RulesetResolverContext, RulesetResolverInput, SeverityThreshold, SpecProvider, SpectralLogger, SpectralPluginOptions, StartupLintMode, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, exceedsThreshold, isEnabled, lintOpenApi, loadResolvedRuleset, loadRuleset, normalizeFindings, resolveStartupMode, shouldFail, spectralPlugin };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { a as OpenApiLintThresholdError, c as shouldFail, d as defaultRulesetResolvers, f as loadResolvedRuleset, h as normalizeFindings, i as resolveStartupMode, l as resolveReporter, m as lintOpenApi, n as createOpenApiLintRuntime, o as enforceThreshold, p as loadRuleset, r as isEnabled, s as exceedsThreshold, t as OpenApiLintArtifactWriteError, u as RulesetLoadError } from "./core-Czin3kvK.mjs";
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
//#region src/plugin.ts
|
|
4
|
+
const spectralPlugin = (options = {}) => {
|
|
5
|
+
const runtime = createOpenApiLintRuntime(options);
|
|
6
|
+
const hostAppRef = { current: null };
|
|
7
|
+
const reporter = resolveReporter(options.logger);
|
|
8
|
+
let plugin = new Elysia({ name: "@opsydyn/elysia-spectral" }).state("openApiLint", runtime).onStart(async (context) => {
|
|
9
|
+
const app = context.app ?? context;
|
|
10
|
+
hostAppRef.current = app;
|
|
11
|
+
const startupMode = resolveStartupMode(options);
|
|
12
|
+
if (startupMode === "off") return;
|
|
13
|
+
try {
|
|
14
|
+
await runtime.run(app);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (startupMode === "report" && error instanceof OpenApiLintThresholdError) {
|
|
17
|
+
reporter.report(`OpenAPI lint exceeded the "${options.failOn ?? "error"}" threshold, but startup is continuing because startup.mode is "report".`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
if (options.healthcheck) {
|
|
24
|
+
const healthcheckPath = options.healthcheck.path ?? "/__openapi/health";
|
|
25
|
+
plugin = plugin.get(healthcheckPath, async ({ request, set }) => {
|
|
26
|
+
const fresh = new URL(request.url).searchParams.get("fresh") === "1";
|
|
27
|
+
const threshold = options.failOn ?? "error";
|
|
28
|
+
const currentApp = hostAppRef.current;
|
|
29
|
+
if (!currentApp) {
|
|
30
|
+
set.status = 503;
|
|
31
|
+
return {
|
|
32
|
+
ok: false,
|
|
33
|
+
cached: false,
|
|
34
|
+
threshold,
|
|
35
|
+
error: "OpenAPI lint runtime is not initialized yet."
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const usedCache = !fresh && (runtime.latest !== null || runtime.running);
|
|
40
|
+
const result = usedCache ? runtime.latest ?? await runtime.run(currentApp) : await runtime.run(currentApp);
|
|
41
|
+
if (result === null) {
|
|
42
|
+
set.status = 500;
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
cached: false,
|
|
46
|
+
threshold,
|
|
47
|
+
error: "OpenAPI lint returned no result."
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const healthy = !shouldFail(result, threshold);
|
|
51
|
+
set.status = healthy ? 200 : 503;
|
|
52
|
+
return {
|
|
53
|
+
ok: healthy,
|
|
54
|
+
cached: usedCache,
|
|
55
|
+
threshold,
|
|
56
|
+
result
|
|
57
|
+
};
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (error instanceof OpenApiLintThresholdError) {
|
|
60
|
+
set.status = 503;
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
cached: false,
|
|
64
|
+
threshold,
|
|
65
|
+
result: error.result,
|
|
66
|
+
error: error.message
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
set.status = 500;
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
cached: false,
|
|
73
|
+
threshold,
|
|
74
|
+
error: error instanceof Error ? error.message : String(error)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}, { detail: {
|
|
78
|
+
hide: true,
|
|
79
|
+
summary: "OpenAPI lint healthcheck"
|
|
80
|
+
} });
|
|
81
|
+
}
|
|
82
|
+
return plugin;
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
export { OpenApiLintArtifactWriteError, OpenApiLintThresholdError, RulesetLoadError, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, exceedsThreshold, isEnabled, lintOpenApi, loadResolvedRuleset, loadRuleset, normalizeFindings, resolveStartupMode, shouldFail, spectralPlugin };
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opsydyn/elysia-spectral",
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "Thin Elysia plugin that lints generated OpenAPI documents with Spectral.",
|
|
5
|
+
"packageManager": "bun@1.2.9",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "./dist/index.mjs",
|
|
12
|
+
"module": "./dist/index.mjs",
|
|
13
|
+
"types": "./dist/index.d.mts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.mts",
|
|
17
|
+
"import": "./dist/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"./core": {
|
|
20
|
+
"types": "./dist/core/index.d.mts",
|
|
21
|
+
"import": "./dist/core/index.mjs"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"typesVersions": {
|
|
25
|
+
"*": {
|
|
26
|
+
"core": [
|
|
27
|
+
"./dist/core/index.d.mts"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"CHANGELOG.md",
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md",
|
|
35
|
+
"spectral.yaml"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsdown",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"test": "bun test",
|
|
41
|
+
"test:watch": "bun test --watch"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"elysia",
|
|
45
|
+
"openapi",
|
|
46
|
+
"spectral",
|
|
47
|
+
"lint",
|
|
48
|
+
"bun"
|
|
49
|
+
],
|
|
50
|
+
"author": "Alan P Currie",
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/opsydyn/elysia-spectral.git",
|
|
55
|
+
"directory": "packages/elysia-spectral"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/opsydyn/elysia-spectral#readme",
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/opsydyn/elysia-spectral/issues"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=22.0.0",
|
|
63
|
+
"bun": ">=1.3.0"
|
|
64
|
+
},
|
|
65
|
+
"peerDependencies": {
|
|
66
|
+
"elysia": "^1.4.0"
|
|
67
|
+
},
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"@stoplight/spectral-core": "^1.22.0",
|
|
70
|
+
"@stoplight/spectral-formats": "^1.8.0",
|
|
71
|
+
"@stoplight/spectral-functions": "^1.10.2",
|
|
72
|
+
"@stoplight/spectral-parsers": "^1.0.4",
|
|
73
|
+
"@stoplight/spectral-rulesets": "^1.22.1",
|
|
74
|
+
"signale": "^1.4.0",
|
|
75
|
+
"yaml": "^2.8.1"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@elysiajs/openapi": "^1.4.0",
|
|
79
|
+
"@types/bun": "^1.3.12",
|
|
80
|
+
"@types/node": "^25.6.0",
|
|
81
|
+
"@types/signale": "^1.4.7",
|
|
82
|
+
"elysia": "^1.4.0",
|
|
83
|
+
"tsdown": "^0.21.8",
|
|
84
|
+
"typescript": "^5.8.3"
|
|
85
|
+
}
|
|
86
|
+
}
|
package/spectral.yaml
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
extends:
|
|
2
|
+
- spectral:oas
|
|
3
|
+
rules:
|
|
4
|
+
oas3-api-servers: off
|
|
5
|
+
info-contact: off
|
|
6
|
+
|
|
7
|
+
elysia-operation-summary:
|
|
8
|
+
description: Operations should define a summary for generated docs and clients.
|
|
9
|
+
severity: warn
|
|
10
|
+
given: $.paths[*][get,put,post,delete,options,head,patch,trace]
|
|
11
|
+
then:
|
|
12
|
+
field: summary
|
|
13
|
+
function: truthy
|
|
14
|
+
|
|
15
|
+
elysia-operation-tags:
|
|
16
|
+
description: Operations should declare at least one tag for grouping and downstream tooling.
|
|
17
|
+
severity: warn
|
|
18
|
+
given: $.paths[*][get,put,post,delete,options,head,patch,trace]
|
|
19
|
+
then:
|
|
20
|
+
field: tags
|
|
21
|
+
function: schema
|
|
22
|
+
functionOptions:
|
|
23
|
+
schema:
|
|
24
|
+
type: array
|
|
25
|
+
minItems: 1
|