@ebarahona/loopback-openapi-v3 1.0.0 → 1.1.1
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 +106 -72
- package/dist/errors.d.ts +19 -0
- package/dist/errors.js +37 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/keys.d.ts +1 -1
- package/dist/keys.js.map +1 -1
- package/dist/openapi-version.component.d.ts +2 -1
- package/dist/openapi-version.component.js.map +1 -1
- package/dist/openapi-version.enhancer.js.map +1 -1
- package/dist/transform.d.ts +1 -1
- package/dist/transform.js +58 -17
- package/dist/transform.js.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +22 -6
- package/src/__tests__/install-hooks-script.spec.ts +267 -0
- package/src/__tests__/openapi-version.enhancer.spec.ts +1 -1
- package/src/__tests__/transform.spec.ts +122 -51
- package/src/errors.ts +83 -0
- package/src/index.ts +26 -6
- package/src/keys.ts +17 -1
- package/src/openapi-version.component.ts +4 -5
- package/src/openapi-version.enhancer.ts +8 -9
- package/src/transform.ts +209 -41
- package/src/types.ts +16 -0
package/README.md
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
# @ebarahona/loopback-openapi-v3
|
|
2
2
|
|
|
3
|
-
OpenAPI version compatibility enhancer for LoopBack 4.
|
|
3
|
+
OpenAPI version compatibility enhancer for LoopBack 4. Transforms LoopBack-generated OpenAPI 3.0 specs into OAS 3.1 or 3.2 at boot, handles nullable conversion both ways, and emits structured warnings for lossy downgrades.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```bash
|
|
6
|
+
npm install @ebarahona/loopback-openapi-v3
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
> Part of the [`@ebarahona/loopback-*` plugin portfolio](https://github.com/ebarahona/loopback-plugins). See the [portfolio roadmap](https://github.com/ebarahona/loopback-plugins/blob/main/ROADMAP.md) for sibling plugins (`loopback-connector-mongodb`, `loopback-transport-core`, planned `loopback-graphql`) and the shared infrastructure they use.
|
|
10
|
+
|
|
11
|
+
LoopBack 4 emits OpenAPI 3.0 by default. This component registers an OAS Enhancer that runs during spec generation and rewrites the document to the target version. Upgrades convert `{type, nullable}` to `{type: [type, 'null']}` and emit 3.1+ idioms. Downgrades strip fields that have no 3.0 equivalent and emit a `TransformWarning` for each one. The enhancer is pure and deterministic, so LoopBack's spec cache reuses the result on subsequent requests.
|
|
12
|
+
|
|
13
|
+
## What This Provides
|
|
14
|
+
|
|
15
|
+
| Export | Purpose |
|
|
16
|
+
| ------------------------------------- | ---------------------------------------------------------------------- |
|
|
17
|
+
| `OpenApiVersionComponent` | LoopBack 4 component that registers the enhancer |
|
|
18
|
+
| `OpenApiVersionEnhancer` | OAS Enhancer wired into `@loopback/openapi-v3` spec generation |
|
|
19
|
+
| `OpenApiVersionBindings` | Typed `BindingKey` namespace (`CONFIG`) |
|
|
20
|
+
| `OpenApiVersion` | Version literal type (`'3.0.0' \| '3.1.0' \| '3.2.0'`) |
|
|
21
|
+
| `OpenApiVersionConfig` | Component config type (target version, nullable transform flag) |
|
|
22
|
+
| `transformOpenApiSpec(spec, config?)` | Pure transform function, usable outside LoopBack |
|
|
23
|
+
| `parseVersion(version)` | Parses `'3.1.0'` into `{major, minor, patch}` |
|
|
24
|
+
| `TransformResult` | `{spec, warnings}` returned by `transformOpenApiSpec` |
|
|
25
|
+
| `TransformWarning` | Structured warning describing a stripped or rewritten field |
|
|
26
|
+
| `OpenApiVersionError` | Base class for every error thrown by this package |
|
|
27
|
+
| `OpenApiVersionConfigError` | Thrown when component config is invalid |
|
|
28
|
+
| `OpenApiTransformError` | Thrown when the input spec cannot be transformed |
|
|
29
|
+
| `OpenApiDowngradeError` | Thrown when a downgrade target cannot represent required source fields |
|
|
6
30
|
|
|
7
31
|
## Install
|
|
8
32
|
|
|
@@ -12,111 +36,121 @@ npm install @ebarahona/loopback-openapi-v3
|
|
|
12
36
|
|
|
13
37
|
## Usage
|
|
14
38
|
|
|
39
|
+
### Component path (recommended)
|
|
40
|
+
|
|
15
41
|
```typescript
|
|
16
|
-
import {
|
|
42
|
+
import {RestApplication} from '@loopback/rest';
|
|
43
|
+
import {
|
|
44
|
+
OpenApiVersionComponent,
|
|
45
|
+
OpenApiVersionBindings,
|
|
46
|
+
} from '@ebarahona/loopback-openapi-v3';
|
|
17
47
|
|
|
18
48
|
const app = new RestApplication();
|
|
19
49
|
app.component(OpenApiVersionComponent);
|
|
20
|
-
app.bind(OpenApiVersionBindings.CONFIG).to({
|
|
50
|
+
app.bind(OpenApiVersionBindings.CONFIG).to({
|
|
51
|
+
version: '3.1.0',
|
|
52
|
+
transformNullable: true,
|
|
53
|
+
});
|
|
21
54
|
```
|
|
22
55
|
|
|
23
|
-
The spec served at `/openapi.json`
|
|
56
|
+
The spec served at `/openapi.json` is rewritten to `"openapi": "3.1.0"`, with nullable fields converted to type arrays. LoopBack caches the transformed spec; the enhancer does not re-run per request.
|
|
24
57
|
|
|
25
|
-
|
|
58
|
+
### Pure-function path (standalone)
|
|
59
|
+
|
|
60
|
+
`transformOpenApiSpec` is exported for use outside a LoopBack application (build-time tooling, tests, fixture generation):
|
|
26
61
|
|
|
27
62
|
```typescript
|
|
28
|
-
|
|
29
|
-
// Target version: '3.0.0' (default, no-op), '3.1.0', or '3.2.0'
|
|
30
|
-
version: '3.1.0',
|
|
63
|
+
import {transformOpenApiSpec} from '@ebarahona/loopback-openapi-v3';
|
|
31
64
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Defaults to true for 3.1+
|
|
35
|
-
transformNullable: true,
|
|
65
|
+
const {spec, warnings} = transformOpenApiSpec(inputSpec, {
|
|
66
|
+
version: '3.1.0',
|
|
36
67
|
});
|
|
68
|
+
|
|
69
|
+
for (const w of warnings) {
|
|
70
|
+
console.warn(`[${w.field}] ${w.message}`);
|
|
71
|
+
}
|
|
37
72
|
```
|
|
38
73
|
|
|
39
|
-
|
|
74
|
+
The input spec is deep-cloned with `structuredClone`; the original is never mutated.
|
|
75
|
+
|
|
76
|
+
## Configuration
|
|
77
|
+
|
|
78
|
+
`OpenApiVersionBindings.CONFIG` accepts an `OpenApiVersionConfig`:
|
|
79
|
+
|
|
80
|
+
| Field | Type | Default | Purpose |
|
|
81
|
+
| ------------------- | ------------------------------- | --------- | ------------------------------------------------------------------------------------------------------ |
|
|
82
|
+
| `version` | `'3.0.0' \| '3.1.0' \| '3.2.0'` | `'3.0.0'` | Target OAS version. `'3.0.0'` is a no-op pass-through. |
|
|
83
|
+
| `transformNullable` | `boolean` | `true` | Rewrite `{type, nullable: true}` to `{type: [type, 'null']}` on upgrade, and the reverse on downgrade. |
|
|
40
84
|
|
|
41
|
-
|
|
42
|
-
|---|---|---|
|
|
43
|
-
| 3.0.0 | Default (no-op) | [spec.openapis.org/oas/v3.0.3](https://spec.openapis.org/oas/v3.0.3.html) |
|
|
44
|
-
| 3.1.0 | Supported | [spec.openapis.org/oas/v3.1.0](https://spec.openapis.org/oas/v3.1.0.html) |
|
|
45
|
-
| 3.2.0 | Supported | [spec.openapis.org/oas/v3.2.0](https://spec.openapis.org/oas/v3.2.0.html) |
|
|
85
|
+
Invalid configurations (unrecognized version, non-boolean flag) throw `OpenApiVersionConfigError` synchronously when the component binds.
|
|
46
86
|
|
|
47
|
-
##
|
|
87
|
+
## Supported versions
|
|
48
88
|
|
|
49
|
-
|
|
89
|
+
| Version | Status | Spec |
|
|
90
|
+
| ------- | ------------- | ------------------------------------------------------------------------- |
|
|
91
|
+
| 3.0.0 | Default no-op | [spec.openapis.org/oas/v3.0.3](https://spec.openapis.org/oas/v3.0.3.html) |
|
|
92
|
+
| 3.1.0 | Supported | [spec.openapis.org/oas/v3.1.0](https://spec.openapis.org/oas/v3.1.0.html) |
|
|
93
|
+
| 3.2.0 | Supported | [spec.openapis.org/oas/v3.2.0](https://spec.openapis.org/oas/v3.2.0.html) |
|
|
50
94
|
|
|
51
|
-
### Upgrades (3.0 -> 3.1/3.2)
|
|
95
|
+
### Upgrades (3.0 -> 3.1 / 3.2)
|
|
52
96
|
|
|
53
|
-
- `nullable: true`
|
|
54
|
-
- Handles nullable with `oneOf`, `anyOf`, `allOf`, and
|
|
97
|
+
- `nullable: true` rewritten to type arrays (`{type: ['string', 'null']}`).
|
|
98
|
+
- Handles nullable combined with `oneOf`, `anyOf`, `allOf`, and schemas without an explicit `type`.
|
|
55
99
|
|
|
56
|
-
### Downgrades (3.2 -> 3.0/3.1)
|
|
100
|
+
### Downgrades (3.2 -> 3.0 / 3.1)
|
|
57
101
|
|
|
58
|
-
Downgrades are lossy. Features that have no equivalent in lower versions are stripped
|
|
102
|
+
Downgrades are lossy. Features that have no equivalent in lower versions are stripped, and a `TransformWarning` is emitted for each one.
|
|
59
103
|
|
|
60
104
|
**3.1 features stripped when targeting 3.0:**
|
|
61
105
|
|
|
62
|
-
| Feature
|
|
63
|
-
|
|
64
|
-
| JSON Schema dialect
|
|
65
|
-
| Webhooks
|
|
66
|
-
| Reusable path items
|
|
67
|
-
| SPDX license identifier | `info.license.identifier`
|
|
68
|
-
| Type arrays
|
|
106
|
+
| Feature | Field |
|
|
107
|
+
| ----------------------- | ---------------------------------------------- |
|
|
108
|
+
| JSON Schema dialect | `jsonSchemaDialect` |
|
|
109
|
+
| Webhooks | `webhooks` |
|
|
110
|
+
| Reusable path items | `components.pathItems` |
|
|
111
|
+
| SPDX license identifier | `info.license.identifier` |
|
|
112
|
+
| Type arrays | `type: ['string', 'null']` -> `nullable: true` |
|
|
69
113
|
|
|
70
114
|
**3.2 features stripped when targeting 3.0 or 3.1:**
|
|
71
115
|
|
|
72
|
-
| Feature
|
|
73
|
-
|
|
74
|
-
| Document identity
|
|
75
|
-
| Server name
|
|
76
|
-
| QUERY HTTP method
|
|
77
|
-
| Additional HTTP methods | `paths.*.additionalOperations`
|
|
78
|
-
| Tag nesting
|
|
79
|
-
| Querystring parameter
|
|
80
|
-
| OAuth2 device flow
|
|
81
|
-
| SSE streaming schema
|
|
82
|
-
| SSE streaming encoding
|
|
83
|
-
| Example formats
|
|
84
|
-
| Reusable media types
|
|
85
|
-
| XML text nodes
|
|
86
|
-
|
|
87
|
-
### Diagnostics
|
|
88
|
-
|
|
89
|
-
The transformer emits warnings for every stripped feature:
|
|
116
|
+
| Feature | Field |
|
|
117
|
+
| ----------------------- | --------------------------------------------- |
|
|
118
|
+
| Document identity | `$self` |
|
|
119
|
+
| Server name | `servers[].name` |
|
|
120
|
+
| QUERY HTTP method | `paths.*.query` |
|
|
121
|
+
| Additional HTTP methods | `paths.*.additionalOperations` |
|
|
122
|
+
| Tag nesting | `tags[].summary`, `.parent`, `.kind` |
|
|
123
|
+
| Querystring parameter | `parameters[].in: 'querystring'` -> `'query'` |
|
|
124
|
+
| OAuth2 device flow | `securitySchemes.*.flows.device` |
|
|
125
|
+
| SSE streaming schema | `content.*.itemSchema` |
|
|
126
|
+
| SSE streaming encoding | `content.*.itemEncoding`, `.prefixEncoding` |
|
|
127
|
+
| Example formats | `examples.*.dataValue`, `.serializedValue` |
|
|
128
|
+
| Reusable media types | `components.mediaTypes` |
|
|
129
|
+
| XML text nodes | `xml.text` |
|
|
90
130
|
|
|
91
|
-
|
|
92
|
-
import {transformOpenApiSpec} from '@ebarahona/loopback-openapi-v3';
|
|
131
|
+
When a required source field cannot be expressed in the target version (for example a 3.2 spec that depends on `$self` referenced elsewhere in the document), the transform raises `OpenApiDowngradeError` instead of silently dropping the reference.
|
|
93
132
|
|
|
94
|
-
|
|
95
|
-
for (const w of warnings) {
|
|
96
|
-
console.log(`[${w.field}] ${w.message}`);
|
|
97
|
-
}
|
|
98
|
-
// [webhooks] Removed webhooks because target version is 3.0.x (webhooks require 3.1+)
|
|
99
|
-
// [$self] Removed $self because target version is 3.0.x
|
|
100
|
-
```
|
|
133
|
+
## Typed errors
|
|
101
134
|
|
|
102
|
-
|
|
135
|
+
This package throws four typed error classes, all derived from `OpenApiVersionError`. Catch the base class for a category check, or the specific subclass for fine-grained handling.
|
|
103
136
|
|
|
104
|
-
|
|
137
|
+
- `OpenApiVersionError`: abstract base. Every error thrown by this package extends it.
|
|
138
|
+
- `OpenApiVersionConfigError`: invalid component configuration. Thrown synchronously when the component binds or when `transformOpenApiSpec` is called with an unsupported target version.
|
|
139
|
+
- `OpenApiTransformError`: input spec is structurally invalid (missing `openapi` field, unparsable version string, cyclic schema references).
|
|
140
|
+
- `OpenApiDowngradeError`: a downgrade target cannot represent a load-bearing source field. The error includes the offending field path and the source/target version pair.
|
|
105
141
|
|
|
106
|
-
|
|
107
|
-
import {transformOpenApiSpec, parseVersion} from '@ebarahona/loopback-openapi-v3';
|
|
142
|
+
## Stability
|
|
108
143
|
|
|
109
|
-
|
|
110
|
-
```
|
|
144
|
+
Every export listed in [What This Provides](#what-this-provides) is tagged `@public` from v1.0 onward. The public-API surface is audited by the `/lb4-public-api-audit` skill before every release; experimental additions are tagged `@experimental` until at least one real consumer has exercised the surface.
|
|
111
145
|
|
|
112
146
|
## Requirements
|
|
113
147
|
|
|
114
|
-
- Node.js >=
|
|
115
|
-
- LoopBack 4
|
|
116
|
-
|
|
117
|
-
- `@loopback/rest` >=
|
|
148
|
+
- Node.js >= 20.19.0 (for `structuredClone` and the package's engines field)
|
|
149
|
+
- LoopBack 4 application
|
|
150
|
+
|
|
151
|
+
Peer dependencies: `@loopback/core` (>=7.0.0 <8.0.0), `@loopback/openapi-v3` (>=11.0.0 <12.0.0), `@loopback/rest` (>=15.0.0 <16.0.0).
|
|
118
152
|
|
|
119
|
-
## Spec
|
|
153
|
+
## Spec references
|
|
120
154
|
|
|
121
155
|
- [OpenAPI 3.0.3](https://spec.openapis.org/oas/v3.0.3.html)
|
|
122
156
|
- [OpenAPI 3.1.0](https://spec.openapis.org/oas/v3.1.0.html)
|
|
@@ -125,7 +159,7 @@ const {spec, warnings} = transformOpenApiSpec(inputSpec, {version: '3.1.0'});
|
|
|
125
159
|
|
|
126
160
|
## Contributing
|
|
127
161
|
|
|
128
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
162
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Help-wanted topics with measurable acceptance criteria are listed in [HELP_WANTED.md](HELP_WANTED.md).
|
|
129
163
|
|
|
130
164
|
## License
|
|
131
165
|
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface OpenApiErrorOptions {
|
|
2
|
+
cause?: unknown;
|
|
3
|
+
}
|
|
4
|
+
export declare class OpenApiVersionError extends Error {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly cause?: unknown;
|
|
7
|
+
constructor(message: string, options?: OpenApiErrorOptions);
|
|
8
|
+
}
|
|
9
|
+
export declare class OpenApiVersionConfigError extends OpenApiVersionError {
|
|
10
|
+
readonly name = "OpenApiVersionConfigError";
|
|
11
|
+
}
|
|
12
|
+
export declare class OpenApiTransformError extends OpenApiVersionError {
|
|
13
|
+
readonly name = "OpenApiTransformError";
|
|
14
|
+
}
|
|
15
|
+
export declare class OpenApiDowngradeError extends OpenApiVersionError {
|
|
16
|
+
readonly unsupportedFeatures: readonly string[];
|
|
17
|
+
readonly name = "OpenApiDowngradeError";
|
|
18
|
+
constructor(message: string, unsupportedFeatures: readonly string[], options?: OpenApiErrorOptions);
|
|
19
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpenApiDowngradeError = exports.OpenApiTransformError = exports.OpenApiVersionConfigError = exports.OpenApiVersionError = void 0;
|
|
4
|
+
class OpenApiVersionError extends Error {
|
|
5
|
+
constructor(message, options) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'OpenApiVersionError';
|
|
8
|
+
if (options && 'cause' in options) {
|
|
9
|
+
this.cause = options.cause;
|
|
10
|
+
}
|
|
11
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.OpenApiVersionError = OpenApiVersionError;
|
|
15
|
+
class OpenApiVersionConfigError extends OpenApiVersionError {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments);
|
|
18
|
+
this.name = 'OpenApiVersionConfigError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.OpenApiVersionConfigError = OpenApiVersionConfigError;
|
|
22
|
+
class OpenApiTransformError extends OpenApiVersionError {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
this.name = 'OpenApiTransformError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.OpenApiTransformError = OpenApiTransformError;
|
|
29
|
+
class OpenApiDowngradeError extends OpenApiVersionError {
|
|
30
|
+
constructor(message, unsupportedFeatures, options) {
|
|
31
|
+
super(message, options);
|
|
32
|
+
this.unsupportedFeatures = unsupportedFeatures;
|
|
33
|
+
this.name = 'OpenApiDowngradeError';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.OpenApiDowngradeError = OpenApiDowngradeError;
|
|
37
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AA8BA,MAAa,mBAAoB,SAAQ,KAAK;IAG5C,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAW,qBAAqB,CAAC;QAOrD,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO,EAAE;YACjC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;SAC5B;QACD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAbD,kDAaC;AAQD,MAAa,yBAA0B,SAAQ,mBAAmB;IAAlE;;QACoB,SAAI,GAAG,2BAA2B,CAAC;IACvD,CAAC;CAAA;AAFD,8DAEC;AAQD,MAAa,qBAAsB,SAAQ,mBAAmB;IAA9D;;QACoB,SAAI,GAAG,uBAAuB,CAAC;IACnD,CAAC;CAAA;AAFD,sDAEC;AAUD,MAAa,qBAAsB,SAAQ,mBAAmB;IAE5D,YACE,OAAe,EACC,mBAAsC,EACtD,OAA6B;QAE7B,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAHR,wBAAmB,GAAnB,mBAAmB,CAAmB;QAHtC,SAAI,GAAG,uBAAuB,CAAC;IAOjD,CAAC;CACF;AATD,sDASC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { OpenApiVersionComponent } from './openapi-version.component';
|
|
2
2
|
export { OpenApiVersionEnhancer } from './openapi-version.enhancer';
|
|
3
3
|
export { OpenApiVersionBindings } from './keys';
|
|
4
|
-
export { OpenApiVersion, OpenApiVersionConfig } from './types';
|
|
5
|
-
export { transformOpenApiSpec, parseVersion
|
|
4
|
+
export type { OpenApiVersion, OpenApiVersionConfig } from './types';
|
|
5
|
+
export { transformOpenApiSpec, parseVersion } from './transform';
|
|
6
|
+
export type { TransformResult, TransformWarning } from './transform';
|
|
7
|
+
export { OpenApiVersionError, OpenApiVersionConfigError, OpenApiTransformError, OpenApiDowngradeError, } from './errors';
|
|
8
|
+
export type { OpenApiErrorOptions } from './errors';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseVersion = exports.transformOpenApiSpec = exports.OpenApiVersionBindings = exports.OpenApiVersionEnhancer = exports.OpenApiVersionComponent = void 0;
|
|
3
|
+
exports.OpenApiDowngradeError = exports.OpenApiTransformError = exports.OpenApiVersionConfigError = exports.OpenApiVersionError = exports.parseVersion = exports.transformOpenApiSpec = exports.OpenApiVersionBindings = exports.OpenApiVersionEnhancer = exports.OpenApiVersionComponent = void 0;
|
|
4
4
|
var openapi_version_component_1 = require("./openapi-version.component");
|
|
5
5
|
Object.defineProperty(exports, "OpenApiVersionComponent", { enumerable: true, get: function () { return openapi_version_component_1.OpenApiVersionComponent; } });
|
|
6
6
|
var openapi_version_enhancer_1 = require("./openapi-version.enhancer");
|
|
@@ -10,4 +10,9 @@ Object.defineProperty(exports, "OpenApiVersionBindings", { enumerable: true, get
|
|
|
10
10
|
var transform_1 = require("./transform");
|
|
11
11
|
Object.defineProperty(exports, "transformOpenApiSpec", { enumerable: true, get: function () { return transform_1.transformOpenApiSpec; } });
|
|
12
12
|
Object.defineProperty(exports, "parseVersion", { enumerable: true, get: function () { return transform_1.parseVersion; } });
|
|
13
|
+
var errors_1 = require("./errors");
|
|
14
|
+
Object.defineProperty(exports, "OpenApiVersionError", { enumerable: true, get: function () { return errors_1.OpenApiVersionError; } });
|
|
15
|
+
Object.defineProperty(exports, "OpenApiVersionConfigError", { enumerable: true, get: function () { return errors_1.OpenApiVersionConfigError; } });
|
|
16
|
+
Object.defineProperty(exports, "OpenApiTransformError", { enumerable: true, get: function () { return errors_1.OpenApiTransformError; } });
|
|
17
|
+
Object.defineProperty(exports, "OpenApiDowngradeError", { enumerable: true, get: function () { return errors_1.OpenApiDowngradeError; } });
|
|
13
18
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAUA,yEAAoE;AAA5D,oIAAA,uBAAuB,OAAA;AAE/B,uEAAkE;AAA1D,kIAAA,sBAAsB,OAAA;AAE9B,+BAA8C;AAAtC,8GAAA,sBAAsB,OAAA;AAI9B,yCAA+D;AAAvD,iHAAA,oBAAoB,OAAA;AAAE,yGAAA,YAAY,OAAA;AAI1C,mCAKkB;AAJhB,6GAAA,mBAAmB,OAAA;AACnB,mHAAA,yBAAyB,OAAA;AACzB,+GAAA,qBAAqB,OAAA;AACrB,+GAAA,qBAAqB,OAAA"}
|
package/dist/keys.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BindingKey } from '@loopback/core';
|
|
2
|
-
import { OpenApiVersionConfig } from './types';
|
|
2
|
+
import type { OpenApiVersionConfig } from './types';
|
|
3
3
|
export declare namespace OpenApiVersionBindings {
|
|
4
4
|
const CONFIG: BindingKey<OpenApiVersionConfig>;
|
|
5
5
|
const COMPONENT = "openapi-version.component";
|
package/dist/keys.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":";;;AAAA,yCAA0C;
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":";;;AAAA,yCAA0C;AAQ1C,IAAiB,sBAAsB,CAgBtC;AAhBD,WAAiB,sBAAsB;IAMxB,6BAAM,GAAG,iBAAU,CAAC,MAAM,CACrC,wBAAwB,CACzB,CAAC;IAOW,gCAAS,GAAG,2BAA2B,CAAC;AACvD,CAAC,EAhBgB,sBAAsB,sCAAtB,sBAAsB,QAgBtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi-version.component.js","sourceRoot":"","sources":["../src/openapi-version.component.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"openapi-version.component.js","sourceRoot":"","sources":["../src/openapi-version.component.ts"],"names":[],"mappings":";;;AAAA,yCAA+D;AAE/D,qDAAoD;AACpD,iCAA8C;AAC9C,yEAAkE;AAClE,mCAAuC;AAevC,MAAa,uBAAuB;IAApC;QACE,aAAQ,GAAc;YACpB,cAAO,CAAC,IAAI,CAAC,6BAAsB,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,sBAAc,CAAC;YAC9D,IAAA,6BAAsB,EAAC,iDAAsB,CAAC,CAAC,KAAK,CAAC,2BAAc,CAAC;SACrE,CAAC;IACJ,CAAC;CAAA;AALD,0DAKC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi-version.enhancer.js","sourceRoot":"","sources":["../src/openapi-version.enhancer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,yCAAkD;
|
|
1
|
+
{"version":3,"file":"openapi-version.enhancer.js","sourceRoot":"","sources":["../src/openapi-version.enhancer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,yCAAkD;AAElD,iCAAiC;AACjC,iCAA8C;AAC9C,2CAAiD;AACjD,mCAA6D;AAE7D,MAAM,KAAK,GAAG,IAAA,eAAY,EAAC,0BAA0B,CAAC,CAAC;AAuBhD,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IAGjC,YAEE,UAAwC,sBAAc;QAA9C,YAAO,GAAP,OAAO,CAAuC;QAJxD,SAAI,GAAG,iBAAiB,CAAC;IAKtB,CAAC;IAEJ,UAAU,CAAC,IAAiB;QAC1B,MAAM,IAAI,GAAG,EAAC,GAAG,sBAAc,EAAE,GAAG,IAAI,CAAC,OAAO,EAAC,CAAC;QAClD,KAAK,CACH,8DAA8D,EAC9D,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,iBAAiB,KAAK,KAAK,CACjC,CAAC;QACF,MAAM,MAAM,GAAG,IAAA,gCAAoB,EAAC,IAA+B,EAAE,IAAI,CAAC,CAAC;QAE3E,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;YAC9B,KAAK,CACH,2CAA2C,EAC3C,MAAM,CAAC,QAAQ,CAAC,MAAM,CACvB,CAAC;YACF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE;gBAC/B,KAAK,CAAC,kBAAkB,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;aAC/C;SACF;QAED,OAAO,MAAM,CAAC,IAAmB,CAAC;IACpC,CAAC;CACF,CAAA;AA7BY,wDAAsB;iCAAtB,sBAAsB;IADlC,IAAA,iBAAU,GAAE;IAKR,WAAA,IAAA,aAAM,EAAC,EAAC,WAAW,EAAE,6BAAsB,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC,CAAA;;GAJ5D,sBAAsB,CA6BlC"}
|
package/dist/transform.d.ts
CHANGED
package/dist/transform.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.transformOpenApiSpec = exports.parseVersion = void 0;
|
|
4
|
+
const errors_1 = require("./errors");
|
|
4
5
|
const types_1 = require("./types");
|
|
5
6
|
const SUPPORTED_MINORS = [0, 1, 2];
|
|
6
7
|
function isSupportedMinor(minor) {
|
|
@@ -16,23 +17,23 @@ function warnOnce(warnings, seen, field, message) {
|
|
|
16
17
|
function parseVersion(version) {
|
|
17
18
|
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
|
|
18
19
|
if (!match) {
|
|
19
|
-
throw new
|
|
20
|
+
throw new errors_1.OpenApiVersionConfigError(`Invalid OpenAPI version: "${version}". Expected format: "3.x.x"`);
|
|
20
21
|
}
|
|
21
22
|
const major = Number(match[1]);
|
|
22
23
|
const minor = Number(match[2]);
|
|
23
24
|
const patch = Number(match[3]);
|
|
24
25
|
if (major !== 3) {
|
|
25
|
-
throw new
|
|
26
|
+
throw new errors_1.OpenApiVersionConfigError(`Unsupported OpenAPI major version: ${major}. Only version 3.x is supported.`);
|
|
26
27
|
}
|
|
27
28
|
if (!isSupportedMinor(minor)) {
|
|
28
|
-
throw new
|
|
29
|
+
throw new errors_1.OpenApiVersionConfigError(`Unsupported OpenAPI minor version: 3.${minor}. Supported: 3.0.x, 3.1.x, 3.2.x`);
|
|
29
30
|
}
|
|
30
31
|
return { major, minor, patch };
|
|
31
32
|
}
|
|
32
33
|
exports.parseVersion = parseVersion;
|
|
33
34
|
function transformOpenApiSpec(spec, config = types_1.DEFAULT_CONFIG) {
|
|
34
35
|
if (typeof spec.openapi !== 'string') {
|
|
35
|
-
throw new
|
|
36
|
+
throw new errors_1.OpenApiTransformError('Invalid OpenAPI spec: missing required string field "openapi".');
|
|
36
37
|
}
|
|
37
38
|
const sourceVersion = parseVersion(spec.openapi);
|
|
38
39
|
const targetVersion = parseVersion(config.version);
|
|
@@ -45,7 +46,9 @@ function transformOpenApiSpec(spec, config = types_1.DEFAULT_CONFIG) {
|
|
|
45
46
|
if (sourceMinor === targetMinor) {
|
|
46
47
|
return { spec: out, warnings };
|
|
47
48
|
}
|
|
48
|
-
if (config.transformNullable !== false &&
|
|
49
|
+
if (config.transformNullable !== false &&
|
|
50
|
+
targetMinor >= 1 &&
|
|
51
|
+
sourceMinor < 1) {
|
|
49
52
|
upgradeNullable(out);
|
|
50
53
|
}
|
|
51
54
|
if (targetMinor < 2 && sourceMinor >= 2) {
|
|
@@ -98,8 +101,10 @@ function downgradeNullable(spec, warnings, seen) {
|
|
|
98
101
|
if (!Array.isArray(s[key]))
|
|
99
102
|
continue;
|
|
100
103
|
const arr = s[key];
|
|
101
|
-
const nullIdx = arr.findIndex(item => typeof item === 'object' &&
|
|
102
|
-
|
|
104
|
+
const nullIdx = arr.findIndex(item => typeof item === 'object' &&
|
|
105
|
+
item !== null &&
|
|
106
|
+
Object.keys(item).length === 1 &&
|
|
107
|
+
item.type === 'null');
|
|
103
108
|
if (nullIdx !== -1) {
|
|
104
109
|
arr.splice(nullIdx, 1);
|
|
105
110
|
s.nullable = true;
|
|
@@ -189,7 +194,9 @@ function strip32Features(spec, targetMinor, warnings, seen) {
|
|
|
189
194
|
for (const tag of spec.tags) {
|
|
190
195
|
if (tag && typeof tag === 'object') {
|
|
191
196
|
const t = tag;
|
|
192
|
-
if (t.summary !== undefined ||
|
|
197
|
+
if (t.summary !== undefined ||
|
|
198
|
+
t.parent !== undefined ||
|
|
199
|
+
t.kind !== undefined) {
|
|
193
200
|
delete t.summary;
|
|
194
201
|
delete t.parent;
|
|
195
202
|
delete t.kind;
|
|
@@ -307,7 +314,9 @@ function walkAllSchemas(spec, visitor) {
|
|
|
307
314
|
visit(p);
|
|
308
315
|
}
|
|
309
316
|
}
|
|
310
|
-
if (schema.items &&
|
|
317
|
+
if (schema.items &&
|
|
318
|
+
typeof schema.items === 'object' &&
|
|
319
|
+
!('$ref' in schema.items)) {
|
|
311
320
|
visit(schema.items);
|
|
312
321
|
}
|
|
313
322
|
for (const key of ['allOf', 'oneOf', 'anyOf', 'prefixItems']) {
|
|
@@ -318,7 +327,9 @@ function walkAllSchemas(spec, visitor) {
|
|
|
318
327
|
}
|
|
319
328
|
}
|
|
320
329
|
}
|
|
321
|
-
if (schema.not &&
|
|
330
|
+
if (schema.not &&
|
|
331
|
+
typeof schema.not === 'object' &&
|
|
332
|
+
!('$ref' in schema.not)) {
|
|
322
333
|
visit(schema.not);
|
|
323
334
|
}
|
|
324
335
|
if (schema.additionalProperties &&
|
|
@@ -332,10 +343,14 @@ function walkAllSchemas(spec, visitor) {
|
|
|
332
343
|
const media = content[mt];
|
|
333
344
|
if (!media || typeof media !== 'object')
|
|
334
345
|
continue;
|
|
335
|
-
if (media.schema &&
|
|
346
|
+
if (media.schema &&
|
|
347
|
+
typeof media.schema === 'object' &&
|
|
348
|
+
!('$ref' in media.schema)) {
|
|
336
349
|
visit(media.schema);
|
|
337
350
|
}
|
|
338
|
-
if (media.itemSchema &&
|
|
351
|
+
if (media.itemSchema &&
|
|
352
|
+
typeof media.itemSchema === 'object' &&
|
|
353
|
+
!('$ref' in media.itemSchema)) {
|
|
339
354
|
visit(media.itemSchema);
|
|
340
355
|
}
|
|
341
356
|
}
|
|
@@ -345,7 +360,9 @@ function walkAllSchemas(spec, visitor) {
|
|
|
345
360
|
if (!p || typeof p !== 'object' || '$ref' in p)
|
|
346
361
|
continue;
|
|
347
362
|
const param = p;
|
|
348
|
-
if (param.schema &&
|
|
363
|
+
if (param.schema &&
|
|
364
|
+
typeof param.schema === 'object' &&
|
|
365
|
+
!('$ref' in param.schema)) {
|
|
349
366
|
visit(param.schema);
|
|
350
367
|
}
|
|
351
368
|
if (param.content && typeof param.content === 'object') {
|
|
@@ -369,7 +386,9 @@ function walkAllSchemas(spec, visitor) {
|
|
|
369
386
|
const p = params[name];
|
|
370
387
|
if (p && typeof p === 'object' && !('$ref' in p)) {
|
|
371
388
|
const param = p;
|
|
372
|
-
if (param.schema &&
|
|
389
|
+
if (param.schema &&
|
|
390
|
+
typeof param.schema === 'object' &&
|
|
391
|
+
!('$ref' in param.schema)) {
|
|
373
392
|
visit(param.schema);
|
|
374
393
|
}
|
|
375
394
|
}
|
|
@@ -404,7 +423,9 @@ function walkAllSchemas(spec, visitor) {
|
|
|
404
423
|
if (!h || typeof h !== 'object' || '$ref' in h)
|
|
405
424
|
continue;
|
|
406
425
|
const header = h;
|
|
407
|
-
if (header.schema &&
|
|
426
|
+
if (header.schema &&
|
|
427
|
+
typeof header.schema === 'object' &&
|
|
428
|
+
!('$ref' in header.schema)) {
|
|
408
429
|
visit(header.schema);
|
|
409
430
|
}
|
|
410
431
|
}
|
|
@@ -419,7 +440,17 @@ function walkAllSchemas(spec, visitor) {
|
|
|
419
440
|
if (Array.isArray(pi.parameters)) {
|
|
420
441
|
visitParams(pi.parameters);
|
|
421
442
|
}
|
|
422
|
-
const verbs = [
|
|
443
|
+
const verbs = [
|
|
444
|
+
'get',
|
|
445
|
+
'post',
|
|
446
|
+
'put',
|
|
447
|
+
'patch',
|
|
448
|
+
'delete',
|
|
449
|
+
'options',
|
|
450
|
+
'head',
|
|
451
|
+
'trace',
|
|
452
|
+
'query',
|
|
453
|
+
];
|
|
423
454
|
for (const verb of verbs) {
|
|
424
455
|
const op = pi[verb];
|
|
425
456
|
if (!op || typeof op !== 'object')
|
|
@@ -465,7 +496,17 @@ function walkAllSchemas(spec, visitor) {
|
|
|
465
496
|
}
|
|
466
497
|
}
|
|
467
498
|
function forEachOperation(spec, visitor) {
|
|
468
|
-
const verbs = [
|
|
499
|
+
const verbs = [
|
|
500
|
+
'get',
|
|
501
|
+
'post',
|
|
502
|
+
'put',
|
|
503
|
+
'patch',
|
|
504
|
+
'delete',
|
|
505
|
+
'options',
|
|
506
|
+
'head',
|
|
507
|
+
'trace',
|
|
508
|
+
'query',
|
|
509
|
+
];
|
|
469
510
|
function visitPathItems(pathsObj, prefix) {
|
|
470
511
|
for (const path in pathsObj) {
|
|
471
512
|
const item = pathsObj[path];
|