@opsydyn/elysia-spectral 0.2.4 → 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/CHANGELOG.md +14 -0
- package/README.md +96 -39
- package/dist/core/index.d.mts +1 -1
- package/dist/core/index.mjs +1 -1
- package/dist/{core-Czin3kvK.mjs → core-DBjV7-E8.mjs} +193 -37
- package/dist/{index-BrFQCFDI.d.mts → index-YZElrxnl.d.mts} +5 -2
- package/dist/index.d.mts +41 -2
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.0](https://github.com/opsydyn/elysia-spectral/compare/v0.2.5...v0.3.0) (2026-04-14)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add recommended, server, and strict governance presets ([692061e](https://github.com/opsydyn/elysia-spectral/commit/692061e23ba5735fc0ad1a8d1a9110ced1f869fe))
|
|
9
|
+
|
|
10
|
+
## [0.2.5](https://github.com/opsydyn/elysia-spectral/compare/v0.2.4...v0.2.5) (2026-04-14)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* deep-merge partial rule overrides in mergeRulesets ([4308196](https://github.com/opsydyn/elysia-spectral/commit/4308196ad4b017d94ac015239c51815c26cafb20))
|
|
16
|
+
|
|
3
17
|
## [0.2.4](https://github.com/opsydyn/elysia-spectral/compare/v0.2.3...v0.2.4) (2026-04-14)
|
|
4
18
|
|
|
5
19
|
|
package/README.md
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/opsydyn/elysia-spectral/main/
|
|
2
|
+
<img src="https://raw.githubusercontent.com/opsydyn/elysia-spectral/main/repo_header_graphic.png" alt="@opsydyn/elysia-spectral" width="480" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
# @opsydyn/elysia-spectral
|
|
6
6
|
|
|
7
7
|
Thin Elysia plugin that lints the OpenAPI document generated by `@elysiajs/openapi` with Spectral.
|
|
8
8
|
|
|
9
|
+
## What Is Elysia?
|
|
10
|
+
|
|
11
|
+
Elysia is a fast, ergonomic TypeScript web framework for Bun. It uses a plugin model for composing functionality and integrates with `@elysiajs/openapi` to generate OpenAPI documentation directly from route schemas — no separate spec file or annotation layer required.
|
|
12
|
+
|
|
13
|
+
- Official site: <https://elysiajs.com/>
|
|
14
|
+
- `@elysiajs/openapi`: <https://elysiajs.com/plugins/openapi>
|
|
15
|
+
|
|
9
16
|
## What Is Spectral?
|
|
10
17
|
|
|
11
18
|
Spectral is an open-source linter and style guide engine for API descriptions. It was built with OpenAPI, AsyncAPI, and JSON Schema in mind, and is commonly used to enforce API style guides, catch weak or inconsistent contract definitions, and improve the usefulness of generated API descriptions.
|
|
@@ -25,6 +32,12 @@ Official Spectral references:
|
|
|
25
32
|
- Stoplight overview: <https://stoplight.io/open-source/spectral>
|
|
26
33
|
- GitHub repository: <https://github.com/stoplightio/spectral>
|
|
27
34
|
|
|
35
|
+
## Standards
|
|
36
|
+
|
|
37
|
+
- RFC 9457 — Problem Details for HTTP APIs: <https://datatracker.ietf.org/doc/html/rfc9457>
|
|
38
|
+
|
|
39
|
+
The `strict` preset enforces RFC 9457 by requiring that all 4xx and 5xx responses declare `application/problem+json` as their content type.
|
|
40
|
+
|
|
28
41
|
This README is organized using the Diataxis documentation model:
|
|
29
42
|
|
|
30
43
|
- Tutorial: learn by building a working setup
|
|
@@ -51,7 +64,7 @@ Current package scope:
|
|
|
51
64
|
|
|
52
65
|
### Add OpenAPI linting to an Elysia app
|
|
53
66
|
|
|
54
|
-
This tutorial takes a minimal Elysia app and adds startup OpenAPI linting
|
|
67
|
+
This tutorial takes a minimal Elysia app and adds startup OpenAPI linting using the `strict` preset. The `strict` preset is the recommended starting point for teams shipping production APIs — it enforces summaries, descriptions, operation IDs, tags, server declarations, and RFC 9457 Problem Details on error responses.
|
|
55
68
|
|
|
56
69
|
1. Install the dependencies.
|
|
57
70
|
|
|
@@ -63,24 +76,7 @@ bun add elysia @elysiajs/openapi @opsydyn/elysia-spectral
|
|
|
63
76
|
npm install elysia @elysiajs/openapi @opsydyn/elysia-spectral
|
|
64
77
|
```
|
|
65
78
|
|
|
66
|
-
2.
|
|
67
|
-
|
|
68
|
-
```yaml
|
|
69
|
-
extends:
|
|
70
|
-
- spectral:oas
|
|
71
|
-
|
|
72
|
-
rules:
|
|
73
|
-
operation-description:
|
|
74
|
-
severity: error
|
|
75
|
-
|
|
76
|
-
operation-tags:
|
|
77
|
-
severity: warn
|
|
78
|
-
|
|
79
|
-
elysia-operation-summary:
|
|
80
|
-
severity: error
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
3. Add `@elysiajs/openapi` and `spectralPlugin` to your app.
|
|
79
|
+
2. Add `@elysiajs/openapi` and `spectralPlugin` to your app with the `strict` preset.
|
|
84
80
|
|
|
85
81
|
```ts
|
|
86
82
|
import { Elysia, t } from 'elysia'
|
|
@@ -93,18 +89,19 @@ new Elysia()
|
|
|
93
89
|
documentation: {
|
|
94
90
|
info: {
|
|
95
91
|
title: 'Example API',
|
|
96
|
-
version: '1.0.0'
|
|
92
|
+
version: '1.0.0',
|
|
93
|
+
contact: { name: 'API Team', url: 'https://example.com/support' }
|
|
97
94
|
},
|
|
95
|
+
servers: [{ url: 'https://api.example.com' }],
|
|
98
96
|
tags: [{ name: 'Users', description: 'User operations' }]
|
|
99
97
|
}
|
|
100
98
|
})
|
|
101
99
|
)
|
|
102
100
|
.use(
|
|
103
101
|
spectralPlugin({
|
|
102
|
+
preset: 'strict',
|
|
104
103
|
failOn: 'error',
|
|
105
|
-
startup: {
|
|
106
|
-
mode: 'enforce'
|
|
107
|
-
},
|
|
104
|
+
startup: { mode: 'enforce' },
|
|
108
105
|
output: {
|
|
109
106
|
console: true,
|
|
110
107
|
jsonReportPath: './artifacts/openapi-lint.json',
|
|
@@ -113,36 +110,44 @@ new Elysia()
|
|
|
113
110
|
})
|
|
114
111
|
)
|
|
115
112
|
.get('/users', () => [{ id: '1', name: 'Ada Lovelace' }], {
|
|
116
|
-
response: {
|
|
117
|
-
200: t.Array(
|
|
118
|
-
t.Object({
|
|
119
|
-
id: t.String(),
|
|
120
|
-
name: t.String()
|
|
121
|
-
})
|
|
122
|
-
)
|
|
123
|
-
},
|
|
124
113
|
detail: {
|
|
125
114
|
summary: 'List users',
|
|
126
|
-
description: 'Return all users.',
|
|
115
|
+
description: 'Return all registered users.',
|
|
127
116
|
operationId: 'listUsers',
|
|
128
117
|
tags: ['Users']
|
|
118
|
+
},
|
|
119
|
+
response: {
|
|
120
|
+
200: t.Array(
|
|
121
|
+
t.Object({ id: t.String(), name: t.String() })
|
|
122
|
+
),
|
|
123
|
+
500: t.Object(
|
|
124
|
+
{
|
|
125
|
+
type: t.String(),
|
|
126
|
+
title: t.String(),
|
|
127
|
+
status: t.Number(),
|
|
128
|
+
detail: t.String()
|
|
129
|
+
},
|
|
130
|
+
{ description: 'Internal server error (RFC 9457 Problem Details)' }
|
|
131
|
+
)
|
|
129
132
|
}
|
|
130
133
|
})
|
|
131
134
|
.listen(3000)
|
|
132
135
|
```
|
|
133
136
|
|
|
134
|
-
|
|
137
|
+
The `strict` preset requires error responses to use `application/problem+json` (RFC 9457). The `500` response above satisfies that when `@elysiajs/openapi` generates the schema with the correct content type.
|
|
138
|
+
|
|
139
|
+
3. Start the app.
|
|
135
140
|
|
|
136
141
|
```bash
|
|
137
142
|
bun run src/index.ts
|
|
138
143
|
```
|
|
139
144
|
|
|
140
|
-
|
|
145
|
+
4. Confirm the outcome.
|
|
141
146
|
|
|
142
147
|
- the app serves OpenAPI JSON at `/openapi/json`
|
|
143
|
-
- the plugin lints that generated document during startup
|
|
144
|
-
- a clean run prints
|
|
145
|
-
- `./artifacts/openapi-lint.json` contains the lint result
|
|
148
|
+
- the plugin lints that generated document during startup using the `strict` preset
|
|
149
|
+
- a clean run prints a success banner in the terminal
|
|
150
|
+
- `./artifacts/openapi-lint.json` contains the full lint result
|
|
146
151
|
- `./<package-name>.open-api.json` contains the generated OpenAPI snapshot
|
|
147
152
|
|
|
148
153
|
If startup fails, the terminal output includes:
|
|
@@ -152,6 +157,27 @@ If startup fails, the terminal output includes:
|
|
|
152
157
|
- a fix hint when one is known
|
|
153
158
|
- a spec reference in `open-api.json#/json/pointer` form
|
|
154
159
|
|
|
160
|
+
### Choose a preset
|
|
161
|
+
|
|
162
|
+
Three first-party presets are available. Import them directly or set `preset` in the plugin options.
|
|
163
|
+
|
|
164
|
+
| Preset | Use case | elysia rules | description / operationId | servers | RFC 9457 |
|
|
165
|
+
|---|---|---|---|---|---|
|
|
166
|
+
| `recommended` | local dev, low friction | warn | — | off | — |
|
|
167
|
+
| `server` | production API gate | error | warn | warn | — |
|
|
168
|
+
| `strict` | full API governance | error | error | error | warn |
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
// simplest — preset string
|
|
172
|
+
spectralPlugin({ preset: 'strict' })
|
|
173
|
+
|
|
174
|
+
// or import the preset object and pass as ruleset
|
|
175
|
+
import { strict } from '@opsydyn/elysia-spectral'
|
|
176
|
+
spectralPlugin({ ruleset: strict })
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
When `preset` is set, autodiscovered `spectral.yaml` overrides merge on top of the preset rather than the package default. This lets you tighten or loosen individual rules without losing the preset baseline.
|
|
180
|
+
|
|
155
181
|
## How-to Guides
|
|
156
182
|
|
|
157
183
|
### Use a repo-level ruleset
|
|
@@ -453,6 +479,8 @@ That example uses `startup.mode: 'report'`, so the app still boots while the pac
|
|
|
453
479
|
### Package API
|
|
454
480
|
|
|
455
481
|
```ts
|
|
482
|
+
type PresetName = 'recommended' | 'server' | 'strict'
|
|
483
|
+
|
|
456
484
|
type SeverityThreshold = 'error' | 'warn' | 'info' | 'hint' | 'never'
|
|
457
485
|
|
|
458
486
|
type StartupLintMode = 'enforce' | 'report' | 'off'
|
|
@@ -474,7 +502,7 @@ type OpenApiLintSink = {
|
|
|
474
502
|
spec: Record<string, unknown>
|
|
475
503
|
logger: SpectralLogger
|
|
476
504
|
}
|
|
477
|
-
) =>
|
|
505
|
+
) => undefined | Partial<OpenApiLintArtifacts> | Promise<undefined | Partial<OpenApiLintArtifacts>>
|
|
478
506
|
}
|
|
479
507
|
|
|
480
508
|
type RulesetResolver = (
|
|
@@ -490,9 +518,14 @@ type LoadResolvedRulesetOptions = {
|
|
|
490
518
|
baseDir?: string
|
|
491
519
|
resolvers?: RulesetResolver[]
|
|
492
520
|
mergeAutodiscoveredWithDefault?: boolean
|
|
521
|
+
/** Override the base ruleset used for autodiscovery merging and the fallback when no ruleset is configured. */
|
|
522
|
+
defaultRuleset?: RulesetDefinition
|
|
493
523
|
}
|
|
494
524
|
|
|
495
525
|
type SpectralPluginOptions = {
|
|
526
|
+
/** First-party governance preset. Sets the base ruleset and autodiscovery merge target. */
|
|
527
|
+
preset?: PresetName
|
|
528
|
+
/** Custom ruleset path, object, or inline definition. Merged on top of preset when both are set. */
|
|
496
529
|
ruleset?: string | RulesetDefinition | Record<string, unknown>
|
|
497
530
|
failOn?: SeverityThreshold
|
|
498
531
|
healthcheck?: false | { path?: string }
|
|
@@ -522,6 +555,30 @@ type SpectralPluginOptions = {
|
|
|
522
555
|
}
|
|
523
556
|
```
|
|
524
557
|
|
|
558
|
+
### Presets
|
|
559
|
+
|
|
560
|
+
| Preset | Extends | elysia-operation-summary | elysia-operation-tags | operation-description | operation-operationId | oas3-api-servers | info-contact | rfc9457-problem-details |
|
|
561
|
+
|---|---|---|---|---|---|---|---|---|
|
|
562
|
+
| `recommended` | spectral:oas/recommended | warn | warn | — | — | off | off | — |
|
|
563
|
+
| `server` | spectral:oas/recommended | error | error | warn | warn | warn | off | — |
|
|
564
|
+
| `strict` | spectral:oas/recommended | error | error | error | error | error | warn | warn |
|
|
565
|
+
|
|
566
|
+
All three presets are exported as `RulesetDefinition` objects and can be passed directly to `ruleset` if you need to compose them:
|
|
567
|
+
|
|
568
|
+
```ts
|
|
569
|
+
import { strict } from '@opsydyn/elysia-spectral'
|
|
570
|
+
|
|
571
|
+
spectralPlugin({
|
|
572
|
+
ruleset: {
|
|
573
|
+
...strict,
|
|
574
|
+
rules: {
|
|
575
|
+
...(strict.rules as object),
|
|
576
|
+
'rfc9457-problem-details': 'error' // escalate to error for this service
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
})
|
|
580
|
+
```
|
|
581
|
+
|
|
525
582
|
### Runtime state
|
|
526
583
|
|
|
527
584
|
The runtime object exposes:
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ 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, 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, y as loadRuleset } from "../index-
|
|
1
|
+
import { _ 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, 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, y as loadRuleset } from "../index-YZElrxnl.mjs";
|
|
2
2
|
export { LoadResolvedRulesetOptions, LoadedRuleset, OpenApiLintArtifactWriteError, OpenApiLintThresholdError, ResolvedRulesetCandidate, RulesetLoadError, RulesetResolver, RulesetResolverContext, RulesetResolverInput, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, exceedsThreshold, isEnabled, lintOpenApi, loadResolvedRuleset, loadRuleset, normalizeFindings, resolveStartupMode, shouldFail };
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as OpenApiLintThresholdError, c as shouldFail,
|
|
1
|
+
import { a as OpenApiLintThresholdError, c as shouldFail, g as loadRuleset, h as loadResolvedRuleset, i as resolveStartupMode, m as defaultRulesetResolvers, n as createOpenApiLintRuntime, o as enforceThreshold, p as RulesetLoadError, r as isEnabled, s as exceedsThreshold, t as OpenApiLintArtifactWriteError, v as lintOpenApi, y as normalizeFindings } from "../core-DBjV7-E8.mjs";
|
|
2
2
|
export { OpenApiLintArtifactWriteError, OpenApiLintThresholdError, RulesetLoadError, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, exceedsThreshold, isEnabled, lintOpenApi, loadResolvedRuleset, loadRuleset, normalizeFindings, resolveStartupMode, shouldFail };
|
|
@@ -12,7 +12,12 @@ const guidanceByCode = {
|
|
|
12
12
|
"elysia-operation-summary": "Add detail.summary to the Elysia route options so generated docs and clients have a short operation label.",
|
|
13
13
|
"elysia-operation-tags": "Add detail.tags with at least one stable tag, for example ['Users'] or ['Dev'].",
|
|
14
14
|
"operation-description": "Add detail.description with a short user-facing explanation of what the route does.",
|
|
15
|
-
"operation-tags": "Add a non-empty detail.tags array on the route so the OpenAPI operation is grouped consistently."
|
|
15
|
+
"operation-tags": "Add a non-empty detail.tags array on the route so the OpenAPI operation is grouped consistently.",
|
|
16
|
+
"operation-operationId": "Add detail.operationId with a unique camelCase identifier so generated clients and SDKs have stable method names.",
|
|
17
|
+
"operation-success-response": "Add at least one 2xx response schema to the route, for example response: { 200: t.Object(...) }.",
|
|
18
|
+
"oas3-api-servers": "Add a servers array to the OpenAPI documentation config with at least one base URL.",
|
|
19
|
+
"info-contact": "Add an info.contact object to the OpenAPI documentation config with a name and url or email.",
|
|
20
|
+
"rfc9457-problem-details": "Add an \"application/problem+json\" content entry to the error response. See RFC 9457 for the Problem Details schema."
|
|
16
21
|
};
|
|
17
22
|
const getFindingRecommendation = (code, message) => {
|
|
18
23
|
const direct = guidanceByCode[code];
|
|
@@ -117,31 +122,38 @@ const lintOpenApi = async (spec, ruleset) => {
|
|
|
117
122
|
return normalizeFindings(await spectral.run(spec, { ignoreUnknownFormat: false }), spec);
|
|
118
123
|
};
|
|
119
124
|
//#endregion
|
|
120
|
-
//#region src/
|
|
121
|
-
const { schema: schema$
|
|
122
|
-
const { oas: oas$
|
|
123
|
-
const operationSelector = "$.paths[*][get,put,post,delete,options,head,patch,trace]";
|
|
124
|
-
|
|
125
|
-
|
|
125
|
+
//#region src/presets/recommended.ts
|
|
126
|
+
const { schema: schema$3, truthy: truthy$3 } = spectralFunctions;
|
|
127
|
+
const { oas: oas$3 } = spectralRulesets;
|
|
128
|
+
const operationSelector$2 = "$.paths[*][get,put,post,delete,options,head,patch,trace]";
|
|
129
|
+
/**
|
|
130
|
+
* Baseline quality preset. Equivalent to the package default ruleset.
|
|
131
|
+
*
|
|
132
|
+
* - Extends spectral:oas/recommended
|
|
133
|
+
* - elysia-operation-summary and elysia-operation-tags at warn
|
|
134
|
+
* - oas3-api-servers and info-contact disabled (local-dev friendly)
|
|
135
|
+
*/
|
|
136
|
+
const recommended = {
|
|
137
|
+
extends: [[oas$3, "recommended"]],
|
|
126
138
|
rules: {
|
|
127
139
|
"oas3-api-servers": "off",
|
|
128
140
|
"info-contact": "off",
|
|
129
141
|
"elysia-operation-summary": {
|
|
130
142
|
description: "Operations should define a summary for generated docs and clients.",
|
|
131
143
|
severity: "warn",
|
|
132
|
-
given: operationSelector,
|
|
144
|
+
given: operationSelector$2,
|
|
133
145
|
then: {
|
|
134
146
|
field: "summary",
|
|
135
|
-
function: truthy$
|
|
147
|
+
function: truthy$3
|
|
136
148
|
}
|
|
137
149
|
},
|
|
138
150
|
"elysia-operation-tags": {
|
|
139
151
|
description: "Operations should declare at least one tag for grouping and downstream tooling.",
|
|
140
152
|
severity: "warn",
|
|
141
|
-
given: operationSelector,
|
|
153
|
+
given: operationSelector$2,
|
|
142
154
|
then: {
|
|
143
155
|
field: "tags",
|
|
144
|
-
function: schema$
|
|
156
|
+
function: schema$3,
|
|
145
157
|
functionOptions: { schema: {
|
|
146
158
|
type: "array",
|
|
147
159
|
minItems: 1
|
|
@@ -151,9 +163,12 @@ const defaultRuleset = {
|
|
|
151
163
|
}
|
|
152
164
|
};
|
|
153
165
|
//#endregion
|
|
166
|
+
//#region src/rulesets/default-ruleset.ts
|
|
167
|
+
const defaultRuleset = recommended;
|
|
168
|
+
//#endregion
|
|
154
169
|
//#region src/core/load-ruleset.ts
|
|
155
|
-
const { alphabetical, casing, defined, enumeration, falsy, length, or, pattern, schema, truthy, undefined: undefinedFunction, unreferencedReusableObject, xor } = spectralFunctions;
|
|
156
|
-
const { oas } = spectralRulesets;
|
|
170
|
+
const { alphabetical, casing, defined, enumeration, falsy, length, or, pattern, schema: schema$2, truthy: truthy$2, undefined: undefinedFunction, unreferencedReusableObject, xor } = spectralFunctions;
|
|
171
|
+
const { oas: oas$2 } = spectralRulesets;
|
|
157
172
|
const functionMap = {
|
|
158
173
|
alphabetical,
|
|
159
174
|
casing,
|
|
@@ -163,13 +178,13 @@ const functionMap = {
|
|
|
163
178
|
length,
|
|
164
179
|
or,
|
|
165
180
|
pattern,
|
|
166
|
-
schema,
|
|
167
|
-
truthy,
|
|
181
|
+
schema: schema$2,
|
|
182
|
+
truthy: truthy$2,
|
|
168
183
|
undefined: undefinedFunction,
|
|
169
184
|
unreferencedReusableObject,
|
|
170
185
|
xor
|
|
171
186
|
};
|
|
172
|
-
const extendsMap = { "spectral:oas": oas };
|
|
187
|
+
const extendsMap = { "spectral:oas": oas$2 };
|
|
173
188
|
const autodiscoverRulesetFilenames = [
|
|
174
189
|
"spectral.yaml",
|
|
175
190
|
"spectral.yml",
|
|
@@ -202,7 +217,7 @@ const loadResolvedRuleset = async (input, baseDirOrOptions = process.cwd()) => {
|
|
|
202
217
|
const options = normalizeLoadResolvedRulesetOptions(baseDirOrOptions);
|
|
203
218
|
const context = {
|
|
204
219
|
baseDir: options.baseDir,
|
|
205
|
-
defaultRuleset,
|
|
220
|
+
defaultRuleset: options.defaultRuleset,
|
|
206
221
|
mergeAutodiscoveredWithDefault: options.mergeAutodiscoveredWithDefault
|
|
207
222
|
};
|
|
208
223
|
for (const resolver of options.resolvers) {
|
|
@@ -213,19 +228,21 @@ const loadResolvedRuleset = async (input, baseDirOrOptions = process.cwd()) => {
|
|
|
213
228
|
return normalized;
|
|
214
229
|
}
|
|
215
230
|
}
|
|
216
|
-
if (input === void 0) return { ruleset: defaultRuleset };
|
|
231
|
+
if (input === void 0) return { ruleset: options.defaultRuleset };
|
|
217
232
|
throw new RulesetLoadError("Ruleset input could not be resolved.");
|
|
218
233
|
};
|
|
219
234
|
const normalizeLoadResolvedRulesetOptions = (value) => {
|
|
220
235
|
if (typeof value === "string") return {
|
|
221
236
|
baseDir: value,
|
|
222
237
|
resolvers: defaultRulesetResolvers,
|
|
223
|
-
mergeAutodiscoveredWithDefault: true
|
|
238
|
+
mergeAutodiscoveredWithDefault: true,
|
|
239
|
+
defaultRuleset
|
|
224
240
|
};
|
|
225
241
|
return {
|
|
226
242
|
baseDir: value.baseDir ?? process.cwd(),
|
|
227
243
|
resolvers: value.resolvers ?? defaultRulesetResolvers,
|
|
228
|
-
mergeAutodiscoveredWithDefault: value.mergeAutodiscoveredWithDefault ?? true
|
|
244
|
+
mergeAutodiscoveredWithDefault: value.mergeAutodiscoveredWithDefault ?? true,
|
|
245
|
+
defaultRuleset: value.defaultRuleset ?? defaultRuleset
|
|
229
246
|
};
|
|
230
247
|
};
|
|
231
248
|
const resolveAutodiscoveredRuleset = async (input, context) => {
|
|
@@ -324,35 +341,44 @@ const loadModuleRuleset = async (resolvedPath) => {
|
|
|
324
341
|
});
|
|
325
342
|
};
|
|
326
343
|
const resolveModuleRulesetValue = (imported) => {
|
|
327
|
-
if (!isRecord(imported)) return;
|
|
344
|
+
if (!isRecord$1(imported)) return;
|
|
328
345
|
if ("default" in imported) return imported.default;
|
|
329
346
|
if ("ruleset" in imported) return imported.ruleset;
|
|
330
347
|
};
|
|
331
348
|
const resolveModuleFunctions = (imported) => {
|
|
332
|
-
if (!isRecord(imported) || !("functions" in imported)) return {};
|
|
349
|
+
if (!isRecord$1(imported) || !("functions" in imported)) return {};
|
|
333
350
|
const { functions } = imported;
|
|
334
|
-
if (!isRecord(functions)) throw new RulesetLoadError("Module ruleset \"functions\" export must be an object map of function names to Spectral functions.");
|
|
351
|
+
if (!isRecord$1(functions)) throw new RulesetLoadError("Module ruleset \"functions\" export must be an object map of function names to Spectral functions.");
|
|
335
352
|
const entries = Object.entries(functions).filter(([, value]) => typeof value === "function");
|
|
336
353
|
return Object.fromEntries(entries);
|
|
337
354
|
};
|
|
338
355
|
const isYamlRulesetPath = (value) => value.endsWith(".yaml") || value.endsWith(".yml");
|
|
339
356
|
const isModuleRulesetPath = (value) => value.endsWith(".js") || value.endsWith(".mjs") || value.endsWith(".cjs") || value.endsWith(".ts") || value.endsWith(".mts") || value.endsWith(".cts");
|
|
340
357
|
const normalizeRulesetDefinition = (input, availableFunctions = functionMap) => {
|
|
341
|
-
if (!isRecord(input)) throw new RulesetLoadError("Ruleset must be an object.");
|
|
358
|
+
if (!isRecord$1(input)) throw new RulesetLoadError("Ruleset must be an object.");
|
|
342
359
|
const normalized = { ...input };
|
|
343
360
|
if ("extends" in normalized) normalized.extends = normalizeExtends(normalized.extends);
|
|
344
361
|
if ("rules" in normalized) normalized.rules = normalizeRules(normalized.rules, availableFunctions);
|
|
345
362
|
return normalized;
|
|
346
363
|
};
|
|
364
|
+
const mergeRuleEntry = (base, override) => {
|
|
365
|
+
if (!isRecord$1(override)) return override;
|
|
366
|
+
if ("given" in override || "then" in override) return override;
|
|
367
|
+
if (isRecord$1(base) && ("given" in base || "then" in base)) return {
|
|
368
|
+
...base,
|
|
369
|
+
...override
|
|
370
|
+
};
|
|
371
|
+
const keys = Object.keys(override);
|
|
372
|
+
if (keys.length === 1 && keys[0] === "severity") return override.severity;
|
|
373
|
+
return override;
|
|
374
|
+
};
|
|
347
375
|
const mergeRulesets = (baseRuleset, overrideRuleset) => {
|
|
348
376
|
const mergedBase = baseRuleset;
|
|
349
377
|
const mergedOverride = overrideRuleset;
|
|
350
|
-
const baseRules = isRecord(mergedBase.rules) ? mergedBase.rules : {};
|
|
351
|
-
const overrideRules = isRecord(mergedOverride.rules) ? mergedOverride.rules : {};
|
|
352
|
-
const mergedRules = {
|
|
353
|
-
|
|
354
|
-
...overrideRules
|
|
355
|
-
};
|
|
378
|
+
const baseRules = isRecord$1(mergedBase.rules) ? mergedBase.rules : {};
|
|
379
|
+
const overrideRules = isRecord$1(mergedOverride.rules) ? mergedOverride.rules : {};
|
|
380
|
+
const mergedRules = { ...baseRules };
|
|
381
|
+
for (const [name, overrideRule] of Object.entries(overrideRules)) mergedRules[name] = mergeRuleEntry(baseRules[name], overrideRule);
|
|
356
382
|
const baseExtends = toExtendsArray(mergedBase.extends);
|
|
357
383
|
const overrideExtends = toExtendsArray(mergedOverride.extends);
|
|
358
384
|
const mergedExtends = [...baseExtends, ...overrideExtends];
|
|
@@ -385,12 +411,12 @@ const resolveExtendsEntry = (value) => {
|
|
|
385
411
|
return resolved;
|
|
386
412
|
};
|
|
387
413
|
const normalizeRules = (value, availableFunctions) => {
|
|
388
|
-
if (!isRecord(value)) return value;
|
|
414
|
+
if (!isRecord$1(value)) return value;
|
|
389
415
|
const entries = Object.entries(value).map(([ruleName, ruleValue]) => [ruleName, normalizeRule(ruleValue, availableFunctions)]);
|
|
390
416
|
return Object.fromEntries(entries);
|
|
391
417
|
};
|
|
392
418
|
const normalizeRule = (value, availableFunctions) => {
|
|
393
|
-
if (!isRecord(value)) return value;
|
|
419
|
+
if (!isRecord$1(value)) return value;
|
|
394
420
|
const normalized = { ...value };
|
|
395
421
|
if ("then" in normalized) normalized.then = normalizeThen(normalized.then, availableFunctions);
|
|
396
422
|
return normalized;
|
|
@@ -400,7 +426,7 @@ const normalizeThen = (value, availableFunctions) => {
|
|
|
400
426
|
return normalizeThenEntry(value, availableFunctions);
|
|
401
427
|
};
|
|
402
428
|
const normalizeThenEntry = (value, availableFunctions) => {
|
|
403
|
-
if (!isRecord(value)) return value;
|
|
429
|
+
if (!isRecord$1(value)) return value;
|
|
404
430
|
const normalized = { ...value };
|
|
405
431
|
if (typeof normalized.function === "string") {
|
|
406
432
|
const resolved = availableFunctions[normalized.function];
|
|
@@ -409,7 +435,7 @@ const normalizeThenEntry = (value, availableFunctions) => {
|
|
|
409
435
|
}
|
|
410
436
|
return normalized;
|
|
411
437
|
};
|
|
412
|
-
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
438
|
+
const isRecord$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
413
439
|
//#endregion
|
|
414
440
|
//#region src/output/terminal-format.ts
|
|
415
441
|
const severityStyles = {
|
|
@@ -864,6 +890,133 @@ const createOutputSinks = (options) => {
|
|
|
864
890
|
return sinks;
|
|
865
891
|
};
|
|
866
892
|
//#endregion
|
|
893
|
+
//#region src/presets/server.ts
|
|
894
|
+
const { schema: schema$1, truthy: truthy$1 } = spectralFunctions;
|
|
895
|
+
const { oas: oas$1 } = spectralRulesets;
|
|
896
|
+
const operationSelector$1 = "$.paths[*][get,put,post,delete,options,head,patch,trace]";
|
|
897
|
+
/**
|
|
898
|
+
* Production API quality preset. Suitable as a CI gate for teams shipping
|
|
899
|
+
* public or internal APIs where contract quality matters.
|
|
900
|
+
*
|
|
901
|
+
* Tightens recommended:
|
|
902
|
+
* - elysia-operation-summary and elysia-operation-tags escalated to error
|
|
903
|
+
* - operation-description, operation-operationId, operation-success-response at warn
|
|
904
|
+
* - oas3-api-servers at warn (servers should be declared in production specs)
|
|
905
|
+
*/
|
|
906
|
+
const server = {
|
|
907
|
+
extends: [[oas$1, "recommended"]],
|
|
908
|
+
rules: {
|
|
909
|
+
"oas3-api-servers": "warn",
|
|
910
|
+
"info-contact": "off",
|
|
911
|
+
"elysia-operation-summary": {
|
|
912
|
+
description: "Operations should define a summary for generated docs and clients.",
|
|
913
|
+
severity: "error",
|
|
914
|
+
given: operationSelector$1,
|
|
915
|
+
then: {
|
|
916
|
+
field: "summary",
|
|
917
|
+
function: truthy$1
|
|
918
|
+
}
|
|
919
|
+
},
|
|
920
|
+
"elysia-operation-tags": {
|
|
921
|
+
description: "Operations should declare at least one tag for grouping and downstream tooling.",
|
|
922
|
+
severity: "error",
|
|
923
|
+
given: operationSelector$1,
|
|
924
|
+
then: {
|
|
925
|
+
field: "tags",
|
|
926
|
+
function: schema$1,
|
|
927
|
+
functionOptions: { schema: {
|
|
928
|
+
type: "array",
|
|
929
|
+
minItems: 1
|
|
930
|
+
} }
|
|
931
|
+
}
|
|
932
|
+
},
|
|
933
|
+
"operation-description": "warn",
|
|
934
|
+
"operation-operationId": "warn",
|
|
935
|
+
"operation-success-response": "warn"
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
//#endregion
|
|
939
|
+
//#region src/presets/strict.ts
|
|
940
|
+
const { schema, truthy } = spectralFunctions;
|
|
941
|
+
const { oas } = spectralRulesets;
|
|
942
|
+
const operationSelector = "$.paths[*][get,put,post,delete,options,head,patch,trace]";
|
|
943
|
+
const isRecord = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
944
|
+
/**
|
|
945
|
+
* Checks that all 4xx/5xx responses declare application/problem+json as
|
|
946
|
+
* their content type, conforming to RFC 9457 Problem Details for HTTP APIs.
|
|
947
|
+
*/
|
|
948
|
+
const checkProblemDetails = (operation) => {
|
|
949
|
+
if (!isRecord(operation) || !isRecord(operation.responses)) return;
|
|
950
|
+
const results = [];
|
|
951
|
+
for (const [statusCode, response] of Object.entries(operation.responses)) {
|
|
952
|
+
const code = Number(statusCode);
|
|
953
|
+
if (!Number.isFinite(code) || code < 400) continue;
|
|
954
|
+
if (!isRecord(response)) continue;
|
|
955
|
+
const content = response.content;
|
|
956
|
+
if (!isRecord(content) || !("application/problem+json" in content)) results.push({
|
|
957
|
+
message: `${statusCode} error response should use "application/problem+json" content type (RFC 9457 Problem Details).`,
|
|
958
|
+
path: ["responses", statusCode]
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
return results.length > 0 ? results : void 0;
|
|
962
|
+
};
|
|
963
|
+
/**
|
|
964
|
+
* Full API governance preset. Suitable for teams with formal API governance
|
|
965
|
+
* requirements, public API programs, or downstream client generation pipelines.
|
|
966
|
+
*
|
|
967
|
+
* Tightens server:
|
|
968
|
+
* - All elysia rules and operation metadata rules escalated to error
|
|
969
|
+
* - info-contact at warn (API ownership should be declared)
|
|
970
|
+
* - oas3-api-servers at error (server declaration is required)
|
|
971
|
+
* - rfc9457-problem-details at warn (error responses should use Problem Details)
|
|
972
|
+
*/
|
|
973
|
+
const strict = {
|
|
974
|
+
extends: [[oas, "recommended"]],
|
|
975
|
+
rules: {
|
|
976
|
+
"oas3-api-servers": "error",
|
|
977
|
+
"info-contact": "warn",
|
|
978
|
+
"elysia-operation-summary": {
|
|
979
|
+
description: "Operations should define a summary for generated docs and clients.",
|
|
980
|
+
severity: "error",
|
|
981
|
+
given: operationSelector,
|
|
982
|
+
then: {
|
|
983
|
+
field: "summary",
|
|
984
|
+
function: truthy
|
|
985
|
+
}
|
|
986
|
+
},
|
|
987
|
+
"elysia-operation-tags": {
|
|
988
|
+
description: "Operations should declare at least one tag for grouping and downstream tooling.",
|
|
989
|
+
severity: "error",
|
|
990
|
+
given: operationSelector,
|
|
991
|
+
then: {
|
|
992
|
+
field: "tags",
|
|
993
|
+
function: schema,
|
|
994
|
+
functionOptions: { schema: {
|
|
995
|
+
type: "array",
|
|
996
|
+
minItems: 1
|
|
997
|
+
} }
|
|
998
|
+
}
|
|
999
|
+
},
|
|
1000
|
+
"operation-description": "error",
|
|
1001
|
+
"operation-operationId": "error",
|
|
1002
|
+
"operation-success-response": "error",
|
|
1003
|
+
"rfc9457-problem-details": {
|
|
1004
|
+
description: "Error responses (4xx, 5xx) should use RFC 9457 Problem Details (application/problem+json).",
|
|
1005
|
+
message: "{{error}}",
|
|
1006
|
+
severity: "warn",
|
|
1007
|
+
given: operationSelector,
|
|
1008
|
+
then: { function: checkProblemDetails }
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
//#endregion
|
|
1013
|
+
//#region src/presets/index.ts
|
|
1014
|
+
const presets = {
|
|
1015
|
+
recommended,
|
|
1016
|
+
server,
|
|
1017
|
+
strict
|
|
1018
|
+
};
|
|
1019
|
+
//#endregion
|
|
867
1020
|
//#region src/providers/spec-provider.ts
|
|
868
1021
|
var BaseSpecProvider = class {
|
|
869
1022
|
constructor(app, options = {}) {
|
|
@@ -1014,8 +1167,11 @@ const createOpenApiLintRuntime = (options = {}) => {
|
|
|
1014
1167
|
reporter.start("OpenAPI lint started.");
|
|
1015
1168
|
try {
|
|
1016
1169
|
const spec = await new PublicSpecProvider(app, options.source).getSpec();
|
|
1017
|
-
const loadedRuleset = await loadResolvedRuleset(options.ruleset);
|
|
1018
|
-
if (loadedRuleset.source?.autodiscovered)
|
|
1170
|
+
const loadedRuleset = await loadResolvedRuleset(options.ruleset, { ...options.preset ? { defaultRuleset: presets[options.preset] } : {} });
|
|
1171
|
+
if (loadedRuleset.source?.autodiscovered) {
|
|
1172
|
+
const base = options.preset ? `"${options.preset}" preset` : "package default ruleset";
|
|
1173
|
+
reporter.ruleset(`OpenAPI lint autodiscovered ruleset ${loadedRuleset.source.path} and merged it with the ${base}.`);
|
|
1174
|
+
} else if (options.preset && !loadedRuleset.source?.path) reporter.ruleset(`OpenAPI lint using "${options.preset}" preset.`);
|
|
1019
1175
|
else if (loadedRuleset.source?.path) reporter.ruleset(`OpenAPI lint loaded ruleset ${loadedRuleset.source.path}.`);
|
|
1020
1176
|
const result = await lintOpenApi(spec, loadedRuleset.ruleset);
|
|
1021
1177
|
await writeOutputSinks(result, spec, options, artifactWriteFailureMode);
|
|
@@ -1096,4 +1252,4 @@ const resolveStartupMode = (options = {}) => {
|
|
|
1096
1252
|
return options.enabled === false ? "off" : "enforce";
|
|
1097
1253
|
};
|
|
1098
1254
|
//#endregion
|
|
1099
|
-
export { OpenApiLintThresholdError as a, shouldFail as c,
|
|
1255
|
+
export { recommended as _, OpenApiLintThresholdError as a, shouldFail as c, server as d, resolveReporter as f, loadRuleset as g, loadResolvedRuleset as h, resolveStartupMode as i, presets as l, defaultRulesetResolvers as m, createOpenApiLintRuntime as n, enforceThreshold as o, RulesetLoadError as p, isEnabled as r, exceedsThreshold as s, OpenApiLintArtifactWriteError as t, strict as u, lintOpenApi as v, normalizeFindings as y };
|
|
@@ -2,6 +2,7 @@ import { ISpectralDiagnostic, RulesetDefinition } from "@stoplight/spectral-core
|
|
|
2
2
|
import { AnyElysia } from "elysia";
|
|
3
3
|
|
|
4
4
|
//#region src/types.d.ts
|
|
5
|
+
type PresetName = 'recommended' | 'server' | 'strict';
|
|
5
6
|
type SeverityThreshold = 'error' | 'warn' | 'info' | 'hint' | 'never';
|
|
6
7
|
type LintSeverity = 'error' | 'warn' | 'info' | 'hint';
|
|
7
8
|
type StartupLintMode = 'enforce' | 'report' | 'off';
|
|
@@ -27,6 +28,7 @@ type OpenApiLintSink = {
|
|
|
27
28
|
write: (result: LintRunResult, context: OpenApiLintSinkContext) => undefined | Partial<OpenApiLintArtifacts> | Promise<undefined | Partial<OpenApiLintArtifacts>>;
|
|
28
29
|
};
|
|
29
30
|
type SpectralPluginOptions = {
|
|
31
|
+
preset?: PresetName;
|
|
30
32
|
ruleset?: string | RulesetDefinition | Record<string, unknown>;
|
|
31
33
|
failOn?: SeverityThreshold;
|
|
32
34
|
healthcheck?: false | {
|
|
@@ -135,7 +137,8 @@ type RulesetResolver = (input: RulesetResolverInput, context: RulesetResolverCon
|
|
|
135
137
|
type LoadResolvedRulesetOptions = {
|
|
136
138
|
baseDir?: string;
|
|
137
139
|
resolvers?: RulesetResolver[];
|
|
138
|
-
mergeAutodiscoveredWithDefault?: boolean;
|
|
140
|
+
mergeAutodiscoveredWithDefault?: boolean; /** Override the ruleset used as the merge base for autodiscovery and the fallback when no ruleset is configured. Defaults to the package default (recommended preset). */
|
|
141
|
+
defaultRuleset?: RulesetDefinition;
|
|
139
142
|
};
|
|
140
143
|
declare class RulesetLoadError extends Error {
|
|
141
144
|
constructor(message: string, options?: {
|
|
@@ -169,4 +172,4 @@ declare const exceedsThreshold: (severity: LintSeverity, threshold: SeverityThre
|
|
|
169
172
|
declare const shouldFail: (result: LintRunResult, threshold: SeverityThreshold) => boolean;
|
|
170
173
|
declare const enforceThreshold: (result: LintRunResult, threshold: SeverityThreshold) => void;
|
|
171
174
|
//#endregion
|
|
172
|
-
export { OpenApiLintSinkContext as A, LintRunResult as C, OpenApiLintRuntimeFailure as D, OpenApiLintRuntime as E,
|
|
175
|
+
export { OpenApiLintSinkContext as A, LintRunResult as C, OpenApiLintRuntimeFailure as D, OpenApiLintRuntime as E, SpectralPluginOptions as F, StartupLintMode as I, SeverityThreshold as M, SpecProvider as N, OpenApiLintRuntimeStatus as O, SpectralLogger 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, PresetName 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
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { A as OpenApiLintSinkContext, C as LintRunResult, D as OpenApiLintRuntimeFailure, E as OpenApiLintRuntime, F as StartupLintMode, M as
|
|
1
|
+
import { A as OpenApiLintSinkContext, C as LintRunResult, D as OpenApiLintRuntimeFailure, E as OpenApiLintRuntime, F as SpectralPluginOptions, I as StartupLintMode, M as SeverityThreshold, N as SpecProvider, O as OpenApiLintRuntimeStatus, P as SpectralLogger, 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 PresetName, 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-YZElrxnl.mjs";
|
|
2
|
+
import { RulesetDefinition } from "@stoplight/spectral-core";
|
|
2
3
|
import { Elysia } from "elysia";
|
|
3
4
|
|
|
4
5
|
//#region src/plugin.d.ts
|
|
@@ -33,4 +34,42 @@ declare const spectralPlugin: (options?: SpectralPluginOptions) => Elysia<"", {
|
|
|
33
34
|
response: {};
|
|
34
35
|
}>;
|
|
35
36
|
//#endregion
|
|
36
|
-
|
|
37
|
+
//#region src/presets/recommended.d.ts
|
|
38
|
+
/**
|
|
39
|
+
* Baseline quality preset. Equivalent to the package default ruleset.
|
|
40
|
+
*
|
|
41
|
+
* - Extends spectral:oas/recommended
|
|
42
|
+
* - elysia-operation-summary and elysia-operation-tags at warn
|
|
43
|
+
* - oas3-api-servers and info-contact disabled (local-dev friendly)
|
|
44
|
+
*/
|
|
45
|
+
declare const recommended: RulesetDefinition;
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/presets/server.d.ts
|
|
48
|
+
/**
|
|
49
|
+
* Production API quality preset. Suitable as a CI gate for teams shipping
|
|
50
|
+
* public or internal APIs where contract quality matters.
|
|
51
|
+
*
|
|
52
|
+
* Tightens recommended:
|
|
53
|
+
* - elysia-operation-summary and elysia-operation-tags escalated to error
|
|
54
|
+
* - operation-description, operation-operationId, operation-success-response at warn
|
|
55
|
+
* - oas3-api-servers at warn (servers should be declared in production specs)
|
|
56
|
+
*/
|
|
57
|
+
declare const server: RulesetDefinition;
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/presets/strict.d.ts
|
|
60
|
+
/**
|
|
61
|
+
* Full API governance preset. Suitable for teams with formal API governance
|
|
62
|
+
* requirements, public API programs, or downstream client generation pipelines.
|
|
63
|
+
*
|
|
64
|
+
* Tightens server:
|
|
65
|
+
* - All elysia rules and operation metadata rules escalated to error
|
|
66
|
+
* - info-contact at warn (API ownership should be declared)
|
|
67
|
+
* - oas3-api-servers at error (server declaration is required)
|
|
68
|
+
* - rfc9457-problem-details at warn (error responses should use Problem Details)
|
|
69
|
+
*/
|
|
70
|
+
declare const strict: RulesetDefinition;
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/presets/index.d.ts
|
|
73
|
+
declare const presets: Record<PresetName, RulesetDefinition>;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { ArtifactWriteFailureMode, LintFinding, LintRunResult, LintSeverity, LoadResolvedRulesetOptions, LoadedRuleset, OpenApiLintArtifactWriteError, OpenApiLintArtifacts, OpenApiLintRuntime, OpenApiLintRuntimeFailure, OpenApiLintRuntimeStatus, OpenApiLintSink, OpenApiLintSinkContext, OpenApiLintThresholdError, PresetName, ResolvedRulesetCandidate, RulesetLoadError, RulesetResolver, RulesetResolverContext, RulesetResolverInput, SeverityThreshold, SpecProvider, SpectralLogger, SpectralPluginOptions, StartupLintMode, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, exceedsThreshold, isEnabled, lintOpenApi, loadResolvedRuleset, loadRuleset, normalizeFindings, presets, recommended, resolveStartupMode, server, shouldFail, spectralPlugin, strict };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as OpenApiLintThresholdError, c as shouldFail, d as
|
|
1
|
+
import { _ as recommended, a as OpenApiLintThresholdError, c as shouldFail, d as server, f as resolveReporter, g as loadRuleset, h as loadResolvedRuleset, i as resolveStartupMode, l as presets, m as defaultRulesetResolvers, n as createOpenApiLintRuntime, o as enforceThreshold, p as RulesetLoadError, r as isEnabled, s as exceedsThreshold, t as OpenApiLintArtifactWriteError, u as strict, v as lintOpenApi, y as normalizeFindings } from "./core-DBjV7-E8.mjs";
|
|
2
2
|
import { Elysia } from "elysia";
|
|
3
3
|
//#region src/plugin.ts
|
|
4
4
|
const spectralPlugin = (options = {}) => {
|
|
@@ -82,4 +82,4 @@ const spectralPlugin = (options = {}) => {
|
|
|
82
82
|
return plugin;
|
|
83
83
|
};
|
|
84
84
|
//#endregion
|
|
85
|
-
export { OpenApiLintArtifactWriteError, OpenApiLintThresholdError, RulesetLoadError, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, exceedsThreshold, isEnabled, lintOpenApi, loadResolvedRuleset, loadRuleset, normalizeFindings, resolveStartupMode, shouldFail, spectralPlugin };
|
|
85
|
+
export { OpenApiLintArtifactWriteError, OpenApiLintThresholdError, RulesetLoadError, createOpenApiLintRuntime, defaultRulesetResolvers, enforceThreshold, exceedsThreshold, isEnabled, lintOpenApi, loadResolvedRuleset, loadRuleset, normalizeFindings, presets, recommended, resolveStartupMode, server, shouldFail, spectralPlugin, strict };
|