feat: config reloading

This commit is contained in:
cha0s 2024-02-07 10:15:07 -06:00
parent c552a64ee5
commit 47aed47b5e
29 changed files with 918 additions and 116 deletions

400
package-lock.json generated
View File

@ -2021,6 +2021,18 @@
"node": ">=6.9.0"
}
},
"node_modules/@dependents/detective-less": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@dependents/detective-less/-/detective-less-4.1.0.tgz",
"integrity": "sha512-KrkT6qO5NxqNfy68sBl6CTSoJ4SNDIS5iQArkibhlbGU4LaDukZ3q2HIkh8aUKDio6o4itU4xDR7t82Y2eP1Bg==",
"dependencies": {
"gonzales-pe": "^4.3.0",
"node-source-walk": "^6.0.1"
},
"engines": {
"node": ">=14"
}
},
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
@ -4225,6 +4237,87 @@
"@types/node": "*"
}
},
"node_modules/@typescript-eslint/types": {
"version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
"integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz",
"integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
"dependencies": {
"@typescript-eslint/types": "5.62.0",
"@typescript-eslint/visitor-keys": "5.62.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz",
"integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
"dependencies": {
"@typescript-eslint/types": "5.62.0",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@ -5037,6 +5130,14 @@
"node": "*"
}
},
"node_modules/ast-module-types": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-5.0.0.tgz",
"integrity": "sha512-JvqziE0Wc0rXQfma0HZC/aY7URXHFuZV84fJRtP8u+lhp0JYCNd5wJzVXP45t0PH0Mej3ynlzvdyITYIu0G4LQ==",
"engines": {
"node": ">=14"
}
},
"node_modules/ast-types-flow": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
@ -7135,6 +7236,105 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/detective-amd": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-5.0.2.tgz",
"integrity": "sha512-XFd/VEQ76HSpym80zxM68ieB77unNuoMwopU2TFT/ErUk5n4KvUTwW4beafAVUugrjV48l4BmmR0rh2MglBaiA==",
"dependencies": {
"ast-module-types": "^5.0.0",
"escodegen": "^2.0.0",
"get-amd-module-type": "^5.0.1",
"node-source-walk": "^6.0.1"
},
"bin": {
"detective-amd": "bin/cli.js"
},
"engines": {
"node": ">=14"
}
},
"node_modules/detective-cjs": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-5.0.1.tgz",
"integrity": "sha512-6nTvAZtpomyz/2pmEmGX1sXNjaqgMplhQkskq2MLrar0ZAIkHMrDhLXkRiK2mvbu9wSWr0V5/IfiTrZqAQMrmQ==",
"dependencies": {
"ast-module-types": "^5.0.0",
"node-source-walk": "^6.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/detective-es6": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-4.0.1.tgz",
"integrity": "sha512-k3Z5tB4LQ8UVHkuMrFOlvb3GgFWdJ9NqAa2YLUU/jTaWJIm+JJnEh4PsMc+6dfT223Y8ACKOaC0qcj7diIhBKw==",
"dependencies": {
"node-source-walk": "^6.0.1"
},
"engines": {
"node": ">=14"
}
},
"node_modules/detective-postcss": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-6.1.3.tgz",
"integrity": "sha512-7BRVvE5pPEvk2ukUWNQ+H2XOq43xENWbH0LcdCE14mwgTBEAMoAx+Fc1rdp76SmyZ4Sp48HlV7VedUnP6GA1Tw==",
"dependencies": {
"is-url": "^1.2.4",
"postcss": "^8.4.23",
"postcss-values-parser": "^6.0.2"
},
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/detective-sass": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-5.0.3.tgz",
"integrity": "sha512-YsYT2WuA8YIafp2RVF5CEfGhhyIVdPzlwQgxSjK+TUm3JoHP+Tcorbk3SfG0cNZ7D7+cYWa0ZBcvOaR0O8+LlA==",
"dependencies": {
"gonzales-pe": "^4.3.0",
"node-source-walk": "^6.0.1"
},
"engines": {
"node": ">=14"
}
},
"node_modules/detective-scss": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-4.0.3.tgz",
"integrity": "sha512-VYI6cHcD0fLokwqqPFFtDQhhSnlFWvU614J42eY6G0s8c+MBhi9QAWycLwIOGxlmD8I/XvGSOUV1kIDhJ70ZPg==",
"dependencies": {
"gonzales-pe": "^4.3.0",
"node-source-walk": "^6.0.1"
},
"engines": {
"node": ">=14"
}
},
"node_modules/detective-stylus": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/detective-stylus/-/detective-stylus-4.0.0.tgz",
"integrity": "sha512-TfPotjhszKLgFBzBhTOxNHDsutIxx9GTWjrL5Wh7Qx/ydxKhwUrlSFeLIn+ZaHPF+h0siVBkAQSuy6CADyTxgQ==",
"engines": {
"node": ">=14"
}
},
"node_modules/detective-typescript": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-11.1.0.tgz",
"integrity": "sha512-Mq8egjnW2NSCkzEb/Az15/JnBI/Ryyl6Po0Y+0mABTFvOS6DAyUGRZqz1nyhu4QJmWWe0zaGs/ITIBeWkvCkGw==",
"dependencies": {
"@typescript-eslint/typescript-estree": "^5.59.5",
"ast-module-types": "^5.0.0",
"node-source-walk": "^6.0.1",
"typescript": "^5.0.4"
},
"engines": {
"node": "^14.14.0 || >=16.0.0"
}
},
"node_modules/diff": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
@ -7839,6 +8039,26 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/escodegen": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
"integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
"dependencies": {
"esprima": "^4.0.1",
"estraverse": "^5.2.0",
"esutils": "^2.0.2"
},
"bin": {
"escodegen": "bin/escodegen.js",
"esgenerate": "bin/esgenerate.js"
},
"engines": {
"node": ">=6.0"
},
"optionalDependencies": {
"source-map": "~0.6.1"
}
},
"node_modules/eslint": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
@ -8445,7 +8665,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true,
"license": "BSD-2-Clause",
"bin": {
"esparse": "bin/esparse.js",
@ -9454,6 +9673,18 @@
"node": ">=6.9.0"
}
},
"node_modules/get-amd-module-type": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-5.0.1.tgz",
"integrity": "sha512-jb65zDeHyDjFR1loOVk0HQGM5WNwoGB8aLWy3LKCieMKol0/ProHkhO2X1JxojuN10vbz1qNn09MJ7tNp7qMzw==",
"dependencies": {
"ast-module-types": "^5.0.0",
"node-source-walk": "^6.0.1"
},
"engines": {
"node": ">=14"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@ -9882,6 +10113,20 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/gonzales-pe": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz",
"integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==",
"dependencies": {
"minimist": "^1.2.5"
},
"bin": {
"gonzales": "bin/gonzales.js"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
@ -11518,6 +11763,22 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-url": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww=="
},
"node_modules/is-url-superb": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-4.0.0.tgz",
"integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-weakmap": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
@ -11574,7 +11835,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
"integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=16"
@ -11652,6 +11912,11 @@
"node": "*"
}
},
"node_modules/javascript-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz",
"integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg=="
},
"node_modules/jest-diff": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
@ -13295,6 +13560,21 @@
"node": ">=0.10.0"
}
},
"node_modules/module-definition": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/module-definition/-/module-definition-5.0.1.tgz",
"integrity": "sha512-kvw3B4G19IXk+BOXnYq/D/VeO9qfHaapMeuS7w7sNUqmGaA6hywdFHMi+VWeR9wUScXM7XjoryTffCZ5B0/8IA==",
"dependencies": {
"ast-module-types": "^5.0.0",
"node-source-walk": "^6.0.1"
},
"bin": {
"module-definition": "bin/cli.js"
},
"engines": {
"node": ">=14"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
@ -13527,6 +13807,17 @@
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
"license": "MIT"
},
"node_modules/node-source-walk": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-6.0.2.tgz",
"integrity": "sha512-jn9vOIK/nfqoFCcpK89/VCVaLg1IHE6UVfDOzvqmANaJ/rWCTEdH8RZ1V278nv2jr36BJdyQXIAavBLXpzdlag==",
"dependencies": {
"@babel/parser": "^7.21.8"
},
"engines": {
"node": ">=14"
}
},
"node_modules/nopt": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz",
@ -15691,6 +15982,22 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"license": "MIT"
},
"node_modules/postcss-values-parser": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz",
"integrity": "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==",
"dependencies": {
"color-name": "^1.1.4",
"is-url-superb": "^4.0.0",
"quote-unquote": "^1.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"postcss": "^8.2.9"
}
},
"node_modules/postcss/node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@ -15735,6 +16042,39 @@
"node": ">=10"
}
},
"node_modules/precinct": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/precinct/-/precinct-11.0.5.tgz",
"integrity": "sha512-oHSWLC8cL/0znFhvln26D14KfCQFFn4KOLSw6hmLhd+LQ2SKt9Ljm89but76Pc7flM9Ty1TnXyrA2u16MfRV3w==",
"dependencies": {
"@dependents/detective-less": "^4.1.0",
"commander": "^10.0.1",
"detective-amd": "^5.0.2",
"detective-cjs": "^5.0.1",
"detective-es6": "^4.0.1",
"detective-postcss": "^6.1.3",
"detective-sass": "^5.0.3",
"detective-scss": "^4.0.3",
"detective-stylus": "^4.0.0",
"detective-typescript": "^11.1.0",
"module-definition": "^5.0.1",
"node-source-walk": "^6.0.2"
},
"bin": {
"precinct": "bin/cli.js"
},
"engines": {
"node": "^14.14.0 || >=16.0.0"
}
},
"node_modules/precinct/node_modules/commander": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
"engines": {
"node": ">=14"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -15971,6 +16311,11 @@
"node": ">=8"
}
},
"node_modules/quote-unquote": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/quote-unquote/-/quote-unquote-1.0.0.tgz",
"integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg=="
},
"node_modules/random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
@ -19614,6 +19959,25 @@
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"license": "0BSD"
},
"node_modules/tsutils": {
"version": "3.21.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
"dependencies": {
"tslib": "^1.8.1"
},
"engines": {
"node": ">= 6"
},
"peerDependencies": {
"typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
}
},
"node_modules/tsutils/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/tuf-js": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz",
@ -20643,8 +21007,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
"integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
"dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^3.1.1"
},
@ -20992,6 +21354,27 @@
"node": ">= 6"
}
},
"node_modules/yaml-loader": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/yaml-loader/-/yaml-loader-0.8.0.tgz",
"integrity": "sha512-LjeKnTzVBKWiQBeE2L9ssl6WprqaUIxCSNs5tle8PaDydgu3wVFXTbMfsvF2MSErpy9TDVa092n4q6adYwJaWg==",
"dependencies": {
"javascript-stringify": "^2.0.1",
"loader-utils": "^2.0.0",
"yaml": "^2.0.0"
},
"engines": {
"node": ">= 12.13"
}
},
"node_modules/yaml-loader/node_modules/yaml": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
"integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==",
"engines": {
"node": ">= 14"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
@ -21109,6 +21492,7 @@
"babel-merge": "^3.0.0",
"chai": "4.2.0",
"chai-as-promised": "7.1.1",
"chokidar": "^3.5.3",
"commander": "11.1.0",
"copy-webpack-plugin": "^11.0.0",
"enhanced-resolve": "^5.9.2",
@ -21126,12 +21510,14 @@
"graceful-fs": "^4.2.11",
"js-yaml": "4.1.0",
"mocha": "^10.2.0",
"precinct": "^11.0.5",
"rimraf": "^5.0.5",
"source-map-loader": "4.0.1",
"source-map-support": "0.5.19",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-node-externals": "^3.0.0"
"webpack-node-externals": "^3.0.0",
"yaml-loader": "^0.8.0"
},
"bin": {
"flecks": "build/cli.js"
@ -21166,7 +21552,8 @@
"lodash.get": "^4.4.2",
"set-value": "^4.1.0",
"source-map-support": "0.5.19",
"supports-color": "9.2.1"
"supports-color": "9.2.1",
"which": "^4.0.0"
},
"devDependencies": {
"@babel/core": "^7.12.10",
@ -21388,7 +21775,6 @@
"dependencies": {
"@flecks/core": "^3.2.1",
"babel-merge": "^3.0.0",
"chokidar": "^3.5.3",
"debug": "^4.3.3",
"mocha": "^10.2.0"
},

View File

@ -197,7 +197,15 @@ module.exports = class Build extends Flecks {
return this.resolver.resolve(join(fleck, 'build', config));
}
async runtimeCompiler(runtime, config) {
async runtimeCompiler(runtime, config, env, argv) {
if ('production' !== argv.mode) {
config.module.rules.push(
{
test: /\.ya?ml$/,
use: 'yaml-loader',
},
);
}
// Compile?
const compiled = this.roots.filter(([path, request]) => path !== request);
if (compiled.length > 0) {

View File

@ -4,7 +4,11 @@ const {
readFile,
writeFile,
} = require('fs/promises');
const {join} = require('path');
const {
dirname,
join,
resolve,
} = require('path');
const {parseAsync} = require('@babel/core');
const {default: generate} = require('@babel/generator');
@ -22,7 +26,10 @@ const {
lockFile,
spawnWith,
} = require('@flecks/core/src/server');
const chokidar = require('chokidar');
const {glob} = require('glob');
const {load: loadYml} = require('js-yaml');
const {paperwork} = require('precinct');
const {rimraf} = require('rimraf');
const addPathsToYml = require('./add-paths-to-yml');
@ -57,6 +64,39 @@ function stringLiteralSinglequote(value) {
};
}
const dependencies = {};
async function gatherDependencies(request, resolver) {
const resolved = await resolver.resolve(request);
if (!resolved || dependencies[resolved]) {
return;
}
try {
const localDeps = paperwork(resolved);
dependencies[resolved] = true;
await Promise.all(
localDeps.map(
async (dependency) => {
const resolvedDependency = await resolver.resolve(
resolve(dirname(resolved), dependency),
);
return resolvedDependency && gatherDependencies(resolvedDependency, resolver);
},
),
);
}
// eslint-disable-next-line no-empty
catch (error) {}
}
async function rootsDependencies(roots, resolver) {
return Promise.all(
roots.map(([request]) => (
gatherDependencies(join(request, 'build', 'flecks.bootstrap.js'), resolver)
)),
);
}
exports.commands = (program, flecks) => {
const commands = {
add: {
@ -176,7 +216,12 @@ exports.commands = (program, flecks) => {
production,
watch,
} = opts;
debug('Building...', opts);
if (watch) {
debug('Watching...', opts);
}
else {
debug('Building...', opts);
}
const webpackConfig = await flecks.resolveBuildConfig('fleckspack.config.js');
const cmd = [
await binaryPath('webpack'),
@ -184,16 +229,70 @@ exports.commands = (program, flecks) => {
'--config', webpackConfig,
'--mode', (production && !hot) ? 'production' : 'development',
];
return spawnWith(
cmd,
{
env: {
FLECKS_CORE_IS_PRODUCTION: production,
...(target ? {FLECKS_CORE_BUILD_LIST: target} : {}),
...(hot ? {FLECKS_ENV__flecks_server__hot: 'true'} : {}),
let webpack;
const spawnWebpack = () => {
webpack = spawnWith(
cmd,
{
env: {
FLECKS_BUILD_IS_PRODUCTION: production,
...(target ? {FLECKS_CORE_BUILD_LIST: target} : {}),
...(hot ? {FLECKS_ENV__flecks_server__hot: 'true'} : {}),
},
useFork: true,
},
},
);
);
webpack.on('message', (message) => {
if ('restart' === message) {
webpack.kill();
spawnWebpack();
}
});
};
spawnWebpack();
if (watch) {
await rootsDependencies(flecks.roots, flecks.resolver);
const watched = Object.keys(dependencies);
watched.push(
...await Promise.all(
flecks.roots.map(([, request]) => flecks.resolver.resolve(join(request, 'package.json'))),
),
);
watched.push(join(FLECKS_CORE_ROOT, 'build/flecks.yml'));
const watcher = chokidar.watch(watched, {
awaitWriteFinish: {
stabilityThreshold: 50,
pollInterval: 5,
},
});
await new Promise((resolve, reject) => {
watcher.on('error', reject);
watcher.on('ready', resolve);
});
const configPath = join(FLECKS_CORE_ROOT, 'build', 'flecks.yml');
const initialConfig = loadYml(await readFile(configPath));
watcher.on('all', async (event, path) => {
let respawn = false;
if (configPath === path) {
const config = loadYml(await readFile(configPath));
if (
JSON.stringify(Object.keys(initialConfig).sort())
!== JSON.stringify(Object.keys(config).sort())
) {
debug('Config keys changed');
respawn = true;
}
}
else {
respawn = true;
}
if (respawn) {
debug('Respawning...');
webpack.kill();
spawnWebpack();
}
});
}
},
};
}

View File

@ -34,6 +34,7 @@
"babel-merge": "^3.0.0",
"chai": "4.2.0",
"chai-as-promised": "7.1.1",
"chokidar": "^3.5.3",
"commander": "11.1.0",
"copy-webpack-plugin": "^11.0.0",
"enhanced-resolve": "^5.9.2",
@ -51,11 +52,13 @@
"graceful-fs": "^4.2.11",
"js-yaml": "4.1.0",
"mocha": "^10.2.0",
"precinct": "^11.0.5",
"rimraf": "^5.0.5",
"source-map-loader": "4.0.1",
"source-map-support": "0.5.19",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-node-externals": "^3.0.0"
"webpack-node-externals": "^3.0.0",
"yaml-loader": "^0.8.0"
}
}

View File

@ -102,6 +102,21 @@ export const hooks = {
}
},
/**
* Invoked when `flecks.yml` is hot reloaded. Throw to abort hot reload and restart application.
* Must be synchronous.
*
* @param {string} fleck The fleck whose config changed.
* @param {Object} config The new config.
*/
'@flecks/core.reload': (fleck, config, flecks) => {
if ('i-care-about' === fleck) {
if (flecks.get(`${fleck}.volatile`) !== config.volatile) {
throw new Error('Changes too volatile');
}
}
}
/**
* Invoked when the application is starting.
* @invoke SequentialAsync

View File

@ -74,13 +74,13 @@ class Flecks {
* @param {object} runtime.flecks fleck modules.
*/
constructor({
bootstrappedConfig = {},
config = {},
flecks = {},
} = {}) {
const emptyConfigForAllFlecks = Object.fromEntries(
Object.keys(flecks).map((path) => [path, {}]),
);
this.config = {...emptyConfigForAllFlecks, ...config};
this.bootstrappedConfig = JSON.parse(JSON.stringify(bootstrappedConfig));
this.originalConfig = JSON.parse(JSON.stringify(config));
this.config = {};
const entries = Object.entries(flecks);
this.constructor.debugSilly('paths: %O', entries.map(([fleck]) => fleck));
for (let i = 0; i < entries.length; i++) {
@ -100,8 +100,9 @@ class Flecks {
*/
configureFleckDefaults(fleck) {
this.config[fleck] = {
...this.bootstrappedConfig[fleck] || {},
...this.invokeFleck('@flecks/core.config', fleck),
...this.config[fleck],
...this.originalConfig[fleck],
};
}
@ -111,7 +112,7 @@ class Flecks {
* @protected
*/
configureFlecksDefaults() {
const flecks = this.flecksImplementing('@flecks/core.config');
const flecks = Object.keys(this.flecks);
for (let i = 0; i < flecks.length; i++) {
this.configureFleckDefaults(flecks[i]);
}

View File

@ -30,7 +30,8 @@
"lodash.get": "^4.4.2",
"set-value": "^4.1.0",
"source-map-support": "0.5.19",
"supports-color": "9.2.1"
"supports-color": "9.2.1",
"which": "^4.0.0"
},
"devDependencies": {
"@babel/core": "^7.12.10",

View File

@ -1,5 +1,11 @@
/* eslint-disable global-require */
const {join} = require('path');
const {
FLECKS_CORE_ROOT = process.cwd(),
} = process.env;
module.exports = {
Class: require('../build/class'),
compose: require('../build/compose'),
@ -7,6 +13,27 @@ module.exports = {
EventEmitter: require('../build/event-emitter'),
...require('../build/flecks'),
hooks: {
'@flecks/core.hmr': (path, config, flecks) => {
if (path !== join(FLECKS_CORE_ROOT, 'build', 'flecks.yml')) {
return;
}
Object.entries(flecks.constructor.dealiasedConfig(config))
.forEach(([fleck, value]) => {
if (JSON.stringify(flecks.originalConfig[fleck]) !== JSON.stringify(value)) {
const fleckList = flecks.flecksImplementing('@flecks/core.reload');
for (let i = 0; i < fleckList.length; ++i) {
try {
flecks.invokeFleck('@flecks/core.reload', fleckList[i], fleck, value);
}
catch (error) {
throw new Error(`'${fleck}' aborted reload: ${error.name}: ${error.message}`);
}
}
flecks.originalConfig[fleck] = value;
flecks.configureFleckDefaults(fleck);
}
});
},
'@flecks/web.config': async (req, flecks) => ({
id: flecks.get('@flecks/core.id'),
}),

View File

@ -1,4 +1,4 @@
const {exec, spawn} = require('child_process');
const {exec, fork, spawn} = require('child_process');
const D = require('../../build/debug');
@ -28,14 +28,15 @@ exports.processCode = (child) => new Promise((resolve, reject) => {
const children = [];
exports.spawnWith = (cmd, opts = {}) => {
debug("spawning: '%s'", cmd.join(' '));
debugSilly('with options: %O', opts);
const child = spawn(cmd[0], cmd.slice(1), {
const {useFork, ...rest} = opts;
debug("%sing: '%s'", useFork ? 'fork' : 'spawn', cmd.join(' '));
debugSilly('with options: %O', rest);
const child = (useFork ? fork : spawn)(cmd[0], cmd.slice(1), {
stdio: 'inherit',
...opts,
...rest,
env: {
...process.env,
...opts.env,
...rest.env,
},
});
children.push(child);

View File

@ -22,7 +22,6 @@
"dependencies": {
"@flecks/core": "^3.2.1",
"babel-merge": "^3.0.0",
"chokidar": "^3.5.3",
"debug": "^4.3.3",
"mocha": "^10.2.0"
},

View File

@ -3,10 +3,10 @@ const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'
const plugins = [];
const {
FLECKS_CORE_IS_PRODUCTION,
FLECKS_BUILD_IS_PRODUCTION,
} = process.env;
if ('true' !== FLECKS_CORE_IS_PRODUCTION) {
if ('true' !== FLECKS_BUILD_IS_PRODUCTION) {
plugins.push('react-refresh/babel');
}

View File

@ -1,13 +1,9 @@
const {externals} = require('@flecks/build/src/server');
const D = require('@flecks/core/build/debug');
const debug = D('@flecks/server/build/runtime');
const {join} = require('path');
const {version} = require('../package.json');
module.exports = async (config, env, argv, flecks) => {
const runtimePath = await flecks.resolver.resolve('@flecks/server/runtime');
async function runtimeModule(compilation, flecks) {
const {compiler} = compilation;
// Inject flecks configuration.
const paths = Object.keys(flecks.flecks);
const resolvedPaths = (await Promise.all(
@ -15,8 +11,18 @@ module.exports = async (config, env, argv, flecks) => {
))
.filter(([, resolved]) => resolved)
.map(([path]) => path);
const ymlPath = join(flecks.root, 'build', 'flecks.yml');
const runtime = {
config: JSON.stringify(flecks.config),
/* eslint-disable indent */
bootstrappedConfig: JSON.stringify(flecks.invoke('@flecks/core.config')),
config: (`
dealiasedConfig(${
'production' === compiler.options.mode
? JSON.stringify(flecks.originalConfig)
: `require('${ymlPath}').default`
})
`),
/* eslint-enable indent */
loadFlecks: [
'async () => (',
' Object.fromEntries(',
@ -48,11 +54,23 @@ module.exports = async (config, env, argv, flecks) => {
.map(([key, value]) => `"${key}": ${value}`).join(', ')
}}`;
const source = [
`const {dealiasedConfig} = {${flecks.constructor.dealiasedConfig.toString()}};`,
"process.env.FLECKS_CORE_BUILD_TARGET = 'server';",
`module.exports = (async () => (${runtimeString}))();`,
];
// HMR.
source.push('if (module.hot) {');
source.push(` module.hot.accept('${ymlPath}', async () => {`);
source.push(` const M = require('${ymlPath}').default;`);
source.push(' try {');
source.push(` global.flecks.invokeSequential('@flecks/core.hmr', '${ymlPath}', M);`);
source.push(' }');
source.push(' catch (error) {');
// eslint-disable-next-line no-template-curly-in-string
source.push(' console.error(`flecks.reload() failed: ${error.message}`);');
source.push(' module.hot.invalidate();');
source.push(' }');
source.push(' });');
// Keep HMR junk out of our output path.
source.push(' const {glob} = require("glob");');
source.push(' const {join} = require("path");');
@ -61,7 +79,7 @@ module.exports = async (config, env, argv, flecks) => {
source.push(' module.hot.addStatusHandler(async (status) => {');
source.push(' if ("idle" === status) {');
source.push(' const disposing = await glob(');
source.push(` join('${config.output.path}', \`*\${previousHash}.hot-update.*\`),`);
source.push(` join('${compiler.options.output.path}', \`*\${previousHash}.hot-update.*\`),`);
source.push(' );');
source.push(' await Promise.all(disposing.map((filename) => unlink(filename)));');
source.push(' previousHash = __webpack_hash__;');
@ -83,43 +101,8 @@ module.exports = async (config, env, argv, flecks) => {
source.push(' });');
});
source.push('}');
// Create runtime.
config.module.rules.push(
{
test: runtimePath,
use: [
{
loader: runtimePath,
options: {
source: source.join('\n'),
},
},
],
},
);
const allowlist = [
'@flecks/server/entry',
'@flecks/server/runtime',
/^@babel\/runtime\/helpers\/esm/,
];
config.resolve.alias['@flecks/server/runtime$'] = runtimePath;
Object.entries(flecks.resolver.aliases).forEach(([path, request]) => {
debug('server runtime de-externalized %s, alias: %s', path, request);
allowlist.push(new RegExp(`^${path}`));
});
// Stubs.
flecks.stubs.forEach((stub) => {
config.resolve.alias[stub] = false;
});
await flecks.runtimeCompiler('server', config, env, argv);
// Rewrite to signals for HMR.
if ('production' !== argv.mode) {
allowlist.push(/^webpack\/hot\/signal/);
}
// Externalize the rest.
config.externals = await externals({
additionalModuleDirs: flecks.resolver.modules,
allowlist,
importType: 'commonjs',
});
};
// Create asset.
return source.join('\n');
}
exports.runtimeModule = runtimeModule;

View File

@ -3,10 +3,15 @@ const {delimiter, join} = require('path');
const {
banner,
defaultConfig,
externals,
webpack,
} = require('@flecks/build/src/server');
const runtime = require('./runtime');
const D = require('@flecks/core/build/debug');
const debug = D('@flecks/server/build');
const {runtimeModule} = require('./runtime');
const startServer = require('./start');
const {
@ -66,7 +71,53 @@ module.exports = async (env, argv, flecks) => {
}),
);
}
// Build the server runtime.
await runtime(config, env, argv, flecks);
// Create runtime.
const runtimePath = await flecks.resolver.resolve('@flecks/server/runtime');
config.module.rules.push(
{
test: runtimePath,
use: [
{
loader: runtimePath,
options: {
source: await runtimeModule(
{
compiler: {
options: {
mode: argv.mode, output: {path: config.output.path},
},
},
},
flecks,
),
},
},
],
},
);
const allowlist = [
'@flecks/server/entry',
'@flecks/server/runtime',
/^@babel\/runtime\/helpers\/esm/,
];
Object.entries(flecks.resolver.aliases).forEach(([path, request]) => {
debug('server runtime de-externalized %s, alias: %s', path, request);
allowlist.push(new RegExp(`^${path}`));
});
// Stubs.
flecks.stubs.forEach((stub) => {
config.resolve.alias[stub] = false;
});
await flecks.runtimeCompiler('server', config, env, argv);
// Rewrite to signals for HMR.
if ('production' !== argv.mode) {
allowlist.push(/^webpack\/hot\/signal/);
}
// Externalize the rest.
config.externals = await externals({
additionalModuleDirs: flecks.resolver.modules,
allowlist,
importType: 'commonjs',
});
return config;
};

View File

@ -19,7 +19,7 @@ class StartServerPlugin {
['exit', 'SIGINT', 'SIGTERM']
.forEach((event) => {
process.on(event, () => {
this.worker.kill('exit' === event ? 'SIGKILL' : event);
this.worker?.kill('exit' === event ? 'SIGKILL' : event);
});
});
}
@ -102,7 +102,7 @@ class StartServerPlugin {
if (this.worker.exitedAfterDisconnect) {
// eslint-disable-next-line no-console
console.error('[HMR] Restarting application...');
this.worker = cluster.fork(env);
process.send('restart');
}
});
}

View File

@ -6,7 +6,6 @@ import {join} from 'path';
import {D, Flecks} from '@flecks/core';
(async () => {
// eslint-disable-next-line import/no-extraneous-dependencies
const runtime = await import('@flecks/server/runtime');
const {loadFlecks, version} = runtime;
// eslint-disable-next-line no-console
@ -33,7 +32,7 @@ import {D, Flecks} from '@flecks/core';
})();
if (module.hot) {
module.hot.accept('./runtime', () => {
module.hot.accept('@flecks/server/runtime', () => {
if (cluster.isWorker) {
cluster.worker.disconnect();
const error = new Error('Restart requested!');

View File

@ -3,10 +3,10 @@ import {join} from 'path';
import {expect} from 'chai';
import {createApplicationAt, build} from './build/build';
import {createApplication, build} from './build/build';
it('builds for development', async () => {
const path = await createApplicationAt('development');
const path = await createApplication();
await build(path, {args: ['-d']});
let artifact;
try {

View File

@ -3,10 +3,10 @@ import {join} from 'path';
import {expect} from 'chai';
import {createApplicationAt, build} from './build/build';
import {createApplication, build} from './build/build';
it('builds for production', async () => {
const path = await createApplicationAt('production');
const path = await createApplication();
await build(path);
let artifact;
try {

View File

@ -4,6 +4,7 @@ import {join} from 'path';
import {rimraf} from '@flecks/build/server';
import {binaryPath, processCode, spawnWith} from '@flecks/core/server';
import id from './id';
import {listen} from './listen';
const {
@ -13,12 +14,18 @@ const {
export const applications = join(FLECKS_CORE_ROOT, 'node_modules', '.cache', '@flecks', 'server');
export const template = join(FLECKS_CORE_ROOT, 'test', 'server', 'template');
export async function createApplicationAt(path) {
await rimraf(join(applications, path));
await mkdir(join(applications, path), {recursive: true});
const qualified = join(applications, path, process.pid.toString());
await cp(template, qualified, {recursive: true});
return qualified;
export async function createApplication() {
const path = join(applications, await id());
await rimraf(path);
await mkdir(path, {recursive: true});
await cp(template, path, {recursive: true});
// sheeeeesh
process.prependListener('message', async (message) => {
if ('__workerpool-terminate__' === message) {
rimraf.sync(path);
}
});
return path;
}
export async function buildChild(path, {args = [], opts = {}} = {}) {
@ -27,7 +34,7 @@ export async function buildChild(path, {args = [], opts = {}} = {}) {
{
...opts,
env: {
FLECKS_ENV__flecks_server__stats: '{"all": false}',
FLECKS_ENV__flecks_server__stats: '{"preset": "none"}',
FLECKS_ENV__flecks_server__start: 0,
FLECKS_CORE_ROOT: path,
...opts.env,
@ -41,7 +48,7 @@ export async function build(path, {args = [], opts = {}} = {}) {
}
export async function serverActions(path, actions) {
const {connected, listening, path: socketPath} = await listen();
const {listening, path: socketPath, socketServer} = await listen();
await listening;
const server = spawnWith(
['node', join(path, 'dist', 'server')],
@ -53,7 +60,7 @@ export async function serverActions(path, actions) {
);
const [code, results] = await Promise.all([
processCode(server),
connected.then(async (socket) => {
socketServer.waitForSocket().then(async (socket) => {
const results = [];
await actions.reduce(
(p, action) => (

View File

@ -52,17 +52,26 @@ export async function listen() {
await mkdir(dirname(path), {recursive: true});
const server = createServer();
server.listen(path);
return {
connected: new Promise((resolve, reject) => {
server.waitForSocket = () => (
new Promise((resolve, reject) => {
server.on('error', reject);
server.on('connection', (socket) => {
resolve(new SocketWrapper(socket));
});
}),
})
);
return {
listening: new Promise((resolve, reject) => {
server.on('error', reject);
server.on('listening', resolve);
}),
path,
socketServer: server,
};
}
export async function socketListener() {
const {listening, path: socketPath, socketServer} = await listen();
await listening;
return {socketServer, socketPath};
}

View File

@ -0,0 +1,65 @@
import {writeFile} from 'fs/promises';
import {join} from 'path';
import {expect} from 'chai';
import {build, createApplication} from './build/build';
import {socketListener} from './build/listen';
it('updates config', async () => {
const path = await createApplication();
const {socketPath, socketServer} = await socketListener();
build(
path,
{
args: ['-h'],
opts: {
env: {
FLECKS_ENV__flecks_server__start: true,
FLECKS_SERVER_TEST_SOCKET: socketPath,
},
},
},
);
const socket = await socketServer.waitForSocket();
expect((await socket.send({type: 'config.get', payload: 'comm.foo'})).payload)
.to.equal('bar');
await writeFile(
join(path, 'build', 'flecks.yml'),
`
'@flecks/build': {}
'@flecks/core': {}
'@flecks/server': {}
'comm:./comm': {foo: 'baz'}
`,
);
await socket.waitForHmr();
expect((await socket.send({type: 'config.get', payload: 'comm.foo'})).payload)
.to.equal('baz');
let restarted;
const whatHappened = Promise.race([
socket.waitForHmr()
.then(() => {
restarted = false;
})
.catch(() => {}),
new Promise((resolve) => {
socket.socket.on('close', () => {
restarted = true;
resolve();
});
}),
]);
await writeFile(
join(path, 'build', 'flecks.yml'),
`
'@flecks/build': {}
'@flecks/core': {}
'@flecks/server': {}
'comm:./comm': {foo: 'fail'}
`,
);
await whatHappened;
expect(restarted)
.to.be.true;
});

View File

@ -0,0 +1,59 @@
import {writeFile} from 'fs/promises';
import {join} from 'path';
import {expect} from 'chai';
import {buildChild, createApplication} from './build/build';
import {socketListener} from './build/listen';
it('restarts when config keys change', async () => {
const path = await createApplication();
const {socketPath, socketServer} = await socketListener();
await buildChild(
path,
{
args: ['-w'],
opts: {
env: {
FLECKS_ENV__flecks_server__start: true,
FLECKS_SERVER_TEST_SOCKET: socketPath,
},
},
},
);
const socket = await socketServer.waitForSocket();
let restarted;
const whatHappened = Promise.race([
socket.waitForHmr()
.then(() => {
restarted = false;
})
.catch(() => {}),
new Promise((resolve) => {
socket.socket.on('close', () => {
restarted = true;
resolve();
});
}),
]);
await writeFile(
join(path, 'build', 'flecks.yml'),
`
'@flecks/build': {}
'@flecks/core': {}
'@flecks/repl': {}
'@flecks/server': {}
'comm:./comm': {}
`,
);
await whatHappened;
expect(restarted)
.to.be.true;
let config;
await socketServer.waitForSocket()
.then(async (socket) => {
({payload: config} = await socket.send({type: 'config.get', payload: '@flecks/repl/server'}));
});
expect(config)
.to.not.be.undefined;
});

View File

@ -0,0 +1,40 @@
import {writeFile} from 'fs/promises';
import {join} from 'path';
import {expect} from 'chai';
import {build, createApplication} from './build/build';
import {socketListener} from './build/listen';
it('updates config', async () => {
const path = await createApplication();
const {socketPath, socketServer} = await socketListener();
build(
path,
{
args: ['-h'],
opts: {
env: {
FLECKS_ENV__flecks_server__start: true,
FLECKS_SERVER_TEST_SOCKET: socketPath,
},
},
},
);
const socket = await socketServer.waitForSocket();
expect((await socket.send({type: 'config.get', payload: '@flecks/core.id'})).payload)
.to.equal('flecks');
await writeFile(
join(path, 'build', 'flecks.yml'),
`
'@flecks/build': {}
'@flecks/core': {id: 'testing'}
'@flecks/server': {}
'comm:./comm': {}
`,
);
await socket.waitForHmr();
expect((await socket.send({type: 'config.get', payload: '@flecks/core.id'})).payload)
.to.equal('testing');
await socket.send({type: 'exit'});
});

View File

@ -1,9 +1,9 @@
import {expect} from 'chai';
import {build, createApplicationAt, serverActions} from './build/build';
import {build, createApplication, serverActions} from './build/build';
it('propagates bootstrap config', async () => {
const path = await createApplicationAt('runtime-config-bootstrap');
const path = await createApplication();
await build(path, {args: ['-d']});
const {results: [{payload: id}]} = await serverActions(path, [
{type: 'config.get', payload: '@flecks/core.id'},

View File

@ -3,10 +3,10 @@ import {join} from 'path';
import {expect} from 'chai';
import {build, createApplicationAt, serverActions} from './build/build';
import {build, createApplication, serverActions} from './build/build';
it('propagates bootstrap config', async () => {
const path = await createApplicationAt('runtime-config-override');
const path = await createApplication();
await writeFile(
join(path, 'build', 'flecks.yml'),
`

View File

@ -1,9 +1,9 @@
import {expect} from 'chai';
import {build, createApplicationAt, serverActions} from './build/build';
import {build, createApplication, serverActions} from './build/build';
it('propagates runtime config', async () => {
const path = await createApplicationAt('runtime-config-runtime');
const path = await createApplication();
await build(path, {args: ['-d']});
const {results: [{payload: foo}]} = await serverActions(path, [
{type: 'config.get', payload: 'comm.foo'},

View File

@ -1,9 +1,9 @@
import {expect} from 'chai';
import {build, createApplicationAt, serverActions} from './build/build';
import {build, createApplication, serverActions} from './build/build';
it('connects', async () => {
const path = await createApplicationAt('runtime-connect');
const path = await createApplication();
await build(path, {args: ['-d']});
const {code} = await serverActions(path, [
{type: 'exit', payload: 42},

View File

@ -0,0 +1,43 @@
import {writeFile} from 'fs/promises';
import {join} from 'path';
import {expect} from 'chai';
import {buildChild, createApplication} from './build/build';
import {socketListener} from './build/listen';
it('restarts when root sources change', async () => {
const path = await createApplication();
const {socketPath, socketServer} = await socketListener();
await buildChild(
path,
{
args: ['-w'],
opts: {
env: {
FLECKS_ENV__flecks_server__start: true,
FLECKS_SERVER_TEST_SOCKET: socketPath,
},
},
},
);
const socket = await socketServer.waitForSocket();
let restarted;
const whatHappened = Promise.race([
socket.waitForHmr()
.then(() => {
restarted = false;
})
.catch(() => {}),
new Promise((resolve) => {
socket.socket.on('close', () => {
restarted = true;
resolve();
});
}),
]);
await writeFile(join(path, 'comm', 'package.json'), '{}');
await whatHappened;
expect(restarted)
.to.be.true;
});

View File

@ -6,6 +6,11 @@ const {
} = process.env;
export const hooks = {
'@flecks/core.reload': (fleck, config) => {
if ('comm' === fleck && 'fail' === config.foo) {
throw new Error();
}
},
'@flecks/core.hmr': async (path, M, flecks) => {
if (!flecks.socket) {
return;

View File

@ -52,6 +52,7 @@ module.exports = async (config, env, argv, flecks) => {
.map(([path]) => path);
const source = [
'module.exports = (update) => (async () => ({',
` bootstrappedConfig: ${JSON.stringify(buildFlecks.invoke('@flecks/core.config'))},`,
" config: window[Symbol.for('@flecks/web.config')],",
' flecks: Object.fromEntries(await Promise.all([',
...resolvedPaths