@heal-dev/heal-playwright-tracer 1.0.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 (125) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +245 -0
  3. package/dist/application/babel-playwright-tracer-plugin/index.d.ts +16 -0
  4. package/dist/application/babel-playwright-tracer-plugin/index.js +111 -0
  5. package/dist/application/heal-config/index.d.ts +8 -0
  6. package/dist/application/heal-config/index.js +22 -0
  7. package/dist/application/heal-config/registry.d.ts +37 -0
  8. package/dist/application/heal-config/registry.js +64 -0
  9. package/dist/application/heal-config/types.d.ts +73 -0
  10. package/dist/application/heal-config/types.js +7 -0
  11. package/dist/application/playwright-fixture/index.d.ts +14 -0
  12. package/dist/application/playwright-fixture/index.js +234 -0
  13. package/dist/application/trace-event-recorder-runtime/index.d.ts +15 -0
  14. package/dist/application/trace-event-recorder-runtime/index.js +68 -0
  15. package/dist/domain/code-hook-injector/service/meta-fields/enclosing-scope-labeler.d.ts +12 -0
  16. package/dist/domain/code-hook-injector/service/meta-fields/enclosing-scope-labeler.js +53 -0
  17. package/dist/domain/code-hook-injector/service/meta-fields/leading-comment-extractor.d.ts +14 -0
  18. package/dist/domain/code-hook-injector/service/meta-fields/leading-comment-extractor.js +20 -0
  19. package/dist/domain/code-hook-injector/service/meta-fields/relative-file-path.d.ts +6 -0
  20. package/dist/domain/code-hook-injector/service/meta-fields/relative-file-path.js +57 -0
  21. package/dist/domain/code-hook-injector/service/meta-fields/source-snippet-extractor.d.ts +12 -0
  22. package/dist/domain/code-hook-injector/service/meta-fields/source-snippet-extractor.js +28 -0
  23. package/dist/domain/code-hook-injector/service/playwright-import-rewriter.d.ts +10 -0
  24. package/dist/domain/code-hook-injector/service/playwright-import-rewriter.js +21 -0
  25. package/dist/domain/code-hook-injector/service/statement-analysis/cjs-artifact-detector.d.ts +14 -0
  26. package/dist/domain/code-hook-injector/service/statement-analysis/cjs-artifact-detector.js +30 -0
  27. package/dist/domain/code-hook-injector/service/statement-analysis/for-head-declaration-detector.d.ts +10 -0
  28. package/dist/domain/code-hook-injector/service/statement-analysis/for-head-declaration-detector.js +18 -0
  29. package/dist/domain/code-hook-injector/service/statement-analysis/leaf-statement-classifier.d.ts +15 -0
  30. package/dist/domain/code-hook-injector/service/statement-analysis/leaf-statement-classifier.js +66 -0
  31. package/dist/domain/code-hook-injector/service/statement-analysis/non-wrappable-statement.d.ts +11 -0
  32. package/dist/domain/code-hook-injector/service/statement-analysis/non-wrappable-statement.js +20 -0
  33. package/dist/domain/code-hook-injector/service/trace-hook/enter-meta-literal.d.ts +20 -0
  34. package/dist/domain/code-hook-injector/service/trace-hook/enter-meta-literal.js +34 -0
  35. package/dist/domain/code-hook-injector/service/trace-hook/global-trace-call.d.ts +10 -0
  36. package/dist/domain/code-hook-injector/service/trace-hook/global-trace-call.js +16 -0
  37. package/dist/domain/code-hook-injector/service/trace-hook/try-finally-wrapper.d.ts +18 -0
  38. package/dist/domain/code-hook-injector/service/trace-hook/try-finally-wrapper.js +44 -0
  39. package/dist/domain/code-hook-injector/service/trace-hook/variable-declaration-hoister.d.ts +20 -0
  40. package/dist/domain/code-hook-injector/service/trace-hook/variable-declaration-hoister.js +27 -0
  41. package/dist/domain/code-hook-injector/service/traced-file-matcher.d.ts +10 -0
  42. package/dist/domain/code-hook-injector/service/traced-file-matcher.js +26 -0
  43. package/dist/domain/trace-event-recorder/model/enter-meta.d.ts +24 -0
  44. package/dist/domain/trace-event-recorder/model/enter-meta.js +7 -0
  45. package/dist/domain/trace-event-recorder/model/global-names.d.ts +8 -0
  46. package/dist/domain/trace-event-recorder/model/global-names.js +20 -0
  47. package/dist/domain/trace-event-recorder/model/serialized-error.d.ts +16 -0
  48. package/dist/domain/trace-event-recorder/model/serialized-error.js +7 -0
  49. package/dist/domain/trace-event-recorder/model/statement-trace-schema.d.ts +171 -0
  50. package/dist/domain/trace-event-recorder/model/statement-trace-schema.js +33 -0
  51. package/dist/domain/trace-event-recorder/model/trace-schema.d.ts +114 -0
  52. package/dist/domain/trace-event-recorder/model/trace-schema.js +16 -0
  53. package/dist/domain/trace-event-recorder/port/clock.d.ts +9 -0
  54. package/dist/domain/trace-event-recorder/port/clock.js +7 -0
  55. package/dist/domain/trace-event-recorder/port/heal-trace-exporter.d.ts +11 -0
  56. package/dist/domain/trace-event-recorder/port/heal-trace-exporter.js +7 -0
  57. package/dist/domain/trace-event-recorder/port/system-info-provider.d.ts +18 -0
  58. package/dist/domain/trace-event-recorder/port/system-info-provider.js +7 -0
  59. package/dist/domain/trace-event-recorder/port/trace-event-consumer.d.ts +11 -0
  60. package/dist/domain/trace-event-recorder/port/trace-event-consumer.js +7 -0
  61. package/dist/domain/trace-event-recorder/service/active-enter-stack.d.ts +15 -0
  62. package/dist/domain/trace-event-recorder/service/active-enter-stack.js +34 -0
  63. package/dist/domain/trace-event-recorder/service/event-builders/enter-event-builder.d.ts +8 -0
  64. package/dist/domain/trace-event-recorder/service/event-builders/enter-event-builder.js +37 -0
  65. package/dist/domain/trace-event-recorder/service/event-builders/meta-event-builder.d.ts +7 -0
  66. package/dist/domain/trace-event-recorder/service/event-builders/meta-event-builder.js +19 -0
  67. package/dist/domain/trace-event-recorder/service/event-builders/ok-event-builder.d.ts +7 -0
  68. package/dist/domain/trace-event-recorder/service/event-builders/ok-event-builder.js +27 -0
  69. package/dist/domain/trace-event-recorder/service/event-builders/throw-event-builder.d.ts +7 -0
  70. package/dist/domain/trace-event-recorder/service/event-builders/throw-event-builder.js +23 -0
  71. package/dist/domain/trace-event-recorder/service/exporters/composite-heal-trace-exporter.d.ts +12 -0
  72. package/dist/domain/trace-event-recorder/service/exporters/composite-heal-trace-exporter.js +32 -0
  73. package/dist/domain/trace-event-recorder/service/index.d.ts +10 -0
  74. package/dist/domain/trace-event-recorder/service/index.js +15 -0
  75. package/dist/domain/trace-event-recorder/service/projectors/index.d.ts +6 -0
  76. package/dist/domain/trace-event-recorder/service/projectors/index.js +10 -0
  77. package/dist/domain/trace-event-recorder/service/projectors/statement-projector.d.ts +26 -0
  78. package/dist/domain/trace-event-recorder/service/projectors/statement-projector.js +183 -0
  79. package/dist/domain/trace-event-recorder/service/serializers/error-serializer.d.ts +8 -0
  80. package/dist/domain/trace-event-recorder/service/serializers/error-serializer.js +49 -0
  81. package/dist/domain/trace-event-recorder/service/serializers/variable-snapshot-serializer.d.ts +7 -0
  82. package/dist/domain/trace-event-recorder/service/serializers/variable-snapshot-serializer.js +102 -0
  83. package/dist/domain/trace-event-recorder/service/trace-event-recorder-state.d.ts +19 -0
  84. package/dist/domain/trace-event-recorder/service/trace-event-recorder-state.js +7 -0
  85. package/dist/domain/trace-event-recorder/service/trace-event-recorder.d.ts +56 -0
  86. package/dist/domain/trace-event-recorder/service/trace-event-recorder.js +80 -0
  87. package/dist/index.d.ts +11 -0
  88. package/dist/index.js +43 -0
  89. package/dist/infrastructure/ndjson-exporter-adapter/index.d.ts +6 -0
  90. package/dist/infrastructure/ndjson-exporter-adapter/index.js +10 -0
  91. package/dist/infrastructure/ndjson-exporter-adapter/ndjson-exporter.d.ts +13 -0
  92. package/dist/infrastructure/ndjson-exporter-adapter/ndjson-exporter.js +77 -0
  93. package/dist/infrastructure/perf-hooks-clock-adapter/index.d.ts +6 -0
  94. package/dist/infrastructure/perf-hooks-clock-adapter/index.js +10 -0
  95. package/dist/infrastructure/perf-hooks-clock-adapter/perf-hooks-clock.d.ts +11 -0
  96. package/dist/infrastructure/perf-hooks-clock-adapter/perf-hooks-clock.js +22 -0
  97. package/dist/infrastructure/playwright-locator-screenshot-adapter/assertion-wrapper.d.ts +6 -0
  98. package/dist/infrastructure/playwright-locator-screenshot-adapter/assertion-wrapper.js +109 -0
  99. package/dist/infrastructure/playwright-locator-screenshot-adapter/index.d.ts +9 -0
  100. package/dist/infrastructure/playwright-locator-screenshot-adapter/index.js +21 -0
  101. package/dist/infrastructure/playwright-locator-screenshot-adapter/locator-patch.d.ts +11 -0
  102. package/dist/infrastructure/playwright-locator-screenshot-adapter/locator-patch.js +79 -0
  103. package/dist/infrastructure/playwright-locator-screenshot-adapter/overlay-helpers.d.ts +15 -0
  104. package/dist/infrastructure/playwright-locator-screenshot-adapter/overlay-helpers.js +33 -0
  105. package/dist/infrastructure/playwright-locator-screenshot-adapter/screenshot-capture-session.d.ts +26 -0
  106. package/dist/infrastructure/playwright-locator-screenshot-adapter/screenshot-capture-session.js +125 -0
  107. package/dist/infrastructure/playwright-step-tracking-adapter/index.d.ts +7 -0
  108. package/dist/infrastructure/playwright-step-tracking-adapter/index.js +10 -0
  109. package/dist/infrastructure/playwright-step-tracking-adapter/playwright-step-tracking-adapter.d.ts +14 -0
  110. package/dist/infrastructure/playwright-step-tracking-adapter/playwright-step-tracking-adapter.js +51 -0
  111. package/dist/infrastructure/playwright-test-context-adapter/heal-tag-prefix.d.ts +25 -0
  112. package/dist/infrastructure/playwright-test-context-adapter/heal-tag-prefix.js +28 -0
  113. package/dist/infrastructure/playwright-test-context-adapter/index.d.ts +8 -0
  114. package/dist/infrastructure/playwright-test-context-adapter/index.js +12 -0
  115. package/dist/infrastructure/playwright-test-context-adapter/playwright-test-context-adapter.d.ts +19 -0
  116. package/dist/infrastructure/playwright-test-context-adapter/playwright-test-context-adapter.js +43 -0
  117. package/dist/infrastructure/stdout-capture-adapter/index.d.ts +7 -0
  118. package/dist/infrastructure/stdout-capture-adapter/index.js +10 -0
  119. package/dist/infrastructure/stdout-capture-adapter/stdout-capture-session.d.ts +20 -0
  120. package/dist/infrastructure/stdout-capture-adapter/stdout-capture-session.js +47 -0
  121. package/dist/infrastructure/system-info-adapter/index.d.ts +6 -0
  122. package/dist/infrastructure/system-info-adapter/index.js +10 -0
  123. package/dist/infrastructure/system-info-adapter/system-info-adapter.d.ts +12 -0
  124. package/dist/infrastructure/system-info-adapter/system-info-adapter.js +83 -0
  125. package/package.json +95 -0
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright: (c) Myia SAS 2026.
3
+ * This file and its contents are licensed under the AGPLv3 License.
4
+ * Please see the LICENSE file at the root of this repository
5
+ */
6
+ import type * as BabelTypes from '@babel/types';
7
+ type Types = typeof BabelTypes;
8
+ type Node = BabelTypes.Node;
9
+ export interface LeafStatementClassifier {
10
+ isLeafStatement: (node: Node) => boolean;
11
+ kindOf: (node: Node) => string;
12
+ containsAwait: (node: Node) => boolean;
13
+ }
14
+ export declare function createLeafStatementClassifier(t: Types): LeafStatementClassifier;
15
+ export {};
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright: (c) Myia SAS 2026.
4
+ * This file and its contents are licensed under the AGPLv3 License.
5
+ * Please see the LICENSE file at the root of this repository
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createLeafStatementClassifier = createLeafStatementClassifier;
9
+ function createLeafStatementClassifier(t) {
10
+ const isLeafStatement = (node) => t.isExpressionStatement(node) ||
11
+ t.isVariableDeclaration(node) ||
12
+ t.isReturnStatement(node) ||
13
+ t.isThrowStatement(node) ||
14
+ t.isBreakStatement(node) ||
15
+ t.isContinueStatement(node) ||
16
+ t.isDebuggerStatement(node);
17
+ const kindOf = (node) => {
18
+ if (t.isExpressionStatement(node))
19
+ return 'expression';
20
+ if (t.isVariableDeclaration(node))
21
+ return 'variable';
22
+ if (t.isReturnStatement(node))
23
+ return 'return';
24
+ if (t.isThrowStatement(node))
25
+ return 'throw';
26
+ if (t.isBreakStatement(node))
27
+ return 'break';
28
+ if (t.isContinueStatement(node))
29
+ return 'continue';
30
+ if (t.isDebuggerStatement(node))
31
+ return 'debugger';
32
+ return node.type;
33
+ };
34
+ const containsAwait = (node) => {
35
+ let found = false;
36
+ const walk = (n) => {
37
+ if (!n || found || typeof n !== 'object')
38
+ return;
39
+ const asNode = n;
40
+ if (t.isAwaitExpression(asNode)) {
41
+ found = true;
42
+ return;
43
+ }
44
+ if (t.isFunction(asNode))
45
+ return;
46
+ for (const key of Object.keys(asNode)) {
47
+ if (key === 'loc' || key === 'type' || key === 'start' || key === 'end')
48
+ continue;
49
+ const v = asNode[key];
50
+ if (v == null)
51
+ continue;
52
+ if (Array.isArray(v)) {
53
+ for (const item of v)
54
+ if (item && item.type)
55
+ walk(item);
56
+ }
57
+ else if (v.type) {
58
+ walk(v);
59
+ }
60
+ }
61
+ };
62
+ walk(node);
63
+ return found;
64
+ };
65
+ return { isLeafStatement, kindOf, containsAwait };
66
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copyright: (c) Myia SAS 2026.
3
+ * This file and its contents are licensed under the AGPLv3 License.
4
+ * Please see the LICENSE file at the root of this repository
5
+ */
6
+ import type * as BabelTypes from '@babel/types';
7
+ import type { NodePath } from '@babel/traverse';
8
+ type Types = typeof BabelTypes;
9
+ export type NonWrappableStatementPredicate = (path: NodePath<BabelTypes.Statement>) => boolean;
10
+ export declare function createNonWrappableStatementPredicate(t: Types, isGeneratedModuleStatement: (node: BabelTypes.Node) => boolean): NonWrappableStatementPredicate;
11
+ export {};
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright: (c) Myia SAS 2026.
4
+ * This file and its contents are licensed under the AGPLv3 License.
5
+ * Please see the LICENSE file at the root of this repository
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createNonWrappableStatementPredicate = createNonWrappableStatementPredicate;
9
+ function createNonWrappableStatementPredicate(t, isGeneratedModuleStatement) {
10
+ return function isNonWrappableStatement(path) {
11
+ const node = path.node;
12
+ return (t.isFunctionDeclaration(node) ||
13
+ t.isImportDeclaration(node) ||
14
+ t.isExportDeclaration(node) ||
15
+ path.parentPath.isExportDeclaration() ||
16
+ t.isBlockStatement(node) ||
17
+ t.isEmptyStatement(node) ||
18
+ isGeneratedModuleStatement(node));
19
+ };
20
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Copyright: (c) Myia SAS 2026.
3
+ * This file and its contents are licensed under the AGPLv3 License.
4
+ * Please see the LICENSE file at the root of this repository
5
+ */
6
+ import type * as BabelTypes from '@babel/types';
7
+ import type { NodePath } from '@babel/traverse';
8
+ import type { PluginPass } from '@babel/core';
9
+ type Types = typeof BabelTypes;
10
+ interface MetaHelpers {
11
+ kindOf: (node: BabelTypes.Node) => string;
12
+ containsAwait: (node: BabelTypes.Node) => boolean;
13
+ findScopeName: (nodePath: NodePath) => string;
14
+ extractSource: (code: string | undefined | null, node: BabelTypes.Node) => string;
15
+ extractLeadingComment: (node: BabelTypes.Node) => string | null;
16
+ relFile: (cwd: string, absFile: string | undefined | null) => string;
17
+ }
18
+ export type EnterMetaLiteralBuilder = (state: PluginPass, nodePath: NodePath, node: BabelTypes.Node, cwd: string) => BabelTypes.ObjectExpression;
19
+ export declare function createEnterMetaLiteralBuilder(t: Types, helpers: MetaHelpers): EnterMetaLiteralBuilder;
20
+ export {};
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright: (c) Myia SAS 2026.
4
+ * This file and its contents are licensed under the AGPLv3 License.
5
+ * Please see the LICENSE file at the root of this repository
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createEnterMetaLiteralBuilder = createEnterMetaLiteralBuilder;
9
+ function createEnterMetaLiteralBuilder(t, helpers) {
10
+ const { kindOf, containsAwait, findScopeName, extractSource, extractLeadingComment, relFile } = helpers;
11
+ return function buildEnterMetaLiteral(state, nodePath, node, cwd) {
12
+ const file = relFile(cwd, state.file.opts.filename);
13
+ const loc = node.loc;
14
+ const properties = [
15
+ t.objectProperty(t.identifier('file'), t.stringLiteral(file)),
16
+ t.objectProperty(t.identifier('startLine'), t.numericLiteral(loc.start.line)),
17
+ t.objectProperty(t.identifier('startCol'), t.numericLiteral(loc.start.column)),
18
+ t.objectProperty(t.identifier('endLine'), t.numericLiteral(loc.end.line)),
19
+ t.objectProperty(t.identifier('endCol'), t.numericLiteral(loc.end.column)),
20
+ t.objectProperty(t.identifier('kind'), t.stringLiteral(kindOf(node))),
21
+ t.objectProperty(t.identifier('scope'), t.stringLiteral(findScopeName(nodePath))),
22
+ t.objectProperty(t.identifier('hasAwait'), t.booleanLiteral(containsAwait(node))),
23
+ t.objectProperty(t.identifier('source'), t.stringLiteral(extractSource(state.file.code, node))),
24
+ ];
25
+ // Optional — only emit when the source has an attached comment
26
+ // so the common case stays at 9 keys and no `"leadingComment":
27
+ // null` leaks into the NDJSON.
28
+ const leadingComment = extractLeadingComment(node);
29
+ if (leadingComment !== null) {
30
+ properties.push(t.objectProperty(t.identifier('leadingComment'), t.stringLiteral(leadingComment)));
31
+ }
32
+ return t.objectExpression(properties);
33
+ };
34
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright: (c) Myia SAS 2026.
3
+ * This file and its contents are licensed under the AGPLv3 License.
4
+ * Please see the LICENSE file at the root of this repository
5
+ */
6
+ import type * as BabelTypes from '@babel/types';
7
+ type Types = typeof BabelTypes;
8
+ export type GlobalTraceCallBuilder = (name: string, args: BabelTypes.Expression[]) => BabelTypes.ExpressionStatement;
9
+ export declare function createGlobalTraceCallBuilder(t: Types): GlobalTraceCallBuilder;
10
+ export {};
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright: (c) Myia SAS 2026.
4
+ * This file and its contents are licensed under the AGPLv3 License.
5
+ * Please see the LICENSE file at the root of this repository
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createGlobalTraceCallBuilder = createGlobalTraceCallBuilder;
9
+ function createGlobalTraceCallBuilder(t) {
10
+ return function buildGlobalTraceCall(name, args) {
11
+ const callee = t.memberExpression(t.identifier('globalThis'), t.identifier(name));
12
+ const stmt = t.expressionStatement(t.optionalCallExpression(callee, args, /* optional */ true));
13
+ stmt._traced = true;
14
+ return stmt;
15
+ };
16
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Copyright: (c) Myia SAS 2026.
3
+ * This file and its contents are licensed under the AGPLv3 License.
4
+ * Please see the LICENSE file at the root of this repository
5
+ */
6
+ import type * as BabelTypes from '@babel/types';
7
+ import type { Scope } from '@babel/traverse';
8
+ import type { GlobalTraceCallBuilder } from './global-trace-call';
9
+ type Types = typeof BabelTypes;
10
+ export interface TryFinallyWrapperBuilder {
11
+ buildTryFinally: (scope: Scope, tryBodyStmts: BabelTypes.Statement[], okArgs?: BabelTypes.Expression[]) => {
12
+ threwId: BabelTypes.Identifier;
13
+ tryStmt: BabelTypes.TryStatement;
14
+ };
15
+ buildThrewDecl: (threwId: BabelTypes.Identifier) => BabelTypes.VariableDeclaration;
16
+ }
17
+ export declare function createTryFinallyWrapperBuilder(t: Types, buildGlobalTraceCall: GlobalTraceCallBuilder): TryFinallyWrapperBuilder;
18
+ export {};
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright: (c) Myia SAS 2026.
4
+ * This file and its contents are licensed under the AGPLv3 License.
5
+ * Please see the LICENSE file at the root of this repository
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createTryFinallyWrapperBuilder = createTryFinallyWrapperBuilder;
9
+ const global_names_1 = require("../../../trace-event-recorder/model/global-names");
10
+ function createTryFinallyWrapperBuilder(t, buildGlobalTraceCall) {
11
+ function buildTryFinally(scope, tryBodyStmts, okArgs = []) {
12
+ const errId = scope.generateUidIdentifier('traceErr');
13
+ const threwId = scope.generateUidIdentifier('traceThrew');
14
+ const rethrow = t.throwStatement(t.cloneNode(errId));
15
+ rethrow._traced = true;
16
+ const assignThrew = t.expressionStatement(t.assignmentExpression('=', t.cloneNode(threwId), t.booleanLiteral(true)));
17
+ assignThrew._traced = true;
18
+ const catchBlock = t.blockStatement([
19
+ assignThrew,
20
+ buildGlobalTraceCall(global_names_1.HEAL_THROW, [t.cloneNode(errId)]),
21
+ rethrow,
22
+ ]);
23
+ const finallyBlock = t.blockStatement([
24
+ t.ifStatement(t.unaryExpression('!', t.cloneNode(threwId)), t.blockStatement([buildGlobalTraceCall(global_names_1.HEAL_OK, okArgs)])),
25
+ ]);
26
+ finallyBlock.body.forEach((n) => {
27
+ n._traced = true;
28
+ const withConsequent = n;
29
+ if (withConsequent.consequent)
30
+ withConsequent.consequent._traced = true;
31
+ });
32
+ const tryStmt = t.tryStatement(t.blockStatement(tryBodyStmts), t.catchClause(errId, catchBlock), finallyBlock);
33
+ tryStmt._traced = true;
34
+ return { threwId, tryStmt };
35
+ }
36
+ function buildThrewDecl(threwId) {
37
+ const decl = t.variableDeclaration('let', [
38
+ t.variableDeclarator(threwId, t.booleanLiteral(false)),
39
+ ]);
40
+ decl._traced = true;
41
+ return decl;
42
+ }
43
+ return { buildTryFinally, buildThrewDecl };
44
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Copyright: (c) Myia SAS 2026.
3
+ * This file and its contents are licensed under the AGPLv3 License.
4
+ * Please see the LICENSE file at the root of this repository
5
+ */
6
+ import type * as BabelTypes from '@babel/types';
7
+ type Types = typeof BabelTypes;
8
+ export interface HoistedVariableDeclaration {
9
+ /** Names introduced by the declaration, flattened across destructuring. */
10
+ bindingNames: Set<string>;
11
+ /** `let x, y;` — the bindings hoisted out of the try block. */
12
+ hoistDecl: BabelTypes.VariableDeclaration;
13
+ /** `x = EXPR; y = EXPR;` — one per declarator that had an initializer. */
14
+ assignments: BabelTypes.ExpressionStatement[];
15
+ /** `{ x, y }` — shorthand object literal passed to `__ok`. */
16
+ varsObject: BabelTypes.ObjectExpression;
17
+ }
18
+ export type VariableDeclarationHoister = (declaration: BabelTypes.VariableDeclaration) => HoistedVariableDeclaration;
19
+ export declare function createVariableDeclarationHoister(t: Types): VariableDeclarationHoister;
20
+ export {};
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright: (c) Myia SAS 2026.
4
+ * This file and its contents are licensed under the AGPLv3 License.
5
+ * Please see the LICENSE file at the root of this repository
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createVariableDeclarationHoister = createVariableDeclarationHoister;
9
+ function createVariableDeclarationHoister(t) {
10
+ return function hoistVariableDeclaration(declaration) {
11
+ const bindingNames = new Set();
12
+ for (const d of declaration.declarations) {
13
+ Object.keys(t.getBindingIdentifiers(d.id)).forEach((n) => bindingNames.add(n));
14
+ }
15
+ const hoistDecl = t.variableDeclaration('let', [...bindingNames].map((n) => t.variableDeclarator(t.identifier(n))));
16
+ hoistDecl._traced = true;
17
+ const assignments = declaration.declarations
18
+ .filter((d) => d.init != null)
19
+ .map((d) => {
20
+ const stmt = t.expressionStatement(t.assignmentExpression('=', d.id, d.init));
21
+ stmt._traced = true;
22
+ return stmt;
23
+ });
24
+ const varsObject = t.objectExpression([...bindingNames].map((n) => t.objectProperty(t.identifier(n), t.identifier(n), false, true)));
25
+ return { bindingNames, hoistDecl, assignments, varsObject };
26
+ };
27
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright: (c) Myia SAS 2026.
3
+ * This file and its contents are licensed under the AGPLv3 License.
4
+ * Please see the LICENSE file at the root of this repository
5
+ */
6
+ export type IncludeEntry = string | RegExp | ((filename: string) => unknown);
7
+ export type Include = IncludeEntry | IncludeEntry[] | null | undefined;
8
+ export type Matcher = (filename: string | undefined | null) => boolean;
9
+ export declare const defaultInclude: IncludeEntry[];
10
+ export declare function buildMatcher(include: Include): Matcher;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright: (c) Myia SAS 2026.
4
+ * This file and its contents are licensed under the AGPLv3 License.
5
+ * Please see the LICENSE file at the root of this repository
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.defaultInclude = void 0;
9
+ exports.buildMatcher = buildMatcher;
10
+ exports.defaultInclude = [/\/tests\//];
11
+ function buildMatcher(include) {
12
+ const entries = include == null ? exports.defaultInclude : Array.isArray(include) ? include : [include];
13
+ return (filename) => {
14
+ if (!filename)
15
+ return false;
16
+ for (const e of entries) {
17
+ if (typeof e === 'string' && filename.includes(e))
18
+ return true;
19
+ if (e instanceof RegExp && e.test(filename))
20
+ return true;
21
+ if (typeof e === 'function' && e(filename))
22
+ return true;
23
+ }
24
+ return false;
25
+ };
26
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Copyright: (c) Myia SAS 2026.
3
+ * This file and its contents are licensed under the AGPLv3 License.
4
+ * Please see the LICENSE file at the root of this repository
5
+ */
6
+ export interface EnterMeta {
7
+ file: string;
8
+ startLine: number;
9
+ startCol: number;
10
+ endLine: number;
11
+ endCol: number;
12
+ kind: string;
13
+ scope: string;
14
+ hasAwait: boolean;
15
+ source: string;
16
+ /**
17
+ * User-written `// …` / `/* … *\/` comments attached to this
18
+ * statement's AST node, joined with `\n` in source order. Present
19
+ * only when the parser attached at least one comment; see
20
+ * `./statement-trace-schema.ts` `Statement.leadingComment` for the
21
+ * attachment caveats consumers should know about.
22
+ */
23
+ leadingComment?: string;
24
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright: (c) Myia SAS 2026.
4
+ * This file and its contents are licensed under the AGPLv3 License.
5
+ * Please see the LICENSE file at the root of this repository
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Copyright: (c) Myia SAS 2026.
3
+ * This file and its contents are licensed under the AGPLv3 License.
4
+ * Please see the LICENSE file at the root of this repository
5
+ */
6
+ export declare const HEAL_ENTER = "__heal_enter";
7
+ export declare const HEAL_OK = "__heal_ok";
8
+ export declare const HEAL_THROW = "__heal_throw";
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright: (c) Myia SAS 2026.
4
+ * This file and its contents are licensed under the AGPLv3 License.
5
+ * Please see the LICENSE file at the root of this repository
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.HEAL_THROW = exports.HEAL_OK = exports.HEAL_ENTER = void 0;
9
+ // Names of the global functions the Babel plugin injects into
10
+ // instrumented source and the recorder entrypoint installs on
11
+ // `globalThis`. Both sides import from here so the contract lives in
12
+ // one place.
13
+ //
14
+ // The names are deliberately prefixed with `__heal_` so anyone
15
+ // reading the Babel-transformed test source understands at a glance
16
+ // which library these hooks belong to. Rename them together (plugin
17
+ // and recorder entrypoint) if the package is ever renamed.
18
+ exports.HEAL_ENTER = '__heal_enter';
19
+ exports.HEAL_OK = '__heal_ok';
20
+ exports.HEAL_THROW = '__heal_throw';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Copyright: (c) Myia SAS 2026.
3
+ * This file and its contents are licensed under the AGPLv3 License.
4
+ * Please see the LICENSE file at the root of this repository
5
+ */
6
+ export interface SerializedError {
7
+ name?: string;
8
+ message: string;
9
+ stack?: string;
10
+ isPlaywrightError?: boolean;
11
+ causes?: Array<{
12
+ name?: string;
13
+ message: string;
14
+ stack?: string;
15
+ }>;
16
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright: (c) Myia SAS 2026.
4
+ * This file and its contents are licensed under the AGPLv3 License.
5
+ * Please see the LICENSE file at the root of this repository
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Copyright: (c) Myia SAS 2026.
3
+ * This file and its contents are licensed under the AGPLv3 License.
4
+ * Please see the LICENSE file at the root of this repository
5
+ */
6
+ export declare const HEAL_TRACE_SCHEMA_VERSION = 1;
7
+ /**
8
+ * One line of a `heal-traces.ndjson` file. Discriminated by `kind`.
9
+ */
10
+ export type HealTraceRecord = TestHeaderRecord | StatementRecord | TestResultRecord;
11
+ export interface TestHeaderRecord {
12
+ kind: 'test-header';
13
+ schemaVersion: typeof HEAL_TRACE_SCHEMA_VERSION;
14
+ test: TestHeader;
15
+ }
16
+ export interface TestHeader {
17
+ title: string;
18
+ titlePath: string[];
19
+ file: string;
20
+ project: string;
21
+ workerIndex: number;
22
+ retry: number;
23
+ /** Wall-clock ms (Date.now()) at which the recorder was reset for this test. */
24
+ startedAt: number;
25
+ env: TestEnv;
26
+ context: TestContext;
27
+ }
28
+ /**
29
+ * Per-test correlation identifiers. Populated by the fixture from
30
+ * Playwright's `TestInfo`. The global correlation key for a test
31
+ * attempt is `(testId, attempt)` — two attempts of the same test
32
+ * share `testId` and differ in `attempt`.
33
+ */
34
+ export interface TestContext {
35
+ /**
36
+ * Playwright's `testInfo.testId` — a stable hash of
37
+ * (file, title, project). Distinct tests always get distinct
38
+ * values; two attempts of the same test share this value. Also
39
+ * serves as the cross-worker correlation key: a retry landing in
40
+ * a different worker still reports the same `testId`.
41
+ */
42
+ testId: string;
43
+ /**
44
+ * 1-indexed attempt number. Equal to `testInfo.retry + 1` — the
45
+ * first run of a test is attempt 1, the first retry is attempt 2,
46
+ * and so on.
47
+ */
48
+ attempt: number;
49
+ /**
50
+ * Heal test case id sourced from the `@heal-<id>` Playwright tag
51
+ * on the test (e.g. `test('…', { tag: '@heal-42' }, …)`). The
52
+ * suffix after `@heal-` is parsed as a positive integer — it must
53
+ * match the `bigint` primary key on `backend.test_cases`. When
54
+ * present, the backend resolver links every trace event for this
55
+ * run to the referenced test case (after verifying the id belongs
56
+ * to the API key's org). When absent, events go to the unlinked
57
+ * bucket.
58
+ *
59
+ * Tags over annotations because they are CLI-filterable — the
60
+ * backend triggers a specific set of test cases with
61
+ * `npx playwright test -g "@heal-42|@heal-57"`, no file paths or
62
+ * line numbers required.
63
+ */
64
+ testCaseId?: number;
65
+ }
66
+ export interface TestEnv {
67
+ nodeVersion?: string;
68
+ platform?: string;
69
+ arch?: string;
70
+ hostname?: string;
71
+ isCI?: boolean;
72
+ cwd?: string;
73
+ gitSha?: string;
74
+ pid?: number;
75
+ }
76
+ export interface StatementRecord {
77
+ kind: 'statement';
78
+ /**
79
+ * Always a ROOT statement (its runtime parentSeq was null). The
80
+ * full subtree is nested in `statement.children`; nested calls
81
+ * never appear as separate records in the stream.
82
+ */
83
+ statement: Statement;
84
+ }
85
+ export interface Statement {
86
+ /** Execution-order sequence number, identical to the raw enter event's seq. */
87
+ seq: number;
88
+ file: string;
89
+ line: number;
90
+ endLine: number;
91
+ kind: string;
92
+ scope: string;
93
+ source: string;
94
+ hasAwait: boolean;
95
+ /**
96
+ * User-written source comments (`// …` or `/* … *\/`) that Babel's
97
+ * parser attached to this statement's AST node, joined with `\n`
98
+ * in source order, with a single leading/trailing space stripped
99
+ * from each comment value. Absent from the JSON when the node has
100
+ * no attached comments (the field is never `null` or `""`).
101
+ *
102
+ * Intended as a best-effort intent hint — the autopilot agent and
103
+ * humans debugging a failing test read this the same way they
104
+ * would read the comment above a step in the source file.
105
+ *
106
+ * Attachment caveat: Babel assigns a same-line trailing comment
107
+ * of statement N as a *leading* comment of statement N+1 when the
108
+ * two are separated only by whitespace. `foo(); // about foo\n
109
+ * bar();` therefore surfaces `about foo` on `bar()`, not `foo()`.
110
+ * Treat the field accordingly.
111
+ */
112
+ leadingComment?: string;
113
+ /** Innermost test.step title, or null if not inside one. */
114
+ step: string | null;
115
+ /** Full chain of enclosing test.step titles. */
116
+ stepPath: string[] | null;
117
+ status: 'ok' | 'threw';
118
+ /** Milliseconds the statement took to execute. */
119
+ duration: number;
120
+ /** Relative start time in ms (from test.startedAt). */
121
+ t: number;
122
+ pageUrl?: string;
123
+ /** Snapshot of bindings introduced by `const`/`let` that succeeded. */
124
+ vars?: Record<string, unknown>;
125
+ /** Present only when status === 'threw'. */
126
+ error?: StatementError;
127
+ /**
128
+ * Filename (relative to the test's heal-data directory) of the
129
+ * highlight screenshot captured by locator-screenshots while this
130
+ * statement was running. Present only for statements that call a
131
+ * patched Locator action (`click`, `fill`, `hover`, …) or a
132
+ * wrapped assertion.
133
+ */
134
+ screenshot?: string;
135
+ /**
136
+ * Statements executed inside this one. Sorted by execution order
137
+ * (`seq`). Empty for leaves.
138
+ */
139
+ children: Statement[];
140
+ }
141
+ export interface TestResultRecord {
142
+ kind: 'test-result';
143
+ status: 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';
144
+ /** Total test duration in ms, as reported by Playwright's TestInfo. */
145
+ duration: number;
146
+ stdout?: string[];
147
+ stderr?: string[];
148
+ }
149
+ /**
150
+ * Normalized error attached to a statement whose status is `threw`.
151
+ * Decoupled from the runtime `SerializedError` type so consumers of
152
+ * `heal-traces.ndjson` can import this schema without reaching into
153
+ * internal modules.
154
+ */
155
+ export interface StatementError {
156
+ name?: string;
157
+ message: string;
158
+ stack?: string;
159
+ /**
160
+ * True when the error originated from Playwright itself (timeouts,
161
+ * locator failures, expect mismatches) as opposed to user code.
162
+ */
163
+ isPlaywrightError?: boolean;
164
+ /** Walked `.cause` chain, up to 5 levels deep. */
165
+ causes?: StatementErrorCause[];
166
+ }
167
+ export interface StatementErrorCause {
168
+ name?: string;
169
+ message: string;
170
+ stack?: string;
171
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright: (c) Myia SAS 2026.
4
+ * This file and its contents are licensed under the AGPLv3 License.
5
+ * Please see the LICENSE file at the root of this repository
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.HEAL_TRACE_SCHEMA_VERSION = void 0;
9
+ // Output schema for `heal-traces.ndjson` — the agent-facing wire
10
+ // format written one record per line.
11
+ //
12
+ // The file is a stream, not a single document: each line is exactly
13
+ // one `HealTraceRecord`. Per test the records appear in this order:
14
+ //
15
+ // 1. Exactly one `test-header` record (first line). Carries static
16
+ // per-test metadata and environment fields; the fields known
17
+ // only at test-end (status, duration, stdout, stderr) live on
18
+ // `test-result` instead.
19
+ // 2. Zero or more `statement` records, each a ROOT statement
20
+ // (directly inside the test body). Nested calls live inline
21
+ // inside `statement.children` and never appear as standalone
22
+ // records. Consumers must not expect a flat stream of every
23
+ // executed statement.
24
+ // 3. Exactly one `test-result` record (last line). If it is
25
+ // missing, the test crashed mid-run and the trace should be
26
+ // treated as partial.
27
+ //
28
+ // The projector in `../../trace-event-recorder/projectors/
29
+ // statement-projector.ts` is what produces these records from the
30
+ // raw recorder event stream (enter/ok/throw/meta). Consumers (the
31
+ // Heal autopilot agent, humans debugging a failing test) should
32
+ // import these types to stay in sync with the file format.
33
+ exports.HEAL_TRACE_SCHEMA_VERSION = 1;