specli 0.0.11 → 0.0.13

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.
Files changed (335) hide show
  1. package/README.md +216 -325
  2. package/bin/cli.sh +27 -0
  3. package/dist/{src/ai → ai}/tools.d.ts +0 -1
  4. package/dist/ai/tools.js +161 -0
  5. package/dist/ai/tools.test.d.ts +1 -0
  6. package/dist/ai/tools.test.js +56 -0
  7. package/dist/{src/cli → cli}/auth-requirements.d.ts +0 -1
  8. package/dist/cli/auth-requirements.js +65 -0
  9. package/dist/cli/auth-requirements.test.d.ts +1 -0
  10. package/dist/cli/auth-requirements.test.js +16 -0
  11. package/dist/{src/cli → cli}/auth-schemes.d.ts +0 -1
  12. package/dist/cli/auth-schemes.js +112 -0
  13. package/dist/cli/auth-schemes.test.d.ts +1 -0
  14. package/dist/cli/auth-schemes.test.js +56 -0
  15. package/dist/{src/cli → cli}/capabilities.d.ts +0 -1
  16. package/dist/cli/capabilities.js +41 -0
  17. package/dist/cli/capabilities.test.d.ts +1 -0
  18. package/dist/cli/capabilities.test.js +84 -0
  19. package/dist/{src/cli → cli}/command-id.d.ts +0 -1
  20. package/dist/cli/command-id.js +8 -0
  21. package/dist/cli/command-id.test.d.ts +1 -0
  22. package/dist/cli/command-id.test.js +27 -0
  23. package/dist/{src/cli → cli}/command-index.d.ts +0 -1
  24. package/dist/cli/command-index.js +9 -0
  25. package/dist/{src/cli → cli}/command-model.d.ts +0 -1
  26. package/dist/cli/command-model.js +53 -0
  27. package/dist/cli/command-model.test.d.ts +1 -0
  28. package/dist/cli/command-model.test.js +40 -0
  29. package/dist/{src/cli → cli}/compile.d.ts +0 -1
  30. package/dist/cli/compile.js +79 -0
  31. package/dist/{src/cli → cli}/crypto.d.ts +0 -1
  32. package/dist/cli/crypto.js +9 -0
  33. package/dist/{src/cli → cli}/derive-name.d.ts +0 -1
  34. package/dist/cli/derive-name.js +96 -0
  35. package/dist/{src/cli → cli}/exec.d.ts +0 -1
  36. package/dist/cli/exec.js +50 -0
  37. package/dist/{src/cli → cli}/main.d.ts +0 -1
  38. package/dist/cli/main.js +177 -0
  39. package/dist/{src/cli → cli}/naming.d.ts +0 -1
  40. package/dist/cli/naming.js +191 -0
  41. package/dist/cli/naming.test.d.ts +1 -0
  42. package/dist/cli/naming.test.js +75 -0
  43. package/dist/{src/cli → cli}/operations.d.ts +0 -1
  44. package/dist/cli/operations.js +100 -0
  45. package/dist/cli/operations.test.d.ts +1 -0
  46. package/dist/cli/operations.test.js +51 -0
  47. package/dist/{src/cli → cli}/params.d.ts +0 -1
  48. package/dist/cli/params.js +36 -0
  49. package/dist/cli/params.test.d.ts +1 -0
  50. package/dist/cli/params.test.js +62 -0
  51. package/dist/{src/cli → cli}/pluralize.d.ts +0 -1
  52. package/dist/cli/pluralize.js +38 -0
  53. package/dist/{src/cli → cli}/positional.d.ts +0 -1
  54. package/dist/cli/positional.js +35 -0
  55. package/dist/cli/positional.test.d.ts +1 -0
  56. package/dist/cli/positional.test.js +60 -0
  57. package/dist/{src/cli → cli}/request-body.d.ts +0 -1
  58. package/dist/cli/request-body.js +44 -0
  59. package/dist/cli/request-body.test.d.ts +1 -0
  60. package/dist/cli/request-body.test.js +31 -0
  61. package/dist/{src/cli → cli}/runtime/argv.d.ts +0 -1
  62. package/dist/cli/runtime/argv.js +15 -0
  63. package/dist/{src/cli → cli}/runtime/auth/resolve.d.ts +0 -1
  64. package/dist/cli/runtime/auth/resolve.js +39 -0
  65. package/dist/{src/cli → cli}/runtime/body-flags.d.ts +0 -1
  66. package/dist/cli/runtime/body-flags.js +117 -0
  67. package/dist/cli/runtime/body-flags.test.d.ts +1 -0
  68. package/dist/cli/runtime/body-flags.test.js +192 -0
  69. package/dist/{src/cli → cli}/runtime/body.d.ts +0 -1
  70. package/dist/cli/runtime/body.js +16 -0
  71. package/dist/{src/cli → cli}/runtime/collect.d.ts +0 -1
  72. package/dist/cli/runtime/collect.js +3 -0
  73. package/dist/{src/cli → cli}/runtime/compat.d.ts +0 -1
  74. package/dist/cli/runtime/compat.js +78 -0
  75. package/dist/{src/cli → cli}/runtime/context.d.ts +0 -1
  76. package/dist/cli/runtime/context.js +44 -0
  77. package/dist/{src/cli → cli}/runtime/execute.d.ts +0 -1
  78. package/dist/cli/runtime/execute.js +106 -0
  79. package/dist/{src/cli → cli}/runtime/generated.d.ts +0 -1
  80. package/dist/cli/runtime/generated.js +168 -0
  81. package/dist/{src/cli → cli}/runtime/headers.d.ts +0 -1
  82. package/dist/cli/runtime/headers.js +30 -0
  83. package/dist/{src/cli → cli}/runtime/index.d.ts +0 -1
  84. package/dist/cli/runtime/index.js +3 -0
  85. package/dist/{src/cli → cli}/runtime/profile/secrets.d.ts +0 -1
  86. package/dist/cli/runtime/profile/secrets.js +53 -0
  87. package/dist/{src/cli → cli}/runtime/profile/store.d.ts +0 -1
  88. package/dist/cli/runtime/profile/store.js +63 -0
  89. package/dist/{src/cli → cli}/runtime/request.d.ts +0 -1
  90. package/dist/cli/runtime/request.js +283 -0
  91. package/dist/cli/runtime/request.test.d.ts +1 -0
  92. package/dist/cli/runtime/request.test.js +332 -0
  93. package/dist/{src/cli → cli}/runtime/server-url.d.ts +0 -1
  94. package/dist/cli/runtime/server-url.js +28 -0
  95. package/dist/{src/cli → cli}/runtime/template.d.ts +0 -1
  96. package/dist/cli/runtime/template.js +22 -0
  97. package/dist/cli/runtime/validate/ajv.d.ts +2 -0
  98. package/dist/cli/runtime/validate/ajv.js +11 -0
  99. package/dist/{src/cli → cli}/runtime/validate/coerce.d.ts +0 -1
  100. package/dist/cli/runtime/validate/coerce.js +63 -0
  101. package/dist/cli/runtime/validate/coerce.test.d.ts +1 -0
  102. package/dist/cli/runtime/validate/coerce.test.js +75 -0
  103. package/dist/{src/cli → cli}/runtime/validate/error.d.ts +0 -1
  104. package/dist/cli/runtime/validate/error.js +19 -0
  105. package/dist/{src/cli → cli}/runtime/validate/index.d.ts +0 -1
  106. package/dist/cli/runtime/validate/index.js +4 -0
  107. package/dist/{src/cli → cli}/runtime/validate/schema.d.ts +0 -1
  108. package/dist/cli/runtime/validate/schema.js +38 -0
  109. package/dist/{src/cli → cli}/schema-shape.d.ts +0 -1
  110. package/dist/cli/schema-shape.js +34 -0
  111. package/dist/{src/cli → cli}/schema.d.ts +0 -1
  112. package/dist/cli/schema.js +31 -0
  113. package/dist/{src/cli → cli}/server.d.ts +0 -1
  114. package/dist/cli/server.js +130 -0
  115. package/dist/cli/server.test.d.ts +1 -0
  116. package/dist/cli/server.test.js +49 -0
  117. package/dist/{src/cli → cli}/spec-id.d.ts +0 -1
  118. package/dist/cli/spec-id.js +8 -0
  119. package/dist/{src/cli → cli}/spec-loader.d.ts +0 -1
  120. package/dist/cli/spec-loader.js +40 -0
  121. package/dist/{src/cli → cli}/stable-json.d.ts +0 -1
  122. package/dist/cli/stable-json.js +29 -0
  123. package/dist/{src/cli → cli}/strings.d.ts +0 -1
  124. package/dist/cli/strings.js +20 -0
  125. package/dist/{src/cli → cli}/types.d.ts +0 -1
  126. package/dist/cli/types.js +3 -0
  127. package/dist/cli.d.ts +0 -1
  128. package/dist/cli.js +51 -2324
  129. package/dist/compiled.d.ts +2 -0
  130. package/{src/compiled.ts → dist/compiled.js} +8 -11
  131. package/dist/macros/env.d.ts +10 -0
  132. package/dist/macros/env.js +22 -0
  133. package/dist/macros/spec.d.ts +5 -0
  134. package/dist/macros/spec.js +16 -0
  135. package/package.json +17 -25
  136. package/bin/specli.js +0 -26
  137. package/cli.ts +0 -77
  138. package/dist/cli.d.ts.map +0 -1
  139. package/dist/cli.js.map +0 -53
  140. package/dist/index.d.ts +0 -2
  141. package/dist/index.d.ts.map +0 -1
  142. package/dist/index.js +0 -2032
  143. package/dist/index.js.map +0 -48
  144. package/dist/src/ai/tools.d.ts.map +0 -1
  145. package/dist/src/ai/tools.js +0 -1656
  146. package/dist/src/ai/tools.js.map +0 -45
  147. package/dist/src/cli/auth-requirements.d.ts.map +0 -1
  148. package/dist/src/cli/auth-requirements.js +0 -66
  149. package/dist/src/cli/auth-requirements.js.map +0 -10
  150. package/dist/src/cli/auth-schemes.d.ts.map +0 -1
  151. package/dist/src/cli/auth-schemes.js +0 -116
  152. package/dist/src/cli/auth-schemes.js.map +0 -11
  153. package/dist/src/cli/capabilities.d.ts.map +0 -1
  154. package/dist/src/cli/capabilities.js +0 -45
  155. package/dist/src/cli/capabilities.js.map +0 -10
  156. package/dist/src/cli/command-id.d.ts.map +0 -1
  157. package/dist/src/cli/command-id.js +0 -18
  158. package/dist/src/cli/command-id.js.map +0 -11
  159. package/dist/src/cli/command-index.d.ts.map +0 -1
  160. package/dist/src/cli/command-index.js +0 -15
  161. package/dist/src/cli/command-index.js.map +0 -10
  162. package/dist/src/cli/command-model.d.ts.map +0 -1
  163. package/dist/src/cli/command-model.js +0 -274
  164. package/dist/src/cli/command-model.js.map +0 -18
  165. package/dist/src/cli/compile.d.ts.map +0 -1
  166. package/dist/src/cli/compile.js +0 -146
  167. package/dist/src/cli/compile.js.map +0 -11
  168. package/dist/src/cli/crypto.d.ts.map +0 -1
  169. package/dist/src/cli/crypto.js +0 -15
  170. package/dist/src/cli/crypto.js.map +0 -10
  171. package/dist/src/cli/derive-name.d.ts.map +0 -1
  172. package/dist/src/cli/derive-name.js +0 -70
  173. package/dist/src/cli/derive-name.js.map +0 -10
  174. package/dist/src/cli/exec.d.ts.map +0 -1
  175. package/dist/src/cli/exec.js +0 -2077
  176. package/dist/src/cli/exec.js.map +0 -49
  177. package/dist/src/cli/main.d.ts.map +0 -1
  178. package/dist/src/cli/main.js +0 -2032
  179. package/dist/src/cli/main.js.map +0 -48
  180. package/dist/src/cli/naming.d.ts.map +0 -1
  181. package/dist/src/cli/naming.js +0 -216
  182. package/dist/src/cli/naming.js.map +0 -12
  183. package/dist/src/cli/operations.d.ts.map +0 -1
  184. package/dist/src/cli/operations.js +0 -103
  185. package/dist/src/cli/operations.js.map +0 -10
  186. package/dist/src/cli/params.d.ts.map +0 -1
  187. package/dist/src/cli/params.js +0 -79
  188. package/dist/src/cli/params.js.map +0 -12
  189. package/dist/src/cli/pluralize.d.ts.map +0 -1
  190. package/dist/src/cli/pluralize.js +0 -43
  191. package/dist/src/cli/pluralize.js.map +0 -10
  192. package/dist/src/cli/positional.d.ts.map +0 -1
  193. package/dist/src/cli/positional.js +0 -39
  194. package/dist/src/cli/positional.js.map +0 -10
  195. package/dist/src/cli/request-body.d.ts.map +0 -1
  196. package/dist/src/cli/request-body.js +0 -82
  197. package/dist/src/cli/request-body.js.map +0 -12
  198. package/dist/src/cli/runtime/argv.d.ts.map +0 -1
  199. package/dist/src/cli/runtime/argv.js +0 -22
  200. package/dist/src/cli/runtime/argv.js.map +0 -10
  201. package/dist/src/cli/runtime/auth/resolve.d.ts.map +0 -1
  202. package/dist/src/cli/runtime/auth/resolve.js +0 -38
  203. package/dist/src/cli/runtime/auth/resolve.js.map +0 -10
  204. package/dist/src/cli/runtime/body-flags.d.ts.map +0 -1
  205. package/dist/src/cli/runtime/body-flags.js +0 -86
  206. package/dist/src/cli/runtime/body-flags.js.map +0 -10
  207. package/dist/src/cli/runtime/body.d.ts.map +0 -1
  208. package/dist/src/cli/runtime/body.js +0 -40
  209. package/dist/src/cli/runtime/body.js.map +0 -11
  210. package/dist/src/cli/runtime/collect.d.ts.map +0 -1
  211. package/dist/src/cli/runtime/collect.js +0 -9
  212. package/dist/src/cli/runtime/collect.js.map +0 -10
  213. package/dist/src/cli/runtime/compat.d.ts.map +0 -1
  214. package/dist/src/cli/runtime/compat.js +0 -62
  215. package/dist/src/cli/runtime/compat.js.map +0 -10
  216. package/dist/src/cli/runtime/context.d.ts.map +0 -1
  217. package/dist/src/cli/runtime/context.js +0 -936
  218. package/dist/src/cli/runtime/context.js.map +0 -32
  219. package/dist/src/cli/runtime/execute.d.ts.map +0 -1
  220. package/dist/src/cli/runtime/execute.js +0 -670
  221. package/dist/src/cli/runtime/execute.js.map +0 -22
  222. package/dist/src/cli/runtime/generated.d.ts.map +0 -1
  223. package/dist/src/cli/runtime/generated.js +0 -869
  224. package/dist/src/cli/runtime/generated.js.map +0 -23
  225. package/dist/src/cli/runtime/headers.d.ts.map +0 -1
  226. package/dist/src/cli/runtime/headers.js +0 -36
  227. package/dist/src/cli/runtime/headers.js.map +0 -10
  228. package/dist/src/cli/runtime/index.d.ts.map +0 -1
  229. package/dist/src/cli/runtime/index.js +0 -1808
  230. package/dist/src/cli/runtime/index.js.map +0 -46
  231. package/dist/src/cli/runtime/profile/secrets.d.ts.map +0 -1
  232. package/dist/src/cli/runtime/profile/secrets.js +0 -51
  233. package/dist/src/cli/runtime/profile/secrets.js.map +0 -11
  234. package/dist/src/cli/runtime/profile/store.d.ts.map +0 -1
  235. package/dist/src/cli/runtime/profile/store.js +0 -102
  236. package/dist/src/cli/runtime/profile/store.js.map +0 -11
  237. package/dist/src/cli/runtime/request.d.ts.map +0 -1
  238. package/dist/src/cli/runtime/request.js +0 -571
  239. package/dist/src/cli/runtime/request.js.map +0 -21
  240. package/dist/src/cli/runtime/server-url.d.ts.map +0 -1
  241. package/dist/src/cli/runtime/server-url.js +0 -55
  242. package/dist/src/cli/runtime/server-url.js.map +0 -11
  243. package/dist/src/cli/runtime/template.d.ts.map +0 -1
  244. package/dist/src/cli/runtime/template.js +0 -29
  245. package/dist/src/cli/runtime/template.js.map +0 -10
  246. package/dist/src/cli/runtime/validate/ajv.d.ts +0 -3
  247. package/dist/src/cli/runtime/validate/ajv.d.ts.map +0 -1
  248. package/dist/src/cli/runtime/validate/ajv.js +0 -17
  249. package/dist/src/cli/runtime/validate/ajv.js.map +0 -10
  250. package/dist/src/cli/runtime/validate/coerce.d.ts.map +0 -1
  251. package/dist/src/cli/runtime/validate/coerce.js +0 -60
  252. package/dist/src/cli/runtime/validate/coerce.js.map +0 -10
  253. package/dist/src/cli/runtime/validate/error.d.ts.map +0 -1
  254. package/dist/src/cli/runtime/validate/error.js +0 -21
  255. package/dist/src/cli/runtime/validate/error.js.map +0 -10
  256. package/dist/src/cli/runtime/validate/index.d.ts.map +0 -1
  257. package/dist/src/cli/runtime/validate/index.js +0 -122
  258. package/dist/src/cli/runtime/validate/index.js.map +0 -13
  259. package/dist/src/cli/runtime/validate/schema.d.ts.map +0 -1
  260. package/dist/src/cli/runtime/validate/schema.js +0 -36
  261. package/dist/src/cli/runtime/validate/schema.js.map +0 -10
  262. package/dist/src/cli/schema-shape.d.ts.map +0 -1
  263. package/dist/src/cli/schema-shape.js +0 -41
  264. package/dist/src/cli/schema-shape.js.map +0 -10
  265. package/dist/src/cli/schema.d.ts.map +0 -1
  266. package/dist/src/cli/schema.js +0 -38
  267. package/dist/src/cli/schema.js.map +0 -10
  268. package/dist/src/cli/server.d.ts.map +0 -1
  269. package/dist/src/cli/server.js +0 -64
  270. package/dist/src/cli/server.js.map +0 -11
  271. package/dist/src/cli/spec-id.d.ts.map +0 -1
  272. package/dist/src/cli/spec-id.js +0 -21
  273. package/dist/src/cli/spec-id.js.map +0 -11
  274. package/dist/src/cli/spec-loader.d.ts.map +0 -1
  275. package/dist/src/cli/spec-loader.js +0 -110
  276. package/dist/src/cli/spec-loader.js.map +0 -15
  277. package/dist/src/cli/stable-json.d.ts.map +0 -1
  278. package/dist/src/cli/stable-json.js +0 -35
  279. package/dist/src/cli/stable-json.js.map +0 -10
  280. package/dist/src/cli/strings.d.ts.map +0 -1
  281. package/dist/src/cli/strings.js +0 -16
  282. package/dist/src/cli/strings.js.map +0 -10
  283. package/dist/src/cli/types.d.ts.map +0 -1
  284. package/dist/src/cli/types.js +0 -9
  285. package/dist/src/cli/types.js.map +0 -10
  286. package/index.ts +0 -1
  287. package/src/ai/tools.ts +0 -211
  288. package/src/cli/auth-requirements.ts +0 -91
  289. package/src/cli/auth-schemes.ts +0 -187
  290. package/src/cli/capabilities.ts +0 -88
  291. package/src/cli/command-id.ts +0 -16
  292. package/src/cli/command-index.ts +0 -19
  293. package/src/cli/command-model.ts +0 -128
  294. package/src/cli/compile.ts +0 -101
  295. package/src/cli/crypto.ts +0 -9
  296. package/src/cli/derive-name.ts +0 -101
  297. package/src/cli/exec.ts +0 -72
  298. package/src/cli/main.ts +0 -231
  299. package/src/cli/naming.ts +0 -224
  300. package/src/cli/operations.ts +0 -152
  301. package/src/cli/params.ts +0 -71
  302. package/src/cli/pluralize.ts +0 -41
  303. package/src/cli/positional.ts +0 -75
  304. package/src/cli/request-body.ts +0 -94
  305. package/src/cli/runtime/argv.ts +0 -14
  306. package/src/cli/runtime/auth/resolve.ts +0 -59
  307. package/src/cli/runtime/body-flags.ts +0 -176
  308. package/src/cli/runtime/body.ts +0 -24
  309. package/src/cli/runtime/collect.ts +0 -6
  310. package/src/cli/runtime/compat.ts +0 -89
  311. package/src/cli/runtime/context.ts +0 -62
  312. package/src/cli/runtime/execute.ts +0 -147
  313. package/src/cli/runtime/generated.ts +0 -242
  314. package/src/cli/runtime/headers.ts +0 -37
  315. package/src/cli/runtime/index.ts +0 -3
  316. package/src/cli/runtime/profile/secrets.ts +0 -83
  317. package/src/cli/runtime/profile/store.ts +0 -100
  318. package/src/cli/runtime/request.ts +0 -390
  319. package/src/cli/runtime/server-url.ts +0 -45
  320. package/src/cli/runtime/template.ts +0 -26
  321. package/src/cli/runtime/validate/ajv.ts +0 -13
  322. package/src/cli/runtime/validate/coerce.ts +0 -71
  323. package/src/cli/runtime/validate/error.ts +0 -29
  324. package/src/cli/runtime/validate/index.ts +0 -4
  325. package/src/cli/runtime/validate/schema.ts +0 -54
  326. package/src/cli/schema-shape.ts +0 -36
  327. package/src/cli/schema.ts +0 -76
  328. package/src/cli/server.ts +0 -88
  329. package/src/cli/spec-id.ts +0 -12
  330. package/src/cli/spec-loader.ts +0 -58
  331. package/src/cli/stable-json.ts +0 -35
  332. package/src/cli/strings.ts +0 -21
  333. package/src/cli/types.ts +0 -59
  334. package/src/macros/env.ts +0 -21
  335. package/src/macros/spec.ts +0 -17
package/src/cli/naming.ts DELETED
@@ -1,224 +0,0 @@
1
- import { pluralize } from "./pluralize.ts";
2
- import { kebabCase } from "./strings.ts";
3
- import type { NormalizedOperation } from "./types.ts";
4
-
5
- export type PlannedOperation = NormalizedOperation & {
6
- resource: string;
7
- action: string;
8
- pathArgs: string[];
9
- style: "rest" | "rpc";
10
- canonicalAction: string;
11
- aliasOf?: string;
12
- };
13
-
14
- const GENERIC_TAGS = new Set(["default", "defaults", "api"]);
15
-
16
- function getPathSegments(path: string): string[] {
17
- return path
18
- .split("/")
19
- .map((s) => s.trim())
20
- .filter(Boolean);
21
- }
22
-
23
- function getPathArgs(path: string): string[] {
24
- const args: string[] = [];
25
- const re = /\{([^}]+)\}/g;
26
-
27
- while (true) {
28
- const match = re.exec(path);
29
- if (!match) break;
30
- // biome-ignore lint/style/noNonNullAssertion: unknown
31
- args.push(match[1]!);
32
- }
33
-
34
- return args;
35
- }
36
-
37
- function pickResourceFromTags(tags: string[]): string | undefined {
38
- if (!tags.length) return undefined;
39
- const first = tags[0]?.trim();
40
- if (!first) return undefined;
41
- if (GENERIC_TAGS.has(first.toLowerCase())) return undefined;
42
- return first;
43
- }
44
-
45
- function splitOperationId(operationId: string): {
46
- prefix?: string;
47
- suffix?: string;
48
- } {
49
- const trimmed = operationId.trim();
50
- if (!trimmed) return {};
51
-
52
- // Prefer dot-notation when present: Contacts.List
53
- if (trimmed.includes(".")) {
54
- const [prefix, ...rest] = trimmed.split(".");
55
- return { prefix, suffix: rest.join(".") };
56
- }
57
-
58
- // Try separators: Contacts_List, Contacts__List
59
- if (trimmed.includes("__")) {
60
- const [prefix, ...rest] = trimmed.split("__");
61
- return { prefix, suffix: rest.join("__") };
62
- }
63
-
64
- if (trimmed.includes("_")) {
65
- const [prefix, ...rest] = trimmed.split("_");
66
- return { prefix, suffix: rest.join("_") };
67
- }
68
-
69
- return { suffix: trimmed };
70
- }
71
-
72
- function inferStyle(op: NormalizedOperation): "rest" | "rpc" {
73
- // Path-based RPC convention (common in gRPC-ish HTTP gateways)
74
- // - POST /Contacts.List
75
- // - POST /Contacts/Service.List
76
- if (op.path.includes(".")) return "rpc";
77
-
78
- // operationId dot-notation alone is not enough to call it RPC; many REST APIs
79
- // have dotted ids. We treat dotted operationId as a weak signal.
80
- if (op.operationId?.includes(".") && op.method === "POST") return "rpc";
81
-
82
- return "rest";
83
- }
84
-
85
- function inferResource(op: NormalizedOperation): string {
86
- const tag = pickResourceFromTags(op.tags);
87
- if (tag) return pluralize(kebabCase(tag));
88
-
89
- if (op.operationId) {
90
- const { prefix } = splitOperationId(op.operationId);
91
- if (prefix) {
92
- const fromId = kebabCase(prefix);
93
- if (fromId === "ping") return "ping";
94
- return pluralize(fromId);
95
- }
96
- }
97
-
98
- const segments = getPathSegments(op.path);
99
- let first = segments[0] ?? "api";
100
-
101
- // If first segment is rpc-ish, like Contacts.List, split it.
102
- // biome-ignore lint/style/noNonNullAssertion: split always returns at least one element
103
- first = first.includes(".") ? first.split(".")[0]! : first;
104
-
105
- // Singletons like /ping generally shouldn't become `pings`.
106
- if (first.toLowerCase() === "ping") return "ping";
107
-
108
- // Strip path params if they appear in first segment (rare)
109
- const cleaned = first.replace(/^\{.+\}$/, "");
110
- return pluralize(kebabCase(cleaned || "api"));
111
- }
112
-
113
- function canonicalizeAction(action: string): string {
114
- const a = kebabCase(action);
115
-
116
- // Common RPC verbs -> REST canonical verbs
117
- if (a === "retrieve" || a === "read") return "get";
118
- if (a === "list" || a === "search") return "list";
119
- if (a === "create") return "create";
120
- if (a === "update" || a === "patch") return "update";
121
- if (a === "delete" || a === "remove") return "delete";
122
-
123
- return a;
124
- }
125
-
126
- function inferRestAction(op: NormalizedOperation): string {
127
- // If operationId is present and looks intentional, prefer it.
128
- // This helps with singleton endpoints like GET /ping (Ping.Get) vs collections.
129
- if (op.operationId) {
130
- const { suffix } = splitOperationId(op.operationId);
131
- if (suffix) {
132
- const fromId = canonicalizeAction(suffix);
133
- if (
134
- fromId === "get" ||
135
- fromId === "list" ||
136
- fromId === "create" ||
137
- fromId === "update" ||
138
- fromId === "delete"
139
- ) {
140
- return fromId;
141
- }
142
- }
143
- }
144
-
145
- const method = op.method.toUpperCase();
146
- const args = getPathArgs(op.path);
147
- const hasId = args.length > 0;
148
-
149
- if (method === "GET" && !hasId) return "list";
150
- if (method === "POST" && !hasId) return "create";
151
-
152
- if (method === "GET" && hasId) return "get";
153
- if ((method === "PUT" || method === "PATCH") && hasId) return "update";
154
- if (method === "DELETE" && hasId) return "delete";
155
-
156
- return kebabCase(method);
157
- }
158
-
159
- function inferRpcAction(op: NormalizedOperation): string {
160
- // Prefer operationId suffix: Contacts.List -> list
161
- if (op.operationId) {
162
- const { suffix } = splitOperationId(op.operationId);
163
- if (suffix) return canonicalizeAction(suffix);
164
- }
165
-
166
- // Else take last segment and split by '.'
167
- const segments = getPathSegments(op.path);
168
- const last = segments[segments.length - 1] ?? "";
169
- if (last.includes(".")) {
170
- const part = last.split(".").pop() ?? last;
171
- return canonicalizeAction(part);
172
- }
173
-
174
- return kebabCase(op.method);
175
- }
176
-
177
- export function planOperation(op: NormalizedOperation): PlannedOperation {
178
- const style = inferStyle(op);
179
- const resource = inferResource(op);
180
- const action = style === "rpc" ? inferRpcAction(op) : inferRestAction(op);
181
-
182
- return {
183
- ...op,
184
- key: op.key,
185
- style,
186
- resource,
187
- action,
188
- canonicalAction: action,
189
- pathArgs: getPathArgs(op.path).map((a) => kebabCase(a)),
190
- };
191
- }
192
-
193
- export function planOperations(ops: NormalizedOperation[]): PlannedOperation[] {
194
- const planned = ops.map(planOperation);
195
-
196
- // Stable collision handling: if resource+action repeats, add a suffix.
197
- const counts = new Map<string, number>();
198
- for (const op of planned) {
199
- const key = `${op.resource}:${op.action}`;
200
- counts.set(key, (counts.get(key) ?? 0) + 1);
201
- }
202
-
203
- const seen = new Map<string, number>();
204
- return planned.map((op) => {
205
- const key = `${op.resource}:${op.action}`;
206
- const total = counts.get(key) ?? 0;
207
- if (total <= 1) return op;
208
-
209
- const idx = (seen.get(key) ?? 0) + 1;
210
- seen.set(key, idx);
211
-
212
- const suffix = op.operationId
213
- ? kebabCase(op.operationId)
214
- : kebabCase(`${op.method}-${op.path}`);
215
-
216
- const disambiguatedAction = `${op.action}-${suffix}-${idx}`;
217
-
218
- return {
219
- ...op,
220
- action: disambiguatedAction,
221
- aliasOf: `${op.resource} ${op.canonicalAction}`,
222
- };
223
- });
224
- }
@@ -1,152 +0,0 @@
1
- import type {
2
- NormalizedOperation,
3
- NormalizedParameter,
4
- NormalizedRequestBody,
5
- OpenApiDoc,
6
- } from "./types.ts";
7
-
8
- function operationKey(method: string, path: string): string {
9
- return `${method.toUpperCase()} ${path}`;
10
- }
11
-
12
- const HTTP_METHODS = [
13
- "get",
14
- "post",
15
- "put",
16
- "patch",
17
- "delete",
18
- "options",
19
- "head",
20
- "trace",
21
- ] as const;
22
-
23
- type RawParameter = {
24
- in?: string;
25
- name?: string;
26
- required?: boolean;
27
- description?: string;
28
- schema?: unknown;
29
- };
30
-
31
- type RawRequestBody = {
32
- required?: boolean;
33
- content?: Record<string, { schema?: unknown } | undefined>;
34
- };
35
-
36
- type RawOperation = {
37
- operationId?: string;
38
- tags?: string[];
39
- summary?: string;
40
- description?: string;
41
- deprecated?: boolean;
42
- security?: OpenApiDoc["security"];
43
- parameters?: RawParameter[];
44
- requestBody?: RawRequestBody;
45
- };
46
-
47
- type RawPathItem = {
48
- parameters?: RawParameter[];
49
- } & Partial<Record<(typeof HTTP_METHODS)[number], RawOperation>>;
50
-
51
- function normalizeParam(p: RawParameter): NormalizedParameter | undefined {
52
- if (!p || typeof p !== "object") return undefined;
53
- const loc = p.in;
54
- const name = p.name;
55
- if (
56
- loc !== "path" &&
57
- loc !== "query" &&
58
- loc !== "header" &&
59
- loc !== "cookie"
60
- ) {
61
- return undefined;
62
- }
63
- if (!name) return undefined;
64
-
65
- return {
66
- in: loc,
67
- name,
68
- required: Boolean(p.required || loc === "path"),
69
- description: p.description,
70
- schema: p.schema,
71
- };
72
- }
73
-
74
- function mergeParameters(
75
- pathParams: RawParameter[] | undefined,
76
- opParams: RawParameter[] | undefined,
77
- ): NormalizedParameter[] {
78
- const merged = new Map<string, NormalizedParameter>();
79
-
80
- for (const p of pathParams ?? []) {
81
- const normalized = normalizeParam(p);
82
- if (!normalized) continue;
83
- merged.set(`${normalized.in}:${normalized.name}`, normalized);
84
- }
85
-
86
- for (const p of opParams ?? []) {
87
- const normalized = normalizeParam(p);
88
- if (!normalized) continue;
89
- merged.set(`${normalized.in}:${normalized.name}`, normalized);
90
- }
91
-
92
- return [...merged.values()];
93
- }
94
-
95
- function normalizeRequestBody(
96
- rb: RawRequestBody | undefined,
97
- ): NormalizedRequestBody | undefined {
98
- if (!rb) return undefined;
99
-
100
- const content = rb.content ?? {};
101
- const contentTypes = Object.keys(content);
102
- const schemasByContentType: Record<string, unknown | undefined> = {};
103
-
104
- for (const contentType of contentTypes) {
105
- schemasByContentType[contentType] = content[contentType]?.schema;
106
- }
107
-
108
- return {
109
- required: Boolean(rb.required),
110
- contentTypes,
111
- schemasByContentType,
112
- };
113
- }
114
-
115
- export function indexOperations(doc: OpenApiDoc): NormalizedOperation[] {
116
- const out: NormalizedOperation[] = [];
117
- const paths = doc.paths ?? {};
118
-
119
- for (const [path, rawPathItem] of Object.entries(paths)) {
120
- if (!rawPathItem || typeof rawPathItem !== "object") continue;
121
-
122
- const pathItem = rawPathItem as RawPathItem;
123
-
124
- for (const method of HTTP_METHODS) {
125
- const op = pathItem[method];
126
- if (!op) continue;
127
-
128
- const parameters = mergeParameters(pathItem.parameters, op.parameters);
129
- const normalizedMethod = method.toUpperCase();
130
- out.push({
131
- key: operationKey(normalizedMethod, path),
132
- method: normalizedMethod,
133
- path,
134
- operationId: op.operationId,
135
- tags: op.tags ?? [],
136
- summary: op.summary,
137
- description: op.description,
138
- deprecated: op.deprecated,
139
- security: (op.security ?? doc.security) as OpenApiDoc["security"],
140
- parameters,
141
- requestBody: normalizeRequestBody(op.requestBody),
142
- });
143
- }
144
- }
145
-
146
- out.sort((a, b) => {
147
- if (a.path !== b.path) return a.path.localeCompare(b.path);
148
- return a.method.localeCompare(b.method);
149
- });
150
-
151
- return out;
152
- }
package/src/cli/params.ts DELETED
@@ -1,71 +0,0 @@
1
- import {
2
- getSchemaEnumStrings,
3
- getSchemaFormat,
4
- getSchemaType,
5
- } from "./schema-shape.ts";
6
- import { kebabCase } from "./strings.ts";
7
- import type { NormalizedOperation, NormalizedParameter } from "./types.ts";
8
-
9
- export type ParamType = import("./schema-shape.ts").ParamType;
10
-
11
- export type ParamSpec = {
12
- kind: "positional" | "flag";
13
- in: NormalizedParameter["in"];
14
- name: string;
15
- flag: string;
16
- required: boolean;
17
- description?: string;
18
- type: ParamType;
19
- format?: string;
20
- enum?: string[];
21
-
22
- // Arrays
23
- itemType?: ParamType;
24
- itemFormat?: string;
25
- itemEnum?: string[];
26
-
27
- // Original schema for Ajv validation and future advanced flag expansion.
28
- schema?: import("./types.ts").JsonSchema;
29
- };
30
-
31
- export function deriveParamSpecs(op: NormalizedOperation): ParamSpec[] {
32
- const out: ParamSpec[] = [];
33
-
34
- for (const p of op.parameters) {
35
- const flag = `--${kebabCase(p.name)}`;
36
- const type = getSchemaType(p.schema);
37
- const schemaObj =
38
- p.schema && typeof p.schema === "object"
39
- ? (p.schema as import("./types.ts").JsonSchema)
40
- : undefined;
41
-
42
- const itemsSchema =
43
- schemaObj && type === "array" && typeof schemaObj.items === "object"
44
- ? (schemaObj.items as unknown)
45
- : undefined;
46
-
47
- out.push({
48
- kind: p.in === "path" ? "positional" : "flag",
49
- in: p.in,
50
- name: p.name,
51
- flag,
52
- required: p.required,
53
- description: p.description,
54
- type,
55
- format: getSchemaFormat(p.schema),
56
- enum: getSchemaEnumStrings(p.schema),
57
- itemType: type === "array" ? getSchemaType(itemsSchema) : undefined,
58
- itemFormat: type === "array" ? getSchemaFormat(itemsSchema) : undefined,
59
- itemEnum:
60
- type === "array" ? getSchemaEnumStrings(itemsSchema) : undefined,
61
- schema: schemaObj,
62
- });
63
- }
64
-
65
- out.sort((a, b) => {
66
- if (a.in !== b.in) return a.in.localeCompare(b.in);
67
- return a.name.localeCompare(b.name);
68
- });
69
-
70
- return out;
71
- }
@@ -1,41 +0,0 @@
1
- const IRREGULAR: Record<string, string> = {
2
- person: "people",
3
- man: "men",
4
- woman: "women",
5
- child: "children",
6
- tooth: "teeth",
7
- foot: "feet",
8
- mouse: "mice",
9
- goose: "geese",
10
- };
11
-
12
- const UNCOUNTABLE = new Set([
13
- "metadata",
14
- "information",
15
- "equipment",
16
- "money",
17
- "series",
18
- "species",
19
- ]);
20
-
21
- export function pluralize(word: string): string {
22
- const w = word.trim();
23
- if (!w) return w;
24
-
25
- const lower = w.toLowerCase();
26
- if (UNCOUNTABLE.has(lower)) return lower;
27
- if (IRREGULAR[lower]) return IRREGULAR[lower];
28
-
29
- // already plural-ish
30
- if (lower.endsWith("s")) return lower;
31
-
32
- if (/[bcdfghjklmnpqrstvwxyz]y$/.test(lower)) {
33
- return lower.replace(/y$/, "ies");
34
- }
35
-
36
- if (/(ch|sh|x|z)$/.test(lower)) {
37
- return `${lower}es`;
38
- }
39
-
40
- return `${lower}s`;
41
- }
@@ -1,75 +0,0 @@
1
- import type { ParamSpec } from "./params.ts";
2
-
3
- export type ActionShapeForCli = {
4
- pathArgs: string[];
5
- params: ParamSpec[];
6
- };
7
-
8
- export type PositionalArg = {
9
- name: string;
10
- required: boolean;
11
- description?: string;
12
- type: import("./schema-shape.ts").ParamType;
13
- format?: string;
14
- enum?: string[];
15
- };
16
-
17
- export type FlagsIndex = {
18
- flags: Array<
19
- Pick<
20
- import("./params.ts").ParamSpec,
21
- | "in"
22
- | "name"
23
- | "flag"
24
- | "required"
25
- | "description"
26
- | "type"
27
- | "format"
28
- | "enum"
29
- | "itemType"
30
- | "itemFormat"
31
- | "itemEnum"
32
- >
33
- >;
34
- };
35
-
36
- export function derivePositionals(action: ActionShapeForCli): PositionalArg[] {
37
- const byName = new Map<string, PositionalArg>();
38
-
39
- // Use pathArgs order; match metadata from params when available.
40
- for (const name of action.pathArgs) {
41
- const p = action.params.find(
42
- (x: ParamSpec) => x.in === "path" && x.name === name,
43
- );
44
- byName.set(name, {
45
- name,
46
- required: true,
47
- description: p?.description,
48
- type: p?.type ?? "unknown",
49
- format: p?.format,
50
- enum: p?.enum,
51
- });
52
- }
53
-
54
- return [...byName.values()];
55
- }
56
-
57
- export function deriveFlags(action: ActionShapeForCli): FlagsIndex {
58
- return {
59
- flags: action.params
60
- .filter((p: ParamSpec) => p.kind === "flag")
61
- .map((p: ParamSpec) => ({
62
- in: p.in,
63
- name: p.name,
64
- flag: p.flag,
65
- required: p.required,
66
- description: p.description,
67
- type: p.type,
68
- format: p.format,
69
- enum: p.enum,
70
- itemType: p.itemType,
71
- itemFormat: p.itemFormat,
72
- itemEnum: p.itemEnum,
73
- })),
74
- };
75
- }
@@ -1,94 +0,0 @@
1
- import {
2
- getSchemaEnumStrings,
3
- getSchemaFormat,
4
- getSchemaType,
5
- } from "./schema-shape.ts";
6
- import type {
7
- JsonSchema,
8
- NormalizedOperation,
9
- NormalizedRequestBody,
10
- } from "./types.ts";
11
- import { isJsonSchema } from "./types.ts";
12
-
13
- export type RequestBodyContent = {
14
- contentType: string;
15
- required: boolean;
16
- schemaType: import("./schema-shape.ts").ParamType;
17
- schemaFormat?: string;
18
- schemaEnum?: string[];
19
- };
20
-
21
- export type RequestBodyInfo = {
22
- required: boolean;
23
- content: RequestBodyContent[];
24
- // Convenience flags for later arg generation.
25
- hasJson: boolean;
26
- hasFormUrlEncoded: boolean;
27
- hasMultipart: boolean;
28
-
29
- // Phase 1 planning: supported generic body inputs.
30
- bodyFlags: string[];
31
- preferredContentType?: string;
32
-
33
- // Original JSON Schema (for expanded flags + validation)
34
- preferredSchema?: JsonSchema;
35
- };
36
-
37
- function getRequestBody(
38
- op: NormalizedOperation,
39
- ): NormalizedRequestBody | undefined {
40
- return op.requestBody;
41
- }
42
-
43
- export function deriveRequestBodyInfo(
44
- op: NormalizedOperation,
45
- ): RequestBodyInfo | undefined {
46
- const rb = getRequestBody(op);
47
- if (!rb) return undefined;
48
-
49
- const content: RequestBodyContent[] = [];
50
- for (const contentType of rb.contentTypes) {
51
- const schema = rb.schemasByContentType[contentType];
52
- content.push({
53
- contentType,
54
- required: rb.required,
55
- schemaType: getSchemaType(schema),
56
- schemaFormat: getSchemaFormat(schema),
57
- schemaEnum: getSchemaEnumStrings(schema),
58
- });
59
- }
60
-
61
- content.sort((a, b) => a.contentType.localeCompare(b.contentType));
62
-
63
- const hasJson = content.some((c) => c.contentType.includes("json"));
64
- const hasFormUrlEncoded = content.some(
65
- (c) => c.contentType === "application/x-www-form-urlencoded",
66
- );
67
- const hasMultipart = content.some((c) =>
68
- c.contentType.startsWith("multipart/"),
69
- );
70
-
71
- const bodyFlags = ["--data", "--file"]; // always available when requestBody exists
72
-
73
- const preferredContentType =
74
- content.find((c) => c.contentType === "application/json")?.contentType ??
75
- content.find((c) => c.contentType.includes("json"))?.contentType ??
76
- content[0]?.contentType;
77
-
78
- const preferredSchema = preferredContentType
79
- ? rb.schemasByContentType[preferredContentType]
80
- : undefined;
81
-
82
- return {
83
- required: rb.required,
84
- content,
85
- hasJson,
86
- hasFormUrlEncoded,
87
- hasMultipart,
88
- bodyFlags,
89
- preferredContentType,
90
- preferredSchema: isJsonSchema(preferredSchema)
91
- ? preferredSchema
92
- : undefined,
93
- };
94
- }
@@ -1,14 +0,0 @@
1
- export function getArgValue(argv: string[], key: string): string | undefined {
2
- for (let i = 0; i < argv.length; i++) {
3
- const a = argv[i];
4
- if (!a) continue;
5
-
6
- if (a === key) return argv[i + 1];
7
- if (a.startsWith(`${key}=`)) return a.slice(key.length + 1);
8
- }
9
- return undefined;
10
- }
11
-
12
- export function hasAnyArg(argv: string[], names: string[]): boolean {
13
- return argv.some((a) => a && names.includes(a));
14
- }