trickle-backend 0.1.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 (65) hide show
  1. package/dist/db/connection.d.ts +3 -0
  2. package/dist/db/connection.js +16 -0
  3. package/dist/db/migrations.d.ts +2 -0
  4. package/dist/db/migrations.js +51 -0
  5. package/dist/db/queries.d.ts +70 -0
  6. package/dist/db/queries.js +186 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +10 -0
  9. package/dist/routes/audit.d.ts +2 -0
  10. package/dist/routes/audit.js +251 -0
  11. package/dist/routes/codegen.d.ts +2 -0
  12. package/dist/routes/codegen.js +224 -0
  13. package/dist/routes/coverage.d.ts +2 -0
  14. package/dist/routes/coverage.js +98 -0
  15. package/dist/routes/dashboard.d.ts +2 -0
  16. package/dist/routes/dashboard.js +433 -0
  17. package/dist/routes/diff.d.ts +2 -0
  18. package/dist/routes/diff.js +181 -0
  19. package/dist/routes/errors.d.ts +2 -0
  20. package/dist/routes/errors.js +86 -0
  21. package/dist/routes/functions.d.ts +2 -0
  22. package/dist/routes/functions.js +69 -0
  23. package/dist/routes/ingest.d.ts +2 -0
  24. package/dist/routes/ingest.js +111 -0
  25. package/dist/routes/mock.d.ts +2 -0
  26. package/dist/routes/mock.js +57 -0
  27. package/dist/routes/search.d.ts +2 -0
  28. package/dist/routes/search.js +136 -0
  29. package/dist/routes/tail.d.ts +2 -0
  30. package/dist/routes/tail.js +11 -0
  31. package/dist/routes/types.d.ts +2 -0
  32. package/dist/routes/types.js +97 -0
  33. package/dist/server.d.ts +2 -0
  34. package/dist/server.js +40 -0
  35. package/dist/services/sse-broker.d.ts +10 -0
  36. package/dist/services/sse-broker.js +39 -0
  37. package/dist/services/type-differ.d.ts +2 -0
  38. package/dist/services/type-differ.js +126 -0
  39. package/dist/services/type-generator.d.ts +319 -0
  40. package/dist/services/type-generator.js +3207 -0
  41. package/dist/types.d.ts +56 -0
  42. package/dist/types.js +2 -0
  43. package/package.json +22 -0
  44. package/src/db/connection.ts +16 -0
  45. package/src/db/migrations.ts +50 -0
  46. package/src/db/queries.ts +260 -0
  47. package/src/index.ts +11 -0
  48. package/src/routes/audit.ts +283 -0
  49. package/src/routes/codegen.ts +237 -0
  50. package/src/routes/coverage.ts +120 -0
  51. package/src/routes/dashboard.ts +435 -0
  52. package/src/routes/diff.ts +215 -0
  53. package/src/routes/errors.ts +91 -0
  54. package/src/routes/functions.ts +75 -0
  55. package/src/routes/ingest.ts +139 -0
  56. package/src/routes/mock.ts +66 -0
  57. package/src/routes/search.ts +169 -0
  58. package/src/routes/tail.ts +12 -0
  59. package/src/routes/types.ts +106 -0
  60. package/src/server.ts +40 -0
  61. package/src/services/sse-broker.ts +51 -0
  62. package/src/services/type-differ.ts +141 -0
  63. package/src/services/type-generator.ts +3853 -0
  64. package/src/types.ts +37 -0
  65. package/tsconfig.json +8 -0
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sseBroker = void 0;
4
+ const events_1 = require("events");
5
+ class SseBroker extends events_1.EventEmitter {
6
+ clients = [];
7
+ addClient(res, filter) {
8
+ res.writeHead(200, {
9
+ "Content-Type": "text/event-stream",
10
+ "Cache-Control": "no-cache",
11
+ Connection: "keep-alive",
12
+ "X-Accel-Buffering": "no",
13
+ });
14
+ res.write(":\n\n"); // initial comment to establish connection
15
+ const client = { res, filter };
16
+ this.clients.push(client);
17
+ res.on("close", () => {
18
+ this.removeClient(res);
19
+ });
20
+ }
21
+ removeClient(res) {
22
+ this.clients = this.clients.filter((c) => c.res !== res);
23
+ }
24
+ broadcast(event, data) {
25
+ const payload = JSON.stringify(data);
26
+ const functionName = typeof data === "object" && data !== null && "functionName" in data
27
+ ? data.functionName
28
+ : undefined;
29
+ for (const client of this.clients) {
30
+ if (client.filter && functionName) {
31
+ if (!functionName.includes(client.filter)) {
32
+ continue;
33
+ }
34
+ }
35
+ client.res.write(`event: ${event}\ndata: ${payload}\n\n`);
36
+ }
37
+ }
38
+ }
39
+ exports.sseBroker = new SseBroker();
@@ -0,0 +1,2 @@
1
+ import { TypeNode, TypeDiff } from "../types";
2
+ export declare function diffTypes(from: TypeNode, to: TypeNode, basePath?: string): TypeDiff[];
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.diffTypes = diffTypes;
4
+ function diffTypes(from, to, basePath = "") {
5
+ const diffs = [];
6
+ if (from.kind !== to.kind) {
7
+ diffs.push({ kind: "changed", path: basePath || "(root)", from, to });
8
+ return diffs;
9
+ }
10
+ switch (from.kind) {
11
+ case "primitive": {
12
+ const toNode = to;
13
+ if (from.name !== toNode.name) {
14
+ diffs.push({ kind: "changed", path: basePath || "(root)", from, to });
15
+ }
16
+ break;
17
+ }
18
+ case "object": {
19
+ const toNode = to;
20
+ const fromKeys = new Set(Object.keys(from.properties));
21
+ const toKeys = new Set(Object.keys(toNode.properties));
22
+ for (const key of fromKeys) {
23
+ const childPath = basePath ? `${basePath}.${key}` : key;
24
+ if (!toKeys.has(key)) {
25
+ diffs.push({ kind: "removed", path: childPath, type: from.properties[key] });
26
+ }
27
+ else {
28
+ diffs.push(...diffTypes(from.properties[key], toNode.properties[key], childPath));
29
+ }
30
+ }
31
+ for (const key of toKeys) {
32
+ if (!fromKeys.has(key)) {
33
+ const childPath = basePath ? `${basePath}.${key}` : key;
34
+ diffs.push({ kind: "added", path: childPath, type: toNode.properties[key] });
35
+ }
36
+ }
37
+ break;
38
+ }
39
+ case "array": {
40
+ const toNode = to;
41
+ diffs.push(...diffTypes(from.element, toNode.element, `${basePath || "(root)"}[]`));
42
+ break;
43
+ }
44
+ case "union": {
45
+ const toNode = to;
46
+ const fromSerialized = from.members.map((m) => JSON.stringify(m));
47
+ const toSerialized = toNode.members.map((m) => JSON.stringify(m));
48
+ const fromSet = new Set(fromSerialized);
49
+ const toSet = new Set(toSerialized);
50
+ for (let i = 0; i < fromSerialized.length; i++) {
51
+ if (!toSet.has(fromSerialized[i])) {
52
+ diffs.push({
53
+ kind: "removed",
54
+ path: `${basePath || "(root)"}|[${i}]`,
55
+ type: from.members[i],
56
+ });
57
+ }
58
+ }
59
+ for (let i = 0; i < toSerialized.length; i++) {
60
+ if (!fromSet.has(toSerialized[i])) {
61
+ diffs.push({
62
+ kind: "added",
63
+ path: `${basePath || "(root)"}|[${i}]`,
64
+ type: toNode.members[i],
65
+ });
66
+ }
67
+ }
68
+ break;
69
+ }
70
+ case "function": {
71
+ const toNode = to;
72
+ const maxParams = Math.max(from.params.length, toNode.params.length);
73
+ for (let i = 0; i < maxParams; i++) {
74
+ const paramPath = `${basePath || "(root)"}.params[${i}]`;
75
+ if (i >= from.params.length) {
76
+ diffs.push({ kind: "added", path: paramPath, type: toNode.params[i] });
77
+ }
78
+ else if (i >= toNode.params.length) {
79
+ diffs.push({ kind: "removed", path: paramPath, type: from.params[i] });
80
+ }
81
+ else {
82
+ diffs.push(...diffTypes(from.params[i], toNode.params[i], paramPath));
83
+ }
84
+ }
85
+ diffs.push(...diffTypes(from.returnType, toNode.returnType, `${basePath || "(root)"}.return`));
86
+ break;
87
+ }
88
+ case "promise": {
89
+ const toNode = to;
90
+ diffs.push(...diffTypes(from.resolved, toNode.resolved, `${basePath || "(root)"}<resolved>`));
91
+ break;
92
+ }
93
+ case "map": {
94
+ const toNode = to;
95
+ diffs.push(...diffTypes(from.key, toNode.key, `${basePath || "(root)"}<key>`));
96
+ diffs.push(...diffTypes(from.value, toNode.value, `${basePath || "(root)"}<value>`));
97
+ break;
98
+ }
99
+ case "set": {
100
+ const toNode = to;
101
+ diffs.push(...diffTypes(from.element, toNode.element, `${basePath || "(root)"}<element>`));
102
+ break;
103
+ }
104
+ case "tuple": {
105
+ const toNode = to;
106
+ const maxLen = Math.max(from.elements.length, toNode.elements.length);
107
+ for (let i = 0; i < maxLen; i++) {
108
+ const elPath = `${basePath || "(root)"}[${i}]`;
109
+ if (i >= from.elements.length) {
110
+ diffs.push({ kind: "added", path: elPath, type: toNode.elements[i] });
111
+ }
112
+ else if (i >= toNode.elements.length) {
113
+ diffs.push({ kind: "removed", path: elPath, type: from.elements[i] });
114
+ }
115
+ else {
116
+ diffs.push(...diffTypes(from.elements[i], toNode.elements[i], elPath));
117
+ }
118
+ }
119
+ break;
120
+ }
121
+ case "unknown":
122
+ // Both unknown — no diff
123
+ break;
124
+ }
125
+ return diffs;
126
+ }
@@ -0,0 +1,319 @@
1
+ import { TypeNode } from "../types";
2
+ /**
3
+ * Accumulates extracted named interfaces so complex nested objects
4
+ * get their own `export interface Foo { ... }` block.
5
+ */
6
+ interface ExtractedInterface {
7
+ name: string;
8
+ node: Extract<TypeNode, {
9
+ kind: "object";
10
+ }>;
11
+ }
12
+ /**
13
+ * Convert a TypeNode to a TypeScript type string.
14
+ *
15
+ * Large nested objects (>2 properties) are extracted into named interfaces
16
+ * so the output stays readable.
17
+ */
18
+ declare function typeNodeToTS(node: TypeNode, extracted: ExtractedInterface[], parentName: string, propName: string | undefined, indent: number): string;
19
+ /**
20
+ * Generate TypeScript definitions for a single function.
21
+ *
22
+ * For a function `processOrder(order)` that takes an object and returns an object:
23
+ * ```ts
24
+ * export interface ProcessOrderInput { id: string; customer: Customer; ... }
25
+ * export interface ProcessOrderOutput { orderId: string; total: number; ... }
26
+ * export declare function processOrder(order: ProcessOrderInput): ProcessOrderOutput;
27
+ * ```
28
+ */
29
+ export declare function generateFunctionTypes(functionName: string, argsType: TypeNode, returnType: TypeNode, meta?: {
30
+ module?: string;
31
+ env?: string;
32
+ observedAt?: string;
33
+ }): string;
34
+ /**
35
+ * Generate a complete TypeScript declarations file for all functions.
36
+ */
37
+ export declare function generateAllTypes(functions: Array<{
38
+ name: string;
39
+ argsType: TypeNode;
40
+ returnType: TypeNode;
41
+ module?: string;
42
+ env?: string;
43
+ observedAt?: string;
44
+ }>): string;
45
+ export declare function generatePythonTypes(functions: Array<{
46
+ name: string;
47
+ argsType: TypeNode;
48
+ returnType: TypeNode;
49
+ module?: string;
50
+ env?: string;
51
+ observedAt?: string;
52
+ }>): string;
53
+ /**
54
+ * Generate a fully-typed fetch-based API client from runtime-observed routes.
55
+ *
56
+ * Output is a single TypeScript file with:
57
+ * - All request/response interfaces
58
+ * - A `createTrickleClient(baseUrl)` factory that returns typed fetch wrappers
59
+ * - Proper path parameter substitution
60
+ * - Request body typing for POST/PUT/PATCH
61
+ */
62
+ export declare function generateApiClient(functions: Array<{
63
+ name: string;
64
+ argsType: TypeNode;
65
+ returnType: TypeNode;
66
+ module?: string;
67
+ env?: string;
68
+ observedAt?: string;
69
+ }>): string;
70
+ /**
71
+ * Generate an OpenAPI 3.0 specification from runtime-observed route types.
72
+ */
73
+ export declare function generateOpenApiSpec(functions: Array<{
74
+ name: string;
75
+ argsType: TypeNode;
76
+ returnType: TypeNode;
77
+ module?: string;
78
+ env?: string;
79
+ observedAt?: string;
80
+ }>, options?: {
81
+ title?: string;
82
+ version?: string;
83
+ serverUrl?: string;
84
+ }): object;
85
+ /**
86
+ * Generate typed Express handler type aliases from runtime-observed routes.
87
+ *
88
+ * For each route like `GET /api/users/:id`, produces:
89
+ * - `GetApiUsersIdHandler` — a fully typed `RequestHandler` with
90
+ * `Request<Params, ResBody, ReqBody, Query>` and `Response<ResBody>`
91
+ *
92
+ * Developers can use these to type their route handlers:
93
+ * app.get('/api/users/:id', ((req, res) => { ... }) as GetApiUsersIdHandler);
94
+ */
95
+ export declare function generateHandlerTypes(functions: Array<{
96
+ name: string;
97
+ argsType: TypeNode;
98
+ returnType: TypeNode;
99
+ module?: string;
100
+ env?: string;
101
+ observedAt?: string;
102
+ }>): string;
103
+ /**
104
+ * Generate Zod validation schemas from runtime-observed types.
105
+ *
106
+ * For each function/route, generates a named Zod schema that can be used for:
107
+ * - Runtime validation of API inputs/outputs
108
+ * - TypeScript type inference via `z.infer<typeof schema>`
109
+ * - Form validation, config parsing, etc.
110
+ */
111
+ export declare function generateZodSchemas(functions: Array<{
112
+ name: string;
113
+ argsType: TypeNode;
114
+ returnType: TypeNode;
115
+ module?: string;
116
+ env?: string;
117
+ observedAt?: string;
118
+ }>): string;
119
+ /**
120
+ * Generate fully-typed TanStack Query (React Query) hooks from runtime-observed routes.
121
+ *
122
+ * For each route:
123
+ * - GET → useQuery hook with typed response and query keys
124
+ * - POST/PUT/PATCH/DELETE → useMutation hook with typed input/output
125
+ * - Query key factory for cache invalidation
126
+ * - All request/response interfaces included
127
+ */
128
+ export declare function generateReactQueryHooks(functions: Array<{
129
+ name: string;
130
+ argsType: TypeNode;
131
+ returnType: TypeNode;
132
+ module?: string;
133
+ env?: string;
134
+ observedAt?: string;
135
+ }>): string;
136
+ /**
137
+ * Generate TypeScript type guard functions from runtime-observed types.
138
+ *
139
+ * For each route:
140
+ * - `isGetApiUsersResponse(value): value is GetApiUsersResponse`
141
+ * - `isPostApiUsersRequest(value): value is PostApiUsersRequest`
142
+ *
143
+ * Type guards perform structural checks: verify typeof, key existence,
144
+ * array shapes, and nested object structure.
145
+ */
146
+ export declare function generateTypeGuards(functions: Array<{
147
+ name: string;
148
+ argsType: TypeNode;
149
+ returnType: TypeNode;
150
+ module?: string;
151
+ env?: string;
152
+ observedAt?: string;
153
+ }>): string;
154
+ /**
155
+ * Generate Express validation middleware from runtime-observed types.
156
+ *
157
+ * For each POST/PUT/PATCH route, generates middleware that:
158
+ * - Validates request body structure and types
159
+ * - Returns 400 with structured errors on failure
160
+ * - Passes through to next() on success
161
+ *
162
+ * Self-contained — no external validation library required.
163
+ */
164
+ export declare function generateMiddleware(functions: Array<{
165
+ name: string;
166
+ argsType: TypeNode;
167
+ returnType: TypeNode;
168
+ module?: string;
169
+ env?: string;
170
+ observedAt?: string;
171
+ }>): string;
172
+ /**
173
+ * Generate Mock Service Worker (MSW) request handlers from observed API routes.
174
+ *
175
+ * Output:
176
+ * - Import from 'msw'
177
+ * - Response type interfaces for each route
178
+ * - Individual handler exports (e.g. getApiUsersHandler)
179
+ * - A combined `handlers` array for setupServer/setupWorker
180
+ */
181
+ export declare function generateMswHandlers(functions: Array<{
182
+ name: string;
183
+ argsType: TypeNode;
184
+ returnType: TypeNode;
185
+ module?: string;
186
+ env?: string;
187
+ observedAt?: string;
188
+ }>): string;
189
+ /**
190
+ * Generate JSON Schema definitions from observed runtime types.
191
+ *
192
+ * Output is a single JSON object with $defs for each route/function's
193
+ * request and response types, suitable for use with ajv, joi, or any
194
+ * JSON Schema-compatible validator.
195
+ */
196
+ export declare function generateJsonSchemas(functions: Array<{
197
+ name: string;
198
+ argsType: TypeNode;
199
+ returnType: TypeNode;
200
+ module?: string;
201
+ env?: string;
202
+ observedAt?: string;
203
+ }>): string;
204
+ /**
205
+ * Generate typed SWR hooks from observed API routes.
206
+ *
207
+ * Output:
208
+ * - Import from 'swr' and 'swr/mutation'
209
+ * - Response/input type interfaces
210
+ * - A configurable fetcher
211
+ * - useSWR hooks for GET routes
212
+ * - useSWRMutation hooks for POST/PUT/PATCH/DELETE routes
213
+ */
214
+ export declare function generateSwrHooks(functions: Array<{
215
+ name: string;
216
+ argsType: TypeNode;
217
+ returnType: TypeNode;
218
+ module?: string;
219
+ env?: string;
220
+ observedAt?: string;
221
+ }>): string;
222
+ /**
223
+ * Generate Pydantic BaseModel classes from observed runtime types.
224
+ *
225
+ * Unlike --python (TypedDict), Pydantic models provide:
226
+ * - Runtime validation (model_validate)
227
+ * - JSON serialization (model_dump_json)
228
+ * - JSON Schema generation (model_json_schema)
229
+ * - Direct use as FastAPI request/response models
230
+ */
231
+ export declare function generatePydanticModels(functions: Array<{
232
+ name: string;
233
+ argsType: TypeNode;
234
+ returnType: TypeNode;
235
+ module?: string;
236
+ env?: string;
237
+ observedAt?: string;
238
+ }>): string;
239
+ /**
240
+ * Generate class-validator DTO classes from observed runtime types.
241
+ *
242
+ * Output: NestJS-ready DTOs with class-validator decorators for
243
+ * request validation and class-transformer for nested object support.
244
+ */
245
+ export declare function generateClassValidatorDtos(functions: Array<{
246
+ name: string;
247
+ argsType: TypeNode;
248
+ returnType: TypeNode;
249
+ module?: string;
250
+ env?: string;
251
+ observedAt?: string;
252
+ }>): string;
253
+ /**
254
+ * Generate a GraphQL SDL schema from runtime-observed API routes.
255
+ *
256
+ * Converts REST routes into Query (GET) and Mutation (POST/PUT/PATCH/DELETE)
257
+ * fields with properly typed inputs and outputs.
258
+ */
259
+ export declare function generateGraphqlSchema(functions: Array<{
260
+ name: string;
261
+ argsType: TypeNode;
262
+ returnType: TypeNode;
263
+ module?: string;
264
+ env?: string;
265
+ observedAt?: string;
266
+ }>): string;
267
+ /**
268
+ * Generate a fully-typed tRPC router from runtime-observed API routes.
269
+ *
270
+ * - GET routes → t.procedure.query()
271
+ * - POST/PUT/PATCH/DELETE routes → t.procedure.input(zodSchema).mutation()
272
+ * - Includes Zod input schemas for request body validation
273
+ * - Includes TypeScript return type annotations from observed responses
274
+ * - Exports AppRouter type for client-side type inference
275
+ */
276
+ export declare function generateTrpcRouter(functions: Array<{
277
+ name: string;
278
+ argsType: TypeNode;
279
+ returnType: TypeNode;
280
+ module?: string;
281
+ env?: string;
282
+ observedAt?: string;
283
+ }>): string;
284
+ /**
285
+ * Generate a typed Axios client from runtime-observed API routes.
286
+ *
287
+ * For each route:
288
+ * - Request/response interfaces
289
+ * - Typed function using axios.get/post/put/patch/delete
290
+ * - Path parameter interpolation
291
+ * - Query parameter support
292
+ * - Configurable base URL and axios instance
293
+ */
294
+ export declare function generateAxiosClient(functions: Array<{
295
+ name: string;
296
+ argsType: TypeNode;
297
+ returnType: TypeNode;
298
+ module?: string;
299
+ env?: string;
300
+ observedAt?: string;
301
+ }>): string;
302
+ /**
303
+ * Generate compact inline type annotations for annotating source code.
304
+ * Unlike generateFunctionTypes which emits full interfaces, this produces
305
+ * minimal inline annotations suitable for inserting into source files.
306
+ */
307
+ export declare function generateInlineAnnotations(functions: Array<{
308
+ name: string;
309
+ argsType: TypeNode;
310
+ returnType: TypeNode;
311
+ module?: string;
312
+ }>, language?: "typescript" | "python"): Record<string, {
313
+ params: Array<{
314
+ name: string;
315
+ type: string;
316
+ }>;
317
+ returnType: string;
318
+ }>;
319
+ export { typeNodeToTS as typeNodeToTSPublic };