@blokjs/shared 0.2.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 (85) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/__tests__/unit/GlobalError.test.ts +93 -0
  3. package/__tests__/unit/GlobalLogger.test.ts +77 -0
  4. package/__tests__/unit/Metrics.test.ts +77 -0
  5. package/__tests__/unit/NodeBase.test.ts +286 -0
  6. package/__tests__/unit/Trigger.test.ts +23 -0
  7. package/__tests__/unit/utils/CpuUsage.test.ts +102 -0
  8. package/__tests__/unit/utils/Mapper.test.ts +124 -0
  9. package/__tests__/unit/utils/MemoryUsage.test.ts +121 -0
  10. package/__tests__/unit/utils/Time.test.ts +60 -0
  11. package/dist/GlobalError.d.ts +11 -0
  12. package/dist/GlobalError.js +29 -0
  13. package/dist/GlobalError.js.map +1 -0
  14. package/dist/GlobalLogger.d.ts +11 -0
  15. package/dist/GlobalLogger.js +16 -0
  16. package/dist/GlobalLogger.js.map +1 -0
  17. package/dist/Metrics.d.ts +18 -0
  18. package/dist/Metrics.js +40 -0
  19. package/dist/Metrics.js.map +1 -0
  20. package/dist/NodeBase.d.ts +26 -0
  21. package/dist/NodeBase.js +98 -0
  22. package/dist/NodeBase.js.map +1 -0
  23. package/dist/Trigger.d.ts +7 -0
  24. package/dist/Trigger.js +3 -0
  25. package/dist/Trigger.js.map +1 -0
  26. package/dist/index.d.ts +16 -0
  27. package/dist/index.js +8 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/types/ConfigContext.d.ts +5 -0
  30. package/dist/types/ConfigContext.js +2 -0
  31. package/dist/types/ConfigContext.js.map +1 -0
  32. package/dist/types/Context.d.ts +25 -0
  33. package/dist/types/Context.js +2 -0
  34. package/dist/types/Context.js.map +1 -0
  35. package/dist/types/EnvContext.d.ts +5 -0
  36. package/dist/types/EnvContext.js +2 -0
  37. package/dist/types/EnvContext.js.map +1 -0
  38. package/dist/types/ErrorContext.d.ts +9 -0
  39. package/dist/types/ErrorContext.js +2 -0
  40. package/dist/types/ErrorContext.js.map +1 -0
  41. package/dist/types/FunctionContext.d.ts +4 -0
  42. package/dist/types/FunctionContext.js +2 -0
  43. package/dist/types/FunctionContext.js.map +1 -0
  44. package/dist/types/LoggerContext.d.ts +9 -0
  45. package/dist/types/LoggerContext.js +2 -0
  46. package/dist/types/LoggerContext.js.map +1 -0
  47. package/dist/types/NodeConfigContext.d.ts +5 -0
  48. package/dist/types/NodeConfigContext.js +2 -0
  49. package/dist/types/NodeConfigContext.js.map +1 -0
  50. package/dist/types/ParamsDictionary.d.ts +3 -0
  51. package/dist/types/ParamsDictionary.js +2 -0
  52. package/dist/types/ParamsDictionary.js.map +1 -0
  53. package/dist/types/RequestContext.d.ts +5 -0
  54. package/dist/types/RequestContext.js +2 -0
  55. package/dist/types/RequestContext.js.map +1 -0
  56. package/dist/types/ResponseContext.d.ts +8 -0
  57. package/dist/types/ResponseContext.js +2 -0
  58. package/dist/types/ResponseContext.js.map +1 -0
  59. package/dist/types/Step.d.ts +10 -0
  60. package/dist/types/Step.js +2 -0
  61. package/dist/types/Step.js.map +1 -0
  62. package/dist/types/VarsContext.d.ts +5 -0
  63. package/dist/types/VarsContext.js +2 -0
  64. package/dist/types/VarsContext.js.map +1 -0
  65. package/dist/utils/CpuUsage.d.ts +19 -0
  66. package/dist/utils/CpuUsage.js +57 -0
  67. package/dist/utils/CpuUsage.js.map +1 -0
  68. package/dist/utils/Mapper.d.ts +10 -0
  69. package/dist/utils/Mapper.js +56 -0
  70. package/dist/utils/Mapper.js.map +1 -0
  71. package/dist/utils/MemoryUsage.d.ts +17 -0
  72. package/dist/utils/MemoryUsage.js +36 -0
  73. package/dist/utils/MemoryUsage.js.map +1 -0
  74. package/dist/utils/MetricsBase.d.ts +24 -0
  75. package/dist/utils/MetricsBase.js +3 -0
  76. package/dist/utils/MetricsBase.js.map +1 -0
  77. package/dist/utils/Time.d.ts +10 -0
  78. package/dist/utils/Time.js +24 -0
  79. package/dist/utils/Time.js.map +1 -0
  80. package/dist/utils/index.d.ts +5 -0
  81. package/dist/utils/index.js +6 -0
  82. package/dist/utils/index.js.map +1 -0
  83. package/package.json +34 -0
  84. package/tsconfig.json +19 -0
  85. package/vitest.config.ts +29 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,69 @@
1
+ # @blokjs/shared
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Initial public release of Blok packages.
8
+
9
+ This release includes:
10
+
11
+ - Core packages: @blokjs/shared, @blokjs/helper, @blokjs/runner
12
+ - Node packages: @blokjs/api-call, @blokjs/if-else, @blokjs/react
13
+ - Trigger packages: pubsub, queue, webhook, websocket, worker, cron, grpc
14
+ - CLI tool: blokctl
15
+ - Editor support: @blokjs/lsp-server, @blokjs/syntax
16
+
17
+ ## 0.0.9
18
+
19
+ ### Patch Changes
20
+
21
+ - Python3 runtime implemented in the runner
22
+
23
+ ## 0.0.8
24
+
25
+ ### Patch Changes
26
+
27
+ - Added examples and create project' command to include examples and 'create node' command with options for type ('module' or 'class') and template ('class' or 'ui')
28
+
29
+ ## 0.0.7
30
+
31
+ ### Patch Changes
32
+
33
+ - Added support for YAML, XML and TOML in the workflow file. Upgraded package version recommended by Dependabot.
34
+
35
+ ## 0.0.6
36
+
37
+ ### Patch Changes
38
+
39
+ - Updated the quickstart mdx and fixed api-call error issue with rest
40
+
41
+ ## 0.0.5
42
+
43
+ ### Patch Changes
44
+
45
+ - Implemented a react node and the chatbot demo page
46
+
47
+ ## 0.0.4
48
+
49
+ ### Patch Changes
50
+
51
+ - Improved Loki metrics
52
+
53
+ ## 0.0.3
54
+
55
+ ### Patch Changes
56
+
57
+ - Improved and extended the open telemetry feature
58
+
59
+ ## 0.0.2
60
+
61
+ ### Patch Changes
62
+
63
+ - Fixed open telemetry issues and types
64
+
65
+ ## 0.0.1
66
+
67
+ ### Patch Changes
68
+
69
+ - Fixed issue with the cli node creation test
@@ -0,0 +1,93 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import GlobalError from "../../src/GlobalError";
3
+
4
+ describe("GlobalError", () => {
5
+ describe("constructor", () => {
6
+ it("should create error with message string", () => {
7
+ const error = new GlobalError("test error");
8
+ expect(error.message).toBe("test error");
9
+ expect(error.context.message).toBe("test error");
10
+ });
11
+
12
+ it("should extend Error", () => {
13
+ const error = new GlobalError("test");
14
+ expect(error).toBeInstanceOf(Error);
15
+ });
16
+
17
+ it("should set prototype correctly for instanceof", () => {
18
+ const error = new GlobalError("test");
19
+ expect(error).toBeInstanceOf(GlobalError);
20
+ });
21
+
22
+ it("should handle undefined message", () => {
23
+ const error = new GlobalError(undefined);
24
+ expect(error.context.message).toBeUndefined();
25
+ });
26
+ });
27
+
28
+ describe("setCode", () => {
29
+ it("should set code on context", () => {
30
+ const error = new GlobalError("test");
31
+ error.setCode(404);
32
+ expect(error.context.code).toBe(404);
33
+ });
34
+
35
+ it("should handle undefined code", () => {
36
+ const error = new GlobalError("test");
37
+ error.setCode(undefined);
38
+ expect(error.context.code).toBeUndefined();
39
+ });
40
+ });
41
+
42
+ describe("setJson", () => {
43
+ it("should set json on context", () => {
44
+ const json = { key: "value" };
45
+ const error = new GlobalError("test");
46
+ error.setJson(json);
47
+ expect(error.context.json).toEqual(json);
48
+ });
49
+ });
50
+
51
+ describe("setStack", () => {
52
+ it("should set stack on context", () => {
53
+ const error = new GlobalError("test");
54
+ error.setStack("Error\n at test.ts:1");
55
+ expect(error.context.stack).toBe("Error\n at test.ts:1");
56
+ });
57
+ });
58
+
59
+ describe("setName", () => {
60
+ it("should set name on context", () => {
61
+ const error = new GlobalError("test");
62
+ error.setName("my-node");
63
+ expect(error.context.name).toBe("my-node");
64
+ });
65
+ });
66
+
67
+ describe("hasJson", () => {
68
+ it("should return false when no json set", () => {
69
+ const error = new GlobalError("test");
70
+ expect(error.hasJson()).toBe(false);
71
+ });
72
+
73
+ it("should return true when json is set", () => {
74
+ const error = new GlobalError("test");
75
+ error.setJson({ key: "value" });
76
+ expect(error.hasJson()).toBe(true);
77
+ });
78
+ });
79
+
80
+ describe("toString", () => {
81
+ it("should return JSON string when json is set", () => {
82
+ const error = new GlobalError("test");
83
+ const json = { key: "value" };
84
+ error.setJson(json);
85
+ expect(error.toString()).toBe(JSON.stringify(json));
86
+ });
87
+
88
+ it("should return message string when no json", () => {
89
+ const error = new GlobalError("test error");
90
+ expect(error.toString()).toBe("test error");
91
+ });
92
+ });
93
+ });
@@ -0,0 +1,77 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import GlobalLogger from "../../src/GlobalLogger";
3
+
4
+ class TestLogger extends GlobalLogger {
5
+ log(message: string): void {
6
+ this.logs.push(message);
7
+ }
8
+
9
+ logLevel(level: string, message: string): void {
10
+ this.logs.push(`[${level}] ${message}`);
11
+ }
12
+
13
+ error(message: string, stack: string): void {
14
+ this.logs.push(`ERROR: ${message} - ${stack}`);
15
+ }
16
+ }
17
+
18
+ describe("GlobalLogger", () => {
19
+ it("should initialize with empty logs array", () => {
20
+ const logger = new TestLogger();
21
+ expect(logger.getLogs()).toEqual([]);
22
+ });
23
+
24
+ describe("getLogs", () => {
25
+ it("should return logs after logging", () => {
26
+ const logger = new TestLogger();
27
+ logger.log("msg1");
28
+ logger.log("msg2");
29
+ expect(logger.getLogs()).toEqual(["msg1", "msg2"]);
30
+ });
31
+ });
32
+
33
+ describe("getLogsAsText", () => {
34
+ it("should join logs with newline", () => {
35
+ const logger = new TestLogger();
36
+ logger.log("line1");
37
+ logger.log("line2");
38
+ expect(logger.getLogsAsText()).toBe("line1\nline2");
39
+ });
40
+
41
+ it("should return empty string for no logs", () => {
42
+ const logger = new TestLogger();
43
+ expect(logger.getLogsAsText()).toBe("");
44
+ });
45
+ });
46
+
47
+ describe("getLogsAsBase64", () => {
48
+ it("should return base64 encoded logs", () => {
49
+ const logger = new TestLogger();
50
+ logger.log("hello");
51
+ const expected = Buffer.from("hello").toString("base64");
52
+ expect(logger.getLogsAsBase64()).toBe(expected);
53
+ });
54
+
55
+ it("should return base64 of empty string for no logs", () => {
56
+ const logger = new TestLogger();
57
+ const expected = Buffer.from("").toString("base64");
58
+ expect(logger.getLogsAsBase64()).toBe(expected);
59
+ });
60
+ });
61
+
62
+ describe("logLevel", () => {
63
+ it("should format with level prefix", () => {
64
+ const logger = new TestLogger();
65
+ logger.logLevel("WARN", "something happened");
66
+ expect(logger.getLogs()).toEqual(["[WARN] something happened"]);
67
+ });
68
+ });
69
+
70
+ describe("error", () => {
71
+ it("should format error with stack", () => {
72
+ const logger = new TestLogger();
73
+ logger.error("fail", "stack trace");
74
+ expect(logger.getLogs()).toEqual(["ERROR: fail - stack trace"]);
75
+ });
76
+ });
77
+ });
@@ -0,0 +1,77 @@
1
+ import { beforeEach, describe, expect, it } from "vitest";
2
+ import { Metrics } from "../../src/Metrics";
3
+
4
+ describe("Metrics", () => {
5
+ let metrics: Metrics;
6
+
7
+ beforeEach(() => {
8
+ metrics = new Metrics();
9
+ });
10
+
11
+ it("should construct without errors", () => {
12
+ expect(metrics).toBeDefined();
13
+ });
14
+
15
+ describe("start()", () => {
16
+ it("should call start on all sub-metrics without error", () => {
17
+ expect(() => metrics.start()).not.toThrow();
18
+ });
19
+ });
20
+
21
+ describe("stop()", () => {
22
+ it("should call stop without error after start", () => {
23
+ metrics.start();
24
+ expect(() => metrics.stop()).not.toThrow();
25
+ });
26
+ });
27
+
28
+ describe("retry()", () => {
29
+ it("should call start on memoryUsage only", () => {
30
+ metrics.start();
31
+ expect(() => metrics.retry()).not.toThrow();
32
+ });
33
+ });
34
+
35
+ describe("clear()", () => {
36
+ it("should clear memory usage values", () => {
37
+ metrics.start();
38
+ metrics.clear();
39
+ // After clear, memory metrics should be zeroed
40
+ const result = metrics.getMetrics();
41
+ expect(result.memory.min).toBe(0);
42
+ expect(result.memory.max).toBe(0);
43
+ });
44
+ });
45
+
46
+ describe("getMetrics()", () => {
47
+ it("should return object with cpu, memory, time", () => {
48
+ metrics.start();
49
+ metrics.stop();
50
+
51
+ const result = metrics.getMetrics();
52
+ expect(result).toHaveProperty("cpu");
53
+ expect(result).toHaveProperty("memory");
54
+ expect(result).toHaveProperty("time");
55
+ });
56
+
57
+ it("should return valid metric shapes after start/stop cycle", () => {
58
+ metrics.start();
59
+ metrics.stop();
60
+
61
+ const result = metrics.getMetrics();
62
+ // CPU
63
+ expect(result.cpu).toHaveProperty("total");
64
+ expect(result.cpu).toHaveProperty("average");
65
+ expect(result.cpu).toHaveProperty("usage");
66
+ expect(result.cpu).toHaveProperty("model");
67
+ // Memory
68
+ expect(result.memory).toHaveProperty("total");
69
+ expect(result.memory).toHaveProperty("min");
70
+ expect(result.memory).toHaveProperty("max");
71
+ // Time
72
+ expect(result.time).toHaveProperty("startTime");
73
+ expect(result.time).toHaveProperty("endTime");
74
+ expect(result.time).toHaveProperty("duration");
75
+ });
76
+ });
77
+ });
@@ -0,0 +1,286 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import GlobalError from "../../src/GlobalError";
3
+ import NodeBase from "../../src/NodeBase";
4
+ import type Context from "../../src/types/Context";
5
+ import type ResponseContext from "../../src/types/ResponseContext";
6
+
7
+ class TestNode extends NodeBase {
8
+ public mockResponse: ResponseContext = { data: { result: "ok" }, error: null, success: true };
9
+ public runCalls: Context[] = [];
10
+
11
+ async run(ctx: Context): Promise<ResponseContext> {
12
+ this.runCalls.push(ctx);
13
+ return this.mockResponse;
14
+ }
15
+ }
16
+
17
+ function createTestContext(overrides: Partial<Context> = {}): Context {
18
+ return {
19
+ id: "test-ctx",
20
+ request: { body: {}, headers: {}, query: {}, params: {} },
21
+ response: { data: null, error: null, success: true },
22
+ error: { message: "" },
23
+ logger: { log: vi.fn(), logLevel: vi.fn(), error: vi.fn() },
24
+ config: {},
25
+ func: {},
26
+ vars: {},
27
+ eventLogger: null,
28
+ _PRIVATE_: null,
29
+ ...overrides,
30
+ } as Context;
31
+ }
32
+
33
+ describe("NodeBase", () => {
34
+ let node: TestNode;
35
+
36
+ beforeEach(() => {
37
+ node = new TestNode();
38
+ node.name = "test-node";
39
+ vi.restoreAllMocks();
40
+ });
41
+
42
+ describe("default properties", () => {
43
+ it("should have correct defaults", () => {
44
+ const n = new TestNode();
45
+ expect(n.flow).toBe(false);
46
+ expect(n.name).toBe("");
47
+ expect(n.active).toBe(true);
48
+ expect(n.stop).toBe(false);
49
+ expect(n.set_var).toBe(false);
50
+ expect(n.contentType).toBe("");
51
+ });
52
+ });
53
+
54
+ describe("process()", () => {
55
+ it("should call run() and return response", async () => {
56
+ const ctx = createTestContext({
57
+ config: { "test-node": { param: "value" } },
58
+ });
59
+
60
+ const response = await node.process(ctx);
61
+ expect(response).toEqual(node.mockResponse);
62
+ expect(node.runCalls).toHaveLength(1);
63
+ });
64
+
65
+ it("should clone config for originalConfig", async () => {
66
+ const configData = { key: "val" };
67
+ const ctx = createTestContext({
68
+ config: { "test-node": configData },
69
+ });
70
+
71
+ await node.process(ctx);
72
+ expect(node.originalConfig).toEqual(configData);
73
+ // Should be a deep clone, not same reference
74
+ expect(node.originalConfig).not.toBe(configData);
75
+ });
76
+
77
+ it("should set ctx.response on success", async () => {
78
+ const ctx = createTestContext({
79
+ config: { "test-node": {} },
80
+ });
81
+
82
+ await node.process(ctx);
83
+ expect(ctx.response).toEqual(node.mockResponse);
84
+ });
85
+
86
+ it("should throw when response has error", async () => {
87
+ const error = new GlobalError("process error");
88
+ node.mockResponse = { data: null, error, success: false };
89
+
90
+ const ctx = createTestContext({
91
+ config: { "test-node": {} },
92
+ });
93
+
94
+ await expect(node.process(ctx)).rejects.toBe(error);
95
+ });
96
+ });
97
+
98
+ describe("processFlow()", () => {
99
+ it("should call run() and return response", async () => {
100
+ const ctx = createTestContext({
101
+ config: { "test-node": {} },
102
+ });
103
+
104
+ const response = await node.processFlow(ctx);
105
+ expect(response).toEqual(node.mockResponse);
106
+ });
107
+
108
+ it("should catch errors and wrap in setError", async () => {
109
+ const testNode = new TestNode();
110
+ testNode.name = "error-node";
111
+ testNode.run = vi.fn().mockRejectedValue({ message: "oops" });
112
+
113
+ const ctx = createTestContext({
114
+ config: { "error-node": {} },
115
+ });
116
+
117
+ const response = await testNode.processFlow(ctx);
118
+ expect(response.success).toBe(false);
119
+ expect(response.error).toBeInstanceOf(GlobalError);
120
+ });
121
+
122
+ it("should set ctx.response on error", async () => {
123
+ const testNode = new TestNode();
124
+ testNode.name = "error-node";
125
+ testNode.run = vi.fn().mockRejectedValue({ message: "fail" });
126
+
127
+ const ctx = createTestContext({
128
+ config: { "error-node": {} },
129
+ });
130
+
131
+ await testNode.processFlow(ctx);
132
+ expect(ctx.response.success).toBe(false);
133
+ expect(ctx.response.error).toBeInstanceOf(GlobalError);
134
+ });
135
+ });
136
+
137
+ describe("runSteps()", () => {
138
+ it('should throw "not implemented" error', () => {
139
+ const ctx = createTestContext();
140
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
141
+
142
+ expect(() => node.runSteps([], ctx)).toThrow("runSteps method is not implemented.");
143
+ consoleSpy.mockRestore();
144
+ });
145
+ });
146
+
147
+ describe("runJs()", () => {
148
+ it("should evaluate simple expressions", () => {
149
+ const ctx = createTestContext();
150
+ const result = node.runJs("1 + 2", ctx);
151
+ expect(result).toBe(3);
152
+ });
153
+
154
+ it("should access ctx parameter", () => {
155
+ const ctx = createTestContext({ id: "my-id" });
156
+ const result = node.runJs("ctx.id", ctx);
157
+ expect(result).toBe("my-id");
158
+ });
159
+
160
+ it("should access data parameter", () => {
161
+ const ctx = createTestContext();
162
+ const result = node.runJs("data.x", ctx, { x: 42 });
163
+ expect(result).toBe(42);
164
+ });
165
+
166
+ it("should access vars parameter", () => {
167
+ const ctx = createTestContext();
168
+ const result = node.runJs("vars.count", ctx, {}, {}, { count: 10 });
169
+ expect(result).toBe(10);
170
+ });
171
+
172
+ it("should handle string concatenation", () => {
173
+ const ctx = createTestContext();
174
+ const result = node.runJs('"hello" + " " + "world"', ctx);
175
+ expect(result).toBe("hello world");
176
+ });
177
+ });
178
+
179
+ describe("setVar()", () => {
180
+ it("should initialize ctx.vars if undefined", () => {
181
+ const ctx = createTestContext();
182
+ ctx.vars = undefined;
183
+ node.setVar(ctx, { key: "value" });
184
+ expect(ctx.vars).toEqual({ key: "value" });
185
+ });
186
+
187
+ it("should merge vars into ctx.vars", () => {
188
+ const ctx = createTestContext({ vars: { existing: "keep" } });
189
+ node.setVar(ctx, { newKey: "newVal" });
190
+ expect(ctx.vars).toEqual({ existing: "keep", newKey: "newVal" });
191
+ });
192
+ });
193
+
194
+ describe("getVar()", () => {
195
+ it("should return value by name", () => {
196
+ const ctx = createTestContext({ vars: { myVar: "found" } });
197
+ expect(node.getVar(ctx, "myVar")).toBe("found");
198
+ });
199
+
200
+ it("should return undefined for missing var", () => {
201
+ const ctx = createTestContext({ vars: { a: 1 } });
202
+ expect(node.getVar(ctx, "nonexistent")).toBeUndefined();
203
+ });
204
+
205
+ it("should handle undefined ctx.vars", () => {
206
+ const ctx = createTestContext();
207
+ ctx.vars = undefined;
208
+ expect(node.getVar(ctx, "any")).toBeUndefined();
209
+ });
210
+ });
211
+
212
+ describe("blueprintMapper()", () => {
213
+ it("should handle string input", () => {
214
+ const ctx = createTestContext();
215
+ const result = node.blueprintMapper("plain text", ctx);
216
+ expect(result).toBe("plain text");
217
+ });
218
+
219
+ it("should handle object input without error", () => {
220
+ const ctx = createTestContext();
221
+ const obj = { key: "value" };
222
+ const result = node.blueprintMapper(obj, ctx);
223
+ expect(result).toEqual(obj);
224
+ });
225
+
226
+ it("should catch and log mapper errors", () => {
227
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
228
+ const ctx = createTestContext();
229
+ // null will cause mapper to fail
230
+ const result = node.blueprintMapper(null as unknown as Record<string, unknown>, ctx);
231
+ // Should not throw
232
+ expect(result).toBeNull();
233
+ consoleSpy.mockRestore();
234
+ });
235
+ });
236
+
237
+ describe("setError()", () => {
238
+ it("should create GlobalError from string", () => {
239
+ const error = node.setError("simple error" as unknown as any);
240
+ expect(error).toBeInstanceOf(GlobalError);
241
+ expect(error.message).toBe("simple error");
242
+ });
243
+
244
+ it("should create GlobalError from {message} only", () => {
245
+ const error = node.setError({ message: "just a message" });
246
+ expect(error).toBeInstanceOf(GlobalError);
247
+ expect(error.message).toBe("just a message");
248
+ });
249
+
250
+ it("should create GlobalError from object with multiple keys", () => {
251
+ const config = { message: "error", detail: "extra info" };
252
+ const error = node.setError(config as any);
253
+ expect(error).toBeInstanceOf(GlobalError);
254
+ expect(error.hasJson()).toBe(true);
255
+ });
256
+
257
+ it("should set json when config has json field", () => {
258
+ const config = { message: "err", json: { detail: "info" } };
259
+ const error = node.setError(config as any);
260
+ expect(error.hasJson()).toBe(true);
261
+ });
262
+
263
+ it("should set stack when config has stack field", () => {
264
+ const config = { message: "err", stack: "Error\n at line 1" };
265
+ const error = node.setError(config);
266
+ expect(error.context.stack).toBe("Error\n at line 1");
267
+ });
268
+
269
+ it("should set numeric code from config", () => {
270
+ const config = { message: "err", code: 404 };
271
+ const error = node.setError(config);
272
+ expect(error.context.code).toBe(404);
273
+ });
274
+
275
+ it("should default to 500 for non-numeric code", () => {
276
+ const config = { message: "err", code: "bad" as unknown as number };
277
+ const error = node.setError(config);
278
+ expect(error.context.code).toBe(500);
279
+ });
280
+
281
+ it("should set name from this.name", () => {
282
+ const error = node.setError({ message: "err" });
283
+ expect(error.context.name).toBe("test-node");
284
+ });
285
+ });
286
+ });
@@ -0,0 +1,23 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import Trigger from "../../src/Trigger";
3
+
4
+ class TestTrigger extends Trigger {
5
+ public listenCalled = false;
6
+
7
+ listen(): void {
8
+ this.listenCalled = true;
9
+ }
10
+ }
11
+
12
+ describe("Trigger", () => {
13
+ it("should allow concrete subclass to implement listen()", () => {
14
+ const trigger = new TestTrigger();
15
+ trigger.listen();
16
+ expect(trigger.listenCalled).toBe(true);
17
+ });
18
+
19
+ it("should be an instance of Trigger", () => {
20
+ const trigger = new TestTrigger();
21
+ expect(trigger).toBeInstanceOf(Trigger);
22
+ });
23
+ });