diff --git a/.gitignore b/.gitignore index 38676c8f..4e787e83 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,8 @@ common/autoinstallers/*/.npmrc **/.rush/temp/ # Visual Studio Code -.vscode \ No newline at end of file +.vscode + +# MikroORM temporary directory +modules/*/temp +test/temp \ No newline at end of file diff --git a/common/changes/@fabernovel/heart-api/feat-mysql-module_2023-07-16-21-06.json b/common/changes/@fabernovel/heart-api/feat-mysql-module_2023-07-16-21-06.json new file mode 100644 index 00000000..d7244e4c --- /dev/null +++ b/common/changes/@fabernovel/heart-api/feat-mysql-module_2023-07-16-21-06.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@fabernovel/heart-api", + "comment": "", + "type": "none" + } + ], + "packageName": "@fabernovel/heart-api" +} \ No newline at end of file diff --git a/common/changes/@fabernovel/heart-bigquery/feat-mysql-module_2023-07-16-21-06.json b/common/changes/@fabernovel/heart-bigquery/feat-mysql-module_2023-07-16-21-06.json new file mode 100644 index 00000000..79317f57 --- /dev/null +++ b/common/changes/@fabernovel/heart-bigquery/feat-mysql-module_2023-07-16-21-06.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@fabernovel/heart-bigquery", + "comment": "", + "type": "none" + } + ], + "packageName": "@fabernovel/heart-bigquery" +} \ No newline at end of file diff --git a/common/changes/@fabernovel/heart-cli/feat-mysql-module_2023-07-02-21-07.json b/common/changes/@fabernovel/heart-cli/feat-mysql-module_2023-07-02-21-07.json new file mode 100644 index 00000000..d6b5cf10 --- /dev/null +++ b/common/changes/@fabernovel/heart-cli/feat-mysql-module_2023-07-02-21-07.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@fabernovel/heart-cli", + "comment": "Add the ability to migrate databases from the new database listener modules", + "type": "none" + } + ], + "packageName": "@fabernovel/heart-cli" +} \ No newline at end of file diff --git a/common/changes/@fabernovel/heart-common/feat-mysql-module_2023-07-02-21-07.json b/common/changes/@fabernovel/heart-common/feat-mysql-module_2023-07-02-21-07.json new file mode 100644 index 00000000..e121bfe6 --- /dev/null +++ b/common/changes/@fabernovel/heart-common/feat-mysql-module_2023-07-02-21-07.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@fabernovel/heart-common", + "comment": "Add the new database listener module definition and database entities", + "type": "none" + } + ], + "packageName": "@fabernovel/heart-common" +} \ No newline at end of file diff --git a/common/changes/@fabernovel/heart-greenit/feat-mysql-module_2023-07-16-21-06.json b/common/changes/@fabernovel/heart-greenit/feat-mysql-module_2023-07-16-21-06.json new file mode 100644 index 00000000..cf872683 --- /dev/null +++ b/common/changes/@fabernovel/heart-greenit/feat-mysql-module_2023-07-16-21-06.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@fabernovel/heart-greenit", + "comment": "", + "type": "none" + } + ], + "packageName": "@fabernovel/heart-greenit" +} \ No newline at end of file diff --git a/common/changes/@fabernovel/heart-lighthouse/feat-mysql-module_2023-07-16-21-06.json b/common/changes/@fabernovel/heart-lighthouse/feat-mysql-module_2023-07-16-21-06.json new file mode 100644 index 00000000..1dc7a5b4 --- /dev/null +++ b/common/changes/@fabernovel/heart-lighthouse/feat-mysql-module_2023-07-16-21-06.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@fabernovel/heart-lighthouse", + "comment": "", + "type": "none" + } + ], + "packageName": "@fabernovel/heart-lighthouse" +} \ No newline at end of file diff --git a/common/changes/@fabernovel/heart-mysql/feat-mysql-module_2023-07-02-21-07.json b/common/changes/@fabernovel/heart-mysql/feat-mysql-module_2023-07-02-21-07.json new file mode 100644 index 00000000..331f7133 --- /dev/null +++ b/common/changes/@fabernovel/heart-mysql/feat-mysql-module_2023-07-02-21-07.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@fabernovel/heart-mysql", + "comment": "Initial release of the module", + "type": "none" + } + ], + "packageName": "@fabernovel/heart-mysql" +} \ No newline at end of file diff --git a/common/changes/@fabernovel/heart-observatory/feat-mysql-module_2023-07-16-21-06.json b/common/changes/@fabernovel/heart-observatory/feat-mysql-module_2023-07-16-21-06.json new file mode 100644 index 00000000..efb3f0bd --- /dev/null +++ b/common/changes/@fabernovel/heart-observatory/feat-mysql-module_2023-07-16-21-06.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@fabernovel/heart-observatory", + "comment": "", + "type": "none" + } + ], + "packageName": "@fabernovel/heart-observatory" +} \ No newline at end of file diff --git a/common/changes/@fabernovel/heart-slack/feat-mysql-module_2023-07-02-21-07.json b/common/changes/@fabernovel/heart-slack/feat-mysql-module_2023-07-02-21-07.json new file mode 100644 index 00000000..37a195f9 --- /dev/null +++ b/common/changes/@fabernovel/heart-slack/feat-mysql-module_2023-07-02-21-07.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@fabernovel/heart-slack", + "comment": "Update the module description", + "type": "none" + } + ], + "packageName": "@fabernovel/heart-slack" +} \ No newline at end of file diff --git a/common/changes/@fabernovel/heart-ssllabs-server/feat-mysql-module_2023-07-16-21-06.json b/common/changes/@fabernovel/heart-ssllabs-server/feat-mysql-module_2023-07-16-21-06.json new file mode 100644 index 00000000..49356fd5 --- /dev/null +++ b/common/changes/@fabernovel/heart-ssllabs-server/feat-mysql-module_2023-07-16-21-06.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@fabernovel/heart-ssllabs-server", + "comment": "", + "type": "none" + } + ], + "packageName": "@fabernovel/heart-ssllabs-server" +} \ No newline at end of file diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 7c39f820..6fae7eb6 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -39,8 +39,8 @@ importers: specifier: ^29.5.3 version: 29.5.3 '@types/node': - specifier: ^18.15.10 - version: 18.15.10 + specifier: ^18.16.19 + version: 18.16.19 '@typescript-eslint/eslint-plugin': specifier: ^5.57.0 version: 5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6) @@ -52,7 +52,7 @@ importers: version: 8.36.0 jest: specifier: ^29.6.1 - version: 29.6.1(@types/node@18.15.10) + version: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) rimraf: specifier: ^5.0.1 version: 5.0.1 @@ -85,8 +85,8 @@ importers: specifier: ^29.5.3 version: 29.5.3 '@types/node': - specifier: ^18.15.10 - version: 18.15.10 + specifier: ^18.16.19 + version: 18.16.19 '@typescript-eslint/eslint-plugin': specifier: ^5.57.0 version: 5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6) @@ -98,7 +98,7 @@ importers: version: 8.36.0 jest: specifier: ^29.6.1 - version: 29.6.1(@types/node@18.15.10) + version: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) rimraf: specifier: ^5.0.1 version: 5.0.1 @@ -134,8 +134,8 @@ importers: specifier: ^29.5.3 version: 29.5.3 '@types/node': - specifier: ^18.15.10 - version: 18.15.10 + specifier: ^18.16.19 + version: 18.16.19 '@typescript-eslint/eslint-plugin': specifier: ^5.57.0 version: 5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6) @@ -150,7 +150,7 @@ importers: version: 4.15.0 jest: specifier: ^29.6.1 - version: 29.6.1(@types/node@18.15.10) + version: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) rimraf: specifier: ^5.0.1 version: 5.0.1 @@ -166,6 +166,12 @@ importers: ../../modules/common: dependencies: + '@mikro-orm/core': + specifier: ^5.7.13 + version: 5.7.13(@mikro-orm/migrations@5.7.13)(@mikro-orm/mysql@5.7.13) + '@mikro-orm/reflection': + specifier: ^5.7.13 + version: 5.7.13(@mikro-orm/core@5.7.13) ajv: specifier: ^8.12.0 version: 8.12.0 @@ -189,8 +195,8 @@ importers: specifier: ^29.5.3 version: 29.5.3 '@types/node': - specifier: ^18.15.10 - version: 18.15.10 + specifier: ^18.16.19 + version: 18.16.19 '@typescript-eslint/eslint-plugin': specifier: ^5.57.0 version: 5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6) @@ -205,7 +211,7 @@ importers: version: 4.15.0 jest: specifier: ^29.6.1 - version: 29.6.1(@types/node@18.15.10) + version: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) lighthouse: specifier: ^10.0.2 version: 10.0.2(typescript@5.1.6) @@ -247,8 +253,8 @@ importers: specifier: ^29.5.3 version: 29.5.3 '@types/node': - specifier: ^18.15.10 - version: 18.15.10 + specifier: ^18.16.19 + version: 18.16.19 '@typescript-eslint/eslint-plugin': specifier: ^5.57.0 version: 5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6) @@ -260,7 +266,7 @@ importers: version: 8.36.0 jest: specifier: ^29.6.1 - version: 29.6.1(@types/node@18.15.10) + version: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) ts-jest: specifier: ^29.1.1 version: 29.1.1(@babel/core@7.22.9)(jest@29.6.1)(typescript@5.1.6) @@ -290,8 +296,8 @@ importers: specifier: ^29.5.3 version: 29.5.3 '@types/node': - specifier: ^18.15.10 - version: 18.15.10 + specifier: ^18.16.19 + version: 18.16.19 '@typescript-eslint/eslint-plugin': specifier: ^5.57.0 version: 5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6) @@ -303,7 +309,7 @@ importers: version: 8.36.0 jest: specifier: ^29.6.1 - version: 29.6.1(@types/node@18.15.10) + version: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) rimraf: specifier: ^5.0.1 version: 5.0.1 @@ -330,8 +336,8 @@ importers: specifier: ^29.5.3 version: 29.5.3 '@types/node': - specifier: ^18.15.10 - version: 18.15.10 + specifier: ^18.16.19 + version: 18.16.19 '@typescript-eslint/eslint-plugin': specifier: ^5.57.0 version: 5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6) @@ -343,7 +349,7 @@ importers: version: 8.36.0 jest: specifier: ^29.6.1 - version: 29.6.1(@types/node@18.15.10) + version: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) rimraf: specifier: ^5.0.1 version: 5.0.1 @@ -354,6 +360,61 @@ importers: specifier: ^5.1.6 version: 5.1.6 + ../../modules/mysql: + dependencies: + '@fabernovel/heart-cli': + specifier: ^4.0.0-alpha.5 + version: 4.0.0-alpha.5 + '@fabernovel/heart-common': + specifier: workspace:^4.0.0-alpha.5 + version: link:../common + '@mikro-orm/core': + specifier: ^5.7.13 + version: 5.7.13(@mikro-orm/migrations@5.7.13)(@mikro-orm/mysql@5.7.13) + '@mikro-orm/migrations': + specifier: ^5.7.13 + version: 5.7.13(@mikro-orm/core@5.7.13) + '@mikro-orm/mysql': + specifier: ^5.7.13 + version: 5.7.13(@mikro-orm/core@5.7.13)(@mikro-orm/migrations@5.7.13) + devDependencies: + '@jest/globals': + specifier: ^29.6.1 + version: 29.6.1 + '@mikro-orm/cli': + specifier: ^5.7.13 + version: 5.7.13(@mikro-orm/migrations@5.7.13)(@mikro-orm/mysql@5.7.13) + '@types/jest': + specifier: ^29.5.3 + version: 29.5.3 + '@types/node': + specifier: ^18.16.19 + version: 18.16.19 + '@typescript-eslint/eslint-plugin': + specifier: ^5.57.0 + version: 5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6) + '@typescript-eslint/parser': + specifier: ^5.57.0 + version: 5.57.0(eslint@8.36.0)(typescript@5.1.6) + eslint: + specifier: ^8.36.0 + version: 8.36.0 + jest: + specifier: ^29.6.1 + version: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) + rimraf: + specifier: ^5.0.1 + version: 5.0.1 + ts-jest: + specifier: ^29.1.1 + version: 29.1.1(@babel/core@7.22.9)(jest@29.6.1)(typescript@5.1.6) + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@18.16.19)(typescript@5.1.6) + typescript: + specifier: ^5.1.6 + version: 5.1.6 + ../../modules/observatory: dependencies: '@fabernovel/heart-cli': @@ -370,8 +431,8 @@ importers: specifier: ^29.5.3 version: 29.5.3 '@types/node': - specifier: ^18.15.10 - version: 18.15.10 + specifier: ^18.16.19 + version: 18.16.19 '@typescript-eslint/eslint-plugin': specifier: ^5.57.0 version: 5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6) @@ -383,7 +444,7 @@ importers: version: 8.36.0 jest: specifier: ^29.6.1 - version: 29.6.1(@types/node@18.15.10) + version: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) rimraf: specifier: ^5.0.1 version: 5.0.1 @@ -413,8 +474,8 @@ importers: specifier: ^29.5.3 version: 29.5.3 '@types/node': - specifier: ^18.15.10 - version: 18.15.10 + specifier: ^18.16.19 + version: 18.16.19 '@typescript-eslint/eslint-plugin': specifier: ^5.57.0 version: 5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6) @@ -426,7 +487,7 @@ importers: version: 8.36.0 jest: specifier: ^29.6.1 - version: 29.6.1(@types/node@18.15.10) + version: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) rimraf: specifier: ^5.0.1 version: 5.0.1 @@ -453,8 +514,8 @@ importers: specifier: ^29.5.3 version: 29.5.3 '@types/node': - specifier: ^18.15.10 - version: 18.15.10 + specifier: ^18.16.19 + version: 18.16.19 '@typescript-eslint/eslint-plugin': specifier: ^5.57.0 version: 5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6) @@ -466,7 +527,7 @@ importers: version: 8.36.0 jest: specifier: ^29.6.1 - version: 29.6.1(@types/node@18.15.10) + version: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) rimraf: specifier: ^5.0.1 version: 5.0.1 @@ -494,6 +555,9 @@ importers: '@fabernovel/heart-lighthouse': specifier: workspace:^4.0.0-alpha.5 version: link:../modules/lighthouse + '@fabernovel/heart-mysql': + specifier: workspace:^4.0.0-alpha.5 + version: link:../modules/mysql '@fabernovel/heart-observatory': specifier: workspace:^4.0.0-alpha.5 version: link:../modules/observatory @@ -856,6 +920,13 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.36.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1064,19 +1135,27 @@ packages: engines: {node: '>=8'} dev: true + /@jercle/yargonaut@1.1.5: + resolution: {integrity: sha512-zBp2myVvBHp1UaJsNTyS6q4UDKT7eRiqTS4oNTS6VQMd6mpxYOdbeK4pY279cDCdakGy6hG0J3ejoXZVsPwHqw==} + dependencies: + chalk: 4.1.2 + figlet: 1.6.0 + parent-require: 1.0.0 + dev: true + /@jest/console@29.6.1: resolution: {integrity: sha512-Aj772AYgwTSr5w8qnyoJ0eDYvN6bMsH3ORH1ivMotrInHLKdUz6BDlaEXHdM6kODaBIkNIyQGzsMvRdOv7VG7Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 chalk: 4.1.2 jest-message-util: 29.6.1 jest-util: 29.6.1 slash: 3.0.0 dev: true - /@jest/core@29.6.1: + /@jest/core@29.6.1(ts-node@10.9.1): resolution: {integrity: sha512-CcowHypRSm5oYQ1obz1wfvkjZZ2qoQlrKKvlfPwh5jUXVU12TWr2qMeH8chLMuTFzHh5a1g2yaqlqDICbr+ukQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -1090,14 +1169,14 @@ packages: '@jest/test-result': 29.6.1 '@jest/transform': 29.6.1 '@jest/types': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.8.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.5.0 - jest-config: 29.6.1(@types/node@18.15.10) + jest-config: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) jest-haste-map: 29.6.1 jest-message-util: 29.6.1 jest-regex-util: 29.4.3 @@ -1124,7 +1203,7 @@ packages: dependencies: '@jest/fake-timers': 29.6.1 '@jest/types': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 jest-mock: 29.6.1 dev: true @@ -1151,7 +1230,7 @@ packages: dependencies: '@jest/types': 29.6.1 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 18.15.10 + '@types/node': 18.16.19 jest-message-util: 29.6.1 jest-mock: 29.6.1 jest-util: 29.6.1 @@ -1184,7 +1263,7 @@ packages: '@jest/transform': 29.6.1 '@jest/types': 29.6.1 '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 18.15.10 + '@types/node': 18.16.19 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -1272,7 +1351,7 @@ packages: '@jest/schemas': 29.6.0 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 '@types/yargs': 17.0.24 chalk: 4.1.2 dev: true @@ -1291,6 +1370,11 @@ packages: engines: {node: '>=6.0.0'} dev: true + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} @@ -1311,18 +1395,232 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@mikro-orm/cli@5.7.13(@mikro-orm/migrations@5.7.13)(@mikro-orm/mysql@5.7.13): + resolution: {integrity: sha512-8M+33h997dutR4v+Dt+Vrgdlf5ymZwWEz5fDiueaCg/MYt+bhzJzcY7djZWvgd8AGTqgwqf+btU+78BTSnWP4Q==} + engines: {node: '>= 14.0.0'} + hasBin: true + peerDependencies: + '@mikro-orm/better-sqlite': ^5.0.0 + '@mikro-orm/entity-generator': ^5.0.0 + '@mikro-orm/mariadb': ^5.0.0 + '@mikro-orm/migrations': ^5.0.0 + '@mikro-orm/migrations-mongodb': ^5.0.0 + '@mikro-orm/mongodb': ^5.0.0 + '@mikro-orm/mysql': ^5.0.0 + '@mikro-orm/postgresql': ^5.0.0 + '@mikro-orm/seeder': ^5.0.0 + '@mikro-orm/sqlite': ^5.0.0 + peerDependenciesMeta: + '@mikro-orm/better-sqlite': + optional: true + '@mikro-orm/entity-generator': + optional: true + '@mikro-orm/mariadb': + optional: true + '@mikro-orm/migrations': + optional: true + '@mikro-orm/migrations-mongodb': + optional: true + '@mikro-orm/mongodb': + optional: true + '@mikro-orm/mysql': + optional: true + '@mikro-orm/postgresql': + optional: true + '@mikro-orm/seeder': + optional: true + '@mikro-orm/sqlite': + optional: true + dependencies: + '@jercle/yargonaut': 1.1.5 + '@mikro-orm/core': 5.7.13(@mikro-orm/migrations@5.7.13)(@mikro-orm/mysql@5.7.13) + '@mikro-orm/knex': 5.7.13(@mikro-orm/core@5.7.13)(@mikro-orm/migrations@5.7.13)(mysql2@3.5.1) + '@mikro-orm/migrations': 5.7.13(@mikro-orm/core@5.7.13) + '@mikro-orm/mysql': 5.7.13(@mikro-orm/core@5.7.13)(@mikro-orm/migrations@5.7.13) + fs-extra: 11.1.1 + tsconfig-paths: 4.2.0 + yargs: 17.7.2 + transitivePeerDependencies: + - better-sqlite3 + - mssql + - mysql + - mysql2 + - pg + - pg-native + - sqlite3 + - supports-color + - tedious + dev: true + + /@mikro-orm/core@5.7.13(@mikro-orm/migrations@5.7.13)(@mikro-orm/mysql@5.7.13): + resolution: {integrity: sha512-XzLlMVt8mcf3VdyRcHP/MZt4FpyLL76fuXtv6VW8px+okb5aAokX9aMtVsAmReGoZL5jM15i3oQFt38RRVAnGw==} + engines: {node: '>= 14.0.0'} + peerDependencies: + '@mikro-orm/better-sqlite': ^5.0.0 + '@mikro-orm/entity-generator': ^5.0.0 + '@mikro-orm/mariadb': ^5.0.0 + '@mikro-orm/migrations': ^5.0.0 + '@mikro-orm/migrations-mongodb': ^5.0.0 + '@mikro-orm/mongodb': ^5.0.0 + '@mikro-orm/mysql': ^5.0.0 + '@mikro-orm/postgresql': ^5.0.0 + '@mikro-orm/seeder': ^5.0.0 + '@mikro-orm/sqlite': ^5.0.0 + peerDependenciesMeta: + '@mikro-orm/better-sqlite': + optional: true + '@mikro-orm/entity-generator': + optional: true + '@mikro-orm/mariadb': + optional: true + '@mikro-orm/migrations': + optional: true + '@mikro-orm/migrations-mongodb': + optional: true + '@mikro-orm/mongodb': + optional: true + '@mikro-orm/mysql': + optional: true + '@mikro-orm/postgresql': + optional: true + '@mikro-orm/seeder': + optional: true + '@mikro-orm/sqlite': + optional: true + dependencies: + '@mikro-orm/migrations': 5.7.13(@mikro-orm/core@5.7.13) + '@mikro-orm/mysql': 5.7.13(@mikro-orm/core@5.7.13)(@mikro-orm/migrations@5.7.13) + acorn-loose: 8.3.0 + acorn-walk: 8.2.0 + dotenv: 16.3.1 + fs-extra: 11.1.1 + globby: 11.1.0 + mikro-orm: 5.7.13 + reflect-metadata: 0.1.13 + + /@mikro-orm/knex@5.7.13(@mikro-orm/core@5.7.13)(@mikro-orm/migrations@5.7.13)(mysql2@3.5.1): + resolution: {integrity: sha512-+YTj7ZoIU3Y4csodPH4usK6107xFqbpiSGM1ADiPd7P4ZvfrROhonIGXzUle7yut9C+1SdKFXyCWBae8jYEjKQ==} + engines: {node: '>= 14.0.0'} + peerDependencies: + '@mikro-orm/core': ^5.0.0 + '@mikro-orm/entity-generator': ^5.0.0 + '@mikro-orm/migrations': ^5.0.0 + better-sqlite3: '*' + mssql: '*' + mysql: '*' + mysql2: '*' + pg: '*' + sqlite3: '*' + peerDependenciesMeta: + '@mikro-orm/entity-generator': + optional: true + '@mikro-orm/migrations': + optional: true + better-sqlite3: + optional: true + mssql: + optional: true + mysql: + optional: true + mysql2: + optional: true + pg: + optional: true + sqlite3: + optional: true + dependencies: + '@mikro-orm/core': 5.7.13(@mikro-orm/migrations@5.7.13)(@mikro-orm/mysql@5.7.13) + '@mikro-orm/migrations': 5.7.13(@mikro-orm/core@5.7.13) + fs-extra: 11.1.1 + knex: 2.5.1(mysql2@3.5.1) + mysql2: 3.5.1 + sqlstring: 2.3.3 + transitivePeerDependencies: + - pg-native + - supports-color + - tedious + + /@mikro-orm/migrations@5.7.13(@mikro-orm/core@5.7.13): + resolution: {integrity: sha512-AtmnXG53sWiLWrZNNqyP6RDgDORGkqLCboziAHNuYIBtA1Z/Mr5SjegAZBa+qeus5g6eN7hUhi8lxusbMeVH+Q==} + engines: {node: '>= 14.0.0'} + peerDependencies: + '@mikro-orm/core': ^5.0.0 + dependencies: + '@mikro-orm/core': 5.7.13(@mikro-orm/migrations@5.7.13)(@mikro-orm/mysql@5.7.13) + '@mikro-orm/knex': 5.7.13(@mikro-orm/core@5.7.13)(@mikro-orm/migrations@5.7.13)(mysql2@3.5.1) + fs-extra: 11.1.1 + knex: 2.5.1(mysql2@3.5.1) + umzug: 3.2.1 + transitivePeerDependencies: + - '@mikro-orm/entity-generator' + - better-sqlite3 + - mssql + - mysql + - mysql2 + - pg + - pg-native + - sqlite3 + - supports-color + - tedious + + /@mikro-orm/mysql@5.7.13(@mikro-orm/core@5.7.13)(@mikro-orm/migrations@5.7.13): + resolution: {integrity: sha512-vaXpc8HuFqeaQRkdTz7XrVs+YNqmJFr4Cwt5juktBSszMhF+9ZBeDZIAoEhT5Ywir6o5b/l1YEvnXAbvr6aH1Q==} + engines: {node: '>= 14.0.0'} + peerDependencies: + '@mikro-orm/core': ^5.0.0 + '@mikro-orm/entity-generator': ^5.0.0 + '@mikro-orm/migrations': ^5.0.0 + '@mikro-orm/seeder': ^5.0.0 + peerDependenciesMeta: + '@mikro-orm/entity-generator': + optional: true + '@mikro-orm/migrations': + optional: true + '@mikro-orm/seeder': + optional: true + dependencies: + '@mikro-orm/core': 5.7.13(@mikro-orm/migrations@5.7.13)(@mikro-orm/mysql@5.7.13) + '@mikro-orm/knex': 5.7.13(@mikro-orm/core@5.7.13)(@mikro-orm/migrations@5.7.13)(mysql2@3.5.1) + '@mikro-orm/migrations': 5.7.13(@mikro-orm/core@5.7.13) + mysql2: 3.5.1 + transitivePeerDependencies: + - better-sqlite3 + - mssql + - mysql + - pg + - pg-native + - sqlite3 + - supports-color + - tedious + + /@mikro-orm/reflection@5.7.13(@mikro-orm/core@5.7.13): + resolution: {integrity: sha512-KdiFdLWSDNvfblF83JqTHFBcSmRttfhKHDgDIMCHxLryDlnfwts731YfT1HNRZjr7xqiMWQaeocqmw8e11VVuQ==} + engines: {node: '>= 14.0.0'} + peerDependencies: + '@mikro-orm/core': ^5.0.0 + dependencies: + '@mikro-orm/core': 5.7.13(@mikro-orm/migrations@5.7.13)(@mikro-orm/mysql@5.7.13) + globby: 11.1.0 + ts-morph: 19.0.0 + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - dev: true /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - dev: true /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} @@ -1330,7 +1628,6 @@ packages: dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - dev: true /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -1382,6 +1679,14 @@ packages: - supports-color dev: false + /@rushstack/ts-command-line@4.15.1: + resolution: {integrity: sha512-EL4jxZe5fhb1uVL/P/wQO+Z8Rc8FMiWJ1G7VgnPDvdIt5GVjRfK7vwzder1CZQiX3x0PY6uxENYLNGTFd1InRQ==} + dependencies: + '@types/argparse': 1.0.38 + argparse: 1.0.10 + colors: 1.2.5 + string-argv: 0.3.2 + /@sentry/core@6.19.7: resolution: {integrity: sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==} engines: {node: '>=6'} @@ -1459,7 +1764,7 @@ packages: resolution: {integrity: sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==} engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} dependencies: - '@types/node': 18.15.10 + '@types/node': 18.16.19 dev: false /@slack/types@2.8.0: @@ -1474,7 +1779,7 @@ packages: '@slack/logger': 3.0.0 '@slack/types': 2.8.0 '@types/is-stream': 1.1.0 - '@types/node': 18.15.10 + '@types/node': 18.16.19 axios: 0.27.2 eventemitter3: 3.1.2 form-data: 2.5.1 @@ -1503,6 +1808,34 @@ packages: engines: {node: '>= 10'} dev: false + /@ts-morph/common@0.20.0: + resolution: {integrity: sha512-7uKjByfbPpwuzkstL3L5MQyuXPSKdoNG93Fmi2JoDcTf3pEP731JdRFAduRVkOs8oqxPsXKA+ScrWkdQ8t/I+Q==} + dependencies: + fast-glob: 3.3.0 + minimatch: 7.4.6 + mkdirp: 2.1.6 + path-browserify: 1.0.1 + dev: false + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + + /@types/argparse@1.0.38: + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + /@types/babel__core@7.20.1: resolution: {integrity: sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==} dependencies: @@ -1536,7 +1869,7 @@ packages: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 18.15.10 + '@types/node': 18.16.19 dev: true /@types/cacheable-request@6.0.3: @@ -1544,20 +1877,20 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 18.15.10 + '@types/node': 18.16.19 '@types/responselike': 1.0.0 dev: false /@types/connect@3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 18.15.10 + '@types/node': 18.16.19 dev: true /@types/express-serve-static-core@4.17.35: resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==} dependencies: - '@types/node': 18.15.10 + '@types/node': 18.16.19 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 '@types/send': 0.17.1 @@ -1575,7 +1908,7 @@ packages: /@types/graceful-fs@4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: - '@types/node': 18.15.10 + '@types/node': 18.16.19 dev: true /@types/har-format@1.2.10: @@ -1593,7 +1926,7 @@ packages: /@types/is-stream@1.1.0: resolution: {integrity: sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==} dependencies: - '@types/node': 18.15.10 + '@types/node': 18.16.19 dev: false /@types/istanbul-lib-coverage@2.0.4: @@ -1626,7 +1959,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.15.10 + '@types/node': 18.16.19 dev: false /@types/mime@1.3.2: @@ -1641,8 +1974,8 @@ packages: resolution: {integrity: sha512-soGmOpVBUq+gaBMwom1M+krC/NNbWlosh4AtGA03SyWNDiqSKtwp7OulO1M6+mg8YkHMvJ/y0AkCeO8d1hNb7A==} dev: false - /@types/node@18.15.10: - resolution: {integrity: sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ==} + /@types/node@18.16.19: + resolution: {integrity: sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==} /@types/prettier@2.7.3: resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} @@ -1659,7 +1992,7 @@ packages: /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 18.15.10 + '@types/node': 18.16.19 dev: false /@types/retry@0.12.0: @@ -1674,7 +2007,7 @@ packages: resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==} dependencies: '@types/mime': 1.3.2 - '@types/node': 18.15.10 + '@types/node': 18.16.19 dev: true /@types/serve-static@1.15.2: @@ -1682,7 +2015,7 @@ packages: dependencies: '@types/http-errors': 2.0.1 '@types/mime': 3.0.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 dev: true /@types/stack-utils@2.0.1: @@ -1703,7 +2036,7 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 18.15.10 + '@types/node': 18.16.19 optional: true /@typescript-eslint/eslint-plugin@5.57.0(@typescript-eslint/parser@5.57.0)(eslint@8.36.0)(typescript@5.1.6): @@ -1879,6 +2212,12 @@ packages: acorn: 8.10.0 dev: true + /acorn-loose@8.3.0: + resolution: {integrity: sha512-75lAs9H19ldmW+fAbyqHdjgdCrz0pWGXKmnqFoh8PyVd1L2RIb4RzYrSjmopeqv3E1G3/Pimu6GgLlrGbrkF7w==} + engines: {node: '>=0.4.0'} + dependencies: + acorn: 8.10.0 + /acorn-walk@7.2.0: resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} engines: {node: '>=0.4.0'} @@ -1887,7 +2226,6 @@ packages: /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} - dev: false /acorn@7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} @@ -2037,11 +2375,14 @@ packages: /archy@1.0.0: resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: sprintf-js: 1.0.3 - dev: true /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2049,7 +2390,6 @@ packages: /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - dev: true /arrify@2.0.1: resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} @@ -2247,7 +2587,6 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.0.1 - dev: true /browser-process-hrtime@1.0.0: resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} @@ -2395,7 +2734,7 @@ packages: engines: {node: '>=12.13.0'} hasBin: true dependencies: - '@types/node': 18.15.10 + '@types/node': 18.16.19 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -2470,6 +2809,10 @@ packages: engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} dev: true + /code-block-writer@12.0.0: + resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==} + dev: false + /collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} dev: true @@ -2491,6 +2834,13 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + /colorette@2.0.19: + resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} + + /colors@1.2.5: + resolution: {integrity: sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==} + engines: {node: '>=0.1.90'} + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -2506,7 +2856,6 @@ packages: /commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} - dev: false /compress-commons@4.1.1: resolution: {integrity: sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==} @@ -2582,6 +2931,10 @@ packages: readable-stream: 3.6.2 dev: false + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + /cross-fetch@3.1.5: resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} dependencies: @@ -2730,6 +3083,10 @@ packages: engines: {node: '>=0.4.0'} dev: false + /denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -2751,12 +3108,16 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} dependencies: path-type: 4.0.0 - dev: true /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} @@ -2786,7 +3147,6 @@ packages: /dotenv@16.3.1: resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} engines: {node: '>=12'} - dev: false /dumb-query-selector@3.3.0: resolution: {integrity: sha512-hPRKbOOZUn9UBgO74fPSidcMqrYOJSMs27CEaDLZ+RjuY20ze0zgc4JH4ESXYyDXyCmGe3kirYHp6sFcH78Zsw==} @@ -2820,6 +3180,10 @@ packages: resolution: {integrity: sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ==} dev: true + /emittery@0.12.1: + resolution: {integrity: sha512-pYyW59MIZo0HxPFf+Vb3+gacUu0gxVS3TZwB2ClwkEZywgF9f9OJDoVmNLojTn0vKX3tO9LC+pdQEcLP4Oz/bQ==} + engines: {node: '>=12'} + /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -2963,6 +3327,10 @@ packages: - supports-color dev: true + /esm@3.2.25: + resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} + engines: {node: '>=6'} + /espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3059,7 +3427,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/expect-utils': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 jest-get-type: 29.4.3 jest-matcher-utils: 29.6.1 jest-message-util: 29.6.1 @@ -3109,7 +3477,6 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 - dev: true /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -3192,6 +3559,12 @@ packages: web-streams-polyfill: 3.2.1 dev: false + /figlet@1.6.0: + resolution: {integrity: sha512-31EQGhCEITv6+hi2ORRPyn3bulaV9Fl4xOdR169cBzH/n1UqcxsiSB/noo6SJdD7Kfb1Ljit+IgR1USvF/XbdA==} + engines: {node: '>= 0.4.0'} + hasBin: true + dev: true + /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -3204,7 +3577,6 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: true /find-my-way@7.6.2: resolution: {integrity: sha512-0OjHn1b1nCX3eVbm9ByeEHiscPYiHLfhei1wOUU9qffQkk98wE0Lo8VrVYfSGMgnSnDh86DxedduAnBf4nwUEw==} @@ -3299,6 +3671,14 @@ packages: /fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + /fs-extra@11.1.1: + resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.0 + /fs-extra@8.1.0: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} @@ -3308,6 +3688,12 @@ packages: universalify: 0.1.2 dev: false + /fs-jetpack@4.3.1: + resolution: {integrity: sha512-dbeOK84F6BiQzk2yqqCVwCPWTxAvVGJ3fMQc6E2wuEohS28mR6yHngbrKuVCK1KHRx/ccByDylqu4H5PCP2urQ==} + dependencies: + minimatch: 3.1.2 + rimraf: 2.7.1 + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -3331,7 +3717,6 @@ packages: /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - dev: true /gaxios@5.1.3: resolution: {integrity: sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==} @@ -3357,6 +3742,11 @@ packages: - supports-color dev: false + /generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + dependencies: + is-property: 1.0.2 + /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -3369,7 +3759,6 @@ packages: /get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} - dev: true /get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} @@ -3394,12 +3783,14 @@ packages: - supports-color dev: false + /getopts@2.3.0: + resolution: {integrity: sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==} + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 - dev: true /glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} @@ -3429,6 +3820,16 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -3451,7 +3852,6 @@ packages: ignore: 5.2.4 merge2: 1.4.1 slash: 3.0.0 - dev: true /google-auth-library@8.9.0: resolution: {integrity: sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==} @@ -3529,7 +3929,6 @@ packages: engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 - dev: true /html-encoding-sniffer@2.0.1: resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==} @@ -3650,13 +4049,18 @@ packages: safer-buffer: 2.1.2 dev: false + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} - dev: true /image-ssim@0.2.0: resolution: {integrity: sha512-W7+sO6/yhxy83L0G7xR8YAc5Z5QFtYEXXRV6EaE8tuYBZJnA3gVgp3q7X7muhLZVodeb9UfvjSbwt9VJwjIYAg==} @@ -3694,6 +4098,10 @@ packages: /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + /interpret@2.2.0: + resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} + engines: {node: '>= 0.10'} + /intl-messageformat-parser@1.8.1: resolution: {integrity: sha512-IMSCKVf0USrM/959vj3xac7s8f87sc+80Y/ipBzdKy4ifBv5Gsj2tZ41EAaURVg01QU71fYr77uA8Meh6kELbg==} deprecated: We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser @@ -3722,7 +4130,6 @@ packages: resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==} dependencies: has: 1.0.3 - dev: true /is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} @@ -3736,7 +4143,6 @@ packages: /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - dev: true /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -3752,7 +4158,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - dev: true /is-gzip@2.0.0: resolution: {integrity: sha512-jtO4Njg6q58zDo/Pu4027beSZ0VdsZlt8/5Moco6yAg+DIxb5BK/xUYqYG2+MD4+piKldXJNHxRkhEYI2fvrxA==} @@ -3767,7 +4172,6 @@ packages: /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true /is-obj@2.0.0: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} @@ -3782,6 +4186,9 @@ packages: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} dev: false + /is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + /is-stream@1.1.0: resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} engines: {node: '>=0.10.0'} @@ -3886,7 +4293,7 @@ packages: '@jest/expect': 29.6.1 '@jest/test-result': 29.6.1 '@jest/types': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -3906,7 +4313,7 @@ packages: - supports-color dev: true - /jest-cli@29.6.1(@types/node@18.15.10): + /jest-cli@29.6.1(@types/node@18.16.19)(ts-node@10.9.1): resolution: {integrity: sha512-607dSgTA4ODIN6go9w6xY3EYkyPFGicx51a69H7yfvt7lN53xNswEVLovq+E77VsTRi5fWprLH0yl4DJgE8Ing==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -3916,14 +4323,14 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 29.6.1 + '@jest/core': 29.6.1(ts-node@10.9.1) '@jest/test-result': 29.6.1 '@jest/types': 29.6.1 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 import-local: 3.1.0 - jest-config: 29.6.1(@types/node@18.15.10) + jest-config: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) jest-util: 29.6.1 jest-validate: 29.6.1 prompts: 2.4.2 @@ -3934,7 +4341,7 @@ packages: - ts-node dev: true - /jest-config@29.6.1(@types/node@18.15.10): + /jest-config@29.6.1(@types/node@18.16.19)(ts-node@10.9.1): resolution: {integrity: sha512-XdjYV2fy2xYixUiV2Wc54t3Z4oxYPAELUzWnV6+mcbq0rh742X2p52pii5A3oeRzYjLnQxCsZmp0qpI6klE2cQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -3949,7 +4356,7 @@ packages: '@babel/core': 7.22.9 '@jest/test-sequencer': 29.6.1 '@jest/types': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 babel-jest: 29.6.1(@babel/core@7.22.9) chalk: 4.1.2 ci-info: 3.8.0 @@ -3969,6 +4376,7 @@ packages: pretty-format: 29.6.1 slash: 3.0.0 strip-json-comments: 3.1.1 + ts-node: 10.9.1(@types/node@18.16.19)(typescript@5.1.6) transitivePeerDependencies: - supports-color dev: true @@ -4008,7 +4416,7 @@ packages: '@jest/environment': 29.6.1 '@jest/fake-timers': 29.6.1 '@jest/types': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 jest-mock: 29.6.1 jest-util: 29.6.1 dev: true @@ -4024,7 +4432,7 @@ packages: dependencies: '@jest/types': 29.6.1 '@types/graceful-fs': 4.1.6 - '@types/node': 18.15.10 + '@types/node': 18.16.19 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -4075,7 +4483,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 jest-util: 29.6.1 dev: true @@ -4130,7 +4538,7 @@ packages: '@jest/test-result': 29.6.1 '@jest/transform': 29.6.1 '@jest/types': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -4161,7 +4569,7 @@ packages: '@jest/test-result': 29.6.1 '@jest/transform': 29.6.1 '@jest/types': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.2 @@ -4214,7 +4622,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 chalk: 4.1.2 ci-info: 3.8.0 graceful-fs: 4.2.11 @@ -4239,7 +4647,7 @@ packages: dependencies: '@jest/test-result': 29.6.1 '@jest/types': 29.6.1 - '@types/node': 18.15.10 + '@types/node': 18.16.19 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -4251,13 +4659,13 @@ packages: resolution: {integrity: sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 18.15.10 + '@types/node': 18.16.19 jest-util: 29.6.1 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@29.6.1(@types/node@18.15.10): + /jest@29.6.1(@types/node@18.16.19)(ts-node@10.9.1): resolution: {integrity: sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -4267,10 +4675,10 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 29.6.1 + '@jest/core': 29.6.1(ts-node@10.9.1) '@jest/types': 29.6.1 import-local: 3.1.0 - jest-cli: 29.6.1(@types/node@18.15.10) + jest-cli: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) transitivePeerDependencies: - '@types/node' - supports-color @@ -4389,6 +4797,13 @@ packages: graceful-fs: 4.2.11 dev: false + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.0 + optionalDependencies: + graceful-fs: 4.2.11 + /jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} dependencies: @@ -4424,6 +4839,52 @@ packages: engines: {node: '>=6'} dev: true + /knex@2.5.1(mysql2@3.5.1): + resolution: {integrity: sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + better-sqlite3: '*' + mysql: '*' + mysql2: '*' + pg: '*' + pg-native: '*' + sqlite3: '*' + tedious: '*' + peerDependenciesMeta: + better-sqlite3: + optional: true + mysql: + optional: true + mysql2: + optional: true + pg: + optional: true + pg-native: + optional: true + sqlite3: + optional: true + tedious: + optional: true + dependencies: + colorette: 2.0.19 + commander: 10.0.1 + debug: 4.3.4 + escalade: 3.1.1 + esm: 3.2.25 + get-package-type: 0.1.0 + getopts: 2.3.0 + interpret: 2.2.0 + lodash: 4.17.21 + mysql2: 3.5.1 + pg-connection-string: 2.6.1 + rechoir: 0.8.0 + resolve-from: 5.0.0 + tarn: 3.0.2 + tildify: 2.0.0 + transitivePeerDependencies: + - supports-color + /lazystream@1.0.1: resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} engines: {node: '>= 0.6.3'} @@ -4603,6 +5064,9 @@ packages: is-unicode-supported: 1.3.0 dev: false + /long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + /lookup-closest-locale@6.2.0: resolution: {integrity: sha512-/c2kL+Vnp1jnV6K6RpDTHK3dgg0Tu2VVp+elEiJpjfS1UyY7AjOYHohRug6wT0OpoX2qFgNORndE9RqesfVxWQ==} @@ -4630,7 +5094,10 @@ packages: /lru-cache@7.18.3: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - dev: false + + /lru-cache@8.0.5: + resolution: {integrity: sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==} + engines: {node: '>=16.14'} /lru_map@0.3.3: resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} @@ -4661,7 +5128,6 @@ packages: /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - dev: true /metaviewport-parser@0.3.0: resolution: {integrity: sha512-EoYJ8xfjQ6kpe9VbVHvZTZHiOl4HL1Z18CrZ+qahvLXT7ZO4YTC2JMyt5FaUp9JJp6J4Ybb/z7IsCXZt86/QkQ==} @@ -4672,7 +5138,10 @@ packages: dependencies: braces: 3.0.2 picomatch: 2.3.1 - dev: true + + /mikro-orm@5.7.13: + resolution: {integrity: sha512-wOQLyTQ91NSYjJrzc/gALoFsLqe5q5TvTw1actjkUKrcCVF1Eacm6OdbXhwxtUOBEQeHs4WaumIflcs8TULLEA==} + engines: {node: '>= 14.0.0'} /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} @@ -4710,6 +5179,12 @@ packages: engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 + + /minimatch@7.4.6: + resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 dev: false /minimatch@9.0.3: @@ -4720,7 +5195,6 @@ packages: /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: false /minipass@7.0.2: resolution: {integrity: sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA==} @@ -4739,6 +5213,12 @@ packages: minimist: 1.2.8 dev: false + /mkdirp@2.1.6: + resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} + engines: {node: '>=10'} + hasBin: true + dev: false + /mnemonist@0.39.5: resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} dependencies: @@ -4754,6 +5234,25 @@ packages: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: false + /mysql2@3.5.1: + resolution: {integrity: sha512-RyaeUBqMiFR1ivk78JToPz0MhwN/sUWCKwPZszbXaFNZ6wmP/EJNPDU6gbZkF+qtLjr3sULQb0Y7JuFrNblnDg==} + engines: {node: '>= 8.0'} + dependencies: + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.6.3 + long: 5.2.3 + lru-cache: 8.0.5 + named-placeholders: 1.1.3 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + + /named-placeholders@1.1.3: + resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} + engines: {node: '>=12.0.0'} + dependencies: + lru-cache: 7.18.3 + /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} dev: true @@ -5028,6 +5527,11 @@ packages: dependencies: callsites: 3.1.0 + /parent-require@1.0.0: + resolution: {integrity: sha512-2MXDNZC4aXdkkap+rBBMv0lUsfJqvX5/2FiYYnfCnorZt3Pk06/IOR5KeaoghgS2w07MLWgjbsnyaq6PdHn2LQ==} + engines: {node: '>= 0.4.0'} + dev: true + /parse-cache-control@1.0.1: resolution: {integrity: sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==} @@ -5044,6 +5548,10 @@ packages: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} dev: false + /path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + dev: false + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -5058,7 +5566,6 @@ packages: /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true /path-scurry@1.10.1: resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} @@ -5074,6 +5581,9 @@ packages: /pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + /pg-connection-string@2.6.1: + resolution: {integrity: sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==} + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true @@ -5081,7 +5591,6 @@ packages: /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: true /pino-abstract-transport@1.0.0: resolution: {integrity: sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==} @@ -5119,6 +5628,10 @@ packages: dependencies: find-up: 4.1.0 + /pony-cause@2.1.10: + resolution: {integrity: sha512-3IKLNXclQgkU++2fSi93sQ6BznFuxSLB11HdvZQ6JW/spahf/P1pAHBQEahr20rs0htZW0UDkM1HmA+nZkXKsw==} + engines: {node: '>=12.0.0'} + /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} @@ -5315,7 +5828,6 @@ packages: /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true /quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -5369,6 +5881,15 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} + /rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + dependencies: + resolve: 1.22.2 + + /reflect-metadata@0.1.13: + resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + /regenerator-runtime@0.13.11: resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} dev: false @@ -5403,7 +5924,6 @@ packages: /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - dev: true /resolve.exports@2.0.2: resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} @@ -5417,7 +5937,6 @@ packages: is-core-module: 2.12.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true /responselike@2.0.1: resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} @@ -5464,7 +5983,6 @@ packages: hasBin: true dependencies: glob: 7.2.3 - dev: false /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} @@ -5487,7 +6005,6 @@ packages: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 - dev: true /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -5507,7 +6024,6 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: false /sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} @@ -5538,6 +6054,9 @@ packages: dependencies: lru-cache: 6.0.0 + /seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + /set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} @@ -5579,7 +6098,6 @@ packages: /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - dev: true /smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} @@ -5625,7 +6143,7 @@ packages: resolution: {integrity: sha512-DI7/OuAUD+GMpR6dmu8lliO2Wg5zfeh+/xsdyJZCzd8o5JgFUjCeLsBDuZjIQJdwXS3J0L/uZYrELKYqx+PXog==} engines: {node: '>=8.0'} dependencies: - '@types/node': 18.15.10 + '@types/node': 18.16.19 image-ssim: 0.2.0 jpeg-js: 0.4.4 @@ -5635,7 +6153,10 @@ packages: /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: true + + /sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} /stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} @@ -5661,6 +6182,10 @@ packages: resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==} dev: false + /string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + /string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -5708,6 +6233,11 @@ packages: dependencies: ansi-regex: 6.0.1 + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + /strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} @@ -5750,7 +6280,6 @@ packages: /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - dev: true /symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -5774,6 +6303,10 @@ packages: inherits: 2.0.4 readable-stream: 3.6.2 + /tarn@3.0.2: + resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==} + engines: {node: '>=8.0.0'} + /teeny-request@8.0.3: resolution: {integrity: sha512-jJZpA5He2y52yUhA7pyAGZlgQpcB+xLjcN0eUFxr9c8hP/H7uOXbBNVo/O0C/xVfJLJs680jvkFgVJEEvk9+ww==} engines: {node: '>=12'} @@ -5829,6 +6362,10 @@ packages: - utf-8-validate dev: false + /tildify@2.0.0: + resolution: {integrity: sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==} + engines: {node: '>=8'} + /tiny-lru@10.4.1: resolution: {integrity: sha512-buLIzw7ppqymuO3pt10jHk/6QMeZLbidihMQU+N6sogF6EnBzG0qtDWIHuhw1x3dyNgVL/KTGIZsTK81+yCzLg==} engines: {node: '>=12'} @@ -5854,7 +6391,6 @@ packages: engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 - dev: true /tough-cookie@4.0.0: resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==} @@ -5913,7 +6449,7 @@ packages: '@babel/core': 7.22.9 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.6.1(@types/node@18.15.10) + jest: 29.6.1(@types/node@18.16.19)(ts-node@10.9.1) jest-util: 29.6.1 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -5923,6 +6459,53 @@ packages: yargs-parser: 21.1.1 dev: true + /ts-morph@19.0.0: + resolution: {integrity: sha512-D6qcpiJdn46tUqV45vr5UGM2dnIEuTGNxVhg0sk5NX11orcouwj6i1bMqZIz2mZTZB1Hcgy7C3oEVhAT+f6mbQ==} + dependencies: + '@ts-morph/common': 0.20.0 + code-block-writer: 12.0.0 + dev: false + + /ts-node@10.9.1(@types/node@18.16.19)(typescript@5.1.6): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 18.16.19 + acorn: 8.10.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.1.6 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -5969,6 +6552,10 @@ packages: engines: {node: '>=10'} dev: true + /type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + /type-fest@3.6.1: resolution: {integrity: sha512-htXWckxlT6U4+ilVgweNliPqlsVSSucbxVexRYllyMVJDtf5rTjv6kF/s+qAd4QSL1BZcnJPEJavYBPQiWuZDA==} engines: {node: '>=14.16'} @@ -5984,6 +6571,17 @@ packages: engines: {node: '>=14.17'} hasBin: true + /umzug@3.2.1: + resolution: {integrity: sha512-XyWQowvP9CKZycKc/Zg9SYWrAWX/gJCE799AUTFqk8yC3tp44K1xWr3LoFF0MNEjClKOo1suCr5ASnoy+KltdA==} + engines: {node: '>=12'} + dependencies: + '@rushstack/ts-command-line': 4.15.1 + emittery: 0.12.1 + fs-jetpack: 4.3.1 + glob: 8.1.0 + pony-cause: 2.1.10 + type-fest: 2.19.0 + /unbzip2-stream@1.4.3: resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} dependencies: @@ -6006,6 +6604,10 @@ packages: engines: {node: '>= 4.0.0'} dev: false + /universalify@2.0.0: + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + engines: {node: '>= 10.0.0'} + /unzipper@0.10.14: resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==} dependencies: @@ -6062,6 +6664,10 @@ packages: hasBin: true dev: false + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + /v8-to-istanbul@9.1.0: resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} engines: {node: '>=10.12.0'} @@ -6314,6 +6920,11 @@ packages: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} diff --git a/docs/CREATE_NEW_MODULE.md b/docs/CREATE_NEW_MODULE.md index 3b5eefc2..4e32b991 100644 --- a/docs/CREATE_NEW_MODULE.md +++ b/docs/CREATE_NEW_MODULE.md @@ -2,17 +2,17 @@ Creating a new module is about copying the module template directory, then rename some files and variable names. -1. Copy the _modules/heart-moduletpl_ directory (except the .rush, node_modules and lib directories inside). +1. Copy the _modules/moduletpl_ directory and rename it. -2. In the copied directory, search and replace the `moduletpl` and `ModuleTpl` strings of every files and directories +2. In the copied directory, search and replace the `moduletpl` and `ModuleTpl` strings in each file and filename. 3. Update the `description`, `license` and `contributors` fields of the pakage.json file -4. Indicates to Rush that he has to manage your new module: add the following lines to the array under the `project` key of the `rush.json` file: +4. Indicates to Rush that he has to manage your new module: add the following lines to the array under the `projects` key of the `rush.json` file: ```json { - "packageName": "@fabernovel/heart-moduletpl", - "projectFolder": "modules/heart-moduletpl", + "packageName": "@fabernovel/moduletpl", + "projectFolder": "modules/moduletpl", "versionPolicyName": "individualVersion" }, ``` diff --git a/modules/api/package.json b/modules/api/package.json index 55b4b516..040f42eb 100644 --- a/modules/api/package.json +++ b/modules/api/package.json @@ -42,7 +42,7 @@ "@jest/globals": "^29.6.1", "@types/express": "^4.17.17", "@types/jest": "^29.5.3", - "@types/node": "^18.15.10", + "@types/node": "^18.16.19", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", "eslint": "^8.36.0", diff --git a/modules/bigquery/package.json b/modules/bigquery/package.json index 725ee0b8..ffc63c9d 100644 --- a/modules/bigquery/package.json +++ b/modules/bigquery/package.json @@ -40,7 +40,7 @@ "devDependencies": { "@jest/globals": "^29.6.1", "@types/jest": "^29.5.3", - "@types/node": "^18.15.10", + "@types/node": "^18.16.19", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", "eslint": "^8.36.0", diff --git a/modules/cli/package.json b/modules/cli/package.json index cb905683..c190d870 100644 --- a/modules/cli/package.json +++ b/modules/cli/package.json @@ -45,7 +45,7 @@ "@fastify/cors": "^8.2.1", "@jest/globals": "^29.6.1", "@types/jest": "^29.5.3", - "@types/node": "^18.15.10", + "@types/node": "^18.16.19", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", "eslint": "^8.36.0", diff --git a/modules/cli/src/command/cli/CliCommand.ts b/modules/cli/src/command/cli/CliCommand.ts index 237efff4..d7ef9c86 100644 --- a/modules/cli/src/command/cli/CliCommand.ts +++ b/modules/cli/src/command/cli/CliCommand.ts @@ -1,10 +1,16 @@ -import type { Config, ModuleListenerInterface } from "@fabernovel/heart-common" +import { + isModuleListenerDatabase, + type Config, + type ModuleListenerDatabaseInterface, + type ModuleListenerInterface, +} from "@fabernovel/heart-common" import type { FastifyCorsOptions } from "@fastify/cors" import { Command } from "commander" import { readFileSync } from "node:fs" import { argv } from "node:process" import type { PackageJson } from "type-fest" import { loadEnvironmentVariables, loadModules } from "../../module/ModuleLoader.js" +import { migrateListenerDatabase } from "../../module/ModuleMigration.js" import { notifyListenerModules, startAnalysis, startServer } from "../../module/ModuleOrchestrator.js" import { createAnalysisSubcommand } from "../analysis/AnalysisCommand.js" import { createServerSubcommand } from "../server/ServerCommand.js" @@ -33,6 +39,14 @@ export async function start(): Promise { const cmd = createCommand() const [analysisModulesMap, listenerModulesMap, serverModulesMap] = await loadModules() const listenerModules = Array.from(listenerModulesMap.values()).map((listenerModule) => listenerModule) + const listenerDatabaseModules = listenerModules.filter((m): m is ModuleListenerDatabaseInterface => + isModuleListenerDatabase(m) + ) + + // run database migrations for Listener database modules + if (listenerDatabaseModules.length > 0) { + await migrateListenerDatabase(listenerDatabaseModules) + } // analysis modules: create a command for each of them analysisModulesMap.forEach((analysisModule, modulePath) => { diff --git a/modules/cli/src/module/ModuleMigration.ts b/modules/cli/src/module/ModuleMigration.ts new file mode 100644 index 00000000..9febcea2 --- /dev/null +++ b/modules/cli/src/module/ModuleMigration.ts @@ -0,0 +1,42 @@ +import { type ModuleListenerDatabaseInterface } from "@fabernovel/heart-common" +import ora from "ora" + +/** + * Migrate the database of every database listener module to their latest version. + * Migrate only the databases that require migrations. + */ +export async function migrateListenerDatabase( + listenerDatabaseModules: ModuleListenerDatabaseInterface[] +): Promise { + const hasPendingMigrations = await Promise.all(listenerDatabaseModules.map((m) => m.hasPendingMigrations())) + + if (hasPendingMigrations.some((hasPendingMigration) => hasPendingMigration)) { + const spinner = ora({ spinner: "dots", interval: 200 }) + + spinner.start(`Update the databases...`) + + try { + await Promise.all(listenerDatabaseModules.map((m) => m.runPendingMigrations())) + + spinner.succeed("Databases update completed.") + + return Promise.resolve() + } catch (error) { + let reason = "" + + if (typeof error === "string") { + reason = error + } else if (error instanceof Error) { + reason = error.message + } + + if (reason.length > 0) { + reason = ` Reason: ${reason}.` + } + + spinner.fail(`Databases update failed.${reason}`) + + return Promise.reject(error) + } + } +} diff --git a/modules/common/package.json b/modules/common/package.json index 98b4ca09..1543133f 100644 --- a/modules/common/package.json +++ b/modules/common/package.json @@ -32,6 +32,8 @@ "test": "NODE_OPTIONS='--experimental-vm-modules' jest" }, "dependencies": { + "@mikro-orm/core": "^5.7.13", + "@mikro-orm/reflection": "^5.7.13", "ajv": "^8.12.0", "ajv-errors": "^3.0.0", "node-fetch": "^3.3.0" @@ -41,7 +43,7 @@ "@jest/globals": "^29.6.1", "@types/har-format": "^1.2.10", "@types/jest": "^29.5.3", - "@types/node": "^18.15.10", + "@types/node": "^18.16.19", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", "eslint": "^8.36.0", diff --git a/modules/common/src/entities/ReportEntity.ts b/modules/common/src/entities/ReportEntity.ts new file mode 100644 index 00000000..6b7ea854 --- /dev/null +++ b/modules/common/src/entities/ReportEntity.ts @@ -0,0 +1,40 @@ +import { Cascade, Entity, ManyToOne, PrimaryKey, Property } from "@mikro-orm/core" +import type Report from "../report/Report.js" +import type { GenericReport } from "../report/Report.js" +import { ServiceEntity } from "./ServiceEntity.js" + +@Entity({ tableName: "report" }) +export class ReportEntity { + @PrimaryKey() + id!: number + + @Property() + analyzedUrl!: Report["analyzedUrl"] + + @Property() + date!: Report["date"] + + @Property() + grade!: Report["grade"] + + @Property() + normalizedGrade!: Report["normalizedGrade"] + + @Property({ type: "json" }) + result!: Result + + @Property() + resultUrl?: Report["resultUrl"] + + @ManyToOne({ cascade: [Cascade.PERSIST] }) + service!: ServiceEntity + + constructor(report: GenericReport) { + this.analyzedUrl = report.analyzedUrl + this.date = report.date + this.grade = report.grade + this.normalizedGrade = report.normalizedGrade + this.result = report.result + this.resultUrl = report.resultUrl + } +} diff --git a/modules/common/src/entities/ServiceEntity.ts b/modules/common/src/entities/ServiceEntity.ts new file mode 100644 index 00000000..099820b8 --- /dev/null +++ b/modules/common/src/entities/ServiceEntity.ts @@ -0,0 +1,16 @@ +import { Entity, PrimaryKey, Property } from "@mikro-orm/core" +import type { Service } from "../service/Service.js" + +@Entity({ tableName: "service" }) +export class ServiceEntity { + @PrimaryKey() + name!: Service["name"] + + @Property() + logo?: Service["logo"] + + constructor(service: Service) { + this.name = service.name + this.logo = service.logo + } +} diff --git a/modules/common/src/index.ts b/modules/common/src/index.ts index ed762a5f..9a6b9d2a 100644 --- a/modules/common/src/index.ts +++ b/modules/common/src/index.ts @@ -1,26 +1,33 @@ +import { ReportEntity } from "./entities/ReportEntity.js" +import { ServiceEntity } from "./entities/ServiceEntity.js" import { InputError } from "./error/InputError.js" import { get, post } from "./http/Request.js" import type { ParsedAnalysisInput, ValidatedAnalysisInput } from "./input/AnalysisInput.js" import type { ParsedServerInput } from "./input/ServerInput.js" +import { Module } from "./module/Module.js" +import type { ModuleIndex } from "./module/ModuleIndex.js" +import type { ModuleInterface } from "./module/ModuleInterface.js" +import { isModuleAnalysis, type ModuleAnalysisInterface } from "./module/analysis/ModuleAnalysisInterface.js" import type { Config } from "./module/config/Config.js" import type { GreenITConfig } from "./module/config/greenit/GreeenITConfig.js" import type { LighthouseConfig } from "./module/config/lighthouse/LighthouseConfig.js" import type { ObservatoryConfig } from "./module/config/observatory/ObservatoryConfig.js" import type { SsllabsServerConfig } from "./module/config/ssllabs-server/SsllabsServerConfig.js" -import { Module } from "./module/Module.js" -import { isModuleAnalysis, type ModuleAnalysisInterface } from "./module/ModuleAnalysisInterface.js" -import type { ModuleIndex } from "./module/ModuleIndex.js" -import type { ModuleInterface } from "./module/ModuleInterface.js" -import { isModuleListener, type ModuleListenerInterface } from "./module/ModuleListenerInterface.js" -import { isModuleServer, type ModuleServerInterface } from "./module/ModuleServerInterface.js" +import { createDatabaseConfig } from "./module/listener/ModuleListenerDatabaseConfig.js" +import { + isModuleListenerDatabase, + type ModuleListenerDatabaseInterface, +} from "./module/listener/ModuleListenerDatabaseInterface.js" +import { isModuleListener, type ModuleListenerInterface } from "./module/listener/ModuleListenerInterface.js" +import { isModuleServer, type ModuleServerInterface } from "./module/server/ModuleServerInterface.js" +import type { GenericReport } from "./report/Report.js" +import type { Result } from "./report/Result.js" import { GreenITReport } from "./report/greenit/GreenITReport.js" import { LighthouseReport } from "./report/lighthouse/LighthouseReport.js" -import { ObservatoryScanState } from "./report/observatory/enum/ObservatoryScanState.js" import { ObservatoryReport } from "./report/observatory/ObservatoryReport.js" -import type { GenericReport } from "./report/Report.js" -import type { Result } from "./report/Result.js" -import { SsllabsServerStatus } from "./report/ssllabs-server/enum/SsllabsServerStatus.js" +import { ObservatoryScanState } from "./report/observatory/enum/ObservatoryScanState.js" import { SsllabsServerReport } from "./report/ssllabs-server/SsllabsServerReport.js" +import { SsllabsServerStatus } from "./report/ssllabs-server/enum/SsllabsServerStatus.js" import { timeout } from "./time/timeout.js" import { getAnalysisValidationSchema, @@ -43,6 +50,7 @@ export type { ModuleIndex, ModuleInterface, ModuleListenerInterface, + ModuleListenerDatabaseInterface, ModuleServerInterface, // Reports @@ -62,6 +70,11 @@ export type { ValidatedAnalysisInput, } export { + // Database + createDatabaseConfig, + ReportEntity, + ServiceEntity, + // Errors InputError, @@ -69,6 +82,7 @@ export { Helper, isModuleAnalysis, isModuleListener, + isModuleListenerDatabase, isModuleServer, Module, diff --git a/modules/common/src/module/ModuleAnalysisInterface.ts b/modules/common/src/module/analysis/ModuleAnalysisInterface.ts similarity index 82% rename from modules/common/src/module/ModuleAnalysisInterface.ts rename to modules/common/src/module/analysis/ModuleAnalysisInterface.ts index 94817486..dedd8bee 100644 --- a/modules/common/src/module/ModuleAnalysisInterface.ts +++ b/modules/common/src/module/analysis/ModuleAnalysisInterface.ts @@ -1,7 +1,7 @@ -import type { Config } from "./config/Config.js" -import type { Result } from "../report/Result.js" -import type { ModuleInterface } from "./ModuleInterface.js" -import type { GenericReport } from "../index.js" +import type { Config } from "../config/Config.js" +import type { Result } from "../../report/Result.js" +import type { ModuleInterface } from "../ModuleInterface.js" +import type { GenericReport } from "../../index.js" /** * Define an Analysis module. diff --git a/modules/common/src/module/listener/ModuleListenerDatabaseConfig.ts b/modules/common/src/module/listener/ModuleListenerDatabaseConfig.ts new file mode 100644 index 00000000..9d2a14b0 --- /dev/null +++ b/modules/common/src/module/listener/ModuleListenerDatabaseConfig.ts @@ -0,0 +1,18 @@ +import { type IDatabaseDriver, type Options } from "@mikro-orm/core" +import { TsMorphMetadataProvider } from "@mikro-orm/reflection" +import { ReportEntity } from "../../entities/ReportEntity.js" +import { ServiceEntity } from "../../entities/ServiceEntity.js" + +const DB_NAME = "heart" + +export function createDatabaseConfig(options: Options): Options { + options.dbName = DB_NAME + options.entities = [ReportEntity, ServiceEntity] + options.metadataProvider = TsMorphMetadataProvider + + if (options.migrations) { + options.migrations.snapshot = false + } + + return options +} diff --git a/modules/common/src/module/listener/ModuleListenerDatabaseInterface.ts b/modules/common/src/module/listener/ModuleListenerDatabaseInterface.ts new file mode 100644 index 00000000..a07cc5e3 --- /dev/null +++ b/modules/common/src/module/listener/ModuleListenerDatabaseInterface.ts @@ -0,0 +1,29 @@ +import type { ModuleListenerInterface } from "./ModuleListenerInterface.js" + +/** + * Define a database Listener module. + */ +export interface ModuleListenerDatabaseInterface extends ModuleListenerInterface { + hasPendingMigrations(): Promise + runPendingMigrations(): Promise +} + +/** + * Constructor interface signature + * @see {@link https://www.typescriptlang.org/docs/handbook/interfaces.html#difference-between-the-static-and-instance-sides-of-classes} + */ +export type ModuleListenerDatabase = new () => ModuleListenerDatabaseInterface + +/** + * Checks if a module is a Listener one. + * @see {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates | User-Defined Type Guards} + */ +export function isModuleListenerDatabase( + module: ModuleListenerInterface +): module is ModuleListenerDatabaseInterface { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return ( + (module as ModuleListenerDatabaseInterface).hasPendingMigrations !== undefined && + (module as ModuleListenerDatabaseInterface).runPendingMigrations !== undefined + ) +} diff --git a/modules/common/src/module/ModuleListenerInterface.ts b/modules/common/src/module/listener/ModuleListenerInterface.ts similarity index 82% rename from modules/common/src/module/ModuleListenerInterface.ts rename to modules/common/src/module/listener/ModuleListenerInterface.ts index 0b47b759..0917b969 100644 --- a/modules/common/src/module/ModuleListenerInterface.ts +++ b/modules/common/src/module/listener/ModuleListenerInterface.ts @@ -1,6 +1,6 @@ -import type { GenericReport } from "../report/Report.js" -import type { Result } from "../report/Result.js" -import type { ModuleInterface } from "./ModuleInterface.js" +import type { GenericReport } from "../../report/Report.js" +import type { Result } from "../../report/Result.js" +import type { ModuleInterface } from "../ModuleInterface.js" /** * Define an Listener module. diff --git a/modules/common/src/module/ModuleServerInterface.ts b/modules/common/src/module/server/ModuleServerInterface.ts similarity index 86% rename from modules/common/src/module/ModuleServerInterface.ts rename to modules/common/src/module/server/ModuleServerInterface.ts index ff8e552a..b1059d70 100644 --- a/modules/common/src/module/ModuleServerInterface.ts +++ b/modules/common/src/module/server/ModuleServerInterface.ts @@ -1,8 +1,8 @@ import type { FastifyCorsOptions } from "@fastify/cors" import type { FastifyInstance } from "fastify" -import type { Config, GenericReport, ModuleListenerInterface, Result } from "../index.js" -import type { ModuleAnalysisInterface } from "./ModuleAnalysisInterface.js" -import type { ModuleInterface } from "./ModuleInterface.js" +import type { Config, GenericReport, ModuleListenerInterface, Result } from "../../index.js" +import type { ModuleInterface } from "../ModuleInterface.js" +import type { ModuleAnalysisInterface } from "../analysis/ModuleAnalysisInterface.js" /** * Define a Server module. diff --git a/modules/common/src/validation/input/analysis/AnalysisInputValidation.ts b/modules/common/src/validation/input/analysis/AnalysisInputValidation.ts index d45d9cf0..1def261a 100644 --- a/modules/common/src/validation/input/analysis/AnalysisInputValidation.ts +++ b/modules/common/src/validation/input/analysis/AnalysisInputValidation.ts @@ -1,6 +1,6 @@ import type { SchemaObject } from "ajv" import type { ValidatedAnalysisInput } from "../../../index.js" -import type { ModuleListenerInterface } from "../../../module/ModuleListenerInterface.js" +import type { ModuleListenerInterface } from "../../../module/listener/ModuleListenerInterface.js" import { validateInput } from "../InputValidation.js" import configSchema from "./schema/config.json" assert { type: "json" } import thresholdSchema from "./schema/threshold.json" assert { type: "json" } diff --git a/modules/common/tests/validation/input/analysis/AnalysisInputValidation.test.ts b/modules/common/tests/validation/input/analysis/AnalysisInputValidation.test.ts index bfb5c4b0..3910e99a 100644 --- a/modules/common/tests/validation/input/analysis/AnalysisInputValidation.test.ts +++ b/modules/common/tests/validation/input/analysis/AnalysisInputValidation.test.ts @@ -1,5 +1,5 @@ import { InputError } from "../../../../src/error/InputError.js" -import type { ModuleListenerInterface } from "../../../../src/module/ModuleListenerInterface.js" +import type { ModuleListenerInterface } from "../../../../src/module/listener/ModuleListenerInterface.js" import { validateAnalysisInput } from "../../../../src/validation/input/analysis/AnalysisInputValidation.js" describe("Invalid option combinations", () => { diff --git a/modules/common/tsconfig.json b/modules/common/tsconfig.json index e3459aad..b476366d 100644 --- a/modules/common/tsconfig.json +++ b/modules/common/tsconfig.json @@ -4,7 +4,9 @@ "extends": "../tsconfig.json", "compilerOptions": { + "emitDecoratorMetadata": true, "esModuleInterop": true, + "experimentalDecorators": true, "resolveJsonModule": true }, diff --git a/modules/greenit/package.json b/modules/greenit/package.json index 45b268fb..e3bced47 100644 --- a/modules/greenit/package.json +++ b/modules/greenit/package.json @@ -47,7 +47,7 @@ "devDependencies": { "@jest/globals": "^29.6.1", "@types/jest": "^29.5.3", - "@types/node": "^18.15.10", + "@types/node": "^18.16.19", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", "eslint": "^8.36.0", diff --git a/modules/lighthouse/package.json b/modules/lighthouse/package.json index 70164ba7..373ceae7 100644 --- a/modules/lighthouse/package.json +++ b/modules/lighthouse/package.json @@ -40,7 +40,7 @@ "devDependencies": { "@jest/globals": "^29.6.1", "@types/jest": "^29.5.3", - "@types/node": "^18.15.10", + "@types/node": "^18.16.19", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", "eslint": "^8.36.0", diff --git a/modules/moduletpl/package.json b/modules/moduletpl/package.json index 00070789..999eff66 100644 --- a/modules/moduletpl/package.json +++ b/modules/moduletpl/package.json @@ -35,7 +35,7 @@ "devDependencies": { "@jest/globals": "^29.6.1", "@types/jest": "^29.5.3", - "@types/node": "^18.15.10", + "@types/node": "^18.16.19", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", "eslint": "^8.36.0", diff --git a/modules/mysql/.env.tpl b/modules/mysql/.env.tpl new file mode 100644 index 00000000..7a5f253e --- /dev/null +++ b/modules/mysql/.env.tpl @@ -0,0 +1,2 @@ +# example on localhost on default port with a root user without password: mysql://root@127.0.0.1:3306 +HEART_MYSQL_DATABASE_URL= diff --git a/modules/mysql/.eslintrc.cjs b/modules/mysql/.eslintrc.cjs new file mode 100644 index 00000000..e007b176 --- /dev/null +++ b/modules/mysql/.eslintrc.cjs @@ -0,0 +1,26 @@ +module.exports = { + parser: "@typescript-eslint/parser", + parserOptions: { + tsconfigRootDir: __dirname, + project: ["./tsconfig.json"], + }, + plugins: ["@typescript-eslint"], + extends: [ + /** + * @see {@link https://eslint.org/docs/latest/rules/ } + */ + "eslint:recommended", + /** + * @see {@link https://typescript-eslint.io/rules/ } + */ + "plugin:@typescript-eslint/recommended", + /** + * @see {@link https://typescript-eslint.io/docs/linting/typed-linting/ } + */ + "plugin:@typescript-eslint/recommended-requiring-type-checking", + /** + * @see {@link https://typescript-eslint.io/linting/configs/#strict } + */ + "plugin:@typescript-eslint/strict", + ], +} diff --git a/modules/mysql/README.md b/modules/mysql/README.md new file mode 100644 index 00000000..0397c8c1 --- /dev/null +++ b/modules/mysql/README.md @@ -0,0 +1,41 @@ +# Heart MySQL + +_Heart MySQL_ is a _listener_ module of _Heart_, which reacts to the end of an analysis and stores the results of an analysis in a MySQL database. + +Note that you must install an _analysis_ module too, to have a minimum viable installation of _Heart_. + +Read more about [the description and design of _Heart_](https://github.com/faberNovel/heart#readme). + +# Usage + +## Standalone + +1. Install the package, _[Heart CLI](https://www.npmjs.com/package/@fabernovel/heart-cli)_ and an _analysis_ module, for example _[Heart GreenIT](https://www.npmjs.com/package/@fabernovel/heart-greenit)_ + + ```bash + npm install @fabernovel/heart-cli @fabernovel/heart-greenit @fabernovel/heart-mysql + ``` + +2. In the project root folder, create a `.env` file with the database connection information set as a URL + + ```dotenv + HEART_MYSQL_DATABASE_URL=mysql://root@127.0.0.1:3306 + ``` + +3. Start an analysis + + ```bash + npx heart greenit --config '{"url":"https://www.fabernovel.com"}' + ``` + + OR + + ```bash + npx heart greenit --file configuration.json + ``` + + Once the analysis is done, the results are stored in your _MySQL_ database. + +## Github Action + +If you're using Github, you can simplify the integration of Heart in your CI scripts by using the [Github Action](https://github.com/marketplace/actions/heart-webpages-evaluation). diff --git a/modules/mysql/jest.config.mjs b/modules/mysql/jest.config.mjs new file mode 100644 index 00000000..4d9ec41d --- /dev/null +++ b/modules/mysql/jest.config.mjs @@ -0,0 +1,194 @@ +/* + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/configuration + */ +import defaultConfig from "../jest.config.mjs" + +export default { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/tmp/jest_rs", + + // Automatically clear mock calls, instances, contexts and results before every test + clearMocks: defaultConfig.clearMocks, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: defaultConfig.collectCoverage, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + collectCoverageFrom: defaultConfig.collectCoverageFrom, + + // The directory where Jest should output its coverage files + // coverageDirectory: undefined, + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: "v8", + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // The default configuration for fake timers + // fakeTimers: { + // "enableGlobally": false + // }, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + moduleDirectories: ["/src", "node_modules"], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "mjs", + // "cjs", + // "jsx", + // "ts", + // "tsx", + // "json", + // "node" + // ], + + extensionsToTreatAsEsm: defaultConfig.extensionsToTreatAsEsm, + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + moduleNameMapper: defaultConfig.moduleNameMapper, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + preset: defaultConfig.preset, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state before every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state and implementation before every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + rootDir: "./", + + // A list of paths to directories that Jest should use to search for files in + roots: ["/src/", "/tests/"], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + // testEnvironment: "jest-environment-node", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jest-circus/runner", + + // A map from regular expressions to paths to transformers + transform: defaultConfig.transform, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/", + // "\\.pnp\\.[^\\/]+$" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +} diff --git a/modules/mysql/package.json b/modules/mysql/package.json new file mode 100644 index 00000000..c12cf633 --- /dev/null +++ b/modules/mysql/package.json @@ -0,0 +1,67 @@ +{ + "name": "@fabernovel/heart-mysql", + "version": "4.0.0-alpha.5", + "description": "Store the results of analysis into a MySQL database", + "homepage": "https://heart.fabernovel.com", + "bugs": { + "url": "https://github.com/faberNovel/heart/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/faberNovel/heart.git", + "directory": "modules/mysql" + }, + "license": "MIT", + "contributors": [ + "Bastien Gatellier " + ], + "type": "module", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "directories": { + "lib": "lib" + }, + "files": [ + "lib/**/*", + ".env.tpl" + ], + "scripts": { + "build": "rimraf lib && tsc --project tsconfig.build.json", + "build:watch": "npm run build --watch", + "lint": "eslint .", + "prepublishOnly": "npm run build", + "test": "NODE_OPTIONS='--experimental-vm-modules' jest --passWithNoTests" + }, + "dependencies": { + "@fabernovel/heart-common": "workspace:^4.0.0-alpha.5", + "@mikro-orm/core": "^5.7.13", + "@mikro-orm/migrations": "^5.7.13", + "@mikro-orm/mysql": "^5.7.13" + }, + "devDependencies": { + "@jest/globals": "^29.6.1", + "@mikro-orm/cli": "^5.7.13", + "@types/jest": "^29.5.3", + "@types/node": "^18.16.19", + "@typescript-eslint/eslint-plugin": "^5.57.0", + "@typescript-eslint/parser": "^5.57.0", + "eslint": "^8.36.0", + "jest": "^29.6.1", + "rimraf": "^5.0.1", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.1", + "typescript": "^5.1.6" + }, + "peerDependencies": { + "@fabernovel/heart-cli": "^4.0.0-alpha.5" + }, + "engines": { + "node": ">=18 <21" + }, + "mikro-orm": { + "useTsNode": true, + "configPaths": [ + "./src/config/mikro-orm.config.ts" + ] + } +} diff --git a/modules/mysql/src/MySQLModule.ts b/modules/mysql/src/MySQLModule.ts new file mode 100644 index 00000000..5b50e7e5 --- /dev/null +++ b/modules/mysql/src/MySQLModule.ts @@ -0,0 +1,40 @@ +import { + Module, + type GenericReport, + type ModuleListenerDatabaseInterface, + type Result, + type ModuleInterface, +} from "@fabernovel/heart-common" +import { MySQLClient } from "./client/Client.js" + +export class MySQLModule extends Module implements ModuleListenerDatabaseInterface { + #client: MySQLClient + + constructor(module: Pick) { + super(module) + this.#client = new MySQLClient() + } + + public async hasPendingMigrations(): Promise { + const migrator = await this.#client.getMigrator() + + const migrations = await migrator.getPendingMigrations() + + return migrations.length > 0 + } + + public async runPendingMigrations(): Promise { + const migrator = await this.#client.getMigrator() + + await migrator.up() + } + + public async notifyAnalysisDone(report: GenericReport): Promise { + try { + await this.#client.save(report) + } catch (error) { + console.error(error) + return Promise.reject(error) + } + } +} diff --git a/modules/mysql/src/client/Client.ts b/modules/mysql/src/client/Client.ts new file mode 100644 index 00000000..6c022170 --- /dev/null +++ b/modules/mysql/src/client/Client.ts @@ -0,0 +1,72 @@ +import { ReportEntity, type GenericReport, type Result, ServiceEntity } from "@fabernovel/heart-common" +import { MikroORM, type IMigrator } from "@mikro-orm/core" +import { SqlEntityManager, type MySqlDriver } from "@mikro-orm/mysql" +import databaseConfig from "../config/mikro-orm.config.js" + +export class MySQLClient { + #em: SqlEntityManager | undefined + #orm: MikroORM | undefined + + public async getMigrator(): Promise { + const orm = await this.getOrCreateOrm() + + return orm.getMigrator() + } + + /** + * Save the given report into the database. + */ + public async save(report: GenericReport): Promise { + const reportEntity = await this.createReportEntity(report) + + const em = await this.getOrCreateEntityManager() + const orm = await this.getOrCreateOrm() + + await em.persistAndFlush(reportEntity) + + return orm.close() + } + + private async createReportEntity(report: GenericReport): Promise> { + const reportEntity = new ReportEntity(report) + const serviceEntity = await this.getOrCreateServiceEntity(report.service) + + reportEntity.service = serviceEntity + + return reportEntity + } + + /** + * Get or create the ORM: avoid the opening of several DB connection at the same or in a very short interval of time. + * As Heart is a CLI tool, having the DB connection opened during a single analysis that longs a couple of minutes is acceptable. + */ + private async getOrCreateOrm(): Promise> { + if (this.#orm === undefined) { + this.#orm = await MikroORM.init(databaseConfig) + } + + return this.#orm + } + + /** + * Get or create the entity manager + * @link https://mikro-orm.io/docs/identity-map + */ + private async getOrCreateEntityManager(): Promise { + const orm = await this.getOrCreateOrm() + + if (this.#em === undefined) { + this.#em = orm.em.fork() + } + + return this.#em + } + + private async getOrCreateServiceEntity(service: GenericReport["service"]): Promise { + const em = await this.getOrCreateEntityManager() + + const serviceEntity = await em.findOne(ServiceEntity, { name: service.name }) + + return serviceEntity ?? new ServiceEntity(service) + } +} diff --git a/modules/mysql/src/config/mikro-orm.config.ts b/modules/mysql/src/config/mikro-orm.config.ts new file mode 100644 index 00000000..af60850b --- /dev/null +++ b/modules/mysql/src/config/mikro-orm.config.ts @@ -0,0 +1,20 @@ +import { createDatabaseConfig } from "@fabernovel/heart-common" +import { MySqlDriver, defineConfig } from "@mikro-orm/mysql" +import { env } from "node:process" +import { Migration20230702150637 } from "../migrations/Migration20230702150637.js" + +export default defineConfig( + createDatabaseConfig({ + clientUrl: env.HEART_MYSQL_DATABASE_URL as string, + migrations: { + migrationsList: [ + { + name: "Migration20230702150637", + class: Migration20230702150637, + }, + ], + }, + charset: "utf8mb4", + collate: "utf8mb4_general_ci", + }) +) diff --git a/modules/mysql/src/index.ts b/modules/mysql/src/index.ts new file mode 100644 index 00000000..da345dac --- /dev/null +++ b/modules/mysql/src/index.ts @@ -0,0 +1,8 @@ +import { MySQLModule } from "./MySQLModule.js" + +export default new MySQLModule({ + name: "Heart MySQL", + service: { + name: "MySQL", + }, +}) diff --git a/modules/mysql/src/migrations/Migration20230702150637.ts b/modules/mysql/src/migrations/Migration20230702150637.ts new file mode 100644 index 00000000..de5fcd90 --- /dev/null +++ b/modules/mysql/src/migrations/Migration20230702150637.ts @@ -0,0 +1,19 @@ +import { Migration } from "@mikro-orm/migrations" + +export class Migration20230702150637 extends Migration { + // eslint-disable-next-line @typescript-eslint/require-await + async up(): Promise { + this.addSql( + "create table `service` (`name` varchar(255) not null, `logo` varchar(255) null, primary key (`name`)) default character set utf8mb4 collate utf8mb4_general_ci engine = InnoDB;" + ) + + this.addSql( + "create table `report` (`id` int unsigned not null auto_increment primary key, `analyzed_url` varchar(255) not null, `date` datetime not null, `grade` varchar(255) not null, `normalized_grade` int not null, `result` json not null, `result_url` varchar(255) null, `service_name` varchar(255) not null) default character set utf8mb4 collate utf8mb4_general_ci engine = InnoDB;" + ) + this.addSql("alter table `report` add index `report_service_name_index`(`service_name`);") + + this.addSql( + "alter table `report` add constraint `report_service_name_foreign` foreign key (`service_name`) references `service` (`name`) on update cascade;" + ) + } +} diff --git a/modules/mysql/tests/.gitignore b/modules/mysql/tests/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/modules/mysql/tsconfig.build.json b/modules/mysql/tsconfig.build.json new file mode 100644 index 00000000..52c4e3f9 --- /dev/null +++ b/modules/mysql/tsconfig.build.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "extends": "./tsconfig.json", + + "compilerOptions": { + "outDir": "lib" + }, + + "include": ["src/**/*.ts"] +} diff --git a/modules/mysql/tsconfig.json b/modules/mysql/tsconfig.json new file mode 100644 index 00000000..8e2b231a --- /dev/null +++ b/modules/mysql/tsconfig.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "extends": "../tsconfig.json", + + "compilerOptions": { + "emitDecoratorMetadata": true, + "esModuleInterop": true, + "experimentalDecorators": true + }, + + "include": ["src/**/*.ts", "tests/**/*.ts"] +} diff --git a/modules/observatory/package.json b/modules/observatory/package.json index 92cd40d8..e4ae2f00 100644 --- a/modules/observatory/package.json +++ b/modules/observatory/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@jest/globals": "^29.6.1", "@types/jest": "^29.5.3", - "@types/node": "^18.15.10", + "@types/node": "^18.16.19", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", "eslint": "^8.36.0", diff --git a/modules/slack/README.md b/modules/slack/README.md index b468129c..d2a6d56f 100644 --- a/modules/slack/README.md +++ b/modules/slack/README.md @@ -6,7 +6,6 @@ Note that you must install an _analysis_ module too, to have a minimum viable in Read more about [the description and design of _Heart_](https://github.com/faberNovel/heart#readme). - # Usage ## Standalone diff --git a/modules/slack/package.json b/modules/slack/package.json index b6c8ee09..4d1f35d4 100644 --- a/modules/slack/package.json +++ b/modules/slack/package.json @@ -1,7 +1,7 @@ { "name": "@fabernovel/heart-slack", "version": "4.0.0-alpha.5", - "description": "Listener module of Heart, which reacts to the end of an analysis by sending the results to a Slack channel", + "description": "Send the analysis results of Heart into a Slack channel", "homepage": "https://heart.fabernovel.com", "bugs": { "url": "https://github.com/faberNovel/heart/issues" @@ -39,7 +39,7 @@ "devDependencies": { "@jest/globals": "^29.6.1", "@types/jest": "^29.5.3", - "@types/node": "^18.15.10", + "@types/node": "^18.16.19", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", "eslint": "^8.36.0", diff --git a/modules/ssllabs-server/package.json b/modules/ssllabs-server/package.json index a43294e2..ffd24c11 100644 --- a/modules/ssllabs-server/package.json +++ b/modules/ssllabs-server/package.json @@ -37,7 +37,7 @@ "devDependencies": { "@jest/globals": "^29.6.1", "@types/jest": "^29.5.3", - "@types/node": "^18.15.10", + "@types/node": "^18.16.19", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", "eslint": "^8.36.0", diff --git a/rush.json b/rush.json index 175c30b3..a348a992 100644 --- a/rush.json +++ b/rush.json @@ -417,6 +417,11 @@ "projectFolder": "modules/moduletpl", "shouldPublish": false }, + { + "packageName": "@fabernovel/heart-mysql", + "projectFolder": "modules/mysql", + "versionPolicyName": "nextMajor" + }, { "packageName": "@fabernovel/heart-observatory", "projectFolder": "modules/observatory", diff --git a/test/.env.template b/test/.env.template index ebef2658..5c97bfa8 100644 --- a/test/.env.template +++ b/test/.env.template @@ -4,3 +4,6 @@ HEART_BIGQUERY_GOOGLE_APPLICATION_CREDENTIALS={"type": "service_account","projec # Heart Slack HEART_SLACK_API_TOKEN= HEART_SLACK_CHANNEL_ID= + +# Heart MySQL +HEART_MYSQL_DATABASE_URL= diff --git a/test/docker-compose.yml b/test/docker-compose.yml new file mode 100644 index 00000000..9a2b9d42 --- /dev/null +++ b/test/docker-compose.yml @@ -0,0 +1,11 @@ +services: + mysql: + container_name: heart_mysql + image: mysql:5.7 + ports: + - 3306:3306 + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: heart + MYSQL_USER: heart + MYSQL_PASSWORD: heart diff --git a/test/package.json b/test/package.json index 2d635199..0601e86c 100644 --- a/test/package.json +++ b/test/package.json @@ -14,6 +14,7 @@ "@fabernovel/heart-cli": "workspace:^4.0.0-alpha.5", "@fabernovel/heart-greenit": "workspace:^4.0.0-alpha.5", "@fabernovel/heart-lighthouse": "workspace:^4.0.0-alpha.5", + "@fabernovel/heart-mysql": "workspace:^4.0.0-alpha.5", "@fabernovel/heart-observatory": "workspace:^4.0.0-alpha.5", "@fabernovel/heart-slack": "workspace:^4.0.0-alpha.5", "@fabernovel/heart-ssllabs-server": "workspace:^4.0.0-alpha.5"