@m6d/cortex-server 1.7.0 → 2.0.0

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 (239) hide show
  1. package/README.md +8 -3
  2. package/contracts/README.md +23 -0
  3. package/contracts/graph/embed.ts +50 -0
  4. package/{src → contracts}/graph/helpers.ts +4 -10
  5. package/{src → contracts}/graph/neo4j.ts +14 -27
  6. package/contracts/graph/schema.ts +65 -0
  7. package/{src → contracts}/graph/types.ts +2 -29
  8. package/contracts/graph.ts +36 -0
  9. package/contracts/runtime.ts +208 -0
  10. package/contracts/wire.ts +143 -0
  11. package/dist/contracts/graph/embed.d.ts +22 -0
  12. package/dist/{src → contracts}/graph/helpers.d.ts +14 -12
  13. package/dist/{src → contracts}/graph/neo4j.d.ts +5 -5
  14. package/dist/contracts/graph/schema.d.ts +62 -0
  15. package/dist/{src → contracts}/graph/types.d.ts +1 -1
  16. package/dist/contracts/graph.d.ts +14 -0
  17. package/dist/contracts/runtime.d.ts +278 -0
  18. package/dist/contracts/wire.d.ts +120 -0
  19. package/dist/index.d.ts +1 -1
  20. package/dist/src/lib/adapters/database/index.d.ts +61 -0
  21. package/dist/src/lib/adapters/database/message-content.d.ts +30 -0
  22. package/dist/src/lib/adapters/database/mssql/attachments.d.ts +100 -0
  23. package/dist/src/lib/adapters/database/mssql/client.d.ts +8 -0
  24. package/dist/src/lib/adapters/database/mssql/index.d.ts +194 -0
  25. package/dist/src/lib/adapters/database/mssql/llm-requests.d.ts +17 -0
  26. package/dist/src/lib/adapters/database/mssql/messages.d.ts +37 -0
  27. package/dist/src/lib/adapters/database/mssql/threads.d.ts +50 -0
  28. package/dist/src/lib/adapters/database/postgres/attachments.d.ts +113 -0
  29. package/dist/src/lib/adapters/database/postgres/client.d.ts +8 -0
  30. package/dist/src/lib/adapters/database/postgres/index.d.ts +207 -0
  31. package/dist/src/lib/adapters/database/postgres/llm-requests.d.ts +17 -0
  32. package/dist/src/lib/adapters/database/postgres/messages.d.ts +37 -0
  33. package/dist/src/lib/adapters/database/postgres/threads.d.ts +50 -0
  34. package/{src/adapters/storage.ts → dist/src/lib/adapters/storage/index.d.ts} +1 -1
  35. package/dist/src/{adapters → lib/adapters/storage}/minio.d.ts +1 -1
  36. package/dist/src/lib/ai/active-runs.d.ts +63 -0
  37. package/dist/src/lib/ai/attachments.d.ts +30 -0
  38. package/dist/src/lib/ai/backend-url.d.ts +16 -0
  39. package/dist/src/lib/ai/cc-runtime.d.ts +61 -0
  40. package/dist/src/lib/ai/commit-gate.d.ts +21 -0
  41. package/dist/src/{ai → lib/ai}/context/builder.d.ts +6 -7
  42. package/dist/src/lib/ai/context/compression-middleware.d.ts +15 -0
  43. package/dist/src/lib/ai/context/compressor.d.ts +7 -0
  44. package/dist/src/lib/ai/context/intra-turn-compressor.d.ts +24 -0
  45. package/dist/src/lib/ai/context/optimize-thread-context.d.ts +9 -0
  46. package/dist/src/lib/ai/context/summarizer.d.ts +5 -0
  47. package/dist/src/{ai → lib/ai}/context/token-estimator.d.ts +5 -8
  48. package/dist/src/{ai → lib/ai}/context/types.d.ts +2 -0
  49. package/dist/src/{ai → lib/ai}/fetch.d.ts +1 -1
  50. package/dist/src/lib/ai/finish-turn.d.ts +23 -0
  51. package/dist/src/lib/ai/helpers.d.ts +38 -0
  52. package/dist/src/lib/ai/index.d.ts +7 -0
  53. package/dist/src/lib/ai/inspector.d.ts +41 -0
  54. package/dist/src/lib/ai/interceptors/request-interceptor.d.ts +13 -0
  55. package/dist/src/{ai → lib/ai}/prompt.d.ts +5 -4
  56. package/dist/src/lib/ai/redis-runs.d.ts +41 -0
  57. package/dist/src/lib/ai/redis-stream-log.d.ts +39 -0
  58. package/dist/src/lib/ai/tools/execute-code.tool.d.ts +18 -0
  59. package/dist/src/lib/ai/tools/query-graph.tool.d.ts +13 -0
  60. package/dist/src/lib/ai/tools/read-attachment.tool.d.ts +21 -0
  61. package/dist/src/lib/ai/tools/search-common.d.ts +17 -0
  62. package/dist/src/lib/ai/tools/search-knowledge.tool.d.ts +15 -0
  63. package/dist/src/lib/ai/tools/search-services.tool.d.ts +12 -0
  64. package/dist/src/lib/ai/tools/search-tools.tool.d.ts +12 -0
  65. package/dist/src/lib/ai/turn-tools.d.ts +39 -0
  66. package/dist/src/lib/ai/vision/rasterize-pdf.d.ts +26 -0
  67. package/dist/src/lib/ai/vision/rasterize-pdf.worker.d.ts +1 -0
  68. package/dist/src/lib/ai/vision/vision-reader.d.ts +45 -0
  69. package/dist/src/lib/cc/client.d.ts +183 -0
  70. package/dist/src/lib/cc/config-cache.d.ts +20 -0
  71. package/dist/src/lib/cc/format.d.ts +23 -0
  72. package/dist/src/lib/cc/registry.d.ts +34 -0
  73. package/dist/src/lib/cc/types.d.ts +27 -0
  74. package/dist/src/lib/config.d.ts +170 -0
  75. package/dist/src/lib/db/drizzle.config.mssql.d.ts +2 -0
  76. package/dist/src/lib/db/drizzle.config.pg.d.ts +2 -0
  77. package/dist/src/lib/db/migrate.d.ts +7 -0
  78. package/dist/src/{db/schema.d.ts → lib/db/schema.mssql.d.ts} +236 -24
  79. package/dist/src/lib/db/schema.pg.d.ts +546 -0
  80. package/dist/src/{factory.d.ts → lib/factory.d.ts} +2 -12
  81. package/dist/src/lib/graph/index.d.ts +10 -0
  82. package/dist/src/{graph → lib/graph}/resolver.d.ts +2 -3
  83. package/dist/src/{index.d.ts → lib/index.d.ts} +3 -4
  84. package/dist/src/lib/redis.d.ts +19 -0
  85. package/dist/src/{routes → lib/routes}/chat.d.ts +1 -1
  86. package/dist/src/{routes → lib/routes}/files.d.ts +1 -1
  87. package/dist/src/lib/routes/owned-thread.d.ts +17 -0
  88. package/dist/src/{routes → lib/routes}/threads.d.ts +1 -1
  89. package/dist/src/{routes → lib/routes}/ws.d.ts +1 -1
  90. package/dist/src/lib/types.d.ts +58 -0
  91. package/dist/src/{ws → lib/ws}/connections.d.ts +2 -1
  92. package/index.ts +1 -1
  93. package/package.json +35 -15
  94. package/src/lib/adapters/database/index.ts +78 -0
  95. package/src/lib/adapters/database/message-content.ts +39 -0
  96. package/src/lib/adapters/database/mssql/attachments.ts +90 -0
  97. package/src/lib/adapters/database/mssql/client.ts +11 -0
  98. package/src/lib/adapters/database/mssql/index.ts +23 -0
  99. package/src/lib/adapters/database/mssql/llm-requests.ts +43 -0
  100. package/src/lib/adapters/database/mssql/messages.ts +92 -0
  101. package/src/lib/adapters/database/mssql/threads.ts +83 -0
  102. package/src/lib/adapters/database/postgres/attachments.ts +88 -0
  103. package/src/lib/adapters/database/postgres/client.ts +11 -0
  104. package/src/lib/adapters/database/postgres/index.ts +23 -0
  105. package/src/lib/adapters/database/postgres/llm-requests.ts +45 -0
  106. package/src/lib/adapters/database/postgres/messages.ts +92 -0
  107. package/src/lib/adapters/database/postgres/threads.ts +84 -0
  108. package/{dist/src/adapters/storage.d.ts → src/lib/adapters/storage/index.ts} +1 -1
  109. package/src/{adapters → lib/adapters/storage}/minio.ts +7 -6
  110. package/src/lib/ai/active-runs.ts +157 -0
  111. package/src/lib/ai/attachments.ts +144 -0
  112. package/src/lib/ai/backend-url.ts +27 -0
  113. package/src/lib/ai/cc-runtime.ts +63 -0
  114. package/src/lib/ai/commit-gate.ts +116 -0
  115. package/src/{ai → lib/ai}/context/builder.ts +12 -14
  116. package/src/lib/ai/context/compression-middleware.ts +44 -0
  117. package/src/lib/ai/context/compressor.ts +267 -0
  118. package/src/lib/ai/context/intra-turn-compressor.ts +181 -0
  119. package/src/lib/ai/context/optimize-thread-context.ts +50 -0
  120. package/src/lib/ai/context/summarizer.ts +48 -0
  121. package/src/lib/ai/context/token-estimator.ts +65 -0
  122. package/src/{ai → lib/ai}/context/types.ts +4 -1
  123. package/src/{ai → lib/ai}/fetch.ts +10 -5
  124. package/src/lib/ai/finish-turn.ts +75 -0
  125. package/src/lib/ai/helpers.ts +100 -0
  126. package/src/lib/ai/index.ts +408 -0
  127. package/src/lib/ai/inspector.ts +112 -0
  128. package/src/lib/ai/interceptors/request-interceptor.ts +89 -0
  129. package/src/lib/ai/prompt.ts +355 -0
  130. package/src/lib/ai/redis-runs.ts +167 -0
  131. package/src/lib/ai/redis-stream-log.ts +219 -0
  132. package/src/lib/ai/tools/execute-code.tool.ts +267 -0
  133. package/src/{ai → lib/ai}/tools/query-graph.tool.ts +7 -8
  134. package/src/lib/ai/tools/read-attachment.tool.ts +59 -0
  135. package/src/lib/ai/tools/search-common.ts +15 -0
  136. package/src/lib/ai/tools/search-knowledge.tool.ts +33 -0
  137. package/src/lib/ai/tools/search-services.tool.ts +31 -0
  138. package/src/lib/ai/tools/search-tools.tool.ts +29 -0
  139. package/src/lib/ai/turn-tools.ts +154 -0
  140. package/src/lib/ai/vision/rasterize-pdf.ts +69 -0
  141. package/src/lib/ai/vision/rasterize-pdf.worker.ts +81 -0
  142. package/src/lib/ai/vision/vision-reader.ts +130 -0
  143. package/src/{auth → lib/auth}/middleware.ts +12 -3
  144. package/src/lib/cc/client.ts +273 -0
  145. package/src/lib/cc/config-cache.ts +32 -0
  146. package/src/lib/cc/format.ts +96 -0
  147. package/src/lib/cc/registry.ts +62 -0
  148. package/src/lib/cc/types.ts +44 -0
  149. package/src/lib/config.ts +212 -0
  150. package/src/lib/db/drizzle.config.mssql.ts +9 -0
  151. package/src/lib/db/drizzle.config.pg.ts +9 -0
  152. package/src/lib/db/migrate.ts +56 -0
  153. package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/mssql/20260326231647_nice_speedball}/migration.sql +12 -0
  154. package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/mssql/20260326231647_nice_speedball}/snapshot.json +106 -1
  155. package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/migration.sql +20 -0
  156. package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/snapshot.json +497 -0
  157. package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/migration.sql +1 -0
  158. package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/snapshot.json +516 -0
  159. package/src/lib/db/migrations/mssql/20260812174642_user_id_text/migration.sql +2 -0
  160. package/src/lib/db/migrations/mssql/20260812174642_user_id_text/snapshot.json +516 -0
  161. package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/migration.sql +53 -0
  162. package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/snapshot.json +584 -0
  163. package/src/lib/db/migrations/pg/20260812174622_user_id_text/migration.sql +2 -0
  164. package/src/lib/db/migrations/pg/20260812174622_user_id_text/snapshot.json +584 -0
  165. package/src/lib/db/schema.mssql.ts +94 -0
  166. package/src/lib/db/schema.pg.ts +95 -0
  167. package/src/lib/factory.ts +127 -0
  168. package/src/lib/graph/index.ts +19 -0
  169. package/src/lib/graph/resolver.ts +387 -0
  170. package/src/{index.ts → lib/index.ts} +13 -5
  171. package/src/lib/redis.ts +44 -0
  172. package/src/lib/routes/chat.ts +115 -0
  173. package/src/lib/routes/files.ts +129 -0
  174. package/src/lib/routes/owned-thread.ts +17 -0
  175. package/src/{routes → lib/routes}/threads.ts +22 -31
  176. package/src/{routes → lib/routes}/ws.ts +9 -3
  177. package/src/lib/types.ts +76 -0
  178. package/src/{ws → lib/ws}/connections.ts +9 -1
  179. package/tsconfig.json +48 -0
  180. package/dist/src/adapters/database.d.ts +0 -30
  181. package/dist/src/adapters/mssql.d.ts +0 -3
  182. package/dist/src/ai/active-streams.d.ts +0 -14
  183. package/dist/src/ai/context/compressor.d.ts +0 -7
  184. package/dist/src/ai/context/index.d.ts +0 -15
  185. package/dist/src/ai/context/summarizer.d.ts +0 -5
  186. package/dist/src/ai/helpers.d.ts +0 -5
  187. package/dist/src/ai/index.d.ts +0 -4
  188. package/dist/src/ai/interceptors/request-interceptor.d.ts +0 -12
  189. package/dist/src/ai/tools/call-endpoint.tool.d.ts +0 -7
  190. package/dist/src/ai/tools/capture-files.tool.d.ts +0 -6
  191. package/dist/src/ai/tools/execute-code.tool.d.ts +0 -4
  192. package/dist/src/ai/tools/query-graph.tool.d.ts +0 -5
  193. package/dist/src/cli/extract-endpoints.d.ts +0 -6
  194. package/dist/src/config.d.ts +0 -165
  195. package/dist/src/db/migrate.d.ts +0 -1
  196. package/dist/src/graph/expand-domains.d.ts +0 -2
  197. package/dist/src/graph/generate-cypher.d.ts +0 -22
  198. package/dist/src/graph/index.d.ts +0 -11
  199. package/dist/src/graph/seed.d.ts +0 -19
  200. package/dist/src/graph/validate.d.ts +0 -2
  201. package/dist/src/routes/index.d.ts +0 -4
  202. package/dist/src/types.d.ts +0 -74
  203. package/dist/src/ws/events.d.ts +0 -39
  204. package/dist/src/ws/index.d.ts +0 -3
  205. package/dist/src/ws/notify.d.ts +0 -2
  206. package/src/adapters/database.ts +0 -29
  207. package/src/adapters/mssql.ts +0 -195
  208. package/src/ai/active-streams.ts +0 -123
  209. package/src/ai/context/compressor.ts +0 -47
  210. package/src/ai/context/index.ts +0 -75
  211. package/src/ai/context/summarizer.ts +0 -50
  212. package/src/ai/context/token-estimator.ts +0 -60
  213. package/src/ai/helpers.ts +0 -32
  214. package/src/ai/index.ts +0 -277
  215. package/src/ai/interceptors/request-interceptor.ts +0 -61
  216. package/src/ai/prompt.ts +0 -126
  217. package/src/ai/tools/call-endpoint.tool.ts +0 -89
  218. package/src/ai/tools/capture-files.tool.ts +0 -20
  219. package/src/ai/tools/execute-code.tool.ts +0 -153
  220. package/src/cli/extract-endpoints.ts +0 -550
  221. package/src/config.ts +0 -182
  222. package/src/db/migrate.ts +0 -21
  223. package/src/db/migrations/20260315000000_add_context_meta/migration.sql +0 -1
  224. package/src/db/schema.ts +0 -70
  225. package/src/factory.ts +0 -171
  226. package/src/graph/expand-domains.ts +0 -276
  227. package/src/graph/generate-cypher.ts +0 -192
  228. package/src/graph/index.ts +0 -47
  229. package/src/graph/resolver.ts +0 -357
  230. package/src/graph/seed.ts +0 -162
  231. package/src/graph/validate.ts +0 -78
  232. package/src/routes/chat.ts +0 -86
  233. package/src/routes/files.ts +0 -100
  234. package/src/routes/index.ts +0 -4
  235. package/src/types.ts +0 -74
  236. package/src/ws/events.ts +0 -39
  237. package/src/ws/index.ts +0 -11
  238. package/src/ws/notify.ts +0 -9
  239. /package/dist/src/{auth → lib/auth}/middleware.d.ts +0 -0
@@ -1,550 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
-
4
- type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
5
- type ResponseKind = "object" | "array" | "paginated" | "file" | "none";
6
- type Obj = Record<string, unknown>;
7
- type Prop = {
8
- name: string;
9
- required: boolean;
10
- type: string;
11
- isArray?: boolean;
12
- properties?: Prop[];
13
- };
14
- type Endpoint = {
15
- key: string;
16
- method: HttpMethod;
17
- path: string;
18
- params: Prop[];
19
- body: Prop[];
20
- response: Prop[];
21
- responseKind: ResponseKind;
22
- successStatus: number;
23
- errorStatuses: number[];
24
- };
25
-
26
- export type ExtractEndpointsOptions = {
27
- swaggerUrl: string;
28
- domainsDir: string;
29
- write?: boolean;
30
- };
31
-
32
- const METHODS: Array<[string, HttpMethod]> = [
33
- ["get", "GET"],
34
- ["post", "POST"],
35
- ["put", "PUT"],
36
- ["delete", "DELETE"],
37
- ];
38
- const AUTO_START = "// @auto-generated-start";
39
- const AUTO_END = "// @auto-generated-end";
40
- const MAX_DEPTH = 8;
41
-
42
- const toCamelCase = (s: string) =>
43
- s
44
- .split(".")
45
- .map((seg) => seg.replace(/^[A-Z]/, (c) => c.toLowerCase()))
46
- .join(".");
47
-
48
- const isObj = (v: unknown): v is Obj => typeof v === "object" && v !== null && !Array.isArray(v);
49
- const obj = (v: unknown): Obj => (isObj(v) ? v : {});
50
- const strings = (v: unknown): string[] =>
51
- Array.isArray(v) ? v.filter((x): x is string => typeof x === "string") : [];
52
- const dedupe = (items: Prop[]): Prop[] => {
53
- const seen = new Set<string>();
54
- return items.filter((x) => (seen.has(x.name) ? false : (seen.add(x.name), true)));
55
- };
56
-
57
- function normalizePath(raw: string) {
58
- let p = raw.startsWith("/") ? raw : `/${raw}`;
59
- p = p.replace(/\{([^}:?]+)(?::[^}]+)?\??\}/g, "{$1}").replace(/^\/api(?=\/|$)/i, "") || "/";
60
- return p.length > 1 ? p.replace(/\/+$/, "") : p;
61
- }
62
-
63
- function walkFiles(dir: string, accept: (filePath: string) => boolean) {
64
- if (!fs.existsSync(dir)) return [];
65
- const out: string[] = [];
66
- function walk(d: string) {
67
- for (const e of fs.readdirSync(d, { withFileTypes: true })) {
68
- const p = path.join(d, e.name);
69
- if (e.isDirectory()) walk(p);
70
- else if (accept(p)) out.push(p);
71
- }
72
- }
73
- walk(dir);
74
- return out;
75
- }
76
-
77
- function endpointFiles(root: string) {
78
- const out = new Map<string, string>();
79
- for (const filePath of walkFiles(root, (p) => p.endsWith(".endpoint.ts"))) {
80
- const c = fs.readFileSync(filePath, "utf8");
81
- const method = c.match(/^\s*method:\s*(['"])([^'"]+)\1/m)?.[2];
82
- const route = c.match(/^\s*path:\s*(['"])([^'"]+)\1/m)?.[2];
83
- if (method && route) out.set(`${method}:${normalizePath(route)}`, filePath);
84
- }
85
- return out;
86
- }
87
-
88
- function resolvePointer(doc: unknown, ref: string) {
89
- if (!ref.startsWith("#/")) return null;
90
- let cur: unknown = doc;
91
- for (const part of ref.slice(2).split("/")) {
92
- const key = part.replace(/~1/g, "/").replace(/~0/g, "~");
93
- if (!isObj(cur) || !(key in cur)) return null;
94
- cur = cur[key];
95
- }
96
- return cur;
97
- }
98
-
99
- function deref(doc: unknown, v: unknown) {
100
- if (!isObj(v)) return {};
101
- let x = v;
102
- const seen = new Set<string>();
103
- while (typeof x.$ref === "string") {
104
- if (seen.has(x.$ref)) return {};
105
- seen.add(x.$ref);
106
- x = obj(resolvePointer(doc, x.$ref));
107
- }
108
- return x;
109
- }
110
-
111
- function schemaType(s: Obj) {
112
- const t = Array.isArray(s.type)
113
- ? s.type.find((x) => typeof x === "string" && x !== "null")
114
- : s.type;
115
- if (typeof t === "string") return t;
116
- if (isObj(s.properties)) return "object";
117
- if (isObj(s.items)) return "array";
118
- return "";
119
- }
120
-
121
- const isJsonNodeRef = (v: unknown) =>
122
- isObj(v) && typeof v.$ref === "string" && /\/System\.Text\.Json\.Nodes\.JsonNode$/.test(v.$ref);
123
-
124
- function isJsonNodeSchema(s: Obj) {
125
- const p = obj(s.properties);
126
- return (
127
- schemaType(s) === "object" &&
128
- "options" in p &&
129
- "parent" in p &&
130
- "root" in p &&
131
- isJsonNodeRef(p.parent) &&
132
- isJsonNodeRef(p.root)
133
- );
134
- }
135
-
136
- function scalarType(s: Obj) {
137
- if (Array.isArray(s.enum) && s.enum.every((v) => typeof v === "string")) {
138
- return s.enum.map((v) => `'${String(v).replace(/'/g, "\\'")}'`).join(" | ");
139
- }
140
- const t = schemaType(s);
141
- const f = typeof s.format === "string" ? s.format : "";
142
- if (f === "uuid") return "uuid";
143
- if (f === "date") return "date";
144
- if (f === "date-time") return "datetime";
145
- if (t === "integer" || t === "number") return "number";
146
- if (t === "boolean") return "boolean";
147
- if (t === "string") return "string";
148
- if (t === "object") return "object";
149
- return "unknown";
150
- }
151
-
152
- function toProps(doc: unknown, schemaIn: unknown, depth = 0) {
153
- if (depth > MAX_DEPTH) return [];
154
- const s = deref(doc, schemaIn);
155
- if (isJsonNodeRef(schemaIn) || isJsonNodeSchema(s)) return [];
156
- const req = new Set(strings(s.required));
157
- return dedupe(
158
- Object.entries(obj(s.properties)).map(([name, child]) =>
159
- toProp(doc, name, child, req.has(name), depth + 1),
160
- ),
161
- );
162
- }
163
-
164
- function toProp(doc: unknown, name: string, schemaIn: unknown, required: boolean, depth = 0): Prop {
165
- if (isJsonNodeRef(schemaIn)) return { name, required, type: "object" };
166
-
167
- const s = deref(doc, schemaIn);
168
- const req = required && s.nullable !== true;
169
-
170
- if (isJsonNodeSchema(s)) return { name, required: req, type: "object" };
171
-
172
- const t = schemaType(s);
173
- if (t === "array") {
174
- const item = deref(doc, s.items);
175
- if (schemaType(item) === "object") {
176
- const properties = toProps(doc, item, depth + 1);
177
- return {
178
- name,
179
- required: req,
180
- type: "object",
181
- isArray: true,
182
- ...(properties.length ? { properties } : {}),
183
- };
184
- }
185
- return { name, required: req, type: scalarType(item), isArray: true };
186
- }
187
-
188
- if (t === "object") {
189
- const properties = toProps(doc, s, depth + 1);
190
- return {
191
- name,
192
- required: req,
193
- type: "object",
194
- ...(properties.length ? { properties } : {}),
195
- };
196
- }
197
-
198
- return { name, required: req, type: scalarType(s) };
199
- }
200
-
201
- function pickContent(contentIn: unknown) {
202
- const content = obj(contentIn);
203
- const mimes = Object.keys(content);
204
- if (!mimes.length) return null;
205
- const mime =
206
- mimes.find((x) => x.toLowerCase() === "application/json") ??
207
- mimes.find((x) => x.toLowerCase().includes("json")) ??
208
- mimes[0];
209
- return { schema: obj(content[mime!]).schema, mimes };
210
- }
211
-
212
- function parseResponse(
213
- doc: unknown,
214
- responsesIn: unknown,
215
- ): {
216
- successStatus: number;
217
- errorStatuses: number[];
218
- responseKind: ResponseKind;
219
- response: Prop[];
220
- } {
221
- const responses = obj(responsesIn);
222
- const statuses = Object.keys(responses)
223
- .map(Number)
224
- .filter(Number.isInteger)
225
- .sort((a, b) => a - b);
226
- const successStatus = statuses.find((s) => s >= 200 && s < 300) ?? 200;
227
- const errorStatuses = [...new Set(statuses.filter((s) => s >= 400))];
228
-
229
- if (successStatus === 204)
230
- return {
231
- successStatus,
232
- errorStatuses,
233
- responseKind: "none",
234
- response: [],
235
- };
236
-
237
- const success = deref(doc, responses[String(successStatus)]);
238
- const content = pickContent(success.content);
239
- if (!content)
240
- return {
241
- successStatus,
242
- errorStatuses,
243
- responseKind: "none",
244
- response: [],
245
- };
246
-
247
- const s = deref(doc, content.schema);
248
- const t = schemaType(s);
249
- const isFile =
250
- content.mimes.some((m) =>
251
- /(application\/octet-stream|application\/pdf|application\/vnd\.|text\/csv)/i.test(m),
252
- ) ||
253
- (t === "string" && ["binary", "base64"].includes(String(s.format ?? "")));
254
-
255
- if (isFile)
256
- return {
257
- successStatus,
258
- errorStatuses,
259
- responseKind: "file",
260
- response: [],
261
- };
262
-
263
- if (t === "array") {
264
- const item = deref(doc, s.items);
265
- return {
266
- successStatus,
267
- errorStatuses,
268
- responseKind: "array",
269
- response:
270
- schemaType(item) === "object"
271
- ? toProps(doc, item)
272
- : [toProp(doc, "item", item, true)],
273
- };
274
- }
275
-
276
- if (t === "object") {
277
- const props = obj(s.properties);
278
- const hasPaging = ["totalCount", "pageNumber", "pageSize", "totalPages"].some(
279
- (k) => k in props,
280
- );
281
- const list = deref(doc, props.items ?? props.data ?? props.results);
282
-
283
- if (hasPaging && schemaType(list) === "array") {
284
- const item = deref(doc, list.items);
285
- return {
286
- successStatus,
287
- errorStatuses,
288
- responseKind: "paginated",
289
- response:
290
- schemaType(item) === "object"
291
- ? toProps(doc, item)
292
- : [toProp(doc, "item", item, true)],
293
- };
294
- }
295
-
296
- return {
297
- successStatus,
298
- errorStatuses,
299
- responseKind: "object",
300
- response: toProps(doc, s),
301
- };
302
- }
303
-
304
- if (Object.keys(s).length) {
305
- return {
306
- successStatus,
307
- errorStatuses,
308
- responseKind: "object",
309
- response: [toProp(doc, "value", s, true)],
310
- };
311
- }
312
-
313
- return { successStatus, errorStatuses, responseKind: "none", response: [] };
314
- }
315
-
316
- function extractEndpoint(
317
- doc: unknown,
318
- route: string,
319
- method: HttpMethod,
320
- pathItemIn: unknown,
321
- operationIn: unknown,
322
- ) {
323
- const pathItem = obj(pathItemIn);
324
- const operation = obj(operationIn);
325
-
326
- const rawParams = [
327
- ...(Array.isArray(pathItem.parameters) ? pathItem.parameters : []),
328
- ...(Array.isArray(operation.parameters) ? operation.parameters : []),
329
- ]
330
- .map((x) => deref(doc, x))
331
- .filter(
332
- (x) => ["path", "query"].includes(String(x.in ?? "")) && typeof x.name === "string",
333
- );
334
-
335
- // Collapse JsonNode-expanded dot-notation query params into a single
336
- // object param. Swagger expands JsonNode properties (Options, Parent,
337
- // Root, …) into many individual params like "CustomFilter.Parent.Root".
338
- const jsonNodeRoots = new Set<string>();
339
- for (const x of rawParams) {
340
- const name = String(x.name);
341
- if (!name.includes(".")) continue;
342
- const root = name.split(".")[0]!;
343
- const rest = name.slice(root.length + 1);
344
- if (/^(Options|Parent|Root)\b/.test(rest)) jsonNodeRoots.add(root);
345
- }
346
-
347
- const params = dedupe(
348
- rawParams
349
- .filter((x) => {
350
- const name = String(x.name);
351
- if (!name.includes(".")) return true;
352
- const root = name.split(".")[0]!;
353
- return !jsonNodeRoots.has(root);
354
- })
355
- .map((x) =>
356
- toProp(
357
- doc,
358
- toCamelCase(String(x.name)),
359
- x.schema,
360
- String(x.in) === "path" || x.required === true,
361
- ),
362
- )
363
- .concat(
364
- [...jsonNodeRoots].map((root) => ({
365
- name: toCamelCase(root),
366
- required: false,
367
- type: "object",
368
- })),
369
- ),
370
- );
371
-
372
- const body: Prop[] = [];
373
- const requestBody = deref(doc, operation.requestBody);
374
- const requestContent = pickContent(requestBody.content);
375
- if (requestContent) {
376
- const s = deref(doc, requestContent.schema);
377
- const t = schemaType(s);
378
- if (t === "object") body.push(...toProps(doc, s));
379
- else if (t === "array") body.push(toProp(doc, "items", s, requestBody.required === true));
380
- else body.push(toProp(doc, "body", s, requestBody.required === true));
381
- }
382
-
383
- const parsed = parseResponse(doc, operation.responses);
384
- const normalizedPath = normalizePath(route);
385
-
386
- return {
387
- key: `${method}:${normalizedPath}`,
388
- method,
389
- path: normalizedPath,
390
- params,
391
- body: dedupe(body),
392
- response: dedupe(parsed.response),
393
- responseKind: parsed.responseKind,
394
- successStatus: parsed.successStatus,
395
- errorStatuses: parsed.errorStatuses,
396
- } satisfies Endpoint;
397
- }
398
-
399
- function parseSwaggerEndpoints(swagger: unknown) {
400
- const out = new Map<string, Endpoint>();
401
- const doc = obj(swagger);
402
- for (const [route, pathItem] of Object.entries(obj(doc.paths))) {
403
- for (const [openApiMethod, method] of METHODS) {
404
- const operation = obj(pathItem)[openApiMethod];
405
- if (!isObj(operation)) continue;
406
- const endpoint = extractEndpoint(doc, route, method, pathItem, operation);
407
- out.set(endpoint.key, endpoint);
408
- }
409
- }
410
- return out;
411
- }
412
-
413
- function serializeProps(props: Prop[], depth: number) {
414
- return props
415
- .map((p) => {
416
- const indent = " ".repeat(depth);
417
- const childIndent = " ".repeat(depth + 1);
418
- const typeLiteral = p.type.includes("'")
419
- ? `"${p.type.replace(/"/g, '\\"')}"`
420
- : `'${p.type}'`;
421
-
422
- const parts = [`name: '${p.name}'`, `required: ${p.required}`, `type: ${typeLiteral}`];
423
- if (p.isArray) parts.push("isArray: true");
424
- if (p.properties?.length)
425
- parts.push(
426
- `properties: [\n${serializeProps(p.properties, depth + 1)}\n${childIndent}]`,
427
- );
428
-
429
- return `${indent} { ${parts.join(", ")} },`;
430
- })
431
- .join("\n");
432
- }
433
-
434
- function blockFor(endpoint: Endpoint) {
435
- return ` autoGenerated: {
436
- params: [
437
- ${serializeProps(endpoint.params, 2)}
438
- ] as const,
439
- body: [
440
- ${serializeProps(endpoint.body, 2)}
441
- ] as const,
442
- response: [
443
- ${serializeProps(endpoint.response, 2)}
444
- ] as const,
445
- successStatus: ${endpoint.successStatus},
446
- errorStatuses: [${endpoint.errorStatuses.join(", ")}],
447
- responseKind: '${endpoint.responseKind}',
448
- } as const,`;
449
- }
450
-
451
- function formatGeneratedFiles(cwd: string, filePaths: string[]) {
452
- if (!filePaths.length) return;
453
- try {
454
- Bun.spawnSync(["bunx", "prettier", "--write", ...filePaths], {
455
- cwd,
456
- stdio: ["ignore", "ignore", "ignore"],
457
- });
458
- } catch (error) {
459
- const message = error instanceof Error ? error.message : String(error);
460
- console.warn(`WARN failed to format generated files: ${message}`);
461
- }
462
- }
463
-
464
- async function fetchJson(url: string) {
465
- const response = await fetch(url, {
466
- headers: { Accept: "application/json" },
467
- });
468
- if (!response.ok) {
469
- throw new Error(`Swagger request failed (${response.status})`);
470
- }
471
- return response.json();
472
- }
473
-
474
- function resolveEndpoint(key: string, extracted: Map<string, Endpoint>) {
475
- const [method, route] = key.split(":");
476
- if (!method || !route) return undefined;
477
- return (
478
- extracted.get(key) ??
479
- extracted.get(`${method}:${normalizePath(`/api${route}`)}`) ??
480
- (route.startsWith("/api/")
481
- ? extracted.get(`${method}:${normalizePath(route.slice(4))}`)
482
- : undefined)
483
- );
484
- }
485
-
486
- export async function extractEndpoints(options: ExtractEndpointsOptions) {
487
- const { swaggerUrl, domainsDir, write = false } = options;
488
-
489
- console.log(`Fetching Swagger from ${swaggerUrl}`);
490
-
491
- const extracted = parseSwaggerEndpoints(await fetchJson(swaggerUrl));
492
- const files = endpointFiles(domainsDir);
493
-
494
- console.log(`Parsed ${extracted.size} endpoints from Swagger`);
495
- console.log(`Found ${files.size} graph endpoint files`);
496
-
497
- let matched = 0;
498
- let missing = 0;
499
- let updated = 0;
500
- const updatedFiles: string[] = [];
501
-
502
- for (const [key, filePath] of files.entries()) {
503
- const endpoint = resolveEndpoint(key, extracted);
504
- if (!endpoint) {
505
- missing++;
506
- console.log(
507
- `MISSING backend endpoint for ${path.relative(domainsDir, filePath)} (${key})`,
508
- );
509
- continue;
510
- }
511
-
512
- matched++;
513
- if (!write) continue;
514
-
515
- const current = fs.readFileSync(filePath, "utf8");
516
- const start = current.indexOf(AUTO_START);
517
- const end = current.indexOf(AUTO_END);
518
-
519
- let next: string;
520
- if (start >= 0 && end > start) {
521
- // Markers exist — replace content between them
522
- next =
523
- current.slice(0, start + AUTO_START.length) +
524
- `\n${blockFor(endpoint)}\n ` +
525
- current.slice(end);
526
- } else {
527
- // No markers — insert before the closing `});`
528
- const closingIndex = current.lastIndexOf("});");
529
- if (closingIndex < 0) continue;
530
- next =
531
- current.slice(0, closingIndex) +
532
- `\n ${AUTO_START}\n${blockFor(endpoint)}\n ${AUTO_END}\n});` +
533
- current.slice(closingIndex + 3);
534
- }
535
-
536
- if (next === current) continue;
537
-
538
- fs.writeFileSync(filePath, next);
539
- updatedFiles.push(filePath);
540
- updated++;
541
- console.log(`UPDATED ${path.relative(domainsDir, filePath)}`);
542
- }
543
-
544
- if (write) formatGeneratedFiles(domainsDir, updatedFiles);
545
-
546
- console.log("\n--- Summary ---");
547
- console.log(`Matched: ${matched}/${files.size}`);
548
- console.log(`Missing: ${missing}`);
549
- console.log(write ? `Updated: ${updated}` : "Run with --write to update files");
550
- }