testaro 78.0.8 → 78.4.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.
- package/.gitattributes +94 -0
- package/.github/workflows/ci.yml +58 -0
- package/.github/workflows/publish.yml +49 -0
- package/.github/workflows/typescript.yml +35 -0
- package/AGENTS.md +2 -2
- package/CLAUDE.md +24 -18
- package/CONTAINERS.md +9 -6
- package/CONTRIBUTING.md +29 -3
- package/Dockerfile +1 -1
- package/README.md +73 -19
- package/UPGRADES.md +4 -0
- package/actSpecs-doc.md +13 -1
- package/actSpecs.js +29 -5
- package/call.js +6 -5
- package/docker-compose.yml +8 -2
- package/docs/checkpoint-scanning.md +199 -0
- package/docs/standard-result-outcome.md +165 -0
- package/env.example +44 -10
- package/eslint.config.mjs +101 -0
- package/netWatch.js +372 -222
- package/package.json +46 -31
- package/pour/README.md +47 -0
- package/pour/pour.min.js +8 -0
- package/procs/actDo.js +808 -0
- package/procs/catalog.d.ts +17 -0
- package/procs/catalog.js +312 -221
- package/procs/catalog.ts +398 -0
- package/procs/checkpoint.js +110 -0
- package/procs/config.d.ts +35 -0
- package/procs/config.js +62 -0
- package/procs/dateTime.js +2 -1
- package/procs/doActs.js +442 -849
- package/procs/doTestAct.js +27 -121
- package/procs/flow.js +221 -0
- package/procs/generateRuleRegistry.js +94 -0
- package/procs/getSource.d.ts +26 -0
- package/procs/job.js +115 -1
- package/procs/launch.d.ts +40 -0
- package/procs/launch.js +239 -108
- package/procs/nu.d.ts +48 -0
- package/procs/scope.js +196 -0
- package/procs/shoot.d.ts +33 -0
- package/procs/standard.d.ts +18 -0
- package/procs/standard.js +83 -0
- package/procs/standard.ts +126 -0
- package/procs/testAct.js +145 -0
- package/procs/testaro.d.ts +20 -0
- package/procs/testaro.js +236 -189
- package/procs/testaro.ts +321 -0
- package/procs/userPath.js +126 -0
- package/procs/xPath.d.ts +4 -0
- package/procs/xPath.js +81 -69
- package/procs/xPath.ts +106 -0
- package/procs/xPathScript.d.ts +6 -0
- package/procs/xPathScript.js +70 -0
- package/run.js +3 -2
- package/surea11y/README.md +44 -0
- package/surea11y/surea11y.browser.js +14 -0
- package/testaro/adbID.d.ts +3 -0
- package/testaro/adbID.js +38 -41
- package/testaro/adbID.ts +66 -0
- package/testaro/allCapStyle.d.ts +3 -0
- package/testaro/allCapStyle.js +31 -34
- package/testaro/allCapStyle.ts +55 -0
- package/testaro/allCaps.d.ts +16 -0
- package/testaro/allCaps.js +179 -151
- package/testaro/allCaps.ts +228 -0
- package/testaro/allHidden.d.ts +13 -0
- package/testaro/allHidden.js +29 -30
- package/testaro/allHidden.ts +50 -0
- package/testaro/allSlanted.d.ts +3 -0
- package/testaro/allSlanted.js +30 -33
- package/testaro/allSlanted.ts +54 -0
- package/testaro/altScheme.d.ts +3 -0
- package/testaro/altScheme.js +27 -30
- package/testaro/altScheme.ts +50 -0
- package/testaro/attVal.d.ts +3 -0
- package/testaro/attVal.js +20 -21
- package/testaro/attVal.ts +52 -0
- package/testaro/autocomplete.d.ts +3 -0
- package/testaro/autocomplete.js +59 -75
- package/testaro/autocomplete.ts +101 -0
- package/testaro/bulk.d.ts +13 -0
- package/testaro/bulk.js +32 -33
- package/testaro/bulk.ts +55 -0
- package/testaro/buttonMenu.d.ts +9 -0
- package/testaro/buttonMenu.js +317 -319
- package/testaro/buttonMenu.ts +391 -0
- package/testaro/captionLoc.d.ts +3 -0
- package/testaro/captionLoc.js +17 -20
- package/testaro/captionLoc.ts +40 -0
- package/testaro/datalistRef.d.ts +3 -0
- package/testaro/datalistRef.js +33 -36
- package/testaro/datalistRef.ts +55 -0
- package/testaro/distortion.d.ts +3 -0
- package/testaro/distortion.js +57 -26
- package/testaro/distortion.ts +81 -0
- package/testaro/docType.d.ts +15 -0
- package/testaro/docType.js +25 -25
- package/testaro/docType.ts +45 -0
- package/testaro/dupAtt.d.ts +16 -0
- package/testaro/dupAtt.js +113 -104
- package/testaro/dupAtt.ts +144 -0
- package/testaro/elements.d.ts +6 -0
- package/testaro/elements.js +153 -153
- package/testaro/elements.ts +215 -0
- package/testaro/embAc.d.ts +3 -0
- package/testaro/embAc.js +19 -20
- package/testaro/embAc.ts +40 -0
- package/testaro/focAll.d.ts +13 -0
- package/testaro/focAll.js +185 -191
- package/testaro/focAll.ts +217 -0
- package/testaro/focAndOp.d.ts +3 -0
- package/testaro/focAndOp.js +100 -104
- package/testaro/focAndOp.ts +128 -0
- package/testaro/focInd.d.ts +3 -0
- package/testaro/focInd.js +64 -66
- package/testaro/focInd.ts +96 -0
- package/testaro/focVis.d.ts +3 -0
- package/testaro/focVis.js +28 -30
- package/testaro/focVis.ts +52 -0
- package/testaro/headEl.d.ts +10 -0
- package/testaro/headEl.js +61 -62
- package/testaro/headEl.ts +82 -0
- package/testaro/headingAmb.d.ts +3 -0
- package/testaro/headingAmb.js +54 -63
- package/testaro/headingAmb.ts +72 -0
- package/testaro/hovInd.d.ts +12 -0
- package/testaro/hovInd.js +163 -130
- package/testaro/hovInd.ts +199 -0
- package/testaro/hover.d.ts +3 -0
- package/testaro/hover.js +155 -126
- package/testaro/hover.ts +154 -0
- package/testaro/hr.d.ts +3 -0
- package/testaro/hr.js +14 -17
- package/testaro/hr.ts +36 -0
- package/testaro/imageLink.d.ts +3 -0
- package/testaro/imageLink.js +17 -20
- package/testaro/imageLink.ts +42 -0
- package/testaro/labClash.d.ts +3 -0
- package/testaro/labClash.js +31 -33
- package/testaro/labClash.ts +54 -0
- package/testaro/legendLoc.d.ts +3 -0
- package/testaro/legendLoc.js +17 -20
- package/testaro/legendLoc.ts +42 -0
- package/testaro/lineHeight.d.ts +3 -0
- package/testaro/lineHeight.js +43 -48
- package/testaro/lineHeight.ts +70 -0
- package/testaro/linkAmb.d.ts +7 -0
- package/testaro/linkAmb.js +79 -80
- package/testaro/linkAmb.ts +105 -0
- package/testaro/linkExt.d.ts +3 -0
- package/testaro/linkExt.js +13 -16
- package/testaro/linkExt.ts +35 -0
- package/testaro/linkOldAtt.d.ts +3 -0
- package/testaro/linkOldAtt.js +25 -28
- package/testaro/linkOldAtt.ts +48 -0
- package/testaro/linkTo.d.ts +3 -0
- package/testaro/linkTo.js +22 -22
- package/testaro/linkTo.ts +43 -0
- package/testaro/linkUl.d.ts +3 -0
- package/testaro/linkUl.js +30 -34
- package/testaro/linkUl.ts +54 -0
- package/testaro/miniText.d.ts +3 -0
- package/testaro/miniText.js +41 -44
- package/testaro/miniText.ts +68 -0
- package/testaro/motion.d.ts +10 -0
- package/testaro/motion.js +92 -96
- package/testaro/motion.ts +125 -0
- package/testaro/nonTable.d.ts +3 -0
- package/testaro/nonTable.js +39 -45
- package/testaro/nonTable.ts +66 -0
- package/testaro/optRoleSel.d.ts +3 -0
- package/testaro/optRoleSel.js +16 -19
- package/testaro/optRoleSel.ts +41 -0
- package/testaro/phOnly.d.ts +3 -0
- package/testaro/phOnly.js +18 -21
- package/testaro/phOnly.ts +43 -0
- package/testaro/pseudoP.d.ts +3 -0
- package/testaro/pseudoP.js +36 -38
- package/testaro/pseudoP.ts +59 -0
- package/testaro/radioSet.d.ts +3 -0
- package/testaro/radioSet.js +57 -59
- package/testaro/radioSet.ts +79 -0
- package/testaro/registry.d.ts +63 -0
- package/testaro/registry.js +67 -0
- package/testaro/registry.ts +141 -0
- package/testaro/role.d.ts +3 -0
- package/testaro/role.js +27 -29
- package/testaro/role.ts +53 -0
- package/testaro/secHeading.d.ts +3 -0
- package/testaro/secHeading.js +30 -33
- package/testaro/secHeading.ts +53 -0
- package/testaro/styleDiff.d.ts +25 -0
- package/testaro/styleDiff.js +248 -252
- package/testaro/styleDiff.ts +303 -0
- package/testaro/tabNav.d.ts +33 -0
- package/testaro/tabNav.js +272 -342
- package/testaro/tabNav.ts +454 -0
- package/testaro/targetsNear.d.ts +9 -0
- package/testaro/targetsNear.js +130 -132
- package/testaro/targetsNear.ts +160 -0
- package/testaro/textNodes.d.ts +6 -0
- package/testaro/textNodes.js +139 -135
- package/testaro/textNodes.ts +185 -0
- package/testaro/textSem.d.ts +3 -0
- package/testaro/textSem.js +25 -28
- package/testaro/textSem.ts +47 -0
- package/testaro/title.d.ts +9 -0
- package/testaro/title.js +16 -13
- package/testaro/title.ts +31 -0
- package/testaro/titledEl.d.ts +3 -0
- package/testaro/titledEl.js +15 -18
- package/testaro/titledEl.ts +38 -0
- package/testaro/zIndex.d.ts +3 -0
- package/testaro/zIndex.js +19 -22
- package/testaro/zIndex.ts +42 -0
- package/tests/alfa.d.ts +45 -0
- package/tests/alfa.js +138 -141
- package/tests/alfa.ts +214 -0
- package/tests/aslint.d.ts +33 -0
- package/tests/aslint.js +272 -249
- package/tests/aslint.ts +301 -0
- package/tests/axe.d.ts +27 -0
- package/tests/axe.js +199 -200
- package/tests/axe.ts +277 -0
- package/tests/ed11y.d.ts +28 -0
- package/tests/ed11y.js +141 -99
- package/tests/ed11y.ts +178 -0
- package/tests/htmlcs.d.ts +21 -0
- package/tests/htmlcs.js +174 -140
- package/tests/htmlcs.ts +181 -0
- package/tests/ibm.d.ts +52 -0
- package/tests/ibm.js +164 -166
- package/tests/ibm.ts +251 -0
- package/tests/nuVal.d.ts +13 -0
- package/tests/nuVal.js +107 -112
- package/tests/nuVal.ts +145 -0
- package/tests/nuVnu.d.ts +15 -0
- package/tests/nuVnu.js +141 -111
- package/tests/nuVnu.ts +144 -0
- package/tests/pour.d.ts +31 -0
- package/tests/pour.js +242 -0
- package/tests/pour.ts +273 -0
- package/tests/qualWeb.d.ts +39 -0
- package/tests/qualWeb.js +302 -272
- package/tests/qualWeb.ts +415 -0
- package/tests/surea11y.d.ts +33 -0
- package/tests/surea11y.js +288 -0
- package/tests/surea11y.ts +334 -0
- package/tests/testaro.d.ts +25 -0
- package/tests/testaro.js +745 -652
- package/tests/testaro.ts +862 -0
- package/tests/wave.d.ts +46 -0
- package/tests/wave.js +166 -177
- package/tests/wave.ts +252 -0
- package/tsconfig.json +17 -0
- package/types.d.ts +243 -0
- package/types.js +10 -0
- package/types.ts +376 -0
- package/validation/act/README.md +46 -0
- package/validation/act/capture.js +424 -0
- package/validation/act/chromium-issue-draft.md +66 -0
- package/validation/act/fp-triage-2026-08-22.md +86 -0
- package/validation/act/isolation-notes.md +159 -0
- package/validation/act/playwright-issue-draft.md +73 -0
- package/validation/act/propose-mappings.js +0 -0
- package/validation/act/repro-cdp-raw.js +105 -0
- package/validation/act/repro-metarefresh.js +65 -0
- package/validation/act/score.js +184 -0
- package/validation/act/stage3a-stress-report.md +69 -0
- package/validation/act/stage3b-mapping-proposals.md +94 -0
- package/validation/act/stage3b-triage-draft.md +138 -0
- package/validation/act/surea11y-track-a-2026-09-01.md +50 -0
- package/validation/executors/netWatch.js +180 -90
- package/validation/executors/test.js +17 -2
- package/validation/executors/tests.js +118 -10
- package/validation/jobs/reports/raw/260901T1000-surea11y-validation.json +964 -0
- package/validation/jobs/todo/240101T1200-simple-example.json +14 -6
- package/validation/jobs/todo/240101T1300-shoot-example.json +2 -1
- package/validation/jobs/todo/260821T1900-pour-validation.json +45 -0
- package/validation/jobs/todo/260901T1000-surea11y-validation.json +45 -0
- package/validation/knownFailures.json +1 -0
- package/validation/tests/jobProperties/adbID.json +27 -2
- package/validation/tests/jobProperties/{focOp.json → allCapStyle.json} +56 -53
- package/validation/tests/jobProperties/allCaps.json +25 -0
- package/validation/tests/jobProperties/allHidden.json +133 -13
- package/validation/tests/jobProperties/allSlanted.json +3 -3
- package/validation/tests/jobProperties/altScheme.json +23 -8
- package/validation/tests/jobProperties/attVal.json +57 -57
- package/validation/tests/jobProperties/autocomplete.json +11 -1
- package/validation/tests/jobProperties/bulk.json +6 -1
- package/validation/tests/jobProperties/buttonMenu.json +63 -42
- package/validation/tests/jobProperties/captionLoc.json +1 -6
- package/validation/tests/jobProperties/checkpoint-browser.json +401 -0
- package/validation/tests/jobProperties/checkpoint-page.json +397 -0
- package/validation/tests/jobProperties/checkpoint.json +395 -0
- package/validation/tests/jobProperties/datalistRef.json +20 -5
- package/validation/tests/jobProperties/distortion.json +28 -3
- package/validation/tests/jobProperties/docType.json +2 -2
- package/validation/tests/jobProperties/dupAtt.json +48 -33
- package/validation/tests/jobProperties/elements.json +28 -28
- package/validation/tests/jobProperties/embAc.json +36 -31
- package/validation/tests/jobProperties/focAndOp.json +284 -0
- package/validation/tests/jobProperties/focInd.json +34 -34
- package/validation/tests/jobProperties/focVis.json +1 -1
- package/validation/tests/jobProperties/hover.json +39 -37
- package/validation/tests/jobProperties/hr.json +13 -3
- package/validation/tests/jobProperties/imageLink.json +0 -5
- package/validation/tests/jobProperties/labClash.json +68 -33
- package/validation/tests/jobProperties/legendLoc.json +1 -6
- package/validation/tests/jobProperties/lineHeight.json +2 -2
- package/validation/tests/jobProperties/linkAmb.json +13 -18
- package/validation/tests/jobProperties/linkExt.json +1 -1
- package/validation/tests/jobProperties/linkOldAtt.json +12 -2
- package/validation/tests/jobProperties/linkTo.json +1 -1
- package/validation/tests/jobProperties/linkUl.json +65 -65
- package/validation/tests/jobProperties/miniText.json +7 -2
- package/validation/tests/jobProperties/motion.json +4 -50
- package/validation/tests/jobProperties/nonTable.json +48 -3
- package/validation/tests/jobProperties/optRoleSel.json +12 -2
- package/validation/tests/jobProperties/phOnly.json +6 -16
- package/validation/tests/jobProperties/pseudoP.json +17 -2
- package/validation/tests/jobProperties/radioSet.json +30 -30
- package/validation/tests/jobProperties/role.json +25 -5
- package/validation/tests/jobProperties/secHeading.json +26 -21
- package/validation/tests/jobProperties/styleDiff.json +35 -5
- package/validation/tests/jobProperties/tabNav.json +3 -1
- package/validation/tests/jobProperties/targetsNear.json +237 -0
- package/validation/tests/jobProperties/textNodes.json +29 -29
- package/validation/tests/jobProperties/textSem.json +26 -1
- package/validation/tests/jobProperties/title.json +12 -2
- package/validation/tests/jobProperties/titledEl.json +39 -9
- package/validation/tests/jobProperties/userPath.json +317 -0
- package/validation/tests/jobProperties/zIndex.json +40 -35
- package/validation/tests/targets/allCapStyle/index.html +26 -0
- package/validation/tests/targets/checkpoint/index.html +38 -0
- package/validation/tests/targets/datalistRef/index.html +1 -1
- package/validation/tests/targets/focAndOp/bad.html +29 -0
- package/validation/tests/targets/{focOp → focAndOp}/good.html +3 -1
- package/validation/tests/targets/focInd/bad.html +2 -1
- package/validation/tests/targets/headEl/index.html +10 -1
- package/validation/tests/targets/{targetSmall → targetsNear}/index.html +15 -1
- package/validation/tests/targets/userPath/index.html +42 -0
- package/validation/validateTest.js +61 -9
- package/.claude/settings.local.json +0 -11
- package/.eslintrc.json +0 -41
- package/htmlcs/.eslintrc.json +0 -67
- package/validation/tests/jobProperties/linkTitle.json +0 -127
- package/validation/tests/jobProperties/opFoc.json +0 -164
- package/validation/tests/jobProperties/targetSmall.json +0 -152
- package/validation/tests/jobProperties/targetTiny.json +0 -142
- package/validation/tests/targets/focOp/bad.html +0 -25
- package/validation/tests/targets/linkTitle/index.html +0 -24
- package/validation/tests/targets/opFoc/bad.html +0 -26
- package/validation/tests/targets/opFoc/good.html +0 -23
package/netWatch.js
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
|
|
19
19
|
// Module to keep secrets.
|
|
20
20
|
require('dotenv').config();
|
|
21
|
+
// Module to access files.
|
|
22
|
+
const fs = require('fs/promises');
|
|
21
23
|
// Module to validate jobs.
|
|
22
24
|
const {isValidJob} = require('./procs/job');
|
|
23
25
|
// Modules to make requests to servers.
|
|
@@ -30,9 +32,16 @@ const {nowString} = require('./procs/dateTime');
|
|
|
30
32
|
|
|
31
33
|
// CONSTANTS
|
|
32
34
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
// Auth types and the environment variables each one requires.
|
|
36
|
+
const authRequirements = {
|
|
37
|
+
none: [],
|
|
38
|
+
pathBody: ['NETWATCH_WORKER_SECRET'],
|
|
39
|
+
header: ['NETWATCH_WORKER_ID', 'NETWATCH_WORKER_SECRET']
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// FUNCTIONS
|
|
43
|
+
|
|
44
|
+
// Returns the URL represented by a string, or null if invalid.
|
|
36
45
|
const toURL = urlString => {
|
|
37
46
|
try {
|
|
38
47
|
return new URL(urlString);
|
|
@@ -41,14 +50,6 @@ const toURL = urlString => {
|
|
|
41
50
|
return null;
|
|
42
51
|
}
|
|
43
52
|
};
|
|
44
|
-
const jobURL = toURL(process.env.NETWATCH_URL_JOB);
|
|
45
|
-
const jobHost = jobURL && jobURL.host;
|
|
46
|
-
const reportURL = toURL(process.env.NETWATCH_URL_REPORT);
|
|
47
|
-
const reportHost = reportURL && reportURL.host;
|
|
48
|
-
const agentPW = process.env.NETWATCH_URL_AUTH;
|
|
49
|
-
|
|
50
|
-
// FUNCTIONS
|
|
51
|
-
|
|
52
53
|
// Waits.
|
|
53
54
|
const wait = ms => {
|
|
54
55
|
return new Promise(resolve => {
|
|
@@ -57,142 +58,301 @@ const wait = ms => {
|
|
|
57
58
|
}, ms);
|
|
58
59
|
});
|
|
59
60
|
};
|
|
60
|
-
//
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
// Returns the value of a basic authorization header for a worker ID and secret.
|
|
62
|
+
const basicAuthHeader = (workerID, workerSecret) =>
|
|
63
|
+
`Basic ${Buffer.from(`${workerID}:${workerSecret}`).toString('base64')}`;
|
|
64
|
+
// Returns the netWatch configuration from the environment, with any problems named.
|
|
65
|
+
const getConfig = () => {
|
|
66
|
+
const problems = [];
|
|
67
|
+
const warnings = [];
|
|
68
|
+
let authType = process.env.NETWATCH_AUTH_TYPE;
|
|
69
|
+
let workerID = process.env.NETWATCH_WORKER_ID;
|
|
70
|
+
let workerSecret = process.env.NETWATCH_WORKER_SECRET;
|
|
71
|
+
// If the worker ID is specified only by the deprecated AGENT variable:
|
|
72
|
+
if (! workerID && process.env.AGENT) {
|
|
73
|
+
// Adopt it and warn about the deprecation.
|
|
74
|
+
workerID = process.env.AGENT;
|
|
75
|
+
warnings.push('AGENT is deprecated; rename it to NETWATCH_WORKER_ID');
|
|
76
|
+
}
|
|
77
|
+
// If the auth type is unspecified but the deprecated NETWATCH_URL_AUTH variable exists:
|
|
78
|
+
if (! authType && process.env.NETWATCH_URL_AUTH) {
|
|
79
|
+
// Treat the configuration as a pathBody configuration and warn about the deprecation.
|
|
80
|
+
authType = 'pathBody';
|
|
81
|
+
workerSecret ||= process.env.NETWATCH_URL_AUTH;
|
|
82
|
+
warnings.push(
|
|
83
|
+
'NETWATCH_URL_AUTH is deprecated; set NETWATCH_AUTH_TYPE=pathBody and NETWATCH_WORKER_SECRET'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
// If no auth configuration exists at all:
|
|
87
|
+
if (! authType && ! workerSecret) {
|
|
88
|
+
// Treat the configuration as an unauthenticated configuration and warn about this.
|
|
89
|
+
authType = 'none';
|
|
90
|
+
warnings.push('NETWATCH_AUTH_TYPE not set; defaulting to none (no credentials sent)');
|
|
91
|
+
}
|
|
92
|
+
const jobURL = toURL(process.env.NETWATCH_URL_JOB);
|
|
93
|
+
const reportURL = toURL(process.env.NETWATCH_URL_REPORT);
|
|
94
|
+
// Identify any problems with the configuration, naming the offending variables.
|
|
95
|
+
if (! jobURL) {
|
|
96
|
+
problems.push(`NETWATCH_URL_JOB missing or not a valid URL (${process.env.NETWATCH_URL_JOB})`);
|
|
97
|
+
}
|
|
98
|
+
if (! reportURL) {
|
|
99
|
+
problems.push(
|
|
100
|
+
`NETWATCH_URL_REPORT missing or not a valid URL (${process.env.NETWATCH_URL_REPORT})`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
if (! Object.keys(authRequirements).includes(authType)) {
|
|
104
|
+
problems.push(
|
|
105
|
+
`NETWATCH_AUTH_TYPE (${authType}) not one of ${Object.keys(authRequirements).join(', ')}`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
const varValues = {
|
|
110
|
+
NETWATCH_WORKER_ID: workerID,
|
|
111
|
+
NETWATCH_WORKER_SECRET: workerSecret
|
|
112
|
+
};
|
|
113
|
+
authRequirements[authType].forEach(varName => {
|
|
114
|
+
if (! varValues[varName]) {
|
|
115
|
+
problems.push(`${varName} required when NETWATCH_AUTH_TYPE is ${authType}`);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
// If the worker ID contains a colon, prohibited in basic authentication by RFC 7617:
|
|
119
|
+
if (authType === 'header' && workerID && workerID.includes(':')) {
|
|
120
|
+
problems.push('NETWATCH_WORKER_ID must not contain a colon');
|
|
121
|
+
}
|
|
122
|
+
// If any credential is not ASCII-only, so servers may decode it differently:
|
|
123
|
+
if ([workerID, workerSecret].some(value => value && /[^\x20-\x7e]/.test(value))) {
|
|
124
|
+
warnings.push(
|
|
125
|
+
'NETWATCH_WORKER_ID or NETWATCH_WORKER_SECRET contains non-ASCII characters, which servers may decode differently'
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return {problems, warnings, jobURL, reportURL, authType, workerID, workerSecret};
|
|
130
|
+
};
|
|
131
|
+
// Saves a report that could not be submitted, so it is not lost.
|
|
132
|
+
const saveFailedReport = async report => {
|
|
133
|
+
try {
|
|
134
|
+
const saveDir = `${process.env.REPORTDIR || '.'}/netWatchFailed`;
|
|
135
|
+
await fs.mkdir(saveDir, {recursive: true});
|
|
136
|
+
const savePath = `${saveDir}/${report && report.id || 'report'}.json`;
|
|
137
|
+
await fs.writeFile(savePath, JSON.stringify(report, null, 2));
|
|
138
|
+
console.log(`Unsubmitted report saved at ${savePath}`);
|
|
139
|
+
}
|
|
140
|
+
catch(error) {
|
|
141
|
+
console.log(`ERROR saving unsubmitted report (${error.message})`);
|
|
142
|
+
}
|
|
64
143
|
};
|
|
65
144
|
/*
|
|
66
145
|
Requests a network job and, when found, performs and reports it.
|
|
67
146
|
Arguments:
|
|
68
147
|
0. whether to continue watching after a job is run.
|
|
69
148
|
1: interval in seconds from a no-job check to the next check.
|
|
70
|
-
2. whether to ignore unknown-certificate errors from watched
|
|
149
|
+
2. whether to ignore unknown-certificate errors from a watched server (default false).
|
|
150
|
+
Returns whether watching ended without an abort, an invalid configuration, or a lost report.
|
|
71
151
|
*/
|
|
72
|
-
exports.netWatch = async (isForever, intervalInSeconds, isCertTolerant =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
152
|
+
exports.netWatch = async (isForever, intervalInSeconds, isCertTolerant = false) => {
|
|
153
|
+
const {problems, warnings, jobURL, reportURL, authType, workerID, workerSecret} = getConfig();
|
|
154
|
+
// Report any warnings about the configuration.
|
|
155
|
+
warnings.forEach(warning => {
|
|
156
|
+
console.log(`WARNING: ${warning}`);
|
|
157
|
+
});
|
|
158
|
+
// If the netWatch configuration is invalid:
|
|
159
|
+
if (problems.length) {
|
|
160
|
+
// Report each problem and quit.
|
|
161
|
+
problems.forEach(problem => {
|
|
162
|
+
console.log(`ERROR: ${problem}`);
|
|
163
|
+
});
|
|
164
|
+
console.log('ERROR: Configuration of netWatch is invalid');
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
// Configure the watch.
|
|
168
|
+
const headers = {
|
|
169
|
+
'content-type': 'application/json; charset=utf-8'
|
|
170
|
+
};
|
|
171
|
+
if (authType === 'header') {
|
|
172
|
+
headers.authorization = basicAuthHeader(workerID, workerSecret);
|
|
173
|
+
}
|
|
174
|
+
// Body properties transmitting the credentials, if the auth type requires them there.
|
|
175
|
+
const authBody = authType === 'pathBody' ? {agentPW: workerSecret} : {};
|
|
176
|
+
const jobRequestJSON = JSON.stringify(authBody);
|
|
177
|
+
// Returns the client and request options for a request to a URL.
|
|
178
|
+
const requestConfigFor = url => {
|
|
179
|
+
const options = {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
headers: {... headers}
|
|
182
|
+
};
|
|
183
|
+
let client = httpClient;
|
|
184
|
+
if (url.protocol === 'https:') {
|
|
185
|
+
client = httpsClient;
|
|
186
|
+
options.rejectUnauthorized = ! isCertTolerant;
|
|
187
|
+
}
|
|
188
|
+
return {client, options};
|
|
189
|
+
};
|
|
190
|
+
// If certificate tolerance would expose credentials on an encrypted connection:
|
|
191
|
+
if (isCertTolerant && [jobURL, reportURL].some(url => url.protocol === 'https:')) {
|
|
192
|
+
// Warn about this.
|
|
81
193
|
console.log(
|
|
82
|
-
|
|
194
|
+
'WARNING: Certificate tolerance disables certificate verification, so credentials and reports are exposed to interception'
|
|
83
195
|
);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
196
|
+
}
|
|
197
|
+
let noJobYet = true;
|
|
198
|
+
let abort = false;
|
|
199
|
+
let reportLost = false;
|
|
200
|
+
const jobHost = jobURL.host;
|
|
201
|
+
const certInfo = `Certificate-${isCertTolerant ? '' : 'in'}tolerant`;
|
|
202
|
+
const foreverInfo = isForever ? 'repeating' : 'one-job';
|
|
203
|
+
const intervalInfo = `with ${intervalInSeconds}-second intervals`;
|
|
204
|
+
console.log(
|
|
205
|
+
`${certInfo} ${foreverInfo} network watching started ${intervalInfo} (${nowString()})\n`
|
|
206
|
+
);
|
|
207
|
+
// As long as watching is to continue:
|
|
208
|
+
while ((isForever || noJobYet) && ! abort) {
|
|
209
|
+
// Log the start of a check.
|
|
210
|
+
console.log('--');
|
|
211
|
+
// Configure the next check.
|
|
212
|
+
const logStart = `Requested job from ${jobHost} and got `;
|
|
213
|
+
// Perform it.
|
|
214
|
+
await new Promise(resolve => {
|
|
215
|
+
// Ensure the check is concluded at most once, so error events cannot start overlapping checks.
|
|
216
|
+
let settled = false;
|
|
217
|
+
let jobDispatched = false;
|
|
218
|
+
const finish = () => {
|
|
219
|
+
if (! settled) {
|
|
220
|
+
settled = true;
|
|
221
|
+
resolve(true);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
const finishAfterWait = async () => {
|
|
225
|
+
if (! settled) {
|
|
226
|
+
// Wait for the specified interval.
|
|
227
|
+
await wait(1000 * intervalInSeconds);
|
|
228
|
+
finish();
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
try {
|
|
232
|
+
// Get the client and request options for a job request.
|
|
233
|
+
const {client, options} = requestConfigFor(jobURL);
|
|
234
|
+
// Request a job.
|
|
235
|
+
client.request(jobURL, options, response => {
|
|
236
|
+
// Initialize a collection of data from the response.
|
|
237
|
+
const chunks = [];
|
|
238
|
+
response
|
|
239
|
+
// If the response throws an error:
|
|
240
|
+
.on('error', error => {
|
|
241
|
+
// Report it.
|
|
242
|
+
console.log(`${logStart}error message ${error.message}`);
|
|
243
|
+
// Unless a job is already being performed, wait and conclude the check.
|
|
244
|
+
if (! jobDispatched) {
|
|
245
|
+
finishAfterWait();
|
|
100
246
|
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
247
|
+
})
|
|
248
|
+
// If the response delivers data:
|
|
249
|
+
.on('data', chunk => {
|
|
250
|
+
// Add them to the collection.
|
|
251
|
+
chunks.push(chunk);
|
|
252
|
+
})
|
|
253
|
+
// When the response is completed:
|
|
254
|
+
.on('end', async () => {
|
|
255
|
+
const content = chunks.join('');
|
|
256
|
+
const {statusCode} = response;
|
|
257
|
+
// If the server reported a failure:
|
|
258
|
+
if (statusCode < 200 || statusCode > 299) {
|
|
108
259
|
// Report it.
|
|
109
|
-
console.log(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
260
|
+
console.log(
|
|
261
|
+
`ERROR: ${logStart}status ${statusCode} and response ${content.slice(0, 1000)}`
|
|
262
|
+
);
|
|
263
|
+
// If it was an authentication or authorization rejection:
|
|
264
|
+
if ([401, 403].includes(statusCode)) {
|
|
265
|
+
// Abort the watch, because rechecking cannot succeed until it is reconfigured.
|
|
266
|
+
abort = true;
|
|
267
|
+
finish();
|
|
268
|
+
}
|
|
269
|
+
// Otherwise, i.e. if the failure may be transient:
|
|
270
|
+
else {
|
|
271
|
+
// Wait and conclude the check.
|
|
272
|
+
finishAfterWait();
|
|
273
|
+
}
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
// Parse it as a JSON job.
|
|
278
|
+
let contentObj = JSON.parse(content);
|
|
279
|
+
const {id} = contentObj;
|
|
280
|
+
// If it is a no-job message:
|
|
281
|
+
if (! Object.keys(contentObj).length) {
|
|
282
|
+
// Report this.
|
|
283
|
+
console.log(`${logStart}no job to do; waiting ${intervalInSeconds} sec before next check`);
|
|
284
|
+
// Wait and conclude the check.
|
|
285
|
+
finishAfterWait();
|
|
286
|
+
}
|
|
287
|
+
// Otherwise, if it is a job:
|
|
288
|
+
else if (id) {
|
|
289
|
+
// Check it for validity.
|
|
290
|
+
const jobValidity = isValidJob(contentObj);
|
|
291
|
+
// If it is invalid:
|
|
292
|
+
if (! jobValidity.isValid) {
|
|
125
293
|
// Report this.
|
|
126
|
-
console.log(`${logStart}
|
|
127
|
-
// Wait
|
|
128
|
-
|
|
129
|
-
resolve(true);
|
|
294
|
+
console.log(`${logStart}invalid job (${jobValidity.error})`);
|
|
295
|
+
// Wait and conclude the check.
|
|
296
|
+
finishAfterWait();
|
|
130
297
|
}
|
|
131
|
-
// Otherwise, if it is
|
|
132
|
-
else
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
message: `invalidJob`,
|
|
140
|
-
error: jobValidity.error
|
|
141
|
-
}, response);
|
|
142
|
-
console.log(`${logStart}invalid job (${jobValidity.error})`);
|
|
143
|
-
// Wait for the specified interval.
|
|
144
|
-
await wait(1000 * intervalInSeconds);
|
|
145
|
-
resolve(true);
|
|
298
|
+
// Otherwise, i.e. if it is valid:
|
|
299
|
+
else {
|
|
300
|
+
// Prevent further watching, if unwanted.
|
|
301
|
+
noJobYet = false;
|
|
302
|
+
jobDispatched = true;
|
|
303
|
+
// Identify this worker in the report, if a worker ID exists.
|
|
304
|
+
if (workerID && contentObj.sources) {
|
|
305
|
+
contentObj.sources.agent = workerID;
|
|
146
306
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// Prevent further watching, if unwanted.
|
|
150
|
-
noJobYet = false;
|
|
151
|
-
// Add the agent and the server ID to the job.
|
|
152
|
-
sources.agent = process.env.AGENT || '';
|
|
307
|
+
console.log(`${logStart}job ${id} (${nowString()})`);
|
|
308
|
+
try {
|
|
153
309
|
// Perform the job and create a report.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
310
|
+
const report = await doJob(contentObj);
|
|
311
|
+
// Make it the report property of the response body, with any body credentials.
|
|
312
|
+
const responseObj = {
|
|
313
|
+
... authBody,
|
|
314
|
+
report
|
|
315
|
+
};
|
|
316
|
+
let responseJSON = JSON.stringify(responseObj, null, 2);
|
|
317
|
+
console.log(`Job ${id} finished (${nowString()})`);
|
|
318
|
+
const reportLogStart = `Submitted report ${id} to ${reportURL} and got `;
|
|
319
|
+
// Get the client and request options for a report-submission request.
|
|
320
|
+
const {client: repClient, options: repOptions} = requestConfigFor(reportURL);
|
|
321
|
+
// Submit the report.
|
|
322
|
+
repClient.request(reportURL, repOptions, repResponse => {
|
|
323
|
+
// Initialize a collection of data from the response.
|
|
324
|
+
const repChunks = [];
|
|
325
|
+
repResponse
|
|
326
|
+
// If the response to the report threw an error:
|
|
327
|
+
.on('error', error => {
|
|
328
|
+
// Report this.
|
|
329
|
+
console.log(`${reportLogStart}error message ${error.message}\n`);
|
|
330
|
+
// Wait and conclude the check.
|
|
331
|
+
finishAfterWait();
|
|
332
|
+
})
|
|
333
|
+
// If the response delivers data:
|
|
334
|
+
.on('data', chunk => {
|
|
335
|
+
// Add them to the collection.
|
|
336
|
+
repChunks.push(chunk);
|
|
337
|
+
})
|
|
338
|
+
// When the response to the report is completed:
|
|
339
|
+
.on('end', async () => {
|
|
340
|
+
const repContent = repChunks.join('');
|
|
341
|
+
const repStatusCode = repResponse.statusCode;
|
|
342
|
+
// If the server reported a failure:
|
|
343
|
+
if (repStatusCode < 200 || repStatusCode > 299) {
|
|
344
|
+
// Report this and save the report, so it is not lost.
|
|
345
|
+
console.log(
|
|
346
|
+
`ERROR: ${reportLogStart}status ${repStatusCode} and response ${repContent.slice(0, 1000)}\n`
|
|
347
|
+
);
|
|
348
|
+
reportLost = true;
|
|
349
|
+
await saveFailedReport(report);
|
|
169
350
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const client = reportURL.protocol === 'https:' ? httpsClient : httpClient;
|
|
173
|
-
client.request(reportURL, requestOptions, repResponse => {
|
|
174
|
-
// Initialize a collection of data from the response.
|
|
175
|
-
const chunks = [];
|
|
176
|
-
repResponse
|
|
177
|
-
// If the response to the report threw an error:
|
|
178
|
-
.on('error', async error => {
|
|
179
|
-
// Report this.
|
|
180
|
-
console.log(`${reportLogStart}error message ${error.message}\n`);
|
|
181
|
-
// Wait for the specified interval.
|
|
182
|
-
await wait(1000 * intervalInSeconds);
|
|
183
|
-
resolve(true);
|
|
184
|
-
})
|
|
185
|
-
// If the response delivers data:
|
|
186
|
-
.on('data', chunk => {
|
|
187
|
-
// Add them to the collection.
|
|
188
|
-
chunks.push(chunk);
|
|
189
|
-
})
|
|
190
|
-
// When the response to the report is completed:
|
|
191
|
-
.on('end', async () => {
|
|
192
|
-
const content = chunks.join('');
|
|
351
|
+
// Otherwise, i.e. if the server accepted the report:
|
|
352
|
+
else {
|
|
193
353
|
try {
|
|
194
|
-
// Parse
|
|
195
|
-
const ackObj = JSON.parse(
|
|
354
|
+
// Parse the acknowledgement as JSON.
|
|
355
|
+
const ackObj = JSON.parse(repContent);
|
|
196
356
|
// Report it.
|
|
197
357
|
console.log(
|
|
198
358
|
`${reportLogStart}response message: ${JSON.stringify(ackObj, null, 2)}\n`
|
|
@@ -202,105 +362,95 @@ exports.netWatch = async (isForever, intervalInSeconds, isCertTolerant = true) =
|
|
|
202
362
|
catch(error) {
|
|
203
363
|
// Report this.
|
|
204
364
|
console.log(
|
|
205
|
-
`ERROR: ${reportLogStart}status ${
|
|
365
|
+
`ERROR: ${reportLogStart}status ${repStatusCode}, error message ${error.message}, and response ${repContent.slice(0, 1000)}\n`
|
|
206
366
|
);
|
|
207
|
-
// Wait for the specified interval.
|
|
208
|
-
await wait(1000 * intervalInSeconds);
|
|
209
367
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
368
|
+
}
|
|
369
|
+
// Free the memory used by the job and the report.
|
|
370
|
+
contentObj = {};
|
|
371
|
+
responseJSON = '';
|
|
372
|
+
finish();
|
|
373
|
+
});
|
|
374
|
+
})
|
|
375
|
+
// If the report submission throws an error:
|
|
376
|
+
.on('error', async error => {
|
|
377
|
+
// Abort the watch.
|
|
378
|
+
abort = true;
|
|
379
|
+
// Report this and save the report, so it is not lost.
|
|
380
|
+
console.log(
|
|
381
|
+
`ERROR ${error.code} in report submission: ${reportLogStart}error message ${error.message}\n`
|
|
382
|
+
);
|
|
383
|
+
reportLost = true;
|
|
384
|
+
await saveFailedReport(report);
|
|
385
|
+
finish();
|
|
386
|
+
})
|
|
387
|
+
// Finish submitting the report.
|
|
388
|
+
.end(responseJSON);
|
|
389
|
+
}
|
|
390
|
+
catch(error) {
|
|
391
|
+
console.log(`ERROR performing job ${id} (${error.message})`);
|
|
392
|
+
// Wait and conclude the check.
|
|
393
|
+
finishAfterWait();
|
|
237
394
|
}
|
|
238
|
-
}
|
|
239
|
-
// Otherwise, i.e. if it is a message:
|
|
240
|
-
else {
|
|
241
|
-
// Report it.
|
|
242
|
-
console.log(`${logStart}${JSON.stringify(contentObj, null, 2)}`);
|
|
243
|
-
// Wait for the specified interval.
|
|
244
|
-
await wait(1000 * intervalInSeconds);
|
|
245
|
-
resolve(true);
|
|
246
395
|
}
|
|
247
396
|
}
|
|
248
|
-
// Otherwise, i.e. if it is
|
|
249
|
-
|
|
250
|
-
// Report
|
|
251
|
-
console.log(
|
|
252
|
-
// Wait
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
};
|
|
256
|
-
});
|
|
257
|
-
})
|
|
258
|
-
// If the job request throws an error:
|
|
259
|
-
.on('error', async error => {
|
|
260
|
-
// If it is a refusal to connect:
|
|
261
|
-
if (error.code && error.code.includes('ECONNREFUSED')) {
|
|
262
|
-
// Report this.
|
|
263
|
-
console.log(`${logStart}no connection`);
|
|
264
|
-
}
|
|
265
|
-
// Otherwise, if it was a DNS failure:
|
|
266
|
-
else if (error.code && error.code.includes('ENOTFOUND')) {
|
|
267
|
-
// Report this.
|
|
268
|
-
console.log(`${logStart}no domain name resolution`);
|
|
269
|
-
}
|
|
270
|
-
// Otherwise, if it was any other error with a message:
|
|
271
|
-
else if (error.message) {
|
|
272
|
-
// Report this.
|
|
273
|
-
console.log(`ERROR: ${logStart}got error message ${error.message.slice(0, 200)}`);
|
|
397
|
+
// Otherwise, i.e. if it is a message:
|
|
398
|
+
else {
|
|
399
|
+
// Report it.
|
|
400
|
+
console.log(`${logStart}${JSON.stringify(contentObj, null, 2)}`);
|
|
401
|
+
// Wait and conclude the check.
|
|
402
|
+
finishAfterWait();
|
|
403
|
+
}
|
|
274
404
|
}
|
|
275
|
-
// Otherwise, i.e. if it
|
|
276
|
-
|
|
405
|
+
// Otherwise, i.e. if it is not JSON:
|
|
406
|
+
catch(error) {
|
|
277
407
|
// Report this.
|
|
278
|
-
console.log(`ERROR: ${logStart}
|
|
408
|
+
console.log(`ERROR: ${logStart}status ${response.statusCode}, error message ${error.message}, and non-JSON response ${content.slice(0, 1000)}\n`);
|
|
409
|
+
// Wait and conclude the check.
|
|
410
|
+
finishAfterWait();
|
|
279
411
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
//
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
412
|
+
});
|
|
413
|
+
})
|
|
414
|
+
// If the job request throws an error:
|
|
415
|
+
.on('error', error => {
|
|
416
|
+
// If it is a refusal to connect:
|
|
417
|
+
if (error.code && error.code.includes('ECONNREFUSED')) {
|
|
418
|
+
// Report this.
|
|
419
|
+
console.log(`${logStart}no connection`);
|
|
420
|
+
}
|
|
421
|
+
// Otherwise, if it was a DNS failure:
|
|
422
|
+
else if (error.code && error.code.includes('ENOTFOUND')) {
|
|
423
|
+
// Report this.
|
|
424
|
+
console.log(`${logStart}no domain name resolution`);
|
|
425
|
+
}
|
|
426
|
+
// Otherwise, if it was any other error with a message:
|
|
427
|
+
else if (error.message) {
|
|
428
|
+
// Report this.
|
|
429
|
+
console.log(`ERROR: ${logStart}got error message ${error.message.slice(0, 200)}`);
|
|
430
|
+
}
|
|
431
|
+
// Otherwise, i.e. if it was any other error with no message:
|
|
432
|
+
else {
|
|
433
|
+
// Report this.
|
|
434
|
+
console.log(`ERROR: ${logStart}got an error with no message`);
|
|
435
|
+
}
|
|
436
|
+
// Unless a job is already being performed, wait and conclude the check.
|
|
437
|
+
if (! jobDispatched) {
|
|
438
|
+
finishAfterWait();
|
|
439
|
+
}
|
|
440
|
+
})
|
|
441
|
+
// Finish sending the job request.
|
|
442
|
+
.end(jobRequestJSON);
|
|
443
|
+
}
|
|
444
|
+
// If requesting a job throws an error:
|
|
445
|
+
catch(error) {
|
|
446
|
+
// Report this.
|
|
447
|
+
console.log(`ERROR requesting a network job (${error.message})`);
|
|
448
|
+
// Wait and conclude the check.
|
|
449
|
+
finishAfterWait();
|
|
450
|
+
}
|
|
451
|
+
});
|
|
305
452
|
}
|
|
453
|
+
console.log(`Watching ${abort ? 'aborted' : 'complete'}`);
|
|
454
|
+
return ! (abort || reportLost);
|
|
306
455
|
};
|
|
456
|
+
exports.basicAuthHeader = basicAuthHeader;
|