@checkdigit/eslint-plugin 7.5.0 → 7.6.0-PR.75-5da1

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 (66) hide show
  1. package/dist-mjs/agent/add-assert-import.mjs +58 -0
  2. package/dist-mjs/agent/add-base-path-const.mjs +65 -0
  3. package/dist-mjs/agent/add-base-path-import.mjs +60 -0
  4. package/dist-mjs/agent/add-url-domain.mjs +61 -0
  5. package/dist-mjs/agent/agent-test-wiring.mjs +221 -0
  6. package/dist-mjs/agent/fetch-response-body-json.mjs +146 -0
  7. package/dist-mjs/agent/fetch-response-header-getter.mjs +117 -0
  8. package/dist-mjs/agent/fetch-response-status.mjs +66 -0
  9. package/dist-mjs/agent/fetch-then.mjs +269 -0
  10. package/dist-mjs/agent/fetch.mjs +38 -0
  11. package/dist-mjs/agent/file.mjs +43 -0
  12. package/dist-mjs/agent/fix-function-call-arguments.mjs +153 -0
  13. package/dist-mjs/agent/no-fixture.mjs +361 -0
  14. package/dist-mjs/agent/no-mapped-response.mjs +75 -0
  15. package/dist-mjs/agent/no-service-wrapper.mjs +185 -0
  16. package/dist-mjs/agent/no-status-code.mjs +59 -0
  17. package/dist-mjs/agent/no-unused-function-argument.mjs +79 -0
  18. package/dist-mjs/agent/no-unused-imports.mjs +81 -0
  19. package/dist-mjs/agent/no-unused-service-variable.mjs +74 -0
  20. package/dist-mjs/agent/response-reference.mjs +70 -0
  21. package/dist-mjs/agent/url.mjs +32 -0
  22. package/dist-mjs/index.mjs +146 -4
  23. package/dist-types/agent/add-assert-import.d.ts +4 -0
  24. package/dist-types/agent/add-base-path-const.d.ts +4 -0
  25. package/dist-types/agent/add-base-path-import.d.ts +4 -0
  26. package/dist-types/agent/add-url-domain.d.ts +4 -0
  27. package/dist-types/agent/agent-test-wiring.d.ts +4 -0
  28. package/dist-types/agent/fetch-response-body-json.d.ts +4 -0
  29. package/dist-types/agent/fetch-response-header-getter.d.ts +4 -0
  30. package/dist-types/agent/fetch-response-status.d.ts +4 -0
  31. package/dist-types/agent/fetch-then.d.ts +4 -0
  32. package/dist-types/agent/fetch.d.ts +5 -0
  33. package/dist-types/agent/file.d.ts +7 -0
  34. package/dist-types/agent/fix-function-call-arguments.d.ts +9 -0
  35. package/dist-types/agent/no-fixture.d.ts +4 -0
  36. package/dist-types/agent/no-mapped-response.d.ts +4 -0
  37. package/dist-types/agent/no-service-wrapper.d.ts +4 -0
  38. package/dist-types/agent/no-status-code.d.ts +4 -0
  39. package/dist-types/agent/no-unused-function-argument.d.ts +4 -0
  40. package/dist-types/agent/no-unused-imports.d.ts +4 -0
  41. package/dist-types/agent/no-unused-service-variable.d.ts +4 -0
  42. package/dist-types/agent/response-reference.d.ts +16 -0
  43. package/dist-types/agent/url.d.ts +4 -0
  44. package/package.json +1 -96
  45. package/src/agent/add-assert-import.ts +74 -0
  46. package/src/agent/add-base-path-const.ts +81 -0
  47. package/src/agent/add-base-path-import.ts +69 -0
  48. package/src/agent/add-url-domain.ts +76 -0
  49. package/src/agent/agent-test-wiring.ts +273 -0
  50. package/src/agent/fetch-response-body-json.ts +197 -0
  51. package/src/agent/fetch-response-header-getter.ts +148 -0
  52. package/src/agent/fetch-response-status.ts +87 -0
  53. package/src/agent/fetch-then.ts +357 -0
  54. package/src/agent/fetch.ts +57 -0
  55. package/src/agent/file.ts +42 -0
  56. package/src/agent/fix-function-call-arguments.ts +200 -0
  57. package/src/agent/no-fixture.ts +521 -0
  58. package/src/agent/no-mapped-response.ts +84 -0
  59. package/src/agent/no-service-wrapper.ts +241 -0
  60. package/src/agent/no-status-code.ts +72 -0
  61. package/src/agent/no-unused-function-argument.ts +98 -0
  62. package/src/agent/no-unused-imports.ts +103 -0
  63. package/src/agent/no-unused-service-variable.ts +93 -0
  64. package/src/agent/response-reference.ts +129 -0
  65. package/src/agent/url.ts +32 -0
  66. package/src/index.ts +142 -0
@@ -0,0 +1,117 @@
1
+ // src/agent/fetch-response-header-getter.ts
2
+ import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
3
+ import getDocumentationUrl from "../get-documentation-url.mjs";
4
+ var ruleId = "fetch-response-header-getter-ts";
5
+ var HEADER_BUILTIN_FUNCTIONS = Object.keys(Headers.prototype);
6
+ var createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
7
+ var rule = createRule({
8
+ name: ruleId,
9
+ meta: {
10
+ type: "suggestion",
11
+ docs: {
12
+ description: 'Use "get()" method to get header value from the headers object of the fetch response.'
13
+ },
14
+ messages: {
15
+ useGetter: 'Use "get()" method to get header value from the headers object of the fetch response.',
16
+ unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}.'
17
+ },
18
+ fixable: "code",
19
+ schema: []
20
+ },
21
+ defaultOptions: [],
22
+ create(context) {
23
+ const parserServices = ESLintUtils.getParserServices(context);
24
+ const typeChecker = parserServices.program.getTypeChecker();
25
+ const sourceCode = context.sourceCode;
26
+ return {
27
+ MemberExpression: (responseHeadersAccess) => {
28
+ try {
29
+ if (responseHeadersAccess.property.type === AST_NODE_TYPES.Identifier && HEADER_BUILTIN_FUNCTIONS.includes(responseHeadersAccess.property.name)) {
30
+ return;
31
+ }
32
+ const responseHeadersTsNode = parserServices.esTreeNodeToTSNodeMap.get(responseHeadersAccess.object);
33
+ let responseHeadersType = typeChecker.getTypeAtLocation(responseHeadersTsNode);
34
+ responseHeadersType = responseHeadersType.isUnion() ? responseHeadersType.types[0] : responseHeadersType;
35
+ const responseHeadersTypeName = (
36
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
37
+ (responseHeadersType.symbol ?? responseHeadersType.aliasSymbol)?.escapedName
38
+ );
39
+ if (responseHeadersTypeName !== "Headers" && responseHeadersTypeName !== "HeaderGetter") {
40
+ return;
41
+ }
42
+ let replacementText;
43
+ if (!responseHeadersAccess.computed) {
44
+ replacementText = `${sourceCode.getText(responseHeadersAccess.object)}.get('${sourceCode.getText(responseHeadersAccess.property)}')`;
45
+ } else if (responseHeadersAccess.property.type === AST_NODE_TYPES.Identifier || responseHeadersAccess.property.type === AST_NODE_TYPES.Literal || responseHeadersAccess.property.type === AST_NODE_TYPES.TemplateLiteral) {
46
+ replacementText = `${sourceCode.getText(responseHeadersAccess.object)}.get(${sourceCode.getText(responseHeadersAccess.property)})`;
47
+ } else {
48
+ throw new Error(`Unexpected property type: ${responseHeadersAccess.property.type}`);
49
+ }
50
+ context.report({
51
+ messageId: "useGetter",
52
+ node: responseHeadersAccess.property,
53
+ fix(fixer) {
54
+ return fixer.replaceText(responseHeadersAccess, replacementText);
55
+ }
56
+ });
57
+ } catch (error) {
58
+ console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
59
+ context.report({
60
+ node: responseHeadersAccess,
61
+ messageId: "unknownError",
62
+ data: {
63
+ fileName: context.filename,
64
+ error: error instanceof Error ? error.toString() : JSON.stringify(error)
65
+ }
66
+ });
67
+ }
68
+ },
69
+ // convert response.get() to response.headers.get()
70
+ 'CallExpression[callee.property.name="get"]': (responseHeadersAccess) => {
71
+ try {
72
+ if (responseHeadersAccess.callee.type !== AST_NODE_TYPES.MemberExpression) {
73
+ return;
74
+ }
75
+ if (responseHeadersAccess.callee.object.type !== AST_NODE_TYPES.Identifier || responseHeadersAccess.callee.object.name === "request") {
76
+ return;
77
+ }
78
+ const responseNode = responseHeadersAccess.callee.object;
79
+ const responseHeadersTsNode = parserServices.esTreeNodeToTSNodeMap.get(responseNode);
80
+ const responseType = typeChecker.getTypeAtLocation(responseHeadersTsNode);
81
+ const typeName = typeChecker.typeToString(responseType);
82
+ if (typeName === "InboundContext" || typeName.endsWith("RequestType")) {
83
+ return;
84
+ }
85
+ const hasHeadersProperty = responseType.getProperties().some((symbol) => symbol.name === "headers");
86
+ if (!hasHeadersProperty) {
87
+ return;
88
+ }
89
+ const replacementText = `${sourceCode.getText(responseNode)}.headers`;
90
+ context.report({
91
+ messageId: "useGetter",
92
+ node: responseHeadersAccess,
93
+ fix(fixer) {
94
+ return fixer.replaceText(responseNode, replacementText);
95
+ }
96
+ });
97
+ } catch (error) {
98
+ console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
99
+ context.report({
100
+ node: responseHeadersAccess,
101
+ messageId: "unknownError",
102
+ data: {
103
+ fileName: context.filename,
104
+ error: error instanceof Error ? error.toString() : JSON.stringify(error)
105
+ }
106
+ });
107
+ }
108
+ }
109
+ };
110
+ }
111
+ });
112
+ var fetch_response_header_getter_default = rule;
113
+ export {
114
+ fetch_response_header_getter_default as default,
115
+ ruleId
116
+ };
117
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2FnZW50L2ZldGNoLXJlc3BvbnNlLWhlYWRlci1nZXR0ZXIudHMiXSwKICAibWFwcGluZ3MiOiAiO0FBUUEsU0FBUyxnQkFBZ0IsbUJBQTZCO0FBRXRELE9BQU8seUJBQXlCO0FBRXpCLElBQU0sU0FBUztBQUN0QixJQUFNLDJCQUEyQixPQUFPLEtBQUssUUFBUSxTQUFTO0FBRTlELElBQU0sYUFBYSxZQUFZLFlBQVksQ0FBQyxTQUFTLG9CQUFvQixJQUFJLENBQUM7QUFFOUUsSUFBTSxPQUE2RCxXQUFXO0FBQUEsRUFDNUUsTUFBTTtBQUFBLEVBQ04sTUFBTTtBQUFBLElBQ0osTUFBTTtBQUFBLElBQ04sTUFBTTtBQUFBLE1BQ0osYUFBYTtBQUFBLElBQ2Y7QUFBQSxJQUNBLFVBQVU7QUFBQSxNQUNSLFdBQVc7QUFBQSxNQUNYLGNBQWM7QUFBQSxJQUNoQjtBQUFBLElBQ0EsU0FBUztBQUFBLElBQ1QsUUFBUSxDQUFDO0FBQUEsRUFDWDtBQUFBLEVBQ0EsZ0JBQWdCLENBQUM7QUFBQSxFQUNqQixPQUFPLFNBQVM7QUFDZCxVQUFNLGlCQUFpQixZQUFZLGtCQUFrQixPQUFPO0FBQzVELFVBQU0sY0FBYyxlQUFlLFFBQVEsZUFBZTtBQUMxRCxVQUFNLGFBQWEsUUFBUTtBQUUzQixXQUFPO0FBQUEsTUFDTCxrQkFBa0IsQ0FBQywwQkFBcUQ7QUFDdEUsWUFBSTtBQUNGLGNBQ0Usc0JBQXNCLFNBQVMsU0FBUyxlQUFlLGNBQ3ZELHlCQUF5QixTQUFTLHNCQUFzQixTQUFTLElBQUksR0FDckU7QUFFQTtBQUFBLFVBQ0Y7QUFFQSxnQkFBTSx3QkFBd0IsZUFBZSxzQkFBc0IsSUFBSSxzQkFBc0IsTUFBTTtBQUNuRyxjQUFJLHNCQUFzQixZQUFZLGtCQUFrQixxQkFBcUI7QUFFN0UsZ0NBQXNCLG9CQUFvQixRQUFRLElBQUksb0JBQW9CLE1BQU0sQ0FBQyxJQUFLO0FBQ3RGLGdCQUFNO0FBQUE7QUFBQSxhQUNILG9CQUFvQixVQUFV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
@@ -0,0 +1,66 @@
1
+ // src/agent/fetch-response-status.ts
2
+ import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
3
+ import getDocumentationUrl from "../get-documentation-url.mjs";
4
+ var ruleId = "fetch-response-status";
5
+ var createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
6
+ var rule = createRule({
7
+ name: ruleId,
8
+ meta: {
9
+ type: "problem",
10
+ docs: {
11
+ description: 'Replace "response.body" with "await response.json()".'
12
+ },
13
+ messages: {
14
+ renameStatusCodeProperty: 'Rename "statusCode" with "status".',
15
+ unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}.'
16
+ },
17
+ fixable: "code",
18
+ schema: []
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ return {
23
+ VariableDeclaration: (variableDeclaration) => {
24
+ const variableInit = variableDeclaration.declarations[0]?.init;
25
+ if (!variableInit || variableInit.type !== AST_NODE_TYPES.AwaitExpression || variableInit.argument.type !== AST_NODE_TYPES.CallExpression || variableInit.argument.callee.type !== AST_NODE_TYPES.Identifier || variableInit.argument.callee.name !== "fetch") {
26
+ return;
27
+ }
28
+ const variableId = variableDeclaration.declarations[0]?.id;
29
+ if (variableId.type !== AST_NODE_TYPES.ObjectPattern) {
30
+ return;
31
+ }
32
+ const statusCodeProperty = variableId.properties.find(
33
+ (property) => property.type === AST_NODE_TYPES.Property && property.key.type === AST_NODE_TYPES.Identifier && property.key.name === "statusCode"
34
+ );
35
+ if (!statusCodeProperty) {
36
+ return;
37
+ }
38
+ try {
39
+ context.report({
40
+ node: statusCodeProperty,
41
+ messageId: "renameStatusCodeProperty",
42
+ fix(fixer) {
43
+ return statusCodeProperty.shorthand ? fixer.replaceText(statusCodeProperty, "status: statusCode") : fixer.replaceText(statusCodeProperty.key, "status");
44
+ }
45
+ });
46
+ } catch (error) {
47
+ console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
48
+ context.report({
49
+ node: statusCodeProperty,
50
+ messageId: "unknownError",
51
+ data: {
52
+ fileName: context.filename,
53
+ error: error instanceof Error ? error.toString() : JSON.stringify(error)
54
+ }
55
+ });
56
+ }
57
+ }
58
+ };
59
+ }
60
+ });
61
+ var fetch_response_status_default = rule;
62
+ export {
63
+ fetch_response_status_default as default,
64
+ ruleId
65
+ };
66
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2FnZW50L2ZldGNoLXJlc3BvbnNlLXN0YXR1cy50cyJdLAogICJtYXBwaW5ncyI6ICI7QUFRQSxTQUFTLGdCQUFnQixtQkFBNkI7QUFFdEQsT0FBTyx5QkFBeUI7QUFFekIsSUFBTSxTQUFTO0FBRXRCLElBQU0sYUFBYSxZQUFZLFlBQVksQ0FBQyxTQUFTLG9CQUFvQixJQUFJLENBQUM7QUFFOUUsSUFBTSxPQUE0RSxXQUFXO0FBQUEsRUFDM0YsTUFBTTtBQUFBLEVBQ04sTUFBTTtBQUFBLElBQ0osTUFBTTtBQUFBLElBQ04sTUFBTTtBQUFBLE1BQ0osYUFBYTtBQUFBLElBQ2Y7QUFBQSxJQUNBLFVBQVU7QUFBQSxNQUNSLDBCQUEwQjtBQUFBLE1BQzFCLGNBQWM7QUFBQSxJQUNoQjtBQUFBLElBQ0EsU0FBUztBQUFBLElBQ1QsUUFBUSxDQUFDO0FBQUEsRUFDWDtBQUFBLEVBQ0EsZ0JBQWdCLENBQUM7QUFBQSxFQUNqQixPQUFPLFNBQVM7QUFDZCxXQUFPO0FBQUEsTUFDTCxxQkFBcUIsQ0FBQyx3QkFBc0Q7QUFDMUUsY0FBTSxlQUFlLG9CQUFvQixhQUFhLENBQUMsR0FBRztBQUMxRCxZQUNFLENBQUMsZ0JBQ0QsYUFBYSxTQUFTLGVBQWUsbUJBQ3JDLGFBQWEsU0FBUyxTQUFTLGVBQWUsa0JBQzlDLGFBQWEsU0FBUyxPQUFPLFNBQVMsZUFBZSxjQUNyRCxhQUFhLFNBQVMsT0FBTyxTQUFTLFNBQ3RDO0FBQ0E7QUFBQSxRQUNGO0FBRUEsY0FBTSxhQUFhLG9CQUFvQixhQUFhLENBQUMsR0FBRztBQUN4RCxZQUFJLFdBQVcsU0FBUyxlQUFlLGVBQWU7QUFDcEQ7QUFBQSxRQUNGO0FBQ0EsY0FBTSxxQkFBcUIsV0FBVyxXQUFXO0FBQUEsVUFDL0MsQ0FBQyxhQUNDLFNBQVMsU0FBUyxlQUFlLFlBQ2pDLFNBQVMsSUFBSSxTQUFTLGVBQWUsY0FDckMsU0FBUyxJQUFJLFNBQVM7QUFBQSxRQUMxQjtBQUNBLFlBQUksQ0FBQyxvQkFBb0I7QUFDdkI7QUFBQSxRQUNGO0FBRUEsWUFBSTtBQUNGLGtCQUFRLE9BQU87QUFBQSxZQUNiLE1BQU07QUFBQSxZQUNOLFdBQVc7QUFBQSxZQUNYLElBQUksT0FBTztBQUNULHFCQUFPLG1CQUFtQixZQUN0QixNQUFNLFlBQVksb0JBQW9CLG9CQUFvQixJQUMxRCxNQUFNLFlBQVksbUJBQW1CLEtBQUssUUFBUTtBQUFBLFlBQ3hEO0FBQUEsVUFDRixDQUFDO0FBQUEsUUFDSCxTQUFTLE9BQU87QUFFZCxrQkFBUSxNQUFNLG1CQUFtQixNQUFNLG1CQUFtQixRQUFRLFFBQVEsTUFBTSxLQUFLO0FBQ3JGLGtCQUFRLE9BQU87QUFBQSxZQUNiLE1BQU07QUFBQSxZQUNOLFdBQVc7QUFBQSxZQUNYLE1BQU07QUFBQSxjQUNKLFVBQVUsUUFBUTtBQUFBLGNBQ2xCLE9BQU8saUJBQWlCLFFBQVEsTUFBTSxTQUFTLElBQUksS0FBSyxVQUFVLEtBQUs7QUFBQSxZQUN6RTtBQUFBLFVBQ0YsQ0FBQztBQUFBLFFBQ0g7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDRixDQUFDO0FBRUQsSUFBTyxnQ0FBUTsiLAogICJuYW1lcyI6IFtdCn0K
@@ -0,0 +1,269 @@
1
+ // src/agent/fetch-then.ts
2
+ import { strict as assert } from "node:assert";
3
+ import "eslint";
4
+ import { getEnclosingFunction, getEnclosingStatement, getParent, isUsedInArrayOrAsArgument } from "../library/tree.mjs";
5
+ import getDocumentationUrl from "../get-documentation-url.mjs";
6
+ import { getIndentation } from "../library/format.mjs";
7
+ import { isValidPropertyName } from "../library/variable.mjs";
8
+ import { hasAssertions, isInvalidResponseHeadersAccess } from "./fetch.mjs";
9
+ import { replaceEndpointUrlPrefixWithBasePath } from "./url.mjs";
10
+ var ruleId = "fetch-then";
11
+ function analyzeFixtureCall(call, results, sourceCode) {
12
+ const parent = getParent(call);
13
+ if (!parent) {
14
+ return;
15
+ }
16
+ let nextCall;
17
+ if (parent.type !== "MemberExpression") {
18
+ results.fixtureNode = call;
19
+ return;
20
+ }
21
+ if (parent.property.type === "Identifier") {
22
+ if (parent.property.name === "expect") {
23
+ const assertionCall = getParent(parent);
24
+ assert.ok(assertionCall && assertionCall.type === "CallExpression");
25
+ results.assertions = [...results.assertions ?? [], assertionCall.arguments];
26
+ nextCall = assertionCall;
27
+ } else if (parent.property.name === "send") {
28
+ const sendRequestBodyCall = getParent(parent);
29
+ assert.ok(sendRequestBodyCall && sendRequestBodyCall.type === "CallExpression");
30
+ results.requestBody = sendRequestBodyCall.arguments[0];
31
+ nextCall = sendRequestBodyCall;
32
+ } else if (parent.property.name === "set") {
33
+ const setRequestHeaderCall = getParent(parent);
34
+ assert.ok(setRequestHeaderCall && setRequestHeaderCall.type === "CallExpression");
35
+ const [name, value] = setRequestHeaderCall.arguments;
36
+ results.requestHeaders = [...results.requestHeaders ?? [], { name, value }];
37
+ nextCall = setRequestHeaderCall;
38
+ }
39
+ } else {
40
+ throw new Error(`Unexpected expression in fixture/supertest call ${sourceCode.getText(parent)}.`);
41
+ }
42
+ if (nextCall) {
43
+ analyzeFixtureCall(nextCall, results, sourceCode);
44
+ }
45
+ }
46
+ function createResponseAssertions(fixtureCallInformation, sourceCode, responseVariableName) {
47
+ let statusAssertion;
48
+ const nonStatusAssertions = [];
49
+ for (const expectArguments of fixtureCallInformation.assertions ?? []) {
50
+ if (expectArguments.length === 1) {
51
+ const [assertionArgument] = expectArguments;
52
+ assert.ok(assertionArgument);
53
+ if (assertionArgument.type === "MemberExpression" && assertionArgument.object.type === "Identifier" && assertionArgument.object.name === "StatusCodes" || assertionArgument.type === "Literal" || sourceCode.getText(assertionArgument).includes("StatusCodes.")) {
54
+ statusAssertion = `assert.equal(${responseVariableName}.status, ${sourceCode.getText(assertionArgument)})`;
55
+ } else if (assertionArgument.type === "ArrowFunctionExpression") {
56
+ let functionBody = sourceCode.getText(assertionArgument.body);
57
+ const [originalResponseArgument] = assertionArgument.params;
58
+ assert.ok(originalResponseArgument?.type === "Identifier");
59
+ const originalResponseArgumentName = originalResponseArgument.name;
60
+ if (originalResponseArgumentName !== responseVariableName) {
61
+ functionBody = functionBody.replace(
62
+ new RegExp(`\\b${originalResponseArgumentName}\\b`, "ug"),
63
+ responseVariableName
64
+ );
65
+ }
66
+ nonStatusAssertions.push(`assert.doesNotThrow(()=>${functionBody})`);
67
+ } else if (assertionArgument.type === "Identifier") {
68
+ nonStatusAssertions.push(
69
+ `assert.doesNotThrow(()=>${sourceCode.getText(assertionArgument)}(${responseVariableName}))`
70
+ );
71
+ } else if (assertionArgument.type === "ObjectExpression" || assertionArgument.type === "CallExpression") {
72
+ nonStatusAssertions.push(
73
+ `assert.deepEqual(await ${responseVariableName}.json(), ${sourceCode.getText(assertionArgument)})`
74
+ );
75
+ } else {
76
+ throw new Error(`Unexpected Supertest assertion argument: ".expect(${sourceCode.getText(assertionArgument)})`);
77
+ }
78
+ } else if (expectArguments.length === 2) {
79
+ const [headerName, headerValue] = expectArguments;
80
+ assert.ok(headerName && headerValue);
81
+ const headersReference = `${responseVariableName}.headers`;
82
+ if (headerValue.type === "Literal" && headerValue.value instanceof RegExp) {
83
+ nonStatusAssertions.push(
84
+ `assert.ok(${headersReference}.get(${sourceCode.getText(headerName)}).match(${sourceCode.getText(headerValue)}))`
85
+ );
86
+ } else {
87
+ nonStatusAssertions.push(
88
+ `assert.equal(${headersReference}.get(${sourceCode.getText(headerName)}), ${sourceCode.getText(headerValue)})`
89
+ );
90
+ }
91
+ }
92
+ }
93
+ return {
94
+ statusAssertion,
95
+ nonStatusAssertions
96
+ };
97
+ }
98
+ function getResponseHeadersAccesses(responseVariables, scopeManager, sourceCode) {
99
+ const responseHeadersAccesses = [];
100
+ for (const responseVariable of responseVariables) {
101
+ for (const responseReference of responseVariable.references) {
102
+ const responseAccess = getParent(responseReference.identifier);
103
+ if (!responseAccess || responseAccess.type !== "MemberExpression") {
104
+ continue;
105
+ }
106
+ const responseAccessParent = getParent(responseAccess);
107
+ if (!responseAccessParent) {
108
+ continue;
109
+ }
110
+ if (responseAccessParent.type === "CallExpression" && responseAccessParent.arguments[0]?.type === "ArrowFunctionExpression") {
111
+ responseHeadersAccesses.push(
112
+ ...getResponseHeadersAccesses(
113
+ scopeManager.getDeclaredVariables(responseAccessParent.arguments[0]),
114
+ scopeManager,
115
+ sourceCode
116
+ )
117
+ );
118
+ continue;
119
+ }
120
+ if (responseAccess.computed && responseAccess.property.type === "Literal" && responseAccessParent.type === "MemberExpression") {
121
+ responseHeadersAccesses.push(responseAccessParent);
122
+ } else {
123
+ responseHeadersAccesses.push(responseAccess);
124
+ }
125
+ }
126
+ }
127
+ return responseHeadersAccesses;
128
+ }
129
+ var rule = {
130
+ meta: {
131
+ type: "suggestion",
132
+ docs: {
133
+ description: "Prefer native fetch API over customized fixture API.",
134
+ url: getDocumentationUrl(ruleId)
135
+ },
136
+ messages: {
137
+ preferNativeFetch: "Prefer native fetch API over customized fixture API.",
138
+ shouldUseHeaderGetter: "Getter should be used to access response headers.",
139
+ unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}.'
140
+ },
141
+ fixable: "code",
142
+ schema: []
143
+ },
144
+ create(context) {
145
+ const sourceCode = context.sourceCode;
146
+ const scopeManager = sourceCode.scopeManager;
147
+ return {
148
+ 'CallExpression[callee.object.object.name="fixture"][callee.object.property.name="api"]': (fixtureCall) => {
149
+ try {
150
+ if (!hasAssertions(fixtureCall)) {
151
+ return;
152
+ }
153
+ if (!(isUsedInArrayOrAsArgument(fixtureCall) || getEnclosingFunction(fixtureCall)?.async === false)) {
154
+ return;
155
+ }
156
+ assert.ok(fixtureCall.type === "CallExpression");
157
+ const fixtureFunction = fixtureCall.callee;
158
+ assert.ok(fixtureFunction.type === "MemberExpression");
159
+ const indentation = getIndentation(fixtureCall, sourceCode);
160
+ const [urlArgumentNode] = fixtureCall.arguments;
161
+ assert.ok(urlArgumentNode !== void 0);
162
+ const fixtureCallInformation = {};
163
+ analyzeFixtureCall(fixtureCall, fixtureCallInformation, sourceCode);
164
+ const originalUrlArgumentText = sourceCode.getText(urlArgumentNode);
165
+ const fetchUrlArgumentText = replaceEndpointUrlPrefixWithBasePath(originalUrlArgumentText);
166
+ const methodNode = fixtureFunction.property;
167
+ assert.ok(methodNode.type === "Identifier");
168
+ const fetchRequestArgumentLines = [
169
+ "{",
170
+ ` method: '${methodNode.name.toUpperCase()}',`,
171
+ ...fixtureCallInformation.requestBody ? [` body: JSON.stringify(${sourceCode.getText(fixtureCallInformation.requestBody)}),`] : [],
172
+ ...fixtureCallInformation.requestHeaders ? [
173
+ ` headers: {`,
174
+ ...fixtureCallInformation.requestHeaders.map(
175
+ ({ name, value }) => (
176
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, no-nested-ternary, sonarjs/no-nested-template-literals
177
+ ` ${name.type === "Literal" ? isValidPropertyName(name.value) ? name.value : `'${name.value}'` : `[${sourceCode.getText(name)}]`}: ${sourceCode.getText(value)},`
178
+ )
179
+ ),
180
+ ` },`
181
+ ] : [],
182
+ "}"
183
+ ].join(`
184
+ ${indentation}`);
185
+ const responseVariableNameToUse = "res";
186
+ const { statusAssertion, nonStatusAssertions } = createResponseAssertions(
187
+ fixtureCallInformation,
188
+ sourceCode,
189
+ responseVariableNameToUse
190
+ );
191
+ const disableLintComment = "// eslint-disable-next-line @checkdigit/no-promise-instance-method";
192
+ const fetchCallText = `fetch(${fetchUrlArgumentText}, ${fetchRequestArgumentLines})`;
193
+ const appendingAssignmentAndAssertionText = [
194
+ ...statusAssertion !== void 0 ? [statusAssertion] : [],
195
+ ...nonStatusAssertions
196
+ ].join(`;
197
+ ${indentation}`);
198
+ const replacementText = fixtureCallInformation.assertions ? [
199
+ disableLintComment,
200
+ `${fetchCallText}.then((${responseVariableNameToUse}) => {`,
201
+ appendingAssignmentAndAssertionText === "" ? "" : ` ${appendingAssignmentAndAssertionText};`,
202
+ ` return ${responseVariableNameToUse};`,
203
+ `})`
204
+ ].join(`
205
+ ${indentation}`) : fetchCallText;
206
+ context.report({
207
+ node: fixtureCall,
208
+ messageId: "preferNativeFetch",
209
+ fix(fixer) {
210
+ return fixer.replaceText(fixtureCallInformation.fixtureNode, replacementText);
211
+ }
212
+ });
213
+ const responsesVariable = getEnclosingStatement(fixtureCallInformation.fixtureNode);
214
+ if (!responsesVariable) {
215
+ return;
216
+ }
217
+ const responseVariableReferences = scopeManager.getDeclaredVariables(responsesVariable);
218
+ const responseHeadersAccesses = getResponseHeadersAccesses(
219
+ responseVariableReferences,
220
+ scopeManager,
221
+ sourceCode
222
+ );
223
+ for (const responseHeadersAccess of responseHeadersAccesses) {
224
+ if (isInvalidResponseHeadersAccess(responseHeadersAccess)) {
225
+ const headerAccess = getParent(responseHeadersAccess);
226
+ if (headerAccess?.type === "MemberExpression") {
227
+ const headerNameNode = headerAccess.property;
228
+ const headerName = headerAccess.computed ? sourceCode.getText(headerNameNode) : `'${sourceCode.getText(headerNameNode)}'`;
229
+ const headerAccessReplacementText = `${sourceCode.getText(headerAccess.object)}.get(${headerName})`;
230
+ context.report({
231
+ node: headerAccess,
232
+ messageId: "shouldUseHeaderGetter",
233
+ fix(fixer) {
234
+ return fixer.replaceText(headerAccess, headerAccessReplacementText);
235
+ }
236
+ });
237
+ } else if (headerAccess?.type === "CallExpression" && responseHeadersAccess.property.type === "Identifier" && responseHeadersAccess.property.name === "get") {
238
+ const headerAccessReplacementText = `${sourceCode.getText(responseHeadersAccess.object)}.headers.get(${sourceCode.getText(headerAccess.arguments[0])})`;
239
+ context.report({
240
+ node: headerAccess,
241
+ messageId: "shouldUseHeaderGetter",
242
+ fix(fixer) {
243
+ return fixer.replaceText(headerAccess, headerAccessReplacementText);
244
+ }
245
+ });
246
+ }
247
+ }
248
+ }
249
+ } catch (error) {
250
+ console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
251
+ context.report({
252
+ node: fixtureCall,
253
+ messageId: "unknownError",
254
+ data: {
255
+ fileName: context.filename,
256
+ error: error instanceof Error ? error.toString() : JSON.stringify(error)
257
+ }
258
+ });
259
+ }
260
+ }
261
+ };
262
+ }
263
+ };
264
+ var fetch_then_default = rule;
265
+ export {
266
+ fetch_then_default as default,
267
+ ruleId
268
+ };
269
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2FnZW50L2ZldGNoLXRoZW4udHMiXSwKICAibWFwcGluZ3MiOiAiO0FBUUEsU0FBUyxVQUFVLGNBQWM7QUFHakMsT0FBa0Q7QUFFbEQsU0FBUyxzQkFBc0IsdUJBQXVCLFdBQVcsaUNBQWlDO0FBQ2xHLE9BQU8seUJBQXlCO0FBQ2hDLFNBQVMsc0JBQXNCO0FBQy9CLFNBQVMsMkJBQTJCO0FBQ3BDLFNBQVMsZUFBZSxzQ0FBc0M7QUFDOUQsU0FBUyw0Q0FBNEM7QUFFOUMsSUFBTSxTQUFTO0FBVXRCLFNBQVMsbUJBQW1CLE1BQTRCLFNBQWlDLFlBQXdCO0FBQy9HLFFBQU0sU0FBUyxVQUFVLElBQUk7QUFDN0IsTUFBSSxDQUFDLFFBQVE7QUFDWDtBQUFBLEVBQ0Y7QUFFQSxNQUFJO0FBQ0osTUFBSSxPQUFPLFNBQVMsb0JBQW9CO0FBQ3RDLFlBQVEsY0FBYztBQUN0QjtBQUFBLEVBQ0Y7QUFFQSxNQUFJLE9BQU8sU0FBUyxTQUFTLGNBQWM7QUFDekMsUUFBSSxPQUFPLFNBQVMsU0FBUyxVQUFVO0FBRXJDLFlBQU0sZ0JBQWdCLFVBQVUsTUFBTTtBQUN0QyxhQUFPLEdBQUcsaUJBQWlCLGNBQWMsU0FBUyxnQkFBZ0I7QUFDbEUsY0FBUSxhQUFhLENBQUMsR0FBSSxRQUFRLGNBQWMsQ0FBQyxHQUFJLGNBQWMsU0FBeUI7QUFDNUYsaUJBQVc7QUFBQSxJQUNiLFdBQVcsT0FBTyxTQUFTLFNBQVMsUUFBUTtBQUUxQyxZQUFNLHNCQUFzQixVQUFVLE1BQU07QUFDNUMsYUFBTyxHQUFHLHVCQUF1QixvQkFBb0IsU0FBUyxnQkFBZ0I7QUFDOUUsY0FBUSxjQUFjLG9CQUFvQixVQUFVLENBQUM7QUFDckQsaUJBQVc7QUFBQSxJQUNiLFdBQVcsT0FBTyxTQUFTLFNBQVMsT0FBTztBQUV6QyxZQUFNLHVCQUF1QixVQUFVLE1BQU07QUFDN0MsYUFBTyxHQUFHLHdCQUF3QixxQkFBcUIsU0FBUyxnQkFBZ0I7QUFDaEYsWUFBTSxDQUFDLE1BQU0sS0FBSyxJQUFJLHFCQUFxQjtBQUMzQyxjQUFRLGlCQUFpQixDQUFDLEdBQUksUUFBUSxrQkFBa0IsQ0FBQyxHQUFJLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDNUUsaUJBQVc7QUFBQSxJQUNiO0FBQUEsRUFDRixPQUFPO0FBQ0wsVUFBTSxJQUFJLE1BQU0sbURBQW1ELFdBQVcsUUFBUSxNQUFNLENBQUMsR0FBRztBQUFBLEVBQ2xHO0FBQ0EsTUFBSSxVQUFVO0FBQ1osdUJBQW1CLFVBQVUsU0FBUyxVQUFVO0FBQUEsRUFDbEQ7QUFDRjtBQUdBLFNBQVMseUJBQ1Asd0JBQ0EsWUFDQSxzQkFDQTtBQUNBLE1BQUk7QUFDSixRQUFNLHNCQUFnQyxDQUFDO0FBQ3ZDLGFBQVcsbUJBQW1CLHVCQUF1QixjQUFjLENBQUMsR0FBRztBQUNyRSxRQUFJLGdCQUFnQixXQUFXLEdBQUc7QUFDaEMsWUFBTSxDQUFDLGlCQUFpQixJQUFJO0FBQzVCLGFBQU8sR0FBRyxpQkFBaUI7QUFDM0IsVUFDRyxrQkFBa0IsU0FBUyxzQkFDMUIsa0JBQWtCLE9BQU8sU0FBUyxnQkFDbEMsa0JBQWtCLE9BQU8sU0FBUyxpQkFDcEMsa0JBQWtCLFNBQVMsYUFDM0IsV0FBVyxRQUFRLGlCQUFpQixFQUFFLFNBQVMsY0FBYyxHQUM3RDtBQUVBLDBCQUFrQixnQkFBZ0Isb0JBQW9CLFlBQVksV0FBVyxRQUFRLGlCQUFpQixDQUFDO0FBQUEsTUFDekcsV0FBVyxrQkFBa0IsU0FBUywyQkFBMkI7QUFFL0QsWUFBSSxlQUFlLFdBQVcsUUFBUSxrQkFBa0IsSUFBSTtBQUU1RCxjQUFNLENBQUMsd0JBQXdCLElBQUksa0JBQWtCO0FBQ3JELGVBQU8sR0FBRywwQkFBMEIsU0FBUyxZQUFZO0FBQ3pELGNBQU0sK0JBQStCLHlCQUF5QjtBQUM5RCxZQUFJLGlDQUFpQyxzQkFBc0I7QUFDekQseUJBQWUsYUFBYTtBQUFBLFlBQzFCLElBQUksT0FBTyxNQUFNLDRCQUE0QixPQUFPLElBQUk7QUFBQSxZQUN4RDtBQUFBLFVBQ0Y7QUFBQSxRQUNGO0FBQ0EsNEJBQW9CLEtBQUssMkJBQTJCLFlBQVksR0FBRztBQUFBLE1BQ3JFLFdBQVcsa0JBQWtCLFNBQVMsY0FBYztBQUVsRCw0QkFBb0I7QUFBQSxVQUNsQiwyQkFBMkIsV0FBVyxRQUFRLGlCQUFpQixDQUFDLElBQUksb0JBQW9CO0FBQUEsUUFDMUY7QUFBQSxNQUNGLFdBQVcsa0JBQWtCLFNBQVMsc0JBQXNCLGtCQUFrQixTQUFTLGtCQUFrQjtBQUV2Ryw0QkFBb0I7QUFBQSxVQUNsQiwwQkFBMEIsb0JBQW9CLFlBQVksV0FBVyxRQUFRLGlCQUFpQixDQUFDO0FBQUEsUUFDakc7QUFBQSxNQUNGLE9BQU87QUFDTCxjQUFNLElBQUksTUFBTSxxREFBcUQsV0FBVyxRQUFRLGlCQUFpQixDQUFDLEdBQUc7QUFBQSxNQUMvRztBQUFBLElBQ0YsV0FBVyxnQkFBZ0IsV0FBVyxHQUFHO0FBRXZDLFlBQU0sQ0FBQyxZQUFZLFdBQVcsSUFBSTtBQUNsQyxhQUFPLEdBQUcsY0FBYyxXQUFXO0FBQ25DLFlBQU0sbUJBQW1CLEdBQUcsb0JBQW9CO0FBQ2hELFVBQUksWUFBWSxTQUFTLGFBQWEsWUFBWSxpQkFBaUIsUUFBUTtBQUN6RSw0QkFBb0I7QUFBQSxVQUNsQixhQUFhLGdCQUFnQixRQUFRLFdBQVcsUUFBUSxVQUFVLENBQUMsV0FBVyxXQUFXLFFBQVEsV0FBVyxDQUFDO0FBQUEsUUFDL0c7QUFBQSxNQUNGLE9BQU87QUFDTCw0QkFBb0I7QUFBQSxVQUNsQixnQkFBZ0IsZ0JBQWdCLFFBQVEsV0FBVyxRQUFRLFVBQVUsQ0FBQyxNQUFNLFdBQVcsUUFBUSxXQUFXLENBQUM7QUFBQSxRQUM3RztBQUFBLE1BQ0Y7QUFBQSxJQUNGO0FBQUEsRUFDRjtBQUNBLFNBQU87QUFBQSxJQUNMO0FBQUEsSUFDQTtBQUFBLEVBQ0Y7QUFDRjtBQUVBLFNBQVMsMkJBQ1AsbUJBQ0EsY0FDQSxZQUNBO0FBQ0EsUUFBTSwwQkFBOEMsQ0FBQztBQUNyRCxhQUFXLG9CQUFvQixtQkFBbUI7QUFDaEQsZUFBVyxxQkFBcUIsaUJBQWlCLFlBQVk7QUFDM0QsWUFBTSxpQkFBaUIsVUFBVSxrQkFBa0IsVUFBVTtBQUM3RCxVQUFJLENBQUMsa0JBQWtCLGVBQWUsU0FBUyxvQkFBb0I7QUFDakU7QUFBQSxNQUNGO0FBRUEsWUFBTSx1QkFBdUIsVUFBVSxjQUFjO0FBQ3JELFVBQUksQ0FBQyxzQkFBc0I7QUFDekI7QUFBQSxNQUNGO0FBRUEsVUFDRSxxQkFBcUIsU0FBUyxvQkFDOUIscUJBQXFCLFVBQVUsQ0FBQyxHQUFHLFNBQVMsMkJBQzVDO0FBRUEsZ0NBQXdCO0FBQUEsVUFDdEIsR0FBRztBQUFBLFlBQ0QsYUFBYSxxQkFBcUIscUJBQXFCLFVBQVUsQ0FBQyxDQUFDO0FBQUEsWUFDbkU7QUFBQSxZQUNBO0FBQUEsVUFDRjtBQUFBLFFBQ0Y7QUFDQTtBQUFBLE1BQ0Y7QUFFQSxVQUNFLGVBQWUsWUFDZixlQUFlLFNBQVMsU0FBUyxhQUNqQyxxQkFBcUIsU0FBUyxvQkFDOUI7QUFFQSxnQ0FBd0IsS0FBSyxvQkFBb0I7QUFBQSxNQUNuRCxPQUFPO0FBQ0wsZ0NBQXdCLEtBQUssY0FBYztBQUFBLE1BQzdDO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDQSxTQUFPO0FBQ1Q7QUFFQSxJQUFNLE9BQXdCO0FBQUEsRUFDNUIsTUFBTTtBQUFBLElBQ0osTUFBTTtBQUFBLElBQ04sTUFBTTtBQUFBLE1BQ0osYUFBYTtBQUFBLE1BQ2IsS0FBSyxvQkFBb0IsTUFBTTtBQUFBLElBQ2pDO0FBQUEsSUFDQSxVQUFVO0FBQUEsTUFDUixtQkFBbUI7QUFBQSxNQUNuQix1QkFBdUI7QUFBQSxNQUN2QixjQUFjO0FBQUEsSUFDaEI7QUFBQSxJQUNBLFNBQVM7QUFBQSxJQUNULFFBQVEsQ0FBQztBQUFBLEVBQ1g7QUFBQSxFQUVBLE9BQU8sU0FBUztBQUNkLFVBQU0sYUFBYSxRQUFRO0FBQzNCLFVBQU0sZUFBZSxXQUFXO0FBRWhDLFdBQU87QUFBQSxNQUNMLDBGQUEwRixDQUN4RixnQkFFRztBQUNILFlBQUk7QUFDRixjQUFJLENBQUMsY0FBYyxXQUFXLEdBQUc7QUFFL0I7QUFBQSxVQUNGO0FBRUEsY0FBSSxFQUFFLDBCQUEwQixXQUFXLEtBQUsscUJBQXFCLFdBQVcsR0FBRyxVQUFVLFFBQVE7QUFDbkc7QUFBQSxVQUNGO0FBRUEsaUJBQU8sR0FBRyxZQUFZLFNBQVMsZ0JBQWdCO0FBQy9DLGdCQUFNLGtCQUFrQixZQUFZO0FBQ3BDLGlCQUFPLEdBQUcsZ0JBQWdCLFNBQVMsa0JBQWtCO0FBQ3JELGdCQUFNLGNBQWMsZUFBZSxhQUFhLFVBQVU7QUFFMUQsZ0JBQU0sQ0FBQyxlQUFlLElBQUksWUFBWTtBQUN0QyxpQkFBTyxHQUFHLG9CQUFvQixNQUFTO0FBRXZDLGdCQUFNLHlCQUF5QixDQUFDO0FBQ2hDLDZCQUFtQixhQUFhLHdCQUF3QixVQUFVO0FBR2xFLGdCQUFNLDBCQUEwQixXQUFXLFFBQVEsZUFBZTtBQUNsRSxnQkFBTSx1QkFBdUIscUNBQXFDLHVCQUF1QjtBQUd6RixnQkFBTSxhQUFhLGdCQUFnQjtBQUNuQyxpQkFBTyxHQUFHLFdBQVcsU0FBUyxZQUFZO0FBQzFDLGdCQUFNLDRCQUE0QjtBQUFBLFlBQ2hDO0FBQUEsWUFDQSxjQUFjLFdBQVcsS0FBSyxZQUFZLENBQUM7QUFBQSxZQUMzQyxHQUFJLHVCQUF1QixjQUN2QixDQUFDLDBCQUEwQixXQUFXLFFBQVEsdUJBQXVCLFdBQVcsQ0FBQyxJQUFJLElBQ3JGLENBQUM7QUFBQSxZQUNMLEdBQUksdUJBQXVCLGlCQUN2QjtBQUFBLGNBQ0U7QUFBQSxjQUNBLEdBQUcsdUJBQXVCLGVBQWU7QUFBQSxnQkFDdkMsQ0FBQyxFQUFFLE1BQU0sTUFBTTtBQUFBO0FBQUEsa0JBRWIsT0FBTyxLQUFLLFNBQVMsWUFBYSxvQkFBb0IsS0FBSyxLQUFLLElBQUksS0FBSyxRQUFRLElBQUksS0FBSyxLQUFLLE1BQU8sSUFBSSxXQUFXLFFBQVEsSUFBSSxDQUFDLEdBQUcsS0FBSyxXQUFXLFFBQVEsS0FBSyxDQUFDO0FBQUE7QUFBQSxjQUN2SztBQUFBLGNBQ0E7QUFBQSxZQUNGLElBQ0EsQ0FBQztBQUFBLFlBQ0w7QUFBQSxVQUNGLEVBQUUsS0FBSztBQUFBLEVBQUssV0FBVyxFQUFFO0FBRXpCLGdCQUFNLDRCQUE0QjtBQUNsQyxnQkFBTSxFQUFFLGlCQUFpQixvQkFBb0IsSUFBSTtBQUFBLFlBQy9DO0FBQUEsWUFDQTtBQUFBLFlBQ0E7QUFBQSxVQUNGO0FBR0EsZ0JBQU0scUJBQXFCO0FBQzNCLGdCQUFNLGdCQUFnQixTQUFTLG9CQUFvQixLQUFLLHlCQUF5QjtBQUNqRixnQkFBTSxzQ0FBc0M7QUFBQSxZQUMxQyxHQUFJLG9CQUFvQixTQUFZLENBQUMsZUFBZSxJQUFJLENBQUM7QUFBQSxZQUN6RCxHQUFHO0FBQUEsVUFDTCxFQUFFLEtBQUs7QUFBQSxFQUFNLFdBQVcsRUFBRTtBQUMxQixnQkFBTSxrQkFBa0IsdUJBQXVCLGFBQzNDO0FBQUEsWUFDRTtBQUFBLFlBQ0EsR0FBRyxhQUFhLFVBQVUseUJBQXlCO0FBQUEsWUFDbkQsd0NBQXdDLEtBQUssS0FBSyxLQUFLLG1DQUFtQztBQUFBLFlBQzFGLFlBQVkseUJBQXlCO0FBQUEsWUFDckM7QUFBQSxVQUNGLEVBQUUsS0FBSztBQUFBLEVBQUssV0FBVyxFQUFFLElBQ3pCO0FBRUosa0JBQVEsT0FBTztBQUFBLFlBQ2IsTUFBTTtBQUFBLFlBQ04sV0FBVztBQUFBLFlBQ1gsSUFBSSxPQUFPO0FBQ1QscUJBQU8sTUFBTSxZQUFZLHVCQUF1QixhQUFhLGVBQWU7QUFBQSxZQUM5RTtBQUFBLFVBQ0YsQ0FBQztBQUVELGdCQUFNLG9CQUFvQixzQkFBc0IsdUJBQXVCLFdBQVc7QUFDbEYsY0FBSSxDQUFDLG1CQUFtQjtBQUN0QjtBQUFBLFVBQ0Y7QUFFQSxnQkFBTSw2QkFBNkIsYUFBYSxxQkFBcUIsaUJBQWlCO0FBQ3RGLGdCQUFNLDBCQUEwQjtBQUFBLFlBQzlCO0FBQUEsWUFDQTtBQUFBLFlBQ0E7QUFBQSxVQUNGO0FBQ0EscUJBQVcseUJBQXlCLHlCQUF5QjtBQUMzRCxnQkFBSSwrQkFBK0IscUJBQXFCLEdBQUc7QUFDekQsb0JBQU0sZUFBZSxVQUFVLHFCQUFxQjtBQUNwRCxrQkFBSSxjQUFjLFNBQVMsb0JBQW9CO0FBQzdDLHNCQUFNLGlCQUFpQixhQUFhO0FBQ3BDLHNCQUFNLGFBQWEsYUFBYSxXQUM1QixXQUFXLFFBQVEsY0FBYyxJQUNqQyxJQUFJLFdBQVcsUUFBUSxjQUFjLENBQUM7QUFDMUMsc0JBQU0sOEJBQThCLEdBQUcsV0FBVyxRQUFRLGFBQWEsTUFBTSxDQUFDLFFBQVEsVUFBVTtBQUVoRyx3QkFBUSxPQUFPO0FBQUEsa0JBQ2IsTUFBTTtBQUFBLGtCQUNOLFdBQVc7QUFBQSxrQkFDWCxJQUFJLE9BQU87QUFDVCwyQkFBTyxNQUFNLFlBQVksY0FBYywyQkFBMkI7QUFBQSxrQkFDcEU7QUFBQSxnQkFDRixDQUFDO0FBQUEsY0FDSCxXQUNFLGNBQWMsU0FBUyxvQkFDdkIsc0JBQXNCLFNBQVMsU0FBUyxnQkFDeEMsc0JBQXNCLFNBQVMsU0FBUyxPQUN4QztBQUNBLHNCQUFNLDhCQUE4QixHQUFHLFdBQVcsUUFBUSxzQkFBc0IsTUFBTSxDQUFDLGdCQUFnQixXQUFXLFFBQVEsYUFBYSxVQUFVLENBQUMsQ0FBQyxDQUFDO0FBRXBKLHdCQUFRLE9BQU87QUFBQSxrQkFDYixNQUFNO0FBQUEsa0JBQ04sV0FBVztBQUFBLGtCQUNYLElBQUksT0FBTztBQUNULDJCQUFPLE1BQU0sWUFBWSxjQUFjLDJCQUEyQjtBQUFBLGtCQUNwRTtBQUFBLGdCQUNGLENBQUM7QUFBQSxjQUNIO0FBQUEsWUFDRjtBQUFBLFVBQ0Y7QUFBQSxRQUNGLFNBQVMsT0FBTztBQUVkLGtCQUFRLE1BQU0sbUJBQW1CLE1BQU0sbUJBQW1CLFFBQVEsUUFBUSxNQUFNLEtBQUs7QUFDckYsa0JBQVEsT0FBTztBQUFBLFlBQ2IsTUFBTTtBQUFBLFlBQ04sV0FBVztBQUFBLFlBQ1gsTUFBTTtBQUFBLGNBQ0osVUFBVSxRQUFRO0FBQUEsY0FDbEIsT0FBTyxpQkFBaUIsUUFBUSxNQUFNLFNBQVMsSUFBSSxLQUFLLFVBQVUsS0FBSztBQUFBLFlBQ3pFO0FBQUEsVUFDRixDQUFDO0FBQUEsUUFDSDtBQUFBLE1BQ0Y7QUFBQSxJQUNGO0FBQUEsRUFDRjtBQUNGO0FBRUEsSUFBTyxxQkFBUTsiLAogICJuYW1lcyI6IFtdCn0K
@@ -0,0 +1,38 @@
1
+ // src/agent/fetch.ts
2
+ import { getParent, isBlockStatement } from "../library/tree.mjs";
3
+ function getResponseBodyRetrievalText(responseVariableName) {
4
+ return `await ${responseVariableName}.json()`;
5
+ }
6
+ function getResponseHeadersRetrievalText(responseVariableName) {
7
+ return `${responseVariableName}.headers`;
8
+ }
9
+ function isInvalidResponseHeadersAccess(responseHeadersAccess) {
10
+ const responseHeaderAccessParent = getParent(responseHeadersAccess);
11
+ if (responseHeaderAccessParent?.type === "VariableDeclarator") {
12
+ return false;
13
+ }
14
+ if (responseHeaderAccessParent?.type === "CallExpression" && responseHeaderAccessParent.callee.type === "MemberExpression" && responseHeaderAccessParent.callee.property.type === "Identifier" && responseHeaderAccessParent.callee.property.name === "get") {
15
+ return true;
16
+ }
17
+ return !(responseHeaderAccessParent?.type === "MemberExpression" && responseHeaderAccessParent.property.type === "Identifier" && responseHeaderAccessParent.property.name === "get");
18
+ }
19
+ function hasAssertions(fixtureCall) {
20
+ if (isBlockStatement(fixtureCall)) {
21
+ return false;
22
+ }
23
+ const parent = getParent(fixtureCall);
24
+ if (!parent) {
25
+ return false;
26
+ }
27
+ if (parent.type === "MemberExpression" && parent.property.type === "Identifier" && parent.property.name === "expect" && getParent(parent)?.type === "CallExpression") {
28
+ return true;
29
+ }
30
+ return hasAssertions(parent);
31
+ }
32
+ export {
33
+ getResponseBodyRetrievalText,
34
+ getResponseHeadersRetrievalText,
35
+ hasAssertions,
36
+ isInvalidResponseHeadersAccess
37
+ };
38
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2FnZW50L2ZldGNoLnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQUlBLFNBQVMsV0FBVyx3QkFBd0I7QUFFckMsU0FBUyw2QkFBNkIsc0JBQThCO0FBQ3pFLFNBQU8sU0FBUyxvQkFBb0I7QUFDdEM7QUFFTyxTQUFTLGdDQUFnQyxzQkFBOEI7QUFDNUUsU0FBTyxHQUFHLG9CQUFvQjtBQUNoQztBQUVPLFNBQVMsK0JBQStCLHVCQUFzQztBQUNuRixRQUFNLDZCQUE2QixVQUFVLHFCQUFxQjtBQUNsRSxNQUFJLDRCQUE0QixTQUFTLHNCQUFzQjtBQUM3RCxXQUFPO0FBQUEsRUFDVDtBQUVBLE1BQ0UsNEJBQTRCLFNBQVMsb0JBQ3JDLDJCQUEyQixPQUFPLFNBQVMsc0JBQzNDLDJCQUEyQixPQUFPLFNBQVMsU0FBUyxnQkFDcEQsMkJBQTJCLE9BQU8sU0FBUyxTQUFTLE9BQ3BEO0FBQ0EsV0FBTztBQUFBLEVBQ1Q7QUFFQSxTQUFPLEVBQ0wsNEJBQTRCLFNBQVMsc0JBQ3JDLDJCQUEyQixTQUFTLFNBQVMsZ0JBQzdDLDJCQUEyQixTQUFTLFNBQVM7QUFFakQ7QUFFTyxTQUFTLGNBQWMsYUFBNEI7QUFDeEQsTUFBSSxpQkFBaUIsV0FBVyxHQUFHO0FBQ2pDLFdBQU87QUFBQSxFQUNUO0FBRUEsUUFBTSxTQUFTLFVBQVUsV0FBVztBQUNwQyxNQUFJLENBQUMsUUFBUTtBQUNYLFdBQU87QUFBQSxFQUNUO0FBRUEsTUFDRSxPQUFPLFNBQVMsc0JBQ2hCLE9BQU8sU0FBUyxTQUFTLGdCQUN6QixPQUFPLFNBQVMsU0FBUyxZQUN6QixVQUFVLE1BQU0sR0FBRyxTQUFTLGtCQUM1QjtBQUNBLFdBQU87QUFBQSxFQUNUO0FBRUEsU0FBTyxjQUFjLE1BQU07QUFDN0I7IiwKICAibmFtZXMiOiBbXQp9Cg==
@@ -0,0 +1,43 @@
1
+ // src/agent/file.ts
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ function isApiIndexFile(filename) {
5
+ return /.*\/src\/api\/v\d+\/index.ts/u.test(filename);
6
+ }
7
+ function getProjectRootFolder(indexFilename) {
8
+ return indexFilename.substring(0, indexFilename.lastIndexOf("/src/"));
9
+ }
10
+ function getSwaggerPathByIndexFile(indexFilename) {
11
+ return indexFilename.replace(/index\.ts$/u, "swagger.yml");
12
+ }
13
+ function loadSwagger(filename) {
14
+ return fs.readFileSync(filename, "utf8");
15
+ }
16
+ function loadPackageJson(projectRoot) {
17
+ return fs.readFileSync(`${projectRoot}/package.json`, "utf8");
18
+ }
19
+ function getApiFolder(folder) {
20
+ if (/^\/?(?<absolutePath>(?:[^/]+\/)*)src\/api\/v\d+$/u.test(folder)) {
21
+ return folder;
22
+ }
23
+ const upperFolder = folder.substring(0, folder.lastIndexOf("/"));
24
+ return upperFolder.trim() === "" ? void 0 : getApiFolder(upperFolder);
25
+ }
26
+ function getApiIndexPathByFilename(filename) {
27
+ const apiFolder = getApiFolder(filename);
28
+ if (apiFolder === void 0) {
29
+ return void 0;
30
+ }
31
+ const relativePath = path.relative(path.dirname(filename), `${apiFolder}/index`);
32
+ return relativePath.startsWith("../") ? relativePath : `./${relativePath}`;
33
+ }
34
+ export {
35
+ getApiFolder,
36
+ getApiIndexPathByFilename,
37
+ getProjectRootFolder,
38
+ getSwaggerPathByIndexFile,
39
+ isApiIndexFile,
40
+ loadPackageJson,
41
+ loadSwagger
42
+ };
43
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2FnZW50L2ZpbGUudHMiXSwKICAibWFwcGluZ3MiOiAiO0FBRUEsT0FBTyxRQUFRO0FBQ2YsT0FBTyxVQUFVO0FBRVYsU0FBUyxlQUFlLFVBQTJCO0FBQ3hELFNBQU8sZ0NBQWdDLEtBQUssUUFBUTtBQUN0RDtBQUVPLFNBQVMscUJBQXFCLGVBQStCO0FBQ2xFLFNBQU8sY0FBYyxVQUFVLEdBQUcsY0FBYyxZQUFZLE9BQU8sQ0FBQztBQUN0RTtBQUVPLFNBQVMsMEJBQTBCLGVBQStCO0FBQ3ZFLFNBQU8sY0FBYyxRQUFRLGVBQWUsYUFBYTtBQUMzRDtBQUVPLFNBQVMsWUFBWSxVQUEwQjtBQUNwRCxTQUFPLEdBQUcsYUFBYSxVQUFVLE1BQU07QUFDekM7QUFFTyxTQUFTLGdCQUFnQixhQUE2QjtBQUMzRCxTQUFPLEdBQUcsYUFBYSxHQUFHLFdBQVcsaUJBQWlCLE1BQU07QUFDOUQ7QUFFTyxTQUFTLGFBQWEsUUFBb0M7QUFDL0QsTUFBSSxvREFBb0QsS0FBSyxNQUFNLEdBQUc7QUFDcEUsV0FBTztBQUFBLEVBQ1Q7QUFDQSxRQUFNLGNBQWMsT0FBTyxVQUFVLEdBQUcsT0FBTyxZQUFZLEdBQUcsQ0FBQztBQUMvRCxTQUFPLFlBQVksS0FBSyxNQUFNLEtBQUssU0FBWSxhQUFhLFdBQVc7QUFDekU7QUFFTyxTQUFTLDBCQUEwQixVQUFzQztBQUM5RSxRQUFNLFlBQVksYUFBYSxRQUFRO0FBQ3ZDLE1BQUksY0FBYyxRQUFXO0FBQzNCLFdBQU87QUFBQSxFQUNUO0FBRUEsUUFBTSxlQUFlLEtBQUssU0FBUyxLQUFLLFFBQVEsUUFBUSxHQUFHLEdBQUcsU0FBUyxRQUFRO0FBQy9FLFNBQU8sYUFBYSxXQUFXLEtBQUssSUFBSSxlQUFlLEtBQUssWUFBWTtBQUMxRTsiLAogICJuYW1lcyI6IFtdCn0K