@agentica/core 0.43.2 → 0.44.0-dev.20260313

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 (205) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +218 -218
  3. package/lib/constants/AgenticaSystemPrompt.js +1 -1
  4. package/lib/constants/AgenticaSystemPrompt.js.map +1 -1
  5. package/lib/context/AgenticaOperation.d.ts +3 -2
  6. package/lib/context/internal/AgenticaOperationComposer.js.map +1 -1
  7. package/lib/context/internal/AgenticaTokenUsageAggregator.js.map +1 -1
  8. package/lib/context/internal/__IChatInitialApplication.d.ts +1 -1
  9. package/lib/errors/AgenticaJsonParseError.d.ts +5 -8
  10. package/lib/errors/AgenticaJsonParseError.js +11 -6
  11. package/lib/errors/AgenticaJsonParseError.js.map +1 -1
  12. package/lib/errors/AgenticaValidationError.d.ts +1 -1
  13. package/lib/events/AgenticaExecuteEvent.d.ts +1 -1
  14. package/lib/events/AgenticaJsonParseErrorEvent.d.ts +2 -2
  15. package/lib/factory/events.d.ts +2 -3
  16. package/lib/factory/events.js +1 -2
  17. package/lib/factory/events.js.map +1 -1
  18. package/lib/functional/assertHttpController.d.ts +2 -3
  19. package/lib/functional/assertHttpController.js +3939 -2656
  20. package/lib/functional/assertHttpController.js.map +1 -1
  21. package/lib/functional/assertHttpLlmApplication.d.ts +3 -3
  22. package/lib/functional/assertHttpLlmApplication.js +3939 -2656
  23. package/lib/functional/assertHttpLlmApplication.js.map +1 -1
  24. package/lib/functional/assertMcpController.d.ts +2 -2
  25. package/lib/functional/assertMcpController.js +202 -629
  26. package/lib/functional/assertMcpController.js.map +1 -1
  27. package/lib/functional/createMcpLlmApplication.d.ts +6 -0
  28. package/lib/functional/createMcpLlmApplication.js +56 -0
  29. package/lib/functional/createMcpLlmApplication.js.map +1 -0
  30. package/lib/functional/validateHttpController.d.ts +3 -3
  31. package/lib/functional/validateHttpController.js +3367 -2268
  32. package/lib/functional/validateHttpController.js.map +1 -1
  33. package/lib/functional/validateHttpLlmApplication.d.ts +3 -4
  34. package/lib/functional/validateHttpLlmApplication.js +3367 -2268
  35. package/lib/functional/validateHttpLlmApplication.js.map +1 -1
  36. package/lib/functional/validateMcpController.d.ts +2 -2
  37. package/lib/functional/validateMcpController.js +388 -1161
  38. package/lib/functional/validateMcpController.js.map +1 -1
  39. package/lib/histories/AgenticaExecuteHistory.d.ts +1 -1
  40. package/lib/index.mjs +21256 -18948
  41. package/lib/index.mjs.map +1 -1
  42. package/lib/orchestrate/call.js +43 -56
  43. package/lib/orchestrate/call.js.map +1 -1
  44. package/lib/orchestrate/cancel.js +9 -66
  45. package/lib/orchestrate/cancel.js.map +1 -1
  46. package/lib/orchestrate/initialize.js +4 -947
  47. package/lib/orchestrate/initialize.js.map +1 -1
  48. package/lib/orchestrate/select.js +11 -68
  49. package/lib/orchestrate/select.js.map +1 -1
  50. package/lib/structures/IAgenticaController.d.ts +143 -151
  51. package/lib/structures/IMcpTool.d.ts +52 -0
  52. package/lib/structures/IMcpTool.js +3 -0
  53. package/lib/structures/IMcpTool.js.map +1 -0
  54. package/lib/utils/ChatGptCompletionMessageUtil.js +16 -5
  55. package/lib/utils/ChatGptCompletionMessageUtil.js.map +1 -1
  56. package/lib/utils/ChatGptCompletionMessageUtil.spec.js +0 -5
  57. package/lib/utils/ChatGptCompletionMessageUtil.spec.js.map +1 -1
  58. package/package.json +7 -9
  59. package/prompts/cancel.md +5 -5
  60. package/prompts/common.md +3 -3
  61. package/prompts/describe.md +7 -7
  62. package/prompts/execute.md +122 -122
  63. package/prompts/initialize.md +3 -3
  64. package/prompts/json_parse_error.md +35 -33
  65. package/prompts/select.md +7 -7
  66. package/prompts/validate.md +123 -123
  67. package/prompts/validate_repeated.md +31 -31
  68. package/src/Agentica.ts +367 -367
  69. package/src/MicroAgentica.ts +357 -357
  70. package/src/constants/AgenticaConstant.ts +4 -4
  71. package/src/constants/AgenticaDefaultPrompt.ts +44 -44
  72. package/src/constants/AgenticaSystemPrompt.ts +1 -1
  73. package/src/constants/index.ts +2 -2
  74. package/src/context/AgenticaContext.ts +136 -136
  75. package/src/context/AgenticaContextRequestResult.ts +14 -14
  76. package/src/context/AgenticaOperation.ts +73 -72
  77. package/src/context/AgenticaOperationCollection.ts +49 -49
  78. package/src/context/AgenticaOperationSelection.ts +9 -9
  79. package/src/context/AgenticaTokenUsage.ts +186 -186
  80. package/src/context/MicroAgenticaContext.ts +99 -99
  81. package/src/context/index.ts +5 -5
  82. package/src/context/internal/AgenticaOperationComposer.ts +177 -177
  83. package/src/context/internal/AgenticaTokenUsageAggregator.ts +66 -66
  84. package/src/context/internal/__IChatCancelFunctionsApplication.ts +23 -23
  85. package/src/context/internal/__IChatFunctionReference.ts +21 -21
  86. package/src/context/internal/__IChatInitialApplication.ts +15 -15
  87. package/src/context/internal/__IChatSelectFunctionsApplication.ts +24 -24
  88. package/src/context/internal/isAgenticaContext.ts +11 -11
  89. package/src/errors/AgenticaJsonParseError.ts +52 -47
  90. package/src/errors/AgenticaValidationError.ts +49 -49
  91. package/src/errors/index.ts +2 -2
  92. package/src/events/AgenticaAssistantMessageEvent.ts +12 -12
  93. package/src/events/AgenticaCallEvent.ts +27 -27
  94. package/src/events/AgenticaCancelEvent.ts +9 -9
  95. package/src/events/AgenticaDescribeEvent.ts +14 -14
  96. package/src/events/AgenticaEvent.ts +59 -59
  97. package/src/events/AgenticaEvent.type.ts +19 -19
  98. package/src/events/AgenticaEventBase.ts +18 -18
  99. package/src/events/AgenticaEventSource.ts +6 -6
  100. package/src/events/AgenticaExecuteEvent.ts +45 -45
  101. package/src/events/AgenticaInitializeEvent.ts +7 -7
  102. package/src/events/AgenticaJsonParseErrorEvent.ts +16 -15
  103. package/src/events/AgenticaRequestEvent.ts +27 -27
  104. package/src/events/AgenticaResponseEvent.ts +32 -32
  105. package/src/events/AgenticaSelectEvent.ts +11 -11
  106. package/src/events/AgenticaUserMessageEvent.ts +12 -12
  107. package/src/events/AgenticaValidateEvent.ts +32 -32
  108. package/src/events/MicroAgenticaEvent.ts +45 -45
  109. package/src/events/index.ts +15 -15
  110. package/src/factory/events.ts +357 -359
  111. package/src/factory/histories.ts +348 -348
  112. package/src/factory/index.ts +3 -3
  113. package/src/factory/operations.ts +16 -16
  114. package/src/functional/assertHttpController.ts +106 -104
  115. package/src/functional/assertHttpLlmApplication.ts +52 -57
  116. package/src/functional/assertMcpController.ts +47 -44
  117. package/src/functional/createMcpLlmApplication.ts +72 -0
  118. package/src/functional/index.ts +7 -7
  119. package/src/functional/validateHttpController.ts +113 -110
  120. package/src/functional/validateHttpLlmApplication.ts +65 -70
  121. package/src/functional/validateMcpController.ts +53 -50
  122. package/src/histories/AgenticaAssistantMessageHistory.ts +10 -10
  123. package/src/histories/AgenticaCancelHistory.ts +8 -8
  124. package/src/histories/AgenticaDescribeHistory.ts +18 -18
  125. package/src/histories/AgenticaExecuteHistory.ts +64 -64
  126. package/src/histories/AgenticaHistory.ts +28 -28
  127. package/src/histories/AgenticaHistoryBase.ts +35 -35
  128. package/src/histories/AgenticaSelectHistory.ts +8 -8
  129. package/src/histories/AgenticaSystemMessageHistory.ts +10 -10
  130. package/src/histories/AgenticaUserMessageHistory.ts +11 -11
  131. package/src/histories/MicroAgenticaHistory.ts +19 -19
  132. package/src/histories/contents/AgenticaUserMessageAudioContent.ts +21 -21
  133. package/src/histories/contents/AgenticaUserMessageContent.ts +19 -19
  134. package/src/histories/contents/AgenticaUserMessageContentBase.ts +6 -6
  135. package/src/histories/contents/AgenticaUserMessageFileContent.ts +25 -25
  136. package/src/histories/contents/AgenticaUserMessageImageContent.ts +33 -33
  137. package/src/histories/contents/AgenticaUserMessageTextContent.ts +15 -15
  138. package/src/histories/contents/index.ts +5 -5
  139. package/src/histories/index.ts +10 -10
  140. package/src/index.ts +15 -15
  141. package/src/json/IAgenticaEventJson.ts +265 -265
  142. package/src/json/IAgenticaEventJson.type.ts +19 -19
  143. package/src/json/IAgenticaHistoryJson.ts +165 -165
  144. package/src/json/IAgenticaHistoryJson.type.ts +19 -19
  145. package/src/json/IAgenticaOperationJson.ts +36 -36
  146. package/src/json/IAgenticaOperationSelectionJson.ts +26 -26
  147. package/src/json/IAgenticaTokenUsageJson.ts +107 -107
  148. package/src/json/IMicroAgenticaEventJson.ts +22 -22
  149. package/src/json/IMicroAgenticaHistoryJson.ts +25 -25
  150. package/src/json/index.ts +7 -7
  151. package/src/orchestrate/call.ts +542 -558
  152. package/src/orchestrate/cancel.ts +265 -269
  153. package/src/orchestrate/describe.ts +66 -66
  154. package/src/orchestrate/execute.ts +61 -61
  155. package/src/orchestrate/index.ts +6 -6
  156. package/src/orchestrate/initialize.ts +102 -102
  157. package/src/orchestrate/internal/cancelFunctionFromContext.ts +33 -33
  158. package/src/orchestrate/internal/selectFunctionFromContext.ts +34 -34
  159. package/src/orchestrate/select.ts +320 -322
  160. package/src/structures/IAgenticaConfig.ts +83 -83
  161. package/src/structures/IAgenticaConfigBase.ts +87 -87
  162. package/src/structures/IAgenticaController.ts +143 -151
  163. package/src/structures/IAgenticaExecutor.ts +167 -167
  164. package/src/structures/IAgenticaProps.ts +78 -78
  165. package/src/structures/IAgenticaSystemPrompt.ts +236 -236
  166. package/src/structures/IAgenticaVendor.ts +54 -54
  167. package/src/structures/IMcpTool.ts +60 -0
  168. package/src/structures/IMicroAgenticaConfig.ts +56 -56
  169. package/src/structures/IMicroAgenticaExecutor.ts +67 -67
  170. package/src/structures/IMicroAgenticaProps.ts +77 -77
  171. package/src/structures/IMicroAgenticaSystemPrompt.ts +169 -169
  172. package/src/structures/index.ts +10 -10
  173. package/src/transformers/transformHistory.ts +172 -172
  174. package/src/utils/AssistantMessageEmptyError.ts +20 -20
  175. package/src/utils/AsyncQueue.spec.ts +355 -355
  176. package/src/utils/AsyncQueue.ts +95 -95
  177. package/src/utils/ByteArrayUtil.ts +5 -5
  178. package/src/utils/ChatGptCompletionMessageUtil.spec.ts +314 -320
  179. package/src/utils/ChatGptCompletionMessageUtil.ts +210 -195
  180. package/src/utils/ChatGptCompletionStreamingUtil.spec.ts +909 -909
  181. package/src/utils/ChatGptCompletionStreamingUtil.ts +91 -91
  182. package/src/utils/ChatGptTokenUsageAggregator.spec.ts +226 -226
  183. package/src/utils/ChatGptTokenUsageAggregator.ts +57 -57
  184. package/src/utils/MPSC.spec.ts +276 -276
  185. package/src/utils/MPSC.ts +42 -42
  186. package/src/utils/Singleton.spec.ts +138 -138
  187. package/src/utils/Singleton.ts +42 -42
  188. package/src/utils/StreamUtil.spec.ts +512 -512
  189. package/src/utils/StreamUtil.ts +87 -87
  190. package/src/utils/__map_take.spec.ts +140 -140
  191. package/src/utils/__map_take.ts +13 -13
  192. package/src/utils/__retry.spec.ts +198 -198
  193. package/src/utils/__retry.ts +18 -18
  194. package/src/utils/assertExecuteFailure.ts +16 -16
  195. package/src/utils/index.ts +4 -4
  196. package/src/utils/request.ts +140 -140
  197. package/src/utils/types.ts +50 -50
  198. package/lib/context/internal/AgenticaOperationComposer.spec.d.ts +0 -1
  199. package/lib/context/internal/AgenticaOperationComposer.spec.js +0 -353
  200. package/lib/context/internal/AgenticaOperationComposer.spec.js.map +0 -1
  201. package/lib/utils/JsonUtil.d.ts +0 -8
  202. package/lib/utils/JsonUtil.js +0 -350
  203. package/lib/utils/JsonUtil.js.map +0 -1
  204. package/src/context/internal/AgenticaOperationComposer.spec.ts +0 -401
  205. package/src/utils/JsonUtil.ts +0 -462
@@ -1,512 +1,512 @@
1
- import { streamDefaultReaderToAsyncGenerator, StreamUtil, toAsyncGenerator } from "./StreamUtil";
2
-
3
- // Helper function to create a stream with numbers from start to end
4
- function createNumberStream(
5
- start: number,
6
- end: number,
7
- ): ReadableStream<number> {
8
- return new ReadableStream<number>({
9
- start(controller) {
10
- for (let i = start; i <= end; i++) {
11
- controller.enqueue(i);
12
- }
13
- controller.close();
14
- },
15
- });
16
- }
17
-
18
- // Helper function to create an empty stream
19
- function createEmptyStream<T>(): ReadableStream<T> {
20
- return new ReadableStream<T>({
21
- start(controller) {
22
- controller.close();
23
- },
24
- });
25
- }
26
-
27
- // Helper function to convert ReadableStream to array
28
- async function streamToArray<T>(stream: ReadableStream<T>): Promise<T[]> {
29
- const reader = stream.getReader();
30
- const result: T[] = [];
31
-
32
- while (true) {
33
- const { done, value } = await reader.read();
34
- if (done) {
35
- break;
36
- }
37
- result.push(value);
38
- }
39
-
40
- return result;
41
- }
42
-
43
- // Helper function to create test stream
44
- function createTestStream<T>(items: T[]): ReadableStream<T> {
45
- return new ReadableStream<T>({
46
- start(controller) {
47
- for (const item of items) {
48
- controller.enqueue(item);
49
- }
50
- controller.close();
51
- },
52
- });
53
- }
54
-
55
- // Delay function (for simulating async operations)
56
- async function delay(ms: number): Promise<void> {
57
- return new Promise(resolve => setTimeout(resolve, ms));
58
- }
59
-
60
- // Helper function to create a stream with numbers from start to end asynchronously
61
- async function createDelayedNumberStream(
62
- start: number,
63
- end: number,
64
- delayMs: number,
65
- ): Promise<ReadableStream<number>> {
66
- // Simulate async work
67
- await delay(delayMs);
68
-
69
- return new ReadableStream<number>({
70
- start(controller) {
71
- for (let i = start; i <= end; i++) {
72
- controller.enqueue(i);
73
- }
74
- controller.close();
75
- },
76
- });
77
- }
78
-
79
- // Helper function to create an empty stream asynchronously
80
- async function createEmptyDelayedStream<T>(
81
- delayMs: number,
82
- ): Promise<ReadableStream<T>> {
83
- // Simulate async work
84
- await delay(delayMs);
85
-
86
- return new ReadableStream<T>({
87
- start(controller) {
88
- controller.close();
89
- },
90
- });
91
- }
92
-
93
- // Helper function to create a stream with items having various delay times
94
- async function createVariableDelayedNumberStream(
95
- items: Array<{ value: number; delay: number }>,
96
- ): Promise<ReadableStream<number>> {
97
- // Wait for all items to be ready based on their delays
98
- await Promise.all(items.map(async item => delay(item.delay)));
99
-
100
- return new ReadableStream<number>({
101
- start(controller) {
102
- for (const item of items) {
103
- controller.enqueue(item.value);
104
- }
105
- controller.close();
106
- },
107
- });
108
- }
109
-
110
- describe("streamUtil", () => {
111
- describe("readAll", () => {
112
- it("should read all values from a stream", async () => {
113
- const stream = createNumberStream(1, 5);
114
- const result = await StreamUtil.readAll(stream);
115
-
116
- expect(result).toEqual([1, 2, 3, 4, 5]);
117
- });
118
-
119
- it("should return empty array for empty stream", async () => {
120
- const stream = createEmptyStream<number>();
121
- const result = await StreamUtil.readAll(stream);
122
-
123
- expect(result).toEqual([]);
124
- });
125
-
126
- it("should handle error in stream", async () => {
127
- const stream = new ReadableStream<number>({
128
- start(controller) {
129
- controller.enqueue(1);
130
- controller.enqueue(2);
131
- controller.error(new Error("Stream error"));
132
- },
133
- });
134
-
135
- await expect(StreamUtil.readAll(stream)).rejects.toThrow("Stream error");
136
- });
137
-
138
- it("should handle null or undefined values in stream", async () => {
139
- const stream = createTestStream<number | null | undefined>([1, null, 3, undefined, 5]);
140
-
141
- const result = await StreamUtil.readAll(stream);
142
- expect(result).toEqual([1, null, 3, undefined, 5]);
143
- });
144
- });
145
-
146
- describe("reduce", () => {
147
- it("should concatenate strings from number stream", async () => {
148
- const stringStream = createNumberStream(1, 3);
149
- const stringResult = await StreamUtil.reduce<number, string>(
150
- stringStream,
151
- (acc, cur) => acc + cur.toString(),
152
- { initial: "" },
153
- );
154
-
155
- expect(stringResult).toBe("123");
156
- });
157
-
158
- it("should sum numbers from stream", async () => {
159
- const sumStream = createNumberStream(1, 5);
160
- const sumResult = await StreamUtil.reduce<number, number>(
161
- sumStream,
162
- (acc, cur) => acc + cur,
163
- { initial: 0 },
164
- );
165
-
166
- expect(sumResult).toBe(15);
167
- });
168
-
169
- it("should work without initial value", async () => {
170
- const noInitialStream = createNumberStream(1, 4);
171
- const noInitialResult = await StreamUtil.reduce<number>(
172
- noInitialStream,
173
- (acc, cur) => acc + cur,
174
- { initial: 0 },
175
- );
176
-
177
- expect(noInitialResult).toBe(10);
178
- });
179
-
180
- it("should return initial value for empty stream", async () => {
181
- const emptyStream = createEmptyStream<number>();
182
- const emptyResult = await StreamUtil.reduce<number, string>(
183
- emptyStream,
184
- (acc, cur) => acc + cur.toString(),
185
- { initial: "initial value" },
186
- );
187
-
188
- expect(emptyResult).toBe("initial value");
189
- });
190
-
191
- it("should work with async generated stream", async () => {
192
- const stringStream = await createDelayedNumberStream(1, 3, 10);
193
- const stringResult = await StreamUtil.reduce<number, string>(
194
- stringStream,
195
- (acc, cur) => acc + cur.toString(),
196
- { initial: "" },
197
- );
198
-
199
- expect(stringResult).toBe("123");
200
- });
201
-
202
- it("should work with async stream without initial value", async () => {
203
- const noInitialStream = await createDelayedNumberStream(1, 4, 15);
204
- const noInitialResult = await StreamUtil.reduce<number>(
205
- noInitialStream,
206
- (acc, cur) => acc + cur,
207
- { initial: 0 },
208
- );
209
-
210
- expect(noInitialResult).toBe(10);
211
- });
212
-
213
- it("should work with async stream transformation and aggregation into array", async () => {
214
- const transformStream = await createDelayedNumberStream(1, 3, 10);
215
- const transformResult = await StreamUtil.reduce<number, string[]>(
216
- transformStream,
217
- (acc: string[] | number, cur: number): string[] => {
218
- if (typeof acc === "number") {
219
- // Handle case when no initial value is provided
220
- return [`item${acc}`, `item${cur}`];
221
- }
222
- return [...acc, `item${cur}`];
223
- },
224
- { initial: [] },
225
- );
226
-
227
- expect(transformResult).toEqual(["item1", "item2", "item3"]);
228
- });
229
-
230
- it("should return initial value for async generated empty stream", async () => {
231
- const emptyStream = await createEmptyDelayedStream<number>(30);
232
- const emptyResult = await StreamUtil.reduce<number, string>(
233
- emptyStream,
234
- (acc, cur) => acc + cur.toString(),
235
- { initial: "initial" },
236
- );
237
-
238
- expect(emptyResult).toBe("initial");
239
- });
240
-
241
- it("should work with stream with values created with various async delays", async () => {
242
- const delayStream = await createVariableDelayedNumberStream([
243
- { value: 1, delay: 20 },
244
- { value: 2, delay: 40 },
245
- { value: 3, delay: 10 },
246
- ]);
247
-
248
- const delayResult = await StreamUtil.reduce<number, number>(
249
- delayStream,
250
- (acc, cur) => acc + cur,
251
- { initial: 0 },
252
- );
253
-
254
- expect(delayResult).toBe(6);
255
- });
256
-
257
- it("should handle error in reducer function", async () => {
258
- const stream = createNumberStream(1, 3);
259
-
260
- await expect(
261
- StreamUtil.reduce<number, number>(
262
- stream,
263
- (acc, cur) => {
264
- if (cur === 2) {
265
- throw new Error("Test error");
266
- }
267
- return acc + cur;
268
- },
269
- { initial: 0 },
270
- ),
271
- ).rejects.toThrow("Test error");
272
- });
273
-
274
- it("should handle null or undefined values in stream", async () => {
275
- const stream = createTestStream<number | null | undefined>([1, null, 3, undefined, 5]);
276
-
277
- const result = await StreamUtil.reduce<number | null | undefined, number>(
278
- stream,
279
- (acc, cur) => (acc ?? 0) + (cur ?? 0),
280
- { initial: 0 },
281
- );
282
-
283
- expect(result).toBe(9); // 1 + 0 + 3 + 0 + 5 = 9
284
- });
285
- });
286
-
287
- describe("from", () => {
288
- it("should create a stream with a single value", async () => {
289
- const stream = StreamUtil.from("Hello, world!");
290
- const reader = stream.getReader();
291
- const { done, value } = await reader.read();
292
-
293
- expect(done).toBe(false);
294
- expect(value).toBe("Hello, world!");
295
-
296
- const next = await reader.read();
297
- expect(next.done).toBe(true);
298
- });
299
-
300
- it("should handle null or undefined values", async () => {
301
- const stream = StreamUtil.from(null);
302
- const reader = stream.getReader();
303
- const { done, value } = await reader.read();
304
-
305
- expect(done).toBe(false);
306
- expect(value).toBeNull();
307
-
308
- const next = await reader.read();
309
- expect(next.done).toBe(true);
310
- });
311
- });
312
-
313
- describe("transform", () => {
314
- it("should transform number stream by doubling", async () => {
315
- const numbersInput = [1, 2, 3, 4, 5];
316
- const numberStream = createTestStream(numbersInput);
317
-
318
- const doubledStream = StreamUtil.transform(
319
- numberStream,
320
- (num: number) => num * 2,
321
- );
322
-
323
- const doubledResult = await streamToArray(doubledStream);
324
- const expectedDoubled = numbersInput.map(n => n * 2);
325
-
326
- expect(doubledResult).toEqual(expectedDoubled);
327
- });
328
-
329
- it("should transform object stream", async () => {
330
- const objectsInput = [
331
- { name: "item1", value: 10 },
332
- { name: "item2", value: 20 },
333
- { name: "item3", value: 30 },
334
- ];
335
-
336
- const objectStream = createTestStream(objectsInput);
337
-
338
- const transformedObjectStream = StreamUtil.transform(objectStream, obj => ({
339
- id: obj.name.toUpperCase(),
340
- doubledValue: obj.value * 2,
341
- }));
342
-
343
- const transformedObjects = await streamToArray(transformedObjectStream);
344
- const expectedObjects = objectsInput.map(obj => ({
345
- id: obj.name.toUpperCase(),
346
- doubledValue: obj.value * 2,
347
- }));
348
-
349
- expect(transformedObjects).toEqual(expectedObjects);
350
- });
351
-
352
- it("should handle empty stream", async () => {
353
- const emptyStream = createEmptyStream<number>();
354
- const transformedEmptyStream = StreamUtil.transform(
355
- emptyStream,
356
- n => n * 2,
357
- );
358
-
359
- const emptyResult = await streamToArray(transformedEmptyStream);
360
-
361
- expect(emptyResult).toEqual([]);
362
- });
363
-
364
- it("should transform type (number -> string)", async () => {
365
- const numbersInput = [1, 2, 3, 4, 5];
366
- const numberStream = createTestStream(numbersInput);
367
-
368
- const stringStream = StreamUtil.transform(
369
- numberStream,
370
- num => `Number: ${num}`,
371
- );
372
-
373
- const stringResult = await streamToArray(stringStream);
374
- const expectedStrings = numbersInput.map(n => `Number: ${n}`);
375
-
376
- expect(stringResult).toEqual(expectedStrings);
377
- });
378
-
379
- it("should handle error in transformer function", async () => {
380
- const stream = createNumberStream(1, 3);
381
-
382
- const transformedStream = StreamUtil.transform(
383
- stream,
384
- (num) => {
385
- if (num === 2) {
386
- throw new Error("Transform error");
387
- }
388
- return num * 2;
389
- },
390
- );
391
-
392
- await expect(streamToArray(transformedStream)).rejects.toThrow("Transform error");
393
- });
394
-
395
- it("should handle null or undefined values in stream", async () => {
396
- const stream = createTestStream<number | null | undefined>([1, null, 3, undefined, 5]);
397
-
398
- const transformedStream = StreamUtil.transform(
399
- stream,
400
- num => (num ?? 0) * 2,
401
- );
402
-
403
- const result = await streamToArray(transformedStream);
404
- expect(result).toEqual([2, 0, 6, 0, 10]);
405
- });
406
- });
407
-
408
- describe("toAsyncGenerator", () => {
409
- it("should yield a single value", async () => {
410
- const generator = toAsyncGenerator("test value");
411
- const result = [];
412
-
413
- for await (const value of generator) {
414
- result.push(value);
415
- }
416
-
417
- expect(result).toEqual(["test value"]);
418
- });
419
-
420
- it("should handle null or undefined values", async () => {
421
- const generator = toAsyncGenerator(null);
422
- const result = [];
423
-
424
- for await (const value of generator) {
425
- result.push(value);
426
- }
427
-
428
- expect(result).toEqual([null]);
429
- });
430
-
431
- it("should handle object values", async () => {
432
- const testObj = { id: 1, name: "test" };
433
- const generator = toAsyncGenerator(testObj);
434
- const result = [];
435
-
436
- for await (const value of generator) {
437
- result.push(value);
438
- }
439
-
440
- expect(result).toEqual([testObj]);
441
- });
442
- });
443
-
444
- describe("streamDefaultReaderToAsyncGenerator", () => {
445
- it("should convert stream reader to async generator", async () => {
446
- const stream = createNumberStream(1, 5);
447
- const reader = stream.getReader();
448
- const generator = streamDefaultReaderToAsyncGenerator(reader);
449
- const result = [];
450
-
451
- for await (const value of generator) {
452
- result.push(value);
453
- }
454
-
455
- expect(result).toEqual([1, 2, 3, 4, 5]);
456
- });
457
-
458
- it("should handle empty stream", async () => {
459
- const stream = createEmptyStream<number>();
460
- const reader = stream.getReader();
461
- const generator = streamDefaultReaderToAsyncGenerator(reader);
462
- const result = [];
463
-
464
- for await (const value of generator) {
465
- result.push(value);
466
- }
467
-
468
- expect(result).toEqual([]);
469
- });
470
-
471
- it("should handle null or undefined values in stream", async () => {
472
- const stream = createTestStream<number | null | undefined>([1, null, 3, undefined, 5]);
473
- const reader = stream.getReader();
474
- const generator = streamDefaultReaderToAsyncGenerator(reader);
475
- const result = [];
476
-
477
- for await (const value of generator) {
478
- result.push(value);
479
- }
480
-
481
- expect(result).toEqual([1, null, 3, undefined, 5]);
482
- });
483
-
484
- it("should handle error in stream", async () => {
485
- const stream = new ReadableStream<number>({
486
- start(controller) {
487
- controller.enqueue(1);
488
- controller.enqueue(2);
489
- delay(1000).then(() => controller.error(new Error("Stream error"))).catch(() => {});
490
- },
491
- });
492
-
493
- const reader = stream.getReader();
494
- const generator = streamDefaultReaderToAsyncGenerator(reader);
495
- const result = [];
496
-
497
- try {
498
- for await (const value of generator) {
499
- result.push(value);
500
- }
501
- // Should not reach here
502
- expect(true).toBe(false);
503
- }
504
- catch (error) {
505
- console.error(error);
506
- expect(error).toBeInstanceOf(Error);
507
- expect((error as Error).message).toBe("Stream error");
508
- expect(result).toEqual([1, 2]);
509
- }
510
- });
511
- });
512
- });
1
+ import { streamDefaultReaderToAsyncGenerator, StreamUtil, toAsyncGenerator } from "./StreamUtil";
2
+
3
+ // Helper function to create a stream with numbers from start to end
4
+ function createNumberStream(
5
+ start: number,
6
+ end: number,
7
+ ): ReadableStream<number> {
8
+ return new ReadableStream<number>({
9
+ start(controller) {
10
+ for (let i = start; i <= end; i++) {
11
+ controller.enqueue(i);
12
+ }
13
+ controller.close();
14
+ },
15
+ });
16
+ }
17
+
18
+ // Helper function to create an empty stream
19
+ function createEmptyStream<T>(): ReadableStream<T> {
20
+ return new ReadableStream<T>({
21
+ start(controller) {
22
+ controller.close();
23
+ },
24
+ });
25
+ }
26
+
27
+ // Helper function to convert ReadableStream to array
28
+ async function streamToArray<T>(stream: ReadableStream<T>): Promise<T[]> {
29
+ const reader = stream.getReader();
30
+ const result: T[] = [];
31
+
32
+ while (true) {
33
+ const { done, value } = await reader.read();
34
+ if (done) {
35
+ break;
36
+ }
37
+ result.push(value);
38
+ }
39
+
40
+ return result;
41
+ }
42
+
43
+ // Helper function to create test stream
44
+ function createTestStream<T>(items: T[]): ReadableStream<T> {
45
+ return new ReadableStream<T>({
46
+ start(controller) {
47
+ for (const item of items) {
48
+ controller.enqueue(item);
49
+ }
50
+ controller.close();
51
+ },
52
+ });
53
+ }
54
+
55
+ // Delay function (for simulating async operations)
56
+ async function delay(ms: number): Promise<void> {
57
+ return new Promise(resolve => setTimeout(resolve, ms));
58
+ }
59
+
60
+ // Helper function to create a stream with numbers from start to end asynchronously
61
+ async function createDelayedNumberStream(
62
+ start: number,
63
+ end: number,
64
+ delayMs: number,
65
+ ): Promise<ReadableStream<number>> {
66
+ // Simulate async work
67
+ await delay(delayMs);
68
+
69
+ return new ReadableStream<number>({
70
+ start(controller) {
71
+ for (let i = start; i <= end; i++) {
72
+ controller.enqueue(i);
73
+ }
74
+ controller.close();
75
+ },
76
+ });
77
+ }
78
+
79
+ // Helper function to create an empty stream asynchronously
80
+ async function createEmptyDelayedStream<T>(
81
+ delayMs: number,
82
+ ): Promise<ReadableStream<T>> {
83
+ // Simulate async work
84
+ await delay(delayMs);
85
+
86
+ return new ReadableStream<T>({
87
+ start(controller) {
88
+ controller.close();
89
+ },
90
+ });
91
+ }
92
+
93
+ // Helper function to create a stream with items having various delay times
94
+ async function createVariableDelayedNumberStream(
95
+ items: Array<{ value: number; delay: number }>,
96
+ ): Promise<ReadableStream<number>> {
97
+ // Wait for all items to be ready based on their delays
98
+ await Promise.all(items.map(async item => delay(item.delay)));
99
+
100
+ return new ReadableStream<number>({
101
+ start(controller) {
102
+ for (const item of items) {
103
+ controller.enqueue(item.value);
104
+ }
105
+ controller.close();
106
+ },
107
+ });
108
+ }
109
+
110
+ describe("streamUtil", () => {
111
+ describe("readAll", () => {
112
+ it("should read all values from a stream", async () => {
113
+ const stream = createNumberStream(1, 5);
114
+ const result = await StreamUtil.readAll(stream);
115
+
116
+ expect(result).toEqual([1, 2, 3, 4, 5]);
117
+ });
118
+
119
+ it("should return empty array for empty stream", async () => {
120
+ const stream = createEmptyStream<number>();
121
+ const result = await StreamUtil.readAll(stream);
122
+
123
+ expect(result).toEqual([]);
124
+ });
125
+
126
+ it("should handle error in stream", async () => {
127
+ const stream = new ReadableStream<number>({
128
+ start(controller) {
129
+ controller.enqueue(1);
130
+ controller.enqueue(2);
131
+ controller.error(new Error("Stream error"));
132
+ },
133
+ });
134
+
135
+ await expect(StreamUtil.readAll(stream)).rejects.toThrow("Stream error");
136
+ });
137
+
138
+ it("should handle null or undefined values in stream", async () => {
139
+ const stream = createTestStream<number | null | undefined>([1, null, 3, undefined, 5]);
140
+
141
+ const result = await StreamUtil.readAll(stream);
142
+ expect(result).toEqual([1, null, 3, undefined, 5]);
143
+ });
144
+ });
145
+
146
+ describe("reduce", () => {
147
+ it("should concatenate strings from number stream", async () => {
148
+ const stringStream = createNumberStream(1, 3);
149
+ const stringResult = await StreamUtil.reduce<number, string>(
150
+ stringStream,
151
+ (acc, cur) => acc + cur.toString(),
152
+ { initial: "" },
153
+ );
154
+
155
+ expect(stringResult).toBe("123");
156
+ });
157
+
158
+ it("should sum numbers from stream", async () => {
159
+ const sumStream = createNumberStream(1, 5);
160
+ const sumResult = await StreamUtil.reduce<number, number>(
161
+ sumStream,
162
+ (acc, cur) => acc + cur,
163
+ { initial: 0 },
164
+ );
165
+
166
+ expect(sumResult).toBe(15);
167
+ });
168
+
169
+ it("should work without initial value", async () => {
170
+ const noInitialStream = createNumberStream(1, 4);
171
+ const noInitialResult = await StreamUtil.reduce<number>(
172
+ noInitialStream,
173
+ (acc, cur) => acc + cur,
174
+ { initial: 0 },
175
+ );
176
+
177
+ expect(noInitialResult).toBe(10);
178
+ });
179
+
180
+ it("should return initial value for empty stream", async () => {
181
+ const emptyStream = createEmptyStream<number>();
182
+ const emptyResult = await StreamUtil.reduce<number, string>(
183
+ emptyStream,
184
+ (acc, cur) => acc + cur.toString(),
185
+ { initial: "initial value" },
186
+ );
187
+
188
+ expect(emptyResult).toBe("initial value");
189
+ });
190
+
191
+ it("should work with async generated stream", async () => {
192
+ const stringStream = await createDelayedNumberStream(1, 3, 10);
193
+ const stringResult = await StreamUtil.reduce<number, string>(
194
+ stringStream,
195
+ (acc, cur) => acc + cur.toString(),
196
+ { initial: "" },
197
+ );
198
+
199
+ expect(stringResult).toBe("123");
200
+ });
201
+
202
+ it("should work with async stream without initial value", async () => {
203
+ const noInitialStream = await createDelayedNumberStream(1, 4, 15);
204
+ const noInitialResult = await StreamUtil.reduce<number>(
205
+ noInitialStream,
206
+ (acc, cur) => acc + cur,
207
+ { initial: 0 },
208
+ );
209
+
210
+ expect(noInitialResult).toBe(10);
211
+ });
212
+
213
+ it("should work with async stream transformation and aggregation into array", async () => {
214
+ const transformStream = await createDelayedNumberStream(1, 3, 10);
215
+ const transformResult = await StreamUtil.reduce<number, string[]>(
216
+ transformStream,
217
+ (acc: string[] | number, cur: number): string[] => {
218
+ if (typeof acc === "number") {
219
+ // Handle case when no initial value is provided
220
+ return [`item${acc}`, `item${cur}`];
221
+ }
222
+ return [...acc, `item${cur}`];
223
+ },
224
+ { initial: [] },
225
+ );
226
+
227
+ expect(transformResult).toEqual(["item1", "item2", "item3"]);
228
+ });
229
+
230
+ it("should return initial value for async generated empty stream", async () => {
231
+ const emptyStream = await createEmptyDelayedStream<number>(30);
232
+ const emptyResult = await StreamUtil.reduce<number, string>(
233
+ emptyStream,
234
+ (acc, cur) => acc + cur.toString(),
235
+ { initial: "initial" },
236
+ );
237
+
238
+ expect(emptyResult).toBe("initial");
239
+ });
240
+
241
+ it("should work with stream with values created with various async delays", async () => {
242
+ const delayStream = await createVariableDelayedNumberStream([
243
+ { value: 1, delay: 20 },
244
+ { value: 2, delay: 40 },
245
+ { value: 3, delay: 10 },
246
+ ]);
247
+
248
+ const delayResult = await StreamUtil.reduce<number, number>(
249
+ delayStream,
250
+ (acc, cur) => acc + cur,
251
+ { initial: 0 },
252
+ );
253
+
254
+ expect(delayResult).toBe(6);
255
+ });
256
+
257
+ it("should handle error in reducer function", async () => {
258
+ const stream = createNumberStream(1, 3);
259
+
260
+ await expect(
261
+ StreamUtil.reduce<number, number>(
262
+ stream,
263
+ (acc, cur) => {
264
+ if (cur === 2) {
265
+ throw new Error("Test error");
266
+ }
267
+ return acc + cur;
268
+ },
269
+ { initial: 0 },
270
+ ),
271
+ ).rejects.toThrow("Test error");
272
+ });
273
+
274
+ it("should handle null or undefined values in stream", async () => {
275
+ const stream = createTestStream<number | null | undefined>([1, null, 3, undefined, 5]);
276
+
277
+ const result = await StreamUtil.reduce<number | null | undefined, number>(
278
+ stream,
279
+ (acc, cur) => (acc ?? 0) + (cur ?? 0),
280
+ { initial: 0 },
281
+ );
282
+
283
+ expect(result).toBe(9); // 1 + 0 + 3 + 0 + 5 = 9
284
+ });
285
+ });
286
+
287
+ describe("from", () => {
288
+ it("should create a stream with a single value", async () => {
289
+ const stream = StreamUtil.from("Hello, world!");
290
+ const reader = stream.getReader();
291
+ const { done, value } = await reader.read();
292
+
293
+ expect(done).toBe(false);
294
+ expect(value).toBe("Hello, world!");
295
+
296
+ const next = await reader.read();
297
+ expect(next.done).toBe(true);
298
+ });
299
+
300
+ it("should handle null or undefined values", async () => {
301
+ const stream = StreamUtil.from(null);
302
+ const reader = stream.getReader();
303
+ const { done, value } = await reader.read();
304
+
305
+ expect(done).toBe(false);
306
+ expect(value).toBeNull();
307
+
308
+ const next = await reader.read();
309
+ expect(next.done).toBe(true);
310
+ });
311
+ });
312
+
313
+ describe("transform", () => {
314
+ it("should transform number stream by doubling", async () => {
315
+ const numbersInput = [1, 2, 3, 4, 5];
316
+ const numberStream = createTestStream(numbersInput);
317
+
318
+ const doubledStream = StreamUtil.transform(
319
+ numberStream,
320
+ (num: number) => num * 2,
321
+ );
322
+
323
+ const doubledResult = await streamToArray(doubledStream);
324
+ const expectedDoubled = numbersInput.map(n => n * 2);
325
+
326
+ expect(doubledResult).toEqual(expectedDoubled);
327
+ });
328
+
329
+ it("should transform object stream", async () => {
330
+ const objectsInput = [
331
+ { name: "item1", value: 10 },
332
+ { name: "item2", value: 20 },
333
+ { name: "item3", value: 30 },
334
+ ];
335
+
336
+ const objectStream = createTestStream(objectsInput);
337
+
338
+ const transformedObjectStream = StreamUtil.transform(objectStream, obj => ({
339
+ id: obj.name.toUpperCase(),
340
+ doubledValue: obj.value * 2,
341
+ }));
342
+
343
+ const transformedObjects = await streamToArray(transformedObjectStream);
344
+ const expectedObjects = objectsInput.map(obj => ({
345
+ id: obj.name.toUpperCase(),
346
+ doubledValue: obj.value * 2,
347
+ }));
348
+
349
+ expect(transformedObjects).toEqual(expectedObjects);
350
+ });
351
+
352
+ it("should handle empty stream", async () => {
353
+ const emptyStream = createEmptyStream<number>();
354
+ const transformedEmptyStream = StreamUtil.transform(
355
+ emptyStream,
356
+ n => n * 2,
357
+ );
358
+
359
+ const emptyResult = await streamToArray(transformedEmptyStream);
360
+
361
+ expect(emptyResult).toEqual([]);
362
+ });
363
+
364
+ it("should transform type (number -> string)", async () => {
365
+ const numbersInput = [1, 2, 3, 4, 5];
366
+ const numberStream = createTestStream(numbersInput);
367
+
368
+ const stringStream = StreamUtil.transform(
369
+ numberStream,
370
+ num => `Number: ${num}`,
371
+ );
372
+
373
+ const stringResult = await streamToArray(stringStream);
374
+ const expectedStrings = numbersInput.map(n => `Number: ${n}`);
375
+
376
+ expect(stringResult).toEqual(expectedStrings);
377
+ });
378
+
379
+ it("should handle error in transformer function", async () => {
380
+ const stream = createNumberStream(1, 3);
381
+
382
+ const transformedStream = StreamUtil.transform(
383
+ stream,
384
+ (num) => {
385
+ if (num === 2) {
386
+ throw new Error("Transform error");
387
+ }
388
+ return num * 2;
389
+ },
390
+ );
391
+
392
+ await expect(streamToArray(transformedStream)).rejects.toThrow("Transform error");
393
+ });
394
+
395
+ it("should handle null or undefined values in stream", async () => {
396
+ const stream = createTestStream<number | null | undefined>([1, null, 3, undefined, 5]);
397
+
398
+ const transformedStream = StreamUtil.transform(
399
+ stream,
400
+ num => (num ?? 0) * 2,
401
+ );
402
+
403
+ const result = await streamToArray(transformedStream);
404
+ expect(result).toEqual([2, 0, 6, 0, 10]);
405
+ });
406
+ });
407
+
408
+ describe("toAsyncGenerator", () => {
409
+ it("should yield a single value", async () => {
410
+ const generator = toAsyncGenerator("test value");
411
+ const result = [];
412
+
413
+ for await (const value of generator) {
414
+ result.push(value);
415
+ }
416
+
417
+ expect(result).toEqual(["test value"]);
418
+ });
419
+
420
+ it("should handle null or undefined values", async () => {
421
+ const generator = toAsyncGenerator(null);
422
+ const result = [];
423
+
424
+ for await (const value of generator) {
425
+ result.push(value);
426
+ }
427
+
428
+ expect(result).toEqual([null]);
429
+ });
430
+
431
+ it("should handle object values", async () => {
432
+ const testObj = { id: 1, name: "test" };
433
+ const generator = toAsyncGenerator(testObj);
434
+ const result = [];
435
+
436
+ for await (const value of generator) {
437
+ result.push(value);
438
+ }
439
+
440
+ expect(result).toEqual([testObj]);
441
+ });
442
+ });
443
+
444
+ describe("streamDefaultReaderToAsyncGenerator", () => {
445
+ it("should convert stream reader to async generator", async () => {
446
+ const stream = createNumberStream(1, 5);
447
+ const reader = stream.getReader();
448
+ const generator = streamDefaultReaderToAsyncGenerator(reader);
449
+ const result = [];
450
+
451
+ for await (const value of generator) {
452
+ result.push(value);
453
+ }
454
+
455
+ expect(result).toEqual([1, 2, 3, 4, 5]);
456
+ });
457
+
458
+ it("should handle empty stream", async () => {
459
+ const stream = createEmptyStream<number>();
460
+ const reader = stream.getReader();
461
+ const generator = streamDefaultReaderToAsyncGenerator(reader);
462
+ const result = [];
463
+
464
+ for await (const value of generator) {
465
+ result.push(value);
466
+ }
467
+
468
+ expect(result).toEqual([]);
469
+ });
470
+
471
+ it("should handle null or undefined values in stream", async () => {
472
+ const stream = createTestStream<number | null | undefined>([1, null, 3, undefined, 5]);
473
+ const reader = stream.getReader();
474
+ const generator = streamDefaultReaderToAsyncGenerator(reader);
475
+ const result = [];
476
+
477
+ for await (const value of generator) {
478
+ result.push(value);
479
+ }
480
+
481
+ expect(result).toEqual([1, null, 3, undefined, 5]);
482
+ });
483
+
484
+ it("should handle error in stream", async () => {
485
+ const stream = new ReadableStream<number>({
486
+ start(controller) {
487
+ controller.enqueue(1);
488
+ controller.enqueue(2);
489
+ delay(1000).then(() => controller.error(new Error("Stream error"))).catch(() => {});
490
+ },
491
+ });
492
+
493
+ const reader = stream.getReader();
494
+ const generator = streamDefaultReaderToAsyncGenerator(reader);
495
+ const result = [];
496
+
497
+ try {
498
+ for await (const value of generator) {
499
+ result.push(value);
500
+ }
501
+ // Should not reach here
502
+ expect(true).toBe(false);
503
+ }
504
+ catch (error) {
505
+ console.error(error);
506
+ expect(error).toBeInstanceOf(Error);
507
+ expect((error as Error).message).toBe("Stream error");
508
+ expect(result).toEqual([1, 2]);
509
+ }
510
+ });
511
+ });
512
+ });