@adhd/apigen-plugin-openapi 0.1.1 → 0.1.2
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 +1 -1
- package/index.js +1 -1
- package/index.mjs +89 -41
- package/lib/plugin.d.ts +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -10,5 +10,5 @@ npx @adhd/apigen-cli generate --source ./api.ts --type openapi --out-dir ./out
|
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
```ts
|
|
13
|
-
import { openapiPlugin, type OpenapiOptions } from '@adhd/apigen-plugin-openapi'
|
|
13
|
+
import { openapiPlugin, type OpenapiOptions } from '@adhd/apigen-plugin-openapi';
|
|
14
14
|
```
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});function m(e){return e.words.join("-")}function
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});require("ts-morph");require("node:fs");require("node:path");require("ts-json-schema-generator");const d=new Set(["string","number","boolean","integer"]);function v(e){if(typeof e!="object"||e===null)return!1;const t=e;if(t.$ref!==void 0||t.oneOf||t.anyOf||t.allOf)return!1;const{type:r}=t;return typeof r=="string"?d.has(r):Array.isArray(r)?r.every(o=>d.has(o)||o==="null"):Array.isArray(t.enum)&&t.enum.length>0?t.enum.every(o=>["string","number","boolean"].includes(typeof o)):!1}function O(e){if(!e||typeof e!="object")return!1;const t=e.properties;return t===void 0||typeof t!="object"||t===null?!1:Object.values(t).every(v)}const q={"decimal.js":"Decimal"};function A(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}Object.entries(q).map(([e,t])=>({pattern:new RegExp(`import\\(["'][^"']*${A(e)}[^"']*["']\\)\\.\\w+`,"g"),key:t}));function m(e){return e.words.join("-")}function y(e){return e.words.map(w).join("")}function g(e){return e.words.join("_")}function P(e,t={}){var r,o;const n=[e.namespace,...e.path],i=e.safe||O(e.input)?"GET":"POST",c=((o=(r=t.http)==null?void 0:r.verb)==null?void 0:o[e.id])??i,l="/"+n.map(s=>m(s)).join("/"),a=n.map(s=>g(s)).join("_"),f=n.map(s=>g(s)).slice(0,-1).join("."),u=y(n[n.length-2]??n[0]),p=y(n[n.length-1]),j=n.map(s=>m(s));return{http:{verb:c,route:l},mcp:{name:a},grpc:{package:f,service:u,method:p},cli:{path:j}}}function w(e){return e.length===0?e:e[0].toUpperCase()+e.slice(1)}function S(e,t={}){const r=t.title??"API",o=t.version??"0.0.0",n={};for(const i of e){const c=P(i),{verb:l,route:a}=c.http,f=l.toLowerCase(),u={operationId:i.id,summary:i.id,responses:{200:_(i.output)}};if(l==="GET")u.parameters=k(i.input);else{const p=i.input;typeof p=="object"&&p!==null&&Object.keys(p).length>0&&(u.requestBody={required:!0,content:{"application/json":{schema:p}}})}n[a]===void 0&&(n[a]={}),n[a][f]=u}return{openapi:"3.1.0",info:{title:r,version:o},paths:n}}function _(e){return typeof e=="object"&&e!==null&&Object.keys(e).length>0?{description:"Success",content:{"application/json":{schema:e}}}:{description:"Success"}}function k(e){if(typeof e!="object"||e===null||typeof e.properties!="object"||e.properties===null)return[];const t=Array.isArray(e.required)?e.required:[],r=e.properties;return Object.entries(r).map(([o,n])=>({name:o,in:"query",required:t.includes(o),schema:n}))}function b(e){return{raw:e,words:e.split(/(?=[A-Z])|[-_]/).map(t=>t.toLowerCase()).filter(Boolean)}}function T(e,t={}){return[{id:"_meta/openapi",host:e.host,namespace:b("meta"),path:[b("openapi")],kind:"query",async:!1,streaming:!1,safe:!0,input:{},output:{type:"object"},envelope:{},typeText:null,transports:["http"],handler:r=>S(e.operations,{title:t.title,version:t.version})}]}const h={id:"openapi",description:"Mount plugin: serves the OpenAPI 3.1 doc at GET /meta/openapi",language:"ts",optionsSchema:{type:"object",properties:{title:{type:"string",description:"OpenAPI info.title"},version:{type:"string",description:"OpenAPI info.version"}},additionalProperties:!1},capabilities:{mount:{operations(e,t){return T(e,t)}}}};exports.default=h;exports.openapiPlugin=h;
|
package/index.mjs
CHANGED
|
@@ -1,40 +1,81 @@
|
|
|
1
|
-
|
|
1
|
+
import "ts-morph";
|
|
2
|
+
import "node:fs";
|
|
3
|
+
import "node:path";
|
|
4
|
+
import "ts-json-schema-generator";
|
|
5
|
+
const m = /* @__PURE__ */ new Set(["string", "number", "boolean", "integer"]);
|
|
6
|
+
function j(e) {
|
|
7
|
+
if (typeof e != "object" || e === null)
|
|
8
|
+
return !1;
|
|
9
|
+
const t = e;
|
|
10
|
+
if (t.$ref !== void 0 || t.oneOf || t.anyOf || t.allOf)
|
|
11
|
+
return !1;
|
|
12
|
+
const { type: o } = t;
|
|
13
|
+
return typeof o == "string" ? m.has(o) : Array.isArray(o) ? o.every((r) => m.has(r) || r === "null") : Array.isArray(t.enum) && t.enum.length > 0 ? t.enum.every((r) => ["string", "number", "boolean"].includes(typeof r)) : !1;
|
|
14
|
+
}
|
|
15
|
+
function v(e) {
|
|
16
|
+
if (!e || typeof e != "object")
|
|
17
|
+
return !1;
|
|
18
|
+
const t = e.properties;
|
|
19
|
+
return t === void 0 || typeof t != "object" || t === null ? !1 : Object.values(t).every(
|
|
20
|
+
j
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
const O = {
|
|
24
|
+
"decimal.js": "Decimal"
|
|
25
|
+
};
|
|
26
|
+
function A(e) {
|
|
27
|
+
return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
28
|
+
}
|
|
29
|
+
Object.entries(O).map(([e, t]) => ({
|
|
30
|
+
// Matches: import("...{modSpec}...").<anyMemberName> (single or double quotes)
|
|
31
|
+
// The module specifier appears inside an absolute path, so we allow any
|
|
32
|
+
// characters between the quote and the specifier, and between the specifier
|
|
33
|
+
// and the closing quote. The trailing member accessor (`.default`, `.Decimal`,
|
|
34
|
+
// …) is matched generically (`\w+`) rather than anchored to one spelling —
|
|
35
|
+
// see the doc comment above for why.
|
|
36
|
+
pattern: new RegExp(
|
|
37
|
+
`import\\(["'][^"']*${A(e)}[^"']*["']\\)\\.\\w+`,
|
|
38
|
+
"g"
|
|
39
|
+
),
|
|
40
|
+
key: t
|
|
41
|
+
}));
|
|
42
|
+
function d(e) {
|
|
2
43
|
return e.words.join("-");
|
|
3
44
|
}
|
|
4
45
|
function y(e) {
|
|
5
|
-
return e.words.map(
|
|
46
|
+
return e.words.map(w).join("");
|
|
6
47
|
}
|
|
7
|
-
function
|
|
48
|
+
function h(e) {
|
|
8
49
|
return e.words.join("_");
|
|
9
50
|
}
|
|
10
|
-
function
|
|
11
|
-
var
|
|
12
|
-
const n = [e.namespace, ...e.path],
|
|
51
|
+
function q(e, t = {}) {
|
|
52
|
+
var o, r;
|
|
53
|
+
const n = [e.namespace, ...e.path], i = e.safe || v(e.input) ? "GET" : "POST", l = ((r = (o = t.http) == null ? void 0 : o.verb) == null ? void 0 : r[e.id]) ?? i, c = "/" + n.map((s) => d(s)).join("/"), a = n.map((s) => h(s)).join("_"), f = n.map((s) => h(s)).slice(0, -1).join("."), u = y(n[n.length - 2] ?? n[0]), p = y(n[n.length - 1]), g = n.map((s) => d(s));
|
|
13
54
|
return {
|
|
14
|
-
http: { verb:
|
|
15
|
-
mcp: { name:
|
|
16
|
-
grpc: { package:
|
|
17
|
-
cli: { path:
|
|
55
|
+
http: { verb: l, route: c },
|
|
56
|
+
mcp: { name: a },
|
|
57
|
+
grpc: { package: f, service: u, method: p },
|
|
58
|
+
cli: { path: g }
|
|
18
59
|
};
|
|
19
60
|
}
|
|
20
|
-
function
|
|
61
|
+
function w(e) {
|
|
21
62
|
return e.length === 0 ? e : e[0].toUpperCase() + e.slice(1);
|
|
22
63
|
}
|
|
23
64
|
function P(e, t = {}) {
|
|
24
|
-
const
|
|
25
|
-
for (const
|
|
26
|
-
const l =
|
|
27
|
-
operationId:
|
|
28
|
-
summary:
|
|
65
|
+
const o = t.title ?? "API", r = t.version ?? "0.0.0", n = {};
|
|
66
|
+
for (const i of e) {
|
|
67
|
+
const l = q(i), { verb: c, route: a } = l.http, f = c.toLowerCase(), u = {
|
|
68
|
+
operationId: i.id,
|
|
69
|
+
summary: i.id,
|
|
29
70
|
responses: {
|
|
30
|
-
200:
|
|
71
|
+
200: k(i.output)
|
|
31
72
|
}
|
|
32
73
|
};
|
|
33
|
-
if (
|
|
34
|
-
|
|
74
|
+
if (c === "GET")
|
|
75
|
+
u.parameters = S(i.input);
|
|
35
76
|
else {
|
|
36
|
-
const p =
|
|
37
|
-
typeof p == "object" && p !== null && Object.keys(p).length > 0 && (
|
|
77
|
+
const p = i.input;
|
|
78
|
+
typeof p == "object" && p !== null && Object.keys(p).length > 0 && (u.requestBody = {
|
|
38
79
|
required: !0,
|
|
39
80
|
content: {
|
|
40
81
|
"application/json": {
|
|
@@ -43,15 +84,15 @@ function P(e, t = {}) {
|
|
|
43
84
|
}
|
|
44
85
|
});
|
|
45
86
|
}
|
|
46
|
-
|
|
87
|
+
n[a] === void 0 && (n[a] = {}), n[a][f] = u;
|
|
47
88
|
}
|
|
48
89
|
return {
|
|
49
90
|
openapi: "3.1.0",
|
|
50
|
-
info: { title:
|
|
51
|
-
paths:
|
|
91
|
+
info: { title: o, version: r },
|
|
92
|
+
paths: n
|
|
52
93
|
};
|
|
53
94
|
}
|
|
54
|
-
function
|
|
95
|
+
function k(e) {
|
|
55
96
|
return typeof e == "object" && e !== null && Object.keys(e).length > 0 ? {
|
|
56
97
|
description: "Success",
|
|
57
98
|
content: {
|
|
@@ -59,28 +100,31 @@ function O(e) {
|
|
|
59
100
|
}
|
|
60
101
|
} : { description: "Success" };
|
|
61
102
|
}
|
|
62
|
-
function
|
|
103
|
+
function S(e) {
|
|
63
104
|
if (typeof e != "object" || e === null || typeof e.properties != "object" || e.properties === null)
|
|
64
105
|
return [];
|
|
65
|
-
const t = Array.isArray(e.required) ? e.required : [],
|
|
66
|
-
return Object.entries(
|
|
67
|
-
name:
|
|
106
|
+
const t = Array.isArray(e.required) ? e.required : [], o = e.properties;
|
|
107
|
+
return Object.entries(o).map(([r, n]) => ({
|
|
108
|
+
name: r,
|
|
68
109
|
in: "query",
|
|
69
|
-
required: t.includes(
|
|
70
|
-
schema:
|
|
110
|
+
required: t.includes(r),
|
|
111
|
+
schema: n
|
|
71
112
|
}));
|
|
72
113
|
}
|
|
73
|
-
function
|
|
74
|
-
return {
|
|
114
|
+
function b(e) {
|
|
115
|
+
return {
|
|
116
|
+
raw: e,
|
|
117
|
+
words: e.split(/(?=[A-Z])|[-_]/).map((t) => t.toLowerCase()).filter(Boolean)
|
|
118
|
+
};
|
|
75
119
|
}
|
|
76
|
-
function
|
|
120
|
+
function I(e, t = {}) {
|
|
77
121
|
return [
|
|
78
122
|
{
|
|
79
123
|
// Canonical id — the `_meta` prefix is a convention for meta-endpoints.
|
|
80
124
|
id: "_meta/openapi",
|
|
81
125
|
host: e.host,
|
|
82
|
-
namespace:
|
|
83
|
-
path: [
|
|
126
|
+
namespace: b("meta"),
|
|
127
|
+
path: [b("openapi")],
|
|
84
128
|
// query + safe → GET /meta/openapi (SPEC §5)
|
|
85
129
|
kind: "query",
|
|
86
130
|
async: !1,
|
|
@@ -96,16 +140,17 @@ function A(e, t = {}) {
|
|
|
96
140
|
// Expose only on HTTP — the doc is a tooling artefact (SPEC §7.2b).
|
|
97
141
|
transports: ["http"],
|
|
98
142
|
// Handler: derive the doc from the descriptor at request time.
|
|
99
|
-
handler: (
|
|
143
|
+
handler: (o) => P(e.operations, {
|
|
100
144
|
title: t.title,
|
|
101
145
|
version: t.version
|
|
102
146
|
})
|
|
103
147
|
}
|
|
104
148
|
];
|
|
105
149
|
}
|
|
106
|
-
const
|
|
150
|
+
const x = {
|
|
107
151
|
id: "openapi",
|
|
108
152
|
description: "Mount plugin: serves the OpenAPI 3.1 doc at GET /meta/openapi",
|
|
153
|
+
language: "ts",
|
|
109
154
|
optionsSchema: {
|
|
110
155
|
type: "object",
|
|
111
156
|
properties: {
|
|
@@ -117,12 +162,15 @@ const S = {
|
|
|
117
162
|
capabilities: {
|
|
118
163
|
mount: {
|
|
119
164
|
operations(e, t) {
|
|
120
|
-
return
|
|
165
|
+
return I(
|
|
166
|
+
e,
|
|
167
|
+
t
|
|
168
|
+
);
|
|
121
169
|
}
|
|
122
170
|
}
|
|
123
171
|
}
|
|
124
172
|
};
|
|
125
173
|
export {
|
|
126
|
-
|
|
127
|
-
|
|
174
|
+
x as default,
|
|
175
|
+
x as openapiPlugin
|
|
128
176
|
};
|
package/lib/plugin.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhd/apigen-plugin-openapi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"main": "./index.js",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@adhd/apigen-core": "^0.1.0",
|
|
7
|
-
"@adhd/apigen-codegen-openapi": "^0.1.
|
|
6
|
+
"@adhd/apigen-core-client": "^0.1.0",
|
|
7
|
+
"@adhd/apigen-codegen-openapi": "^0.1.2"
|
|
8
8
|
},
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|