openapi-jsonrpc-jsdoc 1.4.1 → 1.4.3
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 +4 -6
- package/index.mjs +166 -67
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ npm i openapi-jsonrpc-jsdoc
|
|
|
8
8
|
|
|
9
9
|
## Examples
|
|
10
10
|
|
|
11
|
-
###
|
|
11
|
+
### Usage Example
|
|
12
12
|
|
|
13
13
|
```js
|
|
14
14
|
// api/api-v1.js
|
|
@@ -28,11 +28,9 @@ module.exports = (parameters) => {
|
|
|
28
28
|
}
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
###
|
|
31
|
+
### Generate OpenAPI JSON
|
|
32
32
|
|
|
33
33
|
```js
|
|
34
|
-
// index.js
|
|
35
|
-
const fs = require('fs');
|
|
36
34
|
const openapiJSONRpcJSDoc = require('openapi-jsonrpc-jsdoc');
|
|
37
35
|
openapiJSONRpcJSDoc({
|
|
38
36
|
api: '/',
|
|
@@ -44,7 +42,7 @@ openapiJSONRpcJSDoc({
|
|
|
44
42
|
packageUrl: './package.json',
|
|
45
43
|
files: './api/*.js',
|
|
46
44
|
}).then(data => {
|
|
47
|
-
|
|
45
|
+
JSON.stringify(data, null, 2);// openapi.json
|
|
48
46
|
});
|
|
49
47
|
```
|
|
50
48
|
|
|
@@ -54,7 +52,7 @@ openapiJSONRpcJSDoc({
|
|
|
54
52
|
```json
|
|
55
53
|
{
|
|
56
54
|
"x-send-defaults": true,
|
|
57
|
-
"openapi": "3.
|
|
55
|
+
"openapi": "3.1.0",
|
|
58
56
|
"x-api-id": "json-rpc-example",
|
|
59
57
|
"x-headers": [],
|
|
60
58
|
"x-explorer-enabled": true,
|
package/index.mjs
CHANGED
|
@@ -1,6 +1,139 @@
|
|
|
1
1
|
import jsdoc from 'jsdoc-api';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
function extractName(name) {
|
|
4
|
+
try {
|
|
5
|
+
return name.match(/\.(.+)/i)[1]; //eslint-disable-line
|
|
6
|
+
} catch {
|
|
7
|
+
return name;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function extractTypeName(names = []) {
|
|
12
|
+
let typeName;
|
|
13
|
+
if (names.length === 0) {
|
|
14
|
+
typeName = 'null';
|
|
15
|
+
} else if (names.length === 1) {
|
|
16
|
+
[typeName] = names;
|
|
17
|
+
} else {
|
|
18
|
+
typeName = 'enum';
|
|
19
|
+
}
|
|
20
|
+
return typeName;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolveSchemaFromTypeNames(names) {
|
|
24
|
+
let type;
|
|
25
|
+
let format;
|
|
26
|
+
let items;
|
|
27
|
+
let nullable;
|
|
28
|
+
let constant;
|
|
29
|
+
let enumData;
|
|
30
|
+
|
|
31
|
+
switch (extractTypeName(names)) {
|
|
32
|
+
case 'Array.<string>': {
|
|
33
|
+
type = 'array';
|
|
34
|
+
items = {type: 'string'};
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case 'Array.<number>': {
|
|
38
|
+
type = 'array';
|
|
39
|
+
items = {type: 'number'};
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case 'URL': {
|
|
43
|
+
type = 'string';
|
|
44
|
+
format = 'url';
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case 'String':
|
|
48
|
+
case 'string': {
|
|
49
|
+
type = 'string';
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case 'Number':
|
|
53
|
+
case 'number': {
|
|
54
|
+
type = 'number';
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case 'Boolean':
|
|
58
|
+
case 'boolean': {
|
|
59
|
+
type = 'boolean';
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case 'Date': {
|
|
63
|
+
type = 'string';
|
|
64
|
+
format = 'date-time';
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
case 'enum': {
|
|
68
|
+
enumData = names;
|
|
69
|
+
if (enumData.includes('null')) {
|
|
70
|
+
nullable = true;
|
|
71
|
+
enumData = enumData.filter(n => n !== 'null');
|
|
72
|
+
}
|
|
73
|
+
enumData = enumData.map((n) => {
|
|
74
|
+
if (!Number.isNaN(Number(n))) {
|
|
75
|
+
return Number(n);
|
|
76
|
+
}
|
|
77
|
+
return n;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (enumData.every(n => Number.isInteger(Number(n)))) {
|
|
81
|
+
type = 'integer';
|
|
82
|
+
} else if (enumData.every(n => !Number.isNaN(Number(n)))) {
|
|
83
|
+
type = 'number';
|
|
84
|
+
} else if (enumData.every(n => n?.toLowerCase() === 'boolean')) {
|
|
85
|
+
type = 'boolean';
|
|
86
|
+
} else if (enumData.every(n => n?.toLowerCase() === 'number')) {
|
|
87
|
+
type = 'number';
|
|
88
|
+
} else if (enumData.every(n => n?.toLowerCase() === 'string')) {
|
|
89
|
+
type = 'string';
|
|
90
|
+
} else if (enumData.every(n => n?.toLowerCase() === 'date')) {
|
|
91
|
+
type = 'string';
|
|
92
|
+
format = 'date-time';
|
|
93
|
+
} else if (enumData.every(n => n === 'URL')) {
|
|
94
|
+
type = 'string';
|
|
95
|
+
format = 'url';
|
|
96
|
+
} else if (enumData.every(n => n?.toLowerCase() === 'true')) {
|
|
97
|
+
type = 'boolean';
|
|
98
|
+
constant = true;
|
|
99
|
+
} else if (enumData.every(n => n?.toLowerCase() === 'false')) {
|
|
100
|
+
type = 'boolean';
|
|
101
|
+
constant = false;
|
|
102
|
+
} else {
|
|
103
|
+
type = 'string';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (enumData?.length === 1) {
|
|
107
|
+
if (!format) {
|
|
108
|
+
[format] = enumData;
|
|
109
|
+
}
|
|
110
|
+
enumData = undefined;
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
default: {
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
type,
|
|
121
|
+
format,
|
|
122
|
+
nullable,
|
|
123
|
+
items,
|
|
124
|
+
constant,
|
|
125
|
+
enumData,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export default async function openapiJsonrpcJsdoc({
|
|
130
|
+
openapi = '3.1.0',
|
|
131
|
+
files,
|
|
132
|
+
securitySchemes = {},
|
|
133
|
+
packageUrl,
|
|
134
|
+
servers,
|
|
135
|
+
api = '/',
|
|
136
|
+
}) {
|
|
4
137
|
const allData = await jsdoc.explain({
|
|
5
138
|
files: Array.isArray(files) ? files : [files],
|
|
6
139
|
package: packageUrl,
|
|
@@ -9,12 +142,13 @@ export default async function openapiJsonrpcJsdoc({ files, securitySchemes = {},
|
|
|
9
142
|
undocumented: false,
|
|
10
143
|
allowUnknownTags: true,
|
|
11
144
|
dictionaries: ['jsdoc'],
|
|
145
|
+
cache: true,
|
|
12
146
|
});
|
|
13
147
|
const package_ = allData.find(item => item.kind === 'package');
|
|
14
148
|
const documents = allData.filter(item => item.kind !== 'package');
|
|
15
149
|
const temporaryDocument = {
|
|
150
|
+
'openapi': openapi,
|
|
16
151
|
'x-send-defaults': true,
|
|
17
|
-
'openapi': '3.0.0',
|
|
18
152
|
'x-api-id': 'json-rpc-example',
|
|
19
153
|
'x-headers': [],
|
|
20
154
|
'x-explorer-enabled': true,
|
|
@@ -52,11 +186,7 @@ export default async function openapiJsonrpcJsdoc({ files, securitySchemes = {},
|
|
|
52
186
|
},
|
|
53
187
|
}
|
|
54
188
|
},
|
|
55
|
-
'security': [
|
|
56
|
-
{
|
|
57
|
-
BasicAuth: [],
|
|
58
|
-
},
|
|
59
|
-
],
|
|
189
|
+
'security': Object.keys(securitySchemes).map(val => ({ [val]: [] })),
|
|
60
190
|
'tags': [],
|
|
61
191
|
};
|
|
62
192
|
const requiredSchema = ['method', 'jsonrpc'];
|
|
@@ -110,6 +240,7 @@ export default async function openapiJsonrpcJsdoc({ files, securitySchemes = {},
|
|
|
110
240
|
}
|
|
111
241
|
},
|
|
112
242
|
requestBody: {
|
|
243
|
+
required: true,
|
|
113
244
|
content: {
|
|
114
245
|
'application/json': {
|
|
115
246
|
schema: {
|
|
@@ -122,9 +253,7 @@ export default async function openapiJsonrpcJsdoc({ files, securitySchemes = {},
|
|
|
122
253
|
description: `API method ${apiName}`,
|
|
123
254
|
},
|
|
124
255
|
id: {
|
|
125
|
-
type: '
|
|
126
|
-
default: 1,
|
|
127
|
-
format: 'int32',
|
|
256
|
+
type: ['string'],
|
|
128
257
|
description: 'Request ID',
|
|
129
258
|
},
|
|
130
259
|
jsonrpc: {
|
|
@@ -157,61 +286,38 @@ export default async function openapiJsonrpcJsdoc({ files, securitySchemes = {},
|
|
|
157
286
|
if (parameter.type.names[0] === 'object') {
|
|
158
287
|
return accumulator;
|
|
159
288
|
}
|
|
160
|
-
let type;
|
|
161
|
-
if (parameter.type.names.length === 0) {
|
|
162
|
-
type = 'null';
|
|
163
|
-
} else if (parameter.type.names.length === 1) {
|
|
164
|
-
type = parameter.type.names[0];
|
|
165
|
-
} else {
|
|
166
|
-
type = 'enum';
|
|
167
|
-
}
|
|
168
|
-
let items;
|
|
169
|
-
let enumData;
|
|
170
|
-
let oneOf;
|
|
171
289
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
type = 'array';
|
|
175
|
-
items = { type: 'string' };
|
|
176
|
-
break;
|
|
177
|
-
}
|
|
178
|
-
case 'Array.<number>': {
|
|
179
|
-
type = 'array';
|
|
180
|
-
items = { type: 'number' };
|
|
181
|
-
break;
|
|
182
|
-
}
|
|
183
|
-
case 'enum': {
|
|
184
|
-
enumData = parameter.type.names;
|
|
185
|
-
oneOf = parameter.type.names.map((n) => {
|
|
186
|
-
if (!Number.isNaN(Number(n))) {
|
|
187
|
-
return Number(n);
|
|
188
|
-
}
|
|
189
|
-
return n;
|
|
190
|
-
});
|
|
191
|
-
if (parameter.type.names.every(n => Number.isInteger(Number(n)))) {
|
|
192
|
-
type = 'integer';
|
|
193
|
-
} else if (parameter.type.names.every(n => !Number.isNaN(Number(n)))) {
|
|
194
|
-
type = 'number';
|
|
195
|
-
} else {
|
|
196
|
-
type = 'string';
|
|
197
|
-
}
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
default: {
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
290
|
+
const name = extractName(parameter.name);
|
|
291
|
+
accumulator.properties[name] = accumulator.properties[name] ?? {};
|
|
204
292
|
const description = parameter.description;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
293
|
+
const defaultValue = parameter.defaultvalue;
|
|
294
|
+
|
|
295
|
+
const {
|
|
296
|
+
items,
|
|
297
|
+
constant,
|
|
298
|
+
enumData,
|
|
299
|
+
type,
|
|
300
|
+
format,
|
|
301
|
+
nullable,
|
|
302
|
+
} = resolveSchemaFromTypeNames(parameter.type.names)
|
|
211
303
|
if (!parameter.optional) {
|
|
212
304
|
accumulator.required.push(name);
|
|
213
305
|
}
|
|
214
|
-
|
|
306
|
+
if (nullable) {
|
|
307
|
+
accumulator.properties[name].nullable = nullable;
|
|
308
|
+
}
|
|
309
|
+
if (defaultValue) {
|
|
310
|
+
accumulator.properties[name].default = defaultValue;
|
|
311
|
+
}
|
|
312
|
+
if (constant) {
|
|
313
|
+
accumulator.properties[name].const = constant;
|
|
314
|
+
}
|
|
315
|
+
if (format) {
|
|
316
|
+
accumulator.properties[name].format = format;
|
|
317
|
+
}
|
|
318
|
+
if (enumData) {
|
|
319
|
+
accumulator.properties[name].enum = enumData;
|
|
320
|
+
}
|
|
215
321
|
if (type) {
|
|
216
322
|
accumulator.properties[name].type = type;
|
|
217
323
|
}
|
|
@@ -221,13 +327,6 @@ export default async function openapiJsonrpcJsdoc({ files, securitySchemes = {},
|
|
|
221
327
|
if (items) {
|
|
222
328
|
accumulator.properties[name].items = items;
|
|
223
329
|
}
|
|
224
|
-
if (enumData) {
|
|
225
|
-
accumulator.properties[name].enum = enumData;
|
|
226
|
-
}
|
|
227
|
-
if (oneOf) {
|
|
228
|
-
accumulator.properties[name].oneOf = oneOf;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
330
|
return accumulator;
|
|
232
331
|
},
|
|
233
332
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openapi-jsonrpc-jsdoc",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
4
4
|
"description": "Transform JSDoc-annotated JSON-RPC 2.0 methods into OpenAPI specifications.",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -33,8 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"ava": "~6.4.1",
|
|
36
|
-
"express": "~5.2.1"
|
|
37
|
-
"express-openapi-validator": "~5.6.2"
|
|
36
|
+
"express": "~5.2.1"
|
|
38
37
|
},
|
|
39
38
|
"engines": {
|
|
40
39
|
"node": ">= 22"
|