@adhd/apigen-plugin-openapi 0.2.0 → 0.2.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/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 0.2.2 (2026-08-07)
2
+
3
+
4
+ ### 🚀 Features
5
+
6
+ - **apigen:** generic batch/bulk fan-out operations (FEAT-APIGEN-BULK-OPS-001)
7
+
8
+
9
+ ### 🩹 Fixes
10
+
11
+ - **apigen-cli:** restore 2768 files mass-deleted by 0117eb22 (BUG-APIGEN-052)
12
+
13
+
14
+ ### ❤️ Thank You
15
+
16
+ - parity-harness-self-test
17
+ - pseudosky
18
+
19
+ ## 0.2.1 (2026-07-28)
20
+
21
+ This was a version bump only for apigen-plugin-openapi to align it with other projects, there were no code changes.
22
+
1
23
  ## 0.1.7 (2026-07-25)
2
24
 
3
25
 
package/index.js CHANGED
@@ -1 +1 @@
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(P).join("")}function g(e){return e.words.join("_")}function w(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 P(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=w(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=T(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 T(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 k(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 k(e,t)}}}};exports.default=h;exports.openapiPlugin=h;
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");function m(e){return{raw:e,words:e.split(/(?=[A-Z])|[-_]/).map(t=>t.toLowerCase()).filter(Boolean)}}function j(e,t,n={}){const r=e.split("/");if(r.length<2||r.some(l=>l.length===0))throw new Error(`syntheticOp: id "${e}" must be "<namespace>/<path...>" with non-empty segments`);const[o,...i]=r,c=o.replace(/^_+/,""),p=n.kind??"action",s={id:e,host:t.host,namespace:m(c),path:i.map(m),kind:p,async:n.async??!1,streaming:n.streaming??!1,safe:n.safe??p==="query",input:n.input??{},output:n.output??{},envelope:n.envelope??{},typeText:n.typeText??null};return n.hasCtx!==void 0&&(s.hasCtx=n.hasCtx),n.transports!==void 0&&(s.transports=n.transports),s}const O={"decimal.js":"Decimal"};function w(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}Object.entries(O).map(([e,t])=>({pattern:new RegExp(`import\\(["'][^"']*${w(e)}[^"']*["']\\)\\.\\w+`,"g"),key:t}));const d=new Set(["string","number","boolean","integer"]);function q(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:n}=t;return typeof n=="string"?d.has(n):Array.isArray(n)?n.every(r=>d.has(r)||r==="null"):Array.isArray(t.enum)&&t.enum.length>0?t.enum.every(r=>["string","number","boolean"].includes(typeof r)):!1}function A(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(q)}const P={"decimal.js":"Decimal"};function k(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}Object.entries(P).map(([e,t])=>({pattern:new RegExp(`import\\(["'][^"']*${k(e)}[^"']*["']\\)\\.\\w+`,"g"),key:t}));function y(e){return e.words.join("-")}function h(e){return e.words.map(_).join("")}function g(e){return e.words.join("_")}function S(e,t={}){var n,r;const o=[e.namespace,...e.path],i=e.safe||A(e.input)?"GET":"POST",c=((r=(n=t.http)==null?void 0:n.verb)==null?void 0:r[e.id])??i,p="/"+o.map(a=>y(a)).join("/"),s=o.map(a=>g(a)).join("_"),l=o.map(a=>g(a)).slice(0,-1).join("."),f=h(o[o.length-2]??o[0]),u=h(o[o.length-1]),v=o.map(a=>y(a));return{http:{verb:c,route:p},mcp:{name:s},grpc:{package:l,service:f,method:u},cli:{path:v}}}function _(e){return e.length===0?e:e[0].toUpperCase()+e.slice(1)}function $(e,t={}){const n=t.title??"API",r=t.version??"0.0.0",o={};for(const i of e){const c=S(i),{verb:p,route:s}=c.http,l=p.toLowerCase(),f={operationId:i.id,summary:i.id,responses:{200:x(i.output)}};if(p==="GET")f.parameters=E(i.input);else{const u=i.input;typeof u=="object"&&u!==null&&Object.keys(u).length>0&&(f.requestBody={required:!0,content:{"application/json":{schema:u}}})}o[s]===void 0&&(o[s]={}),o[s][l]=f}return{openapi:"3.1.0",info:{title:n,version:r},paths:o}}function x(e){return typeof e=="object"&&e!==null&&Object.keys(e).length>0?{description:"Success",content:{"application/json":{schema:e}}}:{description:"Success"}}function E(e){if(typeof e!="object"||e===null||typeof e.properties!="object"||e.properties===null)return[];const t=Array.isArray(e.required)?e.required:[],n=e.properties;return Object.entries(n).map(([r,o])=>({name:r,in:"query",required:t.includes(r),schema:o}))}function T(e,t={}){return[{...j("_meta/openapi",e,{kind:"query",safe:!0,output:{type:"object"},transports:["http"]}),handler:n=>$(e.operations,{title:t.title,version:t.version})}]}const b={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=b;exports.openapiPlugin=b;
package/index.mjs CHANGED
@@ -2,31 +2,78 @@ import "ts-morph";
2
2
  import "node:fs";
3
3
  import "node:path";
4
4
  import "ts-json-schema-generator";
5
- const m = /* @__PURE__ */ new Set(["string", "number", "boolean", "integer"]);
6
- function j(e) {
5
+ function m(e) {
6
+ return {
7
+ raw: e,
8
+ words: e.split(/(?=[A-Z])|[-_]/).map((t) => t.toLowerCase()).filter(Boolean)
9
+ };
10
+ }
11
+ function j(e, t, n = {}) {
12
+ const i = e.split("/");
13
+ if (i.length < 2 || i.some((l) => l.length === 0))
14
+ throw new Error(
15
+ `syntheticOp: id "${e}" must be "<namespace>/<path...>" with non-empty segments`
16
+ );
17
+ const [o, ...r] = i, c = o.replace(/^_+/, ""), p = n.kind ?? "action", s = {
18
+ id: e,
19
+ host: t.host,
20
+ namespace: m(c),
21
+ path: r.map(m),
22
+ kind: p,
23
+ async: n.async ?? !1,
24
+ streaming: n.streaming ?? !1,
25
+ safe: n.safe ?? p === "query",
26
+ input: n.input ?? {},
27
+ output: n.output ?? {},
28
+ envelope: n.envelope ?? {},
29
+ typeText: n.typeText ?? null
30
+ };
31
+ return n.hasCtx !== void 0 && (s.hasCtx = n.hasCtx), n.transports !== void 0 && (s.transports = n.transports), s;
32
+ }
33
+ const v = {
34
+ "decimal.js": "Decimal"
35
+ };
36
+ function O(e) {
37
+ return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
38
+ }
39
+ Object.entries(v).map(([e, t]) => ({
40
+ // Matches: import("...{modSpec}...").<anyMemberName> (single or double quotes)
41
+ // The module specifier appears inside an absolute path, so we allow any
42
+ // characters between the quote and the specifier, and between the specifier
43
+ // and the closing quote. The trailing member accessor (`.default`, `.Decimal`,
44
+ // …) is matched generically (`\w+`) rather than anchored to one spelling —
45
+ // see the doc comment above for why.
46
+ pattern: new RegExp(
47
+ `import\\(["'][^"']*${O(e)}[^"']*["']\\)\\.\\w+`,
48
+ "g"
49
+ ),
50
+ key: t
51
+ }));
52
+ const d = /* @__PURE__ */ new Set(["string", "number", "boolean", "integer"]);
53
+ function w(e) {
7
54
  if (typeof e != "object" || e === null)
8
55
  return !1;
9
56
  const t = e;
10
57
  if (t.$ref !== void 0 || t.oneOf || t.anyOf || t.allOf)
11
58
  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;
59
+ const { type: n } = t;
60
+ return typeof n == "string" ? d.has(n) : Array.isArray(n) ? n.every((i) => d.has(i) || i === "null") : Array.isArray(t.enum) && t.enum.length > 0 ? t.enum.every((i) => ["string", "number", "boolean"].includes(typeof i)) : !1;
14
61
  }
15
- function v(e) {
62
+ function A(e) {
16
63
  if (!e || typeof e != "object")
17
64
  return !1;
18
65
  const t = e.properties;
19
66
  return t === void 0 || typeof t != "object" || t === null ? !1 : Object.values(t).every(
20
- j
67
+ w
21
68
  );
22
69
  }
23
- const O = {
70
+ const q = {
24
71
  "decimal.js": "Decimal"
25
72
  };
26
- function A(e) {
73
+ function k(e) {
27
74
  return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
28
75
  }
29
- Object.entries(O).map(([e, t]) => ({
76
+ Object.entries(q).map(([e, t]) => ({
30
77
  // Matches: import("...{modSpec}...").<anyMemberName> (single or double quotes)
31
78
  // The module specifier appears inside an absolute path, so we allow any
32
79
  // characters between the quote and the specifier, and between the specifier
@@ -34,65 +81,65 @@ Object.entries(O).map(([e, t]) => ({
34
81
  // …) is matched generically (`\w+`) rather than anchored to one spelling —
35
82
  // see the doc comment above for why.
36
83
  pattern: new RegExp(
37
- `import\\(["'][^"']*${A(e)}[^"']*["']\\)\\.\\w+`,
84
+ `import\\(["'][^"']*${k(e)}[^"']*["']\\)\\.\\w+`,
38
85
  "g"
39
86
  ),
40
87
  key: t
41
88
  }));
42
- function d(e) {
43
- return e.words.join("-");
44
- }
45
89
  function y(e) {
46
- return e.words.map(q).join("");
90
+ return e.words.join("-");
47
91
  }
48
92
  function h(e) {
93
+ return e.words.map(P).join("");
94
+ }
95
+ function g(e) {
49
96
  return e.words.join("_");
50
97
  }
51
- function w(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((p) => d(p)).join("/"), a = n.map((p) => h(p)).join("_"), f = n.map((p) => h(p)).slice(0, -1).join("."), u = y(n[n.length - 2] ?? n[0]), s = y(n[n.length - 1]), g = n.map((p) => d(p));
98
+ function x(e, t = {}) {
99
+ var n, i;
100
+ const o = [e.namespace, ...e.path], r = e.safe || A(e.input) ? "GET" : "POST", c = ((i = (n = t.http) == null ? void 0 : n.verb) == null ? void 0 : i[e.id]) ?? r, p = "/" + o.map((a) => y(a)).join("/"), s = o.map((a) => g(a)).join("_"), l = o.map((a) => g(a)).slice(0, -1).join("."), f = h(o[o.length - 2] ?? o[0]), u = h(o[o.length - 1]), b = o.map((a) => y(a));
54
101
  return {
55
- http: { verb: l, route: c },
56
- mcp: { name: a },
57
- grpc: { package: f, service: u, method: s },
58
- cli: { path: g }
102
+ http: { verb: c, route: p },
103
+ mcp: { name: s },
104
+ grpc: { package: l, service: f, method: u },
105
+ cli: { path: b }
59
106
  };
60
107
  }
61
- function q(e) {
108
+ function P(e) {
62
109
  return e.length === 0 ? e : e[0].toUpperCase() + e.slice(1);
63
110
  }
64
- function P(e, t = {}) {
65
- const o = t.title ?? "API", r = t.version ?? "0.0.0", n = {};
66
- for (const i of e) {
67
- const l = w(i), { verb: c, route: a } = l.http, f = c.toLowerCase(), u = {
68
- operationId: i.id,
69
- summary: i.id,
111
+ function $(e, t = {}) {
112
+ const n = t.title ?? "API", i = t.version ?? "0.0.0", o = {};
113
+ for (const r of e) {
114
+ const c = x(r), { verb: p, route: s } = c.http, l = p.toLowerCase(), f = {
115
+ operationId: r.id,
116
+ summary: r.id,
70
117
  responses: {
71
- 200: S(i.output)
118
+ 200: E(r.output)
72
119
  }
73
120
  };
74
- if (c === "GET")
75
- u.parameters = k(i.input);
121
+ if (p === "GET")
122
+ f.parameters = C(r.input);
76
123
  else {
77
- const s = i.input;
78
- typeof s == "object" && s !== null && Object.keys(s).length > 0 && (u.requestBody = {
124
+ const u = r.input;
125
+ typeof u == "object" && u !== null && Object.keys(u).length > 0 && (f.requestBody = {
79
126
  required: !0,
80
127
  content: {
81
128
  "application/json": {
82
- schema: s
129
+ schema: u
83
130
  }
84
131
  }
85
132
  });
86
133
  }
87
- n[a] === void 0 && (n[a] = {}), n[a][f] = u;
134
+ o[s] === void 0 && (o[s] = {}), o[s][l] = f;
88
135
  }
89
136
  return {
90
137
  openapi: "3.1.0",
91
- info: { title: o, version: r },
92
- paths: n
138
+ info: { title: n, version: i },
139
+ paths: o
93
140
  };
94
141
  }
95
- function S(e) {
142
+ function E(e) {
96
143
  return typeof e == "object" && e !== null && Object.keys(e).length > 0 ? {
97
144
  description: "Success",
98
145
  content: {
@@ -100,54 +147,39 @@ function S(e) {
100
147
  }
101
148
  } : { description: "Success" };
102
149
  }
103
- function k(e) {
150
+ function C(e) {
104
151
  if (typeof e != "object" || e === null || typeof e.properties != "object" || e.properties === null)
105
152
  return [];
106
- const t = Array.isArray(e.required) ? e.required : [], o = e.properties;
107
- return Object.entries(o).map(([r, n]) => ({
108
- name: r,
153
+ const t = Array.isArray(e.required) ? e.required : [], n = e.properties;
154
+ return Object.entries(n).map(([i, o]) => ({
155
+ name: i,
109
156
  in: "query",
110
- required: t.includes(r),
111
- schema: n
157
+ required: t.includes(i),
158
+ schema: o
112
159
  }));
113
160
  }
114
- function b(e) {
115
- return {
116
- raw: e,
117
- words: e.split(/(?=[A-Z])|[-_]/).map((t) => t.toLowerCase()).filter(Boolean)
118
- };
119
- }
120
- function E(e, t = {}) {
161
+ function S(e, t = {}) {
121
162
  return [
122
163
  {
123
- // Canonical id — the `_meta` prefix is a convention for meta-endpoints.
124
- id: "_meta/openapi",
125
- host: e.host,
126
- namespace: b("meta"),
127
- path: [b("openapi")],
128
- // query + safe → GET /meta/openapi (SPEC §5)
129
- kind: "query",
130
- async: !1,
131
- streaming: !1,
132
- safe: !0,
133
- // Input: no domain params — this is a zero-arg metadata endpoint.
134
- input: {},
135
- // Output: an object (the full OpenAPI doc shape; not narrowed further
136
- // here because the exact schema varies with the descriptor content).
137
- output: { type: "object" },
138
- envelope: {},
139
- typeText: null,
140
- // Expose only on HTTP — the doc is a tooling artefact (SPEC §7.2b).
141
- transports: ["http"],
164
+ ...j("_meta/openapi", e, {
165
+ // query + safe → GET /meta/openapi (SPEC §5)
166
+ kind: "query",
167
+ safe: !0,
168
+ // Output: an object (the full OpenAPI doc shape; not narrowed further
169
+ // here because the exact schema varies with the descriptor content).
170
+ output: { type: "object" },
171
+ // Expose only on HTTP — the doc is a tooling artefact (SPEC §7.2b).
172
+ transports: ["http"]
173
+ }),
142
174
  // Handler: derive the doc from the descriptor at request time.
143
- handler: (o) => P(e.operations, {
175
+ handler: (n) => $(e.operations, {
144
176
  title: t.title,
145
177
  version: t.version
146
178
  })
147
179
  }
148
180
  ];
149
181
  }
150
- const x = {
182
+ const B = {
151
183
  id: "openapi",
152
184
  description: "Mount plugin: serves the OpenAPI 3.1 doc at GET /meta/openapi",
153
185
  language: "ts",
@@ -162,7 +194,7 @@ const x = {
162
194
  capabilities: {
163
195
  mount: {
164
196
  operations(e, t) {
165
- return E(
197
+ return S(
166
198
  e,
167
199
  t
168
200
  );
@@ -171,6 +203,6 @@ const x = {
171
203
  }
172
204
  };
173
205
  export {
174
- x as default,
175
- x as openapiPlugin
206
+ B as default,
207
+ B as openapiPlugin
176
208
  };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@adhd/apigen-plugin-openapi",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "main": "./index.js",
5
5
  "dependencies": {
6
- "@adhd/apigen-core-client": "^0.2.0",
7
- "@adhd/apigen-codegen-openapi": "^0.2.0"
6
+ "@adhd/apigen-core-client": "^0.3.0",
7
+ "@adhd/apigen-codegen-openapi": "^0.2.2"
8
8
  },
9
9
  "publishConfig": {
10
10
  "access": "public"