@jrpool/kilotest 46.1.2 → 47.0.1

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 (147) hide show
  1. package/.claude/settings.local.json +13 -1
  2. package/.devin/config.local.json +5 -1
  3. package/.github/workflows/ci.yml +0 -3
  4. package/.github/workflows/periodic-smoke-tests.yml +30 -0
  5. package/.github/workflows/publish-mcp.yml +4 -4
  6. package/{alerts.test.js → alerts.test.ts} +17 -29
  7. package/{alerts.js → alerts.ts} +10 -13
  8. package/api/{getReport.test.js → getReport.test.ts} +9 -10
  9. package/api/{getReport.js → getReport.ts} +23 -24
  10. package/api/{listDiagnoses.test.js → listDiagnoses.test.ts} +26 -27
  11. package/api/{listDiagnoses.js → listDiagnoses.ts} +46 -70
  12. package/api/{listIssues.test.js → listIssues.test.ts} +21 -14
  13. package/api/{listIssues.js → listIssues.ts} +75 -74
  14. package/api/{listReports.test.js → listReports.test.ts} +17 -16
  15. package/api/listReports.ts +87 -0
  16. package/api/{listViolators.test.js → listViolators.test.ts} +19 -20
  17. package/api/{listViolators.js → listViolators.ts} +45 -70
  18. package/api/requestFeature.test.ts +54 -0
  19. package/api/{requestFeature.js → requestFeature.ts} +18 -15
  20. package/api/{requestRetest.test.js → requestRetest.test.ts} +33 -28
  21. package/api/{requestRetest.js → requestRetest.ts} +32 -25
  22. package/api/{requestTest.test.js → requestTest.test.ts} +35 -30
  23. package/api/{requestTest.js → requestTest.ts} +35 -28
  24. package/api/{routes.test.js → routes.test.ts} +5 -5
  25. package/api/{routes.js → routes.ts} +6 -6
  26. package/api/{schemas.js → schemas.ts} +69 -50
  27. package/api/{util.test.js → util.test.ts} +31 -11
  28. package/api/{util.js → util.ts} +61 -36
  29. package/api/version.ts +8 -0
  30. package/{apiIntegration.test.js → apiIntegration.test.ts} +12 -13
  31. package/balances.ts +87 -0
  32. package/{checkinfo.js → checkinfo.ts} +24 -20
  33. package/docs/AI-TOOLS.md +4 -4
  34. package/docs/BACKLOG.md +356 -0
  35. package/docs/SERVICE.md +144 -17
  36. package/docs/decisions/002-coverage-tool.md +40 -0
  37. package/docs/decisions/003-adr-threshold.md +34 -0
  38. package/docs/decisions/004-docs-directory.md +23 -0
  39. package/docs/decisions/005-observability.md +28 -0
  40. package/docs/decisions/006-git-workflow.md +28 -0
  41. package/docs/decisions/007-internal-testing.md +34 -0
  42. package/docs/decisions/008-dependencies.md +28 -0
  43. package/docs/decisions/009-qai-integration.md +40 -0
  44. package/docs/decisions/QAI-HISTORICAL-CONTEXT.md +42 -0
  45. package/eslint.config.mjs +27 -0
  46. package/index.html +3 -3
  47. package/{index.test.js → index.test.ts} +380 -143
  48. package/{index.js → index.ts} +382 -283
  49. package/{indexBalances.test.js → indexBalances.test.ts} +21 -17
  50. package/{indexServe.test.js → indexServe.test.ts} +9 -9
  51. package/linkValidation.test.ts +190 -0
  52. package/{mcp.test.js → mcp.test.ts} +53 -26
  53. package/{mcp.js → mcp.ts} +29 -20
  54. package/memory/MEMORY.md +7 -0
  55. package/memory/user_maintainer_context.md +24 -0
  56. package/openapi.yaml +115 -68
  57. package/package.json +35 -15
  58. package/pm2.config.cjs +29 -0
  59. package/{routes.test.js → routes.test.ts} +8 -7
  60. package/{routing.test.js → routing.test.ts} +11 -12
  61. package/scripts/{generate-openapi.js → generate-openapi.ts} +13 -13
  62. package/serve.ts +15 -0
  63. package/smokeTest.ts +234 -0
  64. package/test/dbFixture.ts +21 -0
  65. package/test/fixtures/{buildFixtures.js → buildFixtures.ts} +14 -13
  66. package/test/{setup.cjs → setup.ts} +2 -2
  67. package/tsconfig.json +16 -0
  68. package/{util.test.js → util.test.ts} +350 -257
  69. package/util.ts +921 -0
  70. package/web/ai0BalanceForm/{ai0BalanceForm.test.js → ai0BalanceForm.test.ts} +15 -15
  71. package/web/ai0BalanceForm/{index.js → index.ts} +10 -18
  72. package/web/enqueue/index.ts +56 -0
  73. package/web/enqueueForm/index.html +9 -9
  74. package/web/enqueueForm/index.ts +43 -0
  75. package/web/expungeReportsForm/{expungeReportsForm.test.js → expungeReportsForm.test.ts} +18 -20
  76. package/web/expungeReportsForm/index.ts +18 -0
  77. package/web/hideReportForm/{hideReportForm.test.js → hideReportForm.test.ts} +14 -14
  78. package/web/hideReportForm/{index.js → index.ts} +17 -21
  79. package/web/listDiagnoses/{index.js → index.ts} +48 -58
  80. package/web/listDiagnoses/{listDiagnoses.test.js → listDiagnoses.test.ts} +39 -34
  81. package/web/listIssues/{index.js → index.ts} +67 -92
  82. package/web/listIssues/{listIssues.test.js → listIssues.test.ts} +45 -51
  83. package/web/listReports/index.html +3 -3
  84. package/web/listReports/{index.js → index.ts} +48 -53
  85. package/web/listReports/{listReports.test.js → listReports.test.ts} +41 -43
  86. package/web/listRules/{index.js → index.ts} +18 -23
  87. package/web/listRules/{listRules.test.js → listRules.test.ts} +26 -19
  88. package/web/listTopIssues/{index.js → index.ts} +36 -47
  89. package/web/listTopIssues/{listTopIssues.test.js → listTopIssues.test.ts} +26 -17
  90. package/web/listViolators/{index.js → index.ts} +51 -71
  91. package/web/listViolators/{listViolators.test.js → listViolators.test.ts} +43 -36
  92. package/web/manage/index.ts +22 -0
  93. package/web/pruneReportsForm/index.ts +18 -0
  94. package/web/pruneReportsForm/{pruneReportsForm.test.js → pruneReportsForm.test.ts} +11 -11
  95. package/web/reannotate/index.ts +70 -0
  96. package/web/reannotate/{reannotate.test.js → reannotate.test.ts} +19 -22
  97. package/web/reannotateForm/{index.js → index.ts} +41 -50
  98. package/web/reannotateForm/{reannotateForm.test.js → reannotateForm.test.ts} +13 -16
  99. package/web/renewWCAG/{index.js → index.ts} +9 -9
  100. package/web/renewWCAG/{renewWCAG.test.js → renewWCAG.test.ts} +17 -20
  101. package/web/renewWCAGForm/index.ts +22 -0
  102. package/web/{expungeReportsForm/index.js → reportDeletion.ts} +56 -38
  103. package/web/requestRetest/index.html +7 -7
  104. package/web/requestRetest/index.ts +31 -0
  105. package/web/requestRetest/requestRetest.test.ts +116 -0
  106. package/web/requestRetestForm/{index.js → index.ts} +10 -16
  107. package/web/requestRetestForm/{requestRetestForm.test.js → requestRetestForm.test.ts} +11 -12
  108. package/web/requestTest/index.html +7 -7
  109. package/web/requestTest/{index.js → index.ts} +5 -5
  110. package/web/requestTest/{requestTest.test.js → requestTest.test.ts} +21 -21
  111. package/web/requestTestForm/index.html +4 -4
  112. package/web/requestTestForm/index.ts +21 -0
  113. package/web/requestTestForm/{requestTestForm.test.js → requestTestForm.test.ts} +9 -9
  114. package/web/rewindReportsForm/index.ts +18 -0
  115. package/web/rewindReportsForm/{rewindReportsForm.test.js → rewindReportsForm.test.ts} +11 -11
  116. package/web/tutorialAI/index.html +184 -0
  117. package/web/tutorialAI/index.ts +80 -0
  118. package/web/tutorialAI/tutorialAI.test.ts +192 -0
  119. package/web/{tutorial → tutorialWeb}/index.html +9 -8
  120. package/web/tutorialWeb/index.ts +78 -0
  121. package/web/tutorialWeb/tutorialWeb.test.ts +195 -0
  122. package/web/unhideReportForm/{index.js → index.ts} +17 -21
  123. package/web/unhideReportForm/{unhideReportForm.test.js → unhideReportForm.test.ts} +16 -16
  124. package/aceconfig.js +0 -28
  125. package/api/listReports.js +0 -89
  126. package/api/requestFeature.test.js +0 -58
  127. package/api/version.js +0 -8
  128. package/indexFallback.test.js +0 -33
  129. package/indexWorkers.test.js +0 -79
  130. package/jsconfig.json +0 -8
  131. package/pm2.config.js +0 -18
  132. package/smokeTest.js +0 -128
  133. package/util.js +0 -744
  134. package/web/enqueue/index.js +0 -67
  135. package/web/enqueueForm/index.js +0 -49
  136. package/web/manage/index.js +0 -22
  137. package/web/pruneReportsForm/index.js +0 -123
  138. package/web/reannotate/index.js +0 -58
  139. package/web/renewWCAGForm/index.js +0 -22
  140. package/web/requestRetest/index.js +0 -31
  141. package/web/requestRetest/requestRetest.test.js +0 -119
  142. package/web/requestTestForm/index.js +0 -21
  143. package/web/rewindReportsForm/index.js +0 -121
  144. package/web/tutorial/index.js +0 -63
  145. package/web/tutorial/tutorial.test.js +0 -97
  146. /package/test/fixtures/db/jobs/{recs.json → testRequests.json} +0 -0
  147. /package/web/{tutorial → tutorialWeb}/images/newsletter-form.png +0 -0
package/smokeTest.ts ADDED
@@ -0,0 +1,234 @@
1
+ /*
2
+ smokeTest.ts
3
+ Sends requests to all valid GET and POST paths on the deployed server and verifies that Caddy forwards them to Kilotest (i.e., the response is not a bare Caddy 404), then sends a real MCP initialize request to verify end-to-end MCP delivery through the proxy.
4
+ */
5
+
6
+ // IMPORTS
7
+
8
+ import https from 'node:https';
9
+ import {routes} from './index.ts';
10
+ import {mcpPath} from './mcp.ts';
11
+
12
+ // CONSTANTS
13
+
14
+ // Concrete paths matching each wildcard pattern in the routes table, for smoke testing.
15
+ const concretePaths: Record<string, Record<string, string>> = {
16
+ GET: {
17
+ '*.html*': '/listReports.html',
18
+ '/': '/',
19
+ '/api-docs': '/api-docs',
20
+ '/api/*': '/api/listReports',
21
+ '/capability.md': '/capability.md',
22
+ '/favicon.*': '/favicon.ico',
23
+ '/fullReport.json/*': '/fullReport.json/260101T0000/mix',
24
+ '/index.html': '/index.html',
25
+ '/llms-full.txt': '/llms-full.txt',
26
+ '/llms.txt': '/llms.txt',
27
+ '/mcp': '/mcp',
28
+ '/openapi.json': '/openapi.json',
29
+ '/openapi.yaml': '/openapi.yaml',
30
+ '/qai': '/qai',
31
+ '/qai/comments': '/qai/comments',
32
+ '/robots.txt': '/robots.txt',
33
+ '/sitemap.xml': '/sitemap.xml',
34
+ '/style.css': '/style.css',
35
+ '/swagger.json': '/swagger.json',
36
+ '/swagger.yaml': '/swagger.yaml',
37
+ '/tutorialAI/images/*': '/tutorialAI/images/example.png',
38
+ '/tutorialWeb/images/*': '/tutorialWeb/images/newsletter-form.png'
39
+ },
40
+ POST: {
41
+ '/api/*': '/api/requestFeature',
42
+ '/mcp': '/mcp',
43
+ '/reannotate.html': '/reannotate.html',
44
+ '/requestAction.html': '/requestAction.html',
45
+ '/renewWCAG.html': '/renewWCAG.html',
46
+ '/requestRetest.html/*': '/requestRetest.html/260101T0001/ct',
47
+ '/requestTest.html': '/requestTest.html',
48
+ '/tutorialAIComment.html': '/tutorialAIComment.html',
49
+ '/tutorialWebComment.html': '/tutorialWebComment.html',
50
+ '/worker/job': '/worker/job',
51
+ '/worker/report': '/worker/report'
52
+ }
53
+ };
54
+ // Minimal POST bodies for paths that require them.
55
+ const postBodies: Record<string, object> = {
56
+ '/api/requestFeature': {feature: 'smoke test'},
57
+ '/mcp': {},
58
+ '/reannotate.html': {authCode: 'invalid'},
59
+ '/requestAction.html': {
60
+ target: 'https://smoketest.example.com\tSmoke Test Page', authCode: 'invalid'
61
+ },
62
+ '/renewWCAG.html': {authCode: 'invalid'},
63
+ '/requestRetest.html/260101T0001/ct': {why: 'smoke test'},
64
+ '/requestTest.html': {description: 'Smoke Test Page', url: 'https://smoketest.example.com', why: 'smoke test'},
65
+ '/tutorialAIComment.html': {content: 'smoke test'},
66
+ '/tutorialWebComment.html': {content: 'smoke test'},
67
+ '/worker/job': {},
68
+ '/worker/report': {}
69
+ };
70
+ // The JSON-RPC initialize request sent by the positive MCP probe.
71
+ const mcpInitialize = {
72
+ jsonrpc: '2.0',
73
+ method: 'initialize',
74
+ id: 'smoke',
75
+ params: {
76
+ protocolVersion: '2025-06-18',
77
+ capabilities: {},
78
+ clientInfo: {name: 'kilotest-smoke', version: '1.0'}
79
+ }
80
+ };
81
+
82
+ // FUNCTIONS
83
+
84
+ // Sends an HTTPS request and returns the status code and body length.
85
+ const sendRequest = (method: string, requestPath: string) =>
86
+ new Promise<{statusCode: number | undefined, bodyLength: number}>((resolve, reject) => {
87
+ const body = method === 'POST' ? JSON.stringify(postBodies[requestPath] || {}) : null;
88
+ const headers: Record<string, string | number> = {'x-kilotest-smoke': '1'};
89
+ if (body) {
90
+ headers['content-type'] = 'application/json; charset=utf-8';
91
+ headers['content-length'] = Buffer.byteLength(body);
92
+ }
93
+ const options = {
94
+ method,
95
+ host: process.env.SMOKE_HOST || 'kilotest.com',
96
+ path: requestPath,
97
+ headers
98
+ };
99
+ const req = https.request(options, response => {
100
+ const chunks: Buffer[] = [];
101
+ response.on('data', chunk => chunks.push(chunk));
102
+ response.on('end', () => {
103
+ const responseBody = chunks.join('');
104
+ resolve({statusCode: response.statusCode, bodyLength: responseBody.length});
105
+ });
106
+ });
107
+ req.on('error', reject);
108
+ req.end(body || '');
109
+ });
110
+
111
+ // Returns whether a response is a bare Caddy 404 (the failure this test detects).
112
+ const isCaddy404 = (result: {statusCode: number | undefined, bodyLength: number}) =>
113
+ result.statusCode === 404 && result.bodyLength === 0;
114
+
115
+ // Sends a request to the MCP endpoint without the smoke header and returns the status code and body.
116
+ const sendMCPRequest = (method: string, accept: string, body: object | null = null) =>
117
+ new Promise<{statusCode: number | undefined, body: string}>((resolve, reject) => {
118
+ const bodyData = body ? JSON.stringify(body) : null;
119
+ const headers: Record<string, string | number> = {accept};
120
+ if (bodyData) {
121
+ headers['content-type'] = 'application/json';
122
+ headers['content-length'] = Buffer.byteLength(bodyData);
123
+ }
124
+ const req = https.request({
125
+ method,
126
+ host: process.env.SMOKE_HOST || 'kilotest.com',
127
+ path: mcpPath,
128
+ headers
129
+ }, response => {
130
+ const chunks: Buffer[] = [];
131
+ response.on('data', chunk => chunks.push(chunk));
132
+ response.on('end', () => {
133
+ resolve({statusCode: response.statusCode, body: chunks.join('')});
134
+ });
135
+ });
136
+ req.on('error', reject);
137
+ req.end(bodyData || '');
138
+ });
139
+
140
+ // Returns whether an MCP probe response is a valid initialize result from Kilotest.
141
+ const isValidMCPResponse = (result: {statusCode: number | undefined, body: string}) => {
142
+ if (result.statusCode !== 200) {
143
+ return false;
144
+ }
145
+ try {
146
+ const dataLine = result.body.split('\n').find(line => line.startsWith('data: '));
147
+ const message = JSON.parse(dataLine ? dataLine.slice(6) : result.body);
148
+ return message?.result?.serverInfo?.name === 'Kilotest';
149
+ }
150
+ catch {
151
+ return false;
152
+ }
153
+ };
154
+
155
+ // Returns whether a response is a JSON-RPC protocol error, the expected rejection of an invalid MCP request.
156
+ const isMCPErrorResponse = (result: {statusCode: number | undefined, body: string}) => {
157
+ if (result.statusCode === undefined || result.statusCode < 400 || result.statusCode >= 500) {
158
+ return false;
159
+ }
160
+ try {
161
+ const message = JSON.parse(result.body);
162
+ return message?.jsonrpc === '2.0' && typeof message?.error?.code === 'number';
163
+ }
164
+ catch {
165
+ return false;
166
+ }
167
+ };
168
+
169
+ // EXECUTION
170
+
171
+ (async () => {
172
+ let failures = 0;
173
+ for (const method of ['GET', 'POST']) {
174
+ console.log(`\n=== ${method} paths ===`);
175
+ for (const pattern of routes[method as 'GET' | 'POST']) {
176
+ const requestPath = concretePaths[method]![pattern];
177
+ if (!requestPath) {
178
+ console.log(`FAIL: no concrete path defined for pattern ${pattern}`);
179
+ failures++;
180
+ continue;
181
+ }
182
+ try {
183
+ const result = await sendRequest(method, requestPath);
184
+ if (isCaddy404(result)) {
185
+ console.log(`FAIL: ${method} ${requestPath} -> Caddy 404 (path not forwarded)`);
186
+ failures++;
187
+ }
188
+ else {
189
+ console.log(`PASS: ${method} ${requestPath} -> ${result.statusCode} (body ${result.bodyLength} bytes)`);
190
+ }
191
+ }
192
+ catch (error) {
193
+ const message = error instanceof Error ? error.message : String(error);
194
+ console.log(`FAIL: ${method} ${requestPath} -> error: ${message}`);
195
+ failures++;
196
+ }
197
+ }
198
+ }
199
+ // Send a real MCP initialize request to verify routing, header forwarding, and SSE delivery.
200
+ console.log('\n=== MCP probes ===');
201
+ try {
202
+ const probe = await sendMCPRequest('POST', 'application/json, text/event-stream', mcpInitialize);
203
+ if (isValidMCPResponse(probe)) {
204
+ console.log(`PASS: POST ${mcpPath} initialize -> ${probe.statusCode} (valid MCP response)`);
205
+ }
206
+ else {
207
+ console.log(`FAIL: POST ${mcpPath} initialize -> ${probe.statusCode} (body: ${probe.body.slice(0, 120)})`);
208
+ failures++;
209
+ }
210
+ }
211
+ catch (error) {
212
+ const message = error instanceof Error ? error.message : String(error);
213
+ console.log(`FAIL: POST ${mcpPath} initialize -> error: ${message}`);
214
+ failures++;
215
+ }
216
+ // Send a browser-style GET to verify that invalid MCP requests get JSON-RPC errors end-to-end.
217
+ try {
218
+ const probe = await sendMCPRequest('GET', 'text/html');
219
+ if (isMCPErrorResponse(probe)) {
220
+ console.log(`PASS: GET ${mcpPath} browser -> ${probe.statusCode} (JSON-RPC error)`);
221
+ }
222
+ else {
223
+ console.log(`FAIL: GET ${mcpPath} browser -> ${probe.statusCode} (body: ${probe.body.slice(0, 120)})`);
224
+ failures++;
225
+ }
226
+ }
227
+ catch (error) {
228
+ const message = error instanceof Error ? error.message : String(error);
229
+ console.log(`FAIL: GET ${mcpPath} browser -> error: ${message}`);
230
+ failures++;
231
+ }
232
+ console.log(`\n=== ${failures === 0 ? 'All checks passed' : `${failures} failure(s)`} ===`);
233
+ process.exit(failures === 0 ? 0 : 1);
234
+ })();
@@ -0,0 +1,21 @@
1
+ /*
2
+ test/dbFixture.ts
3
+ Provides a disposable copy of the fixture database, so that tests never modify the tracked fixtures in test/fixtures/db.
4
+ */
5
+
6
+ // IMPORTS
7
+
8
+ import fs from 'node:fs';
9
+ import os from 'node:os';
10
+ import path from 'node:path';
11
+
12
+ // CONSTANTS
13
+
14
+ // Path of this process' temporary copy of the fixture database.
15
+ export const fixtureDBDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kilotest-db-'));
16
+
17
+ // Copy the tracked fixture database to the temporary directory.
18
+ fs.cpSync(path.join(import.meta.dirname, 'fixtures', 'db'), fixtureDBDir, {recursive: true});
19
+
20
+ // Remove the copy when the process exits.
21
+ process.on('exit', () => fs.rmSync(fixtureDBDir, {recursive: true, force: true}));
@@ -1,16 +1,16 @@
1
1
  /*
2
- buildFixtures.js
2
+ buildFixtures.ts
3
3
  Builds the fixture corpus for Kilotest tests.
4
4
 
5
5
  Each fixture is a minimal Testaro report in the current format, crafted so that the expected API response can be hand-computed and hard-coded into tests. The fixtures collectively cover the `outcome` property values (`failed`, `cantTell`, and missing/undefined), superseded reports, empty reports, and prevented rule engines.
6
6
 
7
- Run with: node test/fixtures/buildFixtures.js [targetDir]
7
+ Run with: node test/fixtures/buildFixtures.ts [targetDir]
8
8
  */
9
9
 
10
10
  // IMPORTS
11
11
 
12
- const fs = require('fs/promises');
13
- const path = require('path');
12
+ import fs from 'node:fs/promises';
13
+ import path from 'node:path';
14
14
 
15
15
  // CONSTANTS
16
16
 
@@ -22,7 +22,7 @@ const issueFocusIndication = 'focusIndicationBad';
22
22
  // weight 1 (lowest)
23
23
  const issueAllCaps = 'allCaps';
24
24
 
25
- // Rule engine IDs that exist in util.js ruleEngines.
25
+ // Rule engine IDs that exist in util.ts ruleEngines.
26
26
  const engineAxe = 'axe';
27
27
  const engineAlfa = 'alfa';
28
28
  const engineIbm = 'ibm';
@@ -30,7 +30,7 @@ const engineIbm = 'ibm';
30
30
  // FUNCTIONS
31
31
 
32
32
  // Returns a minimal catalog item.
33
- const catalogItem = (tagName, text, pathID = '/html', boxID = '0:0:100:50') => ({
33
+ const catalogItem = (tagName: string, text: string, pathID = '/html', boxID = '0:0:100:50') => ({
34
34
  tagName,
35
35
  id: '',
36
36
  startTag: `<${tagName.toLowerCase()}>`,
@@ -44,7 +44,8 @@ const catalogItem = (tagName, text, pathID = '/html', boxID = '0:0:100:50') => (
44
44
 
45
45
  // Returns a minimal standard instance.
46
46
  const instance = (
47
- ruleID, what, outcome, issueID, catalogIndex, ordinalSeverity = 2, count = 1
47
+ ruleID: string, what: string, outcome: string, issueID: string, catalogIndex: number,
48
+ ordinalSeverity = 2, count = 1
48
49
  ) => ({
49
50
  ruleID,
50
51
  what,
@@ -57,7 +58,7 @@ const instance = (
57
58
  });
58
59
 
59
60
  // Returns a minimal test act.
60
- const testAct = (which, instances) => ({
61
+ const testAct = (which: string, instances: any[]) => ({
61
62
  type: 'test',
62
63
  which,
63
64
  startTime: '26-01-01T00:00',
@@ -74,7 +75,7 @@ const testAct = (which, instances) => ({
74
75
  });
75
76
 
76
77
  // Returns a minimal valid report.
77
- const report = (id, what, url, acts, catalog, endTime = '26-01-01T00:10') => ({
78
+ const report = (id: string, what: string, url: string, acts: any[], catalog: any, endTime = '26-01-01T00:10') => ({
78
79
  id,
79
80
  what,
80
81
  strict: false,
@@ -99,14 +100,14 @@ const report = (id, what, url, acts, catalog, endTime = '26-01-01T00:10') => ({
99
100
  });
100
101
 
101
102
  // Writes a JSON file with a trailing newline.
102
- const writeJSON = async (filePath, object) => {
103
+ const writeJSON = async (filePath: string, object: any) => {
103
104
  await fs.writeFile(filePath, `${JSON.stringify(object, null, 2)}\n`);
104
105
  };
105
106
 
106
107
  // MAIN
107
108
 
108
109
  const main = async () => {
109
- const targetDir = process.argv[2] || path.join(__dirname, 'db');
110
+ const targetDir = process.argv[2] || path.join(import.meta.dirname, 'db');
110
111
  const reportsDir = path.join(targetDir, 'reports');
111
112
  await fs.mkdir(reportsDir, {recursive: true});
112
113
  await fs.mkdir(path.join(targetDir, 'jobs', 'queue'), {recursive: true});
@@ -316,8 +317,8 @@ const main = async () => {
316
317
  delete brdReport.jobData.preventions;
317
318
  await writeJSON(path.join(reportsDir, '260101T0009-brd.json'), brdReport);
318
319
 
319
- // Write an empty recs.json so getRecs does not try to create one.
320
- await writeJSON(path.join(targetDir, 'jobs', 'recs.json'), {});
320
+ // Write an empty testRequests.json so getTestRequests does not try to create one.
321
+ await writeJSON(path.join(targetDir, 'jobs', 'testRequests.json'), {});
321
322
 
322
323
  console.log(`Fixtures built in ${targetDir}`);
323
324
  };
@@ -1,12 +1,12 @@
1
1
  /*
2
- test/setup.cjs
2
+ test/setup.ts
3
3
  Clears alert configuration env vars before any modules are required,
4
4
  so that tests calling sendAlert indirectly never trigger real email alerts.
5
5
  This file is loaded via --require before any test file runs.
6
6
 
7
7
  The env vars are set to empty strings rather than deleted, because
8
8
  dotenv does not override existing env vars. This prevents the .env
9
- file loaded by index.js from re-arming the alert configuration.
9
+ file loaded by index.ts from re-arming the alert configuration.
10
10
  */
11
11
 
12
12
  for (const key of ['MANAGER_EMAIL', 'ALERT_API_HOST', 'ALERT_API_PATH', 'ALERT_API_KEY', 'ALERT_FROM']) {
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "allowImportingTsExtensions": true,
7
+ "allowJs": true,
8
+ "checkJs": false,
9
+ "resolveJsonModule": true,
10
+ "noEmit": true,
11
+ "strict": true,
12
+ "noUncheckedIndexedAccess": true,
13
+ "skipLibCheck": true
14
+ },
15
+ "exclude": ["node_modules", "coverage"]
16
+ }