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,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const express_1 = require("express");
4
+ const connection_1 = require("../db/connection");
5
+ const queries_1 = require("../db/queries");
6
+ const router = (0, express_1.Router)();
7
+ // GET / — list errors with filters
8
+ router.get("/", (req, res) => {
9
+ try {
10
+ const { functionName, env, since, limit, offset } = req.query;
11
+ const result = (0, queries_1.listErrors)(connection_1.db, {
12
+ functionName: functionName,
13
+ env: env,
14
+ since: since,
15
+ limit: limit ? parseInt(limit, 10) : undefined,
16
+ offset: offset ? parseInt(offset, 10) : undefined,
17
+ });
18
+ const parsed = result.rows.map((row) => ({
19
+ ...row,
20
+ args_type: tryParseJson(row.args_type),
21
+ return_type: tryParseJson(row.return_type),
22
+ args_snapshot: tryParseJson(row.args_snapshot),
23
+ }));
24
+ res.json({ errors: parsed, total: result.total });
25
+ }
26
+ catch (err) {
27
+ console.error("List errors error:", err);
28
+ res.status(500).json({ error: "Internal server error" });
29
+ }
30
+ });
31
+ // GET /:id — get single error with full context
32
+ router.get("/:id", (req, res) => {
33
+ try {
34
+ const id = parseInt(req.params.id, 10);
35
+ if (isNaN(id)) {
36
+ res.status(400).json({ error: "Invalid error id" });
37
+ return;
38
+ }
39
+ const errorRow = (0, queries_1.getError)(connection_1.db, id);
40
+ if (!errorRow) {
41
+ res.status(404).json({ error: "Error not found" });
42
+ return;
43
+ }
44
+ // Find the associated type snapshot if type_hash is available
45
+ let snapshot;
46
+ if (errorRow.type_hash && errorRow.function_id) {
47
+ const stmt = connection_1.db.prepare(`
48
+ SELECT * FROM type_snapshots
49
+ WHERE function_id = ? AND type_hash = ?
50
+ ORDER BY observed_at DESC
51
+ LIMIT 1
52
+ `);
53
+ snapshot = stmt.get(errorRow.function_id, errorRow.type_hash);
54
+ }
55
+ res.json({
56
+ error: {
57
+ ...errorRow,
58
+ args_type: tryParseJson(errorRow.args_type),
59
+ return_type: tryParseJson(errorRow.return_type),
60
+ args_snapshot: tryParseJson(errorRow.args_snapshot),
61
+ },
62
+ snapshot: snapshot
63
+ ? {
64
+ ...snapshot,
65
+ args_type: tryParseJson(snapshot.args_type),
66
+ return_type: tryParseJson(snapshot.return_type),
67
+ }
68
+ : null,
69
+ });
70
+ }
71
+ catch (err) {
72
+ console.error("Get error error:", err);
73
+ res.status(500).json({ error: "Internal server error" });
74
+ }
75
+ });
76
+ function tryParseJson(value) {
77
+ if (typeof value !== "string")
78
+ return value;
79
+ try {
80
+ return JSON.parse(value);
81
+ }
82
+ catch {
83
+ return value;
84
+ }
85
+ }
86
+ exports.default = router;
@@ -0,0 +1,2 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const express_1 = require("express");
4
+ const connection_1 = require("../db/connection");
5
+ const queries_1 = require("../db/queries");
6
+ const router = (0, express_1.Router)();
7
+ // GET / — list functions
8
+ router.get("/", (req, res) => {
9
+ try {
10
+ const { q, env, language, limit, offset } = req.query;
11
+ const result = (0, queries_1.listFunctions)(connection_1.db, {
12
+ search: q,
13
+ env: env,
14
+ language: language,
15
+ limit: limit ? parseInt(limit, 10) : undefined,
16
+ offset: offset ? parseInt(offset, 10) : undefined,
17
+ });
18
+ res.json({ functions: result.rows, total: result.total });
19
+ }
20
+ catch (err) {
21
+ console.error("List functions error:", err);
22
+ res.status(500).json({ error: "Internal server error" });
23
+ }
24
+ });
25
+ // GET /:id — get single function with latest snapshots
26
+ router.get("/:id", (req, res) => {
27
+ try {
28
+ const id = parseInt(req.params.id, 10);
29
+ if (isNaN(id)) {
30
+ res.status(400).json({ error: "Invalid function id" });
31
+ return;
32
+ }
33
+ const func = (0, queries_1.getFunction)(connection_1.db, id);
34
+ if (!func) {
35
+ res.status(404).json({ error: "Function not found" });
36
+ return;
37
+ }
38
+ // Get latest snapshots per known env
39
+ const envStmt = connection_1.db.prepare(`
40
+ SELECT DISTINCT env FROM type_snapshots WHERE function_id = ?
41
+ `);
42
+ const envs = envStmt.all(id).map((r) => r.env);
43
+ const latestSnapshots = {};
44
+ for (const env of envs) {
45
+ const snapshot = (0, queries_1.getLatestSnapshot)(connection_1.db, id, env);
46
+ if (snapshot) {
47
+ latestSnapshots[env] = {
48
+ ...snapshot,
49
+ args_type: tryParseJson(snapshot.args_type),
50
+ return_type: tryParseJson(snapshot.return_type),
51
+ };
52
+ }
53
+ }
54
+ res.json({ function: func, latestSnapshots });
55
+ }
56
+ catch (err) {
57
+ console.error("Get function error:", err);
58
+ res.status(500).json({ error: "Internal server error" });
59
+ }
60
+ });
61
+ function tryParseJson(value) {
62
+ try {
63
+ return JSON.parse(value);
64
+ }
65
+ catch {
66
+ return value;
67
+ }
68
+ }
69
+ exports.default = router;
@@ -0,0 +1,2 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const express_1 = require("express");
4
+ const connection_1 = require("../db/connection");
5
+ const queries_1 = require("../db/queries");
6
+ const sse_broker_1 = require("../services/sse-broker");
7
+ const router = (0, express_1.Router)();
8
+ function processPayload(payload) {
9
+ const { functionName, module, language, environment, typeHash, argsType, returnType, sampleInput, sampleOutput, error, } = payload;
10
+ const env = environment || "unknown";
11
+ const func = (0, queries_1.upsertFunction)(connection_1.db, { functionName, module, environment: env, language });
12
+ const functionId = func.id;
13
+ let isNewType = false;
14
+ const existingSnapshot = (0, queries_1.findSnapshotByHash)(connection_1.db, functionId, typeHash, env);
15
+ if (!existingSnapshot) {
16
+ (0, queries_1.insertSnapshot)(connection_1.db, {
17
+ functionId,
18
+ typeHash,
19
+ argsType: JSON.stringify(argsType),
20
+ returnType: JSON.stringify(returnType),
21
+ variablesType: null,
22
+ sampleInput: sampleInput !== undefined ? JSON.stringify(sampleInput) : null,
23
+ sampleOutput: sampleOutput !== undefined ? JSON.stringify(sampleOutput) : null,
24
+ env,
25
+ });
26
+ isNewType = true;
27
+ sse_broker_1.sseBroker.broadcast("type:new", {
28
+ functionName,
29
+ module,
30
+ typeHash,
31
+ environment: env,
32
+ });
33
+ }
34
+ let errorRecord;
35
+ if (error) {
36
+ errorRecord = (0, queries_1.insertError)(connection_1.db, {
37
+ functionId,
38
+ errorType: error.type,
39
+ errorMessage: error.message,
40
+ stackTrace: error.stackTrace || null,
41
+ argsType: JSON.stringify(argsType),
42
+ returnType: JSON.stringify(returnType),
43
+ variablesType: null,
44
+ argsSnapshot: error.argsSnapshot !== undefined ? JSON.stringify(error.argsSnapshot) : null,
45
+ typeHash,
46
+ env,
47
+ });
48
+ sse_broker_1.sseBroker.broadcast("error:new", {
49
+ functionName,
50
+ module,
51
+ errorType: error.type,
52
+ errorMessage: error.message,
53
+ environment: env,
54
+ });
55
+ }
56
+ return { functionId, isNewType, error: errorRecord };
57
+ }
58
+ // POST / — single payload ingest
59
+ router.post("/", (req, res) => {
60
+ try {
61
+ const payload = req.body;
62
+ if (!payload.functionName || !payload.module || !payload.typeHash) {
63
+ res.status(400).json({ error: "Missing required fields: functionName, module, typeHash" });
64
+ return;
65
+ }
66
+ const result = processPayload(payload);
67
+ res.status(200).json({
68
+ ok: true,
69
+ functionId: result.functionId,
70
+ isNewType: result.isNewType,
71
+ errorId: result.error?.id,
72
+ });
73
+ }
74
+ catch (err) {
75
+ console.error("Ingest error:", err);
76
+ res.status(500).json({ error: "Internal server error" });
77
+ }
78
+ });
79
+ // POST /batch — batch ingest
80
+ router.post("/batch", (req, res) => {
81
+ try {
82
+ const { payloads } = req.body;
83
+ if (!Array.isArray(payloads) || payloads.length === 0) {
84
+ res.status(400).json({ error: "Expected non-empty payloads array" });
85
+ return;
86
+ }
87
+ const results = [];
88
+ const transaction = connection_1.db.transaction(() => {
89
+ for (const payload of payloads) {
90
+ if (!payload.functionName || !payload.module || !payload.typeHash) {
91
+ results.push({ error: "Missing required fields", functionName: payload.functionName });
92
+ continue;
93
+ }
94
+ const result = processPayload(payload);
95
+ results.push({
96
+ ok: true,
97
+ functionId: result.functionId,
98
+ isNewType: result.isNewType,
99
+ errorId: result.error?.id,
100
+ });
101
+ }
102
+ });
103
+ transaction();
104
+ res.status(200).json({ ok: true, results });
105
+ }
106
+ catch (err) {
107
+ console.error("Batch ingest error:", err);
108
+ res.status(500).json({ error: "Internal server error" });
109
+ }
110
+ });
111
+ exports.default = router;
@@ -0,0 +1,2 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const express_1 = require("express");
4
+ const connection_1 = require("../db/connection");
5
+ const queries_1 = require("../db/queries");
6
+ const router = (0, express_1.Router)();
7
+ function tryParseJson(value) {
8
+ if (typeof value !== "string")
9
+ return value;
10
+ try {
11
+ return JSON.parse(value);
12
+ }
13
+ catch {
14
+ return value;
15
+ }
16
+ }
17
+ /** Route-style function name → { method, path } */
18
+ function parseRouteName(name) {
19
+ const match = name.match(/^(GET|POST|PUT|DELETE|PATCH)\s+(.+)$/i);
20
+ if (!match)
21
+ return null;
22
+ return { method: match[1].toUpperCase(), path: match[2] };
23
+ }
24
+ /**
25
+ * GET /api/mock-config
26
+ *
27
+ * Returns all observed routes with their sample data, ready for mock server use.
28
+ */
29
+ router.get("/", (_req, res) => {
30
+ try {
31
+ const { rows } = (0, queries_1.listFunctions)(connection_1.db, { limit: 500 });
32
+ const routes = [];
33
+ for (const fn of rows) {
34
+ const parsed = parseRouteName(fn.function_name);
35
+ if (!parsed)
36
+ continue; // Skip non-route functions
37
+ const snapshot = (0, queries_1.getLatestSnapshot)(connection_1.db, fn.id);
38
+ if (!snapshot)
39
+ continue;
40
+ routes.push({
41
+ method: parsed.method,
42
+ path: parsed.path,
43
+ functionName: fn.function_name,
44
+ module: fn.module,
45
+ sampleInput: tryParseJson(snapshot.sample_input),
46
+ sampleOutput: tryParseJson(snapshot.sample_output),
47
+ observedAt: snapshot.observed_at,
48
+ });
49
+ }
50
+ res.json({ routes });
51
+ }
52
+ catch (err) {
53
+ console.error("Mock config error:", err);
54
+ res.status(500).json({ error: "Internal server error" });
55
+ }
56
+ });
57
+ exports.default = router;
@@ -0,0 +1,2 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const express_1 = require("express");
4
+ const connection_1 = require("../db/connection");
5
+ const queries_1 = require("../db/queries");
6
+ const router = (0, express_1.Router)();
7
+ function tryParseJson(value) {
8
+ try {
9
+ return JSON.parse(value);
10
+ }
11
+ catch {
12
+ return value;
13
+ }
14
+ }
15
+ /**
16
+ * Recursively search a TypeNode tree for fields matching the query.
17
+ * Returns an array of matching field paths.
18
+ */
19
+ function searchTypeNode(node, query, currentPath, results) {
20
+ const lowerQuery = query.toLowerCase();
21
+ switch (node.kind) {
22
+ case "object": {
23
+ for (const [key, val] of Object.entries(node.properties)) {
24
+ const fieldPath = currentPath ? `${currentPath}.${key}` : key;
25
+ // Check if the field name matches
26
+ if (key.toLowerCase().includes(lowerQuery)) {
27
+ results.push({
28
+ path: fieldPath,
29
+ kind: val.kind,
30
+ typeName: val.kind === "primitive" ? val.name : val.kind,
31
+ });
32
+ }
33
+ // Recurse into the value
34
+ searchTypeNode(val, query, fieldPath, results);
35
+ }
36
+ break;
37
+ }
38
+ case "array":
39
+ searchTypeNode(node.element, query, `${currentPath}[]`, results);
40
+ break;
41
+ case "union":
42
+ for (const member of node.members) {
43
+ searchTypeNode(member, query, currentPath, results);
44
+ }
45
+ break;
46
+ case "tuple":
47
+ for (let i = 0; i < node.elements.length; i++) {
48
+ searchTypeNode(node.elements[i], query, `${currentPath}[${i}]`, results);
49
+ }
50
+ break;
51
+ case "promise":
52
+ searchTypeNode(node.resolved, query, currentPath, results);
53
+ break;
54
+ case "map":
55
+ searchTypeNode(node.key, query, `${currentPath}.key`, results);
56
+ searchTypeNode(node.value, query, `${currentPath}.value`, results);
57
+ break;
58
+ case "set":
59
+ searchTypeNode(node.element, query, `${currentPath}[]`, results);
60
+ break;
61
+ case "primitive": {
62
+ // Match on primitive type name (e.g., searching "number" finds all number fields)
63
+ if (node.name.toLowerCase().includes(lowerQuery) && currentPath) {
64
+ // Only add if not already matched by field name
65
+ const alreadyMatched = results.some((r) => r.path === currentPath);
66
+ if (!alreadyMatched) {
67
+ results.push({
68
+ path: currentPath,
69
+ kind: "primitive",
70
+ typeName: node.name,
71
+ });
72
+ }
73
+ }
74
+ break;
75
+ }
76
+ }
77
+ }
78
+ // GET / — search across all observed types
79
+ router.get("/", (req, res) => {
80
+ try {
81
+ const query = req.query.q;
82
+ if (!query || query.trim().length === 0) {
83
+ res.status(400).json({ error: "Missing query parameter 'q'" });
84
+ return;
85
+ }
86
+ const env = req.query.env;
87
+ const lowerQuery = query.toLowerCase();
88
+ // Get all functions
89
+ const { rows: functionRows } = (0, queries_1.listFunctions)(connection_1.db, { env, limit: 500 });
90
+ const results = [];
91
+ for (const fn of functionRows) {
92
+ const functionId = fn.id;
93
+ const functionName = fn.function_name;
94
+ const moduleName = fn.module;
95
+ const environment = fn.environment || "development";
96
+ const lastSeen = fn.last_seen_at;
97
+ // Check function name match
98
+ const nameMatches = functionName.toLowerCase().includes(lowerQuery);
99
+ // Get latest snapshot and search types
100
+ const snapshot = (0, queries_1.getLatestSnapshot)(connection_1.db, functionId, env);
101
+ const fieldMatches = [];
102
+ if (snapshot) {
103
+ const argsType = tryParseJson(snapshot.args_type);
104
+ const returnType = tryParseJson(snapshot.return_type);
105
+ if (argsType) {
106
+ searchTypeNode(argsType, query, "args", fieldMatches);
107
+ }
108
+ if (returnType) {
109
+ searchTypeNode(returnType, query, "response", fieldMatches);
110
+ }
111
+ }
112
+ // Include if function name matches or any fields match
113
+ if (nameMatches || fieldMatches.length > 0) {
114
+ results.push({
115
+ functionName,
116
+ module: moduleName,
117
+ environment,
118
+ lastSeen,
119
+ matches: nameMatches && fieldMatches.length === 0
120
+ ? [{ path: "(function name)", kind: "name", typeName: undefined }]
121
+ : fieldMatches,
122
+ });
123
+ }
124
+ }
125
+ res.json({
126
+ query,
127
+ total: results.length,
128
+ results,
129
+ });
130
+ }
131
+ catch (err) {
132
+ console.error("Search error:", err);
133
+ res.status(500).json({ error: "Internal server error" });
134
+ }
135
+ });
136
+ exports.default = router;
@@ -0,0 +1,2 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const express_1 = require("express");
4
+ const sse_broker_1 = require("../services/sse-broker");
5
+ const router = (0, express_1.Router)();
6
+ // GET / — SSE endpoint
7
+ router.get("/", (req, res) => {
8
+ const filter = req.query.filter;
9
+ sse_broker_1.sseBroker.addClient(res, filter);
10
+ });
11
+ exports.default = router;
@@ -0,0 +1,2 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const express_1 = require("express");
4
+ const connection_1 = require("../db/connection");
5
+ const queries_1 = require("../db/queries");
6
+ const type_differ_1 = require("../services/type-differ");
7
+ const router = (0, express_1.Router)();
8
+ // GET /:functionId — get type snapshots for a function
9
+ router.get("/:functionId", (req, res) => {
10
+ try {
11
+ const functionId = parseInt(req.params.functionId, 10);
12
+ if (isNaN(functionId)) {
13
+ res.status(400).json({ error: "Invalid functionId" });
14
+ return;
15
+ }
16
+ const { env, limit } = req.query;
17
+ const snapshots = (0, queries_1.listSnapshots)(connection_1.db, {
18
+ functionId,
19
+ env: env,
20
+ limit: limit ? parseInt(limit, 10) : undefined,
21
+ });
22
+ const parsed = snapshots.map((s) => ({
23
+ ...s,
24
+ args_type: tryParseJson(s.args_type),
25
+ return_type: tryParseJson(s.return_type),
26
+ sample_input: tryParseJson(s.sample_input),
27
+ sample_output: tryParseJson(s.sample_output),
28
+ }));
29
+ res.json({ snapshots: parsed });
30
+ }
31
+ catch (err) {
32
+ console.error("List types error:", err);
33
+ res.status(500).json({ error: "Internal server error" });
34
+ }
35
+ });
36
+ // GET /:functionId/diff — diff between two snapshots or envs
37
+ router.get("/:functionId/diff", (req, res) => {
38
+ try {
39
+ const functionId = parseInt(req.params.functionId, 10);
40
+ if (isNaN(functionId)) {
41
+ res.status(400).json({ error: "Invalid functionId" });
42
+ return;
43
+ }
44
+ const { from, to, fromEnv, toEnv } = req.query;
45
+ let fromSnapshot;
46
+ let toSnapshot;
47
+ if (from && to) {
48
+ // Diff by snapshot IDs
49
+ const stmt = connection_1.db.prepare(`SELECT * FROM type_snapshots WHERE id = ? AND function_id = ?`);
50
+ fromSnapshot = stmt.get(parseInt(from, 10), functionId);
51
+ toSnapshot = stmt.get(parseInt(to, 10), functionId);
52
+ }
53
+ else if (fromEnv && toEnv) {
54
+ // Diff between envs (latest snapshot in each)
55
+ const stmt = connection_1.db.prepare(`
56
+ SELECT * FROM type_snapshots
57
+ WHERE function_id = ? AND env = ?
58
+ ORDER BY observed_at DESC
59
+ LIMIT 1
60
+ `);
61
+ fromSnapshot = stmt.get(functionId, fromEnv);
62
+ toSnapshot = stmt.get(functionId, toEnv);
63
+ }
64
+ else {
65
+ res.status(400).json({ error: "Provide 'from' and 'to' snapshot IDs, or 'fromEnv' and 'toEnv'" });
66
+ return;
67
+ }
68
+ if (!fromSnapshot || !toSnapshot) {
69
+ res.status(404).json({ error: "One or both snapshots not found" });
70
+ return;
71
+ }
72
+ const fromArgs = JSON.parse(fromSnapshot.args_type);
73
+ const toArgs = JSON.parse(toSnapshot.args_type);
74
+ const fromReturn = JSON.parse(fromSnapshot.return_type);
75
+ const toReturn = JSON.parse(toSnapshot.return_type);
76
+ const argsDiff = (0, type_differ_1.diffTypes)(fromArgs, toArgs, "args");
77
+ const returnDiff = (0, type_differ_1.diffTypes)(fromReturn, toReturn, "return");
78
+ res.json({
79
+ from: { id: fromSnapshot.id, env: fromSnapshot.env, observed_at: fromSnapshot.observed_at },
80
+ to: { id: toSnapshot.id, env: toSnapshot.env, observed_at: toSnapshot.observed_at },
81
+ diffs: [...argsDiff, ...returnDiff],
82
+ });
83
+ }
84
+ catch (err) {
85
+ console.error("Type diff error:", err);
86
+ res.status(500).json({ error: "Internal server error" });
87
+ }
88
+ });
89
+ function tryParseJson(value) {
90
+ try {
91
+ return JSON.parse(value);
92
+ }
93
+ catch {
94
+ return value;
95
+ }
96
+ }
97
+ exports.default = router;
@@ -0,0 +1,2 @@
1
+ declare const app: import("express-serve-static-core").Express;
2
+ export { app };
package/dist/server.js ADDED
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.app = void 0;
7
+ const express_1 = __importDefault(require("express"));
8
+ const cors_1 = __importDefault(require("cors"));
9
+ const ingest_1 = __importDefault(require("./routes/ingest"));
10
+ const functions_1 = __importDefault(require("./routes/functions"));
11
+ const types_1 = __importDefault(require("./routes/types"));
12
+ const errors_1 = __importDefault(require("./routes/errors"));
13
+ const tail_1 = __importDefault(require("./routes/tail"));
14
+ const codegen_1 = __importDefault(require("./routes/codegen"));
15
+ const mock_1 = __importDefault(require("./routes/mock"));
16
+ const diff_1 = __importDefault(require("./routes/diff"));
17
+ const dashboard_1 = __importDefault(require("./routes/dashboard"));
18
+ const coverage_1 = __importDefault(require("./routes/coverage"));
19
+ const audit_1 = __importDefault(require("./routes/audit"));
20
+ const search_1 = __importDefault(require("./routes/search"));
21
+ const app = (0, express_1.default)();
22
+ exports.app = app;
23
+ app.use((0, cors_1.default)());
24
+ app.use(express_1.default.json({ limit: "5mb" }));
25
+ app.use("/api/ingest", ingest_1.default);
26
+ app.use("/api/functions", functions_1.default);
27
+ app.use("/api/types", types_1.default);
28
+ app.use("/api/errors", errors_1.default);
29
+ app.use("/api/tail", tail_1.default);
30
+ app.use("/api/codegen", codegen_1.default);
31
+ app.use("/api/mock-config", mock_1.default);
32
+ app.use("/api/diff", diff_1.default);
33
+ app.use("/dashboard", dashboard_1.default);
34
+ app.use("/api/coverage", coverage_1.default);
35
+ app.use("/api/audit", audit_1.default);
36
+ app.use("/api/search", search_1.default);
37
+ // Health check
38
+ app.get("/api/health", (_req, res) => {
39
+ res.json({ ok: true, timestamp: new Date().toISOString() });
40
+ });
@@ -0,0 +1,10 @@
1
+ import { EventEmitter } from "events";
2
+ import { Response } from "express";
3
+ declare class SseBroker extends EventEmitter {
4
+ private clients;
5
+ addClient(res: Response, filter?: string): void;
6
+ removeClient(res: Response): void;
7
+ broadcast(event: string, data: unknown): void;
8
+ }
9
+ export declare const sseBroker: SseBroker;
10
+ export {};