@elench/testkit 0.1.82 → 0.1.84

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 (100) hide show
  1. package/README.md +37 -7
  2. package/lib/cli/agents/index.mjs +64 -0
  3. package/lib/cli/agents/investigate.mjs +75 -0
  4. package/lib/cli/agents/investigation-context.mjs +102 -0
  5. package/lib/cli/agents/prompt-builder.mjs +25 -0
  6. package/lib/cli/agents/providers/claude.mjs +74 -0
  7. package/lib/cli/agents/providers/codex.mjs +83 -0
  8. package/lib/cli/agents/providers/shared.mjs +134 -0
  9. package/lib/cli/command-helpers.mjs +53 -25
  10. package/lib/cli/commands/investigate.mjs +87 -0
  11. package/lib/cli/entrypoint.mjs +3 -0
  12. package/lib/cli/presentation/colors.mjs +12 -0
  13. package/lib/cli/presentation/events-reporter.mjs +135 -0
  14. package/lib/cli/presentation/summary-box.mjs +11 -11
  15. package/lib/cli/presentation/tree-reporter.mjs +159 -0
  16. package/lib/cli/tui/run-app.mjs +1 -0
  17. package/lib/cli/tui/run-session-app.mjs +370 -0
  18. package/lib/cli/tui/run-session-state.mjs +481 -0
  19. package/lib/cli/tui/run-tree-state.mjs +1 -0
  20. package/lib/config-api/auth-fixtures.mjs +15 -10
  21. package/lib/discovery/index.mjs +1 -1
  22. package/lib/index.d.ts +5 -1
  23. package/lib/runner/orchestrator.mjs +1 -0
  24. package/lib/runtime/index.d.ts +138 -5
  25. package/lib/runtime/index.mjs +68 -2
  26. package/lib/runtime-src/k6/http-assertions.js +31 -1
  27. package/lib/runtime-src/k6/http-checks.js +120 -0
  28. package/lib/runtime-src/k6/http-suite-runtime.js +5 -1
  29. package/lib/runtime-src/k6/http.js +213 -23
  30. package/lib/runtime-src/shared/error-body.mjs +42 -0
  31. package/lib/runtime-src/shared/http-parsing.mjs +68 -0
  32. package/node_modules/@elench/next-analysis/package.json +1 -1
  33. package/node_modules/@elench/testkit-bridge/package.json +2 -2
  34. package/node_modules/@elench/testkit-protocol/package.json +1 -1
  35. package/node_modules/@elench/ts-analysis/package.json +1 -1
  36. package/package.json +7 -6
  37. package/lib/app/configs.test.mjs +0 -34
  38. package/lib/app/typecheck.test.mjs +0 -24
  39. package/lib/bundler/index.test.mjs +0 -164
  40. package/lib/cli/args.test.mjs +0 -110
  41. package/lib/cli/presentation/code-frames.test.mjs +0 -71
  42. package/lib/cli/presentation/run-reporter.test.mjs +0 -192
  43. package/lib/cli/presentation/summary-box.test.mjs +0 -43
  44. package/lib/cli/presentation/terminal-layout.test.mjs +0 -23
  45. package/lib/config/database.test.mjs +0 -29
  46. package/lib/config/discovery.test.mjs +0 -276
  47. package/lib/config/env.test.mjs +0 -40
  48. package/lib/config/index.test.mjs +0 -44
  49. package/lib/config/paths.test.mjs +0 -27
  50. package/lib/config/runtime.test.mjs +0 -82
  51. package/lib/config/skip-config.test.mjs +0 -63
  52. package/lib/config-api/index.test.mjs +0 -344
  53. package/lib/config-api/next-runtime-tsconfig.test.mjs +0 -58
  54. package/lib/coverage/backend-discovery.test.mjs +0 -61
  55. package/lib/coverage/evidence.test.mjs +0 -87
  56. package/lib/coverage/index.test.mjs +0 -715
  57. package/lib/coverage/routing.test.mjs +0 -36
  58. package/lib/coverage/shared.test.mjs +0 -72
  59. package/lib/database/fingerprint.test.mjs +0 -99
  60. package/lib/database/index.test.mjs +0 -95
  61. package/lib/database/naming.test.mjs +0 -39
  62. package/lib/database/state.test.mjs +0 -66
  63. package/lib/database/template-steps.test.mjs +0 -43
  64. package/lib/discovery/file-metadata.test.mjs +0 -51
  65. package/lib/discovery/index.test.mjs +0 -182
  66. package/lib/discovery/path-policy.test.mjs +0 -65
  67. package/lib/drizzle/index.test.mjs +0 -33
  68. package/lib/env/index.test.mjs +0 -82
  69. package/lib/history/index.test.mjs +0 -115
  70. package/lib/package.test.mjs +0 -59
  71. package/lib/playwright/index.test.mjs +0 -43
  72. package/lib/regressions/github.test.mjs +0 -324
  73. package/lib/regressions/index.test.mjs +0 -187
  74. package/lib/reporters/playwright.test.mjs +0 -167
  75. package/lib/runner/default-runtime-errors.test.mjs +0 -49
  76. package/lib/runner/execution-config.test.mjs +0 -67
  77. package/lib/runner/failure-details.test.mjs +0 -114
  78. package/lib/runner/formatting.test.mjs +0 -205
  79. package/lib/runner/metadata.test.mjs +0 -52
  80. package/lib/runner/planning.test.mjs +0 -371
  81. package/lib/runner/playwright-config.test.mjs +0 -78
  82. package/lib/runner/processes.test.mjs +0 -21
  83. package/lib/runner/regressions.test.mjs +0 -168
  84. package/lib/runner/reporting.test.mjs +0 -310
  85. package/lib/runner/results.test.mjs +0 -376
  86. package/lib/runner/runtime-manager.test.mjs +0 -252
  87. package/lib/runner/runtime-preparation.test.mjs +0 -141
  88. package/lib/runner/selection.test.mjs +0 -24
  89. package/lib/runner/setup-operations.test.mjs +0 -94
  90. package/lib/runner/state.test.mjs +0 -62
  91. package/lib/runner/suite-selection.test.mjs +0 -49
  92. package/lib/runner/template.test.mjs +0 -272
  93. package/lib/shared/build-config.test.mjs +0 -132
  94. package/lib/shared/configured-steps.test.mjs +0 -102
  95. package/lib/shared/execution-schema.test.mjs +0 -26
  96. package/lib/shared/file-timeout.test.mjs +0 -64
  97. package/lib/shared/test-context.test.mjs +0 -43
  98. package/lib/timing/index.test.mjs +0 -64
  99. package/lib/toolchains/index.test.mjs +0 -168
  100. package/lib/vitest/index.test.mjs +0 -20
@@ -36,13 +36,21 @@ export function createHttpClient(config) {
36
36
  throw new Error("baseUrl is required");
37
37
  }
38
38
 
39
- function buildHeaders(builder, context, extraHeaders = {}) {
40
- return {
41
- ...defaultHeaders,
39
+ function buildHeaders(builder, context, extraHeaders = {}, options = {}) {
40
+ const { includeDefaultHeaders = true, contentTypeJson = true } = options;
41
+ const resolvedHeaders = {
42
+ ...(includeDefaultHeaders ? defaultHeaders : {}),
42
43
  ...safeHeaders(builder, context),
43
44
  ...routeHeaders,
44
45
  ...extraHeaders,
45
46
  };
47
+
48
+ if (contentTypeJson === false) {
49
+ delete resolvedHeaders["Content-Type"];
50
+ delete resolvedHeaders["content-type"];
51
+ }
52
+
53
+ return resolvedHeaders;
46
54
  }
47
55
 
48
56
  function buildHeaderContext(actorName = null) {
@@ -60,23 +68,75 @@ export function createHttpClient(config) {
60
68
  }
61
69
  }
62
70
 
63
- function requestAs(actorName, method, path, body, extraHeaders = {}) {
71
+ function resolvedHeadersFor(actorName, extraHeaders = {}) {
72
+ ensureKnownActor(actorName);
73
+ const headerContext = buildHeaderContext(actorName);
74
+ return buildHeaders(getHeaders, headerContext, extraHeaders);
75
+ }
76
+
77
+ function resolvedRawHeadersFor(actorName = null, extraHeaders = {}) {
78
+ ensureKnownActor(actorName);
79
+ const headerContext = buildHeaderContext(actorName);
80
+ return buildHeaders(getRawHeaders, headerContext, extraHeaders);
81
+ }
82
+
83
+ function requestAs(actorName, method, path, body, extraHeaders = {}, options = {}) {
64
84
  ensureKnownActor(actorName);
65
85
  const url = `${baseUrl}${path}`;
66
86
  const headerContext = buildHeaderContext(actorName);
67
- const headers = buildHeaders(getHeaders, headerContext, extraHeaders);
68
- return runHttpRequest(method, path, url, body, headers, headerContext.actor);
87
+ const headers = buildHeaders(getHeaders, headerContext, extraHeaders, {
88
+ contentTypeJson: options.contentTypeJson !== false,
89
+ includeDefaultHeaders: options.includeDefaultHeaders !== false,
90
+ });
91
+ return runHttpRequest(method, path, url, body, headers, headerContext.actor, options);
69
92
  }
70
93
 
71
- function raw(method, path, body, extraHeaders = {}) {
94
+ function rawAs(actorName, method, path, body, extraHeaders = {}, options = {}) {
95
+ ensureKnownActor(actorName);
72
96
  const url = `${baseUrl}${path}`;
73
- const headerContext = buildHeaderContext(null);
74
- const headers = buildHeaders(getRawHeaders, headerContext, extraHeaders);
75
- return runHttpRequest(method, path, url, body, headers, null);
97
+ const headerContext = buildHeaderContext(actorName);
98
+ const headers = buildHeaders(getRawHeaders, headerContext, extraHeaders, {
99
+ contentTypeJson: options.contentTypeJson !== false,
100
+ includeDefaultHeaders: options.includeDefaultHeaders !== false,
101
+ });
102
+ return runHttpRequest(method, path, url, body, headers, headerContext.actor, options);
103
+ }
104
+
105
+ function raw(method, path, body, extraHeaders = {}) {
106
+ return rawAs(null, method, path, body, extraHeaders);
107
+ }
108
+
109
+ function requestMultipart(actorName, method, path, payload, extraHeaders = {}, options = {}) {
110
+ const body = buildMultipartBody(payload);
111
+ return requestAs(actorName, method, path, body, extraHeaders, {
112
+ ...options,
113
+ contentTypeJson: false,
114
+ includeDefaultHeaders: false,
115
+ serialize: "raw",
116
+ });
117
+ }
118
+
119
+ function rawMultipart(actorName, method, path, payload, extraHeaders = {}, options = {}) {
120
+ const body = buildMultipartBody(payload);
121
+ return rawAs(actorName, method, path, body, extraHeaders, {
122
+ ...options,
123
+ contentTypeJson: false,
124
+ includeDefaultHeaders: false,
125
+ serialize: "raw",
126
+ });
76
127
  }
77
128
 
78
129
  function createActorClient(actorName) {
130
+ const actorRawReq = createRawInvoker(actorName);
131
+
79
132
  return {
133
+ headers(extraHeaders = {}) {
134
+ return resolvedHeadersFor(actorName, extraHeaders);
135
+ },
136
+ rawHeaders(extraHeaders = {}) {
137
+ return resolvedRawHeadersFor(actorName, extraHeaders);
138
+ },
139
+ rawReq: actorRawReq,
80
140
  delete(path, extraHeaders = {}) {
81
141
  return requestAs(actorName, "DELETE", path, null, extraHeaders);
82
142
  },
@@ -95,19 +155,98 @@ export function createHttpClient(config) {
95
155
  request(method, path, body, extraHeaders = {}) {
96
156
  return requestAs(actorName, method, path, body, extraHeaders);
97
157
  },
158
+ raw(method, path, body, extraHeaders = {}) {
159
+ return actorRawReq(method, path, body, extraHeaders);
160
+ },
161
+ rawDelete(path, extraHeaders = {}) {
162
+ return actorRawReq.delete(path, extraHeaders);
163
+ },
164
+ rawGet(path, extraHeaders = {}) {
165
+ return actorRawReq.get(path, extraHeaders);
166
+ },
167
+ rawPatch(path, body, extraHeaders = {}) {
168
+ return actorRawReq.patch(path, body, extraHeaders);
169
+ },
170
+ rawPost(path, body, extraHeaders = {}) {
171
+ return actorRawReq.post(path, body, extraHeaders);
172
+ },
173
+ rawPut(path, body, extraHeaders = {}) {
174
+ return actorRawReq.put(path, body, extraHeaders);
175
+ },
176
+ multipart: {
177
+ post(path, payload, extraHeaders = {}) {
178
+ return requestMultipart(actorName, "POST", path, payload, extraHeaders);
179
+ },
180
+ put(path, payload, extraHeaders = {}) {
181
+ return requestMultipart(actorName, "PUT", path, payload, extraHeaders);
182
+ },
183
+ patch(path, payload, extraHeaders = {}) {
184
+ return requestMultipart(actorName, "PATCH", path, payload, extraHeaders);
185
+ },
186
+ },
187
+ rawMultipart: {
188
+ post(path, payload, extraHeaders = {}) {
189
+ return actorRawReq.multipart.post(path, payload, extraHeaders);
190
+ },
191
+ put(path, payload, extraHeaders = {}) {
192
+ return actorRawReq.multipart.put(path, payload, extraHeaders);
193
+ },
194
+ patch(path, payload, extraHeaders = {}) {
195
+ return actorRawReq.multipart.patch(path, payload, extraHeaders);
196
+ },
197
+ },
198
+ };
199
+ }
200
+
201
+ function createRawInvoker(defaultRawActor = null) {
202
+ const invoker = (method, path, body, extraHeaders = {}) =>
203
+ rawAs(defaultRawActor, method, path, body, extraHeaders);
204
+
205
+ invoker.request = (method, path, body, extraHeaders = {}) =>
206
+ rawAs(defaultRawActor, method, path, body, extraHeaders);
207
+ invoker.get = (path, extraHeaders = {}) => rawAs(defaultRawActor, "GET", path, null, extraHeaders);
208
+ invoker.post = (path, body, extraHeaders = {}) => rawAs(defaultRawActor, "POST", path, body, extraHeaders);
209
+ invoker.put = (path, body, extraHeaders = {}) => rawAs(defaultRawActor, "PUT", path, body, extraHeaders);
210
+ invoker.patch = (path, body, extraHeaders = {}) => rawAs(defaultRawActor, "PATCH", path, body, extraHeaders);
211
+ invoker.delete = (path, extraHeaders = {}) =>
212
+ rawAs(defaultRawActor, "DELETE", path, null, extraHeaders);
213
+ invoker.headers = (extraHeaders = {}) => resolvedRawHeadersFor(defaultRawActor, extraHeaders);
214
+ invoker.as = (actorName) => {
215
+ ensureKnownActor(actorName);
216
+ return createRawInvoker(actorName);
217
+ };
218
+ invoker.multipart = {
219
+ post(path, payload, extraHeaders = {}) {
220
+ return rawMultipart(defaultRawActor, "POST", path, payload, extraHeaders);
221
+ },
222
+ put(path, payload, extraHeaders = {}) {
223
+ return rawMultipart(defaultRawActor, "PUT", path, payload, extraHeaders);
224
+ },
225
+ patch(path, payload, extraHeaders = {}) {
226
+ return rawMultipart(defaultRawActor, "PATCH", path, payload, extraHeaders);
227
+ },
98
228
  };
229
+
230
+ return invoker;
99
231
  }
100
232
 
101
233
  const defaultClient = createActorClient(defaultActor);
234
+ const rawClient = createRawInvoker(null);
102
235
 
103
236
  return {
104
237
  rawHttp: http,
238
+ headers(extraHeaders = {}) {
239
+ return resolvedHeadersFor(defaultActor, extraHeaders);
240
+ },
241
+ rawHeaders(actorName = null, extraHeaders = {}) {
242
+ return resolvedRawHeadersFor(actorName, extraHeaders);
243
+ },
105
244
  as(actorName) {
106
245
  ensureKnownActor(actorName);
107
246
  return createActorClient(actorName);
108
247
  },
109
248
  request: defaultClient.request,
110
- raw,
249
+ raw: rawClient,
111
250
  get(path, extraHeaders = {}) {
112
251
  return defaultClient.get(path, extraHeaders);
113
252
  },
@@ -124,19 +263,41 @@ export function createHttpClient(config) {
124
263
  return defaultClient.delete(path, extraHeaders);
125
264
  },
126
265
  rawGet(path, extraHeaders = {}) {
127
- return raw("GET", path, null, extraHeaders);
266
+ return rawClient.get(path, extraHeaders);
128
267
  },
129
268
  rawPost(path, body, extraHeaders = {}) {
130
- return raw("POST", path, body, extraHeaders);
269
+ return rawClient.post(path, body, extraHeaders);
131
270
  },
132
271
  rawPut(path, body, extraHeaders = {}) {
133
- return raw("PUT", path, body, extraHeaders);
272
+ return rawClient.put(path, body, extraHeaders);
134
273
  },
135
274
  rawPatch(path, body, extraHeaders = {}) {
136
- return raw("PATCH", path, body, extraHeaders);
275
+ return rawClient.patch(path, body, extraHeaders);
137
276
  },
138
277
  rawDelete(path, extraHeaders = {}) {
139
- return raw("DELETE", path, null, extraHeaders);
278
+ return rawClient.delete(path, extraHeaders);
279
+ },
280
+ multipart: {
281
+ post(path, payload, extraHeaders = {}) {
282
+ return requestMultipart(defaultActor, "POST", path, payload, extraHeaders);
283
+ },
284
+ put(path, payload, extraHeaders = {}) {
285
+ return requestMultipart(defaultActor, "PUT", path, payload, extraHeaders);
286
+ },
287
+ patch(path, payload, extraHeaders = {}) {
288
+ return requestMultipart(defaultActor, "PATCH", path, payload, extraHeaders);
289
+ },
290
+ },
291
+ rawMultipart: {
292
+ post(path, payload, extraHeaders = {}) {
293
+ return rawClient.multipart.post(path, payload, extraHeaders);
294
+ },
295
+ put(path, payload, extraHeaders = {}) {
296
+ return rawClient.multipart.put(path, payload, extraHeaders);
297
+ },
298
+ patch(path, payload, extraHeaders = {}) {
299
+ return rawClient.multipart.patch(path, payload, extraHeaders);
300
+ },
140
301
  },
141
302
  };
142
303
  }
@@ -159,7 +320,7 @@ export function makeRawReq(baseUrl, routeHeaders = {}, getRawHeaders = null) {
159
320
  }).raw;
160
321
  }
161
322
 
162
- function runHttpRequest(method, path, url, body, headers, actorRecord = null) {
323
+ function runHttpRequest(method, path, url, body, headers, actorRecord = null, requestOptions = {}) {
163
324
  const ordinal = nextTraceOrdinal();
164
325
  const requestId = buildRequestId(ordinal);
165
326
  const finalHeaders = {
@@ -175,14 +336,15 @@ function runHttpRequest(method, path, url, body, headers, actorRecord = null) {
175
336
  actorRecord,
176
337
  requestHeaders: finalHeaders,
177
338
  });
178
- const options = { headers: finalHeaders };
339
+ const transportOptions = { headers: finalHeaders };
179
340
 
180
341
  let rawResponse;
181
- if (method === "GET") rawResponse = http.get(url, options);
182
- else if (method === "PUT") rawResponse = http.put(url, JSON.stringify(body), options);
183
- else if (method === "POST") rawResponse = http.post(url, JSON.stringify(body), options);
184
- else if (method === "PATCH") rawResponse = http.patch(url, JSON.stringify(body), options);
185
- else if (method === "DELETE") rawResponse = http.del(url, null, options);
342
+ const requestBody = serializeRequestBody(body, requestOptions.serialize || "json");
343
+ if (method === "GET") rawResponse = http.get(url, transportOptions);
344
+ else if (method === "PUT") rawResponse = http.put(url, requestBody, transportOptions);
345
+ else if (method === "POST") rawResponse = http.post(url, requestBody, transportOptions);
346
+ else if (method === "PATCH") rawResponse = http.patch(url, requestBody, transportOptions);
347
+ else if (method === "DELETE") rawResponse = http.del(url, null, transportOptions);
186
348
  else throw new Error(`unsupported method: ${method}`);
187
349
 
188
350
  finalizeTrace(trace, rawResponse);
@@ -420,3 +582,31 @@ function decodeQueryComponent(value) {
420
582
  return String(value || "");
421
583
  }
422
584
  }
585
+
586
+ function serializeRequestBody(body, mode = "json") {
587
+ if (body == null) return body;
588
+ if (mode === "raw") return body;
589
+ if (typeof body === "string") return body;
590
+ return JSON.stringify(body);
591
+ }
592
+
593
+ function buildMultipartBody(payload = {}) {
594
+ const fields = payload?.fields || {};
595
+ const files = Array.isArray(payload?.files) ? payload.files : [];
596
+ const body = { ...fields };
597
+
598
+ for (const fileEntry of files) {
599
+ if (!fileEntry || typeof fileEntry !== "object") {
600
+ throw new Error("multipart file entries must be objects");
601
+ }
602
+
603
+ const field = String(fileEntry.field || "").trim();
604
+ if (!field) {
605
+ throw new Error("multipart file entries require a field name");
606
+ }
607
+
608
+ body[field] = http.file(fileEntry.data, fileEntry.filename, fileEntry.contentType);
609
+ }
610
+
611
+ return body;
612
+ }
@@ -0,0 +1,42 @@
1
+ export function extractErrorMessageFromBody(body) {
2
+ if (!body || typeof body !== "object") return null;
3
+
4
+ if ("error" in body) {
5
+ const error = body.error;
6
+ if (typeof error === "string" && error.length > 0) return error;
7
+ if (error && typeof error === "object" && "message" in error) {
8
+ const message = error.message;
9
+ if (typeof message === "string" && message.length > 0) return message;
10
+ }
11
+ }
12
+
13
+ if ("message" in body) {
14
+ const message = body.message;
15
+ if (typeof message === "string" && message.length > 0) return message;
16
+ }
17
+
18
+ return null;
19
+ }
20
+
21
+ export function hasStandardErrorShape(body) {
22
+ if (!body || typeof body !== "object") return false;
23
+ const error = body.error;
24
+ if (!error || typeof error !== "object") return false;
25
+ return typeof error.code === "string" && typeof error.message === "string";
26
+ }
27
+
28
+ export function deriveDeterministicIp(seed, offset = 0) {
29
+ const hash = numericSeed(`${seed}:${offset}`);
30
+ const thirdOctet = (hash % 250) + 1;
31
+ const fourthOctet = (Math.floor(hash / 250) % 250) + 1;
32
+ return `198.51.${thirdOctet}.${fourthOctet}`;
33
+ }
34
+
35
+ function numericSeed(seed) {
36
+ let hash = 0;
37
+ const text = String(seed);
38
+ for (let index = 0; index < text.length; index += 1) {
39
+ hash = (hash * 31 + text.charCodeAt(index)) >>> 0;
40
+ }
41
+ return hash;
42
+ }
@@ -0,0 +1,68 @@
1
+ export function parseJsonBody(response) {
2
+ return JSON.parse(response.body);
3
+ }
4
+
5
+ export function extractCookie(response, cookieName) {
6
+ const cookies = Object.entries(response?.headers || {})
7
+ .filter(([name]) => String(name).toLowerCase() === "set-cookie")
8
+ .flatMap(([, value]) => (Array.isArray(value) ? value : value ? [value] : []));
9
+
10
+ if (cookies.length === 0) return null;
11
+
12
+ const cookiePattern = new RegExp(`(?:^|,\\s*)${escapeRegExp(cookieName)}=([^;,]+)`);
13
+ for (const headerValue of cookies) {
14
+ const match = cookiePattern.exec(String(headerValue));
15
+ if (match?.[1]) {
16
+ return match[1];
17
+ }
18
+ }
19
+
20
+ return null;
21
+ }
22
+
23
+ export function parseSseEvents(body) {
24
+ return String(body || "")
25
+ .split(/\n\n+/)
26
+ .map((block) => block.trim())
27
+ .filter(Boolean)
28
+ .map((block) => {
29
+ const lines = block.split("\n");
30
+ let event = null;
31
+ const dataLines = [];
32
+
33
+ for (const line of lines) {
34
+ if (line.startsWith("event:")) {
35
+ event = line.slice("event:".length).trim() || null;
36
+ }
37
+ if (line.startsWith("data:")) {
38
+ dataLines.push(line.slice("data:".length).trim());
39
+ }
40
+ }
41
+
42
+ const rawData = dataLines.join("\n");
43
+ if (!rawData) {
44
+ return { event, data: null };
45
+ }
46
+
47
+ try {
48
+ return {
49
+ event,
50
+ data: JSON.parse(rawData),
51
+ };
52
+ } catch {
53
+ return {
54
+ event,
55
+ data: rawData,
56
+ };
57
+ }
58
+ });
59
+ }
60
+
61
+ export function getSseEventData(body, eventName) {
62
+ const match = parseSseEvents(body).find((event) => event.event === eventName);
63
+ return match?.data ?? null;
64
+ }
65
+
66
+ function escapeRegExp(value) {
67
+ return String(value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
68
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/next-analysis",
3
- "version": "0.1.82",
3
+ "version": "0.1.84",
4
4
  "description": "SWC-backed Next.js source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-bridge",
3
- "version": "0.1.82",
3
+ "version": "0.1.84",
4
4
  "description": "Browser bridge helpers for testkit",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,7 @@
22
22
  "typecheck": "tsc -p tsconfig.json --noEmit"
23
23
  },
24
24
  "dependencies": {
25
- "@elench/testkit-protocol": "0.1.82"
25
+ "@elench/testkit-protocol": "0.1.84"
26
26
  },
27
27
  "private": false
28
28
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-protocol",
3
- "version": "0.1.82",
3
+ "version": "0.1.84",
4
4
  "description": "Shared browser protocol for testkit bridge and extension consumers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/ts-analysis",
3
- "version": "0.1.82",
3
+ "version": "0.1.84",
4
4
  "description": "TypeScript compiler-backed source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit",
3
- "version": "0.1.82",
3
+ "version": "0.1.84",
4
4
  "description": "CLI for discovering and running local HTTP, DAL, and Playwright test suites",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -66,7 +66,8 @@
66
66
  "files": [
67
67
  "bin/",
68
68
  "lib/",
69
- "vendor/"
69
+ "vendor/",
70
+ "!lib/**/*.test.*"
70
71
  ],
71
72
  "bundleDependencies": [
72
73
  "@elench/next-analysis",
@@ -81,10 +82,10 @@
81
82
  },
82
83
  "dependencies": {
83
84
  "@babel/code-frame": "^7.29.0",
84
- "@elench/next-analysis": "0.1.82",
85
- "@elench/testkit-bridge": "0.1.82",
86
- "@elench/testkit-protocol": "0.1.82",
87
- "@elench/ts-analysis": "0.1.82",
85
+ "@elench/next-analysis": "0.1.84",
86
+ "@elench/testkit-bridge": "0.1.84",
87
+ "@elench/testkit-protocol": "0.1.84",
88
+ "@elench/ts-analysis": "0.1.84",
88
89
  "@oclif/core": "^4.10.6",
89
90
  "esbuild": "^0.25.11",
90
91
  "execa": "^9.5.0",
@@ -1,34 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- collectRequiredConfigs,
4
- filterConfigsByService,
5
- resolveTargetConfig,
6
- topologicallySortConfigs,
7
- } from "./configs.mjs";
8
-
9
- function makeConfig(name, dependsOn = [], provider = null) {
10
- return {
11
- name,
12
- testkit: {
13
- dependsOn,
14
- database: provider ? { provider } : undefined,
15
- },
16
- };
17
- }
18
-
19
- describe("app config helpers", () => {
20
- it("filters and resolves target configs", () => {
21
- const configs = [makeConfig("api"), makeConfig("frontend", ["api"], "local")];
22
-
23
- expect(filterConfigsByService(configs, "api")).toEqual([configs[0]]);
24
- expect(resolveTargetConfig(configs)).toBe(configs[1]);
25
- expect(() => filterConfigsByService(configs, "missing")).toThrow('Service "missing" not found');
26
- });
27
-
28
- it("collects dependencies and sorts them topologically", () => {
29
- const configs = [makeConfig("frontend", ["api"]), makeConfig("api"), makeConfig("worker", ["api"])];
30
-
31
- expect(collectRequiredConfigs(configs, "frontend").map((config) => config.name).sort()).toEqual(["api", "frontend"]);
32
- expect(topologicallySortConfigs(configs).map((config) => config.name)).toEqual(["api", "frontend", "worker"]);
33
- });
34
- });
@@ -1,24 +0,0 @@
1
- import path from "path";
2
- import { describe, expect, it } from "vitest";
3
- import { buildTypecheckCompilerOptions } from "./typecheck.mjs";
4
-
5
- describe("typecheck app helpers", () => {
6
- it("preserves inherited service alias paths when generating next-service configs", () => {
7
- const fixtureRoot = "/home/georgedlr/workspace/elench/bourne";
8
- const extendsPath = path.join(fixtureRoot, "frontend", "tsconfig.json");
9
- const generatedTsconfig = path.join(fixtureRoot, ".testkit", "_typecheck", "frontend.tsconfig.json");
10
-
11
- const compilerOptions = buildTypecheckCompilerOptions({
12
- tsconfigPath: generatedTsconfig,
13
- extendsPath,
14
- });
15
-
16
- expect(compilerOptions.paths?.["@/*"]).toEqual(["../../frontend/src/*"]);
17
- expect(compilerOptions.paths?.["@playwright-fixtures/*"]).toEqual([
18
- "../../frontend/tests/playwright-fixtures/*",
19
- ]);
20
- expect(compilerOptions.paths?.["@elench/testkit/config"]?.[0]).toMatch(
21
- /(?:\.\.\/)+(?:node_modules\/@elench\/testkit|testkit)\/lib\/config-api\/index\.d\.ts$/
22
- );
23
- });
24
- });