blokctl 0.6.20 → 0.7.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 (77) hide show
  1. package/dist/__tests__/modular-observability.capstone.e2e.test.js +72 -0
  2. package/dist/commands/create/node.js +46 -66
  3. package/dist/commands/create/project.js +55 -9
  4. package/dist/commands/create/utils/Examples.d.ts +8 -20
  5. package/dist/commands/create/utils/Examples.js +138 -412
  6. package/dist/commands/dev/index.js +40 -1
  7. package/dist/commands/gen/appTypes.js +40 -1
  8. package/dist/commands/generate/NodeGenerator.d.ts +0 -2
  9. package/dist/commands/generate/NodeGenerator.js +0 -20
  10. package/dist/commands/generate/RuntimeGenerator.d.ts +0 -2
  11. package/dist/commands/generate/RuntimeGenerator.js +0 -19
  12. package/dist/commands/generate/RuntimeGenerator.test.js +0 -29
  13. package/dist/commands/generate/TriggerGenerator.d.ts +0 -2
  14. package/dist/commands/generate/TriggerGenerator.js +0 -19
  15. package/dist/commands/generate/WorkflowGenerator.d.ts +0 -2
  16. package/dist/commands/generate/WorkflowGenerator.js +0 -19
  17. package/dist/commands/generate/e2e/NodeGenerator.e2e.test.js +0 -12
  18. package/dist/commands/generate/e2e/RuntimeGenerator.e2e.test.js +0 -12
  19. package/dist/commands/generate/e2e/TriggerGenerator.e2e.test.js +0 -14
  20. package/dist/commands/monitor/monitor-component.js +5 -5
  21. package/dist/commands/observability/add.d.ts +2 -0
  22. package/dist/commands/observability/add.js +113 -0
  23. package/dist/commands/observability/alerting-module.test.js +43 -0
  24. package/dist/commands/observability/apply.d.ts +10 -0
  25. package/dist/commands/observability/apply.js +11 -0
  26. package/dist/commands/observability/descriptor.d.ts +37 -0
  27. package/dist/commands/observability/descriptor.js +203 -0
  28. package/dist/commands/observability/descriptor.test.d.ts +1 -0
  29. package/dist/commands/observability/descriptor.test.js +40 -0
  30. package/dist/commands/observability/index.d.ts +1 -0
  31. package/dist/commands/observability/index.js +53 -0
  32. package/dist/commands/observability/list.d.ts +2 -0
  33. package/dist/commands/observability/list.js +45 -0
  34. package/dist/commands/observability/logging-module.test.d.ts +1 -0
  35. package/dist/commands/observability/logging-module.test.js +43 -0
  36. package/dist/commands/observability/obs-stack-module.test.d.ts +1 -0
  37. package/dist/commands/observability/obs-stack-module.test.js +33 -0
  38. package/dist/commands/observability/remove.d.ts +2 -0
  39. package/dist/commands/observability/remove.js +62 -0
  40. package/dist/commands/observability/shared.d.ts +6 -0
  41. package/dist/commands/observability/shared.js +23 -0
  42. package/dist/commands/observability/status.d.ts +2 -0
  43. package/dist/commands/observability/status.js +36 -0
  44. package/dist/commands/observability/tracing-module.test.d.ts +1 -0
  45. package/dist/commands/observability/tracing-module.test.js +42 -0
  46. package/dist/commands/profile/index.js +7 -10
  47. package/dist/commands/watch/format.d.ts +23 -0
  48. package/dist/commands/watch/format.js +60 -0
  49. package/dist/commands/watch/index.d.ts +1 -0
  50. package/dist/commands/watch/index.js +53 -0
  51. package/dist/commands/watch/sse.d.ts +16 -0
  52. package/dist/commands/watch/sse.js +82 -0
  53. package/dist/index.d.ts +2 -0
  54. package/dist/index.js +4 -0
  55. package/dist/services/obs-setup.d.ts +5 -0
  56. package/dist/services/obs-setup.js +68 -0
  57. package/dist/services/obs-setup.test.d.ts +1 -0
  58. package/dist/services/obs-setup.test.js +71 -0
  59. package/dist/services/obs-tiers.d.ts +9 -0
  60. package/dist/services/obs-tiers.js +16 -0
  61. package/dist/services/observability-mutations.d.ts +4 -0
  62. package/dist/services/observability-mutations.js +46 -0
  63. package/dist/services/observability-mutations.test.d.ts +1 -0
  64. package/dist/services/observability-mutations.test.js +57 -0
  65. package/dist/services/runtime-setup.d.ts +12 -1
  66. package/dist/services/runtime-setup.js +274 -14
  67. package/dist/studio-dist/assets/{index-BD8_9YPN.js → index-CnFqCRQe.js} +17 -17
  68. package/dist/studio-dist/index.html +1 -1
  69. package/package.json +3 -3
  70. package/dist/commands/generate/GenerationAnalytics.d.ts +0 -61
  71. package/dist/commands/generate/GenerationAnalytics.js +0 -163
  72. package/dist/commands/generate/GenerationAnalytics.test.js +0 -407
  73. package/dist/commands/generate/PromptVersioning.d.ts +0 -25
  74. package/dist/commands/generate/PromptVersioning.js +0 -71
  75. package/dist/commands/generate/PromptVersioning.test.js +0 -120
  76. /package/dist/{commands/generate/GenerationAnalytics.test.d.ts → __tests__/modular-observability.capstone.e2e.test.d.ts} +0 -0
  77. /package/dist/commands/{generate/PromptVersioning.test.d.ts → observability/alerting-module.test.d.ts} +0 -0
@@ -15,7 +15,7 @@
15
15
  href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Newsreader:ital,wght@1,400;1,500&display=swap"
16
16
  rel="stylesheet"
17
17
  />
18
- <script type="module" crossorigin src="/assets/index-BD8_9YPN.js"></script>
18
+ <script type="module" crossorigin src="/assets/index-CnFqCRQe.js"></script>
19
19
  <link rel="modulepreload" crossorigin href="/assets/tanstack-query-Day3Mt-4.js">
20
20
  <link rel="modulepreload" crossorigin href="/assets/tanstack-router-BB95iErN.js">
21
21
  <link rel="modulepreload" crossorigin href="/assets/charts-Dh48HebV.js">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blokctl",
3
- "version": "0.6.20",
3
+ "version": "0.7.0",
4
4
  "author": "Deskree Technologies Inc.",
5
5
  "license": "Apache-2.0",
6
6
  "description": "cli for blok",
@@ -13,7 +13,7 @@
13
13
  "bin": {
14
14
  "blokctl": "./dist/index.js"
15
15
  },
16
- "files": ["dist", "node_modules"],
16
+ "files": ["dist"],
17
17
  "scripts": {
18
18
  "build": "rm -rf dist && bun run tsc",
19
19
  "build:dev": "tsc --watch",
@@ -30,7 +30,7 @@
30
30
  "keywords": ["blokctl", "cli", "blok", "blok"],
31
31
  "dependencies": {
32
32
  "@ai-sdk/openai": "^1.3.22",
33
- "@blokjs/runner": "^0.6.20",
33
+ "@blokjs/runner": "^0.7.0",
34
34
  "@clack/prompts": "^1.0.0",
35
35
  "ai": "^4.3.16",
36
36
  "better-sqlite3": "^12.6.2",
@@ -1,61 +0,0 @@
1
- export type GenerationType = "node" | "workflow" | "trigger";
2
- export interface GenerationEvent {
3
- id: string;
4
- timestamp: string;
5
- type: GenerationType;
6
- subtype: string;
7
- name: string;
8
- success: boolean;
9
- attempts: number;
10
- durationMs: number;
11
- errors: string[];
12
- promptVersion: string;
13
- }
14
- export interface GenerationStats {
15
- totalGenerations: number;
16
- successCount: number;
17
- failureCount: number;
18
- successRate: number;
19
- averageAttempts: number;
20
- averageDurationMs: number;
21
- topErrors: Array<{
22
- pattern: string;
23
- count: number;
24
- }>;
25
- byType: Record<GenerationType, TypeStats>;
26
- }
27
- export interface TypeStats {
28
- total: number;
29
- success: number;
30
- failure: number;
31
- successRate: number;
32
- averageAttempts: number;
33
- }
34
- export declare class GenerationAnalytics {
35
- private events;
36
- private static instance;
37
- static getInstance(): GenerationAnalytics;
38
- recordEvent(event: Omit<GenerationEvent, "id" | "timestamp">): GenerationEvent;
39
- startTimer(): () => number;
40
- getStats(): GenerationStats;
41
- private getTypeStats;
42
- private getTopErrors;
43
- private normalizeErrorPattern;
44
- getEvents(filter?: {
45
- type?: GenerationType;
46
- success?: boolean;
47
- since?: string;
48
- }): GenerationEvent[];
49
- getFirstAttemptSuccessRate(): number;
50
- getSuccessRateByPromptVersion(): Record<string, {
51
- total: number;
52
- success: number;
53
- rate: number;
54
- }>;
55
- toJSON(): string;
56
- fromJSON(json: string): void;
57
- clear(): void;
58
- static resetInstance(): void;
59
- private generateId;
60
- }
61
- export default GenerationAnalytics;
@@ -1,163 +0,0 @@
1
- export class GenerationAnalytics {
2
- events = [];
3
- static instance = null;
4
- static getInstance() {
5
- if (!GenerationAnalytics.instance) {
6
- GenerationAnalytics.instance = new GenerationAnalytics();
7
- }
8
- return GenerationAnalytics.instance;
9
- }
10
- recordEvent(event) {
11
- const fullEvent = {
12
- ...event,
13
- id: this.generateId(),
14
- timestamp: new Date().toISOString(),
15
- };
16
- this.events.push(fullEvent);
17
- return fullEvent;
18
- }
19
- startTimer() {
20
- const start = performance.now();
21
- return () => Math.round(performance.now() - start);
22
- }
23
- getStats() {
24
- const total = this.events.length;
25
- if (total === 0) {
26
- return {
27
- totalGenerations: 0,
28
- successCount: 0,
29
- failureCount: 0,
30
- successRate: 0,
31
- averageAttempts: 0,
32
- averageDurationMs: 0,
33
- topErrors: [],
34
- byType: {
35
- node: { total: 0, success: 0, failure: 0, successRate: 0, averageAttempts: 0 },
36
- workflow: { total: 0, success: 0, failure: 0, successRate: 0, averageAttempts: 0 },
37
- trigger: { total: 0, success: 0, failure: 0, successRate: 0, averageAttempts: 0 },
38
- },
39
- };
40
- }
41
- const successCount = this.events.filter((e) => e.success).length;
42
- const failureCount = total - successCount;
43
- const totalAttempts = this.events.reduce((sum, e) => sum + e.attempts, 0);
44
- const totalDuration = this.events.reduce((sum, e) => sum + e.durationMs, 0);
45
- return {
46
- totalGenerations: total,
47
- successCount,
48
- failureCount,
49
- successRate: Math.round((successCount / total) * 100),
50
- averageAttempts: Math.round((totalAttempts / total) * 10) / 10,
51
- averageDurationMs: Math.round(totalDuration / total),
52
- topErrors: this.getTopErrors(),
53
- byType: {
54
- node: this.getTypeStats("node"),
55
- workflow: this.getTypeStats("workflow"),
56
- trigger: this.getTypeStats("trigger"),
57
- },
58
- };
59
- }
60
- getTypeStats(type) {
61
- const typeEvents = this.events.filter((e) => e.type === type);
62
- const total = typeEvents.length;
63
- if (total === 0) {
64
- return { total: 0, success: 0, failure: 0, successRate: 0, averageAttempts: 0 };
65
- }
66
- const success = typeEvents.filter((e) => e.success).length;
67
- const failure = total - success;
68
- const totalAttempts = typeEvents.reduce((sum, e) => sum + e.attempts, 0);
69
- return {
70
- total,
71
- success,
72
- failure,
73
- successRate: Math.round((success / total) * 100),
74
- averageAttempts: Math.round((totalAttempts / total) * 10) / 10,
75
- };
76
- }
77
- getTopErrors() {
78
- const errorCounts = new Map();
79
- for (const event of this.events) {
80
- for (const error of event.errors) {
81
- const pattern = this.normalizeErrorPattern(error);
82
- errorCounts.set(pattern, (errorCounts.get(pattern) || 0) + 1);
83
- }
84
- }
85
- return Array.from(errorCounts.entries())
86
- .map(([pattern, count]) => ({ pattern, count }))
87
- .sort((a, b) => b.count - a.count)
88
- .slice(0, 10);
89
- }
90
- normalizeErrorPattern(error) {
91
- return error
92
- .replace(/TS\d+/, "TS****")
93
- .replace(/"[^"]*"/, '"..."')
94
- .replace(/'\S+'/, "'...'")
95
- .replace(/line \d+/, "line N")
96
- .replace(/column \d+/, "column N")
97
- .replace(/at index \d+/, "at index N")
98
- .trim();
99
- }
100
- getEvents(filter) {
101
- let filtered = [...this.events];
102
- if (filter?.type) {
103
- filtered = filtered.filter((e) => e.type === filter.type);
104
- }
105
- if (filter?.success !== undefined) {
106
- filtered = filtered.filter((e) => e.success === filter.success);
107
- }
108
- if (filter?.since) {
109
- const since = filter.since;
110
- filtered = filtered.filter((e) => e.timestamp >= since);
111
- }
112
- return filtered;
113
- }
114
- getFirstAttemptSuccessRate() {
115
- if (this.events.length === 0)
116
- return 0;
117
- const firstAttemptSuccess = this.events.filter((e) => e.success && e.attempts === 1).length;
118
- return Math.round((firstAttemptSuccess / this.events.length) * 100);
119
- }
120
- getSuccessRateByPromptVersion() {
121
- const byVersion = {};
122
- for (const event of this.events) {
123
- if (!byVersion[event.promptVersion]) {
124
- byVersion[event.promptVersion] = { total: 0, success: 0 };
125
- }
126
- byVersion[event.promptVersion].total++;
127
- if (event.success) {
128
- byVersion[event.promptVersion].success++;
129
- }
130
- }
131
- const result = {};
132
- for (const [version, stats] of Object.entries(byVersion)) {
133
- result[version] = {
134
- ...stats,
135
- rate: Math.round((stats.success / stats.total) * 100),
136
- };
137
- }
138
- return result;
139
- }
140
- toJSON() {
141
- return JSON.stringify({
142
- events: this.events,
143
- stats: this.getStats(),
144
- exportedAt: new Date().toISOString(),
145
- }, null, 2);
146
- }
147
- fromJSON(json) {
148
- const data = JSON.parse(json);
149
- if (data.events && Array.isArray(data.events)) {
150
- this.events.push(...data.events);
151
- }
152
- }
153
- clear() {
154
- this.events = [];
155
- }
156
- static resetInstance() {
157
- GenerationAnalytics.instance = null;
158
- }
159
- generateId() {
160
- return `gen_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
161
- }
162
- }
163
- export default GenerationAnalytics;
@@ -1,407 +0,0 @@
1
- import { beforeEach, describe, expect, it } from "vitest";
2
- import { GenerationAnalytics } from "./GenerationAnalytics.js";
3
- describe("GenerationAnalytics", () => {
4
- let analytics;
5
- beforeEach(() => {
6
- GenerationAnalytics.resetInstance();
7
- analytics = GenerationAnalytics.getInstance();
8
- analytics.clear();
9
- });
10
- describe("singleton pattern", () => {
11
- it("should return the same instance", () => {
12
- const a = GenerationAnalytics.getInstance();
13
- const b = GenerationAnalytics.getInstance();
14
- expect(a).toBe(b);
15
- });
16
- it("should create new instance after reset", () => {
17
- const a = GenerationAnalytics.getInstance();
18
- GenerationAnalytics.resetInstance();
19
- const b = GenerationAnalytics.getInstance();
20
- expect(a).not.toBe(b);
21
- });
22
- });
23
- describe("recordEvent", () => {
24
- it("should record a generation event and return it with id and timestamp", () => {
25
- const event = analytics.recordEvent({
26
- type: "node",
27
- subtype: "function",
28
- name: "fetch-user",
29
- success: true,
30
- attempts: 1,
31
- durationMs: 2500,
32
- errors: [],
33
- promptVersion: "create-fn-node@2.0.0",
34
- });
35
- expect(event.id).toBeTruthy();
36
- expect(event.id).toMatch(/^gen_/);
37
- expect(event.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/);
38
- expect(event.type).toBe("node");
39
- expect(event.success).toBe(true);
40
- });
41
- it("should track multiple events", () => {
42
- analytics.recordEvent({
43
- type: "node",
44
- subtype: "function",
45
- name: "node1",
46
- success: true,
47
- attempts: 1,
48
- durationMs: 1000,
49
- errors: [],
50
- promptVersion: "v1",
51
- });
52
- analytics.recordEvent({
53
- type: "workflow",
54
- subtype: "http",
55
- name: "wf1",
56
- success: false,
57
- attempts: 3,
58
- durationMs: 5000,
59
- errors: ["Error 1"],
60
- promptVersion: "v1",
61
- });
62
- const events = analytics.getEvents();
63
- expect(events.length).toBe(2);
64
- });
65
- });
66
- describe("getStats - empty", () => {
67
- it("should return zero stats when no events recorded", () => {
68
- const stats = analytics.getStats();
69
- expect(stats.totalGenerations).toBe(0);
70
- expect(stats.successCount).toBe(0);
71
- expect(stats.failureCount).toBe(0);
72
- expect(stats.successRate).toBe(0);
73
- expect(stats.averageAttempts).toBe(0);
74
- expect(stats.averageDurationMs).toBe(0);
75
- expect(stats.topErrors).toEqual([]);
76
- });
77
- });
78
- describe("getStats - with data", () => {
79
- beforeEach(() => {
80
- analytics.recordEvent({
81
- type: "node",
82
- subtype: "function",
83
- name: "n1",
84
- success: true,
85
- attempts: 1,
86
- durationMs: 1000,
87
- errors: [],
88
- promptVersion: "v1",
89
- });
90
- analytics.recordEvent({
91
- type: "node",
92
- subtype: "function",
93
- name: "n2",
94
- success: true,
95
- attempts: 2,
96
- durationMs: 3000,
97
- errors: ["Missing import"],
98
- promptVersion: "v1",
99
- });
100
- analytics.recordEvent({
101
- type: "workflow",
102
- subtype: "http",
103
- name: "w1",
104
- success: true,
105
- attempts: 1,
106
- durationMs: 2000,
107
- errors: [],
108
- promptVersion: "v2",
109
- });
110
- analytics.recordEvent({
111
- type: "trigger",
112
- subtype: "queue",
113
- name: "t1",
114
- success: false,
115
- attempts: 3,
116
- durationMs: 8000,
117
- errors: ["Missing TriggerBase", "Missing createContext"],
118
- promptVersion: "v1",
119
- });
120
- });
121
- it("should calculate correct totals", () => {
122
- const stats = analytics.getStats();
123
- expect(stats.totalGenerations).toBe(4);
124
- expect(stats.successCount).toBe(3);
125
- expect(stats.failureCount).toBe(1);
126
- });
127
- it("should calculate correct success rate", () => {
128
- const stats = analytics.getStats();
129
- expect(stats.successRate).toBe(75);
130
- });
131
- it("should calculate correct average attempts", () => {
132
- const stats = analytics.getStats();
133
- expect(stats.averageAttempts).toBe(1.8);
134
- });
135
- it("should calculate correct average duration", () => {
136
- const stats = analytics.getStats();
137
- expect(stats.averageDurationMs).toBe(3500);
138
- });
139
- it("should provide per-type breakdown", () => {
140
- const stats = analytics.getStats();
141
- expect(stats.byType.node.total).toBe(2);
142
- expect(stats.byType.node.success).toBe(2);
143
- expect(stats.byType.node.successRate).toBe(100);
144
- expect(stats.byType.workflow.total).toBe(1);
145
- expect(stats.byType.workflow.success).toBe(1);
146
- expect(stats.byType.workflow.successRate).toBe(100);
147
- expect(stats.byType.trigger.total).toBe(1);
148
- expect(stats.byType.trigger.success).toBe(0);
149
- expect(stats.byType.trigger.successRate).toBe(0);
150
- });
151
- it("should track top error patterns", () => {
152
- const stats = analytics.getStats();
153
- expect(stats.topErrors.length).toBeGreaterThan(0);
154
- for (let i = 1; i < stats.topErrors.length; i++) {
155
- expect(stats.topErrors[i].count).toBeLessThanOrEqual(stats.topErrors[i - 1].count);
156
- }
157
- });
158
- });
159
- describe("getEvents - filtering", () => {
160
- beforeEach(() => {
161
- analytics.recordEvent({
162
- type: "node",
163
- subtype: "function",
164
- name: "n1",
165
- success: true,
166
- attempts: 1,
167
- durationMs: 1000,
168
- errors: [],
169
- promptVersion: "v1",
170
- });
171
- analytics.recordEvent({
172
- type: "workflow",
173
- subtype: "http",
174
- name: "w1",
175
- success: false,
176
- attempts: 3,
177
- durationMs: 5000,
178
- errors: ["Error"],
179
- promptVersion: "v1",
180
- });
181
- analytics.recordEvent({
182
- type: "trigger",
183
- subtype: "queue",
184
- name: "t1",
185
- success: true,
186
- attempts: 2,
187
- durationMs: 3000,
188
- errors: ["Retried"],
189
- promptVersion: "v2",
190
- });
191
- });
192
- it("should filter by type", () => {
193
- const nodes = analytics.getEvents({ type: "node" });
194
- expect(nodes.length).toBe(1);
195
- expect(nodes[0].name).toBe("n1");
196
- });
197
- it("should filter by success", () => {
198
- const successes = analytics.getEvents({ success: true });
199
- expect(successes.length).toBe(2);
200
- const failures = analytics.getEvents({ success: false });
201
- expect(failures.length).toBe(1);
202
- });
203
- it("should filter by type and success combined", () => {
204
- const failedWorkflows = analytics.getEvents({ type: "workflow", success: false });
205
- expect(failedWorkflows.length).toBe(1);
206
- expect(failedWorkflows[0].name).toBe("w1");
207
- });
208
- it("should return all events when no filter", () => {
209
- const all = analytics.getEvents();
210
- expect(all.length).toBe(3);
211
- });
212
- });
213
- describe("getFirstAttemptSuccessRate", () => {
214
- it("should return 0 when no events", () => {
215
- expect(analytics.getFirstAttemptSuccessRate()).toBe(0);
216
- });
217
- it("should calculate first-attempt success rate correctly", () => {
218
- analytics.recordEvent({
219
- type: "node",
220
- subtype: "function",
221
- name: "n1",
222
- success: true,
223
- attempts: 1,
224
- durationMs: 1000,
225
- errors: [],
226
- promptVersion: "v1",
227
- });
228
- analytics.recordEvent({
229
- type: "node",
230
- subtype: "function",
231
- name: "n2",
232
- success: true,
233
- attempts: 2,
234
- durationMs: 3000,
235
- errors: [],
236
- promptVersion: "v1",
237
- });
238
- analytics.recordEvent({
239
- type: "node",
240
- subtype: "function",
241
- name: "n3",
242
- success: true,
243
- attempts: 1,
244
- durationMs: 1000,
245
- errors: [],
246
- promptVersion: "v1",
247
- });
248
- analytics.recordEvent({
249
- type: "node",
250
- subtype: "function",
251
- name: "n4",
252
- success: false,
253
- attempts: 3,
254
- durationMs: 8000,
255
- errors: ["Error"],
256
- promptVersion: "v1",
257
- });
258
- expect(analytics.getFirstAttemptSuccessRate()).toBe(50);
259
- });
260
- });
261
- describe("getSuccessRateByPromptVersion", () => {
262
- it("should break down success rate by prompt version", () => {
263
- analytics.recordEvent({
264
- type: "node",
265
- subtype: "function",
266
- name: "n1",
267
- success: true,
268
- attempts: 1,
269
- durationMs: 1000,
270
- errors: [],
271
- promptVersion: "create-fn-node@1.0.0",
272
- });
273
- analytics.recordEvent({
274
- type: "node",
275
- subtype: "function",
276
- name: "n2",
277
- success: false,
278
- attempts: 3,
279
- durationMs: 5000,
280
- errors: ["Error"],
281
- promptVersion: "create-fn-node@1.0.0",
282
- });
283
- analytics.recordEvent({
284
- type: "node",
285
- subtype: "function",
286
- name: "n3",
287
- success: true,
288
- attempts: 1,
289
- durationMs: 1000,
290
- errors: [],
291
- promptVersion: "create-fn-node@2.0.0",
292
- });
293
- analytics.recordEvent({
294
- type: "node",
295
- subtype: "function",
296
- name: "n4",
297
- success: true,
298
- attempts: 2,
299
- durationMs: 3000,
300
- errors: [],
301
- promptVersion: "create-fn-node@2.0.0",
302
- });
303
- const rates = analytics.getSuccessRateByPromptVersion();
304
- expect(rates["create-fn-node@1.0.0"]).toBeDefined();
305
- expect(rates["create-fn-node@1.0.0"].total).toBe(2);
306
- expect(rates["create-fn-node@1.0.0"].rate).toBe(50);
307
- expect(rates["create-fn-node@2.0.0"]).toBeDefined();
308
- expect(rates["create-fn-node@2.0.0"].total).toBe(2);
309
- expect(rates["create-fn-node@2.0.0"].rate).toBe(100);
310
- });
311
- });
312
- describe("serialization", () => {
313
- it("should serialize to JSON", () => {
314
- analytics.recordEvent({
315
- type: "node",
316
- subtype: "function",
317
- name: "n1",
318
- success: true,
319
- attempts: 1,
320
- durationMs: 1000,
321
- errors: [],
322
- promptVersion: "v1",
323
- });
324
- const json = analytics.toJSON();
325
- const parsed = JSON.parse(json);
326
- expect(parsed.events).toBeDefined();
327
- expect(parsed.events.length).toBe(1);
328
- expect(parsed.stats).toBeDefined();
329
- expect(parsed.exportedAt).toBeTruthy();
330
- });
331
- it("should import from JSON", () => {
332
- const exportData = {
333
- events: [
334
- {
335
- id: "gen_imported_1",
336
- timestamp: "2026-01-28T00:00:00Z",
337
- type: "node",
338
- subtype: "function",
339
- name: "imported-node",
340
- success: true,
341
- attempts: 1,
342
- durationMs: 1000,
343
- errors: [],
344
- promptVersion: "v1",
345
- },
346
- ],
347
- };
348
- analytics.fromJSON(JSON.stringify(exportData));
349
- const events = analytics.getEvents();
350
- expect(events.length).toBe(1);
351
- expect(events[0].name).toBe("imported-node");
352
- });
353
- });
354
- describe("startTimer", () => {
355
- it("should measure duration", async () => {
356
- const getElapsed = analytics.startTimer();
357
- await new Promise((resolve) => setTimeout(resolve, 50));
358
- const elapsed = getElapsed();
359
- expect(elapsed).toBeGreaterThanOrEqual(40);
360
- expect(elapsed).toBeLessThan(200);
361
- });
362
- });
363
- describe("error pattern normalization", () => {
364
- it("should normalize TypeScript error codes in top errors", () => {
365
- analytics.recordEvent({
366
- type: "node",
367
- subtype: "function",
368
- name: "n1",
369
- success: false,
370
- attempts: 3,
371
- durationMs: 5000,
372
- errors: ['TS2304: Cannot find name "defineNode"', "TS2307: Cannot find module '@blokjs/runner'"],
373
- promptVersion: "v1",
374
- });
375
- analytics.recordEvent({
376
- type: "node",
377
- subtype: "function",
378
- name: "n2",
379
- success: false,
380
- attempts: 3,
381
- durationMs: 5000,
382
- errors: ['TS2304: Cannot find name "z"'],
383
- promptVersion: "v1",
384
- });
385
- const stats = analytics.getStats();
386
- const ts2304 = stats.topErrors.filter((e) => e.pattern.includes("TS****"));
387
- expect(ts2304.length).toBeGreaterThan(0);
388
- });
389
- });
390
- describe("clear", () => {
391
- it("should clear all events", () => {
392
- analytics.recordEvent({
393
- type: "node",
394
- subtype: "function",
395
- name: "n1",
396
- success: true,
397
- attempts: 1,
398
- durationMs: 1000,
399
- errors: [],
400
- promptVersion: "v1",
401
- });
402
- analytics.clear();
403
- const events = analytics.getEvents();
404
- expect(events.length).toBe(0);
405
- });
406
- });
407
- });
@@ -1,25 +0,0 @@
1
- export interface PromptVersion {
2
- id: string;
3
- version: string;
4
- changelog: string;
5
- createdAt: string;
6
- contentHash: string;
7
- }
8
- export interface PromptRegistry {
9
- [promptId: string]: PromptVersion;
10
- }
11
- export declare const PROMPT_VERSIONS: PromptRegistry;
12
- export declare function computeContentHash(content: string): string;
13
- export declare function getPromptVersion(promptId: string): PromptVersion | undefined;
14
- export declare function getAllPromptVersions(): PromptVersion[];
15
- export declare function getVersionStamp(promptId: string): string;
16
- export declare function registerPromptContent(promptId: string, content: string): void;
17
- declare const _default: {
18
- PROMPT_VERSIONS: PromptRegistry;
19
- getPromptVersion: typeof getPromptVersion;
20
- getAllPromptVersions: typeof getAllPromptVersions;
21
- getVersionStamp: typeof getVersionStamp;
22
- computeContentHash: typeof computeContentHash;
23
- registerPromptContent: typeof registerPromptContent;
24
- };
25
- export default _default;