@pranshulsoni/flowwatch 1.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 (73) hide show
  1. package/README.md +442 -0
  2. package/dist/ai/groqInsightService.d.ts +39 -0
  3. package/dist/ai/groqInsightService.js +230 -0
  4. package/dist/createFlowwatch.d.ts +17 -0
  5. package/dist/createFlowwatch.js +90 -0
  6. package/dist/dashboard/routes/dashboardResponse.d.ts +204 -0
  7. package/dist/dashboard/routes/dashboardResponse.js +248 -0
  8. package/dist/dashboard/routes/router.d.ts +13 -0
  9. package/dist/dashboard/routes/router.js +708 -0
  10. package/dist/dashboard/static/dashboard.html +6061 -0
  11. package/dist/engine/background/queues/workflowQueue.d.ts +6 -0
  12. package/dist/engine/background/queues/workflowQueue.js +14 -0
  13. package/dist/engine/background/workers/workflowWorker.d.ts +15 -0
  14. package/dist/engine/background/workers/workflowWorker.js +98 -0
  15. package/dist/engine/errors/errorEngine.d.ts +27 -0
  16. package/dist/engine/errors/errorEngine.js +115 -0
  17. package/dist/engine/flags/evaluateFlag.d.ts +3 -0
  18. package/dist/engine/flags/evaluateFlag.js +50 -0
  19. package/dist/engine/flags/flagEngine.d.ts +9 -0
  20. package/dist/engine/flags/flagEngine.js +52 -0
  21. package/dist/engine/flags/hashRollout.d.ts +1 -0
  22. package/dist/engine/flags/hashRollout.js +9 -0
  23. package/dist/engine/flags/types.d.ts +7 -0
  24. package/dist/engine/flags/types.js +1 -0
  25. package/dist/engine/trace/traceEngine.d.ts +26 -0
  26. package/dist/engine/trace/traceEngine.js +76 -0
  27. package/dist/engine/workflows/types.d.ts +28 -0
  28. package/dist/engine/workflows/types.js +1 -0
  29. package/dist/engine/workflows/workflowEngine.d.ts +15 -0
  30. package/dist/engine/workflows/workflowEngine.js +112 -0
  31. package/dist/index.d.ts +9 -0
  32. package/dist/index.js +3 -0
  33. package/dist/persistence/cache/redisClient.d.ts +2 -0
  34. package/dist/persistence/cache/redisClient.js +4 -0
  35. package/dist/persistence/db/postgres.d.ts +3 -0
  36. package/dist/persistence/db/postgres.js +4 -0
  37. package/dist/persistence/migrations/migrationRunner.d.ts +3 -0
  38. package/dist/persistence/migrations/migrationRunner.js +46 -0
  39. package/dist/persistence/migrations/migrations.d.ts +5 -0
  40. package/dist/persistence/migrations/migrations.js +191 -0
  41. package/dist/persistence/repositories/errors/errorRepository.d.ts +38 -0
  42. package/dist/persistence/repositories/errors/errorRepository.js +63 -0
  43. package/dist/persistence/repositories/flags/flagRepository.d.ts +72 -0
  44. package/dist/persistence/repositories/flags/flagRepository.js +245 -0
  45. package/dist/persistence/repositories/traces/traceRepository.d.ts +64 -0
  46. package/dist/persistence/repositories/traces/traceRepository.js +110 -0
  47. package/dist/persistence/repositories/workflows/workflowRepository.d.ts +93 -0
  48. package/dist/persistence/repositories/workflows/workflowRepository.js +260 -0
  49. package/dist/persistence/transaction.d.ts +2 -0
  50. package/dist/persistence/transaction.js +16 -0
  51. package/dist/runtime/config/normalizeConfig.d.ts +2 -0
  52. package/dist/runtime/config/normalizeConfig.js +46 -0
  53. package/dist/runtime/config/validationConfig.d.ts +2 -0
  54. package/dist/runtime/config/validationConfig.js +119 -0
  55. package/dist/runtime/health/healthService.d.ts +30 -0
  56. package/dist/runtime/health/healthService.js +54 -0
  57. package/dist/runtime/tracing/traceContext.d.ts +12 -0
  58. package/dist/runtime/tracing/traceContext.js +28 -0
  59. package/dist/runtime/tracing/tracingMiddleware.d.ts +3 -0
  60. package/dist/runtime/tracing/tracingMiddleware.js +46 -0
  61. package/dist/search/elasticsearch/client.d.ts +2 -0
  62. package/dist/search/elasticsearch/client.js +4 -0
  63. package/dist/search/elasticsearch/indexSetup.d.ts +3 -0
  64. package/dist/search/elasticsearch/indexSetup.js +43 -0
  65. package/dist/search/elasticsearch/indexer.d.ts +9 -0
  66. package/dist/search/elasticsearch/indexer.js +86 -0
  67. package/dist/search/elasticsearch/mappingChecker.d.ts +2 -0
  68. package/dist/search/elasticsearch/mappingChecker.js +28 -0
  69. package/dist/types/index.d.ts +48 -0
  70. package/dist/types/index.js +1 -0
  71. package/dist/utils/flowwatchEnvStore.d.ts +27 -0
  72. package/dist/utils/flowwatchEnvStore.js +145 -0
  73. package/package.json +63 -0
@@ -0,0 +1,248 @@
1
+ import { isGroqApiKeyConfigured, getGroqModel } from "../../utils/flowwatchEnvStore.js";
2
+ function asIso(value) {
3
+ if (!value) {
4
+ return null;
5
+ }
6
+ if (value instanceof Date) {
7
+ return value.toISOString();
8
+ }
9
+ return String(value);
10
+ }
11
+ function durationBetween(start, end) {
12
+ if (!start || !end) {
13
+ return null;
14
+ }
15
+ const startedAt = new Date(String(start)).getTime();
16
+ const endedAt = new Date(String(end)).getTime();
17
+ if (Number.isNaN(startedAt) || Number.isNaN(endedAt)) {
18
+ return null;
19
+ }
20
+ return Math.max(0, endedAt - startedAt);
21
+ }
22
+ function formatDashboardTime(value) {
23
+ const iso = asIso(value);
24
+ if (!iso) {
25
+ return "-";
26
+ }
27
+ return new Date(iso).toLocaleTimeString("en-US", {
28
+ hour12: false,
29
+ hour: "2-digit",
30
+ minute: "2-digit",
31
+ second: "2-digit",
32
+ });
33
+ }
34
+ function formatDashboardDateTime(value) {
35
+ const iso = asIso(value);
36
+ if (!iso) {
37
+ return "-";
38
+ }
39
+ return new Date(iso).toISOString().slice(0, 16).replace("T", " ");
40
+ }
41
+ function formatDuration(ms) {
42
+ if (ms === null || ms === undefined) {
43
+ return "-";
44
+ }
45
+ if (ms < 1000) {
46
+ return `${ms}ms`;
47
+ }
48
+ return `${(ms / 1000).toFixed(ms >= 10000 ? 0 : 1)}s`;
49
+ }
50
+ function dashboardStatus(status) {
51
+ if (status === "ok") {
52
+ return "completed";
53
+ }
54
+ if (status === "error") {
55
+ return "failed";
56
+ }
57
+ return status || "unknown";
58
+ }
59
+ export function serializeFlag(flag, ruleCount = 0) {
60
+ return {
61
+ id: flag.id,
62
+ key: flag.key,
63
+ description: flag.description,
64
+ enabled: flag.enabled,
65
+ rollout: flag.rollout_percentage,
66
+ rolloutPercentage: flag.rollout_percentage,
67
+ rules: ruleCount,
68
+ changedBy: flag.changed_by ?? "dashboard",
69
+ createdAt: asIso(flag.created_at),
70
+ updatedAt: asIso(flag.updated_at),
71
+ };
72
+ }
73
+ export function serializeRule(rule) {
74
+ return {
75
+ id: rule.id,
76
+ flagId: rule.flag_id,
77
+ attribute: rule.attribute,
78
+ operator: rule.operator,
79
+ value: rule.value,
80
+ enabled: rule.enabled,
81
+ createdAt: asIso(rule.created_at),
82
+ updatedAt: asIso(rule.updated_at),
83
+ };
84
+ }
85
+ export function serializeAuditLog(log) {
86
+ return {
87
+ id: log.id,
88
+ flagId: log.flag_id,
89
+ action: log.action,
90
+ before: log.before,
91
+ after: log.after,
92
+ changedBy: log.changed_by,
93
+ createdAt: asIso(log.created_at),
94
+ };
95
+ }
96
+ export function serializeError(error) {
97
+ return {
98
+ id: error.id,
99
+ message: error.message,
100
+ name: error.name || "Error",
101
+ category: error.category,
102
+ level: error.level,
103
+ source: error.source,
104
+ status: error.status_code,
105
+ statusCode: error.status_code,
106
+ trace: error.trace_id || "-",
107
+ traceId: error.trace_id,
108
+ spanId: error.span_id,
109
+ fingerprint: error.fingerprint,
110
+ metadata: error.metadata,
111
+ stack: error.stack,
112
+ occurred: formatDashboardTime(error.occurred_at),
113
+ occurredAt: asIso(error.occurred_at),
114
+ createdAt: asIso(error.created_at),
115
+ };
116
+ }
117
+ export function serializeTrace(trace, spans = []) {
118
+ return {
119
+ id: trace.id,
120
+ method: trace.method,
121
+ path: trace.path,
122
+ status: trace.status_code,
123
+ statusCode: trace.status_code,
124
+ ip: trace.ip,
125
+ duration: trace.duration_ms ?? durationBetween(trace.started_at, trace.ended_at),
126
+ durationMs: trace.duration_ms ?? durationBetween(trace.started_at, trace.ended_at),
127
+ userId: trace.user_id,
128
+ userAgent: trace.user_agent,
129
+ metadata: trace.metadata,
130
+ startedAt: asIso(trace.started_at),
131
+ endedAt: asIso(trace.ended_at),
132
+ createdAt: asIso(trace.created_at),
133
+ spans: spans.map((span) => ({
134
+ id: span.id,
135
+ traceId: span.trace_id,
136
+ parentSpanId: span.parent_span_id,
137
+ name: span.name,
138
+ type: span.type,
139
+ status: dashboardStatus(span.status),
140
+ duration: span.duration_ms ?? durationBetween(span.started_at, span.ended_at),
141
+ durationMs: span.duration_ms ?? durationBetween(span.started_at, span.ended_at),
142
+ metadata: span.metadata,
143
+ startedAt: asIso(span.started_at),
144
+ endedAt: asIso(span.ended_at),
145
+ })),
146
+ };
147
+ }
148
+ export function serializeExecution(execution, steps = []) {
149
+ const failedStep = steps.find((step) => step.status === "failed");
150
+ const startedAt = execution.started_at || execution.created_at;
151
+ const finishedAt = execution.completed_at || execution.failed_at;
152
+ const durationMs = durationBetween(startedAt, finishedAt);
153
+ return {
154
+ id: execution.id,
155
+ workflowId: execution.workflow_id,
156
+ workflow: execution.workflow_name,
157
+ workflowName: execution.workflow_name,
158
+ workflowVersion: execution.workflow_version,
159
+ status: execution.status,
160
+ input: execution.input,
161
+ output: execution.output,
162
+ error: execution.error,
163
+ started: formatDashboardTime(startedAt),
164
+ finished: formatDashboardTime(finishedAt),
165
+ failedStep: failedStep?.step_name || "-",
166
+ attempts: steps.reduce((total, step) => total + Number(step.attempt_count || 0), 0),
167
+ duration: formatDuration(durationMs),
168
+ createdAt: asIso(execution.created_at),
169
+ startedAt: asIso(execution.started_at),
170
+ completedAt: asIso(execution.completed_at),
171
+ failedAt: asIso(execution.failed_at),
172
+ durationMs,
173
+ steps: steps.map((step) => ({
174
+ id: step.id,
175
+ executionId: step.execution_id,
176
+ workflowStepId: step.workflow_step_id,
177
+ stepIndex: step.step_index,
178
+ stepName: step.step_name,
179
+ name: step.step_name,
180
+ status: step.status,
181
+ input: step.input,
182
+ output: step.output,
183
+ error: step.error,
184
+ attempts: step.attempt_count,
185
+ attemptCount: step.attempt_count,
186
+ maxRetries: step.max_retries,
187
+ startedAt: asIso(step.started_at),
188
+ completedAt: asIso(step.completed_at),
189
+ failedAt: asIso(step.failed_at),
190
+ nextRetryAt: asIso(step.next_retry_at),
191
+ durationMs: durationBetween(step.started_at, step.completed_at || step.failed_at),
192
+ duration: formatDuration(durationBetween(step.started_at, step.completed_at || step.failed_at)),
193
+ })),
194
+ };
195
+ }
196
+ export function serializeSettings(config) {
197
+ return {
198
+ serviceName: config.runtime.serviceName,
199
+ environment: config.runtime.environment,
200
+ dashboard: {
201
+ path: config.dashboard.path,
202
+ enabled: config.dashboard.enabled,
203
+ authEnabled: Boolean(config.dashboard.auth || config.dashboard.token),
204
+ },
205
+ worker: {
206
+ ...config.worker,
207
+ concurrency: config.worker.workflowConcurrency,
208
+ },
209
+ migrations: config.migrations,
210
+ ai: {
211
+ groqApiKeyConfigured: isGroqApiKeyConfigured(),
212
+ groqModel: getGroqModel() || "llama-3.3-70b-versatile",
213
+ },
214
+ };
215
+ }
216
+ export function serializeWorkflowSummary(workflow, executions, definitionSteps, latestExecution, latestExecutionSteps = []) {
217
+ const workflowExecutions = executions.filter((e) => e.workflow_name === workflow.name);
218
+ const completed = workflowExecutions.filter((e) => e.status === "completed").length;
219
+ const failed = workflowExecutions.filter((e) => e.status === "failed").length;
220
+ const running = workflowExecutions.filter((e) => e.status === "running").length;
221
+ return {
222
+ id: workflow.id,
223
+ name: workflow.name,
224
+ version: workflow.version,
225
+ steps: definitionSteps.length,
226
+ created: formatDashboardDateTime(workflow.created_at),
227
+ createdAt: asIso(workflow.created_at),
228
+ updatedAt: asIso(workflow.updated_at),
229
+ lastStatus: latestExecution?.status || "never run",
230
+ totalRuns: workflowExecutions.length,
231
+ completedRuns: completed,
232
+ failedRuns: failed,
233
+ runningRuns: running,
234
+ failures: failed,
235
+ pinned: false,
236
+ chain: definitionSteps.map((step) => step.name),
237
+ latestExecution: latestExecution ? serializeExecution(latestExecution, latestExecutionSteps) : undefined,
238
+ };
239
+ }
240
+ export function latestByWorkflow(executions) {
241
+ const latest = new Map();
242
+ for (const execution of executions) {
243
+ if (!latest.has(execution.workflow_name)) {
244
+ latest.set(execution.workflow_name, execution);
245
+ }
246
+ }
247
+ return latest;
248
+ }
@@ -0,0 +1,13 @@
1
+ import type { Client } from "@elastic/elasticsearch";
2
+ import { Router } from "express";
3
+ import type { Redis } from "ioredis";
4
+ import type { Pool } from "pg";
5
+ import type { NormalizedFlowwatchConfig } from "../../types/index.js";
6
+ interface DashboardRouterOptions {
7
+ config: NormalizedFlowwatchConfig;
8
+ postgresPool: Pool;
9
+ redisClient: Redis;
10
+ elasticsearchClient: Client;
11
+ }
12
+ export declare function createDashboardRouter(options: DashboardRouterOptions): Router;
13
+ export {};