vitest-pool-assemblyscript 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/BINARYEN_VERSION +1 -0
  2. package/LICENSE +53 -0
  3. package/README.md +607 -0
  4. package/assembly/compare.ts +219 -0
  5. package/assembly/describe.ts +104 -0
  6. package/assembly/expect.ts +335 -0
  7. package/assembly/index.ts +14 -0
  8. package/assembly/options.ts +198 -0
  9. package/assembly/test.ts +147 -0
  10. package/assembly/tsconfig.json +6 -0
  11. package/binding.gyp +62 -0
  12. package/dist/ast-visitor-DC3SuTzs.mjs +310 -0
  13. package/dist/ast-visitor-DC3SuTzs.mjs.map +1 -0
  14. package/dist/compile-runner-8h0dBwG2.mjs +80 -0
  15. package/dist/compile-runner-8h0dBwG2.mjs.map +1 -0
  16. package/dist/compiler/transforms/strip-inline.d.mts +18 -0
  17. package/dist/compiler/transforms/strip-inline.d.mts.map +1 -0
  18. package/dist/compiler/transforms/strip-inline.mjs +38 -0
  19. package/dist/compiler/transforms/strip-inline.mjs.map +1 -0
  20. package/dist/compiler-CN6BRK_N.mjs +295 -0
  21. package/dist/compiler-CN6BRK_N.mjs.map +1 -0
  22. package/dist/config/index-v3.d.mts +111 -0
  23. package/dist/config/index-v3.d.mts.map +1 -0
  24. package/dist/config/index-v3.mjs +11 -0
  25. package/dist/config/index-v3.mjs.map +1 -0
  26. package/dist/config/index.d.mts +4 -0
  27. package/dist/config/index.mjs +8 -0
  28. package/dist/constants-CA50WBdr.mjs +130 -0
  29. package/dist/constants-CA50WBdr.mjs.map +1 -0
  30. package/dist/coverage-merge-0WqdC-dq.mjs +22 -0
  31. package/dist/coverage-merge-0WqdC-dq.mjs.map +1 -0
  32. package/dist/coverage-provider/index.d.mts +15 -0
  33. package/dist/coverage-provider/index.d.mts.map +1 -0
  34. package/dist/coverage-provider/index.mjs +535 -0
  35. package/dist/coverage-provider/index.mjs.map +1 -0
  36. package/dist/custom-provider-options-CF5C1kXb.d.mts +26 -0
  37. package/dist/custom-provider-options-CF5C1kXb.d.mts.map +1 -0
  38. package/dist/debug-IeEHsxy0.mjs +195 -0
  39. package/dist/debug-IeEHsxy0.mjs.map +1 -0
  40. package/dist/index-internal.d.mts +23 -0
  41. package/dist/index-internal.d.mts.map +1 -0
  42. package/dist/index-internal.mjs +4 -0
  43. package/dist/index-v3.d.mts +7 -0
  44. package/dist/index-v3.d.mts.map +1 -0
  45. package/dist/index-v3.mjs +206 -0
  46. package/dist/index-v3.mjs.map +1 -0
  47. package/dist/index.d.mts +3 -0
  48. package/dist/index.mjs +8 -0
  49. package/dist/load-user-imports-J9eaAW0_.mjs +801 -0
  50. package/dist/load-user-imports-J9eaAW0_.mjs.map +1 -0
  51. package/dist/pool-runner-init-CEwLyNI3.d.mts +8 -0
  52. package/dist/pool-runner-init-CEwLyNI3.d.mts.map +1 -0
  53. package/dist/pool-runner-init-d5qScS41.mjs +400 -0
  54. package/dist/pool-runner-init-d5qScS41.mjs.map +1 -0
  55. package/dist/pool-thread/compile-worker-thread.d.mts +7 -0
  56. package/dist/pool-thread/compile-worker-thread.d.mts.map +1 -0
  57. package/dist/pool-thread/compile-worker-thread.mjs +42 -0
  58. package/dist/pool-thread/compile-worker-thread.mjs.map +1 -0
  59. package/dist/pool-thread/test-worker-thread.d.mts +7 -0
  60. package/dist/pool-thread/test-worker-thread.d.mts.map +1 -0
  61. package/dist/pool-thread/test-worker-thread.mjs +39 -0
  62. package/dist/pool-thread/test-worker-thread.mjs.map +1 -0
  63. package/dist/pool-thread/v3-tinypool-thread.d.mts +7 -0
  64. package/dist/pool-thread/v3-tinypool-thread.d.mts.map +1 -0
  65. package/dist/pool-thread/v3-tinypool-thread.mjs +57 -0
  66. package/dist/pool-thread/v3-tinypool-thread.mjs.map +1 -0
  67. package/dist/resolve-config-as1w-Qyz.mjs +65 -0
  68. package/dist/resolve-config-as1w-Qyz.mjs.map +1 -0
  69. package/dist/test-runner-B2BpyPNK.mjs +142 -0
  70. package/dist/test-runner-B2BpyPNK.mjs.map +1 -0
  71. package/dist/types-8KKo9Hbf.d.mts +228 -0
  72. package/dist/types-8KKo9Hbf.d.mts.map +1 -0
  73. package/dist/vitest-file-tasks-BUwzh375.mjs +61 -0
  74. package/dist/vitest-file-tasks-BUwzh375.mjs.map +1 -0
  75. package/dist/vitest-tasks-BKS7689f.mjs +319 -0
  76. package/dist/vitest-tasks-BKS7689f.mjs.map +1 -0
  77. package/dist/worker-rpc-channel-lbhK7Qz8.mjs +25 -0
  78. package/dist/worker-rpc-channel-lbhK7Qz8.mjs.map +1 -0
  79. package/package.json +112 -0
  80. package/prebuilds/linux-x64/vitest-pool-assemblyscript.glibc.node +0 -0
  81. package/scripts/install.js +91 -0
  82. package/scripts/setup-binaryen.js +179 -0
  83. package/src/native-instrumentation/addon.cpp +788 -0
@@ -0,0 +1,22 @@
1
+ //#region src/coverage-provider/coverage-merge.ts
2
+ /**
3
+ * Merge incoming CoverageData into accumulated CoverageData
4
+ *
5
+ * Combines by filepath + position, summing hit counts.
6
+ * Mutates the accumulated object in place.
7
+ *
8
+ * @param accumulated - Accumulated coverage data (mutated)
9
+ * @param incoming - New coverage data to merge in
10
+ */
11
+ function mergeCoverageData(accumulated, incoming) {
12
+ for (const [filePath, positions] of Object.entries(incoming.hitCountsByFileAndPosition)) {
13
+ if (!accumulated.hitCountsByFileAndPosition[filePath]) accumulated.hitCountsByFileAndPosition[filePath] = {};
14
+ const accumulatedPositions = accumulated.hitCountsByFileAndPosition[filePath];
15
+ for (const [positionKey, hitCount] of Object.entries(positions)) if (accumulatedPositions[positionKey] !== void 0) accumulatedPositions[positionKey] += hitCount;
16
+ else accumulatedPositions[positionKey] = hitCount;
17
+ }
18
+ }
19
+
20
+ //#endregion
21
+ export { mergeCoverageData };
22
+ //# sourceMappingURL=coverage-merge-0WqdC-dq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage-merge-0WqdC-dq.mjs","names":[],"sources":["../src/coverage-provider/coverage-merge.ts"],"sourcesContent":["/**\n * Coverage Data Merge Utilities\n *\n * Functions for merging CoverageData objects\n */\n\nimport type { CoverageData } from '../types/types.js';\n\n/**\n * Merge incoming CoverageData into accumulated CoverageData\n *\n * Combines by filepath + position, summing hit counts.\n * Mutates the accumulated object in place.\n *\n * @param accumulated - Accumulated coverage data (mutated)\n * @param incoming - New coverage data to merge in\n */\nexport function mergeCoverageData(\n accumulated: CoverageData,\n incoming: CoverageData\n): void {\n for (const [filePath, positions] of Object.entries(incoming.hitCountsByFileAndPosition)) {\n // Ensure file exists in accumulated\n if (!accumulated.hitCountsByFileAndPosition[filePath]) {\n accumulated.hitCountsByFileAndPosition[filePath] = {};\n }\n\n const accumulatedPositions = accumulated.hitCountsByFileAndPosition[filePath];\n\n for (const [positionKey, hitCount] of Object.entries(positions)) {\n if (accumulatedPositions[positionKey] !== undefined) {\n // Position exists - sum hit counts\n accumulatedPositions[positionKey] += hitCount;\n } else {\n // New position - set hit count\n accumulatedPositions[positionKey] = hitCount;\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,kBACd,aACA,UACM;AACN,MAAK,MAAM,CAAC,UAAU,cAAc,OAAO,QAAQ,SAAS,2BAA2B,EAAE;AAEvF,MAAI,CAAC,YAAY,2BAA2B,UAC1C,aAAY,2BAA2B,YAAY,EAAE;EAGvD,MAAM,uBAAuB,YAAY,2BAA2B;AAEpE,OAAK,MAAM,CAAC,aAAa,aAAa,OAAO,QAAQ,UAAU,CAC7D,KAAI,qBAAqB,iBAAiB,OAExC,sBAAqB,gBAAgB;MAGrC,sBAAqB,eAAe"}
@@ -0,0 +1,15 @@
1
+ import { CoverageProviderModule } from "vitest/node";
2
+
3
+ //#region src/coverage-provider/index.d.ts
4
+ /**
5
+ * Hybrid Coverage Provider
6
+ *
7
+ * This provider handles both AssemblyScript and JavaScript and coverage
8
+ * - Converts AS coverage to Istanbul format
9
+ * - Delegates JS coverage to Vitest's v8 provider
10
+ * - Merges both into a unified coverage report
11
+ */
12
+ declare const hybridProviderModule: CoverageProviderModule;
13
+ //#endregion
14
+ export { hybridProviderModule as default };
15
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/coverage-provider/index.ts"],"mappings":";;;;;;;;;;;cAwBM,oBAAA,EAAsB,sBAAA"}
@@ -0,0 +1,535 @@
1
+ import { ASCommonFlags, ASNodeKind, COVERAGE_PAYLOAD_FORMATS, POOL_ERROR_NAMES } from "../constants-CA50WBdr.mjs";
2
+ import { getProjectSerializedOrGlobalConfig } from "../resolve-config-as1w-Qyz.mjs";
3
+ import { createPoolError, debug } from "../debug-IeEHsxy0.mjs";
4
+ import { mergeCoverageData } from "../coverage-merge-0WqdC-dq.mjs";
5
+ import { ASTVisitor } from "../ast-visitor-DC3SuTzs.mjs";
6
+ import { basename, parse, relative } from "node:path";
7
+ import { readFile } from "node:fs/promises";
8
+ import v8CoverageModule from "@vitest/coverage-v8";
9
+ import { createCoverageMap } from "istanbul-lib-coverage";
10
+ import { Parser } from "assemblyscript";
11
+ import { resolve as resolve$1 } from "path";
12
+ import TestExclude from "test-exclude";
13
+
14
+ //#region src/coverage-provider/containment-matcher.ts
15
+ /**
16
+ * Find the source function whose range contains the given position.
17
+ *
18
+ * For nested functions, uses "tightest fit" - returns the innermost function
19
+ * (the one with the largest start position among all containing functions).
20
+ *
21
+ * @param functionsByStartLine - Functions for a single file, indexed by start line
22
+ * @param line - Target line number (1-based)
23
+ * @param column - Target column number (1-based)
24
+ * @returns The containing function, or undefined if no match
25
+ */
26
+ function findFunctionContainingPosition(functionsByStartLine, line, column) {
27
+ let bestMatch;
28
+ let bestStartLine = -1;
29
+ let bestStartColumn = -1;
30
+ for (const [startLineStr, functions] of Object.entries(functionsByStartLine)) {
31
+ const startLine = Number(startLineStr);
32
+ if (startLine > line) continue;
33
+ for (const func of functions) {
34
+ const { range } = func;
35
+ if (!isPositionInRange(line, column, range)) continue;
36
+ if (startLine > bestStartLine || startLine === bestStartLine && range.startColumn > bestStartColumn) {
37
+ bestMatch = func;
38
+ bestStartLine = startLine;
39
+ bestStartColumn = range.startColumn;
40
+ }
41
+ }
42
+ }
43
+ return bestMatch;
44
+ }
45
+ /**
46
+ * Check if a position (line, column) falls within a source range.
47
+ *
48
+ * @param line - Target line number (1-based)
49
+ * @param column - Target column number (1-based)
50
+ * @param range - Source range to check against
51
+ * @returns true if position is within range (inclusive)
52
+ */
53
+ function isPositionInRange(line, column, range) {
54
+ if (line < range.startLine || line > range.endLine) return false;
55
+ if (line === range.startLine && column < range.startColumn) return false;
56
+ if (line === range.endLine && column > range.endColumn) return false;
57
+ return true;
58
+ }
59
+
60
+ //#endregion
61
+ //#region src/coverage-provider/istanbul-converter.ts
62
+ function istanbulDebug(...args) {}
63
+ /**
64
+ * Convert AssemblyScript coverage data to Istanbul format for a single file
65
+ *
66
+ * Algorithm (containment matching):
67
+ * 1. For each hit position in fileHitCountsByPosition:
68
+ * - Use containment matcher to find which source function contains this position
69
+ * - Record the hit count for that function
70
+ * 2. For each function in fileFunctionsByStartLine:
71
+ * - Add function mapping to fnMap
72
+ * - Add function hit count to f (from matched hits, or 0 if not hit)
73
+ * - Add corresponding statement mapping to statementMap
74
+ * - Add same hit count to s (statement coverage matches function coverage)
75
+ *
76
+ * @param fileFunctionsByStartLine - Functions for this file, keyed by start line (from AST parser)
77
+ * @param fileHitCountsByPosition - Hit counts for this file, keyed by position "line:column" (from accumulated coverage)
78
+ * @param absoluteFilePath - Absolute path to the source file (for Istanbul output)
79
+ * @returns Istanbul FileCoverage object
80
+ */
81
+ async function convertToIstanbulFormat(fileFunctionsByStartLine, fileHitCountsByPosition, absoluteFilePath) {
82
+ const startMatch = performance.now();
83
+ istanbulDebug(() => {
84
+ const sourceFunctionCount = Object.values(fileFunctionsByStartLine).reduce((sum, funcs) => sum + funcs.length, 0);
85
+ const uniqueHitPosCount = Object.keys(fileHitCountsByPosition).length;
86
+ return `[IstanbulConverter] Processing source file: "${absoluteFilePath}"\n[IstanbulConverter] Source: ${sourceFunctionCount} total functions, Coverage: ${uniqueHitPosCount} unique hit positions\n[IstanbulConverter] Sanity Check - AS File Coverage Estimate: ${sourceFunctionCount === 0 ? Infinity : (uniqueHitPosCount * 100 / sourceFunctionCount).toFixed(2)}%\n[IstanbulConverter] Containment matching functions: coverage hit positions to source functions by range`;
87
+ });
88
+ const hitCountsBySourceFunctionName = /* @__PURE__ */ new Map();
89
+ for (const [positionKey, hitCount] of Object.entries(fileHitCountsByPosition)) {
90
+ const parts = positionKey.split(":");
91
+ const lineStr = parts[0];
92
+ const columnStr = parts[1];
93
+ if (lineStr && columnStr) {
94
+ const containingFunction = findFunctionContainingPosition(fileFunctionsByStartLine, parseInt(lineStr, 10), parseInt(columnStr, 10));
95
+ if (containingFunction) {
96
+ const existingHits = hitCountsBySourceFunctionName.get(containingFunction);
97
+ const existingHitsCount = existingHits ?? 0;
98
+ const max = Math.max(existingHitsCount, hitCount);
99
+ hitCountsBySourceFunctionName.set(containingFunction, max);
100
+ if (existingHits !== void 0) istanbulDebug(`[IstanbulConverter] Position ${positionKey} → function "${containingFunction.shortName}" EXISTING HITS: ${existingHits} NEW COUNT: ${max}`);
101
+ else istanbulDebug(`[IstanbulConverter] Position ${positionKey} → function "${containingFunction.shortName}" (hits: ${hitCount})`);
102
+ } else istanbulDebug(`[IstanbulConverter] Position ${positionKey} has no containing function`);
103
+ }
104
+ }
105
+ const startConvert = performance.now();
106
+ istanbulDebug(`[IstanbulConverter] Matching Complete - Converting to Istanbul format`);
107
+ const fnMap = {};
108
+ const f = {};
109
+ const statementMap = {};
110
+ const s = {};
111
+ const branchMap = {};
112
+ const b = {};
113
+ let funcIdx = 0;
114
+ for (const functions of Object.values(fileFunctionsByStartLine)) for (const funcInfo of functions) {
115
+ const { range, shortName } = funcInfo;
116
+ if (range.startLine === 0) continue;
117
+ const hitCount = hitCountsBySourceFunctionName.get(funcInfo) ?? 0;
118
+ istanbulDebug(`[IstanbulConverter] Istanbul function index ${funcIdx}: "${shortName && shortName !== "" ? shortName : "<anonymous>"}" (source ${range.startLine}:${range.startColumn} - ${range.endLine}:${range.endColumn}), hits: ${hitCount}`);
119
+ const istanbulRange = {
120
+ start: {
121
+ line: range.startLine,
122
+ column: range.startColumn - 1
123
+ },
124
+ end: {
125
+ line: range.endLine,
126
+ column: range.endColumn - 1
127
+ }
128
+ };
129
+ const idxStr = funcIdx.toString();
130
+ fnMap[idxStr] = {
131
+ name: shortName,
132
+ decl: istanbulRange,
133
+ loc: istanbulRange,
134
+ line: range.startLine
135
+ };
136
+ f[idxStr] = hitCount;
137
+ statementMap[idxStr] = istanbulRange;
138
+ s[idxStr] = hitCount;
139
+ funcIdx++;
140
+ }
141
+ const done = performance.now();
142
+ const matchingMs = (startConvert - startMatch).toFixed(2);
143
+ const convertMs = (done - startConvert).toFixed(2);
144
+ const totalMs = (done - startMatch).toFixed(2);
145
+ istanbulDebug(`[IstanbulConverter] Coverage Coversion Complete: ${Object.keys(fnMap).length} functions, ${totalMs} ms total (matching: ${matchingMs} ms, convert: ${convertMs} ms)`);
146
+ return {
147
+ path: absoluteFilePath,
148
+ fnMap,
149
+ f,
150
+ statementMap,
151
+ s,
152
+ branchMap,
153
+ b
154
+ };
155
+ }
156
+
157
+ //#endregion
158
+ //#region src/coverage-provider/ast-parser.ts
159
+ /**
160
+ * AST Parser for AssemblyScript Source Files
161
+ *
162
+ * Parses AS source files to extract function metadata for coverage.
163
+ * Used by generateCoverage to build empty coverage map from all source files.
164
+ *
165
+ * Source AST is the source of truth for what SHOULD be covered.
166
+ * Binary instrumentation tells us what we CAN measure (hit counts).
167
+ *
168
+ * Functions are grouped by start line for efficient containment matching.
169
+ *
170
+ * Architecture:
171
+ * - Uses shared ASTVisitor for complete NodeKind coverage
172
+ * - Overrides hooks to extract function information during traversal
173
+ */
174
+ /**
175
+ * Visitor that extracts function information from AST nodes
176
+ */
177
+ var FunctionExtractorVisitor = class extends ASTVisitor {
178
+ /** Source file being parsed */
179
+ source;
180
+ /** Module path for building qualified names */
181
+ modulePath;
182
+ /** Absolute file path */
183
+ filePath;
184
+ /** Accumulated function records, keyed by start line */
185
+ functions = {};
186
+ /** Current class name (when inside a class) */
187
+ currentClassName = null;
188
+ constructor(source, modulePath, filePath) {
189
+ super();
190
+ this.source = source;
191
+ this.modulePath = modulePath;
192
+ this.filePath = filePath;
193
+ }
194
+ /**
195
+ * Track class context when entering a class
196
+ */
197
+ onClassEnter(node) {
198
+ this.currentClassName = node.name?.text ?? "Anonymous";
199
+ }
200
+ /**
201
+ * Restore class context when exiting a class
202
+ */
203
+ onClassExit(_node) {
204
+ this.currentClassName = null;
205
+ }
206
+ /**
207
+ * Extract function info from function declarations
208
+ */
209
+ onFunctionDeclaration(node) {
210
+ if (node.body && this.hasBodyStatements(node.body)) {
211
+ const shortName = node.name?.text ?? "~anonymous";
212
+ const qualifiedName = `${this.modulePath}/${shortName}`;
213
+ const range = this.buildRange(node, node.name ?? null);
214
+ this.addFunction(qualifiedName, shortName, range);
215
+ }
216
+ return true;
217
+ }
218
+ /**
219
+ * Extract function info from method declarations
220
+ */
221
+ onMethodDeclaration(node) {
222
+ if (node.body && this.hasBodyStatements(node.body)) {
223
+ const methodName = node.name?.text ?? "constructor";
224
+ const className = this.currentClassName ?? "Unknown";
225
+ const flags = node.flags;
226
+ const isStatic = (flags & ASCommonFlags.Static) !== 0;
227
+ const isGetter = (flags & ASCommonFlags.Get) !== 0;
228
+ const isSetter = (flags & ASCommonFlags.Set) !== 0;
229
+ let shortName;
230
+ if (isStatic) shortName = `${className}.${methodName}`;
231
+ else if (isGetter) shortName = `${className}#get:${methodName}`;
232
+ else if (isSetter) shortName = `${className}#set:${methodName}`;
233
+ else shortName = `${className}#${methodName}`;
234
+ const qualifiedName = `${this.modulePath}/${shortName}`;
235
+ const range = this.buildRange(node, node.name ?? null);
236
+ this.addFunction(qualifiedName, shortName, range);
237
+ }
238
+ return true;
239
+ }
240
+ /**
241
+ * Extract function info from variable declarations (arrow functions)
242
+ */
243
+ onVariableDeclaration(node) {
244
+ if (node.initializer && node.initializer.kind === ASNodeKind.Function) {
245
+ const funcDecl = node.initializer.declaration;
246
+ if (funcDecl.body && this.hasBodyStatements(funcDecl.body)) {
247
+ const shortName = node.name.text;
248
+ const qualifiedName = `${this.modulePath}/${shortName}`;
249
+ const range = {
250
+ filePath: this.filePath,
251
+ startLine: this.source.lineAt(node.range.start),
252
+ startColumn: this.source.columnAt(),
253
+ endLine: this.source.lineAt(node.range.end),
254
+ endColumn: this.source.columnAt()
255
+ };
256
+ this.addFunction(qualifiedName, shortName, range);
257
+ }
258
+ if (funcDecl.body) this.visitNode(funcDecl.body);
259
+ return false;
260
+ }
261
+ return true;
262
+ }
263
+ /**
264
+ * Check if a function body has statements (non-empty body)
265
+ */
266
+ hasBodyStatements(body) {
267
+ if (body.kind === ASNodeKind.Block) return body.statements.length > 0;
268
+ return true;
269
+ }
270
+ /**
271
+ * Add a function to the functions record, keyed by start line
272
+ */
273
+ addFunction(qualifiedName, shortName, range) {
274
+ const startLine = range.startLine;
275
+ if (!this.functions[startLine]) this.functions[startLine] = [];
276
+ this.functions[startLine].push({
277
+ qualifiedName,
278
+ shortName,
279
+ range
280
+ });
281
+ }
282
+ /**
283
+ * Build a SourceRange for a node, using name.range.start to skip decorators
284
+ */
285
+ buildRange(node, nameNode) {
286
+ const startNode = nameNode ?? node;
287
+ return {
288
+ filePath: this.filePath,
289
+ startLine: this.source.lineAt(startNode.range.start),
290
+ startColumn: this.source.columnAt(),
291
+ endLine: this.source.lineAt(node.range.end),
292
+ endColumn: this.source.columnAt()
293
+ };
294
+ }
295
+ };
296
+ /**
297
+ * Parse functions from a single AS source file
298
+ *
299
+ * @param absoluteSourceFilePath - Absolute path to AS source file
300
+ * @param relativeSourceFilePath - Relative path to AS source file (derived once in caller and used several places)
301
+ * @returns Record of start line to array of ParsedSourceFunctionInfo (multiple functions can start on same line)
302
+ */
303
+ async function parseFunctionsFromFile(absoluteSourceFilePath, relativeSourceFilePath) {
304
+ const sourceCode = await readFile(absoluteSourceFilePath, "utf8");
305
+ const parsed = parse(relativeSourceFilePath);
306
+ const modulePath = parsed.dir ? `${parsed.dir}/${parsed.name}` : parsed.name;
307
+ const asParser = new Parser();
308
+ asParser.parseFile(sourceCode, relativeSourceFilePath, true);
309
+ const source = asParser.currentSource;
310
+ if (!source) return {};
311
+ const visitor = new FunctionExtractorVisitor(source, modulePath, absoluteSourceFilePath);
312
+ visitor.visitSource(source);
313
+ return visitor.functions || {};
314
+ }
315
+
316
+ //#endregion
317
+ //#region src/coverage-provider/glob-utils.ts
318
+ /**
319
+ * Glob Utilities for Coverage
320
+ *
321
+ * Uses test-exclude (same as Vitest's v8 coverage provider) to glob
322
+ * AssemblyScript files matching coverage.include patterns.
323
+ */
324
+ /**
325
+ * Glob files matching coverage include/exclude patterns
326
+ *
327
+ * Uses test-exclude for consistent behavior with Vitest's built-in
328
+ * coverage providers.
329
+ *
330
+ * @param include - Include patterns (e.g., ['assembly/**\/*.ts'])
331
+ * @param exclude - Exclude patterns (e.g., ['**\/*.test.ts'])
332
+ * @param projectRoot - Project root directory
333
+ * @returns Array of absolute file paths
334
+ */
335
+ function globFiles(include, exclude, projectRoot) {
336
+ if (include.length === 0) return [];
337
+ return new TestExclude({
338
+ cwd: projectRoot,
339
+ include,
340
+ exclude,
341
+ excludeNodeModules: true
342
+ }).globSync(projectRoot).map((file) => ({
343
+ absolute: resolve$1(projectRoot, file),
344
+ projectRootRelative: file
345
+ })) || [];
346
+ }
347
+
348
+ //#endregion
349
+ //#region src/coverage-provider/hybrid-coverage-provider.ts
350
+ /**
351
+ * Hybrid Coverage Provider
352
+ *
353
+ * This provider handles both AssemblyScript and JavaScript and coverage
354
+ * - Converts AS coverage to Istanbul format
355
+ * - Delegates JS coverage to Vitest's v8 provider
356
+ * - Merges both into a unified coverage report
357
+ */
358
+ var HybridCoverageProvider = class {
359
+ name = "hybrid-assemblyscript-v8";
360
+ v8Provider;
361
+ accumulatedCoverageData = { hitCountsByFileAndPosition: {} };
362
+ projectConfig = {};
363
+ coverageOptions = {};
364
+ /**
365
+ * Initialize the provider and get reference to v8 provider
366
+ */
367
+ async initialize(ctx) {
368
+ this.projectConfig = getProjectSerializedOrGlobalConfig(ctx).config;
369
+ debug("[HybridCoverageProvider] Initializing Provider");
370
+ this.v8Provider = await v8CoverageModule.getProvider();
371
+ if (!this.v8Provider) throw createPoolError("HybridCoverageProvider - initialize failed to get delegated v8 provider", POOL_ERROR_NAMES.HybridCoverageProviderError);
372
+ await this.v8Provider.initialize(ctx);
373
+ this.v8Provider.name = "hybrid-assemblyscript-v8 (delegated v8 reporter)";
374
+ debug("[HybridCoverageProvider] Initialized with delegated v8 provider");
375
+ }
376
+ /**
377
+ * Handle suite completion - delegate based on coverage format marker
378
+ */
379
+ async onAfterSuiteRun(meta) {
380
+ const start = performance.now();
381
+ const format = (meta?.coverage)?.__format;
382
+ let suiteLogLabel = meta.testFiles.length > 0 ? basename(meta.testFiles[0]) : "";
383
+ if (format === COVERAGE_PAYLOAD_FORMATS.AssemblyScript) {
384
+ const { coverageData, suiteLogLabel: label } = meta.coverage;
385
+ suiteLogLabel = label;
386
+ debug(() => {
387
+ const fileCount = Object.keys(coverageData.hitCountsByFileAndPosition).length;
388
+ const positionCount = Object.values(coverageData.hitCountsByFileAndPosition).reduce((sum, positions) => sum + Object.keys(positions).length, 0);
389
+ return `[HybridCoverageProvider] ${suiteLogLabel} - onAfterSuiteRun - Suite payload: ${positionCount} unique positions over ${fileCount} source files`;
390
+ });
391
+ mergeCoverageData(this.accumulatedCoverageData, coverageData);
392
+ debug(() => {
393
+ const fileCount = Object.keys(this.accumulatedCoverageData.hitCountsByFileAndPosition).length;
394
+ const positionCount = Object.values(this.accumulatedCoverageData.hitCountsByFileAndPosition).reduce((sum, positions) => sum + Object.keys(positions).length, 0);
395
+ return `[HybridCoverageProvider] ${suiteLogLabel} - onAfterSuiteRun - Accumulated coverage: ${positionCount} unique positions over ${fileCount} source files`;
396
+ });
397
+ } else {
398
+ if (!this.v8Provider) throw createPoolError("HybridCoverageProvider - onAfterSuiteRun failed to delegate to v8 provider", POOL_ERROR_NAMES.HybridCoverageProviderError);
399
+ debug(`[HybridCoverageProvider] ${suiteLogLabel} - Delegating to v8 provider`);
400
+ await this.v8Provider.onAfterSuiteRun(meta);
401
+ }
402
+ debug(() => {
403
+ const files = meta.testFiles.map((tf) => relative(this.projectConfig.root, tf)).join(",");
404
+ return `[HybridCoverageProvider] ${suiteLogLabel} - onAfterSuiteRun complete - TIMING ${(performance.now() - start).toFixed(2)} ms | testFiles: "${files}"`;
405
+ });
406
+ }
407
+ /**
408
+ * Generate unified coverage map (merging JS and AS coverage)
409
+ *
410
+ * Flow:
411
+ * 1. Parse included AS source files to get sourceDebugInfo (source of truth for line numbers)
412
+ * 1. Build merged CoverageData (all source functions + accumulated hit counts)
413
+ * 4. Convert merged CoverageData to Istanbul format
414
+ * 5. Get JS coverage from v8 provider
415
+ * 6. Merge AS coverage into JS coverage
416
+ */
417
+ async generateCoverage(context) {
418
+ const start = performance.now();
419
+ debug("[HybridCoverageProvider] Generating coverage for test run");
420
+ if (!this.v8Provider) throw createPoolError("HybridCoverageProvider - generateCoverage failed to delegate to v8 provider", POOL_ERROR_NAMES.HybridCoverageProviderError);
421
+ let asCoverageMap = createCoverageMap();
422
+ if (this.coverageOptions.globbedAssemblyScriptInclude?.length > 0) {
423
+ debug(`[HybridCoverageProvider] Building AS coverage map with ${this.coverageOptions.globbedAssemblyScriptInclude.length} source files `);
424
+ debug(() => {
425
+ return `[HybridCoverageProvider] Accumulated coverage data has ${Object.values(this.accumulatedCoverageData.hitCountsByFileAndPosition).reduce((sum, positions) => sum + Object.keys(positions)?.length, 0)} unique positions hit across ${Object.keys(this.accumulatedCoverageData.hitCountsByFileAndPosition).length} debug source files`;
426
+ });
427
+ const fileProcessingPromises = this.coverageOptions.globbedAssemblyScriptInclude.map(async (include) => {
428
+ debug(`[HybridCoverageProvider] Parsing AS source for expected coverage: "${include.absolute}" (file key: "${include.projectRootRelative}")`);
429
+ const functionsByStartLine = await parseFunctionsFromFile(include.absolute, include.projectRootRelative) || {};
430
+ debug(`[HybridCoverageProvider] Parsed ${Object.keys(functionsByStartLine).length} AS source functions in "${include.projectRootRelative}"`);
431
+ const fileHitCountsByPosition = this.accumulatedCoverageData.hitCountsByFileAndPosition[include.projectRootRelative] ?? {};
432
+ debug(`[HybridCoverageProvider] Accumulated AS coverage has ${Object.keys(fileHitCountsByPosition).length} positions for "${include.projectRootRelative}"`);
433
+ return convertToIstanbulFormat(functionsByStartLine, fileHitCountsByPosition, include.absolute);
434
+ });
435
+ const istanbulResults = await Promise.all(fileProcessingPromises);
436
+ for (const istanbulData of istanbulResults) asCoverageMap.addFileCoverage(istanbulData);
437
+ debug(`[HybridCoverageProvider] Built AS coverage map with ${Object.keys(asCoverageMap.data).length} files`);
438
+ } else debug("[HybridCoverageProvider] WARNING: No assemblyScriptInclude patterns yieldled files - Coverage Map will be empty!");
439
+ const asGenerateEnd = performance.now();
440
+ debug(`[HybridCoverageProvider] TIMING AS generateCoverage: ${(asGenerateEnd - start).toFixed(2)} ms`);
441
+ debug("[HybridCoverageProvider] Getting JS coverage from v8 provider");
442
+ const jsCoverage = await this.v8Provider.generateCoverage(context);
443
+ debug(`[HybridCoverageProvider] JS coverage has ${Object.keys(jsCoverage.data).length} files`);
444
+ debug(`[HybridCoverageProvider] TIMING JS generateCoverage: ${(performance.now() - asGenerateEnd).toFixed(2)} ms`);
445
+ debug("[HybridCoverageProvider] Merging AS coverage into JS coverage");
446
+ jsCoverage.merge(asCoverageMap);
447
+ debug(`[HybridCoverageProvider] Final merged coverage has ${Object.keys(jsCoverage.data).length} files`);
448
+ debug(`[HybridCoverageProvider] TIMING Total generateCoverage: ${(performance.now() - start).toFixed(2)} ms`);
449
+ return jsCoverage;
450
+ }
451
+ /**
452
+ * Report coverage - delegate to v8 provider
453
+ */
454
+ async reportCoverage(coverageMap, context) {
455
+ if (!this.v8Provider) throw createPoolError("HybridCoverageProvider - reportCoverage failed to delegate to v8 provider", POOL_ERROR_NAMES.HybridCoverageProviderError);
456
+ debug(`[HybridCoverageProvider] Reporting coverage (allTestsRun=${context.allTestsRun})`);
457
+ await this.v8Provider.reportCoverage(coverageMap, context);
458
+ }
459
+ /**
460
+ * Resolve options
461
+ */
462
+ resolveOptions() {
463
+ if (!this.v8Provider) throw createPoolError("HybridCoverageProvider - resolveOptions failed to delegate to v8 provider", POOL_ERROR_NAMES.HybridCoverageProviderError);
464
+ debug(`[HybridCoverageProvider] Resolving Coverage Options`);
465
+ const definedCoverageOptions = this.projectConfig.coverage;
466
+ const resolvedV8Options = this.v8Provider.resolveOptions();
467
+ const sanitizedV8Options = {
468
+ ...resolvedV8Options,
469
+ include: resolvedV8Options.include?.map((i) => i.replace(/\0/g, "")) || void 0,
470
+ exclude: resolvedV8Options.exclude?.map((i) => i.replace(/\0/g, "")) || void 0
471
+ };
472
+ debug(`[HybridCoverageProvider] AS include: ${(definedCoverageOptions.assemblyScriptInclude || []).join(", ") || "(none)"}`);
473
+ debug(`[HybridCoverageProvider] AS exclude: ${(definedCoverageOptions.assemblyScriptExclude || []).join(", ") || "(none)"}`);
474
+ debug(`[HybridCoverageProvider] JS include: ${(sanitizedV8Options.include || []).join(", ") || "(none)"}`);
475
+ debug(`[HybridCoverageProvider] JS exclude: ${(sanitizedV8Options.exclude || []).join(", ") || "(none)"}`);
476
+ debug(`[HybridCoverageProvider] Globbing AS source files to include for coverage map basis`);
477
+ const globbedAssemblyScriptInclude = globFiles(definedCoverageOptions.assemblyScriptInclude || [], definedCoverageOptions.assemblyScriptExclude || [], this.projectConfig.root);
478
+ debug(`[HybridCoverageProvider] Including ${globbedAssemblyScriptInclude.length} AS files in coverage map`);
479
+ const globbedAssemblyScriptExcludeOnly = globFiles(definedCoverageOptions.assemblyScriptExclude || [], [], this.projectConfig.root);
480
+ debug(`[HybridCoverageProvider] Excluding ${globbedAssemblyScriptExcludeOnly.length} AS files from coverage map & instrumentation`);
481
+ const resolvedCoverageOptions = {
482
+ ...resolvedV8Options,
483
+ provider: "custom",
484
+ customProviderModule: definedCoverageOptions.customProviderModule,
485
+ assemblyScriptInclude: definedCoverageOptions.assemblyScriptInclude ?? [],
486
+ assemblyScriptExclude: definedCoverageOptions.assemblyScriptExclude ?? [],
487
+ globbedAssemblyScriptInclude,
488
+ globbedAssemblyScriptProjectRelativeExcludeOnly: globbedAssemblyScriptExcludeOnly.map((gr) => gr.projectRootRelative)
489
+ };
490
+ this.coverageOptions = resolvedCoverageOptions;
491
+ return resolvedCoverageOptions;
492
+ }
493
+ async clean(clean = true) {
494
+ debug("[HybridCoverageProvider] Clean coverage data - clean:", clean);
495
+ if (clean) {
496
+ this.accumulatedCoverageData = { hitCountsByFileAndPosition: {} };
497
+ debug("[HybridCoverageProvider] Cleaned all internal coverage data");
498
+ }
499
+ if (this.v8Provider) {
500
+ await this.v8Provider.clean(clean);
501
+ debug(`[HybridCoverageProvider] V8 provider finished clean(${clean})`);
502
+ }
503
+ }
504
+ };
505
+
506
+ //#endregion
507
+ //#region src/coverage-provider/index.ts
508
+ /**
509
+ * Hybrid Coverage Provider
510
+ *
511
+ * This provider handles both AssemblyScript and JavaScript and coverage
512
+ * - Converts AS coverage to Istanbul format
513
+ * - Delegates JS coverage to Vitest's v8 provider
514
+ * - Merges both into a unified coverage report
515
+ */
516
+ const hybridProviderModule = {
517
+ getProvider: () => new HybridCoverageProvider(),
518
+ startCoverage: async (runtimeOptions) => {
519
+ if (v8CoverageModule.startCoverage) return await v8CoverageModule.startCoverage(runtimeOptions);
520
+ else throw createPoolError("HybridCoverageProvider - v8 coverage module does not provide `startCoverage`", POOL_ERROR_NAMES.HybridCoverageProviderError);
521
+ },
522
+ takeCoverage: async (runtimeOptions) => {
523
+ if (v8CoverageModule.takeCoverage) return await v8CoverageModule.takeCoverage(runtimeOptions);
524
+ else throw createPoolError("HybridCoverageProvider - v8 coverage module does not provide `takeCoverage`", POOL_ERROR_NAMES.HybridCoverageProviderError);
525
+ },
526
+ stopCoverage: async (runtimeOptions) => {
527
+ if (v8CoverageModule.stopCoverage) return await v8CoverageModule.stopCoverage(runtimeOptions);
528
+ else throw createPoolError("HybridCoverageProvider - v8 coverage module does not provide `stopCoverage`", POOL_ERROR_NAMES.HybridCoverageProviderError);
529
+ }
530
+ };
531
+ var coverage_provider_default = hybridProviderModule;
532
+
533
+ //#endregion
534
+ export { coverage_provider_default as default };
535
+ //# sourceMappingURL=index.mjs.map