@copilotkit/runtime 1.8.12-next.3 → 1.8.12-next.5

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 (47) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/{chunk-LZBWDTON.mjs → chunk-BILRAIYB.mjs} +2 -2
  3. package/dist/{chunk-CF5VXJC6.mjs → chunk-JUBPE6KE.mjs} +2 -2
  4. package/dist/{chunk-Y4H3U52G.mjs → chunk-MVKCCH5U.mjs} +216 -173
  5. package/dist/chunk-MVKCCH5U.mjs.map +1 -0
  6. package/dist/{chunk-2L6VOEJ4.mjs → chunk-RVYUQL6I.mjs} +4 -3
  7. package/dist/chunk-RVYUQL6I.mjs.map +1 -0
  8. package/dist/{chunk-KVRZ3PWO.mjs → chunk-Y3WEJHDT.mjs} +2 -2
  9. package/dist/index.js +217 -173
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +5 -5
  12. package/dist/lib/index.js +109 -82
  13. package/dist/lib/index.js.map +1 -1
  14. package/dist/lib/index.mjs +5 -5
  15. package/dist/lib/integrations/index.js +2 -1
  16. package/dist/lib/integrations/index.js.map +1 -1
  17. package/dist/lib/integrations/index.mjs +5 -5
  18. package/dist/lib/integrations/nest/index.js +2 -1
  19. package/dist/lib/integrations/nest/index.js.map +1 -1
  20. package/dist/lib/integrations/nest/index.mjs +3 -3
  21. package/dist/lib/integrations/node-express/index.js +2 -1
  22. package/dist/lib/integrations/node-express/index.js.map +1 -1
  23. package/dist/lib/integrations/node-express/index.mjs +3 -3
  24. package/dist/lib/integrations/node-http/index.js +2 -1
  25. package/dist/lib/integrations/node-http/index.js.map +1 -1
  26. package/dist/lib/integrations/node-http/index.mjs +2 -2
  27. package/dist/service-adapters/index.js +215 -172
  28. package/dist/service-adapters/index.js.map +1 -1
  29. package/dist/service-adapters/index.mjs +1 -1
  30. package/jest.config.js +8 -3
  31. package/package.json +3 -2
  32. package/src/service-adapters/anthropic/anthropic-adapter.ts +124 -66
  33. package/src/service-adapters/anthropic/utils.ts +0 -19
  34. package/src/service-adapters/openai/openai-adapter.ts +107 -69
  35. package/tests/global.d.ts +13 -0
  36. package/tests/service-adapters/anthropic/allowlist-approach.test.ts +226 -0
  37. package/tests/service-adapters/anthropic/anthropic-adapter.test.ts +604 -0
  38. package/tests/service-adapters/openai/allowlist-approach.test.ts +238 -0
  39. package/tests/service-adapters/openai/openai-adapter.test.ts +301 -0
  40. package/tests/setup.jest.ts +21 -0
  41. package/tests/tsconfig.json +10 -0
  42. package/tsconfig.json +1 -1
  43. package/dist/chunk-2L6VOEJ4.mjs.map +0 -1
  44. package/dist/chunk-Y4H3U52G.mjs.map +0 -1
  45. /package/dist/{chunk-LZBWDTON.mjs.map → chunk-BILRAIYB.mjs.map} +0 -0
  46. /package/dist/{chunk-CF5VXJC6.mjs.map → chunk-JUBPE6KE.mjs.map} +0 -0
  47. /package/dist/{chunk-KVRZ3PWO.mjs.map → chunk-Y3WEJHDT.mjs.map} +0 -0
@@ -0,0 +1,238 @@
1
+ /**
2
+ * @jest-environment node
3
+ */
4
+
5
+ import { describe, it, expect } from "@jest/globals";
6
+
7
+ describe("OpenAI Adapter - Allowlist Approach", () => {
8
+ it("should filter out tool_result messages with no corresponding tool_call ID", () => {
9
+ // Setup test data
10
+ const validToolCallIds = new Set<string>(["valid-id-1", "valid-id-2"]);
11
+
12
+ // Messages to filter - valid and invalid ones
13
+ const messages = [
14
+ { type: "text", role: "user", content: "Hello" },
15
+ { type: "tool_result", actionExecutionId: "valid-id-1", result: "result1" },
16
+ { type: "tool_result", actionExecutionId: "invalid-id", result: "invalid" },
17
+ { type: "tool_result", actionExecutionId: "valid-id-2", result: "result2" },
18
+ { type: "tool_result", actionExecutionId: "valid-id-1", result: "duplicate" }, // Duplicate ID
19
+ ];
20
+
21
+ // Implement the filtering logic, similar to the adapter
22
+ const filteredMessages = messages.filter((message) => {
23
+ if (message.type === "tool_result") {
24
+ // Skip if there's no corresponding tool_call
25
+ if (!validToolCallIds.has(message.actionExecutionId)) {
26
+ return false;
27
+ }
28
+
29
+ // Remove this ID from valid IDs so we don't process duplicates
30
+ validToolCallIds.delete(message.actionExecutionId);
31
+ }
32
+
33
+ // Keep all non-tool-result messages
34
+ return true;
35
+ });
36
+
37
+ // Verify results
38
+ expect(filteredMessages.length).toBe(3); // text + 2 valid tool results (no duplicates or invalid)
39
+
40
+ // Valid results should be included
41
+ expect(
42
+ filteredMessages.some(
43
+ (m) => m.type === "tool_result" && m.actionExecutionId === "valid-id-1",
44
+ ),
45
+ ).toBe(true);
46
+
47
+ expect(
48
+ filteredMessages.some(
49
+ (m) => m.type === "tool_result" && m.actionExecutionId === "valid-id-2",
50
+ ),
51
+ ).toBe(true);
52
+
53
+ // Invalid result should be excluded
54
+ expect(
55
+ filteredMessages.some(
56
+ (m) => m.type === "tool_result" && m.actionExecutionId === "invalid-id",
57
+ ),
58
+ ).toBe(false);
59
+
60
+ // Duplicate should be excluded - we used a different approach than Anthropic
61
+ const validId1Count = filteredMessages.filter(
62
+ (m) => m.type === "tool_result" && m.actionExecutionId === "valid-id-1",
63
+ ).length;
64
+
65
+ expect(validId1Count).toBe(1);
66
+ });
67
+
68
+ it("should maintain correct order of messages when filtering", () => {
69
+ // Setup test data
70
+ const validToolCallIds = new Set<string>(["tool-1", "tool-2", "tool-3"]);
71
+
72
+ // Test with a complex conversation pattern
73
+ const messages = [
74
+ { type: "text", role: "user", content: "Initial message" },
75
+ { type: "text", role: "assistant", content: "I'll help with that" },
76
+ { type: "tool_call", id: "tool-1", name: "firstTool" },
77
+ { type: "tool_result", actionExecutionId: "tool-1", result: "result1" },
78
+ { type: "text", role: "assistant", content: "Got the first result" },
79
+ { type: "tool_call", id: "tool-2", name: "secondTool" },
80
+ { type: "tool_result", actionExecutionId: "tool-2", result: "result2" },
81
+ { type: "tool_result", actionExecutionId: "invalid-id", result: "invalid-result" },
82
+ { type: "tool_call", id: "tool-3", name: "thirdTool" },
83
+ { type: "tool_result", actionExecutionId: "tool-1", result: "duplicate-result" }, // Duplicate
84
+ { type: "tool_result", actionExecutionId: "tool-3", result: "result3" },
85
+ { type: "text", role: "user", content: "Final message" },
86
+ ];
87
+
88
+ // Apply OpenAI's filter approach (using filter instead of loop)
89
+ const filteredMessages = messages.filter((message) => {
90
+ if (message.type === "tool_result") {
91
+ // Skip if there's no corresponding tool_call
92
+ if (!validToolCallIds.has(message.actionExecutionId)) {
93
+ return false;
94
+ }
95
+
96
+ // Remove this ID from valid IDs so we don't process duplicates
97
+ validToolCallIds.delete(message.actionExecutionId);
98
+ }
99
+
100
+ // Keep all non-tool-result messages
101
+ return true;
102
+ });
103
+
104
+ // Verify results
105
+ expect(filteredMessages.length).toBe(10); // 12 original - 2 filtered out
106
+
107
+ // Check that the message order is preserved
108
+ expect(filteredMessages[0].type).toBe("text"); // Initial user message
109
+ expect(filteredMessages[0].content).toBe("Initial message");
110
+ expect(filteredMessages[1].type).toBe("text"); // Assistant response
111
+ expect(filteredMessages[2].type).toBe("tool_call"); // First tool
112
+ expect(filteredMessages[3].type).toBe("tool_result"); // First result
113
+ expect(filteredMessages[3].actionExecutionId).toBe("tool-1");
114
+ expect(filteredMessages[4].type).toBe("text"); // Assistant comment
115
+ expect(filteredMessages[5].type).toBe("tool_call"); // Second tool
116
+ expect(filteredMessages[6].type).toBe("tool_result"); // Second result
117
+ expect(filteredMessages[6].actionExecutionId).toBe("tool-2");
118
+ expect(filteredMessages[7].type).toBe("tool_call"); // Third tool
119
+ expect(filteredMessages[8].type).toBe("tool_result"); // Third result
120
+ expect(filteredMessages[8].actionExecutionId).toBe("tool-3");
121
+ expect(filteredMessages[9].type).toBe("text"); // Final user message
122
+
123
+ // Each valid tool result should appear exactly once
124
+ const toolResultCounts = new Map();
125
+ filteredMessages.forEach((message) => {
126
+ if (message.type === "tool_result") {
127
+ const id = message.actionExecutionId;
128
+ toolResultCounts.set(id, (toolResultCounts.get(id) || 0) + 1);
129
+ }
130
+ });
131
+
132
+ expect(toolResultCounts.size).toBe(3); // Should have 3 different tool results
133
+ expect(toolResultCounts.get("tool-1")).toBe(1);
134
+ expect(toolResultCounts.get("tool-2")).toBe(1);
135
+ expect(toolResultCounts.get("tool-3")).toBe(1);
136
+ expect(toolResultCounts.has("invalid-id")).toBe(false);
137
+ });
138
+
139
+ it("should handle empty message array", () => {
140
+ const validToolCallIds = new Set<string>(["valid-id-1", "valid-id-2"]);
141
+ const messages = [];
142
+
143
+ // Apply OpenAI's filter approach
144
+ const filteredMessages = messages.filter((message) => {
145
+ if (message.type === "tool_result") {
146
+ if (!validToolCallIds.has(message.actionExecutionId)) {
147
+ return false;
148
+ }
149
+ validToolCallIds.delete(message.actionExecutionId);
150
+ }
151
+ return true;
152
+ });
153
+
154
+ expect(filteredMessages.length).toBe(0);
155
+ });
156
+
157
+ it("should handle edge cases with mixed message types", () => {
158
+ // Setup test data with various message types
159
+ const validToolCallIds = new Set<string>(["valid-id-1"]);
160
+
161
+ const messages = [
162
+ { type: "text", role: "user", content: "Hello" },
163
+ { type: "image", url: "https://example.com/image.jpg" }, // Non-tool message type
164
+ { type: "tool_result", actionExecutionId: "valid-id-1", result: "result1" },
165
+ { type: "custom", data: { key: "value" } }, // Another custom type
166
+ { type: "tool_result", actionExecutionId: "valid-id-1", result: "duplicate" }, // Duplicate
167
+ { type: "null", value: null }, // Edge case
168
+ { type: "undefined" }, // Edge case
169
+ ];
170
+
171
+ // Apply OpenAI's filter approach
172
+ const filteredMessages = messages.filter((message) => {
173
+ if (message.type === "tool_result") {
174
+ if (!validToolCallIds.has(message.actionExecutionId)) {
175
+ return false;
176
+ }
177
+ validToolCallIds.delete(message.actionExecutionId);
178
+ }
179
+ return true;
180
+ });
181
+
182
+ // Should have all non-tool_result messages + 1 valid tool_result
183
+ expect(filteredMessages.length).toBe(6); // 7 original - 1 duplicate
184
+
185
+ // Valid tool_result should be included exactly once
186
+ const toolResults = filteredMessages.filter((m) => m.type === "tool_result");
187
+ expect(toolResults.length).toBe(1);
188
+ expect(toolResults[0].actionExecutionId).toBe("valid-id-1");
189
+
190
+ // All non-tool_result messages should be preserved
191
+ expect(filteredMessages.filter((m) => m.type === "text").length).toBe(1);
192
+ expect(filteredMessages.filter((m) => m.type === "image").length).toBe(1);
193
+ expect(filteredMessages.filter((m) => m.type === "custom").length).toBe(1);
194
+ expect(filteredMessages.filter((m) => m.type === "null").length).toBe(1);
195
+ expect(filteredMessages.filter((m) => m.type === "undefined").length).toBe(1);
196
+ });
197
+
198
+ it("should properly handle multiple duplicate tool results", () => {
199
+ // Setup test data with multiple duplicates
200
+ const validToolCallIds = new Set<string>(["tool-1", "tool-2"]);
201
+
202
+ const messages = [
203
+ { type: "text", role: "user", content: "Initial prompt" },
204
+ { type: "tool_call", id: "tool-1", name: "firstTool" },
205
+ { type: "tool_result", actionExecutionId: "tool-1", result: "first-result" },
206
+ { type: "tool_result", actionExecutionId: "tool-1", result: "duplicate-1" }, // Duplicate 1
207
+ { type: "tool_call", id: "tool-2", name: "secondTool" },
208
+ { type: "tool_result", actionExecutionId: "tool-1", result: "duplicate-2" }, // Duplicate 2
209
+ { type: "tool_result", actionExecutionId: "tool-2", result: "second-result" },
210
+ { type: "tool_result", actionExecutionId: "tool-2", result: "duplicate-3" }, // Duplicate 3
211
+ { type: "tool_result", actionExecutionId: "tool-1", result: "duplicate-4" }, // Duplicate 4
212
+ ];
213
+
214
+ // Apply OpenAI's filter approach
215
+ const filteredMessages = messages.filter((message) => {
216
+ if (message.type === "tool_result") {
217
+ if (!validToolCallIds.has(message.actionExecutionId)) {
218
+ return false;
219
+ }
220
+ validToolCallIds.delete(message.actionExecutionId);
221
+ }
222
+ return true;
223
+ });
224
+
225
+ // Should have text + tool calls + only the first occurrence of each tool result
226
+ expect(filteredMessages.length).toBe(5);
227
+
228
+ // Check that only the first occurrence of each tool result is kept
229
+ const toolResults = filteredMessages.filter((m) => m.type === "tool_result");
230
+ expect(toolResults.length).toBe(2);
231
+
232
+ expect(toolResults[0].actionExecutionId).toBe("tool-1");
233
+ expect(toolResults[0].result).toBe("first-result"); // First occurrence should be kept
234
+
235
+ expect(toolResults[1].actionExecutionId).toBe("tool-2");
236
+ expect(toolResults[1].result).toBe("second-result"); // First occurrence should be kept
237
+ });
238
+ });
@@ -0,0 +1,301 @@
1
+ // Mock the modules first
2
+ jest.mock("openai", () => {
3
+ function MockOpenAI() {}
4
+ return { default: MockOpenAI };
5
+ });
6
+
7
+ // Mock the OpenAIAdapter class to avoid the "new OpenAI()" issue
8
+ jest.mock("../../../src/service-adapters/openai/openai-adapter", () => {
9
+ class MockOpenAIAdapter {
10
+ _openai: any;
11
+ model: string = "gpt-4o";
12
+ keepSystemRole: boolean = false;
13
+ disableParallelToolCalls: boolean = false;
14
+
15
+ constructor() {
16
+ this._openai = {
17
+ beta: {
18
+ chat: {
19
+ completions: {
20
+ stream: jest.fn(),
21
+ },
22
+ },
23
+ },
24
+ };
25
+ }
26
+
27
+ get openai() {
28
+ return this._openai;
29
+ }
30
+
31
+ async process(request: any) {
32
+ // Mock implementation that calls our event source but doesn't do the actual processing
33
+ request.eventSource.stream(async (stream: any) => {
34
+ stream.complete();
35
+ return Promise.resolve();
36
+ });
37
+
38
+ return { threadId: request.threadId || "mock-thread-id" };
39
+ }
40
+ }
41
+
42
+ return { OpenAIAdapter: MockOpenAIAdapter };
43
+ });
44
+
45
+ // Now import the modules
46
+ import { OpenAIAdapter } from "../../../src/service-adapters/openai/openai-adapter";
47
+
48
+ // Mock the Message classes since they use TypeGraphQL decorators
49
+ jest.mock("../../../src/graphql/types/converted", () => {
50
+ // Create minimal implementations of the message classes
51
+ class MockTextMessage {
52
+ content: string;
53
+ role: string;
54
+ id: string;
55
+
56
+ constructor(role: string, content: string) {
57
+ this.role = role;
58
+ this.content = content;
59
+ this.id = "mock-text-" + Math.random().toString(36).substring(7);
60
+ }
61
+
62
+ isTextMessage() {
63
+ return true;
64
+ }
65
+ isImageMessage() {
66
+ return false;
67
+ }
68
+ isActionExecutionMessage() {
69
+ return false;
70
+ }
71
+ isResultMessage() {
72
+ return false;
73
+ }
74
+ }
75
+
76
+ class MockActionExecutionMessage {
77
+ id: string;
78
+ name: string;
79
+ arguments: string;
80
+
81
+ constructor(params: { id: string; name: string; arguments: string }) {
82
+ this.id = params.id;
83
+ this.name = params.name;
84
+ this.arguments = params.arguments;
85
+ }
86
+
87
+ isTextMessage() {
88
+ return false;
89
+ }
90
+ isImageMessage() {
91
+ return false;
92
+ }
93
+ isActionExecutionMessage() {
94
+ return true;
95
+ }
96
+ isResultMessage() {
97
+ return false;
98
+ }
99
+ }
100
+
101
+ class MockResultMessage {
102
+ actionExecutionId: string;
103
+ result: string;
104
+ id: string;
105
+
106
+ constructor(params: { actionExecutionId: string; result: string }) {
107
+ this.actionExecutionId = params.actionExecutionId;
108
+ this.result = params.result;
109
+ this.id = "mock-result-" + Math.random().toString(36).substring(7);
110
+ }
111
+
112
+ isTextMessage() {
113
+ return false;
114
+ }
115
+ isImageMessage() {
116
+ return false;
117
+ }
118
+ isActionExecutionMessage() {
119
+ return false;
120
+ }
121
+ isResultMessage() {
122
+ return true;
123
+ }
124
+ }
125
+
126
+ return {
127
+ TextMessage: MockTextMessage,
128
+ ActionExecutionMessage: MockActionExecutionMessage,
129
+ ResultMessage: MockResultMessage,
130
+ };
131
+ });
132
+
133
+ describe("OpenAIAdapter", () => {
134
+ let adapter: OpenAIAdapter;
135
+ let mockEventSource: any;
136
+
137
+ beforeEach(() => {
138
+ jest.clearAllMocks();
139
+ adapter = new OpenAIAdapter();
140
+ mockEventSource = {
141
+ stream: jest.fn((callback) => {
142
+ const mockStream = {
143
+ sendTextMessageStart: jest.fn(),
144
+ sendTextMessageContent: jest.fn(),
145
+ sendTextMessageEnd: jest.fn(),
146
+ sendActionExecutionStart: jest.fn(),
147
+ sendActionExecutionArgs: jest.fn(),
148
+ sendActionExecutionEnd: jest.fn(),
149
+ complete: jest.fn(),
150
+ };
151
+ callback(mockStream);
152
+ }),
153
+ };
154
+ });
155
+
156
+ describe("Tool ID handling", () => {
157
+ it("should filter out tool_result messages that don't have corresponding tool_call IDs", async () => {
158
+ // Import dynamically after mocking
159
+ const {
160
+ TextMessage,
161
+ ActionExecutionMessage,
162
+ ResultMessage,
163
+ } = require("../../../src/graphql/types/converted");
164
+
165
+ // Create messages including one valid pair and one invalid tool_result
166
+ const systemMessage = new TextMessage("system", "System message");
167
+ const userMessage = new TextMessage("user", "User message");
168
+
169
+ // Valid tool execution message
170
+ const validToolExecution = new ActionExecutionMessage({
171
+ id: "valid-tool-id",
172
+ name: "validTool",
173
+ arguments: '{"arg":"value"}',
174
+ });
175
+
176
+ // Valid result for the above tool
177
+ const validToolResult = new ResultMessage({
178
+ actionExecutionId: "valid-tool-id",
179
+ result: '{"result":"success"}',
180
+ });
181
+
182
+ // Invalid tool result with no corresponding tool execution
183
+ const invalidToolResult = new ResultMessage({
184
+ actionExecutionId: "invalid-tool-id",
185
+ result: '{"result":"failure"}',
186
+ });
187
+
188
+ // Spy on the process method to test it's called properly
189
+ const processSpy = jest.spyOn(adapter, "process");
190
+
191
+ await adapter.process({
192
+ threadId: "test-thread",
193
+ model: "gpt-4o",
194
+ messages: [
195
+ systemMessage,
196
+ userMessage,
197
+ validToolExecution,
198
+ validToolResult,
199
+ invalidToolResult,
200
+ ],
201
+ actions: [],
202
+ eventSource: mockEventSource,
203
+ forwardedParameters: {},
204
+ });
205
+
206
+ // Verify the process method was called
207
+ expect(processSpy).toHaveBeenCalledTimes(1);
208
+
209
+ // Verify the stream function was called
210
+ expect(mockEventSource.stream).toHaveBeenCalled();
211
+ });
212
+
213
+ it("should handle duplicate tool IDs by only using each once", async () => {
214
+ // Import dynamically after mocking
215
+ const {
216
+ TextMessage,
217
+ ActionExecutionMessage,
218
+ ResultMessage,
219
+ } = require("../../../src/graphql/types/converted");
220
+
221
+ // Create messages including duplicate tool results for the same ID
222
+ const systemMessage = new TextMessage("system", "System message");
223
+
224
+ // Valid tool execution message
225
+ const toolExecution = new ActionExecutionMessage({
226
+ id: "tool-id-1",
227
+ name: "someTool",
228
+ arguments: '{"arg":"value"}',
229
+ });
230
+
231
+ // Two results for the same tool ID
232
+ const firstToolResult = new ResultMessage({
233
+ actionExecutionId: "tool-id-1",
234
+ result: '{"result":"first"}',
235
+ });
236
+
237
+ const duplicateToolResult = new ResultMessage({
238
+ actionExecutionId: "tool-id-1",
239
+ result: '{"result":"duplicate"}',
240
+ });
241
+
242
+ // Spy on the process method to test it's called properly
243
+ const processSpy = jest.spyOn(adapter, "process");
244
+
245
+ await adapter.process({
246
+ threadId: "test-thread",
247
+ model: "gpt-4o",
248
+ messages: [systemMessage, toolExecution, firstToolResult, duplicateToolResult],
249
+ actions: [],
250
+ eventSource: mockEventSource,
251
+ forwardedParameters: {},
252
+ });
253
+
254
+ // Verify the process method was called
255
+ expect(processSpy).toHaveBeenCalledTimes(1);
256
+
257
+ // Verify the stream function was called
258
+ expect(mockEventSource.stream).toHaveBeenCalled();
259
+ });
260
+
261
+ it("should call the stream method on eventSource", async () => {
262
+ // Import dynamically after mocking
263
+ const { TextMessage } = require("../../../src/graphql/types/converted");
264
+
265
+ // Create messages
266
+ const systemMessage = new TextMessage("system", "System message");
267
+ const userMessage = new TextMessage("user", "User message");
268
+
269
+ await adapter.process({
270
+ threadId: "test-thread",
271
+ model: "gpt-4o",
272
+ messages: [systemMessage, userMessage],
273
+ actions: [],
274
+ eventSource: mockEventSource,
275
+ forwardedParameters: {},
276
+ });
277
+
278
+ // Verify the stream function was called
279
+ expect(mockEventSource.stream).toHaveBeenCalled();
280
+ });
281
+
282
+ it("should return the provided threadId", async () => {
283
+ // Import dynamically after mocking
284
+ const { TextMessage } = require("../../../src/graphql/types/converted");
285
+
286
+ // Create a message
287
+ const systemMessage = new TextMessage("system", "System message");
288
+
289
+ const result = await adapter.process({
290
+ threadId: "test-thread",
291
+ model: "gpt-4o",
292
+ messages: [systemMessage],
293
+ actions: [],
294
+ eventSource: mockEventSource,
295
+ forwardedParameters: {},
296
+ });
297
+
298
+ expect(result.threadId).toBe("test-thread");
299
+ });
300
+ });
301
+ });
@@ -0,0 +1,21 @@
1
+ // Import reflect-metadata to support TypeGraphQL
2
+ import "reflect-metadata";
3
+
4
+ // Import Jest types and functions
5
+ import {
6
+ jest,
7
+ describe,
8
+ expect,
9
+ it,
10
+ test,
11
+ beforeEach,
12
+ afterEach,
13
+ beforeAll,
14
+ afterAll,
15
+ } from "@jest/globals";
16
+
17
+ // Suppress console output during tests
18
+ jest.spyOn(console, "log").mockImplementation(() => {});
19
+ jest.spyOn(console, "error").mockImplementation(() => {});
20
+
21
+ // The global types are already declared in global.d.ts, so we don't need to set globals here
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "types": ["node", "jest"],
5
+ "esModuleInterop": true,
6
+ "noEmit": true,
7
+ "allowJs": true
8
+ },
9
+ "include": ["./**/*.ts"]
10
+ }
package/tsconfig.json CHANGED
@@ -8,6 +8,6 @@
8
8
  "strict": false,
9
9
  "resolveJsonModule": true
10
10
  },
11
- "include": ["./src/**/*.ts", "./src/**/*.test.ts", "./src/**/__tests__/*"],
11
+ "include": ["./src/**/*.ts", "./src/**/*.test.ts", "./src/**/__tests__/*", "./tests/**/*.ts"],
12
12
  "exclude": ["dist", "build", "node_modules"]
13
13
  }