Repository: rokucommunity/brighterscript
Pull request #1254 (2024-07-19 19:44:35)

Fix standalone project issues
Fixes several issues related to standalone workspaces:

- We weren't properly destroying standalone workspaces when a different project handled that file, thus leaving diagnostics alive forever
- we weren't handling file and document changes for standalone workspaces, leaving the file in its initial state unless you close the file
- fix recovery issues when a bsconfig was loaded with invalid syntax (so open files load as standalone), but then the bsconfig.json was fixed....the files never got handed back to the main project.


Patch:
diff --git a/package-lock.json b/package-lock.json
index 0e4b1037..21d9dd0f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -34,7 +34,7 @@
         "parse-ms": "^2.1.0",
         "readline": "^1.3.0",
         "require-relative": "^0.8.7",
-        "roku-deploy": "^3.12.0",
+        "roku-deploy": "^3.12.1",
         "safe-json-stringify": "^1.2.0",
         "serialize-error": "^7.0.1",
         "source-map": "^0.7.4",
@@ -108,6 +108,58 @@
         "vscode-jsonrpc": "^5.0.1"
       }
     },
+    "../roku-deploy": {
+      "version": "3.12.0",
+      "extraneous": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^2.4.2",
+        "dateformat": "^3.0.3",
+        "dayjs": "^1.11.0",
+        "fast-glob": "^3.2.12",
+        "fs-extra": "^7.0.1",
+        "is-glob": "^4.0.3",
+        "jsonc-parser": "^2.3.0",
+        "jszip": "^3.6.0",
+        "lodash": "^4.17.21",
+        "micromatch": "^4.0.4",
+        "moment": "^2.29.1",
+        "parse-ms": "^2.1.0",
+        "postman-request": "^2.88.1-postman.32",
+        "temp-dir": "^2.0.0",
+        "xml2js": "^0.5.0"
+      },
+      "bin": {
+        "roku-deploy": "dist/cli.js"
+      },
+      "devDependencies": {
+        "@types/chai": "^4.2.22",
+        "@types/fs-extra": "^5.0.1",
+        "@types/is-glob": "^4.0.2",
+        "@types/lodash": "^4.14.200",
+        "@types/micromatch": "^4.0.2",
+        "@types/mocha": "^9.0.0",
+        "@types/node": "^16.11.3",
+        "@types/q": "^1.5.8",
+        "@types/request": "^2.47.0",
+        "@types/sinon": "^10.0.4",
+        "@types/xml2js": "^0.4.5",
+        "@typescript-eslint/eslint-plugin": "5.1.0",
+        "@typescript-eslint/parser": "5.1.0",
+        "chai": "^4.3.4",
+        "coveralls-next": "^4.2.0",
+        "dedent": "^0.7.0",
+        "eslint": "8.0.1",
+        "mocha": "^9.1.3",
+        "nyc": "^15.1.0",
+        "q": "^1.5.1",
+        "rimraf": "^2.6.2",
+        "sinon": "^11.1.2",
+        "source-map-support": "^0.5.13",
+        "ts-node": "^10.3.1",
+        "typescript": "^4.4.4"
+      }
+    },
     "node_modules/@babel/code-frame": {
       "version": "7.24.2",
       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
@@ -181,15 +233,6 @@
         "url": "https://opencollective.com/babel"
       }
     },
-    "node_modules/@babel/core/node_modules/@babel/code-frame": {
-      "version": "7.12.13",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
-      "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
-      "dev": true,
-      "dependencies": {
-        "@babel/highlight": "^7.12.13"
-      }
-    },
     "node_modules/@babel/core/node_modules/semver": {
       "version": "6.3.1",
       "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -404,20 +447,6 @@
         "@babel/types": "^7.13.0"
       }
     },
-    "node_modules/@babel/highlight": {
-      "version": "7.14.5",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
-      "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-validator-identifier": "^7.14.5",
-        "chalk": "^2.0.0",
-        "js-tokens": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@babel/parser": {
       "version": "7.13.13",
       "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz",
@@ -452,15 +481,6 @@
         "@babel/types": "^7.12.13"
       }
     },
-    "node_modules/@babel/template/node_modules/@babel/code-frame": {
-      "version": "7.12.13",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
-      "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
-      "dev": true,
-      "dependencies": {
-        "@babel/highlight": "^7.12.13"
-      }
-    },
     "node_modules/@babel/traverse": {
       "version": "7.24.1",
       "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz",
@@ -844,9 +864,9 @@
       }
     },
     "node_modules/@postman/tough-cookie": {
-      "version": "4.1.2-postman.2",
-      "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.2-postman.2.tgz",
-      "integrity": "sha512-nrBdX3jA5HzlxTrGI/I0g6pmUKic7xbGA4fAMLFgmJCA3DL2Ma+3MvmD+Sdiw9gLEzZJIF4fz33sT8raV/L/PQ==",
+      "version": "4.1.3-postman.1",
+      "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz",
+      "integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==",
       "dependencies": {
         "psl": "^1.1.33",
         "punycode": "^2.1.1",
@@ -866,9 +886,9 @@
       }
     },
     "node_modules/@postman/tunnel-agent": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.3.tgz",
-      "integrity": "sha512-k57fzmAZ2PJGxfOA4SGR05ejorHbVAa/84Hxh/2nAztjNXc4ZjOm9NUIk6/Z6LCrBvJZqjRZbN8e/nROVUPVdg==",
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.4.tgz",
+      "integrity": "sha512-CJJlq8V7rNKhAw4sBfjixKpJW00SHqebqNUQKxMoepgeWZIbdPcD+rguRcivGhS4N12PymDcKgUgSD4rVC+RjQ==",
       "dependencies": {
         "safe-buffer": "^5.0.1"
       },
@@ -2011,9 +2031,9 @@
       "dev": true
     },
     "node_modules/asn1": {
-      "version": "0.2.4",
-      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+      "version": "0.2.6",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+      "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
       "dependencies": {
         "safer-buffer": "~2.1.0"
       }
@@ -2021,7 +2041,7 @@
     "node_modules/assert-plus": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+      "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
       "engines": {
         "node": ">=0.8"
       }
@@ -2064,15 +2084,15 @@
     "node_modules/aws-sign2": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+      "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
       "engines": {
         "node": "*"
       }
     },
     "node_modules/aws4": {
-      "version": "1.12.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz",
-      "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg=="
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.0.tgz",
+      "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g=="
     },
     "node_modules/balanced-match": {
       "version": "1.0.0",
@@ -2101,7 +2121,7 @@
     "node_modules/bcrypt-pbkdf": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
       "dependencies": {
         "tweetnacl": "^0.14.3"
       }
@@ -2680,7 +2700,7 @@
     "node_modules/dashdash": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
       "dependencies": {
         "assert-plus": "^1.0.0"
       },
@@ -2763,9 +2783,9 @@
       }
     },
     "node_modules/dayjs": {
-      "version": "1.11.7",
-      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
-      "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
+      "version": "1.11.12",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz",
+      "integrity": "sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg=="
     },
     "node_modules/debounce-promise": {
       "version": "3.1.2",
@@ -2798,19 +2818,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/decomment": {
-      "version": "0.9.4",
-      "resolved": "https://registry.npmjs.org/decomment/-/decomment-0.9.4.tgz",
-      "integrity": "sha512-8eNlhyI5cSU4UbBlrtagWpR03dqXcE5IR9zpe7PnO6UzReXDskucsD8usgrzUmQ6qJ3N82aws/p/mu/jqbURWw==",
-      "dev": true,
-      "dependencies": {
-        "esprima": "4.0.1"
-      },
-      "engines": {
-        "node": ">=6.4",
-        "npm": ">=2.15"
-      }
-    },
     "node_modules/deep-eql": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
@@ -2911,15 +2918,15 @@
       }
     },
     "node_modules/dependency-tree": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-8.0.0.tgz",
-      "integrity": "sha512-zagnV3jgizudEWY3FIFkGCrRr3+GukSMLlw1snIWAOL2beceC22hBXdeNjCnnfPZvbHIPB9DvacSCfD+IoOG3w==",
+      "version": "8.1.2",
+      "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-8.1.2.tgz",
+      "integrity": "sha512-c4CL1IKxkKng0oT5xrg4uNiiMVFqTGOXqHSFx7XEFdgSsp6nw3AGGruICppzJUrfad/r7GLqt26rmWU4h4j39A==",
       "dev": true,
       "dependencies": {
         "commander": "^2.20.3",
         "debug": "^4.3.1",
-        "filing-cabinet": "^3.0.0",
-        "precinct": "^7.0.0",
+        "filing-cabinet": "^3.0.1",
+        "precinct": "^8.0.0",
         "typescript": "^3.9.7"
       },
       "bin": {
@@ -2935,6 +2942,33 @@
       "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
       "dev": true
     },
+    "node_modules/dependency-tree/node_modules/precinct": {
+      "version": "8.3.1",
+      "resolved": "https://registry.npmjs.org/precinct/-/precinct-8.3.1.tgz",
+      "integrity": "sha512-pVppfMWLp2wF68rwHqBIpPBYY8Kd12lDhk8LVQzOwqllifVR15qNFyod43YLyFpurKRZQKnE7E4pofAagDOm2Q==",
+      "dev": true,
+      "dependencies": {
+        "commander": "^2.20.3",
+        "debug": "^4.3.3",
+        "detective-amd": "^3.1.0",
+        "detective-cjs": "^3.1.1",
+        "detective-es6": "^2.2.1",
+        "detective-less": "^1.0.2",
+        "detective-postcss": "^4.0.0",
+        "detective-sass": "^3.0.1",
+        "detective-scss": "^2.0.1",
+        "detective-stylus": "^1.0.0",
+        "detective-typescript": "^7.0.0",
+        "module-definition": "^3.3.1",
+        "node-source-walk": "^4.2.0"
+      },
+      "bin": {
+        "precinct": "bin/cli.js"
+      },
+      "engines": {
+        "node": "^10.13 || ^12 || >=14"
+      }
+    },
     "node_modules/dependency-tree/node_modules/typescript": {
       "version": "3.9.10",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
@@ -2980,15 +3014,15 @@
       }
     },
     "node_modules/detective-es6": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-2.2.0.tgz",
-      "integrity": "sha512-fSpNY0SLER7/sVgQZ1NxJPwmc9uCTzNgdkQDhAaj8NPYwr7Qji9QBcmbNvtMCnuuOGMuKn3O7jv0An+/WRWJZQ==",
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-2.2.2.tgz",
+      "integrity": "sha512-eZUKCUsbHm8xoeoCM0z6JFwvDfJ5Ww5HANo+jPR7AzkFpW9Mun3t/TqIF2jjeWa2TFbAiGaWESykf2OQp3oeMw==",
       "dev": true,
       "dependencies": {
         "node-source-walk": "^4.0.0"
       },
       "engines": {
-        "node": ">= 6.0"
+        "node": ">=6.0"
       }
     },
     "node_modules/detective-less": {
@@ -3188,7 +3222,7 @@
     "node_modules/ecc-jsbn": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+      "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
       "dependencies": {
         "jsbn": "~0.1.0",
         "safer-buffer": "^2.1.0"
@@ -4123,7 +4157,7 @@
     "node_modules/extsprintf": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+      "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
       "engines": [
         "node >=0.6.0"
       ]
@@ -4188,23 +4222,23 @@
       }
     },
     "node_modules/filing-cabinet": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-3.0.0.tgz",
-      "integrity": "sha512-o8Qac5qxZ1uVidR4Sd7ZQbbqObFZlqXU4xu1suAYg9PQPcQFNTzOmxQa/MehIDMgIvXHTb42mWPNV9l3eHBPSw==",
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-3.3.1.tgz",
+      "integrity": "sha512-renEK4Hh6DUl9Vl22Y3cxBq1yh8oNvbAdXnhih0wVpmea+uyKjC9K4QeRjUaybIiIewdzfum+Fg15ZqJ/GyCaA==",
       "dev": true,
       "dependencies": {
         "app-module-path": "^2.2.0",
         "commander": "^2.20.3",
-        "debug": "^4.3.1",
-        "decomment": "^0.9.3",
-        "enhanced-resolve": "^5.3.2",
+        "debug": "^4.3.3",
+        "enhanced-resolve": "^5.8.3",
         "is-relative-path": "^1.0.2",
         "module-definition": "^3.3.1",
-        "module-lookup-amd": "^7.0.0",
-        "resolve": "^1.19.0",
+        "module-lookup-amd": "^7.0.1",
+        "resolve": "^1.21.0",
         "resolve-dependency-path": "^2.0.0",
         "sass-lookup": "^3.0.0",
         "stylus-lookup": "^3.0.1",
+        "tsconfig-paths": "^3.10.1",
         "typescript": "^3.9.7"
       },
       "bin": {
@@ -4369,7 +4403,7 @@
     "node_modules/forever-agent": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+      "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
       "engines": {
         "node": "*"
       }
@@ -4608,7 +4642,7 @@
     "node_modules/getpass": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
       "dependencies": {
         "assert-plus": "^1.0.0"
       }
@@ -4763,7 +4797,7 @@
     "node_modules/har-schema": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+      "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
       "engines": {
         "node": ">=4"
       }
@@ -4951,6 +4985,19 @@
       "integrity": "sha512-3hZT2z2/531A5pc8hYhn1gU5Qb1SIRSgMLQ6zuHA5xtt16lWAxUGprtr8lJuc9zNJMXEIIBWfSnzqBP/4mglpA==",
       "dev": true
     },
+    "node_modules/http-signature": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
+      "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==",
+      "dependencies": {
+        "assert-plus": "^1.0.0",
+        "jsprim": "^2.0.2",
+        "sshpk": "^1.14.1"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
     "node_modules/ieee754": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -4982,7 +5029,7 @@
     "node_modules/immediate": {
       "version": "3.0.6",
       "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
-      "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
     },
     "node_modules/import-fresh": {
       "version": "3.3.0",
@@ -5451,7 +5498,7 @@
     "node_modules/isstream": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+      "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
     },
     "node_modules/istanbul-lib-coverage": {
       "version": "3.0.0",
@@ -5624,7 +5671,7 @@
     "node_modules/jsbn": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
+      "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
     },
     "node_modules/jsdoc-type-pratt-parser": {
       "version": "3.1.0",
@@ -5667,7 +5714,7 @@
     "node_modules/json-stringify-safe": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+      "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="
     },
     "node_modules/json5": {
       "version": "2.2.3",
@@ -5694,6 +5741,20 @@
         "graceful-fs": "^4.1.6"
       }
     },
+    "node_modules/jsprim": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
+      "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==",
+      "engines": [
+        "node >=0.6.0"
+      ],
+      "dependencies": {
+        "assert-plus": "1.0.0",
+        "extsprintf": "1.3.0",
+        "json-schema": "0.4.0",
+        "verror": "1.10.0"
+      }
+    },
     "node_modules/jszip": {
       "version": "3.10.1",
       "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@@ -7145,7 +7206,7 @@
     "node_modules/performance-now": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+      "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
     },
     "node_modules/phin": {
       "version": "3.7.1",
@@ -7273,12 +7334,12 @@
       }
     },
     "node_modules/postman-request": {
-      "version": "2.88.1-postman.32",
-      "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.32.tgz",
-      "integrity": "sha512-Zf5D0b2G/UmnmjRwQKhYy4TBkuahwD0AMNyWwFK3atxU1u5GS38gdd7aw3vyR6E7Ii+gD//hREpflj2dmpbE7w==",
+      "version": "2.88.1-postman.34",
+      "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.34.tgz",
+      "integrity": "sha512-GkolJ4cIzgamcwHRDkeZc/taFWO1u2HuGNML47K9ZAsFH2LdEkS5Yy8QanpzhjydzV3WWthl9v60J8E7SjKodQ==",
       "dependencies": {
         "@postman/form-data": "~3.1.1",
-        "@postman/tough-cookie": "~4.1.2-postman.1",
+        "@postman/tough-cookie": "~4.1.3-postman.1",
         "@postman/tunnel-agent": "^0.6.3",
         "aws-sign2": "~0.7.0",
         "aws4": "^1.12.0",
@@ -7304,33 +7365,6 @@
         "node": ">= 6"
       }
     },
-    "node_modules/postman-request/node_modules/http-signature": {
-      "version": "1.3.6",
-      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
-      "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==",
-      "dependencies": {
-        "assert-plus": "^1.0.0",
-        "jsprim": "^2.0.2",
-        "sshpk": "^1.14.1"
-      },
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "node_modules/postman-request/node_modules/jsprim": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
-      "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==",
-      "engines": [
-        "node >=0.6.0"
-      ],
-      "dependencies": {
-        "assert-plus": "1.0.0",
-        "extsprintf": "1.3.0",
-        "json-schema": "0.4.0",
-        "verror": "1.10.0"
-      }
-    },
     "node_modules/postman-request/node_modules/uuid": {
       "version": "8.3.2",
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@@ -7457,9 +7491,9 @@
       "dev": true
     },
     "node_modules/psl": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
-      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+      "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
     },
     "node_modules/pump": {
       "version": "3.0.0",
@@ -7759,9 +7793,9 @@
       }
     },
     "node_modules/roku-deploy": {
-      "version": "3.12.0",
-      "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.12.0.tgz",
-      "integrity": "sha512-YiCZeQ+sEmFW9ZfXtMNH+/CBSHQ5deNZYWONM+s6gCEQsrz7kCMFPj5YEdgfqW+d2b8G1ve9GELHcSt2FsfM8g==",
+      "version": "3.12.1",
+      "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.12.1.tgz",
+      "integrity": "sha512-PEOdiFKGW1jrcoC9zjb+9kOWC/Q3eH7dzPvSi5kKignjNcyDiyJi+/wINwNrzw9SsxAVtw+NLvYueyZi9wQVsw==",
       "dependencies": {
         "chalk": "^2.4.2",
         "dateformat": "^3.0.3",
@@ -8210,9 +8244,9 @@
       "dev": true
     },
     "node_modules/sshpk": {
-      "version": "1.16.1",
-      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
-      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+      "version": "1.18.0",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
+      "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
       "dependencies": {
         "asn1": "~0.2.3",
         "assert-plus": "^1.0.0",
@@ -8692,7 +8726,7 @@
     "node_modules/tweetnacl": {
       "version": "0.14.5",
       "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
+      "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
     },
     "node_modules/type-check": {
       "version": "0.4.0",
@@ -8928,7 +8962,7 @@
     "node_modules/verror": {
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
       "engines": [
         "node >=0.6.0"
       ],
@@ -9332,15 +9366,6 @@
         "source-map": "^0.5.0"
       },
       "dependencies": {
-        "@babel/code-frame": {
-          "version": "7.12.13",
-          "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
-          "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
-          "dev": true,
-          "requires": {
-            "@babel/highlight": "^7.12.13"
-          }
-        },
         "semver": {
           "version": "6.3.1",
           "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -9530,17 +9555,6 @@
         "@babel/types": "^7.13.0"
       }
     },
-    "@babel/highlight": {
-      "version": "7.14.5",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
-      "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
-      "dev": true,
-      "requires": {
-        "@babel/helper-validator-identifier": "^7.14.5",
-        "chalk": "^2.0.0",
-        "js-tokens": "^4.0.0"
-      }
-    },
     "@babel/parser": {
       "version": "7.13.13",
       "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz",
@@ -9564,17 +9578,6 @@
         "@babel/code-frame": "^7.12.13",
         "@babel/parser": "^7.12.13",
         "@babel/types": "^7.12.13"
-      },
-      "dependencies": {
-        "@babel/code-frame": {
-          "version": "7.12.13",
-          "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
-          "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
-          "dev": true,
-          "requires": {
-            "@babel/highlight": "^7.12.13"
-          }
-        }
       }
     },
     "@babel/traverse": {
@@ -9889,9 +9892,9 @@
       }
     },
     "@postman/tough-cookie": {
-      "version": "4.1.2-postman.2",
-      "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.2-postman.2.tgz",
-      "integrity": "sha512-nrBdX3jA5HzlxTrGI/I0g6pmUKic7xbGA4fAMLFgmJCA3DL2Ma+3MvmD+Sdiw9gLEzZJIF4fz33sT8raV/L/PQ==",
+      "version": "4.1.3-postman.1",
+      "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz",
+      "integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==",
       "requires": {
         "psl": "^1.1.33",
         "punycode": "^2.1.1",
@@ -9907,9 +9910,9 @@
       }
     },
     "@postman/tunnel-agent": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.3.tgz",
-      "integrity": "sha512-k57fzmAZ2PJGxfOA4SGR05ejorHbVAa/84Hxh/2nAztjNXc4ZjOm9NUIk6/Z6LCrBvJZqjRZbN8e/nROVUPVdg==",
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.4.tgz",
+      "integrity": "sha512-CJJlq8V7rNKhAw4sBfjixKpJW00SHqebqNUQKxMoepgeWZIbdPcD+rguRcivGhS4N12PymDcKgUgSD4rVC+RjQ==",
       "requires": {
         "safe-buffer": "^5.0.1"
       }
@@ -10728,9 +10731,9 @@
       "dev": true
     },
     "asn1": {
-      "version": "0.2.4",
-      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+      "version": "0.2.6",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+      "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
       "requires": {
         "safer-buffer": "~2.1.0"
       }
@@ -10738,7 +10741,7 @@
     "assert-plus": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+      "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
     },
     "assertion-error": {
       "version": "1.1.0",
@@ -10769,12 +10772,12 @@
     "aws-sign2": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+      "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA=="
     },
     "aws4": {
-      "version": "1.12.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz",
-      "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg=="
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.0.tgz",
+      "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g=="
     },
     "balanced-match": {
       "version": "1.0.0",
@@ -10789,7 +10792,7 @@
     "bcrypt-pbkdf": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
       "requires": {
         "tweetnacl": "^0.14.3"
       }
@@ -11236,7 +11239,7 @@
     "dashdash": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
       "requires": {
         "assert-plus": "^1.0.0"
       }
@@ -11288,9 +11291,9 @@
       "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q=="
     },
     "dayjs": {
-      "version": "1.11.7",
-      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
-      "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
+      "version": "1.11.12",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz",
+      "integrity": "sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg=="
     },
     "debounce-promise": {
       "version": "3.1.2",
@@ -11312,15 +11315,6 @@
       "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
       "dev": true
     },
-    "decomment": {
-      "version": "0.9.4",
-      "resolved": "https://registry.npmjs.org/decomment/-/decomment-0.9.4.tgz",
-      "integrity": "sha512-8eNlhyI5cSU4UbBlrtagWpR03dqXcE5IR9zpe7PnO6UzReXDskucsD8usgrzUmQ6qJ3N82aws/p/mu/jqbURWw==",
-      "dev": true,
-      "requires": {
-        "esprima": "4.0.1"
-      }
-    },
     "deep-eql": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
@@ -11394,15 +11388,15 @@
       "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
     },
     "dependency-tree": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-8.0.0.tgz",
-      "integrity": "sha512-zagnV3jgizudEWY3FIFkGCrRr3+GukSMLlw1snIWAOL2beceC22hBXdeNjCnnfPZvbHIPB9DvacSCfD+IoOG3w==",
+      "version": "8.1.2",
+      "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-8.1.2.tgz",
+      "integrity": "sha512-c4CL1IKxkKng0oT5xrg4uNiiMVFqTGOXqHSFx7XEFdgSsp6nw3AGGruICppzJUrfad/r7GLqt26rmWU4h4j39A==",
       "dev": true,
       "requires": {
         "commander": "^2.20.3",
         "debug": "^4.3.1",
-        "filing-cabinet": "^3.0.0",
-        "precinct": "^7.0.0",
+        "filing-cabinet": "^3.0.1",
+        "precinct": "^8.0.0",
         "typescript": "^3.9.7"
       },
       "dependencies": {
@@ -11412,6 +11406,27 @@
           "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
           "dev": true
         },
+        "precinct": {
+          "version": "8.3.1",
+          "resolved": "https://registry.npmjs.org/precinct/-/precinct-8.3.1.tgz",
+          "integrity": "sha512-pVppfMWLp2wF68rwHqBIpPBYY8Kd12lDhk8LVQzOwqllifVR15qNFyod43YLyFpurKRZQKnE7E4pofAagDOm2Q==",
+          "dev": true,
+          "requires": {
+            "commander": "^2.20.3",
+            "debug": "^4.3.3",
+            "detective-amd": "^3.1.0",
+            "detective-cjs": "^3.1.1",
+            "detective-es6": "^2.2.1",
+            "detective-less": "^1.0.2",
+            "detective-postcss": "^4.0.0",
+            "detective-sass": "^3.0.1",
+            "detective-scss": "^2.0.1",
+            "detective-stylus": "^1.0.0",
+            "detective-typescript": "^7.0.0",
+            "module-definition": "^3.3.1",
+            "node-source-walk": "^4.2.0"
+          }
+        },
         "typescript": {
           "version": "3.9.10",
           "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
@@ -11443,9 +11458,9 @@
       }
     },
     "detective-es6": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-2.2.0.tgz",
-      "integrity": "sha512-fSpNY0SLER7/sVgQZ1NxJPwmc9uCTzNgdkQDhAaj8NPYwr7Qji9QBcmbNvtMCnuuOGMuKn3O7jv0An+/WRWJZQ==",
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-2.2.2.tgz",
+      "integrity": "sha512-eZUKCUsbHm8xoeoCM0z6JFwvDfJ5Ww5HANo+jPR7AzkFpW9Mun3t/TqIF2jjeWa2TFbAiGaWESykf2OQp3oeMw==",
       "dev": true,
       "requires": {
         "node-source-walk": "^4.0.0"
@@ -11600,7 +11615,7 @@
     "ecc-jsbn": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+      "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
       "requires": {
         "jsbn": "~0.1.0",
         "safer-buffer": "^2.1.0"
@@ -12318,7 +12333,7 @@
     "extsprintf": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+      "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="
     },
     "fast-deep-equal": {
       "version": "3.1.3",
@@ -12371,23 +12386,23 @@
       "integrity": "sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA=="
     },
     "filing-cabinet": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-3.0.0.tgz",
-      "integrity": "sha512-o8Qac5qxZ1uVidR4Sd7ZQbbqObFZlqXU4xu1suAYg9PQPcQFNTzOmxQa/MehIDMgIvXHTb42mWPNV9l3eHBPSw==",
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-3.3.1.tgz",
+      "integrity": "sha512-renEK4Hh6DUl9Vl22Y3cxBq1yh8oNvbAdXnhih0wVpmea+uyKjC9K4QeRjUaybIiIewdzfum+Fg15ZqJ/GyCaA==",
       "dev": true,
       "requires": {
         "app-module-path": "^2.2.0",
         "commander": "^2.20.3",
-        "debug": "^4.3.1",
-        "decomment": "^0.9.3",
-        "enhanced-resolve": "^5.3.2",
+        "debug": "^4.3.3",
+        "enhanced-resolve": "^5.8.3",
         "is-relative-path": "^1.0.2",
         "module-definition": "^3.3.1",
-        "module-lookup-amd": "^7.0.0",
-        "resolve": "^1.19.0",
+        "module-lookup-amd": "^7.0.1",
+        "resolve": "^1.21.0",
         "resolve-dependency-path": "^2.0.0",
         "sass-lookup": "^3.0.0",
         "stylus-lookup": "^3.0.1",
+        "tsconfig-paths": "^3.10.1",
         "typescript": "^3.9.7"
       },
       "dependencies": {
@@ -12501,7 +12516,7 @@
     "forever-agent": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+      "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="
     },
     "form-data": {
       "version": "2.3.3",
@@ -12662,7 +12677,7 @@
     "getpass": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
       "requires": {
         "assert-plus": "^1.0.0"
       }
@@ -12771,7 +12786,7 @@
     "har-schema": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+      "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q=="
     },
     "har-validator": {
       "version": "5.1.5",
@@ -12909,6 +12924,16 @@
         }
       }
     },
+    "http-signature": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
+      "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==",
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "jsprim": "^2.0.2",
+        "sshpk": "^1.14.1"
+      }
+    },
     "ieee754": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -12923,7 +12948,7 @@
     "immediate": {
       "version": "3.0.6",
       "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
-      "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
     },
     "import-fresh": {
       "version": "3.3.0",
@@ -13242,7 +13267,7 @@
     "isstream": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+      "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
     },
     "istanbul-lib-coverage": {
       "version": "3.0.0",
@@ -13381,7 +13406,7 @@
     "jsbn": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
+      "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
     },
     "jsdoc-type-pratt-parser": {
       "version": "3.1.0",
@@ -13415,7 +13440,7 @@
     "json-stringify-safe": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+      "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="
     },
     "json5": {
       "version": "2.2.3",
@@ -13436,6 +13461,17 @@
         "graceful-fs": "^4.1.6"
       }
     },
+    "jsprim": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
+      "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==",
+      "requires": {
+        "assert-plus": "1.0.0",
+        "extsprintf": "1.3.0",
+        "json-schema": "0.4.0",
+        "verror": "1.10.0"
+      }
+    },
     "jszip": {
       "version": "3.10.1",
       "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@@ -14533,7 +14569,7 @@
     "performance-now": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+      "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
     },
     "phin": {
       "version": "3.7.1",
@@ -14613,12 +14649,12 @@
       }
     },
     "postman-request": {
-      "version": "2.88.1-postman.32",
-      "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.32.tgz",
-      "integrity": "sha512-Zf5D0b2G/UmnmjRwQKhYy4TBkuahwD0AMNyWwFK3atxU1u5GS38gdd7aw3vyR6E7Ii+gD//hREpflj2dmpbE7w==",
+      "version": "2.88.1-postman.34",
+      "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.34.tgz",
+      "integrity": "sha512-GkolJ4cIzgamcwHRDkeZc/taFWO1u2HuGNML47K9ZAsFH2LdEkS5Yy8QanpzhjydzV3WWthl9v60J8E7SjKodQ==",
       "requires": {
         "@postman/form-data": "~3.1.1",
-        "@postman/tough-cookie": "~4.1.2-postman.1",
+        "@postman/tough-cookie": "~4.1.3-postman.1",
         "@postman/tunnel-agent": "^0.6.3",
         "aws-sign2": "~0.7.0",
         "aws4": "^1.12.0",
@@ -14641,27 +14677,6 @@
         "uuid": "^8.3.2"
       },
       "dependencies": {
-        "http-signature": {
-          "version": "1.3.6",
-          "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
-          "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==",
-          "requires": {
-            "assert-plus": "^1.0.0",
-            "jsprim": "^2.0.2",
-            "sshpk": "^1.14.1"
-          }
-        },
-        "jsprim": {
-          "version": "2.0.2",
-          "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
-          "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==",
-          "requires": {
-            "assert-plus": "1.0.0",
-            "extsprintf": "1.3.0",
-            "json-schema": "0.4.0",
-            "verror": "1.10.0"
-          }
-        },
         "uuid": {
           "version": "8.3.2",
           "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@@ -14763,9 +14778,9 @@
       "dev": true
     },
     "psl": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
-      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+      "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
     },
     "pump": {
       "version": "3.0.0",
@@ -14989,9 +15004,9 @@
       }
     },
     "roku-deploy": {
-      "version": "3.12.0",
-      "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.12.0.tgz",
-      "integrity": "sha512-YiCZeQ+sEmFW9ZfXtMNH+/CBSHQ5deNZYWONM+s6gCEQsrz7kCMFPj5YEdgfqW+d2b8G1ve9GELHcSt2FsfM8g==",
+      "version": "3.12.1",
+      "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.12.1.tgz",
+      "integrity": "sha512-PEOdiFKGW1jrcoC9zjb+9kOWC/Q3eH7dzPvSi5kKignjNcyDiyJi+/wINwNrzw9SsxAVtw+NLvYueyZi9wQVsw==",
       "requires": {
         "chalk": "^2.4.2",
         "dateformat": "^3.0.3",
@@ -15358,9 +15373,9 @@
       "dev": true
     },
     "sshpk": {
-      "version": "1.16.1",
-      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
-      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+      "version": "1.18.0",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
+      "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
       "requires": {
         "asn1": "~0.2.3",
         "assert-plus": "^1.0.0",
@@ -15722,7 +15737,7 @@
     "tweetnacl": {
       "version": "0.14.5",
       "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
+      "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
     },
     "type-check": {
       "version": "0.4.0",
@@ -15899,7 +15914,7 @@
     "verror": {
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
       "requires": {
         "assert-plus": "^1.0.0",
         "core-util-is": "1.0.2",
diff --git a/package.json b/package.json
index b5b48f5c..9a87784d 100644
--- a/package.json
+++ b/package.json
@@ -157,7 +157,7 @@
     "parse-ms": "^2.1.0",
     "readline": "^1.3.0",
     "require-relative": "^0.8.7",
-    "roku-deploy": "^3.12.0",
+    "roku-deploy": "^3.12.1",
     "safe-json-stringify": "^1.2.0",
     "serialize-error": "^7.0.1",
     "source-map": "^0.7.4",
diff --git a/src/DiagnosticCollection.ts b/src/DiagnosticCollection.ts
index b2861b13..89dc7013 100644
--- a/src/DiagnosticCollection.ts
+++ b/src/DiagnosticCollection.ts
@@ -7,7 +7,7 @@ export class DiagnosticCollection {
     private previousDiagnosticsByFile: Record<string, KeyedDiagnostic[]> = {};
 
     /**
-     * Get a patch of any changed diagnostics since last time. This takes a single project and diagnostics, but evaulates
+     * Get a patch of any changed diagnostics since last time. This takes a single project and diagnostics, but evaluates
      * the patch based on all previously seen projects. It's supposed to be a rolling patch.
      * This will include _ALL_ diagnostics for a file if any diagnostics have changed for that file, due to how the language server expects diagnostics to be sent.
      */
@@ -41,7 +41,7 @@ export class DiagnosticCollection {
             for (let i = diagnostics.length - 1; i >= 0; i--) {
                 const diagnostic = diagnostics[i];
 
-                //remember this diagnostic key for use when deduping down below
+                //remember this diagnostic key for use when deduplicating down below
                 diagnosticsByKey.set(diagnostic.key, diagnostic);
 
                 //unlink the diagnostic from this project
diff --git a/src/LanguageServer.spec.ts b/src/LanguageServer.spec.ts
index 5c31aabf..dab32a67 100644
--- a/src/LanguageServer.spec.ts
+++ b/src/LanguageServer.spec.ts
@@ -1,7 +1,7 @@
 import { expect } from './chai-config.spec';
 import * as fsExtra from 'fs-extra';
 import * as path from 'path';
-import type { DidChangeWatchedFilesParams, Location } from 'vscode-languageserver';
+import type { DidChangeWatchedFilesParams, Location, PublishDiagnosticsParams } from 'vscode-languageserver';
 import { FileChangeType } from 'vscode-languageserver';
 import { Deferred } from './deferred';
 import { CustomCommands, LanguageServer } from './LanguageServer';
@@ -10,7 +10,8 @@ import { standardizePath as s, util } from './util';
 import { TextDocument } from 'vscode-languageserver-textdocument';
 import type { Program } from './Program';
 import * as assert from 'assert';
-import { expectZeroDiagnostics, trim } from './testHelpers.spec';
+import type { PartialDiagnostic } from './testHelpers.spec';
+import { expectZeroDiagnostics, normalizeDiagnostics, trim } from './testHelpers.spec';
 import { isBrsFile, isLiteralString } from './astUtils/reflection';
 import { createVisitor, WalkMode } from './astUtils/visitors';
 import { tempDir, rootDir } from './testHelpers.spec';
@@ -19,6 +20,8 @@ import { BusyStatusTracker } from './BusyStatusTracker';
 import type { BscFile } from '.';
 import type { Project } from './lsp/Project';
 import { LogLevel, Logger, createLogger } from './logging';
+import { DiagnosticMessages } from './DiagnosticMessages';
+import { standardizePath } from 'roku-deploy';
 
 const sinon = createSandbox();
 
@@ -92,6 +95,7 @@ describe('LanguageServer', () => {
         server['hasConfigurationCapability'] = true;
     });
     afterEach(() => {
+        sinon.restore();
         fsExtra.emptyDirSync(tempDir);
         server['dispose']();
         LanguageServer.enableThreadingDefault = enableThreadingDefault;
@@ -155,7 +159,7 @@ describe('LanguageServer', () => {
         });
     });
 
-    describe('project-reload', () => {
+    describe('project-activate', () => {
         it('should sync all open document changes to all projects', async () => {
 
             //force an open text document
@@ -169,7 +173,7 @@ describe('LanguageServer', () => {
                 return Promise.resolve();
             });
 
-            server['projectManager']['emit']('project-reload', {
+            server['projectManager']['emit']('project-activate', {
                 project: server['projectManager'].projects[0]
             });
 
@@ -186,7 +190,7 @@ describe('LanguageServer', () => {
         });
 
         it('handles when there were no open documents', () => {
-            server['projectManager']['emit']('project-reload', {
+            server['projectManager']['emit']('project-activate', {
                 project: {
                     projectNumber: 1
                 }
@@ -1202,6 +1206,187 @@ describe('LanguageServer', () => {
         ]);
         expectZeroDiagnostics((server['projectManager'].projects[0] as Project)['builder'].program);
     });
+
+    describe('sendDiagnostics', () => {
+        let diagnostics = {};
+        let diagnosticsDeferred = new Deferred();
+
+        beforeEach(() => {
+            server['connection'] = connection as any;
+            sinon.stub(Logger.prototype, 'write').callsFake(() => {
+                //do nothing, logging is too noisy
+            });
+
+            diagnosticsDeferred = new Deferred();
+
+            let timer = setTimeout(() => { }, 0);
+            sinon.stub(server['connection'], 'sendDiagnostics').callsFake((params: PublishDiagnosticsParams) => {
+                clearTimeout(timer);
+                if (params.diagnostics.length === 0) {
+                    delete diagnostics[params.uri];
+                } else {
+                    diagnostics[params.uri] = params.diagnostics;
+                }
+                //debounce the promise so we get the final snapshot of diagnostics sent
+                timer = setTimeout(() => {
+                    diagnosticsDeferred.resolve();
+                    diagnosticsDeferred = new Deferred();
+                }, 100);
+                return Promise.resolve();
+            });
+        });
+
+        async function diagnosticsEquals(expectedDiagnostics: Record<string, Array<PartialDiagnostic | string | number>>) {
+            //wait for a patch
+            await diagnosticsDeferred.promise;
+
+            let actualDiagnostics = { ...diagnostics };
+
+            //normalize the keys
+            for (let collection of [actualDiagnostics, expectedDiagnostics]) {
+                //convert a URI-like string to an fsPath
+                for (let key in collection) {
+                    let keyNormalized = key.startsWith('file:') ? URI.parse(key).fsPath : key;
+                    keyNormalized = standardizePath(
+                        path.isAbsolute(keyNormalized) ? keyNormalized : s`${rootDir}/${keyNormalized}`
+                    );
+                    //if we changed the key, replace this in the collection
+                    if (keyNormalized !== key) {
+                        collection[keyNormalized] = collection[key];
+                        delete collection[key];
+                    }
+                }
+            }
+
+            //normalize the actual diagnostics so it has diagnostics in the same format as the expected
+            for (let key in actualDiagnostics) {
+                const [actual, expected] = normalizeDiagnostics(actualDiagnostics[key], expectedDiagnostics[key] ?? []);
+                actualDiagnostics[key] = actual;
+                expectedDiagnostics[key] = expected;
+            }
+            expect(actualDiagnostics).to.eql(expectedDiagnostics);
+        }
+
+        it('clears standalone file project diagnostics when that file is adopted by at least one project', async () => {
+            const projectManager = server['projectManager'];
+            const documentManager = projectManager['documentManager'];
+
+            //force instant document flushes
+            documentManager['options'].delay = 0;
+
+            //build a small functional project
+            fsExtra.outputFileSync(`${rootDir}/source/main.bs`, `
+                sub main()
+                    alpha.beta()
+                    print missing
+                end sub
+            `);
+            fsExtra.outputFileSync(`${rootDir}/source/lib.bs`, `
+                    namespace alpha
+                    sub beta()
+                    end sub
+                end namespace
+            `);
+            fsExtra.outputFileSync(`${rootDir}/bsconfig.json`, `
+                {
+                    "files": ["source/**/*.bs"],
+                    //silence the logger, it's noisy
+                    "logLevel": "error"
+                }
+            `);
+            server.run();
+
+            await server['onInitialized']();
+
+            await diagnosticsEquals({
+                'source/main.bs': [
+                    DiagnosticMessages.cannotFindName('missing').message
+                ]
+            });
+
+            const document = TextDocument.create(
+                URI.file(s`${rootDir}/source/main.bs`).toString(),
+                'brightscript',
+                0, `
+                    sub main()
+                        alpha.beta()
+                        print missing2
+                    end sub
+                `
+            );
+            //open the main.bs file so it gets reloaded in a standalone project
+            server['documents'].all = () => [document];
+
+            await server['onTextDocumentDidChangeContent']({
+                document: document
+            });
+
+            await diagnosticsEquals({
+                'source/main.bs': [
+                    DiagnosticMessages.cannotFindName('missing2').message
+                ]
+            });
+
+            //mangle the bsconfig and then sync the project. this should produce new diagnostics from the file as it's now in a standalone project
+            fsExtra.outputFileSync(`${rootDir}/bsconfig.json`, `
+                    {
+                        "files": ["source/lib.bs"]
+                //missing closing curly brace (and also have a comma, oops
+            `);
+
+            //tell the language server we've changed a bsconfig. it'll reload the file (fail cuz syntax error) and create a standalone project for the opened file
+            await server['onDidChangeWatchedFiles']({
+                changes: [{
+                    type: FileChangeType.Changed,
+                    uri: URI.file(`${rootDir}/bsconfig.json`).toString()
+                }]
+            });
+
+            //wait for the manager to settle
+            await projectManager.onIdle();
+
+            //we should get a patch clearing the diagnostics from the unloaded main project, then
+            //when the standalone project finishes loading, we should get another diagnostics patch, then
+            //when the project activates, we flush open document changes. So now the opened copy of the file is re-processed and we get the correct error message `missing2`
+            await diagnosticsEquals({
+                'source/main.bs': [
+                    DiagnosticMessages.cannotFindName('alpha').message,
+                    DiagnosticMessages.cannotFindName('missing2').message
+                ],
+                'bsconfig.json': [
+                    'Encountered syntax errors in bsconfig.json: CloseBraceExpected'
+                ]
+            });
+
+
+            //now fix the bsconfig and sync again. This should dispose the standalone project and send new diagnostics
+            fsExtra.outputFileSync(`${rootDir}/bsconfig.json`, `
+                {
+                    "files": ["source/**/*.bs"],
+                    //silence the logger, it's noisy
+                    "logLevel": "error"
+                }
+            `);
+
+            //tell the language server we've changed a bsconfig
+            await server['onDidChangeWatchedFiles']({
+                changes: [{
+                    type: FileChangeType.Changed,
+                    uri: URI.file(`${rootDir}/bsconfig.json`).toString()
+                }]
+            });
+
+            //let the manager settle
+            await projectManager.onIdle();
+
+            //and then get more diagnostics when the opened file is parsed as well
+            await diagnosticsEquals({
+                'source/main.bs': [
+                    DiagnosticMessages.cannotFindName('missing2').message
+                ]
+            });
+        });
+    });
 });
 
 export function getFileProtocolPath(fullPath: string) {
diff --git a/src/LanguageServer.ts b/src/LanguageServer.ts
index be668b31..f4ee127a 100644
--- a/src/LanguageServer.ts
+++ b/src/LanguageServer.ts
@@ -48,7 +48,6 @@ import ignore from 'ignore';
 import * as micromatch from 'micromatch';
 import type { LspProject, LspDiagnostic } from './lsp/LspProject';
 import { PathFilterer } from './lsp/PathFilterer';
-import type { Project } from './lsp/Project';
 import type { WorkspaceConfig } from './lsp/ProjectManager';
 import { ProjectManager } from './lsp/ProjectManager';
 import * as fsExtra from 'fs-extra';
@@ -72,14 +71,6 @@ export class LanguageServer {
      */
     private projectManager: ProjectManager;
 
-    /**
-     * These projects are created on the fly whenever a file is opened that is not included
-     * in any of the workspace-based projects.
-     * Basically these are single-file projects to at least get parsing for standalone files.
-     * Also, they should only be created when the file is opened, and destroyed when the file is closed.
-     */
-    protected standaloneFileProjects = {} as Record<string, Project>;
-
     private hasConfigurationCapability = false;
 
     /**
@@ -123,17 +114,18 @@ export class LanguageServer {
             this.sendDiagnostics(event).catch(logAndIgnoreError);
         });
 
-        // Send all open document changes whenever a project reloads. This is necessary because the project loads files from disk
+        // Send all open document changes whenever a project is activated. This is necessary because at project startup, the project loads files from disk
         // and may not have the latest unsaved file changes. Any existing projects that already use these files will just ignore the changes
         // because the file contents haven't changed.
-        this.projectManager.on('project-reload', (event) => {
+
+        this.projectManager.on('project-activate', (event) => {
             //keep logLevel in sync with the most verbose log level found across all projects
             this.syncLogLevel().catch(logAndIgnoreError);
 
             //resend all open document changes
             const documents = [...this.documents.all()];
             if (documents.length > 0) {
-                this.logger.log(`Project ${event.project?.projectNumber} reloaded. Resending all open document changes.`, documents.map(x => x.uri));
+                this.logger.log(`Project ${event.project?.projectNumber} loaded or changed. Resending all open document changes.`, documents.map(x => x.uri));
                 for (const document of this.documents.all()) {
                     this.onTextDocumentDidChangeContent({
                         document: document
@@ -240,7 +232,7 @@ export class LanguageServer {
     public async onInitialized() {
         this.logger.log('onInitialized');
 
-        //set our logger to the most verbose loglevel found across any project
+        //set our logger to the most verbose logLevel found across any project
         await this.syncLogLevel();
 
         try {
diff --git a/src/ProgramBuilder.ts b/src/ProgramBuilder.ts
index f8ffb9f6..9694a8a2 100644
--- a/src/ProgramBuilder.ts
+++ b/src/ProgramBuilder.ts
@@ -129,6 +129,11 @@ export class ProgramBuilder {
             this.loadRequires();
             this.loadPlugins();
         } catch (e: any) {
+            //For now, just use a default options object so we have a functioning program
+            this.options = util.normalizeConfig({
+                showDiagnosticsInConsole: options?.showDiagnosticsInConsole
+            });
+
             if (e?.file && e.message && e.code) {
                 let err = e as BsDiagnostic;
                 this.staticDiagnostics.push(err);
@@ -136,11 +141,8 @@ export class ProgramBuilder {
                 //if this is not a diagnostic, something else is wrong...
                 throw e;
             }
-            this.printDiagnostics();
 
-            //we added diagnostics, so hopefully that draws attention to the underlying issues.
-            //For now, just use a default options object so we have a functioning program
-            this.options = util.normalizeConfig({});
+            this.printDiagnostics();
         }
         this.logger.logLevel = this.options.logLevel;
 
@@ -365,7 +367,7 @@ export class ProgramBuilder {
     }
 
     /**
-     * Run the process once, allowing cancelability.
+     * Run the process once, allowing it to be cancelled.
      * NOTE: This should only be called by `runOnce`.
      */
     private async _runOnce(options: { cancellationToken: { isCanceled: any }; skipValidation: boolean }) {
diff --git a/src/lsp/LspProject.ts b/src/lsp/LspProject.ts
index 81c246d4..b45d5bc9 100644
--- a/src/lsp/LspProject.ts
+++ b/src/lsp/LspProject.ts
@@ -9,6 +9,11 @@ import type { Logger, LogLevel } from '../logging';
  */
 export interface LspProject {
 
+    /**
+     * Is this a standalone project?
+     */
+    isStandaloneProject: boolean;
+
     /**
      * A logger instance used for logging in this project
      */
diff --git a/src/lsp/PathFilterer.spec.ts b/src/lsp/PathFilterer.spec.ts
index f607e540..7322573c 100644
--- a/src/lsp/PathFilterer.spec.ts
+++ b/src/lsp/PathFilterer.spec.ts
@@ -1,5 +1,5 @@
-import { PathFilterer } from './PathFilterer';
-import { rootDir } from '../testHelpers.spec';
+import { PathCollection, PathFilterer } from './PathFilterer';
+import { cwd, rootDir } from '../testHelpers.spec';
 import { expect } from 'chai';
 import { standardizePath as s } from '../util';
 
@@ -24,6 +24,16 @@ describe('PathFilterer', () => {
         ]);
     });
 
+    it('supports standalone workspace style', () => {
+        const filterer = new PathCollection({
+            rootDir: s`${cwd}/src/lsp/standalone-project-1`,
+            globs: [s`${cwd}/.tmp/rootDir/source/main.bs`]
+        });
+        expect(
+            filterer.isMatch(`${cwd}/.tmp/rootDir/source/main.bs`)
+        ).to.be.true;
+    });
+
     it('filters files', () => {
         filterer.registerExcludeList(rootDir, ['**/*.brs']);
         expect(
diff --git a/src/lsp/PathFilterer.ts b/src/lsp/PathFilterer.ts
index ec198d00..04ce75af 100644
--- a/src/lsp/PathFilterer.ts
+++ b/src/lsp/PathFilterer.ts
@@ -2,6 +2,7 @@ import * as micromatch from 'micromatch';
 import * as path from 'path';
 import type { Logger } from '../logging';
 import { createLogger } from '../logging';
+import util from '../util';
 
 /**
  * Manage collections of glob patterns used to filter paths.
@@ -172,10 +173,8 @@ export class PathCollection {
             //build matcher patterns from the globs
             for (const glob of options.globs ?? []) {
                 const pattern = path.resolve(
-                    path.posix.join(
-                        options.rootDir,
-                        glob
-                    )
+                    options.rootDir,
+                    glob
                 ).replace(/\\+/g, '/');
                 this.matchers.push(
                     micromatch.matcher(pattern)
@@ -188,6 +187,8 @@ export class PathCollection {
 
     private matchers: Array<(string) => boolean> = [];
     public isMatch(path: string) {
+        //coerce the path into a normalized form and unix slashes
+        path = util.standardizePath(path).replace(/\\+/g, '/');
         for (let matcher of this.matchers) {
             if (matcher(path)) {
                 return true;
diff --git a/src/lsp/Project.ts b/src/lsp/Project.ts
index f033a1c0..22030269 100644
--- a/src/lsp/Project.ts
+++ b/src/lsp/Project.ts
@@ -7,6 +7,7 @@ import type { CompilerPlugin, Hover, MaybePromise } from '../interfaces';
 import { DiagnosticMessages } from '../DiagnosticMessages';
 import { URI } from 'vscode-uri';
 import { Deferred } from '../deferred';
+import type { StandardizedFileEntry } from 'roku-deploy';
 import { rokuDeploy } from 'roku-deploy';
 import type { DocumentSymbol, Position, Range, Location, WorkspaceSymbol } from 'vscode-languageserver-protocol';
 import { CompletionList } from 'vscode-languageserver-protocol';
@@ -71,15 +72,17 @@ export class Project implements LspProject {
             skipInitialValidation: true
         } as BsConfig;
 
-        //Assign .files (mostly used for standalone projects) if avaiable, as a dedicated assignment because `undefined` overrides the default value in the `bsconfig.json`
+        //Assign .files (mostly used for standalone projects) if available, as a dedicated assignment because `undefined` overrides the default value in the `bsconfig.json`
         if (options.files) {
-            builderOptions.files = options.files;
+            builderOptions.files = rokuDeploy.normalizeFilesArray(options.files);
         }
 
         //run the builder to initialize the program. Skip validation for now, we'll trigger it soon in a more cancellable way
         await this.builder.run({
             ...builderOptions,
-            skipInitialValidation: true
+            skipInitialValidation: true,
+            //don't show diagnostics in the console since this is run via the language server, they're presented in a different way
+            showDiagnosticsInConsole: false
         });
 
         //flush diagnostics every time the program finishes validating
@@ -115,6 +118,8 @@ export class Project implements LspProject {
         };
     }
 
+    public isStandaloneProject = false;
+
     public logger: Logger;
 
     /**
@@ -252,7 +257,7 @@ export class Project implements LspProject {
         }
         if (didChangeFiles) {
             //trigger a validation (but don't wait for it. That way we can cancel it sooner if we get new incoming data or requests)
-            void this.validate();
+            this.validate().catch(e => this.logger.error(e));
         }
 
         this.logger.debug('project.applyFileChanges done', documentActions.map(x => x.srcPath));
@@ -264,7 +269,14 @@ export class Project implements LspProject {
      * Determine if this project will accept the file at the specified path (i.e. does it match a pattern in the project's files array)
      */
     private willAcceptFile(srcPath: string) {
-        return !!rokuDeploy.getDestPath(srcPath, this.builder.program.options.files, this.builder.program.options.rootDir);
+        srcPath = util.standardizePath(srcPath);
+        if (rokuDeploy.getDestPath(srcPath, this.builder.program.options.files, this.builder.program.options.rootDir) !== undefined) {
+            return true;
+            //is this exact path in the `files` array? (this check is mostly for standalone projects)
+        } else if ((this.builder.program.options.files as StandardizedFileEntry[]).find(x => s`${x.src}` === srcPath)) {
+            return true;
+        }
+        return false;
     }
 
     /**
diff --git a/src/lsp/ProjectManager.ts b/src/lsp/ProjectManager.ts
index 79129d3e..17b4eb0c 100644
--- a/src/lsp/ProjectManager.ts
+++ b/src/lsp/ProjectManager.ts
@@ -83,7 +83,7 @@ export class ProjectManager {
             action.id = idSequence++;
         }
 
-        console.info(`Flushing ${actions.length} document changes`, actions.map(x => ({
+        this.logger.info(`Flushing ${actions.length} document changes`, actions.map(x => ({
             type: x.type,
             srcPath: x.srcPath
         })));
@@ -102,7 +102,11 @@ export class ProjectManager {
                 return action.type === 'delete' || filterer.isMatch(action.srcPath);
             });
             if (projectActions.length > 0) {
-                return project.applyFileChanges(projectActions);
+                const responseActions = await project.applyFileChanges(projectActions);
+                return responseActions.map(x => ({
+                    project: project,
+                    action: x
+                }));
             }
         }));
 
@@ -110,17 +114,33 @@ export class ProjectManager {
         const flatResponses = responses.flat();
         for (const action of actions) {
             //skip this action if it doesn't support standalone projects
-            if (!action.allowStandaloneProject || action.type === 'delete') {
+            if (!action.allowStandaloneProject || action.type !== 'set') {
                 continue;
             }
 
-            // create a standalone project if this action was handled by zero projects and was a 'set' operation
-            const wasHandled = flatResponses.some(x => x?.id === action.id && action.type === 'set');
-            if (wasHandled === false) {
+            //a list of responses that handled this action
+            const handledResponses = flatResponses.filter(x => x?.action?.id === action.id && x?.action?.status === 'accepted');
+
+            //remove any standalone project created for this file since it was handled by a normal project
+            if (handledResponses.some(x => x.project.isStandaloneProject === false)) {
+                this.removeStandaloneProject(action.srcPath);
+
+                // create a standalone project if this action was handled by zero normal projects.
+                //(save to call even if there's already a standalone project, won't create dupes)
+            } else {
+                //TODO only create standalone projects for files we understand (brightscript, brighterscript, scenegraph xml, etc)
                 await this.createStandaloneProject(action.srcPath);
             }
+            this.logger.log('flushDocumentChanges complete', event.actions.map(x => x.srcPath));
         }
-        this.logger.log('flushDocumentChanges complete', event.actions.map(x => x.srcPath));
+    }
+
+    /**
+     * Get a standalone project for a given file path
+     */
+    private getStandaloneProject(srcPath: string) {
+        srcPath = util.standardizePath(srcPath);
+        return this.standaloneProjects.find(x => x.srcPath === srcPath);
     }
 
     /**
@@ -129,6 +149,11 @@ export class ProjectManager {
     private async createStandaloneProject(srcPath: string) {
         srcPath = util.standardizePath(srcPath);
 
+        //if we already have a standalone project with this path, do nothing because it already exists
+        if (this.getStandaloneProject(srcPath)) {
+            return;
+        }
+
         this.logger.log(`Creating standalone project for '${srcPath}'`);
 
         const projectNumber = ProjectManager.projectNumberSequence++;
@@ -144,8 +169,11 @@ export class ProjectManager {
                 dest: 'source/standalone.brs'
             }]
         };
+
         const project = this.constructProject(projectOptions) as StandaloneProject;
         project.srcPath = srcPath;
+        project.isStandaloneProject = true;
+
         this.standaloneProjects.push(project);
         await this.activateProject(project, projectOptions);
     }
@@ -362,7 +390,6 @@ export class ProjectManager {
     private async reloadProject(project: LspProject) {
         this.removeProject(project);
         project = await this.createAndActivateProject(project.activateOptions);
-        this.emit('project-reload', { project: project });
     }
 
     /**
@@ -406,7 +433,7 @@ export class ProjectManager {
     public async getCompletions(options: { srcPath: string; position: Position; cancellationToken?: CancellationToken }): Promise<CompletionList> {
         await this.onIdle();
 
-        //if the request has been cancelled since originall requested due to idle time being slow, skip the rest of the wor
+        //if the request has been cancelled since originally requested due to idle time being slow, skip the rest of the wor
         if (options?.cancellationToken?.isCancellationRequested) {
             this.logger.log('ProjectManager getCompletions cancelled', options);
             return;
@@ -642,6 +669,8 @@ export class ProjectManager {
         if (idx > -1) {
             this.projects.splice(idx, 1);
         }
+        //anytime we remove a project, we should emit an event that clears all of its diagnostics
+        this.emit('diagnostics', { project: project, diagnostics: [] });
         project?.dispose();
         this.busyStatusTracker.endAllRunsForScope(project);
     }
@@ -720,6 +749,9 @@ export class ProjectManager {
     private async activateProject(project: LspProject, config: ProjectConfig) {
         await project.activate(config);
 
+        //send an event to indicate that this project has been activated
+        this.emit('project-activate', { project: project });
+
         //register this project's list of files with the path filterer
         const unregister = this.pathFilterer.registerIncludeList(project.rootDir, project.filePatterns);
         project.disposables.push({ dispose: unregister });
@@ -728,7 +760,7 @@ export class ProjectManager {
     public on(eventName: 'validate-begin', handler: (data: { project: LspProject }) => MaybePromise<void>);
     public on(eventName: 'validate-end', handler: (data: { project: LspProject }) => MaybePromise<void>);
     public on(eventName: 'critical-failure', handler: (data: { project: LspProject; message: string }) => MaybePromise<void>);
-    public on(eventName: 'project-reload', handler: (data: { project: LspProject }) => MaybePromise<void>);
+    public on(eventName: 'project-activate', handler: (data: { project: LspProject }) => MaybePromise<void>);
     public on(eventName: 'diagnostics', handler: (data: { project: LspProject; diagnostics: LspDiagnostic[] }) => MaybePromise<void>);
     public on(eventName: string, handler: (payload: any) => MaybePromise<void>) {
         this.emitter.on(eventName, handler as any);
@@ -740,7 +772,7 @@ export class ProjectManager {
     private emit(eventName: 'validate-begin', data: { project: LspProject });
     private emit(eventName: 'validate-end', data: { project: LspProject });
     private emit(eventName: 'critical-failure', data: { project: LspProject; message: string });
-    private emit(eventName: 'project-reload', data: { project: LspProject });
+    private emit(eventName: 'project-activate', data: { project: LspProject });
     private emit(eventName: 'diagnostics', data: { project: LspProject; diagnostics: LspDiagnostic[] });
     private async emit(eventName: string, data?) {
         //emit these events on next tick, otherwise they will be processed immediately which could cause issues
diff --git a/src/lsp/worker/WorkerThreadProject.ts b/src/lsp/worker/WorkerThreadProject.ts
index 82bca30b..086738e4 100644
--- a/src/lsp/worker/WorkerThreadProject.ts
+++ b/src/lsp/worker/WorkerThreadProject.ts
@@ -76,6 +76,8 @@ export class WorkerThreadProject implements LspProject {
 
     public logger: Logger;
 
+    public isStandaloneProject = false;
+
     private activationDeferred = new Deferred();
 
     /**
diff --git a/src/util.spec.ts b/src/util.spec.ts
index 6409714c..9334d008 100644
--- a/src/util.spec.ts
+++ b/src/util.spec.ts
@@ -559,7 +559,7 @@ describe('util', () => {
         let id = 1;
 
         beforeEach(() => {
-            // `require` caches plugins, so  generate a unique plugin name for every test
+            // `require` caches plugins, so generate a unique plugin name for every test
             pluginPath = `${tempDir}/plugin${id++}.js`;
         });
 


Test patch:
diff --git a/src/testHelpers.spec.ts b/src/testHelpers.spec.ts
index 46b775d7..c049e07d 100644
--- a/src/testHelpers.spec.ts
+++ b/src/testHelpers.spec.ts
@@ -14,6 +14,7 @@ import { getDiagnosticLine } from './diagnosticUtils';
 import { firstBy } from 'thenby';
 import undent from 'undent';
 
+export const cwd = s`${__dirname}/../`;
 export const tempDir = s`${__dirname}/../.tmp`;
 export const rootDir = s`${tempDir}/rootDir`;
 export const stagingDir = s`${tempDir}/stagingDir`;
@@ -67,7 +68,7 @@ function cloneObject<TOriginal, TTemplate>(original: TOriginal, template: TTempl
     return clone;
 }
 
-interface PartialDiagnostic {
+export interface PartialDiagnostic {
     range?: Range;
     severity?: DiagnosticSeverity;
     code?: integer | string;
@@ -83,7 +84,7 @@ interface PartialDiagnostic {
 /**
  *  Helper function to clone a Diagnostic so it will give partial data that has the same properties as the expected
  */
-function cloneDiagnostic(actualDiagnosticInput: BsDiagnostic, expectedDiagnostic: BsDiagnostic) {
+export function cloneDiagnostic(actualDiagnosticInput: BsDiagnostic, expectedDiagnostic: BsDiagnostic) {
     const actualDiagnostic = cloneObject(
         actualDiagnosticInput,
         expectedDiagnostic,
@@ -125,14 +126,23 @@ export async function expectDiagnosticsAsync(arg: DiagnosticCollectionAsync, exp
 /**
  * Ensure the DiagnosticCollection exactly contains the data from expected list.
  * @param arg - any object that contains diagnostics (such as `Program`, `Scope`, or even an array of diagnostics)
- * @param expected an array of expected diagnostics. if it's a string, assume that's a diagnostic error message
+ * @param expectedDiagnostics an array of expected diagnostics. if it's a string, assume that's a diagnostic error message
  */
-export function expectDiagnostics(arg: DiagnosticCollection, expected: Array<PartialDiagnostic | string | number>) {
-    const actualDiagnostics = sortDiagnostics(
-        getDiagnostics(arg)
-    );
-    const expectedDiagnostics = sortDiagnostics(
-        expected.map(x => {
+export function expectDiagnostics(arg: DiagnosticCollection, expectedDiagnostics: Array<PartialDiagnostic | string | number>) {
+    const [actual, expected] = normalizeDiagnostics(getDiagnostics(arg), expectedDiagnostics);
+    expect(actual).to.eql(expected);
+}
+
+/**
+ * Normalizes a collection of diagnostics for comparison
+ * @param actualDiagnostics the actual diagnostics that were found
+ * @param expectedDiagnostics the diagnostics we're expecing
+ */
+export function normalizeDiagnostics(actualDiagnostics: BsDiagnostic[], expectedDiagnostics: Array<PartialDiagnostic | string | number>) {
+    actualDiagnostics = sortDiagnostics([...actualDiagnostics]);
+
+    expectedDiagnostics = sortDiagnostics(
+        expectedDiagnostics.map(x => {
             let result = x;
             if (typeof x === 'string') {
                 result = { message: x };
@@ -143,13 +153,12 @@ export function expectDiagnostics(arg: DiagnosticCollection, expected: Array<Par
         })
     );
 
-    const actual = [] as BsDiagnostic[];
     for (let i = 0; i < actualDiagnostics.length; i++) {
-        const expectedDiagnostic = expectedDiagnostics[i];
+        const expectedDiagnostic = expectedDiagnostics[i] as BsDiagnostic;
         const actualDiagnostic = cloneDiagnostic(actualDiagnostics[i], expectedDiagnostic);
-        actual.push(actualDiagnostic as any);
+        actualDiagnostics[i] = actualDiagnostic as any;
     }
-    expect(actual).to.eql(expectedDiagnostics);
+    return [actualDiagnostics, expectedDiagnostics];
 }
 
 /**